[BACK]Return to lastlogin.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / usr.sbin / lastlogin

Annotation of src/usr.sbin/lastlogin/lastlogin.c, Revision 1.12

1.12    ! martin      1: /*     $NetBSD: lastlogin.c,v 1.11 2004/11/19 21:43:40 christos Exp $  */
1.1       phil        2: /*
                      3:  * Copyright (c) 1996 John M. Vinopal
                      4:  * All rights reserved.
                      5:  *
                      6:  * Redistribution and use in source and binary forms, with or without
                      7:  * modification, are permitted provided that the following conditions
                      8:  * are met:
                      9:  * 1. Redistributions of source code must retain the above copyright
                     10:  *    notice, this list of conditions and the following disclaimer.
                     11:  * 2. Redistributions in binary form must reproduce the above copyright
                     12:  *    notice, this list of conditions and the following disclaimer in the
                     13:  *    documentation and/or other materials provided with the distribution.
                     14:  * 3. All advertising materials mentioning features or use of this software
                     15:  *    must display the following acknowledgement:
                     16:  *     This product includes software developed for the NetBSD Project
                     17:  *     by John M. Vinopal.
                     18:  * 4. The name of the author may not be used to endorse or promote products
                     19:  *    derived from this software without specific prior written permission.
                     20:  *
                     21:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     22:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     23:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
                     24:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
                     25:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
                     26:  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
                     27:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
                     28:  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
                     29:  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     30:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     31:  * SUCH DAMAGE.
                     32:  */
                     33:
1.3       lukem      34: #include <sys/cdefs.h>
                     35: #ifndef lint
1.12    ! martin     36: __RCSID("$NetBSD: lastlogin.c,v 1.11 2004/11/19 21:43:40 christos Exp $");
1.3       lukem      37: #endif
                     38:
1.1       phil       39: #include <sys/types.h>
1.3       lukem      40: #include <err.h>
1.9       christos   41: #include <db.h>
1.1       phil       42: #include <errno.h>
                     43: #include <pwd.h>
                     44: #include <stdio.h>
1.5       matt       45: #include <stdlib.h>
1.7       elric      46: #include <string.h>
1.9       christos   47: #include <fcntl.h>
1.3       lukem      48: #include <time.h>
1.9       christos   49: #include <arpa/inet.h>
                     50: #include <sys/socket.h>
                     51: #ifdef SUPPORT_UTMP
1.3       lukem      52: #include <utmp.h>
1.9       christos   53: #endif
                     54: #ifdef SUPPORT_UTMPX
                     55: #include <utmpx.h>
                     56: #endif
1.4       perry      57: #include <unistd.h>
1.11      christos   58: #include <util.h>
1.1       phil       59:
1.7       elric      60: struct output {
1.9       christos   61:        struct timeval   o_tv;
                     62:        struct sockaddr_storage o_ss;
                     63:        char             o_name[64];
                     64:        char             o_line[64];
                     65:        char             o_host[256];
1.7       elric      66:        struct output   *next;
                     67: };
                     68:
1.9       christos   69: static char *logfile =
                     70: #if defined(SUPPORT_UTMPX)
                     71:     _PATH_LASTLOGX;
                     72: #elif defined(SUPPORT_UTMP)
                     73:     _PATH_LASTLOG;
                     74: #else
                     75:        #error "either SUPPORT_UTMP or SUPPORT_UTMPX must be defined"
                     76: #endif
1.1       phil       77:
1.7       elric      78: #define SORT_NONE      0x0000
                     79: #define SORT_REVERSE   0x0001
                     80: #define SORT_TIME      0x0002
                     81: #define DOSORT(x)      ((x) & (SORT_TIME))
                     82: static int sortlog = SORT_NONE;
                     83: static struct output *outstack = NULL;
                     84:
1.9       christos   85: static int numeric = 0;
                     86: static size_t namelen = UT_NAMESIZE;
                     87: static size_t linelen = UT_LINESIZE;
                     88: static size_t hostlen = UT_HOSTSIZE;
                     89:
                     90:        int     main(int, char **);
                     91: static int     comparelog(const void *, const void *);
                     92: static void    output(struct output *);
                     93: #ifdef SUPPORT_UTMP
                     94: static void    process_entry(struct passwd *, struct lastlog *);
                     95: static void    dolastlog(const char *, int, char *[]);
                     96: #endif
                     97: #ifdef SUPPORT_UTMPX
                     98: static void    process_entryx(struct passwd *, struct lastlogx *);
                     99: static void    dolastlogx(const char *, int, char *[]);
                    100: #endif
                    101: static void    push(struct output *);
                    102: static const char      *gethost(struct output *);
                    103: static void    sortoutput(struct output *);
                    104: static void    usage(void);
