/*
 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
 * Copyright (c) 1991-1998 University of Maryland at College Park
 * Copyright (c) 2007-2012 Zmanda, Inc.  All Rights Reserved.
 * Copyright (c) 2013-2016 Carbonite, Inc.  All Rights Reserved.
 * All Rights Reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of U.M. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  U.M. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Authors: the Amanda Development Team.  Its members are listed in a
 * file named AUTHORS, in the root directory of this distribution.
 */
/*
 * $Id: killpgrp.c,v 1.17 2006/07/25 18:27:56 martinea Exp $
 *
 * if it is the process group leader, it kills all processes in its
 * process group when it is killed itself.
 *
 * argv[0] is the killpgrp program name
 * argv[1] is the config name or NOCONFIG
 *
 */
#include "amanda.h"
#include "amutil.h"
#include "conffile.h"

#ifdef HAVE_GETPGRP
#ifdef GETPGRP_VOID
#define AM_GETPGRP() getpgrp()
#else
#define AM_GETPGRP() getpgrp(getpid())
#endif
#else
/* we cannot check it, so let us assume it is ok */
#define AM_GETPGRP() getpid()
#endif
 
int main(int argc, char **argv);
static void term_kill_soft(int sig);
static void term_kill_hard(int sig);

int
main(
    int		argc,
    char **	argv)
{
    int ch;
    char *exitstr;
    amwait_t status;

    glib_init();

    if (argc > 1 && argv[1] && g_str_equal(argv[1], "--version")) {
	printf("killpgrp-%s\n", VERSION);
	return (0);
    }

    /*
     * Configure program for internationalization:
     *   1) Only set the message locale for now.
     *   2) Set textdomain for all amanda related programs to "amanda"
     *      We don't want to be forced to support dozens of message catalogs.
     */  
    setlocale(LC_MESSAGES, "C");
    textdomain("amanda"); 

    safe_fd(-1, 0);
    safe_cd();

    set_pname("killpgrp");

    dbopen(DBG_SUBDIR_CLIENT);
    config_init(CONFIG_INIT_CLIENT|CONFIG_INIT_GLOBAL, NULL);

    if (argc < 2) {
	error("Need at least 2 arguments\n");
	/*NOTREACHED*/
    }
    dbprintf(_("version %s\n"), VERSION);
    dbprintf(_("config: %s\n"), argv[1]);
    if (!g_str_equal(argv[1], "NOCONFIG"))
	dbrename(argv[1], DBG_SUBDIR_CLIENT);

#ifdef WANT_SETUID_CLIENT
    check_running_as(RUNNING_AS_CLIENT_LOGIN | RUNNING_AS_UID_ONLY);
    if (!become_root()) {
	error(_("error [%s could not become root (is the setuid bit set?)]\n"), get_pname());
	/*NOTREACHED*/
    }
#else
    check_running_as(RUNNING_AS_CLIENT_LOGIN);
#endif

    if (AM_GETPGRP() != getpid()) {
	error(_("error [must be the process group leader]"));
	/*NOTREACHED*/
    }

    signal(SIGTERM, term_kill_soft);

    /* Consume any extranious input */
    do {
	ch = getchar();
	/* wait until EOF */
    } while (ch != EOF);

    term_kill_soft(0);

    for(;;) {
	if (wait(&status) != -1)
	    break;
	if (errno != EINTR) {
	    error(_("error [wait() failed: %s]"), strerror(errno));
	    /*NOTREACHED*/
	}
    }
    exitstr = str_exit_status("child", status);
    dbprintf("%s\n", exitstr);
    amfree(exitstr);

    /*@ignore@*/
    return WIFEXITED(status)?WEXITSTATUS(status):1;
    /*@end@*/
}

static void term_kill_soft(
    int sig)
{
    pid_t dumppid = getpid();
    int killerr;

    (void)sig;	/* Quiet unused parameter warning */

    signal(SIGTERM, SIG_IGN);
    signal(SIGALRM, term_kill_hard);
    alarm(3);
    /*
     * First, try to kill the dump process nicely.  If it ignores us
     * for three seconds, hit it harder.
     */
    dbprintf(_("sending SIGTERM to process group %ld\n"), (long) dumppid);
    killerr = kill(-dumppid, SIGTERM);
    if (killerr == -1) {
	dbprintf(_("kill failed: %s\n"), strerror(errno));
    }
}

static void term_kill_hard(
    int sig)
{
    pid_t dumppid = getpid();
    int killerr;

    (void)sig;	/* Quiet unused parameter warning */

    dbprintf(_("It won\'t die with SIGTERM, but SIGKILL should do.\n"));
    dbprintf(_("Don't expect any further output, this will be suicide.\n"));
    killerr = kill(-dumppid, SIGKILL);
    /* should never reach this point, but so what? */
    if (killerr == -1) {
	dbprintf(_("kill failed: %s\n"), strerror(errno));
	dbprintf(_("waiting until child terminates\n"));
    }
}
