GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/w/w.c Lines: 0 199 0.0 %
Date: 2016-12-06 Branches: 0 179 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: w.c,v 1.61 2016/03/19 00:11:49 deraadt Exp $	*/
2
3
/*-
4
 * Copyright (c) 1980, 1991, 1993, 1994
5
 *	The Regents of the University of California.  All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 * 3. Neither the name of the University nor the names of its contributors
16
 *    may be used to endorse or promote products derived from this software
17
 *    without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
 * SUCH DAMAGE.
30
 */
31
32
/*
33
 * w - print system status (who and what)
34
 *
35
 * This program is similar to the systat command on Tenex/Tops 10/20
36
 *
37
 */
38
#include <sys/param.h>	/* MAXCOMLEN */
39
#include <sys/time.h>
40
#include <sys/stat.h>
41
#include <sys/sysctl.h>
42
#include <sys/signal.h>
43
#include <sys/proc.h>
44
#include <sys/ioctl.h>
45
#include <sys/socket.h>
46
#include <sys/tty.h>
47
48
#include <netinet/in.h>
49
#include <arpa/inet.h>
50
51
#include <ctype.h>
52
#include <err.h>
53
#include <errno.h>
54
#include <fcntl.h>
55
#include <kvm.h>
56
#include <netdb.h>
57
#include <nlist.h>
58
#include <paths.h>
59
#include <stdio.h>
60
#include <stdlib.h>
61
#include <string.h>
62
#include <unistd.h>
63
#include <limits.h>
64
#include <utmp.h>
65
#include <vis.h>
66
67
#include "extern.h"
68
69
struct timeval	boottime;
70
struct utmp	utmp;
71
struct winsize	ws;
72
kvm_t	       *kd;
73
time_t		now;		/* the current time of day */
74
int		ttywidth;	/* width of tty */
75
int		argwidth;	/* width of tty */
76
int		header = 1;	/* true if -h flag: don't print heading */
77
int		nflag = 1;	/* true if -n flag: don't convert addrs */
78
int		sortidle;	/* sort by idle time */
79
char	       *sel_user;	/* login of particular user selected */
80
char		domain[HOST_NAME_MAX+1];
81
82
#define	NAME_WIDTH	8
83
#define HOST_WIDTH	16
84
85
/*
86
 * One of these per active utmp entry.
87
 */
88
struct	entry {
89
	struct	entry *next;
90
	struct	utmp utmp;
91
	dev_t	tdev;			/* dev_t of terminal */
92
	time_t	idle;			/* idle time of terminal in seconds */
93
	struct	kinfo_proc *kp;		/* `most interesting' proc */
94
} *ep, *ehead = NULL, **nextp = &ehead;
95
96
static void	 pr_args(struct kinfo_proc *);
97
static void	 pr_header(time_t *, int);
98
static struct stat
99
		*ttystat(char *);
100
static void	 usage(int);
101
102
int
103
main(int argc, char *argv[])
104
{
105
	extern char *__progname;
106
	struct kinfo_proc *kp;
107
	struct hostent *hp;
108
	struct stat *stp;
109
	FILE *ut;
110
	struct in_addr addr;
111
	int ch, i, nentries, nusers, wcmd;
112
	char *memf, *nlistf, *p, *x;
113
	char buf[HOST_NAME_MAX+1], errbuf[_POSIX2_LINE_MAX];
114
115
	/* Are we w(1) or uptime(1)? */
116
	p = __progname;
117
	if (*p == '-')
118
		p++;
119
	if (p[0] == 'w' && p[1] == '\0') {
120
		wcmd = 1;
121
		p = "hiflM:N:asuw";
122
	} else if (!strcmp(p, "uptime")) {
123
		wcmd = 0;
124
		p = "";
125
	} else
126
		errx(1,
127
		 "this program should be invoked only as \"w\" or \"uptime\"");
128
129
	memf = nlistf = NULL;
130
	while ((ch = getopt(argc, argv, p)) != -1)
131
		switch (ch) {
132
		case 'h':
133
			header = 0;
134
			break;
135
		case 'i':
136
			sortidle = 1;
137
			break;
138
		case 'M':
139
			header = 0;
140
			memf = optarg;
141
			break;
142
		case 'N':
143
			nlistf = optarg;
144
			break;
145
		case 'a':
146
			nflag = 0;
147
			break;
148
		case 'f': case 'l': case 's': case 'u': case 'w':
149
			warnx("[-flsuw] no longer supported");
150
			/* FALLTHROUGH */
151
		case '?':
152
		default:
153
			usage(wcmd);
154
		}
155
	argc -= optind;
156
	argv += optind;
157
158
	if (nflag == 0) {
159
		if (pledge("stdio tty rpath dns ps vminfo wpath cpath", NULL) == -1)
160
			err(1, "pledge");
161
	} else {
162
		if (pledge("stdio tty rpath ps vminfo wpath cpath", NULL) == -1)
163
			err(1, "pledge");
164
	}
165
166
	if (nlistf == NULL && memf == NULL) {
167
		if ((kd = kvm_openfiles(nlistf, memf, NULL, KVM_NO_FILES,
168
		    errbuf)) == NULL)
169
			errx(1, "%s", errbuf);
170
	} else {
171
		if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL)
172
			errx(1, "%s", errbuf);
173
	}
