1/*
2 * find_pid_by_name from busybox
3 */
4
5#include <sys/types.h>
6#include <sys/stat.h>
7#include <fcntl.h>
8#include <dirent.h>
9#include <stdlib.h>
10#include <stdio.h>
11#include <errno.h>
12#include <string.h>
13#include <unistd.h>
14#include <stddef.h>
15#include <ctype.h>
16
17enum {
18        PSSCAN_PID      = 1 << 0,
19        PSSCAN_PPID     = 1 << 1,
20        PSSCAN_PGID     = 1 << 2,
21        PSSCAN_SID      = 1 << 3,
22        PSSCAN_UIDGID   = 1 << 4,
23        PSSCAN_COMM     = 1 << 5,
24        /* PSSCAN_CMD      = 1 << 6, - use read_cmdline instead */
25        PSSCAN_ARGV0    = 1 << 7,
26        /* PSSCAN_EXE      = 1 << 8, - not implemented */
27        PSSCAN_STATE    = 1 << 9,
28        PSSCAN_VSZ      = 1 << 10,
29        PSSCAN_RSS      = 1 << 11,
30        PSSCAN_STIME    = 1 << 12,
31        PSSCAN_UTIME    = 1 << 13,
32        PSSCAN_TTY      = 1 << 14,
33        PSSCAN_SMAPS    = (1 << 15) * 0,
34        PSSCAN_ARGVN    = (1 << 16) * 1,
35        PSSCAN_START_TIME = 1 << 18,
36        /* These are all retrieved from proc/NN/stat in one go: */
37        PSSCAN_STAT     = PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID
38                        | PSSCAN_COMM | PSSCAN_STATE
39                        | PSSCAN_VSZ | PSSCAN_RSS
40                        | PSSCAN_STIME | PSSCAN_UTIME | PSSCAN_START_TIME
41                        | PSSCAN_TTY,
42};
43
44/*
45In Linux we have three ways to determine "process name":
461. /proc/PID/stat has "...(name)...", among other things. It's so-called "comm" field.
472. /proc/PID/cmdline's first NUL-terminated string. It's argv[0] from exec syscall.
483. /proc/PID/exe symlink. Points to the running executable file.
49
50kernel threads:
51 comm: thread name
52 cmdline: empty
53 exe: <readlink fails>
54
55executable
56 comm: first 15 chars of base name
57 (if executable is a symlink, then first 15 chars of symlink name are used)
58 cmdline: argv[0] from exec syscall
59 exe: points to executable (resolves symlink, unlike comm)
60
61script (an executable with #!/path/to/interpreter):
62 comm: first 15 chars of script's base name (symlinks are not resolved)
63 cmdline: /path/to/interpreter (symlinks are not resolved)
64 (script name is in argv[1], args are pushed into argv[2] etc)
65 exe: points to interpreter's executable (symlinks are resolved)
66
67If FEATURE_PREFER_APPLETS=y (and more so if FEATURE_SH_STANDALONE=y),
68some commands started from busybox shell, xargs or find are started by
69execXXX("/proc/self/exe", applet_name, params....)
70and therefore comm field contains "exe".
71*/
72
73#define PROCPS_BUFSIZE 1024
74
75static int read_to_buf(const char *filename, void *buf)
76{
77	int fd;
78	/* open_read_close() would do two reads, checking for EOF.
79	 * When you have 10000 /proc/$NUM/stat to read, it isn't desirable */
80	int ret = -1;
81	fd = open(filename, O_RDONLY);
82	if (fd >= 0) {
83		ret = read(fd, buf, PROCPS_BUFSIZE-1);
84		close(fd);
85	}
86	((char *)buf)[ret > 0 ? ret : 0] = '\0';
87	return ret;
88}
89
90void* xzalloc(size_t size)
91{
92        void *ptr = malloc(size);
93        memset(ptr, 0, size);
94        return ptr;
95}
96
97typedef struct procps_status_t {
98        DIR *dir;
99        unsigned char shift_pages_to_bytes;
100        unsigned char shift_pages_to_kb;
101/* Fields are set to 0/NULL if failed to determine (or not requested) */
102        unsigned int argv_len;
103        char *argv0;
104        /* Everything below must contain no ptrs to malloc'ed data:
105         * it is memset(0) for each process in procps_scan() */
106        unsigned long vsz, rss; /* we round it to kbytes */
107        unsigned long stime, utime;
108        unsigned long start_time;
109        unsigned pid;
110        unsigned ppid;
111        unsigned pgid;
112        unsigned sid;
113        unsigned uid;
114        unsigned gid;
115        unsigned tty_major,tty_minor;
116        char state[4];
117        /* basename of executable in exec(2), read from /proc/N/stat
118         * (if executable is symlink or script, it is NOT replaced
119         * by link target or interpreter name) */
120        char comm[16];
121        /* user/group? - use passwd/group parsing functions */
122} procps_status_t;
123
124static procps_status_t* alloc_procps_scan(void)
125{
126	unsigned n = getpagesize();
127	procps_status_t* sp = xzalloc(sizeof(procps_status_t));
128	sp->dir = opendir("/proc");
129	while (1) {
130		n >>= 1;
131		if (!n) break;
132		sp->shift_pages_to_bytes++;
133	}
134	sp->shift_pages_to_kb = sp->shift_pages_to_bytes - 10;
135	return sp;
136}
137
138void BUG_comm_size(void)
139{
140}
141
142#define ULLONG_MAX     (~0ULL)
143#define UINT_MAX       (~0U)
144
145static unsigned long long ret_ERANGE(void)
146{
147        errno = ERANGE; /* this ain't as small as it looks (on glibc) */
148        return ULLONG_MAX;
149}
150
151static unsigned long long handle_errors(unsigned long long v, char **endp, char *endptr)
152{
153        if (endp) *endp = endptr;
154
155        /* errno is already set to ERANGE by strtoXXX if value overflowed */
156        if (endptr[0]) {
157                /* "1234abcg" or out-of-range? */
158                if (isalnum(endptr[0]) || errno)
159                        return ret_ERANGE();
160                /* good number, just suspicious terminator */
161                errno = EINVAL;
162        }
163        return v;
164}
165
166unsigned bb_strtou(const char *arg, char **endp, int base)
167{
168        unsigned long v;
169        char *endptr;
170
171        if (!isalnum(arg[0])) return ret_ERANGE();
172        errno = 0;
173        v = strtoul(arg, &endptr, base);
174        if (v > UINT_MAX) return ret_ERANGE();
175        return handle_errors(v, endp, endptr);
176}
177
178unsigned long long bb_strtoull(const char *arg, char **endp, int base)
179{
180        unsigned long long v;
181        char *endptr;
182
183        /* strtoul("  -4200000000") returns 94967296, errno 0 (!) */
184        /* I don't think that this is right. Preventing this... */
185        if (!isalnum(arg[0])) return ret_ERANGE();
186
187        /* not 100% correct for lib func, but convenient for the caller */
188        errno = 0;
189        v = strtoull(arg, &endptr, base);
190        return handle_errors(v, endp, endptr);
191}
192
193void* xrealloc(void *ptr, size_t size)
194{
195        ptr = realloc(ptr, size);
196        if (ptr == NULL && size != 0)
197                perror("no memory");
198        return ptr;
199}
200
201void* xrealloc_vector_helper(void *vector, unsigned sizeof_and_shift, int idx)
202{
203        int mask = 1 << (unsigned char)sizeof_and_shift;
204
205        if (!(idx & (mask - 1))) {
206                sizeof_and_shift >>= 8; /* sizeof(vector[0]) */
207                vector = xrealloc(vector, sizeof_and_shift * (idx + mask + 1));
208                memset((char*)vector + (sizeof_and_shift * idx), 0, sizeof_and_shift * (mask + 1));
209        }
210        return vector;
211}
212
213#define xrealloc_vector(vector, shift, idx) \
214        xrealloc_vector_helper((vector), (sizeof((vector)[0]) << 8) + (shift), (idx))
215
216void free_procps_scan(procps_status_t* sp)
217{
218	closedir(sp->dir);
219	free(sp->argv0);
220	free(sp);
221}
222
223procps_status_t* procps_scan(procps_status_t* sp, int flags)
224{
225	struct dirent *entry;
226	char buf[PROCPS_BUFSIZE];
227	char filename[sizeof("/proc//cmdline") + sizeof(int)*3];
228	char *filename_tail;
229	long tasknice;
230	unsigned pid;
231	int n;
232	struct stat sb;
233
234	if (!sp)
235		sp = alloc_procps_scan();
236
237	for (;;) {
238		entry = readdir(sp->dir);
239		if (entry == NULL) {
240			free_procps_scan(sp);
241			return NULL;
242		}
243		pid = bb_strtou(entry->d_name, NULL, 10);
244		if (errno)
245			continue;
246
247		/* After this point we have to break, not continue
248		 * ("continue" would mean that current /proc/NNN
249		 * is not a valid process info) */
250
251		memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz));
252
253		sp->pid = pid;
254		if (!(flags & ~PSSCAN_PID)) break;
255
256		filename_tail = filename + sprintf(filename, "/proc/%d", pid);
257
258		if (flags & PSSCAN_UIDGID) {
259			if (stat(filename, &sb))
260				break;
261			/* Need comment - is this effective or real UID/GID? */
262			sp->uid = sb.st_uid;
263			sp->gid = sb.st_gid;
264		}
265
266		if (flags & PSSCAN_STAT) {
267			char *cp, *comm1;
268			int tty;
269			unsigned long vsz, rss;
270
271			/* see proc(5) for some details on this */
272			strcpy(filename_tail, "/stat");
273			n = read_to_buf(filename, buf);
274			if (n < 0)
275				break;
276			cp = strrchr(buf, ')'); /* split into "PID (cmd" and "<rest>" */
277			/*if (!cp || cp[1] != ' ')
278				break;*/
279			cp[0] = '\0';
280			if (sizeof(sp->comm) < 16)
281				BUG_comm_size();
282			comm1 = strchr(buf, '(');
283			/*if (comm1)*/
284				strncpy(sp->comm, comm1 + 1, sizeof(sp->comm));
285
286			n = sscanf(cp+2,
287				"%c %u "               /* state, ppid */
288				"%u %u %d %*s "        /* pgid, sid, tty, tpgid */
289				"%*s %*s %*s %*s %*s " /* flags, min_flt, cmin_flt, maj_flt, cmaj_flt */
290				"%lu %lu "             /* utime, stime */
291				"%*s %*s %*s "         /* cutime, cstime, priority */
292				"%ld "                 /* nice */
293				"%*s %*s "             /* timeout, it_real_value */
294				"%lu "                 /* start_time */
295				"%lu "                 /* vsize */
296				"%lu "                 /* rss */
297			/*	"%lu %lu %lu %lu %lu %lu " rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip */
298			/*	"%u %u %u %u "         signal, blocked, sigignore, sigcatch */
299			/*	"%lu %lu %lu"          wchan, nswap, cnswap */
300				,
301				sp->state, &sp->ppid,
302				&sp->pgid, &sp->sid, &tty,
303				&sp->utime, &sp->stime,
304				&tasknice,
305				&sp->start_time,
306				&vsz,
307				&rss);
308			if (n != 11)
309				break;
310			/* vsz is in bytes and we want kb */
311			sp->vsz = vsz >> 10;
312			/* vsz is in bytes but rss is in *PAGES*! Can you believe that? */
313			sp->rss = rss << sp->shift_pages_to_kb;
314			sp->tty_major = (tty >> 8) & 0xfff;
315			sp->tty_minor = (tty & 0xff) | ((tty >> 12) & 0xfff00);
316
317			if (sp->vsz == 0 && sp->state[0] != 'Z')
318				sp->state[1] = 'W';
319			else
320				sp->state[1] = ' ';
321			if (tasknice < 0)
322				sp->state[2] = '<';
323			else if (tasknice) /* > 0 */
324				sp->state[2] = 'N';
325			else
326				sp->state[2] = ' ';
327
328		}
329
330		if (flags & (PSSCAN_ARGV0|PSSCAN_ARGVN)) {
331			free(sp->argv0);
332			sp->argv0 = NULL;
333			strcpy(filename_tail, "/cmdline");
334			n = read_to_buf(filename, buf);
335			if (n <= 0)
336				break;
337			if (flags & PSSCAN_ARGVN) {
338				sp->argv_len = n;
339				sp->argv0 = malloc(n + 1);
340				memcpy(sp->argv0, buf, n + 1);
341				/* sp->argv0[n] = '\0'; - buf has it */
342			} else {
343				sp->argv_len = 0;
344				sp->argv0 = strdup(buf);
345			}
346		}
347		break;
348	}
349	return sp;
350}
351
352const char* bb_basename(const char *name)
353{
354        const char *cp = strrchr(name, '/');
355        if (cp)
356                return cp + 1;
357        return name;
358}
359
360static int comm_match(procps_status_t *p, const char *procName)
361{
362	int argv1idx;
363
364	/* comm does not match */
365	if (strncmp(p->comm, procName, 15) != 0)
366		return 0;
367
368	/* in Linux, if comm is 15 chars, it may be a truncated */
369	if (p->comm[14] == '\0') /* comm is not truncated - match */
370		return 1;
371
372	/* comm is truncated, but first 15 chars match.
373	 * This can be crazily_long_script_name.sh!
374	 * The telltale sign is basename(argv[1]) == procName. */
375
376	if (!p->argv0)
377		return 0;
378
379	argv1idx = strlen(p->argv0) + 1;
380	if (argv1idx >= p->argv_len)
381		return 0;
382
383	if (strcmp(bb_basename(p->argv0 + argv1idx), procName) != 0)
384		return 0;
385
386	return 1;
387}
388
389/* find_pid_by_name()
390 *
391 *  Modified by Vladimir Oleynik for use with libbb/procps.c
392 *  This finds the pid of the specified process.
393 *  Currently, it's implemented by rummaging through
394 *  the proc filesystem.
395 *
396 *  Returns a list of all matching PIDs
397 *  It is the caller's duty to free the returned pidlist.
398 */
399pid_t* find_pid_by_name(const char *procName)
400{
401	pid_t* pidList;
402	int i = 0;
403	procps_status_t* p = NULL;
404
405	pidList = xzalloc(sizeof(*pidList));
406	while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM|PSSCAN_ARGVN))) {
407		if (comm_match(p, procName)
408		/* or we require argv0 to match (essential for matching reexeced /proc/self/exe)*/
409		 || (p->argv0 && strcmp(bb_basename(p->argv0), procName) == 0)
410		/* TOOD: we can also try /proc/NUM/exe link, do we want that? */
411		) {
412			if (p->state[0] != 'Z')
413			{
414				pidList = xrealloc_vector(pidList, 2, i);
415				pidList[i++] = p->pid;
416			}
417		}
418	}
419
420	pidList[i] = 0;
421	return pidList;
422}
423
424int pids_main(char *appname)
425{
426	pid_t *pidList;
427	pid_t *pl;
428	int count = 0;
429
430	pidList = find_pid_by_name(appname);
431	for (pl = pidList; *pl; pl++) {
432		count++;
433		fprintf(stderr, "%u ", (unsigned)*pl);
434	}
435	if (count) fprintf(stderr, "\n");
436	free(pidList);
437
438	fprintf(stderr, "pid count: %d\n", count);
439
440	return count;
441}
442
443int pids(char *appname)
444{
445	pid_t *pidList;
446	pid_t *pl;
447	int count = 0;
448
449	pidList = find_pid_by_name(appname);
450	for (pl = pidList; *pl; pl++) {
451		count++;
452	}
453	free(pidList);
454
455	if (count)
456		return 1;
457	else
458		return 0;
459}
460