]> granicus.if.org Git - procps-ng/blob - src/ps/global.c
ps: Add configurable date format for lstart field
[procps-ng] / src / ps / global.c
1 /*
2  * global.c - generic ps symbols and functions
3  * Copyright 1998-2002 by Albert Cahalan
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include <fcntl.h>
21 #include <grp.h>
22 #include <pwd.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <termios.h>
27 #include <unistd.h>
28
29 #include <sys/ioctl.h>
30 #include <sys/stat.h>
31 #include <sys/sysmacros.h>
32 #include <sys/types.h>
33
34 #include "c.h"
35 #include "xalloc.h"
36
37 #include "common.h"
38
39 #ifndef __GNU_LIBRARY__
40 #define __GNU_LIBRARY__ -1
41 #endif
42 #ifndef __GLIBC__
43 #define __GLIBC__ -1
44 #endif
45 #ifndef __GLIBC_MINOR__
46 #define __GLIBC_MINOR__ -1
47 #endif
48
49 // --- <pids> interface begin ||||||||||||||||||||||||||||||||||||||||||||
50 // -----------------------------------------------------------------------
51 struct pids_info *Pids_info = NULL;   // our required <pids> context
52 enum pids_item *Pids_items;           // allocated as PIDSITEMS
53 int Pids_index;                       // actual number of active enums
54
55 // most of these could be defined as static in the output.c module
56 // (but for future flexibility, the easiest route has been chosen)
57 makREL(ADDR_CODE_END)
58 makREL(ADDR_CODE_START)
59 makREL(ADDR_CURR_EIP)
60 makREL(ADDR_CURR_ESP)
61 makREL(ADDR_STACK_START)
62 makREL(AUTOGRP_ID)
63 makREL(AUTOGRP_NICE)
64 makREL(CGNAME)
65 makREL(CGROUP)
66 makREL(CMD)
67 makREL(CMDLINE)
68 makREL(ENVIRON)
69 makREL(EXE)
70 makREL(FLAGS)
71 makREL(FLT_MAJ)
72 makREL(FLT_MAJ_C)
73 makREL(FLT_MIN)
74 makREL(FLT_MIN_C)
75 makREL(ID_EGID)
76 makREL(ID_EGROUP)
77 makREL(ID_EUID)
78 makREL(ID_EUSER)
79 makREL(ID_FGID)
80 makREL(ID_FGROUP)
81 makREL(ID_FUID)
82 makREL(ID_FUSER)
83 makREL(ID_LOGIN)
84 makREL(ID_PGRP)
85 makREL(ID_PID)
86 makREL(ID_PPID)
87 makREL(ID_RGID)
88 makREL(ID_RGROUP)
89 makREL(ID_RUID)
90 makREL(ID_RUSER)
91 makREL(ID_SESSION)
92 makREL(ID_SGID)
93 makREL(ID_SGROUP)
94 makREL(ID_SUID)
95 makREL(ID_SUSER)
96 makREL(ID_TGID)
97 makREL(ID_TPGID)
98 makREL(IO_READ_BYTES)
99 makREL(IO_READ_CHARS)
100 makREL(IO_READ_OPS)
101 makREL(IO_WRITE_BYTES)
102 makREL(IO_WRITE_CBYTES)
103 makREL(IO_WRITE_CHARS)
104 makREL(IO_WRITE_OPS)
105 makREL(LXCNAME)
106 makREL(NICE)
107 makREL(NLWP)
108 makREL(NS_CGROUP)
109 makREL(NS_IPC)
110 makREL(NS_MNT)
111 makREL(NS_NET)
112 makREL(NS_PID)
113 makREL(NS_TIME)
114 makREL(NS_USER)
115 makREL(NS_UTS)
116 makREL(OOM_ADJ)
117 makREL(OOM_SCORE)
118 makREL(PRIORITY)
119 makREL(PRIORITY_RT)
120 makREL(PROCESSOR)
121 makREL(PROCESSOR_NODE)
122 makREL(RSS)
123 makREL(RSS_RLIM)
124 makREL(SCHED_CLASS)
125 makREL(SD_MACH)
126 makREL(SD_OUID)
127 makREL(SD_SEAT)
128 makREL(SD_SESS)
129 makREL(SD_SLICE)
130 makREL(SD_UNIT)
131 makREL(SD_UUNIT)
132 makREL(SIGBLOCKED)
133 makREL(SIGCATCH)
134 makREL(SIGIGNORE)
135 makREL(SIGNALS)
136 makREL(SIGPENDING)
137 makREL(SMAP_PRV_TOTAL)
138 makREL(SMAP_PSS)
139 makREL(STATE)
140 makREL(SUPGIDS)
141 makREL(SUPGROUPS)
142 makREL(TICS_ALL)
143 makREL(TICS_ALL_C)
144 makREL(TIME_ALL)
145 makREL(TIME_ELAPSED)
146 makREL(TICS_BEGAN)
147 makREL(TTY)
148 makREL(TTY_NAME)
149 makREL(TTY_NUMBER)
150 makREL(UTILIZATION)
151 makREL(UTILIZATION_C)
152 makREL(VM_DATA)
153 makREL(VM_RSS_LOCKED)
154 makREL(VM_RSS)
155 makREL(VM_SIZE)
156 makREL(VM_STACK)
157 makREL(VSIZE_BYTES)
158 makREL(WCHAN_NAME)
159 makREL(extra)
160 makREL(noop)
161 // -----------------------------------------------------------------------
162 // --- <pids> interface end ||||||||||||||||||||||||||||||||||||||||||||||
163
164
165 static const char * saved_personality_text = "You found a bug!";
166
167 int             all_processes = -1;
168 const char     *bsd_j_format = (const char *)0xdeadbeef;
169 const char     *bsd_l_format = (const char *)0xdeadbeef;
170 const char     *bsd_s_format = (const char *)0xdeadbeef;
171 const char     *bsd_u_format = (const char *)0xdeadbeef;
172 const char     *bsd_v_format = (const char *)0xdeadbeef;
173 int             bsd_c_option = -1;
174 int             bsd_e_option = -1;
175 unsigned        cached_euid = 0xffffffff;
176 int             cached_tty = -1;
177 char            forest_prefix[4 * 32*1024 + 100];     // FIXME
178 int             forest_type = -1;
179 unsigned        format_flags = 0xffffffff;   /* -l -f l u s -j... */
180 format_node    *format_list = (format_node *)0xdeadbeef; /* digested formatting options */
181 unsigned        format_modifiers = 0xffffffff;   /* -c -j -y -P -L... */
182 int             header_gap = -1;
183 int             header_type = -1;
184 int             include_dead_children = -1;
185 int             lines_to_next_header = -1;
186 char           *lstart_format = NULL;
187 int             negate_selection = -1;
188 int             running_only = -1;
189 int             page_size = -1;  // "int" for math reasons?
190 unsigned        personality = 0xffffffff;
191 int             prefer_bsd_defaults = -1;
192 int             screen_cols = -1;
193 int             screen_rows = -1;
194 selection_node *selection_list = (selection_node *)0xdeadbeef;
195 unsigned        simple_select = 0xffffffff;
196 sort_node      *sort_list = (sort_node *)0xdeadbeef; /* ready-to-use sort list */
197 const char     *sysv_f_format = (const char *)0xdeadbeef;
198 const char     *sysv_fl_format = (const char *)0xdeadbeef;
199 const char     *sysv_j_format = (const char *)0xdeadbeef;
200 const char     *sysv_l_format = (const char *)0xdeadbeef;
201 unsigned        thread_flags = 0xffffffff;
202 int             unix_f_option = -1;
203 int             user_is_number = -1;
204 int             wchan_is_number = -1;
205 const char     *the_word_help;
206
207 static void reset_selection_list(void){
208   selection_node *old;
209   selection_node *walk = selection_list;
210   if(selection_list == (selection_node *)0xdeadbeef){
211     selection_list = NULL;
212     return;
213   }
214   while(walk){
215     old = walk;
216     walk = old->next;
217     free(old->u);
218     free(old);
219   }
220   selection_list = NULL;
221 }
222
223 // The rules:
224 // 1. Defaults are implementation-specific. (ioctl,termcap,guess)
225 // 2. COLUMNS and LINES override the defaults. (standards compliance)
226 // 3. Command line options override everything else.
227 // 4. Actual output may be more if the above is too narrow.
228 //
229 // SysV tends to spew semi-wide output in all cases. The args
230 // will be limited to 64 or 80 characters, without regard to
231 // screen size. So lines of 120 to 160 chars are normal.
232 // Tough luck if you want more or less than that! HP-UX has a
233 // new "-x" option for 1024-char args in place of comm that
234 // we'll implement at some point.
235 //
236 // BSD tends to make a good effort, then fall back to 80 cols.
237 // Use "ww" to get infinity. This is nicer for "ps | less"
238 // and "watch ps". It can run faster too.
239 static void set_screen_size(void){
240   struct winsize ws;
241   char *columns; /* Unix98 environment variable */
242   char *lines;   /* Unix98 environment variable */
243
244   do{
245     int fd;
246     if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break;
247     if(ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break;
248     if(ioctl(STDIN_FILENO,  TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0) break;
249     fd = open("/dev/tty", O_NOCTTY|O_NONBLOCK|O_RDONLY);
250     if(fd != -1){
251       int ret = ioctl(fd, TIOCGWINSZ, &ws);
252       close(fd);
253       if(ret != -1 && ws.ws_col>0 && ws.ws_row>0) break;
254     }
255     // TODO: ought to do tgetnum("co") and tgetnum("li") here
256     ws.ws_col = 80;
257     ws.ws_row = 24;
258   }while(0);
259   screen_cols = ws.ws_col;  // hmmm, NetBSD subtracts 1
260   screen_rows = ws.ws_row;
261
262   // TODO: delete this line
263   if(!isatty(STDOUT_FILENO)) screen_cols = OUTBUF_SIZE;
264
265   columns = getenv("COLUMNS");
266   if(columns && *columns){
267     long t;
268     char *endptr;
269     t = strtol(columns, &endptr, 0);
270     if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_cols = (int)t;
271   }
272
273   lines   = getenv("LINES");
274   if(lines && *lines){
275     long t;
276     char *endptr;
277     t = strtol(lines, &endptr, 0);
278     if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_rows = (int)t;
279   }
280
281   if((screen_cols<9) || (screen_rows<2))
282     fprintf(stderr,_("your %dx%d screen size is bogus. expect trouble\n"),
283       screen_cols, screen_rows
284     );
285 }
286
287 /**************** personality control **************/
288
289 typedef struct personality_table_struct {
290   const char *name; /* personality name */
291   const void *jump; /* See gcc extension info.   :-)   */
292 } personality_table_struct;
293
294 static int compare_personality_table_structs(const void *a, const void *b){
295   return strcasecmp(((const personality_table_struct*)a)->name,((const personality_table_struct*)b)->name);
296 }
297
298 static const char *set_personality(void){
299   const char *s;
300   size_t sl;
301   char buf[16];
302   personality_table_struct findme = { buf, NULL};
303   personality_table_struct *found;
304   static const personality_table_struct personality_table[] = {
305   {"390",      &&case_390},
306   {"aix",      &&case_aix},
307   {"bsd",      &&case_bsd},
308   {"compaq",   &&case_compaq},
309   {"debian",   &&case_debian},
310   {"default",  &&case_default},
311   {"digital",  &&case_digital},
312   {"gnu",      &&case_gnu},
313   {"hp",       &&case_hp},
314   {"hpux",     &&case_hpux},
315   {"irix",     &&case_irix},
316   {"linux",    &&case_linux},
317   {"old",      &&case_old},
318   {"os390",    &&case_os390},
319   {"posix",    &&case_posix},
320   {"s390",     &&case_s390},
321   {"sco",      &&case_sco},
322   {"sgi",      &&case_sgi},
323   {"solaris2", &&case_solaris2},
324   {"sunos4",   &&case_sunos4},
325   {"svr4",     &&case_svr4},
326   {"sysv",     &&case_sysv},
327   {"tru64",    &&case_tru64},
328   {"unix",     &&case_unix},
329   {"unix95",   &&case_unix95},
330   {"unix98",   &&case_unix98},
331   {"unknown",  &&case_unknown}
332   };
333   const int personality_table_count = sizeof(personality_table)/sizeof(personality_table_struct);
334
335   personality = 0;
336   prefer_bsd_defaults = 0;
337
338   bsd_j_format = "OL_j";
339   bsd_l_format = "OL_l";
340   bsd_s_format = "OL_s";
341   bsd_u_format = "OL_u";
342   bsd_v_format = "OL_v";
343
344   /* When these are NULL, the code does SysV output modifier logic */
345   sysv_f_format  = NULL;
346   sysv_fl_format = NULL;
347   sysv_j_format  = NULL;
348   sysv_l_format  = NULL;
349
350   s = getenv("PS_PERSONALITY");
351   if(!s || !*s) s = getenv("CMD_ENV");
352   if(!s || !*s) s="unknown";   /* "Do The Right Thing[tm]" */
353   if(getenv("I_WANT_A_BROKEN_PS")) s="old";
354   sl = strlen(s);
355   if(sl > 15) return _("environment specified an unknown personality");
356   strncpy(buf, s, sl);
357   buf[sl] = '\0';
358   if ((saved_personality_text = strdup(buf))==NULL) {
359     fprintf(stderr, _("cannot strdup() personality text\n"));
360     exit(EXIT_FAILURE);
361   }
362
363   found = bsearch(&findme, personality_table, personality_table_count,
364       sizeof(personality_table_struct), compare_personality_table_structs
365   );
366
367   if(!found) return _("environment specified an unknown personality");
368
369   goto *(found->jump);    /* See gcc extension info.  :-)   */
370
371   case_bsd:
372     personality = PER_FORCE_BSD | PER_BSD_h | PER_BSD_m;
373     prefer_bsd_defaults = 1;
374     bsd_j_format = "FB_j";
375     bsd_l_format = "FB_l";
376     /* bsd_s_format not used */
377     bsd_u_format = "FB_u";
378     bsd_v_format = "FB_v";
379     return NULL;
380
381   case_old:
382     personality = PER_FORCE_BSD | PER_OLD_m;
383     prefer_bsd_defaults = 1;
384     return NULL;
385
386   case_debian:  /* Toss this? They don't seem to care much. */
387   case_gnu:
388     personality = PER_GOOD_o | PER_OLD_m;
389     prefer_bsd_defaults = 1;
390     sysv_f_format  = "RD_f";
391     /* sysv_fl_format = "RD_fl"; */   /* old Debian ps can't do this! */
392     sysv_j_format  = "RD_j";
393     sysv_l_format  = "RD_l";
394     return NULL;
395
396   case_linux:
397     personality = PER_GOOD_o | PER_ZAP_ADDR | PER_SANE_USER;
398     return NULL;
399
400   case_default: /* use defaults for ps, ignoring other environment variables */
401   case_unknown: /* defaults, but also check inferior environment variables */
402     return NULL;
403
404   case_aix:
405     bsd_j_format = "FB_j";
406     bsd_l_format = "FB_l";
407     /* bsd_s_format not used */
408     bsd_u_format = "FB_u";
409     bsd_v_format = "FB_v";
410     return NULL;
411
412   case_tru64:
413   case_compaq:
414   case_digital:
415     // no PER_NO_DEFAULT_g even though man page claims it
416     // Reality: the g is a NOP
417     personality = PER_GOOD_o | PER_BSD_h;
418     prefer_bsd_defaults = 1;
419     sysv_f_format  = "F5FMT";
420     sysv_fl_format = "FL5FMT";
421     sysv_j_format  = "JFMT";
422     sysv_l_format  = "L5FMT";
423     bsd_j_format = "JFMT";
424     bsd_l_format = "LFMT";
425     bsd_s_format = "SFMT";
426     bsd_u_format = "UFMT";
427     bsd_v_format = "VFMT";
428     return NULL;
429
430   case_sunos4:
431     personality = PER_NO_DEFAULT_g;
432     prefer_bsd_defaults = 1;
433     bsd_j_format = "FB_j";
434     bsd_l_format = "FB_l";
435     /* bsd_s_format not used */
436     bsd_u_format = "FB_u";
437     bsd_v_format = "FB_v";
438     return NULL;
439
440   case_irix:
441   case_sgi:
442     s = getenv("_XPG");
443     if(s && s[0]>'0' && s[0]<='9')
444         return NULL;
445     personality = PER_IRIX_l;
446     return NULL;
447
448   case_os390:  /* IBM's OS/390 OpenEdition on the S/390 mainframe */
449   case_s390:
450   case_390:
451     sysv_j_format  = "J390";  /* don't know what -jl and -jf do */
452     return NULL;
453
454   case_hp:
455   case_hpux:
456     personality = PER_HPUX_x;
457     return NULL;
458
459   case_svr4:
460   case_sysv:
461   case_sco:
462     personality = PER_SVR4_x;
463     return NULL;
464
465   case_posix:
466   case_solaris2:
467   case_unix95:
468   case_unix98:
469   case_unix:
470     return NULL;
471 }
472
473
474 /************ Call this to reinitialize everything ***************/
475 void reset_global(void){
476   proc_t *p;
477   int i;
478
479   reset_selection_list();
480
481 // --- <pids> interface --------------------------------------------------
482   if (!Pids_items)
483     Pids_items = xcalloc(PIDSITEMS, sizeof(enum pids_item));
484
485   for (i = 0; i < PIDSITEMS; i++)
486     Pids_items[i] = PIDS_noop;
487
488   if (!Pids_info) {
489     if (procps_pids_new(&Pids_info, Pids_items, i)) {
490       fprintf(stderr, _("fatal library error, context\n"));
491       exit(EXIT_FAILURE);
492     }
493   }
494
495   Pids_items[0] = PIDS_TTY;
496   procps_pids_reset(Pids_info, Pids_items, 1);
497   if (!(p = fatal_proc_unmounted(Pids_info, 1))) {
498     fprintf(stderr, _("fatal library error, lookup self\n"));
499     exit(EXIT_FAILURE);
500   }
501 // --- <pids> interface --------------------------------------------------
502
503   set_screen_size();
504   set_personality();
505
506   all_processes         = 0;
507   bsd_c_option          = 0;
508   bsd_e_option          = 0;
509   cached_euid           = geteuid();
510   cached_tty            = PIDS_VAL(0, s_int, p, Pids_info);
511 /* forest_prefix must be all zero because of POSIX */
512   forest_type           = 0;
513   format_flags          = 0;   /* -l -f l u s -j... */
514   format_list           = NULL; /* digested formatting options */
515   format_modifiers      = 0;   /* -c -j -y -P -L... */
516   header_gap            = -1;  /* send lines_to_next_header to -infinity */
517   header_type           = HEAD_SINGLE;
518   include_dead_children = 0;
519   lines_to_next_header  = 1;
520   negate_selection      = 0;
521   page_size             = getpagesize();
522   running_only          = 0;
523   selection_list        = NULL;
524   simple_select         = 0;
525   sort_list             = NULL;
526   thread_flags          = 0;
527   unix_f_option         = 0;
528   user_is_number        = 0;
529   wchan_is_number       = 0;
530 /* Translation Note:
531    . The following translatable word will be used to recognize the
532    . user's request for help text.  In other words, the translation
533    . you provide will alter program behavior.
534    .
535    . It must be limited to 15 characters or less.
536    */
537   the_word_help         = _("help");
538 }
539
540 static const char archdefs[] =
541 #ifdef __alpha__
542 " alpha"
543 #endif
544 #ifdef __arm__
545 " arm"
546 #endif
547 #ifdef __hppa__
548 " hppa"
549 #endif
550 #ifdef __i386__
551 " i386"
552 #endif
553 #ifdef __ia64__
554 " ia64"
555 #endif
556 #ifdef __mc68000__
557 " mc68000"
558 #endif
559 #ifdef __mips64__
560 " mips64"
561 #endif
562 #ifdef __mips__
563 " mips"
564 #endif
565 #ifdef __powerpc__
566 " powerpc"
567 #endif
568 #ifdef __sh3__
569 " sh3"
570 #endif
571 #ifdef __sh__
572 " sh"
573 #endif
574 #ifdef __sparc__
575 " sparc"
576 #endif
577 #ifdef __sparc_v9__
578 " sparc_v9"
579 #endif
580 #ifdef __x86_64__
581 " x86_64"
582 #endif
583 "";
584
585 /*********** spew variables ***********/
586 void self_info(void){
587   fprintf(stderr,
588     "BSD j    %s\n"
589     "BSD l    %s\n"
590     "BSD s    %s\n"
591     "BSD u    %s\n"
592     "BSD v    %s\n"
593     "SysV -f  %s\n"
594     "SysV -fl %s\n"
595     "SysV -j  %s\n"
596     "SysV -l  %s\n"
597     "\n",
598     bsd_j_format   ? bsd_j_format   : "(none)",
599     bsd_l_format   ? bsd_l_format   : "(none)",
600     bsd_s_format   ? bsd_s_format   : "(none)",
601     bsd_u_format   ? bsd_u_format   : "(none)",
602     bsd_v_format   ? bsd_v_format   : "(none)",
603     sysv_f_format  ? sysv_f_format  : "(none)",
604     sysv_fl_format ? sysv_fl_format : "(none)",
605     sysv_j_format  ? sysv_j_format  : "(none)",
606     sysv_l_format  ? sysv_l_format  : "(none)"
607   );
608
609   fprintf(stderr, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
610   /* __libc_print_version(); */  /* how can we get the run-time version? */
611   fprintf(stderr, "Compiled with: glibc %d.%d, gcc %d.%d\n\n",
612     __GLIBC__, __GLIBC_MINOR__, __GNUC__, __GNUC_MINOR__
613   );
614
615   fprintf(stderr,
616     "header_gap=%d lines_to_next_header=%d\n"
617     "screen_cols=%d screen_rows=%d\n"
618     "\n",
619     header_gap, lines_to_next_header,
620     screen_cols, screen_rows
621   );
622
623   fprintf(stderr,
624     "personality=0x%08x (from \"%s\")\n"
625     "EUID=%d TTY=%d,%d page_size=%d\n",
626     personality, saved_personality_text,
627     cached_euid, (int)major(cached_tty), (int)minor(cached_tty),
628     (int)(page_size)
629   );
630
631   fprintf(stderr,
632     "sizeof(proc_t)=%d sizeof(long)=%d sizeof(long)=%d\n",
633     (int)sizeof(proc_t), (int)sizeof(long), (int)sizeof(long)
634   );
635
636   fprintf(stderr, "archdefs:%s\n", archdefs);
637 }
638
639 void __attribute__ ((__noreturn__))
640 catastrophic_failure(const char *filename,
641                      unsigned int linenum,
642                      const char *message)
643 {
644   error_at_line(0, 0, filename, linenum, "%s", message);
645   exit(EXIT_FAILURE);
646 }