/* $Copyright: $
 * Copyright (c) 1998 by Steve Baker (ice@mama.indstate.edu)
 * All Rights reserved
 *
 * This software is provided as is without any express or implied
 * warranties, including, without limitation, the implied warranties
 * of merchant-ability and fitness for a particular purpose.
 */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef SOLARIS
#include <utmpx.h>
#else
#include <utmp.h>
#endif
#include <string.h>
#include <sys/file.h>
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>

static char *version = "$Version: $ writetmp v1.0 (c) 1998 by Steve Baker $";

#ifdef SOLARIS
#define _PATH_WTMP WTMPX_FILE
#else
#ifndef _PATH_WTMP
#define _PATH_WTMP  "/var/log/wtmp"
#endif
#endif

#ifdef SOLARIS
#define UT_UNKNOWN	0
#define UT_NAMESIZE	32
#define UT_LINESIZE	32
#define UT_HOSTSIZE	257
#define utmp	utmpx
#define ut_time	ut_tv.tv_sec
#endif

struct tacacs3_utmp {
  char ut_line[8];
  char ut_user[8];
  char ut_host[16];
  time_t ut_tactime;
};

struct tacacs4_utmp {
  char ut_line[8];
  char ut_user[8];
  char ut_host[16];
  time_t ut_tactime;
  char ut_comment[16];
};

struct uttypes {
  char *name;
  int val;
} uttypes[] = {
  {"unknown", UT_UNKNOWN},
  {"runlevel", RUN_LVL},
  {"boottime", BOOT_TIME},
  {"newtime", NEW_TIME},
  {"oldtime", OLD_TIME},
  {"init", INIT_PROCESS},
  {"login", LOGIN_PROCESS},
  {"user", USER_PROCESS},
  {"dead", DEAD_PROCESS},
  {NULL, 0}
};

enum { TRUE=1, FALSE=0 };

enum { SHUTDOWN=0, HALT, REBOOT, BOOTTIME, RUNLEVEL, OLDTIME, NEWTIME };

struct wtype {
  char *command;
  char *user;
  char *line;
  char *id;
  short ut_type;
} wtype[] = {
 {"shutdown", "shutdown", "~",	"~~", RUN_LVL},
 {"halt",     "shutdown", "~",	"~~", RUN_LVL},
 {"reboot",   "shutdown", "~",	"~~", RUN_LVL},
 {"boottime", "reboot",	  "~",	"~~", BOOT_TIME},
 {"runlevel", "runlevel", "~",	"~~", RUN_LVL},
 {"oldtime",  "",	  "",	"|",  OLD_TIME},
 {"newtime",  "",	  "",	"{",  NEW_TIME},
 {NULL, NULL, NULL, NULL, 0}
};

void usage(int);
int doit(int, char *, char *, char *, short, char *, pid_t);
int doit_tacacs3(int, char *, char *, char *);
int doit_tacacs4(int, char *, char *, char *, char *);

int fd;

/*
 * writetmp [-w wtmp] [-X[3|4]] [-u user] [-l line] [-h host] [-t type]
 *        [-i id] [-p pid] [-c comment] [entry-type] [--help] [--version]
 */
int main(int argc, char **argv)
{
  char *file = _PATH_WTMP, tacacs3 = FALSE, tacacs4 = FALSE;
  char *user = NULL, *line = NULL, *host = NULL, *id = NULL, *comment = NULL;
  pid_t pid = 0;
  short type = 0;
  int i, j, n, r;
  int command = -1;

  for(n=i=1;i<argc;i=n) {
    n++;
    if (argv[i][0] == '-') {
      for(j=1;argv[i][j];j++) {
	switch (argv[i][j]) {
	  case 'w':
	    file = argv[n++];
	    break;
	  case 'X':
	    if (argv[i][j+1] == '4') {
	      j++;
	      tacacs4 = TRUE;
	    } else if (argv[i][j+1] == '3') {
	      j++;
	      tacacs3 = TRUE;
	    } else tacacs3 = TRUE;
	    break;
	  case '-':
	    if (j == 1) {
	      if (!strcmp("--help",argv[i])) usage(4);
	      if (!strcmp("--version",argv[i])) usage(5);
	    }
	  case 'u':	/* user    */
	    user = argv[n++];
	    break;
	  case 'l':	/* line    */
	    line = argv[n++];
	    break;
	  case 'h':	/* host    */
	    host = argv[n++];
	    break;
	  case 't':	/* type	   */
	    for(r=0;uttypes[r].name;r++)
	      if (!strcmp(uttypes[r].name,argv[n])) {
		type = uttypes[r].val;
		break;
	      }
	    if (uttypes[r].name == NULL) type = atoi(argv[n]);
	    n++;
	    break;
	  case 'i':	/* id      */
	    id = argv[n++];
	    break;
	  case 'p':	/* pid     */
	    pid = atoi(argv[n++]);
	    break;
	  case 'c':	/* comment */
	    comment = argv[n++];
	    break;
	  default:
	    usage(1);
	}
      }
    } else {
      for(j=0;wtype[j].command;j++)
	if (!strcmp(argv[i],wtype[j].command)) {
	  command = j;
	  break;
	}
    }
  }

  if (!strcmp(file,"-")) fd=1;
  else if ((fd = open(file,O_WRONLY | O_APPEND | O_CREAT,0777)) < 0) usage(2);

  if (tacacs4) r=doit_tacacs4(command,line,user,host,comment);
  else if (tacacs3) r=doit_tacacs3(command,line,user,host);
  else r=doit(command,line,user,host,type,id,pid);

  close(fd);
  return r;
}

