Upload Tizen:Base source
[framework/base/util-linux-ng.git] / login-utils / last.c
1 /*
2  * Berkeley last for Linux. Currently maintained by poe@daimi.aau.dk at
3  * ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil*
4  *
5  * Copyright (c) 1987 Regents of the University of California.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms are permitted
9  * provided that the above copyright notice and this paragraph are
10  * duplicated in all such forms and that any documentation,
11  * advertising materials, and other materials related to such
12  * distribution and use acknowledge that the software was developed
13  * by the University of California, Berkeley.  The name of the
14  * University may not be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  */
20
21  /* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
22   * - added Native Language Support
23   */
24
25  /* 2001-02-14 Marek Zelem <marek@fornax.sk>
26   * - using mmap() on Linux - great speed improvement
27   */
28
29 /*
30  * last
31  */
32 #include <sys/param.h>
33 #include <sys/stat.h>
34 #include <sys/file.h>
35 #include <sys/types.h>
36 #include <sys/mman.h>
37 #include <signal.h>
38 #include <string.h>
39 #include <time.h>
40 #include <utmp.h>
41 #include <stdio.h>
42 #include <getopt.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49
50 #include "pathnames.h"
51 #include "nls.h"
52
53 #define SECDAY  (24*60*60)                      /* seconds in a day */
54 #define NO      0                               /* false/no */
55 #define YES     1                               /* true/yes */
56
57 static struct utmp      utmpbuf;
58
59 #define HMAX    (int)sizeof(utmpbuf.ut_host)    /* size of utmp host field */
60 #define LMAX    (int)sizeof(utmpbuf.ut_line)    /* size of utmp tty field */
61 #define NMAX    (int)sizeof(utmpbuf.ut_name)    /* size of utmp name field */
62
63 #ifndef MIN
64 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
65 #endif
66
67 /* maximum sizes used for printing */
68 /* probably we want a two-pass version that computes the right length */
69 int hmax = MIN(HMAX, 16);
70 int lmax = MIN(LMAX, 8);
71 int nmax = MIN(NMAX, 16);
72
73 typedef struct arg {
74         char    *name;                          /* argument */
75 #define HOST_TYPE       -2
76 #define TTY_TYPE        -3
77 #define USER_TYPE       -4
78 #define INET_TYPE       -5
79         int     type;                           /* type of arg */
80         struct arg      *next;                  /* linked list pointer */
81 } ARG;
82 ARG     *arglist;                               /* head of linked list */
83
84 typedef struct ttytab {
85         long    logout;                         /* log out time */
86         char    tty[LMAX + 1];                  /* terminal name */
87         struct ttytab   *next;                  /* linked list pointer */
88 } TTY;
89 TTY     *ttylist;                               /* head of linked list */
90
91 static long     currentout,                     /* current logout value */
92                 maxrec;                         /* records to display */
93 static char     *file = _PATH_WTMP;             /* wtmp file */
94
95 static int      doyear = 0;                     /* output year in dates */
96 static int      dolong = 0;                     /* print also ip-addr */
97
98 static void wtmp(void);
99 static void addarg(int, char *);
100 static void hostconv(char *);
101 static void onintr(int);
102 static int want(struct utmp *, int);
103 TTY *addtty(char *);
104 static char *ttyconv(char *);
105
106 int
107 main(int argc, char **argv) {
108         extern int      optind;
109         extern char     *optarg;
110         int     ch;
111
112         setlocale(LC_ALL, "");
113         bindtextdomain(PACKAGE, LOCALEDIR);
114         textdomain(PACKAGE);
115
116         while ((ch = getopt(argc, argv, "0123456789yli:f:h:t:")) != -1)
117                 switch((char)ch) {
118                 case '0': case '1': case '2': case '3': case '4':
119                 case '5': case '6': case '7': case '8': case '9':
120                         /*
121                          * kludge: last was originally designed to take
122                          * a number after a dash.
123                          */
124                         if (!maxrec)
125                                 maxrec = atol(argv[optind - 1] + 1);
126                         break;
127                 case 'f':
128                         file = optarg;
129                         break;
130                 case 'h':
131                         hostconv(optarg);
132                         addarg(HOST_TYPE, optarg);
133                         break;
134                 case 't':
135                         addarg(TTY_TYPE, ttyconv(optarg));
136                         break;
137                 case 'y':
138                         doyear = 1;
139                         break;
140                 case 'l':
141                         dolong = 1;
142                         break;
143                 case 'i':
144                         addarg(INET_TYPE, optarg);
145                         break;
146                 case '?':
147                 default:
148                         fputs(_("usage: last [-#] [-f file] [-t tty] [-h hostname] [user ...]\n"), stderr);
149                         exit(1);
150                 }
151         for (argv += optind; *argv; ++argv) {
152 #define COMPATIBILITY
153 #ifdef  COMPATIBILITY
154                 /* code to allow "last p5" to work */
155                 addarg(TTY_TYPE, ttyconv(*argv));
156 #endif
157                 addarg(USER_TYPE, *argv);
158         }
159         wtmp();
160         exit(0);
161 }
162
163 /*
164  * print_partial_line --
165  *      print the first part of each output line according to specified format
166  */
167 static void
168 print_partial_line(struct utmp *bp) {
169     char *ct;
170
171     ct = ctime(&bp->ut_time);
172     printf("%-*.*s  %-*.*s ", nmax, nmax, bp->ut_name, 
173            lmax, lmax, bp->ut_line);
174
175     if (dolong) {
176         if (bp->ut_addr) {
177             struct in_addr foo;
178             foo.s_addr = bp->ut_addr;
179             printf("%-*.*s ", hmax, hmax, inet_ntoa(foo));
180         } else {
181             printf("%-*.*s ", hmax, hmax, "");
182         }
183     } else {
184         printf("%-*.*s ", hmax, hmax, bp->ut_host);
185     }
186
187     if (doyear) {
188         printf("%10.10s %4.4s %5.5s ", ct, ct + 20, ct + 11);
189     } else {
190         printf("%10.10s %5.5s ", ct, ct + 11);
191     }
192 }
193
194 /*
195  * wtmp --
196  *      read through the wtmp file
197  */
198 static void
199 wtmp(void) {
200         register struct utmp    *bp;            /* current structure */
201         register TTY    *T;                     /* tty list entry */
202         long    delta;                          /* time difference */
203         char *crmsg = NULL;
204         char *ct = NULL;
205         int fd;
206         struct utmp *utl;
207         struct stat st;
208         int utl_len;
209         int listnr = 0;
210         int i;
211         
212         utmpname(file);
213
214         (void)time(&utmpbuf.ut_time);
215         (void)signal(SIGINT, onintr);
216         (void)signal(SIGQUIT, onintr);
217
218         if ((fd = open(file,O_RDONLY)) < 0)
219                 exit(1);
220         fstat(fd, &st);
221         utl_len = st.st_size;
222         utl = mmap(NULL, utl_len, PROT_READ|PROT_WRITE,
223                    MAP_PRIVATE|MAP_FILE, fd, 0);
224         if (utl == NULL)
225                 exit(1);
226         listnr = utl_len/sizeof(struct utmp);
227
228         if(listnr) 
229                 ct = ctime(&utl[0].ut_time);
230
231         for(i = listnr - 1; i >= 0; i--) {
232                 bp = utl+i;
233                 /*
234                  * if the terminal line is '~', the machine stopped.
235                  * see utmp(5) for more info.
236                  */
237                 if (!strncmp(bp->ut_line, "~", LMAX)) {
238                     /* 
239                      * utmp(5) also mentions that the user 
240                      * name should be 'shutdown' or 'reboot'.
241                      * Not checking the name causes e.g. runlevel
242                      * changes to be displayed as 'crash'. -thaele
243                      */
244                     if (!strncmp(bp->ut_user, "reboot", NMAX) ||
245                         !strncmp(bp->ut_user, "shutdown", NMAX)) {      
246                         /* everybody just logged out */
247                         for (T = ttylist; T; T = T->next)
248                             T->logout = -bp->ut_time;
249                     }
250
251                     currentout = -bp->ut_time;
252                     crmsg = (strncmp(bp->ut_name, "shutdown", NMAX)
253                             ? "crash" : "down ");
254                     if (!bp->ut_name[0])
255                         (void)strcpy(bp->ut_name, "reboot");
256                     if (want(bp, NO)) {
257                         ct = ctime(&bp->ut_time);
258                         if(bp->ut_type != LOGIN_PROCESS) {
259                             print_partial_line(bp);
260                             putchar('\n');
261                         }
262                         if (maxrec && !--maxrec)
263                             return;
264                     }
265                     continue;
266                 }
267                 /* find associated tty */
268                 for (T = ttylist;; T = T->next) {
269                     if (!T) {
270                         /* add new one */
271                         T = addtty(bp->ut_line);
272                         break;
273                     }
274                     if (!strncmp(T->tty, bp->ut_line, LMAX))
275                         break;
276                 }
277                 if (bp->ut_name[0] && bp->ut_type != LOGIN_PROCESS
278                     && bp->ut_type != DEAD_PROCESS
279                     && want(bp, YES)) {
280
281                     print_partial_line(bp);
282
283                     if (!T->logout)
284                         puts(_("  still logged in"));
285                     else {
286                         if (T->logout < 0) {
287                             T->logout = -T->logout;
288                             printf("- %s", crmsg);
289                         }
290                         else
291                             printf("- %5.5s", ctime(&T->logout)+11);
292                         delta = T->logout - bp->ut_time;
293                         if (delta < SECDAY)
294                             printf("  (%5.5s)\n", asctime(gmtime(&delta))+11);
295                         else
296                             printf(" (%ld+%5.5s)\n", delta / SECDAY, asctime(gmtime(&delta))+11);
297                     }
298                     if (maxrec != -1 && !--maxrec)
299                         return;
300                 }
301                 T->logout = bp->ut_time;
302                 utmpbuf.ut_time = bp->ut_time;
303         }
304         munmap(utl,utl_len);
305         close(fd);
306         if(ct) printf(_("\nwtmp begins %s"), ct);       /* ct already ends in \n */
307 }
308
309 /*
310  * want --
311  *      see if want this entry
312  */
313 static int
314 want(struct utmp *bp, int check) {
315         register ARG    *step;
316
317         if (check) {
318                 /*
319                  * when uucp and ftp log in over a network, the entry in
320                  * the utmp file is the name plus their process id.  See
321                  * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
322                  */
323                 if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1))
324                         bp->ut_line[3] = '\0';
325                 else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1))
326                         bp->ut_line[4] = '\0';
327         }
328         if (!arglist)
329                 return(YES);
330
331         for (step = arglist; step; step = step->next)
332                 switch(step->type) {
333                 case HOST_TYPE:
334                         if (!strncmp(step->name, bp->ut_host, HMAX))
335                                 return(YES);
336                         break;
337                 case TTY_TYPE:
338                         if (!strncmp(step->name, bp->ut_line, LMAX))
339                                 return(YES);
340                         break;
341                 case USER_TYPE:
342                         if (!strncmp(step->name, bp->ut_name, NMAX))
343                                 return(YES);
344                         break;
345                 case INET_TYPE:
346                         if (bp->ut_addr == inet_addr(step->name))
347                           return(YES);
348                         break;
349         }
350         return(NO);
351 }
352
353 /*
354  * addarg --
355  *      add an entry to a linked list of arguments
356  */
357 static void
358 addarg(int type, char *arg) {
359         register ARG    *cur;
360
361         if (!(cur = (ARG *)malloc((unsigned int)sizeof(ARG)))) {
362                 fputs(_("last: malloc failure.\n"), stderr);
363                 exit(1);
364         }
365         cur->next = arglist;
366         cur->type = type;
367         cur->name = arg;
368         arglist = cur;
369 }
370
371 /*
372  * addtty --
373  *      add an entry to a linked list of ttys
374  */
375 TTY *
376 addtty(char *ttyname) {
377         register TTY    *cur;
378
379         if (!(cur = (TTY *)malloc((unsigned int)sizeof(TTY)))) {
380                 fputs(_("last: malloc failure.\n"), stderr);
381                 exit(1);
382         }
383         cur->next = ttylist;
384         cur->logout = currentout;
385         memcpy(cur->tty, ttyname, LMAX);
386         return(ttylist = cur);
387 }
388
389 /*
390  * hostconv --
391  *      convert the hostname to search pattern; if the supplied host name
392  *      has a domain attached that is the same as the current domain, rip
393  *      off the domain suffix since that's what login(1) does.
394  */
395 static void
396 hostconv(char *arg) {
397         static int      first = 1;
398         static char     *hostdot,
399                         name[MAXHOSTNAMELEN];
400         char    *argdot;
401
402         if (!(argdot = strchr(arg, '.')))
403                 return;
404         if (first) {
405                 first = 0;
406                 if (gethostname(name, sizeof(name))) {
407                         perror(_("last: gethostname"));
408                         exit(1);
409                 }
410                 hostdot = strchr(name, '.');
411         }
412         if (hostdot && !strcmp(hostdot, argdot))
413                 *argdot = '\0';
414 }
415
416 /*
417  * ttyconv --
418  *      convert tty to correct name.
419  */
420 static char *
421 ttyconv(char *arg) {
422         char    *mval;
423
424         /*
425          * kludge -- we assume that all tty's end with
426          * a two character suffix.
427          */
428         if (strlen(arg) == 2) {
429                 /* either 6 for "ttyxx" or 8 for "console" */
430                 if (!(mval = malloc((unsigned int)8))) {
431                         fputs(_("last: malloc failure.\n"), stderr);
432                         exit(1);
433                 }
434                 if (!strcmp(arg, "co"))
435                         (void)strcpy(mval, "console");
436                 else {
437                         (void)strcpy(mval, "tty");
438                         (void)strcpy(mval + 3, arg);
439                 }
440                 return(mval);
441         }
442         if (!strncmp(arg, "/dev/", sizeof("/dev/") - 1))
443                 return(arg + 5);
444         return(arg);
445 }
446
447 /*
448  * onintr --
449  *      on interrupt, we inform the user how far we've gotten
450  */
451 static void
452 onintr(int signo) {
453         char    *ct;
454
455         ct = ctime(&utmpbuf.ut_time);
456         printf(_("\ninterrupted %10.10s %5.5s \n"), ct, ct + 11);
457         if (signo == SIGINT)
458                 exit(1);
459         (void)fflush(stdout);                   /* fix required for rsh */
460 }