/* This file is part of the Project Athena Zephyr Notification System.
 * It is one of the source files comprising zwgc, the Zephyr WindowGram
 * client.
 *
 *      Created by:     Marc Horowitz <marc@athena.mit.edu>
 *
 *      $Source: /afs/sipb.mit.edu/project/zion/src/clients/zwgc/RCS/main.c,v $
 *      $Author: mkgray $
 *
 *      Copyright (c) 1989 by the Massachusetts Institute of Technology.
 *      For copying and distribution information, see the file
 *      "mit-copyright.h".
 */

#include <sysdep.h>

#if (!defined(lint) && !defined(SABER))
static const char rcsid_main_c[] = "$Id: main.c,v 1.1 96/08/16 02:37:20 mkgray Exp Locker: mkgray $";
#endif

#include <sys/resource.h>
#include <zephyr/mit-copyright.h>
#include <zephyr/zephyr.h>

#include <EXTERN.h>
/* #include <embed.h> */
#include <perl.h>

#include "new_memory.h"
#include "zwgc.h"
#include "parser.h"
#include "node.h"
#include "exec.h"
#include "zephyr.h"
#include "notice.h"
#include "subscriptions.h"
#include "file.h"
#include "mux.h"
#include "port.h"
#include "variables.h"
#include "main.h"

extern void notice_handler();
extern void boot_DynaLoader _((CV* cv));
static void setup_signals(), detach();
void XS_Zion_fields(CV* cv);
void XS_Zion_put(CV* cv);
extern void xs_init _((void));

/*
 * Global zwgc-wide variables:
 */

#ifdef DEBUG
int zwgc_debug = 0;
#endif

static char *zwgc_version_string = "1.0";

/*
 * description_filename_override - <<<>>>
 */

static char *description_filename_override = NULL;

/*
 * progname - <<<>>> export!
 */

char *progname = NULL;

/*
 * subscriptions_filename_override - <<<>>> export!
 */

char *subscriptions_filename_override = NULL;

/****************************************************************************/
/*                                                                          */
/*             Code to deal with reading in the description file:           */
/*                                                                          */
/****************************************************************************/

/*
 * program - this holds a pointer to the node representation of the
 *           description file once it has been read in.
 */

static struct _Node *program = NULL;
static PerlInterpreter *zwgcdescperl = NULL;

/*
 * <<<>>>
 */

static void fake_startup_packet()
{
    ZNotice_t notice;
    struct timezone tz;
    char msgbuf[BUFSIZ];

    var_set_variable("version", zwgc_version_string);

    (void) memset(&notice, 0, sizeof(notice));

    notice.z_version = "";
    notice.z_class = WG_CTL_CLASS;
    notice.z_class_inst = WG_CTL_USER; 
    notice.z_opcode = USER_STARTUP; 
    notice.z_default_format = "Zwgc mark II version $version now running...\n";
    notice.z_recipient = "";
    notice.z_sender = "ZWGC";
    gettimeofday(&notice.z_time,&tz);
    notice.z_port = 0;
    notice.z_kind = ACKED;
    notice.z_auth = ZAUTH_YES;
    sprintf(msgbuf,"Zwgc mark II version %s now running...",
	    zwgc_version_string);
    notice.z_message = msgbuf;
    notice.z_message_len = strlen(notice.z_message)+1;
    
    notice_handler(&notice);
}

static void read_in_description_file()
{
    FILE *input_file;
    char defdesc[128];
    char descfilename[200];
    char *perltmp[2];
    static int bootedzion = 0;

/*    var_clear_all_variables(); <<<>>> */

    sprintf(defdesc, "%s/%s", DATADIR, DEFDESC);
    input_file = locate_file(description_filename_override, USRDESC, defdesc, descfilename);
    if (input_file != NULL)
      {
	*perltmp = '\0';
	*(perltmp+1) = descfilename;
	/*        perl_parse(zwgcdescperl, xs_init, 2, perltmp, NULL); */
        perl_parse(zwgcdescperl, NULL, 2, perltmp, NULL);
	if(!bootedzion) {
	  boot_Zion();
	  bootedzion = 1;
	}
	else {
	  newXS("Zion::fields", XS_Zion_fields, __FILE__);
	  newXS("Zion::put", XS_Zion_put, __FILE__);
	}
/*      program = parse_file(input_file); */
   }
    else{
/*      program = NULL; */
    }
    /*    fake_startup_packet(); 
    fprintf(stderr, "Sent startup packet\n"); */
}

/****************************************************************************/
/*                                                                          */
/*            Code to deal with argument parsing & overall control:         */
/*                                                                          */
/****************************************************************************/

