1 /*- 2 * SPDX-License-Identifier: BSD-4-Clause 3 * 4 * Copyright (c) 1996 John M. Vinopal 5 * Copyright (c) 2018 Philip Paeps 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed for the NetBSD Project 19 * by John M. Vinopal. 20 * 4. The name of the author may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #ifndef lint 38 __RCSID("$NetBSD: lastlogin.c,v 1.4 1998/02/03 04:45:35 perry Exp $"); 39 #endif 40 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <time.h> 45 #include <unistd.h> 46 #include <utmpx.h> 47 48 #include <libxo/xo.h> 49 50 #define LASTLOGIN_XO_VERSION "1" 51 52 int main(int, char **); 53 static void output(struct utmpx *); 54 static void usage(void); 55 static int utcmp_user(const void *, const void *); 56 57 static int order = 1; 58 static const char *file = NULL; 59 static int (*utcmp)(const void *, const void *) = utcmp_user; 60 61 static int 62 utcmp_user(const void *u1, const void *u2) 63 { 64 65 return (order * strcmp(((const struct utmpx *)u1)->ut_user, 66 ((const struct utmpx *)u2)->ut_user)); 67 } 68 69 static int 70 utcmp_time(const void *u1, const void *u2) 71 { 72 time_t t1, t2; 73 74 t1 = ((const struct utmpx *)u1)->ut_tv.tv_sec; 75 t2 = ((const struct utmpx *)u2)->ut_tv.tv_sec; 76 return (t1 < t2 ? order : t1 > t2 ? -order : 0); 77 } 78 79 int 80 main(int argc, char *argv[]) 81 { 82 int ch, i, ulistsize; 83 struct utmpx *u, *ulist; 84 85 argc = xo_parse_args(argc, argv); 86 if (argc < 0) 87 exit(1); 88 89 while ((ch = getopt(argc, argv, "f:rt")) != -1) { 90 switch (ch) { 91 case 'f': 92 file = optarg; 93 break; 94 case 'r': 95 order = -1; 96 break; 97 case 't': 98 utcmp = utcmp_time; 99 break; 100 default: 101 usage(); 102 } 103 } 104 argc -= optind; 105 argv += optind; 106 107 xo_set_version(LASTLOGIN_XO_VERSION); 108 xo_open_container("lastlogin-information"); 109 xo_open_list("lastlogin"); 110 111 if (argc > 0) { 112 /* Process usernames given on the command line. */ 113 for (i = 0; i < argc; i++) { 114 if (setutxdb(UTXDB_LASTLOGIN, file) != 0) 115 xo_err(1, "failed to open lastlog database"); 116 if ((u = getutxuser(argv[i])) == NULL) { 117 xo_warnx("user '%s' not found", argv[i]); 118 continue; 119 } 120 output(u); 121 endutxent(); 122 } 123 } else { 124 /* Read all lastlog entries, looking for active ones. */ 125 if (setutxdb(UTXDB_LASTLOGIN, file) != 0) 126 xo_err(1, "failed to open lastlog database"); 127 ulist = NULL; 128 ulistsize = 0; 129 while ((u = getutxent()) != NULL) { 130 if (u->ut_type != USER_PROCESS) 131 continue; 132 if ((ulistsize % 16) == 0) { 133 ulist = realloc(ulist, 134 (ulistsize + 16) * sizeof(struct utmpx)); 135 if (ulist == NULL) 136 xo_err(1, "malloc"); 137 } 138 ulist[ulistsize++] = *u; 139 } 140 endutxent(); 141 142 qsort(ulist, ulistsize, sizeof(struct utmpx), utcmp); 143 for (i = 0; i < ulistsize; i++) 144 output(&ulist[i]); 145 } 146 147 xo_close_list("lastlogin"); 148 xo_close_container("lastlogin-information"); 149 if (xo_finish() < 0) 150 xo_err(1, "stdout"); 151 152 exit(0); 153 } 154 155 /* Duplicate the output of last(1) */ 156 static void 157 output(struct utmpx *u) 158 { 159 time_t t = u->ut_tv.tv_sec; 160 161 xo_open_instance("lastlogin"); 162 xo_emit("{:user/%-10s/%s} {:tty/%-8s/%s} {:from/%-22.22s/%s}", 163 u->ut_user, u->ut_line, u->ut_host); 164 xo_attr("seconds", "%lu", (unsigned long)t); 165 xo_emit(" {:login-time/%.24s/%.24s}\n", ctime(&t)); 166 xo_close_instance("lastlogin"); 167 } 168 169 static void 170 usage(void) 171 { 172 xo_error("usage: lastlogin [-f file] [-rt] [user ...]\n"); 173 exit(1); 174 } 175