last.c revision 91536
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 91536 2002-03-01 17:37:06Z iedowse $ 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 8791536Siedowsestatic const char *crmsg; /* cause of last reboot */ 881590Srgrimesstatic long currentout, /* current logout value */ 891590Srgrimes maxrec; /* records to display */ 9078201Sddstatic const char *file = _PATH_WTMP; /* wtmp file */ 9136434Sdannystatic int sflag = 0; /* show delta in seconds */ 9236434Sdannystatic int width = 5; /* show seconds in delta */ 9374588Sachestatic int d_first; 9477291Sddstatic time_t snaptime; /* if != 0, we will only 9577291Sdd * report users logged in 9677291Sdd * at this snapshot time 9777291Sdd */ 981590Srgrimes 991590Srgrimesvoid addarg __P((int, char *)); 10078201Sddtime_t dateconv __P((char *)); 10191536Siedowsevoid doentry __P((struct utmp *)); 1021590Srgrimesvoid hostconv __P((char *)); 1031590Srgrimesvoid onintr __P((int)); 10491536Siedowsevoid printentry __P((struct utmp *, struct ttytab *)); 1051590Srgrimeschar *ttyconv __P((char *)); 10611547Sdgint want __P((struct utmp *)); 10778201Sddvoid usage __P((void)); 1081590Srgrimesvoid wtmp __P((void)); 1091590Srgrimes 11036434Sdannyvoid 11136434Sdannyusage(void) 11236434Sdanny{ 11336434Sdanny (void)fprintf(stderr, 11481161Sdd"usage: last [-#] [-d [[CC]YY][MMDD]hhmm[.SS]] [-f file] [-h hostname]\n" 11581161Sdd"\t[-t tty] [-s|w] [user ...]\n"); 11636434Sdanny exit(1); 11736434Sdanny} 11836434Sdanny 1191590Srgrimesint 1201590Srgrimesmain(argc, argv) 1211590Srgrimes int argc; 1221590Srgrimes char *argv[]; 1231590Srgrimes{ 1241590Srgrimes int ch; 1251590Srgrimes char *p; 1261590Srgrimes 12716438Sache (void) setlocale(LC_TIME, ""); 12874588Sache d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 12916438Sache 1301590Srgrimes maxrec = -1; 13177291Sdd snaptime = 0; 13277291Sdd while ((ch = getopt(argc, argv, "0123456789d:f:h:st:w")) != -1) 1331590Srgrimes switch (ch) { 1341590Srgrimes case '0': case '1': case '2': case '3': case '4': 1351590Srgrimes case '5': case '6': case '7': case '8': case '9': 1361590Srgrimes /* 1371590Srgrimes * kludge: last was originally designed to take 1381590Srgrimes * a number after a dash. 1391590Srgrimes */ 1401590Srgrimes if (maxrec == -1) { 1411590Srgrimes p = argv[optind - 1]; 1421590Srgrimes if (p[0] == '-' && p[1] == ch && !p[2]) 1431590Srgrimes maxrec = atol(++p); 1441590Srgrimes else 1451590Srgrimes maxrec = atol(argv[optind] + 1); 1461590Srgrimes if (!maxrec) 1471590Srgrimes exit(0); 1481590Srgrimes } 1491590Srgrimes break; 15077291Sdd case 'd': 15177291Sdd snaptime = dateconv(optarg); 15277291Sdd break; 1531590Srgrimes case 'f': 1541590Srgrimes file = optarg; 1551590Srgrimes break; 1561590Srgrimes case 'h': 1571590Srgrimes hostconv(optarg); 1581590Srgrimes addarg(HOST_TYPE, optarg); 1591590Srgrimes break; 16036434Sdanny case 's': 16136434Sdanny sflag++; /* Show delta as seconds */ 16236434Sdanny break; 1631590Srgrimes case 't': 1641590Srgrimes addarg(TTY_TYPE, ttyconv(optarg)); 1651590Srgrimes break; 16636434Sdanny case 'w': 16736434Sdanny width = 8; 16836434Sdanny break; 1691590Srgrimes case '?': 1701590Srgrimes default: 17136434Sdanny usage(); 1721590Srgrimes } 1731590Srgrimes 17436434Sdanny if (sflag && width == 8) usage(); 17536434Sdanny 1761590Srgrimes if (argc) { 1771590Srgrimes setlinebuf(stdout); 1781590Srgrimes for (argv += optind; *argv; ++argv) { 1791590Srgrimes#define COMPATIBILITY 1801590Srgrimes#ifdef COMPATIBILITY 1811590Srgrimes /* code to allow "last p5" to work */ 1821590Srgrimes addarg(TTY_TYPE, ttyconv(*argv)); 1831590Srgrimes#endif 1841590Srgrimes addarg(USER_TYPE, *argv); 1851590Srgrimes } 1861590Srgrimes } 1871590Srgrimes wtmp(); 1881590Srgrimes exit(0); 1891590Srgrimes} 1901590Srgrimes 1911590Srgrimes/* 1921590Srgrimes * wtmp -- 1931590Srgrimes * read through the wtmp file 1941590Srgrimes */ 1951590Srgrimesvoid 1961590Srgrimeswtmp() 1971590Srgrimes{ 1981590Srgrimes struct utmp *bp; /* current structure */ 1991590Srgrimes struct stat stb; /* stat of file for size */ 20036062Sjb long bl; 2011590Srgrimes int bytes, wfd; 20216438Sache char ct[80]; 20316438Sache struct tm *tm; 20485648Sdillon time_t t; 2051590Srgrimes 20611547Sdg LIST_INIT(&ttylist); 20711547Sdg 2081590Srgrimes if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) 2091590Srgrimes err(1, "%s", file); 2101590Srgrimes bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); 2111590Srgrimes 21285648Sdillon (void)time(&t); 21389572Sdillon buf[0].ut_time = _time_to_int(t); 2141590Srgrimes (void)signal(SIGINT, onintr); 2151590Srgrimes (void)signal(SIGQUIT, onintr); 2161590Srgrimes 2171590Srgrimes while (--bl >= 0) { 2181590Srgrimes if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 || 2191590Srgrimes (bytes = read(wfd, buf, sizeof(buf))) == -1) 2201590Srgrimes err(1, "%s", file); 22191536Siedowse for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) 22291536Siedowse doentry(bp); 2231590Srgrimes } 22489572Sdillon t = _int_to_time(buf[0].ut_time); 22585648Sdillon tm = localtime(&t); 22678201Sdd (void) strftime(ct, sizeof(ct), "\nwtmp begins %+\n", tm); 22762871Skris printf("%s", ct); 2281590Srgrimes} 2291590Srgrimes 2301590Srgrimes/* 23191536Siedowse * doentry -- 23291536Siedowse * process a single wtmp entry 23391536Siedowse */ 23491536Siedowsevoid 23591536Siedowsedoentry(bp) 23691536Siedowse struct utmp *bp; 23791536Siedowse{ 23891536Siedowse struct ttytab *tt, *ttx; /* ttylist entry */ 23991536Siedowse int snapfound = 0; /* found snapshot entry? */ 24091536Siedowse 24191536Siedowse /* 24291536Siedowse * if the terminal line is '~', the machine stopped. 24391536Siedowse * see utmp(5) for more info. 24491536Siedowse */ 24591536Siedowse if (bp->ut_line[0] == '~' && !bp->ut_line[1]) { 24691536Siedowse /* everybody just logged out */ 24791536Siedowse for (tt = LIST_FIRST(&ttylist); tt;) { 24891536Siedowse LIST_REMOVE(tt, list); 24991536Siedowse ttx = tt; 25091536Siedowse tt = LIST_NEXT(tt, list); 25191536Siedowse free(ttx); 25291536Siedowse } 25391536Siedowse currentout = -bp->ut_time; 25491536Siedowse crmsg = strncmp(bp->ut_name, "shutdown", UT_NAMESIZE) ? 25591536Siedowse "crash" : "shutdown"; 25691536Siedowse /* 25791536Siedowse * if we're in snapshot mode, we want to exit if this 25891536Siedowse * shutdown/reboot appears while we we are tracking the 25991536Siedowse * active range 26091536Siedowse */ 26191536Siedowse if (snaptime && snapfound) 26291536Siedowse exit(0); 26391536Siedowse /* 26491536Siedowse * don't print shutdown/reboot entries unless flagged for 26591536Siedowse */ 26691536Siedowse if (!snaptime && want(bp)) { 26791536Siedowse printentry(bp, NULL); 26891536Siedowse if (maxrec != -1 && !--maxrec) 26991536Siedowse exit(0); 27091536Siedowse } 27191536Siedowse return; 27291536Siedowse } 27391536Siedowse /* 27491536Siedowse * if the line is '{' or '|', date got set; see 27591536Siedowse * utmp(5) for more info. 27691536Siedowse */ 27791536Siedowse if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|') && 27891536Siedowse !bp->ut_line[1]) { 27991536Siedowse if (want(bp) && !snaptime) { 28091536Siedowse printentry(bp, NULL); 28191536Siedowse if (maxrec && !--maxrec) 28291536Siedowse exit(0); 28391536Siedowse } 28491536Siedowse return; 28591536Siedowse } 28691536Siedowse /* find associated tty */ 28791536Siedowse LIST_FOREACH(tt, &ttylist, list) 28891536Siedowse if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE)) 28991536Siedowse break; 29091536Siedowse 29191536Siedowse if (tt == NULL) { 29291536Siedowse /* add new one */ 29391536Siedowse tt = malloc(sizeof(struct ttytab)); 29491536Siedowse if (tt == NULL) 29591536Siedowse err(1, "malloc failure"); 29691536Siedowse tt->logout = currentout; 29791536Siedowse strncpy(tt->tty, bp->ut_line, UT_LINESIZE); 29891536Siedowse LIST_INSERT_HEAD(&ttylist, tt, list); 29991536Siedowse } 30091536Siedowse 30191536Siedowse /* 30291536Siedowse * print record if not in snapshot mode and wanted 30391536Siedowse * or in snapshot mode and in snapshot range 30491536Siedowse */ 30591536Siedowse if (bp->ut_name[0] && (want(bp) || (bp->ut_time < snaptime && 30691536Siedowse (tt->logout > snaptime || tt->logout < 1)))) { 30791536Siedowse snapfound = 1; 30891536Siedowse /* 30991536Siedowse * when uucp and ftp log in over a network, the entry in 31091536Siedowse * the utmp file is the name plus their process id. See 31191536Siedowse * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information. 31291536Siedowse */ 31391536Siedowse if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1)) 31491536Siedowse bp->ut_line[3] = '\0'; 31591536Siedowse else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1)) 31691536Siedowse bp->ut_line[4] = '\0'; 31791536Siedowse printentry(bp, tt); 31891536Siedowse if (maxrec != -1 && !--maxrec) 31991536Siedowse return; 32091536Siedowse } 32191536Siedowse tt->logout = bp->ut_time; 32291536Siedowse} 32391536Siedowse 32491536Siedowse/* 32591536Siedowse * printentry -- 32691536Siedowse * output an entry 32791536Siedowse * 32891536Siedowse * If `tt' is non-NULL, use it and `crmsg' to print the logout time or 32991536Siedowse * logout type (crash/shutdown) as appropriate. 33091536Siedowse */ 33191536Siedowsevoid 33291536Siedowseprintentry(bp, tt) 33391536Siedowse struct utmp *bp; 33491536Siedowse struct ttytab *tt; 33591536Siedowse{ 33691536Siedowse char ct[80]; 33791536Siedowse struct tm *tm; 33891536Siedowse time_t delta; /* time difference */ 33991536Siedowse time_t t; 34091536Siedowse 34191536Siedowse t = _int_to_time(bp->ut_time); 34291536Siedowse tm = localtime(&t); 34391536Siedowse (void) strftime(ct, sizeof(ct), d_first ? "%a %e %b %R" : 34491536Siedowse "%a %b %e %R", tm); 34591536Siedowse printf("%-*.*s %-*.*s %-*.*s %s%c", 34691536Siedowse UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 34791536Siedowse UT_LINESIZE, UT_LINESIZE, bp->ut_line, 34891536Siedowse UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 34991536Siedowse ct, tt == NULL ? '\n' : ' '); 35091536Siedowse if (tt == NULL) 35191536Siedowse return; 35291536Siedowse if (!tt->logout) { 35391536Siedowse puts(" still logged in"); 35491536Siedowse return; 35591536Siedowse } 35691536Siedowse if (tt->logout < 0) { 35791536Siedowse tt->logout = -tt->logout; 35891536Siedowse printf("- %s", crmsg); 35991536Siedowse } else { 36091536Siedowse tm = localtime(&tt->logout); 36191536Siedowse (void) strftime(ct, sizeof(ct), "%R", tm); 36291536Siedowse printf("- %s", ct); 36391536Siedowse } 36491536Siedowse delta = tt->logout - bp->ut_time; 36591536Siedowse if (sflag) { 36691536Siedowse printf(" (%8ld)\n", (long)delta); 36791536Siedowse } else { 36891536Siedowse tm = gmtime(&delta); 36991536Siedowse (void) strftime(ct, sizeof(ct), width >= 8 ? "%T" : "%R", tm); 37091536Siedowse if (delta < 86400) 37191536Siedowse printf(" (%s)\n", ct); 37291536Siedowse else 37391536Siedowse printf(" (%ld+%s)\n", (long)delta / 86400, ct); 37491536Siedowse } 37591536Siedowse} 37691536Siedowse 37791536Siedowse/* 3781590Srgrimes * want -- 3791590Srgrimes * see if want this entry 3801590Srgrimes */ 3811590Srgrimesint 38211547Sdgwant(bp) 3831590Srgrimes struct utmp *bp; 3841590Srgrimes{ 3851590Srgrimes ARG *step; 3861590Srgrimes 38777291Sdd if (snaptime) 38877291Sdd return (NO); 38977291Sdd 3901590Srgrimes if (!arglist) 3911590Srgrimes return (YES); 3921590Srgrimes 3931590Srgrimes for (step = arglist; step; step = step->next) 3941590Srgrimes switch(step->type) { 3951590Srgrimes case HOST_TYPE: 3961590Srgrimes if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE)) 3971590Srgrimes return (YES); 3981590Srgrimes break; 3991590Srgrimes case TTY_TYPE: 4001590Srgrimes if (!strncmp(step->name, bp->ut_line, UT_LINESIZE)) 4011590Srgrimes return (YES); 4021590Srgrimes break; 4031590Srgrimes case USER_TYPE: 4041590Srgrimes if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE)) 4051590Srgrimes return (YES); 4061590Srgrimes break; 40791536Siedowse } 4081590Srgrimes return (NO); 4091590Srgrimes} 4101590Srgrimes 4111590Srgrimes/* 4121590Srgrimes * addarg -- 4131590Srgrimes * add an entry to a linked list of arguments 4141590Srgrimes */ 4151590Srgrimesvoid 4161590Srgrimesaddarg(type, arg) 4171590Srgrimes int type; 4181590Srgrimes char *arg; 4191590Srgrimes{ 4201590Srgrimes ARG *cur; 4211590Srgrimes 4221590Srgrimes if (!(cur = (ARG *)malloc((u_int)sizeof(ARG)))) 4231590Srgrimes err(1, "malloc failure"); 4241590Srgrimes cur->next = arglist; 4251590Srgrimes cur->type = type; 4261590Srgrimes cur->name = arg; 4271590Srgrimes arglist = cur; 4281590Srgrimes} 4291590Srgrimes 4301590Srgrimes/* 4311590Srgrimes * hostconv -- 4321590Srgrimes * convert the hostname to search pattern; if the supplied host name 4331590Srgrimes * has a domain attached that is the same as the current domain, rip 4341590Srgrimes * off the domain suffix since that's what login(1) does. 4351590Srgrimes */ 4361590Srgrimesvoid 4371590Srgrimeshostconv(arg) 4381590Srgrimes char *arg; 4391590Srgrimes{ 4401590Srgrimes static int first = 1; 4411590Srgrimes static char *hostdot, name[MAXHOSTNAMELEN]; 4421590Srgrimes char *argdot; 4431590Srgrimes 4441590Srgrimes if (!(argdot = strchr(arg, '.'))) 4451590Srgrimes return; 4461590Srgrimes if (first) { 4471590Srgrimes first = 0; 4481590Srgrimes if (gethostname(name, sizeof(name))) 4491590Srgrimes err(1, "gethostname"); 4501590Srgrimes hostdot = strchr(name, '.'); 4511590Srgrimes } 4521590Srgrimes if (hostdot && !strcasecmp(hostdot, argdot)) 4531590Srgrimes *argdot = '\0'; 4541590Srgrimes} 4551590Srgrimes 4561590Srgrimes/* 4571590Srgrimes * ttyconv -- 4581590Srgrimes * convert tty to correct name. 4591590Srgrimes */ 4601590Srgrimeschar * 4611590Srgrimesttyconv(arg) 4621590Srgrimes char *arg; 4631590Srgrimes{ 4641590Srgrimes char *mval; 4651590Srgrimes 4661590Srgrimes /* 4671590Srgrimes * kludge -- we assume that all tty's end with 4681590Srgrimes * a two character suffix. 4691590Srgrimes */ 4701590Srgrimes if (strlen(arg) == 2) { 4711590Srgrimes /* either 6 for "ttyxx" or 8 for "console" */ 4721590Srgrimes if (!(mval = malloc((u_int)8))) 4731590Srgrimes err(1, "malloc failure"); 4741590Srgrimes if (!strcmp(arg, "co")) 4751590Srgrimes (void)strcpy(mval, "console"); 4761590Srgrimes else { 4771590Srgrimes (void)strcpy(mval, "tty"); 4781590Srgrimes (void)strcpy(mval + 3, arg); 4791590Srgrimes } 4801590Srgrimes return (mval); 4811590Srgrimes } 4821590Srgrimes if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1)) 4831590Srgrimes return (arg + 5); 4841590Srgrimes return (arg); 4851590Srgrimes} 4861590Srgrimes 4871590Srgrimes/* 48877291Sdd * dateconv -- 48977291Sdd * Convert the snapshot time in command line given in the format 49077291Sdd * [[CC]YY]MMDDhhmm[.SS]] to a time_t. 49177291Sdd * Derived from atime_arg1() in usr.bin/touch/touch.c 49277291Sdd */ 49377291Sddtime_t 49477291Sdddateconv(arg) 49577291Sdd char *arg; 49677291Sdd{ 49777291Sdd time_t timet; 49877291Sdd struct tm *t; 49977291Sdd int yearset; 50077291Sdd char *p; 50177291Sdd 50277291Sdd /* Start with the current time. */ 50377291Sdd if (time(&timet) < 0) 50477291Sdd err(1, "time"); 50577291Sdd if ((t = localtime(&timet)) == NULL) 50677291Sdd err(1, "localtime"); 50777291Sdd 50877291Sdd /* [[CC]YY]MMDDhhmm[.SS] */ 50977291Sdd if ((p = strchr(arg, '.')) == NULL) 51077291Sdd t->tm_sec = 0; /* Seconds defaults to 0. */ 51177291Sdd else { 51277291Sdd if (strlen(p + 1) != 2) 51377291Sdd goto terr; 51477291Sdd *p++ = '\0'; 51577291Sdd t->tm_sec = ATOI2(p); 51677291Sdd } 51777291Sdd 51877291Sdd yearset = 0; 51977291Sdd switch (strlen(arg)) { 52077291Sdd case 12: /* CCYYMMDDhhmm */ 52177291Sdd t->tm_year = ATOI2(arg); 52277291Sdd t->tm_year *= 100; 52377291Sdd yearset = 1; 52477291Sdd /* FALLTHOUGH */ 52577291Sdd case 10: /* YYMMDDhhmm */ 52677291Sdd if (yearset) { 52777291Sdd yearset = ATOI2(arg); 52877291Sdd t->tm_year += yearset; 52977291Sdd } else { 53077291Sdd yearset = ATOI2(arg); 53177291Sdd if (yearset < 69) 53277291Sdd t->tm_year = yearset + 2000; 53377291Sdd else 53477291Sdd t->tm_year = yearset + 1900; 53577291Sdd } 53677291Sdd t->tm_year -= 1900; /* Convert to UNIX time. */ 53777291Sdd /* FALLTHROUGH */ 53877291Sdd case 8: /* MMDDhhmm */ 53977291Sdd t->tm_mon = ATOI2(arg); 54077291Sdd --t->tm_mon; /* Convert from 01-12 to 00-11 */ 54177291Sdd t->tm_mday = ATOI2(arg); 54277291Sdd t->tm_hour = ATOI2(arg); 54377291Sdd t->tm_min = ATOI2(arg); 54477291Sdd break; 54577291Sdd case 4: /* hhmm */ 54677291Sdd t->tm_hour = ATOI2(arg); 54777291Sdd t->tm_min = ATOI2(arg); 54877291Sdd break; 54977291Sdd default: 55077291Sdd goto terr; 55177291Sdd } 55277291Sdd t->tm_isdst = -1; /* Figure out DST. */ 55377291Sdd timet = mktime(t); 55477291Sdd if (timet == -1) 55577291Sddterr: errx(1, 55677291Sdd "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); 55777291Sdd return timet; 55877291Sdd} 55977291Sdd 56077291Sdd 56177291Sdd/* 5621590Srgrimes * onintr -- 5631590Srgrimes * on interrupt, we inform the user how far we've gotten 5641590Srgrimes */ 5651590Srgrimesvoid 5661590Srgrimesonintr(signo) 5671590Srgrimes int signo; 5681590Srgrimes{ 56916438Sache char ct[80]; 57016438Sache struct tm *tm; 57189572Sdillon time_t t = _int_to_time(buf[0].ut_time); 5721590Srgrimes 57385648Sdillon tm = localtime(&t); 57474588Sache (void) strftime(ct, sizeof(ct), 57574588Sache d_first ? "%a %e %b %R" : "%a %b %e %R", 57674588Sache tm); 57774588Sache printf("\ninterrupted %s\n", ct); 5781590Srgrimes if (signo == SIGINT) 5791590Srgrimes exit(1); 5801590Srgrimes (void)fflush(stdout); /* fix required for rsh */ 5811590Srgrimes} 582