1.1       phil      105:
                    106: int
                    107: main(argc, argv)
                    108:        int argc;
                    109:        char *argv[];
                    110: {
1.9       christos  111:        int     ch;
                    112:        size_t  len;
1.1       phil      113:
1.9       christos  114:        while ((ch = getopt(argc, argv, "f:H:L:nN:rt")) != -1) {
1.7       elric     115:                switch (ch) {
1.9       christos  116:                case 'H':
                    117:                        hostlen = atoi(optarg);
                    118:                        break;
                    119:                case 'f':
                    120:                        logfile = optarg;
                    121:                        break;
                    122:                case 'L':
                    123:                        linelen = atoi(optarg);
                    124:                        break;
                    125:                case 'n':
                    126:                        numeric++;
                    127:                        break;
                    128:                case 'N':
                    129:                        namelen = atoi(optarg);
                    130:                        break;
1.7       elric     131:                case 'r':
                    132:                        sortlog |= SORT_REVERSE;
                    133:                        break;
                    134:                case 't':
                    135:                        sortlog |= SORT_TIME;
                    136:                        break;
                    137:                default:
                    138:                        usage();
                    139:                }
1.1       phil      140:        }
1.7       elric     141:        argc -= optind;
                    142:        argv += optind;
1.1       phil      143:
1.9       christos  144:        len = strlen(logfile);
                    145:
                    146:        setpassent(1);  /* Keep passwd file pointers open */
                    147:
                    148: #if defined(SUPPORT_UTMPX)
                    149:        if (len > 0 && logfile[len - 1] == 'x')
                    150:                dolastlogx(logfile, argc, argv);
                    151:        else
                    152: #endif
                    153: #if defined(SUPPORT_UTMP)
                    154:                dolastlog(logfile, argc, argv);
                    155: #endif
                    156:
                    157:        setpassent(0);  /* Close passwd file pointers */
                    158:
1.12    ! martin    159:        if (outstack && DOSORT(sortlog))
1.9       christos  160:                sortoutput(outstack);
                    161:
                    162:        return 0;
                    163: }
                    164:
                    165: #ifdef SUPPORT_UTMP
                    166: static void
                    167: dolastlog(const char *logfile, int argc, char **argv)
                    168: {
                    169:        int i;
                    170:        FILE *fp = fopen(logfile, "r");
                    171:        struct passwd   *passwd;
                    172:        struct lastlog l;
                    173:
1.1       phil      174:        if (fp == NULL)
                    175:                err(1, "%s", logfile);
                    176:
                    177:        /* Process usernames given on the command line. */
1.8       wulf      178:        if (argc > 0) {
1.9       christos  179:                off_t offset;
1.8       wulf      180:                for (i = 0; i < argc; i++) {
1.1       phil      181:                        if ((passwd = getpwnam(argv[i])) == NULL) {
                    182:                                warnx("user '%s' not found", argv[i]);
                    183:                                continue;
                    184:                        }
                    185:                        /* Calculate the offset into the lastlog file. */
1.9       christos  186:                        offset = passwd->pw_uid * sizeof(l);
                    187:                        if (fseeko(fp, offset, SEEK_SET)) {
1.1       phil      188:                                warn("fseek error");
                    189:                                continue;
                    190:                        }
1.9       christos  191:                        if (fread(&l, sizeof(l), 1, fp) != 1) {
1.1       phil      192:                                warnx("fread error on '%s'", passwd->pw_name);
                    193:                                clearerr(fp);
                    194:                                continue;
                    195:                        }
1.9       christos  196:                        process_entry(passwd, &l);
1.1       phil      197:                }
                    198:        }
                    199:        /* Read all lastlog entries, looking for active ones */
                    200:        else {
1.9       christos  201:                for (i = 0; fread(&l, sizeof(l), 1, fp) == 1; i++) {
                    202:                        if (l.ll_time == 0)
1.2       christos  203:                                continue;
                    204:                        if ((passwd = getpwuid(i)) != NULL)
1.9       christos  205:                                process_entry(passwd, &l);
1.1       phil      206:                }
                    207:                if (ferror(fp))
                    208:                        warnx("fread error");
                    209:        }
                    210:
1.9       christos  211:        (void)fclose(fp);
                    212: }
                    213:
                    214: static void
                    215: process_entry(struct passwd *p, struct lastlog *l)
                    216: {
                    217:        struct output   o;
                    218:
                    219:        (void)strlcpy(o.o_name, p->pw_name, sizeof(o.o_name));
                    220:        (void)strlcpy(o.o_line, l->ll_line, sizeof(l->ll_line));
                    221:        (void)strlcpy(o.o_host, l->ll_host, sizeof(l->ll_host));
                    222:        o.o_tv.tv_sec = l->ll_time;
                    223:        o.o_tv.tv_usec = 0;
                    224:        (void)memset(&o.o_ss, 0, sizeof(o.o_ss));
                    225:        o.next = NULL;
1.1       phil      226:
1.9       christos  227:        /*
                    228:         * If we are sorting it, we need all the entries in memory so
                    229:         * push the current entry onto a stack.  Otherwise, we can just
                    230:         * output it.
                    231:         */
1.7       elric     232:        if (DOSORT(sortlog))
1.9       christos  233:                push(&o);
                    234:        else
                    235:                output(&o);
                    236: }
                    237: #endif
                    238:
                    239: #ifdef SUPPORT_UTMPX
                    240: static void
                    241: dolastlogx(const char *logfile, int argc, char **argv)
                    242: {
                    243:        int i = 0;
                    244:        DB *db = dbopen(logfile, O_RDONLY|O_SHLOCK, 0, DB_HASH, NULL);
                    245:        DBT key, data;
                    246:        struct lastlogx l;
                    247:        struct passwd   *passwd;
                    248:
                    249:        if (db == NULL)
                    250:                err(1, "%s", logfile);
                    251:
                    252:        if (argc > 0) {
                    253:                for (i = 0; i < argc; i++) {
                    254:                        if ((passwd = getpwnam(argv[i])) == NULL) {
                    255:                                warnx("User `%s' not found", argv[i]);
                    256:                                continue;
                    257:                        }
                    258:                        key.data = &passwd->pw_uid;
                    259:                        key.size = sizeof(passwd->pw_uid);
                    260:
                    261:                        switch ((*db->get)(db, &key, &data, 0)) {
                    262:                        case 0:
                    263:                                break;
                    264:                        case 1:
                    265:                                warnx("User `%s' not found", passwd->pw_name);
                    266:                                continue;
                    267:                        case -1:
                    268:                                warn("Error looking up `%s'", passwd->pw_name);
                    269:                                continue;
                    270:                        default:
                    271:                                abort();
                    272:                        }
                    273:
                    274:                        if (data.size != sizeof(l)) {
                    275:                                errno = EFTYPE;
                    276:                                err(1, "%s", logfile);
                    277:                        }
                    278:                        (void)memcpy(&l, data.data, sizeof(l));
                    279:
                    280:                        process_entryx(passwd, &l);
                    281:                }
                    282:        }
                    283:        /* Read all lastlog entries, looking for active ones */
                    284:        else {
                    285:                switch ((*db->seq)(db, &key, &data, R_FIRST)) {
                    286:                case 0:
                    287:                        break;
                    288:                case 1:
                    289:                        warnx("No entries found");
                    290:                        (*db->close)(db);
                    291:                        return;
                    292:                case -1:
                    293:                        warn("Error seeking to first entry");
                    294:                        (*db->close)(db);
                    295:                        return;
                    296:                default:
                    297:                        abort();
                    298:                }
                    299:
                    300:                do {
                    301:                        uid_t uid;
                    302:
                    303:                        if (key.size != sizeof(uid) || data.size != sizeof(l)) {
                    304:                                errno = EFTYPE;
                    305:                                err(1, "%s", logfile);
                    306:                        }
                    307:                        (void)memcpy(&uid, key.data, sizeof(uid));
                    308:
                    309:                        if ((passwd = getpwuid(uid)) == NULL) {
                    310:                                warnx("Cannot find user for uid %lu",
                    311:                                    (unsigned long)uid);
                    312:                                continue;
                    313:                        }
                    314:                        (void)memcpy(&l, data.data, sizeof(l));
                    315:                        process_entryx(passwd, &l);
                    316:                } while ((i = (*db->seq)(db, &key, &data, R_NEXT)) == 0);
                    317:
                    318:                switch (i) {
                    319:                case 1:
                    320:                        break;
                    321:                case -1:
                    322:                        warn("Error seeking to last entry");
                    323:                        break;
                    324:                case 0:
                    325:                default:
                    326:                        abort();
                    327:                }
                    328:        }
1.7       elric     329:
1.9       christos  330:        (*db->close)(db);
1.1       phil      331: }
                    332:
1.7       elric     333: static void
1.9       christos  334: process_entryx(struct passwd *p, struct lastlogx *l)
1.7       elric     335: {
                    336:        struct output   o;
                    337:
1.9       christos  338:        (void)strlcpy(o.o_name, p->pw_name, sizeof(o.o_name));
                    339:        (void)strlcpy(o.o_line, l->ll_line, sizeof(l->ll_line));
                    340:        (void)strlcpy(o.o_host, l->ll_host, sizeof(l->ll_host));
                    341:        (void)memcpy(&o.o_ss, &l->ll_ss, sizeof(o.o_ss));
                    342:        o.o_tv = l->ll_tv;
1.7       elric     343:        o.next = NULL;
                    344:
                    345:        /*
                    346:         * If we are sorting it, we need all the entries in memory so
                    347:         * push the current entry onto a stack.  Otherwise, we can just
                    348:         * output it.
                    349:         */
                    350:        if (DOSORT(sortlog))
                    351:                push(&o);
                    352:        else
                    353:                output(&o);
                    354: }
1.9       christos  355: #endif
1.7       elric     356:
                    357: static void
                    358: push(struct output *o)
                    359: {
                    360:        struct output   *out;
                    361:
                    362:        out = malloc(sizeof(*out));
                    363:        if (!out)
                    364:                err(EXIT_FAILURE, "malloc failed");
1.9       christos  365:        (void)memcpy(out, o, sizeof(*out));
1.7       elric     366:        out->next = NULL;
                    367:
                    368:        if (outstack) {
                    369:                out->next = outstack;
                    370:                outstack = out;
                    371:        } else {
                    372:                outstack = out;
                    373:        }
                    374: }
                    375:
                    376: static void
                    377: sortoutput(struct output *o)
                    378: {
                    379:        struct  output **outs;
                    380:        struct  output *tmpo;
                    381:        int     num;
                    382:        int     i;
                    383:
                    384:        /* count the number of entries to display */
                    385:        for (num=0, tmpo = o; tmpo; tmpo=tmpo->next, num++)
                    386:                ;
                    387:
                    388:        outs = malloc(sizeof(*outs) * num);
                    389:        if (!outs)
                    390:                err(EXIT_FAILURE, "malloc failed");
                    391:        for (i=0, tmpo = o; i < num; tmpo=tmpo->next, i++)
                    392:                outs[i] = tmpo;
                    393:
                    394:        mergesort(outs, num, sizeof(*outs), comparelog);
                    395:
                    396:        for (i=0; i < num; i++)
                    397:                output(outs[i]);
                    398: }
                    399:
                    400: static int
                    401: comparelog(const void *left, const void *right)
                    402: {
                    403:        struct output *l = *(struct output **)left;
                    404:        struct output *r = *(struct output **)right;
                    405:        int order = (sortlog&SORT_REVERSE)?-1:1;
                    406:
1.9       christos  407:        if (l->o_tv.tv_sec < r->o_tv.tv_sec)
1.7       elric     408:                return 1 * order;
1.9       christos  409:        if (l->o_tv.tv_sec == r->o_tv.tv_sec)
1.7       elric     410:                return 0;
                    411:        return -1 * order;
                    412: }
                    413:
