PageRenderTime 61ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/src/freebsd/usr.bin/w/w.c

https://bitbucket.org/killerpenguinassassins/open_distrib_devel
C | 528 lines | 408 code | 44 blank | 76 comment | 129 complexity | 37de475983075b420b700ae4890f7dee MD5 | raw file
Possible License(s): CC0-1.0, MIT, LGPL-2.0, LGPL-3.0, WTFPL, GPL-2.0, BSD-2-Clause, AGPL-3.0, CC-BY-SA-3.0, MPL-2.0, JSON, BSD-3-Clause-No-Nuclear-License-2014, LGPL-2.1, CPL-1.0, AGPL-1.0, 0BSD, ISC, Apache-2.0, GPL-3.0, IPL-1.0, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. /*-
  2. * Copyright (c) 1980, 1991, 1993, 1994
  3. * The Regents of the University of California. All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 4. Neither the name of the University nor the names of its contributors
  14. * may be used to endorse or promote products derived from this software
  15. * without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  18. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  21. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27. * SUCH DAMAGE.
  28. */
  29. #include <sys/cdefs.h>
  30. __FBSDID("$FreeBSD$");
  31. #ifndef lint
  32. static const char copyright[] =
  33. "@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
  34. The Regents of the University of California. All rights reserved.\n";
  35. #endif
  36. #ifndef lint
  37. static const char sccsid[] = "@(#)w.c 8.4 (Berkeley) 4/16/94";
  38. #endif
  39. /*
  40. * w - print system status (who and what)
  41. *
  42. * This program is similar to the systat command on Tenex/Tops 10/20
  43. *
  44. */
  45. #include <sys/param.h>
  46. #include <sys/time.h>
  47. #include <sys/stat.h>
  48. #include <sys/sysctl.h>
  49. #include <sys/proc.h>
  50. #include <sys/user.h>
  51. #include <sys/ioctl.h>
  52. #include <sys/socket.h>
  53. #include <sys/tty.h>
  54. #include <machine/cpu.h>
  55. #include <netinet/in.h>
  56. #include <arpa/inet.h>
  57. #include <arpa/nameser.h>
  58. #include <ctype.h>
  59. #include <err.h>
  60. #include <errno.h>
  61. #include <fcntl.h>
  62. #include <kvm.h>
  63. #include <langinfo.h>
  64. #include <libutil.h>
  65. #include <limits.h>
  66. #include <locale.h>
  67. #include <netdb.h>
  68. #include <nlist.h>
  69. #include <paths.h>
  70. #include <resolv.h>
  71. #include <stdio.h>
  72. #include <stdlib.h>
  73. #include <string.h>
  74. #include <timeconv.h>
  75. #include <unistd.h>
  76. #include <utmpx.h>
  77. #include <vis.h>
  78. #include "extern.h"
  79. static struct utmpx *utmp;
  80. static struct winsize ws;
  81. static kvm_t *kd;
  82. static time_t now; /* the current time of day */
  83. static int ttywidth; /* width of tty */
  84. static int argwidth; /* width of tty */
  85. static int header = 1; /* true if -h flag: don't print heading */
  86. static int nflag; /* true if -n flag: don't convert addrs */
  87. static int dflag; /* true if -d flag: output debug info */
  88. static int sortidle; /* sort by idle time */
  89. int use_ampm; /* use AM/PM time */
  90. static int use_comma; /* use comma as floats separator */
  91. static char **sel_users; /* login array of particular users selected */
  92. /*
  93. * One of these per active utmp entry.
  94. */
  95. static struct entry {
  96. struct entry *next;
  97. struct utmpx utmp;
  98. dev_t tdev; /* dev_t of terminal */
  99. time_t idle; /* idle time of terminal in seconds */
  100. struct kinfo_proc *kp; /* `most interesting' proc */
  101. char *args; /* arg list of interesting process */
  102. struct kinfo_proc *dkp; /* debug option proc list */
  103. } *ep, *ehead = NULL, **nextp = &ehead;
  104. #define debugproc(p) *(&((struct kinfo_proc *)p)->ki_udata)
  105. #define W_DISPUSERSIZE 10
  106. #define W_DISPLINESIZE 8
  107. #define W_DISPHOSTSIZE 24
  108. static void pr_header(time_t *, int);
  109. static struct stat *ttystat(char *);
  110. static void usage(int);
  111. static int this_is_uptime(const char *s);
  112. char *fmt_argv(char **, char *, int); /* ../../bin/ps/fmt.c */
  113. int
  114. main(int argc, char *argv[])
  115. {
  116. struct kinfo_proc *kp;
  117. struct kinfo_proc *dkp;
  118. struct stat *stp;
  119. time_t touched;
  120. int ch, i, nentries, nusers, wcmd, longidle, longattime, dropgid;
  121. const char *memf, *nlistf, *p;
  122. char *x_suffix;
  123. char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX];
  124. char fn[MAXHOSTNAMELEN];
  125. char *dot;
  126. (void)setlocale(LC_ALL, "");
  127. use_ampm = (*nl_langinfo(T_FMT_AMPM) != '\0');
  128. use_comma = (*nl_langinfo(RADIXCHAR) != ',');
  129. /* Are we w(1) or uptime(1)? */
  130. if (this_is_uptime(argv[0]) == 0) {
  131. wcmd = 0;
  132. p = "";
  133. } else {
  134. wcmd = 1;
  135. p = "dhiflM:N:nsuw";
  136. }
  137. dropgid = 0;
  138. memf = _PATH_DEVNULL;
  139. nlistf = NULL;
  140. while ((ch = getopt(argc, argv, p)) != -1)
  141. switch (ch) {
  142. case 'd':
  143. dflag = 1;
  144. break;
  145. case 'h':
  146. header = 0;
  147. break;
  148. case 'i':
  149. sortidle = 1;
  150. break;
  151. case 'M':
  152. header = 0;
  153. memf = optarg;
  154. dropgid = 1;
  155. break;
  156. case 'N':
  157. nlistf = optarg;
  158. dropgid = 1;
  159. break;
  160. case 'n':
  161. nflag = 1;
  162. break;
  163. case 'f': case 'l': case 's': case 'u': case 'w':
  164. warnx("[-flsuw] no longer supported");
  165. /* FALLTHROUGH */
  166. case '?':
  167. default:
  168. usage(wcmd);
  169. }
  170. argc -= optind;
  171. argv += optind;
  172. if (!(_res.options & RES_INIT))
  173. res_init();
  174. _res.retrans = 2; /* resolver timeout to 2 seconds per try */
  175. _res.retry = 1; /* only try once.. */
  176. /*
  177. * Discard setgid privileges if not the running kernel so that bad
  178. * guys can't print interesting stuff from kernel memory.
  179. */
  180. if (dropgid)
  181. setgid(getgid());
  182. if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL)
  183. errx(1, "%s", errbuf);
  184. (void)time(&now);
  185. if (*argv)
  186. sel_users = argv;
  187. setutxent();
  188. for (nusers = 0; (utmp = getutxent()) != NULL;) {
  189. if (utmp->ut_type != USER_PROCESS)
  190. continue;
  191. if (!(stp = ttystat(utmp->ut_line)))
  192. continue; /* corrupted record */
  193. ++nusers;
  194. if (wcmd == 0)
  195. continue;
  196. if (sel_users) {
  197. int usermatch;
  198. char **user;
  199. usermatch = 0;
  200. for (user = sel_users; !usermatch && *user; user++)
  201. if (!strcmp(utmp->ut_user, *user))
  202. usermatch = 1;
  203. if (!usermatch)
  204. continue;
  205. }
  206. if ((ep = calloc(1, sizeof(struct entry))) == NULL)
  207. errx(1, "calloc");
  208. *nextp = ep;
  209. nextp = &ep->next;
  210. memmove(&ep->utmp, utmp, sizeof *utmp);
  211. ep->tdev = stp->st_rdev;
  212. /*
  213. * If this is the console device, attempt to ascertain
  214. * the true console device dev_t.
  215. */
  216. if (ep->tdev == 0) {
  217. size_t size;
  218. size = sizeof(dev_t);
  219. (void)sysctlbyname("machdep.consdev", &ep->tdev, &size, NULL, 0);
  220. }
  221. touched = stp->st_atime;
  222. if (touched < ep->utmp.ut_tv.tv_sec) {
  223. /* tty untouched since before login */
  224. touched = ep->utmp.ut_tv.tv_sec;
  225. }
  226. if ((ep->idle = now - touched) < 0)
  227. ep->idle = 0;
  228. }
  229. endutxent();
  230. if (header || wcmd == 0) {
  231. pr_header(&now, nusers);
  232. if (wcmd == 0) {
  233. (void)kvm_close(kd);
  234. exit(0);
  235. }
  236. #define HEADER_USER "USER"
  237. #define HEADER_TTY "TTY"
  238. #define HEADER_FROM "FROM"
  239. #define HEADER_LOGIN_IDLE "LOGIN@ IDLE "
  240. #define HEADER_WHAT "WHAT\n"
  241. #define WUSED (W_DISPUSERSIZE + W_DISPLINESIZE + W_DISPHOSTSIZE + \
  242. sizeof(HEADER_LOGIN_IDLE) + 3) /* header width incl. spaces */
  243. (void)printf("%-*.*s %-*.*s %-*.*s %s",
  244. W_DISPUSERSIZE, W_DISPUSERSIZE, HEADER_USER,
  245. W_DISPLINESIZE, W_DISPLINESIZE, HEADER_TTY,
  246. W_DISPHOSTSIZE, W_DISPHOSTSIZE, HEADER_FROM,
  247. HEADER_LOGIN_IDLE HEADER_WHAT);
  248. }
  249. if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL)
  250. err(1, "%s", kvm_geterr(kd));
  251. for (i = 0; i < nentries; i++, kp++) {
  252. if (kp->ki_stat == SIDL || kp->ki_stat == SZOMB ||
  253. kp->ki_tdev == NODEV)
  254. continue;
  255. for (ep = ehead; ep != NULL; ep = ep->next) {
  256. if (ep->tdev == kp->ki_tdev) {
  257. /*
  258. * proc is associated with this terminal
  259. */
  260. if (ep->kp == NULL && kp->ki_pgid == kp->ki_tpgid) {
  261. /*
  262. * Proc is 'most interesting'
  263. */
  264. if (proc_compare(ep->kp, kp))
  265. ep->kp = kp;
  266. }
  267. /*
  268. * Proc debug option info; add to debug
  269. * list using kinfo_proc ki_spare[0]
  270. * as next pointer; ptr to ptr avoids the
  271. * ptr = long assumption.
  272. */
  273. dkp = ep->dkp;
  274. ep->dkp = kp;
  275. debugproc(kp) = dkp;
  276. }
  277. }
  278. }
  279. if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 &&
  280. ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 &&
  281. ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0)
  282. ttywidth = 79;
  283. else
  284. ttywidth = ws.ws_col - 1;
  285. argwidth = ttywidth - WUSED;
  286. if (argwidth < 4)
  287. argwidth = 8;
  288. for (ep = ehead; ep != NULL; ep = ep->next) {
  289. if (ep->kp == NULL) {
  290. ep->args = strdup("-");
  291. continue;
  292. }
  293. ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth),
  294. ep->kp->ki_comm, MAXCOMLEN);
  295. if (ep->args == NULL)
  296. err(1, NULL);
  297. }
  298. /* sort by idle time */
  299. if (sortidle && ehead != NULL) {
  300. struct entry *from, *save;
  301. from = ehead;
  302. ehead = NULL;
  303. while (from != NULL) {
  304. for (nextp = &ehead;
  305. (*nextp) && from->idle >= (*nextp)->idle;
  306. nextp = &(*nextp)->next)
  307. continue;
  308. save = from;
  309. from = from->next;
  310. save->next = *nextp;
  311. *nextp = save;
  312. }
  313. }
  314. for (ep = ehead; ep != NULL; ep = ep->next) {
  315. struct addrinfo hints, *res;
  316. struct sockaddr_storage ss;
  317. struct sockaddr *sa = (struct sockaddr *)&ss;
  318. struct sockaddr_in *lsin = (struct sockaddr_in *)&ss;
  319. struct sockaddr_in6 *lsin6 = (struct sockaddr_in6 *)&ss;
  320. time_t t;
  321. int isaddr;
  322. p = *ep->utmp.ut_host ? ep->utmp.ut_host : "-";
  323. if ((x_suffix = strrchr(p, ':')) != NULL) {
  324. if ((dot = strchr(x_suffix, '.')) != NULL &&
  325. strchr(dot+1, '.') == NULL)
  326. *x_suffix++ = '\0';
  327. else
  328. x_suffix = NULL;
  329. }
  330. isaddr = 0;
  331. memset(&ss, '\0', sizeof(ss));
  332. if (inet_pton(AF_INET6, p, &lsin6->sin6_addr) == 1) {
  333. lsin6->sin6_len = sizeof(*lsin6);
  334. lsin6->sin6_family = AF_INET6;
  335. isaddr = 1;
  336. } else if (inet_pton(AF_INET, p, &lsin->sin_addr) == 1) {
  337. lsin->sin_len = sizeof(*lsin);
  338. lsin->sin_family = AF_INET;
  339. isaddr = 1;
  340. }
  341. if (!nflag) {
  342. /* Attempt to change an IP address into a name */
  343. if (isaddr && realhostname_sa(fn, sizeof(fn), sa,
  344. sa->sa_len) == HOSTNAME_FOUND)
  345. p = fn;
  346. } else if (!isaddr) {
  347. /*
  348. * If a host has only one A/AAAA RR, change a
  349. * name into an IP address
  350. */
  351. memset(&hints, 0, sizeof(hints));
  352. hints.ai_flags = AI_PASSIVE;
  353. hints.ai_family = AF_UNSPEC;
  354. hints.ai_socktype = SOCK_STREAM;
  355. if (getaddrinfo(p, NULL, &hints, &res) == 0) {
  356. if (res->ai_next == NULL &&
  357. getnameinfo(res->ai_addr, res->ai_addrlen,
  358. fn, sizeof(fn), NULL, 0,
  359. NI_NUMERICHOST) == 0)
  360. p = fn;
  361. freeaddrinfo(res);
  362. }
  363. }
  364. if (x_suffix) {
  365. (void)snprintf(buf, sizeof(buf), "%s:%s", p, x_suffix);
  366. p = buf;
  367. }
  368. if (dflag) {
  369. for (dkp = ep->dkp; dkp != NULL; dkp = debugproc(dkp)) {
  370. const char *ptr;
  371. ptr = fmt_argv(kvm_getargv(kd, dkp, argwidth),
  372. dkp->ki_comm, MAXCOMLEN);
  373. if (ptr == NULL)
  374. ptr = "-";
  375. (void)printf("\t\t%-9d %s\n",
  376. dkp->ki_pid, ptr);
  377. }
  378. }
  379. (void)printf("%-*.*s %-*.*s %-*.*s ",
  380. W_DISPUSERSIZE, W_DISPUSERSIZE, ep->utmp.ut_user,
  381. W_DISPLINESIZE, W_DISPLINESIZE,
  382. *ep->utmp.ut_line ?
  383. (strncmp(ep->utmp.ut_line, "tty", 3) &&
  384. strncmp(ep->utmp.ut_line, "cua", 3) ?
  385. ep->utmp.ut_line : ep->utmp.ut_line + 3) : "-",
  386. W_DISPHOSTSIZE, W_DISPHOSTSIZE, *p ? p : "-");
  387. t = ep->utmp.ut_tv.tv_sec;
  388. longattime = pr_attime(&t, &now);
  389. longidle = pr_idle(ep->idle);
  390. (void)printf("%.*s\n", argwidth - longidle - longattime,
  391. ep->args);
  392. }
  393. (void)kvm_close(kd);
  394. exit(0);
  395. }
  396. static void
  397. pr_header(time_t *nowp, int nusers)
  398. {
  399. double avenrun[3];
  400. time_t uptime;
  401. struct timespec tp;
  402. int days, hrs, i, mins, secs;
  403. char buf[256];
  404. /*
  405. * Print time of day.
  406. */
  407. if (strftime(buf, sizeof(buf),
  408. use_ampm ? "%l:%M%p" : "%k:%M", localtime(nowp)) != 0)
  409. (void)printf("%s ", buf);
  410. /*
  411. * Print how long system has been up.
  412. */
  413. if (clock_gettime(CLOCK_MONOTONIC, &tp) != -1) {
  414. uptime = tp.tv_sec;
  415. if (uptime > 60)
  416. uptime += 30;
  417. days = uptime / 86400;
  418. uptime %= 86400;
  419. hrs = uptime / 3600;
  420. uptime %= 3600;
  421. mins = uptime / 60;
  422. secs = uptime % 60;
  423. (void)printf(" up");
  424. if (days > 0)
  425. (void)printf(" %d day%s,", days, days > 1 ? "s" : "");
  426. if (hrs > 0 && mins > 0)
  427. (void)printf(" %2d:%02d,", hrs, mins);
  428. else if (hrs > 0)
  429. (void)printf(" %d hr%s,", hrs, hrs > 1 ? "s" : "");
  430. else if (mins > 0)
  431. (void)printf(" %d min%s,", mins, mins > 1 ? "s" : "");
  432. else
  433. (void)printf(" %d sec%s,", secs, secs > 1 ? "s" : "");
  434. }
  435. /* Print number of users logged in to system */
  436. (void)printf(" %d user%s", nusers, nusers == 1 ? "" : "s");
  437. /*
  438. * Print 1, 5, and 15 minute load averages.
  439. */
  440. if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1)
  441. (void)printf(", no load average information available\n");
  442. else {
  443. (void)printf(", load averages:");
  444. for (i = 0; i < (int)(sizeof(avenrun) / sizeof(avenrun[0])); i++) {
  445. if (use_comma && i > 0)
  446. (void)printf(",");
  447. (void)printf(" %.2f", avenrun[i]);
  448. }
  449. (void)printf("\n");
  450. }
  451. }
  452. static struct stat *
  453. ttystat(char *line)
  454. {
  455. static struct stat sb;
  456. char ttybuf[MAXPATHLEN];
  457. (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line);
  458. if (stat(ttybuf, &sb) == 0 && S_ISCHR(sb.st_mode)) {
  459. return (&sb);
  460. } else
  461. return (NULL);
  462. }
  463. static void
  464. usage(int wcmd)
  465. {
  466. if (wcmd)
  467. (void)fprintf(stderr,
  468. "usage: w [-dhin] [-M core] [-N system] [user ...]\n");
  469. else
  470. (void)fprintf(stderr, "usage: uptime\n");
  471. exit(1);
  472. }
  473. static int
  474. this_is_uptime(const char *s)
  475. {
  476. const char *u;
  477. if ((u = strrchr(s, '/')) != NULL)
  478. ++u;
  479. else
  480. u = s;
  481. if (strcmp(u, "uptime") == 0)
  482. return (0);
  483. return (-1);
  484. }