blob: 972c484f42ffbf7abcd7a8974b3ea03dde6fe082 [file] [log] [blame]
Eric Andersen420b2082002-09-17 22:14:58 +00001/*
2 * A tiny 'top' utility.
3 *
Eric Andersen08a72202002-09-30 20:52:10 +00004 * This is written specifically for the linux /proc/<PID>/stat(m)
5 * files format.
6
7 * This reads the PIDs of all processes and their status and shows
8 * the status of processes (first ones that fit to screen) at given
9 * intervals.
Eric Andersenc7bda1c2004-03-15 08:29:22 +000010 *
Eric Andersen420b2082002-09-17 22:14:58 +000011 * NOTES:
12 * - At startup this changes to /proc, all the reads are then
13 * relative to that.
Eric Andersenc7bda1c2004-03-15 08:29:22 +000014 *
Eric Andersen420b2082002-09-17 22:14:58 +000015 * (C) Eero Tamminen <oak at welho dot com>
Eric Andersen08a72202002-09-30 20:52:10 +000016 *
Eric Andersenaff114c2004-04-14 17:51:38 +000017 * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>
Eric Andersen420b2082002-09-17 22:14:58 +000018 */
Eric Andersen08a72202002-09-30 20:52:10 +000019
20/* Original code Copyrights */
21/*
22 * Copyright (c) 1992 Branko Lankester
23 * Copyright (c) 1992 Roger Binns
24 * Copyright (C) 1994-1996 Charles L. Blake.
25 * Copyright (C) 1992-1998 Michael K. Johnson
Glenn L McGrath225be8c2004-09-14 18:56:52 +000026 * May
27 * This reads the PIDs of all processes and their status and shows
28 * the status of processes (first ones that fit to screen) at given
29 * intervals.
30 *
31 * NOTES:
32 * - At startup this changes to /proc, all the reads are then
33 * relative to that.
34 *
35 * (C) Eero Tamminen <oak at welho dot com>
36 *
37 * Rewritten by Vladimir Oleynik (C) 2002 <dzo@simtreas.ru>
38 */
39
40/* Original code Copyrights */
41/*
42 * Copyright (c) 1992 Branko Lankester
43 * Copyright (c) 1992 Roger Binns
44 * Copyright (C) 1994-1996 Charles L. Blake.
45 * Copyright (C) 1992-1998 Michael K. Johnson
Eric Andersen08a72202002-09-30 20:52:10 +000046 * May be distributed under the conditions of the
47 * GNU Library General Public License
48 */
49
50#include <sys/types.h>
Eric Andersen420b2082002-09-17 22:14:58 +000051#include <stdio.h>
52#include <stdlib.h>
53#include <unistd.h>
Eric Andersen420b2082002-09-17 22:14:58 +000054#include <string.h>
55#include <sys/ioctl.h>
Eric Andersen08a72202002-09-30 20:52:10 +000056/* get page info */
57#include <asm/page.h>
Eric Andersen420b2082002-09-17 22:14:58 +000058#include "busybox.h"
59
Eric Andersen08a72202002-09-30 20:52:10 +000060//#define FEATURE_CPU_USAGE_PERCENTAGE /* + 2k */
Eric Andersen420b2082002-09-17 22:14:58 +000061
Eric Andersen08a72202002-09-30 20:52:10 +000062#ifdef FEATURE_CPU_USAGE_PERCENTAGE
63#include <time.h>
64#include <sys/time.h>
65#include <fcntl.h>
66#include <netinet/in.h> /* htons */
67#endif
68
69
Eric Andersen44608e92002-10-22 12:21:15 +000070typedef int (*cmp_t)(procps_status_t *P, procps_status_t *Q);
Eric Andersen08a72202002-09-30 20:52:10 +000071
Eric Andersen44608e92002-10-22 12:21:15 +000072static procps_status_t *top; /* Hehe */
Eric Andersen08a72202002-09-30 20:52:10 +000073static int ntop;
74
75
Eric Andersen44608e92002-10-22 12:21:15 +000076static int pid_sort (procps_status_t *P, procps_status_t *Q)
Eric Andersen08a72202002-09-30 20:52:10 +000077{
Glenn L McGrath225be8c2004-09-14 18:56:52 +000078 return (Q->pid - P->pid);
Eric Andersen08a72202002-09-30 20:52:10 +000079}
80
Eric Andersen44608e92002-10-22 12:21:15 +000081static int mem_sort (procps_status_t *P, procps_status_t *Q)
Eric Andersen08a72202002-09-30 20:52:10 +000082{
Glenn L McGrath225be8c2004-09-14 18:56:52 +000083 return (int)(Q->rss - P->rss);
Eric Andersen08a72202002-09-30 20:52:10 +000084}
85
86#ifdef FEATURE_CPU_USAGE_PERCENTAGE
87
88#define sort_depth 3
89static cmp_t sort_function[sort_depth];
90
Eric Andersen44608e92002-10-22 12:21:15 +000091static int pcpu_sort (procps_status_t *P, procps_status_t *Q)
Eric Andersen08a72202002-09-30 20:52:10 +000092{
Glenn L McGrath225be8c2004-09-14 18:56:52 +000093 return (Q->pcpu - P->pcpu);
Eric Andersen08a72202002-09-30 20:52:10 +000094}
95
Eric Andersen44608e92002-10-22 12:21:15 +000096static int time_sort (procps_status_t *P, procps_status_t *Q)
Eric Andersen08a72202002-09-30 20:52:10 +000097{
Glenn L McGrath225be8c2004-09-14 18:56:52 +000098 return (int)((Q->stime + Q->utime) - (P->stime + P->utime));
Eric Andersen08a72202002-09-30 20:52:10 +000099}
100
101int mult_lvl_cmp(void* a, void* b) {
102 int i, cmp_val;
103
104 for(i = 0; i < sort_depth; i++) {
105 cmp_val = (*sort_function[i])(a, b);
106 if (cmp_val != 0)
107 return cmp_val;
108 }
109 return 0;
110}
111
112/* This structure stores some critical information from one frame to
113 the next. mostly used for sorting. Added cumulative and resident fields. */
114struct save_hist {
115 int ticks;
116 int pid;
117 int utime;
118 int stime;
119};
120
121/*
122 * Calculates percent cpu usage for each task.
123 */
124
125static struct save_hist *save_history;
126
127static unsigned long Hertz;
128
129/***********************************************************************
130 * Some values in /proc are expressed in units of 1/HZ seconds, where HZ
131 * is the kernel clock tick rate. One of these units is called a jiffy.
132 * The HZ value used in the kernel may vary according to hacker desire.
133 * According to Linus Torvalds, this is not true. He considers the values
Eric Andersenaff114c2004-04-14 17:51:38 +0000134 * in /proc as being in architecture-dependent units that have no relation
Eric Andersen08a72202002-09-30 20:52:10 +0000135 * to the kernel clock tick rate. Examination of the kernel source code
136 * reveals that opinion as wishful thinking.
137 *
138 * In any case, we need the HZ constant as used in /proc. (the real HZ value
139 * may differ, but we don't care) There are several ways we could get HZ:
140 *
141 * 1. Include the kernel header file. If it changes, recompile this library.
142 * 2. Use the sysconf() function. When HZ changes, recompile the C library!
143 * 3. Ask the kernel. This is obviously correct...
144 *
145 * Linus Torvalds won't let us ask the kernel, because he thinks we should
146 * not know the HZ value. Oh well, we don't have to listen to him.
147 * Someone smuggled out the HZ value. :-)
148 *
149 * This code should work fine, even if Linus fixes the kernel to match his
150 * stated behavior. The code only fails in case of a partial conversion.
151 *
152 */
153
154#define FILE_TO_BUF(filename, fd) do{ \
155 if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) { \
Manuel Novoa III cad53642003-03-19 09:13:01 +0000156 bb_perror_msg_and_die("/proc not be mounted?"); \
Eric Andersen08a72202002-09-30 20:52:10 +0000157 } \
158 lseek(fd, 0L, SEEK_SET); \
159 if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \
Manuel Novoa III cad53642003-03-19 09:13:01 +0000160 bb_perror_msg_and_die("%s", filename); \
Eric Andersen08a72202002-09-30 20:52:10 +0000161 } \
162 buf[local_n] = '\0'; \
163}while(0)
164
165#define FILE_TO_BUF2(filename, fd) do{ \
166 lseek(fd, 0L, SEEK_SET); \
167 if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) { \
Manuel Novoa III cad53642003-03-19 09:13:01 +0000168 bb_perror_msg_and_die("%s", filename); \
Eric Andersen08a72202002-09-30 20:52:10 +0000169 } \
170 buf[local_n] = '\0'; \
171}while(0)
172
173static void init_Hertz_value(void) {
174 unsigned long user_j, nice_j, sys_j, other_j; /* jiffies (clock ticks) */
175 double up_1, up_2, seconds;
176 unsigned long jiffies, h;
177 char buf[80];
178 int uptime_fd = -1;
179 int stat_fd = -1;
180
181 long smp_num_cpus = sysconf(_SC_NPROCESSORS_CONF);
182
183 if(smp_num_cpus<1) smp_num_cpus=1;
184 do {
185 int local_n;
186
187 FILE_TO_BUF("uptime", uptime_fd);
188 up_1 = strtod(buf, 0);
189 FILE_TO_BUF("stat", stat_fd);
190 sscanf(buf, "cpu %lu %lu %lu %lu", &user_j, &nice_j, &sys_j, &other_j);
191 FILE_TO_BUF2("uptime", uptime_fd);
192 up_2 = strtod(buf, 0);
193 } while((long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */
194
195 close(uptime_fd);
196 close(stat_fd);
197
198 jiffies = user_j + nice_j + sys_j + other_j;
199 seconds = (up_1 + up_2) / 2;
200 h = (unsigned long)( (double)jiffies/seconds/smp_num_cpus );
201 /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */
202 switch(h){
203 case 30 ... 34 : Hertz = 32; break; /* ia64 emulator */
204 case 48 ... 52 : Hertz = 50; break;
205 case 58 ... 62 : Hertz = 60; break;
206 case 63 ... 65 : Hertz = 64; break; /* StrongARM /Shark */
207 case 95 ... 105 : Hertz = 100; break; /* normal Linux */
208 case 124 ... 132 : Hertz = 128; break; /* MIPS, ARM */
209 case 195 ... 204 : Hertz = 200; break; /* normal << 1 */
210 case 253 ... 260 : Hertz = 256; break;
211 case 295 ... 304 : Hertz = 300; break; /* 3 cpus */
212 case 393 ... 408 : Hertz = 400; break; /* normal << 2 */
213 case 495 ... 504 : Hertz = 500; break; /* 5 cpus */
214 case 595 ... 604 : Hertz = 600; break; /* 6 cpus */
215 case 695 ... 704 : Hertz = 700; break; /* 7 cpus */
216 case 790 ... 808 : Hertz = 800; break; /* normal << 3 */
217 case 895 ... 904 : Hertz = 900; break; /* 9 cpus */
218 case 990 ... 1010 : Hertz = 1000; break; /* ARM */
219 case 1015 ... 1035 : Hertz = 1024; break; /* Alpha, ia64 */
220 case 1095 ... 1104 : Hertz = 1100; break; /* 11 cpus */
221 case 1180 ... 1220 : Hertz = 1200; break; /* Alpha */
222 default:
223 /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
224 Hertz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
225 }
226}
227
228static void do_stats(void)
229{
230 struct timeval t;
231 static struct timeval oldtime;
232 struct timezone timez;
233 float elapsed_time;
234
Eric Andersen44608e92002-10-22 12:21:15 +0000235 procps_status_t *cur;
Eric Andersen08a72202002-09-30 20:52:10 +0000236 int total_time, i, n;
237 static int prev_count;
238 int systime, usrtime, pid;
239
240 struct save_hist *New_save_hist;
241
242 /*
243 * Finds the current time (in microseconds) and calculates the time
244 * elapsed since the last update.
245 */
246 gettimeofday(&t, &timez);
247 elapsed_time = (t.tv_sec - oldtime.tv_sec)
248 + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0;
249 oldtime.tv_sec = t.tv_sec;
250 oldtime.tv_usec = t.tv_usec;
251
252 New_save_hist = alloca(sizeof(struct save_hist)*ntop);
253 /*
254 * Make a pass through the data to get stats.
255 */
256 for(n = 0; n < ntop; n++) {
257 cur = top + n;
258
259 /*
260 * Calculate time in cur process. Time is sum of user time
261 * (usrtime) plus system time (systime).
262 */
263 systime = cur->stime;
264 usrtime = cur->utime;
265 pid = cur->pid;
266 total_time = systime + usrtime;
267 New_save_hist[n].ticks = total_time;
268 New_save_hist[n].pid = pid;
269 New_save_hist[n].stime = systime;
270 New_save_hist[n].utime = usrtime;
271
272 /* find matching entry from previous pass */
273 for (i = 0; i < prev_count; i++) {
274 if (save_history[i].pid == pid) {
275 total_time -= save_history[i].ticks;
276 systime -= save_history[i].stime;
277 usrtime -= save_history[i].utime;
278 break;
279 }
280 }
281
282 /*
283 * Calculate percent cpu time for cur task.
284 */
285 i = (total_time * 10 * 100/Hertz) / elapsed_time;
286 if (i > 999)
287 i = 999;
288 cur->pcpu = i;
289
290 }
291
292 /*
293 * Save cur frame's information.
294 */
295 free(save_history);
296 save_history = memcpy(xmalloc(sizeof(struct save_hist)*n), New_save_hist,
297 sizeof(struct save_hist)*n);
298 prev_count = n;
Eric Andersen44608e92002-10-22 12:21:15 +0000299 qsort(top, n, sizeof(procps_status_t), (void*)mult_lvl_cmp);
Eric Andersen08a72202002-09-30 20:52:10 +0000300}
301#else
302static cmp_t sort_function;
303#endif /* FEATURE_CPU_USAGE_PERCENTAGE */
304
Eric Andersen420b2082002-09-17 22:14:58 +0000305/* display generic info (meminfo / loadavg) */
Eric Andersen08a72202002-09-30 20:52:10 +0000306static unsigned long display_generic(void)
Eric Andersen420b2082002-09-17 22:14:58 +0000307{
308 FILE *fp;
309 char buf[80];
310 float avg1, avg2, avg3;
311 unsigned long total, used, mfree, shared, buffers, cached;
Eric Andersen7857c032003-10-11 18:47:20 +0000312 unsigned int needs_conversion = 1;
Eric Andersen420b2082002-09-17 22:14:58 +0000313
314 /* read memory info */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000315 fp = bb_xfopen("meminfo", "r");
Eric Andersen420b2082002-09-17 22:14:58 +0000316
Eric Andersen7857c032003-10-11 18:47:20 +0000317 /*
318 * Old kernels (such as 2.4.x) had a nice summary of memory info that
319 * we could parse, however this is gone entirely in 2.6. Try parsing
320 * the old way first, and if that fails, parse each field manually.
321 *
322 * First, we read in the first line. Old kernels will have bogus
323 * strings we don't care about, whereas new kernels will start right
324 * out with MemTotal:
325 * -- PFM.
326 */
327 if (fscanf(fp, "MemTotal: %lu %s\n", &total, buf) != 2) {
328 fgets(buf, sizeof(buf), fp); /* skip first line */
329
330 fscanf(fp, "Mem: %lu %lu %lu %lu %lu %lu",
331 &total, &used, &mfree, &shared, &buffers, &cached);
332 } else {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000333 /*
Eric Andersen7857c032003-10-11 18:47:20 +0000334 * Revert to manual parsing, which incidentally already has the
335 * sizes in kilobytes. This should be safe for both 2.4 and
336 * 2.6.
337 */
338 needs_conversion = 0;
339
340 fscanf(fp, "MemFree: %lu %s\n", &mfree, buf);
341
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000342 /*
Eric Andersen7857c032003-10-11 18:47:20 +0000343 * MemShared: is no longer present in 2.6. Report this as 0,
344 * to maintain consistent behavior with normal procps.
345 */
346 if (fscanf(fp, "MemShared: %lu %s\n", &shared, buf) != 2)
347 shared = 0;
348
349 fscanf(fp, "Buffers: %lu %s\n", &buffers, buf);
350 fscanf(fp, "Cached: %lu %s\n", &cached, buf);
351
352 used = total - mfree;
Eric Andersen420b2082002-09-17 22:14:58 +0000353 }
354 fclose(fp);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000355
Eric Andersen420b2082002-09-17 22:14:58 +0000356 /* read load average */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000357 fp = bb_xfopen("loadavg", "r");
Eric Andersen420b2082002-09-17 22:14:58 +0000358 if (fscanf(fp, "%f %f %f", &avg1, &avg2, &avg3) != 3) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000359 bb_error_msg_and_die("failed to read '%s'", "loadavg");
Eric Andersen420b2082002-09-17 22:14:58 +0000360 }
361 fclose(fp);
362
Eric Andersen7857c032003-10-11 18:47:20 +0000363 if (needs_conversion) {
364 /* convert to kilobytes */
365 used /= 1024;
366 mfree /= 1024;
367 shared /= 1024;
368 buffers /= 1024;
369 cached /= 1024;
370 total /= 1024;
371 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000372
Eric Andersen420b2082002-09-17 22:14:58 +0000373 /* output memory info and load average */
Eric Andersen08a72202002-09-30 20:52:10 +0000374 /* clear screen & go to top */
375 printf("\e[H\e[J" "Mem: "
376 "%ldK used, %ldK free, %ldK shrd, %ldK buff, %ldK cached\n",
377 used, mfree, shared, buffers, cached);
378 printf("Load average: %.2f, %.2f, %.2f "
379 "(State: S=sleeping R=running, W=waiting)\n",
Eric Andersen420b2082002-09-17 22:14:58 +0000380 avg1, avg2, avg3);
Eric Andersen7857c032003-10-11 18:47:20 +0000381 return total;
Eric Andersen420b2082002-09-17 22:14:58 +0000382}
383
384
385/* display process statuses */
Eric Andersen08a72202002-09-30 20:52:10 +0000386static void display_status(int count, int col)
Eric Andersen420b2082002-09-17 22:14:58 +0000387{
Eric Andersen44608e92002-10-22 12:21:15 +0000388 procps_status_t *s = top;
Eric Andersen08a72202002-09-30 20:52:10 +0000389 char rss_str_buf[8];
390 unsigned long total_memory = display_generic();
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000391
Eric Andersen08a72202002-09-30 20:52:10 +0000392#ifdef FEATURE_CPU_USAGE_PERCENTAGE
Eric Andersen420b2082002-09-17 22:14:58 +0000393 /* what info of the processes is shown */
Eric Andersen08a72202002-09-30 20:52:10 +0000394 printf("\n\e[7m PID USER STATUS RSS PPID %%CPU %%MEM COMMAND\e[0m\n");
395#else
396 printf("\n\e[7m PID USER STATUS RSS PPID %%MEM COMMAND\e[0m\n");
397#endif
Eric Andersen420b2082002-09-17 22:14:58 +0000398
399 while (count--) {
Eric Andersen44608e92002-10-22 12:21:15 +0000400 char *namecmd = s->short_cmd;
Eric Andersen08a72202002-09-30 20:52:10 +0000401 int pmem;
Eric Andersen420b2082002-09-17 22:14:58 +0000402
Eric Andersen08a72202002-09-30 20:52:10 +0000403 pmem = 1000.0 * s->rss / total_memory;
404 if (pmem > 999) pmem = 999;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000405
Eric Andersen08a72202002-09-30 20:52:10 +0000406 if(s->rss > 10*1024)
407 sprintf(rss_str_buf, "%6ldM", s->rss/1024);
Manuel Novoa III d4993302002-09-18 19:27:10 +0000408 else
Eric Andersen08a72202002-09-30 20:52:10 +0000409 sprintf(rss_str_buf, "%7ld", s->rss);
410#ifdef FEATURE_CPU_USAGE_PERCENTAGE
Glenn L McGrathf6bba3d2003-12-08 20:31:25 +0000411 printf("%5d %-8s %s %s %5d %2d.%d %2u.%u ",
Eric Andersen08a72202002-09-30 20:52:10 +0000412 s->pid, s->user, s->state, rss_str_buf, s->ppid,
Glenn L McGrathf6bba3d2003-12-08 20:31:25 +0000413 s->pcpu/10, s->pcpu%10, pmem/10, pmem%10);
414#else
415 printf("%5d %-8s %s %s %5d %2u.%u ",
416 s->pid, s->user, s->state, rss_str_buf, s->ppid,
Eric Andersen08a72202002-09-30 20:52:10 +0000417 pmem/10, pmem%10);
Glenn L McGrathf6bba3d2003-12-08 20:31:25 +0000418#endif
Eric Andersen08a72202002-09-30 20:52:10 +0000419 if(strlen(namecmd) > col)
420 namecmd[col] = 0;
421 printf("%s\n", namecmd);
Eric Andersen420b2082002-09-17 22:14:58 +0000422 s++;
Eric Andersen08a72202002-09-30 20:52:10 +0000423 }
Eric Andersen44608e92002-10-22 12:21:15 +0000424}
425
426static void clearmems(void)
427{
Eric Andersen08a72202002-09-30 20:52:10 +0000428 free(top);
Eric Andersen08a72202002-09-30 20:52:10 +0000429 top = 0;
Eric Andersen08a72202002-09-30 20:52:10 +0000430 ntop = 0;
431}
432
433#if defined CONFIG_FEATURE_USE_TERMIOS
434#include <termios.h>
435#include <sys/time.h>
436#include <signal.h>
437
438
439static struct termios initial_settings;
440
441static void reset_term(void)
442{
443 tcsetattr(0, TCSANOW, (void *) &initial_settings);
444#ifdef CONFIG_FEATURE_CLEAN_UP
445 clearmems();
446#ifdef FEATURE_CPU_USAGE_PERCENTAGE
447 free(save_history);
448#endif
449#endif /* CONFIG_FEATURE_CLEAN_UP */
450}
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000451
Eric Andersen08a72202002-09-30 20:52:10 +0000452static void sig_catcher (int sig)
453{
454 reset_term();
455}
456#endif /* CONFIG_FEATURE_USE_TERMIOS */
457
458
Eric Andersen420b2082002-09-17 22:14:58 +0000459int top_main(int argc, char **argv)
460{
Eric Andersen08a72202002-09-30 20:52:10 +0000461 int opt, interval, lines, col;
462#if defined CONFIG_FEATURE_USE_TERMIOS
463 struct termios new_settings;
464 struct timeval tv;
465 fd_set readfds;
466 unsigned char c;
467 struct sigaction sa;
Eric Andersen08a72202002-09-30 20:52:10 +0000468#endif /* CONFIG_FEATURE_USE_TERMIOS */
469
Eric Andersen420b2082002-09-17 22:14:58 +0000470 /* Default update rate is 5 seconds */
471 interval = 5;
Eric Andersen420b2082002-09-17 22:14:58 +0000472
473 /* do normal option parsing */
474 while ((opt = getopt(argc, argv, "d:")) > 0) {
475 switch (opt) {
476 case 'd':
477 interval = atoi(optarg);
478 break;
479 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000480 bb_show_usage();
Eric Andersen420b2082002-09-17 22:14:58 +0000481 }
482 }
483
Eric Andersen08a72202002-09-30 20:52:10 +0000484 /* Default to 25 lines - 5 lines for status */
485 lines = 25 - 5;
486 /* Default CMD format size */
487#ifdef FEATURE_CPU_USAGE_PERCENTAGE
488 col = 35 - 6;
489#else
490 col = 35;
Eric Andersen420b2082002-09-17 22:14:58 +0000491#endif
Eric Andersen44608e92002-10-22 12:21:15 +0000492 /* change to /proc */
Eric Andersen420b2082002-09-17 22:14:58 +0000493 if (chdir("/proc") < 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000494 bb_perror_msg_and_die("chdir('/proc')");
Eric Andersen420b2082002-09-17 22:14:58 +0000495 }
Eric Andersen08a72202002-09-30 20:52:10 +0000496#if defined CONFIG_FEATURE_USE_TERMIOS
497 tcgetattr(0, (void *) &initial_settings);
498 memcpy(&new_settings, &initial_settings, sizeof(struct termios));
499 new_settings.c_lflag &= ~(ISIG | ICANON); /* unbuffered input */
500 /* Turn off echoing */
501 new_settings.c_lflag &= ~(ECHO | ECHONL);
502
503 signal (SIGTERM, sig_catcher);
504 sigaction (SIGTERM, (struct sigaction *) 0, &sa);
505 sa.sa_flags |= SA_RESTART;
506 sa.sa_flags &= ~SA_INTERRUPT;
507 sigaction (SIGTERM, &sa, (struct sigaction *) 0);
508 sigaction (SIGINT, &sa, (struct sigaction *) 0);
509 tcsetattr(0, TCSANOW, (void *) &new_settings);
510 atexit(reset_term);
Eric Andersen8efe9672003-09-15 08:33:45 +0000511
512 get_terminal_width_height(0, &col, &lines);
513 if (lines > 4) {
514 lines -= 5;
Eric Andersen08a72202002-09-30 20:52:10 +0000515#ifdef FEATURE_CPU_USAGE_PERCENTAGE
Eric Andersen8efe9672003-09-15 08:33:45 +0000516 col = col - 80 + 35 - 6;
Eric Andersen08a72202002-09-30 20:52:10 +0000517#else
Eric Andersen8efe9672003-09-15 08:33:45 +0000518 col = col - 80 + 35;
Eric Andersen08a72202002-09-30 20:52:10 +0000519#endif
520 }
Eric Andersen08a72202002-09-30 20:52:10 +0000521#endif /* CONFIG_FEATURE_USE_TERMIOS */
522#ifdef FEATURE_CPU_USAGE_PERCENTAGE
523 sort_function[0] = pcpu_sort;
524 sort_function[1] = mem_sort;
525 sort_function[2] = time_sort;
526#else
527 sort_function = mem_sort;
528#endif
529 while (1) {
530 /* read process IDs & status for all the processes */
Eric Andersen44608e92002-10-22 12:21:15 +0000531 procps_status_t * p;
532
Eric Andersen9e480452003-07-03 10:07:04 +0000533#ifdef CONFIG_SELINUX
534 while ((p = procps_scan(0, 0, NULL) ) != 0) {
535#else
Eric Andersen44608e92002-10-22 12:21:15 +0000536 while ((p = procps_scan(0)) != 0) {
Eric Andersen9e480452003-07-03 10:07:04 +0000537#endif
Eric Andersen44608e92002-10-22 12:21:15 +0000538 int n = ntop;
539
540 top = xrealloc(top, (++ntop)*sizeof(procps_status_t));
541 memcpy(top + n, p, sizeof(procps_status_t));
542 }
543 if (ntop == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000544 bb_perror_msg_and_die("scandir('/proc')");
Eric Andersen420b2082002-09-17 22:14:58 +0000545 }
Eric Andersen08a72202002-09-30 20:52:10 +0000546#ifdef FEATURE_CPU_USAGE_PERCENTAGE
547 if(!Hertz) {
548 init_Hertz_value();
549 do_stats();
550 sleep(1);
551 clearmems();
552 continue;
Eric Andersen420b2082002-09-17 22:14:58 +0000553 }
Eric Andersen08a72202002-09-30 20:52:10 +0000554 do_stats();
555#else
Eric Andersen44608e92002-10-22 12:21:15 +0000556 qsort(top, ntop, sizeof(procps_status_t), (void*)sort_function);
Eric Andersen08a72202002-09-30 20:52:10 +0000557#endif
558 opt = lines;
559 if (opt > ntop) {
560 opt = ntop;
561 }
562 /* show status for each of the processes */
563 display_status(opt, col);
564#if defined CONFIG_FEATURE_USE_TERMIOS
565 tv.tv_sec = interval;
566 tv.tv_usec = 0;
567 FD_ZERO (&readfds);
568 FD_SET (0, &readfds);
569 select (1, &readfds, NULL, NULL, &tv);
570 if (FD_ISSET (0, &readfds)) {
571 if (read (0, &c, 1) <= 0) { /* signal */
Eric Andersen420b2082002-09-17 22:14:58 +0000572 return EXIT_FAILURE;
573 }
Eric Andersen08a72202002-09-30 20:52:10 +0000574 if(c == 'q' || c == initial_settings.c_cc[VINTR])
575 return EXIT_SUCCESS;
576 if(c == 'M') {
577#ifdef FEATURE_CPU_USAGE_PERCENTAGE
578 sort_function[0] = mem_sort;
579 sort_function[1] = pcpu_sort;
580 sort_function[2] = time_sort;
581#else
582 sort_function = mem_sort;
583#endif
584 }
585#ifdef FEATURE_CPU_USAGE_PERCENTAGE
586 if(c == 'P') {
587 sort_function[0] = pcpu_sort;
588 sort_function[1] = mem_sort;
589 sort_function[2] = time_sort;
590 }
591 if(c == 'T') {
592 sort_function[0] = time_sort;
593 sort_function[1] = mem_sort;
594 sort_function[2] = pcpu_sort;
595 }
596#endif
597 if(c == 'N') {
598#ifdef FEATURE_CPU_USAGE_PERCENTAGE
599 sort_function[0] = pid_sort;
600#else
601 sort_function = pid_sort;
602#endif
603 }
604 }
605#else
Eric Andersen420b2082002-09-17 22:14:58 +0000606 sleep(interval);
Eric Andersen08a72202002-09-30 20:52:10 +0000607#endif /* CONFIG_FEATURE_USE_TERMIOS */
608 clearmems();
Eric Andersen420b2082002-09-17 22:14:58 +0000609 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000610
Eric Andersen420b2082002-09-17 22:14:58 +0000611 return EXIT_SUCCESS;
612}