2 * pidof.c - Utility for listing pids of running processes
4 * Copyright (C) 2013 Jaromir Capik <jcapik@redhat.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include "fileutils.h"
28 #include "proc/readproc.h"
29 #include "proc/version.h" /* procps_version */
32 #define grow_size(x) (x = x * 5 / 4 + 1024)
33 #define safe_free(x) if (x) { free(x); x=NULL; }
40 struct el *procs = NULL;
41 static int proc_count = 0;
43 struct el *omitted_procs = NULL;
44 static int omit_count = 0;
46 static char *program = NULL;
49 static int opt_single_shot = 0; /* -s */
50 static int opt_scripts_too = 0; /* -x */
51 static int opt_rootdir_check = 0; /* -c */
53 static char *pidof_root = NULL;
55 static int __attribute__ ((__noreturn__)) usage(int opt)
57 int err = (opt == '?');
58 FILE *fp = err ? stderr : stdout;
60 fputs(USAGE_HEADER, fp);
61 fprintf(fp, _(" %s [options] [program [...]]\n"), program_invocation_short_name);
62 fputs(USAGE_OPTIONS, fp);
63 fputs(_(" -s, --single-shot return one PID only\n"), fp);
64 fputs(_(" -c, --check-root omit processes with different root\n"), fp);
65 fputs(_(" -x also find shells running the named scripts\n"), fp);
66 fputs(_(" -o, --omit-pid <PID,...> omit processes with PID\n"), fp);
67 fputs(USAGE_SEPARATOR, fp);
68 fputs(USAGE_HELP, fp);
69 fputs(USAGE_VERSION, fp);
70 fprintf(fp, USAGE_MAN_TAIL("pidof(1)"));
72 exit(fp == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
76 static int is_omitted (pid_t pid)
80 for (i = 0; i < omit_count; i++) {
81 if (pid == omitted_procs[i].pid) return 1;
88 static char *get_basename (char *filename)
93 pos = result = filename;
94 while (*pos != '\0') {
95 if (*(pos++) == '/') result = pos;
102 static char *pid_link (pid_t pid, const char *base_name)
104 char link [PROCPATHLEN];
106 ssize_t path_alloc_size;
109 snprintf(link, sizeof(link), "/proc/%d/%s", pid, base_name);
111 len = path_alloc_size = 0;
114 grow_size(path_alloc_size);
115 result = xrealloc(result, path_alloc_size);
117 if ((len = readlink(link, result, path_alloc_size)) < 0) {
122 } while (len == path_alloc_size);
130 static void select_procs (void)
136 char *cmd_arg0, *cmd_arg0base;
137 char *cmd_arg1, *cmd_arg1base;
144 /* get the input base name */
145 program_base = get_basename(program);
147 ptp = openproc (PROC_FILLCOM | PROC_FILLSTAT);
149 exe_link = root_link = NULL;
150 memset(&task, 0, sizeof (task));
151 while(readproc(ptp, &task)) {
153 if (opt_rootdir_check) {
154 /* get the /proc/<pid>/root symlink value */
155 root_link = pid_link(task.XXXID, "root");
156 match = !strcmp(pidof_root, root_link);
157 safe_free(root_link);
159 if (!match) { /* root check failed */
160 memset (&task, 0, sizeof (task));
165 if (!is_omitted(task.XXXID) && task.cmdline) {
167 cmd_arg0 = *task.cmdline;
169 /* processes starting with '-' are login shells */
170 if (*cmd_arg0 == '-') {
174 /* get the argv0 base name */
175 cmd_arg0base = get_basename(cmd_arg0);
177 /* get the /proc/<pid>/exe symlink value */
178 exe_link = pid_link(task.XXXID, "exe");
180 /* get the exe_link base name */
181 exe_link_base = get_basename(exe_link);
185 if (!strcmp(program, cmd_arg0base) ||
186 !strcmp(program_base, cmd_arg0) ||
187 !strcmp(program, cmd_arg0) ||
189 !strcmp(program, exe_link_base) ||
190 !strcmp(program, exe_link))
194 } else if (opt_scripts_too && *(task.cmdline+1)) {
196 pos = cmd_arg1base = cmd_arg1 = *(task.cmdline+1);
198 /* get the arg1 base name */
199 while (*pos != '\0') {
200 if (*(pos++) == '/') cmd_arg1base = pos;
203 /* if script, then task.cmd = argv1, otherwise task.cmd = argv0 */
205 !strncmp(task.cmd, cmd_arg1base, strlen(task.cmd)) &&
206 (!strcmp(program, cmd_arg1base) ||
207 !strcmp(program_base, cmd_arg1) ||
208 !strcmp(program, cmd_arg1)))
213 /* If there is a space in arg0 then process probably has
214 * setproctitle so use the cmdline
216 if (!match && strchr(cmd_arg0, ' ')) {
217 match = (strcmp(program, task.cmd)==0);
223 if (proc_count == size) {
225 procs = xrealloc(procs, size * (sizeof *procs));
228 procs[proc_count++].pid = task.XXXID;
230 xerrx(EXIT_FAILURE, _("internal error"));
236 memset (&task, 0, sizeof (task));
243 static void add_to_omit_list (char *input_arg)
245 static int omit_size = 0;
253 omit_str = strtok(input_arg, ",;:");
256 if (!strcmp(omit_str,"%PPID")) { /* keeping this %PPID garbage for backward compatibility only */
257 omit_pid = getppid(); /* ... as it can be replaced with $$ in common shells */
258 endptr = omit_str + sizeof("%PPID") - 1;
260 omit_pid = strtoul(omit_str, &endptr, 10);
263 if (*endptr == '\0') {
264 if (omit_count == omit_size) {
265 grow_size(omit_size);
266 omitted_procs = xrealloc(omitted_procs, omit_size * sizeof(*omitted_procs));
269 omitted_procs[omit_count++].pid = omit_pid;
271 xerrx(EXIT_FAILURE, _("internal error"));
274 xwarnx(_("illegal omit pid value (%s)!\n"), omit_str);
277 omit_str = strtok(NULL, ",;:");
283 int main (int argc, char **argv)
290 const char *opts = "scnxmo:?Vh";
292 static const struct option longopts[] = {
293 {"check-root", no_argument, NULL, 'c'},
294 {"single-shot", no_argument, NULL, 's'},
295 {"omit-pid", required_argument, NULL, 'o'},
296 {"help", no_argument, NULL, 'h'},
297 {"version", no_argument, NULL, 'V'},
301 #ifdef HAVE_PROGRAM_INVOCATION_NAME
302 program_invocation_name = program_invocation_short_name;
304 setlocale (LC_ALL, "");
305 bindtextdomain (PACKAGE, LOCALEDIR);
306 textdomain (PACKAGE);
307 atexit (close_stdout);
309 /* process command-line options */
310 while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != -1) {
316 add_to_omit_list (optarg);
322 if (geteuid() == 0) {
323 opt_rootdir_check = 1;
324 pidof_root = pid_link(getpid(), "root");
328 printf (PROCPS_NG_VERSION);
334 /* compatibility-only switches */
335 case 'n': /* avoiding stat(2) on NFS volumes doesn't make any sense anymore ... */
336 /* ... as this reworked solution does not use stat(2) at all */
337 case 'm': /* omitting relatives with argv[0] & argv[1] matching the argv[0] & argv[1] ...*/
338 /* ... of explicitly omitted PIDs is too 'expensive' and as we don't know */
339 /* ... wheter it is still needed, we won't re-implement it unless ... */
340 /* ... somebody gives us a good reason to do so :) */
346 while (argc - optind) { /* for each program */
348 program = argv[optind++];
350 select_procs(); /* get the list of matching processes */
355 for (i = proc_count - 1; i >= 0; i--) { /* and display their PIDs */
358 printf ("%ld", (long) procs[i].pid);
360 printf (" %ld", (long) procs[i].pid);
362 if (opt_single_shot) break;
369 /* final line feed */
370 if (found) printf("\n");
374 safe_free(omitted_procs);
375 safe_free(pidof_root);