last.c revision 85648
11590Srgrimes/* 21590Srgrimes * Copyright (c) 1987, 1993, 1994 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 3. All advertising materials mentioning features or use of this software 141590Srgrimes * must display the following acknowledgement: 151590Srgrimes * This product includes software developed by the University of 161590Srgrimes * California, Berkeley and its contributors. 171590Srgrimes * 4. Neither the name of the University nor the names of its contributors 181590Srgrimes * may be used to endorse or promote products derived from this software 191590Srgrimes * without specific prior written permission. 201590Srgrimes * 211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311590Srgrimes * SUCH DAMAGE. 3260833Sjake * 3360833Sjake * $FreeBSD: head/usr.bin/last/last.c 85648 2001-10-29 00:34:20Z dillon $ 341590Srgrimes */ 351590Srgrimes 361590Srgrimes#ifndef lint 3778201Sddstatic const char copyright[] = 381590Srgrimes"@(#) Copyright (c) 1987, 1993, 1994\n\ 391590Srgrimes The Regents of the University of California. All rights reserved.\n"; 401590Srgrimes#endif /* not lint */ 411590Srgrimes 421590Srgrimes#ifndef lint 4378201Sddstatic const char sccsid[] = "@(#)last.c 8.2 (Berkeley) 4/2/94"; 441590Srgrimes#endif /* not lint */ 451590Srgrimes 461590Srgrimes#include <sys/param.h> 471590Srgrimes#include <sys/stat.h> 481590Srgrimes 491590Srgrimes#include <err.h> 501590Srgrimes#include <fcntl.h> 5174588Sache#include <langinfo.h> 5216438Sache#include <locale.h> 531590Srgrimes#include <paths.h> 541590Srgrimes#include <signal.h> 551590Srgrimes#include <stdio.h> 561590Srgrimes#include <stdlib.h> 571590Srgrimes#include <string.h> 581590Srgrimes#include <time.h> 591590Srgrimes#include <unistd.h> 601590Srgrimes#include <utmp.h> 6111547Sdg#include <sys/queue.h> 621590Srgrimes 631590Srgrimes#define NO 0 /* false/no */ 641590Srgrimes#define YES 1 /* true/yes */ 6577291Sdd#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; 661590Srgrimes 671590Srgrimesstatic struct utmp buf[1024]; /* utmp read buffer */ 681590Srgrimes 691590Srgrimestypedef struct arg { 701590Srgrimes char *name; /* argument */ 711590Srgrimes#define HOST_TYPE -2 721590Srgrimes#define TTY_TYPE -3 731590Srgrimes#define USER_TYPE -4 741590Srgrimes int type; /* type of arg */ 751590Srgrimes struct arg *next; /* linked list pointer */ 761590Srgrimes} ARG; 771590SrgrimesARG *arglist; /* head of linked list */ 781590Srgrimes 7960938SjakeLIST_HEAD(ttylisthead, ttytab) ttylist; 8011547Sdg 8111547Sdgstruct ttytab { 8236062Sjb time_t logout; /* log out time */ 831590Srgrimes char tty[UT_LINESIZE + 1]; /* terminal name */ 8460938Sjake LIST_ENTRY(ttytab) list; 8511547Sdg}; 861590Srgrimes 871590Srgrimesstatic long currentout, /* current logout value */ 881590Srgrimes maxrec; /* records to display */ 8978201Sddstatic const char *file = _PATH_WTMP; /* wtmp file */ 9036434Sdannystatic int sflag = 0; /* show delta in seconds */ 9136434Sdannystatic int width = 5; /* show seconds in delta */ 9274588Sachestatic int d_first; 9377291Sddstatic time_t snaptime; /* if != 0, we will only 9477291Sdd * report users logged in 9577291Sdd * at this snapshot time 9677291Sdd */ 971590Srgrimes 981590Srgrimesvoid addarg __P((int, char *)); 9978201Sddtime_t dateconv __P((char *)); 1001590Srgrimesvoid hostconv __P((char *)); 1011590Srgrimesvoid onintr __P((int)); 1021590Srgrimeschar *ttyconv __P((char *)); 10311547Sdgint want __P((struct utmp *)); 10478201Sddvoid usage __P((void)); 1051590Srgrimesvoid wtmp __P((void)); 1061590Srgrimes 10736434Sdannyvoid 10836434Sdannyusage(void) 10936434Sdanny{ 11036434Sdanny (void)fprintf(stderr, 11181161Sdd"usage: last [-#] [-d [[CC]YY][MMDD]hhmm[.SS]] [-f file] [-h hostname]\n" 11281161Sdd"\t[-t tty] [-s|w] [user ...]\n"); 11336434Sdanny exit(1); 11436434Sdanny} 11536434Sdanny 1161590Srgrimesint 1171590Srgrimesmain(argc, argv) 1181590Srgrimes int argc; 1191590Srgrimes char *argv[]; 1201590Srgrimes{ 1211590Srgrimes int ch; 1221590Srgrimes char *p; 1231590Srgrimes 12416438Sache (void) setlocale(LC_TIME, ""); 12574588Sache d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 12616438Sache 1271590Srgrimes maxrec = -1; 12877291Sdd snaptime = 0; 12977291Sdd while ((ch = getopt(argc, argv, "0123456789d:f:h:st:w")) != -1) 1301590Srgrimes switch (ch) { 1311590Srgrimes case '0': case '1': case '2': case '3': case '4': 1321590Srgrimes case '5': case '6': case '7': case '8': case '9': 1331590Srgrimes /* 1341590Srgrimes * kludge: last was originally designed to take 1351590Srgrimes * a number after a dash. 1361590Srgrimes */ 1371590Srgrimes if (maxrec == -1) { 1381590Srgrimes p = argv[optind - 1]; 1391590Srgrimes if (p[0] == '-' && p[1] == ch && !p[2]) 1401590Srgrimes maxrec = atol(++p); 1411590Srgrimes else 1421590Srgrimes maxrec = atol(argv[optind] + 1); 1431590Srgrimes if (!maxrec) 1441590Srgrimes exit(0); 1451590Srgrimes } 1461590Srgrimes break; 14777291Sdd case 'd': 14877291Sdd snaptime = dateconv(optarg); 14977291Sdd break; 1501590Srgrimes case 'f': 1511590Srgrimes file = optarg; 1521590Srgrimes break; 1531590Srgrimes case 'h': 1541590Srgrimes hostconv(optarg); 1551590Srgrimes addarg(HOST_TYPE, optarg); 1561590Srgrimes break; 15736434Sdanny case 's': 15836434Sdanny sflag++; /* Show delta as seconds */ 15936434Sdanny break; 1601590Srgrimes case 't': 1611590Srgrimes addarg(TTY_TYPE, ttyconv(optarg)); 1621590Srgrimes break; 16336434Sdanny case 'w': 16436434Sdanny width = 8; 16536434Sdanny break; 1661590Srgrimes case '?': 1671590Srgrimes default: 16836434Sdanny usage(); 1691590Srgrimes } 1701590Srgrimes 17136434Sdanny if (sflag && width == 8) usage(); 17236434Sdanny 1731590Srgrimes if (argc) { 1741590Srgrimes setlinebuf(stdout); 1751590Srgrimes for (argv += optind; *argv; ++argv) { 1761590Srgrimes#define COMPATIBILITY 1771590Srgrimes#ifdef COMPATIBILITY 1781590Srgrimes /* code to allow "last p5" to work */ 1791590Srgrimes addarg(TTY_TYPE, ttyconv(*argv)); 1801590Srgrimes#endif 1811590Srgrimes addarg(USER_TYPE, *argv); 1821590Srgrimes } 1831590Srgrimes } 1841590Srgrimes wtmp(); 1851590Srgrimes exit(0); 1861590Srgrimes} 1871590Srgrimes 1881590Srgrimes/* 1891590Srgrimes * wtmp -- 1901590Srgrimes * read through the wtmp file 1911590Srgrimes */ 1921590Srgrimesvoid 1931590Srgrimeswtmp() 1941590Srgrimes{ 1951590Srgrimes struct utmp *bp; /* current structure */ 19619223Sjoerg struct ttytab *tt, *ttx; /* ttylist entry */ 1971590Srgrimes struct stat stb; /* stat of file for size */ 19836062Sjb long bl; 19936062Sjb time_t delta; /* time difference */ 2001590Srgrimes int bytes, wfd; 20178201Sdd const char *crmsg; 20216438Sache char ct[80]; 20316438Sache struct tm *tm; 20477291Sdd int snapfound = 0; /* found snapshot entry? */ 20585648Sdillon time_t t; 2061590Srgrimes 20711547Sdg LIST_INIT(&ttylist); 20811547Sdg 2091590Srgrimes if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) 2101590Srgrimes err(1, "%s", file); 2111590Srgrimes bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); 2121590Srgrimes 21385648Sdillon (void)time(&t); 21485648Sdillon buf[0].ut_time = time_to_int(t); 2151590Srgrimes (void)signal(SIGINT, onintr); 2161590Srgrimes (void)signal(SIGQUIT, onintr); 2171590Srgrimes 2181590Srgrimes while (--bl >= 0) { 2191590Srgrimes if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 || 2201590Srgrimes (bytes = read(wfd, buf, sizeof(buf))) == -1) 2211590Srgrimes err(1, "%s", file); 2221590Srgrimes for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) { 2231590Srgrimes /* 2241590Srgrimes * if the terminal line is '~', the machine stopped. 2251590Srgrimes * see utmp(5) for more info. 2261590Srgrimes */ 2271590Srgrimes if (bp->ut_line[0] == '~' && !bp->ut_line[1]) { 2281590Srgrimes /* everybody just logged out */ 22970467Sphk for (tt = LIST_FIRST(&ttylist); tt;) { 23011547Sdg LIST_REMOVE(tt, list); 23119223Sjoerg ttx = tt; 23270467Sphk tt = LIST_NEXT(tt, list); 23319223Sjoerg free(ttx); 23411547Sdg } 2351590Srgrimes currentout = -bp->ut_time; 2361590Srgrimes crmsg = strncmp(bp->ut_name, "shutdown", 2371590Srgrimes UT_NAMESIZE) ? "crash" : "shutdown"; 23877291Sdd /* 23977291Sdd * if we're in snapshot mode, we want to 24077291Sdd * exit if this shutdown/reboot appears 24177291Sdd * while we we are tracking the active 24277291Sdd * range 24377291Sdd */ 24477291Sdd if (snaptime && snapfound) 24577291Sdd return; 24677291Sdd /* 24777291Sdd * don't print shutdown/reboot entries 24877291Sdd * unless flagged for 24977291Sdd */ 25077291Sdd if (!snaptime && want(bp)) { 25185648Sdillon t = int_to_time(bp->ut_time); 25285648Sdillon tm = localtime(&t); 25374588Sache (void) strftime(ct, sizeof(ct), 25474588Sache d_first ? "%a %e %b %R" : 25574588Sache "%a %b %e %R", 25674588Sache tm); 25774588Sache printf("%-*.*s %-*.*s %-*.*s %s\n", 2581590Srgrimes UT_NAMESIZE, UT_NAMESIZE, 2591590Srgrimes bp->ut_name, UT_LINESIZE, 2601590Srgrimes UT_LINESIZE, bp->ut_line, 2611590Srgrimes UT_HOSTSIZE, UT_HOSTSIZE, 26274588Sache bp->ut_host, ct); 2631590Srgrimes if (maxrec != -1 && !--maxrec) 2641590Srgrimes return; 2651590Srgrimes } 2661590Srgrimes continue; 2671590Srgrimes } 2681590Srgrimes /* 2691590Srgrimes * if the line is '{' or '|', date got set; see 2701590Srgrimes * utmp(5) for more info. 2711590Srgrimes */ 2721590Srgrimes if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|') 2731590Srgrimes && !bp->ut_line[1]) { 27477291Sdd if (want(bp) && !snaptime) { 27585648Sdillon t = int_to_time(bp->ut_time); 27685648Sdillon tm = localtime(&t); 27774588Sache (void) strftime(ct, sizeof(ct), 27874588Sache d_first ? "%a %e %b %R" : 27974588Sache "%a %b %e %R", 28074588Sache tm); 28174588Sache printf("%-*.*s %-*.*s %-*.*s %s\n", 28211547Sdg UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 28311547Sdg UT_LINESIZE, UT_LINESIZE, bp->ut_line, 28411547Sdg UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 28574588Sache ct); 2861590Srgrimes if (maxrec && !--maxrec) 2871590Srgrimes return; 2881590Srgrimes } 2891590Srgrimes continue; 2901590Srgrimes } 29177291Sdd /* find associated tty */ 29277291Sdd LIST_FOREACH(tt, &ttylist, list) 29377291Sdd if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE)) 29477291Sdd break; 29577291Sdd 29677291Sdd if (tt == NULL) { 29777291Sdd /* add new one */ 29877291Sdd tt = malloc(sizeof(struct ttytab)); 29977291Sdd if (tt == NULL) 30077291Sdd err(1, "malloc failure"); 30177291Sdd tt->logout = currentout; 30277291Sdd strncpy(tt->tty, bp->ut_line, UT_LINESIZE); 30377291Sdd LIST_INSERT_HEAD(&ttylist, tt, list); 30477291Sdd } 30577291Sdd 30677291Sdd /* 30777291Sdd * print record if not in snapshot mode and wanted 30877291Sdd * or in snapshot mode and in snapshot range 30977291Sdd */ 31077291Sdd if (bp->ut_name[0] && (want(bp) || 31177291Sdd (bp->ut_time < snaptime && 31277291Sdd (tt->logout > snaptime || tt->logout < 1)))) { 31377291Sdd snapfound = 1; 31477291Sdd /* 31577291Sdd * when uucp and ftp log in over a network, the entry in 31677291Sdd * the utmp file is the name plus their process id. See 31777291Sdd * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information. 31877291Sdd */ 31977291Sdd if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1)) 32077291Sdd bp->ut_line[3] = '\0'; 32177291Sdd else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1)) 32277291Sdd bp->ut_line[4] = '\0'; 32385648Sdillon t = int_to_time(bp->ut_time); 32485648Sdillon tm = localtime(&t); 32577291Sdd (void) strftime(ct, sizeof(ct), 32677291Sdd d_first ? "%a %e %b %R" : 32777291Sdd "%a %b %e %R", 32877291Sdd tm); 32977291Sdd printf("%-*.*s %-*.*s %-*.*s %s ", 33077291Sdd UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 33177291Sdd UT_LINESIZE, UT_LINESIZE, bp->ut_line, 33277291Sdd UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 33377291Sdd ct); 33477291Sdd if (!tt->logout) 33577291Sdd puts(" still logged in"); 33677291Sdd else { 33777291Sdd if (tt->logout < 0) { 33877291Sdd tt->logout = -tt->logout; 33977291Sdd printf("- %s", crmsg); 34077291Sdd } 34111547Sdg else { 34277291Sdd tm = localtime(&tt->logout); 34377291Sdd (void) strftime(ct, sizeof(ct), "%R", tm); 34477291Sdd printf("- %s", ct); 34577291Sdd } 34677291Sdd delta = tt->logout - bp->ut_time; 34777291Sdd if ( sflag ) { 34878201Sdd printf(" (%8ld)\n", 34978201Sdd (long)delta); 35077291Sdd } else { 35177291Sdd tm = gmtime(&delta); 35277291Sdd (void) strftime(ct, sizeof(ct), 35377291Sdd width >= 8 ? "%T" : "%R", 35477291Sdd tm); 35577291Sdd if (delta < 86400) 35674588Sache printf(" (%s)\n", ct); 35777291Sdd else 35874588Sache printf(" (%ld+%s)\n", 35978201Sdd (long)delta / 36078201Sdd 86400, ct); 3611590Srgrimes } 3621590Srgrimes } 36377291Sdd if (maxrec != -1 && !--maxrec) 36477291Sdd return; 3651590Srgrimes } 36677328Sru tt->logout = bp->ut_time; 3671590Srgrimes } 3681590Srgrimes } 36985648Sdillon t = int_to_time(buf[0].ut_time); 37085648Sdillon tm = localtime(&t); 37178201Sdd (void) strftime(ct, sizeof(ct), "\nwtmp begins %+\n", tm); 37262871Skris printf("%s", ct); 3731590Srgrimes} 3741590Srgrimes 3751590Srgrimes/* 3761590Srgrimes * want -- 3771590Srgrimes * see if want this entry 3781590Srgrimes */ 3791590Srgrimesint 38011547Sdgwant(bp) 3811590Srgrimes struct utmp *bp; 3821590Srgrimes{ 3831590Srgrimes ARG *step; 3841590Srgrimes 38577291Sdd if (snaptime) 38677291Sdd return (NO); 38777291Sdd 3881590Srgrimes if (!arglist) 3891590Srgrimes return (YES); 3901590Srgrimes 3911590Srgrimes for (step = arglist; step; step = step->next) 3921590Srgrimes switch(step->type) { 3931590Srgrimes case HOST_TYPE: 3941590Srgrimes if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE)) 3951590Srgrimes return (YES); 3961590Srgrimes break; 3971590Srgrimes case TTY_TYPE: 3981590Srgrimes if (!strncmp(step->name, bp->ut_line, UT_LINESIZE)) 3991590Srgrimes return (YES); 4001590Srgrimes break; 4011590Srgrimes case USER_TYPE: 4021590Srgrimes if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE)) 4031590Srgrimes return (YES); 4041590Srgrimes break; 4051590Srgrimes } 4061590Srgrimes return (NO); 4071590Srgrimes} 4081590Srgrimes 4091590Srgrimes/* 4101590Srgrimes * addarg -- 4111590Srgrimes * add an entry to a linked list of arguments 4121590Srgrimes */ 4131590Srgrimesvoid 4141590Srgrimesaddarg(type, arg) 4151590Srgrimes int type; 4161590Srgrimes char *arg; 4171590Srgrimes{ 4181590Srgrimes ARG *cur; 4191590Srgrimes 4201590Srgrimes if (!(cur = (ARG *)malloc((u_int)sizeof(ARG)))) 4211590Srgrimes err(1, "malloc failure"); 4221590Srgrimes cur->next = arglist; 4231590Srgrimes cur->type = type; 4241590Srgrimes cur->name = arg; 4251590Srgrimes arglist = cur; 4261590Srgrimes} 4271590Srgrimes 4281590Srgrimes/* 4291590Srgrimes * hostconv -- 4301590Srgrimes * convert the hostname to search pattern; if the supplied host name 4311590Srgrimes * has a domain attached that is the same as the current domain, rip 4321590Srgrimes * off the domain suffix since that's what login(1) does. 4331590Srgrimes */ 4341590Srgrimesvoid 4351590Srgrimeshostconv(arg) 4361590Srgrimes char *arg; 4371590Srgrimes{ 4381590Srgrimes static int first = 1; 4391590Srgrimes static char *hostdot, name[MAXHOSTNAMELEN]; 4401590Srgrimes char *argdot; 4411590Srgrimes 4421590Srgrimes if (!(argdot = strchr(arg, '.'))) 4431590Srgrimes return; 4441590Srgrimes if (first) { 4451590Srgrimes first = 0; 4461590Srgrimes if (gethostname(name, sizeof(name))) 4471590Srgrimes err(1, "gethostname"); 4481590Srgrimes hostdot = strchr(name, '.'); 4491590Srgrimes } 4501590Srgrimes if (hostdot && !strcasecmp(hostdot, argdot)) 4511590Srgrimes *argdot = '\0'; 4521590Srgrimes} 4531590Srgrimes 4541590Srgrimes/* 4551590Srgrimes * ttyconv -- 4561590Srgrimes * convert tty to correct name. 4571590Srgrimes */ 4581590Srgrimeschar * 4591590Srgrimesttyconv(arg) 4601590Srgrimes char *arg; 4611590Srgrimes{ 4621590Srgrimes char *mval; 4631590Srgrimes 4641590Srgrimes /* 4651590Srgrimes * kludge -- we assume that all tty's end with 4661590Srgrimes * a two character suffix. 4671590Srgrimes */ 4681590Srgrimes if (strlen(arg) == 2) { 4691590Srgrimes /* either 6 for "ttyxx" or 8 for "console" */ 4701590Srgrimes if (!(mval = malloc((u_int)8))) 4711590Srgrimes err(1, "malloc failure"); 4721590Srgrimes if (!strcmp(arg, "co")) 4731590Srgrimes (void)strcpy(mval, "console"); 4741590Srgrimes else { 4751590Srgrimes (void)strcpy(mval, "tty"); 4761590Srgrimes (void)strcpy(mval + 3, arg); 4771590Srgrimes } 4781590Srgrimes return (mval); 4791590Srgrimes } 4801590Srgrimes if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1)) 4811590Srgrimes return (arg + 5); 4821590Srgrimes return (arg); 4831590Srgrimes} 4841590Srgrimes 4851590Srgrimes/* 48677291Sdd * dateconv -- 48777291Sdd * Convert the snapshot time in command line given in the format 48877291Sdd * [[CC]YY]MMDDhhmm[.SS]] to a time_t. 48977291Sdd * Derived from atime_arg1() in usr.bin/touch/touch.c 49077291Sdd */ 49177291Sddtime_t 49277291Sdddateconv(arg) 49377291Sdd char *arg; 49477291Sdd{ 49577291Sdd time_t timet; 49677291Sdd struct tm *t; 49777291Sdd int yearset; 49877291Sdd char *p; 49977291Sdd 50077291Sdd /* Start with the current time. */ 50177291Sdd if (time(&timet) < 0) 50277291Sdd err(1, "time"); 50377291Sdd if ((t = localtime(&timet)) == NULL) 50477291Sdd err(1, "localtime"); 50577291Sdd 50677291Sdd /* [[CC]YY]MMDDhhmm[.SS] */ 50777291Sdd if ((p = strchr(arg, '.')) == NULL) 50877291Sdd t->tm_sec = 0; /* Seconds defaults to 0. */ 50977291Sdd else { 51077291Sdd if (strlen(p + 1) != 2) 51177291Sdd goto terr; 51277291Sdd *p++ = '\0'; 51377291Sdd t->tm_sec = ATOI2(p); 51477291Sdd } 51577291Sdd 51677291Sdd yearset = 0; 51777291Sdd switch (strlen(arg)) { 51877291Sdd case 12: /* CCYYMMDDhhmm */ 51977291Sdd t->tm_year = ATOI2(arg); 52077291Sdd t->tm_year *= 100; 52177291Sdd yearset = 1; 52277291Sdd /* FALLTHOUGH */ 52377291Sdd case 10: /* YYMMDDhhmm */ 52477291Sdd if (yearset) { 52577291Sdd yearset = ATOI2(arg); 52677291Sdd t->tm_year += yearset; 52777291Sdd } else { 52877291Sdd yearset = ATOI2(arg); 52977291Sdd if (yearset < 69) 53077291Sdd t->tm_year = yearset + 2000; 53177291Sdd else 53277291Sdd t->tm_year = yearset + 1900; 53377291Sdd } 53477291Sdd t->tm_year -= 1900; /* Convert to UNIX time. */ 53577291Sdd /* FALLTHROUGH */ 53677291Sdd case 8: /* MMDDhhmm */ 53777291Sdd t->tm_mon = ATOI2(arg); 53877291Sdd --t->tm_mon; /* Convert from 01-12 to 00-11 */ 53977291Sdd t->tm_mday = ATOI2(arg); 54077291Sdd t->tm_hour = ATOI2(arg); 54177291Sdd t->tm_min = ATOI2(arg); 54277291Sdd break; 54377291Sdd case 4: /* hhmm */ 54477291Sdd t->tm_hour = ATOI2(arg); 54577291Sdd t->tm_min = ATOI2(arg); 54677291Sdd break; 54777291Sdd default: 54877291Sdd goto terr; 54977291Sdd } 55077291Sdd t->tm_isdst = -1; /* Figure out DST. */ 55177291Sdd timet = mktime(t); 55277291Sdd if (timet == -1) 55377291Sddterr: errx(1, 55477291Sdd "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); 55577291Sdd return timet; 55677291Sdd} 55777291Sdd 55877291Sdd 55977291Sdd/* 5601590Srgrimes * onintr -- 5611590Srgrimes * on interrupt, we inform the user how far we've gotten 5621590Srgrimes */ 5631590Srgrimesvoid 5641590Srgrimesonintr(signo) 5651590Srgrimes int signo; 5661590Srgrimes{ 56716438Sache char ct[80]; 56816438Sache struct tm *tm; 56985648Sdillon time_t t = int_to_time(buf[0].ut_time); 5701590Srgrimes 57185648Sdillon tm = localtime(&t); 57274588Sache (void) strftime(ct, sizeof(ct), 57374588Sache d_first ? "%a %e %b %R" : "%a %b %e %R", 57474588Sache tm); 57574588Sache printf("\ninterrupted %s\n", ct); 5761590Srgrimes if (signo == SIGINT) 5771590Srgrimes exit(1); 5781590Srgrimes (void)fflush(stdout); /* fix required for rsh */ 5791590Srgrimes} 580