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