/*
 *    void usage()
 *        Effects: Prints out an usage message on stderr then exits the
 *                 program with error code 1.
 */

void zion_usage()
{
#ifdef DEBUG
    fprintf(stderr, "\
zwgc: usage: zwgc [-debug] [-f <filename>] [-subfile <filename>]\n\
                  [-ttymode] [-nofork] [-reenter]\n\
                  [-default <driver>] {-disable <driver>}*\n\
                  [output driver options]\n");
#else
    fprintf(stderr, "\
zwgc: usage: zwgc [-f <filename>] [-subfile <filename>]\n\
                  [-ttymode] [-nofork] [-reenter]\n\
                  [-default <driver>] {-disable <driver>}*\n\
                  [output driver options]\n");
#endif
    exit(1);
}

/*
 * <<<>>>
 */

static void run_initprogs()
{
    /*
     * This code stolen from old zwgc: yuck.  Clean up & fix.  <<<>>>
     * Should this fork instead of just systeming?
     */

    int status;
    char *progname = ZGetVariable("initprogs");
    
    if (!progname)
      return;
    
    status = system(progname);
    if (status == 127) {
	perror("zwgc initprog exec");
	fprintf(stderr,"zwgc initprog of <%s> failed: no shell.\n",
		progname);
    } else if (status!=-1 && status>>8) {
	perror("zwgc initprog exec");
	fprintf(stderr,"zwgc initprog of <%s> failed with status [%d].\n",
		progname, status>>8);
    }
}

/*
 * main -- the program entry point.  Does parsing & top level control.
 */

int main(argc, argv, env)
     int argc;
     char **argv;
     char **env;
{
    char **new;
    register char **current;
    int dofork = 1;

    progname = argv[0];
    zwgcdescperl = NULL;
    /*
     * Process "-f <filename>", "-subfile <filename>", "-nofork",
     * "-reenter" (which is ignored) and (if DEBUG) "-debug"
     * arguments, removing then from argc, argv:
     */
    for (new=current=argv+1; *current; current++) {
	if (string_Eq(*current, "-debug")) {
	    argc--;
#ifdef DEBUG
	    zwgc_debug = 1;
#endif
	} else if (string_Eq(*current, "-f")) {
	    argc -= 2; current++;
	    if (!*current)
	      zion_usage();
	    description_filename_override = *current;
	} else if (string_Eq(*current, "-subfile")) {
	    argc -= 2; current++;
	    if (!*current)
	      zion_usage();
	    subscriptions_filename_override = *current;
	} else if (string_Eq(*current, "-nofork")) {
	    argc--;
	    dofork = 0;
	} else if (string_Eq(*current, "-reenter")) {
	    argc--;			/* just throw it away */
	} else
	  *(new)++ = *current;
    }
    *new = *current;

    /*
     * Initialize various subsystems in proper order:
     */
    zwgcdescperl = perl_alloc();
    perl_construct(zwgcdescperl);

    dprintf("Test Zwgc parser.\n\n");
    read_in_description_file();

    dprintf("Initializing subsystems...\n"); /*<<<>>>*/
    mux_init();
    var_clear_all_variables(); /* <<<>>> */
    init_ports();       /* <<<>>> */
    dprintf("Initializing standard ports...\n");
    init_standard_ports(&argc, argv);
    if (argc>1)
      zion_usage();
    dprintf("Initializing zephyr...\n");
    setup_signals(dofork);
    zephyr_init(notice_handler);

    if (dofork)
	detach();
    /*
     * Run the initprogs program(s) now that we are all set to deal:
     */
    dprintf("Running initprogs program...\n");
    run_initprogs();

    fake_startup_packet();
    dprintf("Entering main loop\n");
    mux_loop();

    dprintf("Returning from main loop\n");
    finalize_zephyr();

    return(0);
}

/****************************************************************************/
/*                                                                          */
/*               :               */
/*                                                                          */
/****************************************************************************/

#define  USER_SUPPRESS     "SUPPRESS"
#define  USER_UNSUPPRESS   "UNSUPPRESS"

