Ticket #1944: 0001-killall-add-killall-command.patch

File 0001-killall-add-killall-command.patch, 11.7 KB (added by Prasad, 11 years ago)
  • src/bin/coreutils/src/Jamfile

    From 98cd77ea9476ee1b71eeb0f5eafe61e3743781e9 Mon Sep 17 00:00:00 2001
    From: Prasad Joshi <prasadjoshi.linux@gmail.com>
    Date: Mon, 22 Oct 2012 22:30:11 +0530
    Subject: [PATCH] killall: add killall command
    
    killall command is used to send signal to proceeses executing any  of
    the command specified by user as an argument.
    
    Fixes the ticket: https://dev.haiku-os.org/ticket/1944
    ---
     src/bin/coreutils/src/Jamfile   |    2 +
     src/bin/coreutils/src/killall.c |  534 +++++++++++++++++++++++++++++++++++++++
     2 files changed, 536 insertions(+), 0 deletions(-)
     create mode 100644 src/bin/coreutils/src/killall.c
    
    diff --git a/src/bin/coreutils/src/Jamfile b/src/bin/coreutils/src/Jamfile
    index 90fdf17..d4ad243 100644
    a b BinCommand "[" : lbracket.c : libfetish.a : $(coreutils_rsrc) ;  
    110110
    111111BinCommand kill : kill.c operand2sig.c : libfetish.a : $(coreutils_rsrc) ;
    112112
     113BinCommand killall : killall.c operand2sig.c : libfetish.a : $(coreutils_rsrc) ;
     114
    113115BinCommand id : id.c group-list.c : libfetish.a : $(coreutils_rsrc) ;
    114116
    115117BinCommand groups : groups.c [ FGristFiles group-list.o ] : libfetish.a : $(coreutils_rsrc) ;
  • new file src/bin/coreutils/src/killall.c

    diff --git a/src/bin/coreutils/src/killall.c b/src/bin/coreutils/src/killall.c
    new file mode 100644
    index 0000000..cf6f8d6
    - +  
     1/*
     2 * killall.c - send signal to processes
     3 *
     4 * Copyright 2012 Haiku, Inc. All rights reserved.
     5 * Distributed under the terms of the MIT License.
     6 *
     7 * Author:
     8 *  Prasad Joshi <prasadjoshi.linux@gmail.com>
     9 */
     10
     11#include <stdio.h>
     12#include <unistd.h>
     13#include <stdlib.h>
     14#include <signal.h>
     15#include <getopt.h>
     16#include <libgen.h>
     17#include <errno.h>
     18#include <sys/types.h>
     19#include <regex.h>
     20#include <pwd.h>
     21#include <OS.h>
     22
     23#include "sig2str.h"
     24#include "operand2sig.h"
     25
     26struct process {
     27    pid_t   pid;
     28    char    *command;
     29    uid_t   uid;
     30    bool    kill;
     31};
     32
     33static bool exact   = false; // perform exact matching
     34static bool ignore_case = false; // case insensitive match
     35static bool interactive = false; // ask before killing
     36static bool group   = false; // kill group
     37static bool regex_match = false; // regular expression match
     38static bool verbose = false; // print details
     39static bool kill_wait   = false; // wait for processes to get killed
     40static bool dry_run = false; // do not kill any process
     41
     42static uid_t uid;       // user id
     43static char *user = NULL;   // user name
     44
     45static int sig_num = SIGTERM;       // signal to send
     46static char sig_name[SIG2STR_MAX];  // signal in string format
     47
     48static char **commands = NULL;  // list of user specified processes
     49
     50void
     51usage(const char *s)
     52{
     53    fprintf(stderr, "\nUsage: killall [OPTION] NAME\n\n");
     54    fprintf(stderr, "\t -l,--list       display list of signals.\n");
     55    fprintf(stderr, "\t -e,--exact      exact match for long names.\n");
     56    fprintf(stderr, "\t -I,--ignore-case    case insensitive name match.\n");
     57    fprintf(stderr, "\t -g,--process-group  kill all processes from process group.\n");
     58    fprintf(stderr, "\t -y,--younger-than   kill processes created after TIME.\n");
     59    fprintf(stderr, "\t -o,--older-than     kill processes created before TIME.\n");
     60    fprintf(stderr, "\t -i,--interactive    confirm before killing.\n");
     61    fprintf(stderr, "\t -r,--regexp         regular expression match for name.\n");
     62    fprintf(stderr, "\t -s,--signal SIGNAL  send specified signal (default: SIGTERM).\n");
     63    fprintf(stderr, "\t -u,--user USER      kill processes of given USER.\n");
     64    fprintf(stderr, "\t -v,--verbose        Verbose output.\n");
     65    fprintf(stderr, "\t -w,--wait           ensure processes are killed.\n");
     66    fprintf(stderr, "\t -d,--dry-run        do not send signal.\n");
     67    fprintf(stderr, "\n");
     68}
     69
     70
     71void
     72free_process(struct process *procs, int no_proccesses)
     73{
     74    int c = no_proccesses - 1;
     75    struct process *t;
     76
     77    for ( ; c >= 0; c--) {
     78        t = &procs[c];
     79        if (t && t->command)
     80            free(t->command);
     81    }
     82    free(procs);
     83}
     84
     85
     86int
     87get_response(char *sig_name, pid_t pid, bool group)
     88{
     89    char   *line = NULL;
     90    size_t size = 0;
     91    int    rc;
     92
     93    if (!group)
     94        printf("Signal (%s) process %d? [n]: ", sig_name, pid);
     95    else
     96        printf("Signal (%s) group of process %d? [n]: ", sig_name, pid);
     97
     98    if (getline(&line, &size, stdin) < 0) {
     99        fprintf(stderr, "Cannot read user input: %s", strerror(errno));
     100        // TODO: treated as false response
     101        return 0;
     102    }
     103
     104    // rc would be 1 only for yes response
     105    rc = rpmatch(line) <= 0? 0 : 1;
     106
     107    free(line);
     108    return rc;
     109}
     110
     111
     112struct
     113process *get_all_processes(int *no_proccesses)
     114{
     115    team_info tm;
     116    uint32    tc;
     117
     118    struct process  *procs = NULL;
     119    struct process  *t     = NULL;
     120    int     c      = 0;
     121
     122    // find the process
     123    tc = 0;
     124    while (get_next_team_info(&tc, &tm) >= B_OK) {
     125        char *p, *q;
     126
     127        q = tm.args;
     128        p = strchr(q, ' ');
     129        if (p) {
     130            // we are only interested in the program name
     131            *p = 0;
     132        }
     133
     134        t = realloc((void *) procs, sizeof(*procs) * (c + 1));
     135        if (!t)
     136            goto error;
     137
     138        procs       = t;
     139        t       = &procs[c];
     140        t->pid      = tm.team;
     141        t->command  = strdup(q);
     142        t->uid      = tm.uid;
     143        t->kill     = false;
     144        c++;
     145
     146        if (!t->command)
     147            goto error;
     148    }
     149    *no_proccesses = c;
     150
     151    return procs;
     152error:
     153    free_process(procs, c);
     154    *no_proccesses = 0;
     155    return NULL;
     156}
     157
     158
     159bool
     160match_regex(char *name, regex_t *re_list, int regs)
     161{
     162    char    *r;
     163    char    *s;
     164    int     i;
     165    regex_t *re;
     166
     167    r = strdup(name);
     168    s = basename(r);
     169
     170    for (i = 0; i < regs; i++) {
     171        re = &re_list[i];
     172
     173        if (regexec(re, s, 0, NULL, 0) != REG_NOMATCH) {
     174            free(r);
     175            return true;
     176        }
     177    }
     178    free(r);
     179    return false;
     180}
     181
     182
     183bool
     184match_names(char *name, char **name_list, int names, bool ignore_case)
     185{
     186    char *r;
     187    char *s;
     188    int  i;
     189    char *n;
     190    bool rc = false;
     191
     192    r = strdup(name);
     193    s = basename(r);
     194
     195    for (i = 0; i < names; i++) {
     196        n = name_list[i];
     197
     198        if (strchr(n, '/')) {
     199            struct stat s1, s2;
     200            ino_t inode;
     201
     202            /*
     203             * user specified the path of the process to kill.
     204             * - find inode number of the path
     205             * - find inode number of the program file
     206             * - match inode number
     207             */
     208            if (stat(n, &s1) < 0)
     209                goto out;
     210
     211            // inode of given path
     212            inode = s1.st_ino;
     213
     214            // find the inode of the program running
     215            if (stat(name, &s2) < 0)
     216                goto out;
     217
     218            if (s1.st_dev == s2.st_dev && inode == s2.st_ino) {
     219                rc = true;
     220                break;
     221            }
     222
     223        } else if ((ignore_case && !strcasecmp(s, n)) || !strcmp(s, n)) {
     224            rc = true;
     225            break;
     226        }
     227    }
     228out:
     229    free(r);
     230    return rc;
     231}
     232
     233
     234int
     235do_kill(struct process *p, int no_proccesses)
     236{
     237    int i;
     238    struct process *t;
     239    pid_t pid;
     240    int no_killed = 0;
     241
     242    for (i = 0; i < no_proccesses; i++) {
     243        t = &p[i];
     244
     245        if (!t->kill)
     246            continue;
     247
     248        pid = t->pid;
     249
     250        // check for user input if needed
     251        if (interactive && !get_response(sig_name, pid, group))
     252            continue;
     253
     254        if (group)
     255            pid = -pid;
     256
     257
     258        if (verbose)
     259            printf("Sending signal (%s) to process (%d).\n", sig_name, pid);
     260
     261        if (!dry_run && (kill(pid, sig_num) < 0)) {
     262            // sending signal to this process failed
     263            t->kill = false;
     264            fprintf(stderr, "Signal to process (%d) failed: %s\n",
     265                    t->pid, strerror(errno));
     266            continue;
     267        }
     268
     269        no_killed++;
     270    }
     271
     272    if (verbose && no_killed)
     273        printf("Sent %s to %d processes.\n", sig_name, no_killed);
     274
     275    if (!kill_wait)
     276        return 0;
     277
     278    /*
     279     * Wait for processes to terminate. If a process does not terminate
     280     * this will hange forever.
     281     */
     282    while (!dry_run && no_killed) {
     283        for (i = 0; i < no_proccesses; i++) {
     284            t = &p[i];
     285
     286            if (!t->kill)
     287                continue;
     288
     289            pid = t->pid;
     290
     291            // check if process is still running
     292            if (kill(pid, 0) < 0) {
     293                // process is killed
     294                no_killed--;
     295                t->kill = false;
     296            }
     297        }
     298
     299        // give other processes chance to run
     300        sleep(2);
     301    }
     302
     303    return 0;
     304}
     305
     306
     307int
     308kill_all(char **cmd_list, int commands)
     309{
     310    int     rc = -1;
     311    struct process  *procs;
     312    int     no_proccesses;
     313    pid_t       self_pid;
     314    int         i;
     315    regex_t     *re;
     316    regex_t     re_list[commands];
     317    int     regs;
     318    struct process  *t;
     319
     320    // find all processes
     321    procs = get_all_processes(&no_proccesses);
     322    if (!procs)
     323        return -1;
     324
     325    self_pid = getpid();
     326
     327    // build regular expressions
     328    if (regex_match) {
     329        int flag = REG_EXTENDED | REG_NOSUB;
     330        if (ignore_case)
     331            flag |= REG_ICASE;
     332
     333        for (i = 0; i < commands; i++) {
     334            char *n = cmd_list[i];
     335
     336            re = &re_list[i];
     337            rc = regcomp(re, n, flag);
     338            if (rc != 0) {
     339                fprintf(stderr, "Incorrect regular expression: %s\n", n);
     340                goto error;
     341            }
     342        }
     343
     344        regs = commands;
     345    }
     346
     347    // find the list of processes to be killed
     348    for (i = 0; i < no_proccesses; i++) {
     349        t = &procs[i];
     350
     351        // do not kill self
     352        if (t->pid == self_pid)
     353            continue;
     354
     355        // match uid
     356        if (user && t->uid != uid)
     357            continue;
     358
     359        if (user && !commands) {
     360            // kill all processes of specified user
     361            t->kill = true;
     362            continue;
     363        }
     364
     365        // match name
     366        if (regex_match)
     367            t->kill = match_regex(t->command, re_list, regs);
     368        else
     369            t->kill = match_names(t->command, cmd_list, commands, ignore_case);
     370    }
     371
     372    do_kill(procs, no_proccesses);
     373
     374    if (regex_match) {
     375        for (i = 0; i < commands; i++) {
     376            re = &re_list[i];
     377            regfree(re);
     378        }
     379    }
     380
     381    rc = 0;
     382error:
     383    free_process(procs, no_proccesses);
     384    return rc;
     385}
     386
     387
     388void
     389list_signals()
     390{
     391    int s;
     392    char name[SIG2STR_MAX];
     393
     394    for (s = 1; s <= SIGNUM_BOUND; s++) {
     395        if (!sig2str(s, name)) {
     396            printf("%s ", name);
     397        }
     398    }
     399    printf("\n");
     400}
     401
     402
     403int
     404main(int argc, char *argv[])
     405{
     406    char *program;
     407    char opt;
     408    static struct option options[] = {
     409        { "list",       no_argument,        NULL, 'l' },
     410        { "ignore-case",    no_argument,        NULL, 'I' },
     411        { "process-group",  no_argument,        NULL, 'g' },
     412        { "interactive",    no_argument,        NULL, 'i' },
     413        { "regexp",     no_argument,        NULL, 'r' },
     414        { "signal",     required_argument,  NULL, 's' },
     415        { "user",       required_argument,  NULL, 'u' },
     416        { "verbose",        no_argument,        NULL, 'v' },
     417        { "wait",       no_argument,        NULL, 'w' },
     418        { "dry-run",        no_argument,        NULL, 'd' },
     419        { 0,            0,          0,     0  }
     420    };
     421
     422    program = argv[0];
     423    if (argc < 2)
     424        usage(NULL);
     425
     426    // tell getopt to not to print 'unrecognized options' error
     427    opterr = 0;
     428    while (1) {
     429        opt = getopt_long_only(argc, argv, "lIgirs:u:vwd", options, NULL);
     430
     431        if (opt < 0)
     432            break;
     433
     434        switch (opt) {
     435        case 'l':
     436            // display the list of signals
     437            list_signals();
     438            return 0;
     439
     440        case 'I':
     441            ignore_case = true;
     442            break;
     443
     444        case 'g':
     445            group = true;
     446            break;
     447
     448        case 'i':
     449            interactive = true;
     450            break;
     451
     452        case 'r':
     453            regex_match = true;
     454            break;
     455
     456        case 's':
     457            sig_num = operand2sig(optarg, sig_name);
     458            if (sig_num < 0)
     459                exit(EINVAL);
     460            break;
     461
     462        case 'u':
     463        {
     464            struct passwd *pw;
     465
     466            if (!optarg) {
     467                usage(NULL);
     468                exit(EINVAL);
     469            }
     470
     471            user = strdup(optarg);
     472            if (!user) {
     473                fprintf(stderr, "Memory allocation failed.\n");
     474                exit(ENOMEM);
     475            }
     476
     477            pw = getpwnam(user);
     478            if (!pw) {
     479                fprintf(stderr, "%s is not a user.\n", user);
     480                free(user);
     481                exit(EINVAL);
     482            }
     483
     484            uid = pw->pw_uid;
     485
     486            break;
     487        }
     488
     489        case 'd':
     490            dry_run = true;
     491            break;
     492
     493        case 'v':
     494            verbose = true;
     495            break;
     496
     497        case 'w':
     498            kill_wait = true;
     499            break;
     500
     501        case '?':
     502        {
     503            char c = argv[optind - 1][1];
     504            sig_num = -1;
     505            if (isdigit(c) || (c >= 'A' && c <= 'Z'))
     506                sig_num = operand2sig(&argv[optind - 1][1], sig_name);
     507
     508            if (sig_num < 0) {
     509                usage(NULL);
     510                exit(EINVAL);
     511            }
     512            break;
     513        }
     514        default:
     515            usage(NULL);
     516            exit(EINVAL);
     517            break;
     518
     519        }
     520    }
     521
     522    if (!argv[optind] && !user) {
     523        // which process to kill?
     524        usage(NULL);
     525        exit(EINVAL);
     526    }
     527
     528    if (sig_num == SIGTERM)
     529        sig2str(sig_num, sig_name);
     530
     531    commands  = &argv[optind];
     532
     533    return kill_all(commands, argc - optind);
     534}