aboutsummaryrefslogtreecommitdiff
path: root/src/librc-depend.c
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2007-04-05 11:18:42 +0000
committerRoy Marples <roy@marples.name>2007-04-05 11:18:42 +0000
commit5af58b45146ab5253ca964738f4e45287bf963d4 (patch)
tree68d3a9a61fa55dd7fe273db776c375f797edaa5b /src/librc-depend.c
Rewrite the core parts in C. We now provide librc so other programs can
query runlevels, services and state without using bash. We also provide libeinfo so other programs can easily use our informational functions. As such, we have dropped the requirement of using bash as the init script shell. We now use /bin/sh and have strived to make the scripts as portable as possible. Shells that work are bash and dash. busybox works provided you disable s-s-d. If you have WIPE_TMP set to yes in conf.d/bootmisc you should disable find too. zsh and ksh do not work at this time. Networking support is currently being re-vamped also as it was heavily bash array based. As such, a new config format is available like so config_eth0="1.2.3.4/24 5.6.7.8/16" or like so config_eth0="'1.2.3.4 netmask 255.255.255.0' '5.6.7.8 netmask 255.255.0.0'" We will still support the old bash array format provided that /bin/sh IS a link it bash. ChangeLog for baselayout-1 can be found in our SVN repo.
Diffstat (limited to 'src/librc-depend.c')
-rw-r--r--src/librc-depend.c838
1 files changed, 838 insertions, 0 deletions
diff --git a/src/librc-depend.c b/src/librc-depend.c
new file mode 100644
index 00000000..0da93aa5
--- /dev/null
+++ b/src/librc-depend.c
@@ -0,0 +1,838 @@
+/*
+ librc-depend
+ rc service dependency and ordering
+ Copyright 2006-2007 Gentoo Foundation
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "einfo.h"
+#include "rc.h"
+#include "rc-misc.h"
+#include "strlist.h"
+
+#define GENDEP RC_LIBDIR "/sh/gendepends.sh"
+
+/* 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_free_deptree (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;
+ }
+}
+
+rc_depinfo_t *rc_load_deptree (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;
+
+ /* Update our deptree, but only if we need too */
+ rc_update_deptree (false);
+
+ 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 = rc_xmalloc (sizeof (rc_depinfo_t));
+ depinfo = deptree;
+ }
+ else
+ {
+ depinfo->next = rc_xmalloc (sizeof (rc_depinfo_t));
+ depinfo = depinfo->next;
+ }
+ memset (depinfo, 0, sizeof (rc_depinfo_t));
+ depinfo->service = strdup (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 = rc_xmalloc (sizeof (rc_deptype_t));
+ deptype = depinfo->depends;
+ memset (deptype, 0, sizeof (rc_deptype_t));
+ }
+ else
+ if (strcmp (deptype->type, type) != 0)
+ {
+ deptype->next = rc_xmalloc (sizeof (rc_deptype_t));
+ deptype = deptype->next;
+ memset (deptype, 0, sizeof (rc_deptype_t));
+ }
+
+ if (! deptype->type)
+ deptype->type = strdup (type);
+
+ deptype->services = rc_strlist_addsort (deptype->services, e);
+ }
+ fclose (fp);
+
+ return (deptree);
+}
+
+rc_depinfo_t *rc_get_depinfo (rc_depinfo_t *deptree, const char *service)
+{
+ rc_depinfo_t *di;
+
+ if (! deptree || ! service)
+ return (NULL);
+
+ for (di = deptree; di; di = di->next)
+ if (strcmp (di->service, service) == 0)
+ return (di);
+
+ return (NULL);
+}
+
+rc_deptype_t *rc_get_deptype (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);
+}
+
+static bool valid_service (const char *runlevel, const char *service)
+{
+ return ((strcmp (runlevel, RC_LEVEL_BOOT) != 0 &&
+ rc_service_in_runlevel (service, RC_LEVEL_BOOT)) ||
+ rc_service_in_runlevel (service, runlevel) ||
+ rc_service_state (service, rc_service_coldplugged) ||
+ rc_service_state (service, rc_service_started));
+}
+
+static bool get_provided1 (const char *runlevel, struct lhead *providers,
+ rc_deptype_t *deptype,
+ const char *level, bool coldplugged,
+ bool started, bool inactive)
+{
+ char *service;
+ int i;
+ bool retval = false;
+
+ STRLIST_FOREACH (deptype->services, service, i)
+ {
+ bool ok = true;
+ if (level)
+ ok = rc_service_in_runlevel (service, level);
+ else if (coldplugged)
+ ok = (rc_service_state (service, rc_service_coldplugged) &&
+ ! rc_service_in_runlevel (service, runlevel) &&
+ ! rc_service_in_runlevel (service, RC_LEVEL_BOOT));
+
+ if (! ok)
+ continue;
+
+ if (started)
+ ok = (rc_service_state (service, rc_service_starting) ||
+ rc_service_state (service, rc_service_started) ||
+ rc_service_state (service, rc_service_stopping));
+ else if (inactive)
+ ok = rc_service_state (service, rc_service_inactive);
+
+ if (! ok)
+ continue;
+
+ retval = true;
+ providers->list = 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 (rc_depinfo_t *deptree, 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 = rc_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)
+ providers.list = rc_strlist_add (providers.list, service);
+
+ return (providers.list);
+ }
+
+ /* If we're strict, then only use what we have in our runlevel */
+ if (options & RC_DEP_STRICT)
+ {
+ STRLIST_FOREACH (dt->services, service, i)
+ if (rc_service_in_runlevel (service, runlevel))
+ providers.list = 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, true, false))
+ { DO }
+ if (get_provided1 (runlevel, &providers, dt, runlevel, false, false, true))
+ { DO }
+ if (get_provided1 (runlevel, &providers, dt, runlevel, false, false, false))
+ return (providers.list);
+
+ /* Check coldplugged started services */
+ if (get_provided1 (runlevel, &providers, dt, NULL, true, true, false))
+ { DO }
+
+ /* Check bootlevel if we're not in it */
+ if (strcmp (runlevel, RC_LEVEL_BOOT) != 0)
+ {
+ if (get_provided1 (runlevel, &providers, dt, RC_LEVEL_BOOT, false, true, false))
+ { DO }
+ if (get_provided1 (runlevel, &providers, dt, RC_LEVEL_BOOT, false, false, true))
+ { DO }
+ }
+
+ /* Check coldplugged inactive services */
+ if (get_provided1 (runlevel, &providers, dt, NULL, true, false, true))
+ { DO }
+
+ /* Check manually started */
+ if (get_provided1 (runlevel, &providers, dt, NULL, false, true, false))
+ { DO }
+ if (get_provided1 (runlevel, &providers, dt, NULL, false, false, true))
+ { DO }
+
+ /* Nothing started then. OK, lets get the stopped services */
+ if (get_provided1 (runlevel, &providers, dt, NULL, true, false, false))
+ return (providers.list);
+ if ((strcmp (runlevel, RC_LEVEL_BOOT) != 0)
+ && (get_provided1 (runlevel, &providers, dt, RC_LEVEL_BOOT, false, false, false)))
+ return (providers.list);
+
+ /* Still nothing? OK, list all services */
+ STRLIST_FOREACH (dt->services, service, i)
+ providers.list = rc_strlist_add (providers.list, service);
+
+ return (providers.list);
+}
+
+static void visit_service (rc_depinfo_t *deptree, char **types,
+ struct lhead *sorted, struct lhead *visited,
+ rc_depinfo_t *depinfo,
+ const char *runlevel, int options)
+{
+ int i, j, k;
+ char *lp, *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 */
+ visited->list = rc_strlist_add (visited->list, depinfo->service);
+
+ STRLIST_FOREACH (types, item, i)
+ {
+ if ((dt = rc_get_deptype (depinfo, item)))
+ {
+ STRLIST_FOREACH (dt->services, service, j)
+ {
+ if (! options & RC_DEP_TRACE || strcmp (item, "iprovide") == 0)
+ {
+ sorted->list = rc_strlist_add (sorted->list, service);
+ continue;
+ }
+
+ di = rc_get_depinfo (deptree, service);
+ if ((provides = get_provided (deptree, di, runlevel, options)))
+ {
+ STRLIST_FOREACH (provides, lp, k)
+ {
+ di = rc_get_depinfo (deptree, lp);
+ if (di && (strcmp (item, "ineed") == 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 ||
+ 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 = rc_get_deptype (depinfo, "iprovide")))
+ {
+ STRLIST_FOREACH (dt->services, service, i)
+ {
+ if ((di = rc_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 (! rc_get_deptype (depinfo, "providedby"))
+ sorted->list = rc_strlist_add (sorted->list, depinfo->service);
+}
+
+char **rc_get_depends (rc_depinfo_t *deptree,
+ char **types, char **services,
+ const char *runlevel, int options)
+{
+ struct lhead sorted;
+ struct lhead visited;
+ rc_depinfo_t *di;
+ char *service;
+ int i;
+
+ if (! deptree || ! types || ! services)
+ return (NULL);
+
+ memset (&sorted, 0, sizeof (struct lhead));
+ memset (&visited, 0, sizeof (struct lhead));
+
+ STRLIST_FOREACH (services, service, i)
+ {
+ di = rc_get_depinfo (deptree, service);
+ visit_service (deptree, types, &sorted, &visited, di, runlevel, options);
+ }
+
+ rc_strlist_free (visited.list);
+ return (sorted.list);
+}
+
+char **rc_order_services (rc_depinfo_t *deptree, const char *runlevel,
+ int options)
+{
+ char **list = NULL;
+ char **types = NULL;
+ char **services = NULL;
+ bool reverse = false;
+
+ if (! runlevel)
+ return (NULL);
+
+ /* 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_ls_dir (list, RC_SVCDIR_STARTING, RC_LS_INITD);
+ list = rc_ls_dir (list, RC_SVCDIR_INACTIVE, RC_LS_INITD);
+ list = rc_ls_dir (list, RC_SVCDIR_STARTED, RC_LS_INITD);
+ reverse = true;
+ }
+ else
+ {
+ list = rc_services_in_runlevel (runlevel);
+
+ /* Add coldplugged services */
+ list = rc_ls_dir (list, RC_SVCDIR_COLDPLUGGED, RC_LS_INITD);
+
+ /* If we're not the boot runlevel then add that too */
+ if (strcmp (runlevel, RC_LEVEL_BOOT) != 0)
+ {
+ char *path = rc_strcatpaths (RC_RUNLEVELDIR, RC_LEVEL_BOOT, NULL);
+ list = rc_ls_dir (list, path, RC_LS_INITD);
+ free (path);
+ }
+ }
+
+ /* Now we have our lists, we need to pull in any dependencies
+ and order them */
+ types = rc_strlist_add (NULL, "ineed");
+ types = rc_strlist_add (types, "iuse");
+ types = rc_strlist_add (types, "iafter");
+ services = rc_get_depends (deptree, types, list, runlevel,
+ RC_DEP_STRICT | RC_DEP_TRACE | options);
+ rc_strlist_free (list);
+ rc_strlist_free (types);
+
+ if (reverse)
+ rc_strlist_reverse (services);
+
+ return (services);
+}
+
+static bool is_newer_than (const char *file, const char *target)
+{
+ struct stat buf;
+ int mtime;
+
+ if (stat (file, &buf) != 0 || buf.st_size == 0)
+ return (false);
+ mtime = buf.st_mtime;
+
+ if (stat (target, &buf) != 0)
+ return (false);
+
+ if (mtime < buf.st_mtime)
+ return (false);
+
+ if (rc_is_dir (target))
+ {
+ char **targets = rc_ls_dir (NULL, target, 0);
+ char *t;
+ int i;
+ bool newer = true;
+ STRLIST_FOREACH (targets, t, i)
+ {
+ char *path = rc_strcatpaths (target, t, NULL);
+ newer = is_newer_than (file, path);
+ free (path);
+ if (! newer)
+ break;
+ }
+ rc_strlist_free (targets);
+ return (newer);
+ }
+
+ return (true);
+}
+
+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",
+ RC_SVCDIR "ebuffer",
+ NULL
+};
+
+/* 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
+ */
+int rc_update_deptree (bool force)
+{
+ char *depends;
+ char *service;
+ char *type;
+ char *depend;
+ int retval = 0;
+ FILE *fp;
+ rc_depinfo_t *deptree;
+ rc_depinfo_t *depinfo;
+ rc_depinfo_t *di;
+ rc_depinfo_t *last_depinfo = NULL;
+ rc_deptype_t *deptype;
+ rc_deptype_t *dt;
+ rc_deptype_t *last_deptype = NULL;
+ char buffer[RC_LINEBUFFER];
+ int len;
+ int i;
+ int j;
+ int k;
+ bool already_added;
+
+ /* Create base directories if needed */
+ for (i = 0; depdirs[i]; i++)
+ if (! rc_is_dir (depdirs[i]))
+ if (mkdir (depdirs[i], 0755) != 0)
+ eerrorx ("mkdir `%s': %s", depdirs[i], strerror (errno));
+
+ if (! force)
+ if (is_newer_than (RC_DEPTREE, RC_INITDIR) &&
+ is_newer_than (RC_DEPTREE, RC_CONFDIR) &&
+ is_newer_than (RC_DEPTREE, "/etc/rc.conf"))
+ return 0;
+
+ ebegin ("Caching service dependencies");
+
+ /* 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")) == NULL)
+ eerrorx ("popen: %s", strerror (errno));
+
+ deptree = rc_xmalloc (sizeof (rc_depinfo_t));
+ memset (deptree, 0, sizeof (rc_depinfo_t));
+ 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 = rc_xmalloc (sizeof (rc_depinfo_t));
+ depinfo = last_depinfo->next;
+ }
+ memset (depinfo, 0, sizeof (rc_depinfo_t));
+ depinfo->service = strdup (service);
+ }
+
+ /* We may not have any depends */
+ if (! type || ! depends)
+ continue;
+
+ 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 = rc_xmalloc (sizeof (rc_deptype_t));
+ deptype = depinfo->depends;
+ }
+ else
+ {
+ last_deptype->next = rc_xmalloc (sizeof (rc_deptype_t));
+ deptype = last_deptype->next;
+ }
+ memset (deptype, 0, sizeof (rc_deptype_t));
+ deptype->type = strdup (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;
+
+ /* .sh files are not init scripts */
+ len = strlen (depend);
+ if (len > 2 &&
+ depend[len - 3] == '.' &&
+ depend[len - 2] == 's' &&
+ depend[len - 1] == 'h')
+ continue;
+
+ deptype->services = rc_strlist_addsort (deptype->services, depend);
+ }
+
+ }
+ pclose (fp);
+
+ /* Phase 3 - add our providors to the tree */
+ for (depinfo = deptree; depinfo; depinfo = depinfo->next)
+ {
+ if ((deptype = rc_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 = rc_xmalloc (sizeof (rc_depinfo_t));
+ di = last_depinfo->next;
+ memset (di, 0, sizeof (rc_depinfo_t));
+ di->service = strdup (service);
+ }
+ }
+ }
+
+ /* Phase 4 - backreference our depends */
+ for (depinfo = deptree; depinfo; depinfo = depinfo->next)
+ {
+ for (i = 0; deppairs[i].depend; i++)
+ {
+ deptype = rc_get_deptype (depinfo, deppairs[i].depend);
+ if (! deptype)
+ continue;
+
+ STRLIST_FOREACH (deptype->services, service, j)
+ {
+ di = rc_get_depinfo (deptree, service);
+ if (! di)
+ {
+ if (strcmp (deptype->type, "ineed") == 0)
+ {
+ eerror ("Service `%s' needs non existant service `%s'",
+ depinfo->service, service);
+ retval = -1;
+ }
+ 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 = rc_xmalloc (sizeof (rc_deptype_t));
+ dt = di->depends;
+ }
+ else
+ {
+ last_deptype->next = rc_xmalloc (sizeof (rc_deptype_t));
+ dt = last_deptype->next;
+ }
+ memset (dt, 0, sizeof (rc_deptype_t));
+ dt->type = strdup (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)
+ dt->services = 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")) == NULL)
+ eerror ("fopen `%s': %s", RC_DEPTREE, strerror (errno));
+ else
+ {
+ 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);
+ }
+
+ rc_free_deptree (deptree);
+
+ eend (retval, "Failed to update the service dependency tree");
+ return (retval);
+}