summaryrefslogtreecommitdiff
path: root/src/librc/librc-daemon.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/librc/librc-daemon.c')
-rw-r--r--src/librc/librc-daemon.c574
1 files changed, 574 insertions, 0 deletions
diff --git a/src/librc/librc-daemon.c b/src/librc/librc-daemon.c
new file mode 100644
index 0000000..fe9509d
--- /dev/null
+++ b/src/librc/librc-daemon.c
@@ -0,0 +1,574 @@
+/*
+ librc-daemon
+ Finds PID for given daemon criteria
+ */
+
+/*
+ * Copyright 2007 Roy Marples
+ * All rights reserved
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "librc.h"
+
+#if defined(__linux__)
+static bool pid_is_cmd (pid_t pid, const char *cmd)
+{
+ char buffer[32];
+ FILE *fp;
+ int c;
+
+ snprintf(buffer, sizeof (buffer), "/proc/%d/stat", pid);
+ if ((fp = fopen (buffer, "r")) == NULL)
+ return (false);
+
+ while ((c = getc (fp)) != EOF && c != '(')
+ ;
+
+ if (c != '(') {
+ fclose(fp);
+ return (false);
+ }
+
+ while ((c = getc (fp)) != EOF && c == *cmd)
+ cmd++;
+
+ fclose (fp);
+
+ return ((c == ')' && *cmd == '\0') ? true : false);
+}
+
+static bool pid_is_exec (pid_t pid, const char *exec)
+{
+ char cmdline[32];
+ char buffer[PATH_MAX];
+ char *p;
+ int fd = -1;
+ int r;
+
+ snprintf (cmdline, sizeof (cmdline), "/proc/%u/exe", pid);
+ memset (buffer, 0, sizeof (buffer));
+ if (readlink (cmdline, buffer, sizeof (buffer)) != -1) {
+ if (strcmp (exec, buffer) == 0)
+ return (true);
+
+ /* We should cater for deleted binaries too */
+ if (strlen (buffer) > 10) {
+ p = buffer + (strlen (buffer) - 10);
+ if (strcmp (p, " (deleted)") == 0) {
+ *p = 0;
+ if (strcmp (buffer, exec) == 0)
+ return (true);
+ }
+ }
+ }
+
+ snprintf (cmdline, sizeof (cmdline), "/proc/%u/cmdline", pid);
+ if ((fd = open (cmdline, O_RDONLY)) < 0)
+ return (false);
+
+ r = read(fd, buffer, sizeof (buffer));
+ close (fd);
+
+ if (r == -1)
+ return 0;
+
+ buffer[r] = 0;
+ return (strcmp (exec, buffer) == 0 ? true : false);
+}
+
+pid_t *rc_find_pids (const char *exec, const char *cmd,
+ uid_t uid, pid_t pid)
+{
+ DIR *procdir;
+ struct dirent *entry;
+ int npids = 0;
+ pid_t p;
+ pid_t *pids = NULL;
+ pid_t *tmp = NULL;
+ char buffer[PATH_MAX];
+ struct stat sb;
+ pid_t runscript_pid = 0;
+ char *pp;
+
+ if ((procdir = opendir ("/proc")) == NULL)
+ return (NULL);
+
+ /*
+ We never match RC_RUNSCRIPT_PID if present so we avoid the below
+ scenario
+
+ /etc/init.d/ntpd stop does
+ start-stop-daemon --stop --name ntpd
+ catching /etc/init.d/ntpd stop
+
+ nasty
+ */
+
+ if ((pp = getenv ("RC_RUNSCRIPT_PID"))) {
+ if (sscanf (pp, "%d", &runscript_pid) != 1)
+ runscript_pid = 0;
+ }
+
+ while ((entry = readdir (procdir)) != NULL) {
+ if (sscanf (entry->d_name, "%d", &p) != 1)
+ continue;
+
+ if (runscript_pid != 0 && runscript_pid == p)
+ continue;
+
+ if (pid != 0 && pid != p)
+ continue;
+
+ if (uid) {
+ snprintf (buffer, sizeof (buffer), "/proc/%d", p);
+ if (stat (buffer, &sb) != 0 || sb.st_uid != uid)
+ continue;
+ }
+
+ if (cmd && ! pid_is_cmd (p, cmd))
+ continue;
+
+ if (exec && ! cmd && ! pid_is_exec (p, exec))
+ continue;
+
+ tmp = realloc (pids, sizeof (pid_t) * (npids + 2));
+ if (! tmp) {
+ free (pids);
+ closedir (procdir);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ pids = tmp;
+
+ pids[npids] = p;
+ pids[npids + 1] = 0;
+ npids++;
+ }
+ closedir (procdir);
+
+ return (pids);
+}
+librc_hidden_def(rc_find_pids)
+
+#elif BSD
+
+# if defined(__DragonFly__) || defined(__FreeBSD__)
+# ifndef KERN_PROC_PROC
+# define KERN_PROC_PROC KERN_PROC_ALL
+# endif
+# define _KINFO_PROC kinfo_proc
+# define _KVM_GETARGV kvm_getargv
+# define _GET_KINFO_UID(kp) (kp.ki_ruid)
+# define _GET_KINFO_COMM(kp) (kp.ki_comm)
+# define _GET_KINFO_PID(kp) (kp.ki_pid)
+# else
+# define _KVM_GETPROC2
+# define _KINFO_PROC kinfo_proc2
+# define _KVM_GETARGV kvm_getargv2
+# define _GET_KINFO_UID(kp) (kp.p_ruid)
+# define _GET_KINFO_COMM(kp) (kp.p_comm)
+# define _GET_KINFO_PID(kp) (kp.p_pid)
+# endif
+
+pid_t *rc_find_pids (const char *exec, const char *cmd,
+ uid_t uid, pid_t pid)
+{
+ static kvm_t *kd = NULL;
+ char errbuf[_POSIX2_LINE_MAX];
+ struct _KINFO_PROC *kp;
+ int i;
+ int processes = 0;
+ int argc = 0;
+ char **argv;
+ pid_t *pids = NULL;
+ pid_t *tmp;
+ int npids = 0;
+
+ if ((kd = kvm_openfiles (NULL, NULL, NULL, O_RDONLY, errbuf)) == NULL) {
+ fprintf (stderr, "kvm_open: %s", errbuf);
+ return (NULL);
+ }
+
+#ifdef _KVM_GETPROC2
+ kp = kvm_getproc2 (kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2),
+ &processes);
+#else
+ kp = kvm_getprocs (kd, KERN_PROC_PROC, 0, &processes);
+#endif
+ for (i = 0; i < processes; i++) {
+ pid_t p = _GET_KINFO_PID (kp[i]);
+ if (pid != 0 && pid != p)
+ continue;
+
+ if (uid != 0 && uid != _GET_KINFO_UID (kp[i]))
+ continue;
+
+ if (cmd) {
+ if (! _GET_KINFO_COMM (kp[i]) ||
+ strcmp (cmd, _GET_KINFO_COMM (kp[i])) != 0)
+ continue;
+ }
+
+ if (exec && ! cmd) {
+ if ((argv = _KVM_GETARGV (kd, &kp[i], argc)) == NULL || ! *argv)
+ continue;
+
+ if (strcmp (*argv, exec) != 0)
+ continue;
+ }
+
+ tmp = realloc (pids, sizeof (pid_t) * (npids + 2));
+ if (! tmp) {
+ free (pids);
+ kvm_close (kd);
+ errno = ENOMEM;
+ return (NULL);
+ }
+ pids = tmp;
+
+ pids[npids] = p;
+ pids[npids + 1] = 0;
+ npids++;
+ }
+ kvm_close (kd);
+
+ return (pids);
+}
+librc_hidden_def(rc_find_pids)
+
+#else
+# error "Platform not supported!"
+#endif
+
+static bool _match_daemon (const char *path, const char *file,
+ const char *mexec, const char *mname,
+ const char *mpidfile)
+{
+ char *buffer;
+ char *ffile = rc_strcatpaths (path, file, (char *) NULL);
+ FILE *fp;
+ int lc = 0;
+ int m = 0;
+
+ if ((fp = fopen (ffile, "r")) == NULL) {
+ free (ffile);
+ return (false);
+ }
+
+ if (! mname)
+ m += 10;
+ if (! mpidfile)
+ m += 100;
+
+ buffer = xmalloc (sizeof (char) * RC_LINEBUFFER);
+ memset (buffer, 0, RC_LINEBUFFER);
+ while ((fgets (buffer, RC_LINEBUFFER, fp))) {
+ int lb = strlen (buffer) - 1;
+ if (buffer[lb] == '\n')
+ buffer[lb] = 0;
+
+ if (strcmp (buffer, mexec) == 0)
+ m += 1;
+ else if (mname && strcmp (buffer, mname) == 0)
+ m += 10;
+ else if (mpidfile && strcmp (buffer, mpidfile) == 0)
+ m += 100;
+
+ if (m == 111)
+ break;
+
+ lc++;
+ if (lc > 5)
+ break;
+ }
+ free (buffer);
+ fclose (fp);
+ free (ffile);
+
+ return (m == 111 ? true : false);
+}
+
+bool rc_service_daemon_set (const char *service, const char *exec,
+ const char *name, const char *pidfile,
+ bool started)
+{
+ char *dirpath;
+ char *file = NULL;
+ int i;
+ char *mexec;
+ char *mname;
+ char *mpidfile;
+ int nfiles = 0;
+ char *oldfile = NULL;
+ bool retval = false;
+ DIR *dp;
+ struct dirent *d;
+
+ if (! exec && ! name && ! pidfile) {
+ errno = EINVAL;
+ return (false);
+ }
+
+ dirpath = rc_strcatpaths (RC_SVCDIR, "daemons",
+ basename_c (service), (char *) NULL);
+
+ if (exec) {
+ i = strlen (exec) + 6;
+ mexec = xmalloc (sizeof (char) * i);
+ snprintf (mexec, i, "exec=%s", exec);
+ } else
+ mexec = xstrdup ("exec=");
+
+ if (name) {
+ i = strlen (name) + 6;
+ mname = xmalloc (sizeof (char) * i);
+ snprintf (mname, i, "name=%s", name);
+ } else
+ mname = xstrdup ("name=");
+
+ if (pidfile) {
+ i = strlen (pidfile) + 9;
+ mpidfile = xmalloc (sizeof (char) * i);
+ snprintf (mpidfile, i, "pidfile=%s", pidfile);
+ } else
+ mpidfile = xstrdup ("pidfile=");
+
+ /* Regardless, erase any existing daemon info */
+ if ((dp = opendir (dirpath))) {
+ while ((d = readdir (dp))) {
+ if (d->d_name[0] == '.')
+ continue;
+ file = rc_strcatpaths (dirpath, d->d_name, (char *) NULL);
+ nfiles++;
+
+ if (! oldfile) {
+ if (_match_daemon (dirpath, d->d_name,
+ mexec, mname, mpidfile))
+ {
+ unlink (file);
+ oldfile = file;
+ nfiles--;
+ }
+ } else {
+ rename (file, oldfile);
+ free (oldfile);
+ oldfile = file;
+ }
+ }
+ free (file);
+ closedir (dp);
+ }
+
+ /* Now store our daemon info */
+ if (started) {
+ char buffer[10];
+ FILE *fp;
+
+ if (mkdir (dirpath, 0755) == 0 || errno == EEXIST) {
+ snprintf (buffer, sizeof (buffer), "%03d", nfiles + 1);
+ file = rc_strcatpaths (dirpath, buffer, (char *) NULL);
+ if ((fp = fopen (file, "w"))) {
+ fprintf (fp, "%s\n%s\n%s\n", mexec, mname, mpidfile);
+ fclose (fp);
+ retval = true;
+ }
+ free (file);
+ }
+ } else
+ retval = true;
+
+ free (mexec);
+ free (mname);
+ free (mpidfile);
+ free (dirpath);
+
+ return (retval);
+}
+librc_hidden_def(rc_service_daemon_set)
+
+bool rc_service_started_daemon (const char *service, const char *exec,
+ int indx)
+{
+ char *dirpath;
+ char *file;
+ int i;
+ char *mexec;
+ bool retval = false;
+ DIR *dp;
+ struct dirent *d;
+
+ if (! service || ! exec)
+ return (false);
+
+ dirpath = rc_strcatpaths (RC_SVCDIR, "daemons", basename_c (service),
+ (char *) NULL);
+
+ i = strlen (exec) + 6;
+ mexec = xmalloc (sizeof (char) * i);
+ snprintf (mexec, i, "exec=%s", exec);
+
+ if (indx > 0) {
+ int len = sizeof (char) * 10;
+ file = xmalloc (len);
+ snprintf (file, len, "%03d", indx);
+ retval = _match_daemon (dirpath, file, mexec, NULL, NULL);
+ free (file);
+ } else {
+ if ((dp = opendir (dirpath))) {
+ while ((d = readdir (dp))) {
+ if (d->d_name[0] == ',')
+ continue;
+ retval = _match_daemon (dirpath, d->d_name, mexec, NULL, NULL);
+ if (retval)
+ break;
+ }
+ closedir (dp);
+ }
+ }
+
+ free (dirpath);
+ free (mexec);
+ return (retval);
+}
+librc_hidden_def(rc_service_started_daemon)
+
+bool rc_service_daemons_crashed (const char *service)
+{
+ char *dirpath;
+ DIR *dp;
+ struct dirent *d;
+ char *path;
+ FILE *fp;
+ char *buffer = NULL;
+ char *exec = NULL;
+ char *name = NULL;
+ char *pidfile = NULL;
+ pid_t pid = 0;
+ pid_t *pids = NULL;
+ char *p;
+ char *token;
+ bool retval = false;
+
+ if (! service)
+ return (false);
+
+ dirpath = rc_strcatpaths (RC_SVCDIR, "daemons", basename_c (service),
+ (char *) NULL);
+
+ if (! (dp = opendir (dirpath))) {
+ free (dirpath);
+ return (false);
+ }
+
+ buffer = xmalloc (sizeof (char) * RC_LINEBUFFER);
+ memset (buffer, 0, RC_LINEBUFFER);
+
+ while ((d = readdir (dp))) {
+ if (d->d_name[0] == '.')
+ continue;
+
+ path = rc_strcatpaths (dirpath, d->d_name, (char *) NULL);
+ fp = fopen (path, "r");
+ free (path);
+ if (! fp)
+ break;
+
+ while ((fgets (buffer, RC_LINEBUFFER, fp))) {
+ int lb = strlen (buffer) - 1;
+ if (buffer[lb] == '\n')
+ buffer[lb] = 0;
+
+ p = buffer;
+ if ((token = strsep (&p, "=")) == NULL || ! p)
+ continue;
+
+ if (strlen (p) == 0)
+ continue;
+
+ if (strcmp (token, "exec") == 0) {
+ if (exec)
+ free (exec);
+ exec = xstrdup (p);
+ } else if (strcmp (token, "name") == 0) {
+ if (name)
+ free (name);
+ name = xstrdup (p);
+ } else if (strcmp (token, "pidfile") == 0) {
+ if (pidfile)
+ free (pidfile);
+ pidfile = xstrdup (p);
+ }
+ }
+ fclose (fp);
+
+ pid = 0;
+ if (pidfile) {
+ if (! exists (pidfile)) {
+ retval = true;
+ break;
+ }
+
+ if ((fp = fopen (pidfile, "r")) == NULL) {
+ retval = true;
+ break;
+ }
+
+ if (fscanf (fp, "%d", &pid) != 1) {
+ fclose (fp);
+ retval = true;
+ break;
+ }
+
+ fclose (fp);
+ free (pidfile);
+ pidfile = NULL;
+
+ /* We have the pid, so no need to match on name */
+ free (exec);
+ exec = NULL;
+ free (name);
+ name = NULL;
+ }
+
+ if ((pids = rc_find_pids (exec, name, 0, pid)) == NULL) {
+ retval = true;
+ break;
+ }
+ free (pids);
+
+ free (exec);
+ exec = NULL;
+ free (name);
+ name = NULL;
+ }
+
+ free (buffer);
+ free (exec);
+ free (name);
+ free (dirpath);
+ closedir (dp);
+
+ return (retval);
+}
+librc_hidden_def(rc_service_daemons_crashed)