last.c revision 99112
1235537Sgber/* 2235537Sgber * Copyright (c) 1987, 1993, 1994 3235537Sgber * The Regents of the University of California. All rights reserved. 4235537Sgber * 5235537Sgber * Redistribution and use in source and binary forms, with or without 6235537Sgber * modification, are permitted provided that the following conditions 7235537Sgber * are met: 8235537Sgber * 1. Redistributions of source code must retain the above copyright 9235537Sgber * notice, this list of conditions and the following disclaimer. 10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright 11235537Sgber * notice, this list of conditions and the following disclaimer in the 12235537Sgber * documentation and/or other materials provided with the distribution. 13235537Sgber * 3. All advertising materials mentioning features or use of this software 14235537Sgber * must display the following acknowledgement: 15235537Sgber * This product includes software developed by the University of 16235537Sgber * California, Berkeley and its contributors. 17235537Sgber * 4. Neither the name of the University nor the names of its contributors 18235537Sgber * may be used to endorse or promote products derived from this software 19235537Sgber * without specific prior written permission. 20235537Sgber * 21235537Sgber * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24235537Sgber * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31235537Sgber * SUCH DAMAGE. 32235537Sgber */ 33235537Sgber 34235537Sgber#ifndef lint 35235537Sgberstatic const char copyright[] = 36235537Sgber"@(#) Copyright (c) 1987, 1993, 1994\n\ 37235537Sgber The Regents of the University of California. All rights reserved.\n"; 38235537Sgber#endif /* not lint */ 39235537Sgber 40235537Sgber#ifndef lint 41235537Sgberstatic const char sccsid[] = "@(#)last.c 8.2 (Berkeley) 4/2/94"; 42235537Sgber#endif /* not lint */ 43235537Sgber#include <sys/cdefs.h> 44235537Sgber__FBSDID("$FreeBSD: head/usr.bin/last/last.c 99112 2002-06-30 05:25:07Z obrien $"); 45235537Sgber 46235537Sgber#include <sys/param.h> 47235537Sgber#include <sys/stat.h> 48235537Sgber 49235537Sgber#include <err.h> 50235537Sgber#include <fcntl.h> 51235537Sgber#include <langinfo.h> 52235537Sgber#include <locale.h> 53235537Sgber#include <paths.h> 54235537Sgber#include <signal.h> 55235537Sgber#include <stdio.h> 56235537Sgber#include <stdlib.h> 57235537Sgber#include <string.h> 58235537Sgber#include <time.h> 59235537Sgber#include <unistd.h> 60235537Sgber#include <utmp.h> 61235537Sgber#include <sys/queue.h> 62235537Sgber 63235537Sgber#define NO 0 /* false/no */ 64235537Sgber#define YES 1 /* true/yes */ 65235537Sgber#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; 66235537Sgber 67235537Sgberstatic struct utmp buf[1024]; /* utmp read buffer */ 68235537Sgber 69235537Sgbertypedef struct arg { 70235537Sgber char *name; /* argument */ 71235537Sgber#define HOST_TYPE -2 72235537Sgber#define TTY_TYPE -3 73235537Sgber#define USER_TYPE -4 74235537Sgber int type; /* type of arg */ 75235537Sgber struct arg *next; /* linked list pointer */ 76235537Sgber} ARG; 77235537SgberARG *arglist; /* head of linked list */ 78235537Sgber 79235537SgberLIST_HEAD(ttylisthead, ttytab) ttylist; 80235537Sgber 81235537Sgberstruct ttytab { 82235537Sgber time_t logout; /* log out time */ 83235537Sgber char tty[UT_LINESIZE + 1]; /* terminal name */ 84235537Sgber LIST_ENTRY(ttytab) list; 85235537Sgber}; 86235537Sgber 87235537Sgberstatic const char *crmsg; /* cause of last reboot */ 88235537Sgberstatic long currentout, /* current logout value */ 89235537Sgber maxrec; /* records to display */ 90235537Sgberstatic const char *file = _PATH_WTMP; /* wtmp file */ 91235537Sgberstatic int sflag = 0; /* show delta in seconds */ 92235537Sgberstatic int width = 5; /* show seconds in delta */ 93235537Sgberstatic int yflag; /* show year */ 94235537Sgberstatic int d_first; 95235537Sgberstatic int snapfound = 0; /* found snapshot entry? */ 96235537Sgberstatic time_t snaptime; /* if != 0, we will only 97235537Sgber * report users logged in 98235537Sgber * at this snapshot time 99235537Sgber */ 100235537Sgber 101235537Sgbervoid addarg(int, char *); 102235537Sgbertime_t dateconv(char *); 103235537Sgbervoid doentry(struct utmp *); 104235537Sgbervoid hostconv(char *); 105235537Sgbervoid onintr(int); 106235537Sgbervoid printentry(struct utmp *, struct ttytab *); 107235537Sgberchar *ttyconv(char *); 108235537Sgberint want(struct utmp *); 109235537Sgbervoid usage(void); 110235537Sgbervoid wtmp(void); 111235537Sgber 112235537Sgbervoid 113235537Sgberusage(void) 114235537Sgber{ 115235537Sgber (void)fprintf(stderr, 116235537Sgber"usage: last [-#] [-y] [-d [[CC]YY][MMDD]hhmm[.SS]] [-f file] [-h host]\n" 117235537Sgber"\t[-t tty] [-s|w] [user ...]\n"); 118235537Sgber exit(1); 119235537Sgber} 120235537Sgber 121235537Sgberint 122235537Sgbermain(argc, argv) 123235537Sgber int argc; 124235537Sgber char *argv[]; 125235537Sgber{ 126235537Sgber int ch; 127235537Sgber char *p; 128235537Sgber 129235537Sgber (void) setlocale(LC_TIME, ""); 130235537Sgber d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 131235537Sgber 132235537Sgber maxrec = -1; 133235537Sgber snaptime = 0; 134235537Sgber while ((ch = getopt(argc, argv, "0123456789d:f:h:st:wy")) != -1) 135235537Sgber switch (ch) { 136235537Sgber case '0': case '1': case '2': case '3': case '4': 137235537Sgber case '5': case '6': case '7': case '8': case '9': 138235537Sgber /* 139235537Sgber * kludge: last was originally designed to take 140235537Sgber * a number after a dash. 141235537Sgber */ 142235537Sgber if (maxrec == -1) { 143235537Sgber p = argv[optind - 1]; 144235537Sgber if (p[0] == '-' && p[1] == ch && !p[2]) 145235537Sgber maxrec = atol(++p); 146235537Sgber else 147235537Sgber maxrec = atol(argv[optind] + 1); 148235537Sgber if (!maxrec) 149235537Sgber exit(0); 150235537Sgber } 151235537Sgber break; 152235537Sgber case 'd': 153235537Sgber snaptime = dateconv(optarg); 154235537Sgber break; 155235537Sgber case 'f': 156235537Sgber file = optarg; 157235537Sgber break; 158235537Sgber case 'h': 159235537Sgber hostconv(optarg); 160235537Sgber addarg(HOST_TYPE, optarg); 161235537Sgber break; 162235537Sgber case 's': 163235537Sgber sflag++; /* Show delta as seconds */ 164235537Sgber break; 165235537Sgber case 't': 166235537Sgber addarg(TTY_TYPE, ttyconv(optarg)); 167235537Sgber break; 168235537Sgber case 'w': 169235537Sgber width = 8; 170235537Sgber break; 171235537Sgber case 'y': 172235537Sgber yflag++; 173235537Sgber break; 174235537Sgber case '?': 175235537Sgber default: 176235537Sgber usage(); 177262586Sbrueffer } 178235537Sgber 179235537Sgber if (sflag && width == 8) usage(); 180235537Sgber 181235537Sgber if (argc) { 182235537Sgber setlinebuf(stdout); 183235537Sgber for (argv += optind; *argv; ++argv) { 184235537Sgber#define COMPATIBILITY 185235537Sgber#ifdef COMPATIBILITY 186235537Sgber /* code to allow "last p5" to work */ 187235537Sgber addarg(TTY_TYPE, ttyconv(*argv)); 188235537Sgber#endif 189235537Sgber addarg(USER_TYPE, *argv); 190235537Sgber } 191235537Sgber } 192235537Sgber wtmp(); 193235537Sgber exit(0); 194235537Sgber} 195235537Sgber 196235537Sgber/* 197235537Sgber * wtmp -- 198259372Sian * read through the wtmp file 199259372Sian */ 200259372Sianvoid 201259372Sianwtmp() 202259372Sian{ 203259372Sian struct utmp *bp; /* current structure */ 204259372Sian struct stat stb; /* stat of file for size */ 205259372Sian long bl; 206259372Sian int bytes, wfd; 207259372Sian char ct[80]; 208259372Sian struct tm *tm; 209259372Sian time_t t; 210259372Sian 211259372Sian LIST_INIT(&ttylist); 212259372Sian 213259372Sian if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) 214259372Sian err(1, "%s", file); 215259372Sian bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); 216259372Sian 217259372Sian (void)time(&t); 218259372Sian buf[0].ut_time = _time_to_int(t); 219235537Sgber (void)signal(SIGINT, onintr); 220235537Sgber (void)signal(SIGQUIT, onintr); 221235537Sgber 222235537Sgber while (--bl >= 0) { 223235537Sgber if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 || 224235537Sgber (bytes = read(wfd, buf, sizeof(buf))) == -1) 225235537Sgber err(1, "%s", file); 226235537Sgber for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) 227235537Sgber doentry(bp); 228235537Sgber } 229235537Sgber t = _int_to_time(buf[0].ut_time); 230235537Sgber tm = localtime(&t); 231235537Sgber (void) strftime(ct, sizeof(ct), "\nwtmp begins %+\n", tm); 232235537Sgber printf("%s", ct); 233235537Sgber} 234235537Sgber 235235537Sgber/* 236235537Sgber * doentry -- 237235537Sgber * process a single wtmp entry 238235537Sgber */ 239235537Sgbervoid 240235537Sgberdoentry(bp) 241235537Sgber struct utmp *bp; 242235537Sgber{ 243235537Sgber struct ttytab *tt, *ttx; /* ttylist entry */ 244235537Sgber 245235537Sgber /* 246235537Sgber * if the terminal line is '~', the machine stopped. 247235537Sgber * see utmp(5) for more info. 248235537Sgber */ 249235537Sgber if (bp->ut_line[0] == '~' && !bp->ut_line[1]) { 250235537Sgber /* everybody just logged out */ 251235537Sgber for (tt = LIST_FIRST(&ttylist); tt;) { 252235537Sgber LIST_REMOVE(tt, list); 253235537Sgber ttx = tt; 254235537Sgber tt = LIST_NEXT(tt, list); 255235537Sgber free(ttx); 256235537Sgber } 257235537Sgber currentout = -bp->ut_time; 258235537Sgber crmsg = strncmp(bp->ut_name, "shutdown", UT_NAMESIZE) ? 259235537Sgber "crash" : "shutdown"; 260235537Sgber /* 261235537Sgber * if we're in snapshot mode, we want to exit if this 262235537Sgber * shutdown/reboot appears while we we are tracking the 263235537Sgber * active range 264235537Sgber */ 265235537Sgber if (snaptime && snapfound) 266235537Sgber exit(0); 267235537Sgber /* 268235537Sgber * don't print shutdown/reboot entries unless flagged for 269235537Sgber */ 270235537Sgber if (!snaptime && want(bp)) 271235537Sgber printentry(bp, NULL); 272235537Sgber return; 273235537Sgber } 274235537Sgber /* 275235537Sgber * if the line is '{' or '|', date got set; see 276235537Sgber * utmp(5) for more info. 277235537Sgber */ 278235537Sgber if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|') && 279235537Sgber !bp->ut_line[1]) { 280235537Sgber if (want(bp) && !snaptime) 281235537Sgber printentry(bp, NULL); 282235537Sgber return; 283235537Sgber } 284235537Sgber /* find associated tty */ 285235537Sgber LIST_FOREACH(tt, &ttylist, list) 286235537Sgber if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE)) 287235537Sgber break; 288235537Sgber 289235537Sgber if (tt == NULL) { 290235537Sgber /* add new one */ 291235537Sgber tt = malloc(sizeof(struct ttytab)); 292235537Sgber if (tt == NULL) 293235537Sgber errx(1, "malloc failure"); 294235537Sgber tt->logout = currentout; 295235537Sgber strncpy(tt->tty, bp->ut_line, UT_LINESIZE); 296235537Sgber LIST_INSERT_HEAD(&ttylist, tt, list); 297235537Sgber } 298235537Sgber 299235537Sgber /* 300235537Sgber * print record if not in snapshot mode and wanted 301235537Sgber * or in snapshot mode and in snapshot range 302235537Sgber */ 303235537Sgber if (bp->ut_name[0] && (want(bp) || (bp->ut_time < snaptime && 304235537Sgber (tt->logout > snaptime || tt->logout < 1)))) { 305235537Sgber snapfound = 1; 306235537Sgber /* 307235537Sgber * when uucp and ftp log in over a network, the entry in 308235537Sgber * the utmp file is the name plus their process id. See 309235537Sgber * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information. 310235537Sgber */ 311235537Sgber if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1)) 312235537Sgber bp->ut_line[3] = '\0'; 313235537Sgber else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1)) 314235537Sgber bp->ut_line[4] = '\0'; 315235537Sgber printentry(bp, tt); 316235537Sgber } 317235537Sgber tt->logout = bp->ut_time; 318235537Sgber} 319235537Sgber 320235537Sgber/* 321235537Sgber * printentry -- 322235537Sgber * output an entry 323235537Sgber * 324235537Sgber * If `tt' is non-NULL, use it and `crmsg' to print the logout time or 325235537Sgber * logout type (crash/shutdown) as appropriate. 326235537Sgber */ 327235537Sgbervoid 328235537Sgberprintentry(bp, tt) 329235537Sgber struct utmp *bp; 330235537Sgber struct ttytab *tt; 331235537Sgber{ 332235537Sgber char ct[80]; 333235537Sgber struct tm *tm; 334235537Sgber time_t delta; /* time difference */ 335235537Sgber time_t t; 336235537Sgber 337235537Sgber if (maxrec != -1 && !maxrec--) 338235537Sgber exit(0); 339235537Sgber t = _int_to_time(bp->ut_time); 340235537Sgber tm = localtime(&t); 341235537Sgber (void) strftime(ct, sizeof(ct), d_first ? 342235537Sgber (yflag ? "%a %e %b %Y %R" : "%a %e %b %R") : 343235537Sgber (yflag ? "%a %b %e %Y %R" : "%a %b %e %R"), tm); 344251022Sgber printf("%-*.*s %-*.*s %-*.*s %s%c", 345235537Sgber UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, 346235537Sgber UT_LINESIZE, UT_LINESIZE, bp->ut_line, 347235537Sgber UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, 348235537Sgber ct, tt == NULL ? '\n' : ' '); 349235537Sgber if (tt == NULL) 350235537Sgber return; 351235537Sgber if (!tt->logout) { 352235537Sgber puts(" still logged in"); 353235537Sgber return; 354235537Sgber } 355235537Sgber if (tt->logout < 0) { 356235537Sgber tt->logout = -tt->logout; 357235537Sgber printf("- %s", crmsg); 358235537Sgber } else { 359235537Sgber tm = localtime(&tt->logout); 360235537Sgber (void) strftime(ct, sizeof(ct), "%R", tm); 361235537Sgber printf("- %s", ct); 362235537Sgber } 363235537Sgber delta = tt->logout - bp->ut_time; 364235537Sgber if (sflag) { 365235537Sgber printf(" (%8ld)\n", (long)delta); 366235537Sgber } else { 367235537Sgber tm = gmtime(&delta); 368235537Sgber (void) strftime(ct, sizeof(ct), width >= 8 ? "%T" : "%R", tm); 369235537Sgber if (delta < 86400) 370235537Sgber printf(" (%s)\n", ct); 371235537Sgber else 372235537Sgber printf(" (%ld+%s)\n", (long)delta / 86400, ct); 373235537Sgber } 374235537Sgber} 375235537Sgber 376235537Sgber/* 377235537Sgber * want -- 378235537Sgber * see if want this entry 379235537Sgber */ 380235537Sgberint 381235537Sgberwant(bp) 382235537Sgber struct utmp *bp; 383235537Sgber{ 384235537Sgber ARG *step; 385235537Sgber 386235537Sgber if (snaptime) 387235537Sgber return (NO); 388235537Sgber 389235537Sgber if (!arglist) 390235537Sgber return (YES); 391235537Sgber 392235537Sgber for (step = arglist; step; step = step->next) 393235537Sgber switch(step->type) { 394235537Sgber case HOST_TYPE: 395235537Sgber if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE)) 396235537Sgber return (YES); 397235537Sgber break; 398235537Sgber case TTY_TYPE: 399235537Sgber if (!strncmp(step->name, bp->ut_line, UT_LINESIZE)) 400235537Sgber return (YES); 401235537Sgber break; 402235537Sgber case USER_TYPE: 403235537Sgber if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE)) 404235537Sgber return (YES); 405235537Sgber break; 406235537Sgber } 407235537Sgber return (NO); 408235537Sgber} 409235537Sgber 410235537Sgber/* 411235537Sgber * addarg -- 412235537Sgber * add an entry to a linked list of arguments 413235537Sgber */ 414235537Sgbervoid 415235537Sgberaddarg(type, arg) 416235537Sgber int type; 417235537Sgber char *arg; 418235537Sgber{ 419235537Sgber ARG *cur; 420235537Sgber 421235537Sgber if ((cur = malloc(sizeof(ARG))) == NULL) 422235537Sgber errx(1, "malloc failure"); 423235537Sgber cur->next = arglist; 424235537Sgber cur->type = type; 425235537Sgber cur->name = arg; 426235537Sgber arglist = cur; 427235537Sgber} 428235537Sgber 429235537Sgber/* 430235537Sgber * hostconv -- 431235537Sgber * convert the hostname to search pattern; if the supplied host name 432235537Sgber * has a domain attached that is the same as the current domain, rip 433235537Sgber * off the domain suffix since that's what login(1) does. 434235537Sgber */ 435235537Sgbervoid 436235537Sgberhostconv(arg) 437235537Sgber char *arg; 438235537Sgber{ 439235537Sgber static int first = 1; 440235537Sgber static char *hostdot, name[MAXHOSTNAMELEN]; 441235537Sgber char *argdot; 442235537Sgber 443235537Sgber if (!(argdot = strchr(arg, '.'))) 444235537Sgber return; 445235537Sgber if (first) { 446235537Sgber first = 0; 447235537Sgber if (gethostname(name, sizeof(name))) 448235537Sgber err(1, "gethostname"); 449235537Sgber hostdot = strchr(name, '.'); 450235537Sgber } 451235537Sgber if (hostdot && !strcasecmp(hostdot, argdot)) 452235537Sgber *argdot = '\0'; 453235537Sgber} 454235537Sgber 455235537Sgber/* 456235537Sgber * ttyconv -- 457235537Sgber * convert tty to correct name. 458235537Sgber */ 459235537Sgberchar * 460235537Sgberttyconv(arg) 461235537Sgber char *arg; 462235537Sgber{ 463235537Sgber char *mval; 464235537Sgber 465235537Sgber /* 466235537Sgber * kludge -- we assume that all tty's end with 467235537Sgber * a two character suffix. 468235537Sgber */ 469235537Sgber if (strlen(arg) == 2) { 470235537Sgber /* either 6 for "ttyxx" or 8 for "console" */ 471235537Sgber if ((mval = malloc(8)) == NULL) 472235537Sgber errx(1, "malloc failure"); 473235537Sgber if (!strcmp(arg, "co")) 474235537Sgber (void)strcpy(mval, "console"); 475235537Sgber else { 476235537Sgber (void)strcpy(mval, "tty"); 477235537Sgber (void)strcpy(mval + 3, arg); 478235537Sgber } 479235537Sgber return (mval); 480235537Sgber } 481235537Sgber if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1)) 482235537Sgber return (arg + 5); 483235537Sgber return (arg); 484235537Sgber} 485235537Sgber 486235537Sgber/* 487235537Sgber * dateconv -- 488235537Sgber * Convert the snapshot time in command line given in the format 489235537Sgber * [[CC]YY]MMDDhhmm[.SS]] to a time_t. 490235537Sgber * Derived from atime_arg1() in usr.bin/touch/touch.c 491235537Sgber */ 492235537Sgbertime_t 493235537Sgberdateconv(arg) 494235537Sgber char *arg; 495235537Sgber{ 496235537Sgber time_t timet; 497235537Sgber struct tm *t; 498235537Sgber int yearset; 499235537Sgber char *p; 500235537Sgber 501235537Sgber /* Start with the current time. */ 502235537Sgber if (time(&timet) < 0) 503235537Sgber err(1, "time"); 504235537Sgber if ((t = localtime(&timet)) == NULL) 505235537Sgber err(1, "localtime"); 506266065Sian 507235537Sgber /* [[CC]YY]MMDDhhmm[.SS] */ 508235537Sgber if ((p = strchr(arg, '.')) == NULL) 509235537Sgber t->tm_sec = 0; /* Seconds defaults to 0. */ 510235537Sgber else { 511235537Sgber if (strlen(p + 1) != 2) 512235537Sgber goto terr; 513235537Sgber *p++ = '\0'; 514235537Sgber t->tm_sec = ATOI2(p); 515235537Sgber } 516235537Sgber 517235537Sgber yearset = 0; 518235537Sgber switch (strlen(arg)) { 519235537Sgber case 12: /* CCYYMMDDhhmm */ 520235537Sgber t->tm_year = ATOI2(arg); 521235537Sgber t->tm_year *= 100; 522235537Sgber yearset = 1; 523235537Sgber /* FALLTHOUGH */ 524235537Sgber case 10: /* YYMMDDhhmm */ 525235537Sgber if (yearset) { 526235537Sgber yearset = ATOI2(arg); 527235537Sgber t->tm_year += yearset; 528235537Sgber } else { 529235537Sgber yearset = ATOI2(arg); 530235537Sgber if (yearset < 69) 531235537Sgber t->tm_year = yearset + 2000; 532235537Sgber else 533235537Sgber t->tm_year = yearset + 1900; 534235537Sgber } 535235537Sgber t->tm_year -= 1900; /* Convert to UNIX time. */ 536235537Sgber /* FALLTHROUGH */ 537235537Sgber case 8: /* MMDDhhmm */ 538235537Sgber t->tm_mon = ATOI2(arg); 539235537Sgber --t->tm_mon; /* Convert from 01-12 to 00-11 */ 540235537Sgber t->tm_mday = ATOI2(arg); 541 t->tm_hour = ATOI2(arg); 542 t->tm_min = ATOI2(arg); 543 break; 544 case 4: /* hhmm */ 545 t->tm_hour = ATOI2(arg); 546 t->tm_min = ATOI2(arg); 547 break; 548 default: 549 goto terr; 550 } 551 t->tm_isdst = -1; /* Figure out DST. */ 552 timet = mktime(t); 553 if (timet == -1) 554terr: errx(1, 555 "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); 556 return timet; 557} 558 559 560/* 561 * onintr -- 562 * on interrupt, we inform the user how far we've gotten 563 */ 564void 565onintr(signo) 566 int signo; 567{ 568 char ct[80]; 569 struct tm *tm; 570 time_t t = _int_to_time(buf[0].ut_time); 571 572 tm = localtime(&t); 573 (void) strftime(ct, sizeof(ct), 574 d_first ? "%a %e %b %R" : "%a %b %e %R", 575 tm); 576 printf("\ninterrupted %s\n", ct); 577 if (signo == SIGINT) 578 exit(1); 579 (void)fflush(stdout); /* fix required for rsh */ 580} 581