]> granicus.if.org Git - procps-ng/blob - src/w.c
63f81c725f6824a4f38d09cbdb04625382711897
[procps-ng] / src / w.c
1 /*
2  * w - show what logged in users are doing.
3  *
4  * Almost entirely rewritten from scratch by Charles Blake circa
5  * June 1996. Some vestigal traces of the original may exist.
6  * That was done in 1993 by Larry Greenfield with some fixes by
7  * Michael K. Johnson.
8  *
9  * Changes by Albert Cahalan, 2002.
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
24  */
25
26 #include <ctype.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <getopt.h>
30 #include <limits.h>
31 #include <locale.h>
32 #include <locale.h>
33 #include <pwd.h>
34 #include <pwd.h>
35 #include <signal.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/ioctl.h>
40 #include <sys/mman.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <termios.h>
44 #include <time.h>
45 #include <unistd.h>
46 #ifdef HAVE_UTMPX_H
47 #       include <utmpx.h>
48 #else
49 #       include <utmp.h>
50 #endif
51 #include <arpa/inet.h>
52
53 #include "c.h"
54 #include "fileutils.h"
55 #include "nls.h"
56
57 #include "misc.h"
58 #include "pids.h"
59
60 static int ignoreuser = 0;      /* for '-u' */
61 static int oldstyle = 0;        /* for '-o' */
62
63 #ifdef HAVE_UTMPX_H
64 typedef struct utmpx utmp_t;
65 #else
66 typedef struct utmp utmp_t;
67 #endif
68
69 #if !defined(UT_HOSTSIZE) || defined(__UT_HOSTSIZE)
70 #       define UT_HOSTSIZE __UT_HOSTSIZE
71 #       define UT_LINESIZE __UT_LINESIZE
72 #       define UT_NAMESIZE __UT_NAMESIZE
73 #endif
74
75 #ifdef W_SHOWFROM
76 # define FROM_STRING "on"
77 #else
78 # define FROM_STRING "off"
79 #endif
80
81 #define MAX_CMD_WIDTH   512
82 #define MIN_CMD_WIDTH   7
83
84 /*
85  * This routine is careful since some programs leave utmp strings
86  * unprintable. Always outputs at least 16 chars padded with
87  * spaces on the right if necessary.
88  */
89 static void print_host(const char *restrict host, int len, const int fromlen)
90 {
91         const char *last;
92         int width = 0;
93
94         if (len > fromlen)
95                 len = fromlen;
96         last = host + len;
97         for (; host < last; host++) {
98                 if (*host == '\0') break;
99                 if (isprint(*host) && *host != ' ') {
100                         fputc(*host, stdout);
101                         ++width;
102                 } else {
103                         fputc('-', stdout);
104                         ++width;
105                         break;
106                 }
107         }
108
109         /*
110          * space-fill, and a '-' too if needed to ensure the
111          * column exists
112          */
113         if (!width) {
114                 fputc('-', stdout);
115                 ++width;
116         }
117         while (width++ < fromlen)
118                 fputc(' ', stdout);
119 }
120
121
122 /* This routine prints the display part of the host or IPv6 link address interface */
123 static void print_display_or_interface(const char *restrict host, int len, int restlen)
124 {
125         const char *const end = host + (len > 0 ? len : 0);
126         const char *disp, *tmp;
127
128         if (restlen <= 0) return; /* not enough space for printing anything */
129
130         /* search for a collon (might be a display) */
131         disp = host;
132         while ( (disp < end) && (*disp != ':') && isprint(*disp) ) disp++;
133
134         /* colon found */
135         if (disp < end && *disp == ':') {
136                 /* detect multiple colons -> IPv6 in the host (not a display) */
137                 tmp = disp+1;
138                 while ( (tmp < end) && (*tmp != ':') && isprint(*tmp) ) tmp++;
139
140                 if (tmp >= end || *tmp != ':') { /* multiple colons not found - it's a display */
141
142                         /* number of chars till the end of the input field */
143                         len -= (disp - host);
144
145                         /* if it is still longer than the rest of the output field, then cut it */
146                         if (len > restlen) len = restlen;
147
148                         /* print the display */
149                         while ((len > 0) && isprint(*disp) && (*disp != ' ')) {
150                                 len--; restlen--;
151                                 fputc(*disp, stdout);
152                                 disp++;
153                         }
154
155                         if ((len > 0) && (*disp != '\0')) { /* space or nonprintable found - replace with dash and stop printing */
156                                 restlen--;
157                                 fputc('-', stdout);
158                         }
159                 } else { /* multiple colons found - it's an IPv6 address */
160
161                         /* search for % (interface separator in case of IPv6 link address) */
162                         while ( (tmp < end) && (*tmp != '%') && isprint(*tmp) ) tmp++;
163
164                         if (tmp < end && *tmp == '%') { /* interface separator found */
165
166                                 /* number of chars till the end of the input field */
167                                 len -= (tmp - host);
168
169                                 /* if it is still longer than the rest of the output field, then cut it */
170                                 if (len > restlen) len = restlen;
171
172                                 /* print the interface */
173                                 while ((len > 0) && isprint(*tmp) && (*tmp != ' ')) {
174                                         len--; restlen--;
175                                         fputc(*tmp, stdout);
176                                         tmp++;
177                                 }
178                                 if ((len > 0) && (*tmp != '\0')) {  /* space or nonprintable found - replace with dash and stop printing */
179                                         restlen--;
180                                         fputc('-', stdout);
181                                 }
182                         }
183                 }
184         }
185
186         /* padding with spaces */
187         while (restlen > 0) {
188                 fputc(' ', stdout);
189                 restlen--;
190         }
191 }
192
193
194 /* This routine prints either the hostname or the IP address of the remote */
195 static void print_from(const utmp_t *restrict const u, const int ip_addresses, const int fromlen) {
196         char buf[fromlen + 1];
197         char buf_ipv6[INET6_ADDRSTRLEN];
198         int len;
199 #ifndef __CYGWIN__
200         int32_t ut_addr_v6[4];      /* IP address of the remote host */
201
202         if (ip_addresses) { /* -i switch used */
203                 memcpy(&ut_addr_v6, &u->ut_addr_v6, sizeof(ut_addr_v6));
204                 if (IN6_IS_ADDR_V4MAPPED(&ut_addr_v6)) {
205                         /* map back */
206                         ut_addr_v6[0] = ut_addr_v6[3];
207                         ut_addr_v6[1] = 0;
208                         ut_addr_v6[2] = 0;
209                         ut_addr_v6[3] = 0;
210                 }
211                 if (ut_addr_v6[1] || ut_addr_v6[2] || ut_addr_v6[3]) {
212                         /* IPv6 */
213                         if (!inet_ntop(AF_INET6, &ut_addr_v6, buf_ipv6, sizeof(buf_ipv6))) {
214                                 strcpy(buf, ""); /* invalid address, clean the buffer */
215                         } else {
216                                 strncpy(buf, buf_ipv6, fromlen); /* address valid, copy to buffer */
217                         }
218                 } else {
219                         /* IPv4 */
220                         if (!(ut_addr_v6[0] && inet_ntop(AF_INET, &ut_addr_v6[0], buf, sizeof(buf)))) {
221                                 strcpy(buf, ""); /* invalid address, clean the buffer */
222                         }
223                 }
224                 buf[fromlen] = '\0';
225
226                 len = strlen(buf);
227                 if (len) { /* IP address is non-empty, print it (and concatenate with display, if present) */
228                         fputs(buf, stdout);
229                         /* show the display part of the host or IPv6 link addr. interface, if present */
230                         print_display_or_interface(u->ut_host, UT_HOSTSIZE, fromlen - len);
231                 } else { /* IP address is empty, print the host instead */
232                         print_host(u->ut_host, UT_HOSTSIZE, fromlen);
233                 }
234         } else {  /* -i switch NOT used */
235                 print_host(u->ut_host, UT_HOSTSIZE, fromlen);
236         }
237 #else
238         print_host(u->ut_host, UT_HOSTSIZE, fromlen);
239 #endif
240 }
241
242
243 /* compact 7 char format for time intervals (belongs in libproc?) */
244 static void print_time_ival7(time_t t, int centi_sec, FILE * fout)
245 {
246         if ((long)t < (long)0) {
247                 /* system clock changed? */
248                 printf("   ?   ");
249                 return;
250         }
251         if (oldstyle) {
252                 if (t >= 48 * 60 * 60)
253                         /* > 2 days */
254                         fprintf(fout, _(" %2lludays"), (unsigned long long)t / (24 * 60 * 60));
255                 else if (t >= 60 * 60)
256                         /* > 1 hour */
257                         /* Translation Hint: Hours:Minutes */
258                         fprintf(fout, " %2llu:%02u ", (unsigned long long)t / (60 * 60),
259                                 (unsigned)((t / 60) % 60));
260                 else if (t > 60)
261                         /* > 1 minute */
262                         /* Translation Hint: Minutes:Seconds */
263                         fprintf(fout, _(" %2llu:%02um"), (unsigned long long)t / 60, (unsigned)t % 60);
264                 else
265                         fprintf(fout, "       ");
266         } else {
267                 if (t >= 48 * 60 * 60)
268                         /* 2 days or more */
269                         fprintf(fout, _(" %2lludays"), (unsigned long long)t / (24 * 60 * 60));
270                 else if (t >= 60 * 60)
271                         /* 1 hour or more */
272                         /* Translation Hint: Hours:Minutes */
273                         fprintf(fout, _(" %2llu:%02um"), (unsigned long long)t / (60 * 60),
274                                 (unsigned)((t / 60) % 60));
275                 else if (t > 60)
276                         /* 1 minute or more */
277                         /* Translation Hint: Minutes:Seconds */
278                         fprintf(fout, " %2llu:%02u ", (unsigned long long)t / 60, (unsigned)t % 60);
279                 else
280                         /* Translation Hint: Seconds:Centiseconds */
281                         fprintf(fout, _(" %2llu.%02us"), (unsigned long long)t, centi_sec);
282         }
283 }
284
285 /* stat the device file to get an idle time */
286 static time_t idletime(const char *restrict const tty)
287 {
288         struct stat sbuf;
289         if (stat(tty, &sbuf) != 0)
290                 return 0;
291         return time(NULL) - sbuf.st_atime;
292 }
293
294 /* 7 character formatted login time */
295
296 static void print_logintime(time_t logt, FILE * fout)
297 {
298
299         /* Abbreviated of weekday can be longer than 3 characters,
300          * see for instance hu_HU.  Using 16 is few bytes more than
301          * enough.  */
302         char time_str[16];
303         time_t curt;
304         struct tm *logtm, *curtm;
305         int today;
306
307         curt = time(NULL);
308         curtm = localtime(&curt);
309         /* localtime returns a pointer to static memory */
310         today = curtm->tm_yday;
311         logtm = localtime(&logt);
312         if (curt - logt > 12 * 60 * 60 && logtm->tm_yday != today) {
313                 if (curt - logt > 6 * 24 * 60 * 60) {
314                         strftime(time_str, sizeof(time_str), "%b", logtm);
315                         fprintf(fout, " %02d%3s%02d", logtm->tm_mday,
316                                 time_str, logtm->tm_year % 100);
317                 } else {
318                         strftime(time_str, sizeof(time_str), "%a", logtm);
319                         fprintf(fout, " %3s%02d  ", time_str,
320                                 logtm->tm_hour);
321                 }
322         } else {
323                 fprintf(fout, " %02d:%02d  ", logtm->tm_hour, logtm->tm_min);
324         }
325 }
326
327 /*
328  * Get the Device ID of the given TTY
329  */
330 static int get_tty_device(const char *restrict const name)
331 {
332     struct stat st;
333     static char buf[32];
334     char *dev_paths[] = { "/dev/%s", "/dev/tty%s", "/dev/pts/%s", NULL};
335     int i;
336
337     if (name[0] == '/' && stat(name, &st) == 0)
338         return st.st_rdev;
339
340     for (i=0; dev_paths[i] != NULL; i++) {
341         snprintf(buf, 32, dev_paths[i], name);
342         if (stat(buf, &st) == 0)
343             return st.st_rdev;
344     }
345     return -1;
346 }
347
348 /*
349  * This function scans the process table accumulating total cpu
350  * times for any processes "associated" with this login session.
351  * It also searches for the "best" process to report as "(w)hat"
352  * the user for that login session is doing currently. This the
353  * essential core of 'w'.
354  */
355 static int find_best_proc(
356         const utmp_t * restrict const u,
357         const char *restrict const tty,
358         unsigned long long *restrict const jcpu,
359         unsigned long long *restrict const pcpu,
360         char *cmdline)
361 {
362 #define PIDS_GETINT(e) PIDS_VAL(EU_ ## e, s_int, reap->stacks[i], info)
363 #define PIDS_GETUNT(e) PIDS_VAL(EU_ ## e, u_int, reap->stacks[i], info)
364 #define PIDS_GETULL(e) PIDS_VAL(EU_ ## e, ull_int, reap->stacks[i], info)
365 #define PIDS_GETSTR(e) PIDS_VAL(EU_ ## e, str, reap->stacks[i], info)
366     unsigned uid = ~0U;
367     int found_utpid = 0;
368     int i, total_procs, line;
369     unsigned long long best_time = 0;
370     unsigned long long secondbest_time = 0;
371
372     struct pids_info *info=NULL;
373     struct pids_fetch *reap;
374     enum pids_item items[] = {
375         PIDS_ID_TGID,
376         PIDS_TICS_BEGAN,
377         PIDS_ID_EUID,
378         PIDS_ID_RUID,
379         PIDS_ID_TPGID,
380         PIDS_ID_PGRP,
381         PIDS_TTY,
382         PIDS_TICS_ALL,
383         PIDS_CMDLINE};
384     enum rel_items {
385         EU_TGID, EU_START, EU_EUID, EU_RUID, EU_TPGID, EU_PGRP, EU_TTY,
386         EU_TICS_ALL, EU_CMDLINE};
387
388     *jcpu = 0;
389     *pcpu = 0;
390     if (!ignoreuser) {
391         char buf[UT_NAMESIZE + 1];
392         struct passwd *passwd_data;
393         strncpy(buf, u->ut_user, UT_NAMESIZE);
394         buf[UT_NAMESIZE] = '\0';
395         if ((passwd_data = getpwnam(buf)) == NULL)
396             return 0;
397         uid = passwd_data->pw_uid;
398         /* OK to have passwd_data go out of scope here */
399     }
400
401     line = get_tty_device(tty);
402
403     if (procps_pids_new(&info, items, 9) < 0)
404         xerrx(EXIT_FAILURE,
405               _("Unable to create pid info structure"));
406     if ((reap = procps_pids_reap(info, PIDS_FETCH_TASKS_ONLY)) == NULL)
407         xerrx(EXIT_FAILURE,
408               _("Unable to load process information"));
409     total_procs = reap->counts->total;
410
411     for (i=0; i < total_procs; i++) {
412         /* is this the login process? */
413         if (PIDS_GETINT(TGID) == u->ut_pid) {
414             found_utpid = 1;
415             if (!best_time) {
416                 best_time = PIDS_GETULL(START);
417                 strncpy(cmdline, PIDS_GETSTR(CMDLINE), MAX_CMD_WIDTH);
418                 *pcpu = PIDS_GETULL(TICS_ALL);
419             }
420
421         }
422         if (PIDS_GETINT(TTY) != line)
423             continue;
424         (*jcpu) += PIDS_VAL(EU_TICS_ALL, ull_int, reap->stacks[i], info);
425         if (!(secondbest_time && PIDS_GETULL(START) <= secondbest_time)) {
426             secondbest_time = PIDS_GETULL(START);
427             if (cmdline[0] == '-' && cmdline[1] == '\0') {
428                 strncpy(cmdline, PIDS_GETSTR(CMDLINE), MAX_CMD_WIDTH);
429                 *pcpu = PIDS_GETULL(TICS_ALL);
430             }
431         }
432         if (
433             (!ignoreuser && uid != PIDS_GETUNT(EUID)
434              && uid != PIDS_GETUNT(RUID))
435             || (PIDS_GETINT(PGRP) != PIDS_GETINT(TPGID))
436             || (PIDS_GETULL(START) <= best_time)
437            )
438             continue;
439         best_time = PIDS_GETULL(START);
440         strncpy(cmdline, PIDS_GETSTR(CMDLINE), MAX_CMD_WIDTH);
441         *pcpu = PIDS_GETULL(TICS_ALL);
442     }
443     procps_pids_unref(&info);
444     return found_utpid;
445 #undef PIDS_GETINT
446 #undef PIDS_GETUNT
447 #undef PIDS_GETULL
448 #undef PIDS_GETSTR
449 }
450
451 static void showinfo(
452             utmp_t * u, int formtype, int maxcmd, int from,
453             const int userlen, const int fromlen, const int ip_addresses)
454 {
455     unsigned long long jcpu, pcpu;
456     unsigned i;
457     char uname[UT_NAMESIZE + 1] = "", tty[5 + UT_LINESIZE + 1] = "/dev/";
458     long hertz;
459     char cmdline[MAX_CMD_WIDTH + 1];
460
461     strcpy(cmdline, "-");
462
463     hertz = procps_hertz_get();
464     for (i = 0; i < UT_LINESIZE; i++)
465         /* clean up tty if garbled */
466         if (isalnum(u->ut_line[i]) || (u->ut_line[i] == '/'))
467             tty[i + 5] = u->ut_line[i];
468         else
469             tty[i + 5] = '\0';
470
471     if (find_best_proc(u, tty + 5, &jcpu, &pcpu, cmdline) == 0)
472     /*
473      * just skip if stale utmp entry (i.e. login proc doesn't
474      * exist). If there is a desire a cmdline flag could be
475      * added to optionally show it with a prefix of (stale)
476      * in front of cmd or something like that.
477      */
478         return;
479
480     /* force NUL term for printf */
481     strncpy(uname, u->ut_user, UT_NAMESIZE);
482
483     if (formtype) {
484         printf("%-*.*s%-9.8s", userlen + 1, userlen, uname, u->ut_line);
485         if (from)
486             print_from(u, ip_addresses, fromlen);
487 #ifdef HAVE_UTMPX_H
488         print_logintime(u->ut_tv.tv_sec, stdout);
489 #else
490         print_logintime(u->ut_time, stdout);
491 #endif
492         if (*u->ut_line == ':')
493             /* idle unknown for xdm logins */
494             printf(" ?xdm? ");
495         else
496             print_time_ival7(idletime(tty), 0, stdout);
497         print_time_ival7(jcpu / hertz, (jcpu % hertz) * (100. / hertz),
498                  stdout);
499         if (pcpu > 0)
500             print_time_ival7(pcpu / hertz,
501                              (pcpu % hertz) * (100. / hertz),
502                              stdout);
503         else
504             printf("   ?   ");
505     } else {
506         printf("%-*.*s%-9.8s", userlen + 1, userlen, u->ut_user,
507                u->ut_line);
508         if (from)
509             print_from(u, ip_addresses, fromlen);
510         if (*u->ut_line == ':')
511             /* idle unknown for xdm logins */
512             printf(" ?xdm? ");
513         else
514             print_time_ival7(idletime(tty), 0, stdout);
515     }
516     printf(" %.*s\n", maxcmd, cmdline);
517 }
518
519 static void __attribute__ ((__noreturn__))
520     usage(FILE * out)
521 {
522         fputs(USAGE_HEADER, out);
523         fprintf(out,
524               _(" %s [options] [user]\n"), program_invocation_short_name);
525         fputs(USAGE_OPTIONS, out);
526         fputs(_(" -h, --no-header     do not print header\n"),out);
527         fputs(_(" -u, --no-current    ignore current process username\n"),out);
528         fputs(_(" -s, --short         short format\n"),out);
529         fputs(_(" -f, --from          show remote hostname field\n"),out);
530         fputs(_(" -o, --old-style     old style output\n"),out);
531         fputs(_(" -i, --ip-addr       display IP address instead of hostname (if possible)\n"), out);
532         fputs(USAGE_SEPARATOR, out);
533         fputs(_("     --help     display this help and exit\n"), out);
534         fputs(USAGE_VERSION, out);
535         fprintf(out, USAGE_MAN_TAIL("w(1)"));
536
537         exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
538 }
539
540 int main(int argc, char **argv)
541 {
542         char *user = NULL, *p;
543         utmp_t *u;
544         struct winsize win;
545         int ch;
546         int maxcmd = 80;
547         int userlen = 8;
548         int fromlen = 16;
549         char *env_var;
550
551         /* switches (defaults) */
552         int header = 1;
553         int longform = 1;
554         int from = 1;
555         int ip_addresses = 0;
556
557         enum {
558                 HELP_OPTION = CHAR_MAX + 1
559         };
560
561         static const struct option longopts[] = {
562                 {"no-header", no_argument, NULL, 'h'},
563                 {"no-current", no_argument, NULL, 'u'},
564                 {"short", no_argument, NULL, 's'},
565                 {"from", no_argument, NULL, 'f'},
566                 {"old-style", no_argument, NULL, 'o'},
567                 {"ip-addr", no_argument, NULL, 'i'},
568                 {"help", no_argument, NULL, HELP_OPTION},
569                 {"version", no_argument, NULL, 'V'},
570                 {NULL, 0, NULL, 0}
571         };
572
573 #ifdef HAVE_PROGRAM_INVOCATION_NAME
574         program_invocation_name = program_invocation_short_name;
575 #endif
576         setlocale (LC_ALL, "");
577         bindtextdomain(PACKAGE, LOCALEDIR);
578         textdomain(PACKAGE);
579         atexit(close_stdout);
580
581 #ifndef W_SHOWFROM
582         from = 0;
583 #endif
584
585         while ((ch =
586                 getopt_long(argc, argv, "husfoVi", longopts, NULL)) != -1)
587                 switch (ch) {
588                 case 'h':
589                         header = 0;
590                         break;
591                 case 's':
592                         longform = 0;
593                         break;
594                 case 'f':
595                         from = !from;
596                         break;
597                 case 'V':
598                         printf(PROCPS_NG_VERSION);
599                         exit(0);
600                 case 'u':
601                         ignoreuser = 1;
602                         break;
603                 case 'o':
604                         oldstyle = 1;
605                         break;
606                 case 'i':
607                         ip_addresses = 1;
608                         from = 1;
609                         break;
610                 case HELP_OPTION:
611                         usage(stdout);
612                 default:
613                         usage(stderr);
614                 }
615
616         if ((argv[optind]))
617                 user = (argv[optind]);
618
619         /* Get user field length from environment */
620         if ((env_var = getenv("PROCPS_USERLEN")) != NULL) {
621                 int ut_namesize = UT_NAMESIZE;
622                 userlen = atoi(env_var);
623                 if (userlen < 8 || ut_namesize < userlen) {
624                         xwarnx
625                             (_("User length environment PROCPS_USERLEN must be between 8 and %i, ignoring.\n"),
626                              ut_namesize);
627                         userlen = 8;
628                 }
629         }
630         /* Get from field length from environment */
631         if ((env_var = getenv("PROCPS_FROMLEN")) != NULL) {
632                 fromlen = atoi(env_var);
633                 if (fromlen < 8 || UT_HOSTSIZE < fromlen) {
634                         xwarnx
635                             (_("from length environment PROCPS_FROMLEN must be between 8 and %d, ignoring\n"),
636                              UT_HOSTSIZE);
637                         fromlen = 16;
638                 }
639         }
640         if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
641                 maxcmd = win.ws_col;
642         else if ((p = getenv("COLUMNS")))
643                 maxcmd = atoi(p);
644         else
645                 maxcmd = MAX_CMD_WIDTH;
646 #define CLAMP_CMD_WIDTH(cw) do { \
647         if ((cw) < MIN_CMD_WIDTH) (cw) = MIN_CMD_WIDTH; \
648         if ((cw) > MAX_CMD_WIDTH) (cw) = MAX_CMD_WIDTH; \
649 } while (0)
650         CLAMP_CMD_WIDTH(maxcmd);
651         maxcmd -= 21 + userlen + (from ? fromlen : 0) + (longform ? 20 : 0);
652         CLAMP_CMD_WIDTH(maxcmd);
653 #undef CLAMP_CMD_WIDTH
654
655
656         if (header) {
657                 /* print uptime and headers */
658                 printf("%s\n", procps_uptime_sprint());
659                 /* Translation Hint: Following five uppercase messages are
660                  * headers. Try to keep alignment intact.  */
661                 printf(_("%-*s TTY      "), userlen, _("USER"));
662                 if (from)
663                         printf("%-*s", fromlen - 1, _("FROM"));
664                 if (longform)
665                         printf(_("  LOGIN@   IDLE   JCPU   PCPU WHAT\n"));
666                 else
667                         printf(_("   IDLE WHAT\n"));
668         }
669
670 #ifdef HAVE_UTMPX_H
671         setutxent();
672 #else
673         utmpname(UTMP_FILE);
674         setutent();
675 #endif
676         if (user) {
677                 for (;;) {
678 #ifdef HAVE_UTMPX_H
679                         u = getutxent();
680 #else
681                         u = getutent();
682 #endif
683                         if (!u)
684                                 break;
685                         if (u->ut_type != USER_PROCESS)
686                                 continue;
687                         if (!strncmp(u->ut_user, user, UT_NAMESIZE))
688                                 showinfo(u, longform, maxcmd, from, userlen,
689                                          fromlen, ip_addresses);
690                 }
691         } else {
692                 for (;;) {
693 #ifdef HAVE_UTMPX_H
694                         u = getutxent();
695 #else
696                         u = getutent();
697 #endif
698                         if (!u)
699                                 break;
700                         if (u->ut_type != USER_PROCESS)
701                                 continue;
702                         if (*u->ut_user)
703                                 showinfo(u, longform, maxcmd, from, userlen,
704                                          fromlen, ip_addresses);
705                 }
706         }
707 #ifdef HAVE_UTMPX_H
708         endutxent();
709 #else
710         endutent();
711 #endif
712
713         return EXIT_SUCCESS;
714 }