int doit(int command, char *line, char *user, char *host, short type, char *id, pid_t pid)
{
  struct utmp u;

  memset(&u,0,sizeof(u));

  if (command >= 0) {
    strncpy(u.ut_line,wtype[command].line,UT_LINESIZE);
    strncpy(u.ut_user,wtype[command].user,UT_NAMESIZE);
    strncpy(u.ut_id,wtype[command].id,2);
    u.ut_type = wtype[command].ut_type;
  } else {
    if (line) strncpy(u.ut_line, line, UT_LINESIZE);
    if (host) strncpy(u.ut_host, host, UT_HOSTSIZE);
    if (user) strncpy(u.ut_user, user, UT_NAMESIZE);
    if (id) strncpy(u.ut_id, id, 2);
    u.ut_type = type;
    u.ut_pid = pid;
  }
  u.ut_time = time(0);

  return write(fd,&u,sizeof(u)) < 0;
}

int doit_tacacs3(int command, char *line, char *user, char *host)
{
  struct tacacs3_utmp u;

  memset(&u,0,sizeof(u));

  if (command >= 0) {
    strncpy(u.ut_line,wtype[command].line,UT_LINESIZE);
    strncpy(u.ut_user,wtype[command].user,UT_NAMESIZE);
  } else {
    if (line) strncpy(u.ut_line, line, 8);
    if (user) strncpy(u.ut_user, user, 8);
    if (host) strncpy(u.ut_host, host, 16);
  }
  u.ut_tactime = time(0);

  return write(fd,&u,sizeof(u)) < 0;
}

int doit_tacacs4(int command, char *line, char *user, char *host, char *comment)
{
  struct tacacs4_utmp u;

  memset(&u,0,sizeof(u));

  if (command) {
    strncpy(u.ut_line,wtype[command].line,UT_LINESIZE);
    strncpy(u.ut_user,wtype[command].user,UT_NAMESIZE);
  } else {
    if (line) strncpy(u.ut_line, line, 8);
    if (user) strncpy(u.ut_user, user, 8);
    if (host) strncpy(u.ut_host, host, 16);
    if (comment) strncpy(u.ut_comment, comment, 16);
  }
  u.ut_tactime = time(0);

  return write(fd,&u,sizeof(u)) < 0;
}


void usage(n)
int n;
{
  switch (n) {
    case 1:
      fprintf(stderr,"usage: writetmp [-w wtmp|-] [-X[3|4]] [-u user] [-l line] [-h host] [-i id]\n\t[-t type] [-p pid] [-c comment] [--help] [--version] [entry-type]\n");
      break;
    case 2:
      fprintf(stderr,"writetmp: Error opening wtmp file for writing.\n");
      break;
    case 4:
      fprintf(stderr,"usage: writetmp [-w wtmp|-] [-X[3|4]] [-u user] [-l line] [-h host] [-i id]\n\t[-t type] [-p pid] [-c comment] [--help] [--version] [entry-type]\n");
      fprintf(stderr,"    -w wtmp|-   Write to alternate wtmp file.\n");
      fprintf(stderr,"    -X[3]       Write in tacacs 3.x wtmp format.\n");
      fprintf(stderr,"    -X4         Write in tacacs 4.0 wtmp format.\n");
      fprintf(stderr,"    -u user     Specify username.\n");
      fprintf(stderr,"    -l line     Specify the line.\n");
      fprintf(stderr,"    -h host     Specify the hostname.\n");
      fprintf(stderr,"    -i id       Specify the init id.\n");
      fprintf(stderr,"    -t type     Specify the type of entry.\n");
      fprintf(stderr,"    -p pid      Specify the pid number.\n");
      fprintf(stderr,"    -c comment  Specify the comment.\n");
      fprintf(stderr,"    --help      Print this help message.\n");
      fprintf(stderr,"    --version   Print the version of rawtmp.\n");
      fprintf(stderr,"  Entry-types:\n");
      fprintf(stderr,"    shutdown    Indicates the system is shutting down.\n");
      fprintf(stderr,"    reboot      Alias for shutdown.\n");
      fprintf(stderr,"    halt        Alias for shutdown.\n");
      fprintf(stderr,"    boottime    Indicates the system is booting.\n");
      fprintf(stderr,"    oldtime     Indicates the time is about to change.\n");
      fprintf(stderr,"    newtime     Indicates the time has changed.\n");
      fprintf(stderr,"    runlevel    Indicates the runlevel has changed.\n");
      break;
    case 5:
      {
        char *v = version+12;
	printf("%.*s\n",(int)strlen(v)-1,v);
	exit(0);
      }
  }
  exit(1);
}
