Imported Upstream version 3.3.12
[platform/upstream/procps-ng.git] / pidof.c
1 /*
2  * pidof.c - Utility for listing pids of running processes
3  *
4  * Copyright (C) 2013  Jaromir Capik <jcapik@redhat.com>
5  *
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.
10  *
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.
15  *
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.
19  */
20
21 #include <stdio.h>
22 #include <getopt.h>
23
24 #include "c.h"
25 #include "fileutils.h"
26 #include "nls.h"
27 #include "xalloc.h"
28 #include "proc/readproc.h"
29 #include "proc/version.h" /* procps_version */
30
31
32 #define grow_size(x)    (x = x * 5 / 4 + 1024)
33 #define safe_free(x)    if (x) { free(x); x=NULL; }
34
35
36 struct el {
37         pid_t pid;
38 };
39
40 struct el *procs = NULL;
41 static int proc_count = 0;
42
43 struct el *omitted_procs = NULL;
44 static int omit_count = 0;
45
46 static char *program = NULL;
47
48 /* switch flags */
49 static int opt_single_shot    = 0;  /* -s */
50 static int opt_scripts_too    = 0;  /* -x */
51 static int opt_rootdir_check  = 0;  /* -c */
52
53 static char *pidof_root = NULL;
54
55 static int __attribute__ ((__noreturn__)) usage(int opt)
56 {
57         int err = (opt == '?');
58         FILE *fp = err ? stderr : stdout;
59
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)"));
71
72         exit(fp == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
73 }
74
75
76 static int is_omitted (pid_t pid)
77 {
78         int i;
79
80         for (i = 0; i < omit_count; i++) {
81                 if (pid == omitted_procs[i].pid) return 1;
82         }
83
84         return 0;
85 }
86
87
88 static char *get_basename (char *filename)
89 {
90         char *pos;
91         char *result;
92
93         pos = result = filename;
94         while (*pos != '\0') {
95                 if (*(pos++) == '/') result = pos;
96         }
97
98         return result;
99 }
100
101
102 static char *pid_link (pid_t pid, const char *base_name)
103 {
104         char link [PROCPATHLEN];
105         char *result;
106         ssize_t path_alloc_size;
107         ssize_t len;
108
109         snprintf(link, sizeof(link), "/proc/%d/%s", pid, base_name);
110
111         len = path_alloc_size = 0;
112         result = NULL;
113         do {
114                 grow_size(path_alloc_size);
115                 result = xrealloc(result, path_alloc_size);
116
117                 if ((len = readlink(link, result, path_alloc_size)) < 0) {
118                         len = 0;
119                         break;
120                 }
121
122         } while (len == path_alloc_size);
123
124         result[len] = '\0';
125
126         return result;
127 }
128
129
130 static void select_procs (void)
131 {
132         PROCTAB *ptp;
133         proc_t task;
134         int match;
135         static int size = 0;
136         char *cmd_arg0, *cmd_arg0base;
137         char *cmd_arg1, *cmd_arg1base;
138         char *pos;
139         char *program_base;
140         char *root_link;
141         char *exe_link;
142         char *exe_link_base;
143
144         /* get the input base name */
145         program_base = get_basename(program);
146
147         ptp = openproc (PROC_FILLCOM | PROC_FILLSTAT);
148
149         exe_link = root_link = NULL;
150         memset(&task, 0, sizeof (task));
151         while(readproc(ptp, &task)) {
152
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);
158
159                         if (!match) {  /* root check failed */
160                                 memset (&task, 0, sizeof (task));
161                                 continue;
162                         }
163                 }
164
165                 if (!is_omitted(task.XXXID) && task.cmdline) {
166
167                         cmd_arg0 = *task.cmdline;
168
169                         /* processes starting with '-' are login shells */
170                         if (*cmd_arg0 == '-') {
171                                 cmd_arg0++;
172                         }
173
174                         /* get the argv0 base name */
175                         cmd_arg0base = get_basename(cmd_arg0);
176
177                         /* get the /proc/<pid>/exe symlink value */
178                         exe_link = pid_link(task.XXXID, "exe");
179
180                         /* get the exe_link base name */
181                         exe_link_base = get_basename(exe_link);
182
183                         match = 0;
184
185                         if (!strcmp(program, cmd_arg0base) ||
186                             !strcmp(program_base, cmd_arg0) ||
187                             !strcmp(program, cmd_arg0) ||
188
189                             !strcmp(program, exe_link_base) ||
190                             !strcmp(program, exe_link))
191                         {
192                                 match = 1;
193
194                         } else if (opt_scripts_too && *(task.cmdline+1)) {
195
196                                 pos = cmd_arg1base = cmd_arg1 = *(task.cmdline+1);
197
198                                 /* get the arg1 base name */
199                                 while (*pos != '\0') {
200                                         if (*(pos++) == '/') cmd_arg1base = pos;
201                                 }
202
203                                 /* if script, then task.cmd = argv1, otherwise task.cmd = argv0 */
204                                 if (task.cmd &&
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)))
209                                 {
210                                         match = 1;
211                                 }
212                         }
213             /* If there is a space in arg0 then process probably has
214              * setproctitle so use the cmdline
215              */
216             if (!match && strchr(cmd_arg0, ' ')) {
217                 match = (strcmp(program, task.cmd)==0);
218             }
219
220                         safe_free(exe_link);
221
222                         if (match) {
223                                 if (proc_count == size) {
224                                         grow_size(size);
225                                         procs = xrealloc(procs, size * (sizeof *procs));
226                                 }
227                                 if (procs) {
228                                         procs[proc_count++].pid = task.XXXID;
229                                 } else {
230                                         xerrx(EXIT_FAILURE, _("internal error"));
231                                 }
232                         }
233
234                 }
235
236                 memset (&task, 0, sizeof (task));
237         }
238
239         closeproc (ptp);
240 }
241
242
243 static void add_to_omit_list (char *input_arg)
244 {
245         static int omit_size = 0;
246
247         char *omit_str;
248         char *endptr;
249
250         pid_t omit_pid;
251
252         omit_str = NULL;
253         omit_str = strtok(input_arg, ",;:");
254         while (omit_str) {
255
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;
259                 } else {
260                         omit_pid = strtoul(omit_str, &endptr, 10);
261                 }
262
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));
267                         }
268                         if (omitted_procs) {
269                                 omitted_procs[omit_count++].pid = omit_pid;
270                         } else {
271                                 xerrx(EXIT_FAILURE, _("internal error"));
272                         }
273                 } else {
274                         xwarnx(_("illegal omit pid value (%s)!\n"), omit_str);
275                 }
276
277                 omit_str = strtok(NULL, ",;:");
278         }
279 }
280
281
282
283 int main (int argc, char **argv)
284 {
285         int opt;
286         signed int i;
287         int found = 0;
288         int first_pid = 1;
289
290         const char *opts = "scnxmo:?Vh";
291
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'},
298                 {NULL, 0, NULL, 0}
299         };
300
301 #ifdef HAVE_PROGRAM_INVOCATION_NAME
302         program_invocation_name = program_invocation_short_name;
303 #endif
304         setlocale (LC_ALL, "");
305         bindtextdomain (PACKAGE, LOCALEDIR);
306         textdomain (PACKAGE);
307         atexit (close_stdout);
308
309         /* process command-line options */
310         while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != -1) {
311                 switch (opt) {
312                 case 's':
313                         opt_single_shot = 1;
314                         break;
315                 case 'o':
316                         add_to_omit_list (optarg);
317                         break;
318                 case 'x':
319                         opt_scripts_too = 1;
320                         break;
321                 case 'c':
322                         if (geteuid() == 0) {
323                                 opt_rootdir_check = 1;
324                                 pidof_root = pid_link(getpid(), "root");
325                         }
326                         break;
327                 case 'V':
328                         printf (PROCPS_NG_VERSION);
329                         exit (EXIT_SUCCESS);
330                 case 'h':
331                 case '?':
332                         usage (opt);
333                         break;
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 :) */
341                         break;
342                 }
343         }
344
345         /* main loop */
346         while (argc - optind) {         /* for each program */
347
348                 program = argv[optind++];
349
350                 select_procs(); /* get the list of matching processes */
351
352                 if (proc_count) {
353
354                         found = 1;
355                         for (i = proc_count - 1; i >= 0; i--) { /* and display their PIDs */
356                                 if (first_pid) {
357                                         first_pid = 0;
358                                         printf ("%ld", (long) procs[i].pid);
359                                 } else {
360                                         printf (" %ld", (long) procs[i].pid);
361                                 }
362                                 if (opt_single_shot) break;
363                         }
364
365                         proc_count = 0;
366                 }
367         }
368
369         /* final line feed */
370         if (found) printf("\n");
371
372         /* some cleaning */
373         safe_free(procs);
374         safe_free(omitted_procs);
375         safe_free(pidof_root);
376
377         return !found;
378 }