last.c revision 118077
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. 321590Srgrimes */ 331590Srgrimes 341590Srgrimes#ifndef lint 3578201Sddstatic const char copyright[] = 361590Srgrimes"@(#) Copyright (c) 1987, 1993, 1994\n\ 371590Srgrimes The Regents of the University of California. All rights reserved.\n"; 381590Srgrimes#endif /* not lint */ 391590Srgrimes 401590Srgrimes#ifndef lint 4178201Sddstatic const char sccsid[] = "@(#)last.c 8.2 (Berkeley) 4/2/94"; 421590Srgrimes#endif /* not lint */ 4399112Sobrien#include <sys/cdefs.h> 4499112Sobrien__FBSDID("$FreeBSD: head/usr.bin/last/last.c 118077 2003-07-27 10:53:28Z tjr $"); 451590Srgrimes 461590Srgrimes#include <sys/param.h> 471590Srgrimes#include <sys/stat.h> 481590Srgrimes 491590Srgrimes#include <err.h> 50118077Stjr#include <errno.h> 511590Srgrimes#include <fcntl.h> 5274588Sache#include <langinfo.h> 5316438Sache#include <locale.h> 541590Srgrimes#include <paths.h> 551590Srgrimes#include <signal.h> 561590Srgrimes#include <stdio.h> 571590Srgrimes#include <stdlib.h> 581590Srgrimes#include <string.h> 591590Srgrimes#include <time.h> 601590Srgrimes#include <unistd.h> 611590Srgrimes#include <utmp.h> 6211547Sdg#include <sys/queue.h> 631590Srgrimes 641590Srgrimes#define NO 0 /* false/no */ 651590Srgrimes#define YES 1 /* true/yes */ 6677291Sdd#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; 671590Srgrimes 681590Srgrimesstatic struct utmp buf[1024]; /* utmp read buffer */ 691590Srgrimes 701590Srgrimestypedef struct arg { 711590Srgrimes char *name; /* argument */ 721590Srgrimes#define HOST_TYPE -2 731590Srgrimes#define TTY_TYPE -3 741590Srgrimes#define USER_TYPE -4 751590Srgrimes int type; /* type of arg */ 761590Srgrimes struct arg *next; /* linked list pointer */ 771590Srgrimes} ARG; 781590SrgrimesARG *arglist; /* head of linked list */ 791590Srgrimes 8060938SjakeLIST_HEAD(ttylisthead, ttytab) ttylist; 8111547Sdg 8211547Sdgstruct ttytab { 8336062Sjb time_t logout; /* log out time */ 841590Srgrimes char tty[UT_LINESIZE + 1]; /* terminal name */ 8560938Sjake LIST_ENTRY(ttytab) list; 8611547Sdg}; 871590Srgrimes 8891536Siedowsestatic const char *crmsg; /* cause of last reboot */ 891590Srgrimesstatic long currentout, /* current logout value */ 901590Srgrimes maxrec; /* records to display */ 9178201Sddstatic const char *file = _PATH_WTMP; /* wtmp file */ 9236434Sdannystatic int sflag = 0; /* show delta in seconds */ 9336434Sdannystatic int width = 5; /* show seconds in delta */ 9491541Siedowsestatic int yflag; /* show year */ 9574588Sachestatic int d_first; 9691538Siedowsestatic int snapfound = 0; /* found snapshot entry? */ 9777291Sddstatic time_t snaptime; /* if != 0, we will only 9877291Sdd * report users logged in 9977291Sdd * at this snapshot time 10077291Sdd */ 1011590Srgrimes 10292920Simpvoid addarg(int, char *); 10392920Simptime_t dateconv(char *); 10492920Simpvoid doentry(struct utmp *); 10592920Simpvoid hostconv(char *); 10692920Simpvoid onintr(int); 10792920Simpvoid printentry(struct utmp *, struct ttytab *); 10892920Simpchar *ttyconv(char *); 10992920Simpint want(struct utmp *); 11092920Simpvoid usage(void); 11192920Simpvoid wtmp(void); 1121590Srgrimes 11336434Sdannyvoid 11436434Sdannyusage(void) 11536434Sdanny{ 11636434Sdanny (void)fprintf(stderr, 117118077Stjr"usage: last [-y] [-d [[CC]YY][MMDD]hhmm[.SS]] [-f file] [-h host]\n" 118118077Stjr"\t[-n maxrec] [-t tty] [-s|w] [user ...]\n"); 11936434Sdanny exit(1); 12036434Sdanny} 12136434Sdanny 1221590Srgrimesint 123102944Sdwmalonemain(int argc, char *argv[]) 1241590Srgrimes{ 1251590Srgrimes int ch; 1261590Srgrimes char *p; 1271590Srgrimes 12816438Sache (void) setlocale(LC_TIME, ""); 12974588Sache d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 13016438Sache 1311590Srgrimes maxrec = -1; 13277291Sdd snaptime = 0; 133118077Stjr while ((ch = getopt(argc, argv, "0123456789d:f:h:n:st:wy")) != -1) 1341590Srgrimes switch (ch) { 1351590Srgrimes case '0': case '1': case '2': case '3': case '4': 1361590Srgrimes case '5': case '6': case '7': case '8': case '9': 1371590Srgrimes /* 1381590Srgrimes * kludge: last was originally designed to take 1391590Srgrimes * a number after a dash. 1401590Srgrimes */ 1411590Srgrimes if (maxrec == -1) { 142106215Smux p = strchr(argv[optind - 1], ch); 143106215Smux if (p == NULL) 144106215Smux p = strchr(argv[optind], ch); 145106215Smux maxrec = atol(p); 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; 160118077Stjr case 'n': 161118077Stjr errno = 0; 162118077Stjr maxrec = strtol(optarg, &p, 10); 163118077Stjr if (p == optarg || *p != '\0' || errno != 0 || 164118077Stjr maxrec <= 0) 165118077Stjr errx(1, "%s: bad line count", optarg); 166118077Stjr break; 16736434Sdanny case 's': 16836434Sdanny sflag++; /* Show delta as seconds */ 16936434Sdanny break; 1701590Srgrimes case 't': 1711590Srgrimes addarg(TTY_TYPE, ttyconv(optarg)); 1721590Srgrimes break; 17336434Sdanny case 'w': 17436434Sdanny width = 8; 17536434Sdanny break; 17691541Siedowse case 'y': 17791541Siedowse yflag++; 17891541Siedowse break; 1791590Srgrimes case '?': 1801590Srgrimes default: 18136434Sdanny usage(); 1821590Srgrimes } 1831590Srgrimes 18436434Sdanny if (sflag && width == 8) usage(); 18536434Sdanny 1861590Srgrimes if (argc) { 1871590Srgrimes setlinebuf(stdout); 1881590Srgrimes for (argv += optind; *argv; ++argv) { 1891590Srgrimes#define COMPATIBILITY 1901590Srgrimes#ifdef COMPATIBILITY 1911590Srgrimes /* code to allow "last p5" to work */ 1921590Srgrimes addarg(TTY_TYPE, ttyconv(*argv)); 1931590Srgrimes#endif 1941590Srgrimes addarg(USER_TYPE, *argv); 1951590Srgrimes } 1961590Srgrimes } 1971590Srgrimes wtmp(); 1981590Srgrimes exit(0); 1991590Srgrimes} 2001590Srgrimes 2011590Srgrimes/* 2021590Srgrimes * wtmp -- 2031590Srgrimes * read through the wtmp file 2041590Srgrimes */ 2051590Srgrimesvoid 206102944Sdwmalonewtmp(void) 2071590Srgrimes{ 2081590Srgrimes struct utmp *bp; /* current structure */ 2091590Srgrimes struct stat stb; /* stat of file for size */ 21036062Sjb long bl; 2111590Srgrimes int bytes, wfd; 21216438Sache char ct[80]; 21316438Sache struct tm *tm; 21485648Sdillon time_t t; 2151590Srgrimes 21611547Sdg LIST_INIT(&ttylist); 21711547Sdg 2181590Srgrimes if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) 2191590Srgrimes err(1, "%s", file); 2201590Srgrimes bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); 2211590Srgrimes 22285648Sdillon (void)time(&t); 22389572Sdillon buf[0].ut_time = _time_to_int(t); 2241590Srgrimes (void)signal(SIGINT, onintr); 2251590Srgrimes (void)signal(SIGQUIT, onintr); 2261590Srgrimes 2271590Srgrimes while (--bl >= 0) { 2281590Srgrimes if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 || 2291590Srgrimes (bytes = read(wfd, buf, sizeof(buf))) == -1) 2301590Srgrimes err(1, "%s", file); 23191536Siedowse for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) 23291536Siedowse doentry(bp); 2331590Srgrimes } 23489572Sdillon t = _int_to_time(buf[0].ut_time); 23585648Sdillon tm = localtime(&t); 23678201Sdd (void) strftime(ct, sizeof(ct), "\nwtmp begins %+\n", tm); 23762871Skris printf("%s", ct); 2381590Srgrimes} 2391590Srgrimes 2401590Srgrimes/* 24191536Siedowse * doentry -- 24291536Siedowse * process a single wtmp entry 24391536Siedowse */ 24491536Siedowsevoid 245102944Sdwmalonedoentry(struct utmp *bp) 24691536Siedowse{ 24791536Siedowse struct ttytab *tt, *ttx; /* ttylist entry */ 24891536Siedowse 24991536Siedowse /* 25091536Siedowse * if the terminal line is '~', the machine stopped. 25191536Siedowse * see utmp(5) for more info. 25291536Siedowse */ 25391536Siedowse if (bp->ut_line[0] == '~' && !bp->ut_line[1]) { 25491536Siedowse /* everybody just logged out */ 25591536Siedowse for (tt = LIST_FIRST(&ttylist); tt;) { 25691536Siedowse LIST_REMOVE(tt, list); 25791536Siedowse ttx = tt; 25891536Siedowse tt = LIST_NEXT(tt, list); 25991536Siedowse free(ttx); 26091536Siedowse } 26191536Siedowse currentout = -bp->ut_time; 26291536Siedowse crmsg = strncmp(bp->ut_name, "shutdown", UT_NAMESIZE) ? 26391536Siedowse "crash" : "shutdown"; 26491536Siedowse /* 26591536Siedowse * if we're in snapshot mode, we want to exit if this 26691536Siedowse * shutdown/reboot appears while we we are tracking the 26791536Siedowse * active range 26891536Siedowse */ 26991536Siedowse if (snaptime && snapfound) 27091536Siedowse exit(0); 27191536Siedowse /* 27291536Siedowse * don't print shutdown/reboot entries unless flagged for 27391536Siedowse */ 27491538Siedowse if (!snaptime && want(bp)) 27591536Siedowse printentry(bp, NULL); 27691536Siedowse return; 27791536Siedowse } 27891536Siedowse /* 27991536Siedowse * if the line is '{' or '|', date got set; see 28091536Siedowse * utmp(5) for more info. 28191536Siedowse */ 28291536Siedowse if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|') && 28391536Siedowse !bp->ut_line[1]) { 28491538Siedowse if (want(bp) && !snaptime) 28591536Siedowse printentry(bp, NULL); 28691536Siedowse return; 28791536Siedowse } 28891536Siedowse /* find associated tty */ 28991536Siedowse LIST_FOREACH(tt, &ttylist, list) 29091536Siedowse if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE)) 29191536Siedowse break; 29291536Siedowse 29391536Siedowse if (tt == NULL) { 29491536Siedowse /* add new one */ 29591536Siedowse tt = malloc(sizeof(struct ttytab)); 29691536Siedowse if (tt == NULL) 29796785Sjmallett errx(1, "malloc failure"); 29891536Siedowse tt->logout = currentout; 29991536Siedowse strncpy(tt->tty, bp->ut_line, UT_LINESIZE); 30091536Siedowse LIST_INSERT_HEAD(&ttylist, tt, list); 30191536Siedowse } 30291536Siedowse 30391536Siedowse /* 30491536Siedowse * print record if not in snapshot mode and wanted 30591536Siedowse * or in snapshot mode and in snapshot range 30691536Siedowse */ 30791536Siedowse if (bp->ut_name[0] && (want(bp) || (bp->ut_time < snaptime && 30891536Siedowse (tt->logout > snaptime || tt->logout < 1)))) { 30991536Siedowse snapfound = 1; 31091536Siedowse /* 31191536Siedowse * when uucp and ftp log in over a network, the entry in 31291536Siedowse * the utmp file is the name plus their process id. See 31391536Siedowse * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information. 31491536Siedowse */ 31591536Siedowse if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1)) 31691536Siedowse bp->ut_line[3] = '\0'; 31791536Siedowse else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1)) 31891536Siedowse bp->ut_line[4] = '\0'; 31991536Siedowse printentry(bp, tt); 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 332102944Sdwmaloneprintentry(struct utmp *bp, struct ttytab *tt) 33391536Siedowse{ 33491536Siedowse char ct[80]; 33591536Siedowse struct tm *tm; 33691536Siedowse time_t delta; /* time difference */ 33791536Siedowse time_t t; 33891536Siedowse 33991538Siedowse if (maxrec != -1 && !maxrec--) 34091538Siedowse exit(0); 34191536Siedowse t = _int_to_time(bp->ut_time); 34291536Siedowse tm = localtime(&t); 34391541Siedowse (void) strftime(ct, sizeof(ct), d_first ? 34491541Siedowse (yflag ? "%a %e %b %Y %R" : "%a %e %b %R") : 34591541Siedowse (yflag ? "%a %b %e %Y %R" : "%a %b %e %R"), tm); 34691536Siedowse printf("%-*.*s %-*.*s %-*.*s %s%c", 34791536Siedowse UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 34891536Siedowse UT_LINESIZE, UT_LINESIZE, bp->ut_line, 34991536Siedowse UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 35091536Siedowse ct, tt == NULL ? '\n' : ' '); 35191536Siedowse if (tt == NULL) 35291536Siedowse return; 35391536Siedowse if (!tt->logout) { 35491536Siedowse puts(" still logged in"); 35591536Siedowse return; 35691536Siedowse } 35791536Siedowse if (tt->logout < 0) { 35891536Siedowse tt->logout = -tt->logout; 35991536Siedowse printf("- %s", crmsg); 36091536Siedowse } else { 36191536Siedowse tm = localtime(&tt->logout); 36291536Siedowse (void) strftime(ct, sizeof(ct), "%R", tm); 36391536Siedowse printf("- %s", ct); 36491536Siedowse } 36591536Siedowse delta = tt->logout - bp->ut_time; 36691536Siedowse if (sflag) { 36791536Siedowse printf(" (%8ld)\n", (long)delta); 36891536Siedowse } else { 36991536Siedowse tm = gmtime(&delta); 37091536Siedowse (void) strftime(ct, sizeof(ct), width >= 8 ? "%T" : "%R", tm); 37191536Siedowse if (delta < 86400) 37291536Siedowse printf(" (%s)\n", ct); 37391536Siedowse else 37491536Siedowse printf(" (%ld+%s)\n", (long)delta / 86400, ct); 37591536Siedowse } 37691536Siedowse} 37791536Siedowse 37891536Siedowse/* 3791590Srgrimes * want -- 3801590Srgrimes * see if want this entry 3811590Srgrimes */ 3821590Srgrimesint 383102944Sdwmalonewant(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 416102944Sdwmaloneaddarg(int type, char *arg) 4171590Srgrimes{ 4181590Srgrimes ARG *cur; 4191590Srgrimes 42096785Sjmallett if ((cur = malloc(sizeof(ARG))) == NULL) 42196785Sjmallett errx(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 435102944Sdwmalonehostconv(char *arg) 4361590Srgrimes{ 4371590Srgrimes static int first = 1; 4381590Srgrimes static char *hostdot, name[MAXHOSTNAMELEN]; 4391590Srgrimes char *argdot; 4401590Srgrimes 4411590Srgrimes if (!(argdot = strchr(arg, '.'))) 4421590Srgrimes return; 4431590Srgrimes if (first) { 4441590Srgrimes first = 0; 4451590Srgrimes if (gethostname(name, sizeof(name))) 4461590Srgrimes err(1, "gethostname"); 4471590Srgrimes hostdot = strchr(name, '.'); 4481590Srgrimes } 4491590Srgrimes if (hostdot && !strcasecmp(hostdot, argdot)) 4501590Srgrimes *argdot = '\0'; 4511590Srgrimes} 4521590Srgrimes 4531590Srgrimes/* 4541590Srgrimes * ttyconv -- 4551590Srgrimes * convert tty to correct name. 4561590Srgrimes */ 4571590Srgrimeschar * 458102944Sdwmalonettyconv(char *arg) 4591590Srgrimes{ 4601590Srgrimes char *mval; 4611590Srgrimes 4621590Srgrimes /* 4631590Srgrimes * kludge -- we assume that all tty's end with 4641590Srgrimes * a two character suffix. 4651590Srgrimes */ 4661590Srgrimes if (strlen(arg) == 2) { 4671590Srgrimes /* either 6 for "ttyxx" or 8 for "console" */ 46896785Sjmallett if ((mval = malloc(8)) == NULL) 46996785Sjmallett errx(1, "malloc failure"); 4701590Srgrimes if (!strcmp(arg, "co")) 4711590Srgrimes (void)strcpy(mval, "console"); 4721590Srgrimes else { 4731590Srgrimes (void)strcpy(mval, "tty"); 4741590Srgrimes (void)strcpy(mval + 3, arg); 4751590Srgrimes } 4761590Srgrimes return (mval); 4771590Srgrimes } 4781590Srgrimes if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1)) 4791590Srgrimes return (arg + 5); 4801590Srgrimes return (arg); 4811590Srgrimes} 4821590Srgrimes 4831590Srgrimes/* 48477291Sdd * dateconv -- 48577291Sdd * Convert the snapshot time in command line given in the format 48677291Sdd * [[CC]YY]MMDDhhmm[.SS]] to a time_t. 48777291Sdd * Derived from atime_arg1() in usr.bin/touch/touch.c 48877291Sdd */ 48977291Sddtime_t 490102944Sdwmalonedateconv(char *arg) 49177291Sdd{ 49277291Sdd time_t timet; 49377291Sdd struct tm *t; 49477291Sdd int yearset; 49577291Sdd char *p; 49677291Sdd 49777291Sdd /* Start with the current time. */ 49877291Sdd if (time(&timet) < 0) 49977291Sdd err(1, "time"); 50077291Sdd if ((t = localtime(&timet)) == NULL) 50177291Sdd err(1, "localtime"); 50277291Sdd 50377291Sdd /* [[CC]YY]MMDDhhmm[.SS] */ 50477291Sdd if ((p = strchr(arg, '.')) == NULL) 50577291Sdd t->tm_sec = 0; /* Seconds defaults to 0. */ 50677291Sdd else { 50777291Sdd if (strlen(p + 1) != 2) 50877291Sdd goto terr; 50977291Sdd *p++ = '\0'; 51077291Sdd t->tm_sec = ATOI2(p); 51177291Sdd } 51277291Sdd 51377291Sdd yearset = 0; 51477291Sdd switch (strlen(arg)) { 51577291Sdd case 12: /* CCYYMMDDhhmm */ 51677291Sdd t->tm_year = ATOI2(arg); 51777291Sdd t->tm_year *= 100; 51877291Sdd yearset = 1; 51977291Sdd /* FALLTHOUGH */ 52077291Sdd case 10: /* YYMMDDhhmm */ 52177291Sdd if (yearset) { 52277291Sdd yearset = ATOI2(arg); 52377291Sdd t->tm_year += yearset; 52477291Sdd } else { 52577291Sdd yearset = ATOI2(arg); 52677291Sdd if (yearset < 69) 52777291Sdd t->tm_year = yearset + 2000; 52877291Sdd else 52977291Sdd t->tm_year = yearset + 1900; 53077291Sdd } 53177291Sdd t->tm_year -= 1900; /* Convert to UNIX time. */ 53277291Sdd /* FALLTHROUGH */ 53377291Sdd case 8: /* MMDDhhmm */ 53477291Sdd t->tm_mon = ATOI2(arg); 53577291Sdd --t->tm_mon; /* Convert from 01-12 to 00-11 */ 53677291Sdd t->tm_mday = ATOI2(arg); 53777291Sdd t->tm_hour = ATOI2(arg); 53877291Sdd t->tm_min = ATOI2(arg); 53977291Sdd break; 54077291Sdd case 4: /* hhmm */ 54177291Sdd t->tm_hour = ATOI2(arg); 54277291Sdd t->tm_min = ATOI2(arg); 54377291Sdd break; 54477291Sdd default: 54577291Sdd goto terr; 54677291Sdd } 54777291Sdd t->tm_isdst = -1; /* Figure out DST. */ 54877291Sdd timet = mktime(t); 54977291Sdd if (timet == -1) 55077291Sddterr: errx(1, 55177291Sdd "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); 55277291Sdd return timet; 55377291Sdd} 55477291Sdd 55577291Sdd 55677291Sdd/* 5571590Srgrimes * onintr -- 5581590Srgrimes * on interrupt, we inform the user how far we've gotten 5591590Srgrimes */ 5601590Srgrimesvoid 561102944Sdwmaloneonintr(int signo) 5621590Srgrimes{ 56316438Sache char ct[80]; 56416438Sache struct tm *tm; 56589572Sdillon time_t t = _int_to_time(buf[0].ut_time); 5661590Srgrimes 56785648Sdillon tm = localtime(&t); 56874588Sache (void) strftime(ct, sizeof(ct), 56974588Sache d_first ? "%a %e %b %R" : "%a %b %e %R", 57074588Sache tm); 57174588Sache printf("\ninterrupted %s\n", ct); 5721590Srgrimes if (signo == SIGINT) 5731590Srgrimes exit(1); 5741590Srgrimes (void)fflush(stdout); /* fix required for rsh */ 5751590Srgrimes} 576