]> granicus.if.org Git - procps-ng/commitdiff
pidof: reimplemented from scratch (replacing sysvinit pidof)
authorJaromir Capik <jcapik@redhat.com>
Fri, 4 Oct 2013 18:35:01 +0000 (20:35 +0200)
committerJaromir Capik <jcapik@redhat.com>
Thu, 10 Oct 2013 15:01:48 +0000 (17:01 +0200)
As the sysvinit becomes obsolete, some of the bundled tools
need to find a new home. The procps-ng project seems to be
the most suitable project for adopting the pidof tool.
This commit introduces a redesigned version of pidof
that satisfies the LSB requirements.
In corner cases the behaviour might differ from the former
one as the new version doesn't use any stat(2) calls.

Makefile.am
configure.ac
pidof.1 [new file with mode: 0644]
pidof.c [new file with mode: 0644]

index 3d66d60f5444feb7bd67ba74f011ce5992ecf6e0..7075e67b706332405a62de2b96a1f499ac4d2e2e 100644 (file)
@@ -52,6 +52,14 @@ EXTRA_DIST = \
        Documentation/TODO \
        sysctl.conf
 
+if BUILD_PIDOF
+usrbin_exec_PROGRAMS += pidof
+dist_man_MANS += pidof.1
+pidof_SOURCES = pidof.c $(top_srcdir)/lib/fileutils.c
+else
+  EXTRA_DIST += pidof.1
+endif
+
 if BUILD_KILL
 bin_PROGRAMS = kill
 dist_man_MANS += kill.1
index 5487bd42bc4d5fba23097fa4f29c2927979f9d83..20bc3ce056b39c1f22f3161310699abb01a1f2c5 100644 (file)
@@ -168,6 +168,12 @@ else
 fi
 
 # AC_ARG_ENABLEs
