1/* vi: set sw=4 ts=4: */ 2/* 3 * Mini ps implementation(s) for busybox 4 * 5 * Copyright (C) 1999,2000,2001 by Lineo, inc. 6 * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org> 7 * 8 * 9 * This contains _two_ implementations of ps for Linux. One uses the 10 * traditional /proc virtual filesystem, and the other use the devps kernel 11 * driver (written by Erik Andersen to avoid using /proc thereby saving 100k+). 12 * 13 * 14 * 15 * This program is free software; you can redistribute it and/or modify it 16 * under the terms of the GNU General Public License as published by the Free 17 * Software Foundation; either version 2 of the License, or (at your option) 18 * any later version. 19 * 20 * This program is distributed in the hope that it will be useful, but WITHOUT 21 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 22 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 23 * more details. 24 * 25 * You should have received a copy of the GNU General Public License along with 26 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 27 * Place, Suite 330, Boston, MA 02111-1307 USA 28 * 29 */ 30 31#include <stdio.h> 32#include <stdlib.h> 33#include <unistd.h> 34#include <dirent.h> 35#include <errno.h> 36#include <fcntl.h> 37#include <ctype.h> 38#include <string.h> 39#include <termios.h> 40#include <sys/ioctl.h> 41#include "busybox.h" 42 43static const int TERMINAL_WIDTH = 79; /* not 80 in case terminal has linefold bug */ 44 45 46 47#if ! defined BB_FEATURE_USE_DEVPS_PATCH 48 49/* The following is the first ps implementation -- 50 * the one using the /proc virtual filesystem. 51 */ 52 53typedef struct proc_s { 54 char 55 cmd[16]; /* basename of executable file in call to exec(2) */ 56 int 57 ruid, /* real only (sorry) */ 58 pid, /* process id */ 59 ppid; /* pid of parent process */ 60 char 61 state; /* single-char code for process state (S=sleeping) */ 62} proc_t; 63 64 65 66static int file2str(char *filename, char *ret, int cap) 67{ 68 int fd, num_read; 69 70 if ((fd = open(filename, O_RDONLY, 0)) == -1) 71 return -1; 72 if ((num_read = read(fd, ret, cap - 1)) <= 0) 73 return -1; 74 ret[num_read] = 0; 75 close(fd); 76 return num_read; 77} 78 79 80static void parse_proc_status(char *S, proc_t * P) 81{ 82 char *tmp; 83 84 memset(P->cmd, 0, sizeof P->cmd); 85 sscanf(S, "Name:\t%15c", P->cmd); 86 tmp = strchr(P->cmd, '\n'); 87 if (tmp) 88 *tmp = '\0'; 89 tmp = strstr(S, "State"); 90 sscanf(tmp, "State:\t%c", &P->state); 91 92 tmp = strstr(S, "Pid:"); 93 if (tmp) 94 sscanf(tmp, "Pid:\t%d\n" "PPid:\t%d\n", &P->pid, &P->ppid); 95 else 96 error_msg("Internal error!"); 97 98 /* For busybox, ignoring effective, saved, etc. */ 99 tmp = strstr(S, "Uid:"); 100 if (tmp) 101 sscanf(tmp, "Uid:\t%d", &P->ruid); 102 else 103 error_msg("Internal error!"); 104 105 106} 107 108extern int ps_main(int argc, char **argv) 109{ 110 proc_t p; 111 DIR *dir; 112 FILE *file; 113 struct dirent *entry; 114 char path[32], sbuf[512]; 115 char uidName[9]; 116 int len, i, c; 117#ifdef BB_FEATURE_AUTOWIDTH 118 struct winsize win = { 0, 0, 0, 0 }; 119 int terminal_width = TERMINAL_WIDTH; 120#else 121#define terminal_width TERMINAL_WIDTH 122#endif 123 124 125 126 dir = opendir("/proc"); 127 if (!dir) 128 error_msg_and_die("Can't open /proc"); 129 130#ifdef BB_FEATURE_AUTOWIDTH 131 ioctl(fileno(stdout), TIOCGWINSZ, &win); 132 if (win.ws_col > 0) 133 terminal_width = win.ws_col - 1; 134#endif 135 136 printf(" PID Uid Stat Command\n"); 137 while ((entry = readdir(dir)) != NULL) { 138 if (!isdigit(*entry->d_name)) 139 continue; 140 sprintf(path, "/proc/%s/status", entry->d_name); 141 if ((file2str(path, sbuf, sizeof sbuf)) != -1) { 142 parse_proc_status(sbuf, &p); 143 } 144 145 /* Make some adjustments as needed */ 146 my_getpwuid(uidName, p.ruid); 147 if (*uidName == '\0') 148 sprintf(uidName, "%d", p.ruid); 149 150 sprintf(path, "/proc/%s/cmdline", entry->d_name); 151 file = fopen(path, "r"); 152 if (file == NULL) 153 continue; 154 i = 0; 155 len = printf("%5d %-8s %c ", p.pid, uidName, p.state); 156 while (((c = getc(file)) != EOF) && (i < (terminal_width-len))) { 157 i++; 158 if (c == '\0') 159 c = ' '; 160 putc(c, stdout); 161 } 162 fclose(file); 163 if (i == 0) 164 printf("[%s]", p.cmd); 165 putchar('\n'); 166 } 167 closedir(dir); 168 return EXIT_SUCCESS; 169} 170 171 172#else /* BB_FEATURE_USE_DEVPS_PATCH */ 173 174 175/* The following is the second ps implementation -- 176 * this one uses the nifty new devps kernel device. 177 */ 178 179#include <linux/devps.h> /* For Erik's nifty devps device driver */ 180 181 182extern int ps_main(int argc, char **argv) 183{ 184 char device[] = "/dev/ps"; 185 int i, j, len, fd; 186 pid_t num_pids; 187 pid_t* pid_array = NULL; 188 struct pid_info info; 189 char uidName[9]; 190#ifdef BB_FEATURE_AUTOWIDTH 191 struct winsize win = { 0, 0, 0, 0 }; 192 int terminal_width = TERMINAL_WIDTH; 193#else 194#define terminal_width TERMINAL_WIDTH 195#endif 196 197 if (argc > 1 && **(argv + 1) == '-') 198 show_usage(); 199 200 /* open device */ 201 fd = open(device, O_RDONLY); 202 if (fd < 0) 203 perror_msg_and_die( "open failed for `%s'", device); 204 205 /* Find out how many processes there are */ 206 if (ioctl (fd, DEVPS_GET_NUM_PIDS, &num_pids)<0) 207 perror_msg_and_die( "\nDEVPS_GET_PID_LIST"); 208 209 /* Allocate some memory -- grab a few extras just in case 210 * some new processes start up while we wait. The kernel will 211 * just ignore any extras if we give it too many, and will trunc. 212 * the list if we give it too few. */ 213 pid_array = (pid_t*) xcalloc( num_pids+10, sizeof(pid_t)); 214 pid_array[0] = num_pids+10; 215 216 /* Now grab the pid list */ 217 if (ioctl (fd, DEVPS_GET_PID_LIST, pid_array)<0) 218 perror_msg_and_die("\nDEVPS_GET_PID_LIST"); 219 220#ifdef BB_FEATURE_AUTOWIDTH 221 ioctl(fileno(stdout), TIOCGWINSZ, &win); 222 if (win.ws_col > 0) 223 terminal_width = win.ws_col - 1; 224#endif 225 226 /* Print up a ps listing */ 227 printf(" PID Uid Stat Command\n"); 228 229 for (i=1; i<pid_array[0] ; i++) { 230 info.pid = pid_array[i]; 231 232 if (ioctl (fd, DEVPS_GET_PID_INFO, &info)<0) 233 perror_msg_and_die("\nDEVPS_GET_PID_INFO"); 234 235 /* Make some adjustments as needed */ 236 my_getpwuid(uidName, info.euid); 237 if (*uidName == '\0') 238 sprintf(uidName, "%ld", info.euid); 239 240 len = printf("%5d %-8s %c ", info.pid, uidName, info.state); 241 242 if (strlen(info.command_line) > 1) { 243 for( j=0; j<(sizeof(info.command_line)-1) && j < (terminal_width-len); j++) { 244 if (*(info.command_line+j) == '\0' && *(info.command_line+j+1) != '\0') { 245 *(info.command_line+j) = ' '; 246 } 247 } 248 *(info.command_line+j) = '\0'; 249 puts(info.command_line); 250 } else { 251 printf("[%s]\n", info.name); 252 } 253 } 254 255 /* Free memory */ 256 free( pid_array); 257 258 /* close device */ 259 if (close (fd) != 0) 260 perror_msg_and_die("close failed for `%s'", device); 261 262 exit (0); 263} 264 265#endif /* BB_FEATURE_USE_DEVPS_PATCH */ 266 267