1.9       christos  414: static const char *
                    415: gethost(struct output *o)
                    416: {
                    417:        if (!numeric)
                    418:                return o->o_host;
                    419:        else {
                    420:                static char buf[512];
1.11      christos  421:                buf[0] = '\0';
                    422:                (void)sockaddr_snprintf(buf, sizeof(buf), "%a",
                    423:                    (struct sockaddr *)&o->o_ss);
1.9       christos  424:                return buf;
                    425:        }
                    426: }
                    427:
1.1       phil      428: /* Duplicate the output of last(1) */
                    429: static void
1.7       elric     430: output(struct output *o)
1.1       phil      431: {
1.9       christos  432:        time_t t = (time_t)o->o_tv.tv_sec;
1.1       phil      433:        printf("%-*.*s  %-*.*s %-*.*s   %s",
1.10      he        434:                (int)namelen, (int)namelen, o->o_name,
                    435:                (int)linelen, (int)linelen, o->o_line,
                    436:                (int)hostlen, (int)hostlen, gethost(o),
1.9       christos  437:                t ? ctime(&t) : "Never logged in\n");
1.1       phil      438: }
                    439:
                    440: static void
1.9       christos  441: usage(void)
1.1       phil      442: {
1.9       christos  443:        (void)fprintf(stderr, "Usage: %s [-nrt] [-f <filename>] "
                    444:            "[-H <hostlen>] [-L <linelen>] [-N <namelen>] [user ...]\n",
                    445:            getprogname());
1.1       phil      446:        exit(1);
                    447: }

CVSweb <webmaster@jp.NetBSD.org>