aboutsummaryrefslogtreecommitdiff
path: root/src/librc/librc-depend.c
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2008-01-05 19:25:55 +0000
committerRoy Marples <roy@marples.name>2008-01-05 19:25:55 +0000
commitac21d75300dabe83578e4373fcfd09d67c3a083b (patch)
treed5f8e2a16920add2277c79ff8a1b7e99ec2976df /src/librc/librc-depend.c
parent112fbde453d55c49b7999d2e35496a8758aaa7b5 (diff)
Add some .mk stubs to impersonate bsk .mk files to make writing our Makefiles easier. libeinfo, librc and rc now have their own seperate directories. More work is needed to tidy this up though.
Diffstat (limited to 'src/librc/librc-depend.c')
-rw-r--r--src/librc/librc-depend.c979
1 files changed, 979 insertions, 0 deletions
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)