/* These routines implement a logging facility which can send its
 * messages either to the console or across the net or both.
 * Currently it depends on the version of printf being used with the
 * VAX.  This printf should probably be used on any machine anyway.
 */

/*
 *------------------------------------------------------------------
 *
 * $Source: /u1/jis/gw/cgw/src/gw/src/RCS/log.c,v $
 * $Revision: 1.3 $
 * $Date: 88/05/30 19:57:22 $
 * $State: Exp $
 * $Author: jon $
 * $Locker: jon $
 *
 * $Log:	log.c,v $
 * Revision 1.3  88/05/30  19:57:22  jon
 * Splits up do_flag_command into two routines so egp can add a non-log flag.
 * 
 * Revision 1.2  87/07/16  01:50:11  jon
 * Adds log flag hacking with support for generic flag modification.
 * 
 * 
 *------------------------------------------------------------------
 */

#ifndef lint
static char *rcsid_log_c = "$Header: log.c,v 1.3 88/05/30 19:57:22 jon Locked $";
#endif	lint

#include <types.h>
#include <sys.h>
#include "const.h"
#include "defs.h"
#include "macs.h"
#include "net.h"
#include "ext.h"
#undef NULL
#include "../../lib.vax/stdio.h"
#include "../opcon/opcon.h"

/* Interface to command interpreter. */
void log_set();
void passwd();
void log_flags_command();

static subcommand log_scmd[] = {
    { "con", "Enable/Disable console logging", log_set, LOG_CON },
    { "net", "Enable/Disable network logging", log_set, LOG_NET },
    { "in", "Give password", passwd, 0 },
    { "out", "Clear password", passwd, 1 },
    { "flags", "Examine/modify logging flags", log_flags_command, 0 },
    { NULL, NULL, NULL, 0 },
};

command log_cmd =
    { NULL, "log", "Logging control", log_scmd };

/* Parameters */
#define LOGPKTLEN 512		/* Max data length in packet */
#define LOGMSGLEN 128		/* Maximum message length */

iorb *log_iob;			/* I/O buffer for logging messages */
byte *log_msgp;			/* Pointer to where next message goes */
unss log_len;			/* Length of messages */
char log_msg[LOGMSGLEN];	/* Storage for the message */
unsf log_missed;		/* Counts number of missed log
				 * messages because couldn't alloc iob */
ext bitf logflg;		/* Where messages should be logged */
ext dct *log_dev;		/* Log device */

int good_password = 0;		/* Set when a valid password is given */

/* max limit for now ... could realloc, but it just isn't worth it */
#define MAXFLAGS 10
static int n_log_flags;

static char *log_flag_name_table[MAXFLAGS];
static bitf *log_flag_addr_table[MAXFLAGS];

#define NFLAGNAMES 11
char *log_flag_bit_names[] = {
  "panic",
  "fatal", 
  "check",
  "uncom_err",
  "uncom_event",
  "com_err",
  "com_event",
  "packet_trace",
  "op_trace",
  "dump",
  NULL,
};
    
  
log_init()
{

     log_iob = NULL;
     log_missed = 0;
     log_len = 0;
     n_log_flags = 0;
}

dolog(fmt, args)
char *fmt;
int args;
{
    intf msg_len;
    iorb *get_log_pkt();

    if (logflg) {
	FILE fil;

	fil.f_count = LOGMSGLEN;
	fil.f_buf = log_msg;
	_doprnt(fmt, &args, &fil);
	*(fil.f_buf) = '\0';
	msg_len = LOGMSGLEN - fil.f_count;
    }

    if (logflg & LOG_CON)
      write(log_dev, log_msg, msg_len);

    if (logflg & LOG_NET) {
	if (log_len + msg_len > LOGPKTLEN) {
	    log_iob->i_breq = log_len;
	    send_log(log_iob);
	    log_iob = NULL;
	    log_len = 0;
	}
	if (log_iob == NULL) {
	    if ((log_iob = get_log_pkt()) == NULL) {
		log_missed++;
		return;
	    }
	    log_msgp = log_iob->i_addr;
	    log_len = 0;
	}
	copy(log_msg, log_msgp, msg_len);
	log_msgp += msg_len;
	log_len += msg_len;
    }
}

/* Flush any outstanding log data */
log_flush()
{
    if (log_iob != NULL) {
	if (log_len > 0) {
	    log_iob->i_breq = log_len;
	    send_log(log_iob);
	}
	else
	  freebuf(log_iob);

	log_iob = NULL;
	log_len = 0;
    }
}

log_temp(new_lf)
bitf new_lf;
{
    static bitf old_lf;

    if (new_lf) {
	old_lf = logflg;
	logflg = new_lf;
    }
    else {
	logflg = old_lf;
    }
}

log_set(devp, arg, jcl)
dct *devp;
int arg;
char *jcl;
{
    static char msg[] = "'on' or 'off'\n";

    if (jcl == NULL) {
	fprintf(devp, msg);
	return;
    }
    if (!good_password) {
	fprintf(devp, "Not allowed\n");
	return;
    }
    if (strcmp(jcl, "on") == 0)
      logflg |= arg;
    else if (strcmp(jcl, "off") == 0)
      logflg &= ~arg;
    else
      fprintf(devp, msg);
}

