chiark / gitweb /
c599150858d59bdad3b3d936ccca545dbd1938ab
[elogind.git] / klibc / klibc / tests / minips.c
1 /*
2  * Copyright 1998 by Albert Cahalan; all rights reserved.
3  * This file may be used subject to the terms and conditions of the
4  * GNU Library General Public License Version 2, or any later version
5  * at your option, as published by the Free Software Foundation.
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9  * GNU Library General Public License for more details.
10  */
11
12 /* This is a minimal /bin/ps, designed to be smaller than the old ps
13  * while still supporting some of the more important features of the
14  * new ps. (for total size, note that this ps does not need libproc)
15  * It is suitable for Linux-on-a-floppy systems only.
16  *
17  * Maintainers: do not compile or install for normal systems.
18  * Anyone needing this will want to tweak their compiler anyway.
19  */
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/ioctl.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <dirent.h>
30
31 #include <asm/param.h>  /* HZ */
32 #include <asm/page.h>   /* PAGE_SIZE */
33
34 static int P_euid;
35 static int P_pid;
36 static char P_cmd[16];
37 static char P_state;
38 static int P_ppid, P_pgrp, P_session, P_tty, P_tpgid;
39 static unsigned long P_flags, P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt, P_utime, P_stime;
40 static long P_cutime, P_cstime, P_priority, P_nice, P_timeout, P_it_real_value;
41 static unsigned long P_start_time, P_vsize;
42 static long P_rss;
43 static unsigned long P_rss_rlim, P_start_code, P_end_code, P_start_stack, P_kstk_esp, P_kstk_eip;
44 static unsigned P_signal, P_blocked, P_sigignore, P_sigcatch;
45 static unsigned long P_wchan, P_nswap, P_cnswap;
46
47
48 #if 0
49 static int screen_cols = 80;
50 static int w_count;
51 #endif
52
53 static int want_one_pid;
54 static const char *want_one_command;
55 static int select_notty;
56 static int select_all;
57
58 static int ps_format;
59 static int old_h_option;
60
61 /* we only pretend to support this */
62 static int show_args;    /* implicit with -f and all BSD options */
63 static int bsd_c_option; /* this option overrides the above */
64
65 static int ps_argc;    /* global argc */
66 static char **ps_argv; /* global argv */
67 static int thisarg;    /* index into ps_argv */
68 static char *flagptr;  /* current location in ps_argv[thisarg] */
69
70
71 #ifndef PAGE_SIZE
72 #warning PAGE_SIZE not defined, assuming it is 4096
73 #define PAGE_SIZE 4096
74 #endif
75
76 #ifndef HZ
77 #warning HZ not defined, assuming it is 100
78 #define HZ 100
79 #endif
80
81
82
83 static void usage(void){
84   fprintf(stderr,
85     "-C   select by command name (minimal ps only accepts one)\n"
86     "-p   select by process ID (minimal ps only accepts one)\n"
87     "-e   all processes (same as ax)\n"
88     "a    all processes w/ tty, including other users\n"
89     "x    processes w/o controlling ttys\n"
90     "-f   full format\n"
91     "-j,j job control format\n"
92     "v    virtual memory format\n"
93     "-l,l long format\n"
94     "u    user-oriented format\n"
95     "-o   user-defined format (limited support, only \"ps -o pid=\")\n"
96     "h    no header\n"
97 /*
98     "-A   all processes (same as ax)\n"
99     "c    true command name\n"
100     "-w,w wide output\n"
101 */
102   );
103   exit(1);
104 }
105
106 /*
107  * Return the next argument, or call the usage function.
108  * This handles both:   -oFOO   -o FOO
109  */
110 static const char *get_opt_arg(void){
111   const char *ret;
112   ret = flagptr+1;    /* assume argument is part of ps_argv[thisarg] */
113   if(*ret) return ret;
114   if(++thisarg >= ps_argc) usage();   /* there is nothing left */
115   /* argument is the new ps_argv[thisarg] */
116   ret = ps_argv[thisarg];
117   if(!ret || !*ret) usage();
118   return ret;
119 }
120
121
122 /* return the PID, or 0 if nothing good */
123 static void parse_pid(const char *str){
124   char *endp;
125   int num;
126   if(!str)            goto bad;
127   num = strtol(str, &endp, 0);
128   if(*endp != '\0')   goto bad;
129   if(num<1)           goto bad;
130   if(want_one_pid)    goto bad;
131   want_one_pid = num;
132   return;
133 bad:
134   usage();
135 }
136
137 /***************** parse SysV options, including Unix98  *****************/
138 static void parse_sysv_option(void){
139   do{
140     switch(*flagptr){
141     /**** selection ****/
142     case 'C': /* end */
143       if(want_one_command) usage();
144       want_one_command = get_opt_arg();
145       return; /* can't have any more options */
146     case 'p': /* end */
147       parse_pid(get_opt_arg());
148       return; /* can't have any more options */
149     case 'A':
150     case 'e':
151       select_all++;
152       select_notty++;
153 case 'w':    /* here for now, since the real one is not used */
154       break;
155     /**** output format ****/
156     case 'f':
157       show_args = 1;
158       /* FALL THROUGH */
159     case 'j':
160     case 'l':
161       if(ps_format) usage();
162       ps_format = *flagptr;
163       break;
164     case 'o': /* end */
165       /* We only support a limited form: "ps -o pid="  (yes, just "pid=") */
166       if(strcmp(get_opt_arg(),"pid=")) usage();
167       if(ps_format) usage();
168       ps_format = 'o';
169       old_h_option++;
170       return; /* can't have any more options */
171     /**** other stuff ****/
172 #if 0
173     case 'w':
174       w_count++;
175       break;
176 #endif
177     default:
178       usage();
179     } /* switch */
180   }while(*++flagptr);
181 }
182
183 /************************* parse BSD options **********************/
184 static void parse_bsd_option(void){
185   do{
186     switch(*flagptr){
187     /**** selection ****/
188     case 'a':
189       select_all++;
190       break;
191     case 'x':
192       select_notty++;
193       break;
194     case 'p': /* end */
195       parse_pid(get_opt_arg());
196       return; /* can't have any more options */
197     /**** output format ****/
198     case 'j':
199     case 'l':
200     case 'u':
201     case 'v':
202       if(ps_format) usage();
203       ps_format = 0x80 | *flagptr;  /* use 0x80 to tell BSD from SysV */
204       break;
205     /**** other stuff ****/
206     case 'c':
207       bsd_c_option++;
208 #if 0
209       break;
210 #endif
211     case 'w':
212 #if 0
213       w_count++;
214 #endif
215       break;
216     case 'h':
217       old_h_option++;
218       break;
219     default:
220       usage();
221     } /* switch */
222   }while(*++flagptr);
223 }
224
225 #if 0
226 /* not used yet */
227 static void choose_dimensions(void){
228   struct winsize ws;
229   char *columns;
230   /* screen_cols is 80 by default */
231   if(ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col>30) screen_cols = ws.ws_col;
232   columns = getenv("COLUMNS");
233   if(columns && *columns){
234     long t;
235     char *endptr;
236     t = strtol(columns, &endptr, 0);
237     if(!*endptr && (t>30) && (t<(long)999999999)) screen_cols = (int)t;
238   }
239   if(w_count && (screen_cols<132)) screen_cols=132;
240   if(w_count>1) screen_cols=999999999;
241 }
242 #endif
243
244 static void arg_parse(int argc, char *argv[]){
245   int sel = 0;  /* to verify option sanity */
246   ps_argc = argc;
247   ps_argv = argv;
248   thisarg = 0;
249   /**** iterate over the args ****/
250   while(++thisarg < ps_argc){
251     flagptr = ps_argv[thisarg];
252     switch(*flagptr){
253     case '0' ... '9':
254       show_args = 1;
255       parse_pid(flagptr);
256       break;
257     case '-':
258       flagptr++;
259       parse_sysv_option();
260       break;
261     default:
262       show_args = 1;
263       parse_bsd_option();
264       break;
265     }
266   }
267   /**** sanity check and clean-up ****/
268   if(want_one_pid) sel++;
269   if(want_one_command) sel++;
270   if(select_notty || select_all) sel++;
271   if(sel>1 || select_notty>1 || select_all>1 || bsd_c_option>1 || old_h_option>1) usage();
272   if(bsd_c_option) show_args = 0;
273 }
274
275 /* return 1 if it works, or 0 for failure */
276 static int stat2proc(int pid) {
277     char buf[800]; /* about 40 fields, 64-bit decimal is about 20 chars */
278     int num;
279     int fd;
280     char* tmp;
281     struct stat sb; /* stat() used to get EUID */
282     snprintf(buf, 32, "/proc/%d/stat", pid);
283     if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0;
284     num = read(fd, buf, sizeof buf - 1);
285     fstat(fd, &sb);
286     P_euid = sb.st_uid;
287     close(fd);
288     if(num<80) return 0;
289     buf[num] = '\0';
290     tmp = strrchr(buf, ')');      /* split into "PID (cmd" and "<rest>" */
291     *tmp = '\0';                  /* replace trailing ')' with NUL */
292     /* parse these two strings separately, skipping the leading "(". */
293     memset(P_cmd, 0, sizeof P_cmd);          /* clear */
294     sscanf(buf, "%d (%15c", &P_pid, P_cmd);  /* comm[16] in kernel */
295     num = sscanf(tmp + 2,                    /* skip space after ')' too */
296        "%c "
297        "%d %d %d %d %d "
298        "%lu %lu %lu %lu %lu %lu %lu "
299        "%ld %ld %ld %ld %ld %ld "
300        "%lu %lu "
301        "%ld "
302        "%lu %lu %lu %lu %lu %lu "
303        "%u %u %u %u " /* no use for RT signals */
304        "%lu %lu %lu",
305        &P_state,
306        &P_ppid, &P_pgrp, &P_session, &P_tty, &P_tpgid,
307        &P_flags, &P_min_flt, &P_cmin_flt, &P_maj_flt, &P_cmaj_flt, &P_utime, &P_stime,
308        &P_cutime, &P_cstime, &P_priority, &P_nice, &P_timeout, &P_it_real_value,
309        &P_start_time, &P_vsize,
310        &P_rss,
311        &P_rss_rlim, &P_start_code, &P_end_code, &P_start_stack, &P_kstk_esp, &P_kstk_eip,
312        &P_signal, &P_blocked, &P_sigignore, &P_sigcatch,
313        &P_wchan, &P_nswap, &P_cnswap
314     );
315 /*    fprintf(stderr, "stat2proc converted %d fields.\n",num); */
316     P_vsize /= 1024;
317     P_rss *= (PAGE_SIZE/1024);
318     if(num < 30) return 0;
319     if(P_pid != pid) return 0;
320     return 1;
321 }
322
323 static const char *do_time(unsigned long t){
324   int hh,mm,ss;
325   static char buf[32];
326   int cnt = 0;
327   t /= HZ;
328   ss = t%60;
329   t /= 60;
330   mm = t%60;
331   t /= 60;
332   hh = t%24;
333   t /= 24;
334   if(t) cnt = snprintf(buf, sizeof buf, "%d-", (int)t);
335   snprintf(cnt + buf, sizeof(buf)-cnt, "%02d:%02d:%02d", hh, mm, ss);
336   return buf;
337 }
338
339 static void print_proc(void){
340   char tty[16];
341   snprintf(tty, sizeof tty, "%3d,%-3d", (P_tty>>8)&0xff, P_tty&0xff);
342   switch(ps_format){
343   case 0:
344     printf("%5d %s %s", P_pid, tty, do_time(P_utime+P_stime));
345     break;
346   case 'o':
347     printf("%d\n", P_pid);
348     return; /* don't want the command */
349   case 'l':
350     printf(
351       "%03x %c %5d %5d %5d  - %3d %3d - "
352       "%5ld %06x %s %s",
353       (unsigned)P_flags&0x777, P_state, P_euid, P_pid, P_ppid,
354       (int)P_priority, (int)P_nice, P_vsize/(PAGE_SIZE/1024),
355       (unsigned)(P_wchan&0xffffff), tty, do_time(P_utime+P_stime)
356     );
357     break;
358   case 'f':
359     printf(
360       "%5d %5d %5d  -   -   %s %s",
361       P_euid, P_pid, P_ppid, tty, do_time(P_utime+P_stime)
362     );
363     break;
364   case 'j':
365     printf(
366       "%5d %5d %5d %s %s",
367       P_pid, P_pgrp, P_session, tty, do_time(P_utime+P_stime)
368     );
369     break;
370   case 'u'|0x80:
371     printf(
372       "%5d %5d    -    - %5ld %5ld %s %c   -   %s",
373       P_euid, P_pid, P_vsize, P_rss, tty, P_state,
374       do_time(P_utime+P_stime)
375     );
376     break;
377   case 'v'|0x80:
378     printf(
379       "%5d %s %c %s %6d   -   - %5d    -",
380       P_pid, tty, P_state, do_time(P_utime+P_stime), (int)P_maj_flt,
381       (int)P_rss
382     );
383     break;
384   case 'j'|0x80:
385     printf(
386       "%5d %5d %5d %5d %s %5d %c %5d %s",
387       P_ppid, P_pid, P_pgrp, P_session, tty, P_tpgid, P_state, P_euid, do_time(P_utime+P_stime)
388     );
389     break;
390   case 'l'|0x80:
391     printf(
392       "%03x %5d %5d %5d %3d %3d "
393       "%5ld %4ld %06x %c %s %s",
394       (unsigned)P_flags&0x777, P_euid, P_pid, P_ppid, (int)P_priority, (int)P_nice,
395       P_vsize, P_rss, (unsigned)(P_wchan&0xffffff), P_state, tty, do_time(P_utime+P_stime)
396     );
397     break;
398   default:
399   }
400   if(show_args) printf(" [%s]\n", P_cmd);
401   else          printf(" %s\n", P_cmd);
402 }
403
404
405 int main(int argc, char *argv[]){
406   arg_parse(argc, argv);
407 #if 0
408   choose_dimensions();
409 #endif
410   if(!old_h_option){
411     const char *head;
412     switch(ps_format){
413     default: /* can't happen */
414     case 0:        head = "  PID TTY         TIME CMD"; break;
415     case 'l':      head = "  F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN    TTY       TIME CMD"; break;
416     case 'f':      head = "  UID   PID  PPID  C STIME   TTY       TIME CMD"; break;
417     case 'j':      head = "  PID  PGID   SID TTY         TIME CMD"; break;
418     case 'u'|0x80: head = "  UID   PID %CPU %MEM   VSZ   RSS   TTY   S START     TIME COMMAND"; break;
419     case 'v'|0x80: head = "  PID   TTY   S     TIME  MAJFL TRS DRS   RSS %MEM COMMAND"; break;
420     case 'j'|0x80: head = " PPID   PID  PGID   SID   TTY   TPGID S   UID     TIME COMMAND"; break;
421     case 'l'|0x80: head = "  F   UID   PID  PPID PRI  NI   VSZ  RSS WCHAN  S   TTY       TIME COMMAND"; break;
422     }
423     printf("%s\n",head);
424   }
425   if(want_one_pid){
426     if(stat2proc(want_one_pid)) print_proc();
427     else exit(1);
428   }else{
429     struct dirent *ent;          /* dirent handle */
430     DIR *dir;
431     int ouruid;
432     int found_a_proc;
433     found_a_proc = 0;
434     ouruid = getuid();
435     dir = opendir("/proc");
436     while(( ent = readdir(dir) )){
437       if(*ent->d_name<'0' || *ent->d_name>'9') continue;
438       if(!stat2proc(atoi(ent->d_name))) continue;
439       if(want_one_command){
440         if(strcmp(want_one_command,P_cmd)) continue;
441       }else{
442         if(!select_notty && P_tty==-1) continue;
443         if(!select_all && P_euid!=ouruid) continue;
444       }
445       found_a_proc++;
446       print_proc();
447     }
448     closedir(dir);
449     exit(!found_a_proc);
450   }
451   return 0;
452 }