PageRenderTime 53ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/pidof.c

https://gitlab.com/stoeckmann/procps
C | 386 lines | 267 code | 79 blank | 40 comment | 57 complexity | 516ab0e6201a07ab667602491f7c9b73 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0
  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. #include <stdio.h>
  21. #include <getopt.h>
  22. #include <limits.h>
  23. #include "c.h"
  24. #include "fileutils.h"
  25. #include "nls.h"
  26. #include "xalloc.h"
  27. #include "proc/readproc.h"
  28. #include "proc/version.h" /* procps_version */
  29. #define grow_size(x) do { \
  30. if ((x) < 0 || (size_t)(x) >= INT_MAX / 5 / sizeof(struct el)) \
  31. xerrx(EXIT_FAILURE, _("integer overflow")); \
  32. (x) = (x) * 5 / 4 + 1024; \
  33. } while (0)
  34. #define safe_free(x) if (x) { free(x); x=NULL; }
  35. struct el {
  36. pid_t pid;
  37. };
  38. struct el *procs = NULL;
  39. static int proc_count = 0;
  40. struct el *omitted_procs = NULL;
  41. static int omit_count = 0;
  42. static char *program = NULL;
  43. /* switch flags */
  44. static int opt_single_shot = 0; /* -s */
  45. static int opt_scripts_too = 0; /* -x */
  46. static int opt_rootdir_check = 0; /* -c */
  47. static char *pidof_root = NULL;
  48. static int __attribute__ ((__noreturn__)) usage(int opt)
  49. {
  50. int err = (opt == '?');
  51. FILE *fp = err ? stderr : stdout;
  52. fputs(USAGE_HEADER, fp);
  53. fprintf(fp, _(" %s [options] [program [...]]\n"), program_invocation_short_name);
  54. fputs(USAGE_OPTIONS, fp);
  55. fputs(_(" -s, --single-shot return one PID only\n"), fp);
  56. fputs(_(" -c, --check-root omit processes with different root\n"), fp);
  57. fputs(_(" -x also find shells running the named scripts\n"), fp);
  58. fputs(_(" -o, --omit-pid <PID,...> omit processes with PID\n"), fp);
  59. fputs(_(" -S, --separator SEP use SEP as separator put between PIDs"), fp);
  60. fputs(USAGE_SEPARATOR, fp);
  61. fputs(USAGE_HELP, fp);
  62. fputs(USAGE_VERSION, fp);
  63. fprintf(fp, USAGE_MAN_TAIL("pidof(1)"));
  64. exit(fp == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
  65. }
  66. static int is_omitted (pid_t pid)
  67. {
  68. int i;
  69. for (i = 0; i < omit_count; i++) {
  70. if (pid == omitted_procs[i].pid) return 1;
  71. }
  72. return 0;
  73. }
  74. static char *get_basename (char *filename)
  75. {
  76. char *pos;
  77. char *result;
  78. pos = result = filename;
  79. while (*pos != '\0') {
  80. if (*(pos++) == '/') result = pos;
  81. }
  82. return result;
  83. }
  84. static char *pid_link (pid_t pid, const char *base_name)
  85. {
  86. char link [PROCPATHLEN];
  87. char *result;
  88. ssize_t path_alloc_size;
  89. ssize_t len;
  90. snprintf(link, sizeof(link), "/proc/%d/%s", pid, base_name);
  91. len = path_alloc_size = 0;
  92. result = NULL;
  93. do {
  94. grow_size(path_alloc_size);
  95. result = xrealloc(result, path_alloc_size);
  96. if ((len = readlink(link, result, path_alloc_size)) < 0) {
  97. len = 0;
  98. break;
  99. }
  100. } while (len == path_alloc_size);
  101. result[len] = '\0';
  102. return result;
  103. }
  104. static void select_procs (void)
  105. {
  106. PROCTAB *ptp;
  107. proc_t task;
  108. int match;
  109. static int size = 0;
  110. char *cmd_arg0, *cmd_arg0base;
  111. char *cmd_arg1, *cmd_arg1base;
  112. char *program_base;
  113. char *root_link;
  114. char *exe_link;
  115. char *exe_link_base;
  116. /* get the input base name */
  117. program_base = get_basename(program);
  118. ptp = openproc (PROC_FILLCOM | PROC_FILLSTAT);
  119. exe_link = root_link = NULL;
  120. memset(&task, 0, sizeof (task));
  121. while(readproc(ptp, &task)) {
  122. if (opt_rootdir_check) {
  123. /* get the /proc/<pid>/root symlink value */
  124. root_link = pid_link(task.XXXID, "root");
  125. match = !strcmp(pidof_root, root_link);
  126. safe_free(root_link);
  127. if (!match) { /* root check failed */
  128. continue;
  129. }
  130. }
  131. if (!is_omitted(task.XXXID) && task.cmdline && *task.cmdline) {
  132. cmd_arg0 = *task.cmdline;
  133. /* processes starting with '-' are login shells */
  134. if (*cmd_arg0 == '-') {
  135. cmd_arg0++;
  136. }
  137. /* get the argv0 base name */
  138. cmd_arg0base = get_basename(cmd_arg0);
  139. /* get the /proc/<pid>/exe symlink value */
  140. exe_link = pid_link(task.XXXID, "exe");
  141. /* get the exe_link base name */
  142. exe_link_base = get_basename(exe_link);
  143. match = 0;
  144. if (!strcmp(program, cmd_arg0base) ||
  145. !strcmp(program_base, cmd_arg0) ||
  146. !strcmp(program, cmd_arg0) ||
  147. !strcmp(program, exe_link_base) ||
  148. !strcmp(program, exe_link))
  149. {
  150. match = 1;
  151. } else if (opt_scripts_too && *(task.cmdline+1)) {
  152. cmd_arg1 = *(task.cmdline+1);
  153. /* get the arg1 base name */
  154. cmd_arg1base = get_basename(cmd_arg1);
  155. /* if script, then task.cmd = argv1, otherwise task.cmd = argv0 */
  156. if (task.cmd &&
  157. !strncmp(task.cmd, cmd_arg1base, strlen(task.cmd)) &&
  158. (!strcmp(program, cmd_arg1base) ||
  159. !strcmp(program_base, cmd_arg1) ||
  160. !strcmp(program, cmd_arg1)))
  161. {
  162. match = 1;
  163. }
  164. }
  165. /* If there is a space in arg0 then process probably has
  166. * setproctitle so use the cmdline
  167. */
  168. if (!match && strchr(cmd_arg0, ' ')) {
  169. match = (strcmp(program, task.cmd)==0);
  170. }
  171. safe_free(exe_link);
  172. if (match) {
  173. if (proc_count == size) {
  174. grow_size(size);
  175. procs = xrealloc(procs, size * (sizeof *procs));
  176. }
  177. if (procs) {
  178. procs[proc_count++].pid = task.XXXID;
  179. } else {
  180. xerrx(EXIT_FAILURE, _("internal error"));
  181. }
  182. }
  183. }
  184. }
  185. closeproc (ptp);
  186. }
  187. static void add_to_omit_list (char *input_arg)
  188. {
  189. static int omit_size = 0;
  190. char *omit_str;
  191. char *endptr;
  192. pid_t omit_pid;
  193. omit_str = NULL;
  194. omit_str = strtok(input_arg, ",;:");
  195. while (omit_str) {
  196. if (!strcmp(omit_str,"%PPID")) { /* keeping this %PPID garbage for backward compatibility only */
  197. omit_pid = getppid(); /* ... as it can be replaced with $$ in common shells */
  198. endptr = omit_str + sizeof("%PPID") - 1;
  199. } else {
  200. omit_pid = strtoul(omit_str, &endptr, 10);
  201. }
  202. if (*endptr == '\0') {
  203. if (omit_count == omit_size) {
  204. grow_size(omit_size);
  205. omitted_procs = xrealloc(omitted_procs, omit_size * sizeof(*omitted_procs));
  206. }
  207. if (omitted_procs) {
  208. omitted_procs[omit_count++].pid = omit_pid;
  209. } else {
  210. xerrx(EXIT_FAILURE, _("internal error"));
  211. }
  212. } else {
  213. xwarnx(_("illegal omit pid value (%s)!\n"), omit_str);
  214. }
  215. omit_str = strtok(NULL, ",;:");
  216. }
  217. }
  218. int main (int argc, char **argv)
  219. {
  220. int opt;
  221. signed int i;
  222. int found = 0;
  223. int first_pid = 1;
  224. const char *separator = " ";
  225. const char *opts = "scdnxmo:S:?Vh";
  226. static const struct option longopts[] = {
  227. {"check-root", no_argument, NULL, 'c'},
  228. {"single-shot", no_argument, NULL, 's'},
  229. {"omit-pid", required_argument, NULL, 'o'},
  230. {"separator", required_argument, NULL, 'S'},
  231. {"help", no_argument, NULL, 'h'},
  232. {"version", no_argument, NULL, 'V'},
  233. {NULL, 0, NULL, 0}
  234. };
  235. #ifdef HAVE_PROGRAM_INVOCATION_NAME
  236. program_invocation_name = program_invocation_short_name;
  237. #endif
  238. setlocale (LC_ALL, "");
  239. bindtextdomain (PACKAGE, LOCALEDIR);
  240. textdomain (PACKAGE);
  241. atexit (close_stdout);
  242. /* process command-line options */
  243. while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != -1) {
  244. switch (opt) {
  245. case 's':
  246. opt_single_shot = 1;
  247. break;
  248. case 'o':
  249. add_to_omit_list (optarg);
  250. break;
  251. case 'x':
  252. opt_scripts_too = 1;
  253. break;
  254. case 'c':
  255. if (geteuid() == 0) {
  256. opt_rootdir_check = 1;
  257. safe_free(pidof_root);
  258. pidof_root = pid_link(getpid(), "root");
  259. }
  260. break;
  261. case 'd': /* sysv pidof uses this for S */
  262. case 'S':
  263. separator = optarg;
  264. break;
  265. case 'V':
  266. printf (PROCPS_NG_VERSION);
  267. exit (EXIT_SUCCESS);
  268. case 'h':
  269. case '?':
  270. usage (opt);
  271. break;
  272. /* compatibility-only switches */
  273. case 'n': /* avoiding stat(2) on NFS volumes doesn't make any sense anymore ... */
  274. /* ... as this reworked solution does not use stat(2) at all */
  275. case 'm': /* omitting relatives with argv[0] & argv[1] matching the argv[0] & argv[1] ...*/
  276. /* ... of explicitly omitted PIDs is too 'expensive' and as we don't know */
  277. /* ... wheter it is still needed, we won't re-implement it unless ... */
  278. /* ... somebody gives us a good reason to do so :) */
  279. break;
  280. }
  281. }
  282. /* main loop */
  283. while (argc - optind) { /* for each program */
  284. program = argv[optind++];
  285. select_procs(); /* get the list of matching processes */
  286. if (proc_count) {
  287. found = 1;
  288. for (i = proc_count - 1; i >= 0; i--) { /* and display their PIDs */
  289. if (first_pid) {
  290. first_pid = 0;
  291. printf ("%ld", (long) procs[i].pid);
  292. } else {
  293. printf ("%s%ld", separator, (long) procs[i].pid);
  294. }
  295. if (opt_single_shot) break;
  296. }
  297. proc_count = 0;
  298. }
  299. }
  300. /* final line feed */
  301. if (found) printf("\n");
  302. /* some cleaning */
  303. safe_free(procs);
  304. safe_free(omitted_procs);
  305. safe_free(pidof_root);
  306. return !found;
  307. }