/* Sets and clears the password flag. */
passwd(devp, arg, jcl)
dct *devp;
int arg;
char *jcl;
{
    if (arg == 0) {
	if (jcl == NULL)
	  fprintf(devp, "Give password\n");
	else if (strcmp(crypt(jcl, "Za"), "ZaLPN5JftyekQ") == 0)
	  good_password = 1;
	else
	  fprintf(devp, "Bad password\n");
	return;
    }
    good_password = 0;
}

/* Flag diddling stuff */

log_register_flag (name, addr)
     char *name;
     bitf *addr;
{
  if (n_log_flags > MAXFLAGS-1) {
    dolog ("log_register_flags:  can't register more than MAXFLAGS.\n");
    return;
  }

  log_flag_name_table[n_log_flags] = name;
  log_flag_addr_table[n_log_flags++] = addr;
}

print_bitmask_with_names (devp, nl, bitmask, nbits, name_string_array, name_to_ignore)
     dct *devp;
     int nl, bitmask, nbits;
     char *name_string_array[], *name_to_ignore;
{
  int i,second = 0;

  if (bitmask == 0) 
    fprintf (devp, "none");
  else {
    for (i = 0; name_string_array[i] != NULL; i++) {
      if (bitmask & (1 << i)) {
	if (second++) fprintf (devp, ", ");
	if (strcmp (name_string_array[i], name_to_ignore) != 0)
	  fprintf (devp, name_string_array[i]);
      }
    }
  }
  if (nl) fprintf (devp, ".\n");
}

int find_string_index (string_array, string_to_match)
     char *string_array[];
     char *string_to_match;
{
  int i;

  for (i = 0; string_array[i] != NULL; i++) {
    if (strcmp (string_array[i], string_to_match) == 0)
      return (i);
  }
  return (-1);
}
  
do_flag_command (devp, jcl, n_flags, addr_table, flag_names,
		 n_bits, bit_names, norm_value,
		 ignore_name)
     dct *devp;
     char *jcl;
     int  n_flags;
     bitf *addr_table[];
     char *flag_names[];
     int  n_bits;
     char *bit_names[];
     bitf norm_value;
     char *ignore_name;
{
  int flag_index, second;
  char *the_bit_name, the_flag_name[32], *get_cmd(); /* in opcon.c */

  if (jcl == NULL) {
    for (flag_index = 0; flag_index < n_flags; flag_index++) {
      fprintf (devp, "%s: ", flag_names[flag_index]);
      print_bitmask_with_names (devp, 1 /* NL */, *(addr_table[flag_index]), 
				n_bits, bit_names, ignore_name);
    }
    return;
  }

  the_bit_name = get_cmd (jcl, the_flag_name);
  
  /* find the flag */
  flag_index = find_string_index (flag_names, the_flag_name);
  if (flag_index == -1) {
    fprintf (devp, "Unknown flag name %s.\n", the_flag_name);
    fprintf (devp, "Must be one of:");
    second = 0;
    while (*flag_names) {
      if (second++) fprintf (devp, ",");
      fprintf (devp, " %s", *flag_names++);
    }
    fprintf (devp, ".\n");
    return;
  }

  do_flag_command_given_flag (devp, addr_table, n_bits, bit_names,
			      norm_value, ignore_name, 
			      flag_index, the_flag_name, the_bit_name);
};

/* Given a flag name and a bit name do the work ... */

do_flag_command_given_flag (devp, addr_table, n_bits, bit_names,
			    norm_value, ignore_name, 
			    flag_index, the_flag_name, the_bit_name)
     dct *devp;
     bitf *addr_table[];
     int  n_bits;
     char *bit_names[];
     bitf norm_value;
     char *ignore_name;
     int  flag_index;
     char *the_flag_name;
     char *the_bit_name;
{
  int bit_index, second, set;

  if (the_bit_name == NULL) {
    fprintf (devp, "%s: ", the_flag_name);
    print_bitmask_with_names (devp, 1 /* NL */, *(addr_table[flag_index]), 
			 n_bits, bit_names, ignore_name);
    return;
  }

  if (!good_password) {
    fprintf(devp, "Not allowed\n");
    return;
  }

  if (strcmp (the_bit_name, "none") == 0) {
    *addr_table[flag_index] = 0;
    return;
  }

  if (strcmp (the_bit_name, "all") == 0) {
    *addr_table[flag_index] = -1;
    return;
  }

  if (strcmp (the_bit_name, "norm") == 0) {
    *addr_table[flag_index] = norm_value;
    return;
  }

  /* hack changing the bit */
  set = 1;
  switch (the_bit_name[0]) {
  case '-':
    set = 0;
    the_bit_name++;
    break;
  case '+':
    set = 1;
    the_bit_name++;
  }

  bit_index = find_string_index (bit_names, the_bit_name);
  if (bit_index == -1) {
    fprintf (devp, "Unknown bit name %s.\n", the_bit_name);
    fprintf (devp, "Must be one of: all, none, norm");
    second = 0;
    while (*bit_names) {
      fprintf (devp, ", %s", *bit_names++);
    }
    fprintf (devp, ".\n");
    return;
  }
  change_attr (set, addr_table[flag_index], 1 << bit_index);
}

log_flags_command (devp, arg, jcl)
     dct *devp;
     int arg;
     char *jcl;
{

  do_flag_command (devp, jcl, n_log_flags, log_flag_addr_table,
		   log_flag_name_table, NFLAGNAMES, log_flag_bit_names,
		   0x1f /* normal value */, NULL);
}

