summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/librcscripts/depend.c')
-rw-r--r--src/core/librcscripts/depend.c557
1 files changed, 557 insertions, 0 deletions
diff --git a/src/core/librcscripts/depend.c b/src/core/librcscripts/depend.c
new file mode 100644
index 0000000..4946326
--- /dev/null
+++ b/src/core/librcscripts/depend.c
@@ -0,0 +1,557 @@
+/*
+ * depend.c
+ *
+ * Dependancy engine for Gentoo style rc-scripts.
+ *
+ * Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Header$
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "debug.h"
+#include "depend.h"
+#include "list.h"
+#include "misc.h"
+
+LIST_HEAD(service_info_list);
+
+/* Names for service types (service_type_t) in depend.h.
+ * Note that this should sync with service_type_t */
+char *service_type_names[] = {
+ "NEED",
+ "NEED_ME",
+ "USE",
+ "USE_ME",
+ "BEFORE",
+ "AFTER",
+ "BROKEN",
+ "PROVIDE",
+ NULL
+};
+
+int __service_resolve_dependency(char *servicename, char *dependency, service_type_t type);
+
+service_info_t *service_get_info(char *servicename)
+{
+ service_info_t *info;
+
+ if ((NULL == servicename) || (0 == strlen(servicename))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return NULL;
+ }
+
+ list_for_each_entry(info, &service_info_list, node) {
+ if (NULL != info->name)
+ if (0 == strcmp(info->name, servicename))
+ return info;
+ }
+
+ /* We use this to check if a service exists, so rather do not
+ * add debugging, otherwise it is very noisy! */
+ /* DBG_MSG("Invalid service name '%s'!\n", servicename); */
+
+ return NULL;
+}
+
+int service_add(char *servicename)
+{
+ service_info_t *info;
+ service_info_t *sorted;
+ int count;
+
+ if ((NULL == servicename) || (0 == strlen(servicename))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return -1;
+ }
+
+ info = service_get_info(servicename);
+ if (NULL == info) {
+ DBG_MSG("Adding service '%s'.\n", servicename);
+
+ info = malloc(sizeof(service_info_t));
+ if (NULL == info) {
+ DBG_MSG("Failed to allocate service_info_t!\n");
+ return -1;
+ }
+
+ info->name = strndup(servicename, strlen(servicename));
+ if (NULL == info->name) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ free(info);
+ return -1;
+ }
+
+ for (count = 0; count < ALL_SERVICE_TYPE_T; count++)
+ info->depend_info[count] = NULL;
+ info->provide = NULL;
+
+ /* We want to keep the list sorted */
+ list_for_each_entry(sorted, &service_info_list, node) {
+ if (strcmp(sorted->name, servicename) > 0) {
+ break;
+ }
+ }
+
+ list_add_tail(&info->node, &sorted->node);
+
+ return 0;
+ } else {
+ DBG_MSG("Tried to add duplicate service '%s'!\n", servicename);
+ }
+
+ return -1;
+}
+
+int service_is_dependency(char *servicename, char *dependency, service_type_t type)
+{
+ service_info_t *info;
+ char *service;
+ int count = 0;
+
+ if ((NULL == servicename)
+ || (0 == strlen(servicename))
+ || (NULL == dependency)
+ || (0 == strlen(dependency))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return -1;
+ }
+
+ info = service_get_info(servicename);
+ if (NULL != info) {
+ STRING_LIST_FOR_EACH(info->depend_info[type], service, count) {
+ if (0 == strcmp(dependency, service))
+ return 0;
+ }
+ } else {
+ DBG_MSG("Invalid service name '%s'!\n", servicename);
+ }
+
+ return -1;
+}
+
+int service_add_dependency(char *servicename, char *dependency, service_type_t type)
+{
+ service_info_t *info;
+ char *tmp_buf;
+
+ if ((NULL == servicename)
+ || (0 == strlen(servicename))
+ || (NULL == dependency)
+ || (0 == strlen(dependency))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return -1;
+ }
+
+ info = service_get_info(servicename);
+ if (NULL != info) {
+ /* Do not add duplicates */
+ if (-1 == service_is_dependency(servicename, dependency, type)) {
+ DBG_MSG("Adding dependency '%s' of service '%s', type '%s'.\n",
+ dependency, servicename, service_type_names[type]);
+
+ tmp_buf = strndup(dependency, strlen(dependency));
+ if (NULL == tmp_buf) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ return -1;
+ }
+
+ STRING_LIST_ADD_SORT(info->depend_info[type], tmp_buf, error);
+ } else {
+ DBG_MSG("Duplicate dependency '%s' for service '%s', type '%s'!\n",
+ dependency, servicename, service_type_names[type]);
+ /* Rather do not fail here, as we add a lot of doubles
+ * during resolving of dependencies */
+ }
+
+ return 0;
+ } else {
+ DBG_MSG("Invalid service name '%s'!\n", servicename);
+ }
+
+error:
+ return -1;
+}
+
+int service_del_dependency(char *servicename, char *dependency, service_type_t type)
+{
+ service_info_t *info;
+
+ if ((NULL == servicename)
+ || (0 == strlen(servicename))
+ || (NULL == dependency)
+ || (0 == strlen(dependency))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return -1;
+ }
+
+ if (-1 == service_is_dependency(servicename, dependency, type)) {
+ DBG_MSG("Tried to remove invalid dependency '%s'!\n", dependency);
+ return -1;
+ }
+
+ info = service_get_info(servicename);
+ if (NULL != info) {
+ DBG_MSG("Removing dependency '%s' of service '%s', type '%s'.\n",
+ dependency , servicename, service_type_names[type]);
+
+ STRING_LIST_DEL(info->depend_info[type], dependency, error);
+ return 0;
+ } else {
+ DBG_MSG("Invalid service name '%s'!\n", servicename);
+ }
+
+error:
+ return -1;
+}
+
+service_info_t *service_get_virtual(char *virtual)
+{
+ service_info_t *info;
+
+ if ((NULL == virtual) || (0 == strlen(virtual))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return NULL;
+ }
+
+ list_for_each_entry(info, &service_info_list, node) {
+ if (NULL != info->provide)
+ if (0 == strcmp(info->provide, virtual))
+ return info;
+ }
+
+ /* We use this to check if a virtual exists, so rather do not
+ * add debugging, otherwise it is very noisy! */
+ /* DBG_MSG("Invalid service name '%s'!\n", virtual); */
+
+ return NULL;
+}
+
+int service_add_virtual(char *servicename, char* virtual)
+{
+ service_info_t *info;
+
+ if ((NULL == servicename)
+ || (0 == strlen(servicename))
+ || (NULL == virtual )
+ || (0 == strlen(virtual))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return -1;
+ }
+
+ if (NULL != service_get_info(virtual)) {
+ EERROR(" Cannot add provide '%s', as a service with the same name exists!\n",
+ virtual);
+ /* Do not fail here as we do have a service that resolves
+ * the virtual */
+ }
+
+ info = service_get_virtual(virtual);
+ if (NULL != info) {
+ /* We cannot have more than one service Providing a virtual */
+ EWARN(" Service '%s' already provides '%s'!;\n",
+ info->name, virtual);
+ EWARN(" Not adding service '%s'...\n", servicename);
+ /* Do not fail here as we do have a service that resolves
+ * the virtual */
+ } else {
+ info = service_get_info(servicename);
+ if (NULL != info) {
+ DBG_MSG("Adding virtual '%s' of service '%s'.\n",
+ virtual, servicename);
+
+ info->provide = strndup(virtual, strlen(virtual));
+ if (NULL == info->provide) {
+ DBG_MSG("Failed to allocate buffer!\n");
+ return -1;
+ }
+ } else {
+ DBG_MSG("Invalid service name '%s'!\n", servicename);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int service_set_mtime(char *servicename, time_t mtime)
+{
+ service_info_t *info;
+
+ if ((NULL == servicename) || (0 == strlen(servicename))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return -1;
+ }
+
+ info = service_get_info(servicename);
+ if (NULL != info) {
+ DBG_MSG("Setting mtime '%li' of service '%s'.\n",
+ mtime, servicename);
+
+ info->mtime = mtime;
+
+ return 0;
+ } else {
+ DBG_MSG("Invalid service name '%s'!\n", servicename);
+ }
+
+ return -1;
+}
+
+int __service_resolve_dependency(char *servicename, char *dependency, service_type_t type)
+{
+ service_info_t *info;
+ int retval;
+
+ if ((NULL == servicename)
+ || (0 == strlen(servicename))
+ || (NULL == dependency)
+ || (0 == strlen(dependency))) {
+ DBG_MSG("Invalid argument passed!\n");
+ return -1;
+ }
+
+ info = service_get_info(servicename);
+ if (NULL == info) {
+ DBG_MSG("Invalid service name passed!\n");
+ return -1;
+ }
+
+ DBG_MSG("Checking dependency '%s' of service '%s', type '%s'.\n",
+ dependency, servicename, service_type_names[type]);
+
+ /* If there are no existing service 'dependency', try to resolve
+ * possible virtual services */
+ info = service_get_info(dependency);
+ if (NULL == info) {
+ info = service_get_virtual(dependency);
+ if (NULL != info) {
+ DBG_MSG("Virtual '%s' -> '%s' for service '%s', type '%s'.\n",
+ dependency, info->name, servicename,
+ service_type_names[type]);
+
+ retval = service_del_dependency(servicename, dependency, type);
+ if (-1 == retval) {
+ DBG_MSG("Failed to delete dependency!\n");
+ return -1;
+ }
+
+ /* Add the actual service name for the virtual */
+ dependency = info->name;
+ retval = service_add_dependency(servicename, dependency, type);
+ if (-1 == retval) {
+ DBG_MSG("Failed to add dependency!\n");
+ return -1;
+ }
+ }
+ }
+
+ /* Handle 'need', as it is the only dependency type that should
+ * handle invalid database entries currently. */
+ if (NULL == info) {
+ if ((type == NEED) || (type == NEED_ME)) {
+ EWARN(" Can't find service '%s' needed by '%s'; continuing...\n",
+ dependency, servicename);
+
+ retval = service_add_dependency(servicename, dependency, BROKEN);
+ if (-1 == retval) {
+ DBG_MSG("Failed to add dependency!\n");
+ return -1;
+ }
+
+ /* Delete invalid entry */
+ goto remove;
+ }
+
+ /* For the rest, if the dependency is not 'net', just silently
+ * die without error. Should not be needed as we add a 'net'
+ * service manually before we start, but you never know ... */
+ if (0 != strcmp(dependency, "net")) {
+ /* Delete invalid entry */
+ goto remove;
+ }
+ }
+
+ /* Ugly bug ... if a service depends on itself, it creates a
+ * 'mini fork bomb' effect, and breaks things horribly ... */
+ if (0 == strcmp(servicename, dependency)) {
+ /* Dont work too well with the '*' before and after */
+ if ((type != BEFORE) && (type != AFTER))
+ EWARN(" Service '%s' can't depend on itself; continuing...\n",
+ servicename);
+
+ /* Delete invalid entry */
+ goto remove;
+ }
+
+ /* Currently only these depend/order types are supported */
+ if ((type == NEED)
+ || (type == USE)
+ || (type == BEFORE)
+ || (type == AFTER)) {
+ if (type == BEFORE) {
+ /* NEED and USE override BEFORE
+ * ('servicename' BEFORE 'dependency') */
+ if ((0 == service_is_dependency(servicename, dependency, NEED))
+ || (0 == service_is_dependency(servicename, dependency, USE))) {
+ /* Delete invalid entry */
+ goto remove;
+ }
+ }
+
+ if (type == AFTER) {
+ /* NEED and USE override AFTER
+ * ('servicename' AFTER 'dependency') */
+ if ((0 == service_is_dependency(dependency, servicename, NEED))
+ || (0 == service_is_dependency(dependency, servicename, USE))) {
+ /* Delete invalid entry */
+ goto remove;
+ }
+ }
+
+ /* We do not want to add circular dependencies ... */
+ if (0 == service_is_dependency(dependency, servicename, type)) {
+ EWARN(" Services '%s' and '%s' have circular\n",
+ servicename, dependency);
+ EWARN(" dependency of type '%s'; continuing...\n",
+ service_type_names[type]);
+
+ /* For now remove this dependency */
+ goto remove;
+ }
+
+ /* Reverse mapping */
+ if (type == NEED) {
+ retval = service_add_dependency(dependency, servicename, NEED_ME);
+ if (-1 == retval) {
+ DBG_MSG("Failed to add dependency!\n");
+ return -1;
+ }
+ }
+
+ /* Reverse mapping */
+ if (type == USE) {
+ retval = service_add_dependency(dependency, servicename, USE_ME);
+ if (-1 == retval) {
+ DBG_MSG("Failed to add dependency!\n");
+ return -1;
+ }
+ }
+
+ /* Reverse mapping */
+ if (type == BEFORE) {
+ retval = service_add_dependency(dependency, servicename, AFTER);
+ if (-1 == retval) {
+ DBG_MSG("Failed to add dependency!\n");
+ return -1;
+ }
+ }
+
+ /* Reverse mapping */
+ if (type == AFTER) {
+ retval = service_add_dependency(dependency, servicename, BEFORE);
+ if (-1 == retval) {
+ DBG_MSG("Failed to add dependency!\n");
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+
+remove:
+ /* Delete invalid entry */
+ DBG_MSG("Removing invalid dependency '%s' of service '%s', type '%s'.\n",
+ dependency, servicename, service_type_names[type]);
+
+ retval = service_del_dependency(servicename, dependency, type);
+ if (-1 == retval) {
+ DBG_MSG("Failed to delete dependency!\n");
+ return -1;
+ }
+
+ /* Here we should not die with error */
+ return 0;
+}
+
+int service_resolve_dependencies(void)
+{
+ service_info_t *info;
+ char *service;
+ char *next = NULL;
+ int count;
+
+ /* Add our 'net' service */
+ if (NULL == service_get_info("net")) {
+ if (-1 == service_add("net")) {
+ DBG_MSG("Failed to add virtual!\n");
+ return -1;
+ }
+ service_set_mtime("net", 0);
+ }
+
+ /* Calculate all virtuals */
+ list_for_each_entry(info, &service_info_list, node) {
+ STRING_LIST_FOR_EACH_SAFE(info->depend_info[PROVIDE], service, next, count)
+ if (-1 == service_add_virtual(info->name, service)) {
+ DBG_MSG("Failed to add virtual!\n");
+ return -1;
+ }
+ }
+
+ /* Now do NEED, USE, BEFORE and AFTER */
+ list_for_each_entry(info, &service_info_list, node) {
+ STRING_LIST_FOR_EACH_SAFE(info->depend_info[NEED], service, next, count) {
+ if (-1 == __service_resolve_dependency(info->name, service, NEED)) {
+ DBG_MSG("Failed to resolve dependency!\n");
+ return -1;
+ }
+ }
+ }
+ list_for_each_entry(info, &service_info_list, node) {
+ STRING_LIST_FOR_EACH_SAFE(info->depend_info[USE], service, next, count) {
+ if (-1 == __service_resolve_dependency(info->name, service, USE)) {
+ DBG_MSG("Failed to resolve dependency!\n");
+ return -1;
+ }
+ }
+ }
+ list_for_each_entry(info, &service_info_list, node) {
+ STRING_LIST_FOR_EACH_SAFE(info->depend_info[BEFORE], service, next, count) {
+ if (-1 == __service_resolve_dependency(info->name, service, BEFORE)) {
+ DBG_MSG("Failed to resolve dependency!\n");
+ return -1;
+ }
+ }
+ }
+ list_for_each_entry(info, &service_info_list, node) {
+ STRING_LIST_FOR_EACH_SAFE(info->depend_info[AFTER], service, next, count) {
+ if (-1 == __service_resolve_dependency(info->name, service, AFTER)) {
+ DBG_MSG("Failed to resolve dependency!\n");
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+