void notice_handler(notice)
     ZNotice_t *notice;
{
    char *control_opcode;

    dprintf("Got a message\n");

    if (control_opcode = decode_notice(notice)) {
#ifdef ZION_DEBUG
	printf("got control opcode <%s>.\n", control_opcode);
#endif
	if (!strcasecmp(control_opcode, USER_REREAD)) {
	    read_in_description_file();
	} else if (!strcasecmp(control_opcode, USER_SHUTDOWN))
	  zwgc_shutdown();
	else if (!strcasecmp(control_opcode, USER_STARTUP)) {
#ifdef DEBUG_MEMORY
	    report_memory_usage(); /* <<<>>> */
#endif
	    zwgc_startup();
	} else if (!strcasecmp(control_opcode, USER_SUPPRESS)) {
	    string class = get_field(notice->z_message,
				     notice->z_message_len, 1);
	    string instance = get_field(notice->z_message,
					notice->z_message_len, 2);
	    string recipient = get_field(notice->z_message,
					 notice->z_message_len, 3);
	    punt(class, instance, recipient);
	    free(class);
	    free(instance);
	    free(recipient);
	} else if (!strcasecmp(control_opcode, USER_UNSUPPRESS)) {
	    string class = get_field(notice->z_message,
				     notice->z_message_len, 1);
	    string instance = get_field(notice->z_message,
					notice->z_message_len, 2);
	    string recipient = get_field(notice->z_message,
					 notice->z_message_len, 3);
	    unpunt(class, instance, recipient);
	    free(class);
	    free(instance);
	    free(recipient);
	} else
	  printf("zwgc: unknown control opcode %s.\n", control_opcode);

	return;
    }

    if (!zwgc_active) {
#ifdef DEBUG
	if (zwgc_debug)
	  printf("NON-ACTIVE: PUNTED <%s>!!!!\n", notice->z_class_inst);
#endif
	return;
    }
    
    if (puntable_address_p(notice->z_class,
			   notice->z_class_inst,
			   notice->z_recipient)) {
#ifdef DEBUG
	if (zwgc_debug)
	  printf("PUNTED <%s>!!!!\n", notice->z_class_inst);
#endif
	return;
    }

    exec_process_packet(zwgcdescperl, notice);
}

/***************************************************************************/

/*
 *
 */

static void signal_exit()
{
    mux_end_loop_p = 1;
}

static void setup_signals(dofork)
     int dofork;
{
#ifdef _POSIX_VERSION
    struct sigaction sa;

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    
    if (dofork) {
	sa.sa_handler = SIG_IGN;
	sigaction(SIGINT, &sa, (struct sigaction *)0);
	sigaction(SIGTSTP, &sa, (struct sigaction *)0);
	sigaction(SIGQUIT, &sa, (struct sigaction *)0);
	sigaction(SIGTTOU, &sa, (struct sigaction *)0);
    } else {
	/* clean up on SIGINT; exiting on logout is the user's problem, now. */
	sa.sa_handler = signal_exit;
	sigaction(SIGINT, &sa, (struct sigaction *)0);
    }

    /* behavior never changes */
    sa.sa_handler = signal_exit;
    sigaction(SIGTERM, &sa, (struct sigaction *)0);
    sigaction(SIGHUP, &sa, (struct sigaction *)0);

    sa.sa_handler = SIG_IGN;
    sigaction(SIGPIPE, &sa, (struct sigaction *)0);

/* We don't want this anymore.  Let perl handle sigchilds
    sa.sa_handler = signal_child;
    sigaction(SIGCHLD, &sa, (struct sigaction *)0);
*/

#ifdef _AIX
    sa.sa_flags = SA_FULLDUMP;
    sa.sa_handler = SIG_DFL;
    sigaction(SIGSEGV, &sa, (struct sigaction *)0);
#endif

#else /* !POSIX */
    if (dofork) {
	/* Ignore keyboard signals if forking.  Bad things will happen. */
	signal(SIGINT, SIG_IGN);
	signal(SIGTSTP, SIG_IGN);
	signal(SIGTTOU, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);
    } else {
	/* clean up on SIGINT; exiting on logout is the user's problem, now. */
	signal(SIGINT, signal_exit);
    }
    
    /* behavior never changes */
    signal(SIGTERM, signal_exit);
    signal(SIGHUP, signal_exit);
/* Let perl handle these from now on...
    signal(SIGCHLD, signal_child); */
    signal(SIGPIPE, SIG_IGN);		/* so that Xlib gets an error */
#endif
}

/* detach() taken from old zwgc, with lots of stuff ripped out */

static void detach()
{
  /* detach from terminal and fork. */
  register int i;

  /* to try to get SIGHUP on user logout */
#if defined(_POSIX_VERSION) && !defined(ultrix)
  (void) setpgid(0, tcgetpgrp(1));
#else
  (void) setpgrp(0, getpgrp(getppid()));
#endif

  /* fork off and let parent exit... */
  if (i = fork()) {
      if (i < 0) {
	  perror("zwgc: cannot fork, aborting:");
	  exit(1);
      }
      exit(0);
  }
}	

/*
static void
xs_init()
{
  char *file = __FILE__;
  dXSUB_SYS;
  fprintf(stderr, "In xs_init()\n");
  newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, file);
  fprintf(stderr, "Set newXS\n");
}
*/
