From afe862ebe4be1b02cc5378c68965b7ee2cb9e3d7 Mon Sep 17 00:00:00 2001 From: Jaromir Capik Date: Fri, 4 Oct 2013 20:35:01 +0200 Subject: [PATCH] pidof: reimplemented from scratch (replacing sysvinit pidof) 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 | 8 ++ configure.ac | 6 + pidof.1 | 60 +++++++++ pidof.c | 371 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 445 insertions(+) create mode 100644 pidof.1 create mode 100644 pidof.c diff --git a/Makefile.am b/Makefile.am index 3d66d60f..7075e67b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/configure.ac b/configure.ac index 5487bd42..20bc3ce0 100644 --- a/configure.ac +++ b/configure.ac @@ -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 index 00000000..f2544c8b --- /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 diff --git a/pidof.c b/pidof.c new file mode 100644 index 00000000..6cce24c7 --- /dev/null +++ b/pidof.c @@ -0,0 +1,371 @@ +/* + * pidof.c - Utility for listing pids of running processes + * + * Copyright (C) 2013 Jaromir Capik + * + * 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 +#include + +#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 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//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//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; +}