174
175
	(void)time(&now);
176
	if ((ut = fopen(_PATH_UTMP, "r")) == NULL)
177
		err(1, "%s", _PATH_UTMP);
178
179
	if (*argv)
180
		sel_user = *argv;
181
182
	for (nusers = 0; fread(&utmp, sizeof(utmp), 1, ut);) {
183
		if (utmp.ut_name[0] == '\0')
184
			continue;
185
		++nusers;
186
		if (wcmd == 0 || (sel_user &&
187
		    strncmp(utmp.ut_name, sel_user, UT_NAMESIZE) != 0))
188
			continue;
189
		if ((ep = calloc(1, sizeof(*ep))) == NULL)
190
			err(1, NULL);
191
		*nextp = ep;
192
		nextp = &(ep->next);
193
		memcpy(&(ep->utmp), &utmp, sizeof(utmp));
194
		if (!(stp = ttystat(ep->utmp.ut_line)))
195
			continue;
196
		ep->tdev = stp->st_rdev;
197
198
		/*
199
		 * If this is the console device, attempt to ascertain
200
		 * the true console device dev_t.
201
		 */
202
		if (ep->tdev == 0) {
203
			int mib[2];
204
			size_t size;
205
206
			mib[0] = CTL_KERN;
207
			mib[1] = KERN_CONSDEV;
208
			size = sizeof(dev_t);
209
			(void) sysctl(mib, 2, &ep->tdev, &size, NULL, 0);
210
		}
211
212
		if ((ep->idle = now - stp->st_atime) < 0)
213
			ep->idle = 0;
214
	}
215
	(void)fclose(ut);
216
217
	if (header || wcmd == 0) {
218
		pr_header(&now, nusers);
219
		if (wcmd == 0)
220
			exit (0);
221
	}
222
223
#define HEADER	"USER    TTY FROM              LOGIN@  IDLE WHAT"
224
#define WUSED	(sizeof(HEADER) - sizeof("WHAT"))
225
	(void)puts(HEADER);
226
227
	kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, sizeof(*kp), &nentries);
228
	if (kp == NULL)
229
		errx(1, "%s", kvm_geterr(kd));
230
231
	if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 &&
232
	    ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 &&
233
	    ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0)
234
		ttywidth = 79;
235
	else
236
		ttywidth = ws.ws_col - 1;
237
	argwidth = ttywidth - WUSED;
238
	if (argwidth < 4)
239
		argwidth = 8;