+AC_ARG_ENABLE([pidof],
+  AS_HELP_STRING([--disable-pidof], [do not build pidof]),
+  [], [enable_pidof=yes]
+)
+AM_CONDITIONAL(BUILD_PIDOF, test "x$enable_pidof" = xyes)
+
 AC_ARG_ENABLE([kill],
   AS_HELP_STRING([--disable-kill], [do not build kill]),
   [], [enable_kill=yes]
diff --git a/pidof.1 b/pidof.1
new file mode 100644 (file)
index 0000000..f2544c8
--- /dev/null
+++ b/pidof.1
@@ -0,0 +1,60 @@
+'\" -*- coding: UTF-8 -*-
+.\" Copyright (C) 1998 Miquel van Smoorenburg.
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program; if not, write to the Free Software
+.\" Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+.\"
+.TH PIDOF 1 "24 Jul 2013" "" "User Commands"
+.SH NAME
+pidof -- find the process ID of a running program.
+.SH SYNOPSIS
+.B pidof
+.RB [ \-s ]
+.RB [ \-c ]
+.RB [ \-x ]
+.RB [ \-o
+.IR omitpid[,omitpid..] ]
+.RB [ \-o
+.IR omitpid[,omitpid..].. ]
+.B program
+.RB [ program.. ]
+.SH DESCRIPTION
+.B Pidof
+finds the process id's (pids) of the named programs. It prints those
+id's on the standard output.
+.SH OPTIONS
+.IP \-s
+Single shot - this instructs the program to only return one \fIpid\fP.
+.IP \-c
+Only return process ids that are running with the same root directory.
+This option is ignored for non-root users, as they will be unable to check
+the current root directory of processes they do not own.
+.IP \-x
+Scripts too - this causes the program to also return process id's of
+shells running the named scripts.
+.IP "-o \fIomitpid\fP"
+Tells \fIpidof\fP to omit processes with that process id.
+.SH "EXIT STATUS"
+.TP
+.B 0
+At least one program was found with the requested name.
+.TP
+.B 1
+No program was found with the requested name.
+
+.SH SEE ALSO
+.BR pgrep (1),
+.BR pkill (1)
+.SH AUTHOR
+Jaromir Capik <jcapik@redhat.com>
diff --git a/pidof.c b/pidof.c
new file mode 100644 (file)
index 0000000..6cce24c
--- /dev/null
+++ b/pidof.c
@@ -0,0 +1,371 @@
+/*
+ * pidof.c - Utility for listing pids of running processes
+ *
+ * Copyright (C) 2013  Jaromir Capik <jcapik@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <getopt.h>
+
+#include "c.h"
+#include "fileutils.h"
+#include "nls.h"
+#include "xalloc.h"
+#include "proc/readproc.h"
+#include "proc/sig.h"
+#include "proc/devname.h"
+#include "proc/sysinfo.h"
+#include "proc/version.h" /* procps_version */
+
+
+#define grow_size(x)   (x = x * 5 / 4 + 1024)
+#define safe_free(x)   if (x) { free(x); x=NULL; }
+
+
+struct el {
+       pid_t pid;
+};
+
+struct el *procs = NULL;
+static int proc_count = 0;
+
+struct el *omitted_procs = NULL;
+static int omit_count = 0;
+
+static char *program = NULL;
+
+/* switch flags */
+static int opt_single_shot    = 0;  /* -s */
+static int opt_scripts_too    = 0;  /* -x */
+static int opt_rootdir_check  = 0;  /* -c */
+
+static char *pidof_root = NULL;
+
+static int __attribute__ ((__noreturn__)) usage(int opt)
+{
+       int err = (opt == '?');
+       FILE *fp = err ? stderr : stdout;
+
+       fputs(USAGE_HEADER, fp);
+       fprintf(fp, _(" %s [options] [program [...]]\n"), program_invocation_short_name);
+       fputs(USAGE_OPTIONS, fp);
+       fputs(_(" -s, --single-shot         return one PID only\n"
+               " -c, --check-root          omit processes with different root\n"
+               " -x                        scripts too\n"
+               " -o, --omit-pid <PID,...>  omit processes with PID\n"), fp);
+       fputs(USAGE_SEPARATOR, fp);
+       fputs(USAGE_HELP, fp);
+       fputs(USAGE_VERSION, fp);
+       fprintf(fp, USAGE_MAN_TAIL("pidof(1)"));
+
+       exit(fp == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+
+static int is_omitted (pid_t pid)
+{
+       int i;
+
+       for (i = 0; i < omit_count; i++) {
+               if (pid == omitted_procs[i].pid) return 1;
+       }
+
+       return 0;
+}
+
+
+static char *get_basename (char *filename)
+{
+       char *pos;
+       char *result;
+
+       pos = result = filename;
+       while (*pos != '\0') {
+               if (*(pos++) == '/') result = pos;
+       }
+
+       return result;
+}
+
+
+static char *pid_link (pid_t pid, const char *base_name)
+{
+       char link [PROCPATHLEN];
+       char *result;
+       int path_alloc_size;
+       int len;
+
+       snprintf(link, sizeof(link), "/proc/%d/%s", pid, base_name);
+
+       len = path_alloc_size = 0;
+       result = NULL;
+       do {
+               if (len == path_alloc_size) {
+                       grow_size (path_alloc_size);
+                       result = (char *) xrealloc (result, path_alloc_size);
+               }
+
+               if ((len = readlink(link, result, path_alloc_size - 1)) < 0) {
+                       len = 0;
+                       break;
+               }
+
+       } while (len == path_alloc_size);
+
+       result[len] = '\0';
+
+       return result;
+}
+
+
+static void select_procs (void)
+{
+       PROCTAB *ptp;
+       proc_t task;
+       int match, root_check_ok;
+       static int size = 0;
+       char *cmd_arg0, *cmd_arg0base;
+       char *cmd_arg1, *cmd_arg1base;
+       char *pos;
+       char *program_base;
+       char *root_link;
+       char *exe_link;
+       char *exe_link_base;
+
+       /* get the input base name */
+       program_base = get_basename(program);
+
+       ptp = openproc (PROC_FILLCOM | PROC_FILLSTAT);
+
+       exe_link = root_link = NULL;
+       memset(&task, 0, sizeof (task));
+       while(readproc(ptp, &task)) {
+
+               if (opt_rootdir_check) {
+                       /* get the /proc/<pid>/root symlink value */
+                       root_link = pid_link(task.XXXID, "root");
+                       match = !strcmp(pidof_root, root_link);
+                       safe_free(root_link);
+
+                       if (!match) {  /* root check failed */
+                               memset (&task, 0, sizeof (task));
+                               continue;
+                       }
+               }
+
+               if (!is_omitted(task.XXXID) && task.cmdline) {
+
+                       cmd_arg0 = *task.cmdline;
+
+                       /* processes starting with '-' are login shells */
+                       if (*cmd_arg0 == '-') {
+                               cmd_arg0++;
+                       }
+
+                       /* get the argv0 base name */
+                       cmd_arg0base = get_basename(cmd_arg0);
+
+                       /* get the /proc/<pid>/exe symlink value */
+                       exe_link = pid_link(task.XXXID, "exe");
+
+                       /* get the exe_link base name */
+                       exe_link_base = get_basename(exe_link);
+
+                       match = 0;
+
+                       if (!strcmp(program, cmd_arg0base) ||
+                           !strcmp(program_base, cmd_arg0) ||
+                           !strcmp(program, cmd_arg0) ||
+
+                           !strcmp(program, exe_link_base) ||
+                           !strcmp(program, exe_link))
+                       {
+                               match = 1;
+
+                       } else if (opt_scripts_too && *(task.cmdline+1)) {
+
+                               pos = cmd_arg1base = cmd_arg1 = *(task.cmdline+1);
+
+                               /* get the arg1 base name */
+                               while (*pos != '\0') {
+                                       if (*(pos++) == '/') cmd_arg1base = pos;
+                               }
+
+                               /* if script, then task.cmd = argv1, otherwise task.cmd = argv0 */
+                               if (task.cmd &&
+                                   !strncmp(task.cmd, cmd_arg1base, strlen(task.cmd)) &&
+                                   (!strcmp(program, cmd_arg1base) ||
+                                   !strcmp(program_base, cmd_arg1) ||
+                                   !strcmp(program, cmd_arg1)))
+                               {
+                                       match = 1;
+                               }
+                       }
+
+                       safe_free(exe_link);
+
+                       if (match) {
+                               if (proc_count == size) {
+                                       grow_size(size);
+                                       procs = xrealloc(procs, size * (sizeof *procs));
+                               }
+                               if (procs) {
+                                       procs[proc_count++].pid = task.XXXID;
+                               } else {
+                                       xerrx(EXIT_FAILURE, _("internal error"));
+                               }
+                       }
+
+               }
+
+               memset (&task, 0, sizeof (task));
+       }
+
+       closeproc (ptp);
+}
+
+
+static void add_to_omit_list (char *input_arg)
+{
+       static int omit_size = 0;
+
+       char *omit_str;
+       char *endptr;
+
+       pid_t omit_pid;
+
+       omit_str = NULL;
+       omit_str = strtok(input_arg, ",");
+       while (omit_str) {
+
+               omit_pid = strtoul(omit_str, &endptr, 10);
+
+               if (*endptr == '\0') {
+                       if (omit_count == omit_size) {
+                               grow_size(omit_size);
+                               omitted_procs = xrealloc(omitted_procs, omit_size * sizeof(*omitted_procs));
+                       }
+                       if (omitted_procs) {
+                               omitted_procs[omit_count++].pid = omit_pid;
+                       } else {
+                               xerrx(EXIT_FAILURE, _("internal error"));
+                       }
+               } else {
+                       xwarnx(_("illegal omit pid value (%s)!\n"), omit_str);
+               }
+
+               omit_str = strtok(NULL, ",");
+       }
+}
+
+
+
+int main (int argc, char **argv)
+{
+       int opt;
+       signed int i;
+       int found = 0;
+       int first_pid = 1;
+
+       const char *opts = "scnxmo:?Vh";
+
+       static const struct option longopts[] = {
+               {"single-shot", no_argument, NULL, 's'},
+               {"omit-pid", required_argument, NULL, 'o'},
+               {"help", no_argument, NULL, 'h'},
+               {"version", no_argument, NULL, 'V'},
+               {NULL, 0, NULL, 0}
+       };
+
+#ifdef HAVE_PROGRAM_INVOCATION_NAME
+       program_invocation_name = program_invocation_short_name;
+#endif
+       setlocale (LC_ALL, "");
+       bindtextdomain (PACKAGE, LOCALEDIR);
+       textdomain (PACKAGE);
+       atexit (close_stdout);
+
+       /* process command-line options */
+       while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != -1) {
+               switch (opt) {
+               case 's':
+                       opt_single_shot = 1;
+                       break;
+               case 'o':
+                       add_to_omit_list (optarg);
+                       break;
+               case 'x':
+                       opt_scripts_too = 1;
+                       break;
+               case 'c':
+                       if (geteuid() == 0) {
+                               opt_rootdir_check = 1;
+                               pidof_root = pid_link(getpid(), "root");
+                       }
+                       break;
+               case 'V':
+                       printf (PROCPS_NG_VERSION);
+                       exit (EXIT_SUCCESS);
+               case 'h':
+               case '?':
+                       usage (opt);
+                       break;
+               /* compatibility-only switches */
+               case 'n': /* avoiding stat(2) on NFS volumes doesn't make any sense anymore ... */
+                         /* ... as this reworked solution does not use stat(2) at all */
+               case 'm': /* omitting relatives with argv[0] & argv[1] matching the argv[0] & argv[1] ...*/
+                         /* ... of explicitly omitted PIDs is too 'expensive' and as we don't know */
+                         /* ... wheter it is still needed, we won't re-implement it unless ... */
+                         /* ... somebody gives us a good reason to do so :) */
+                       break;
+               }
+       }
+
+       /* main loop */
+       while (argc - optind) {         /* for each program */
+
+               program = argv[optind++];
+
+               select_procs(); /* get the list of matching processes */
+
+               if (proc_count) {
+
+                       found = 1;
+                       for (i = proc_count - 1; i >= 0; i--) { /* and display their PIDs */
+                               if (first_pid) {
+                                       first_pid = 0;
+                                       printf ("%ld", procs[i].pid);
+                               } else {
+                                       printf (" %ld", procs[i].pid);
+                               }
+                               if (opt_single_shot) break;
+                       }
+
+                       proc_count = 0;
+               }
+       }
+
+       /* final line feed */
+       if (found) printf("\n");
+
+       /* some cleaning */
+       safe_free(procs);
+       safe_free(omitted_procs);
+       safe_free(pidof_root);
+
+       return !found;
+}