aboutsummaryrefslogtreecommitdiff
path: root/src/librc
diff options
context:
space:
mode:
Diffstat (limited to 'src/librc')
-rw-r--r--src/librc/Makefile14
-rw-r--r--src/librc/librc-daemon.c574
-rw-r--r--src/librc/librc-depend.c979
-rw-r--r--src/librc/librc-depend.h61
-rw-r--r--src/librc/librc-misc.c245
-rw-r--r--src/librc/librc-strlist.c230
-rw-r--r--src/librc/librc.c891
-rw-r--r--src/librc/librc.h127
-rw-r--r--src/librc/rc.h446
9 files changed, 3567 insertions, 0 deletions
diff --git a/src/librc/Makefile b/src/librc/Makefile
new file mode 100644
index 00000000..27ea942c
--- /dev/null
+++ b/src/librc/Makefile
@@ -0,0 +1,14 @@
+TOPDIR= ..
+include $(TOPDIR)/os.mk
+
+LIB= rc
+SHLIB_MAJOR= 1
+SRCS= librc.c librc-daemon.c librc-depend.c librc-misc.c librc-strlist.c
+INCS= rc.h
+
+CPPFLAGS+= -DLIB=\"${LIBNAME}\"
+
+SHLIBDIR= /${LIBNAME}
+
+include $(TOPDIR)/cc.mk
+include $(TOPDIR)/lib.mk
diff --git a/src/librc/librc-daemon.c b/src/librc/librc-daemon.c
new file mode 100644
index 00000000..fe9509de
--- /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)
diff --git a/src/librc/librc-depend.c b/src/librc/librc-depend.c
new file mode 100644
index 00000000..902d012f
--- /dev/null
+++ b/src/librc/librc-depend.c
@@ -0,0 +1,979 @@
+/*
+ librc-depend
+ rc service dependency and ordering
+ */
+
+/*
+ * 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"
+
+#define GENDEP RC_LIBDIR "/sh/gendepends.sh"
+
+#define RC_DEPCONFIG RC_SVCDIR "/depconfig"
+
+static const char *bootlevel = NULL;
+
+/* We use this so we can pass our char array through many functions */
+struct lhead
+{
+ char **list;
+};
+
+static char *get_shell_value (char *string)
+{
+ char *p = string;
+ char *e;
+
+ if (! string)
+ return (NULL);
+
+ if (*p == '\'')
+ p++;
+
+ e = p + strlen (p) - 1;
+ if (*e == '\n')
+ *e-- = 0;
+ if (*e == '\'')
+ *e-- = 0;
+
+ if (*p != 0)
+ return p;
+
+ return (NULL);
+}
+
+void rc_deptree_free (rc_depinfo_t *deptree)
+{
+ rc_depinfo_t *di = deptree;
+ while (di)
+ {
+ rc_depinfo_t *dip = di->next;
+ rc_deptype_t *dt = di->depends;
+ free (di->service);
+ while (dt)
+ {
+ rc_deptype_t *dtp = dt->next;
+ free (dt->type);
+ rc_strlist_free (dt->services);
+ free (dt);
+ dt = dtp;
+ }
+ free (di);
+ di = dip;
+ }
+}
+librc_hidden_def(rc_deptree_free)
+
+static rc_depinfo_t *get_depinfo (const rc_depinfo_t *deptree,
+ const char *service)
+{
+ const rc_depinfo_t *di;
+
+ if (! deptree || ! service)
+ return (NULL);
+
+ for (di = deptree; di; di = di->next)
+ if (strcmp (di->service, service) == 0)
+ return ((rc_depinfo_t *)di);
+
+ return (NULL);
+}
+
+static rc_deptype_t *get_deptype (const rc_depinfo_t *depinfo,
+ const char *type)
+{
+ rc_deptype_t *dt;
+
+ if (! depinfo || !type)
+ return (NULL);
+
+ for (dt = depinfo->depends; dt; dt = dt->next)
+ if (strcmp (dt->type, type) == 0)
+ return (dt);
+
+ return (NULL);
+}
+
+rc_depinfo_t *rc_deptree_load (void)
+{
+ FILE *fp;
+ rc_depinfo_t *deptree = NULL;
+ rc_depinfo_t *depinfo = NULL;
+ rc_deptype_t *deptype = NULL;
+ char buffer [RC_LINEBUFFER];
+ char *type;
+ char *p;
+ char *e;
+ int i;
+
+ if (! (fp = fopen (RC_DEPTREE, "r")))
+ return (NULL);
+
+ while (fgets (buffer, RC_LINEBUFFER, fp))
+ {
+ p = buffer;
+ e = strsep (&p, "_");
+ if (! e || strcmp (e, "depinfo") != 0)
+ continue;
+
+ e = strsep (&p, "_");
+ if (! e || sscanf (e, "%d", &i) != 1)
+ continue;
+
+ if (! (type = strsep (&p, "_=")))
+ continue;
+
+ if (strcmp (type, "service") == 0)
+ {
+ /* Sanity */
+ e = get_shell_value (p);
+ if (! e || strlen (e) == 0)
+ continue;
+
+ if (! deptree)
+ {
+ deptree = xmalloc (sizeof (rc_depinfo_t));
+ depinfo = deptree;
+ }
+ else
+ {
+ depinfo->next = xmalloc (sizeof (rc_depinfo_t));
+ depinfo = depinfo->next;
+ }
+ memset (depinfo, 0, sizeof (rc_depinfo_t));
+ depinfo->service = xstrdup (e);
+ deptype = NULL;
+ continue;
+ }
+
+ e = strsep (&p, "=");
+ if (! e || sscanf (e, "%d", &i) != 1)
+ continue;
+
+ /* Sanity */
+ e = get_shell_value (p);
+ if (! e || strlen (e) == 0)
+ continue;
+
+ if (! deptype)
+ {
+ depinfo->depends = xmalloc (sizeof (rc_deptype_t));
+ deptype = depinfo->depends;
+ memset (deptype, 0, sizeof (rc_deptype_t));
+ }
+ else
+ if (strcmp (deptype->type, type) != 0)
+ {
+ deptype->next = xmalloc (sizeof (rc_deptype_t));
+ deptype = deptype->next;
+ memset (deptype, 0, sizeof (rc_deptype_t));
+ }
+
+ if (! deptype->type)
+ deptype->type = xstrdup (type);
+
+ rc_strlist_addsort (&deptype->services, e);
+ }
+ fclose (fp);
+
+ return (deptree);
+}
+librc_hidden_def(rc_deptree_load)
+
+static bool valid_service (const char *runlevel, const char *service)
+{
+ rc_service_state_t state = rc_service_state (service);
+
+ return ((strcmp (runlevel, bootlevel) != 0 &&
+ rc_service_in_runlevel (service, bootlevel)) ||
+ rc_service_in_runlevel (service, runlevel) ||
+ state & RC_SERVICE_COLDPLUGGED ||
+ state & RC_SERVICE_STARTED);
+}
+
+static bool get_provided1 (const char *runlevel, struct lhead *providers,
+ rc_deptype_t *deptype,
+ const char *level, bool coldplugged,
+ rc_service_state_t state)
+{
+ char *service;
+ int i;
+ bool retval = false;
+
+ STRLIST_FOREACH (deptype->services, service, i)
+ {
+ bool ok = true;
+ rc_service_state_t s = rc_service_state (service);
+ if (level)
+ ok = rc_service_in_runlevel (service, level);
+ else if (coldplugged)
+ ok = (s & RC_SERVICE_COLDPLUGGED &&
+ ! rc_service_in_runlevel (service, runlevel) &&
+ ! rc_service_in_runlevel (service, bootlevel));
+
+ if (! ok)
+ continue;
+
+ switch (state) {
+ case RC_SERVICE_STARTED:
+ ok = (s & RC_SERVICE_STARTED);
+ break;
+ case RC_SERVICE_INACTIVE:
+ case RC_SERVICE_STARTING:
+ case RC_SERVICE_STOPPING:
+ ok = (s & RC_SERVICE_STARTING ||
+ s & RC_SERVICE_STOPPING ||
+ s & RC_SERVICE_INACTIVE);
+ break;
+ default:
+ break;
+ }
+
+ if (! ok)
+ continue;
+
+ retval = true;
+ rc_strlist_add (&providers->list, service);
+ }
+
+ return (retval);
+}
+
+/* Work out if a service is provided by another service.
+ For example metalog provides logger.
+ We need to be able to handle syslogd providing logger too.
+ We do this by checking whats running, then what's starting/stopping,
+ then what's run in the runlevels and finally alphabetical order.
+
+ If there are any bugs in rc-depend, they will probably be here as
+ provided dependancy can change depending on runlevel state.
+ */
+static char **get_provided (const rc_depinfo_t *deptree,
+ const rc_depinfo_t *depinfo,
+ const char *runlevel, int options)
+{
+ rc_deptype_t *dt;
+ struct lhead providers;
+ char *service;
+ int i;
+
+ if (! deptree || ! depinfo)
+ return (NULL);
+ if (rc_service_exists (depinfo->service))
+ return (NULL);
+
+ dt = get_deptype (depinfo, "providedby");
+ if (! dt)
+ return (NULL);
+
+ memset (&providers, 0, sizeof (struct lhead));
+ /* If we are stopping then all depends are true, regardless of state.
+ This is especially true for net services as they could force a restart
+ of the local dns resolver which may depend on net. */
+ if (options & RC_DEP_STOP)
+ {
+ STRLIST_FOREACH (dt->services, service, i)
+ rc_strlist_add (&providers.list, service);
+
+ return (providers.list);
+ }
+
+ /* If we're strict, then only use what we have in our runlevel
+ * and bootlevel */
+ if (options & RC_DEP_STRICT)
+ {
+ STRLIST_FOREACH (dt->services, service, i)
+ if (rc_service_in_runlevel (service, runlevel) ||
+ rc_service_in_runlevel (service, bootlevel))
+ rc_strlist_add (&providers.list, service);
+
+ if (providers.list)
+ return (providers.list);
+ }
+
+ /* OK, we're not strict or there were no services in our runlevel.
+ This is now where the logic gets a little fuzzy :)
+ If there is >1 running service then we return NULL.
+ We do this so we don't hang around waiting for inactive services and
+ our need has already been satisfied as it's not strict.
+ We apply this to our runlevel, coldplugged services, then bootlevel
+ and finally any running.*/
+#define DO \
+ if (providers.list && providers.list[0] && providers.list[1]) \
+ { \
+ rc_strlist_free (providers.list); \
+ return (NULL); \
+ } \
+ else if (providers.list) \
+ return providers.list; \
+
+ /* Anything in the runlevel has to come first */
+ if (get_provided1 (runlevel, &providers, dt, runlevel, false, RC_SERVICE_STARTED))
+ { DO }
+ if (get_provided1 (runlevel, &providers, dt, runlevel, false, RC_SERVICE_STARTING))
+ return (providers.list);
+ if (get_provided1 (runlevel, &providers, dt, runlevel, false, RC_SERVICE_STOPPED))
+ return (providers.list);
+
+ /* Check coldplugged services */
+ if (get_provided1 (runlevel, &providers, dt, NULL, true, RC_SERVICE_STARTED))
+ { DO }
+ if (get_provided1 (runlevel, &providers, dt, NULL, true, RC_SERVICE_STARTING))
+ return (providers.list);
+
+ /* Check bootlevel if we're not in it */
+ if (bootlevel && strcmp (runlevel, bootlevel) != 0)
+ {
+ if (get_provided1 (runlevel, &providers, dt, bootlevel, false, RC_SERVICE_STARTED))
+ { DO }
+ if (get_provided1 (runlevel, &providers, dt, bootlevel, false, RC_SERVICE_STARTING))
+ return (providers.list);
+ }
+
+ /* Check coldplugged services */
+ if (get_provided1 (runlevel, &providers, dt, NULL, true, RC_SERVICE_STOPPED))
+ { DO }
+
+ /* Check manually started */
+ if (get_provided1 (runlevel, &providers, dt, NULL, false, RC_SERVICE_STARTED))
+ { DO }
+ if (get_provided1 (runlevel, &providers, dt, NULL, false, RC_SERVICE_STARTING))
+ return (providers.list);
+
+ /* Nothing started then. OK, lets get the stopped services */
+ if (get_provided1 (runlevel, &providers, dt, runlevel, false, RC_SERVICE_STOPPED))
+ return (providers.list);
+
+ if (bootlevel && (strcmp (runlevel, bootlevel) != 0)
+ && (get_provided1 (runlevel, &providers, dt, bootlevel, false, RC_SERVICE_STOPPED)))
+ return (providers.list);
+
+ /* Still nothing? OK, list all services */
+ STRLIST_FOREACH (dt->services, service, i)
+ rc_strlist_add (&providers.list, service);
+
+ return (providers.list);
+}
+
+static void visit_service (const rc_depinfo_t *deptree,
+ const char * const *types,
+ struct lhead *sorted, struct lhead *visited,
+ const rc_depinfo_t *depinfo,
+ const char *runlevel, int options)
+{
+ int i, j, k;
+ char *lp;
+ const char *item;
+ char *service;
+ rc_depinfo_t *di;
+ rc_deptype_t *dt;
+ char **provides;
+ char *svcname;
+
+ if (! deptree || !sorted || !visited || !depinfo)
+ return;
+
+ /* Check if we have already visited this service or not */
+ STRLIST_FOREACH (visited->list, item, i)
+ if (strcmp (item, depinfo->service) == 0)
+ return;
+
+ /* Add ourselves as a visited service */
+ rc_strlist_add (&visited->list, depinfo->service);
+
+ STRLIST_FOREACH (types, item, i)
+ {
+ if ((dt = get_deptype (depinfo, item)))
+ {
+ STRLIST_FOREACH (dt->services, service, j)
+ {
+ if (! options & RC_DEP_TRACE || strcmp (item, "iprovide") == 0)
+ {
+ rc_strlist_add (&sorted->list, service);
+ continue;
+ }
+
+ di = get_depinfo (deptree, service);
+ if ((provides = get_provided (deptree, di, runlevel, options)))
+ {
+ STRLIST_FOREACH (provides, lp, k)
+ {
+ di = get_depinfo (deptree, lp);
+ if (di && (strcmp (item, "ineed") == 0 ||
+ strcmp (item, "needsme") == 0 ||
+ valid_service (runlevel, di->service)))
+ visit_service (deptree, types, sorted, visited, di,
+ runlevel, options | RC_DEP_TRACE);
+ }
+ rc_strlist_free (provides);
+ }
+ else
+ if (di && (strcmp (item, "ineed") == 0 ||
+ strcmp (item, "needsme") == 0 ||
+ valid_service (runlevel, service)))
+ visit_service (deptree, types, sorted, visited, di,
+ runlevel, options | RC_DEP_TRACE);
+ }
+ }
+ }
+
+ /* Now visit the stuff we provide for */
+ if (options & RC_DEP_TRACE &&
+ (dt = get_deptype (depinfo, "iprovide")))
+ {
+ STRLIST_FOREACH (dt->services, service, i)
+ {
+ if ((di = get_depinfo (deptree, service)))
+ if ((provides = get_provided (deptree, di, runlevel, options)))
+ {
+ STRLIST_FOREACH (provides, lp, j)
+ if (strcmp (lp, depinfo->service) == 0)
+ {
+ visit_service (deptree, types, sorted, visited, di,
+ runlevel, options | RC_DEP_TRACE);
+ break;
+ }
+ rc_strlist_free (provides);
+ }
+ }
+ }
+
+ /* We've visited everything we need, so add ourselves unless we
+ are also the service calling us or we are provided by something */
+ svcname = getenv("SVCNAME");
+ if (! svcname || strcmp (svcname, depinfo->service) != 0)
+ if (! get_deptype (depinfo, "providedby"))
+ rc_strlist_add (&sorted->list, depinfo->service);
+}
+
+char **rc_deptree_depend (const rc_depinfo_t *deptree,
+ const char *service, const char *type)
+{
+ rc_depinfo_t *di;
+ rc_deptype_t *dt;
+ char **svcs = NULL;
+ int i;
+ char *svc;
+
+ if (! (di = get_depinfo (deptree, service)) ||
+ ! (dt = get_deptype (di, type)))
+ {
+ errno = ENOENT;
+ return (NULL);
+ }
+
+ /* For consistency, we copy the array */
+ STRLIST_FOREACH (dt->services, svc, i)
+ rc_strlist_add (&svcs, svc);
+
+ return (svcs);
+}
+librc_hidden_def(rc_deptree_depend)
+
+char **rc_deptree_depends (const rc_depinfo_t *deptree,
+ const char *const *types,
+ const char *const *services,
+ const char *runlevel, int options)
+{
+ struct lhead sorted;
+ struct lhead visited;
+ rc_depinfo_t *di;
+ const char *service;
+ int i;
+
+ if (! deptree || ! services)
+ return (NULL);
+
+ memset (&sorted, 0, sizeof (struct lhead));
+ memset (&visited, 0, sizeof (struct lhead));
+
+ bootlevel = getenv ("RC_BOOTLEVEL");
+ if (! bootlevel)
+ bootlevel = RC_LEVEL_BOOT;
+
+ STRLIST_FOREACH (services, service, i)
+ {
+ if (! (di = get_depinfo (deptree, service))) {
+ errno = ENOENT;
+ continue;
+ }
+ if (types)
+ visit_service (deptree, types, &sorted, &visited,
+ di, runlevel, options);
+ }
+
+ rc_strlist_free (visited.list);
+ return (sorted.list);
+}
+librc_hidden_def(rc_deptree_depends)
+
+static const char * const order_types[] = { "ineed", "iuse", "iafter", NULL };
+char **rc_deptree_order (const rc_depinfo_t *deptree, const char *runlevel,
+ int options)
+{
+ char **list = NULL;
+ char **services = NULL;
+ bool reverse = false;
+ char **tmp = NULL;
+
+ if (! runlevel)
+ return (NULL);
+
+ bootlevel = getenv ("RC_BOOTLEVEL");
+ if (! bootlevel)
+ bootlevel = RC_LEVEL_BOOT;
+
+ /* When shutting down, list all running services */
+ if (strcmp (runlevel, RC_LEVEL_SINGLE) == 0 ||
+ strcmp (runlevel, RC_LEVEL_SHUTDOWN) == 0 ||
+ strcmp (runlevel, RC_LEVEL_REBOOT) == 0)
+ {
+ list = rc_services_in_state (RC_SERVICE_STARTED);
+
+ tmp = rc_services_in_state (RC_SERVICE_INACTIVE);
+ rc_strlist_join (&list, tmp);
+ rc_strlist_free (tmp);
+
+ tmp = rc_services_in_state (RC_SERVICE_STARTING);
+ rc_strlist_join (&list, tmp);
+ rc_strlist_free (tmp);
+ reverse = true;
+ } else {
+ list = rc_services_in_runlevel (runlevel);
+
+ /* Add coldplugged services */
+ tmp = rc_services_in_state (RC_SERVICE_COLDPLUGGED);
+ rc_strlist_join (&list, tmp);
+ rc_strlist_free (tmp);
+
+ /* If we're not the boot runlevel then add that too */
+ if (strcmp (runlevel, bootlevel) != 0) {
+ tmp = rc_services_in_runlevel (bootlevel);
+ rc_strlist_join (&list, tmp);
+ rc_strlist_free (tmp);
+ }
+ }
+
+ /* Now we have our lists, we need to pull in any dependencies
+ and order them */
+ services = rc_deptree_depends (deptree, order_types, (const char **) list,
+ runlevel,
+ RC_DEP_STRICT | RC_DEP_TRACE | options);
+ rc_strlist_free (list);
+
+ if (reverse)
+ rc_strlist_reverse (services);
+
+ return (services);
+}
+librc_hidden_def(rc_deptree_order)
+
+static bool is_newer_than (const char *file, const char *target)
+{
+ struct stat buf;
+ time_t mtime;
+ bool newer = true;
+ DIR *dp;
+ struct dirent *d;
+ char *path;
+
+ if (stat (file, &buf) != 0 || buf.st_size == 0)
+ return (false);
+ mtime = buf.st_mtime;
+
+ /* Of course we are newever than targets that don't exist
+ Such as broken symlinks */
+ if (stat (target, &buf) != 0)
+ return (true);
+
+ if (mtime < buf.st_mtime)
+ return (false);
+
+ if (! (dp = opendir (target)))
+ return (true);
+
+ while ((d = readdir (dp))) {
+ if (d->d_name[0] == '.')
+ continue;
+
+ path = rc_strcatpaths (target, d->d_name, (char *) NULL);
+ newer = is_newer_than (file, path);
+ free (path);
+ if (! newer)
+ break;
+ }
+ closedir (dp);
+
+ return (newer);
+}
+
+typedef struct deppair
+{
+ const char *depend;
+ const char *addto;
+} deppair_t;
+
+static const deppair_t deppairs[] = {
+ { "ineed", "needsme" },
+ { "iuse", "usesme" },
+ { "iafter", "ibefore" },
+ { "ibefore", "iafter" },
+ { "iprovide", "providedby" },
+ { NULL, NULL }
+};
+
+static const char *depdirs[] =
+{
+ RC_SVCDIR "/starting",
+ RC_SVCDIR "/started",
+ RC_SVCDIR "/stopping",
+ RC_SVCDIR "/inactive",
+ RC_SVCDIR "/wasinactive",
+ RC_SVCDIR "/failed",
+ RC_SVCDIR "/coldplugged",
+ RC_SVCDIR "/daemons",
+ RC_SVCDIR "/options",
+ RC_SVCDIR "/exclusive",
+ RC_SVCDIR "/scheduled",
+ NULL
+};
+
+bool rc_deptree_update_needed (void)
+{
+ bool newer = false;
+ char **config;
+ char *service;
+ int i;
+
+ /* Create base directories if needed */
+ for (i = 0; depdirs[i]; i++)
+ if (mkdir (depdirs[i], 0755) != 0 && errno != EEXIST)
+ fprintf (stderr, "mkdir `%s': %s\n", depdirs[i], strerror (errno));
+
+ /* Quick test to see if anything we use has changed */
+ if (! is_newer_than (RC_DEPTREE, RC_INITDIR) ||
+ ! is_newer_than (RC_DEPTREE, RC_CONFDIR) ||
+ ! is_newer_than (RC_DEPTREE, RC_INITDIR_LOCAL) ||
+ ! is_newer_than (RC_DEPTREE, RC_CONFDIR_LOCAL) ||
+ ! is_newer_than (RC_DEPTREE, "/etc/rc.conf"))
+ return (true);
+
+ /* Some init scripts dependencies change depending on config files
+ * outside of baselayout, like syslog-ng, so we check those too. */
+ config = rc_config_list (RC_DEPCONFIG);
+ STRLIST_FOREACH (config, service, i) {
+ if (! is_newer_than (RC_DEPTREE, service)) {
+ newer = true;
+ break;
+ }
+ }
+ rc_strlist_free (config);
+
+ return (newer);
+}
+librc_hidden_def(rc_deptree_update_needed)
+
+/* This is a 5 phase operation
+ Phase 1 is a shell script which loads each init script and config in turn
+ and echos their dependency info to stdout
+ Phase 2 takes that and populates a depinfo object with that data
+ Phase 3 adds any provided services to the depinfo object
+ Phase 4 scans that depinfo object and puts in backlinks
+ Phase 5 saves the depinfo object to disk
+ */
+bool rc_deptree_update (void)
+{
+ char *depends;
+ char *service;
+ char *type;
+ char *depend;
+ char **config = NULL;
+ int retval = true;
+ FILE *fp;
+ rc_depinfo_t *deptree;
+ rc_depinfo_t *depinfo;
+ rc_depinfo_t *di;
+ rc_depinfo_t *last_depinfo = NULL;
+ rc_deptype_t *deptype = NULL;
+ rc_deptype_t *dt;
+ rc_deptype_t *last_deptype = NULL;
+ char *buffer = NULL;
+ int len;
+ int i;
+ int j;
+ int k;
+ bool already_added;
+
+ /* Some init scripts need RC_LIBDIR to source stuff
+ Ideally we should be setting our full env instead */
+ if (! getenv ("RC_LIBDIR"))
+ setenv ("RC_LIBDIR", RC_LIBDIR, 0);
+
+ /* Phase 1 */
+ if (! (fp = popen (GENDEP, "r")))
+ return (false);
+
+ deptree = xmalloc (sizeof (rc_depinfo_t));
+ memset (deptree, 0, sizeof (rc_depinfo_t));
+ buffer = xmalloc (sizeof (char) * RC_LINEBUFFER);
+ memset (buffer, 0, RC_LINEBUFFER);
+
+ /* Phase 2 */
+ while (fgets (buffer, RC_LINEBUFFER, fp))
+ {
+ /* Trim the newline */
+ if (buffer[strlen (buffer) - 1] == '\n')
+ buffer[strlen(buffer) -1] = 0;
+
+ depends = buffer;
+ service = strsep (&depends, " ");
+ if (! service)
+ continue;
+ type = strsep (&depends, " ");
+
+ for (depinfo = deptree; depinfo; depinfo = depinfo->next)
+ {
+ last_depinfo = depinfo;
+ if (depinfo->service && strcmp (depinfo->service, service) == 0)
+ break;
+ }
+
+ if (! depinfo)
+ {
+ if (! last_depinfo->service)
+ depinfo = last_depinfo;
+ else
+ {
+ last_depinfo->next = xmalloc (sizeof (rc_depinfo_t));
+ depinfo = last_depinfo->next;
+ }
+ memset (depinfo, 0, sizeof (rc_depinfo_t));
+ depinfo->service = xstrdup (service);
+ }
+
+ /* We may not have any depends */
+ if (! type || ! depends)
+ continue;
+
+ /* Get the type */
+ if (strcmp (type, "config") != 0) {
+ last_deptype = NULL;
+ for (deptype = depinfo->depends; deptype; deptype = deptype->next)
+ {
+ last_deptype = deptype;
+ if (strcmp (deptype->type, type) == 0)
+ break;
+ }
+
+ if (! deptype)
+ {
+ if (! last_deptype)
+ {
+ depinfo->depends = xmalloc (sizeof (rc_deptype_t));
+ deptype = depinfo->depends;
+ }
+ else
+ {
+ last_deptype->next = xmalloc (sizeof (rc_deptype_t));
+ deptype = last_deptype->next;
+ }
+ memset (deptype, 0, sizeof (rc_deptype_t));
+ deptype->type = xstrdup (type);
+ }
+ }
+
+ /* Now add each depend to our type.
+ We do this individually so we handle multiple spaces gracefully */
+ while ((depend = strsep (&depends, " ")))
+ {
+ if (depend[0] == 0)
+ continue;
+
+ if (strcmp (type, "config") == 0) {
+ rc_strlist_addsort (&config, depend);
+ continue;
+ }
+
+ /* .sh files are not init scripts */
+ len = strlen (depend);
+ if (len > 2 &&
+ depend[len - 3] == '.' &&
+ depend[len - 2] == 's' &&
+ depend[len - 1] == 'h')
+ continue;
+
+ rc_strlist_addsort (&deptype->services, depend);
+
+ /* We need to allow `after *; before local;` to work.
+ * Conversely, we need to allow 'before *; after modules' also */
+ /* If we're before something, remove us from the after list */
+ if (strcmp (type, "ibefore") == 0) {
+ if ((dt = get_deptype (depinfo, "iafter")))
+ rc_strlist_delete (&dt->services, depend);
+ }
+ /* If we're after something, remove us from the before list */
+ if (strcmp (type, "iafter") == 0 ||
+ strcmp (type, "ineed") == 0 ||
+ strcmp (type, "iuse") == 0) {
+ if ((dt = get_deptype (depinfo, "ibefore")))
+ rc_strlist_delete (&dt->services, depend);
+ }
+ }
+ }
+ pclose (fp);
+ free (buffer);
+
+ /* Phase 3 - add our providors to the tree */
+ for (depinfo = deptree; depinfo; depinfo = depinfo->next)
+ {
+ if ((deptype = get_deptype (depinfo, "iprovide")))
+ STRLIST_FOREACH (deptype->services, service, i)
+ {
+ for (di = deptree; di; di = di->next)
+ {
+ last_depinfo = di;
+ if (strcmp (di->service, service) == 0)
+ break;
+ }
+ if (! di)
+ {
+ last_depinfo->next = xmalloc (sizeof (rc_depinfo_t));
+ di = last_depinfo->next;
+ memset (di, 0, sizeof (rc_depinfo_t));
+ di->service = xstrdup (service);
+ }
+ }
+ }
+
+ /* Phase 4 - backreference our depends */
+ for (depinfo = deptree; depinfo; depinfo = depinfo->next)
+ {
+ for (i = 0; deppairs[i].depend; i++)
+ {
+ deptype = get_deptype (depinfo, deppairs[i].depend);
+ if (! deptype)
+ continue;
+
+ STRLIST_FOREACH (deptype->services, service, j)
+ {
+ di = get_depinfo (deptree, service);
+ if (! di)
+ {
+ if (strcmp (deptype->type, "ineed") == 0)
+ {
+ fprintf (stderr,
+ "Service `%s' needs non existant service `%s'\n",
+ depinfo->service, service);
+ }
+ continue;
+ }
+
+ /* Add our deptype now */
+ last_deptype = NULL;
+ for (dt = di->depends; dt; dt = dt->next)
+ {
+ last_deptype = dt;
+ if (strcmp (dt->type, deppairs[i].addto) == 0)
+ break;
+ }
+ if (! dt)
+ {
+ if (! last_deptype)
+ {
+ di->depends = xmalloc (sizeof (rc_deptype_t));
+ dt = di->depends;
+ }
+ else
+ {
+ last_deptype->next = xmalloc (sizeof (rc_deptype_t));
+ dt = last_deptype->next;
+ }
+ memset (dt, 0, sizeof (rc_deptype_t));
+ dt->type = xstrdup (deppairs[i].addto);
+ }
+
+ already_added = false;
+ STRLIST_FOREACH (dt->services, service, k)
+ if (strcmp (service, depinfo->service) == 0)
+ {
+ already_added = true;
+ break;
+ }
+
+ if (! already_added)
+ rc_strlist_addsort (&dt->services, depinfo->service);
+ }
+ }
+ }
+
+ /* Phase 5 - save to disk
+ Now that we're purely in C, do we need to keep a shell parseable file?
+ I think yes as then it stays human readable
+ This works and should be entirely shell parseable provided that depend
+ names don't have any non shell variable characters in
+ */
+ if ((fp = fopen (RC_DEPTREE, "w"))) {
+ i = 0;
+ for (depinfo = deptree; depinfo; depinfo = depinfo->next)
+ {
+ fprintf (fp, "depinfo_%d_service='%s'\n", i, depinfo->service);
+ for (deptype = depinfo->depends; deptype; deptype = deptype->next)
+ {
+ k = 0;
+ STRLIST_FOREACH (deptype->services, service, j)
+ {
+ fprintf (fp, "depinfo_%d_%s_%d='%s'\n", i, deptype->type,
+ k, service);
+ k++;
+ }
+ }
+ i++;
+ }
+ fclose (fp);
+ } else {
+ fprintf (stderr, "fopen `%s': %s\n", RC_DEPTREE, strerror (errno));
+ retval = false;
+ }
+
+ /* Save our external config files to disk */
+ if (config) {
+ if ((fp = fopen (RC_DEPCONFIG, "w"))) {
+ STRLIST_FOREACH (config, service, i)
+ fprintf (fp, "%s\n", service);
+ fclose (fp);
+ } else {
+ fprintf (stderr, "fopen `%s': %s\n", RC_DEPCONFIG, strerror (errno));
+ retval = false;
+ }
+ rc_strlist_free (config);
+ }
+
+ rc_deptree_free (deptree);
+
+ return (retval);
+}
+librc_hidden_def(rc_deptree_update)
diff --git a/src/librc/librc-depend.h b/src/librc/librc-depend.h
new file mode 100644
index 00000000..238f70d1
--- /dev/null
+++ b/src/librc/librc-depend.h
@@ -0,0 +1,61 @@
+/*
+ * librc-depend.h
+ * Internal header file for dependency structures
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef _LIBRC_DEPEND_H
+#define _LIBRC_DEPEND_H
+
+/*! @name Dependency structures
+ * private to librc - rc.h exposes them just a pointers */
+
+/*! Singly linked list of dependency types that list the services the
+ * type is for */
+typedef struct rc_deptype
+{
+ /*! ineed, iuse, iafter, etc */
+ char *type;
+ /*! NULL terminated list of services */
+ char **services;
+ /*! Next dependency type */
+ struct rc_deptype *next;
+} rc_deptype_t;
+
+/*! Singly linked list of services and their dependencies */
+typedef struct rc_depinfo
+{
+ /*! Name of service */
+ char *service;
+ /*! Dependencies */
+ rc_deptype_t *depends;
+ /*! Next service dependency type */
+ struct rc_depinfo *next;
+} rc_depinfo_t;
+
+#endif
diff --git a/src/librc/librc-misc.c b/src/librc/librc-misc.c
new file mode 100644
index 00000000..dcecc293
--- /dev/null
+++ b/src/librc/librc-misc.c
@@ -0,0 +1,245 @@
+/*
+ rc-misc.c
+ rc misc functions
+ */
+
+/*
+ * 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"
+
+bool rc_yesno (const char *value)
+{
+ if (! value) {
+ errno = ENOENT;
+ return (false);
+ }
+
+ if (strcasecmp (value, "yes") == 0 ||
+ strcasecmp (value, "y") == 0 ||
+ strcasecmp (value, "true") == 0 ||
+ strcasecmp (value, "1") == 0)
+ return (true);
+
+ if (strcasecmp (value, "no") != 0 &&
+ strcasecmp (value, "n") != 0 &&
+ strcasecmp (value, "false") != 0 &&
+ strcasecmp (value, "0") != 0)
+ errno = EINVAL;
+
+ return (false);
+}
+librc_hidden_def(rc_yesno)
+
+char *rc_strcatpaths (const char *path1, const char *paths, ...)
+{
+ va_list ap;
+ int length;
+ int i;
+ char *p;
+ char *path;
+ char *pathp;
+
+ if (! path1 || ! paths)
+ return (NULL);
+
+ length = strlen (path1) + strlen (paths) + 1;
+ if (*paths != '/')
+ length ++;
+
+ va_start (ap, paths);
+ while ((p = va_arg (ap, char *)) != NULL) {
+ if (*p != '/')
+ length ++;
+ length += strlen (p);
+ }
+ va_end (ap);
+
+ pathp = path = xmalloc (length * sizeof (char));
+ memset (path, 0, length);
+ i = strlen (path1);
+ memcpy (path, path1, i);
+ pathp += i;
+ if (*paths != '/')
+ *pathp ++ = '/';
+ i = strlen (paths);
+ memcpy (pathp, paths, i);
+ pathp += i;
+
+ va_start (ap, paths);
+ while ((p = va_arg (ap, char *)) != NULL) {
+ if (*p != '/')
+ *pathp ++= '/';
+ i = strlen (p);
+ memcpy (pathp, p, i);
+ pathp += i;
+ }
+ va_end (ap);
+
+ *pathp++ = 0;
+
+ return (path);
+}
+librc_hidden_def(rc_strcatpaths)
+
+
+char **rc_config_load (const char *file)
+{
+ char **list = NULL;
+ FILE *fp;
+ char *buffer;
+ char *p;
+ char *token;
+ char *line;
+ char *linep;
+ char *linetok;
+ int i = 0;
+ bool replaced;
+ char *entry;
+ char *newline;
+
+ if (! (fp = fopen (file, "r")))
+ return (NULL);
+
+ buffer = xmalloc (sizeof (char) * RC_LINEBUFFER);
+ while (fgets (buffer, RC_LINEBUFFER, fp)) {
+ p = buffer;
+
+ /* Strip leading spaces/tabs */
+ while ((*p == ' ') || (*p == '\t'))
+ p++;
+
+ if (! p || strlen (p) < 3 || p[0] == '#')
+ continue;
+
+ /* Get entry */
+ token = strsep (&p, "=");
+
+ if (! token)
+ continue;
+
+ entry = xstrdup (token);
+
+ /* Preserve shell coloring */
+ if (*p == '$')
+ token = p;
+ else
+ do {
+ /* Bash variables are usually quoted */
+ token = strsep (&p, "\"\'");
+ } while ((token) && (strlen (token) == 0));
+
+ /* Drop a newline if that's all we have */
+ i = strlen (token) - 1;
+ if (token[i] == 10)
+ token[i] = 0;
+
+ i = strlen (entry) + strlen (token) + 2;
+ newline = xmalloc (i);
+ snprintf (newline, i, "%s=%s", entry, token);
+
+ replaced = false;
+ /* In shells the last item takes precedence, so we need to remove
+ any prior values we may already have */
+ STRLIST_FOREACH (list, line, i) {
+ char *tmp = xstrdup (line);
+ linep = tmp;
+ linetok = strsep (&linep, "=");
+ if (strcmp (linetok, entry) == 0) {
+ /* We have a match now - to save time we directly replace it */
+ free (list[i - 1]);
+ list[i - 1] = newline;
+ replaced = true;
+ free (tmp);
+ break;
+ }
+ free (tmp);
+ }
+
+ if (! replaced) {
+ rc_strlist_addsort (&list, newline);
+ free (newline);
+ }
+ free (entry);
+ }
+ free (buffer);
+ fclose (fp);
+
+ return (list);
+}
+librc_hidden_def(rc_config_load)
+
+char *rc_config_value (char **list, const char *entry)
+{
+ char *line;
+ int i;
+ char *p;
+
+ STRLIST_FOREACH (list, line, i) {
+ p = strchr (line, '=');
+ if (p && strncmp (entry, line, p - line) == 0)
+ return (p += 1);
+ }
+
+ return (NULL);
+}
+librc_hidden_def(rc_config_value)
+
+char **rc_config_list (const char *file)
+{
+ FILE *fp;
+ char *buffer;
+ char *p;
+ char *token;
+ char **list = NULL;
+
+ if (! (fp = fopen (file, "r")))
+ return (NULL);
+
+ buffer = xmalloc (sizeof (char) * RC_LINEBUFFER);
+ while (fgets (buffer, RC_LINEBUFFER, fp)) {
+ p = buffer;
+
+ /* Strip leading spaces/tabs */
+ while ((*p == ' ') || (*p == '\t'))
+ p++;
+
+ /* Get entry - we do not want comments */
+ token = strsep (&p, "#");
+ if (token && (strlen (token) > 1)) {
+ /* Stip the newline if present */
+ if (token[strlen (token) - 1] == '\n')
+ token[strlen (token) - 1] = 0;
+
+ rc_strlist_add (&list, token);
+ }
+ }
+ free (buffer);
+ fclose (fp);
+
+ return (list);
+}
+librc_hidden_def(rc_config_list)
diff --git a/src/librc/librc-strlist.c b/src/librc/librc-strlist.c
new file mode 100644
index 00000000..815c8370
--- /dev/null
+++ b/src/librc/librc-strlist.c
@@ -0,0 +1,230 @@
+/*
+ librc-strlist.h
+ String list functions for using char ** arrays
+
+ Based on a previous implementation by Martin Schlemmer
+ */
+
+/*
+ * 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"
+
+static char *_rc_strlist_add (char ***list, const char *item, bool uniq)
+{
+ char **newlist;
+ char **lst = *list;
+ int i = 0;
+
+ if (! item)
+ return (NULL);
+
+ while (lst && lst[i]) {
+ if (uniq && strcmp (lst[i], item) == 0) {
+ errno = EEXIST;
+ return (NULL);
+ }
+ i++;
+ }
+
+ newlist = xrealloc (lst, sizeof (char *) * (i + 2));
+ newlist[i] = xstrdup (item);
+ newlist[i + 1] = NULL;
+
+ *list = newlist;
+ return (newlist[i]);
+}
+
+char *rc_strlist_add (char ***list, const char *item)
+{
+ return (_rc_strlist_add (list, item, false));
+}
+librc_hidden_def(rc_strlist_add)
+
+char *rc_strlist_addu (char ***list, const char *item)
+{
+ return (_rc_strlist_add (list, item, true));
+}
+librc_hidden_def(rc_strlist_addu)
+
+static char *_rc_strlist_addsort (char ***list, const char *item,
+ int (*sortfunc) (const char *s1,
+ const char *s2),
+ bool uniq)
+{
+ char **newlist;
+ char **lst = *list;
+ int i = 0;
+ char *tmp1;
+ char *tmp2;
+ char *retval;
+
+ if (! item)
+ return (NULL);
+
+ while (lst && lst[i]) {
+ if (uniq && strcmp (lst[i], item) == 0) {
+ errno = EEXIST;
+ return (NULL);
+ }
+ i++;
+ }
+
+ newlist = xrealloc (lst, sizeof (char *) * (i + 2));
+
+ if (! i)
+ newlist[i] = NULL;
+ newlist[i + 1] = NULL;
+
+ i = 0;
+ while (newlist[i] && sortfunc (newlist[i], item) < 0)
+ i++;
+
+ tmp1 = newlist[i];
+ retval = newlist[i] = xstrdup (item);
+ do {
+ i++;
+ tmp2 = newlist[i];
+ newlist[i] = tmp1;
+ tmp1 = tmp2;
+ } while (tmp1);
+
+ *list = newlist;
+ return (retval);
+}
+
+char *rc_strlist_addsort (char ***list, const char *item)
+{
+ return (_rc_strlist_addsort (list, item, strcoll, false));
+}
+librc_hidden_def(rc_strlist_addsort)
+
+char *rc_strlist_addsortc (char ***list, const char *item)
+{
+ return (_rc_strlist_addsort (list, item, strcmp, false));
+}
+librc_hidden_def(rc_strlist_addsortc)
+
+char *rc_strlist_addsortu (char ***list, const char *item)
+{
+ return (_rc_strlist_addsort (list, item, strcmp, true));
+}
+librc_hidden_def(rc_strlist_addsortu)
+
+bool rc_strlist_delete (char ***list, const char *item)
+{
+ char **lst = *list;
+ int i = 0;
+
+ if (!lst || ! item)
+ return (false);
+
+ while (lst[i]) {
+ if (strcmp (lst[i], item) == 0) {
+ free (lst[i]);
+ do {
+ lst[i] = lst[i + 1];
+ i++;
+ } while (lst[i]);
+ return (true);
+ }
+ i++;
+ }
+
+ errno = ENOENT;
+ return (false);
+}
+librc_hidden_def(rc_strlist_delete)
+
+char *rc_strlist_join (char ***list1, char **list2)
+{
+ char **lst1 = *list1;
+ char **newlist;
+ int i = 0;
+ int j = 0;
+
+ if (! list2)
+ return (NULL);
+
+ while (lst1 && lst1[i])
+ i++;
+
+ while (list2[j])
+ j++;
+
+ newlist = xrealloc (lst1, sizeof (char *) * (i + j + 1));
+
+ j = 0;
+ while (list2[j]) {
+ newlist[i] = list2[j];
+ /* Take the item off the 2nd list as it's only a shallow copy */
+ list2[j] = NULL;
+ i++;
+ j++;
+ }
+ newlist[i] = NULL;
+
+ *list1 = newlist;
+ return (newlist[i == 0 ? 0 : i - 1]);
+}
+librc_hidden_def(rc_strlist_join)
+
+void rc_strlist_reverse (char **list)
+{
+ char *item;
+ int i = 0;
+ int j = 0;
+
+ if (! list)
+ return;
+
+ while (list[j])
+ j++;
+ j--;
+
+ while (i < j && list[i] && list[j]) {
+ item = list[i];
+ list[i] = list[j];
+ list[j] = item;
+ i++;
+ j--;
+ }
+}
+librc_hidden_def(rc_strlist_reverse)
+
+void rc_strlist_free (char **list)
+{
+ int i = 0;
+
+ if (! list)
+ return;
+
+ while (list[i])
+ free (list[i++]);
+
+ free (list);
+}
+librc_hidden_def(rc_strlist_free)
diff --git a/src/librc/librc.c b/src/librc/librc.c
new file mode 100644
index 00000000..15309f87
--- /dev/null
+++ b/src/librc/librc.c
@@ -0,0 +1,891 @@
+/*
+ librc
+ core RC functions
+ */
+
+/*
+ * Copyright 2007-2008 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.
+ */
+
+const char librc_copyright[] = "Copyright (c) 2007-2008 Roy Marples";
+
+#include "librc.h"
+
+#define SOFTLEVEL RC_SVCDIR "/softlevel"
+
+#ifndef S_IXUGO
+# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
+#endif
+
+/* File stream used for plugins to write environ vars to */
+FILE *rc_environ_fd = NULL;
+
+typedef struct rc_service_state_name {
+ rc_service_state_t state;
+ const char *name;
+} rc_service_state_name_t;
+
+/* We MUST list the states below 0x10 first
+ * The rest can be in any order */
+static const rc_service_state_name_t rc_service_state_names[] = {
+ { RC_SERVICE_STARTED, "started" },
+ { RC_SERVICE_STOPPED, "stopped" },
+ { RC_SERVICE_STARTING, "starting" },
+ { RC_SERVICE_STOPPING, "stopping" },
+ { RC_SERVICE_INACTIVE, "inactive" },
+ { RC_SERVICE_WASINACTIVE, "wasinactive" },
+ { RC_SERVICE_COLDPLUGGED, "coldplugged" },
+ { RC_SERVICE_FAILED, "failed" },
+ { RC_SERVICE_SCHEDULED, "scheduled"},
+ { 0, NULL}
+};
+
+#define LS_INITD 0x01
+#define LS_DIR 0x02
+static char **ls_dir (const char *dir, int options)
+{
+ DIR *dp;
+ struct dirent *d;
+ char **list = NULL;
+ struct stat buf;
+
+ if ((dp = opendir (dir)) == NULL)
+ return (NULL);
+
+ while (((d = readdir (dp)) != NULL)) {
+ if (d->d_name[0] != '.') {
+ if (options & LS_INITD) {
+ int l = strlen (d->d_name);
+
+ /* Check that our file really exists.
+ * This is important as a service maybe in a runlevel, but
+ * could also have been removed. */
+ char *file = rc_strcatpaths (dir, d->d_name, NULL);
+ int ok = stat (file, &buf);
+ free (file);
+ if (ok != 0)
+ continue;
+
+ /* .sh files are not init scripts */
+ if (l > 2 && d->d_name[l - 3] == '.' &&
+ d->d_name[l - 2] == 's' &&
+ d->d_name[l - 1] == 'h')
+ continue;
+ }
+ if (options & LS_DIR) {
+ if (stat (d->d_name, &buf) == 0 && ! S_ISDIR (buf.st_mode))
+ continue;
+ }
+ rc_strlist_addsort (&list, d->d_name);
+ }
+ }
+ closedir (dp);
+
+ return (list);
+}
+
+static bool rm_dir (const char *pathname, bool top)
+{
+ DIR *dp;
+ struct dirent *d;
+
+ if ((dp = opendir (pathname)) == NULL)
+ return (false);
+
+ errno = 0;
+ while (((d = readdir (dp)) != NULL) && errno == 0) {
+ if (strcmp (d->d_name, ".") != 0 && strcmp (d->d_name, "..") != 0) {
+ char *tmp = rc_strcatpaths (pathname, d->d_name, (char *) NULL);
+ if (d->d_type == DT_DIR) {
+ if (! rm_dir (tmp, true))
+ {
+ free (tmp);
+ closedir (dp);
+ return (false);
+ }
+ } else {
+ if (unlink (tmp)) {
+ free (tmp);
+ closedir (dp);
+ return (false);
+ }
+ }
+ free (tmp);
+ }
+ }
+ closedir (dp);
+
+ if (top && rmdir (pathname) != 0)
+ return (false);
+
+ return (true);
+}
+
+static const char *rc_parse_service_state (rc_service_state_t state)
+{
+ int i;
+
+ for (i = 0; rc_service_state_names[i].name; i++) {
+ if (rc_service_state_names[i].state == state)
+ return (rc_service_state_names[i].name);
+ }
+
+ return (NULL);
+}
+
+bool rc_runlevel_starting (void)
+{
+ return (exists (RC_STARTING));
+}
+librc_hidden_def(rc_runlevel_starting)
+
+bool rc_runlevel_stopping (void)
+{
+ return (exists (RC_STOPPING));
+}
+librc_hidden_def(rc_runlevel_stopping)
+
+char **rc_runlevel_list (void)
+{
+ return (ls_dir (RC_RUNLEVELDIR, LS_DIR));
+}
+librc_hidden_def(rc_runlevel_list)
+
+char *rc_runlevel_get (void)
+{
+ FILE *fp;
+ char *runlevel = NULL;
+
+ if ((fp = fopen (SOFTLEVEL, "r"))) {
+ runlevel = xmalloc (sizeof (char) * PATH_MAX);
+ if (fgets (runlevel, PATH_MAX, fp)) {
+ int i = strlen (runlevel) - 1;
+ if (runlevel[i] == '\n')
+ runlevel[i] = 0;
+ } else
+ *runlevel = '\0';
+ fclose (fp);
+ }
+
+ if (! runlevel || ! *runlevel) {
+ free (runlevel);
+ runlevel = xstrdup (RC_LEVEL_SYSINIT);
+ }
+
+ return (runlevel);
+}
+librc_hidden_def(rc_runlevel_get)
+
+bool rc_runlevel_set (const char *runlevel)
+{
+ FILE *fp = fopen (SOFTLEVEL, "w");
+
+ if (! fp)
+ return (false);
+ fprintf (fp, "%s", runlevel);
+ fclose (fp);
+ return (true);
+}
+librc_hidden_def(rc_runlevel_set)
+
+bool rc_runlevel_exists (const char *runlevel)
+{
+ char *path;
+ struct stat buf;
+ bool retval = false;
+
+ if (! runlevel)
+ return (false);
+
+ path = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, (char *) NULL);
+ if (stat (path, &buf) == 0 && S_ISDIR (buf.st_mode))
+ retval = true;
+ free (path);
+ return (retval);
+}
+librc_hidden_def(rc_runlevel_exists)
+
+/* Resolve a service name to it's full path */
+char *rc_service_resolve (const char *service)
+{
+ char buffer[PATH_MAX];
+ char *file;
+ int r = 0;
+ struct stat buf;
+
+ if (! service)
+ return (NULL);
+
+ if (service[0] == '/')
+ return (xstrdup (service));
+
+ file = rc_strcatpaths (RC_SVCDIR, "started", service, (char *) NULL);
+ if (lstat (file, &buf) || ! S_ISLNK (buf.st_mode)) {
+ free (file);
+ file = rc_strcatpaths (RC_SVCDIR, "inactive", service, (char *) NULL);
+ if (lstat (file, &buf) || ! S_ISLNK (buf.st_mode)) {
+ free (file);
+ file = NULL;
+ }
+ }
+
+ memset (buffer, 0, sizeof (buffer));
+ if (file) {
+ r = readlink (file, buffer, sizeof (buffer));
+ free (file);
+ if (r > 0)
+ return (xstrdup (buffer));
+ }
+ snprintf (buffer, sizeof (buffer), RC_INITDIR "/%s", service);
+
+ /* So we don't exist in /etc/init.d - check /usr/local/etc/init.d */
+ if (stat (buffer, &buf) != 0) {
+ snprintf (buffer, sizeof (buffer), RC_INITDIR_LOCAL "/%s", service);
+ if (stat (buffer, &buf) != 0)
+ return (NULL);
+ }
+
+ return (xstrdup (buffer));
+}
+librc_hidden_def(rc_service_resolve)
+
+bool rc_service_exists (const char *service)
+{
+ char *file;
+ bool retval = false;
+ int len;
+ struct stat buf;
+
+ if (! service)
+ return (false);
+
+ len = strlen (service);
+
+ /* .sh files are not init scripts */
+ if (len > 2 && service[len - 3] == '.' &&
+ service[len - 2] == 's' &&
+ service[len - 1] == 'h')
+ return (false);
+
+ file = rc_service_resolve (service);
+ if (stat (file, &buf) == 0 && buf.st_mode & S_IXUGO)
+ retval = true;
+ free (file);
+ return (retval);
+}
+librc_hidden_def(rc_service_exists)
+
+#define OPTSTR ". '%s'; echo \"${opts}\""
+char **rc_service_extra_commands (const char *service)
+{
+ char *svc;
+ char *cmd = NULL;
+ char *buffer = NULL;
+ char **commands = NULL;
+ char *token;
+ char *p = buffer;
+ FILE *fp;
+ int l;
+
+ if (! (svc = rc_service_resolve (service)))
+ return (NULL);
+
+ l = strlen (OPTSTR) + strlen (svc) + 1;
+ cmd = xmalloc (sizeof (char) * l);
+ snprintf (cmd, l, OPTSTR, svc);
+ free (svc);
+ if ((fp = popen (cmd, "r"))) {
+ buffer = xmalloc (sizeof (char) * RC_LINEBUFFER);
+ if (fgets (buffer, RC_LINEBUFFER, fp)) {
+ if (buffer[strlen (buffer) - 1] == '\n')
+ buffer[strlen (buffer) - 1] = '\0';
+ while ((token = strsep (&p, " ")))
+ rc_strlist_addsort (&commands, token);
+ }
+ pclose (fp);
+ free (buffer);
+ }
+ free (cmd);
+ return (commands);
+}
+librc_hidden_def(rc_service_extra_commands)
+
+#define DESCSTR ". '%s'; echo \"${description%s%s}\""
+char *rc_service_description (const char *service, const char *option)
+{
+ char *svc;
+ char *cmd = NULL;
+ char *buffer;
+ char *desc = NULL;
+ FILE *fp;
+ int i;
+ int l;
+
+ if (! (svc = rc_service_resolve (service)))
+ return (NULL);
+
+ if (! option)
+ option = "";
+
+ l = strlen (DESCSTR) + strlen (svc) + strlen (option) + 2;
+ cmd = xmalloc (sizeof (char) * l);
+ snprintf (cmd, l, DESCSTR, svc, option ? "_" : "", option);
+ free (svc);
+ if ((fp = popen (cmd, "r"))) {
+ buffer = xmalloc (sizeof (char) * RC_LINEBUFFER);
+ while (fgets (buffer, RC_LINEBUFFER, fp)) {
+ if (! desc) {
+ desc = xmalloc (strlen (buffer) + 1);
+ *desc = '\0';
+ } else {
+ desc = xrealloc (desc, strlen (desc) + strlen (buffer) + 1);
+ }
+ i = strlen (desc);
+ memcpy (desc + i, buffer, strlen (buffer));
+ memset (desc + i + strlen (buffer), 0, 1);
+ }
+ free (buffer);
+ pclose (fp);
+ }
+ free (cmd);
+ return (desc);
+}
+librc_hidden_def(rc_service_description)
+
+bool rc_service_in_runlevel (const char *service, const char *runlevel)
+{
+ char *file;
+ bool retval;
+
+ if (! runlevel || ! service)
+ return (false);
+
+ file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename_c (service),
+ (char *) NULL);
+ retval = exists (file);
+ free (file);
+
+ return (retval);
+}
+librc_hidden_def(rc_service_in_runlevel)
+
+bool rc_service_mark (const char *service, const rc_service_state_t state)
+{
+ char *file;
+ int i = 0;
+ int skip_state = -1;
+ const char *base;
+ char *init = rc_service_resolve (service);
+ bool skip_wasinactive = false;
+
+ if (! init)
+ return (false);
+
+ base = basename_c (service);
+
+ if (state != RC_SERVICE_STOPPED) {
+ if (! exists (init)) {
+ free (init);
+ return (false);
+ }
+
+ file = rc_strcatpaths (RC_SVCDIR, rc_parse_service_state (state), base,
+ (char *) NULL);
+ if (exists (file))
+ unlink (file);
+ i = symlink (init, file);
+ if (i != 0) {
+ free (file);
+ free (init);
+ return (false);
+ }
+
+ free (file);
+ skip_state = state;
+ }
+
+ if (state == RC_SERVICE_COLDPLUGGED || state == RC_SERVICE_FAILED) {
+ free (init);
+ return (true);
+ }
+
+ /* Remove any old states now */
+ for (i = 0; rc_service_state_names[i].name; i++) {
+ int s = rc_service_state_names[i].state;
+
+ if ((s != skip_state &&
+ s != RC_SERVICE_STOPPED &&
+ s != RC_SERVICE_COLDPLUGGED &&
+ s != RC_SERVICE_SCHEDULED) &&
+ (! skip_wasinactive || s != RC_SERVICE_WASINACTIVE))
+ {
+ file = rc_strcatpaths (RC_SVCDIR, rc_parse_service_state (s), base,
+ (char *) NULL);
+ if (exists (file)) {
+ if ((state == RC_SERVICE_STARTING ||
+ state == RC_SERVICE_STOPPING) &&
+ s == RC_SERVICE_INACTIVE)
+ {
+ char *wasfile = rc_strcatpaths (RC_SVCDIR,
+ rc_parse_service_state (RC_SERVICE_WASINACTIVE),
+ base, (char *) NULL);
+
+ symlink (init, wasfile);
+ skip_wasinactive = true;
+ free (wasfile);
+ }
+ unlink (file);
+ }
+ free (file);
+ }
+ }
+
+ /* Remove the exclusive state if we're inactive */
+ if (state == RC_SERVICE_STARTED ||
+ state == RC_SERVICE_STOPPED ||
+ state == RC_SERVICE_INACTIVE)
+ {
+ file = rc_strcatpaths (RC_SVCDIR, "exclusive", base, (char *) NULL);
+ unlink (file);
+ free (file);
+ }
+
+ /* Remove any options and daemons the service may have stored */
+ if (state == RC_SERVICE_STOPPED) {
+ char *dir = rc_strcatpaths (RC_SVCDIR, "options", base, (char *) NULL);
+ rm_dir (dir, true);
+ free (dir);
+
+ dir = rc_strcatpaths (RC_SVCDIR, "daemons", base, (char *) NULL);
+ rm_dir (dir, true);
+ free (dir);
+
+ rc_service_schedule_clear (service);
+ }
+
+ /* These are final states, so remove us from scheduled */
+ if (state == RC_SERVICE_STARTED || state == RC_SERVICE_STOPPED) {
+ char *sdir = rc_strcatpaths (RC_SVCDIR, "scheduled", (char *) NULL);
+ char **dirs = ls_dir (sdir, 0);
+ char *dir;
+ int serrno;
+
+ STRLIST_FOREACH (dirs, dir, i) {
+ char *bdir = rc_strcatpaths (sdir, dir, (char *) NULL);
+ file = rc_strcatpaths (bdir, base, (char *) NULL);
+ unlink (file);
+ free (file);
+
+ /* Try and remove the dir - we don't care about errors */
+ serrno = errno;
+ rmdir (bdir);
+ errno = serrno;
+ free (bdir);
+ }
+ rc_strlist_free (dirs);
+ free (sdir);
+ }
+
+ free (init);
+ return (true);
+}
+librc_hidden_def(rc_service_mark)
+
+rc_service_state_t rc_service_state (const char *service)
+{
+ int i;
+ int state = RC_SERVICE_STOPPED;
+
+ for (i = 0; rc_service_state_names[i].name; i++) {
+ char *file = rc_strcatpaths (RC_SVCDIR, rc_service_state_names[i].name,
+ basename_c (service), (char*) NULL);
+ if (exists (file)) {
+ if (rc_service_state_names[i].state <= 0x10)
+ state = rc_service_state_names[i].state;
+ else
+ state |= rc_service_state_names[i].state;
+ }
+ free (file);
+ }
+
+ if (state & RC_SERVICE_STOPPED) {
+ char **services = rc_services_scheduled_by (service);
+ if (services) {
+ state |= RC_SERVICE_SCHEDULED;
+ free (services);
+ }
+ }
+
+ return (state);
+}
+librc_hidden_def(rc_service_state)
+
+char *rc_service_value_get (const char *service, const char *option)
+{
+ FILE *fp;
+ char *buffer = NULL;
+ char *file = rc_strcatpaths (RC_SVCDIR, "options", service, option,
+ (char *) NULL);
+
+ if ((fp = fopen (file, "r"))) {
+ buffer = xmalloc (sizeof (char) * RC_LINEBUFFER);
+ fgets (buffer, RC_LINEBUFFER, fp);
+ fclose (fp);
+ }
+ free (file);
+
+ return (buffer);
+}
+librc_hidden_def(rc_service_value_get)
+
+bool rc_service_value_set (const char *service, const char *option,
+ const char *value)
+{
+ FILE *fp;
+ char *path = rc_strcatpaths (RC_SVCDIR, "options", service, (char *) NULL);
+ char *file = rc_strcatpaths (path, option, (char *) NULL);
+ bool retval = false;
+
+ if (mkdir (path, 0755) != 0 && errno != EEXIST) {
+ free (path);
+ free (file);
+ return (false);
+ }
+
+ if ((fp = fopen (file, "w"))) {
+ if (value)
+ fprintf (fp, "%s", value);
+ fclose (fp);
+ retval = true;
+ }
+
+ free (path);
+ free (file);
+ return (retval);
+}
+librc_hidden_def(rc_service_value_set)
+
+static pid_t _exec_service (const char *service, const char *arg)
+{
+ char *file;
+ char *fifo;
+ pid_t pid = -1;
+
+ file = rc_service_resolve (service);
+ if (! exists (file)) {
+ rc_service_mark (service, RC_SERVICE_STOPPED);
+ free (file);
+ return (0);
+ }
+
+ /* We create a fifo so that other services can wait until we complete */
+ fifo = rc_strcatpaths (RC_SVCDIR, "exclusive", basename_c (service),
+ (char *) NULL);
+
+ if (mkfifo (fifo, 0600) != 0 && errno != EEXIST) {
+ free (fifo);
+ free (file);
+ return (-1);
+ }
+
+ if ((pid = vfork ()) == 0) {
+ execl (file, file, arg, (char *) NULL);
+ fprintf (stderr, "unable to exec `%s': %s\n", file, strerror (errno));
+ unlink (fifo);
+ _exit (EXIT_FAILURE);
+ }
+
+ free (fifo);
+ free (file);
+
+ if (pid == -1)
+ fprintf (stderr, "vfork: %s\n", strerror (errno));
+
+ return (pid);
+}
+
+pid_t rc_service_stop (const char *service)
+{
+ rc_service_state_t state = rc_service_state (service);
+
+ if (state & RC_SERVICE_FAILED)
+ return (-1);
+
+ if (state & RC_SERVICE_STOPPED)
+ return (0);
+
+ return (_exec_service (service, "stop"));
+}
+librc_hidden_def(rc_service_stop)
+
+pid_t rc_service_start (const char *service)
+{
+ rc_service_state_t state = rc_service_state (service);
+
+ if (state & RC_SERVICE_FAILED)
+ return (-1);
+
+ if (! state & RC_SERVICE_STOPPED)
+ return (0);
+
+ return (_exec_service (service, "start"));
+}
+librc_hidden_def(rc_service_start)
+
+bool rc_service_schedule_start (const char *service,
+ const char *service_to_start)
+{
+ char *dir;
+ char *init;
+ char *file;
+ bool retval;
+
+ /* service may be a provided service, like net */
+ if (! service || ! rc_service_exists (service_to_start))
+ return (false);
+
+ dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename_c (service),
+ (char *) NULL);
+ if (mkdir (dir, 0755) != 0 && errno != EEXIST) {
+ free (dir);
+ return (false);
+ }
+
+ init = rc_service_resolve (service_to_start);
+ file = rc_strcatpaths (dir, basename_c (service_to_start), (char *) NULL);
+ retval = (exists (file) || symlink (init, file) == 0);
+ free (init);
+ free (file);
+ free (dir);
+
+ return (retval);
+}
+librc_hidden_def(rc_service_schedule_start)
+
+bool rc_service_schedule_clear (const char *service)
+{
+ char *dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename_c (service),
+ (char *) NULL);
+ bool retval;
+
+ if (! (retval = rm_dir (dir, true)) && errno == ENOENT)
+ retval = true;
+ free (dir);
+ return (retval);
+}
+librc_hidden_def(rc_service_schedule_clear)
+
+
+char **rc_services_in_runlevel (const char *runlevel)
+{
+ char *dir;
+ char **list = NULL;
+
+ if (! runlevel) {
+ int i;
+ char **local = ls_dir (RC_INITDIR_LOCAL, LS_INITD);
+
+ list = ls_dir (RC_INITDIR, LS_INITD);
+ STRLIST_FOREACH (local, dir, i)
+ rc_strlist_addsortu (&list, dir);
+ rc_strlist_free (local);
+ return (list);
+ }
+
+ /* These special levels never contain any services */
+ if (strcmp (runlevel, RC_LEVEL_SYSINIT) == 0 ||
+ strcmp (runlevel, RC_LEVEL_SINGLE) == 0)
+ return (NULL);
+
+ dir = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, (char *) NULL);
+ list = ls_dir (dir, LS_INITD);
+ free (dir);
+ return (list);
+}
+librc_hidden_def(rc_services_in_runlevel)
+
+char **rc_services_in_state (rc_service_state_t state)
+{
+ char *dir = rc_strcatpaths (RC_SVCDIR, rc_parse_service_state (state),
+ (char *) NULL);
+ char **list = NULL;
+
+ if (state == RC_SERVICE_SCHEDULED) {
+ char **dirs = ls_dir (dir, 0);
+ char *d;
+ int i;
+
+ STRLIST_FOREACH (dirs, d, i) {
+ char *p = rc_strcatpaths (dir, d, (char *) NULL);
+ char **entries = ls_dir (p, LS_INITD);
+ char *e;
+ int j;
+
+ STRLIST_FOREACH (entries, e, j)
+ rc_strlist_addsortu (&list, e);
+
+ if (entries)
+ free (entries);
+ }
+
+ if (dirs)
+ free (dirs);
+ } else {
+ list = ls_dir (dir, LS_INITD);
+ }
+
+ free (dir);
+ return (list);
+}
+librc_hidden_def(rc_services_in_state)
+
+bool rc_service_add (const char *runlevel, const char *service)
+{
+ bool retval;
+ char *init;
+ char *file;
+
+ if (! rc_runlevel_exists (runlevel)) {
+ errno = ENOENT;
+ return (false);
+ }
+
+ if (rc_service_in_runlevel (service, runlevel)) {
+ errno = EEXIST;
+ return (false);
+ }
+
+ init = rc_service_resolve (service);
+
+ /* We need to ensure that only things in /etc/init.d are added
+ * to the boot runlevel */
+ if (strcmp (runlevel, RC_LEVEL_BOOT) == 0) {
+ char *tmp = xstrdup (init);
+ retval = (strcmp (dirname (tmp), RC_INITDIR) == 0);
+ free (tmp);
+ if (! retval) {
+ free (init);
+ errno = EPERM;
+ return (false);
+ }
+ }
+
+ file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename_c (service),
+ (char *) NULL);
+ retval = (symlink (init, file) == 0);
+ free (init);
+ free (file);
+ return (retval);
+}
+librc_hidden_def(rc_service_add)
+
+bool rc_service_delete (const char *runlevel, const char *service)
+{
+ char *file;
+ bool retval = false;
+
+ if (! runlevel || ! service)
+ return (false);
+
+ file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename_c (service),
+ (char *) NULL);
+ if (unlink (file) == 0)
+ retval = true;
+
+ free (file);
+ return (retval);
+}
+librc_hidden_def(rc_service_delete)
+
+char **rc_services_scheduled_by (const char *service)
+{
+ char **dirs = ls_dir (RC_SVCDIR "/scheduled", 0);
+ char **list = NULL;
+ char *dir;
+ int i;
+
+ STRLIST_FOREACH (dirs, dir, i) {
+ char *file = rc_strcatpaths (RC_SVCDIR, "scheduled", dir, service,
+ (char *) NULL);
+ if (exists (file))
+ rc_strlist_add (&list, file);
+ free (file);
+ }
+ rc_strlist_free (dirs);
+
+ return (list);
+}
+librc_hidden_def(rc_services_scheduled_by)
+
+char **rc_services_scheduled (const char *service)
+{
+ char *dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename_c (service),
+ (char *) NULL);
+ char **list = NULL;
+
+ list = ls_dir (dir, LS_INITD);
+ free (dir);
+ return (list);
+}
+librc_hidden_def(rc_services_scheduled)
+
+bool rc_service_plugable (const char *service)
+{
+ char *list;
+ char *p;
+ char *star;
+ char *token;
+ bool allow = true;
+ char *match = getenv ("RC_PLUG_SERVICES");
+ if (! match)
+ return true;
+
+ list = xstrdup (match);
+ p = list;
+ while ((token = strsep (&p, " "))) {
+ bool truefalse = true;
+ if (token[0] == '!') {
+ truefalse = false;
+ token++;
+ }
+
+ star = strchr (token, '*');
+ if (star) {
+ if (strncmp (service, token, star - token) == 0) {
+ allow = truefalse;
+ break;
+ }
+ } else {
+ if (strcmp (service, token) == 0) {
+ allow = truefalse;
+ break;
+ }
+ }
+ }
+
+ free (list);
+ return (allow);
+}
+librc_hidden_def(rc_service_plugable)
diff --git a/src/librc/librc.h b/src/librc/librc.h
new file mode 100644
index 00000000..cf61217a
--- /dev/null
+++ b/src/librc/librc.h
@@ -0,0 +1,127 @@
+/*
+ * librc.h
+ * Internal header file to setup build env for files in librc.so
+ */
+
+/*
+ * 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.
+ */
+
+#ifndef _LIBRC_H_
+#define _LIBRC_H_
+
+#define _IN_LIBRC
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <regex.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+
+#if defined(__DragonFly__) || defined(__FreeBSD__) || \
+ defined(__NetBSD__) || defined (__OpenBSD__)
+#include <sys/param.h>
+#include <sys/user.h>
+#include <sys/sysctl.h>
+#include <kvm.h>
+#endif
+
+#include "librc-depend.h"
+#include "rc.h"
+#include "rc-misc.h"
+#include "strlist.h"
+
+#include "hidden-visibility.h"
+#define librc_hidden_proto(x) hidden_proto(x)
+#define librc_hidden_def(x) hidden_def(x)
+
+librc_hidden_proto(rc_config_list)
+librc_hidden_proto(rc_config_load)
+librc_hidden_proto(rc_config_value)
+librc_hidden_proto(rc_deptree_depend)
+librc_hidden_proto(rc_deptree_depends)
+librc_hidden_proto(rc_deptree_free)
+librc_hidden_proto(rc_deptree_load)
+librc_hidden_proto(rc_deptree_order)
+librc_hidden_proto(rc_deptree_update)
+librc_hidden_proto(rc_deptree_update_needed)
+librc_hidden_proto(rc_find_pids)
+librc_hidden_proto(rc_runlevel_exists)
+librc_hidden_proto(rc_runlevel_get)
+librc_hidden_proto(rc_runlevel_list)
+librc_hidden_proto(rc_runlevel_set)
+librc_hidden_proto(rc_runlevel_starting)
+librc_hidden_proto(rc_runlevel_stopping)
+librc_hidden_proto(rc_service_add)
+librc_hidden_proto(rc_service_daemons_crashed)
+librc_hidden_proto(rc_service_daemon_set)
+librc_hidden_proto(rc_service_delete)
+librc_hidden_proto(rc_service_description)
+librc_hidden_proto(rc_service_exists)
+librc_hidden_proto(rc_service_extra_commands)
+librc_hidden_proto(rc_service_in_runlevel)
+librc_hidden_proto(rc_service_mark)
+librc_hidden_proto(rc_service_plugable)
+librc_hidden_proto(rc_service_resolve)
+librc_hidden_proto(rc_service_schedule_clear)
+librc_hidden_proto(rc_service_schedule_start)
+librc_hidden_proto(rc_service_start)
+librc_hidden_proto(rc_service_stop)
+librc_hidden_proto(rc_services_in_runlevel)
+librc_hidden_proto(rc_services_in_state)
+librc_hidden_proto(rc_services_scheduled)
+librc_hidden_proto(rc_services_scheduled_by)
+librc_hidden_proto(rc_service_started_daemon)
+librc_hidden_proto(rc_service_state)
+librc_hidden_proto(rc_service_value_get)
+librc_hidden_proto(rc_service_value_set)
+librc_hidden_proto(rc_strcatpaths)
+librc_hidden_proto(rc_strlist_add)
+librc_hidden_proto(rc_strlist_addu)
+librc_hidden_proto(rc_strlist_addsort)
+librc_hidden_proto(rc_strlist_addsortc)
+librc_hidden_proto(rc_strlist_addsortu)
+librc_hidden_proto(rc_strlist_delete)
+librc_hidden_proto(rc_strlist_free)
+librc_hidden_proto(rc_strlist_join)
+librc_hidden_proto(rc_strlist_reverse)
+librc_hidden_proto(rc_yesno)
+
+#endif
diff --git a/src/librc/rc.h b/src/librc/rc.h
new file mode 100644
index 00000000..0020764e
--- /dev/null
+++ b/src/librc/rc.h
@@ -0,0 +1,446 @@
+/*
+ * 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.
+ */
+
+#ifndef __RC_H__
+#define __RC_H__
+
+#ifdef __GNUC__
+# define GCC_VERSION (__GNUC__ * 1000 + __GNUC__MINOR)
+# if (GCC_VERSION >= 3005)
+# define SENTINEL __attribute__ ((__sentinel__))
+# endif
+#endif
+#ifndef SENTINEL
+# define SENTINEL
+#endif
+
+#include <sys/types.h>
+#include <stdbool.h>
+
+/*! @name Reserved runlevel names */
+#define RC_LEVEL_SYSINIT "sysinit"
+#define RC_LEVEL_SINGLE "single"
+#define RC_LEVEL_SHUTDOWN "shutdown"
+#define RC_LEVEL_REBOOT "reboot"
+
+/*! Return the current runlevel.
+ * @return the current runlevel */
+char *rc_runlevel_get (void);
+
+/*! Checks if the runlevel exists or not
+ * @param runlevel to check
+ * @return true if the runlevel exists, otherwise false */
+bool rc_runlevel_exists (const char *runlevel);
+
+/*! Return a NULL terminated list of runlevels
+ * @return a NULL terminated list of runlevels */
+char **rc_runlevel_list (void);
+
+/*! Set the runlevel.
+ * This just changes the stored runlevel and does not start or stop any services.
+ * @param runlevel to store */
+bool rc_runlevel_set (const char *runlevel);
+
+/*! Is the runlevel starting?
+ * @return true if yes, otherwise false */
+bool rc_runlevel_starting (void);
+
+/*! Is the runlevel stopping?
+ * @return true if yes, otherwise false */
+bool rc_runlevel_stopping (void);
+
+/*! @name RC
+ * A service can be given as a full path or just its name.
+ * If its just a name then we try to resolve the service to a full path.
+ * This should allow the use if local init.d directories in the future. */
+
+/*! @brief States a service can be in */
+typedef enum
+{
+ /* These are actual states
+ * The service has to be in one only at all times */
+ RC_SERVICE_STOPPED = 0x0001,
+ RC_SERVICE_STARTED = 0x0002,
+ RC_SERVICE_STOPPING = 0x0004,
+ RC_SERVICE_STARTING = 0x0008,
+ RC_SERVICE_INACTIVE = 0x0010,
+
+ /* Service may or may not have been coldplugged */
+ RC_SERVICE_COLDPLUGGED = 0x0100,
+
+ /* Optional states service could also be in */
+ RC_SERVICE_FAILED = 0x0200,
+ RC_SERVICE_SCHEDULED = 0x0400,
+ RC_SERVICE_WASINACTIVE = 0x0800
+} rc_service_state_t;
+
+/*! Add the service to the runlevel
+ * @param runlevel to add to
+ * @param service to add
+ * @return true if successful, otherwise false */
+bool rc_service_add (const char *runlevel, const char *service);
+
+/*! Remove the service from the runlevel
+ * @param runlevel to remove from
+ * @param service to remove
+ * @return true if sucessful, otherwise false */
+bool rc_service_delete (const char *runlevel, const char *service);
+
+/*! Save the arguments to find a running daemon
+ * @param service to save arguments for
+ * @param exec that we started
+ * @param name of the process (optional)
+ * @param pidfile of the process (optional)
+ * @param started if true, add the arguments otherwise remove existing matching arguments */
+bool rc_service_daemon_set (const char *service, const char *exec,
+ const char *name, const char *pidfile,
+ bool started);
+
+/*! Returns a description of what the service and/or option does.
+ * @param service to check
+ * @param option to check (if NULL, service description)
+ * @return a newly allocated pointer to the description */
+char *rc_service_description (const char *service, const char *option);
+
+/*! Checks if a service exists or not.
+ * @param service to check
+ * @return true if service exists, otherwise false */
+bool rc_service_exists (const char *service);
+
+/*! Checks if a service is in a runlevel
+ * @param service to check
+ * @param runlevel it should be in
+ * @return true if service is in the runlevel, otherwise false */
+bool rc_service_in_runlevel (const char *service, const char *runlevel);
+
+/*! Marks the service state
+ * @param service to mark
+ * @param state service should be in
+ * @return true if service state change was successful, otherwise false */
+bool rc_service_mark (const char *service, rc_service_state_t state);
+
+/*! Lists the extra commands a service has
+ * @param service to load the commands from
+ * @return NULL terminated string list of commands */
+char **rc_service_extra_commands (const char *service);
+
+/*! Check if the service is allowed to be hot/cold plugged
+ * @param service to check
+ * @return true if allowed, otherwise false */
+bool rc_service_plugable (const char *service);
+
+/*! Resolves a service name to its full path.
+ * @param service to check
+ * @return pointer to full path of service */
+char *rc_service_resolve (const char *service);
+
+/*! Schedule a service to be started when another service starts
+ * @param service that starts the scheduled service when started
+ * @param service_to_start service that will be started */
+bool rc_service_schedule_start (const char *service,
+ const char *service_to_start);
+/*! Return a NULL terminated list of services that are scheduled to start
+ * when the given service has started
+ * @param service to check
+ * @return NULL terminated list of services scheduled to start */
+char **rc_services_scheduled_by (const char *service);
+
+/*! Clear the list of services scheduled to be started by this service
+ * @param service to clear
+ * @return true if no errors, otherwise false */
+bool rc_service_schedule_clear (const char *service);
+
+/*! Checks if a service in in a state
+ * @param service to check
+ * @return state of the service */
+rc_service_state_t rc_service_state (const char *service);
+
+/*! Start a service
+ * @param service to start
+ * @return pid of the service starting process */
+pid_t rc_service_start (const char *service);
+
+/*! Stop a service
+ * @param service to stop
+ * @return pid of service stopping process */
+pid_t rc_service_stop (const char *service);
+
+/*! Check if the service started the daemon
+ * @param service to check
+ * @param exec to check
+ * @param indx of the daemon (optional - 1st daemon, 2nd daemon, etc)
+ * @return true if started by this service, otherwise false */
+bool rc_service_started_daemon (const char *service, const char *exec,
+ int indx);
+
+/*! Return a saved value for a service
+ * @param service to check
+ * @param option to load
+ * @return saved value */
+char *rc_service_value_get (const char *service, const char *option);
+
+/*! Save a persistent value for a service
+ * @param service to save for
+ * @param option to save
+ * @param value of the option
+ * @return true if saved, otherwise false */
+bool rc_service_value_set (const char *service, const char *option,
+ const char *value);
+
+/*! List the services in a runlevel
+ * @param runlevel to list
+ * @return NULL terminated list of services */
+char **rc_services_in_runlevel (const char *runlevel);
+
+/*! List the services in a state
+ * @param state to list
+ * @return NULL terminated list of services */
+char **rc_services_in_state (rc_service_state_t state);
+
+/*! List the services shceduled to start when this one does
+ * @param service to check
+ * @return NULL terminated list of services */
+char **rc_services_scheduled (const char *service);
+
+/*! Checks that all daemons started with start-stop-daemon by the service
+ * are still running.
+ * @param service to check
+ * @return true if all daemons started are still running, otherwise false */
+bool rc_service_daemons_crashed (const char *service);
+
+/*! @name Dependency options
+ * These options can change the services found by the rc_get_depinfo and
+ * rc_get_depends functions. */
+/*! Trace provided services */
+#define RC_DEP_TRACE 0x01
+/*! Only use services added to runlevels */
+#define RC_DEP_STRICT 0x02
+/*! Runlevel is starting */
+#define RC_DEP_START 0x04
+/*! Runlevel is stopping */
+#define RC_DEP_STOP 0x08
+
+/*! @name Dependencies
+ * We analyse each init script and cache the resultant dependency tree.
+ * This tree can be accessed using the below functions. */
+
+#ifndef _IN_LIBRC
+/* Handles to internal structures */
+typedef void *rc_depinfo_t;
+#endif
+
+/*! Update the cached dependency tree if it's older than any init script,
+ * its configuration file or an external configuration file the init script
+ * has specified.
+ * @return true if successful, otherwise false */
+bool rc_deptree_update (void);
+
+/*! Check if the cached dependency tree is older than any init script,
+ * its configuration file or an external configuration file the init script
+ * has specified.
+ * @return true if it needs updating, otherwise false */
+bool rc_deptree_update_needed (void);
+
+/*! Load the cached dependency tree and return a pointer to it.
+ * This pointer should be freed with rc_deptree_free when done.
+ * @return pointer to the dependency tree */
+rc_depinfo_t *rc_deptree_load (void);
+
+/*! List the depend for the type of service
+ * @param deptree to search
+ * @param type to use (keywords, etc)
+ * @param service to check
+ * @return NULL terminated list of services in order */
+char **rc_deptree_depend (const rc_depinfo_t *deptree,
+ const char *type, const char *service);
+
+/*! List all the services in order that the given services have
+ * for the given types and options.
+ * @param deptree to search
+ * @param types to use (ineed, iuse, etc)
+ * @param services to check
+ * @param options to pass
+ * @return NULL terminated list of services in order */
+char **rc_deptree_depends (const rc_depinfo_t *deptree,
+ const char *const *types,
+ const char *const *services, const char *runlevel,
+ int options);
+
+/*! List all the services that should be stoppned and then started, in order,
+ * for the given runlevel, including sysinit and boot services where
+ * approriate.
+ * @param deptree to search
+ * @param runlevel to change into
+ * @param options to pass
+ * @return NULL terminated list of services in order */
+char **rc_deptree_order (const rc_depinfo_t *deptree, const char *runlevel,
+ int options);
+
+/*! Free a deptree and its information
+ * @param deptree to free */
+void rc_deptree_free (rc_depinfo_t *deptree);
+
+/*! @name Plugins
+ * For each plugin loaded we will call rc_plugin_hook with the below
+ * enum and either the runlevel name or service name.
+ *
+ * Plugins are called when rc does something. This does not indicate an
+ * end result and the plugin should use the above functions to query things
+ * like service status.
+ *
+ * The service hooks have extra ones - now and done. This is because after
+ * start_in we may start other services before we start the service in
+ * question. now shows we really will start the service now and done shows
+ * when we have done it as may start scheduled services at this point. */
+/*! Points at which a plugin can hook into RC */
+typedef enum
+{
+ RC_HOOK_RUNLEVEL_STOP_IN = 1,
+ RC_HOOK_RUNLEVEL_STOP_OUT = 4,
+ RC_HOOK_RUNLEVEL_START_IN = 5,
+ RC_HOOK_RUNLEVEL_START_OUT = 8,
+ /*! We send the abort if an init script requests we abort and drop
+ * into single user mode if system not fully booted */
+ RC_HOOK_ABORT = 99,
+ RC_HOOK_SERVICE_STOP_IN = 101,
+ RC_HOOK_SERVICE_STOP_NOW = 102,
+ RC_HOOK_SERVICE_STOP_DONE = 103,
+ RC_HOOK_SERVICE_STOP_OUT = 104,
+ RC_HOOK_SERVICE_START_IN = 105,
+ RC_HOOK_SERVICE_START_NOW = 106,
+ RC_HOOK_SERVICE_START_DONE = 107,
+ RC_HOOK_SERVICE_START_OUT = 108
+} rc_hook_t;
+
+/*! Plugin entry point
+ * @param hook point
+ * @param name of runlevel or service
+ * @return 0 for success otherwise -1 */
+int rc_plugin_hook (rc_hook_t hook, const char *name);
+
+/*! Plugins should write FOO=BAR to this fd to set any environment
+ * variables they wish. Variables should be separated by NULLs. */
+extern FILE *rc_environ_fd;
+
+/*! @name Configuration
+ * These functions help to deal with shell based configuration files */
+/*! Return a NULL terminated list of non comment lines from a file. */
+char **rc_config_list (const char *file);
+
+/*! Return a NULL terminated list of key=value lines from a file. */
+char **rc_config_load (const char *file);
+
+/*! Return the value of the entry from a key=value list. */
+char *rc_config_value (char **list, const char *entry);
+
+/*! Check if a variable is a boolean and return it's value.
+ * If variable is not a boolean then we set errno to be ENOENT when it does
+ * not exist or EINVAL if it's not a boolean.
+ * @param variable to check
+ * @return true if it matches true, yes or 1, false if otherwise. */
+bool rc_yesno (const char *variable);
+
+/*! @name String List functions
+ * Handy functions for dealing with string arrays of char **.
+ * It's safe to assume that any function here that uses char ** is a string
+ * list that can be manipulated with the below functions. Every string list
+ * should be released with a call to rc_strlist_free. */
+
+/*! Duplicate the item, add it to end of the list and return a pointer to it.
+ * @param list to add the item too
+ * @param item to add.
+ * @return pointer to newly added item */
+char *rc_strlist_add (char ***list, const char *item);
+
+/*! If the item does not exist in the list, duplicate it, add it to the
+ * list and then return a pointer to it.
+ * @param list to add the item too
+ * @param item to add.
+ * @return pointer to newly added item */
+char *rc_strlist_addu (char ***list, const char *item);
+
+/*! Duplicate the item, add it to the list at the point based on locale and
+ * then return a pointer to it.
+ * @param list to add the item too
+ * @param item to add.
+ * @return pointer to newly added item */
+char *rc_strlist_addsort (char ***list, const char *item);
+
+/*! Duplicate the item, add it to the list at the point based on C locale and
+ * then return a pointer to it.
+ * @param list to add the item too
+ * @param item to add.
+ * @return pointer to newly added item */
+char *rc_strlist_addsortc (char ***list, const char *item);
+
+/*! If the item does not exist in the list, duplicate it, add it to the
+ * list based on locale and then return a pointer to it.
+ * @param list to add the item too
+ * @param item to add.
+ * @return pointer to newly added item */
+char *rc_strlist_addsortu (char ***list, const char *item);
+
+/*! Free the item and remove it from the list. Return 0 on success otherwise -1.
+ * @param list to add the item too
+ * @param item to add.
+ * @return true on success, otherwise false */
+bool rc_strlist_delete (char ***list, const char *item);
+
+/*! Moves the contents of list2 onto list1, so list2 is effectively emptied.
+ * Returns a pointer to the last item on the new list.
+ * @param list1 to append to
+ * @param list2 to move from
+ * @return pointer to the last item on the list */
+char *rc_strlist_join (char ***list1, char **list2);
+
+/*! Reverses the contents of the list.
+ * @param list to reverse */
+void rc_strlist_reverse (char **list);
+
+/*! Frees each item on the list and the list itself.
+ * @param list to free */
+void rc_strlist_free (char **list);
+
+/*! Concatenate paths adding '/' if needed. The resultant pointer should be
+ * freed when finished with.
+ * @param path1 starting path
+ * @param paths NULL terminated list of paths to add
+ * @return pointer to the new path */
+char *rc_strcatpaths (const char *path1, const char *paths, ...) SENTINEL;
+
+/*! Find processes based on criteria.
+ * All of these are optional.
+ * pid overrides anything else.
+ * If both exec and cmd are given then we ignore exec.
+ * @param exec to check for
+ * @param cmd to check for
+ * @param uid to check for
+ * @param pid to check for
+ * @return NULL terminated list of pids */
+pid_t *rc_find_pids (const char *exec, const char *cmd,
+ uid_t uid, pid_t pid);
+
+#endif