240
241
	for (i = 0; i < nentries; i++, kp++) {
242
		if (kp->p_psflags & (PS_EMBRYO | PS_ZOMBIE))
243
			continue;
244
		for (ep = ehead; ep != NULL; ep = ep->next) {
245
			/* ftp is a special case. */
246
			if (strncmp(ep->utmp.ut_line, "ftp", 3) == 0) {
247
				char pidstr[UT_LINESIZE-2];
248
				pid_t fp;
249
250
				(void)strncpy(pidstr, &ep->utmp.ut_line[3],
251
				    sizeof(pidstr) - 1);
252
				pidstr[sizeof(pidstr) - 1] = '\0';
253
				fp = (pid_t)strtol(pidstr, NULL, 10);
254
				if (kp->p_pid == fp) {
255
					ep->kp = kp;
256
					break;
257
				}
258
			} else if (ep->tdev == kp->p_tdev &&
259
			    kp->p__pgid == kp->p_tpgid) {
260
				/*
261
				 * Proc is in foreground of this terminal
262
				 */
263
				if (proc_compare(ep->kp, kp))
264
					ep->kp = kp;
265
				break;
266
			}
267
		}
268
	}
269
	/* sort by idle time */
270
	if (sortidle && ehead != NULL) {
271
		struct entry *from = ehead, *save;
272
273
		ehead = NULL;
274
		while (from != NULL) {
275
			for (nextp = &ehead;
276
			    (*nextp) && from->idle >= (*nextp)->idle;
277
			    nextp = &(*nextp)->next)
278
				continue;
279
			save = from;
280
			from = from->next;
281
			save->next = *nextp;
282
			*nextp = save;
283
		}
284
	}
285
286
	if (!nflag) {
287
		if (gethostname(domain, sizeof(domain)) < 0 ||
288
		    (p = strchr(domain, '.')) == 0)
289
			domain[0] = '\0';
290
		else {
291
			domain[sizeof(domain) - 1] = '\0';
292
			memmove(domain, p, strlen(p) + 1);
293
		}
294
	}
295
296
	for (ep = ehead; ep != NULL; ep = ep->next) {
297
		p = *ep->utmp.ut_host ? ep->utmp.ut_host : "-";
298
		for (x = NULL, i = 0; p[i] != '\0' && i < UT_HOSTSIZE; i++)
299
			if (p[i] == ':') {
300
				x = &p[i];
301
				*x++ = '\0';
302
				break;
303
			}
304
		if (!nflag && inet_aton(p, &addr) &&
305
		    (hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET))) {
306
			if (domain[0] != '\0') {
307
				p = hp->h_name;
308
				p += strlen(hp->h_name);
309
				p -= strlen(domain);
310
				if (p > hp->h_name &&
311
				    strcasecmp(p, domain) == 0)
312
					*p = '\0';
313
			}
314
			p = hp->h_name;
315
		}
316
		if (x) {
317
			(void)snprintf(buf, sizeof(buf), "%s:%.*s", p,
318
			    (int)(ep->utmp.ut_host + UT_HOSTSIZE - x), x);
319
			p = buf;
320
		}
321
		(void)printf("%-*.*s %-2.2s %-*.*s ",
322
		    NAME_WIDTH, UT_NAMESIZE, ep->utmp.ut_name,
323
		    strncmp(ep->utmp.ut_line, "tty", 3) ?
324
		    ep->utmp.ut_line : ep->utmp.ut_line + 3,
325
		    HOST_WIDTH, HOST_WIDTH, *p ? p : "-");
326
		pr_attime(&ep->utmp.ut_time, &now);
327
		pr_idle(ep->idle);
328
		pr_args(ep->kp);
329
		printf("\n");
330
	}
331
	exit(0);
