who.c
时间:2006-07-31 来源:anima
1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
22 /* All Rights Reserved */
23
24
25 /*
26 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
28 */
29
30 #pragma ident "@(#)who.c 1.33 06/04/03 SMI"
31
32 /*
33 * This program analyzes information found in /var/adm/utmpx
34 *
35 * Additionally information is gathered from /etc/inittab
36 * if requested.
37 *
38 *
39 * Syntax:
40 *
41 * who am i Displays info on yourself
42 *
43 * who -a Displays information about All
44 * entries in /var/adm/utmpx
45 *
46 * who -b Displays info on last boot
47 *
48 * who -d Displays info on DEAD PROCESSES
49 *
50 * who -H Displays HEADERS for output
51 *
52 * who -l Displays info on LOGIN entries
53 *
54 * who -m Same as who am i
55 *
56 * who -p Displays info on PROCESSES spawned by init
57 *
58 * who -q Displays short information on
59 * current users who LOGGED ON
60 *
61 * who -r Displays info of current run-level
62 *
63 * who -s Displays requested info in SHORT form
64 *
65 * who -t Displays info on TIME changes
66 *
67 * who -T Displays writeability of each user
68 * (+ writeable, - non-writeable, ? hung)
69 *
70 * who -u Displays LONG info on users
71 * who have LOGGED ON
72 */
73
74 #define DATE_FMT "%b %e %H:%M"
75
76 /*
77 * %b Abbreviated month name
78 * %e Day of month
79 * %H hour (24-hour clock)
80 * %M minute
81 */
82 #include <errno.h>
83 #include <fcntl.h>
84 #include <stdio.h>
85 #include <string.h>
86 #include <sys/types.h>
87 #include <unistd.h>
88 #include <stdlib.h>
89 #include <sys/stat.h>
90 #include <time.h>
91 #include <utmpx.h>
92 #include <locale.h>
93 #include <pwd.h>
94 #include <limits.h>
95
96 static void process(void);
97 static void ck_file(char *);
98 static void dump(void);
99
100 static struct utmpx *utmpp; /* pointer for getutxent() */
101
102 /*
103 * utmpx defines wider fields for user and line. For compatibility of output,
104 * we are limiting these to the old maximums in utmp. Define UTMPX_NAMELEN
105 * to use the full lengths.
106 */
107 #ifndef UTMPX_NAMELEN
108 /* XXX - utmp - fix name length */
109 #define NMAX (_POSIX_LOGIN_NAME_MAX - 1)
110 #define LMAX 12
111 #else /* UTMPX_NAMELEN */
112 #define NMAX (sizeof (utmpp->ut_user))
113 #define LMAX (sizeof (utmpp->ut_line))
114 #endif
115
116 static char comment[BUFSIZ]; /* holds inittab comment */
117 static char errmsg[BUFSIZ]; /* used in snprintf for errors */
118 static int fildes; /* file descriptor for inittab */
119 static int Hopt = 0; /* 1 = who -H */
120 static char *inittab; /* ptr to inittab contents */
121 static char *iinit; /* index into inittab */
122 static int justme = 0; /* 1 = who am i */
123 static struct tm *lptr; /* holds user login time */
124 static char *myname; /* pointer to invoker's name */
125 static char *mytty; /* holds device user is on */
126 static char nameval[sizeof (utmpp->ut_user) + 1]; /* invoker's name */
127 static int number = 8; /* number of users per -q line */
128 static int optcnt = 0; /* keeps count of options */
129 static char outbuf[BUFSIZ]; /* buffer for output */
130 static char *program; /* holds name of this program */
131 #ifdef XPG4
132 static int aopt = 0; /* 1 = who -a */
133 static int dopt = 0; /* 1 = who -d */
134 #endif /* XPG4 */
135 static int qopt = 0; /* 1 = who -q */
136 static int sopt = 0; /* 1 = who -s */
137 static struct stat stbuf; /* area for stat buffer */
138 static struct stat *stbufp; /* ptr to structure */
139 static int terse = 1; /* 1 = print terse msgs */
140 static int Topt = 0; /* 1 = who -T */
141 static time_t timnow; /* holds current time */
142 static int totlusrs = 0; /* cntr for users on system */
143 static int uopt = 0; /* 1 = who -u */
144 static char user[sizeof (utmpp->ut_user) + 1]; /* holds user name */
145 static int validtype[UTMAXTYPE+1]; /* holds valid types */
146 static int wrap; /* flag to indicate wrap */
147 static char time_buf[128]; /* holds date and time string */
148 static char *end; /* used in strtol for end pointer */
149
150 int
151 main(int argc, char **argv)
152 {
153 int goerr = 0; /* non-zero indicates cmd error */
154 int i;
155 int optsw; /* switch for while of getopt() */
156
157 (void) setlocale(LC_ALL, "");
158
159 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
160 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
161 #endif
162 (void) textdomain(TEXT_DOMAIN);
163
164 validtype[USER_PROCESS] = 1;
165 validtype[EMPTY] = 0;
166 stbufp = &stbuf;
167
168 /*
169 * Strip off path name of this command
170 */
171 for (i = strlen(argv[0]); i >= 0 && argv[0][i] != '/'; --i);
172 if (i >= 0)
173 argv[0] += i+1;
174 program = argv[0];
175
176 /*
177 * Buffer stdout for speed
178 */
179 setbuf(stdout, outbuf);
180
181 /*
182 * Retrieve options specified on command line
183 * XCU4 - add -m option
184 */
185 while ((optsw = getopt(argc, argv, "abdHlmn:pqrstTu")) != EOF) {
186 optcnt++;
187 switch (optsw) {
188
189 case 'a':
190 optcnt += 7;
191 validtype[BOOT_TIME] = 1;
192 validtype[DEAD_PROCESS] = 1;
193 validtype[LOGIN_PROCESS] = 1;
194 validtype[INIT_PROCESS] = 1;
195 validtype[RUN_LVL] = 1;
196 validtype[OLD_TIME] = 1;
197 validtype[NEW_TIME] = 1;
198 validtype[USER_PROCESS] = 1;
199 #ifdef XPG4
200 aopt = 1;
201 #endif /* XPG4 */
202 uopt = 1;
203 Topt = 1;
204 if (!sopt) terse = 0;
205 break;
206
207 case 'b':
208 validtype[BOOT_TIME] = 1;
209 if (!uopt) validtype[USER_PROCESS] = 0;
210 break;
211
212 case 'd':
213 validtype[DEAD_PROCESS] = 1;
214 if (!uopt) validtype[USER_PROCESS] = 0;
215 #ifdef XPG4
216 dopt = 1;
217 #endif /* XPG4 */
218 break;
219
220 case 'H':
221 optcnt--; /* Don't count Header */
222 Hopt = 1;
223 break;
224
225 case 'l':
226 validtype[LOGIN_PROCESS] = 1;
227 if (!uopt) validtype[USER_PROCESS] = 0;
228 terse = 0;
229 break;
230 case 'm': /* New XCU4 option */
231 justme = 1;
232 break;
233
234 case 'n':
235 errno = 0;
236 number = strtol(optarg, &end, 10);
237 if (errno != 0 || *end != '\0') {
238 (void) fprintf(stderr, gettext(
239 "%s: Invalid numeric argument\n"),
240 program);
241 exit(1);
242 }
243 if (number < 1) {
244 (void) fprintf(stderr, gettext(
245 "%s: Number of users per line must "
246 "be at least 1\n"), program);
247 exit(1);
248 }
249 break;
250
251 case 'p':
252 validtype[INIT_PROCESS] = 1;
253 if (!uopt) validtype[USER_PROCESS] = 0;
254 break;
255
256 case 'q':
257 qopt = 1;
258 break;
259
260 case 'r':
261 validtype[RUN_LVL] = 1;
262 terse = 0;
263 if (!uopt) validtype[USER_PROCESS] = 0;
264 break;
265
266 case 's':
267 sopt = 1;
268 terse = 1;
269 break;
270
271 case 't':
272 validtype[OLD_TIME] = 1;
273 validtype[NEW_TIME] = 1;
274 if (!uopt) validtype[USER_PROCESS] = 0;
275 break;
276
277 case 'T':
278 Topt = 1;
279 #ifdef XPG4
280 terse = 1; /* XPG4 requires -T */
281 #else /* XPG4 */
282 terse = 0;
283 #endif /* XPG4 */
284 break;
285
286 case 'u':
287 uopt = 1;
288 validtype[USER_PROCESS] = 1;
289 if (!sopt) terse = 0;
290 break;
291
292 case '?':
293 goerr++;
294 break;
295 default:
296 break;
297 }
298 }
299 #ifdef XPG4
300 /*
301 * XCU4 changes - check for illegal sopt, Topt & aopt combination
302 */
303 if (sopt == 1) {
304 terse = 1;
305 if (Topt == 1 || aopt == 1)
306 goerr++;
307 }
308 #endif /* XPG4 */
309
310 if (goerr > 0) {
311 #ifdef XPG4
312 /*
313 * XCU4 - slightly different usage with -s -a & -T
314 */
315 (void) fprintf(stderr, gettext("\nUsage:\t%s"), program);
316 (void) fprintf(stderr,
317 gettext(" -s [-bdHlmpqrtu] [utmpx_like_file]\n"));
318
319 (void) fprintf(stderr, gettext(
320 "\t%s [-abdHlmpqrtTu] [utmpx_like_file]\n"), program);
321 #else /* XPG4 */
322 (void) fprintf(stderr, gettext(
323 "\nUsage:\t%s [-abdHlmpqrstTu] [utmpx_like_file]\n"),
324 program);
325 #endif /* XPG4 */
326 (void) fprintf(stderr,
327 gettext("\t%s -q [-n x] [utmpx_like_file]\n"), program);
328 (void) fprintf(stderr, gettext("\t%s [am i]\n"), program);
329 /*
330 * XCU4 changes - be explicit with "am i" options
331 */
332 (void) fprintf(stderr, gettext("\t%s [am I]\n"), program);
333 (void) fprintf(stderr, gettext(
334 "a\tall (bdlprtu options)\n"));
335 (void) fprintf(stderr, gettext("b\tboot time\n"));
336 (void) fprintf(stderr, gettext("d\tdead processes\n"));
337 (void) fprintf(stderr, gettext("H\tprint header\n"));
338 (void) fprintf(stderr, gettext("l\tlogin processes\n"));
339 (void) fprintf(stderr, gettext(
340 "n #\tspecify number of users per line for -q\n"));
341 (void) fprintf(stderr,
342 gettext("p\tprocesses other than getty or users\n"));
343 (void) fprintf(stderr, gettext("q\tquick %s\n"), program);
344 (void) fprintf(stderr, gettext("r\trun level\n"));
345 (void) fprintf(stderr, gettext(
346 "s\tshort form of %s (no time since last output or pid)\n"),
347 program);
348 (void) fprintf(stderr, gettext("t\ttime changes\n"));
349 (void) fprintf(stderr, gettext(
350 "T\tstatus of tty (+ writable, - not writable, "
351 "? hung)\n"));
352 (void) fprintf(stderr, gettext("u\tuseful information\n"));
353 (void) fprintf(stderr,
354 gettext("m\tinformation only about current terminal\n"));
355 (void) fprintf(stderr, gettext(
356 "am i\tinformation about current terminal "
357 "(same as -m)\n"));
358 (void) fprintf(stderr, gettext(
359 "am I\tinformation about current terminal "
360 "(same as -m)\n"));
361 exit(1);
362 }
363
364 /*
365 * XCU4: If -q option ignore all other options
366 */
367 if (qopt == 1) {
368 Hopt = 0;
369 sopt = 0;
370 Topt = 0;
371 uopt = 0;
372 justme = 0;
373 validtype[ACCOUNTING] = 0;
374 validtype[BOOT_TIME] = 0;
375 validtype[DEAD_PROCESS] = 0;
376 validtype[LOGIN_PROCESS] = 0;
377 validtype[INIT_PROCESS] = 0;
378 validtype[RUN_LVL] = 0;
379 validtype[OLD_TIME] = 0;
380 validtype[NEW_TIME] = 0;
381 validtype[USER_PROCESS] = 1;
382 }
383
384 if (argc == optind + 1) {
385 optcnt++;
386 ck_file(argv[optind]);
387 (void) utmpxname(argv[optind]);
388 }
389
390 /*
391 * Test for 'who am i' or 'who am I'
392 * XCU4 - check if justme was already set by -m option
393 */
394 if (justme == 1 || (argc == 3 && strcmp(argv[1], "am") == 0 &&
395 ((argv[2][0] == 'i' || argv[2][0] == 'I') &&
396 argv[2][1] == '\0'))) {
397 justme = 1;
398 myname = nameval;
399 (void) cuserid(myname);
400 if ((mytty = ttyname(fileno(stdin))) == NULL &&
401 (mytty = ttyname(fileno(stdout))) == NULL &&
402 (mytty = ttyname(fileno(stderr))) == NULL) {
403 (void) fprintf(stderr, gettext(
404 "Must be attached to terminal for 'am I' option\n"));
405 (void) fflush(stderr);
406 exit(1);
407 } else
408 mytty += 5; /* bump past "/dev/" */
409 }
410
411 if (!terse) {
412 if (Hopt)
413 (void) printf(gettext(
414 "NAME LINE TIME IDLE PID COMMENTS\n"));
415
416 timnow = time(0);
417
418 if ((fildes = open("/etc/inittab",
419 O_NONBLOCK|O_RDONLY)) == -1) {
420 (void) snprintf(errmsg, sizeof (errmsg),
421 gettext("%s: Cannot open /etc/inittab"), program);
422 perror(errmsg);
423 exit(errno);
424 }
425
426 if (fstat(fildes, stbufp) == -1) {
427 (void) snprintf(errmsg, sizeof (errmsg),
428 gettext("%s: Cannot stat /etc/inittab"), program);
429 perror(errmsg);
430 exit(errno);
431 }
432
433 if ((inittab = malloc(stbufp->st_size + 1)) == NULL) {
434 (void) snprintf(errmsg, sizeof (errmsg),
435 gettext("%s: Cannot allocate %ld bytes"),
436 program, stbufp->st_size);
437 perror(errmsg);
438 exit(errno);
439 }
440
441 if (read(fildes, inittab, stbufp->st_size)
442 != stbufp->st_size) {
443 (void) snprintf(errmsg, sizeof (errmsg),
444 gettext("%s: Error reading /etc/inittab"),
445 program);
446 perror(errmsg);
447 exit(errno);
448 }
449
450 inittab[stbufp->st_size] = '\0';
451 iinit = inittab;
452 } else {
453 if (Hopt) {
454 #ifdef XPG4
455 if (dopt) {
456 (void) printf(gettext(
457 "NAME LINE TIME COMMENTS\n"));
458 } else {
459 (void) printf(
460 gettext("NAME LINE TIME\n"));
461 }
462 #else /* XPG4 */
463 (void) printf(
464 gettext("NAME LINE TIME\n"));
465 #endif /* XPG4 */
466 }
467 }
468 process();
469
470 /*
471 * 'who -q' requires EOL upon exit,
472 * followed by total line
473 */
474 if (qopt)
475 (void) printf(gettext("\n# users=%d\n"), totlusrs);
476 return (0);
477 }
478
479 static void
480 dump()
481 {
482 char device[sizeof (utmpp->ut_line) + 1];
483 time_t hr;
484 time_t idle;
485 time_t min;
486 char path[sizeof (utmpp->ut_line) + 6];
487 int pexit;
488 int pterm;
489 int rc;
490 char w; /* writeability indicator */
491
492 /*
493 * Get and check user name
494 */
495 if (utmpp->ut_user[0] == '\0')
496 (void) strcpy(user, " .");
497 else {
498 (void) strncpy(user, utmpp->ut_user, sizeof (user));
499 user[sizeof (user) - 1] = '\0';
500 }
501 totlusrs++;
502
503 /*
504 * Do print in 'who -q' format
505 */
506 if (qopt) {
507 /*
508 * XCU4 - Use non user macro for correct user count
509 */
510 if (((totlusrs - 1) % number) == 0 && totlusrs > 1)
511 (void) printf("\n");
512 (void) printf("%-*s ", NMAX, user);
513 return;
514 }
515
516
517 pexit = (int)' ';
518 pterm = (int)' ';
519
520 /*
521 * Get exit info if applicable
522 */
523 if (utmpp->ut_type == RUN_LVL || utmpp->ut_type == DEAD_PROCESS) {
524 pterm = utmpp->ut_exit.e_termination;
525 pexit = utmpp->ut_exit.e_exit;
526 }
527
528 /*
529 * Massage ut_xtime field
530 */
531 lptr = localtime(&utmpp->ut_xtime);
532 (void) strftime(time_buf, sizeof (time_buf),
533 dcgettext(NULL, DATE_FMT, LC_TIME), lptr);
534
535 /*
536 * Get and massage device
537 */
538 if (utmpp->ut_line[0] == '\0')
539 (void) strcpy(device, " .");
540 else {
541 (void) strncpy(device, utmpp->ut_line,
542 sizeof (utmpp->ut_line));
543 device[sizeof (utmpp->ut_line)] = '\0';
544 }
545
546 /*
547 * Get writeability if requested
548 * XCU4 - only print + or - for user processes
549 */
550 if (Topt && (utmpp->ut_type == USER_PROCESS)) {
551 w = '-';
552 (void) strcpy(path, "/dev/");
553 (void) strncpy(path + 5, utmpp->ut_line,
554 sizeof (utmpp->ut_line));
555 path[5 + sizeof (utmpp->ut_line)] = '\0';
556
557 if ((rc = stat(path, stbufp)) == -1) w = '?';
558 else if ((stbufp->st_mode & S_IWOTH) ||
559 (stbufp->st_mode & S_IWGRP)) /* Check group & other */
560 w = '+';
561
562 } else
563 w = ' ';
564
565 /*
566 * Print the TERSE portion of the output
567 */
568 (void) printf("%-*s %c %-12s %s", NMAX, user, w, device, time_buf);
569
570 if (!terse) {
571 /*
572 * Stat device for idle time
573 * (Don't complain if you can't)
574 */
575 rc = -1;
576 if (utmpp->ut_type == USER_PROCESS) {
577 (void) strcpy(path, "/dev/");
578 (void) strncpy(path + 5, utmpp->ut_line,
579 sizeof (utmpp->ut_line));
580 path[5 + sizeof (utmpp->ut_line)] = '\0';
581 rc = stat(path, stbufp);
582 }
583 if (rc != -1) {
584 idle = timnow - stbufp->st_mtime;
585 hr = idle/3600;
586 min = (unsigned)(idle/60)%60;
587 if (hr == 0 && min == 0)
588 (void) printf(gettext(" . "));
589 else {
590 if (hr < 24)
591 (void) printf(" %2d:%2.2d", (int)hr,
592 (int)min);
593 else
594 (void) printf(gettext(" old "));
595 }
596 }
597
598 /*
599 * Add PID for verbose output
600 */
601 if (utmpp->ut_type != BOOT_TIME &&
602 utmpp->ut_type != RUN_LVL &&
603 utmpp->ut_type != ACCOUNTING)
604 (void) printf(" %5ld", utmpp->ut_pid);
605
606 /*
607 * Handle /etc/inittab comment
608 */
609 if (utmpp->ut_type == DEAD_PROCESS) {
610 (void) printf(gettext(" id=%4.4s "),
611 utmpp->ut_id);
612 (void) printf(gettext("term=%-3d "), pterm);
613 (void) printf(gettext("exit=%d "), pexit);
614 } else if (utmpp->ut_type != INIT_PROCESS) {
615 /*
616 * Search for each entry in inittab
617 * string. Keep our place from
618 * search to search to try and
619 * minimize the work. Wrap once if needed
620 * for each entry.
621 */
622 wrap = 0;
623 /*
624 * Look for a line beginning with
625 * utmpp->ut_id
626 */
627 while ((rc = strncmp(utmpp->ut_id, iinit,
628 strcspn(iinit, ":"))) != 0) {
629 for (; *iinit != '\n'; iinit++);
630 iinit++;
631
632 /*
633 * Wrap once if necessary to
634 * find entry in inittab
635 */
636 if (*iinit == '\0') {
637 if (!wrap) {
638 iinit = inittab;
639 wrap = 1;
640 }
641 }
642 }
643
644 if (*iinit != '\0') {
645 /*
646 * We found our entry
647 */
648 for (iinit++; *iinit != '#' &&
649 *iinit != '\n'; iinit++);
650 if (*iinit == '#') {
651 for (iinit++; *iinit == ' ' ||
652 *iinit == '\t'; iinit++);
653 for (rc = 0; *iinit != '\n'; iinit++)
654 comment[rc++] = *iinit;
655 comment[rc] = '\0';
656 } else
657 (void) strcpy(comment, " ");
658
659 (void) printf(" %s", comment);
660 } else
661 iinit = inittab; /* Reset pointer */
662 }
663 if (utmpp->ut_type == INIT_PROCESS)
664 (void) printf(gettext(" id=%4.4s"), utmpp->ut_id);
665 }
666 #ifdef XPG4
667 else
668 if (dopt && utmpp->ut_type == DEAD_PROCESS) {
669 (void) printf(gettext("\tterm=%-3d "), pterm);
670 (void) printf(gettext("exit=%d "), pexit);
671 }
672 #endif /* XPG4 */
673
674
675 /*
676 * Handle RUN_LVL process - If no alt. file - Only one!
677 */
678 if (utmpp->ut_type == RUN_LVL) {
679 (void) printf(" %c %5ld %c", pterm, utmpp->ut_pid,
680 pexit);
681 if (optcnt == 1 && !validtype[USER_PROCESS]) {
682 (void) printf("\n");
683 exit(0);
684 }
685 }
686
687 /*
688 * Handle BOOT_TIME process - If no alt. file - Only one!
689 */
690 if (utmpp->ut_type == BOOT_TIME) {
691 if (optcnt == 1 && !validtype[USER_PROCESS]) {
692 (void) printf("\n");
693 exit(0);
694 }
695 }
696
697 /*
698 * Get remote host from utmpx structure
699 */
700 if (utmpp && utmpp->ut_host[0])
701 (void) printf("\t(%.*s)", sizeof (utmpp->ut_host),
702 utmpp->ut_host);
703
704 /*
705 * Now, put on the trailing EOL
706 */
707 (void) printf("\n");
708 }
709
710 static void
711 process()
712 {
713 struct passwd *pwp;
714 int i = 0;
715 char *ttname;
716
717 /*
718 * Loop over each entry in /var/adm/utmpx
719 */
720
721 setutxent();
722 while ((utmpp = getutxent()) != NULL) {
723 #ifdef DEBUG
724 (void) printf(
725 "ut_user '%s'\nut_id '%s'\nut_line '%s'\nut_type '%d'\n\n",
726 utmpp->ut_user, utmpp->ut_id, utmpp->ut_line, utmpp->ut_type);
727 #endif
728 if (utmpp->ut_type <= UTMAXTYPE) {
729 /*
730 * Handle "am i"
731 */
732 if (justme) {
733 if (strncmp(myname, utmpp->ut_user,
734 sizeof (utmpp->ut_user)) == 0 &&
735 strncmp(mytty, utmpp->ut_line,
736 sizeof (utmpp->ut_line)) == 0 &&
737 utmpp->ut_type == USER_PROCESS) {
738 /*
739 * we have have found ourselves
740 * in the utmp file and the entry
741 * is a user process, this is not
742 * meaningful otherwise
743 *
744 */
745
746 dump();
747 exit(0);
748 }
749 continue;
750 }
751
752 /*
753 * Print the line if we want it
754 */
755 if (validtype[utmpp->ut_type]) {
756 #ifdef XPG4
757 if (utmpp->ut_type == LOGIN_PROCESS) {
758 if ((utmpp->ut_line[0] == '\0') ||
759 (strcmp(utmpp->ut_user, "LOGIN") != 0))
760 continue;
761 }
762 #endif /* XPG4 */
763 dump();
764 }
765 } else {
766 (void) fprintf(stderr,
767 gettext("%s: Error --- entry has ut_type "
768 "of %d\n"), program, utmpp->ut_type);
769 (void) fprintf(stderr,
770 gettext(" when maximum is %d\n"), UTMAXTYPE);
771 }
772 }
773
774 /*
775 * If justme is set at this point than the utmp entry
776 * was not found.
777 */
778 if (justme) {
779 static struct utmpx utmpt;
780
781 pwp = getpwuid(geteuid());
782 if (pwp != NULL)
783 while (i < (int)sizeof (utmpt.ut_user) &&
784 *pwp->pw_name != 0)
785 utmpt.ut_user[i++] = *pwp->pw_name++;
786
787 ttname = ttyname(1);
788
789 i = 0;
790 if (ttname != NULL)
791 while (i < (int)sizeof (utmpt.ut_line) &&
792 *ttname != 0)
793 utmpt.ut_line[i++] = *ttname++;
794
795 utmpt.ut_id[0] = 0;
796 utmpt.ut_pid = getpid();
797 utmpt.ut_type = USER_PROCESS;
798 (void) time(&utmpt.ut_xtime);
799 utmpp = &utmpt;
800 dump();
801 exit(0);
802 }
803 }
804
805 /*
806 * This routine checks the following:
807 *
808 * 1. File exists
809 *
810 * 2. We have read permissions
811 *
812 * 3. It is a multiple of utmp entries in size
813 *
814 * Failing any of these conditions causes who(1) to
815 * abort processing.
816 *
817 * 4. If file is empty we exit right away as there
818 * is no info to report on.
819 *
820 * This routine does not check utmpx files.
821 */
822 static void
823 ck_file(char *name)
824 {
825 struct stat sbuf;
826 int rc;
827
828 /*
829 * Does file exist? Do stat to check, and save structure
830 * so that we can check on the file's size later on.
831 */
832 if ((rc = stat(name, &sbuf)) == -1) {
833 (void) snprintf(errmsg, sizeof (errmsg),
834 gettext("%s: Cannot stat file '%s'"), program, name);
835 perror(errmsg);
836 exit(1);
837 }
838
839 /*
840 * The only real way we can be sure we can access the
841 * file is to try. If we succeed then we close it.
842 */
843 if (access(name, R_OK) < 0) {
844 (void) snprintf(errmsg, sizeof (errmsg),
845 gettext("%s: Cannot open file '%s'"), program, name);
846 perror(errmsg);
847 exit(1);
848 }
849
850 /*
851 * If the file is empty, we are all done.
852 */
853 if (!sbuf.st_size)
854 exit(0);
855
856 /*
857 * Make sure the file is a utmp file.
858 * We can only check for size being a multiple of
859 * utmp structures in length.
860 */
861 rc = sbuf.st_size % (int)sizeof (struct utmpx);
862 if (rc) {
863 (void) fprintf(stderr, gettext("%s: File '%s' is not "
864 "a utmpx file\n"), program, name);
865 exit(1);
866 }
867 }
868
(载自http://cvs.opensolaris.org/source/xref/on/usr/src/cmd/who/who.c)
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
22 /* All Rights Reserved */
23
24
25 /*
26 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
28 */
29
30 #pragma ident "@(#)who.c 1.33 06/04/03 SMI"
31
32 /*
33 * This program analyzes information found in /var/adm/utmpx
34 *
35 * Additionally information is gathered from /etc/inittab
36 * if requested.
37 *
38 *
39 * Syntax:
40 *
41 * who am i Displays info on yourself
42 *
43 * who -a Displays information about All
44 * entries in /var/adm/utmpx
45 *
46 * who -b Displays info on last boot
47 *
48 * who -d Displays info on DEAD PROCESSES
49 *
50 * who -H Displays HEADERS for output
51 *
52 * who -l Displays info on LOGIN entries
53 *
54 * who -m Same as who am i
55 *
56 * who -p Displays info on PROCESSES spawned by init
57 *
58 * who -q Displays short information on
59 * current users who LOGGED ON
60 *
61 * who -r Displays info of current run-level
62 *
63 * who -s Displays requested info in SHORT form
64 *
65 * who -t Displays info on TIME changes
66 *
67 * who -T Displays writeability of each user
68 * (+ writeable, - non-writeable, ? hung)
69 *
70 * who -u Displays LONG info on users
71 * who have LOGGED ON
72 */
73
74 #define DATE_FMT "%b %e %H:%M"
75
76 /*
77 * %b Abbreviated month name
78 * %e Day of month
79 * %H hour (24-hour clock)
80 * %M minute
81 */
82 #include <errno.h>
83 #include <fcntl.h>
84 #include <stdio.h>
85 #include <string.h>
86 #include <sys/types.h>
87 #include <unistd.h>
88 #include <stdlib.h>
89 #include <sys/stat.h>
90 #include <time.h>
91 #include <utmpx.h>
92 #include <locale.h>
93 #include <pwd.h>
94 #include <limits.h>
95
96 static void process(void);
97 static void ck_file(char *);
98 static void dump(void);
99
100 static struct utmpx *utmpp; /* pointer for getutxent() */
101
102 /*
103 * utmpx defines wider fields for user and line. For compatibility of output,
104 * we are limiting these to the old maximums in utmp. Define UTMPX_NAMELEN
105 * to use the full lengths.
106 */
107 #ifndef UTMPX_NAMELEN
108 /* XXX - utmp - fix name length */
109 #define NMAX (_POSIX_LOGIN_NAME_MAX - 1)
110 #define LMAX 12
111 #else /* UTMPX_NAMELEN */
112 #define NMAX (sizeof (utmpp->ut_user))
113 #define LMAX (sizeof (utmpp->ut_line))
114 #endif
115
116 static char comment[BUFSIZ]; /* holds inittab comment */
117 static char errmsg[BUFSIZ]; /* used in snprintf for errors */
118 static int fildes; /* file descriptor for inittab */
119 static int Hopt = 0; /* 1 = who -H */
120 static char *inittab; /* ptr to inittab contents */
121 static char *iinit; /* index into inittab */
122 static int justme = 0; /* 1 = who am i */
123 static struct tm *lptr; /* holds user login time */
124 static char *myname; /* pointer to invoker's name */
125 static char *mytty; /* holds device user is on */
126 static char nameval[sizeof (utmpp->ut_user) + 1]; /* invoker's name */
127 static int number = 8; /* number of users per -q line */
128 static int optcnt = 0; /* keeps count of options */
129 static char outbuf[BUFSIZ]; /* buffer for output */
130 static char *program; /* holds name of this program */
131 #ifdef XPG4
132 static int aopt = 0; /* 1 = who -a */
133 static int dopt = 0; /* 1 = who -d */
134 #endif /* XPG4 */
135 static int qopt = 0; /* 1 = who -q */
136 static int sopt = 0; /* 1 = who -s */
137 static struct stat stbuf; /* area for stat buffer */
138 static struct stat *stbufp; /* ptr to structure */
139 static int terse = 1; /* 1 = print terse msgs */
140 static int Topt = 0; /* 1 = who -T */
141 static time_t timnow; /* holds current time */
142 static int totlusrs = 0; /* cntr for users on system */
143 static int uopt = 0; /* 1 = who -u */
144 static char user[sizeof (utmpp->ut_user) + 1]; /* holds user name */
145 static int validtype[UTMAXTYPE+1]; /* holds valid types */
146 static int wrap; /* flag to indicate wrap */
147 static char time_buf[128]; /* holds date and time string */
148 static char *end; /* used in strtol for end pointer */
149
150 int
151 main(int argc, char **argv)
152 {
153 int goerr = 0; /* non-zero indicates cmd error */
154 int i;
155 int optsw; /* switch for while of getopt() */
156
157 (void) setlocale(LC_ALL, "");
158
159 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
160 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
161 #endif
162 (void) textdomain(TEXT_DOMAIN);
163
164 validtype[USER_PROCESS] = 1;
165 validtype[EMPTY] = 0;
166 stbufp = &stbuf;
167
168 /*
169 * Strip off path name of this command
170 */
171 for (i = strlen(argv[0]); i >= 0 && argv[0][i] != '/'; --i);
172 if (i >= 0)
173 argv[0] += i+1;
174 program = argv[0];
175
176 /*
177 * Buffer stdout for speed
178 */
179 setbuf(stdout, outbuf);
180
181 /*
182 * Retrieve options specified on command line
183 * XCU4 - add -m option
184 */
185 while ((optsw = getopt(argc, argv, "abdHlmn:pqrstTu")) != EOF) {
186 optcnt++;
187 switch (optsw) {
188
189 case 'a':
190 optcnt += 7;
191 validtype[BOOT_TIME] = 1;
192 validtype[DEAD_PROCESS] = 1;
193 validtype[LOGIN_PROCESS] = 1;
194 validtype[INIT_PROCESS] = 1;
195 validtype[RUN_LVL] = 1;
196 validtype[OLD_TIME] = 1;
197 validtype[NEW_TIME] = 1;
198 validtype[USER_PROCESS] = 1;
199 #ifdef XPG4
200 aopt = 1;
201 #endif /* XPG4 */
202 uopt = 1;
203 Topt = 1;
204 if (!sopt) terse = 0;
205 break;
206
207 case 'b':
208 validtype[BOOT_TIME] = 1;
209 if (!uopt) validtype[USER_PROCESS] = 0;
210 break;
211
212 case 'd':
213 validtype[DEAD_PROCESS] = 1;
214 if (!uopt) validtype[USER_PROCESS] = 0;
215 #ifdef XPG4
216 dopt = 1;
217 #endif /* XPG4 */
218 break;
219
220 case 'H':
221 optcnt--; /* Don't count Header */
222 Hopt = 1;
223 break;
224
225 case 'l':
226 validtype[LOGIN_PROCESS] = 1;
227 if (!uopt) validtype[USER_PROCESS] = 0;
228 terse = 0;
229 break;
230 case 'm': /* New XCU4 option */
231 justme = 1;
232 break;
233
234 case 'n':
235 errno = 0;
236 number = strtol(optarg, &end, 10);
237 if (errno != 0 || *end != '\0') {
238 (void) fprintf(stderr, gettext(
239 "%s: Invalid numeric argument\n"),
240 program);
241 exit(1);
242 }
243 if (number < 1) {
244 (void) fprintf(stderr, gettext(
245 "%s: Number of users per line must "
246 "be at least 1\n"), program);
247 exit(1);
248 }
249 break;
250
251 case 'p':
252 validtype[INIT_PROCESS] = 1;
253 if (!uopt) validtype[USER_PROCESS] = 0;
254 break;
255
256 case 'q':
257 qopt = 1;
258 break;
259
260 case 'r':
261 validtype[RUN_LVL] = 1;
262 terse = 0;
263 if (!uopt) validtype[USER_PROCESS] = 0;
264 break;
265
266 case 's':
267 sopt = 1;
268 terse = 1;
269 break;
270
271 case 't':
272 validtype[OLD_TIME] = 1;
273 validtype[NEW_TIME] = 1;
274 if (!uopt) validtype[USER_PROCESS] = 0;
275 break;
276
277 case 'T':
278 Topt = 1;
279 #ifdef XPG4
280 terse = 1; /* XPG4 requires -T */
281 #else /* XPG4 */
282 terse = 0;
283 #endif /* XPG4 */
284 break;
285
286 case 'u':
287 uopt = 1;
288 validtype[USER_PROCESS] = 1;
289 if (!sopt) terse = 0;
290 break;
291
292 case '?':
293 goerr++;
294 break;
295 default:
296 break;
297 }
298 }
299 #ifdef XPG4
300 /*
301 * XCU4 changes - check for illegal sopt, Topt & aopt combination
302 */
303 if (sopt == 1) {
304 terse = 1;
305 if (Topt == 1 || aopt == 1)
306 goerr++;
307 }
308 #endif /* XPG4 */
309
310 if (goerr > 0) {
311 #ifdef XPG4
312 /*
313 * XCU4 - slightly different usage with -s -a & -T
314 */
315 (void) fprintf(stderr, gettext("\nUsage:\t%s"), program);
316 (void) fprintf(stderr,
317 gettext(" -s [-bdHlmpqrtu] [utmpx_like_file]\n"));
318
319 (void) fprintf(stderr, gettext(
320 "\t%s [-abdHlmpqrtTu] [utmpx_like_file]\n"), program);
321 #else /* XPG4 */
322 (void) fprintf(stderr, gettext(
323 "\nUsage:\t%s [-abdHlmpqrstTu] [utmpx_like_file]\n"),
324 program);
325 #endif /* XPG4 */
326 (void) fprintf(stderr,
327 gettext("\t%s -q [-n x] [utmpx_like_file]\n"), program);
328 (void) fprintf(stderr, gettext("\t%s [am i]\n"), program);
329 /*
330 * XCU4 changes - be explicit with "am i" options
331 */
332 (void) fprintf(stderr, gettext("\t%s [am I]\n"), program);
333 (void) fprintf(stderr, gettext(
334 "a\tall (bdlprtu options)\n"));
335 (void) fprintf(stderr, gettext("b\tboot time\n"));
336 (void) fprintf(stderr, gettext("d\tdead processes\n"));
337 (void) fprintf(stderr, gettext("H\tprint header\n"));
338 (void) fprintf(stderr, gettext("l\tlogin processes\n"));
339 (void) fprintf(stderr, gettext(
340 "n #\tspecify number of users per line for -q\n"));
341 (void) fprintf(stderr,
342 gettext("p\tprocesses other than getty or users\n"));
343 (void) fprintf(stderr, gettext("q\tquick %s\n"), program);
344 (void) fprintf(stderr, gettext("r\trun level\n"));
345 (void) fprintf(stderr, gettext(
346 "s\tshort form of %s (no time since last output or pid)\n"),
347 program);
348 (void) fprintf(stderr, gettext("t\ttime changes\n"));
349 (void) fprintf(stderr, gettext(
350 "T\tstatus of tty (+ writable, - not writable, "
351 "? hung)\n"));
352 (void) fprintf(stderr, gettext("u\tuseful information\n"));
353 (void) fprintf(stderr,
354 gettext("m\tinformation only about current terminal\n"));
355 (void) fprintf(stderr, gettext(
356 "am i\tinformation about current terminal "
357 "(same as -m)\n"));
358 (void) fprintf(stderr, gettext(
359 "am I\tinformation about current terminal "
360 "(same as -m)\n"));
361 exit(1);
362 }
363
364 /*
365 * XCU4: If -q option ignore all other options
366 */
367 if (qopt == 1) {
368 Hopt = 0;
369 sopt = 0;
370 Topt = 0;
371 uopt = 0;
372 justme = 0;
373 validtype[ACCOUNTING] = 0;
374 validtype[BOOT_TIME] = 0;
375 validtype[DEAD_PROCESS] = 0;
376 validtype[LOGIN_PROCESS] = 0;
377 validtype[INIT_PROCESS] = 0;
378 validtype[RUN_LVL] = 0;
379 validtype[OLD_TIME] = 0;
380 validtype[NEW_TIME] = 0;
381 validtype[USER_PROCESS] = 1;
382 }
383
384 if (argc == optind + 1) {
385 optcnt++;
386 ck_file(argv[optind]);
387 (void) utmpxname(argv[optind]);
388 }
389
390 /*
391 * Test for 'who am i' or 'who am I'
392 * XCU4 - check if justme was already set by -m option
393 */
394 if (justme == 1 || (argc == 3 && strcmp(argv[1], "am") == 0 &&
395 ((argv[2][0] == 'i' || argv[2][0] == 'I') &&
396 argv[2][1] == '\0'))) {
397 justme = 1;
398 myname = nameval;
399 (void) cuserid(myname);
400 if ((mytty = ttyname(fileno(stdin))) == NULL &&
401 (mytty = ttyname(fileno(stdout))) == NULL &&
402 (mytty = ttyname(fileno(stderr))) == NULL) {
403 (void) fprintf(stderr, gettext(
404 "Must be attached to terminal for 'am I' option\n"));
405 (void) fflush(stderr);
406 exit(1);
407 } else
408 mytty += 5; /* bump past "/dev/" */
409 }
410
411 if (!terse) {
412 if (Hopt)
413 (void) printf(gettext(
414 "NAME LINE TIME IDLE PID COMMENTS\n"));
415
416 timnow = time(0);
417
418 if ((fildes = open("/etc/inittab",
419 O_NONBLOCK|O_RDONLY)) == -1) {
420 (void) snprintf(errmsg, sizeof (errmsg),
421 gettext("%s: Cannot open /etc/inittab"), program);
422 perror(errmsg);
423 exit(errno);
424 }
425
426 if (fstat(fildes, stbufp) == -1) {
427 (void) snprintf(errmsg, sizeof (errmsg),
428 gettext("%s: Cannot stat /etc/inittab"), program);
429 perror(errmsg);
430 exit(errno);
431 }
432
433 if ((inittab = malloc(stbufp->st_size + 1)) == NULL) {
434 (void) snprintf(errmsg, sizeof (errmsg),
435 gettext("%s: Cannot allocate %ld bytes"),
436 program, stbufp->st_size);
437 perror(errmsg);
438 exit(errno);
439 }
440
441 if (read(fildes, inittab, stbufp->st_size)
442 != stbufp->st_size) {
443 (void) snprintf(errmsg, sizeof (errmsg),
444 gettext("%s: Error reading /etc/inittab"),
445 program);
446 perror(errmsg);
447 exit(errno);
448 }
449
450 inittab[stbufp->st_size] = '\0';
451 iinit = inittab;
452 } else {
453 if (Hopt) {
454 #ifdef XPG4
455 if (dopt) {
456 (void) printf(gettext(
457 "NAME LINE TIME COMMENTS\n"));
458 } else {
459 (void) printf(
460 gettext("NAME LINE TIME\n"));
461 }
462 #else /* XPG4 */
463 (void) printf(
464 gettext("NAME LINE TIME\n"));
465 #endif /* XPG4 */
466 }
467 }
468 process();
469
470 /*
471 * 'who -q' requires EOL upon exit,
472 * followed by total line
473 */
474 if (qopt)
475 (void) printf(gettext("\n# users=%d\n"), totlusrs);
476 return (0);
477 }
478
479 static void
480 dump()
481 {
482 char device[sizeof (utmpp->ut_line) + 1];
483 time_t hr;
484 time_t idle;
485 time_t min;
486 char path[sizeof (utmpp->ut_line) + 6];
487 int pexit;
488 int pterm;
489 int rc;
490 char w; /* writeability indicator */
491
492 /*
493 * Get and check user name
494 */
495 if (utmpp->ut_user[0] == '\0')
496 (void) strcpy(user, " .");
497 else {
498 (void) strncpy(user, utmpp->ut_user, sizeof (user));
499 user[sizeof (user) - 1] = '\0';
500 }
501 totlusrs++;
502
503 /*
504 * Do print in 'who -q' format
505 */
506 if (qopt) {
507 /*
508 * XCU4 - Use non user macro for correct user count
509 */
510 if (((totlusrs - 1) % number) == 0 && totlusrs > 1)
511 (void) printf("\n");
512 (void) printf("%-*s ", NMAX, user);
513 return;
514 }
515
516
517 pexit = (int)' ';
518 pterm = (int)' ';
519
520 /*
521 * Get exit info if applicable
522 */
523 if (utmpp->ut_type == RUN_LVL || utmpp->ut_type == DEAD_PROCESS) {
524 pterm = utmpp->ut_exit.e_termination;
525 pexit = utmpp->ut_exit.e_exit;
526 }
527
528 /*
529 * Massage ut_xtime field
530 */
531 lptr = localtime(&utmpp->ut_xtime);
532 (void) strftime(time_buf, sizeof (time_buf),
533 dcgettext(NULL, DATE_FMT, LC_TIME), lptr);
534
535 /*
536 * Get and massage device
537 */
538 if (utmpp->ut_line[0] == '\0')
539 (void) strcpy(device, " .");
540 else {
541 (void) strncpy(device, utmpp->ut_line,
542 sizeof (utmpp->ut_line));
543 device[sizeof (utmpp->ut_line)] = '\0';
544 }
545
546 /*
547 * Get writeability if requested
548 * XCU4 - only print + or - for user processes
549 */
550 if (Topt && (utmpp->ut_type == USER_PROCESS)) {
551 w = '-';
552 (void) strcpy(path, "/dev/");
553 (void) strncpy(path + 5, utmpp->ut_line,
554 sizeof (utmpp->ut_line));
555 path[5 + sizeof (utmpp->ut_line)] = '\0';
556
557 if ((rc = stat(path, stbufp)) == -1) w = '?';
558 else if ((stbufp->st_mode & S_IWOTH) ||
559 (stbufp->st_mode & S_IWGRP)) /* Check group & other */
560 w = '+';
561
562 } else
563 w = ' ';
564
565 /*
566 * Print the TERSE portion of the output
567 */
568 (void) printf("%-*s %c %-12s %s", NMAX, user, w, device, time_buf);
569
570 if (!terse) {
571 /*
572 * Stat device for idle time
573 * (Don't complain if you can't)
574 */
575 rc = -1;
576 if (utmpp->ut_type == USER_PROCESS) {
577 (void) strcpy(path, "/dev/");
578 (void) strncpy(path + 5, utmpp->ut_line,
579 sizeof (utmpp->ut_line));
580 path[5 + sizeof (utmpp->ut_line)] = '\0';
581 rc = stat(path, stbufp);
582 }
583 if (rc != -1) {
584 idle = timnow - stbufp->st_mtime;
585 hr = idle/3600;
586 min = (unsigned)(idle/60)%60;
587 if (hr == 0 && min == 0)
588 (void) printf(gettext(" . "));
589 else {
590 if (hr < 24)
591 (void) printf(" %2d:%2.2d", (int)hr,
592 (int)min);
593 else
594 (void) printf(gettext(" old "));
595 }
596 }
597
598 /*
599 * Add PID for verbose output
600 */
601 if (utmpp->ut_type != BOOT_TIME &&
602 utmpp->ut_type != RUN_LVL &&
603 utmpp->ut_type != ACCOUNTING)
604 (void) printf(" %5ld", utmpp->ut_pid);
605
606 /*
607 * Handle /etc/inittab comment
608 */
609 if (utmpp->ut_type == DEAD_PROCESS) {
610 (void) printf(gettext(" id=%4.4s "),
611 utmpp->ut_id);
612 (void) printf(gettext("term=%-3d "), pterm);
613 (void) printf(gettext("exit=%d "), pexit);
614 } else if (utmpp->ut_type != INIT_PROCESS) {
615 /*
616 * Search for each entry in inittab
617 * string. Keep our place from
618 * search to search to try and
619 * minimize the work. Wrap once if needed
620 * for each entry.
621 */
622 wrap = 0;
623 /*
624 * Look for a line beginning with
625 * utmpp->ut_id
626 */
627 while ((rc = strncmp(utmpp->ut_id, iinit,
628 strcspn(iinit, ":"))) != 0) {
629 for (; *iinit != '\n'; iinit++);
630 iinit++;
631
632 /*
633 * Wrap once if necessary to
634 * find entry in inittab
635 */
636 if (*iinit == '\0') {
637 if (!wrap) {
638 iinit = inittab;
639 wrap = 1;
640 }
641 }
642 }
643
644 if (*iinit != '\0') {
645 /*
646 * We found our entry
647 */
648 for (iinit++; *iinit != '#' &&
649 *iinit != '\n'; iinit++);
650 if (*iinit == '#') {
651 for (iinit++; *iinit == ' ' ||
652 *iinit == '\t'; iinit++);
653 for (rc = 0; *iinit != '\n'; iinit++)
654 comment[rc++] = *iinit;
655 comment[rc] = '\0';
656 } else
657 (void) strcpy(comment, " ");
658
659 (void) printf(" %s", comment);
660 } else
661 iinit = inittab; /* Reset pointer */
662 }
663 if (utmpp->ut_type == INIT_PROCESS)
664 (void) printf(gettext(" id=%4.4s"), utmpp->ut_id);
665 }
666 #ifdef XPG4
667 else
668 if (dopt && utmpp->ut_type == DEAD_PROCESS) {
669 (void) printf(gettext("\tterm=%-3d "), pterm);
670 (void) printf(gettext("exit=%d "), pexit);
671 }
672 #endif /* XPG4 */
673
674
675 /*
676 * Handle RUN_LVL process - If no alt. file - Only one!
677 */
678 if (utmpp->ut_type == RUN_LVL) {
679 (void) printf(" %c %5ld %c", pterm, utmpp->ut_pid,
680 pexit);
681 if (optcnt == 1 && !validtype[USER_PROCESS]) {
682 (void) printf("\n");
683 exit(0);
684 }
685 }
686
687 /*
688 * Handle BOOT_TIME process - If no alt. file - Only one!
689 */
690 if (utmpp->ut_type == BOOT_TIME) {
691 if (optcnt == 1 && !validtype[USER_PROCESS]) {
692 (void) printf("\n");
693 exit(0);
694 }
695 }
696
697 /*
698 * Get remote host from utmpx structure
699 */
700 if (utmpp && utmpp->ut_host[0])
701 (void) printf("\t(%.*s)", sizeof (utmpp->ut_host),
702 utmpp->ut_host);
703
704 /*
705 * Now, put on the trailing EOL
706 */
707 (void) printf("\n");
708 }
709
710 static void
711 process()
712 {
713 struct passwd *pwp;
714 int i = 0;
715 char *ttname;
716
717 /*
718 * Loop over each entry in /var/adm/utmpx
719 */
720
721 setutxent();
722 while ((utmpp = getutxent()) != NULL) {
723 #ifdef DEBUG
724 (void) printf(
725 "ut_user '%s'\nut_id '%s'\nut_line '%s'\nut_type '%d'\n\n",
726 utmpp->ut_user, utmpp->ut_id, utmpp->ut_line, utmpp->ut_type);
727 #endif
728 if (utmpp->ut_type <= UTMAXTYPE) {
729 /*
730 * Handle "am i"
731 */
732 if (justme) {
733 if (strncmp(myname, utmpp->ut_user,
734 sizeof (utmpp->ut_user)) == 0 &&
735 strncmp(mytty, utmpp->ut_line,
736 sizeof (utmpp->ut_line)) == 0 &&
737 utmpp->ut_type == USER_PROCESS) {
738 /*
739 * we have have found ourselves
740 * in the utmp file and the entry
741 * is a user process, this is not
742 * meaningful otherwise
743 *
744 */
745
746 dump();
747 exit(0);
748 }
749 continue;
750 }
751
752 /*
753 * Print the line if we want it
754 */
755 if (validtype[utmpp->ut_type]) {
756 #ifdef XPG4
757 if (utmpp->ut_type == LOGIN_PROCESS) {
758 if ((utmpp->ut_line[0] == '\0') ||
759 (strcmp(utmpp->ut_user, "LOGIN") != 0))
760 continue;
761 }
762 #endif /* XPG4 */
763 dump();
764 }
765 } else {
766 (void) fprintf(stderr,
767 gettext("%s: Error --- entry has ut_type "
768 "of %d\n"), program, utmpp->ut_type);
769 (void) fprintf(stderr,
770 gettext(" when maximum is %d\n"), UTMAXTYPE);
771 }
772 }
773
774 /*
775 * If justme is set at this point than the utmp entry
776 * was not found.
777 */
778 if (justme) {
779 static struct utmpx utmpt;
780
781 pwp = getpwuid(geteuid());
782 if (pwp != NULL)
783 while (i < (int)sizeof (utmpt.ut_user) &&
784 *pwp->pw_name != 0)
785 utmpt.ut_user[i++] = *pwp->pw_name++;
786
787 ttname = ttyname(1);
788
789 i = 0;
790 if (ttname != NULL)
791 while (i < (int)sizeof (utmpt.ut_line) &&
792 *ttname != 0)
793 utmpt.ut_line[i++] = *ttname++;
794
795 utmpt.ut_id[0] = 0;
796 utmpt.ut_pid = getpid();
797 utmpt.ut_type = USER_PROCESS;
798 (void) time(&utmpt.ut_xtime);
799 utmpp = &utmpt;
800 dump();
801 exit(0);
802 }
803 }
804
805 /*
806 * This routine checks the following:
807 *
808 * 1. File exists
809 *
810 * 2. We have read permissions
811 *
812 * 3. It is a multiple of utmp entries in size
813 *
814 * Failing any of these conditions causes who(1) to
815 * abort processing.
816 *
817 * 4. If file is empty we exit right away as there
818 * is no info to report on.
819 *
820 * This routine does not check utmpx files.
821 */
822 static void
823 ck_file(char *name)
824 {
825 struct stat sbuf;
826 int rc;
827
828 /*
829 * Does file exist? Do stat to check, and save structure
830 * so that we can check on the file's size later on.
831 */
832 if ((rc = stat(name, &sbuf)) == -1) {
833 (void) snprintf(errmsg, sizeof (errmsg),
834 gettext("%s: Cannot stat file '%s'"), program, name);
835 perror(errmsg);
836 exit(1);
837 }
838
839 /*
840 * The only real way we can be sure we can access the
841 * file is to try. If we succeed then we close it.
842 */
843 if (access(name, R_OK) < 0) {
844 (void) snprintf(errmsg, sizeof (errmsg),
845 gettext("%s: Cannot open file '%s'"), program, name);
846 perror(errmsg);
847 exit(1);
848 }
849
850 /*
851 * If the file is empty, we are all done.
852 */
853 if (!sbuf.st_size)
854 exit(0);
855
856 /*
857 * Make sure the file is a utmp file.
858 * We can only check for size being a multiple of
859 * utmp structures in length.
860 */
861 rc = sbuf.st_size % (int)sizeof (struct utmpx);
862 if (rc) {
863 (void) fprintf(stderr, gettext("%s: File '%s' is not "
864 "a utmpx file\n"), program, name);
865 exit(1);
866 }
867 }
868
(载自http://cvs.opensolaris.org/source/xref/on/usr/src/cmd/who/who.c)
相关阅读 更多 +
排行榜 更多 +