home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / compsrcs / misc / volume05 / s5last < prev    next >
Encoding:
Internet Message Format  |  1991-08-27  |  12.0 KB

  1. From decwrl!purdue!gatech!cwjcc!hal!ncoast!allbery Fri Nov 18 20:44:33 PST 1988
  2. Article 726 of comp.sources.misc:
  3. Path: granite!decwrl!purdue!gatech!cwjcc!hal!ncoast!allbery
  4. From: moran@tron.UUCP
  5. Newsgroups: comp.sources.misc
  6. Subject: v05i049: "last" command for System V
  7. Message-ID: <8811111227.AA24477@tron.WEC.COM>
  8. Date: 15 Nov 88 00:27:31 GMT
  9. Sender: allbery@ncoast.UUCP
  10. Reply-To: moran@tron.UUCP
  11. Lines: 397
  12. Approved: allbery@ncoast.UUCP
  13.  
  14. Posting-number: Volume 5, Issue 49
  15. Submitted-by: "A. Nonymous" <moran@tron.UUCP>
  16. Archive-name: s5last
  17.  
  18. #    This is a shell archive.
  19. #    Remove everything above and including the cut line.
  20. #    Then run the rest of the file through sh.
  21. #----cut here-----cut here-----cut here-----cut here----#
  22. #!/bin/sh
  23. # shar:    Shell Archiver
  24. #    Run the following text with /bin/sh to create:
  25. #    last.1
  26. #    Makefile
  27. #    last.c
  28. # This archive created: Thu Nov 10 00:50:45 1988
  29. echo shar: extracting last.1
  30. sed 's/^X //' << \SHAR_EOF > last.1
  31. X .\"  Copyright (c) 1988  Harvey R. Moran Jr.
  32. X .\"                      1936 Altavue Rd.
  33. X .\"                      Catonsville, Md., 21228
  34. X .\"
  35. X .\"                      moran%tron.UUCP@umbc3.UMD.EDU   Internet
  36. X .\"                      {wb3ffv,netsys}!hrmhpc!harvey   UUCP
  37. X .\"
  38. X .\"  This software may be freely distributed provided that this
  39. X .\"  copyright notice is left intact and provided that:
  40. X .\"     a) The source code in machine readable format 
  41. X .\"        is included with any binary distribution.
  42. X .\"     b) If a binary version of this program or a derived work is sold,
  43. X .\"        the source code must be provided in machine readable
  44. X .\"        format for no additional charge.
  45. X .\"     c) No derived works may impose restrictions limiting free
  46. X .\"        distribution of the source code.
  47. X .\"  Source code in this context includes the C language source code
  48. X .\"  provided here as file "last.c" and the manual page provide here as
  49. X .\"  file "last.1".  It also includes any works derived from either of these.
  50. X .TH last 1L LOCAL
  51. X .SH NAME
  52. X last \- indicate last logins of users
  53. X .SH SYNTAX
  54. X .B last
  55. X [
  56. X .I -N
  57. X ] [
  58. X .I name
  59. X ]
  60. X .SH DESCRIPTION
  61. X The
  62. X .I last
  63. X command
  64. X looks back in the
  65. X .I wtmp
  66. X file which records all logins and logouts for information about
  67. X a user, or all users in reverse time order.
  68. X The optional argument
  69. X .I -N
  70. X specifies the maximum number of lines to print.
  71. X The optional
  72. X .I name
  73. X specifies the name of a user of interest.
  74. X If the session is still continuing \fBlast\fR
  75. X so indicates.
  76. X .PP
  77. X The
  78. X .I last
  79. X command
  80. X with no arguments prints a record of all logins and logouts.
  81. X .SH ORIGIN
  82. X This command is modeled on the
  83. X .I last
  84. X command which comes with the
  85. X Berkeley Software Distribution (BSD) of UNIX(tm).   The Berkeley version
  86. X also permits specifying a terminal name.  It also includes the
  87. X field specifying the remote machine in the case of a network login.
  88. X No such field is present in the System V.2 version of /etc/wtmp.
  89. X .SH FILES
  90. X /etc/wtmp        login data base
  91. X .SH SEE\ ALSO
  92. X utmp(4) login data base format description
  93. SHAR_EOF
  94. if test 2013 -ne "`wc -c last.1`"
  95. then
  96. echo shar: error transmitting last.1 '(should have been 2013 characters)'
  97. fi
  98. echo shar: extracting Makefile
  99. sed 's/^X //' << \SHAR_EOF > Makefile
  100. X DEFS = -DMAXENTRIES=1024    # maximum number of entries in /etc/wtmp handled
  101. X CFLAGS = -O -s $(DEFS)        # -O(ptomize) -s(trip symbols)
  102. X last:    last.c
  103. X     cc $(CFLAGS) -o last last.c
  104. SHAR_EOF
  105. if test 172 -ne "`wc -c Makefile`"
  106. then
  107. echo shar: error transmitting Makefile '(should have been 172 characters)'
  108. fi
  109. echo shar: extracting last.c
  110. sed 's/^X //' << \SHAR_EOF > last.c
  111. X /*
  112. X  * last.c -- examine the wtmp file for last login information
  113. X  *
  114. X  * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
  115. X  *  Copyright (c) 1988  Harvey R. Moran Jr.
  116. X  *                      1936 Altavue Rd.
  117. X  *                      Catonsville, Md., 21228
  118. X  *
  119. X  *                      moran%tron.UUCP@umbc3.UMD.EDU   Internet
  120. X  *                      {wb3ffv,netsys}!hrmhpc!harvey   UUCP
  121. X  *
  122. X  *  This software may be freely distributed provided that this
  123. X  *  copyright notice is left intact and provided that:
  124. X  *     a) The source code in machine readable format 
  125. X  *        is included with any binary distribution.
  126. X  *     b) If a binary version of this program or a derived work is sold,
  127. X  *        the source code must be provided in machine readable
  128. X  *        format for no additional charge.
  129. X  *     c) No derived works may impose restrictions limiting free
  130. X  *        distribution of the source code.
  131. X  *  Source code in this context includes the C language source code
  132. X  *  provided here as file "last.c" and the manual page provide here as
  133. X  *  file "last.1".  It also includes any works derived from either of these.
  134. X  *  
  135. X  * =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
  136. X  *  Developed using Microport System V/AT 2.3U
  137. X  *
  138. X  *   usage:
  139. X  *        last [ -N ] [ username ]
  140. X  *
  141. X  *  This will print the usage of the system as
  142. X  * recorded in WTMP_FILE (defined in /usr/include/utmp.h) for
  143. X  * all users except the ones listed in exclude[] (below).
  144. X  * The option -N specifies that a maximum of N lines of
  145. X  * output will be generated.
  146. X  * The optional username form will restrict information printed
  147. X  * to be logins by the specified user.
  148. X  *
  149. X  * This command is modeled on the "last" command which is distributed
  150. X  * with the Berkeley Software Distribution (BSD) of UNIX(tm).
  151. X  * The print formats are slightly different. The field which
  152. X  * specifies a network login identification are not provided with this
  153. X  * version because the information is not available in the System V.2
  154. X  * UNIX(tm) WTMP_FILE file.
  155. X  *
  156. X  * It will also quit (with error message) if there are more than
  157. X  * MAXENTRIES entries in your WTMP_FILE (/etc/wtmp on my system).
  158. X  *
  159. X  *  Harvey Moran  11/10/88
  160. X  */
  161. X #include <stdio.h>
  162. X #include <fcntl.h>
  163. X #include <sys/types.h>
  164. X #include <utmp.h>
  165. X #include <time.h>
  166. X #ifndef MAXENTRIES
  167. X #    define MAXENTRIES 1024    /* maximum entries in WTMP_FILE handled */
  168. X #endif
  169. X #define STILL_IN -1L
  170. X #define AVOID    -1L
  171. X typedef int BOOL;
  172. X #define YES 1
  173. X #define NO  0
  174. X typedef struct utmp WTMP_t;
  175. X WTMP_t proc_tab;
  176. X typedef struct {
  177. X     char user_name[9];
  178. X     short pid;
  179. X     time_t x_time;
  180. X     time_t y_time;
  181. X     } ENTRY;
  182. X ENTRY entries[MAXENTRIES];
  183. X /*
  184. X  * user process user names to be excluded from display
  185. X  * change per local installation.  Perhaps these should be
  186. X  * in an environment variable instead, but there are already
  187. X  * so many of them.
  188. X  */
  189. X char *exclude[] = { "rc2", "crtsaver", "LOGIN", NULL };
  190. X char *keep_list = (char *) NULL;
  191. X /*
  192. X  * forward declared functions
  193. X  */
  194. X char *delta_time();
  195. X void qsort();
  196. X int cmp_ENTRY(), cmp_ENTRY_1();
  197. X main(ac, av)
  198. X int ac;
  199. X char *av[];
  200. X {
  201. X     int i, j, fd;
  202. X     unsigned  count = (unsigned) -1;    /* BIG unsigned int. */
  203. X     char intime[27], outtime[27], elapsed[27];
  204. X     long time_tmp;
  205. X     if ( ac > 1 && (i = atoi(av[1])) < 0 ) {
  206. X         count = (unsigned) -i;
  207. X         for ( i = 2; i < ac; ++i )
  208. X             av[i-1] = av[i];
  209. X         av[i] = NULL;
  210. X         --ac;
  211. X         }
  212. X     if ( (fd=open(WTMP_FILE, (O_RDONLY|O_NDELAY))) == -1 ) {
  213. X         fprintf(stderr, "%s:Can't open %s\n", av[0], WTMP_FILE);
  214. X         exit(1);
  215. X         }
  216. X     i = 0;
  217. X     while ( read(fd, &proc_tab, sizeof(WTMP_t)) == sizeof(WTMP_t) ) {
  218. X         switch ( proc_tab.ut_type ) {
  219. X             case EMPTY:            /* Empty slot in the WTMP_FILE */
  220. X             case INIT_PROCESS:    /* Process started by "init" */
  221. X             case LOGIN_PROCESS:    /* A "getty" process waiting for login */
  222. X             case ACCOUNTING:
  223. X                 break;            /* IGNORE all these forever */
  224. X             case RUN_LVL:        /* Change of Run Level */
  225. X             case BOOT_TIME:        /* Boot time */
  226. X             case OLD_TIME:        /* Old time of Day was */
  227. X             case NEW_TIME:        /* New Time of Day set */
  228. X                 break;            /* IGNORE all these for now */
  229. X             case USER_PROCESS:    /* A user process was started */
  230. X             case DEAD_PROCESS:    /* A user (I think) process died */
  231. X                 strncpy(entries[i].user_name, proc_tab.ut_user, sizeof(proc_tab.ut_user));
  232. X                 if ( omit(entries[i].user_name, ac, av) )
  233. X                     break;
  234. X                 if ( ac == 2 && strcmp(av[1], entries[i].user_name) )
  235. X                     break;    /* not the user of interest */
  236. X                 entries[i].pid = proc_tab.ut_pid;
  237. X                 entries[i].x_time = proc_tab.ut_time;
  238. X                 if ( ++i > (sizeof(entries)/sizeof(ENTRY)) ) {
  239. X                     fprintf(stderr, "Too many entries in %s\n", WTMP_FILE);
  240. X                     fprintf(stderr, "Fix program %s\n", av[0]);
  241. X                     exit(2);
  242. X                     }
  243. X                 break;
  244. X             default:
  245. X                 fprintf(stderr, "Ignoring GARBAGE in file %s. (ut_type = 0x%02x)\n", WTMP_FILE, proc_tab.ut_type);
  246. X                 break;
  247. X             }
  248. X         }
  249. X     /*
  250. X      * sort by pid, x_time
  251. X      */
  252. X     qsort((char *) entries, i, sizeof(ENTRY), cmp_ENTRY);
  253. X     j = 0;
  254. X #define E entries    /* minimize line wrapping of source code */
  255. X     do {
  256. X         if ( j == (i-1) ) {
  257. X             /*
  258. X              * final login has no corresponding logout
  259. X              * avoid referencing array element with is empty (or does not exist)
  260. X              */
  261. X             E[j].y_time = STILL_IN;
  262. X             break;
  263. X             }
  264. X         else if ( E[j].pid != E[j+1].pid ) { /* login has no corresponding logout */
  265. X             E[j].y_time = STILL_IN;
  266. X             ++j;
  267. X             }
  268. X         else { /* E[j].pid == E[j+1].pid , i.e. login/logout pair */
  269. X             E[j].y_time = E[j+1].x_time;
  270. X             E[j+1].x_time = AVOID;
  271. X             j += 2;
  272. X             }
  273. X         } while ( j < i );
  274. X     /*
  275. X      * sort by descending x_time
  276. X      */
  277. X     qsort((char *) entries, i, sizeof(ENTRY), cmp_ENTRY_1);
  278. X     for ( j = 0; j < i && count; ++j ) {
  279. X         if ( E[j].x_time == AVOID )
  280. X             continue;
  281. X         else if ( E[j].y_time == STILL_IN ) {
  282. X             time_tmp = (long) E[j].x_time;
  283. X             (void) strcpy(intime, asctime(localtime(&time_tmp)));
  284. X             intime[20] = '\0';    /* strip year */
  285. X             printf("%-8s %s %s still-logged-in\n", E[j].user_name, intime, " ");
  286. X             --count;
  287. X             }
  288. X         else {
  289. X             time_tmp = (long) E[j].x_time;
  290. X             (void) strcpy(intime, asctime(localtime(&time_tmp)));
  291. X             time_tmp = (long) E[j].y_time;
  292. X             (void) strcpy(outtime, asctime(localtime(&time_tmp)));
  293. X             intime[20] = outtime[20] = '\0';    /* strip year */
  294. X             time_tmp = (long) (E[j].y_time - E[j].x_time);
  295. X             (void) strcpy(elapsed, delta_time(time_tmp));
  296. X             printf("%-8s %s-%s %s\n", E[j].user_name, intime, outtime+10, elapsed);
  297. X             --count;
  298. X             }
  299. X         }
  300. X #undef E
  301. X }
  302. X /*
  303. X  * return comparison of 2 ENTRY type records
  304. X  * suitable for sorting by: pid, x_time
  305. X  */
  306. X cmp_ENTRY(a, b)
  307. X ENTRY *a, *b;
  308. X {
  309. X     if ( a->pid < b->pid )
  310. X         return (-1);
  311. X     if ( a->pid > b->pid )
  312. X         return (1);
  313. X     if ( a->x_time < b->x_time )
  314. X         return (-1);
  315. X     if ( a->x_time > b->x_time )
  316. X         return (1);
  317. X     return (0);
  318. X }
  319. X /*
  320. X  * return comparison of 2 ENTRY type records
  321. X  * suitable for a descending sort by: x_time
  322. X  */
  323. X cmp_ENTRY_1(a,b)
  324. X ENTRY *a, *b;
  325. X {
  326. X     if ( a->x_time < b->x_time )
  327. X         return (1);
  328. X     if ( a->x_time > b->x_time )
  329. X         return (-1);
  330. X     return (0);
  331. X }
  332. X /*
  333. X  * return whether to omit this name
  334. X  * It is omitted if:
  335. X  *       a) the name matches something in the exclude list
  336. X  *       b) the name does not match something in the invocation argument list
  337. X  *          and the argument list is not null
  338. X  */
  339. X BOOL
  340. X omit(name, ac, av)
  341. X char *name;
  342. X int ac;
  343. X char *av[];
  344. X {
  345. X     char **p;
  346. X     int i;
  347. X     for ( p = &exclude[0]; *p != (char *) NULL; ++p ) {
  348. X         if ( strcmp(*p, name) == 0 )
  349. X             return (YES);
  350. X         }
  351. X     if ( ac == 1 )    /* empty argument list, do not omit any */
  352. X         return (NO);
  353. X     for ( i = 1; i < ac; ++i ) {
  354. X         if ( strcmp(name, av[i]) == 0 )
  355. X             return (NO);
  356. X         }
  357. X     return (YES);
  358. X }
  359. X char *
  360. X delta_time(t)
  361. X long t;
  362. X {
  363. X     static char AscTime[11];
  364. X     int sec, min, hr, da;
  365. X     long x, y;
  366. X     sec = t % 60L;
  367. X     x = (t - (long) sec)/60L;
  368. X     min = x % 60L;
  369. X     x = (x - (long) min)/60L;
  370. X     hr  = x % 24;
  371. X     x   = (x - (long) hr)/24L;
  372. X     da  = x;
  373. X     if ( da )
  374. X         (void) sprintf(AscTime, "%3d+%02d:%02d:%02d", da, hr, min, sec);
  375. X     else
  376. X         (void) sprintf(AscTime, "    %02d:%02d:%02d", hr, min, sec);
  377. X     return (AscTime);
  378. X }
  379. SHAR_EOF
  380. if test 7908 -ne "`wc -c last.c`"
  381. then
  382. echo shar: error transmitting last.c '(should have been 7908 characters)'
  383. fi
  384. #    End of shell archive
  385. exit 0
  386.  
  387.  
  388.