332
}
333
334
static void
335
pr_args(struct kinfo_proc *kp)
336
{
337
	char **argv, *str;
338
	int left;
339
340
	if (kp == NULL)
341
		goto nothing;		/* XXX - can this happen? */
342
	left = argwidth;
343
	argv = kvm_getargv(kd, kp, argwidth+60);  /* +60 for ftpd snip */
344
	if (argv == NULL)
345
		goto nothing;
346
347
	if (*argv == NULL || **argv == '\0') {
348
		/* Process has zeroed argv[0], display executable name. */
349
		fmt_putc('(', &left);
350
		fmt_puts(kp->p_comm, &left);
351
		fmt_putc(')', &left);
352
	}
353
	while (*argv) {
354
		/*
355
		 * ftp argv[0] is in the following format:
356
		 * ftpd: HOSTNAME: [USER/PASS: ]CMD args (ftpd)
357
		 */
358
		if (strncmp(*argv, "ftpd:", 5) == 0) {
359
			if ((str = strchr(*argv + 5, ':')) != NULL)
360
				str = strchr(str + 1, ':');
361
			if (str != NULL) {
362
				if ((str[0] == ':') &&
363
				    isspace((unsigned char)str[1]))
364
					str += 2;
365
				fmt_puts(str, &left);
366
			} else
367
				fmt_puts(*argv, &left);
368
		} else
369
			fmt_puts(*argv, &left);
370
		argv++;
371
		fmt_putc(' ', &left);
372
	}
373
	return;
374
nothing:
375
	putchar('-');
376
}
377
378
static void
379
pr_header(time_t *nowp, int nusers)
380
{
381
	double avenrun[3];
382
	time_t uptime;
383
	int days, hrs, i, mins;
384
	int mib[2];
385
	size_t size;
386
	char buf[256];
387
388
	/*
389
	 * Print time of day.
390
	 */
391
	(void)strftime(buf, sizeof(buf) - 1, "%l:%M%p", localtime(nowp));
392
	buf[sizeof(buf) - 1] = '\0';
393
	(void)printf("%s ", buf);
394
395
	/*
396
	 * Print how long system has been up.
397
	 * (Found by getting "boottime" from the kernel)
398
	 */
399
	mib[0] = CTL_KERN;
400
	mib[1] = KERN_BOOTTIME;
401
	size = sizeof(boottime);
402
	if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1) {
403
		uptime = now - boottime.tv_sec;
404
		if (uptime > 59) {
405
			uptime += 30;
406
			days = uptime / SECSPERDAY;
407
			uptime %= SECSPERDAY;
408
			hrs = uptime / SECSPERHOUR;
409
			uptime %= SECSPERHOUR;
410
			mins = uptime / 60;
411
			(void)printf(" up");
412
			if (days > 0)
413
				(void)printf(" %d day%s,", days,
414
				    days > 1 ? "s" : "");
415
			if (hrs > 0 && mins > 0)
416
				(void)printf(" %2d:%02d,", hrs, mins);
417
			else {
418
				if (hrs > 0)
419
					(void)printf(" %d hr%s,",
420
					    hrs, hrs > 1 ? "s" : "");
421
				if (mins > 0 || (days == 0 && hrs == 0))
422
					(void)printf(" %d min%s,",
423
					    mins, mins != 1 ? "s" : "");
424
			}
425
		} else
426
			printf(" %d secs,", (int)uptime);
427
	}
428
429
	/* Print number of users logged in to system */
430
	(void)printf(" %d user%s", nusers, nusers != 1 ? "s" : "");
431
432
	/*
433
	 * Print 1, 5, and 15 minute load averages.
434
	 */
435
	if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1)
436
		(void)printf(", no load average information available\n");
437
	else {
438
		(void)printf(", load averages:");
439
		for (i = 0; i < (sizeof(avenrun) / sizeof(avenrun[0])); i++) {
440
			if (i > 0)
441
				(void)printf(",");
442
			(void)printf(" %.2f", avenrun[i]);
443
		}
444
		(void)printf("\n");
445
	}
446
}
447
448
static struct stat *
449
ttystat(char *line)
450
{
451
	static struct stat sb;
452
	char ttybuf[sizeof(_PATH_DEV) + UT_LINESIZE];
453
454
	/* Note, line may not be NUL-terminated */
455
	(void)strlcpy(ttybuf, _PATH_DEV, sizeof(ttybuf));
456
	(void)strncat(ttybuf, line, sizeof(ttybuf) - 1 - strlen(ttybuf));
457
	if (stat(ttybuf, &sb))
458
		return (NULL);
459
	return (&sb);
460
}
461
462
static void
463
usage(int wcmd)
464
{
465
	if (wcmd)
466
		(void)fprintf(stderr,
467
		    "usage: w [-ahi] [-M core] [-N system] [user]\n");
468
	else
469
		(void)fprintf(stderr,
470
		    "usage: uptime\n");
471
	exit (1);
472
}