1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187#define FOR_ps
188#include "toys.h"
189
190GLOBALS(
191 union {
192 struct {
193 struct arg_list *G, *g, *U, *u, *t, *s, *p, *O, *o, *P, *k;
194 } ps;
195 struct {
196 long n, m, d, s;
197 struct arg_list *u, *p, *o, *k, *O;
198 } top;
199 struct {
200 char *L;
201 struct arg_list *G, *g, *P, *s, *t, *U, *u;
202 char *d;
203
204 void *regexes, *snapshot;
205 int signal;
206 pid_t self, match;
207 } pgrep;
208 };
209
210 struct ptr_len gg, GG, pp, PP, ss, tt, uu, UU;
211 struct dirtree *threadparent;
212 unsigned width, height;
213 dev_t tty;
214 void *fields, *kfields;
215 long long ticks, bits, time;
216 int kcount, forcek, sortpos, pidlen;
217 int (*match_process)(long long *slot);
218 void (*show_process)(void *tb);
219)
220
221
222
223struct ofields {
224 struct ofields *next, *prev;
225 short which, len, reverse;
226 char *title;
227};
228
229
230
231
232
233
234
235
236
237
238
239enum {
240 SLOT_pid, SLOT_ppid,
241 SLOT_pgrp, SLOT_sid,
242 SLOT_ttynr, SLOT_ttypgrp,
243 SLOT_flags, SLOT_minflt,
244 SLOT_cminflt, SLOT_majflt,
245 SLOT_cmajflt, SLOT_utime,
246 SLOT_stime, SLOT_cutime,
247 SLOT_cstime, SLOT_priority,
248 SLOT_nice, SLOT_numthreads,
249 SLOT_vmlck, SLOT_starttime,
250 SLOT_vsize, SLOT_rss,
251 SLOT_rsslim, SLOT_startcode,
252 SLOT_endcode, SLOT_startstack,
253 SLOT_esp, SLOT_eip,
254 SLOT_iobytes, SLOT_diobytes,
255 SLOT_utime2, SLOT_uid,
256 SLOT_ruid, SLOT_gid,
257 SLOT_rgid, SLOT_exitsig,
258 SLOT_taskcpu, SLOT_rtprio,
259 SLOT_policy, SLOT_blkioticks,
260 SLOT_gtime, SLOT_cgtime,
261 SLOT_startbss, SLOT_endbss,
262
263 SLOT_upticks, SLOT_argv0len,
264 SLOT_uptime, SLOT_totalram,
265 SLOT_vsz, SLOT_shr,
266 SLOT_pcy, SLOT_rchar,
267 SLOT_wchar, SLOT_rbytes,
268 SLOT_wbytes, SLOT_swap,
269 SLOT_bits, SLOT_tid,
270 SLOT_tcount,
271
272 SLOT_count
273};
274
275
276
277
278
279
280
281struct procpid {
282 long long slot[SLOT_count];
283 unsigned short offset[6];
284 char state;
285 char str[];
286};
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311#define XX 64
312
313
314struct typography {
315 char *name, *help;
316 signed char width, slot;
317} static const typos[] = TAGGED_ARRAY(PS,
318
319 {"PID", "Process ID", 6, SLOT_pid},
320 {"PPID", "Parent Process ID", 6, SLOT_ppid},
321 {"PRI", "Priority (dynamic 0 to 139)", 3, SLOT_priority},
322 {"NI", "Niceness (static 19 to -20)", 3, SLOT_nice},
323 {"ADDR", "Instruction pointer", 4+sizeof(long), SLOT_eip},
324 {"SZ", "4k pages to swap out", 5, SLOT_vsize},
325 {"RSS", "Resident Set Size (DRAM pages)", 6, SLOT_rss},
326 {"PGID", "Process Group ID", 5, SLOT_pgrp},
327 {"VSZ", "Virtual memory size (1k units)", 7, SLOT_vsize},
328 {"MAJFL", "Major page faults", 6, SLOT_majflt},
329 {"MINFL", "Minor page faults", 6, SLOT_minflt},
330 {"PR", "Prio Reversed (dyn 39-0, RT)", 2, SLOT_priority},
331 {"PSR", "Processor last executed on", 3, SLOT_taskcpu},
332 {"RTPRIO", "Realtime priority", 6, SLOT_rtprio},
333 {"SCH", "Scheduling policy (0=other, 1=fifo, 2=rr, 3=batch, 4=iso, 5=idle)",
334 3, SLOT_policy},
335 {"CPU", "Which processor running on", 3, SLOT_taskcpu},
336 {"TID", "Thread ID", 5, SLOT_tid},
337 {"TCNT", "Thread count", 4, SLOT_tcount},
338 {"BIT", "32 or 64", 3, SLOT_bits},
339
340
341 {"TTY", "Controlling terminal", -8, -2},
342 {"WCHAN", "Wait location in kernel", -6, -3},
343 {"LABEL", "Security label", -30, -4},
344 {"COMM", "EXE filename (/proc/PID/exe)", -27, -5},
345 {"NAME", "Process name (PID's argv[0])", -27, -7},
346 {"COMMAND", "EXE path (/proc/PID/exe)", -27, -5},
347 {"CMDLINE", "Command line (argv[])", -27, -6},
348 {"ARGS", "CMDLINE minus initial path", -27, -6},
349 {"CMD", "Thread name (/proc/TID/stat:2)", -15, -1},
350
351
352 {"UID", "User id", 5, SLOT_uid},
353 {"USER", "User name", -12, XX|SLOT_uid},
354 {"RUID", "Real (before suid) user ID", 4, SLOT_ruid},
355 {"RUSER", "Real (before suid) user name", -8, XX|SLOT_ruid},
356 {"GID", "Group ID", 8, SLOT_gid},
357 {"GROUP", "Group name", -8, XX|SLOT_gid},
358 {"RGID", "Real (before sgid) Group ID", 4, SLOT_rgid},
359 {"RGROUP", "Real (before sgid) group name", -8, XX|SLOT_rgid},
360
361
362 {"TIME", "CPU time consumed", 8, SLOT_utime},
363 {"ELAPSED", "Elapsed time since PID start", 11, SLOT_starttime},
364 {"TIME+", "CPU time (high precision)", 9, SLOT_utime},
365
366
367 {"C", "Total %CPU used since start", 1, SLOT_utime2},
368 {"%VSZ", "VSZ as % of physical memory", 5, SLOT_vsize},
369 {"%MEM", "RSS as % of physical memory", 5, SLOT_rss},
370 {"%CPU", "Percentage of CPU time used", 4, SLOT_utime2},
371
372
373 {"VIRT", "Virtual memory size", 4, SLOT_vsz},
374 {"RES", "Short RSS", 4, SLOT_rss},
375 {"SHR", "Shared memory", 4, SLOT_shr},
376 {"READ", "Data read", 6, SLOT_rchar},
377 {"WRITE", "Data written", 6, SLOT_wchar},
378 {"IO", "Data I/O", 6, SLOT_iobytes},
379 {"DREAD", "Data read from disk", 6, SLOT_rbytes},
380 {"DWRITE", "Data written to disk", 6, SLOT_wbytes},
381 {"SWAP", "Swap I/O", 6, SLOT_swap},
382 {"DIO", "Disk I/O", 6, SLOT_diobytes},
383
384
385 {"STIME", "Start time (ISO 8601)", 5, SLOT_starttime},
386 {"F", "Flags 1=FORKNOEXEC 4=SUPERPRIV", 1, XX|SLOT_flags},
387 {"S", "Process state:\n"
388 "\t R (running) S (sleeping) D (device I/O) T (stopped) t (trace stop)\n"
389 "\t X (dead) Z (zombie) P (parked) I (idle)\n"
390 "\t Also between Linux 2.6.33 and 3.13:\n"
391 "\t x (dead) K (wakekill) W (waking)\n",
392 -1, XX},
393 {"STAT", "Process state (S) plus:\n"
394 "\t < high priority N low priority L locked memory\n"
395 "\t s session leader + foreground l multithreaded",
396 -5, XX},
397 {"PCY", "Android scheduling policy", 3, XX|SLOT_pcy},
398);
399
400
401static void help_fields(int len, int multi)
402{
403 int i, j, k, left = 0;
404 struct typography *t;
405
406
407 for (j = len; j--; ) {
408 k = -1;
409
410 for (i=0; i<j; i++) {
411 if (strcmp(typos[toybuf[i]].name, typos[toybuf[i+1]].name)>0) {
412 k = toybuf[i];
413 toybuf[i] = toybuf[i+1];
414 toybuf[i+1] = k;
415 }
416 }
417 if (k == -1) break;
418 }
419
420
421 for (i = j = 0; i<len; i++, j++) {
422 t = (void *)(typos+toybuf[i]);
423 if (strlen(t->help)>30) {
424 if (multi) printf(" %-8s%s\n", t->name, t->help);
425 else j--;
426 } else if (!multi) {
427 left = !(j&1);
428 printf(" %-8s%*s%c"+2*!left, t->name, -30*left, t->help, 10+22*left);
429 }
430 }
431 if (!multi && left) xputc('\n');
432}
433
434
435static void help_help(void)
436{
437 int i, jump = PS_CMD+1-PS_COMM;
438
439
440
441
442
443
444
445 printf("Command line field types:\n\n");
446 for (i = 0; i<jump; i++) toybuf[i] = PS_COMM+i;
447 help_fields(jump, 0);
448
449
450
451 printf("\nProcess attribute field types:\n\n");
452 for (i = 0; i<ARRAY_LEN(typos)-jump; i++) toybuf[i] = i+(i>=PS_COMM)*jump;
453 help_fields(ARRAY_LEN(typos)-jump, 1);
454 help_fields(ARRAY_LEN(typos)-jump, 0);
455
456 xexit();
457}
458
459
460static int shared_match_process(long long *slot)
461{
462 struct ptr_len match[] = {
463 {&TT.gg, SLOT_gid}, {&TT.GG, SLOT_rgid}, {&TT.pp, SLOT_pid},
464 {&TT.PP, SLOT_ppid}, {&TT.ss, SLOT_sid}, {&TT.tt, SLOT_ttynr},
465 {&TT.uu, SLOT_uid}, {&TT.UU, SLOT_ruid}
466 };
467 int i, j;
468 long *ll = 0;
469
470
471 for (i = 0; i < ARRAY_LEN(match); i++) {
472 struct ptr_len *mm = match[i].ptr;
473
474 if (mm->len) {
475 ll = mm->ptr;
476 for (j = 0; j<mm->len; j++) if (ll[j] == slot[match[i].len]) return 1;
477 }
478 }
479
480 return ll ? 0 : -1;
481}
482
483
484static int ps_match_process(long long *slot)
485{
486 int i = shared_match_process(slot);
487
488 if (i>0) return 1;
489
490 if (!i) return 0;
491
492
493 if ((FLAG(a)||FLAG(d)) && slot[SLOT_sid]==*slot) return 0;
494 if (FLAG(a) && !slot[SLOT_ttynr]) return 0;
495 if (!(FLAG(a)||FLAG(d)||FLAG(A)||FLAG(e)) && TT.tty!=slot[SLOT_ttynr])
496 return 0;
497
498 return 1;
499}
500
501
502static char *string_field(struct procpid *tb, struct ofields *field)
503{
504 char *buf = toybuf+sizeof(toybuf)-260, *out = buf, *s;
505 int which = field->which, sl = typos[which].slot;
506 long long *slot = tb->slot, ll = (sl >= 0) ? slot[sl&(XX-1)] : 0;
507
508
509 if (which <= PS_BIT) {
510 char *fmt = "%lld";
511
512 if (which==PS_PRI) ll = 39-ll;
513 if (which==PS_ADDR) fmt = "%llx";
514 else if (which==PS_SZ) ll >>= 12;
515 else if (which==PS_RSS) ll <<= 2;
516 else if (which==PS_VSZ) ll >>= 10;
517 else if (which==PS_PR && ll<-9) fmt="RT";
518 else if ((which==PS_RTPRIO || which==PS_BIT) && ll == 0) fmt="-";
519 sprintf(out, fmt, ll);
520
521
522 } else if (sl < 0) {
523 out = tb->str;
524 sl *= -1;
525
526 if (--sl) out += tb->offset[--sl];
527 if (which==PS_ARGS || which==PS_COMM) {
528 int i;
529
530 s = out;
531 for (i = 0; (which==PS_ARGS) ? i < slot[SLOT_argv0len] : out[i]; i++)
532 if (out[i] == '/') s = out+i+1;
533 out = s;
534 }
535 if (which>=PS_COMM && !*out) sprintf(out = buf, "[%s]", tb->str);
536
537
538 } else if (which <= PS_RGROUP) {
539 sprintf(out, "%lld", ll);
540 if (sl&XX) {
541 if (which > PS_RUSER) {
542 struct group *gr = bufgetgrgid(ll);
543
544 if (gr) out = gr->gr_name;
545 } else {
546 struct passwd *pw = bufgetpwuid(ll);
547
548 if (pw) out = pw->pw_name;
549 }
550 }
551
552
553 } else if (which <= PS_TIME_) {
554 int unit = 60, pad = 2, j = TT.ticks;
555 time_t seconds;
556
557 if (which!=PS_TIME_) unit *= 60*24;
558 else pad = 0;
559
560 if (which==PS_ELAPSED) ll = (slot[SLOT_uptime]*j)-slot[SLOT_starttime];
561 seconds = ll/j;
562
563
564
565 for (s = 0, j = 2*(which==PS_TIME_); j<4; j++) {
566 if (!s && (seconds>unit || j == 1+(which!=PS_TIME))) s = out;
567 if (s) {
568 s += sprintf(s, j ? "%0*ld": "%*ld", pad, (long)(seconds/unit));
569 pad = 2;
570 if ((*s = "-::"[j])) s++;
571 }
572 seconds %= unit;
573 unit /= j ? 60 : 24;
574 }
575 if (which==PS_TIME_ && s-out<8)
576 sprintf(s, ".%02lld", (100*(ll%TT.ticks))/TT.ticks);
577
578
579 } else if (which <= PS__CPU) {
580 ll = slot[sl&(XX-1)]*1000;
581 if (which==PS__VSZ || which==PS__MEM)
582 ll /= slot[SLOT_totalram]/((which==PS__VSZ) ? 1024 : 4096);
583 else if (slot[SLOT_upticks]) ll /= slot[SLOT_upticks];
584 sl = ll;
585 if (which==PS_C) sl += 5;
586 sprintf(out, "%d", sl/10);
587 if (which!=PS_C && sl<1000) sprintf(out+strlen(out), ".%d", sl%10);
588
589
590 } else if (which <= PS_DIO) {
591 int i = abs(field->len);
592
593 if (i<4) i = 4;
594 s = out;
595 if ((ll = slot[typos[which].slot])<0) {
596 ll = -ll;
597 *s++ = '-';
598 if (i>4) i--;
599 }
600 if (which <= PS_SHR) ll *= sysconf(_SC_PAGESIZE);
601 if (TT.forcek) sprintf(out, "%lldk", ll/1024);
602 else human_readable_long(s, ll, i-1, 0, 0);
603
604
605
606 } else if (which==PS_F) sprintf(out, "%llo", (slot[SLOT_flags]>>6)&5);
607 else if (which==PS_S || which==PS_STAT) {
608 s = out;
609 *s++ = tb->state;
610 if (which==PS_STAT) {
611
612 if (slot[SLOT_nice]<0) *s++ = '<';
613 else if (slot[SLOT_nice]>0) *s++ = 'N';
614 if (slot[SLOT_sid]==*slot) *s++ = 's';
615 if (slot[SLOT_vmlck]) *s++ = 'L';
616 if (slot[SLOT_ttypgrp]==*slot) *s++ = '+';
617 }
618 *s = 0;
619 } else if (which==PS_STIME) {
620 time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks;
621
622
623
624
625
626 strftime(out, 260, "%F %T", localtime(&t));
627 out = out+strlen(out)-3-abs(field->len);
628 if (out<buf) out = buf;
629
630 } else if (which==PS_PCY) sprintf(out, "%.2s", get_sched_policy_name(ll));
631 else if (CFG_TOYBOX_DEBUG) error_exit("bad which %d", which);
632
633 return out;
634}
635
636
637static void show_ps(void *p)
638{
639 struct procpid *tb = p;
640 struct ofields *field;
641 int pad, len, width = TT.width, abslen, sign, olen, extra = 0;
642
643
644 for (field = TT.fields; field; field = field->next) {
645 char *out = string_field(tb, field);
646
647
648
649
650 if (width<2) break;
651 if (field != TT.fields) {
652 putchar(' ');
653 width--;
654 }
655
656
657
658 abslen = abs(field->len);
659 sign = field->len<0 ? -1 : 1;
660 olen = (TT.tty) ? utf8len(out) : strlen(out);
661 if ((field->which<=PS_BIT || FLAG(w)) && olen>abslen) {
662
663 extra += olen-abslen;
664 abslen = olen;
665 } else if (extra && olen<abslen) {
666 int unused = abslen-olen;
667
668
669 if (unused>extra) unused = extra;
670 abslen -= unused;
671 extra -= unused;
672 }
673 if (abslen>width) abslen = width;
674 len = pad = abslen;
675 pad *= sign;
676
677
678 if (!field->next && sign<0) {
679 pad = -1;
680 len = width;
681 }
682
683
684 if (olen>len && len>1 && sign<0) {
685 width--;
686 len--;
687 if (field->next) pad++;
688 abslen = 0;
689 }
690
691 if (TT.tty) width -= draw_trim(out, pad, len);
692 else width -= printf("%*.*s", pad, len, out);
693 if (!abslen) putchar('+');
694 if (!width) break;
695 }
696 putchar(TT.time ? '\r' : '\n');
697}
698
699
700
701
702static int get_ps(struct dirtree *new)
703{
704 struct {
705 char *name;
706 long long bits;
707 } fetch[] = {
708
709 {"fd/", _PS_TTY}, {"wchan", _PS_WCHAN}, {"attr/current", _PS_LABEL},
710 {"exe", _PS_COMMAND|_PS_COMM}, {"cmdline", _PS_CMDLINE|_PS_ARGS|_PS_NAME},
711 {"", _PS_NAME}
712 };
713 struct procpid *tb = (void *)toybuf;
714 long long *slot = tb->slot;
715 char *name, *s, *buf = tb->str, *end = 0;
716 struct sysinfo si;
717 int i, j, fd;
718 off_t len;
719
720
721 if (!new->parent)
722 return DIRTREE_RECURSE|DIRTREE_SHUTUP|DIRTREE_PROC
723 |(DIRTREE_SAVE*(TT.threadparent||!TT.show_process));
724
725
726 memset(slot, 0, sizeof(tb->slot));
727 slot[SLOT_tid] = *slot = atol(new->name);
728 if (TT.threadparent && TT.threadparent->extra) {
729 struct procpid *tb2 = (struct procpid *)TT.threadparent->extra;
730
731 *slot = *tb2->slot;
732
733
734 if (*slot == slot[SLOT_tid]) slot = tb2->slot;
735 }
736 fd = dirtree_parentfd(new);
737
738
739 len = 2048;
740 sprintf(buf, "%lld/stat", slot[SLOT_tid]);
741 if (!readfileat(fd, buf, buf, &len)) return 0;
742
743
744
745
746
747
748 if (!(name = strchr(buf, '('))) return 0;
749 for (s = ++name; *s; s++) if (*s == ')') end = s;
750 if (!end || end-name>255) return 0;
751 if (1>sscanf(s = end, ") %c%n", &tb->state, &i)) return 0;
752
753
754
755
756 for (j = SLOT_ppid; j<SLOT_upticks; j++)
757 if (1>sscanf(s += i, " %lld%n", slot+j, &i)) break;
758
759
760
761 for (i = 0; i<end-name; i++)
762 if ((tb->str[i] = name[i]) < ' ')
763 if (!TT.tty) tb->str[i] = '?';
764 buf = tb->str+i;
765 *buf++ = 0;
766 len = sizeof(toybuf)-(buf-toybuf);
767
768
769
770
771
772
773 slot[SLOT_uid] = new->st.st_uid;
774 slot[SLOT_gid] = new->st.st_gid;
775
776
777 slot[SLOT_utime] += slot[SLOT_stime];
778 slot[SLOT_utime2] = slot[SLOT_utime];
779
780
781 if ((TT.bits&(_PS_RGROUP|_PS_RUSER|_PS_STAT|_PS_RUID|_PS_RGID|_PS_SWAP
782 |_PS_IO|_PS_DIO)) || TT.GG.len || TT.UU.len)
783 {
784 off_t temp = len;
785
786 sprintf(buf, "%lld/status", slot[SLOT_tid]);
787 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
788 s = strafter(buf, "\nUid:");
789 slot[SLOT_ruid] = s ? atol(s) : new->st.st_uid;
790 s = strafter(buf, "\nGid:");
791 slot[SLOT_rgid] = s ? atol(s) : new->st.st_gid;
792 if ((s = strafter(buf, "\nVmLck:"))) slot[SLOT_vmlck] = atoll(s)*1024;
793 if ((s = strafter(buf, "\nVmSwap:"))) slot[SLOT_swap] = atoll(s)*1024;
794 }
795
796
797 if (TT.bits&(_PS_READ|_PS_WRITE|_PS_DREAD|_PS_DWRITE|_PS_IO|_PS_DIO)) {
798 off_t temp = len;
799
800 sprintf(buf, "%lld/io", slot[SLOT_tid]);
801 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
802 if ((s = strafter(buf, "rchar:"))) slot[SLOT_rchar] = atoll(s);
803 if ((s = strafter(buf, "wchar:"))) slot[SLOT_wchar] = atoll(s);
804 if ((s = strafter(buf, "read_bytes:"))) slot[SLOT_rbytes] = atoll(s);
805 if ((s = strafter(buf, "write_bytes:"))) slot[SLOT_wbytes] = atoll(s);
806 slot[SLOT_iobytes] = slot[SLOT_rchar]+slot[SLOT_wchar]+slot[SLOT_swap];
807 slot[SLOT_diobytes] = slot[SLOT_rbytes]+slot[SLOT_wbytes]+slot[SLOT_swap];
808 }
809
810
811 if (slot != tb->slot) return 0;
812
813
814 if (TT.match_process && !TT.match_process(slot)) return 0;
815
816
817
818 sysinfo(&si);
819 slot[SLOT_uptime] = si.uptime;
820 slot[SLOT_totalram] = si.totalram;
821 slot[SLOT_upticks] = slot[SLOT_uptime]*TT.ticks - slot[SLOT_starttime];
822
823
824 if (TT.bits&(_PS_VIRT|_PS_SHR)) {
825 off_t temp = len;
826
827 sprintf(buf, "%lld/statm", slot[SLOT_tid]);
828 if (!readfileat(fd, buf, buf, &temp)) *buf = 0;
829
830
831 slot[SLOT_vsz] = slot[SLOT_shr] = 0;
832 sscanf(buf, "%lld %*d %lld", &slot[SLOT_vsz], &slot[SLOT_shr]);
833 }
834
835
836 if (TT.bits&_PS_BIT) {
837 off_t temp = 6;
838
839 sprintf(buf, "%lld/exe", slot[SLOT_tid]);
840 if (readfileat(fd, buf, buf, &temp) && !memcmp(buf, "\177ELF", 4)) {
841 if (buf[4] == 1) slot[SLOT_bits] = 32;
842 else if (buf[4] == 2) slot[SLOT_bits] = 64;
843 }
844 }
845
846
847 if (TT.bits&_PS_PCY)
848 get_sched_policy(slot[SLOT_tid], (void *)&slot[SLOT_pcy]);
849
850
851
852
853
854
855
856
857
858
859
860
861
862 slot[SLOT_argv0len] = 0;
863 for (j = 0; j<ARRAY_LEN(fetch); j++) {
864 tb->offset[j] = buf-(tb->str);
865 if (!(TT.bits&fetch[j].bits)) {
866 *buf++ = 0;
867 continue;
868 }
869
870
871
872
873
874
875
876 len = sizeof(toybuf)-256*(ARRAY_LEN(fetch)-j)-(buf-toybuf)-260;
877 sprintf(buf, "%lld/%s", slot[SLOT_tid], fetch[j].name);
878
879
880
881 if (j==3 || j==5) {
882 struct procpid *ptb = 0;
883 int k;
884
885
886 if (TT.threadparent && TT.threadparent->extra)
887 ptb = (void *)TT.threadparent->extra;
888
889 if (j==3 && !ptb) len = readlinkat0(fd, buf, buf, len);
890 else {
891 if (j==3) i = strlen(s = ptb->str+ptb->offset[3]);
892 else {
893 if (!ptb || slot[SLOT_argv0len]) ptb = tb;
894 i = ptb->slot[SLOT_argv0len];
895 s = ptb->str+ptb->offset[4];
896 while (-1!=(k = stridx(s, '/')) && k<i) {
897 s += k+1;
898 i -= k+1;
899 }
900 }
901 if (i<len) len = i;
902 memcpy(buf, s, len);
903 buf[len] = 0;
904 }
905
906
907 } else if (!j) {
908 int rdev = slot[SLOT_ttynr];
909 struct stat st;
910
911
912 strcpy(buf, "?");
913 if (rdev) {
914
915 for (i = 0; i<3; i++) {
916 sprintf(buf, "%lld/fd/%i", slot[SLOT_tid], i);
917 if (!fstatat(fd, buf, &st, 0) && S_ISCHR(st.st_mode)
918 && st.st_rdev == rdev && (len = readlinkat0(fd, buf, buf, len)))
919 break;
920 }
921
922
923 if (i == 3) {
924 FILE *fp = fopen("/proc/tty/drivers", "r");
925 int tty_major = 0, maj = dev_major(rdev), min = dev_minor(rdev);
926
927 if (fp) {
928 while (fscanf(fp, "%*s %256s %d %*s %*s", buf, &tty_major) == 2) {
929
930 if (tty_major == maj) {
931 len = strlen(buf);
932 len += sprintf(buf+len, "%d", min);
933 if (!stat(buf, &st) && S_ISCHR(st.st_mode) && st.st_rdev==rdev)
934 break;
935 }
936 tty_major = 0;
937 }
938 fclose(fp);
939 }
940
941
942 if (!tty_major) len = sprintf(buf, "%d:%d", maj, min);
943 }
944
945 s = buf;
946 if (strstart(&s, "/dev/")) memmove(buf, s, len -= 4);
947 }
948
949
950 } else {
951 int temp = 0;
952
953
954 if (readfileat(fd, buf, buf, &len) && len>0) {
955
956
957 while (len)
958 if (!buf[len-1] || isspace(buf[len-1])) buf[--len] = 0;
959 else break;
960
961
962
963 for (i=0; i<len-1; i++) {
964 char c = buf[i];
965
966 if (!c) {
967 if (!temp) temp = i;
968 c = ' ';
969 } else if (!TT.tty && c<' ') c = '?';
970 buf[i] = c;
971 }
972 } else *buf = len = 0;
973
974
975
976 slot[SLOT_argv0len] = temp ? temp : len;
977 }
978
979
980 buf += len+1;
981 }
982
983
984 TT.kcount++;
985 if (TT.show_process && !TT.threadparent) {
986 TT.show_process(tb);
987
988 return 0;
989 }
990
991
992 s = xmalloc(buf-toybuf);
993 new->extra = (long)s;
994 memcpy(s, toybuf, buf-toybuf);
995
996 return DIRTREE_SAVE;
997}
998
999
1000static int get_threads(struct dirtree *new)
1001{
1002 struct dirtree *dt;
1003 struct procpid *tb;
1004 unsigned pid, kcount;
1005
1006 if (!new->parent) return get_ps(new);
1007 pid = atol(new->name);
1008
1009 TT.threadparent = new;
1010 if (!get_ps(new)) {
1011
1012 TT.threadparent = 0;
1013
1014 return 0;
1015 }
1016
1017
1018
1019 kcount = TT.kcount;
1020 sprintf(toybuf, "/proc/%u/task", pid);
1021 new->child = dirtree_flagread(toybuf, DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
1022 if (new->child == DIRTREE_ABORTVAL) new->child = 0;
1023 TT.threadparent = 0;
1024 kcount = TT.kcount-kcount+1;
1025 tb = (void *)new->extra;
1026 tb->slot[SLOT_tcount] = kcount;
1027
1028
1029
1030 if (new->child) for (dt = new->child->child; dt; dt = dt->next) {
1031 tb = (void *)dt->extra;
1032 tb->slot[SLOT_pid] = pid;
1033 tb->slot[SLOT_tcount] = kcount;
1034 }
1035
1036
1037 if (!TT.show_process) return DIRTREE_SAVE;
1038 TT.show_process((void *)new->extra);
1039 if ((dt = new->child)) {
1040 new->child = 0;
1041 while (dt->child) {
1042 new = dt->child->next;
1043 TT.show_process((void *)dt->child->extra);
1044 free(dt->child);
1045 dt->child = new;
1046 }
1047 free(dt);
1048 }
1049
1050 return 0;
1051}
1052
1053
1054static char *parse_ko(void *data, char *type, int length)
1055{
1056 struct ofields *field;
1057 char *width, *title, *end, *s;
1058 int i, j, k;
1059
1060
1061 if (length==4 && !strncasecmp(type, "HELP", length)) xexit();
1062
1063
1064
1065
1066 if ((end = strchr(type, '=')) && length>(end-type)) {
1067 title = end+1;
1068 length -= (end-type)+1;
1069 } else {
1070 end = type+length;
1071 title = 0;
1072 }
1073
1074
1075 if ((width = strchr(type, ':')) && width<end) {
1076 if (!title) length = width-type;
1077 } else width = 0;
1078
1079
1080
1081 field = xzalloc(sizeof(struct ofields)+(length+1)*!!title);
1082 if (title) {
1083 memcpy(field->title = (char *)(field+1), title, length);
1084 field->title[field->len = length] = 0;
1085 }
1086
1087 if (width) {
1088 field->len = strtol(++width, &title, 10);
1089 if (!isdigit(*width) || title != end) return title;
1090 end = --width;
1091 }
1092
1093
1094 field->reverse = 1;
1095 if (*type == '-') field->reverse = -1;
1096 else if (*type != '+') type--;
1097 type++;
1098 for (i = 0; i<ARRAY_LEN(typos); i++) {
1099 field->which = i;
1100 for (j = 0; j<2; j++) {
1101 if (!j) s = typos[i].name;
1102
1103 else if (-1==(k = stridx((char []){PS_NI, PS_SCH, PS_ELAPSED, PS__CPU,
1104 PS_VSZ, PS_USER, 0}, i))) continue;
1105 else
1106 s = ((char *[]){"NICE", "SCHED", "ETIME", "PCPU", "VSIZE", "UNAME"})[k];
1107
1108 if (!strncasecmp(type, s, end-type) && strlen(s)==end-type) break;
1109 }
1110 if (j!=2) break;
1111 }
1112 if (i==ARRAY_LEN(typos)) return type;
1113 if (!field->title) field->title = typos[field->which].name;
1114 k = i<2 ? TT.pidlen : typos[field->which].width;
1115 if (!field->len) field->len = k;
1116 else if (k<0) field->len *= -1;
1117 dlist_add_nomalloc(data, (void *)field);
1118
1119 return 0;
1120}
1121
1122
1123
1124static long long get_headers(struct ofields *field, char *buf, int blen)
1125{
1126 long long bits = 0;
1127 int len = 0;
1128
1129 for (; field; field = field->next) {
1130 len += snprintf(buf+len, blen-len, " %*s"+!bits, field->len,
1131 field->title);
1132 bits |= 1LL<<field->which;
1133 }
1134
1135 return bits;
1136}
1137
1138
1139static char *parse_rest(void *data, char *str, int len)
1140{
1141 struct ptr_len *pl = (struct ptr_len *)data;
1142 long *ll = pl->ptr;
1143 char *end;
1144 int num = 0;
1145
1146
1147 if (!(15&pl->len))
1148 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
1149
1150
1151 if (isdigit(*str)) {
1152 ll[pl->len] = xstrtol(str, &end, 10);
1153 if (end==(len+str)) num++;
1154
1155 if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
1156 }
1157
1158 if (pl==&TT.pp || pl==&TT.ss) {
1159 if (num && ll[pl->len]>0) {
1160 pl->len++;
1161
1162 return 0;
1163 }
1164 } else if (pl==&TT.tt) {
1165
1166 if (!num) {
1167 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
1168 if (strstart(&str, "pts/")) {
1169 len -= 4;
1170 num++;
1171 } else if (strstart(&str, "tty")) len -= 3;
1172 }
1173 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
1174 struct stat st;
1175
1176 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
1177 memcpy(end, str, len);
1178 end[len] = 0;
1179 xstat(toybuf, &st);
1180 ll[pl->len++] = st.st_rdev;
1181
1182 return 0;
1183 }
1184 } else if (len<255) {
1185 char name[256];
1186
1187 if (num) {
1188 pl->len++;
1189
1190 return 0;
1191 }
1192
1193 memcpy(name, str, len);
1194 name[len] = 0;
1195 if (pl==&TT.gg || pl==&TT.GG) {
1196 struct group *gr = getgrnam(name);
1197 if (gr) {
1198 ll[pl->len++] = gr->gr_gid;
1199
1200 return 0;
1201 }
1202 } else if (pl==&TT.uu || pl==&TT.UU) {
1203 struct passwd *pw = getpwnam(name);
1204 if (pw) {
1205 ll[pl->len++] = pw->pw_uid;
1206
1207 return 0;
1208 }
1209 }
1210 }
1211
1212
1213 return str;
1214}
1215
1216
1217static int ksort(void *aa, void *bb)
1218{
1219 struct ofields *field;
1220 struct procpid *ta = *(struct procpid **)aa, *tb = *(struct procpid **)bb;
1221 int ret = 0, slot;
1222
1223 for (field = TT.kfields; field && !ret; field = field->next) {
1224 slot = typos[field->which].slot;
1225
1226
1227 if (!(slot&XX)) {
1228 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
1229 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
1230 }
1231
1232
1233 if (!ret) {
1234 memccpy(toybuf, string_field(ta, field), 0, 2048);
1235 toybuf[2048] = 0;
1236 ret = strcmp(toybuf, string_field(tb, field));
1237 }
1238 ret *= field->reverse;
1239 }
1240
1241 return ret;
1242}
1243
1244
1245
1246static struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt)
1247{
1248 while (dt) {
1249 struct dirtree *next = dt->next;
1250
1251 if (dt->extra) *(tb++) = (void *)dt->extra;
1252 if (dt->child) tb = collate_leaves(tb, dt->child);
1253 free(dt);
1254 dt = next;
1255 }
1256
1257 return tb;
1258}
1259
1260
1261
1262static struct procpid **collate(int count, struct dirtree *dt)
1263{
1264 struct procpid **tbsort = xmalloc(count*sizeof(struct procpid *));
1265
1266 collate_leaves(tbsort, dt);
1267
1268 return tbsort;
1269}
1270
1271
1272static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
1273{
1274 struct arg_list def;
1275 int x;
1276
1277 memset(&def, 0, sizeof(struct arg_list));
1278 def.arg = s;
1279 WOULD_EXIT(x, comma_args(arg ? arg : &def, fields, err, parse_ko));
1280 if (x) help_help();
1281}
1282
1283void common_setup(void)
1284{
1285 char buf[128];
1286 int i;
1287
1288 TT.ticks = sysconf(_SC_CLK_TCK);
1289
1290 if (-1 != (i = tty_fd())) {
1291 struct stat st;
1292
1293 if (!fstat(i, &st)) TT.tty = st.st_rdev;
1294 }
1295
1296 if (readfile("/proc/sys/kernel/pid_max", buf, 128))
1297 while (isdigit(buf[TT.pidlen])) TT.pidlen++;
1298 else TT.pidlen = 6;
1299}
1300
1301void ps_main(void)
1302{
1303 char **arg;
1304 struct dirtree *dt;
1305 char *not_o;
1306 int i;
1307
1308 common_setup();
1309
1310
1311 TT.width = 80;
1312 if (!isatty(1) || !terminal_size(&TT.width, 0)) toys.optflags |= FLAG_w;
1313 if (FLAG(w)) TT.width = 99999;
1314
1315
1316 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1317 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1318 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1319 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1320 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1321 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1322 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1323 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1324 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1325 dlist_terminate(TT.kfields);
1326
1327
1328 for (arg = toys.optargs; *arg; arg++)
1329 if (parse_rest(&TT.pp, *arg, strlen(*arg))) error_exit("bad %s", *arg);
1330
1331
1332 not_o = "%sTTY,TIME,CMD";
1333 if (FLAG(f))
1334 sprintf(not_o = toybuf+128,
1335 "USER:12=UID,%%sPPID,%s,STIME,TTY,TIME,ARGS=CMD", FLAG(T) ? "TCNT" :"C");
1336 else if (FLAG(l))
1337 not_o = "F,S,UID,%sPPID,C,PRI,NI,BIT,SZ,WCHAN,TTY,TIME,CMD";
1338 else if (CFG_TOYBOX_ON_ANDROID)
1339 sprintf(not_o = toybuf+128,
1340 "USER,%%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10,S,%s",
1341 FLAG(T) ? "CMD" : "NAME");
1342 sprintf(toybuf, not_o, FLAG(T) ? "PID,TID," : "PID,");
1343
1344
1345 if (FLAG(Z)) default_ko("LABEL", &TT.fields, 0, 0);
1346 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1347
1348 if (TT.ps.O) {
1349 if (TT.fields) TT.fields = ((struct ofields *)TT.fields)->prev;
1350 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1351 if (TT.fields) TT.fields = ((struct ofields *)TT.fields)->next;
1352 }
1353 dlist_terminate(TT.fields);
1354
1355
1356 if (FLAG(f)||FLAG(n)) {
1357 struct ofields *field;
1358
1359 for (field = TT.fields; field; field = field->next) {
1360 if (FLAG(n) && field->which>=PS_UID
1361 && field->which<=PS_RGROUP && (typos[field->which].slot&XX))
1362 field->which--;
1363 }
1364 }
1365
1366
1367
1368 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1369 if (!FLAG(M)) printf("%.*s\n", TT.width, toybuf);
1370 if (!(FLAG(k)||FLAG(M))) TT.show_process = show_ps;
1371 TT.match_process = ps_match_process;
1372 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1373 (FLAG(T) || (TT.bits&(_PS_TID|_PS_TCNT)))
1374 ? get_threads : get_ps);
1375
1376 if ((dt != DIRTREE_ABORTVAL) && (FLAG(k)||FLAG(M))) {
1377 struct procpid **tbsort = collate(TT.kcount, dt);
1378
1379 if (FLAG(M)) {
1380 for (i = 0; i<TT.kcount; i++) {
1381 struct ofields *field;
1382
1383 for (field = TT.fields; field; field = field->next) {
1384 int len = strlen(string_field(tbsort[i], field));
1385
1386 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1387 }
1388 }
1389
1390
1391 get_headers(TT.fields, toybuf, sizeof(toybuf));
1392 printf("%.*s\n", TT.width, toybuf);
1393 }
1394
1395 if (FLAG(k)) qsort(tbsort, TT.kcount, sizeof(void *), (void *)ksort);
1396 for (i = 0; i<TT.kcount; i++) {
1397 show_ps(tbsort[i]);
1398 free(tbsort[i]);
1399 }
1400 if (CFG_TOYBOX_FREE) free(tbsort);
1401 }
1402
1403 if (CFG_TOYBOX_FREE) {
1404 free(TT.gg.ptr);
1405 free(TT.GG.ptr);
1406 free(TT.pp.ptr);
1407 free(TT.PP.ptr);
1408 free(TT.ss.ptr);
1409 free(TT.tt.ptr);
1410 free(TT.uu.ptr);
1411 free(TT.UU.ptr);
1412 llist_traverse(TT.fields, free);
1413 }
1414}
1415
1416#define CLEANUP_ps
1417#define FOR_top
1418#include "generated/flags.h"
1419
1420
1421static void setsort(int pos)
1422{
1423 struct ofields *field, *field2;
1424 int i = 0;
1425
1426 if (pos<0) pos = 0;
1427
1428 for (field = TT.fields; field; field = field->next) {
1429 if ((TT.sortpos = i++)<pos && field->next) continue;
1430 field2 = TT.kfields;
1431 field2->which = field->which;
1432 field2->len = field->len;
1433 break;
1434 }
1435}
1436
1437
1438
1439
1440static int merge_deltas(long long *oslot, long long *nslot, int milis)
1441{
1442 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1443 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1444 int i;
1445
1446 for (i = 0; i<ARRAY_LEN(deltas); i++)
1447 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1448 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1449
1450 return 1;
1451}
1452
1453static int header_line(int line, int rev)
1454{
1455 if (!line) return 0;
1456
1457 if (FLAG(b)) puts(toybuf);
1458 else {
1459 printf("%s%-*.*s%s\r\n", rev?"\033[7m":"", rev?TT.width:0, TT.width, toybuf,
1460 rev?"\033[0m":"");
1461 }
1462
1463 return line-1;
1464}
1465
1466static void top_cursor_cleanup(void)
1467{
1468 tty_esc("?25h");
1469}
1470
1471static void top_common(
1472 int (*filter)(long long *oslot, long long *nslot, int milis))
1473{
1474 long long timeout = 0, now, stats[16];
1475 struct proclist {
1476 struct procpid **tb;
1477 int count;
1478 long long whence;
1479 } plist[2], *plold, *plnew, old, new, mix;
1480 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1481 "iow", "irq", "sirq", "host"};
1482 unsigned tock = 0;
1483 int i, lines, topoff = 0, done = 0;
1484 char stdout_buf[BUFSIZ];
1485
1486 if (!TT.fields) perror_exit("no -o");
1487
1488
1489 if (!FLAG(b)) {
1490 setbuf(stdout, stdout_buf);
1491 sigatexit(top_cursor_cleanup);
1492 tty_esc("?25l");
1493 }
1494
1495 toys.signal = SIGWINCH;
1496 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1497 *scratch = 0;
1498 memset(plist, 0, sizeof(plist));
1499 memset(stats, 0, sizeof(stats));
1500 do {
1501 struct dirtree *dt;
1502 int recalc = 1;
1503
1504 plold = plist+(tock++&1);
1505 plnew = plist+(tock&1);
1506 plnew->whence = millitime();
1507 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1508 (FLAG(H) || (TT.bits&(_PS_TID|_PS_TCNT))) ? get_threads : get_ps);
1509 if (dt == DIRTREE_ABORTVAL) error_exit("no /proc");
1510 plnew->tb = collate(plnew->count = TT.kcount, dt);
1511 TT.kcount = 0;
1512
1513 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1514 long long *st = stats+8*(tock&1);
1515
1516
1517 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1518 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1519 }
1520
1521
1522 if (!plold->tb) {
1523 msleep(250);
1524 continue;
1525 }
1526
1527
1528 old = *plold;
1529 new = *plnew;
1530 mix.tb = xmalloc((old.count+new.count)*sizeof(struct procpid));
1531 mix.count = 0;
1532
1533 while (old.count || new.count) {
1534 struct procpid *otb = old.count ? *old.tb : 0,
1535 *ntb = new.count ? *new.tb : 0;
1536
1537
1538 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1539 old.tb++;
1540 old.count--;
1541
1542 continue;
1543 }
1544
1545
1546 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1547 else {
1548
1549 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1550 mix.tb[mix.count] = otb;
1551 mix.count++;
1552 }
1553 old.tb++;
1554 old.count--;
1555 }
1556 new.tb++;
1557 new.count--;
1558 }
1559
1560
1561 for (;;) {
1562 char was, is;
1563
1564 if (recalc) {
1565 qsort(mix.tb, mix.count, sizeof(struct procpid *), (void *)ksort);
1566 if (!FLAG(b)) {
1567 printf("\033[H\033[J");
1568 if (toys.signal) {
1569 toys.signal = 0;
1570 terminal_probesize(&TT.width, &TT.height);
1571 }
1572 }
1573 if (TT.top.m) TT.height = TT.top.m+5;
1574 lines = TT.height;
1575 }
1576 if (recalc && !FLAG(q)) {
1577
1578 if (*toys.which->name == 't') {
1579 struct ofields field;
1580 char hr[4][32];
1581 long long ll, up = 0;
1582 long run[6];
1583 int j, k;
1584
1585
1586
1587
1588
1589
1590 field.which = PS_S;
1591 memset(run, 0, sizeof(run));
1592 for (i = 0; i<mix.count; i++)
1593 run[1+stridx("RTtZ", *string_field(mix.tb[i], &field))]++;
1594 sprintf(toybuf,
1595 "%ss: %d total, %3ld running, %3ld sleeping, %3ld stopped, "
1596 "%3ld zombie", FLAG(H)?"Thread":"Task", mix.count, run[1], run[0],
1597 run[2]+run[3], run[4]);
1598 lines = header_line(lines, 0);
1599
1600 if (readfile("/proc/meminfo", toybuf+256, sizeof(toybuf)-256)) {
1601 for (i = 0; i<6; i++) {
1602 j = i%3;
1603 pos = strafter(toybuf+256, (char *[]){"MemTotal:","\nMemFree:",
1604 "\nBuffers:","\nSwapTotal:","\nSwapFree:","\nCached:"}[i]);
1605 run[i] = pos ? atol(pos) : 0;
1606 k = (*run>=10000000);
1607 human_readable_long(hr[j+!!j], run[i]>>(10*k), 9, k+1, HR_NODOT);
1608 if (j==1) human_readable_long(hr[1], (run[i-1]-run[i])>>(10*k),
1609 8, k+1, HR_NODOT);
1610 else if (j==2) {
1611 sprintf(toybuf, " %s:%10s total,%10s used,%10s free,%10s %s",
1612 (i<3) ? " Mem" : "Swap", hr[0], hr[1], hr[2], hr[3],
1613 (i<3) ? "buffers" : "cached");
1614 lines = header_line(lines, 0);
1615 }
1616 }
1617 }
1618 pos = toybuf;
1619 i = sysconf(_SC_NPROCESSORS_CONF);
1620 pos += sprintf(pos, "%d%%cpu", i*100);
1621 j = 4+(i>10);
1622
1623
1624
1625 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1626 if (!up) up = 1;
1627 now = up*i;
1628 ll = stats[3] = stats[11] = 0;
1629 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1630 stats[3] = now - llabs(ll);
1631
1632 for (i = 0; i<8; i++) {
1633 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1634 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1635 }
1636 lines = header_line(lines, 0);
1637 } else {
1638 struct ofields *field;
1639 struct procpid tb;
1640
1641 memset(&tb, 0, sizeof(struct procpid));
1642 pos = stpcpy(toybuf, "Totals:");
1643 for (field = TT.fields; field; field = field->next) {
1644 long long ll, bits = 0;
1645 int slot = typos[field->which].slot&(XX-1);
1646
1647 if (field->which<PS_C || field->which>PS_DIO) continue;
1648 ll = 1LL<<field->which;
1649 if (bits&ll) continue;
1650 bits |= ll;
1651 for (i=0; i<mix.count; i++)
1652 tb.slot[slot] += mix.tb[i]->slot[slot];
1653 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1654 " %s: %*s,", typos[field->which].name,
1655 field->len, string_field(&tb, field));
1656 }
1657 *--pos = 0;
1658 lines = header_line(lines, 0);
1659 }
1660
1661 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1662 for (i = 0, is = ' '; *pos; pos++) {
1663 was = is;
1664 is = *pos;
1665 if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
1666 pos[-1] = '[';
1667 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1668 }
1669 if (FLAG(b)) while (isspace(*(pos-1))) --pos;
1670 *pos = 0;
1671 lines = header_line(lines, 1);
1672 }
1673 if (!recalc && !FLAG(b))
1674 printf("\033[%dH\033[J", 1+TT.height-lines);
1675 recalc = 1;
1676
1677 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1678
1679 int bold = !FLAG(b) && mix.tb[i+topoff]->state == 'R';
1680
1681 if (!FLAG(b) && i) putchar('\n');
1682 if (bold) printf("\033[1m");
1683 show_ps(mix.tb[i+topoff]);
1684 if (bold) printf("\033[m");
1685 }
1686
1687 if (TT.top.n && !--TT.top.n) {
1688 done++;
1689 break;
1690 }
1691
1692 now = millitime();
1693 if (timeout<=now) timeout = new.whence+TT.top.d;
1694 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1695
1696
1697 if (FLAG(b)) {
1698 msleep(timeout-now);
1699
1700 xputs("\n\n");
1701 break;
1702 } else fflush(stdout);
1703
1704 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1705 if (i==-1 || i==3 || toupper(i)=='Q') {
1706 done++;
1707 break;
1708 }
1709 if (i==-2) break;
1710 if (i==-3) continue;
1711
1712
1713 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1714 else if (i=='\r' || i==' ') {
1715 timeout = 0;
1716 break;
1717 } else if (toupper(i)=='R')
1718 ((struct ofields *)TT.kfields)->reverse *= -1;
1719 else {
1720 i -= 256;
1721 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1722 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1723
1724 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1725 recalc = 0;
1726
1727 if (i == KEY_UP) topoff--;
1728 else if (i == KEY_DOWN) topoff++;
1729 else if (i == KEY_PGDN) topoff += lines;
1730 else if (i == KEY_PGUP) topoff -= lines;
1731 if (topoff<0) topoff = 0;
1732 if (topoff>mix.count) topoff = mix.count;
1733 }
1734 }
1735 continue;
1736 }
1737
1738 free(mix.tb);
1739 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1740 free(plold->tb);
1741 } while (!done);
1742
1743 if (!FLAG(b)) tty_reset();
1744}
1745
1746static void top_setup(char *defo, char *defk)
1747{
1748 common_setup();
1749
1750
1751 if (FLAG(b)) TT.width = TT.height = 99999;
1752 else {
1753
1754
1755 TT.time = millitime();
1756 start_redraw(&TT.width, &TT.height);
1757 }
1758
1759 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1760 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1761 TT.match_process = shared_match_process;
1762
1763 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1764 dlist_terminate(TT.fields);
1765
1766
1767 default_ko("-S", &TT.kfields, 0, 0);
1768 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1769 dlist_terminate(TT.kfields);
1770 setsort(TT.top.s-1);
1771}
1772
1773void top_main(void)
1774{
1775 sprintf(toybuf, "%cID,USER,%s%%CPU,%%MEM,TIME+,%s", FLAG(H) ? 'T' : 'P',
1776 TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,",
1777 FLAG(H) ? "CMD:15=THREAD,NAME=PROCESS" : "ARGS");
1778 if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
1779 top_setup(toybuf, "-%CPU,-ETIME,-PID");
1780 if (TT.top.O) {
1781 struct ofields *field = TT.fields;
1782
1783 field = field->next->next;
1784 comma_args(TT.top.O, &field, "bad -O", parse_ko);
1785 }
1786
1787 top_common(merge_deltas);
1788}
1789
1790#define CLEANUP_top
1791#define FOR_iotop
1792#include "generated/flags.h"
1793
1794
1795static int iotop_filter(long long *oslot, long long *nslot, int milis)
1796{
1797
1798 if (!FLAG(a)) merge_deltas(oslot, nslot, milis);
1799 else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
1800
1801 return !FLAG(O)||oslot[SLOT_iobytes+!FLAG(A)];
1802}
1803
1804void iotop_main(void)
1805{
1806 char *s1 = 0, *s2 = 0, *d = "D"+!!FLAG(A);
1807
1808 if (FLAG(K)) TT.forcek++;
1809
1810 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1811 s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1812 free(s1);
1813 free(s2);
1814 top_common(iotop_filter);
1815}
1816
1817
1818
1819
1820#define FORCE_FLAGS
1821#define CLEANUP_iotop
1822#define FOR_pgrep
1823#include "generated/flags.h"
1824
1825struct regex_list {
1826 struct regex_list *next;
1827 regex_t reg;
1828};
1829
1830static void do_pgk(struct procpid *tb)
1831{
1832 if (TT.pgrep.signal) {
1833 if (kill(*tb->slot, TT.pgrep.signal)) {
1834 char *s = num_to_sig(TT.pgrep.signal);
1835
1836 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1837 perror_msg("%s->%lld", s, *tb->slot);
1838 }
1839 }
1840 if (!FLAG(c) && (!TT.pgrep.signal || TT.tty)) {
1841 printf("%lld", *tb->slot);
1842 if (FLAG(l))
1843 printf(" %s", tb->str+tb->offset[4]*!!FLAG(f));
1844
1845 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1846 }
1847}
1848
1849static void match_pgrep(void *p)
1850{
1851 struct procpid *tb = p;
1852 regmatch_t match;
1853 struct regex_list *reg;
1854 char *name = tb->str+tb->offset[4]*!!FLAG(f);
1855
1856
1857 if (TT.pgrep.self == *tb->slot) return;
1858
1859 if (TT.pgrep.regexes) {
1860 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1861 if (regexec(®->reg, name, 1, &match, 0)) continue;
1862 if (FLAG(x))
1863 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1864 break;
1865 }
1866 if (!FLAG(v) == !reg) return;
1867 }
1868
1869
1870 toys.exitval = 0;
1871
1872
1873 TT.sortpos++;
1874 if (FLAG(n)||FLAG(o)) {
1875 long long ll = tb->slot[SLOT_starttime];
1876
1877 if (FLAG(o)) ll *= -1;
1878 if (TT.time && TT.time>ll) return;
1879 TT.time = ll;
1880 free(TT.pgrep.snapshot);
1881 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1882 } else do_pgk(tb);
1883}
1884
1885static int pgrep_match_process(long long *slot)
1886{
1887 return !FLAG(v) == !!shared_match_process(slot);
1888}
1889
1890void pgrep_main(void)
1891{
1892 char **arg;
1893 struct regex_list *reg;
1894
1895 TT.pgrep.self = getpid();
1896
1897
1898 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1899 error_exit("bad -L '%s'", TT.pgrep.L);
1900
1901 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1902 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1903 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1904 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1905 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1906 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1907 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1908
1909 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1910 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1911 if (!toys.optc) help_exit("No PATTERN");
1912
1913 if (FLAG(f)) TT.bits |= _PS_CMDLINE;
1914 for (arg = toys.optargs; *arg; arg++) {
1915 reg = xmalloc(sizeof(struct regex_list));
1916 xregcomp(®->reg, *arg, REG_EXTENDED);
1917 reg->next = TT.pgrep.regexes;
1918 TT.pgrep.regexes = reg;
1919 }
1920 TT.match_process = pgrep_match_process;
1921 TT.show_process = match_pgrep;
1922
1923
1924 toys.exitval = 1;
1925
1926 dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
1927 if (FLAG(c)) printf("%d\n", TT.sortpos);
1928 if (TT.pgrep.snapshot) {
1929 do_pgk(TT.pgrep.snapshot);
1930 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1931 }
1932 if (TT.pgrep.d) xputc('\n');
1933}
1934
1935#define CLEANUP_pgrep
1936#define FOR_pkill
1937#include "generated/flags.h"
1938
1939void pkill_main(void)
1940{
1941 char **args = toys.optargs;
1942
1943 if (!FLAG(l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
1944 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1945 if (FLAG(V)) TT.tty = 1;
1946 pgrep_main();
1947}
1948