home *** CD-ROM | disk | FTP | other *** search
- /*
- * $RCSfile: utmp.c,v $$Revision: 1.2 $$Date: 1991/12/21 22:02:57 $
- *
- * utmp - translate between binary and textual utmp files.
- *
- * This is useful for fixing a corrupted /etc/utmp. Use utmp to obtain
- * a printable representation of the file. Edit it, and then use
- * utmp again to translate it back into binary form.
- *
- * usage: utmp [-v] [infile]
- *
- * utmp reads from the given file, or stdin if none is given (/etc/utmp
- * if stdin is a tty), and writes to stdout. It detects which
- * transformation it is to apply automatically.
- *
- * The flag -v indicates that times are to be printed in a
- * human-comprehensible form. Note that this output format is not
- * currently inverted, so it is not useful for making repairs.
- */
- /**
- * If utmp is working correctly then
- *
- * $ utmp
- * and
- * $ utmp | utmp | utmp
- *
- * should produce the same output. Ideally
- *
- * $ utmp | utmp | cmp - /etc/utmp
- *
- * should produce no output, but some systems don't fill the entire
- * unused part of the character fields with NULs.
- */
- /**
- * The device used to detect what sort of struct utmp we have is
- * to see whether USER_PROCESS (a value for ut_type) is defined by
- * the include files. If it is, then struct utmp probably looks
- * like this:
- *
- * struct utmp
- * {
- * char ut_user[8]; User login name
- * char ut_id[4]; /etc/inittab id
- * char ut_line[12]; device name (console, lnxx)
- * short ut_pid; process id
- * short ut_type; type of entry
- * struct exit_status
- * {
- * short e_termination; Process termination status
- * short e_exit; Process exit status
- * } ut_exit; The exit status of a process
- * marked as DEAD_PROCESS.
- * time_t ut_time; time entry was made
- * };
- *
- * AIX 3 systems have utmp entries like this:
- *
- * struct utmp
- * {
- * char ut_user[8]; User login name
- * char ut_id[14]; /etc/inittab id
- * char ut_line[12]; device name (console, lnxx)
- * short ut_type; type of entry
- * pid_t ut_pid; process id
- * struct exit_status
- * {
- * short e_termination; Process termination status
- * short e_exit; Process exit status
- * } ut_exit; The exit status of a process
- * marked as DEAD_PROCESS.
- * time_t ut_time; time entry was made
- * char ut_host[16]; host name
- * };
- *
- * To use abbreviations of the symbolic ut_type names, compile with
- * SHORT_TYPES defined.
- *
- * If USER_PROCESS is not defined, I assume an archaic struct utmp
- * like this:
- *
- * struct utmp
- * {
- * char ut_line[8]; tty name
- * char ut_name[8]; user id
- * char ut_host[16]; host name, if remote
- * long ut_time; time on
- * };
- *
- * $Log: utmp.c,v $
- * Revision 1.2 1991/12/21 22:02:57 dws
- * Improved the comments.
- *
- * Revision 1.1 1991/12/21 21:44:04 dws
- * Initial revision
- *
- */
-
- #include <sys/types.h>
- #include <assert.h>
- #include <ctype.h>
- #include <time.h>
- #include <stdio.h>
- #include <string.h>
- #include <utmp.h>
-
- extern void exit();
-
- /*
- * Configuration Section
- *
- * Configuration is done on a field-by-field basis, for the greatest
- * flexibility (who knows what those vendors will think up next?).
- *
- * If the symbolic ut_type names are available, assume a SysV-like set
- * of fields. Otherwise, assume BSD fields.
- *
- * On some systems, utmp has no ut_id field, and ut_id is #defined to
- * ut_line.
- *
- * AIX3 systems have the ut_host field.
- */
-
- #ifdef USER_PROCESS
- # define HAVE_ut_user
- # ifndef ut_id
- # define HAVE_ut_id
- # endif
- # define HAVE_ut_line
- # define HAVE_ut_pid
- # define HAVE_ut_type
- # define HAVE_ut_exit
- # define HAVE_ut_time
- # ifdef _AIX
- # define HAVE_ut_host
- # endif
- #else
- # define HAVE_ut_line
- # define HAVE_ut_name
- # define HAVE_ut_host
- # define HAVE_ut_time
- #endif
-
- #ifndef UTMP_FILE
- #define UTMP_FILE "/etc/utmp"
- #endif
-
- /*
- * getopt() Section
- *
- * The usage() function uses write() instead of printf() so that
- * programs using this template will not necessarily have to use stdio.
- */
-
- extern int write();
-
- /*
- * swrite() - write() a "string variable" (char *)
- */
-
- #define swrite(fd, s) (void) write((fd), (s), strlen(s))
-
- /*
- * lwrite() - write() a "string literal" (char [])
- */
-
- #define lwrite(fd, lit) (void) write((fd), (lit), sizeof(lit)-1)
-
- /*
- * basename(s) - assumes the string does not end in '/'
- *
- * This used to be
- *
- * #define basename(s) (strrchr((s),'/') ? strrchr((s),'/')+1 : (s))
- *
- * but some systems don't have strrchr() and I didn't want to mess
- * with switching between strrchr() and rindex().
- */
-
- static char *
- basename(s)
- char *s;
- {
- char *p;
-
- if (!s || !*s)
- return s;
-
- for(p = s; *s; s++)
- if(*s == '/')
- p = s+1;
- return p;
- }
-
- static char cmdopt[] = "v";
- static char cmdusg[] = " [-v] [filename]\n";
-
- static int flag_v = 0;
-
- static void
- usage(cmd)
- char *cmd;
- {
- static char prefix[] = "usage: ";
- char *p = cmd ? basename(cmd) : "a.out";
- int fd = 2;
-
- lwrite(fd, prefix);
- swrite(fd, p);
- lwrite(fd, cmdusg);
- }
-
- /*
- * cmdline() - return the index of the first non-option argument
- */
- static int
- cmdline(argc, argv)
- int argc;
- char **argv;
- {
- extern void exit();
- extern char *optarg; /* points to the option argument */
- extern int opterr; /* when 0, getopt is silent on errors */
- extern int optind; /* argv[optind] is the next argument */
- extern int optopt; /* current option letter */
-
- int errflag = 0; /* usage error flag */
- int errcode = 1; /* usage error exit code */
- int c;
-
- while((c = getopt(argc, argv, cmdopt)) != -1)
- switch(c)
- {
- case 'v':
- flag_v = 1;
- break;
- case '?':
- errflag = 1;
- if(optopt == '?')
- errcode = 0;
- break;
- }
-
- if(errflag)
- {
- usage(argv[0]);
- exit(errcode);
- }
-
- return optind;
- }
-
- extern struct utmp *ud; /* dummy declaration so I can use sizeof() */
-
- /*
- * LINSIZ is the number of bytes per line of the text representation
- * of a utmp file (including the newline). The size of such a text
- * file had better be a multiple of LINSIZ.
- */
- static int LINSIZ = 1 /* 1 for the newline */
-
- #ifdef HAVE_ut_user
- # define nut_user (sizeof(ud->ut_user)/sizeof(ud->ut_user[0]))
- + 1+nut_user
- #endif
-
- #ifdef HAVE_ut_id
- # define nut_id (sizeof(ud->ut_id )/sizeof(ud->ut_id [0]))
- + 1+nut_id
- #endif
-
- #ifdef HAVE_ut_line
- # define nut_line (sizeof(ud->ut_line)/sizeof(ud->ut_line[0]))
- + 1+nut_line
- #endif
-
- #ifdef HAVE_ut_pid
- # define nut_pid 5
- + 1+nut_pid
- #endif
-
- #ifdef HAVE_ut_type
- # if defined(SHORT_TYPES)
- # define nut_type 4
- # else
- # define nut_type 10
- # endif
- + 1+nut_type
- #endif
-
- #ifdef HAVE_ut_exit
- # define nut_exit 17
- + 1+nut_exit
- #endif
-
- #ifdef HAVE_ut_name
- # define nut_name (sizeof(ud->ut_name)/sizeof(ud->ut_name[0]))
- + 1+nut_name
- #endif
-
- #ifdef HAVE_ut_host
- # define nut_host (sizeof(ud->ut_host)/sizeof(ud->ut_host[0]))
- + 1+nut_host
- #endif
-
- #ifdef HAVE_ut_time
- # define nut_time 10
- + nut_time
- #endif
- ; /* End of LINSIZ initialization */
-
- /*
- * VIS_SPC - visible printing character used to represent a space
- *
- * VIS_TAB - visible printing character used to represent a tab
- *
- * VIS_NUL - visible printing character used to represent a NUL
- *
- * The way the i/o translation works, the representation of each field
- * must have no white space. These visible characters are used to
- * represent the invisible characters. (Note the inverse mapping
- * will be wrong if the utmp file actually contains any of these
- * characters.)
- *
- * These characters are not just an artifact of using scanf() to read in
- * each field. I used visible characters so that the extents and
- * boundaries of the array fields would be clear.
- *
- * ENVIS() and DEVIS() perform the translations on the given character
- * array.
- */
-
- #define VIS_NUL '_'
- #define VIS_TAB '^'
- #define VIS_SPC '~'
- #define CHR_NUL 0
- #define CHR_TAB 9
- #define CHR_SPC 32
-
- #define ENVIS(str) \
- { \
- int i; \
- \
- for(i=0; i<sizeof(str)-1; i++) \
- switch((str)[i]) \
- { \
- case CHR_NUL: (str)[i]=VIS_NUL; break; \
- case CHR_TAB: (str)[i]=VIS_TAB; break; \
- case CHR_SPC: (str)[i]=VIS_SPC; break; \
- } \
- }
-
- #define DEVIS(str) \
- { \
- int i; \
- \
- for(i=0; i<sizeof(str)-1; i++) \
- switch((str)[i]) \
- { \
- case VIS_NUL: (str)[i]=CHR_NUL; break; \
- case VIS_TAB: (str)[i]=CHR_TAB; break; \
- case VIS_SPC: (str)[i]=CHR_SPC; break; \
- } \
- }
-
- /*
- * These macros convert the given character args to an integer. (I
- * assume ints are at least 4 bytes long...)
- *
- * They serve as hash functions, with the useful property that they can
- * be used in case labels when given constants as arguments.
- */
-
- #define char1int(a) ((unsigned)(a))
- #define char2int(a,b) ((char1int(a) <<8) + (char1int(b)))
- #define char3int(a,b,c) ((char2int(a,b) <<8) + (char1int(c)))
- #define char4int(a,b,c,d) ((char3int(a,b,c)<<8) + (char1int(d)))
-
- #ifdef HAVE_ut_time
- /*
- * cvt_pb_pm() - convert Abbreviated Month name (cftime(3C) field
- * descriptor %b) to Month Number (01 - 12) (cftime(3C) field
- * descriptor %m).
- */
-
- char *
- cvt_pb_pm(pb)
- char *pb;
- {
- char *pm = "??";
-
- switch(char3int(pb[0],pb[1],pb[2]))
- {
- case char3int('J','a','n'): pm = "01"; break;
- case char3int('F','e','b'): pm = "02"; break;
- case char3int('M','a','r'): pm = "03"; break;
- case char3int('A','p','r'): pm = "04"; break;
- case char3int('M','a','y'): pm = "05"; break;
- case char3int('J','u','n'): pm = "06"; break;
- case char3int('J','u','l'): pm = "07"; break;
- case char3int('A','u','g'): pm = "08"; break;
- case char3int('S','e','p'): pm = "09"; break;
- case char3int('O','c','t'): pm = "10"; break;
- case char3int('N','o','v'): pm = "11"; break;
- case char3int('D','e','c'): pm = "12"; break;
- }
- return (pm);
- }
-
- /*
- * utgets_time() - set the ut_time field from the given file
- */
- utgets_time(f, ut)
- FILE *f;
- struct utmp *ut; /* utmp structure to print */
- {
- (void)fscanf(f,"%ld", &ut->ut_time);
- }
-
- /*
- * utputs_time() - print the ut_time field to the given file
- *
- * Note that if flag_v is set the output will not be invertible by
- * utgets().
- */
- utputs_time(f, ut)
- FILE *f;
- struct utmp *ut; /* utmp structure to print */
- {
- char time[BUFSIZ];
- int ntime;
- char *ptime;
-
- if(!flag_v)
- {
- (void)fprintf(f,"%*ld", nut_time, ut->ut_time);
- return;
- }
-
- /* make sure time[] starts out null */
- for(ptime = time; ptime < time + sizeof time; ptime++)
- *ptime = '\0';
- #if 0
- (void)memset(time,0,sizeof(time)/sizeof(time[0]));
- #endif
-
- /*
- * Format the time field in a compact, sortable form
- *
- * ctime returns a pointer to a 26-character string in the
- * following form:
- *
- * Sun Sep 16 01:03:52 1973\n\0
- * 012345678901234567890123 4 5
- *
- * I want the time displayed as
- *
- * yy/mm/dd hh:mm:ss
- * 01234567890123456
- *
- * Since cftime is not available everywhere, I will construct
- * the time myself using the result of ctime().
- */
- #if 0
- /* This is how simple it is with cftime() */
- ntime = cftime(time, "%y/%m/%d %T", &(ut->ut_time));
- #endif
-
- ntime = 0;
- ptime = ctime(&(ut->ut_time));
-
- strncat(time, ptime + 22, 2); ntime += 2; /* yy */
- time[ntime++] = '/';
- strncat(time, cvt_pb_pm(ptime + 4), 2); ntime += 2; /* mm */
- time[ntime++] = '/';
-
- strncat(time, ptime + 8, 2); ntime += 2; /* dd */
- if(time[ntime-2] == ' ') /* add leading '0' */
- time[ntime-2] = '0';
-
- time[ntime++] = ' ';
- strncat(time, ptime + 11, 8); ntime += 8; /* hh:mm:ss */
-
- time[ntime] = '\0'; /* just to be paranoid */
-
- assert(ntime < (sizeof(time)/sizeof(time[0])) - 1);
-
- (void)fprintf(f, "%*s", ntime, time);
- }
- #endif
-
- /*
- * utgets() - translate a text line from the given file into a struct
- * utmp
- */
- struct utmp *
- utgets(f)
- FILE *f;
- {
- static struct utmp utb;
- static struct utmp *ut = &utb;
-
- if (!f || feof(f))
- return (struct utmp *) 0;
-
- /*
- * Read in the fields
- */
- #ifdef HAVE_ut_user
- {
- char user[nut_user + 1];
-
- (void) fscanf(f, "%s", user);
- user[nut_user] = '\0';
- DEVIS(user);
- (void) strncpy(ut->ut_user, user, nut_user);
- }
- #endif
- #ifdef HAVE_ut_id
- {
- char id[nut_id + 1];
-
- (void) fscanf(f, "%s", id);
- id[nut_id] = '\0';
- DEVIS(id);
- (void) strncpy(ut->ut_id, id, nut_id);
- }
- #endif
- #ifdef HAVE_ut_line
- {
- char line[nut_line + 1];
-
- (void) fscanf(f, "%s", line);
- line[nut_line] = '\0';
- DEVIS(line);
- (void) strncpy(ut->ut_line, line, nut_line);
- }
- #endif
- #ifdef HAVE_ut_pid
- {
- long pid;
- /* (void)fscanf(f,"%hd", &ut->ut_pid); */
-
- (void)fscanf(f,"%ld", &pid);
- ut->ut_pid = pid;
- }
- #endif
- #ifdef HAVE_ut_type
- {
- char type[20];
- short btype = 999;
-
- (void)fscanf(f,"%s", type);
- /* decode the type */
- # if defined(SHORT_TYPES)
- switch(char4int(type[0],type[1],type[2],type[3]))
- {
- case char4int('N','U','L','L'): btype = EMPTY; break;
- case char4int('R','L','V','L'): btype = RUN_LVL; break;
- case char4int('B','O','O','T'): btype = BOOT_TIME; break;
- case char4int('O','L','D','T'): btype = OLD_TIME; break;
- case char4int('N','E','W','T'): btype = NEW_TIME; break;
- case char4int('I','N','I','P'): btype = INIT_PROCESS; break;
- case char4int('L','O','G','P'): btype = LOGIN_PROCESS; break;
- case char4int('U','S','R','P'): btype = USER_PROCESS; break;
- case char4int('D','E','D','P'): btype = DEAD_PROCESS; break;
- case char4int('A','C','C','T'): btype = ACCOUNTING; break;
- default:
- fprintf(stderr, "bad type: %s\n", type);
- }
- # else
- switch(char4int(type[0],type[1],type[2],type[3]))
- {
- case char4int('E','M','P','T'): btype = EMPTY; break;
- case char4int('R','U','N','_'): btype = RUN_LVL; break;
- case char4int('B','O','O','T'): btype = BOOT_TIME; break;
- case char4int('O','L','D','_'): btype = OLD_TIME; break;
- case char4int('N','E','W','_'): btype = NEW_TIME; break;
- case char4int('I','N','I','T'): btype = INIT_PROCESS; break;
- case char4int('L','O','G','I'): btype = LOGIN_PROCESS; break;
- case char4int('U','S','E','R'): btype = USER_PROCESS; break;
- case char4int('D','E','A','D'): btype = DEAD_PROCESS; break;
- case char4int('A','C','C','O'): btype = ACCOUNTING; break;
- default:
- fprintf(stderr, "bad type: %s\n", type);
- }
- # endif
- ut->ut_type = btype;
- }
- #endif
- #ifdef HAVE_ut_exit
- (void)fscanf(f," term=%hd", &ut->ut_exit.e_termination);
- (void)fscanf(f," exit=%hd", &ut->ut_exit.e_exit);
- #endif
- #ifdef HAVE_ut_name
- {
- char name[nut_name + 1];
-
- (void) fscanf(f, "%s", name);
- name[nut_name] = '\0';
- DEVIS(name);
- (void) strncpy(ut->ut_name, name, nut_name);
- }
- #endif
- #ifdef HAVE_ut_host
- {
- char host[nut_host + 1];
-
- (void) fscanf(f, "%s", host);
- host[nut_host] = '\0';
- DEVIS(host);
- (void) strncpy(ut->ut_host, host, nut_host);
- }
- #endif
- #ifdef HAVE_ut_time
- utgets_time(f, ut);
- #endif
-
- (void) fscanf(f, "\n");
-
- return ut;
- }
-
- /*
- * utputs() - translate from a struct utmp to a text line in the given
- * file
- */
- utputs(f, ut)
- FILE *f;
- struct utmp *ut; /* utmp structure to print */
- {
- if (!ut || !f)
- return 0;
-
- /*
- * Print out the fields
- */
-
- #ifdef HAVE_ut_user
- {
- char user[nut_user + 1];
-
- (void) strncpy(user, ut->ut_user, nut_user);
- user[nut_user] = '\0';
- ENVIS(user);
- (void) fprintf(f, "%*s ", nut_user, user);
- }
- #endif
- #ifdef HAVE_ut_id
- {
- char id[nut_id + 1];
-
- (void) strncpy(id, ut->ut_id, nut_id);
- id[nut_id] = '\0';
- ENVIS(id);
- (void) fprintf(f, "%*s ", nut_id, id);
- }
- #endif
- #ifdef HAVE_ut_line
- {
- char line[nut_line + 1];
-
- (void) strncpy(line, ut->ut_line, nut_line);
- line[nut_line] = '\0';
- ENVIS(line);
- (void) fprintf(f, "%*s ", nut_line, line);
- }
- #endif
- #ifdef HAVE_ut_pid
- (void)fprintf(f,"%*d ", nut_pid, ut->ut_pid);
- #endif
- #ifdef HAVE_ut_type
- {
- char *type;
-
- # if defined(SHORT_TYPES)
- switch(ut->ut_type)
- {
- case EMPTY: type = "NULL"; break;
- case RUN_LVL: type = "RLVL"; break;
- case BOOT_TIME: type = "BOOT"; break;
- case OLD_TIME: type = "OLDT"; break;
- case NEW_TIME: type = "NEWT"; break;
- case INIT_PROCESS: type = "INIP"; break;
- case LOGIN_PROCESS: type = "LOGP"; break;
- case USER_PROCESS: type = "USRP"; break;
- case DEAD_PROCESS: type = "DEDP"; break;
- case ACCOUNTING: type = "ACCT"; break;
- default: type = "unk!"; break;
- }
- (void)fprintf(f,"%*s ", nut_type, type); /* 5 bytes */
- # else
- switch(ut->ut_type)
- {
- case EMPTY: type = "EMPTY"; break;
- case RUN_LVL: type = "RUN_LVL"; break;
- case BOOT_TIME: type = "BOOT_TIME"; break;
- case OLD_TIME: type = "OLD_TIME"; break;
- case NEW_TIME: type = "NEW_TIME"; break;
- case INIT_PROCESS: type = "INIT_PROC"; break;
- case LOGIN_PROCESS: type = "LOGIN_PROC"; break;
- case USER_PROCESS: type = "USER_PROC"; break;
- case DEAD_PROCESS: type = "DEAD_PROC"; break;
- case ACCOUNTING: type = "ACCOUNTING"; break;
- default: type = "unknown!"; break;
- }
- (void)fprintf(f,"%-*s ", nut_type, type); /* 11 bytes */
- # endif
- }
- #endif
- #ifdef HAVE_ut_exit
- (void)fprintf(f,"term=%*d exit=%*d ",
- 3,
- ut->ut_exit.e_termination,
- 3,
- ut->ut_exit.e_exit
- );
- #endif
- #ifdef HAVE_ut_name
- {
- char name[nut_name + 1];
-
- (void) strncpy(name, ut->ut_name, nut_name);
- name[nut_name] = '\0';
- ENVIS(name);
- (void) fprintf(f, "%*s ", nut_name, name);
- }
- #endif
- #ifdef HAVE_ut_host
- {
- char host[nut_host + 1];
-
- (void) strncpy(host, ut->ut_host, nut_host);
- host[nut_host] = '\0';
- ENVIS(host);
- (void) fprintf(f, "%*s ", nut_host, host);
- }
- #endif
- #ifdef HAVE_ut_time
- utputs_time(f, ut);
- #endif
-
- (void) putc('\n', f);
-
- return 1;
- }
-
- /*
- * utget() - read a struct utmp from the given file
- */
- struct utmp *
- utget(f)
- FILE *f;
- {
- static struct utmp utb;
- static struct utmp *ut = &utb;
-
- if (fread((char *) ut, sizeof(*ut), 1, f) != 1)
- return (struct utmp *) 0;
-
- return ut;
- }
-
- /*
- * utput() - write a struct utmp to the given file
- */
- utput(f, ut)
- FILE *f;
- struct utmp *ut;
- {
- if (!ut || !f)
- return 0;
-
- return fwrite((char *) ut, sizeof(*ut), 1, f);
- }
-
- /*
- * docopy() - copy from i to o, translating as appropriate
- */
- docopy(i, o)
- FILE *i;
- FILE *o;
- {
- FILE *tmp = tmpfile();
- int ilen = 0; /* size of i file */
- int binary = 0; /* 1 if i file is binary */
- int c;
-
- /*
- * The textual representation may contain only printing
- * characters - only the visible characters and space.
- *
- * If there is any character which is not ascii or is not
- * printable, then treat the file as binary.
- */
-
- while ((c = getc(i)) != EOF)
- {
- ilen++;
-
- if (!isascii(c) || !(isprint(c) || c == '\n'))
- {
- binary = 1;
- }
-
- (void) putc(c, tmp);
- }
- if(ilen == 0)
- return;
-
- rewind(tmp);
-
- if (binary)
- {
- #if 0
- (void) fprintf(stderr, "Input is Binary\n");
- #endif
- if (ilen % sizeof(struct utmp))
- {
- (void) fprintf(stderr, "Binary wrong size!\n");
- }
- while (utputs(o, utget(tmp)))
- continue;
- }
- else
- {
- #if 0
- (void) fprintf(stderr, "Input is Text\n");
- #endif
- if (ilen % LINSIZ)
- {
- (void) fprintf(stderr, "Text wrong size!\n");
- }
- while (utput(o, utgets(tmp)))
- continue;
- }
- }
-
- main(ac, av)
- int ac;
- char **av;
- {
- FILE *in = stdin;
- FILE *out = stdout;
- int optc = cmdline(ac, av);
-
- ac -= optc;
- av += optc;
-
- switch (ac)
- {
- #if 0
- case 2:
- if ((out = fopen(av[1], "w")) == (FILE *) 0)
- {
- exit(1);
- }
- /* PASSTHROUGH */
- #endif
- case 1:
- if ((in = fopen(av[0], "r")) == (FILE *) 0)
- {
- exit(1);
- }
- break;
- case 0:
- if (isatty(0))
- {
- if ((in = fopen(UTMP_FILE, "r")) == (FILE *) 0)
- {
- exit(1);
- }
- }
- break;
- default:
- exit(1);
- }
-
- docopy(in, out);
-
- return 0;
- }
-