top: make it possible to feed commands via pipe
No binary (build failed)
—
(log yok)
| @@ -92,9 +92,9 @@ enum { SORT_DEPTH = 3 }; | |
| struct globals { | |
| top_status_t *top; | |
| int ntop; | |
| + | smallint inverted; |
| #if ENABLE_FEATURE_TOPMEM | |
| smallint sort_field; | |
| - | smallint inverted; |
| #endif | |
| #if ENABLE_FEATURE_TOP_SMP_CPU | |
| smallint smp_cpu_info; /* one/many cpu info lines? */ | |
| @@ -194,9 +194,9 @@ static int mult_lvl_cmp(void* a, void* b) | |
| for (i = 0; i < SORT_DEPTH; i++) { | |
| cmp_val = (*sort_function[i])(a, b); | |
| if (cmp_val != 0) | |
| - | return cmp_val; |
| + | break; |
| } | |
| - | return 0; |
| + | return inverted ? -cmp_val : cmp_val; |
| } | |
| static NOINLINE int read_cpu_jiffy(FILE *fp, jiffy_counts_t *p_jif) | |
| @@ -850,8 +850,105 @@ enum { | |
| | PSSCAN_PID | |
| | PSSCAN_SMAPS | |
| | PSSCAN_COMM, | |
| + | EXIT_MASK = (unsigned)-1, |
| }; | |
| + | #if ENABLE_FEATURE_USE_TERMIOS |
| + | static unsigned handle_input(unsigned scan_mask, unsigned interval) |
| + | { |
| + | unsigned char c, *p, buf[64]; |
| + | int len; |
| + | struct pollfd pfd[1]; |
| + | |
| + | pfd[0].fd = 0; |
| + | pfd[0].events = POLLIN; |
| + | if (safe_poll(pfd, 1, interval * 1000) <= 0) |
| + | return scan_mask; |
| + | |
| + | len = safe_read(STDIN_FILENO, &buf, sizeof(buf)-1); |
| + | if (len <= 0) { /* error/EOF? */ |
| + | option_mask32 |= OPT_EOF; |
| + | return scan_mask; |
| + | } |
| + | |
| + | buf[len] = 0; |
| + | p = buf; |
| + | while ((c = *p++) != 0) { |
| + | if (c == initial_settings.c_cc[VINTR]) |
| + | return EXIT_MASK; |
| + | if (c == initial_settings.c_cc[VEOF]) |
| + | return EXIT_MASK; |
| + | c |= 0x20; /* lowercase */ |
| + | if (c == 'q') |
| + | return EXIT_MASK; |
| + | if (c == 'n') { |
| + | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) |
| + | sort_function[0] = pid_sort; |
| + | } |
| + | if (c == 'm') { |
| + | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) |
| + | sort_function[0] = mem_sort; |
| + | # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
| + | sort_function[1] = pcpu_sort; |
| + | sort_function[2] = time_sort; |
| + | # endif |
| + | } |
| + | # if ENABLE_FEATURE_SHOW_THREADS |
| + | if (c == 'h' |
| + | IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK) |
| + | ) { |
| + | scan_mask ^= PSSCAN_TASKS; |
| + | } |
| + | # endif |
| + | # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
| + | if (c == 'p') { |
| + | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) |
| + | sort_function[0] = pcpu_sort; |
| + | sort_function[1] = mem_sort; |
| + | sort_function[2] = time_sort; |
| + | } |
| + | if (c == 't') { |
| + | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) |
| + | sort_function[0] = time_sort; |
| + | sort_function[1] = mem_sort; |
| + | sort_function[2] = pcpu_sort; |
| + | } |
| + | # if ENABLE_FEATURE_TOPMEM |
| + | if (c == 's') { |
| + | scan_mask = TOPMEM_MASK; |
| + | free(prev_hist); |
| + | prev_hist = NULL; |
| + | prev_hist_count = 0; |
| + | sort_field = (sort_field + 1) % NUM_SORT_FIELD; |
| + | } |
| + | # endif |
| + | if (c == 'r') |
| + | inverted ^= 1; |
| + | # if ENABLE_FEATURE_TOP_SMP_CPU |
| + | /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ |
| + | if (c == 'c' || c == '1') { |
| + | /* User wants to toggle per cpu <> aggregate */ |
| + | if (smp_cpu_info) { |
| + | free(cpu_prev_jif); |
| + | free(cpu_jif); |
| + | cpu_jif = &cur_jif; |
| + | cpu_prev_jif = &prev_jif; |
| + | } else { |
| + | /* Prepare for xrealloc() */ |
| + | cpu_jif = cpu_prev_jif = NULL; |
| + | } |
| + | num_cpus = 0; |
| + | smp_cpu_info = !smp_cpu_info; |
| + | get_jiffy_counts(); |
| + | } |
| + | # endif |
| + | # endif |
| + | } |
| + | |
| + | return scan_mask; |
| + | } |
| + | #endif |
| + | |
| //usage:#if ENABLE_FEATURE_SHOW_THREADS || ENABLE_FEATURE_TOP_SMP_CPU | |
| //usage:# define IF_SHOW_THREADS_OR_TOP_SMP(...) __VA_ARGS__ | |
| //usage:#else | |
| @@ -871,8 +968,9 @@ enum { | |
| //usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/cpu") | |
| //usage: IF_FEATURE_TOP_CPU_USAGE_PERCENTAGE("/time") | |
| //usage: IF_FEATURE_TOPMEM( | |
| - | //usage: "\n"" S: show memory, R: reverse memory sort" |
| + | //usage: "\n"" S: show memory" |
| //usage: ) | |
| + | //usage: "\n"" R: reverse sort" |
| //usage: IF_SHOW_THREADS_OR_TOP_SMP( | |
| //usage: "\n"" " | |
| //usage: IF_FEATURE_SHOW_THREADS("H: toggle threads") | |
| @@ -880,23 +978,34 @@ enum { | |
| //usage: IF_FEATURE_TOP_SMP_CPU("1: toggle SMP") | |
| //usage: ) | |
| //usage: "\n"" Q,^C: exit" | |
| + | //usage: "\n" |
| + | //usage: "\n""Options:" |
| + | //usage: "\n"" -b Batch mode" |
| + | //usage: "\n"" -n N Exit after N iterations" |
| + | //usage: "\n"" -d N Delay between updates" |
| + | //usage: IF_FEATURE_TOPMEM( |
| + | //usage: "\n"" -m Same as 's' key" |
| + | //usage: ) |
| + | |
| + | /* Interactive testing: |
| + | * echo sss | ./busybox top |
| + | * - shows memory screen |
| + | * echo sss | ./busybox top -bn1 >mem |
| + | * - saves memory screen - the *whole* list, not first NROWS porcesses! |
| + | * |
| + | * TODO: -i STRING param as a better alternative? |
| + | */ |
| int top_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |
| int top_main(int argc UNUSED_PARAM, char **argv) | |
| { | |
| int iterations; | |
| unsigned lines, col; | |
| - | int lines_rem; |
| unsigned interval; | |
| char *str_interval, *str_iterations; | |
| unsigned scan_mask = TOP_MASK; | |
| #if ENABLE_FEATURE_USE_TERMIOS | |
| struct termios new_settings; | |
| - | struct pollfd pfd[1]; |
| - | unsigned char c; |
| - | |
| - | pfd[0].fd = 0; |
| - | pfd[0].events = POLLIN; |
| #endif | |
| INIT_G(); | |
| @@ -933,15 +1042,6 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |
| /* change to /proc */ | |
| xchdir("/proc"); | |
| - | #if ENABLE_FEATURE_USE_TERMIOS |
| - | tcgetattr(0, (void *) &initial_settings); |
| - | memcpy(&new_settings, &initial_settings, sizeof(new_settings)); |
| - | /* unbuffered input, turn off echo */ |
| - | new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL); |
| - | |
| - | bb_signals(BB_FATAL_SIGS, sig_catcher); |
| - | tcsetattr_stdin_TCSANOW(&new_settings); |
| - | #endif |
| #if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE | |
| sort_function[0] = pcpu_sort; | |
| @@ -951,21 +1051,41 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |
| sort_function[0] = mem_sort; | |
| #endif | |
| - | while (1) { |
| + | #if ENABLE_FEATURE_USE_TERMIOS |
| + | tcgetattr(0, (void *) &initial_settings); |
| + | memcpy(&new_settings, &initial_settings, sizeof(new_settings)); |
| + | if (!OPT_BATCH_MODE) { |
| + | /* unbuffered input, turn off echo */ |
| + | new_settings.c_lflag &= ~(ISIG | ICANON | ECHO | ECHONL); |
| + | tcsetattr_stdin_TCSANOW(&new_settings); |
| + | } |
| + | |
| + | bb_signals(BB_FATAL_SIGS, sig_catcher); |
| + | |
| + | /* Eat initial input, if any */ |
| + | scan_mask = handle_input(scan_mask, 0); |
| + | #endif |
| + | |
| + | while (scan_mask != EXIT_MASK) { |
| procps_status_t *p = NULL; | |
| - | lines = 24; /* default */ |
| - | col = 79; |
| + | if (OPT_BATCH_MODE) { |
| + | lines = INT_MAX; |
| + | col = LINE_BUF_SIZE - 2; /* +2 bytes for '\n', NUL */ |
| + | } else { |
| + | lines = 24; /* default */ |
| + | col = 79; |
| #if ENABLE_FEATURE_USE_TERMIOS | |
| - | /* We output to stdout, we need size of stdout (not stdin)! */ |
| - | get_terminal_width_height(STDOUT_FILENO, &col, &lines); |
| - | if (lines < 5 || col < 10) { |
| - | sleep(interval); |
| - | continue; |
| - | } |
| + | /* We output to stdout, we need size of stdout (not stdin)! */ |
| + | get_terminal_width_height(STDOUT_FILENO, &col, &lines); |
| + | if (lines < 5 || col < 10) { |
| + | sleep(interval); |
| + | continue; |
| + | } |
| #endif | |
| - | if (col > LINE_BUF_SIZE-2) /* +2 bytes for '\n', NUL, */ |
| - | col = LINE_BUF_SIZE-2; |
| + | if (col > LINE_BUF_SIZE - 2) |
| + | col = LINE_BUF_SIZE - 2; |
| + | } |
| /* read process IDs & status for all the processes */ | |
| while ((p = procps_scan(p, scan_mask)) != NULL) { | |
| @@ -1033,15 +1153,11 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |
| qsort(topmem, ntop, sizeof(topmem_status_t), (void*)topmem_sort); | |
| } | |
| #endif | |
| - | lines_rem = lines; |
| - | if (OPT_BATCH_MODE) { |
| - | lines_rem = INT_MAX; |
| - | } |
| if (scan_mask != TOPMEM_MASK) | |
| - | display_process_list(lines_rem, col); |
| + | display_process_list(lines, col); |
| #if ENABLE_FEATURE_TOPMEM | |
| else | |
| - | display_topmem_process_list(lines_rem, col); |
| + | display_topmem_process_list(lines, col); |
| #endif | |
| clearmems(); | |
| if (iterations >= 0 && !--iterations) | |
| @@ -1052,81 +1168,10 @@ int top_main(int argc UNUSED_PARAM, char **argv) | |
| if (option_mask32 & (OPT_b|OPT_EOF)) | |
| /* batch mode, or EOF on stdin ("top </dev/null") */ | |
| sleep(interval); | |
| - | else if (safe_poll(pfd, 1, interval * 1000) > 0) { |
| - | if (safe_read(STDIN_FILENO, &c, 1) != 1) { /* error/EOF? */ |
| - | option_mask32 |= OPT_EOF; |
| - | continue; |
| - | } |
| - | if (c == initial_settings.c_cc[VINTR]) |
| - | break; |
| - | c |= 0x20; /* lowercase */ |
| - | if (c == 'q') |
| - | break; |
| - | if (c == 'n') { |
| - | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) |
| - | sort_function[0] = pid_sort; |
| - | } |
| - | if (c == 'm') { |
| - | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) |
| - | sort_function[0] = mem_sort; |
| - | # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
| - | sort_function[1] = pcpu_sort; |
| - | sort_function[2] = time_sort; |
| - | # endif |
| - | } |
| - | # if ENABLE_FEATURE_SHOW_THREADS |
| - | if (c == 'h' |
| - | IF_FEATURE_TOPMEM(&& scan_mask != TOPMEM_MASK) |
| - | ) { |
| - | scan_mask ^= PSSCAN_TASKS; |
| - | } |
| - | # endif |
| - | # if ENABLE_FEATURE_TOP_CPU_USAGE_PERCENTAGE |
| - | if (c == 'p') { |
| - | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) |
| - | sort_function[0] = pcpu_sort; |
| - | sort_function[1] = mem_sort; |
| - | sort_function[2] = time_sort; |
| - | } |
| - | if (c == 't') { |
| - | IF_FEATURE_TOPMEM(scan_mask = TOP_MASK;) |
| - | sort_function[0] = time_sort; |
| - | sort_function[1] = mem_sort; |
| - | sort_function[2] = pcpu_sort; |
| - | } |
| - | # if ENABLE_FEATURE_TOPMEM |
| - | if (c == 's') { |
| - | scan_mask = TOPMEM_MASK; |
| - | free(prev_hist); |
| - | prev_hist = NULL; |
| - | prev_hist_count = 0; |
| - | sort_field = (sort_field + 1) % NUM_SORT_FIELD; |
| - | } |
| - | if (c == 'r') |
| - | inverted ^= 1; |
| - | # endif |
| - | # if ENABLE_FEATURE_TOP_SMP_CPU |
| - | /* procps-2.0.18 uses 'C', 3.2.7 uses '1' */ |
| - | if (c == 'c' || c == '1') { |
| - | /* User wants to toggle per cpu <> aggregate */ |
| - | if (smp_cpu_info) { |
| - | free(cpu_prev_jif); |
| - | free(cpu_jif); |
| - | cpu_jif = &cur_jif; |
| - | cpu_prev_jif = &prev_jif; |
| - | } else { |
| - | /* Prepare for xrealloc() */ |
| - | cpu_jif = cpu_prev_jif = NULL; |
| - | } |
| - | num_cpus = 0; |
| - | smp_cpu_info = !smp_cpu_info; |
| - | get_jiffy_counts(); |
| - | } |
| - | # endif |
| - | # endif |
| - | } |
| + | else |
| + | scan_mask = handle_input(scan_mask, interval); |
| #endif /* FEATURE_USE_TERMIOS */ | |
| - | } /* end of "while (1)" */ |
| + | } /* end of "while (not Q)" */ |
| bb_putchar('\n'); | |
| #if ENABLE_FEATURE_USE_TERMIOS | |