11590Srgrimes/* 21590Srgrimes * Copyright (c) 1987, 1993, 1994 31590Srgrimes * The Regents of the University of California. All rights reserved. 4338451Sphilip * Copyright (c) 2018 Philip Paeps 51590Srgrimes * 61590Srgrimes * Redistribution and use in source and binary forms, with or without 71590Srgrimes * modification, are permitted provided that the following conditions 81590Srgrimes * are met: 91590Srgrimes * 1. Redistributions of source code must retain the above copyright 101590Srgrimes * notice, this list of conditions and the following disclaimer. 111590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 121590Srgrimes * notice, this list of conditions and the following disclaimer in the 131590Srgrimes * documentation and/or other materials provided with the distribution. 141590Srgrimes * 4. Neither the name of the University nor the names of its contributors 151590Srgrimes * may be used to endorse or promote products derived from this software 161590Srgrimes * without specific prior written permission. 171590Srgrimes * 181590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 191590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 201590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 211590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 221590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 231590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 241590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 251590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 261590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 271590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 281590Srgrimes * SUCH DAMAGE. 291590Srgrimes */ 301590Srgrimes 311590Srgrimes#ifndef lint 3278201Sddstatic const char copyright[] = 331590Srgrimes"@(#) Copyright (c) 1987, 1993, 1994\n\ 341590Srgrimes The Regents of the University of California. All rights reserved.\n"; 351590Srgrimes#endif /* not lint */ 361590Srgrimes 371590Srgrimes#ifndef lint 3878201Sddstatic const char sccsid[] = "@(#)last.c 8.2 (Berkeley) 4/2/94"; 391590Srgrimes#endif /* not lint */ 4099112Sobrien#include <sys/cdefs.h> 4199112Sobrien__FBSDID("$FreeBSD: stable/11/usr.bin/last/last.c 351925 2019-09-06 05:34:31Z eugen $"); 421590Srgrimes 431590Srgrimes#include <sys/param.h> 441590Srgrimes#include <sys/stat.h> 451590Srgrimes 461590Srgrimes#include <err.h> 47118077Stjr#include <errno.h> 481590Srgrimes#include <fcntl.h> 4974588Sache#include <langinfo.h> 5016438Sache#include <locale.h> 511590Srgrimes#include <paths.h> 521590Srgrimes#include <signal.h> 531590Srgrimes#include <stdio.h> 541590Srgrimes#include <stdlib.h> 551590Srgrimes#include <string.h> 561590Srgrimes#include <time.h> 57125856Sdwmalone#include <timeconv.h> 581590Srgrimes#include <unistd.h> 59202197Sed#include <utmpx.h> 6011547Sdg#include <sys/queue.h> 611590Srgrimes 62338451Sphilip#include <libxo/xo.h> 63338451Sphilip 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 681590Srgrimestypedef struct arg { 691590Srgrimes char *name; /* argument */ 70285742Sed#define REBOOT_TYPE -1 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; 77227168Sedstatic ARG *arglist; /* head of linked list */ 781590Srgrimes 79240425Sedstatic SLIST_HEAD(, idtab) idlist; 8011547Sdg 81202197Sedstruct idtab { 8236062Sjb time_t logout; /* log out time */ 83202197Sed char id[sizeof ((struct utmpx *)0)->ut_id]; /* identifier */ 84240425Sed SLIST_ENTRY(idtab) list; 8511547Sdg}; 861590Srgrimes 8791536Siedowsestatic const char *crmsg; /* cause of last reboot */ 88202197Sedstatic time_t currentout; /* current logout value */ 89202197Sedstatic long maxrec; /* records to display */ 90230458Shrsstatic const char *file = NULL; /* utx.log file */ 91351925Seugenstatic int noctfix = 0; /* locale is C or UTF-8 */ 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 102227168Sedstatic void addarg(int, char *); 103351925Seugenstatic const char *ctf(const char *); 104227168Sedstatic time_t dateconv(char *); 105227168Sedstatic void doentry(struct utmpx *); 106227168Sedstatic void hostconv(char *); 107227168Sedstatic void printentry(struct utmpx *, struct idtab *); 108227168Sedstatic char *ttyconv(char *); 109227168Sedstatic int want(struct utmpx *); 110227168Sedstatic void usage(void); 111227168Sedstatic void wtmp(void); 1121590Srgrimes 113351925Seugenstatic const char* 114351925Seugenctf(const char *fmt) { 115351925Seugen static char buf[31]; 116351925Seugen const char *src, *end; 117351925Seugen char *dst; 118351925Seugen 119351925Seugen if (noctfix) 120351925Seugen return (fmt); 121351925Seugen 122351925Seugen end = buf + sizeof(buf); 123351925Seugen for (src = fmt, dst = buf; dst < end; *dst++ = *src++) { 124351925Seugen if (*src == '\0') { 125351925Seugen *dst = '\0'; 126351925Seugen break; 127351925Seugen } else if (*src == '%' && *(src+1) == 's') { 128351925Seugen *dst++ = '%'; 129351925Seugen *dst++ = 'h'; 130351925Seugen *dst++ = 's'; 131351925Seugen strlcpy(dst, src+2, end - dst); 132351925Seugen return (buf); 133351925Seugen } 134351925Seugen } 135351925Seugen return (buf); 136351925Seugen} 137351925Seugen 138227168Sedstatic void 13936434Sdannyusage(void) 14036434Sdanny{ 141338451Sphilip xo_error( 142119023Stjr"usage: last [-swy] [-d [[CC]YY][MMDD]hhmm[.SS]] [-f file] [-h host]\n" 143119023Stjr" [-n maxrec] [-t tty] [user ...]\n"); 14436434Sdanny exit(1); 14536434Sdanny} 14636434Sdanny 1471590Srgrimesint 148102944Sdwmalonemain(int argc, char *argv[]) 1491590Srgrimes{ 1501590Srgrimes int ch; 1511590Srgrimes char *p; 1521590Srgrimes 15316438Sache (void) setlocale(LC_TIME, ""); 15474588Sache d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 15516438Sache 156351925Seugen (void) setlocale(LC_CTYPE, ""); 157351925Seugen p = nl_langinfo(CODESET); 158351925Seugen if (strcmp(p, "UTF-8") == 0 || strcmp(p, "US-ASCII") == 0) 159351925Seugen noctfix = 1; 160351925Seugen 161338451Sphilip argc = xo_parse_args(argc, argv); 162338451Sphilip if (argc < 0) 163338451Sphilip exit(1); 164338451Sphilip atexit(xo_finish_atexit); 165338451Sphilip 1661590Srgrimes maxrec = -1; 16777291Sdd snaptime = 0; 168118077Stjr while ((ch = getopt(argc, argv, "0123456789d:f:h:n:st:wy")) != -1) 1691590Srgrimes switch (ch) { 1701590Srgrimes case '0': case '1': case '2': case '3': case '4': 1711590Srgrimes case '5': case '6': case '7': case '8': case '9': 1721590Srgrimes /* 1731590Srgrimes * kludge: last was originally designed to take 1741590Srgrimes * a number after a dash. 1751590Srgrimes */ 1761590Srgrimes if (maxrec == -1) { 177106215Smux p = strchr(argv[optind - 1], ch); 178106215Smux if (p == NULL) 179106215Smux p = strchr(argv[optind], ch); 180106215Smux maxrec = atol(p); 1811590Srgrimes if (!maxrec) 1821590Srgrimes exit(0); 1831590Srgrimes } 1841590Srgrimes break; 18577291Sdd case 'd': 18677291Sdd snaptime = dateconv(optarg); 18777291Sdd break; 1881590Srgrimes case 'f': 1891590Srgrimes file = optarg; 1901590Srgrimes break; 1911590Srgrimes case 'h': 1921590Srgrimes hostconv(optarg); 1931590Srgrimes addarg(HOST_TYPE, optarg); 1941590Srgrimes break; 195118077Stjr case 'n': 196118077Stjr errno = 0; 197118077Stjr maxrec = strtol(optarg, &p, 10); 198118077Stjr if (p == optarg || *p != '\0' || errno != 0 || 199118077Stjr maxrec <= 0) 200338451Sphilip xo_errx(1, "%s: bad line count", optarg); 201118077Stjr break; 20236434Sdanny case 's': 20336434Sdanny sflag++; /* Show delta as seconds */ 20436434Sdanny break; 2051590Srgrimes case 't': 2061590Srgrimes addarg(TTY_TYPE, ttyconv(optarg)); 2071590Srgrimes break; 20836434Sdanny case 'w': 20936434Sdanny width = 8; 21036434Sdanny break; 21191541Siedowse case 'y': 21291541Siedowse yflag++; 21391541Siedowse break; 2141590Srgrimes case '?': 2151590Srgrimes default: 21636434Sdanny usage(); 2171590Srgrimes } 2181590Srgrimes 21936434Sdanny if (sflag && width == 8) usage(); 22036434Sdanny 2211590Srgrimes if (argc) { 2221590Srgrimes setlinebuf(stdout); 2231590Srgrimes for (argv += optind; *argv; ++argv) { 224285742Sed if (strcmp(*argv, "reboot") == 0) 225285742Sed addarg(REBOOT_TYPE, *argv); 2261590Srgrimes#define COMPATIBILITY 2271590Srgrimes#ifdef COMPATIBILITY 2281590Srgrimes /* code to allow "last p5" to work */ 2291590Srgrimes addarg(TTY_TYPE, ttyconv(*argv)); 2301590Srgrimes#endif 2311590Srgrimes addarg(USER_TYPE, *argv); 2321590Srgrimes } 2331590Srgrimes } 2341590Srgrimes wtmp(); 2351590Srgrimes exit(0); 2361590Srgrimes} 2371590Srgrimes 2381590Srgrimes/* 2391590Srgrimes * wtmp -- 240230458Shrs * read through the utx.log file 2411590Srgrimes */ 242227168Sedstatic void 243102944Sdwmalonewtmp(void) 2441590Srgrimes{ 245202643Sed struct utmpx *buf = NULL; 246202197Sed struct utmpx *ut; 247202643Sed static unsigned int amount = 0; 248202197Sed time_t t; 24916438Sache char ct[80]; 25016438Sache struct tm *tm; 2511590Srgrimes 252240425Sed SLIST_INIT(&idlist); 253202197Sed (void)time(&t); 25411547Sdg 255338451Sphilip xo_open_container("last-information"); 256338451Sphilip 257202197Sed /* Load the last entries from the file. */ 258202197Sed if (setutxdb(UTXDB_LOG, file) != 0) 259338451Sphilip xo_err(1, "%s", file); 260202197Sed while ((ut = getutxent()) != NULL) { 261202643Sed if (amount % 128 == 0) { 262202643Sed buf = realloc(buf, (amount + 128) * sizeof *ut); 263202643Sed if (buf == NULL) 264338451Sphilip xo_err(1, "realloc"); 265202643Sed } 266202643Sed memcpy(&buf[amount++], ut, sizeof *ut); 267202197Sed if (t > ut->ut_tv.tv_sec) 268202197Sed t = ut->ut_tv.tv_sec; 269202197Sed } 270202197Sed endutxent(); 2711590Srgrimes 272202197Sed /* Display them in reverse order. */ 273338451Sphilip xo_open_list("last"); 274202197Sed while (amount > 0) 275202643Sed doentry(&buf[--amount]); 276338451Sphilip xo_close_list("last"); 277338451Sphilip free(buf); 27885648Sdillon tm = localtime(&t); 279230458Shrs (void) strftime(ct, sizeof(ct), "%+", tm); 280338451Sphilip xo_emit("\n{:utxdb/%s}", (file == NULL) ? "utx.log" : file); 281338451Sphilip xo_attr("seconds", "%lu", (unsigned long) t); 282351925Seugen xo_emit(ctf(" begins {:begins/%s}\n"), ct); 283338451Sphilip xo_close_container("last-information"); 2841590Srgrimes} 2851590Srgrimes 2861590Srgrimes/* 28791536Siedowse * doentry -- 288230458Shrs * process a single utx.log entry 28991536Siedowse */ 290227168Sedstatic void 291202197Seddoentry(struct utmpx *bp) 29291536Siedowse{ 293240425Sed struct idtab *tt; 29491536Siedowse 295202197Sed /* the machine stopped */ 296202197Sed if (bp->ut_type == BOOT_TIME || bp->ut_type == SHUTDOWN_TIME) { 29791536Siedowse /* everybody just logged out */ 298240425Sed while ((tt = SLIST_FIRST(&idlist)) != NULL) { 299240425Sed SLIST_REMOVE_HEAD(&idlist, list); 300240425Sed free(tt); 30191536Siedowse } 302202197Sed currentout = -bp->ut_tv.tv_sec; 303202197Sed crmsg = bp->ut_type != SHUTDOWN_TIME ? 30491536Siedowse "crash" : "shutdown"; 30591536Siedowse /* 30691536Siedowse * if we're in snapshot mode, we want to exit if this 30791536Siedowse * shutdown/reboot appears while we we are tracking the 30891536Siedowse * active range 30991536Siedowse */ 31091536Siedowse if (snaptime && snapfound) 31191536Siedowse exit(0); 31291536Siedowse /* 31391536Siedowse * don't print shutdown/reboot entries unless flagged for 31491536Siedowse */ 31591538Siedowse if (!snaptime && want(bp)) 31691536Siedowse printentry(bp, NULL); 31791536Siedowse return; 31891536Siedowse } 319202197Sed /* date got set */ 320202197Sed if (bp->ut_type == OLD_TIME || bp->ut_type == NEW_TIME) { 32191538Siedowse if (want(bp) && !snaptime) 32291536Siedowse printentry(bp, NULL); 32391536Siedowse return; 32491536Siedowse } 325202197Sed 326202197Sed if (bp->ut_type != USER_PROCESS && bp->ut_type != DEAD_PROCESS) 327202197Sed return; 328202197Sed 329202197Sed /* find associated identifier */ 330240425Sed SLIST_FOREACH(tt, &idlist, list) 331202197Sed if (!memcmp(tt->id, bp->ut_id, sizeof bp->ut_id)) 33291536Siedowse break; 33391536Siedowse 33491536Siedowse if (tt == NULL) { 33591536Siedowse /* add new one */ 336202197Sed tt = malloc(sizeof(struct idtab)); 33791536Siedowse if (tt == NULL) 338338451Sphilip xo_errx(1, "malloc failure"); 33991536Siedowse tt->logout = currentout; 340202197Sed memcpy(tt->id, bp->ut_id, sizeof bp->ut_id); 341240425Sed SLIST_INSERT_HEAD(&idlist, tt, list); 34291536Siedowse } 34391536Siedowse 34491536Siedowse /* 34591536Siedowse * print record if not in snapshot mode and wanted 34691536Siedowse * or in snapshot mode and in snapshot range 34791536Siedowse */ 348202197Sed if (bp->ut_type == USER_PROCESS && (want(bp) || 349202197Sed (bp->ut_tv.tv_sec < snaptime && 35091536Siedowse (tt->logout > snaptime || tt->logout < 1)))) { 35191536Siedowse snapfound = 1; 35291536Siedowse printentry(bp, tt); 35391536Siedowse } 354202197Sed tt->logout = bp->ut_tv.tv_sec; 35591536Siedowse} 35691536Siedowse 35791536Siedowse/* 35891536Siedowse * printentry -- 35991536Siedowse * output an entry 36091536Siedowse * 36191536Siedowse * If `tt' is non-NULL, use it and `crmsg' to print the logout time or 36291536Siedowse * logout type (crash/shutdown) as appropriate. 36391536Siedowse */ 364227168Sedstatic void 365202197Sedprintentry(struct utmpx *bp, struct idtab *tt) 36691536Siedowse{ 36791536Siedowse char ct[80]; 36891536Siedowse struct tm *tm; 36991536Siedowse time_t delta; /* time difference */ 37091536Siedowse time_t t; 37191536Siedowse 37291538Siedowse if (maxrec != -1 && !maxrec--) 37391538Siedowse exit(0); 374338451Sphilip xo_open_instance("last"); 375202197Sed t = bp->ut_tv.tv_sec; 37691536Siedowse tm = localtime(&t); 37791541Siedowse (void) strftime(ct, sizeof(ct), d_first ? 37891541Siedowse (yflag ? "%a %e %b %Y %R" : "%a %e %b %R") : 37991541Siedowse (yflag ? "%a %b %e %Y %R" : "%a %b %e %R"), tm); 380202197Sed switch (bp->ut_type) { 381202197Sed case BOOT_TIME: 382338451Sphilip xo_emit("{:user/%-42s/%s}", "boot time"); 383202197Sed break; 384202197Sed case SHUTDOWN_TIME: 385338451Sphilip xo_emit("{:user/%-42s/%s}", "shutdown time"); 386202197Sed break; 387202197Sed case OLD_TIME: 388338451Sphilip xo_emit("{:user/%-42s/%s}", "old time"); 389202197Sed break; 390202197Sed case NEW_TIME: 391338451Sphilip xo_emit("{:user/%-42s/%s}", "new time"); 392202197Sed break; 393202197Sed case USER_PROCESS: 394338451Sphilip xo_emit("{:user/%-10s/%s} {:tty/%-8s/%s} {:from/%-22.22s/%s}", 395202197Sed bp->ut_user, bp->ut_line, bp->ut_host); 396202197Sed break; 397202197Sed } 398338451Sphilip xo_attr("seconds", "%lu", (unsigned long)t); 399351925Seugen xo_emit(ctf(" {:login-time/%s%c/%s}"), ct, tt == NULL ? '\n' : ' '); 40091536Siedowse if (tt == NULL) 401338451Sphilip goto end; 40291536Siedowse if (!tt->logout) { 403338451Sphilip xo_emit(" {:logout-time/still logged in}\n"); 404338451Sphilip goto end; 40591536Siedowse } 40691536Siedowse if (tt->logout < 0) { 40791536Siedowse tt->logout = -tt->logout; 408338451Sphilip xo_emit("- {:logout-reason/%s}", crmsg); 40991536Siedowse } else { 41091536Siedowse tm = localtime(&tt->logout); 41191536Siedowse (void) strftime(ct, sizeof(ct), "%R", tm); 412338451Sphilip xo_attr("seconds", "%lu", (unsigned long)tt->logout); 413351925Seugen xo_emit(ctf("- {:logout-time/%s}"), ct); 41491536Siedowse } 415202197Sed delta = tt->logout - bp->ut_tv.tv_sec; 416338451Sphilip xo_attr("seconds", "%ld", (long)delta); 41791536Siedowse if (sflag) { 418338451Sphilip xo_emit(" ({:session-length/%8ld})\n", (long)delta); 41991536Siedowse } else { 42091536Siedowse tm = gmtime(&delta); 42191536Siedowse (void) strftime(ct, sizeof(ct), width >= 8 ? "%T" : "%R", tm); 42291536Siedowse if (delta < 86400) 423351925Seugen xo_emit(ctf(" ({:session-length/%s})\n"), ct); 42491536Siedowse else 425351925Seugen xo_emit(ctf(" ({:session-length/%ld+%s})\n"), 426338451Sphilip (long)delta / 86400, ct); 42791536Siedowse } 428338451Sphilip 429338451Sphilipend: 430338451Sphilip xo_close_instance("last"); 43191536Siedowse} 43291536Siedowse 43391536Siedowse/* 4341590Srgrimes * want -- 4351590Srgrimes * see if want this entry 4361590Srgrimes */ 437227168Sedstatic int 438202197Sedwant(struct utmpx *bp) 4391590Srgrimes{ 4401590Srgrimes ARG *step; 4411590Srgrimes 44277291Sdd if (snaptime) 44377291Sdd return (NO); 44477291Sdd 4451590Srgrimes if (!arglist) 4461590Srgrimes return (YES); 4471590Srgrimes 4481590Srgrimes for (step = arglist; step; step = step->next) 4491590Srgrimes switch(step->type) { 450285742Sed case REBOOT_TYPE: 451285742Sed if (bp->ut_type == BOOT_TIME || 452285742Sed bp->ut_type == SHUTDOWN_TIME) 453285742Sed return (YES); 454285742Sed break; 4551590Srgrimes case HOST_TYPE: 456202197Sed if (!strcasecmp(step->name, bp->ut_host)) 4571590Srgrimes return (YES); 4581590Srgrimes break; 4591590Srgrimes case TTY_TYPE: 460202197Sed if (!strcmp(step->name, bp->ut_line)) 4611590Srgrimes return (YES); 4621590Srgrimes break; 4631590Srgrimes case USER_TYPE: 464202197Sed if (!strcmp(step->name, bp->ut_user)) 4651590Srgrimes return (YES); 4661590Srgrimes break; 46791536Siedowse } 4681590Srgrimes return (NO); 4691590Srgrimes} 4701590Srgrimes 4711590Srgrimes/* 4721590Srgrimes * addarg -- 4731590Srgrimes * add an entry to a linked list of arguments 4741590Srgrimes */ 475227168Sedstatic void 476102944Sdwmaloneaddarg(int type, char *arg) 4771590Srgrimes{ 4781590Srgrimes ARG *cur; 4791590Srgrimes 48096785Sjmallett if ((cur = malloc(sizeof(ARG))) == NULL) 481338451Sphilip xo_errx(1, "malloc failure"); 4821590Srgrimes cur->next = arglist; 4831590Srgrimes cur->type = type; 4841590Srgrimes cur->name = arg; 4851590Srgrimes arglist = cur; 4861590Srgrimes} 4871590Srgrimes 4881590Srgrimes/* 4891590Srgrimes * hostconv -- 4901590Srgrimes * convert the hostname to search pattern; if the supplied host name 4911590Srgrimes * has a domain attached that is the same as the current domain, rip 4921590Srgrimes * off the domain suffix since that's what login(1) does. 4931590Srgrimes */ 494227168Sedstatic void 495102944Sdwmalonehostconv(char *arg) 4961590Srgrimes{ 4971590Srgrimes static int first = 1; 4981590Srgrimes static char *hostdot, name[MAXHOSTNAMELEN]; 4991590Srgrimes char *argdot; 5001590Srgrimes 5011590Srgrimes if (!(argdot = strchr(arg, '.'))) 5021590Srgrimes return; 5031590Srgrimes if (first) { 5041590Srgrimes first = 0; 5051590Srgrimes if (gethostname(name, sizeof(name))) 506338451Sphilip xo_err(1, "gethostname"); 5071590Srgrimes hostdot = strchr(name, '.'); 5081590Srgrimes } 5091590Srgrimes if (hostdot && !strcasecmp(hostdot, argdot)) 5101590Srgrimes *argdot = '\0'; 5111590Srgrimes} 5121590Srgrimes 5131590Srgrimes/* 5141590Srgrimes * ttyconv -- 5151590Srgrimes * convert tty to correct name. 5161590Srgrimes */ 517227168Sedstatic char * 518102944Sdwmalonettyconv(char *arg) 5191590Srgrimes{ 5201590Srgrimes char *mval; 5211590Srgrimes 5221590Srgrimes /* 5231590Srgrimes * kludge -- we assume that all tty's end with 5241590Srgrimes * a two character suffix. 5251590Srgrimes */ 5261590Srgrimes if (strlen(arg) == 2) { 5271590Srgrimes /* either 6 for "ttyxx" or 8 for "console" */ 52896785Sjmallett if ((mval = malloc(8)) == NULL) 529338451Sphilip xo_errx(1, "malloc failure"); 5301590Srgrimes if (!strcmp(arg, "co")) 5311590Srgrimes (void)strcpy(mval, "console"); 5321590Srgrimes else { 5331590Srgrimes (void)strcpy(mval, "tty"); 5341590Srgrimes (void)strcpy(mval + 3, arg); 5351590Srgrimes } 5361590Srgrimes return (mval); 5371590Srgrimes } 5381590Srgrimes if (!strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1)) 5391590Srgrimes return (arg + 5); 5401590Srgrimes return (arg); 5411590Srgrimes} 5421590Srgrimes 5431590Srgrimes/* 54477291Sdd * dateconv -- 54577291Sdd * Convert the snapshot time in command line given in the format 54677291Sdd * [[CC]YY]MMDDhhmm[.SS]] to a time_t. 54777291Sdd * Derived from atime_arg1() in usr.bin/touch/touch.c 54877291Sdd */ 549227168Sedstatic time_t 550102944Sdwmalonedateconv(char *arg) 55177291Sdd{ 55277291Sdd time_t timet; 55377291Sdd struct tm *t; 55477291Sdd int yearset; 55577291Sdd char *p; 55677291Sdd 55777291Sdd /* Start with the current time. */ 55877291Sdd if (time(&timet) < 0) 559338451Sphilip xo_err(1, "time"); 56077291Sdd if ((t = localtime(&timet)) == NULL) 561338451Sphilip xo_err(1, "localtime"); 56277291Sdd 56377291Sdd /* [[CC]YY]MMDDhhmm[.SS] */ 56477291Sdd if ((p = strchr(arg, '.')) == NULL) 56577291Sdd t->tm_sec = 0; /* Seconds defaults to 0. */ 56677291Sdd else { 56777291Sdd if (strlen(p + 1) != 2) 56877291Sdd goto terr; 56977291Sdd *p++ = '\0'; 57077291Sdd t->tm_sec = ATOI2(p); 57177291Sdd } 57277291Sdd 57377291Sdd yearset = 0; 57477291Sdd switch (strlen(arg)) { 57577291Sdd case 12: /* CCYYMMDDhhmm */ 57677291Sdd t->tm_year = ATOI2(arg); 57777291Sdd t->tm_year *= 100; 57877291Sdd yearset = 1; 579133332Sdwmalone /* FALLTHROUGH */ 58077291Sdd case 10: /* YYMMDDhhmm */ 58177291Sdd if (yearset) { 58277291Sdd yearset = ATOI2(arg); 58377291Sdd t->tm_year += yearset; 58477291Sdd } else { 58577291Sdd yearset = ATOI2(arg); 58677291Sdd if (yearset < 69) 58777291Sdd t->tm_year = yearset + 2000; 58877291Sdd else 58977291Sdd t->tm_year = yearset + 1900; 59077291Sdd } 59177291Sdd t->tm_year -= 1900; /* Convert to UNIX time. */ 59277291Sdd /* FALLTHROUGH */ 59377291Sdd case 8: /* MMDDhhmm */ 59477291Sdd t->tm_mon = ATOI2(arg); 59577291Sdd --t->tm_mon; /* Convert from 01-12 to 00-11 */ 59677291Sdd t->tm_mday = ATOI2(arg); 59777291Sdd t->tm_hour = ATOI2(arg); 59877291Sdd t->tm_min = ATOI2(arg); 59977291Sdd break; 60077291Sdd case 4: /* hhmm */ 60177291Sdd t->tm_hour = ATOI2(arg); 60277291Sdd t->tm_min = ATOI2(arg); 60377291Sdd break; 60477291Sdd default: 60577291Sdd goto terr; 60677291Sdd } 60777291Sdd t->tm_isdst = -1; /* Figure out DST. */ 60877291Sdd timet = mktime(t); 60977291Sdd if (timet == -1) 610338451Sphilipterr: xo_errx(1, 61177291Sdd "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); 61277291Sdd return timet; 61377291Sdd} 614