Welcome to little lamb

Code » anopa » master » tree

[master] / src / utils / aa-kill.c

/*
 * anopa - Copyright (C) 2015-2017 Olivier Brunel
 *
 * aa-kill.c
 * Copyright (C) 2015-2018 Olivier Brunel <jjk@jjacky.com>
 *
 * This file is part of anopa.
 *
 * anopa is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * anopa is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * anopa. If not, see http://www.gnu.org/licenses/
 */

#define _BSD_SOURCE

#include <getopt.h>
#include <unistd.h>
#include <signal.h>
#include <skalibs/sig.h>
#include <skalibs/stralloc.h>
#include <skalibs/djbunix.h>
#include <skalibs/bytestr.h>
#include <skalibs/types.h>
#include <anopa/common.h>
#include <anopa/output.h>
#include <anopa/scan_dir.h>

static struct {
    unsigned int term : 1;
    unsigned int kill : 1;
    unsigned int hup : 1;

    unsigned int skip_at : 1;
} send = { 0, };
static char ownpid[UINT_FMT];

#if 0
static void _kill (pid_t pid, int sig)
{
    char buf[UINT_FMT];
    unsigned int u;

    u = pid;
    buf[uint_fmt (buf, u)] = 0;
    aa_bs (AA_OUT, "kill(");
    if (u == (unsigned int) -1)
        aa_bs (AA_OUT, "-1");
    else
        aa_bs (AA_OUT, buf);
    aa_bs (AA_OUT, ",");
    aa_bs (AA_OUT, sig_name (sig));
    aa_bs_flush (AA_OUT, ")\n");
}
#else
#define _kill(pid,sig)  kill (pid, sig)
#endif

static int
it_kill (direntry *d, void *data)
{
    stralloc *sa = data;
    char c;
    size_t l;
    ssize_t r;

    /* ignore files, not-number dirs, PID 1 and ourself */
    if (d->d_type != DT_DIR || *d->d_name < '1' || *d->d_name > '9'
            || str_equal (d->d_name, "1") || str_equal (d->d_name, ownpid))
        return 0;

    l = sa->len;
    sa->s[l - 1] = '/';
    if (stralloc_cats (sa, d->d_name)
            && stralloc_catb (sa, "/cmdline", sizeof ("/cmdline")))
        r = openreadnclose (sa->s, &c, 1);
    else
        r = -1;
    sa->len = l;
    sa->s[l - 1] = '\0';

    /* skip empty cmdline (kernel threads) and anything starting with '@' */
    if (r == 1 && c != '@')
    {
        unsigned int u;
        pid_t pid;

        if (!uint_scan (d->d_name, &u))
            goto done;
        pid = (pid_t) u;
        if (send.hup)
            _kill (pid, SIGHUP);
        if (send.term)
        {
            _kill (pid, SIGTERM);
            _kill (pid, SIGCONT);
        }
        if (send.kill)
            _kill (pid, SIGKILL);
    }

done:
    return 0;
}

static void
dieusage (int rc)
{
    aa_die_usage (rc, "[OPTION...]",
            " -D, --double-output           Enable double-output mode\n"
            " -O, --log-file FILE|FD        Write log to FILE|FD\n"
            " -u, --hup                     Send SIGHUP\n"
            " -t, --term                    Send SIGTERM then SIGCONT\n"
            " -k, --kill                    Send SIGKILL\n"
            " -s, --skip-at                 Skip processes whose cmdline starts with '@'\n"
            " -h, --help                    Show this help screen and exit\n"
            " -V, --version                 Show version information and exit\n"
            );
}

int
main (int argc, char * const argv[])
{
    PROG = "aa-kill";

    for (;;)
    {
        struct option longopts[] = {
            { "double-output",      no_argument,        NULL,   'D' },
            { "help",               no_argument,        NULL,   'h' },
            { "kill",               no_argument,        NULL,   'k' },
            { "log-file",           required_argument,  NULL,   'O' },
            { "skip-at",            no_argument,        NULL,   's' },
            { "term",               no_argument,        NULL,   't' },
            { "hup",                no_argument,        NULL,   'u' },
            { "version",            no_argument,        NULL,   'V' },
            { NULL, 0, 0, 0 }
        };
        int c;

        c = getopt_long (argc, argv, "DhkO:stuV", longopts, NULL);
        if (c == -1)
            break;
        switch (c)
        {
            case 'D':
                aa_set_double_output (1);
                break;

            case 'h':
                dieusage (RC_OK);

            case 'k':
                send.kill = 1;
                break;

            case 'O':
                aa_set_log_file_or_die (optarg);
                break;

            case 's':
                send.skip_at = 1;
                break;

            case 't':
                send.term = 1;
                break;

            case 'u':
                send.hup = 1;
                break;

            case 'V':
                aa_die_version ();

            default:
                dieusage (RC_FATAL_USAGE);
        }
    }
    argc -= optind;
    argv += optind;

    if (argc > 0 || (!send.hup && !send.term && !send.kill))
        dieusage (RC_FATAL_USAGE);

    if (send.skip_at)
    {
        stralloc sa = STRALLOC_ZERO;
        unsigned int u;

        u = (unsigned int) getpid ();
        ownpid[uint_fmt (ownpid, u)] = '\0';

        if (!stralloc_catb (&sa, "/proc", sizeof ("/proc")))
            aa_strerr_diefu1sys (RC_FATAL_MEMORY, "stralloc_catb");
        if (aa_scan_dir (&sa, 0, it_kill, &sa) < 0)
            aa_strerr_diefu1sys (RC_FATAL_IO, "scan /proc");
        stralloc_free (&sa);
    }
    else
    {
        if (send.hup)
        {
            sig_ignore (SIGHUP);
            _kill (-1, SIGHUP);
        }

        if (send.term)
        {
            sig_ignore (SIGTERM);
            _kill (-1, SIGTERM);
            _kill (-1, SIGCONT);
        }

        if (send.kill)
            _kill (-1, SIGKILL);
    }

    return RC_OK;
}