/*
 * sh(1) ping PMDA 
 *
 * Copyright (c) 1995-2003 Silicon Graphics, Inc.  All Rights Reserved.
 * 
 * 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.
 */

#include <ctype.h>
#include "shping.h"
#include "domain.h"

__uint32_t	cycletime = 120;	/* default cycle time, 2 minutes */
__uint32_t	timeout = 20;		/* response timeout in seconds */
static char	*username;		/* user account to run under */
cmd_t		*cmdlist;

#ifdef HAVE_SPROC

/* Signals are only used with sproc, threads will never generate SIGGHLD */
void
onchld(int s)
{
    int done;
    int waitStatus;
    int	die = 0;

    while ((done = waitpid(-1, &waitStatus, WNOHANG)) > 0) {

	if (done != sprocpid)
	    continue;
	die = 1;

	if (WIFEXITED(waitStatus)) {

	    if (WEXITSTATUS(waitStatus) == 0)
		logmessage(LOG_INFO, 
			   "Sproc (pid=%d) exited normally\n",
			   done);
	    else
		logmessage(LOG_CRIT, 
			   "Sproc (pid=%d) exited with status = %d\n",
			   done, WEXITSTATUS(waitStatus));
	}
	else if (WIFSIGNALED(waitStatus)) {
	    
	    if (WCOREDUMP(waitStatus))
		logmessage(LOG_CRIT, 
			   "Sproc (pid=%d) received signal = %d and dumped core\n",
			   done, WTERMSIG(waitStatus));
	    else
		logmessage(LOG_CRIT, 
			   "Sproc (pid=%d) received signal = %d\n",
			   done, WTERMSIG(waitStatus));
	}
	else {
	    logmessage(LOG_CRIT, 
		       "Sproc (pid=%d) died, reason unknown\n", done);
	}
    }

    if (die) {
	logmessage(LOG_INFO, "Main process exiting\n");
	exit(0);
    }
}
#endif

void
usage(void)
{
    fprintf(stderr, 
"Usage: %s [options] configfile\n\n\
Options:\n\
  -C           parse configuration file and exit\n\
  -d domain    use domain (numeric) for metrics domain of PMDA\n\
  -I interval  cycle time in seconds between subsequent executions of each\n\
               command [default 120 seconds]\n\
  -l logfile   write log into logfile rather than using the default log\n\
  -t timeout   time in seconds before aborting the wait for individual\n\
               commands to complete [default 20 seconds]\n\
  -U username  run the agent and commands as alternate user [default \"pcp\"]\n",
	pmGetProgname());
    exit(1);
}

int
main(int argc, char **argv)
{
    pmdaInterface	dispatch;
    int			n = 0;
    int			i;
    int			err = 0;
    int			sep = pmPathSeparator();
    int			line;
    int                 numcmd = 0;
    int                 parseonly = 0;
    char		*configfile;
    FILE		*conf;
    char		*endnum;
    char		*p;
    char		*tag;
    char		lbuf[256];
    char		mypath[MAXPATHLEN];

    pmSetProgname(argv[0]);
    pmGetUsername(&username);

    pmsprintf(mypath, sizeof(mypath), "%s%c" "shping" "%c" "help",
		pmGetConfig("PCP_PMDAS_DIR"), sep, sep);
    pmdaDaemon(&dispatch, PMDA_INTERFACE_2, pmGetProgname(), SHPING, 
		"shping.log", mypath);

    while ((n = pmdaGetOpt(argc, argv,"CD:d:I:l:t:U:?",
			&dispatch, &err)) != EOF) {
	switch (n) {

	    case 'C':
		parseonly = 1;
		break;

	    case 'I':
		cycletime = (int)strtol(optarg, &endnum, 10);
		if (*endnum != '\0') {
		    fprintf(stderr, 
		    	    "%s: -I requires number of seconds as argument\n",
			    pmGetProgname());
		    err++;
		}
		break;

	    case 't':
		timeout = (int)strtol(optarg, &endnum, 10);
		if (*endnum != '\0') {
		    fprintf(stderr, 
		    	    "%s: -t requires number of seconds as argument\n",
			    pmGetProgname());
		    err++;
		}
		break;

	    case 'U':
		username = optarg;
		break;

	    case '?':
		err++;
	}
    }

    if (err || optind != argc -1) {
    	usage();
    }

    configfile = argv[optind];
    if ((conf = fopen(configfile, "r")) == NULL) {
	fprintf(stderr, "%s: Unable to open config file \"%s\": %s\n",
	    pmGetProgname(), configfile, osstrerror());
	exit(1);
    }
    line = 0;

    for ( ; ; ) {
	if (fgets(lbuf, sizeof(lbuf), conf) == NULL)
	    break;

	line++;
	p = lbuf;
	while (*p && isspace((int)*p))
	    p++;
	if (*p == '\0' || *p == '\n' || *p == '#')
	    continue;
	tag = p++;
	while (*p && !isspace((int)*p))
	    p++;
	if (*p)
	    *p++ = '\0';
	while (*p && isspace((int)*p))
	    p++;
	if (*p == '\0' || *p == '\n') {
	    fprintf(stderr, "[%s:%d] missing command after tag \"%s\"\n",
		configfile, line, tag);
	    exit(1);
	}

	numcmd++;
	if (parseonly)
	    continue;
	if ((cmdlist = (cmd_t *)realloc(cmdlist, numcmd * sizeof(cmd_t))) == NULL) {
	    pmNoMem("main:cmdlist", numcmd * sizeof(cmd_t), 
		     PM_FATAL_ERR);
	}

	cmdlist[numcmd-1].tag = strdup(tag);
	cmdlist[numcmd-1].cmd = strdup(p);
	cmdlist[numcmd-1].status = STATUS_NOTYET;
	cmdlist[numcmd-1].error = 0;
	cmdlist[numcmd-1].real = cmdlist[numcmd-1].usr = cmdlist[numcmd-1].sys = -1;

	/* trim trailing newline */
	p = cmdlist[numcmd-1].cmd;
	while (*p && *p != '\n')
	    p++;
	*p = '\0';
    }

    fclose(conf);

    if (numcmd == 0) {
	fprintf(stderr, "%s: No commands in config file \"%s\"?\n",
	    pmGetProgname(), configfile);
	exit(1);
    }
    else if (parseonly)
	exit(0);

    if (pmDebugOptions.appl1) {
	fprintf(stderr, "Parsed %d commands\n", numcmd);
	fprintf(stderr, "Tag\tCommand\n");
	for (i = 0; i < numcmd; i++) {
	    fprintf(stderr, "[%s]\t%s\n", cmdlist[i].tag, cmdlist[i].cmd);
	}
    }

    /* set up indom description */
    indomtab.it_numinst = numcmd;
    if ((indomtab.it_set = (pmdaInstid *)malloc(numcmd*sizeof(pmdaInstid))) == NULL) {
	pmNoMem("main.indomtab", numcmd * sizeof(pmdaInstid), PM_FATAL_ERR);
    }
    for (i = 0; i < numcmd; i++) {
	indomtab.it_set[i].i_inst = i;
	indomtab.it_set[i].i_name = cmdlist[i].tag;
    }

#ifdef HAVE_SPROC
    signal(SIGCHLD, onchld);
#endif

    pmdaOpenLog(&dispatch);
    pmSetProcessIdentity(username);

    shping_init(&dispatch);
    pmdaConnect(&dispatch);
    pmdaMain(&dispatch);

    exit(0);
}
