196299Stjr/*- 296299Stjr * Copyright (c) 2002 Tim J. Robbins. 396299Stjr * 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 * 1496299Stjr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 151590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1796299Stjr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 181590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 191590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 201590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 211590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 221590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241590Srgrimes * SUCH DAMAGE. 251590Srgrimes */ 261590Srgrimes 2787679Smarkm#include <sys/cdefs.h> 2887679Smarkm__FBSDID("$FreeBSD: releng/10.2/usr.bin/who/who.c 231537 2012-02-11 21:50:44Z ed $"); 2987679Smarkm 30155875Scognet#include <sys/param.h> 311590Srgrimes#include <sys/types.h> 3296299Stjr#include <sys/ioctl.h> 3396299Stjr#include <sys/stat.h> 3487679Smarkm 3528791Scharnier#include <err.h> 3697800Stjr#include <errno.h> 3774589Sache#include <langinfo.h> 3897800Stjr#include <limits.h> 3928791Scharnier#include <locale.h> 4096299Stjr#include <paths.h> 4128791Scharnier#include <pwd.h> 4228791Scharnier#include <stdio.h> 4378718Sdd#include <stdlib.h> 4428791Scharnier#include <string.h> 459990Sache#include <time.h> 46200462Sdelphij#include <timeconv.h> 4728791Scharnier#include <unistd.h> 48202200Sed#include <utmpx.h> 491590Srgrimes 5096299Stjrstatic void heading(void); 51200166Sedstatic void process_utmp(void); 52200166Sedstatic void quick(void); 53226881Spluknetstatic void row(const struct utmpx *); 5496299Stjrstatic int ttywidth(void); 5596299Stjrstatic void usage(void); 56200166Sedstatic void whoami(void); 5728791Scharnier 5896299Stjrstatic int Hflag; /* Write column headings */ 59231536Sedstatic int aflag; /* Print all entries */ 60226881Spluknetstatic int bflag; /* Show date of the last reboot */ 6196299Stjrstatic int mflag; /* Show info about current terminal */ 6296299Stjrstatic int qflag; /* "Quick" mode */ 6396299Stjrstatic int sflag; /* Show name, line, time */ 6496299Stjrstatic int Tflag; /* Show terminal state */ 6596299Stjrstatic int uflag; /* Show idle time */ 6696299Stjr 6728791Scharnierint 6896299Stjrmain(int argc, char *argv[]) 691590Srgrimes{ 7096299Stjr int ch; 711590Srgrimes 7296299Stjr setlocale(LC_TIME, ""); 7311757Sache 74231536Sed while ((ch = getopt(argc, argv, "HTabmqsu")) != -1) { 7596299Stjr switch (ch) { 7696299Stjr case 'H': /* Write column headings */ 7796299Stjr Hflag = 1; 7896299Stjr break; 7996299Stjr case 'T': /* Show terminal state */ 8096299Stjr Tflag = 1; 8196299Stjr break; 82231536Sed case 'a': /* Same as -bdlprtTu */ 83231536Sed aflag = bflag = Tflag = uflag = 1; 84231536Sed break; 85226881Spluknet case 'b': /* Show date of the last reboot */ 86226881Spluknet bflag = 1; 87226881Spluknet break; 8896299Stjr case 'm': /* Show info about current terminal */ 8996299Stjr mflag = 1; 9096299Stjr break; 9196299Stjr case 'q': /* "Quick" mode */ 9296299Stjr qflag = 1; 9396299Stjr break; 9496299Stjr case 's': /* Show name, line, time */ 9596299Stjr sflag = 1; 9696299Stjr break; 9796299Stjr case 'u': /* Show idle time */ 9896299Stjr uflag = 1; 9996299Stjr break; 10096299Stjr default: 10196299Stjr usage(); 10296299Stjr /*NOTREACHED*/ 10396299Stjr } 10496299Stjr } 10596299Stjr argc -= optind; 10696299Stjr argv += optind; 1071590Srgrimes 10896299Stjr if (argc >= 2 && strcmp(argv[0], "am") == 0 && 10996299Stjr (strcmp(argv[1], "i") == 0 || strcmp(argv[1], "I") == 0)) { 11096299Stjr /* "who am i" or "who am I", equivalent to -m */ 11196299Stjr mflag = 1; 11296299Stjr argc -= 2; 11396299Stjr argv += 2; 11496299Stjr } 11596299Stjr if (argc > 1) 11628791Scharnier usage(); 11796299Stjr 118200166Sed if (*argv != NULL) { 119202200Sed if (setutxdb(UTXDB_ACTIVE, *argv) != 0) 120200166Sed err(1, "%s", *argv); 121200166Sed } 12296299Stjr 12396299Stjr if (qflag) 124200166Sed quick(); 12596299Stjr else { 12696299Stjr if (sflag) 12796299Stjr Tflag = uflag = 0; 12896299Stjr if (Hflag) 12996299Stjr heading(); 13096299Stjr if (mflag) 131200166Sed whoami(); 13296299Stjr else 133200166Sed process_utmp(); 1341590Srgrimes } 13596299Stjr 136200166Sed endutxent(); 13796299Stjr 1381590Srgrimes exit(0); 1391590Srgrimes} 1401590Srgrimes 14198483Stjrstatic void 14296299Stjrusage(void) 14328791Scharnier{ 14496299Stjr 145231537Sed fprintf(stderr, "usage: who [-abHmqsTu] [am I] [file]\n"); 14628791Scharnier exit(1); 14728791Scharnier} 14828791Scharnier 14998483Stjrstatic void 15096299Stjrheading(void) 1511590Srgrimes{ 15296299Stjr 153200166Sed printf("%-16s ", "NAME"); 15496299Stjr if (Tflag) 15596299Stjr printf("S "); 156226881Spluknet printf("%-12s %-12s ", "LINE", "TIME"); 15796299Stjr if (uflag) 15896299Stjr printf("IDLE "); 159200166Sed printf("%-16s\n", "FROM"); 16096299Stjr} 16196299Stjr 16298483Stjrstatic void 163226881Spluknetrow(const struct utmpx *ut) 16496299Stjr{ 165200166Sed char buf[80], tty[PATH_MAX]; 16696299Stjr struct stat sb; 167106966Speter time_t idle, t; 16874589Sache static int d_first = -1; 16996299Stjr struct tm *tm; 17096299Stjr char state; 1711590Srgrimes 17274589Sache if (d_first < 0) 17374589Sache d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); 17474589Sache 175146753Scharnier state = '?'; 176146753Scharnier idle = 0; 17796299Stjr if (Tflag || uflag) { 178200166Sed snprintf(tty, sizeof(tty), "%s%s", _PATH_DEV, ut->ut_line); 17996299Stjr if (stat(tty, &sb) == 0) { 18096299Stjr state = sb.st_mode & (S_IWOTH|S_IWGRP) ? 18196299Stjr '+' : '-'; 18296299Stjr idle = time(NULL) - sb.st_mtime; 18396299Stjr } 18496299Stjr } 18596299Stjr 186200166Sed printf("%-16s ", ut->ut_user); 18796299Stjr if (Tflag) 18896299Stjr printf("%c ", state); 189226881Spluknet if (ut->ut_type == BOOT_TIME) 190226881Spluknet printf("%-12s ", "system boot"); 191226881Spluknet else 192226881Spluknet printf("%-12s ", ut->ut_line); 193200166Sed t = ut->ut_tv.tv_sec; 194106966Speter tm = localtime(&t); 19596299Stjr strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm); 19696299Stjr printf("%-*s ", 12, buf); 19796299Stjr if (uflag) { 19896299Stjr if (idle < 60) 19996299Stjr printf(" . "); 20096299Stjr else if (idle < 24 * 60 * 60) 20196299Stjr printf("%02d:%02d ", (int)(idle / 60 / 60), 20296299Stjr (int)(idle / 60 % 60)); 20396299Stjr else 20496299Stjr printf(" old "); 20596299Stjr } 20696299Stjr if (*ut->ut_host != '\0') 207200166Sed printf("(%s)", ut->ut_host); 20896299Stjr putchar('\n'); 2091590Srgrimes} 2101590Srgrimes 211155875Scognetstatic int 212200166Sedttystat(char *line) 213155875Scognet{ 214155875Scognet struct stat sb; 215155875Scognet char ttybuf[MAXPATHLEN]; 216155875Scognet 217200166Sed (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line); 218155875Scognet if (stat(ttybuf, &sb) == 0) { 219155875Scognet return (0); 220155875Scognet } else 221155875Scognet return (-1); 222155875Scognet} 223155875Scognet 22498483Stjrstatic void 225200166Sedprocess_utmp(void) 2261590Srgrimes{ 227200166Sed struct utmpx *utx; 2281590Srgrimes 229200166Sed while ((utx = getutxent()) != NULL) { 230231536Sed if (((aflag || !bflag) && utx->ut_type == USER_PROCESS) || 231231536Sed (bflag && utx->ut_type == BOOT_TIME)) 232231536Sed if (ttystat(utx->ut_line) == 0) 233231536Sed row(utx); 234155875Scognet } 2351590Srgrimes} 23696299Stjr 23798483Stjrstatic void 238200166Sedquick(void) 23996299Stjr{ 240200166Sed struct utmpx *utx; 24196299Stjr int col, ncols, num; 24296299Stjr 24396299Stjr ncols = ttywidth(); 24496299Stjr col = num = 0; 245200166Sed while ((utx = getutxent()) != NULL) { 246200166Sed if (utx->ut_type != USER_PROCESS) 24796299Stjr continue; 248200166Sed printf("%-16s", utx->ut_user); 249200166Sed if (++col < ncols / (16 + 1)) 25096299Stjr putchar(' '); 25196299Stjr else { 25296299Stjr col = 0; 25396299Stjr putchar('\n'); 25496299Stjr } 25596299Stjr num++; 25696299Stjr } 25796299Stjr if (col != 0) 25896299Stjr putchar('\n'); 25996299Stjr 26096299Stjr printf("# users = %d\n", num); 26196299Stjr} 26296299Stjr 26398483Stjrstatic void 264200166Sedwhoami(void) 26596299Stjr{ 266200166Sed struct utmpx ut, *utx; 26796299Stjr struct passwd *pwd; 268200166Sed const char *name, *tty; 26996299Stjr 27096299Stjr if ((tty = ttyname(STDIN_FILENO)) == NULL) 27196299Stjr tty = "tty??"; 272200166Sed else if (strncmp(tty, _PATH_DEV, sizeof _PATH_DEV - 1) == 0) 273200166Sed tty += sizeof _PATH_DEV - 1; 274200166Sed strlcpy(ut.ut_line, tty, sizeof ut.ut_line); 27596299Stjr 27696299Stjr /* Search utmp for our tty, dump first matching record. */ 277200166Sed if ((utx = getutxline(&ut)) != NULL && utx->ut_type == USER_PROCESS) { 278200166Sed row(utx); 279200166Sed return; 280200166Sed } 28196299Stjr 28296299Stjr /* Not found; fill the utmp structure with the information we have. */ 28396299Stjr memset(&ut, 0, sizeof(ut)); 28496299Stjr if ((pwd = getpwuid(getuid())) != NULL) 28596299Stjr name = pwd->pw_name; 28696299Stjr else 28796299Stjr name = "?"; 288200166Sed strlcpy(ut.ut_user, name, sizeof ut.ut_user); 289200166Sed gettimeofday(&ut.ut_tv, NULL); 29096299Stjr row(&ut); 29196299Stjr} 29296299Stjr 29398483Stjrstatic int 29496299Stjrttywidth(void) 29596299Stjr{ 29696299Stjr struct winsize ws; 29797800Stjr long width; 29897800Stjr char *cols, *ep; 29996299Stjr 30097800Stjr if ((cols = getenv("COLUMNS")) != NULL && *cols != '\0') { 30197800Stjr errno = 0; 30297800Stjr width = strtol(cols, &ep, 10); 30397800Stjr if (errno || width <= 0 || width > INT_MAX || ep == cols || 30497800Stjr *ep != '\0') 30597800Stjr warnx("invalid COLUMNS environment variable ignored"); 30697800Stjr else 307121548Speter return (width); 30897800Stjr } 30996299Stjr if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) 31097800Stjr return (ws.ws_col); 31196299Stjr 31297800Stjr return (80); 31396299Stjr} 314