/*
 * CFINGERD
 * Starting option routines
 *
 * 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 1, 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 "cfingerd.h"
#include "proto.h"

#include "version.h"

/*
 * SET_DAEMON_MODE
 *
 * Eventually, this will bind to the finger socket, and stay running as a
 * true daemon.  For the time being, though, it is only INETD compliant.
 */
void set_daemon_mode(void)
{
#ifdef DAEMON_MODE
    pid_t pid;
    int i, lsock, fd, clen;
    int oval = 1;
    struct sockaddr caddr;
    struct sockaddr_in laddr;

    if ((lsock = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
	syslog (LOG_ERR,"can't open socket: %m");
	exit (1);
    }

    if (setsockopt (lsock, SOL_SOCKET, SO_REUSEADDR, (char *)&oval, sizeof(oval)) < 0) {
	syslog (LOG_ERR,"can't setsockopt(SO_REUSEADDR): %m");
	exit (1);
    }

    laddr.sin_family = AF_INET;
    laddr.sin_addr.s_addr = listen_addr;
    laddr.sin_port = listen_port;

    if (bind (lsock, (struct sockaddr *) &laddr, sizeof(laddr)) < 0) {
	syslog (LOG_ERR,"can't bind: %m");
	exit (1);
    }

    if ((pid = fork()) < 0) {
	syslog (LOG_ERR, "can't fork(): %m");
	exit (1);
    } else if (pid != 0)
    	exit (0);

    syslog(LOG_NOTICE, "Daemon mode initiated");

    setsid();

    for (i = 0; i < 3; i++) {
	close(i);
	fd = open ("/dev/null", O_RDWR);
	if (fd != i) {
	    dup2 (fd, i);
	    close (fd);
	}
    }

    listen (lsock,20);

    clen = sizeof (caddr);


    while (1) {
	fd = accept (lsock, &caddr, &clen);
	if (fd < 0) {
#ifndef __FreeBSD__
	    if (errno == EPROTO) {
#else
	    if (errno == EPROTOTYPE) {
#endif /* !__FreeBSD__ */
		remote_addr = inet_ntoa (((struct sockaddr_in *)&caddr)->sin_addr);
		syslog(LOG_ERR,"failed connect (possible port scan) from %s: %m",
		       remote_addr);
	    }
	    continue;
	}
	remote_addr = inet_ntoa (((struct sockaddr_in *)&caddr)->sin_addr);

	/*
	 * FIXME: Support for tcp wrapper via hosts.allow and .deny is missing
	 */

	for (i = 0; (pid = fork ()) < 0; ++i) {
	    if (i == 5) {
		syslog (LOG_ERR,"terminate; can't fork client: %m");
		continue;
	    }
	    syslog (LOG_ERR,"waiting; can't fork client: %m");
	    sleep (1);
	}

	if (pid != 0) {
	    close (fd);
	    continue;
	} else {

	    /* 
	     * Now we're in cfingerd child.
	     */

	    close (lsock);
	    dup2 (fd,0);
	    close (fd);
	    dup2 (0,1);
	    dup2 (0,2);
	    
	    signal (SIGCHLD, SIG_DFL);

	    return;
	}
    }
#else
    printf("CFINGERD %s daemon mode starting.\n", VERSION);
    printf("\nSorry, daemon mode for cfingerd is not compiled in.  Uncomment\n");
    printf("DAEMON_MODE in src/config.h.\n\n");
    fflush(stdout);
    exit(0);
#endif
}

/*
 * GIVE_HELP
 *
 * This gives help about command line options under cfingerd.
 */
void give_help(void)
{
    printf("\nCFINGERD command line options:\n\n");
    printf("\t-c\t\tChecks the configuration to make sure it's okay\n");
    printf("\t-d\t\tRuns the daemon as stand-alone (non-inetd)\n");
    printf("\t-e [user]\tEmulates a local finger from the command line\n");
    printf("\t-o\t\tTurn off all incoming finger queries\n");
    printf("\t-v\t\tRetrieves daemon version information\n\n");
    printf("Any other options will give you this screen.  Commands are not\n");
    printf("case sensitive.\n\n");
    fflush(stdout);

    exit(PROGRAM_OKAY);
}

/*
 * CHECK_OPTIONS
 *
 * This is called at runtime (and is usually the first option).  This takes
 * in the argv pointer, and verifies the options passed.
 */
void check_options(int argc, char *argv[])
{
    if (!strncasecmp(argv[1], "-d", 2)) {
	set_daemon_mode();
	return;
    } else if (!strncasecmp(argv[1], "-v", 2))
	show_version_info();
    else if (!strncasecmp(argv[1], "-e", 2)) {
	if (argc > 2) {
	    emulated = TRUE;
	    syslog(LOG_NOTICE, "Emulated: \"%s\"", (char *) argv[2]);
	    printf("[127.0.0.1 : Username: %s]\n", (char *) argv[2]);
	    fflush(stdout);
	    return;
	} else
	    CF_ERROR(E_COMMANDLINE);
    } else if (!strncasecmp(argv[1], "-o", 2)) {
	CF_ERROR(E_FINGEROFF);
    } else if (!strncasecmp(argv[1], "-c", 2)) {
	read_configuration();
	printf("\nConfiguration okay.\n\n");
	fflush(stdout);
	exit(PROGRAM_OKAY);
    }

    give_help();
}

/*
 * Local variables:
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 8
 * End:
 */
