Move everything from /var/adm to /var/log
[unleashed.git] / usr / src / cmd / who / who.c
blobc61f2a8d98b8689ec789f65d3a369c8e34a71554
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
21 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
22 /* All Rights Reserved */
26 * Copyright (c) 2013 Gary Mills
28 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
29 * Use is subject to license terms.
33 * This program analyzes information found in /var/log/utmpx
35 * Additionally information is gathered from /etc/inittab
36 * if requested.
39 * Syntax:
41 * who am i Displays info on yourself
43 * who -a Displays information about All
44 * entries in /var/log/utmpx
46 * who -b Displays info on last boot
48 * who -d Displays info on DEAD PROCESSES
50 * who -H Displays HEADERS for output
52 * who -l Displays info on LOGIN entries
54 * who -m Same as who am i
56 * who -p Displays info on PROCESSES spawned by init
58 * who -q Displays short information on
59 * current users who LOGGED ON
61 * who -r Displays info of current run-level
63 * who -s Displays requested info in SHORT form
65 * who -t Displays info on TIME changes
67 * who -T Displays writeability of each user
68 * (+ writeable, - non-writeable, ? hung)
70 * who -u Displays LONG info on users
71 * who have LOGGED ON
74 #define DATE_FMT "%b %e %H:%M"
77 * %b Abbreviated month name
78 * %e Day of month
79 * %H hour (24-hour clock)
80 * %M minute
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>
96 static void process(void);
97 static void ck_file(char *);
98 static void dump(void);
100 static struct utmpx *utmpp; /* pointer for getutxent() */
103 * Use the full lengths from utmpx for user and line.
105 #define NMAX (sizeof (utmpp->ut_user))
106 #define LMAX (sizeof (utmpp->ut_line))
108 /* Print minimum field widths. */
109 #define LOGIN_WIDTH 8
110 #define LINE_WIDTH 12
112 static char comment[BUFSIZ]; /* holds inittab comment */
113 static char errmsg[BUFSIZ]; /* used in snprintf for errors */
114 static int fildes; /* file descriptor for inittab */
115 static int Hopt = 0; /* 1 = who -H */
116 static char *inittab; /* ptr to inittab contents */
117 static char *iinit; /* index into inittab */
118 static int justme = 0; /* 1 = who am i */
119 static struct tm *lptr; /* holds user login time */
120 static char *myname; /* pointer to invoker's name */
121 static char *mytty; /* holds device user is on */
122 static char nameval[sizeof (utmpp->ut_user) + 1]; /* invoker's name */
123 static int number = 8; /* number of users per -q line */
124 static int optcnt = 0; /* keeps count of options */
125 static char outbuf[BUFSIZ]; /* buffer for output */
126 static char *program; /* holds name of this program */
127 #ifdef XPG4
128 static int aopt = 0; /* 1 = who -a */
129 static int dopt = 0; /* 1 = who -d */
130 #endif /* XPG4 */
131 static int qopt = 0; /* 1 = who -q */
132 static int sopt = 0; /* 1 = who -s */
133 static struct stat stbuf; /* area for stat buffer */
134 static struct stat *stbufp; /* ptr to structure */
135 static int terse = 1; /* 1 = print terse msgs */
136 static int Topt = 0; /* 1 = who -T */
137 static time_t timnow; /* holds current time */
138 static int totlusrs = 0; /* cntr for users on system */
139 static int uopt = 0; /* 1 = who -u */
140 static char user[sizeof (utmpp->ut_user) + 1]; /* holds user name */
141 static int validtype[UTMAXTYPE+1]; /* holds valid types */
142 static int wrap; /* flag to indicate wrap */
143 static char time_buf[128]; /* holds date and time string */
144 static char *end; /* used in strtol for end pointer */
147 main(int argc, char **argv)
149 int goerr = 0; /* non-zero indicates cmd error */
150 int i;
151 int optsw; /* switch for while of getopt() */
153 (void) setlocale(LC_ALL, "");
155 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
156 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
157 #endif
158 (void) textdomain(TEXT_DOMAIN);
160 validtype[USER_PROCESS] = 1;
161 validtype[EMPTY] = 0;
162 stbufp = &stbuf;
165 * Strip off path name of this command
167 for (i = strlen(argv[0]); i >= 0 && argv[0][i] != '/'; --i)
169 if (i >= 0)
170 argv[0] += i+1;
171 program = argv[0];
174 * Buffer stdout for speed
176 setbuf(stdout, outbuf);
179 * Retrieve options specified on command line
180 * XCU4 - add -m option
182 while ((optsw = getopt(argc, argv, "abdHlmn:pqrstTu")) != EOF) {
183 optcnt++;
184 switch (optsw) {
186 case 'a':
187 optcnt += 7;
188 validtype[BOOT_TIME] = 1;
189 validtype[DEAD_PROCESS] = 1;
190 validtype[LOGIN_PROCESS] = 1;
191 validtype[INIT_PROCESS] = 1;
192 validtype[RUN_LVL] = 1;
193 validtype[OLD_TIME] = 1;
194 validtype[NEW_TIME] = 1;
195 validtype[USER_PROCESS] = 1;
196 #ifdef XPG4
197 aopt = 1;
198 #endif /* XPG4 */
199 uopt = 1;
200 Topt = 1;
201 if (!sopt) terse = 0;
202 break;
204 case 'b':
205 validtype[BOOT_TIME] = 1;
206 if (!uopt) validtype[USER_PROCESS] = 0;
207 break;
209 case 'd':
210 validtype[DEAD_PROCESS] = 1;
211 if (!uopt) validtype[USER_PROCESS] = 0;
212 #ifdef XPG4
213 dopt = 1;
214 #endif /* XPG4 */
215 break;
217 case 'H':
218 optcnt--; /* Don't count Header */
219 Hopt = 1;
220 break;
222 case 'l':
223 validtype[LOGIN_PROCESS] = 1;
224 if (!uopt) validtype[USER_PROCESS] = 0;
225 terse = 0;
226 break;
227 case 'm': /* New XCU4 option */
228 justme = 1;
229 break;
231 case 'n':
232 errno = 0;
233 number = strtol(optarg, &end, 10);
234 if (errno != 0 || *end != '\0') {
235 (void) fprintf(stderr, gettext(
236 "%s: Invalid numeric argument\n"),
237 program);
238 exit(1);
240 if (number < 1) {
241 (void) fprintf(stderr, gettext(
242 "%s: Number of users per line must "
243 "be at least 1\n"), program);
244 exit(1);
246 break;
248 case 'p':
249 validtype[INIT_PROCESS] = 1;
250 if (!uopt) validtype[USER_PROCESS] = 0;
251 break;
253 case 'q':
254 qopt = 1;
255 break;
257 case 'r':
258 validtype[RUN_LVL] = 1;
259 terse = 0;
260 if (!uopt) validtype[USER_PROCESS] = 0;
261 break;
263 case 's':
264 sopt = 1;
265 terse = 1;
266 break;
268 case 't':
269 validtype[OLD_TIME] = 1;
270 validtype[NEW_TIME] = 1;
271 if (!uopt) validtype[USER_PROCESS] = 0;
272 break;
274 case 'T':
275 Topt = 1;
276 #ifdef XPG4
277 terse = 1; /* XPG4 requires -T */
278 #else /* XPG4 */
279 terse = 0;
280 #endif /* XPG4 */
281 break;
283 case 'u':
284 uopt = 1;
285 validtype[USER_PROCESS] = 1;
286 if (!sopt) terse = 0;
287 break;
289 case '?':
290 goerr++;
291 break;
292 default:
293 break;
296 #ifdef XPG4
298 * XCU4 changes - check for illegal sopt, Topt & aopt combination
300 if (sopt == 1) {
301 terse = 1;
302 if (Topt == 1 || aopt == 1)
303 goerr++;
305 #endif /* XPG4 */
307 if (goerr > 0) {
308 #ifdef XPG4
310 * XCU4 - slightly different usage with -s -a & -T
312 (void) fprintf(stderr, gettext("\nUsage:\t%s"), program);
313 (void) fprintf(stderr,
314 gettext(" -s [-bdHlmpqrtu] [utmpx_like_file]\n"));
316 (void) fprintf(stderr, gettext(
317 "\t%s [-abdHlmpqrtTu] [utmpx_like_file]\n"), program);
318 #else /* XPG4 */
319 (void) fprintf(stderr, gettext(
320 "\nUsage:\t%s [-abdHlmpqrstTu] [utmpx_like_file]\n"),
321 program);
322 #endif /* XPG4 */
323 (void) fprintf(stderr,
324 gettext("\t%s -q [-n x] [utmpx_like_file]\n"), program);
325 (void) fprintf(stderr, gettext("\t%s [am i]\n"), program);
327 * XCU4 changes - be explicit with "am i" options
329 (void) fprintf(stderr, gettext("\t%s [am I]\n"), program);
330 (void) fprintf(stderr, gettext(
331 "a\tall (bdlprtu options)\n"));
332 (void) fprintf(stderr, gettext("b\tboot time\n"));
333 (void) fprintf(stderr, gettext("d\tdead processes\n"));
334 (void) fprintf(stderr, gettext("H\tprint header\n"));
335 (void) fprintf(stderr, gettext("l\tlogin processes\n"));
336 (void) fprintf(stderr, gettext(
337 "n #\tspecify number of users per line for -q\n"));
338 (void) fprintf(stderr,
339 gettext("p\tprocesses other than getty or users\n"));
340 (void) fprintf(stderr, gettext("q\tquick %s\n"), program);
341 (void) fprintf(stderr, gettext("r\trun level\n"));
342 (void) fprintf(stderr, gettext(
343 "s\tshort form of %s (no time since last output or pid)\n"),
344 program);
345 (void) fprintf(stderr, gettext("t\ttime changes\n"));
346 (void) fprintf(stderr, gettext(
347 "T\tstatus of tty (+ writable, - not writable, "
348 "? hung)\n"));
349 (void) fprintf(stderr, gettext("u\tuseful information\n"));
350 (void) fprintf(stderr,
351 gettext("m\tinformation only about current terminal\n"));
352 (void) fprintf(stderr, gettext(
353 "am i\tinformation about current terminal "
354 "(same as -m)\n"));
355 (void) fprintf(stderr, gettext(
356 "am I\tinformation about current terminal "
357 "(same as -m)\n"));
358 exit(1);
362 * XCU4: If -q option ignore all other options
364 if (qopt == 1) {
365 Hopt = 0;
366 sopt = 0;
367 Topt = 0;
368 uopt = 0;
369 justme = 0;
370 validtype[ACCOUNTING] = 0;
371 validtype[BOOT_TIME] = 0;
372 validtype[DEAD_PROCESS] = 0;
373 validtype[LOGIN_PROCESS] = 0;
374 validtype[INIT_PROCESS] = 0;
375 validtype[RUN_LVL] = 0;
376 validtype[OLD_TIME] = 0;
377 validtype[NEW_TIME] = 0;
378 validtype[USER_PROCESS] = 1;
381 if (argc == optind + 1) {
382 optcnt++;
383 ck_file(argv[optind]);
384 (void) utmpxname(argv[optind]);
388 * Test for 'who am i' or 'who am I'
389 * XCU4 - check if justme was already set by -m option
391 if (justme == 1 || (argc == 3 && strcmp(argv[1], "am") == 0 &&
392 ((argv[2][0] == 'i' || argv[2][0] == 'I') &&
393 argv[2][1] == '\0'))) {
394 justme = 1;
395 myname = nameval;
396 (void) cuserid(myname);
397 if ((mytty = ttyname(fileno(stdin))) == NULL &&
398 (mytty = ttyname(fileno(stdout))) == NULL &&
399 (mytty = ttyname(fileno(stderr))) == NULL) {
400 (void) fprintf(stderr, gettext(
401 "Must be attached to terminal for 'am I' option\n"));
402 (void) fflush(stderr);
403 exit(1);
404 } else
405 mytty += 5; /* bump past "/dev/" */
408 if (!terse) {
409 if (Hopt)
410 (void) printf(gettext(
411 "NAME LINE TIME IDLE PID COMMENTS\n"));
413 timnow = time(0);
415 if ((fildes = open("/etc/inittab",
416 O_NONBLOCK|O_RDONLY)) == -1) {
417 (void) snprintf(errmsg, sizeof (errmsg),
418 gettext("%s: Cannot open /etc/inittab"), program);
419 perror(errmsg);
420 exit(errno);
423 if (fstat(fildes, stbufp) == -1) {
424 (void) snprintf(errmsg, sizeof (errmsg),
425 gettext("%s: Cannot stat /etc/inittab"), program);
426 perror(errmsg);
427 exit(errno);
430 if ((inittab = malloc(stbufp->st_size + 1)) == NULL) {
431 (void) snprintf(errmsg, sizeof (errmsg),
432 gettext("%s: Cannot allocate %ld bytes"),
433 program, stbufp->st_size);
434 perror(errmsg);
435 exit(errno);
438 if (read(fildes, inittab, stbufp->st_size)
439 != stbufp->st_size) {
440 (void) snprintf(errmsg, sizeof (errmsg),
441 gettext("%s: Error reading /etc/inittab"),
442 program);
443 perror(errmsg);
444 exit(errno);
447 inittab[stbufp->st_size] = '\0';
448 iinit = inittab;
449 } else {
450 if (Hopt) {
451 #ifdef XPG4
452 if (dopt) {
453 (void) printf(gettext(
454 "NAME LINE TIME COMMENTS\n"));
455 } else {
456 (void) printf(
457 gettext("NAME LINE TIME\n"));
459 #else /* XPG4 */
460 (void) printf(
461 gettext("NAME LINE TIME\n"));
462 #endif /* XPG4 */
465 process();
468 * 'who -q' requires EOL upon exit,
469 * followed by total line
471 if (qopt)
472 (void) printf(gettext("\n# users=%d\n"), totlusrs);
473 return (0);
476 static void
477 dump()
479 char device[sizeof (utmpp->ut_line) + 1];
480 time_t hr;
481 time_t idle;
482 time_t min;
483 char path[sizeof (utmpp->ut_line) + 6];
484 int pexit;
485 int pterm;
486 int rc;
487 char w; /* writeability indicator */
490 * Get and check user name
492 if (utmpp->ut_user[0] == '\0')
493 (void) strcpy(user, " .");
494 else {
495 (void) strncpy(user, utmpp->ut_user, sizeof (user));
496 user[sizeof (user) - 1] = '\0';
498 totlusrs++;
501 * Do print in 'who -q' format
503 if (qopt) {
505 * XCU4 - Use non user macro for correct user count
507 if (((totlusrs - 1) % number) == 0 && totlusrs > 1)
508 (void) printf("\n");
509 (void) printf("%-*.*s ", LOGIN_WIDTH, NMAX, user);
510 return;
514 pexit = (int)' ';
515 pterm = (int)' ';
518 * Get exit info if applicable
520 if (utmpp->ut_type == RUN_LVL || utmpp->ut_type == DEAD_PROCESS) {
521 pterm = utmpp->ut_exit.e_termination;
522 pexit = utmpp->ut_exit.e_exit;
526 * Massage ut_xtime field
528 lptr = localtime(&utmpp->ut_xtime);
529 (void) strftime(time_buf, sizeof (time_buf),
530 dcgettext(NULL, DATE_FMT, LC_TIME), lptr);
533 * Get and massage device
535 if (utmpp->ut_line[0] == '\0')
536 (void) strcpy(device, " .");
537 else {
538 (void) strncpy(device, utmpp->ut_line,
539 sizeof (utmpp->ut_line));
540 device[sizeof (utmpp->ut_line)] = '\0';
544 * Get writeability if requested
545 * XCU4 - only print + or - for user processes
547 if (Topt && (utmpp->ut_type == USER_PROCESS)) {
548 w = '-';
549 (void) strcpy(path, "/dev/");
550 (void) strncpy(path + 5, utmpp->ut_line,
551 sizeof (utmpp->ut_line));
552 path[5 + sizeof (utmpp->ut_line)] = '\0';
554 if ((rc = stat(path, stbufp)) == -1) w = '?';
555 else if ((stbufp->st_mode & S_IWOTH) ||
556 (stbufp->st_mode & S_IWGRP)) /* Check group & other */
557 w = '+';
559 } else
560 w = ' ';
563 * Print the TERSE portion of the output
565 (void) printf("%-*.*s %c %-12s %s", LOGIN_WIDTH, NMAX, user,
566 w, device, time_buf);
568 if (!terse) {
570 * Stat device for idle time
571 * (Don't complain if you can't)
573 rc = -1;
574 if (utmpp->ut_type == USER_PROCESS) {
575 (void) strcpy(path, "/dev/");
576 (void) strncpy(path + 5, utmpp->ut_line,
577 sizeof (utmpp->ut_line));
578 path[5 + sizeof (utmpp->ut_line)] = '\0';
579 rc = stat(path, stbufp);
581 if (rc != -1) {
582 idle = timnow - stbufp->st_mtime;
583 hr = idle/3600;
584 min = (unsigned)(idle/60)%60;
585 if (hr == 0 && min == 0)
586 (void) printf(gettext(" . "));
587 else {
588 if (hr < 24)
589 (void) printf(" %2d:%2.2d", (int)hr,
590 (int)min);
591 else
592 (void) printf(gettext(" old "));
597 * Add PID for verbose output
599 if (utmpp->ut_type != BOOT_TIME &&
600 utmpp->ut_type != RUN_LVL &&
601 utmpp->ut_type != ACCOUNTING)
602 (void) printf(" %5ld", utmpp->ut_pid);
605 * Handle /etc/inittab comment
607 if (utmpp->ut_type == DEAD_PROCESS) {
608 (void) printf(gettext(" id=%4.4s "),
609 utmpp->ut_id);
610 (void) printf(gettext("term=%-3d "), pterm);
611 (void) printf(gettext("exit=%d "), pexit);
612 } else if (utmpp->ut_type != INIT_PROCESS) {
614 * Search for each entry in inittab
615 * string. Keep our place from
616 * search to search to try and
617 * minimize the work. Wrap once if needed
618 * for each entry.
620 wrap = 0;
622 * Look for a line beginning with
623 * utmpp->ut_id
625 while ((rc = strncmp(utmpp->ut_id, iinit,
626 strcspn(iinit, ":"))) != 0) {
627 for (; *iinit != '\n'; iinit++)
629 iinit++;
632 * Wrap once if necessary to
633 * find entry in inittab
635 if (*iinit == '\0') {
636 if (!wrap) {
637 iinit = inittab;
638 wrap = 1;
643 if (*iinit != '\0') {
645 * We found our entry
647 for (iinit++; *iinit != '#' &&
648 *iinit != '\n'; iinit++)
650 if (*iinit == '#') {
651 for (iinit++; *iinit == ' ' ||
652 *iinit == '\t'; iinit++)
654 for (rc = 0; *iinit != '\n'; iinit++)
655 comment[rc++] = *iinit;
656 comment[rc] = '\0';
657 } else
658 (void) strcpy(comment, " ");
660 (void) printf(" %s", comment);
661 } else
662 iinit = inittab; /* Reset pointer */
664 if (utmpp->ut_type == INIT_PROCESS)
665 (void) printf(gettext(" id=%4.4s"), utmpp->ut_id);
667 #ifdef XPG4
668 else
669 if (dopt && utmpp->ut_type == DEAD_PROCESS) {
670 (void) printf(gettext("\tterm=%-3d "), pterm);
671 (void) printf(gettext("exit=%d "), pexit);
673 #endif /* XPG4 */
677 * Handle RUN_LVL process - If no alt. file - Only one!
679 if (utmpp->ut_type == RUN_LVL) {
680 (void) printf(" %c %5ld %c", pterm, utmpp->ut_pid,
681 pexit);
682 if (optcnt == 1 && !validtype[USER_PROCESS]) {
683 (void) printf("\n");
684 exit(0);
689 * Handle BOOT_TIME process - If no alt. file - Only one!
691 if (utmpp->ut_type == BOOT_TIME) {
692 if (optcnt == 1 && !validtype[USER_PROCESS]) {
693 (void) printf("\n");
694 exit(0);
699 * Get remote host from utmpx structure
701 if (utmpp && utmpp->ut_host[0])
702 (void) printf("\t(%.*s)", sizeof (utmpp->ut_host),
703 utmpp->ut_host);
706 * Now, put on the trailing EOL
708 (void) printf("\n");
711 static void
712 process()
714 struct passwd *pwp;
715 int i = 0;
716 char *ttname;
719 * Loop over each entry in /var/log/utmpx
722 setutxent();
723 while ((utmpp = getutxent()) != NULL) {
724 #ifdef DEBUG
725 (void) printf(
726 "ut_user '%s'\nut_id '%s'\nut_line '%s'\nut_type '%d'\n\n",
727 utmpp->ut_user, utmpp->ut_id, utmpp->ut_line, utmpp->ut_type);
728 #endif
729 if (utmpp->ut_type <= UTMAXTYPE) {
731 * Handle "am i"
733 if (justme) {
734 if (strncmp(myname, utmpp->ut_user,
735 sizeof (utmpp->ut_user)) == 0 &&
736 strncmp(mytty, utmpp->ut_line,
737 sizeof (utmpp->ut_line)) == 0 &&
738 utmpp->ut_type == USER_PROCESS) {
740 * we have have found ourselves
741 * in the utmp file and the entry
742 * is a user process, this is not
743 * meaningful otherwise
747 dump();
748 exit(0);
750 continue;
754 * Print the line if we want it
756 if (validtype[utmpp->ut_type]) {
757 #ifdef XPG4
758 if (utmpp->ut_type == LOGIN_PROCESS) {
759 if ((utmpp->ut_line[0] == '\0') ||
760 (strcmp(utmpp->ut_user,
761 "LOGIN") != 0))
762 continue;
764 #endif /* XPG4 */
765 dump();
767 } else {
768 (void) fprintf(stderr,
769 gettext("%s: Error --- entry has ut_type "
770 "of %d\n"), program, utmpp->ut_type);
771 (void) fprintf(stderr,
772 gettext(" when maximum is %d\n"), UTMAXTYPE);
777 * If justme is set at this point than the utmp entry
778 * was not found.
780 if (justme) {
781 static struct utmpx utmpt;
783 pwp = getpwuid(geteuid());
784 if (pwp != NULL)
785 while (i < (int)sizeof (utmpt.ut_user) &&
786 *pwp->pw_name != 0)
787 utmpt.ut_user[i++] = *pwp->pw_name++;
789 ttname = ttyname(1);
791 i = 0;
792 if (ttname != NULL)
793 while (i < (int)sizeof (utmpt.ut_line) &&
794 *ttname != 0)
795 utmpt.ut_line[i++] = *ttname++;
797 utmpt.ut_id[0] = 0;
798 utmpt.ut_pid = getpid();
799 utmpt.ut_type = USER_PROCESS;
800 (void) time(&utmpt.ut_xtime);
801 utmpp = &utmpt;
802 dump();
803 exit(0);
808 * This routine checks the following:
810 * 1. File exists
812 * 2. We have read permissions
814 * 3. It is a multiple of utmp entries in size
816 * Failing any of these conditions causes who(1) to
817 * abort processing.
819 * 4. If file is empty we exit right away as there
820 * is no info to report on.
822 * This routine does not check utmpx files.
824 static void
825 ck_file(char *name)
827 struct stat sbuf;
828 int rc;
831 * Does file exist? Do stat to check, and save structure
832 * so that we can check on the file's size later on.
834 if ((rc = stat(name, &sbuf)) == -1) {
835 (void) snprintf(errmsg, sizeof (errmsg),
836 gettext("%s: Cannot stat file '%s'"), program, name);
837 perror(errmsg);
838 exit(1);
842 * The only real way we can be sure we can access the
843 * file is to try. If we succeed then we close it.
845 if (access(name, R_OK) < 0) {
846 (void) snprintf(errmsg, sizeof (errmsg),
847 gettext("%s: Cannot open file '%s'"), program, name);
848 perror(errmsg);
849 exit(1);
853 * If the file is empty, we are all done.
855 if (!sbuf.st_size)
856 exit(0);
859 * Make sure the file is a utmp file.
860 * We can only check for size being a multiple of
861 * utmp structures in length.
863 rc = sbuf.st_size % (int)sizeof (struct utmpx);
864 if (rc) {
865 (void) fprintf(stderr, gettext("%s: File '%s' is not "
866 "a utmpx file\n"), program, name);
867 exit(1);