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;
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", 5, SLOT_pid},
320 {"PPID", "Parent Process ID", 5, 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);
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 if (!field->len) field->len = typos[field->which].width;
1115 else if (typos[field->which].width<0) field->len *= -1;
1116 dlist_add_nomalloc(data, (void *)field);
1117
1118 return 0;
1119}
1120
1121
1122
1123static long long get_headers(struct ofields *field, char *buf, int blen)
1124{
1125 long long bits = 0;
1126 int len = 0;
1127
1128 for (; field; field = field->next) {
1129 len += snprintf(buf+len, blen-len, " %*s"+!bits, field->len,
1130 field->title);
1131 bits |= 1LL<<field->which;
1132 }
1133
1134 return bits;
1135}
1136
1137
1138static char *parse_rest(void *data, char *str, int len)
1139{
1140 struct ptr_len *pl = (struct ptr_len *)data;
1141 long *ll = pl->ptr;
1142 char *end;
1143 int num = 0;
1144
1145
1146 if (!(15&pl->len))
1147 ll = pl->ptr = xrealloc(pl->ptr, sizeof(long)*(pl->len+16));
1148
1149
1150 if (isdigit(*str)) {
1151 ll[pl->len] = xstrtol(str, &end, 10);
1152 if (end==(len+str)) num++;
1153
1154 if (pl==&TT.ss && ll[pl->len]==0) ll[pl->len] = getsid(0);
1155 }
1156
1157 if (pl==&TT.pp || pl==&TT.ss) {
1158 if (num && ll[pl->len]>0) {
1159 pl->len++;
1160
1161 return 0;
1162 }
1163 } else if (pl==&TT.tt) {
1164
1165 if (!num) {
1166 if (strstart(&str, strcpy(toybuf, "/dev/"))) len -= 5;
1167 if (strstart(&str, "pts/")) {
1168 len -= 4;
1169 num++;
1170 } else if (strstart(&str, "tty")) len -= 3;
1171 }
1172 if (len<256 && (!(end = strchr(str, '/')) || end-str>len)) {
1173 struct stat st;
1174
1175 end = toybuf + sprintf(toybuf, "/dev/%s", num ? "pts/" : "tty");
1176 memcpy(end, str, len);
1177 end[len] = 0;
1178 xstat(toybuf, &st);
1179 ll[pl->len++] = st.st_rdev;
1180
1181 return 0;
1182 }
1183 } else if (len<255) {
1184 char name[256];
1185
1186 if (num) {
1187 pl->len++;
1188
1189 return 0;
1190 }
1191
1192 memcpy(name, str, len);
1193 name[len] = 0;
1194 if (pl==&TT.gg || pl==&TT.GG) {
1195 struct group *gr = getgrnam(name);
1196 if (gr) {
1197 ll[pl->len++] = gr->gr_gid;
1198
1199 return 0;
1200 }
1201 } else if (pl==&TT.uu || pl==&TT.UU) {
1202 struct passwd *pw = getpwnam(name);
1203 if (pw) {
1204 ll[pl->len++] = pw->pw_uid;
1205
1206 return 0;
1207 }
1208 }
1209 }
1210
1211
1212 return str;
1213}
1214
1215
1216static int ksort(void *aa, void *bb)
1217{
1218 struct ofields *field;
1219 struct procpid *ta = *(struct procpid **)aa, *tb = *(struct procpid **)bb;
1220 int ret = 0, slot;
1221
1222 for (field = TT.kfields; field && !ret; field = field->next) {
1223 slot = typos[field->which].slot;
1224
1225
1226 if (!(slot&XX)) {
1227 if (ta->slot[slot]<tb->slot[slot]) ret = -1;
1228 if (ta->slot[slot]>tb->slot[slot]) ret = 1;
1229 }
1230
1231
1232 if (!ret) {
1233 memccpy(toybuf, string_field(ta, field), 0, 2048);
1234 toybuf[2048] = 0;
1235 ret = strcmp(toybuf, string_field(tb, field));
1236 }
1237 ret *= field->reverse;
1238 }
1239
1240 return ret;
1241}
1242
1243
1244
1245static struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt)
1246{
1247 while (dt) {
1248 struct dirtree *next = dt->next;
1249
1250 if (dt->extra) *(tb++) = (void *)dt->extra;
1251 if (dt->child) tb = collate_leaves(tb, dt->child);
1252 free(dt);
1253 dt = next;
1254 }
1255
1256 return tb;
1257}
1258
1259
1260
1261static struct procpid **collate(int count, struct dirtree *dt)
1262{
1263 struct procpid **tbsort = xmalloc(count*sizeof(struct procpid *));
1264
1265 collate_leaves(tbsort, dt);
1266
1267 return tbsort;
1268}
1269
1270
1271static void default_ko(char *s, void *fields, char *err, struct arg_list *arg)
1272{
1273 struct arg_list def;
1274 int x;
1275
1276 memset(&def, 0, sizeof(struct arg_list));
1277 def.arg = s;
1278 WOULD_EXIT(x, comma_args(arg ? arg : &def, fields, err, parse_ko));
1279 if (x) help_help();
1280}
1281
1282void ps_main(void)
1283{
1284 char **arg;
1285 struct dirtree *dt;
1286 char *not_o;
1287 int i;
1288
1289 TT.ticks = sysconf(_SC_CLK_TCK);
1290
1291 if (-1 != (i = tty_fd())) {
1292 struct stat st;
1293
1294 if (!fstat(i, &st)) TT.tty = st.st_rdev;
1295 }
1296
1297
1298 TT.width = 80;
1299 if (!isatty(1) || !terminal_size(&TT.width, 0)) toys.optflags |= FLAG_w;
1300 if (FLAG(w)) TT.width = 99999;
1301
1302
1303 comma_args(TT.ps.P, &TT.PP, "bad -P", parse_rest);
1304 comma_args(TT.ps.p, &TT.pp, "bad -p", parse_rest);
1305 comma_args(TT.ps.t, &TT.tt, "bad -t", parse_rest);
1306 comma_args(TT.ps.s, &TT.ss, "bad -s", parse_rest);
1307 comma_args(TT.ps.u, &TT.uu, "bad -u", parse_rest);
1308 comma_args(TT.ps.U, &TT.UU, "bad -U", parse_rest);
1309 comma_args(TT.ps.g, &TT.gg, "bad -g", parse_rest);
1310 comma_args(TT.ps.G, &TT.GG, "bad -G", parse_rest);
1311 comma_args(TT.ps.k, &TT.kfields, "bad -k", parse_ko);
1312 dlist_terminate(TT.kfields);
1313
1314
1315 for (arg = toys.optargs; *arg; arg++)
1316 if (parse_rest(&TT.pp, *arg, strlen(*arg))) error_exit("bad %s", *arg);
1317
1318
1319 not_o = "%sTTY,TIME,CMD";
1320 if (FLAG(f))
1321 sprintf(not_o = toybuf+128,
1322 "USER:12=UID,%%sPPID,%s,STIME,TTY,TIME,ARGS=CMD", FLAG(T) ? "TCNT" :"C");
1323 else if (FLAG(l))
1324 not_o = "F,S,UID,%sPPID,C,PRI,NI,BIT,SZ,WCHAN,TTY,TIME,CMD";
1325 else if (CFG_TOYBOX_ON_ANDROID)
1326 sprintf(not_o = toybuf+128,
1327 "USER,%%sPPID,VSIZE,RSS,WCHAN:10,ADDR:10,S,%s",
1328 FLAG(T) ? "CMD" : "NAME");
1329 sprintf(toybuf, not_o, FLAG(T) ? "PID,TID," : "PID,");
1330
1331
1332 if (FLAG(Z)) default_ko("LABEL", &TT.fields, 0, 0);
1333 default_ko(toybuf, &TT.fields, "bad -o", TT.ps.o);
1334
1335 if (TT.ps.O) {
1336 if (TT.fields) TT.fields = ((struct ofields *)TT.fields)->prev;
1337 comma_args(TT.ps.O, &TT.fields, "bad -O", parse_ko);
1338 if (TT.fields) TT.fields = ((struct ofields *)TT.fields)->next;
1339 }
1340 dlist_terminate(TT.fields);
1341
1342
1343 if (FLAG(f)||FLAG(n)) {
1344 struct ofields *field;
1345
1346 for (field = TT.fields; field; field = field->next) {
1347 if (FLAG(n) && field->which>=PS_UID
1348 && field->which<=PS_RGROUP && (typos[field->which].slot&XX))
1349 field->which--;
1350 }
1351 }
1352
1353
1354
1355 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1356 if (!FLAG(M)) printf("%.*s\n", TT.width, toybuf);
1357 if (!(FLAG(k)||FLAG(M))) TT.show_process = show_ps;
1358 TT.match_process = ps_match_process;
1359 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1360 (FLAG(T) || (TT.bits&(_PS_TID|_PS_TCNT)))
1361 ? get_threads : get_ps);
1362
1363 if ((dt != DIRTREE_ABORTVAL) && (FLAG(k)||FLAG(M))) {
1364 struct procpid **tbsort = collate(TT.kcount, dt);
1365
1366 if (FLAG(M)) {
1367 for (i = 0; i<TT.kcount; i++) {
1368 struct ofields *field;
1369
1370 for (field = TT.fields; field; field = field->next) {
1371 int len = strlen(string_field(tbsort[i], field));
1372
1373 if (abs(field->len)<len) field->len = (field->len<0) ? -len : len;
1374 }
1375 }
1376
1377
1378 get_headers(TT.fields, toybuf, sizeof(toybuf));
1379 printf("%.*s\n", TT.width, toybuf);
1380 }
1381
1382 if (FLAG(k)) qsort(tbsort, TT.kcount, sizeof(void *), (void *)ksort);
1383 for (i = 0; i<TT.kcount; i++) {
1384 show_ps(tbsort[i]);
1385 free(tbsort[i]);
1386 }
1387 if (CFG_TOYBOX_FREE) free(tbsort);
1388 }
1389
1390 if (CFG_TOYBOX_FREE) {
1391 free(TT.gg.ptr);
1392 free(TT.GG.ptr);
1393 free(TT.pp.ptr);
1394 free(TT.PP.ptr);
1395 free(TT.ss.ptr);
1396 free(TT.tt.ptr);
1397 free(TT.uu.ptr);
1398 free(TT.UU.ptr);
1399 llist_traverse(TT.fields, free);
1400 }
1401}
1402
1403#define CLEANUP_ps
1404#define FOR_top
1405#include "generated/flags.h"
1406
1407
1408static void setsort(int pos)
1409{
1410 struct ofields *field, *field2;
1411 int i = 0;
1412
1413 if (pos<0) pos = 0;
1414
1415 for (field = TT.fields; field; field = field->next) {
1416 if ((TT.sortpos = i++)<pos && field->next) continue;
1417 field2 = TT.kfields;
1418 field2->which = field->which;
1419 field2->len = field->len;
1420 break;
1421 }
1422}
1423
1424
1425
1426
1427static int merge_deltas(long long *oslot, long long *nslot, int milis)
1428{
1429 char deltas[] = {SLOT_utime2, SLOT_iobytes, SLOT_diobytes, SLOT_rchar,
1430 SLOT_wchar, SLOT_rbytes, SLOT_wbytes, SLOT_swap};
1431 int i;
1432
1433 for (i = 0; i<ARRAY_LEN(deltas); i++)
1434 oslot[deltas[i]] = nslot[deltas[i]] - oslot[deltas[i]];
1435 oslot[SLOT_upticks] = (milis*TT.ticks)/1000;
1436
1437 return 1;
1438}
1439
1440static int header_line(int line, int rev)
1441{
1442 if (!line) return 0;
1443
1444 if (FLAG(b)) puts(toybuf);
1445 else {
1446 printf("%s%-*.*s%s\r\n", rev?"\033[7m":"", rev?TT.width:0, TT.width, toybuf,
1447 rev?"\033[0m":"");
1448 }
1449
1450 return line-1;
1451}
1452
1453static void top_cursor_cleanup(void)
1454{
1455 tty_esc("?25h");
1456}
1457
1458static void top_common(
1459 int (*filter)(long long *oslot, long long *nslot, int milis))
1460{
1461 long long timeout = 0, now, stats[16];
1462 struct proclist {
1463 struct procpid **tb;
1464 int count;
1465 long long whence;
1466 } plist[2], *plold, *plnew, old, new, mix;
1467 char scratch[16], *pos, *cpufields[] = {"user", "nice", "sys", "idle",
1468 "iow", "irq", "sirq", "host"};
1469 unsigned tock = 0;
1470 int i, lines, topoff = 0, done = 0;
1471 char stdout_buf[BUFSIZ];
1472
1473 if (!TT.fields) perror_exit("no -o");
1474
1475
1476 if (!FLAG(b)) {
1477 setbuf(stdout, stdout_buf);
1478 sigatexit(top_cursor_cleanup);
1479 tty_esc("?25l");
1480 }
1481
1482 toys.signal = SIGWINCH;
1483 TT.bits = get_headers(TT.fields, toybuf, sizeof(toybuf));
1484 *scratch = 0;
1485 memset(plist, 0, sizeof(plist));
1486 memset(stats, 0, sizeof(stats));
1487 do {
1488 struct dirtree *dt;
1489 int recalc = 1;
1490
1491 plold = plist+(tock++&1);
1492 plnew = plist+(tock&1);
1493 plnew->whence = millitime();
1494 dt = dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC,
1495 (FLAG(H) || (TT.bits&(_PS_TID|_PS_TCNT))) ? get_threads : get_ps);
1496 if (dt == DIRTREE_ABORTVAL) error_exit("no /proc");
1497 plnew->tb = collate(plnew->count = TT.kcount, dt);
1498 TT.kcount = 0;
1499
1500 if (readfile("/proc/stat", pos = toybuf, sizeof(toybuf))) {
1501 long long *st = stats+8*(tock&1);
1502
1503
1504 sscanf(pos, "cpu %lld %lld %lld %lld %lld %lld %lld %lld",
1505 st, st+1, st+2, st+3, st+4, st+5, st+6, st+7);
1506 }
1507
1508
1509 if (!plold->tb) {
1510 msleep(250);
1511 continue;
1512 }
1513
1514
1515 old = *plold;
1516 new = *plnew;
1517 mix.tb = xmalloc((old.count+new.count)*sizeof(struct procpid));
1518 mix.count = 0;
1519
1520 while (old.count || new.count) {
1521 struct procpid *otb = old.count ? *old.tb : 0,
1522 *ntb = new.count ? *new.tb : 0;
1523
1524
1525 if (old.count && (!new.count || *otb->slot < *ntb->slot)) {
1526 old.tb++;
1527 old.count--;
1528
1529 continue;
1530 }
1531
1532
1533 if (!old.count || *otb->slot > *ntb->slot) mix.tb[mix.count] = ntb;
1534 else {
1535
1536 if (filter(otb->slot, ntb->slot, new.whence-old.whence)) {
1537 mix.tb[mix.count] = otb;
1538 mix.count++;
1539 }
1540 old.tb++;
1541 old.count--;
1542 }
1543 new.tb++;
1544 new.count--;
1545 }
1546
1547
1548 for (;;) {
1549 char was, is;
1550
1551 if (recalc) {
1552 qsort(mix.tb, mix.count, sizeof(struct procpid *), (void *)ksort);
1553 if (!FLAG(b)) {
1554 printf("\033[H\033[J");
1555 if (toys.signal) {
1556 toys.signal = 0;
1557 terminal_probesize(&TT.width, &TT.height);
1558 }
1559 }
1560 if (TT.top.m) TT.height = TT.top.m+5;
1561 lines = TT.height;
1562 }
1563 if (recalc && !FLAG(q)) {
1564
1565 if (*toys.which->name == 't') {
1566 struct ofields field;
1567 char hr[4][32];
1568 long long ll, up = 0;
1569 long run[6];
1570 int j;
1571
1572
1573
1574
1575
1576
1577 field.which = PS_S;
1578 memset(run, 0, sizeof(run));
1579 for (i = 0; i<mix.count; i++)
1580 run[1+stridx("RTtZ", *string_field(mix.tb[i], &field))]++;
1581 sprintf(toybuf,
1582 "%ss: %d total, %3ld running, %3ld sleeping, %3ld stopped, "
1583 "%3ld zombie", FLAG(H)?"Thread":"Task", mix.count, run[1], run[0],
1584 run[2]+run[3], run[4]);
1585 lines = header_line(lines, 0);
1586
1587 if (readfile("/proc/meminfo", toybuf+256, sizeof(toybuf)-256)) {
1588 for (i = 0; i<6; i++) {
1589 j = i%3;
1590 pos = strafter(toybuf+256, (char *[]){"MemTotal:","\nMemFree:",
1591 "\nBuffers:","\nSwapTotal:","\nSwapFree:","\nCached:"}[i]);
1592 human_readable_long(hr[j+!!j], 1024*(run[i] = pos?atol(pos):0),
1593 8, 0);
1594 if (j==1) human_readable_long(hr[1], 1024*(run[i-1]-run[i]), 8,0);
1595 else if (j==2) {
1596 sprintf(toybuf, (i<3)
1597 ? " Mem: %9s total, %9s used, %9s free, %9s buffers"
1598 : " Swap: %9s total, %9s used, %9s free, %9s cached",
1599 hr[0], hr[1], hr[2], hr[3]);
1600 lines = header_line(lines, 0);
1601 }
1602 }
1603 }
1604
1605 pos = toybuf;
1606 i = sysconf(_SC_NPROCESSORS_CONF);
1607 pos += sprintf(pos, "%d%%cpu", i*100);
1608 j = 4+(i>10);
1609
1610
1611
1612 if (mix.count) up = mix.tb[0]->slot[SLOT_upticks];
1613 if (!up) up = 1;
1614 now = up*i;
1615 ll = stats[3] = stats[11] = 0;
1616 for (i = 0; i<8; i++) ll += stats[i]-stats[i+8];
1617 stats[3] = now - llabs(ll);
1618
1619 for (i = 0; i<8; i++) {
1620 ll = (llabs(stats[i]-stats[i+8])*1000)/up;
1621 pos += sprintf(pos, "% *lld%%%s", j, (ll+5)/10, cpufields[i]);
1622 }
1623 lines = header_line(lines, 0);
1624 } else {
1625 struct ofields *field;
1626 struct procpid tb;
1627
1628 memset(&tb, 0, sizeof(struct procpid));
1629 pos = stpcpy(toybuf, "Totals:");
1630 for (field = TT.fields; field; field = field->next) {
1631 long long ll, bits = 0;
1632 int slot = typos[field->which].slot&(XX-1);
1633
1634 if (field->which<PS_C || field->which>PS_DIO) continue;
1635 ll = 1LL<<field->which;
1636 if (bits&ll) continue;
1637 bits |= ll;
1638 for (i=0; i<mix.count; i++)
1639 tb.slot[slot] += mix.tb[i]->slot[slot];
1640 pos += snprintf(pos, sizeof(toybuf)/2-(pos-toybuf),
1641 " %s: %*s,", typos[field->which].name,
1642 field->len, string_field(&tb, field));
1643 }
1644 *--pos = 0;
1645 lines = header_line(lines, 0);
1646 }
1647
1648 get_headers(TT.fields, pos = toybuf, sizeof(toybuf));
1649 for (i = 0, is = ' '; *pos; pos++) {
1650 was = is;
1651 is = *pos;
1652 if (isspace(was) && !isspace(is) && i++==TT.sortpos && pos!=toybuf)
1653 pos[-1] = '[';
1654 if (!isspace(was) && isspace(is) && i==TT.sortpos+1) *pos = ']';
1655 }
1656 if (FLAG(b)) while (isspace(*(pos-1))) --pos;
1657 *pos = 0;
1658 lines = header_line(lines, 1);
1659 }
1660 if (!recalc && !FLAG(b))
1661 printf("\033[%dH\033[J", 1+TT.height-lines);
1662 recalc = 1;
1663
1664 for (i = 0; i<lines && i+topoff<mix.count; i++) {
1665
1666 int bold = !FLAG(b) && mix.tb[i+topoff]->state == 'R';
1667
1668 if (!FLAG(b) && i) putchar('\n');
1669 if (bold) printf("\033[1m");
1670 show_ps(mix.tb[i+topoff]);
1671 if (bold) printf("\033[m");
1672 }
1673
1674 if (TT.top.n && !--TT.top.n) {
1675 done++;
1676 break;
1677 }
1678
1679 now = millitime();
1680 if (timeout<=now) timeout = new.whence+TT.top.d;
1681 if (timeout<=now || timeout>now+TT.top.d) timeout = now+TT.top.d;
1682
1683
1684 if (FLAG(b)) {
1685 msleep(timeout-now);
1686
1687 xputs("\n\n");
1688 break;
1689 } else fflush(stdout);
1690
1691 i = scan_key_getsize(scratch, timeout-now, &TT.width, &TT.height);
1692 if (i==-1 || i==3 || toupper(i)=='Q') {
1693 done++;
1694 break;
1695 }
1696 if (i==-2) break;
1697 if (i==-3) continue;
1698
1699
1700 if (i==27) while (0<scan_key_getsize(scratch, 0, &TT.width, &TT.height));
1701 else if (i=='\r' || i==' ') {
1702 timeout = 0;
1703 break;
1704 } else if (toupper(i)=='R')
1705 ((struct ofields *)TT.kfields)->reverse *= -1;
1706 else {
1707 i -= 256;
1708 if (i == KEY_LEFT) setsort(TT.sortpos-1);
1709 else if (i == KEY_RIGHT) setsort(TT.sortpos+1);
1710
1711 else if (strchr((char []){KEY_DOWN,KEY_PGUP,KEY_PGDN,KEY_UP}, i)) {
1712 recalc = 0;
1713
1714 if (i == KEY_UP) topoff--;
1715 else if (i == KEY_DOWN) topoff++;
1716 else if (i == KEY_PGDN) topoff += lines;
1717 else if (i == KEY_PGUP) topoff -= lines;
1718 if (topoff<0) topoff = 0;
1719 if (topoff>mix.count) topoff = mix.count;
1720 }
1721 }
1722 continue;
1723 }
1724
1725 free(mix.tb);
1726 for (i=0; i<plold->count; i++) free(plold->tb[i]);
1727 free(plold->tb);
1728 } while (!done);
1729
1730 if (!FLAG(b)) tty_reset();
1731}
1732
1733static void top_setup(char *defo, char *defk)
1734{
1735 TT.ticks = sysconf(_SC_CLK_TCK);
1736 TT.tty = tty_fd() != -1;
1737
1738
1739 if (FLAG(b)) TT.width = TT.height = 99999;
1740 else {
1741
1742
1743 TT.time = millitime();
1744 start_redraw(&TT.width, &TT.height);
1745 }
1746
1747 comma_args(TT.top.u, &TT.uu, "bad -u", parse_rest);
1748 comma_args(TT.top.p, &TT.pp, "bad -p", parse_rest);
1749 TT.match_process = shared_match_process;
1750
1751 default_ko(defo, &TT.fields, "bad -o", TT.top.o);
1752 dlist_terminate(TT.fields);
1753
1754
1755 default_ko("-S", &TT.kfields, 0, 0);
1756 default_ko(defk, &TT.kfields, "bad -k", TT.top.k);
1757 dlist_terminate(TT.kfields);
1758 setsort(TT.top.s-1);
1759}
1760
1761void top_main(void)
1762{
1763 sprintf(toybuf, "%cID,USER,%s%%CPU,%%MEM,TIME+,%s", FLAG(H) ? 'T' : 'P',
1764 TT.top.O ? "" : "PR,NI,VIRT,RES,SHR,S,",
1765 FLAG(H) ? "CMD:15=THREAD,NAME=PROCESS" : "ARGS");
1766 if (!TT.top.s) TT.top.s = TT.top.O ? 3 : 9;
1767 top_setup(toybuf, "-%CPU,-ETIME,-PID");
1768 if (TT.top.O) {
1769 struct ofields *field = TT.fields;
1770
1771 field = field->next->next;
1772 comma_args(TT.top.O, &field, "bad -O", parse_ko);
1773 }
1774
1775 top_common(merge_deltas);
1776}
1777
1778#define CLEANUP_top
1779#define FOR_iotop
1780#include "generated/flags.h"
1781
1782
1783static int iotop_filter(long long *oslot, long long *nslot, int milis)
1784{
1785
1786 if (!FLAG(a)) merge_deltas(oslot, nslot, milis);
1787 else oslot[SLOT_upticks] = ((millitime()-TT.time)*TT.ticks)/1000;
1788
1789 return !FLAG(O)||oslot[SLOT_iobytes+!FLAG(A)];
1790}
1791
1792void iotop_main(void)
1793{
1794 char *s1 = 0, *s2 = 0, *d = "D"+!!FLAG(A);
1795
1796 if (FLAG(K)) TT.forcek++;
1797
1798 top_setup(s1 = xmprintf("PID,PR,USER,%sREAD,%sWRITE,SWAP,%sIO,COMM",d,d,d),
1799 s2 = xmprintf("-%sIO,-ETIME,-PID",d));
1800 free(s1);
1801 free(s2);
1802 top_common(iotop_filter);
1803}
1804
1805
1806
1807
1808#define FORCE_FLAGS
1809#define CLEANUP_iotop
1810#define FOR_pgrep
1811#include "generated/flags.h"
1812
1813struct regex_list {
1814 struct regex_list *next;
1815 regex_t reg;
1816};
1817
1818static void do_pgk(struct procpid *tb)
1819{
1820 if (TT.pgrep.signal) {
1821 if (kill(*tb->slot, TT.pgrep.signal)) {
1822 char *s = num_to_sig(TT.pgrep.signal);
1823
1824 if (!s) sprintf(s = toybuf, "%d", TT.pgrep.signal);
1825 perror_msg("%s->%lld", s, *tb->slot);
1826 }
1827 }
1828 if (!FLAG(c) && (!TT.pgrep.signal || TT.tty)) {
1829 printf("%lld", *tb->slot);
1830 if (FLAG(l))
1831 printf(" %s", tb->str+tb->offset[4]*!!FLAG(f));
1832
1833 printf("%s", TT.pgrep.d ? TT.pgrep.d : "\n");
1834 }
1835}
1836
1837static void match_pgrep(void *p)
1838{
1839 struct procpid *tb = p;
1840 regmatch_t match;
1841 struct regex_list *reg;
1842 char *name = tb->str+tb->offset[4]*!!FLAG(f);
1843
1844
1845 if (TT.pgrep.self == *tb->slot) return;
1846
1847 if (TT.pgrep.regexes) {
1848 for (reg = TT.pgrep.regexes; reg; reg = reg->next) {
1849 if (regexec(®->reg, name, 1, &match, 0)) continue;
1850 if (FLAG(x))
1851 if (match.rm_so || match.rm_eo!=strlen(name)) continue;
1852 break;
1853 }
1854 if (!FLAG(v) == !reg) return;
1855 }
1856
1857
1858 toys.exitval = 0;
1859
1860
1861 TT.sortpos++;
1862 if (FLAG(n)||FLAG(o)) {
1863 long long ll = tb->slot[SLOT_starttime];
1864
1865 if (FLAG(o)) ll *= -1;
1866 if (TT.time && TT.time>ll) return;
1867 TT.time = ll;
1868 free(TT.pgrep.snapshot);
1869 TT.pgrep.snapshot = xmemdup(toybuf, (name+strlen(name)+1)-toybuf);
1870 } else do_pgk(tb);
1871}
1872
1873static int pgrep_match_process(long long *slot)
1874{
1875 return !FLAG(v) == !!shared_match_process(slot);
1876}
1877
1878void pgrep_main(void)
1879{
1880 char **arg;
1881 struct regex_list *reg;
1882
1883 TT.pgrep.self = getpid();
1884
1885
1886 if (TT.pgrep.L && 1>(TT.pgrep.signal = sig_to_num(TT.pgrep.L)))
1887 error_exit("bad -L '%s'", TT.pgrep.L);
1888
1889 comma_args(TT.pgrep.G, &TT.GG, "bad -G", parse_rest);
1890 comma_args(TT.pgrep.g, &TT.gg, "bad -g", parse_rest);
1891 comma_args(TT.pgrep.P, &TT.PP, "bad -P", parse_rest);
1892 comma_args(TT.pgrep.s, &TT.ss, "bad -s", parse_rest);
1893 comma_args(TT.pgrep.t, &TT.tt, "bad -t", parse_rest);
1894 comma_args(TT.pgrep.U, &TT.UU, "bad -U", parse_rest);
1895 comma_args(TT.pgrep.u, &TT.uu, "bad -u", parse_rest);
1896
1897 if ((toys.optflags&(FLAG_x|FLAG_f)) ||
1898 !(toys.optflags&(FLAG_G|FLAG_g|FLAG_P|FLAG_s|FLAG_t|FLAG_U|FLAG_u)))
1899 if (!toys.optc) help_exit("No PATTERN");
1900
1901 if (FLAG(f)) TT.bits |= _PS_CMDLINE;
1902 for (arg = toys.optargs; *arg; arg++) {
1903 reg = xmalloc(sizeof(struct regex_list));
1904 xregcomp(®->reg, *arg, REG_EXTENDED);
1905 reg->next = TT.pgrep.regexes;
1906 TT.pgrep.regexes = reg;
1907 }
1908 TT.match_process = pgrep_match_process;
1909 TT.show_process = match_pgrep;
1910
1911
1912 toys.exitval = 1;
1913
1914 dirtree_flagread("/proc", DIRTREE_SHUTUP|DIRTREE_PROC, get_ps);
1915 if (FLAG(c)) printf("%d\n", TT.sortpos);
1916 if (TT.pgrep.snapshot) {
1917 do_pgk(TT.pgrep.snapshot);
1918 if (CFG_TOYBOX_FREE) free(TT.pgrep.snapshot);
1919 }
1920 if (TT.pgrep.d) xputc('\n');
1921}
1922
1923#define CLEANUP_pgrep
1924#define FOR_pkill
1925#include "generated/flags.h"
1926
1927void pkill_main(void)
1928{
1929 char **args = toys.optargs;
1930
1931 if (!FLAG(l) && *args && **args=='-') TT.pgrep.L = *(args++)+1;
1932 if (!TT.pgrep.L) TT.pgrep.signal = SIGTERM;
1933 if (FLAG(V)) TT.tty = 1;
1934 pgrep_main();
1935}
1936