138a99942STim J. Robbins /*- 238a99942STim J. Robbins * Copyright (c) 2002 Tim J. Robbins. 338a99942STim J. Robbins * All rights reserved. 49b50d902SRodney W. Grimes * 59b50d902SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 69b50d902SRodney W. Grimes * modification, are permitted provided that the following conditions 79b50d902SRodney W. Grimes * are met: 89b50d902SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 99b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 109b50d902SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 119b50d902SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 129b50d902SRodney W. Grimes * documentation and/or other materials provided with the distribution. 139b50d902SRodney W. Grimes * 1438a99942STim J. Robbins * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 159b50d902SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 169b50d902SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738a99942STim J. Robbins * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 189b50d902SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 199b50d902SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 209b50d902SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 219b50d902SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 229b50d902SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 239b50d902SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 249b50d902SRodney W. Grimes * SUCH DAMAGE. 259b50d902SRodney W. Grimes */ 269b50d902SRodney W. Grimes 272c062c85SMark Murray #include <sys/cdefs.h> 282c062c85SMark Murray __FBSDID("$FreeBSD$"); 292c062c85SMark Murray 309b50d902SRodney W. Grimes #include <sys/types.h> 3138a99942STim J. Robbins #include <sys/ioctl.h> 3238a99942STim J. Robbins #include <sys/stat.h> 332c062c85SMark Murray 340abcff76SPhilippe Charnier #include <err.h> 35e36de81eSAndrey A. Chernov #include <langinfo.h> 36965f310cSAndrey A. Chernov #include <locale.h> 3738a99942STim J. Robbins #include <paths.h> 380abcff76SPhilippe Charnier #include <pwd.h> 390abcff76SPhilippe Charnier #include <stdio.h> 407a19d1bbSDima Dorfman #include <stdlib.h> 410abcff76SPhilippe Charnier #include <string.h> 420abcff76SPhilippe Charnier #include <time.h> 430abcff76SPhilippe Charnier #include <unistd.h> 440abcff76SPhilippe Charnier #include <utmp.h> 459b50d902SRodney W. Grimes 4638a99942STim J. Robbins static void heading(void); 4738a99942STim J. Robbins static void process_utmp(FILE *); 4838a99942STim J. Robbins static void quick(FILE *); 4938a99942STim J. Robbins static void row(struct utmp *); 5038a99942STim J. Robbins static int ttywidth(void); 513f330d7dSWarner Losh static void usage(void); 5238a99942STim J. Robbins static void whoami(FILE *); 5338a99942STim J. Robbins 5438a99942STim J. Robbins static int Hflag; /* Write column headings */ 5538a99942STim J. Robbins static int mflag; /* Show info about current terminal */ 5638a99942STim J. Robbins static int qflag; /* "Quick" mode */ 5738a99942STim J. Robbins static int sflag; /* Show name, line, time */ 5838a99942STim J. Robbins static int Tflag; /* Show terminal state */ 5938a99942STim J. Robbins static int uflag; /* Show idle time */ 600abcff76SPhilippe Charnier 610abcff76SPhilippe Charnier int 6238a99942STim J. Robbins main(int argc, char *argv[]) 639b50d902SRodney W. Grimes { 6438a99942STim J. Robbins int ch; 6538a99942STim J. Robbins const char *file; 6638a99942STim J. Robbins FILE *fp; 679b50d902SRodney W. Grimes 6838a99942STim J. Robbins setlocale(LC_TIME, ""); 69965f310cSAndrey A. Chernov 7038a99942STim J. Robbins while ((ch = getopt(argc, argv, "HTabdlmpqrstu")) != -1) { 7138a99942STim J. Robbins switch (ch) { 7238a99942STim J. Robbins case 'H': /* Write column headings */ 7338a99942STim J. Robbins Hflag = 1; 749b50d902SRodney W. Grimes break; 7538a99942STim J. Robbins case 'T': /* Show terminal state */ 7638a99942STim J. Robbins Tflag = 1; 779b50d902SRodney W. Grimes break; 7838a99942STim J. Robbins case 'm': /* Show info about current terminal */ 7938a99942STim J. Robbins mflag = 1; 8038a99942STim J. Robbins break; 8138a99942STim J. Robbins case 'q': /* "Quick" mode */ 8238a99942STim J. Robbins qflag = 1; 8338a99942STim J. Robbins break; 8438a99942STim J. Robbins case 's': /* Show name, line, time */ 8538a99942STim J. Robbins sflag = 1; 8638a99942STim J. Robbins break; 8738a99942STim J. Robbins case 'u': /* Show idle time */ 8838a99942STim J. Robbins uflag = 1; 899b50d902SRodney W. Grimes break; 909b50d902SRodney W. Grimes default: 910abcff76SPhilippe Charnier usage(); 9238a99942STim J. Robbins /*NOTREACHED*/ 939b50d902SRodney W. Grimes } 9438a99942STim J. Robbins } 9538a99942STim J. Robbins argc -= optind; 9638a99942STim J. Robbins argv += optind; 9738a99942STim J. Robbins 9838a99942STim J. Robbins if (argc >= 2 && strcmp(argv[0], "am") == 0 && 9938a99942STim J. Robbins (strcmp(argv[1], "i") == 0 || strcmp(argv[1], "I") == 0)) { 10038a99942STim J. Robbins /* "who am i" or "who am I", equivalent to -m */ 10138a99942STim J. Robbins mflag = 1; 10238a99942STim J. Robbins argc -= 2; 10338a99942STim J. Robbins argv += 2; 10438a99942STim J. Robbins } 10538a99942STim J. Robbins if (argc > 1) 10638a99942STim J. Robbins usage(); 10738a99942STim J. Robbins 10838a99942STim J. Robbins if (*argv != NULL) 10938a99942STim J. Robbins file = *argv; 11038a99942STim J. Robbins else 11138a99942STim J. Robbins file = _PATH_UTMP; 11238a99942STim J. Robbins if ((fp = fopen(file, "r")) == NULL) 11338a99942STim J. Robbins err(1, "%s", file); 11438a99942STim J. Robbins 11538a99942STim J. Robbins if (qflag) 11638a99942STim J. Robbins quick(fp); 11738a99942STim J. Robbins else { 11838a99942STim J. Robbins if (sflag) 11938a99942STim J. Robbins Tflag = uflag = 0; 12038a99942STim J. Robbins if (Hflag) 12138a99942STim J. Robbins heading(); 12238a99942STim J. Robbins if (mflag) 12338a99942STim J. Robbins whoami(fp); 12438a99942STim J. Robbins else 12538a99942STim J. Robbins process_utmp(fp); 12638a99942STim J. Robbins } 12738a99942STim J. Robbins 12838a99942STim J. Robbins fclose(fp); 12938a99942STim J. Robbins 1309b50d902SRodney W. Grimes exit(0); 1319b50d902SRodney W. Grimes } 1329b50d902SRodney W. Grimes 13338a99942STim J. Robbins void 13438a99942STim J. Robbins usage(void) 1350abcff76SPhilippe Charnier { 13638a99942STim J. Robbins 13738a99942STim J. Robbins fprintf(stderr, "usage: who [-HmqsTu] [am I] [file]\n"); 1380abcff76SPhilippe Charnier exit(1); 1390abcff76SPhilippe Charnier } 1400abcff76SPhilippe Charnier 1410abcff76SPhilippe Charnier void 14238a99942STim J. Robbins heading(void) 1439b50d902SRodney W. Grimes { 14438a99942STim J. Robbins 14538a99942STim J. Robbins printf("%-*s ", UT_NAMESIZE, "NAME"); 14638a99942STim J. Robbins if (Tflag) 14738a99942STim J. Robbins printf("S "); 14838a99942STim J. Robbins printf("%-*s ", UT_LINESIZE, "LINE"); 14938a99942STim J. Robbins printf("%-*s ", 12, "TIME"); 15038a99942STim J. Robbins if (uflag) 15138a99942STim J. Robbins printf("IDLE "); 15238a99942STim J. Robbins putchar('\n'); 15338a99942STim J. Robbins } 15438a99942STim J. Robbins 15538a99942STim J. Robbins void 15638a99942STim J. Robbins row(struct utmp *ut) 15738a99942STim J. Robbins { 15838a99942STim J. Robbins char buf[80], tty[sizeof(_PATH_DEV) + UT_LINESIZE]; 15938a99942STim J. Robbins struct stat sb; 16038a99942STim J. Robbins time_t idle; 161e36de81eSAndrey A. Chernov static int d_first = -1; 16238a99942STim J. Robbins struct tm *tm; 16338a99942STim J. Robbins char state; 164e36de81eSAndrey A. Chernov 165e36de81eSAndrey A. Chernov if (d_first < 0) 166e36de81eSAndrey A. Chernov d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 1679b50d902SRodney W. Grimes 16838a99942STim J. Robbins if (Tflag || uflag) { 16938a99942STim J. Robbins snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV, 17038a99942STim J. Robbins UT_LINESIZE, ut->ut_line); 17138a99942STim J. Robbins state = '?'; 17238a99942STim J. Robbins idle = 0; 17338a99942STim J. Robbins if (stat(tty, &sb) == 0) { 17438a99942STim J. Robbins state = sb.st_mode & (S_IWOTH|S_IWGRP) ? 17538a99942STim J. Robbins '+' : '-'; 17638a99942STim J. Robbins idle = time(NULL) - sb.st_mtime; 17738a99942STim J. Robbins } 1789b50d902SRodney W. Grimes } 1799b50d902SRodney W. Grimes 18038a99942STim J. Robbins printf("%-*.*s ", UT_NAMESIZE, UT_NAMESIZE, ut->ut_name); 18138a99942STim J. Robbins if (Tflag) 18238a99942STim J. Robbins printf("%c ", state); 18338a99942STim J. Robbins printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, ut->ut_line); 18438a99942STim J. Robbins tm = localtime(&ut->ut_time); 18538a99942STim J. Robbins strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm); 18638a99942STim J. Robbins printf("%-*s ", 12, buf); 18738a99942STim J. Robbins if (uflag) { 18838a99942STim J. Robbins if (idle < 60) 18938a99942STim J. Robbins printf(" . "); 19038a99942STim J. Robbins else if (idle < 24 * 60 * 60) 19138a99942STim J. Robbins printf("%02d:%02d ", (int)(idle / 60 / 60), 19238a99942STim J. Robbins (int)(idle / 60 % 60)); 19338a99942STim J. Robbins else 19438a99942STim J. Robbins printf(" old "); 19538a99942STim J. Robbins } 19638a99942STim J. Robbins if (*ut->ut_host != '\0') 19738a99942STim J. Robbins printf("(%.*s)", UT_HOSTSIZE, ut->ut_host); 19838a99942STim J. Robbins putchar('\n'); 19938a99942STim J. Robbins } 2009b50d902SRodney W. Grimes 20138a99942STim J. Robbins void 20238a99942STim J. Robbins process_utmp(FILE *fp) 20338a99942STim J. Robbins { 20438a99942STim J. Robbins struct utmp ut; 20538a99942STim J. Robbins 20638a99942STim J. Robbins while (fread(&ut, sizeof(ut), 1, fp) == 1) 20738a99942STim J. Robbins if (*ut.ut_name != '\0') 20838a99942STim J. Robbins row(&ut); 20938a99942STim J. Robbins } 21038a99942STim J. Robbins 21138a99942STim J. Robbins void 21238a99942STim J. Robbins quick(FILE *fp) 21338a99942STim J. Robbins { 21438a99942STim J. Robbins struct utmp ut; 21538a99942STim J. Robbins int col, ncols, num; 21638a99942STim J. Robbins 21738a99942STim J. Robbins ncols = ttywidth(); 21838a99942STim J. Robbins col = num = 0; 21938a99942STim J. Robbins while (fread(&ut, sizeof(ut), 1, fp) == 1) { 22038a99942STim J. Robbins if (*ut.ut_name == '\0') 22138a99942STim J. Robbins continue; 22238a99942STim J. Robbins printf("%-*.*s", UT_NAMESIZE, UT_NAMESIZE, ut.ut_name); 22338a99942STim J. Robbins if (++col < ncols / (UT_NAMESIZE + 1)) 22438a99942STim J. Robbins putchar(' '); 22538a99942STim J. Robbins else { 22638a99942STim J. Robbins col = 0; 22738a99942STim J. Robbins putchar('\n'); 22838a99942STim J. Robbins } 22938a99942STim J. Robbins num++; 23038a99942STim J. Robbins } 23138a99942STim J. Robbins if (col != 0) 23238a99942STim J. Robbins putchar('\n'); 23338a99942STim J. Robbins 23438a99942STim J. Robbins printf("# users = %d\n", num); 23538a99942STim J. Robbins } 23638a99942STim J. Robbins 23738a99942STim J. Robbins void 23838a99942STim J. Robbins whoami(FILE *fp) 23938a99942STim J. Robbins { 24038a99942STim J. Robbins struct utmp ut; 24138a99942STim J. Robbins struct passwd *pwd; 24238a99942STim J. Robbins const char *name, *p, *tty; 24338a99942STim J. Robbins 24438a99942STim J. Robbins if ((tty = ttyname(STDIN_FILENO)) == NULL) 24538a99942STim J. Robbins tty = "tty??"; 24638a99942STim J. Robbins else if ((p = strrchr(tty, '/')) != NULL) 24738a99942STim J. Robbins tty = p + 1; 24838a99942STim J. Robbins 24938a99942STim J. Robbins /* Search utmp for our tty, dump first matching record. */ 25038a99942STim J. Robbins while (fread(&ut, sizeof(ut), 1, fp) == 1) 25138a99942STim J. Robbins if (*ut.ut_name != '\0' && strncmp(ut.ut_line, tty, 25238a99942STim J. Robbins UT_LINESIZE) == 0) { 25338a99942STim J. Robbins row(&ut); 25438a99942STim J. Robbins return; 25538a99942STim J. Robbins } 25638a99942STim J. Robbins 25738a99942STim J. Robbins /* Not found; fill the utmp structure with the information we have. */ 25838a99942STim J. Robbins memset(&ut, 0, sizeof(ut)); 25938a99942STim J. Robbins if ((pwd = getpwuid(getuid())) != NULL) 26038a99942STim J. Robbins name = pwd->pw_name; 26138a99942STim J. Robbins else 26238a99942STim J. Robbins name = "?"; 26338a99942STim J. Robbins strncpy(ut.ut_name, name, UT_NAMESIZE); 26438a99942STim J. Robbins strncpy(ut.ut_line, tty, UT_LINESIZE); 26538a99942STim J. Robbins time(&ut.ut_time); 26638a99942STim J. Robbins row(&ut); 26738a99942STim J. Robbins } 26838a99942STim J. Robbins 26938a99942STim J. Robbins int 27038a99942STim J. Robbins ttywidth(void) 27138a99942STim J. Robbins { 27238a99942STim J. Robbins struct winsize ws; 27338a99942STim J. Robbins int width; 27438a99942STim J. Robbins 27538a99942STim J. Robbins if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) 27638a99942STim J. Robbins width = ws.ws_col; 27738a99942STim J. Robbins else 27838a99942STim J. Robbins width = 80; 27938a99942STim J. Robbins 28038a99942STim J. Robbins return (width); 2819b50d902SRodney W. Grimes } 282