/*
    Copyright (C) 2001-2005  Ben Kibbey <bjk@arbornet.org>

    This program 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 2 of the License, or
    (at your option) any later version.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*
 *
 * Try and kill off a users login process(s). This needs the userinfo utility
 * from http://arbornet.org/~bjk/userinfo/ with the login.so module loaded
 * and chained (-x) before this module.
 *
 * Compile with:
 *     gcc -O2 -g -Wall -shared -fPIC -o kill.so kill.c
 *
 * Run with:
 *     ui -x login.so -p -- -O ./kill.so --
 *
 * Or with any other options you'd want. Just make sure the first option to
 * the login module is -p.
 *
 * Ben Kibbey <bjk@arbornet.org>
 */
#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
#include <sys/types.h>
#include <signal.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#include <err.h>
#include <limits.h>

#define KILL_OPTION_STRING	"s:"

void add_string(char ***, char *);

static int *do_sigs;
static int sig_index;

struct signals {
    int sig;
    char *name;
};

static struct signals sigs[] = {
    {1, "SIGHUP"},
    {2, "SIGINT"},
    {3, "SIGQUIT"},
    {4, "SIGILL"},
    {6, "SIGABRT"},
    {8, "SIGFPE"},
    {9, "SIGKILL"},
    {11, "SIGSEGV"},
    {13, "SIGPIPE"},
    {14, "SIGALRM"},
    {15, "SIGTERM"},
#ifdef __i386__
    /* or PPC */
    {10, "SIGUSR1"},
    {12, "SIGUSR2"},
    {17, "SIGCHLD"},
    {18, "SIGCONT"},
    {19, "SIGSTOP"},
    {20, "SIGTSTP"},
    {21, "SIGTTIN"},
    {22, "SIGTTOU"},
#else
    /* Alpha or SPARC. */
    {30, "SIGUSR1"},
    {31, "SIGUSR2"},
    {20, "SIGCHLD"},
    {19, "SIGCONT"},
    {17, "SIGSTOP"},
    {18, "SIGTSTP"},
    {21, "SIGTTIN"},
    {22, "SIGTTOU"},
#endif
    {-1, NULL}
};

void ui_module_init(int *chainable)
{
    do_sigs = realloc(do_sigs, (sig_index + 2) * sizeof(int *));
    do_sigs[sig_index++] = 1;
    do_sigs = realloc(do_sigs, (sig_index + 2) * sizeof(int *));
    do_sigs[sig_index++] = 9;
    *chainable = 1;
    return;
}

void ui_module_exit()
{
    if (do_sigs)
	free(do_sigs);

    return;
}

void ui_module_help()
{
    printf("  Kill a login process; requires login.so -p (-s SIGHUP,9):\n");
    printf("\t-s  Send these comma separated signals (integers or strings).\n\n");
    return;
}

char *ui_module_options_init(char **defaults)
{
    *defaults = NULL;
    return KILL_OPTION_STRING;
}

int ui_module_options(int argc, char **argv)
{
    int opt;

    while ((opt = getopt(argc, argv, KILL_OPTION_STRING)) != -1) {
	char *tmp;

	switch (opt) {
	    case 's':
		sig_index = 0;

again:
		while ((tmp = strsep(&optarg, ",")) != NULL) {
		    int i;
		    int sig;

		    if (isdigit(*tmp)) {
			sig = atoi(tmp);

			for (i = 0; sigs[i].sig != -1; i++) {
			    if (sig == sigs[i].sig) {
				do_sigs = realloc(do_sigs, (sig_index + 2) * sizeof(int *));
				do_sigs[sig_index++] = sig;
				goto again;
			    }
			}
		    }

		    for (i = 0; sigs[i].name; i++) {
			if (strcasecmp(tmp, sigs[i].name) == 0) {
			    do_sigs = realloc(do_sigs, (sig_index + 2) * sizeof(int *));
			    do_sigs[sig_index++] = sigs[i].sig;
			    goto again;
			}
		    }

		    warnx("kill.so: invalid signal -- %s", tmp);
		    return 1;
		}
		break;
	    case '?':
		warnx("kill.so: invalid option -- %c", optopt);
	    default:
		return 1;
	}
    }

    return 0;
}

static int killit(pid_t pid, int sig)
{
    if (kill(pid, 0) == -1)
	return 1;

    if (kill(pid, sig) == -1)
	return 1;

    return 0;
}

int ui_module_exec(char ***result, const struct passwd *pw, const int
	multi, const int verbose, const char *tf)
{
    pid_t pid;
    char **strings = NULL;
    int i;
    char line[LINE_MAX] = {'\0'};
    char *tmp;
    char m[2] = {multi, '\0'};

    strings = *result;

    if (*result == NULL)
	add_string(&strings, "!");
    else {
	/* There may be more than one pid for multiple logins. Separated the
	 * output with the multi-string deliminator and send the signals to each
	 * pid. */
	while ((tmp = strsep(&*result[0], m)) != NULL) {
	    char *s = "!";

	    if (isdigit(*tmp) == 0) {
		strncat(line, "!", sizeof(line));
		strncat(line, m, sizeof(line));
	    }
	    else {
		pid = atol(tmp);

		for (i = 0; i < sig_index; i++) {
		    if (killit(pid, do_sigs[i]) == 0) {
			s = "1";
			break;
		    }

		    s = "0";
		}

		strncat(line, s, sizeof(line));
		strncat(line, m, sizeof(line));
	    }
	}
    }

    line[strlen(line) - 1] = '\0';
    add_string(&strings, line);
    *result = strings;
    return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1