summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/Makefile.am14
-rw-r--r--src/core/configure.ac13
-rw-r--r--src/core/include/Makefile.am1
-rw-r--r--src/core/include/rcscripts/Makefile.am26
-rw-r--r--src/core/include/rcscripts/core/Makefile.am3
-rw-r--r--src/core/include/rcscripts/core/services.h39
-rw-r--r--src/core/include/rcscripts/rccore.h39
-rw-r--r--src/core/include/rcscripts/rcdefines.h.in37
-rw-r--r--src/core/include/rcscripts/rctypes.h43
-rw-r--r--src/core/include/rcscripts/rcutil.h45
-rw-r--r--src/core/include/rcscripts/util/Makefile.am10
-rw-r--r--src/core/include/rcscripts/util/config.h36
-rw-r--r--src/core/include/rcscripts/util/debug.h136
-rw-r--r--src/core/include/rcscripts/util/dynbuf.h66
-rw-r--r--src/core/include/rcscripts/util/file.h68
-rw-r--r--src/core/include/rcscripts/util/list.h446
-rw-r--r--src/core/include/rcscripts/util/simple-regex.h87
-rw-r--r--src/core/include/rcscripts/util/str_list.h212
-rw-r--r--src/core/include/rcscripts/util/string.h64
-rw-r--r--src/core/librccore/Makefile.am26
-rw-r--r--src/core/librccore/api/depend.h76
-rw-r--r--src/core/librccore/api/parse.h84
-rw-r--r--src/core/librccore/api/runlevels.h46
-rw-r--r--src/core/librccore/api/scripts.h43
-rw-r--r--src/core/librccore/depend.c672
-rw-r--r--src/core/librccore/init.c51
-rw-r--r--src/core/librccore/internal/rccore.h58
-rw-r--r--src/core/librccore/internal/services.h30
-rw-r--r--src/core/librccore/parse.c828
-rw-r--r--src/core/librccore/runlevels.c218
-rw-r--r--src/core/librccore/scripts.c254
-rw-r--r--src/core/librccore/services.c86
-rw-r--r--src/core/librcutil/Makefile.am19
-rw-r--r--src/core/librcutil/config.c205
-rw-r--r--src/core/librcutil/debug.c297
-rw-r--r--src/core/librcutil/dynbuf.c407
-rw-r--r--src/core/librcutil/file.c454
-rw-r--r--src/core/librcutil/librcutil.map62
-rw-r--r--src/core/librcutil/simple-regex.c869
-rw-r--r--src/core/librcutil/string.c109
-rw-r--r--src/core/src/Makefile.am12
-rw-r--r--src/core/src/depscan.c28
-rw-r--r--src/core/src/runscript.c19
-rw-r--r--src/core/tests/Makefile.am20
-rw-r--r--src/core/tests/test-dynbuf.c2
-rw-r--r--src/core/tests/test-mmap.c8
-rw-r--r--src/core/tests/test-regex.c2
-rw-r--r--src/core/tests/test-runlevels.c13
-rw-r--r--src/core/tests/test-services.c51
-rw-r--r--src/env_whitelist16
50 files changed, 6402 insertions, 48 deletions
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index c27343f..57f82f5 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -1,12 +1,18 @@
AUTOMAKE_OPTIONS = dist-bzip2 no-dist-gzip
SUBDIRS = \
- data \
- scripts \
- librcscripts \
- src \
+ data \
+ scripts \
+ include \
+ librcutil \
+ librccore \
+ src \
tests
EXTRA_DIST = \
autogen.sh \
ChangeLog.0
+
+librccore: librcutil
+src: librcutil librccore
+tests: librcutil librccore
diff --git a/src/core/configure.ac b/src/core/configure.ac
index 2e8e66b..a3b0cf7 100644
--- a/src/core/configure.ac
+++ b/src/core/configure.ac
@@ -1,5 +1,5 @@
AC_PREREQ([2.59])
-AC_INIT([rcscript-core], [0.1], [base-system@gentoo.org])
+AC_INIT([rcscripts], [0.1], [base-system@gentoo.org])
AM_INIT_AUTOMAKE
AC_CONFIG_HEADER([config.h])
@@ -10,6 +10,8 @@ AC_PROG_INSTALL
AC_PROG_MAKE_SET
AC_PROG_AWK
+AC_PATH_PROGS([RC_SHELL], [bash sh])
+
AC_ENABLE_SHARED
AC_DISABLE_STATIC
dnl Next four lines is a hack to prevent libtool checking for CXX/F77
@@ -19,7 +21,7 @@ m4_undefine([AC_PROG_F77])
m4_defun([AC_PROG_F77],[])
AC_PROG_LIBTOOL
-AC_PREFIX_DEFAULT([])
+dnl AC_PREFIX_DEFAULT([])
dnl Checks for libraries.
dnl Checks for header files.
@@ -99,7 +101,12 @@ AC_OUTPUT([
Makefile
scripts/Makefile
data/Makefile
- librcscripts/Makefile
+ include/Makefile
+ include/rcscripts/Makefile
+ include/rcscripts/core/Makefile
+ include/rcscripts/util/Makefile
+ librcutil/Makefile
+ librccore/Makefile
src/Makefile
tests/Makefile
])
diff --git a/src/core/include/Makefile.am b/src/core/include/Makefile.am
new file mode 100644
index 0000000..6757e13
--- /dev/null
+++ b/src/core/include/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = rcscripts
diff --git a/src/core/include/rcscripts/Makefile.am b/src/core/include/rcscripts/Makefile.am
new file mode 100644
index 0000000..651b389
--- /dev/null
+++ b/src/core/include/rcscripts/Makefile.am
@@ -0,0 +1,26 @@
+SUBDIRS = \
+ util \
+ core
+
+rcdir = $(pkgincludedir)
+rc_HEADERS = \
+ rctypes.h \
+ rcutil.h \
+ rccore.h
+nodist_rc_HEADERS = rcdefines.h
+
+rcdefines.h: $(srcdir)/rcdefines.h.in
+ @rm -f rcdefines.h
+ @echo "Generating rcdefines.h..."
+ @sed -e 's:@ETCDIR@:$(sysconfdir):g' \
+ -e 's:@BINDIR@:$(bindir):g' \
+ -e 's:@LIBDIR@:$(libdir):g' \
+ -e 's:@SBINDIR@:$(sbindir):g' \
+ -e 's:@VARDIR@:$(localstatedir):g' \
+ -e 's:@SHELL_PARSER@:$(RC_SHELL):g' \
+ $(srcdir)/rcdefines.h.in > rcdefines.h
+
+EXTRA_DIST = rcdefines.h.in
+
+CLEANFILES = rcdefines.h
+DISTCLEANFILES = $(CLEANFILES)
diff --git a/src/core/include/rcscripts/core/Makefile.am b/src/core/include/rcscripts/core/Makefile.am
new file mode 100644
index 0000000..b388d65
--- /dev/null
+++ b/src/core/include/rcscripts/core/Makefile.am
@@ -0,0 +1,3 @@
+rcdir = $(pkgincludedir)/core
+rc_HEADERS = \
+ services.h
diff --git a/src/core/include/rcscripts/core/services.h b/src/core/include/rcscripts/core/services.h
new file mode 100644
index 0000000..0785f3c
--- /dev/null
+++ b/src/core/include/rcscripts/core/services.h
@@ -0,0 +1,39 @@
+/*
+ * services.h
+ *
+ * Functions dealing with services.
+ *
+ * Copyright (C) 2004-2006 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$
+ */
+
+#ifndef __RC_SERVICES_H__
+#define __RC_SERVICES_H__
+
+typedef enum {
+ rc_service_coldplugged,
+ rc_service_starting,
+ rc_service_started,
+ rc_service_inactive,
+ rc_service_wasinactive,
+ rc_service_stopping
+} rc_service_state_t;
+
+bool rc_service_test_state (const char *service, rc_service_state_t state);
+
+#endif /* __RC_SERVICES_H__ */
diff --git a/src/core/include/rcscripts/rccore.h b/src/core/include/rcscripts/rccore.h
new file mode 100644
index 0000000..599fc54
--- /dev/null
+++ b/src/core/include/rcscripts/rccore.h
@@ -0,0 +1,39 @@
+/*
+ * rccore.h
+ *
+ * Core includes.
+ *
+ * Copyright (C) 2004-2006 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$
+ */
+
+#ifndef __RCCORE_H__
+#define __RCCORE_H__
+
+#include "rcscripts/rcdefines.h"
+#include "rcscripts/rcutil.h"
+
+#include "rcscripts/core/services.h"
+
+/* Initialize needed variables, etc. Should be called before anything else
+ * from the rccore library is used. Return 0 on success, else -1 and sets
+ * errno.
+ */
+int rc_init (void);
+
+#endif /* __RCCORE_H__ */
diff --git a/src/core/include/rcscripts/rcdefines.h.in b/src/core/include/rcscripts/rcdefines.h.in
new file mode 100644
index 0000000..17cade6
--- /dev/null
+++ b/src/core/include/rcscripts/rcdefines.h.in
@@ -0,0 +1,37 @@
+/*
+ * rcdefines.h
+ *
+ * Core defines.
+ *
+ * Copyright (C) 2004-2006 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$
+ */
+
+#ifndef __RCDEFINES_H__
+#define __RCDEFINES_H__
+
+#define RCSCRIPTS_ETCDIR "@ETCDIR@"
+#define RCSCRIPTS_CONFDDIR RCSCRIPTS_ETCDIR "/conf.d"
+#define RCSCRIPTS_INITDDIR RCSCRIPTS_ETCDIR "/init.d"
+#define RCSCRIPTS_LIBDIR "@LIBDIR@/rcscripts"
+#define RCSCRIPTS_SBINDIR "@SBINDIR@"
+#define RCSCRIPTS_VARDIR "@VARDIR@"
+
+#define SHELL_PARSER "@SHELL_PARSER@"
+
+#endif /* __RCDEFINES_H__ */
diff --git a/src/core/include/rcscripts/rctypes.h b/src/core/include/rcscripts/rctypes.h
new file mode 100644
index 0000000..6efab4a
--- /dev/null
+++ b/src/core/include/rcscripts/rctypes.h
@@ -0,0 +1,43 @@
+/*
+ * rctypes.h
+ *
+ * Misc types and macro's.
+ *
+ * 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$
+ */
+
+#ifndef __RCTYPES_H__
+#define __RCTYPES_H__
+
+/* Min/Max macro's */
+#ifdef MAX
+# undef MAX
+#endif
+#define MAX(_a, _b) (((_a) > (_b)) ? (_a) : (_b))
+#ifdef MIN
+# undef MIN
+#endif
+#define MIN(_a, _b) ((_a) > (_b) ? (_b) : (_a))
+
+typedef enum {
+ FALSE,
+ TRUE
+} bool;
+
+#endif /* __RCTYPES_H__ */
diff --git a/src/core/include/rcscripts/rcutil.h b/src/core/include/rcscripts/rcutil.h
new file mode 100644
index 0000000..f95d855
--- /dev/null
+++ b/src/core/include/rcscripts/rcutil.h
@@ -0,0 +1,45 @@
+/*
+ * rcutil.h
+ *
+ * Util defines.
+ *
+ * Copyright (C) 2004-2006 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$
+ */
+
+#ifndef __RCUTIL_H__
+#define __RCUTIL_H__
+
+#include <stddef.h>
+#include <sys/types.h>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "rcscripts/rctypes.h"
+#include "rcscripts/util/debug.h"
+#include "rcscripts/util/string.h"
+#include "rcscripts/util/file.h"
+#include "rcscripts/util/config.h"
+#include "rcscripts/util/list.h"
+#include "rcscripts/util/str_list.h"
+#include "rcscripts/util/dynbuf.h"
+#include "rcscripts/util/simple-regex.h"
+
+#endif /* __RCUTIL_H__ */
diff --git a/src/core/include/rcscripts/util/Makefile.am b/src/core/include/rcscripts/util/Makefile.am
new file mode 100644
index 0000000..01a3358
--- /dev/null
+++ b/src/core/include/rcscripts/util/Makefile.am
@@ -0,0 +1,10 @@
+rcdir = $(pkgincludedir)/util
+rc_HEADERS = \
+ debug.h \
+ dynbuf.h \
+ list.h \
+ string.h \
+ file.h \
+ config.h \
+ simple-regex.h \
+ str_list.h
diff --git a/src/core/include/rcscripts/util/config.h b/src/core/include/rcscripts/util/config.h
new file mode 100644
index 0000000..1381ac6
--- /dev/null
+++ b/src/core/include/rcscripts/util/config.h
@@ -0,0 +1,36 @@
+/*
+ * config.h
+ *
+ * Miscellaneous config related macro's and functions.
+ *
+ * Copyright (C) 2004-2006 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$
+ */
+
+#ifndef __RC_CONFIG_H__
+#define __RC_CONFIG_H__
+
+#include <stdio.h>
+
+/* The following return a pointer on success, or NULL with errno set on error.
+ * If it returned NULL, but errno is not set, then there was no error, but
+ * there is nothing to return. */
+char *rc_get_cnf_entry (const char *pathname, const char *entry);
+char ** rc_get_list_file (char **list, char *filename);
+
+#endif /* __RC_CONFIG_H__ */
diff --git a/src/core/include/rcscripts/util/debug.h b/src/core/include/rcscripts/util/debug.h
new file mode 100644
index 0000000..62c0c94
--- /dev/null
+++ b/src/core/include/rcscripts/util/debug.h
@@ -0,0 +1,136 @@
+/*
+ * debug.h
+ *
+ * Simle debugging/logging macro's and functions.
+ *
+ * Copyright (C) 2004-2006 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$
+ */
+
+#ifndef __RC_DEBUG_H__
+#define __RC_DEBUG_H__
+
+#include <errno.h>
+#include <stdio.h>
+
+#define save_errno() int old_errno = errno;
+#define restore_errno() errno = old_errno;
+#define saved_errno old_errno
+
+void
+debug_message (const char *file, const char *func, int line,
+ const char *format, ...);
+
+#define DBG_MSG(_format, _arg...) \
+ do { \
+ debug_message (__FILE__, __FUNCTION__, __LINE__, _format, ## _arg); \
+ } while (0)
+
+#define FATAL_ERROR() \
+ do { \
+ save_errno (); \
+ fprintf(stderr, "ERROR: file '%s', function '%s', line %i.\n", \
+ __FILE__, __FUNCTION__, __LINE__); \
+ restore_errno (); \
+ if (0 != errno) \
+ { \
+ perror("ERROR"); \
+ } \
+ exit(EXIT_FAILURE); \
+ } while (0)
+
+#define NEG_FATAL_ERROR(_x) \
+ do { \
+ if (-1 == _x) \
+ { \
+ FATAL_ERROR(); \
+ } \
+ } while (0)
+
+#define NULL_FATAL_ERROR(_x) \
+ do { \
+ if (NULL == _x) \
+ { \
+ FATAL_ERROR(); \
+ } \
+ } while (0)
+
+/*
+ * Functions to check validity of some types.
+ * They do not set errno.
+ */
+
+inline bool check_ptr (const void *ptr);
+inline bool check_str (const char *str);
+inline bool check_strv (char **str);
+inline bool check_fd (int fd);
+inline bool check_fp (FILE * fp);
+
+/*
+ * Functions and macro's to check validity of some types.
+ * They DO set errno to EINVAL.
+ */
+
+inline bool __check_arg_ptr (const void *ptr, const char *file, const char *func,
+ size_t line);
+inline bool __check_arg_str (const char *str, const char *file, const char *func,
+ size_t line);
+inline bool __check_arg_strv (char **str, const char *file, const char *func,
+ size_t line);
+inline bool __check_arg_fd (int fd, const char *file, const char *func,
+ size_t line);
+inline bool __check_arg_fp (FILE * fp, const char *file, const char *func,
+ size_t line);
+
+#define check_arg_ptr(_ptr) \
+ __check_arg_ptr (_ptr, __FILE__, __FUNCTION__, __LINE__)
+#define check_arg_str(_str) \
+ __check_arg_str (_str, __FILE__, __FUNCTION__, __LINE__)
+#define check_arg_strv(_str) \
+ __check_arg_strv (_str, __FILE__, __FUNCTION__, __LINE__)
+#define check_arg_fd(_fd) \
+ __check_arg_fd (_fd, __FILE__, __FUNCTION__, __LINE__)
+#define check_arg_fp(_fp) \
+ __check_arg_fp (_fp, __FILE__, __FUNCTION__, __LINE__)
+
+/*
+ * Various memory allocation functions and macro's.
+ * They set errno to ENOMEM and print debug info.
+ */
+
+inline void *__xcalloc (size_t nmemb, size_t size, const char *file,
+ const char *func, size_t line);
+inline void *__xmalloc (size_t size, const char *file, const char *func,
+ size_t line);
+inline void *__xrealloc (void *ptr, size_t size, const char *file,
+ const char *func, size_t line);
+
+#define xcalloc(_nmemb, _size) \
+ __xcalloc (_nmemb, _size, __FILE__, __FUNCTION__, __LINE__)
+#define xmalloc(_size) \
+ __xmalloc (_size, __FILE__, __FUNCTION__, __LINE__)
+#define xrealloc(_ptr, _size) \
+ __xrealloc (_ptr, _size, __FILE__, __FUNCTION__, __LINE__)
+
+inline char *__xstrndup (const char *str, size_t size, const char *file,
+ const char *func, size_t line);
+
+#define xstrndup(_str, _size) \
+ __xstrndup (_str, _size, __FILE__, __FUNCTION__, __LINE__)
+
+#endif /* __RC_DEBUG_H__ */
diff --git a/src/core/include/rcscripts/util/dynbuf.h b/src/core/include/rcscripts/util/dynbuf.h
new file mode 100644
index 0000000..313cdf5
--- /dev/null
+++ b/src/core/include/rcscripts/util/dynbuf.h
@@ -0,0 +1,66 @@
+/*
+ * dynbuf.h
+ *
+ * Dynamic allocated buffers.
+ *
+ * Copyright (C) 2004-2006 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$
+ */
+
+#ifndef __RC_DYNBUF_H__
+#define __RC_DYNBUF_H__
+
+#define DYNAMIC_BUFFER_SIZE (sizeof (char) * 2 * 1024)
+
+typedef struct
+{
+ char *data; /* Actual data */
+ size_t length; /* Length of data block */
+ size_t rd_index; /* Current read index */
+ size_t wr_index; /* Current write index */
+ bool file_map; /* File mapped as dynbuf */
+} dyn_buf_t;
+
+dyn_buf_t *new_dyn_buf (void);
+
+dyn_buf_t *new_dyn_buf_mmap_file (const char *name);
+
+void free_dyn_buf (dyn_buf_t *dynbuf);
+
+int write_dyn_buf (dyn_buf_t *dynbuf, const char *buf, size_t length);
+
+int write_dyn_buf_from_fd (int fd, dyn_buf_t *dynbuf, size_t length);
+
+int sprintf_dyn_buf (dyn_buf_t *dynbuf, const char *format, ...);
+
+int read_dyn_buf (dyn_buf_t *dynbuf, char *buf, size_t length);
+
+int read_dyn_buf_to_fd (int fd, dyn_buf_t *dynbuf, size_t length);
+
+char *read_line_dyn_buf (dyn_buf_t *dynbuf);
+
+bool dyn_buf_rd_eof (dyn_buf_t *dynbuf);
+
+inline bool check_dyn_buf (dyn_buf_t *dynbuf);
+inline bool __check_arg_dyn_buf (dyn_buf_t *dynbuf, const char *file,
+ const char *func, size_t line);
+
+#define check_arg_dyn_buf(_dynbuf) \
+ __check_arg_dyn_buf (_dynbuf, __FILE__, __FUNCTION__, __LINE__)
+
+#endif /* __RC_DYNBUF_H__ */
diff --git a/src/core/include/rcscripts/util/file.h b/src/core/include/rcscripts/util/file.h
new file mode 100644
index 0000000..e524db9
--- /dev/null
+++ b/src/core/include/rcscripts/util/file.h
@@ -0,0 +1,68 @@
+/*
+ * file.h
+ *
+ * Miscellaneous file related macro's and functions.
+ *
+ * Copyright (C) 2004-2006 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$
+ */
+
+#ifndef __RC_FILE_H__
+#define __RC_FILE_H__
+
+#include <stdio.h>
+
+/* Return true if filename '_name' ends in '_ext' */
+#define CHECK_FILE_EXTENSION(_name, _ext) \
+ ((check_str (_name)) && (check_str (_ext)) \
+ && (strlen (_name) > strlen (_ext)) \
+ && (0 == strncmp (&(_name[strlen(_name) - strlen(_ext)]), \
+ _ext, strlen(_ext))))
+
+/* The following functions do not care about errors - they only return
+ * 1 if 'pathname' exist, and is the type requested, or else 0.
+ * This is only if pathname is valid ... They also might clear errno */
+int rc_file_exists (const char *pathname);
+int rc_is_file (const char *pathname, int follow_link);
+int rc_is_link (const char *pathname);
+int rc_is_dir (const char *pathname, int follow_link);
+
+/* The following function do not care about errors - it only returns
+ * the mtime of 'pathname' if it exists, and is the type requested,
+ * or else 0. It also might clear errno */
+time_t rc_get_mtime (const char *pathname, int follow_link);
+
+/* The following functions return 0 on success, or -1 with errno set on error. */
+#if !defined(HAVE_REMOVE)
+int remove (const char *pathname);
+#endif
+int rc_mktree (const char *pathname, mode_t mode);
+int rc_rmtree (const char *pathname);
+
+/* The following return a pointer on success, or NULL with errno set on error.
+ * If it returned NULL, but errno is not set, then there was no error, but
+ * there is nothing to return. */
+char **rc_ls_dir (const char *pathname, int hidden);
+
+/* Below two functions (rc_file_map and rc_file_unmap) are from
+ * udev-050 (udev_utils.c). Please see misc.c for copyright info.
+ * (Some are slightly modified, please check udev for originals.) */
+int rc_file_map (const char *filename, char **buf, size_t * bufsize);
+void rc_file_unmap (char *buf, size_t bufsize);
+
+#endif /* __RC_FILE_H__ */
diff --git a/src/core/include/rcscripts/util/list.h b/src/core/include/rcscripts/util/list.h
new file mode 100644
index 0000000..e4c7a74
--- /dev/null
+++ b/src/core/include/rcscripts/util/list.h
@@ -0,0 +1,446 @@
+/*
+ * Copied from the Linux kernel source tree, version 2.6.0-test1.
+ *
+ * Licensed under the GPL v2 as per the whole kernel source tree.
+ *
+ * Ripped out the rcu stuff, as it's not needed.
+ *
+ * $Header$
+ */
+
+#ifndef __LINUX_LIST_H__
+#define __LINUX_LIST_H__
+
+//#include <linux/stddef.h>
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+//#include <linux/prefetch.h>
+static inline void prefetch(const void *x) {;}
+
+//#include <asm/system.h>
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1 ((void *) 0x00100100)
+#define LIST_POISON2 ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+ struct list_head *prev,
+ struct list_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+ __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->next = LIST_POISON1;
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+ struct list_head *head)
+{
+ __list_del(list->prev, list->next);
+ list_add_tail(list, head);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+static inline void __list_splice(struct list_head *list,
+ struct list_head *head)
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+ if (!list_empty(list))
+ __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+ struct list_head *head)
+{
+ if (!list_empty(list)) {
+ __list_splice(list, head);
+ INIT_LIST_HEAD(list);
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, prefetch(pos->next))
+
+/**
+ * __list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list backwards
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+ pos = pos->prev, prefetch(pos->prev))
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_entry - iterate over list of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member) \
+ for (pos = list_entry((head)->prev, typeof(*pos), member), \
+ prefetch(pos->member.prev); \
+ &pos->member != (head); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member), \
+ prefetch(pos->member.prev))
+
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member) \
+ for (pos = list_entry((head)->next, typeof(*pos), member), \
+ n = list_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL)
+
+static __inline__ int hlist_unhashed(struct hlist_node *h)
+{
+ return !h->pprev;
+}
+
+static __inline__ int hlist_empty(struct hlist_head *h)
+{
+ return !h->first;
+}
+
+static __inline__ void __hlist_del(struct hlist_node *n)
+{
+ struct hlist_node *next = n->next;
+ struct hlist_node **pprev = n->pprev;
+ *pprev = next;
+ if (next)
+ next->pprev = pprev;
+}
+
+static __inline__ void hlist_del(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->next = LIST_POISON1;
+ n->pprev = LIST_POISON2;
+}
+
+static __inline__ void hlist_del_init(struct hlist_node *n)
+{
+ if (n->pprev) {
+ __hlist_del(n);
+ INIT_HLIST_NODE(n);
+ }
+}
+
+static __inline__ void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+ n->next = first;
+ if (first)
+ first->pprev = &n->next;
+ h->first = n;
+ n->pprev = &h->first;
+}
+
+/* next must be != NULL */
+static __inline__ void hlist_add_before(struct hlist_node *n, struct hlist_node *next)
+{
+ n->pprev = next->pprev;
+ n->next = next;
+ next->pprev = &n->next;
+ *(n->pprev) = n;
+}
+
+static __inline__ void hlist_add_after(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ next->next = n->next;
+ *(next->pprev) = n;
+ n->next = next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+/* Cannot easily do prefetch unfortunately */
+#define hlist_for_each(pos, head) \
+ for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+ pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+ for (pos = (head)->first; n = pos ? pos->next : 0, pos; \
+ pos = n)
+
+/**
+ * hlist_for_each_entry - iterate over list of given type
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member) \
+ for (pos = (pos)->next; \
+ pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from existing point
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member) \
+ for (; pos && ({ prefetch(pos->next); 1;}) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos: the type * to use as a loop counter.
+ * @pos: the &struct hlist_node to use as a loop counter.
+ * @n: another &struct hlist_node to use as temporary storage
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
+ for (pos = (head)->first; \
+ pos && ({ n = pos->next; 1; }) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+ pos = n)
+
+#endif
diff --git a/src/core/include/rcscripts/util/simple-regex.h b/src/core/include/rcscripts/util/simple-regex.h
new file mode 100644
index 0000000..96d91e1
--- /dev/null
+++ b/src/core/include/rcscripts/util/simple-regex.h
@@ -0,0 +1,87 @@
+/*
+ * simple_regex.h
+ *
+ * Simle regex library.
+ *
+ * Copyright (C) 2004-2006 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$
+ */
+
+#ifndef __RC_SIMPLE_REGEX_H__
+#define __RC_SIMPLE_REGEX_H__
+
+#define REGEX_NO_MATCH 0 /* We have no match */
+#define REGEX_PARTIAL_MATCH 1 /* Some of the string matches the regex */
+#define REGEX_FULL_MATCH 2 /* The whole string matches the regex */
+
+/* Macro to fill in .data and .regex */
+#define FILL_REGEX_DATA(_regex_data, _string, _regex) \
+ do { \
+ _regex_data.data = _string; \
+ _regex_data.regex = _regex; \
+ } while (0)
+
+/* Fill in _regex_data with _data and _regex, on failure goto _error */
+#define DO_REGEX(_regex_data, _data, _regex, _error) \
+ do { \
+ FILL_REGEX_DATA(_regex_data, _data, _regex); \
+ if (-1 == match(&_regex_data)) \
+ { \
+ DBG_MSG("Could not do regex match!\n"); \
+ goto _error; \
+ } \
+ } while (0)
+
+/* Evaluate to true if we have some kind of match */
+#define REGEX_MATCH(_regex_data) \
+ ((REGEX_FULL_MATCH == _regex_data.match) \
+ || (REGEX_PARTIAL_MATCH == _regex_data.match))
+
+/* Same as above, but for use when _regex_data is a pointer */
+#define REGEX_MATCH_P(_regex_data) \
+ ((REGEX_FULL_MATCH == _regex_data->match) \
+ || (REGEX_PARTIAL_MATCH == _regex_data->match))
+
+typedef struct
+{
+ char *data; /* String to perform regex operation on */
+ char *regex; /* String containing regex to use */
+ int match; /* Will be set if there was a match. Check
+ * REGEX_*_MATCH above for possible values */
+ char *where; /* Pointer to where match starts in data */
+ size_t count; /* Count characters from data matched by regex */
+ size_t r_count; /* Count characters of regex used for match. This
+ * should normally be the lenght of regex, but might
+ * not be for some internal functions ... */
+} regex_data_t;
+
+/*
+ * Return:
+ *
+ * 0 - There was no error. If there was a match, regex_data->match
+ * - will be > 0 (this is the definitive check - if not true, the
+ * - other values of the struct may be bogus), regex_data->count
+ * - will be the amount of data that was matched (might be 0 for
+ * - some wildcards), and regex_data->r_count will be > 0.
+ *
+ * -1 - An error occured. Check errno for more info.
+ *
+ */
+int match (regex_data_t * regex_data);
+
+#endif /* __RC_SIMPLE_REGEX_H__ */
diff --git a/src/core/include/rcscripts/util/str_list.h b/src/core/include/rcscripts/util/str_list.h
new file mode 100644
index 0000000..3b63365
--- /dev/null
+++ b/src/core/include/rcscripts/util/str_list.h
@@ -0,0 +1,212 @@
+/*
+ * str_list.h
+ *
+ * String list macros.
+ *
+ * Copyright (C) 2004-2006 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$
+ */
+
+#ifndef __RC_STR_LIST_H__
+#define __RC_STR_LIST_H__
+
+/* Add a new item to a string list. If the pointer to the list is NULL,
+ * allocate enough memory for the amount of entries needed. Ditto for
+ * when it already exists, but we add one more entry than it can
+ * contain. The list is NULL terminated.
+ * NOTE: _only_ memory for the list are allocated, and not for the items - that
+ * should be done by relevant code (unlike str_list_del_item() that will
+ * free the memory) */
+#define str_list_add_item(_string_list, _item, _error) \
+ do { \
+ char **_tmp_p; \
+ int _i = 0; \
+ if (!check_str (_item)) \
+ { \
+ goto _error; \
+ } \
+ while ((NULL != _string_list) && (NULL != _string_list[_i])) \
+ { \
+ _i++; \
+ } \
+ /* Amount of entries + new + terminator */ \
+ _tmp_p = xrealloc (_string_list, sizeof (char *) * (_i + 2)); \
+ if (NULL == _tmp_p) \
+ { \
+ goto _error; \
+ } \
+ _string_list = _tmp_p; \
+ _string_list[_i] = _item; \
+ /* Terminator */ \
+ _string_list[_i+1] = NULL; \
+ } while (0)
+
+/* Add a new item to a string list (foundamental the same as above), but make
+ * sure we have all the items alphabetically sorted. */
+#define str_list_add_item_sorted(_string_list, _item, _error) \
+ do { \
+ char **_tmp_p; \
+ char *_str_p1; \
+ char *_str_p2; \
+ int _i = 0; \
+ if (!check_str (_item)) \
+ { \
+ goto _error; \
+ } \
+ while ((NULL != _string_list) && (NULL != _string_list[_i])) \
+ { \
+ _i++; \
+ } \
+ /* Amount of entries + new + terminator */ \
+ _tmp_p = xrealloc (_string_list, sizeof (char *) * (_i + 2)); \
+ if (NULL == _tmp_p) \
+ { \
+ goto _error; \
+ } \
+ _string_list = _tmp_p; \
+ if (0 == _i) \
+ { \
+ /* Needed so that the end NULL will propagate
+ * (iow, make sure our 'NULL != _str_p1' test below
+ * do not fail) */ \
+ _string_list[_i] = NULL; \
+ } \
+ /* Actual terminator that needs adding */ \
+ _string_list[_i+1] = NULL; \
+ _i = 0; \
+ /* See where we should insert the new item to have it all \
+ * alphabetically sorted */ \
+ while (NULL != _string_list[_i]) \
+ { \
+ if (strcmp (_string_list[_i], _item) > 0) \
+ { \
+ break; \
+ } \
+ _i++; \
+ } \
+ /* Now just insert the new item, and shift the rest one over.
+ * '_str_p2' is temporary storage to swap the indexes in a loop,
+ * and 'str_p1' is used to store the old value across the loop */ \
+ _str_p1 = _string_list[_i]; \
+ _string_list[_i] = _item; \
+ do { \
+ _i++;\
+ _str_p2 = _string_list[_i]; \
+ _string_list[_i] = _str_p1; \
+ _str_p1 = _str_p2; \
+ } while (NULL != _str_p1); \
+ } while (0)
+
+/* Delete one entry from the string list, and shift the rest down if the entry
+ * was not at the end. For now we do not resize the amount of entries the
+ * string list can contain, and free the memory for the matching item */
+#define str_list_del_item(_string_list, _item, _error) \
+ do { \
+ int _i = 0; \
+ if (!check_str (_item)) \
+ { \
+ goto _error; \
+ } \
+ if (NULL == _string_list) \
+ { \
+ errno = EINVAL; \
+ DBG_MSG ("Invalid string list passed!\n"); \
+ goto _error; \
+ } \
+ while (NULL != _string_list[_i]) \
+ { \
+ if (0 == strcmp (_item, _string_list[_i])) \
+ { \
+ break; \
+ } \
+ else \
+ { \
+ _i++; \
+ } \
+ } \
+ if (NULL == _string_list[_i]) \
+ { \
+ errno = EINVAL; \
+ DBG_MSG ("Invalid string list item passed!\n"); \
+ goto _error; \
+ } \
+ free (_string_list[_i]); \
+ /* Shift all the following items one forward */ \
+ do { \
+ _string_list[_i] = _string_list[_i+1]; \
+ /* This stupidity is to shutup gcc */ \
+ _i++; \
+ } while (NULL != _string_list[_i]); \
+ } while (0)
+
+/* Step through each entry in the string list, setting '_pos' to the
+ * beginning of the entry. '_counter' is used by the macro as index,
+ * but should not be used by code as index (or if really needed, then
+ * it should usually by +1 from what you expect, and should only be
+ * used in the scope of the macro) */
+#define str_list_for_each_item(_string_list, _pos, _counter) \
+ if ((NULL != _string_list) && (0 == (_counter = 0))) \
+ while (NULL != (_pos = _string_list[_counter++]))
+
+/* Same as above (with the same warning about '_counter'). Now we just
+ * have '_next' that are also used for indexing. Once again rather refrain
+ * from using it if not absolutely needed. The major difference to above,
+ * is that it should be safe from having the item removed from under you. */
+#define str_list_for_each_item_safe(_string_list, _pos, _next, _counter) \
+ if ((NULL != _string_list) && (0 == (_counter = 0))) \
+ /* First part of the while checks if this is the
+ * first loop, and if so setup _pos and _next
+ * and increment _counter */ \
+ while ((((0 == _counter) \
+ && (NULL != (_pos = _string_list[_counter])) \
+ && (_pos != (_next = _string_list[++_counter]))) \
+ /* Second part is when it is not the first loop
+ * and _pos was not removed from under us. We
+ * just increment _counter, and setup _pos and
+ * _next */ \
+ || ((0 != _counter) \
+ && (_pos == _string_list[_counter-1]) \
+ && (_next == _string_list[_counter]) \
+ && (NULL != (_pos = _string_list[_counter])) \
+ && (_pos != (_next = _string_list[++_counter]))) \
+ /* Last part is when _pos was removed from under
+ * us. We basically just setup _pos and _next,
+ * but leave _counter alone */ \
+ || ((0 != _counter) \
+ && (_pos != _string_list[_counter-1]) \
+ && (_next == _string_list[_counter-1]) \
+ && (NULL != (_pos = _string_list[_counter-1])) \
+ && (_pos != (_next = _string_list[_counter])))))
+
+/* Just free the whole string list */
+#define str_list_free(_string_list) \
+ do { \
+ if (NULL != _string_list) \
+ { \
+ int _i = 0; \
+ while (NULL != _string_list[_i]) \
+ { \
+ free (_string_list[_i]); \
+ _string_list[_i++] = NULL; \
+ } \
+ free (_string_list); \
+ _string_list = NULL; \
+ } \
+ } while (0)
+
+#endif /* __RC_STR_LIST_H__ */
diff --git a/src/core/include/rcscripts/util/string.h b/src/core/include/rcscripts/util/string.h
new file mode 100644
index 0000000..6d5d598
--- /dev/null
+++ b/src/core/include/rcscripts/util/string.h
@@ -0,0 +1,64 @@
+/*
+ * string.h
+ *
+ * Miscellaneous string related macro's and functions.
+ *
+ * Copyright (C) 2004-2006 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$
+ */
+
+#ifndef __RC_STRING_H__
+#define __RC_STRING_H__
+
+#include <stdio.h>
+
+/* Gentoo style e* printing macro's */
+#define EINFO(_args...) \
+ do { \
+ save_errno (); \
+ printf (" \033[32;01m*\033[0m " _args); \
+ restore_errno (); \
+ } while (0)
+
+#define EWARN(_args...) \
+ do { \
+ save_errno (); \
+ printf (" \033[33;01m*\033[0m " _args); \
+ restore_errno (); \
+ } while (0)
+
+#define EERROR(_args...) \
+ do { \
+ save_errno (); \
+ fprintf (stderr, " \033[31;01m*\033[0m " _args); \
+ restore_errno (); \
+ } while (0)
+
+/* String functions. Return a string on success, or NULL on error
+ * or no action taken. On error errno will be set.*/
+char *rc_memrepchr (char **str, char old, char _new, size_t size);
+/* Concat two paths adding '/' if needed. Memory will be allocated
+ * with the malloc() call. */
+char *rc_strcatpaths (const char *pathname1, const char *pathname2);
+
+/* Compat functions for GNU extensions */
+char *rc_strndup (const char *str, size_t size);
+/* Same as basename(3), but do not modify path */
+char *rc_basename (const char *path);
+
+#endif /* __RC_STRING_H__ */
diff --git a/src/core/librccore/Makefile.am b/src/core/librccore/Makefile.am
new file mode 100644
index 0000000..7513965
--- /dev/null
+++ b/src/core/librccore/Makefile.am
@@ -0,0 +1,26 @@
+AUTOMAKE_OPTIONS = foreign
+
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir)/include \
+ $(RCSCRIPTS_DEFINES)
+
+LIBRCUTIL = $(top_builddir)/librcutil/librcutil.la
+
+lib_LTLIBRARIES = librccore.la
+
+librccore_la_LIBADD = $(LIBRCUTIL)
+librccore_la_SOURCES = \
+ internal/rccore.h \
+ scripts.c \
+ api/scripts.h \
+ runlevels.c \
+ api/runlevels.h \
+ parse.c \
+ api/parse.h \
+ depend.c \
+ api/depend.h \
+ services.c \
+ internal/services.h \
+ init.c
diff --git a/src/core/librccore/api/depend.h b/src/core/librccore/api/depend.h
new file mode 100644
index 0000000..d6662c6
--- /dev/null
+++ b/src/core/librccore/api/depend.h
@@ -0,0 +1,76 @@
+/*
+ * depend.h
+ *
+ * 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$
+ */
+
+#ifndef __DEPEND_H__
+#define __DEPEND_H__
+
+/* Dependency types supported or still to be implemented */
+typedef enum
+{
+ NEED, /* All dependencies needed by specified service */
+ NEED_ME, /* All dependencies that need specified service */
+ USE, /* All dependencies used by specified service */
+ USE_ME, /* All dependencies that use specified service */
+ BEFORE, /* All services started before specified service */
+ AFTER, /* All services started after specified service */
+ BROKEN, /* All dependencies of type NEED missing for
+ specified service */
+ PROVIDE, /* All virtual services provided by specified service */
+ ALL_SERVICE_TYPE_T
+} service_type_t;
+
+/* Names for above service types (service_type_t).
+ * Note that this should sync with above service_type_t */
+extern char *service_type_names[];
+
+typedef struct
+{
+ struct list_head node;
+
+ char *name; /* Name of service */
+ char **depend_info[ALL_SERVICE_TYPE_T]; /* String lists for each service
+ type */
+ char *provide; /* Name of virtual service it
+ provides. This is only valid
+ after we resolving - thus after
+ service_resolve_dependencies() */
+ time_t mtime; /* Modification time of script */
+} service_info_t;
+
+struct list_head service_info_list;
+
+service_info_t *service_get_info (char *servicename);
+int service_add (char *servicename);
+int service_is_dependency (char *servicename, char *dependency,
+ service_type_t type);
+int service_add_dependency (char *servicename, char *dependency,
+ service_type_t type);
+int service_del_dependency (char *servicename, char *dependency,
+ service_type_t type);
+service_info_t *service_get_virtual (char *virtual);
+int service_add_virtual (char *servicename, char *virtual);
+int service_set_mtime (char *servicename, time_t mtime);
+int service_resolve_dependencies (void);
+
+#endif /* __DEPEND_H__ */
diff --git a/src/core/librccore/api/parse.h b/src/core/librccore/api/parse.h
new file mode 100644
index 0000000..b5e07fa
--- /dev/null
+++ b/src/core/librccore/api/parse.h
@@ -0,0 +1,84 @@
+/*
+ * parse.h
+ *
+ * Parser 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$
+ */
+
+#ifndef __PARSE_H__
+#define __PARSE_H__
+
+#define LEGACY_CACHE_FILE_NAME "deptree"
+
+#define FIELD_RCSCRIPT "RCSCRIPT"
+#define FIELD_NEED "NEED"
+#define FIELD_USE "USE"
+#define FIELD_BEFORE "BEFORE"
+#define FIELD_AFTER "AFTER"
+#define FIELD_PROVIDE "PROVIDE"
+#define FIELD_FAILED "FAILED"
+
+size_t generate_stage1 (dyn_buf_t *data);
+size_t generate_stage2 (dyn_buf_t *data);
+size_t read_stage2 (char **data);
+int write_stage2 (FILE * outfile);
+size_t generate_stage3 (char **data);
+size_t read_stage3 (char **data);
+int write_stage3 (FILE * outfile);
+int write_legacy_stage3 (FILE * output);
+int parse_cache (const dyn_buf_t *data);
+
+/*
+ * get_rcscripts()
+ * |
+ * V
+ * check_rcscripts_mtime() ------------------------------> read_stage3()
+ * | |
+ * | |
+ * V V
+ * generate_stage1() (Called by generate_stage2()) parse_cache()
+ * | |
+ * | |
+ * V |
+ * generate_stage2() ----> write_stage2() (Debugging) |
+ * | |
+ * | |
+ * | === parse_cache() |
+ * V | | |
+ * generate_stage3() ==| | |
+ * | | | |
+ * | | V |
+ * | === service_resolve_dependencies() |
+ * | |
+ * | |
+ * |-------> write_legacy_stage3() (Proof of Concept |
+ * | or Debugging) |
+ * | |
+ * V |
+ * write_stage3() |
+ * | |
+ * | V
+ * |<-------------------------------------------------------
+ * |
+ * V
+ *
+ */
+
+#endif /* __PARSE_H__ */
diff --git a/src/core/librccore/api/runlevels.h b/src/core/librccore/api/runlevels.h
new file mode 100644
index 0000000..029041b
--- /dev/null
+++ b/src/core/librccore/api/runlevels.h
@@ -0,0 +1,46 @@
+/*
+ * runlevels.h
+ *
+ * Functions dealing with runlevels.
+ *
+ * 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$
+ */
+
+#ifndef __RUNLEVELS_H__
+#define __RUNLEVELS_H__
+
+#define RUNLEVELS_DIR RCSCRIPTS_ETCDIR "/runlevels"
+
+typedef struct
+{
+ struct list_head node;
+
+ char *dirname; /* Name of this runlevel */
+ struct list_head entries; /* rcscript_info_t list of rc-scripts */
+} runlevel_info_t;
+
+struct list_head runlevel_list;
+
+int get_runlevels (void);
+
+runlevel_info_t *get_runlevel_info (const char *runlevel);
+
+bool is_runlevel (const char *runlevel);
+
+#endif /* __RUNLEVELS_H__ */
diff --git a/src/core/librccore/api/scripts.h b/src/core/librccore/api/scripts.h
new file mode 100644
index 0000000..75003b4
--- /dev/null
+++ b/src/core/librccore/api/scripts.h
@@ -0,0 +1,43 @@
+/*
+ * scripts.h
+ *
+ * Get info etc 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$
+ */
+
+#ifndef __SCRIPTS_H__
+#define __SCRIPTS_H__
+
+typedef struct
+{
+ struct list_head node;
+
+ char *filename;
+ time_t mtime;
+ time_t confd_mtime;
+} rcscript_info_t;
+
+struct list_head rcscript_list;
+
+int get_rcscripts (void);
+int check_rcscripts_mtime (const char *cachefile);
+rcscript_info_t *get_rcscript_info (const char *scriptname);
+
+#endif /* __SCRIPTS_H__ */
diff --git a/src/core/librccore/depend.c b/src/core/librccore/depend.c
new file mode 100644
index 0000000..0255547
--- /dev/null
+++ b/src/core/librccore/depend.c
@@ -0,0 +1,672 @@
+/*
+ * 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 "internal/rccore.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
+};
+
+static char *service_is_recursive_dependency (char *servicename,
+ char *dependency,
+ bool checkuse);
+static 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 (!check_arg_str (servicename))
+ 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 (!check_arg_str (servicename))
+ return -1;
+
+ info = service_get_info (servicename);
+ if (NULL == info)
+ {
+ DBG_MSG ("Adding service '%s'.\n", servicename);
+
+ info = xmalloc (sizeof (service_info_t));
+ if (NULL == info)
+ return -1;
+
+ info->name = xstrndup (servicename, strlen (servicename));
+ if (NULL == info->name)
+ {
+ 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 ((!check_arg_str (servicename)) || (!check_arg_str (dependency)))
+ return -1;
+
+ info = service_get_info (servicename);
+ if (NULL != info)
+ {
+ str_list_for_each_item (info->depend_info[type], service, count)
+ {
+ if (0 == strcmp (dependency, service))
+ return 0;
+ }
+ }
+ else
+ {
+ DBG_MSG ("Invalid service name '%s'!\n", servicename);
+ }
+
+ return -1;
+}
+
+char *
+service_is_recursive_dependency (char *servicename, char *dependency,
+ bool checkuse)
+{
+ service_info_t *info;
+ char *depend;
+ int count = 0;
+
+ if ((!check_arg_str (servicename)) || (!check_arg_str (dependency)))
+ return NULL;
+
+ info = service_get_info (dependency);
+ if (NULL != info)
+ {
+ str_list_for_each_item (info->depend_info[NEED_ME], depend, count)
+ {
+ if ((0 == service_is_dependency (servicename, depend, NEED))
+ || (0 == service_is_dependency (servicename, depend, USE)))
+ return depend;
+ }
+ if (checkuse)
+ {
+ str_list_for_each_item (info->depend_info[USE_ME], depend, count)
+ {
+ if ((0 == service_is_dependency (servicename, depend, NEED))
+ || (0 == service_is_dependency (servicename, depend, USE)))
+ return depend;
+ }
+ }
+ }
+ else
+ {
+ DBG_MSG ("Invalid service name '%s'!\n", servicename);
+ }
+
+ return NULL;
+}
+
+int
+service_add_dependency (char *servicename, char *dependency,
+ service_type_t type)
+{
+ service_info_t *info;
+ char *buf;
+
+ if ((!check_arg_str (servicename)) || (!check_arg_str (dependency)))
+ 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]);
+
+ buf = xstrndup (dependency, strlen (dependency));
+ if (NULL == buf)
+ return -1;
+
+ str_list_add_item_sorted (info->depend_info[type], 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 ((!check_arg_str (servicename)) || (!check_arg_str (dependency)))
+ 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]);
+
+ str_list_del_item (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 (!check_arg_str (virtual))
+ 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 ((!check_arg_str (servicename)) || (!check_arg_str (virtual)))
+ 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 = xstrndup (virtual, strlen (virtual));
+ if (NULL == info->provide)
+ 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 (!check_arg_str (servicename))
+ 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 ((!check_arg_str (servicename)) || (!check_arg_str (dependency)))
+ 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)
+ {
+ char *depend;
+
+ /* 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;
+ }
+
+ depend = service_is_recursive_dependency (servicename, dependency,
+ TRUE);
+ if (NULL != depend)
+ {
+ EWARN (" Service '%s' should be BEFORE service '%s', but '%s'\n",
+ servicename, dependency, depend);
+ EWARN (" needed by '%s', depends in return on '%s'!\n",
+ servicename, dependency);
+
+ /* Delete invalid entry */
+ goto remove;
+ }
+ }
+
+ if (type == AFTER)
+ {
+ char *depend;
+
+ /* 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;
+ }
+
+ depend = service_is_recursive_dependency (dependency, servicename,
+ TRUE);
+ if (NULL != depend)
+ {
+ EWARN (" Service '%s' should be AFTER service '%s', but '%s'\n",
+ servicename, dependency, depend);
+ EWARN (" needed by '%s', depends in return on '%s'!\n",
+ dependency, servicename);
+
+ /* 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 = NULL;
+ 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)
+ {
+ str_list_for_each_item_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)
+ {
+ str_list_for_each_item_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)
+ {
+ str_list_for_each_item_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)
+ {
+ str_list_for_each_item_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)
+ {
+ str_list_for_each_item_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;
+}
diff --git a/src/core/librccore/init.c b/src/core/librccore/init.c
new file mode 100644
index 0000000..8eb9247
--- /dev/null
+++ b/src/core/librccore/init.c
@@ -0,0 +1,51 @@
+/*
+ * init.c
+ *
+ * Functions dealing with initialization.
+ *
+ * Copyright (C) 2004-2006 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 <stdlib.h>
+
+char *rc_config_svcdir = NULL;
+
+#include "internal/rccore.h"
+
+static bool rc_initialized = FALSE;
+
+int
+rc_init (void)
+{
+ if (TRUE == rc_initialized)
+ return 0;
+
+ rc_config_svcdir = rc_get_cnf_entry (RC_CONFD_FILE_NAME, SVCDIR_CONFIG_ENTRY);
+ if (NULL == rc_config_svcdir)
+ {
+ DBG_MSG ("Failed to get config entry '%s'!\n", SVCDIR_CONFIG_ENTRY);
+ return -1;
+ }
+
+ rc_initialized = TRUE;
+
+ return 0;
+}
+
diff --git a/src/core/librccore/internal/rccore.h b/src/core/librccore/internal/rccore.h
new file mode 100644
index 0000000..cef67e1
--- /dev/null
+++ b/src/core/librccore/internal/rccore.h
@@ -0,0 +1,58 @@
+/*
+ * rccore.h
+ *
+ * Internal Core includes.
+ *
+ * Copyright (C) 2004-2006 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$
+ */
+
+#ifndef __INTERNAL_RCCORE_H__
+#define __INTERNAL_RCCORE_H__
+
+#include "rcscripts/rccore.h"
+#include "internal/services.h"
+
+#include "librccore/api/scripts.h"
+#include "librccore/api/runlevels.h"
+#include "librccore/api/parse.h"
+#include "librccore/api/depend.h"
+
+#define RC_CONF_FILE_NAME RCSCRIPTS_ETCDIR "/rc.conf"
+#define RC_CONFD_FILE_NAME RCSCRIPTS_CONFDDIR "/rc"
+
+#define SVCDIR_CONFIG_ENTRY "svcdir"
+
+#define SBIN_RC RCSCRIPTS_SBINDIR "/rc"
+#define PROFILE_ENV RCSCRIPTS_ETCDIR "/profile.env"
+
+#define RCSCRIPT_HELP RCSCRIPTS_LIBDIR "/sh/rc-help.sh"
+
+#define DEFAULT_PATH "PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin"
+
+#define SELINUX_LIB RCSCRIPTS_LIBDIR "/runscript_selinux.so"
+
+#define SYS_WHITELIST RCSCRIPTS_LIBDIR "/conf.d/env_whitelist"
+#define USR_WHITELIST RCSCRIPTS_CONFDDIR "/env_whitelist"
+
+#define SOFTLEVEL "SOFTLEVEL"
+
+/* Value of 'svcdir' in config files */
+extern char *rc_config_svcdir;
+
+#endif /* __INTERNAL_RCCORE_H__ */
diff --git a/src/core/librccore/internal/services.h b/src/core/librccore/internal/services.h
new file mode 100644
index 0000000..b9b46ae
--- /dev/null
+++ b/src/core/librccore/internal/services.h
@@ -0,0 +1,30 @@
+/*
+ * services.h
+ *
+ * Internal functions dealing with services.
+ *
+ * Copyright (C) 2004-2006 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$
+ */
+
+#ifndef __INTERNAL_RC_SERVICES_H__
+#define __INTERNAL_RC_SERVICES_H__
+
+#define RC_SYSINIT_STATE "/dev/.rcsysinit"
+
+#endif /* __INTERNAL_RC_SERVICES_H__ */
diff --git a/src/core/librccore/parse.c b/src/core/librccore/parse.c
new file mode 100644
index 0000000..944a852
--- /dev/null
+++ b/src/core/librccore/parse.c
@@ -0,0 +1,828 @@
+/*
+ * parse.c
+ *
+ * Parser 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 <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/poll.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include "internal/rccore.h"
+
+#define READ_PIPE 0
+#define WRITE_PIPE 1
+
+/* _pipe[0] is used to send data to the parent (thus the parent only use the
+ * read pipe, and the child uses the write pipe)
+ * _pipe[1] is used to send data to the child (thus the child only use the read
+ * pipe, and the parent uses the write pipe)
+ */
+#define PARENT_READ_PIPE(_pipe) (_pipe[0][READ_PIPE])
+#define PARENT_WRITE_PIPE(_pipe) (_pipe[1][WRITE_PIPE])
+#define CHILD_READ_PIPE(_pipe) (_pipe[1][READ_PIPE])
+#define CHILD_WRITE_PIPE(_pipe) (_pipe[0][WRITE_PIPE])
+
+#define PARSE_BUFFER_SIZE 256
+
+static size_t parse_rcscript (char *scriptname, dyn_buf_t *data);
+
+static size_t parse_print_start (dyn_buf_t *data);
+static size_t parse_print_header (char *scriptname, dyn_buf_t *data);
+static size_t parse_print_body (char *scriptname, dyn_buf_t *data);
+
+/* Return count on success, -1 on error. If it was critical, errno will be set. */
+size_t
+parse_rcscript (char *scriptname, dyn_buf_t *data)
+{
+ regex_data_t tmp_data;
+ dyn_buf_t *dynbuf = NULL;
+ char *buf = NULL;
+ size_t write_count = 0;
+ size_t tmp_count;
+
+ if (!check_arg_dyn_buf (data))
+ return -1;
+
+ if (!check_arg_str (scriptname))
+ return -1;
+
+ dynbuf = new_dyn_buf_mmap_file (scriptname);
+ if (NULL == dynbuf)
+ {
+ DBG_MSG ("Could not open '%s' for reading!\n", rc_basename (scriptname));
+ return -1;
+ }
+
+ DBG_MSG ("Parsing '%s'.\n", rc_basename (scriptname));
+
+ tmp_count = parse_print_header (rc_basename (scriptname), data);
+ if (-1 == tmp_count)
+ {
+ DBG_MSG ("Failed to call parse_print_header()!\n");
+ goto error;
+ }
+ write_count += tmp_count;
+
+ while (NULL != (buf = read_line_dyn_buf(dynbuf)))
+ {
+ /* Check for lines with comments, and skip them */
+ DO_REGEX (tmp_data, buf, "^[ \t]*#", error);
+ if (REGEX_MATCH (tmp_data))
+ {
+ free (buf);
+ continue;
+ }
+
+ /* If the line contains 'depend()', call parse_print_body () and break */
+ DO_REGEX (tmp_data, buf, "depend[ \t]*()[ \t]*{?", error);
+ if (REGEX_MATCH (tmp_data))
+ {
+ DBG_MSG ("Got 'depend()' function.\n");
+
+ tmp_count = parse_print_body (rc_basename (scriptname), data);
+ if (-1 == tmp_count)
+ {
+ DBG_MSG ("Failed to call parse_print_body()!\n");
+ goto error;
+ }
+
+ write_count += tmp_count;
+
+ /* This is the last loop */
+ free (buf);
+ break;
+ }
+
+ free (buf);
+ }
+
+ /* read_line_dyn_buf() returned NULL with errno set */
+ if ((NULL == buf) && (0 != errno))
+ {
+ DBG_MSG ("Failed to read line from dynamic buffer!\n");
+ free_dyn_buf (dynbuf);
+
+ return -1;
+ }
+
+ free_dyn_buf (dynbuf);
+
+ return write_count;
+
+error:
+ if (NULL != buf)
+ free (buf);
+ if (NULL != dynbuf)
+ free_dyn_buf (dynbuf);
+
+ return -1;
+}
+
+
+size_t
+generate_stage1 (dyn_buf_t *data)
+{
+ rcscript_info_t *info;
+ size_t write_count = 0;
+ size_t tmp_count;
+
+ if (!check_arg_dyn_buf (data))
+ return -1;
+
+ write_count = parse_print_start (data);
+ if (-1 == write_count)
+ {
+ DBG_MSG ("Failed to call parse_print_start()!\n");
+ return -1;
+ }
+
+ list_for_each_entry (info, &rcscript_list, node)
+ {
+ tmp_count = parse_rcscript (info->filename, data);
+ if (-1 == tmp_count)
+ {
+ DBG_MSG ("Failed to parse '%s'!\n", rc_basename (info->filename));
+
+ /* If 'errno' is set, it is critical (hopefully) */
+ if (0 != errno)
+ return -1;
+ }
+ else
+ {
+ write_count += tmp_count;
+ }
+ }
+
+ return write_count;
+}
+
+/* Empty signal handler for SIGPIPE */
+static void
+sig_handler (int signum)
+{
+ return;
+}
+
+/* Returns data's lenght on success, else -1 on error. */
+size_t
+generate_stage2 (dyn_buf_t *data)
+{
+ int pipe_fds[2][2] = { {0, 0}, {0, 0} };
+ pid_t child_pid;
+ size_t write_count = 0;
+ int old_errno = 0;
+
+ if (!check_arg_dyn_buf (data))
+ return -1;
+
+ /* Pipe to send data to parent */
+ if (-1 == pipe (pipe_fds[0]))
+ {
+ DBG_MSG ("Failed to open pipe!\n");
+ goto error;
+ }
+ /* Pipe to send data to child */
+ if (-1 == pipe (pipe_fds[1]))
+ {
+ DBG_MSG ("Failed to open pipe!\n");
+ /* Close parent_pfds */
+ goto error;
+ }
+
+ child_pid = fork ();
+ if (-1 == child_pid)
+ {
+ DBG_MSG ("Failed to fork()!\n");
+ /* Close all pipes */
+ goto error;
+ }
+ if (0 == child_pid)
+ {
+ /***
+ *** In child
+ ***/
+
+ char *const argv[] = {
+ "bash",
+ "--noprofile",
+ "--norc",
+ "--",
+ NULL
+ };
+
+ /* Close the sides of the pipes we do not use */
+ close (PARENT_WRITE_PIPE (pipe_fds));
+ close (PARENT_READ_PIPE (pipe_fds));
+
+ /* dup2 child side read pipe to STDIN */
+ dup2 (CHILD_READ_PIPE (pipe_fds), STDIN_FILENO);
+ /* dup2 child side write pipe to STDOUT */
+ dup2 (CHILD_WRITE_PIPE (pipe_fds), STDOUT_FILENO);
+
+ /* We need to be in RCSCRIPTS_INITDDIR for 'before'/'after' '*' to work */
+ if (-1 == chdir (RCSCRIPTS_INITDDIR))
+ {
+ DBG_MSG ("Failed to chdir to '%s'!\n", RCSCRIPTS_INITDDIR);
+ exit (EXIT_FAILURE);
+ }
+
+ if (-1 == execv (SHELL_PARSER, argv))
+ {
+ DBG_MSG ("Failed to execv %s!\n", SHELL_PARSER);
+ exit (EXIT_FAILURE);
+ }
+ }
+ else
+ {
+ /***
+ *** In parent
+ ***/
+
+ dyn_buf_t *stage1_data;
+ struct sigaction act_new;
+ struct sigaction act_old;
+ struct pollfd poll_fds[2];
+ int status = 0;
+
+ DBG_MSG ("Child pid = %i\n", child_pid);
+
+ /* Set signal handler for SIGPIPE to empty in case bash errors
+ * out. It will then close the write pipe, and instead of us
+ * getting SIGPIPE, we can handle the write error like normal.
+ */
+ memset (&act_new, 0x00, sizeof (act_new));
+ act_new.sa_handler = (void (*)(int)) sig_handler;
+ sigemptyset (&act_new.sa_mask);
+ act_new.sa_flags = 0;
+ sigaction (SIGPIPE, &act_new, &act_old);
+
+ /* Close the sides of the pipes we do not use */
+ close (CHILD_WRITE_PIPE (pipe_fds));
+ CHILD_WRITE_PIPE (pipe_fds) = 0;
+ close (CHILD_READ_PIPE (pipe_fds));
+ CHILD_READ_PIPE (pipe_fds) = 0;
+
+ stage1_data = new_dyn_buf ();
+ if (NULL == stage1_data)
+ {
+ DBG_MSG ("Failed to allocate dynamic buffer!\n");
+ goto error;
+ }
+
+ /* Pipe parse_rcscripts() to bash */
+ if (-1 == generate_stage1 (stage1_data))
+ {
+ DBG_MSG ("Failed to generate stage1!\n");
+ goto error;
+ }
+
+#if 0
+ int tmp_fd = open ("bar", O_CREAT | O_TRUNC | O_RDWR, 0600);
+ write (tmp_fd, stage1_data->data, stage1_data->wr_index);
+ close (tmp_fd);
+#endif
+
+ do
+ {
+ int tmp_count = 0;
+ int do_write = 0;
+ int do_read = 0;
+
+ /* Check if we can write or read */
+ poll_fds[WRITE_PIPE].fd = PARENT_WRITE_PIPE (pipe_fds);
+ poll_fds[WRITE_PIPE].events = POLLOUT;
+ poll_fds[READ_PIPE].fd = PARENT_READ_PIPE (pipe_fds);
+ poll_fds[READ_PIPE].events = POLLIN | POLLPRI;
+ if (!dyn_buf_rd_eof (stage1_data))
+ {
+ poll (poll_fds, 2, -1);
+ if (poll_fds[WRITE_PIPE].revents & POLLOUT)
+ do_write = 1;
+ }
+ else
+ {
+ poll (&(poll_fds[READ_PIPE]), 1, -1);
+ }
+ if ((poll_fds[READ_PIPE].revents & POLLIN)
+ || (poll_fds[READ_PIPE].revents & POLLPRI))
+ do_read = 1;
+
+ do
+ {
+ /* While we can write, or there is nothing to
+ * read, keep feeding the write pipe */
+ if ((dyn_buf_rd_eof (stage1_data))
+ || (1 == do_read)
+ || (1 != do_write))
+ break;
+
+ tmp_count = read_dyn_buf_to_fd (PARENT_WRITE_PIPE (pipe_fds),
+ stage1_data, PARSE_BUFFER_SIZE);
+ if ((-1 == tmp_count) && (EINTR != errno))
+ {
+ DBG_MSG ("Error writing to PARENT_WRITE_PIPE!\n");
+ goto failed;
+ }
+ /* We were interrupted, try to write again */
+ if (-1 == tmp_count)
+ {
+ errno = 0;
+ /* Make sure we retry */
+ tmp_count = 1;
+ continue;
+ }
+
+ /* Close the write pipe if we done
+ * writing to get a EOF signaled to
+ * bash */
+ if (dyn_buf_rd_eof (stage1_data))
+ {
+ close (PARENT_WRITE_PIPE (pipe_fds));
+ PARENT_WRITE_PIPE (pipe_fds) = 0;
+ }
+ }
+ while ((tmp_count > 0) && (!dyn_buf_rd_eof (stage1_data)));
+
+ /* Reset tmp_count for below read loop */
+ tmp_count = 0;
+
+ do
+ {
+ if (1 != do_read)
+ continue;
+
+ tmp_count = write_dyn_buf_from_fd (PARENT_READ_PIPE (pipe_fds),
+ data, PARSE_BUFFER_SIZE);
+ if ((-1 == tmp_count) && (EINTR != errno))
+ {
+ DBG_MSG ("Error reading PARENT_READ_PIPE!\n");
+ goto failed;
+ }
+ /* We were interrupted, try to read again */
+ if ((-1 == tmp_count) || (0 == tmp_count))
+ {
+ errno = 0;
+ continue;
+ }
+
+ write_count += tmp_count;
+ }
+ while (tmp_count > 0);
+ }
+ while (!(poll_fds[READ_PIPE].revents & POLLHUP));
+
+failed:
+ /* Set old_errno to disable child exit code checking below */
+ if (0 != errno)
+ old_errno = errno;
+
+ free_dyn_buf (stage1_data);
+
+ if (0 != PARENT_WRITE_PIPE (pipe_fds))
+ close (PARENT_WRITE_PIPE (pipe_fds));
+ close (PARENT_READ_PIPE (pipe_fds));
+
+ /* Restore the old signal handler for SIGPIPE */
+ sigaction (SIGPIPE, &act_old, NULL);
+
+ /* Wait for bash to finish */
+ waitpid (child_pid, &status, 0);
+ /* If old_errno is set, we had an error in the read loop, so do
+ * not worry about the child's exit code */
+ if (0 == old_errno)
+ {
+ if ((!WIFEXITED (status)) || (0 != WEXITSTATUS (status)))
+ {
+ /* FIXME: better errno ? */
+ errno = ECANCELED;
+ DBG_MSG ("Bash failed with status 0x%x!\n", status);
+
+ return -1;
+ }
+ }
+ else
+ {
+ /* Right, we had an error, so set errno, and exit */
+ errno = old_errno;
+ return -1;
+ }
+ }
+
+ return write_count;
+
+ /* Close parent side pipes */
+error:
+ /* Close all pipes */
+ old_errno = errno;
+ if (0 != CHILD_READ_PIPE (pipe_fds))
+ close (CHILD_READ_PIPE (pipe_fds));
+ if (0 != CHILD_WRITE_PIPE (pipe_fds))
+ close (CHILD_WRITE_PIPE (pipe_fds));
+ if (0 != PARENT_READ_PIPE (pipe_fds))
+ close (PARENT_READ_PIPE (pipe_fds));
+ if (0 != PARENT_WRITE_PIPE (pipe_fds))
+ close (PARENT_WRITE_PIPE (pipe_fds));
+ /* close() might have changed it */
+ errno = old_errno;
+
+ return -1;
+}
+
+int
+write_legacy_stage3 (FILE * output)
+{
+ service_info_t *info;
+ char *service;
+ int count;
+ int sindex = 0;
+ int dep_count;
+ int i;
+
+ if (!check_arg_fp (output))
+ return -1;
+
+ fprintf (output, "rc_type_ineed=2\n");
+ fprintf (output, "rc_type_needsme=3\n");
+ fprintf (output, "rc_type_iuse=4\n");
+ fprintf (output, "rc_type_usesme=5\n");
+ fprintf (output, "rc_type_ibefore=6\n");
+ fprintf (output, "rc_type_iafter=7\n");
+ fprintf (output, "rc_type_broken=8\n");
+ fprintf (output, "rc_type_mtime=9\n");
+ fprintf (output, "rc_index_scale=10\n\n");
+ fprintf (output, "declare -a RC_DEPEND_TREE\n\n");
+
+ list_for_each_entry (info, &service_info_list, node)
+ {
+ sindex++;
+ }
+ if (0 == sindex)
+ {
+ EERROR ("No services to generate dependency tree for!\n");
+ return -1;
+ }
+
+ fprintf (output, "RC_DEPEND_TREE[0]=%i\n\n", sindex);
+
+ sindex = 1;
+
+ list_for_each_entry (info, &service_info_list, node)
+ {
+ fprintf (output, "RC_DEPEND_TREE[%i]=\"%s\"\n", sindex * 10, info->name);
+
+ for (i = 0; i <= BROKEN; i++)
+ {
+ dep_count = 0;
+
+ fprintf (output, "RC_DEPEND_TREE[%i+%i]=", (sindex * 10), (i + 2));
+
+ str_list_for_each_item (info->depend_info[i], service, count)
+ {
+ if (0 == dep_count)
+ fprintf (output, "\"%s", service);
+ else
+ fprintf (output, " %s", service);
+
+ dep_count++;
+ }
+
+ if (dep_count > 0)
+ fprintf (output, "\"\n");
+ else
+ fprintf (output, "\n");
+ }
+
+ fprintf (output, "RC_DEPEND_TREE[%i+9]=\"%li\"\n\n",
+ sindex * 10, info->mtime);
+ sindex++;
+ }
+
+ fprintf (output, "RC_GOT_DEPTREE_INFO=\"yes\"\n");
+
+ info = service_get_virtual ("logger");
+ if (NULL == info)
+ {
+ DBG_MSG ("No service provides the 'logger' logger virtual!\n");
+ fprintf (output, "\nLOGGER_SERVICE=\n");
+ }
+ else
+ {
+ fprintf (output, "\nLOGGER_SERVICE=\"%s\"\n", info->name);
+ }
+
+
+ return 0;
+}
+
+int
+parse_cache (const dyn_buf_t *data)
+{
+ service_info_t *info;
+ service_type_t type = ALL_SERVICE_TYPE_T;
+ rcscript_info_t *rs_info;
+ char *buf = NULL;
+ char *rc_name = NULL;
+ char *str_ptr;
+ char *token;
+ char *field;
+ int retval;
+
+ if (!check_arg_dyn_buf ((dyn_buf_t *) data))
+ goto error;
+
+ while (NULL != (buf = read_line_dyn_buf ((dyn_buf_t *) data)))
+ {
+ str_ptr = buf;
+
+ /* Strip leading spaces/tabs */
+ while ((str_ptr[0] == ' ') || (str_ptr[0] == '\t'))
+ str_ptr++;
+
+ /* Get FIELD name and FIELD value */
+ token = strsep (&str_ptr, " ");
+
+ /* FIELD name empty/bogus? */
+ if ((!check_str (token))
+ /* We got an empty FIELD value */
+ || (!check_str (str_ptr)))
+ {
+ errno = EMSGSIZE;
+ DBG_MSG ("Parsing stopped due to short read!\n");
+
+ goto error;
+ }
+
+ if (0 == strcmp (token, FIELD_RCSCRIPT))
+ {
+ DBG_MSG ("Field = '%s', value = '%s'\n", token, str_ptr);
+
+ /* Add the service to the list, and initialize all data */
+ retval = service_add (str_ptr);
+ if (-1 == retval)
+ {
+ DBG_MSG ("Failed to add %s to service list!\n", str_ptr);
+ goto error;
+ }
+
+ info = service_get_info (str_ptr);
+ if (NULL == info)
+ {
+ DBG_MSG ("Failed to get info for '%s'!\n", str_ptr);
+ goto error;
+ }
+ /* Save the rc-script name for next passes of loop */
+ rc_name = info->name;
+
+ goto _continue;
+ }
+
+ if (NULL == rc_name)
+ {
+ DBG_MSG ("Other fields should come after '%s'!\n", FIELD_RCSCRIPT);
+ goto error;
+ }
+
+ if (0 == strcmp (token, FIELD_NEED))
+ type = NEED;
+ else if (0 == strcmp (token, FIELD_USE))
+ type = USE;
+ else if (0 == strcmp (token, FIELD_BEFORE))
+ type = BEFORE;
+ else if (0 == strcmp (token, FIELD_AFTER))
+ type = AFTER;
+ else if (0 == strcmp (token, FIELD_PROVIDE))
+ type = PROVIDE;
+ else if (0 == strcmp (token, FIELD_FAILED))
+ {
+ type = BROKEN;
+
+ /* FIXME: Need to think about what to do syntax BROKEN
+ * services */
+ EWARN ("'%s' has syntax errors, please correct!\n", rc_name);
+ }
+
+ if (type < ALL_SERVICE_TYPE_T)
+ {
+ /* Get the first value *
+ * As the values are passed to a bash function, and we
+ * then use 'echo $*' to parse them, they should only
+ * have one space between each value ... */
+ token = strsep (&str_ptr, " ");
+
+ /* Get the correct type name */
+ field = service_type_names[type];
+
+ while (NULL != token)
+ {
+ DBG_MSG ("Field = '%s', service = '%s', value = '%s'\n",
+ field, rc_name, token);
+
+ retval = service_add_dependency (rc_name, token, type);
+ if (-1 == retval)
+ {
+ DBG_MSG
+ ("Failed to add dependency '%s' to service '%s', type '%s'!\n",
+ token, rc_name, field);
+ goto error;
+ }
+
+ /* Get the next value (if any) */
+ token = strsep (&str_ptr, " ");
+ }
+
+ goto _continue;
+ }
+
+ /* Fall through */
+ DBG_MSG ("Unknown FIELD in data!\n");
+
+_continue:
+ type = ALL_SERVICE_TYPE_T;
+ free (buf);
+ /* Do not free 'rc_name', as it should be consistant
+ * across loops */
+ }
+
+ /* read_line_dyn_buf() returned NULL with errno set */
+ if ((NULL == buf) && (0 != errno))
+ {
+ DBG_MSG ("Failed to read line from dynamic buffer!\n");
+ return -1;
+ }
+
+ /* Set the mtimes
+ * FIXME: Can drop this when we no longer need write_legacy_stage3() */
+ list_for_each_entry (rs_info, &rcscript_list, node)
+ {
+ rc_name = rc_basename (rs_info->filename);
+ if (NULL == service_get_info (rc_name))
+ continue;
+
+ retval = service_set_mtime (rc_name, rs_info->mtime);
+ if (-1 == retval)
+ {
+ DBG_MSG ("Failed to set mtime for service '%s'!\n", rc_name);
+ return -1;
+ }
+ }
+
+ return 0;
+
+error:
+ free (buf);
+
+ return -1;
+}
+
+size_t
+parse_print_start (dyn_buf_t *data)
+{
+ size_t write_count;
+
+ if (!check_arg_dyn_buf (data))
+ return -1;
+
+ write_count =
+ sprintf_dyn_buf (data,
+ ". /sbin/functions.sh\n"
+ "[ -e /etc/rc.conf ] && . /etc/rc.conf\n"
+ "\n"
+ /* "set -e\n" */
+ "\n");
+
+ return write_count;
+}
+
+size_t
+parse_print_header (char *scriptname, dyn_buf_t *data)
+{
+ size_t write_count;
+
+ if (!check_arg_dyn_buf (data))
+ return -1;
+
+ write_count =
+ sprintf_dyn_buf (data,
+ "#*** %s ***\n"
+ "\n"
+ "myservice=\"%s\"\n"
+ "echo \"RCSCRIPT ${myservice}\"\n"
+ "\n", scriptname, scriptname);
+
+ return write_count;
+}
+
+size_t
+parse_print_body (char *scriptname, dyn_buf_t *data)
+{
+ size_t write_count;
+ char *buf = NULL;
+ char *str_ptr;
+ char *base;
+ char *ext;
+
+ if (!check_arg_dyn_buf (data))
+ return -1;
+
+ buf = xstrndup (scriptname, strlen (scriptname));
+ if (NULL == buf)
+ return -1;
+
+ /*
+ * Rather do the next block in C than bash, in case we want to
+ * use ash or another shell in the place of bash
+ */
+
+ /* bash: base="${myservice%%.*}" */
+ base = buf;
+ str_ptr = strchr (buf, '.');
+ if (NULL != str_ptr)
+ {
+ str_ptr[0] = '\0';
+ str_ptr++;
+ }
+ else
+ {
+ str_ptr = buf;
+ }
+ /* bash: ext="${myservice##*.}" */
+ ext = strrchr (str_ptr, '.');
+ if (NULL == ext)
+ ext = str_ptr;
+
+ write_count =
+ sprintf_dyn_buf (data,
+ "\n"
+ "(\n"
+ " # Get settings for rc-script ...\n"
+ " [ -e \"/etc/conf.d/${myservice}\" ] && \\\n"
+ " . \"/etc/conf.d/${myservice}\"\n"
+ " [ -e /etc/conf.d/net ] && \\\n"
+ " [ \"%s\" = \"net\" ] && \\\n"
+ " [ \"%s\" != \"${myservice}\" ] && \\\n"
+ " . /etc/conf.d/net\n"
+ " depend() {\n"
+ " return 0\n"
+ " }\n"
+ " \n"
+ " # Actual depend() function ...\n"
+ " (\n"
+ " set -e\n"
+ " . \"/etc/init.d/%s\" >/dev/null 2>&1\n"
+ " set +e\n"
+ " \n"
+ " need() {\n"
+ " [ \"$#\" -gt 0 ] && echo \"NEED $*\"; return 0\n"
+ " }\n"
+ " \n"
+ " use() {\n"
+ " [ \"$#\" -gt 0 ] && echo \"USE $*\"; return 0\n"
+ " }\n"
+ " \n"
+ " before() {\n"
+ " [ \"$#\" -gt 0 ] && echo \"BEFORE $*\"; return 0\n"
+ " }\n"
+ " \n"
+ " after() {\n"
+ " [ \"$#\" -gt 0 ] && echo \"AFTER $*\"; return 0\n"
+ " }\n"
+ " \n"
+ " provide() {\n"
+ " [ \"$#\" -gt 0 ] && echo \"PROVIDE $*\"; return 0\n"
+ " }\n"
+ " \n"
+ " depend\n"
+ " ) || echo \"FAILED ${myservice}\"\n"
+ ")\n" "\n\n", base, ext, scriptname);
+
+ return write_count;
+}
diff --git a/src/core/librccore/runlevels.c b/src/core/librccore/runlevels.c
new file mode 100644
index 0000000..8062912
--- /dev/null
+++ b/src/core/librccore/runlevels.c
@@ -0,0 +1,218 @@
+/*
+ * runlevels.c
+ *
+ * Functions dealing with runlevels.
+ *
+ * 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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "internal/rccore.h"
+
+static char **get_runlevel_dirs (void);
+
+LIST_HEAD (runlevel_list);
+
+char **
+get_runlevel_dirs (void)
+{
+ char **dir_list = NULL;
+ char **runlvl_list = NULL;
+ char *dir_item;
+ int count;
+
+ dir_list = rc_ls_dir (RUNLEVELS_DIR, 0);
+ if (NULL == dir_list)
+ {
+ errno = ENOENT;
+ DBG_MSG ("Failed to get any entries in '%' !\n", RUNLEVELS_DIR);
+
+ return NULL;
+ }
+
+ str_list_for_each_item (dir_list, dir_item, count)
+ {
+ if (rc_is_dir (dir_item, 0))
+ {
+ char *tmp_str;
+
+ tmp_str = xstrndup (dir_item, strlen (dir_item));
+ if (NULL == tmp_str)
+ goto error;
+
+ str_list_add_item (runlvl_list, tmp_str, error);
+ }
+ }
+
+ str_list_free (dir_list);
+
+ if (!check_strv (runlvl_list))
+ {
+ if (NULL != runlvl_list)
+ str_list_free (runlvl_list);
+ }
+
+ return runlvl_list;
+
+error:
+ if (NULL != dir_list)
+ str_list_free (dir_list);
+ if (NULL != runlvl_list)
+ str_list_free (runlvl_list);
+
+ return NULL;
+}
+
+int
+get_runlevels (void)
+{
+ char **runlvl_list = NULL;
+ char *runlevel;
+ int count;
+
+ runlvl_list = get_runlevel_dirs ();
+ if (NULL == runlvl_list)
+ {
+ DBG_MSG ("Failed to get any runlevels\n");
+
+ return -1;
+ }
+
+ str_list_for_each_item (runlvl_list, runlevel, count)
+ {
+ runlevel_info_t *runlevel_info;
+ char **dir_list = NULL;
+ char *dir_item;
+ int dir_count;
+
+ DBG_MSG ("Adding runlevel '%s'\n", rc_basename (runlevel));
+
+ runlevel_info = xmalloc (sizeof (runlevel_info_t));
+ if (NULL == runlevel_info)
+ goto error;
+
+ runlevel_info->dirname = xstrndup (runlevel, strlen (runlevel));
+ if (NULL == runlevel_info->dirname)
+ goto error;
+
+ INIT_LIST_HEAD (&runlevel_info->entries);
+
+ dir_list = rc_ls_dir (runlevel, 0);
+ if (NULL == dir_list)
+ {
+ if (0 != errno)
+ goto error;
+
+ goto no_entries;
+ }
+
+ str_list_for_each_item (dir_list, dir_item, dir_count)
+ {
+ rcscript_info_t *script_info;
+ rcscript_info_t *new_script_info = NULL;
+
+ if (!rc_is_link (dir_item))
+ {
+ DBG_MSG ("Skipping non symlink '%s' !\n", dir_item);
+ continue;
+ }
+
+ script_info = get_rcscript_info (rc_basename (dir_item));
+ if (NULL == script_info)
+ {
+ DBG_MSG ("Skipping invalid entry '%s' !\n", dir_item);
+ continue;
+ }
+
+ new_script_info = xmalloc (sizeof (rcscript_info_t));
+ if (NULL == new_script_info)
+ {
+ str_list_free (dir_list);
+ goto error;
+ }
+
+ DBG_MSG ("Adding '%s' to runlevel '%s'\n",
+ rc_basename (script_info->filename),
+ rc_basename (runlevel));
+
+ /* Add a copy, as the next and prev pointers will be changed */
+ memcpy (new_script_info, script_info, sizeof (rcscript_info_t));
+ list_add_tail (&new_script_info->node, &runlevel_info->entries);
+ }
+
+ str_list_free (dir_list);
+
+no_entries:
+ list_add_tail (&runlevel_info->node, &runlevel_list);
+ }
+
+ str_list_free (runlvl_list);
+
+ return 0;
+
+error:
+ if (NULL != runlvl_list)
+ str_list_free (runlvl_list);
+
+ return -1;
+}
+
+runlevel_info_t *
+get_runlevel_info (const char *runlevel)
+{
+ runlevel_info_t *info;
+
+ if (!check_arg_str (runlevel))
+ return NULL;
+
+ list_for_each_entry (info, &runlevel_list, node)
+ {
+ if ((strlen (runlevel) == strlen (rc_basename (info->dirname)))
+ && (0 == strncmp (runlevel, rc_basename (info->dirname),
+ strlen (runlevel))))
+ return info;
+ }
+
+ return NULL;
+}
+
+bool
+is_runlevel (const char *runlevel)
+{
+ char *runlevel_dir = NULL;
+ int len;
+
+ /* strlen (RUNLEVELS_DIR) + strlen (runlevel) + "/" + '\0' */
+ len = strlen (RUNLEVELS_DIR) + strlen (runlevel) + 2;
+ runlevel_dir = xmalloc (sizeof (char) * len);
+ if (NULL == runlevel_dir)
+ return FALSE;
+
+ snprintf (runlevel_dir, len, "%s/%s", RUNLEVELS_DIR, runlevel);
+
+ if (rc_is_dir (runlevel_dir, 0))
+ return TRUE;
+
+ return FALSE;
+}
+
diff --git a/src/core/librccore/scripts.c b/src/core/librccore/scripts.c
new file mode 100644
index 0000000..cbd97d7
--- /dev/null
+++ b/src/core/librccore/scripts.c
@@ -0,0 +1,254 @@
+/*
+ * scripts.c
+ *
+ * Get info etc 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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "internal/rccore.h"
+
+LIST_HEAD (rcscript_list);
+
+int
+get_rcscripts (void)
+{
+ rcscript_info_t *info;
+ char **file_list = NULL;
+ char *rcscript;
+ char *confd_file = NULL;
+ int count;
+
+ file_list = rc_ls_dir (RCSCRIPTS_INITDDIR, 0);
+ if (NULL == file_list)
+ {
+ errno = ENOENT;
+ DBG_MSG ("'%s' is empty!\n", RCSCRIPTS_INITDDIR);
+
+ return -1;
+ }
+
+ str_list_for_each_item (file_list, rcscript, count)
+ {
+ /* Is it a file? */
+ if (!(rc_is_file (rcscript, 1))
+ /* Do not process scripts, source or backup files. */
+ || (CHECK_FILE_EXTENSION (rcscript, ".c"))
+ || (CHECK_FILE_EXTENSION (rcscript, ".bak"))
+ || (CHECK_FILE_EXTENSION (rcscript, "~")))
+ {
+ DBG_MSG ("'%s' is not a valid rc-script!\n", rc_basename (rcscript));
+ }
+ else
+ {
+ regex_data_t tmp_data;
+ dyn_buf_t *dynbuf = NULL;
+ char *buf = NULL;
+
+ dynbuf = new_dyn_buf_mmap_file (rcscript);
+ if (NULL == dynbuf)
+ {
+ DBG_MSG ("Could not open '%s' for reading!\n",
+ rc_basename (rcscript));
+ goto error;
+ }
+
+ buf = read_line_dyn_buf (dynbuf);
+ free_dyn_buf (dynbuf);
+ if ((NULL == buf) && (0 != errno))
+ goto error;
+ if (NULL == buf)
+ {
+ DBG_MSG ("'%s' is not a valid rc-script!\n",
+ rc_basename (rcscript));
+ continue;
+ }
+
+ /* Check if it starts with '#!/sbin/runscript' */
+ DO_REGEX (tmp_data, buf, "[ \t]*#![ \t]*/sbin/runscript[ \t]*.*",
+ check_error);
+ free (buf);
+ if (REGEX_FULL_MATCH != tmp_data.match)
+ {
+ DBG_MSG ("'%s' is not a valid rc-script!\n",
+ rc_basename (rcscript));
+ continue;
+ }
+
+ /* We do not want rc-scripts ending in '.sh' */
+ if (CHECK_FILE_EXTENSION (rcscript, ".sh"))
+ {
+ EWARN ("'%s' is invalid (should not end with '.sh')!\n",
+ rc_basename (rcscript));
+ continue;
+ }
+
+ DBG_MSG ("Adding rc-script '%s' to list.\n", rc_basename (rcscript));
+
+ info = xmalloc (sizeof (rcscript_info_t));
+ if (NULL == info)
+ goto error;
+
+ /* Copy the name */
+ info->filename = xstrndup (rcscript, strlen (rcscript));
+ if (NULL == info->filename)
+ goto loop_error;
+
+ /* Get the modification time */
+ info->mtime = rc_get_mtime (rcscript, 1);
+ if (0 == info->mtime)
+ {
+ DBG_MSG ("Failed to get modification time for '%s'!\n", rcscript);
+ /* We do not care if it fails - we will pick up
+ * later if there is a problem with the file */
+ }
+
+ /* File name for the conf.d config file (if any) */
+ confd_file = rc_strcatpaths (RCSCRIPTS_CONFDDIR, rc_basename (rcscript));
+ if (NULL == confd_file)
+ {
+ DBG_MSG ("Failed to allocate temporary buffer!\n");
+ goto loop_error;
+ }
+
+ /* Get the modification time of the conf.d file
+ * (if any rc_file_exists) */
+ info->confd_mtime = rc_get_mtime (confd_file, 1);
+ if (0 == info->confd_mtime)
+ {
+ DBG_MSG ("Failed to get modification time for '%s'!\n",
+ confd_file);
+ /* We do not care that it fails, as not all
+ * rc-scripts will have conf.d config files */
+ }
+
+ free (confd_file);
+
+ list_add_tail (&info->node, &rcscript_list);
+
+ continue;
+
+check_error:
+ free (buf);
+ goto error;
+
+loop_error:
+ if (NULL != info)
+ free (info->filename);
+ free (info);
+
+ goto error;
+ }
+ }
+
+ /* Final check if we have some entries */
+ if (!check_strv (file_list))
+ {
+ errno = ENOENT;
+ DBG_MSG ("No rc-scripts to parse!\n");
+ goto error;
+ }
+
+ str_list_free (file_list);
+
+ return 0;
+
+error:
+ str_list_free (file_list);
+
+ return -1;
+}
+
+/* Returns 0 if we do not need to regen the cache file, else -1 with
+ * errno set if something went wrong */
+int
+check_rcscripts_mtime (const char *cachefile)
+{
+ rcscript_info_t *info;
+ time_t cache_mtime;
+ time_t rc_conf_mtime;
+ time_t rc_confd_mtime;
+
+ if (!check_arg_str (cachefile))
+ return -1;
+
+ cache_mtime = rc_get_mtime (cachefile, 1);
+ if (0 == cache_mtime)
+ {
+ DBG_MSG ("Could not get modification time for cache file '%s'!\n",
+ cachefile);
+ return -1;
+ }
+
+ /* Get and compare mtime for RC_CONF_FILE_NAME with that of cachefile */
+ rc_conf_mtime = rc_get_mtime (RC_CONF_FILE_NAME, 1);
+ if (rc_conf_mtime > cache_mtime)
+ {
+ DBG_MSG ("'%s' have a later modification time than '%s'.\n",
+ RC_CONF_FILE_NAME, cachefile);
+ return -1;
+ }
+ /* Get and compare mtime for RC_CONFD_FILE_NAME with that of cachefile */
+ rc_confd_mtime = rc_get_mtime (RC_CONFD_FILE_NAME, 1);
+ if (rc_confd_mtime > cache_mtime)
+ {
+ DBG_MSG ("'%s' have a later modification time than '%s'.\n",
+ RC_CONFD_FILE_NAME, cachefile);
+ return -1;
+ }
+
+ /* Get and compare mtime for each rc-script and its conf.d config file
+ * with that of cachefile */
+ list_for_each_entry (info, &rcscript_list, node)
+ {
+ if ((info->mtime > cache_mtime) || (info->confd_mtime > cache_mtime))
+ {
+ DBG_MSG ("'%s' have a later modification time than '%s'.\n",
+ info->filename, cachefile);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+rcscript_info_t *
+get_rcscript_info (const char *scriptname)
+{
+ rcscript_info_t *info;
+
+ if (!check_arg_str (scriptname))
+ return NULL;
+
+ list_for_each_entry (info, &rcscript_list, node)
+ {
+ if ((strlen (scriptname) == strlen (rc_basename (info->filename)))
+ && (0 == strncmp (scriptname, rc_basename (info->filename),
+ strlen (scriptname))))
+ return info;
+ }
+
+ return NULL;
+}
+
diff --git a/src/core/librccore/services.c b/src/core/librccore/services.c
new file mode 100644
index 0000000..140df1e
--- /dev/null
+++ b/src/core/librccore/services.c
@@ -0,0 +1,86 @@
+/*
+ * services.c
+ *
+ * Functions dealing with services.
+ *
+ * Copyright (C) 2004-2006 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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "internal/rccore.h"
+
+char *rc_service_state_names[] = {
+ "coldplugged",
+ "starting",
+ "started",
+ "inactive",
+ "wasinactive",
+ "stopping",
+ NULL
+};
+
+bool
+rc_service_test_state (const char *service, rc_service_state_t state)
+{
+ char *state_dir;
+ char *state_link;
+
+ if (!check_str (service))
+ return FALSE;
+
+ /*
+ * NB: Should check if its actually a valid service before doing all the
+ * rest of the checks!
+ */
+
+ if (rc_file_exists (RC_SYSINIT_STATE))
+ return FALSE;
+
+ state_dir = rc_strcatpaths (rc_config_svcdir, rc_service_state_names[state]);
+ if (NULL == state_dir)
+ {
+ DBG_MSG ("Failed to allocate buffer!\n");
+ return FALSE;
+ }
+
+ state_link = rc_strcatpaths (state_dir, service);
+ if (NULL == state_link)
+ {
+ free (state_dir);
+ DBG_MSG ("Failed to allocate buffer!\n");
+ return FALSE;
+ }
+
+ if (rc_file_exists (state_link))
+ {
+ free (state_link);
+ free (state_dir);
+ return TRUE;
+ }
+
+ free (state_link);
+ free (state_dir);
+
+ return FALSE;
+}
+
diff --git a/src/core/librcutil/Makefile.am b/src/core/librcutil/Makefile.am
new file mode 100644
index 0000000..086764b
--- /dev/null
+++ b/src/core/librcutil/Makefile.am
@@ -0,0 +1,19 @@
+AUTOMAKE_OPTIONS = foreign
+
+INCLUDES = \
+ -I$(top_srcdir)/include \
+ $(RCSCRIPTS_DEFINES)
+
+lib_LTLIBRARIES = librcutil.la
+
+librcutil_la_LDFLAGS = \
+ -Wl,--version-script,$(srcdir)/librcutil.map
+librcutil_la_SOURCES = \
+ debug.c \
+ string.c \
+ file.c \
+ config.c \
+ dynbuf.c \
+ simple-regex.c
+
+EXTRA_DIST = librcutil.map
diff --git a/src/core/librcutil/config.c b/src/core/librcutil/config.c
new file mode 100644
index 0000000..1767bbb
--- /dev/null
+++ b/src/core/librcutil/config.c
@@ -0,0 +1,205 @@
+/*
+ * misc.c
+ *
+ * Miscellaneous macro's and functions.
+ *
+ * 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 <stdlib.h>
+
+#include "rcscripts/rcutil.h"
+
+/* This handles simple 'entry="bar"' type variables. If it is more complex
+ * ('entry="$(pwd)"' or such), it will obviously not work, but current behaviour
+ * should be fine for the type of variables we want. */
+char *
+rc_get_cnf_entry (const char *pathname, const char *entry)
+{
+ dyn_buf_t *dynbuf = NULL;
+ char *buf = NULL;
+ char *str_ptr;
+ char *value = NULL;
+ char *token;
+
+
+ if ((!check_arg_str (pathname)) || (!check_arg_str (entry)))
+ return NULL;
+
+ /* If it is not a file or symlink pointing to a file, bail */
+ if (1 != rc_is_file (pathname, 1))
+ {
+ errno = ENOENT;
+ DBG_MSG ("Given pathname is not a file or do not exist!\n");
+ return NULL;
+ }
+
+ dynbuf = new_dyn_buf_mmap_file (pathname);
+ if (NULL == dynbuf)
+ {
+ DBG_MSG ("Could not open config file for reading!\n");
+ return NULL;
+ }
+
+ while (NULL != (buf = read_line_dyn_buf (dynbuf)))
+ {
+ str_ptr = buf;
+
+ /* Strip leading spaces/tabs */
+ while ((str_ptr[0] == ' ') || (str_ptr[0] == '\t'))
+ str_ptr++;
+
+ /* Get entry and value */
+ token = strsep (&str_ptr, "=");
+ /* Bogus entry or value */
+ if (NULL == token)
+ goto _continue;
+
+ /* Make sure we have a string that is larger than 'entry', and
+ * the first part equals 'entry' */
+ if ((strlen (token) > 0) && (0 == strcmp (entry, token)))
+ {
+ do
+ {
+ /* Bash variables are usually quoted */
+ token = strsep (&str_ptr, "\"\'");
+ /* If quoted, the first match will be "" */
+ }
+ while ((NULL != token) && (0 == strlen (token)));
+
+ /* We have a 'entry='. We respect bash rules, so NULL
+ * value for now (if not already) */
+ if (NULL == token)
+ {
+ /* We might have 'entry=' and later 'entry="bar"',
+ * so just continue for now ... we will handle
+ * it below when 'value == NULL' */
+ if (NULL != value)
+ {
+ free (value);
+ value = NULL;
+ }
+ goto _continue;
+ }
+
+ /* If we have already allocated 'value', free it */
+ if (NULL != value)
+ free (value);
+
+ value = xstrndup (token, strlen (token));
+ if (NULL == value)
+ {
+ free_dyn_buf (dynbuf);
+ free (buf);
+
+ return NULL;
+ }
+
+ /* We do not break, as there might be more than one entry
+ * defined, and as bash uses the last, so should we */
+ /* break; */
+ }
+
+_continue:
+ free (buf);
+ }
+
+ /* read_line_dyn_buf() returned NULL with errno set */
+ if ((NULL == buf) && (0 != errno))
+ {
+ DBG_MSG ("Failed to read line from dynamic buffer!\n");
+ free_dyn_buf (dynbuf);
+ if (NULL != value)
+ free (value);
+
+ return NULL;
+ }
+
+
+ if (NULL == value)
+ DBG_MSG ("Failed to get value for config entry '%s'!\n", entry);
+
+ free_dyn_buf (dynbuf);
+
+ return value;
+}
+
+char **
+rc_get_list_file (char **list, char *filename)
+{
+ dyn_buf_t *dynbuf = NULL;
+ char *buf = NULL;
+ char *tmp_p = NULL;
+ char *token = NULL;
+
+ if (!check_arg_str (filename))
+ return NULL;
+
+ dynbuf = new_dyn_buf_mmap_file (filename);
+ if (NULL == dynbuf)
+ return NULL;
+
+ while (NULL != (buf = read_line_dyn_buf (dynbuf)))
+ {
+ tmp_p = buf;
+
+ /* Strip leading spaces/tabs */
+ while ((tmp_p[0] == ' ') || (tmp_p[0] == '\t'))
+ tmp_p++;
+
+ /* Get entry - we do not want comments, and only the first word
+ * on a line is valid */
+ token = strsep (&tmp_p, "# \t");
+ if (check_str (token))
+ {
+ tmp_p = xstrndup (token, strlen (token));
+ if (NULL == tmp_p)
+ {
+ if (NULL != list)
+ str_list_free (list);
+ free_dyn_buf (dynbuf);
+ free (buf);
+
+ return NULL;
+ }
+
+ str_list_add_item (list, tmp_p, error);
+ }
+
+ free (buf);
+ }
+
+ /* read_line_dyn_buf() returned NULL with errno set */
+ if ((NULL == buf) && (0 != errno))
+ {
+ DBG_MSG ("Failed to read line from dynamic buffer!\n");
+error:
+ if (NULL != list)
+ str_list_free (list);
+ free_dyn_buf (dynbuf);
+
+ return NULL;
+ }
+
+ free_dyn_buf (dynbuf);
+
+ return list;
+}
diff --git a/src/core/librcutil/debug.c b/src/core/librcutil/debug.c
new file mode 100644
index 0000000..2a1b9e4
--- /dev/null
+++ b/src/core/librcutil/debug.c
@@ -0,0 +1,297 @@
+/*
+ * debug.c
+ *
+ * Simle debugging/logging macro's and functions.
+ *
+ * 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "rcscripts/rcutil.h"
+
+static char log_domain[] = "rcscripts";
+
+void
+debug_message (const char *file, const char *func, int line,
+ const char *format, ...)
+{
+ va_list arg;
+ char *format_str;
+ int length;
+
+ save_errno ();
+
+ length = strlen (log_domain) + strlen ("(): ") + 1;
+ /* Do not use xmalloc() here, else we may have recursive issues */
+ format_str = malloc (length);
+ if (NULL == format_str)
+ {
+ fprintf (stderr, "(%s) error: in %s, function %s(), line %i:\n",
+ log_domain, __FILE__, __FUNCTION__, __LINE__);
+ fprintf (stderr, "(%s) Failed to allocate buffer!\n",
+ log_domain);
+ abort ();
+ }
+
+ snprintf (format_str, length, "(%s) ", log_domain);
+
+ va_start (arg, format);
+
+#if !defined(RC_DEBUG)
+ /* Bit of a hack, as how we do things tend to cause seek
+ * errors when reading the parent/child pipes */
+ /* if ((0 != errno) && (ESPIPE != errno)) { */
+ if (0 != saved_errno)
+ {
+#endif
+ if (0 != saved_errno)
+ fprintf (stderr, "(%s) error: ", log_domain);
+ else
+ fprintf (stderr, "(%s) debug: ", log_domain);
+
+ fprintf (stderr, "in %s, function %s(), line %i:\n", file, func, line);
+
+ fprintf (stderr, "%s ", format_str);
+ vfprintf (stderr, format, arg);
+
+#if defined(RC_DEBUG)
+ if (0 != saved_errno)
+ {
+#endif
+ perror (format_str);
+#if defined(RC_DEBUG)
+ }
+#endif
+#if !defined(RC_DEBUG)
+ }
+#endif
+
+ va_end (arg);
+
+ free (format_str);
+ restore_errno ();
+}
+
+inline bool
+check_ptr (const void *ptr)
+{
+ if (NULL == ptr)
+ return FALSE;
+
+ return TRUE;
+}
+
+inline bool
+check_str (const char *str)
+{
+ if ((NULL == str) || (0 == strlen (str)))
+ return FALSE;
+
+ return TRUE;
+}
+
+inline bool
+check_strv (char **str)
+{
+ if ((NULL == str) || (NULL == *str) || (0 == strlen (*str)))
+ return FALSE;
+
+ return TRUE;
+}
+
+inline bool
+check_fd (int fd)
+{
+ if ((0 >= fd) || (-1 == fcntl (fd, F_GETFL)))
+ return FALSE;
+
+ return TRUE;
+}
+
+inline bool
+check_fp (FILE *fp)
+{
+ if ((NULL == fp) || (-1 == fileno (fp)))
+ return FALSE;
+
+ return TRUE;
+}
+
+inline bool
+__check_arg_ptr (const void *ptr, const char *file, const char *func, size_t line)
+{
+ if (!check_ptr (ptr))
+ {
+ errno = EINVAL;
+
+ debug_message (file, func, line, "Invalid pointer passed!\n");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+inline bool
+__check_arg_str (const char *str, const char *file, const char *func, size_t line)
+{
+ if (!check_str (str))
+ {
+ errno = EINVAL;
+
+ debug_message (file, func, line, "Invalid string passed!\n");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+inline bool
+__check_arg_strv (char **str, const char *file, const char *func, size_t line)
+{
+ if (!check_strv (str))
+ {
+ errno = EINVAL;
+
+ debug_message (file, func, line, "Invalid string array passed!\n");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+inline bool
+__check_arg_fd (int fd, const char *file, const char *func, size_t line)
+{
+ if (!check_fd (fd))
+ {
+ errno = EBADF;
+
+ debug_message (file, func, line, "Invalid file descriptor passed!\n");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+inline bool
+__check_arg_fp (FILE *fp, const char *file, const char *func, size_t line)
+{
+ if (!check_fp (fp))
+ {
+ errno = EBADF;
+
+ debug_message (file, func, line, "Invalid file descriptor passed!\n");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+inline void *
+__xcalloc(size_t nmemb, size_t size, const char *file,
+ const char *func, size_t line)
+{
+ void *new_ptr;
+
+ new_ptr = calloc (nmemb, size);
+ if (NULL == new_ptr)
+ {
+ /* Set errno in case specific malloc() implementation does not */
+ errno = ENOMEM;
+
+ debug_message (file, func, line, "Failed to allocate buffer!\n");
+
+ return NULL;
+ }
+
+ return new_ptr;
+}
+
+inline void *
+__xmalloc (size_t size, const char *file, const char *func, size_t line)
+{
+ void *new_ptr;
+
+ new_ptr = malloc (size);
+ if (NULL == new_ptr)
+ {
+ /* Set errno in case specific malloc() implementation does not */
+ errno = ENOMEM;
+
+ debug_message (file, func, line, "Failed to allocate buffer!\n");
+
+ return NULL;
+ }
+
+ return new_ptr;
+}
+
+inline void *
+__xrealloc (void *ptr, size_t size, const char *file,
+ const char *func, size_t line)
+{
+ void *new_ptr;
+
+ new_ptr = realloc (ptr, size);
+ if (NULL == new_ptr)
+ {
+ /* Set errno in case specific realloc() implementation does not */
+ errno = ENOMEM;
+
+ debug_message (file, func, line, "Failed to reallocate buffer!\n");
+
+ return NULL;
+ }
+
+ return new_ptr;
+}
+
+inline char *
+__xstrndup (const char *str, size_t size, const char *file,
+ const char *func, size_t line)
+{
+ char *new_ptr;
+
+ new_ptr = rc_strndup (str, size);
+ if (NULL == new_ptr)
+ {
+ /* Set errno in case specific realloc() implementation does not */
+ errno = ENOMEM;
+
+ debug_message (file, func, line,
+ "Failed to duplicate string via rc_strndup() !\n");
+
+ return NULL;
+ }
+
+ return new_ptr;
+}
+
diff --git a/src/core/librcutil/dynbuf.c b/src/core/librcutil/dynbuf.c
new file mode 100644
index 0000000..b6f6cdc
--- /dev/null
+++ b/src/core/librcutil/dynbuf.c
@@ -0,0 +1,407 @@
+/*
+ * dynbuf.c
+ *
+ * Dynamic allocated buffers.
+ *
+ * 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "rcscripts/rcutil.h"
+
+static dyn_buf_t *reallocate_dyn_buf (dyn_buf_t *dynbuf, size_t needed);
+
+dyn_buf_t *
+new_dyn_buf (void)
+{
+ dyn_buf_t *dynbuf = NULL;
+
+ dynbuf = xmalloc (sizeof (dyn_buf_t));
+ if (NULL == dynbuf)
+ return NULL;
+
+ dynbuf->data = xmalloc (DYNAMIC_BUFFER_SIZE);
+ if (NULL == dynbuf->data)
+ {
+ free (dynbuf);
+ return NULL;
+ }
+
+ dynbuf->length = DYNAMIC_BUFFER_SIZE;
+ dynbuf->rd_index = 0;
+ dynbuf->wr_index = 0;
+ dynbuf->file_map = FALSE;
+
+ return dynbuf;
+}
+
+dyn_buf_t *
+new_dyn_buf_mmap_file (const char *name)
+{
+ dyn_buf_t *dynbuf = NULL;
+
+ dynbuf = xmalloc (sizeof (dyn_buf_t));
+ if (NULL == dynbuf)
+ return NULL;
+
+ if (-1 == rc_file_map (name, &dynbuf->data, &dynbuf->length))
+ {
+ DBG_MSG ("Failed to mmap file '%s'\n", name);
+ free (dynbuf);
+
+ return NULL;
+ }
+
+ dynbuf->wr_index = dynbuf->length;
+ dynbuf->rd_index = 0;
+ dynbuf->file_map = TRUE;
+
+ return dynbuf;
+}
+
+dyn_buf_t *
+reallocate_dyn_buf (dyn_buf_t *dynbuf, size_t needed)
+{
+ int len;
+
+ if (!check_arg_dyn_buf (dynbuf))
+ return NULL;
+
+ if (dynbuf->file_map)
+ {
+ errno = EPERM;
+ DBG_MSG ("Cannot reallocate mmap()'d file!\n");
+
+ return NULL;
+ }
+
+ len = sizeof (char) * (dynbuf->wr_index + needed + 1);
+
+ if (dynbuf->length < len)
+ {
+ char *new_ptr;
+
+ /* Increase size in chunks to minimize reallocations */
+ if (len < (dynbuf->length + DYNAMIC_BUFFER_SIZE))
+ len = dynbuf->length + DYNAMIC_BUFFER_SIZE;
+
+ new_ptr = xrealloc (dynbuf->data, len);
+ if (NULL == new_ptr)
+ return NULL;
+
+ dynbuf->data = new_ptr;
+ dynbuf->length = len;
+ }
+
+ return dynbuf;
+}
+
+void
+free_dyn_buf (dyn_buf_t *dynbuf)
+{
+ if (NULL == dynbuf)
+ return;
+
+ if (!dynbuf->file_map)
+ {
+ if (NULL != dynbuf->data)
+ {
+ free (dynbuf->data);
+ dynbuf->data = NULL;
+ }
+ }
+ else
+ {
+ save_errno ();
+ rc_file_unmap (dynbuf->data, dynbuf->length);
+ restore_errno ();
+ }
+
+ dynbuf->length = 0;
+ dynbuf->rd_index = 0;
+ dynbuf->wr_index = 0;
+
+ free (dynbuf);
+ dynbuf = NULL;
+}
+
+int
+write_dyn_buf (dyn_buf_t *dynbuf, const char *buf, size_t length)
+{
+ int len;
+
+ if (!check_arg_dyn_buf (dynbuf))
+ return -1;
+
+ if (!check_arg_str (buf))
+ return -1;
+
+ if (dynbuf->file_map)
+ {
+ errno = EPERM;
+ DBG_MSG ("Cannot write to readonly mmap()'d file!\n");
+
+ return -1;
+ }
+
+ if (NULL == reallocate_dyn_buf (dynbuf, length))
+ {
+ DBG_MSG ("Could not reallocate dynamic buffer!\n");
+ return -1;
+ }
+
+ len = snprintf ((dynbuf->data + dynbuf->wr_index), length + 1, "%s", buf);
+
+ /* If len is less than length, it means the string was shorter than
+ * given length */
+ if (length > len)
+ length = len;
+
+ if (0 < length)
+ dynbuf->wr_index += length;
+
+ if (-1 == length)
+ DBG_MSG ("Failed to write to dynamic buffer!\n");
+
+ return length;
+}
+
+int write_dyn_buf_from_fd (int fd, dyn_buf_t *dynbuf, size_t length)
+{
+ int len = length;
+
+ if (!check_arg_dyn_buf (dynbuf))
+ return -1;
+
+ if (!check_arg_fd (fd))
+ return -1;
+
+ if (dynbuf->file_map)
+ {
+ errno = EPERM;
+ DBG_MSG ("Cannot write to readonly mmap()'d file!\n");
+
+ return -1;
+ }
+
+ if (NULL == reallocate_dyn_buf (dynbuf, length))
+ {
+ DBG_MSG ("Could not reallocate dynamic buffer!\n");
+ return -1;
+ }
+
+ len = read (fd, (dynbuf->data + dynbuf->wr_index), len);
+
+ if (length > len)
+ length = len;
+
+ if (0 < length)
+ dynbuf->wr_index += length;
+
+ dynbuf->data[dynbuf->wr_index] = '\0';
+
+ if (-1 == length)
+ DBG_MSG ("Failed to write to dynamic buffer!\n");
+
+ return length;
+}
+
+int
+sprintf_dyn_buf (dyn_buf_t *dynbuf, const char *format, ...)
+{
+ va_list arg1, arg2;
+ char test_str[10];
+ int needed, written = 0;
+
+ if (!check_arg_dyn_buf (dynbuf))
+ return -1;
+
+ if (!check_arg_str (format))
+ return -1;
+
+ if (dynbuf->file_map)
+ {
+ errno = EPERM;
+ DBG_MSG ("Cannot write to readonly mmap()'d file!\n");
+
+ return -1;
+ }
+
+ va_start (arg1, format);
+ va_copy (arg2, arg1);
+
+ /* XXX: Lame way to try and figure out how much space we need */
+ needed = vsnprintf (test_str, sizeof (test_str), format, arg2);
+ va_end (arg2);
+
+ if (NULL == reallocate_dyn_buf (dynbuf, needed))
+ {
+ DBG_MSG ("Could not reallocate dynamic buffer!\n");
+ return -1;
+ }
+
+ written = vsnprintf ((dynbuf->data + dynbuf->wr_index), needed + 1,
+ format, arg1);
+ va_end (arg1);
+
+ if (0 < written)
+ dynbuf->wr_index += written;
+
+ if (-1 == written)
+ DBG_MSG ("Failed to write to dynamic buffer!\n");
+
+ return written;
+}
+
+int
+read_dyn_buf (dyn_buf_t *dynbuf, char *buf, size_t length)
+{
+ int len = length;
+
+ if (!check_arg_dyn_buf (dynbuf))
+ return -1;
+
+ if (!check_arg_ptr (buf))
+ return -1;
+
+ if (dynbuf->rd_index >= dynbuf->length)
+ return 0;
+
+ if (dynbuf->wr_index < (dynbuf->rd_index + length))
+ len = dynbuf->wr_index - dynbuf->rd_index;
+
+ len = snprintf (buf, len + 1, "%s", (dynbuf->data + dynbuf->rd_index));
+
+ /* If len is less than length, it means the string was shorter than
+ * given length */
+ if (length > len)
+ length = len;
+
+ if (0 < length)
+ dynbuf->rd_index += length;
+
+ if (-1 == length)
+ DBG_MSG ("Failed to write from dynamic buffer!\n");
+
+ return length;
+}
+
+int
+read_dyn_buf_to_fd (int fd, dyn_buf_t *dynbuf, size_t length)
+{
+ int len = length;
+
+ if (!check_arg_dyn_buf (dynbuf))
+ return -1;
+
+ if (!check_arg_fd (fd))
+ return -1;
+
+ if (dynbuf->rd_index >= dynbuf->length)
+ return 0;
+
+ if (dynbuf->wr_index < (dynbuf->rd_index + length))
+ len = dynbuf->wr_index - dynbuf->rd_index;
+
+ len = write (fd, (dynbuf->data + dynbuf->rd_index), len);
+ if (length > len)
+ length = len;
+
+ if (0 < length)
+ dynbuf->rd_index += length;
+
+ if (-1 == length)
+ DBG_MSG ("Failed to write from dynamic buffer!\n");
+
+ return length;
+}
+
+char *
+read_line_dyn_buf (dyn_buf_t *dynbuf)
+{
+ char *buf = NULL;
+ size_t count = 0;
+
+ if (!check_arg_dyn_buf (dynbuf))
+ return NULL;
+
+ if (dynbuf->rd_index == dynbuf->wr_index)
+ return NULL;
+
+ for (count = dynbuf->rd_index; count < dynbuf->wr_index && dynbuf->data[count] != '\n'; count++);
+
+ if (count <= dynbuf->wr_index)
+ {
+ buf = xstrndup ((dynbuf->data + dynbuf->rd_index),
+ (count - dynbuf->rd_index));
+ if (NULL == buf)
+ return NULL;
+
+ dynbuf->rd_index = count;
+
+ /* Also skip the '\n' .. */
+ if (dynbuf->rd_index < dynbuf->wr_index)
+ dynbuf->rd_index++;
+ }
+
+ return buf;
+}
+
+bool
+dyn_buf_rd_eof (dyn_buf_t *dynbuf)
+{
+ if (!check_arg_dyn_buf (dynbuf))
+ return FALSE;
+
+ if (dynbuf->rd_index >= dynbuf->wr_index)
+ return TRUE;
+
+ return FALSE;
+}
+
+inline bool
+check_dyn_buf (dyn_buf_t *dynbuf)
+{
+ if ((NULL == dynbuf) || (NULL == dynbuf->data) || (0 == dynbuf->length))
+ return FALSE;
+
+ return TRUE;
+}
+
+inline bool
+__check_arg_dyn_buf (dyn_buf_t *dynbuf, const char *file, const char *func,
+ size_t line)
+{
+ if (!check_dyn_buf (dynbuf))
+ {
+ errno = EINVAL;
+
+ debug_message (file, func, line, "Invalid dynamic buffer passed!\n");
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
diff --git a/src/core/librcutil/file.c b/src/core/librcutil/file.c
new file mode 100644
index 0000000..d0f5d4e
--- /dev/null
+++ b/src/core/librcutil/file.c
@@ -0,0 +1,454 @@
+/*
+ * file.c
+ *
+ * Miscellaneous file related macro's and functions.
+ *
+ * Copyright (C) 2004-2006 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 <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "rcscripts/rcutil.h"
+
+int
+rc_file_exists (const char *pathname)
+{
+ struct stat buf;
+ int retval;
+
+ if (!check_arg_str (pathname))
+ return -1;
+
+ retval = lstat (pathname, &buf);
+ if (-1 != retval)
+ return 1;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+int
+rc_is_file (const char *pathname, int follow_link)
+{
+ struct stat buf;
+ int retval;
+
+ if (!check_arg_str (pathname))
+ return -1;
+
+ retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf);
+ if ((-1 != retval) && (S_ISREG (buf.st_mode)))
+ return 1;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+int
+rc_is_link (const char *pathname)
+{
+ struct stat buf;
+ int retval;
+
+ if (!check_arg_str (pathname))
+ return -1;
+
+ retval = lstat (pathname, &buf);
+ if ((-1 != retval) && (S_ISLNK (buf.st_mode)))
+ return 1;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+int
+rc_is_dir (const char *pathname, int follow_link)
+{
+ struct stat buf;
+ int retval;
+
+ if (!check_arg_str (pathname))
+ return -1;
+
+ retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf);
+ if ((-1 != retval) && (S_ISDIR (buf.st_mode)))
+ return 1;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+time_t
+rc_get_mtime (const char *pathname, int follow_link)
+{
+ struct stat buf;
+ int retval;
+
+ if (!check_arg_str (pathname))
+ return -1;
+
+ retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf);
+ if (-1 != retval)
+ return buf.st_mtime;
+
+ /* Clear errno, as we do not want debugging to trigger */
+ errno = 0;
+
+ return 0;
+}
+
+#if !defined(HAVE_REMOVE)
+int
+remove (const char *pathname)
+{
+ int retval;
+
+ if (!check_arg_str (pathname))
+ return -1;
+
+ if (1 == rc_is_dir (pathname, 0))
+ retval = rmdir (pathname);
+ else
+ retval = unlink (pathname);
+
+ return retval;
+}
+#endif
+
+int
+rc_mktree (const char *pathname, mode_t mode)
+{
+ char *temp_name = NULL;
+ char *temp_token = NULL;
+ char *token_p;
+ char *token;
+ int retval;
+ int lenght;
+
+ if (!check_arg_str (pathname))
+ return -1;
+
+ /* Lenght of 'pathname' + extra for "./" if needed */
+ lenght = strlen (pathname) + 2;
+ /* lenght + '\0' */
+ temp_name = xmalloc (lenght + 1);
+ if (NULL == temp_name)
+ return -1;
+
+ temp_token = xstrndup (pathname, strlen (pathname));
+ if (NULL == temp_token)
+ goto error;
+
+ token_p = temp_token;
+
+ if (pathname[0] == '/')
+ temp_name[0] = '\0';
+ else
+ /* If not an absolute path, make it local */
+ strncpy (temp_name, ".", lenght);
+
+ token = strsep (&token_p, "/");
+ /* First token might be "", but that is OK as it will be when the
+ * pathname starts with '/' */
+ while (NULL != token)
+ {
+ strncat (temp_name, "/", lenght - strlen (temp_name));
+ strncat (temp_name, token, lenght - strlen (temp_name));
+
+ /* If it does not exist, create the dir. If it does exit,
+ * but is not a directory, we will catch it below. */
+ if (1 != rc_file_exists (temp_name))
+ {
+ retval = mkdir (temp_name, mode);
+ if (-1 == retval)
+ {
+ DBG_MSG ("Failed to create directory!\n");
+ goto error;
+ }
+ /* Not a directory or symlink pointing to a directory */
+ }
+ else if (1 != rc_is_dir (temp_name, 1))
+ {
+ errno = ENOTDIR;
+ DBG_MSG ("Component in pathname is not a directory!\n");
+ goto error;
+ }
+
+ do
+ {
+ token = strsep (&token_p, "/");
+ /* The first "" was Ok, but rather skip double '/' after that */
+ }
+ while ((NULL != token) && (0 == strlen (token)));
+ }
+
+ free (temp_name);
+ free (temp_token);
+
+ return 0;
+
+error:
+ free (temp_name);
+ free (temp_token);
+
+ return -1;
+}
+
+int
+rc_rmtree (const char *pathname)
+{
+ char **dirlist = NULL;
+ int i = 0;
+
+ if (!check_arg_str (pathname))
+ return -1;
+
+ if (1 != rc_file_exists (pathname))
+ {
+ errno = ENOENT;
+ DBG_MSG ("'%s' does not rc_file_exists!\n", pathname);
+ return -1;
+ }
+
+ dirlist = rc_ls_dir (pathname, 1);
+ if ((NULL == dirlist) && (0 != errno))
+ {
+ /* Do not error out - caller should decide itself if it
+ * it is an issue */
+ DBG_MSG ("Could not get listing for '%s'!\n", pathname);
+ return -1;
+ }
+
+ while ((NULL != dirlist) && (NULL != dirlist[i]))
+ {
+ /* If it is a directory, call rc_rmtree() again with
+ * it as argument */
+ if (1 == rc_is_dir (dirlist[i], 0))
+ {
+ if (-1 == rc_rmtree (dirlist[i]))
+ {
+ DBG_MSG ("Failed to delete sub directory!\n");
+ goto error;
+ }
+ }
+
+ /* Now actually remove it. Note that if it was a directory,
+ * it should already be removed by above rc_rmtree() call */
+ if ((1 == rc_file_exists (dirlist[i]) && (-1 == remove (dirlist[i]))))
+ {
+ DBG_MSG ("Failed to remove '%s'!\n", dirlist[i]);
+ goto error;
+ }
+ i++;
+ }
+
+ str_list_free (dirlist);
+
+ /* Now remove the parent */
+ if (-1 == remove (pathname))
+ {
+ DBG_MSG ("Failed to remove '%s'!\n", pathname);
+ goto error;
+ }
+
+ return 0;
+error:
+ str_list_free (dirlist);
+
+ return -1;
+}
+
+char **
+rc_ls_dir (const char *pathname, int hidden)
+{
+ DIR *dp;
+ struct dirent *dir_entry;
+ char **dirlist = NULL;
+
+ if (!check_arg_str (pathname))
+ return NULL;
+
+ dp = opendir (pathname);
+ if (NULL == dp)
+ {
+ DBG_MSG ("Failed to call opendir()!\n");
+ /* errno will be set by opendir() */
+ goto error;
+ }
+
+ do
+ {
+ /* Clear errno to distinguish between EOF and error */
+ errno = 0;
+ dir_entry = readdir (dp);
+ /* Only an error if 'errno' != 0, else EOF */
+ if ((NULL == dir_entry) && (0 != errno))
+ {
+ DBG_MSG ("Failed to call readdir()!\n");
+ goto error;
+ }
+ if ((NULL != dir_entry)
+ /* Should we display hidden files? */
+ && (hidden ? 1 : dir_entry->d_name[0] != '.'))
+ {
+ char *d_name = dir_entry->d_name;
+ char *str_ptr;
+
+ /* Do not list current or parent entries */
+ if ((0 == strcmp (d_name, ".")) || (0 == strcmp (d_name, "..")))
+ continue;
+
+ str_ptr = rc_strcatpaths (pathname, d_name);
+ if (NULL == str_ptr)
+ {
+ DBG_MSG ("Failed to allocate buffer!\n");
+ goto error;
+ }
+
+ str_list_add_item (dirlist, str_ptr, error);
+ }
+ }
+ while (NULL != dir_entry);
+
+ if (!check_strv (dirlist))
+ {
+ if (NULL != dirlist)
+ str_list_free (dirlist);
+
+ DBG_MSG ("Directory is empty.\n");
+ }
+
+ closedir (dp);
+
+ return dirlist;
+
+error:
+ /* Free dirlist on error */
+ str_list_free (dirlist);
+
+ if (NULL != dp)
+ {
+ save_errno ();
+ closedir (dp);
+ /* closedir() might have changed it */
+ restore_errno ();
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Below two functions (rc_file_map and rc_file_unmap) are
+ * from udev-050 (udev_utils.c).
+ * (Some are slightly modified, please check udev for originals.)
+ *
+ * Copyright (C) 2004 Kay Sievers <kay@vrfy.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.
+ *
+ */
+
+int
+rc_file_map (const char *filename, char **buf, size_t * bufsize)
+{
+ struct stat stats;
+ int fd;
+
+ fd = open (filename, O_RDONLY);
+ if (fd < 0)
+ {
+ DBG_MSG ("Failed to open file!\n");
+ return -1;
+ }
+
+ if (fstat (fd, &stats) < 0)
+ {
+ DBG_MSG ("Failed to stat file!\n");
+
+ save_errno ();
+ close (fd);
+ restore_errno ();
+
+ return -1;
+ }
+
+ if (0 == stats.st_size)
+ {
+ errno = EINVAL;
+ DBG_MSG ("Failed to mmap file with 0 size!\n");
+
+ save_errno ();
+ close (fd);
+ restore_errno ();
+
+ return -1;
+ }
+
+ *buf = mmap (NULL, stats.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (*buf == MAP_FAILED)
+ {
+ DBG_MSG ("Failed to mmap file!\n");
+
+ save_errno ();
+ close (fd);
+ restore_errno ();
+
+ return -1;
+ }
+ *bufsize = stats.st_size;
+
+ close (fd);
+
+ return 0;
+}
+
+void
+rc_file_unmap (char *buf, size_t bufsize)
+{
+ munmap (buf, bufsize);
+}
+
diff --git a/src/core/librcutil/librcutil.map b/src/core/librcutil/librcutil.map
new file mode 100644
index 0000000..8b6fe0a
--- /dev/null
+++ b/src/core/librcutil/librcutil.map
@@ -0,0 +1,62 @@
+RCUTIL_0.0.0 {
+ global:
+ # debug.c
+ debug_message;
+ check_ptr;
+ check_str;
+ check_strv;
+ check_fd;
+ check_fp;
+ __check_arg_ptr;
+ __check_arg_str;
+ __check_arg_strv;
+ __check_arg_fd;
+ __check_arg_fp;
+ __xcalloc;
+ __xmalloc;
+ __xrealloc;
+ __xstrndup;
+
+ # config.c
+ rc_get_cnf_entry;
+ rc_get_list_file;
+
+ # dynbuf.c
+ new_dyn_buf;
+ new_dyn_buf_mmap_file;
+ free_dyn_buf;
+ write_dyn_buf;
+ write_dyn_buf_from_fd;
+ sprintf_dyn_buf;
+ read_dyn_buf;
+ read_dyn_buf_to_fd;
+ read_line_dyn_buf;
+ dyn_buf_rd_eof;
+ check_dyn_buf;
+ __check_arg_dyn_buf;
+
+ # file.c
+ rc_file_exists;
+ rc_is_file;
+ rc_is_link;
+ rc_is_dir;
+ rc_get_mtime;
+ remove;
+ rc_mktree;
+ rc_rmtree;
+ rc_ls_dir;
+ rc_file_map;
+ rc_file_unmap;
+
+ # simple-regex.c
+ match;
+
+ # string.c
+ rc_memrepchr;
+ rc_strcatpaths;
+ rc_strndup;
+ rc_basename;
+
+ local:
+ *;
+};
diff --git a/src/core/librcutil/simple-regex.c b/src/core/librcutil/simple-regex.c
new file mode 100644
index 0000000..06f48a3
--- /dev/null
+++ b/src/core/librcutil/simple-regex.c
@@ -0,0 +1,869 @@
+/*
+ * simple_regex.c
+ *
+ * Simle regex library.
+ *
+ * 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$
+ */
+
+/*
+ * Some notes:
+ *
+ * - This is a very simple regex library (read: return a match if some string
+ * matches some regex). It is probably not POSIX (if there are a POSIX or
+ * other standard) compatible.
+ *
+ * - I primarily wrote it to _not_ use glibc type regex functions, in case we
+ * might want to use it in code that have to be linked agaist klibc, etc.
+ *
+ * - It really is not optimized in any way yet.
+ *
+ * - Supported operators are:
+ *
+ * '.', '?', '*', '+' - So called 'wildcards'
+ * '[a-z]', '[^a-z]' - Basic 'lists'. Note that 'a-z' just specify that
+ * it supports basic lists as well as sequences ..
+ * The '^' is for an inverted list of course.
+ * '^', '$' - The 'from start' and 'to end' operators. If these
+ * are not used at the start ('^') or end ('$') of the
+ * regex, they will be treated as normal characters
+ * (this of course exclude the use of '^' in a 'list').
+ *
+ * - If an invalid argument was passed, the functions returns 0 with
+ * 'regex_data-match == 0' (no error with no match) rather than -1. It may
+ * not be consistant with other practices, but I personally do not feel it is
+ * a critical error for these types of functions, and there are debugging you
+ * can enable to verify that there are no such issues.
+ *
+ * - __somefunction() is usually a helper function for somefunction(). I guess
+ * recursion might be an alternative, but I try to avoid it.
+ *
+ * - In general if we are matching a 'wildcard' ('*', '+' or '?'), a 'word'
+ * (read: some part of the regex that do not contain a 'wildcard' or 'list')
+ * will have a greater 'weight' than the 'wildcard'. This means that we
+ * will only continue to evaluate the 'wildcard' until the following 'word'
+ * (if any) matches. Currently this do not hold true for a 'list' not
+ * followed by a 'wildcard' - I might fix this in future.
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "rcscripts/rcutil.h"
+
+/* Macro to check if a regex_data_t pointer is valid */
+#define CHECK_REGEX_DATA_P(_regex_data, _on_error) \
+ do { \
+ if ((NULL == _regex_data) \
+ || (NULL == _regex_data->data) \
+ /* We do not check for this, as it might still \
+ * provide a match ('*' or '?' wildcard) */ \
+ /* || (0 == strlen(_regex_data->data)) */ \
+ || (NULL == _regex_data->regex) \
+ || (0 == strlen(_regex_data->regex))) \
+ { \
+ errno = EINVAL; \
+ DBG_MSG("Invalid argument passed!\n"); \
+ goto _on_error; \
+ } \
+ } while (0)
+
+static size_t get_word (const char *regex, char **r_word);
+static int match_word (regex_data_t * regex_data);
+static size_t get_list_size (const char *regex);
+static size_t get_list (const char *regex, char **r_list);
+static int __match_list (regex_data_t * regex_data);
+static int match_list (regex_data_t * regex_data);
+static size_t get_wildcard (const char *regex, char *r_wildcard);
+static int __match_wildcard (regex_data_t * regex_data,
+ int (*match_func) (regex_data_t * regex_data),
+ const char *regex);
+static int match_wildcard (regex_data_t * regex_data);
+static int __match (regex_data_t * regex_data);
+
+/*
+ * Return values for match_* functions
+ *
+ * 0 - There was no error. If there was a match, regex_data->match
+ * - will be > 0 (this is the definitive check - if not true, the
+ * - other values of the struct may be bogus), regex_data->count
+ * - will be the amount of data that was matched (might be 0 for
+ * - some wildcards), and regex_data->r_count will be > 0.
+ *
+ * -1 - An error occured. Check errno for more info.
+ *
+ */
+
+size_t
+get_word (const char *regex, char **r_word)
+{
+ char *r_list;
+ char *str_ptr;
+ size_t count = 0;
+ size_t tmp_count;
+
+ if (!check_arg_str (regex))
+ return 0;
+
+ *r_word = xmalloc (strlen (regex) + 1);
+ if (NULL == r_word)
+ return 0;
+
+ str_ptr = *r_word;
+
+ while (strlen (regex) > 0)
+ {
+ switch (regex[0])
+ {
+ case '*':
+ case '+':
+ case '?':
+ /* If its a wildcard, backup one step */
+ *(--str_ptr) = '\0';
+ count--;
+ return count;
+ case '[':
+ tmp_count = get_list (regex, &r_list);
+ free (r_list);
+ /* In theory should not happen, but you never know
+ * what may happen in future ... */
+ if (-1 == tmp_count)
+ goto error;
+
+ /* Bail if we have a list */
+ if (tmp_count > 0)
+ {
+ str_ptr[0] = '\0';
+ return count;
+ }
+ default:
+ *str_ptr++ = *regex++;
+ count++;
+ break;
+ }
+ }
+
+ str_ptr[0] = '\0';
+
+ return count;
+
+error:
+ free (*r_word);
+
+ return -1;
+}
+
+int
+match_word (regex_data_t * regex_data)
+{
+ char *data_p = regex_data->data;
+ char *r_word = NULL, *r_word_p;
+ size_t count = 0;
+
+ CHECK_REGEX_DATA_P (regex_data, exit);
+
+ count = get_word (regex_data->regex, &r_word);
+ if (-1 == count)
+ goto error;
+ if (0 == count)
+ goto exit;
+ r_word_p = r_word;
+
+ while ((strlen (data_p) > 0) && (strlen (r_word_p) > 0))
+ {
+ /* If 'r_word' is not 100% part of 'string', we do not have
+ * a match. If its a '.', it matches no matter what. */
+ if ((data_p[0] != r_word_p[0]) && ('.' != r_word_p[0]))
+ {
+ count = 0;
+ goto exit;
+ }
+
+ data_p++;
+ r_word_p++;
+ }
+
+ /* If 'string' is shorter than 'r_word', we do not have a match */
+ if ((0 == strlen (data_p)) && (0 < strlen (r_word_p)))
+ {
+ count = 0;
+ goto exit;
+ }
+
+exit:
+ /* Fill in our structure */
+ if (0 == count)
+ regex_data->match = REGEX_NO_MATCH;
+ else if (strlen (regex_data->data) == count)
+ regex_data->match = REGEX_FULL_MATCH;
+ else
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ if (regex_data->match != REGEX_NO_MATCH)
+ regex_data->where = regex_data->data;
+ else
+ regex_data->where = NULL;
+ regex_data->count = count;
+ regex_data->r_count = count;
+
+ free (r_word);
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ free (r_word);
+ return -1;
+}
+
+size_t
+get_list_size (const char *regex)
+{
+ size_t count = 0;
+
+ if (!check_arg_str (regex))
+ return 0;
+
+ if ('[' != regex[0])
+ {
+ errno = EINVAL;
+ DBG_MSG ("Invalid argument passed!\n");
+ return 0;
+ }
+
+ regex++;
+
+ while ((strlen (regex) > 0) && (']' != regex[0]))
+ {
+ /* We have a sequence (x-y) */
+ if (('-' == regex[0])
+ && (']' != regex[1])
+ && (strlen (regex) >= 2) && (regex[-1] < regex[1]))
+ {
+ /* Add current + diff in sequence */
+ count += regex[1] - regex[-1];
+ /* Take care of '-' and next char */
+ regex += 2;
+ }
+ else
+ {
+ regex++;
+ count++;
+ }
+ }
+
+ return count;
+}
+
+size_t
+get_list (const char *regex, char **r_list)
+{
+ char *buf = NULL;
+ size_t count = 0;
+ size_t size;
+
+ if (!check_arg_str (regex))
+ return 0;
+
+ /* Bail if we do not have a list. Do not add debugging, as
+ * it is very noisy (used a lot when we call match_list() in
+ * __match() and match() to test for list matching) */
+ if ('[' != regex[0])
+ return 0;
+
+ size = get_list_size (regex);
+ if (0 == size)
+ {
+ /* Should not be an issue, but just in case */
+ DBG_MSG ("0 returned by get_list_size.\n");
+ return 0;
+ }
+
+ *r_list = xmalloc (size + 1);
+ if (NULL == *r_list)
+ return -1;
+
+ buf = *r_list;
+
+ /* Take care of '[' */
+ regex++;
+ count++;
+
+ while ((strlen (regex) > 0) && (']' != regex[0]))
+ {
+ /* We have a sequence (x-y) */
+ if (('-' == regex[0])
+ && (']' != regex[1])
+ && (strlen (regex) >= 2) && (regex[-1] < regex[1]))
+ {
+ /* Fill in missing chars in sequence */
+ while (buf[-1] < regex[1])
+ {
+ buf[0] = (char) (buf[-1] + 1);
+ buf++;
+ /* We do not increase count */
+ }
+ /* Take care of '-' and next char */
+ count += 2;
+ regex += 2;
+ }
+ else
+ {
+ *buf++ = *regex++;
+ count++;
+ }
+ }
+
+ buf[0] = '\0';
+ /* Take care of ']' */
+ count++;
+
+ /* We do not have a list as it does not end in ']' */
+ if (']' != regex[0])
+ {
+ count = 0;
+ free (*r_list);
+ }
+
+ return count;
+}
+
+/* If the first is the '^' character, everything but the list is matched
+ * NOTE: We only evaluate _ONE_ data character at a time!! */
+int
+__match_list (regex_data_t * regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *list_p = regex_data->regex;
+ char test_regex[2] = { '\0', '\0' };
+ int invert = 0;
+ int lmatch;
+ int retval;
+
+ CHECK_REGEX_DATA_P (regex_data, failed);
+
+ if ('^' == list_p[0])
+ {
+ /* We need to invert the match */
+ invert = 1;
+ /* Make sure '^' is not part of our list */
+ list_p++;
+ }
+
+ if (invert)
+ /* All should be a match if not in the list */
+ lmatch = 1;
+ else
+ /* We only have a match if in the list */
+ lmatch = 0;
+
+ while (strlen (list_p) > 0)
+ {
+ test_regex[0] = list_p[0];
+
+ FILL_REGEX_DATA (tmp_data, data_p, test_regex);
+ retval = match_word (&tmp_data);
+ if (-1 == retval)
+ goto error;
+
+ if (REGEX_MATCH (tmp_data))
+ {
+ if (invert)
+ /* If we exclude the list from
+ * characters we try to match, we
+ * have a match until one of the
+ * list is found. */
+ lmatch = 0;
+ else
+ /* If not, we have to keep looking
+ * until one from the list match
+ * before we have a match */
+ lmatch = 1;
+ break;
+ }
+ list_p++;
+ }
+
+ /* Fill in our structure */
+ if (lmatch)
+ {
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ regex_data->where = regex_data->data;
+ regex_data->count = 1;
+ /* This one is more cosmetic, as match_list() will
+ * do the right thing */
+ regex_data->r_count = 0; /* strlen(regex_data->regex); */
+ }
+ else
+ {
+failed:
+ regex_data->match = REGEX_NO_MATCH;
+ regex_data->where = NULL;
+ regex_data->count = 0;
+ regex_data->r_count = 0;
+ }
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ return -1;
+}
+
+int
+match_list (regex_data_t * regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *list_p = regex_data->regex;
+ char *r_list = NULL;
+ size_t r_count = 0;
+ int retval;
+
+ CHECK_REGEX_DATA_P (regex_data, failed);
+
+ r_count = get_list (list_p, &r_list);
+ if (-1 == r_count)
+ goto error;
+ if (0 == r_count)
+ goto failed;
+
+ FILL_REGEX_DATA (tmp_data, data_p, &list_p[r_count - 1]);
+ retval = __match_wildcard (&tmp_data, __match_list, r_list);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH (tmp_data))
+ {
+ /* This should be 2 ('word' + 'wildcard'), so just remove
+ * the wildcard */
+ tmp_data.r_count--;
+ goto exit;
+ }
+
+ FILL_REGEX_DATA (tmp_data, data_p, r_list);
+ retval = __match_list (&tmp_data);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH (tmp_data))
+ goto exit;
+
+failed:
+ /* We will fill in regex_data below */
+ tmp_data.match = REGEX_NO_MATCH;
+ tmp_data.where = NULL;
+ tmp_data.count = 0;
+ tmp_data.r_count = 0;
+
+exit:
+ /* Fill in our structure */
+ regex_data->match = tmp_data.match;
+ regex_data->where = tmp_data.where;
+ regex_data->count = tmp_data.count;
+ if (regex_data->match != REGEX_NO_MATCH)
+ /* tmp_data.r_count for __match_wildcard will take care of the
+ * wildcard, and tmp_data.r_count for __match_list will be 0 */
+ regex_data->r_count = r_count + tmp_data.r_count;
+ else
+ regex_data->r_count = 0;
+
+ free (r_list);
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ free (r_list);
+ return -1;
+}
+
+size_t
+get_wildcard (const char *regex, char *r_wildcard)
+{
+ if (!check_arg_str (regex))
+ return 0;
+
+ r_wildcard[0] = regex[0];
+ r_wildcard[2] = '\0';
+
+ switch (regex[1])
+ {
+ case '*':
+ case '+':
+ case '?':
+ r_wildcard[1] = regex[1];
+ break;
+ default:
+ r_wildcard[0] = '\0';
+ return 0;
+ }
+
+ return strlen (r_wildcard);
+}
+
+int
+__match_wildcard (regex_data_t * regex_data,
+ int (*match_func) (regex_data_t * regex_data),
+ const char *regex)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *wildcard_p = regex_data->regex;
+ char r_wildcard[3];
+ size_t count = 0;
+ size_t r_count = 0;
+ int is_match = 0;
+ int retval;
+
+ CHECK_REGEX_DATA_P (regex_data, exit);
+
+ if (NULL == match_func)
+ {
+ errno = EINVAL;
+ DBG_MSG ("NULL match_func was passed!\n");
+ goto exit;
+ }
+
+ r_count = get_wildcard (wildcard_p, r_wildcard);
+ if (0 == r_count)
+ goto exit;
+
+ FILL_REGEX_DATA (tmp_data, data_p, (char *) regex);
+ retval = match_func (&tmp_data);
+ if (-1 == retval)
+ goto error;
+
+ switch (r_wildcard[1])
+ {
+ case '*':
+ case '?':
+ /* '*' and '?' always matches */
+ is_match = 1;
+ case '+':
+ /* We need to match all of them */
+ do
+ {
+ /* If we have at least one match for '+', or none
+ * for '*' or '?', check if we have a word or list match.
+ * We do this because a word weights more than a wildcard */
+ if ((strlen (wildcard_p) > 2)
+ && ((count > 0)
+ || ('*' == r_wildcard[1])
+ || ('?' == r_wildcard[1])))
+ {
+ regex_data_t tmp_data2;
+#if 0
+ printf ("data_p = %s, wildcard_p = %s\n", data_p, wildcard_p);
+#endif
+
+ FILL_REGEX_DATA (tmp_data2, data_p, &wildcard_p[2]);
+ retval = match (&tmp_data2);
+ if (-1 == retval)
+ goto error;
+
+ if (
+ /* '.' might be a special case ... */
+ /* ('.' != wildcard_p[2]) && */
+ ((REGEX_MATCH (tmp_data2))
+ && (REGEX_FULL_MATCH == tmp_data2.match)))
+ {
+ goto exit;
+ }
+ }
+
+ if (REGEX_MATCH (tmp_data))
+ {
+ data_p += tmp_data.count;
+ count += tmp_data.count;
+ is_match = 1;
+
+ FILL_REGEX_DATA (tmp_data, data_p, (char *) regex);
+ retval = match_func (&tmp_data);
+ if (-1 == retval)
+ goto error;
+ }
+ /* Only once for '?' */
+ }
+ while ((REGEX_MATCH (tmp_data)) && ('?' != r_wildcard[1]));
+
+ break;
+ default:
+ /* No wildcard */
+ break;
+ }
+
+exit:
+ /* Fill in our structure */
+ /* We can still have a match ('*' and '?'), although count == 0 */
+ if ((0 == count) && (0 == is_match))
+ regex_data->match = REGEX_NO_MATCH;
+ else if (strlen (regex_data->data) == count)
+ regex_data->match = REGEX_FULL_MATCH;
+ else
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ if (regex_data->match != REGEX_NO_MATCH)
+ regex_data->where = regex_data->data;
+ else
+ regex_data->where = NULL;
+ regex_data->count = count;
+ regex_data->r_count = r_count;
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ return -1;
+}
+
+int
+match_wildcard (regex_data_t * regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *wildcard_p = regex_data->regex;
+ char r_wildcard[3];
+ size_t r_count;
+ int retval;
+
+ CHECK_REGEX_DATA_P (regex_data, failed);
+
+ /* Invalid wildcard - we need a character + a regex operator */
+ if (strlen (wildcard_p) < 2)
+ goto failed;
+
+ r_count = get_wildcard (wildcard_p, r_wildcard);
+ if (0 == r_count)
+ goto failed;
+
+ /* Needed so that match_word() will not bail if it sees the wildcard */
+ r_wildcard[1] = '\0';
+
+ FILL_REGEX_DATA (tmp_data, data_p, wildcard_p);
+ retval = __match_wildcard (&tmp_data, match_word, r_wildcard);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH (tmp_data))
+ goto exit;
+
+failed:
+ /* We will fill in regex_data below */
+ tmp_data.match = REGEX_NO_MATCH;
+ tmp_data.where = NULL;
+ tmp_data.count = 0;
+ tmp_data.r_count = 0;
+
+exit:
+ /* Fill in our structure */
+ regex_data->match = tmp_data.match;
+ regex_data->where = tmp_data.where;
+ regex_data->count = tmp_data.count;
+ regex_data->r_count = tmp_data.r_count;
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ return -1;
+}
+
+int
+__match (regex_data_t * regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *regex_p = regex_data->regex;
+ size_t count = 0;
+ size_t r_count = 0;
+ int rmatch = 0;
+ int retval;
+
+ CHECK_REGEX_DATA_P (regex_data, failed);
+
+ while (strlen (regex_p) > 0)
+ {
+#if 0
+ printf ("data_p = '%s', regex_p = '%s'\n", data_p, regex_p);
+#endif
+
+ FILL_REGEX_DATA (tmp_data, data_p, regex_p);
+ retval = match_list (&tmp_data);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH (tmp_data))
+ goto have_match;
+
+ FILL_REGEX_DATA (tmp_data, data_p, regex_p);
+ retval = match_wildcard (&tmp_data);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH (tmp_data))
+ goto have_match;
+
+ FILL_REGEX_DATA (tmp_data, data_p, regex_p);
+ retval = match_word (&tmp_data);
+ if (-1 == retval)
+ goto error;
+ if (REGEX_MATCH (tmp_data))
+ goto have_match;
+
+ break;
+
+have_match:
+ data_p += tmp_data.count;
+ count += tmp_data.count;
+ regex_p += tmp_data.r_count;
+ r_count += tmp_data.r_count;
+ rmatch = 1;
+
+ /* Check that we do not go out of bounds */
+ if (((data_p - regex_data->data) > strlen (regex_data->data))
+ || ((regex_p - regex_data->regex) > strlen (regex_data->regex)))
+ goto failed;
+ }
+
+ /* We could not match the whole regex (data too short?) */
+ if (0 != strlen (regex_p))
+ goto failed;
+
+ goto exit;
+
+failed:
+ /* We will fill in regex_data below */
+ count = 0;
+ r_count = 0;
+ rmatch = 0;
+
+exit:
+ /* Fill in our structure */
+ /* We can still have a match ('*' and '?'), although count == 0 */
+ if ((0 == count) && (0 == rmatch))
+ regex_data->match = REGEX_NO_MATCH;
+ else if (strlen (regex_data->data) == count)
+ regex_data->match = REGEX_FULL_MATCH;
+ else
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ if (regex_data->match != REGEX_NO_MATCH)
+ regex_data->where = regex_data->data;
+ else
+ regex_data->where = NULL;
+ regex_data->count = count;
+ regex_data->r_count = r_count;
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+
+ return -1;
+}
+
+int
+match (regex_data_t * regex_data)
+{
+ regex_data_t tmp_data;
+ char *data_p = regex_data->data;
+ char *regex_p;
+ char *buf = NULL;
+ int from_start = 0;
+ int to_end = 0;
+ int retval;
+
+ CHECK_REGEX_DATA_P (regex_data, failed);
+
+ /* We might be modifying regex_p, so make a copy */
+ buf = xstrndup (regex_data->regex, strlen (regex_data->regex));
+ if (NULL == buf)
+ goto error;
+
+ regex_p = buf;
+
+ /* Should we only match from the start? */
+ if ('^' == regex_p[0])
+ {
+ regex_p++;
+ from_start = 1;
+ }
+
+ /* Should we match up to the end? */
+ if ('$' == regex_p[strlen (regex_p) - 1])
+ {
+ regex_p[strlen (regex_p) - 1] = '\0';
+ to_end = 1;
+ }
+
+ do
+ {
+ FILL_REGEX_DATA (tmp_data, data_p, regex_p);
+ retval = __match (&tmp_data);
+ if (-1 == retval)
+ goto error;
+ }
+ while ((strlen (data_p++) > 0)
+ && (!REGEX_MATCH (tmp_data)) && (0 == from_start));
+
+ /* Compensate for above extra inc */
+ data_p--;
+
+ /* Fill in our structure */
+ if (REGEX_MATCH (tmp_data))
+ {
+ /* Check if we had an '$' at the end of the regex, and
+ * verify that we still have a match */
+ if ((1 == to_end) && (tmp_data.count != strlen (data_p)))
+ {
+ goto failed;
+ }
+
+ if ((data_p == regex_data->data)
+ && (tmp_data.match == REGEX_FULL_MATCH))
+ regex_data->match = REGEX_FULL_MATCH;
+ else
+ regex_data->match = REGEX_PARTIAL_MATCH;
+ regex_data->where = data_p;
+ regex_data->count = tmp_data.count;
+ regex_data->r_count = tmp_data.r_count;
+ if (1 == from_start)
+ regex_data->r_count++;
+ if (1 == to_end)
+ regex_data->r_count++;
+ }
+ else
+ {
+failed:
+ regex_data->match = REGEX_NO_MATCH;
+ regex_data->where = NULL;
+ regex_data->count = 0;
+ regex_data->r_count = 0;
+ }
+
+ free (buf);
+
+ return 0;
+
+error:
+ regex_data->match = REGEX_NO_MATCH;
+ free (buf);
+
+ return -1;
+}
diff --git a/src/core/librcutil/string.c b/src/core/librcutil/string.c
new file mode 100644
index 0000000..4081b12
--- /dev/null
+++ b/src/core/librcutil/string.c
@@ -0,0 +1,109 @@
+/*
+ * string.c
+ *
+ * Miscellaneous macro's and functions.
+ *
+ * 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 <stdlib.h>
+
+#include "rcscripts/rcutil.h"
+
+char *
+rc_memrepchr (char **str, char old, char new, size_t size)
+{
+ char *str_p;
+
+ if (!check_arg_strv (str))
+ return NULL;
+
+ str_p = memchr (*str, old, size);
+
+ while (NULL != str_p)
+ {
+ str_p[0] = new;
+ str_p = memchr (&str_p[1], old, size - (str_p - *str) - 1);
+ }
+
+ return *str;
+}
+
+char *
+rc_strcatpaths (const char *pathname1, const char *pathname2)
+{
+ char *new_path = NULL;
+ int lenght;
+
+ if ((!check_arg_str (pathname1)) || (!check_arg_str (pathname2)))
+ return 0;
+
+ /* Lenght of pathname1 + lenght of pathname2 + '/' if needed */
+ lenght = strlen (pathname1) + strlen (pathname2) + 2;
+ /* lenght + '\0' */
+ new_path = xmalloc (lenght);
+ if (NULL == new_path)
+ return NULL;
+
+ snprintf (new_path, lenght, "%s%s%s", pathname1,
+ (new_path[strlen (new_path) - 1] != '/') ? "/" : "",
+ pathname2);
+
+ return new_path;
+}
+
+char *
+rc_strndup (const char *str, size_t size)
+{
+ char *new_str = NULL;
+ size_t len;
+
+ /* We cannot check if its a valid string here, as it might
+ * not be '\0' terminated ... */
+ if (!check_arg_ptr (str))
+ return NULL;
+
+ /* Check lenght of str without breaching the size limit */
+ for (len = 0; (len < size) && ('\0' != str[len]); len++);
+
+ new_str = xmalloc (len + 1);
+ if (NULL == new_str)
+ return NULL;
+
+ /* Make sure our string is NULL terminated */
+ new_str[len] = '\0';
+
+ return (char *) memcpy (new_str, str, len);
+}
+
+char *
+rc_basename (const char *path)
+{
+ char *new_path = NULL;
+
+ if (!check_arg_str (path))
+ return NULL;
+
+ /* Copied from glibc */
+ new_path = strrchr (path, '/');
+ return new_path ? new_path + 1 : (char *) path;
+}
+
diff --git a/src/core/src/Makefile.am b/src/core/src/Makefile.am
index 27d52d9..6c9dd49 100644
--- a/src/core/src/Makefile.am
+++ b/src/core/src/Makefile.am
@@ -3,15 +3,19 @@ sbin_PROGRAMS = \
runscript
INCLUDES = \
- -I$(top_srcdir) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/librccore \
+ -I$(top_builddir)/include \
$(RCSCRIPTS_DEFINES)
-LIBRCSCRIPTS = $(top_builddir)/librcscripts/librcscripts.la
+LIBRCUTIL = $(top_builddir)/librcutil/librcutil.la
+LIBRCCORE = $(top_builddir)/librccore/librccore.la
depscan_CFLAGS = -DLEGACY_DEPSCAN
-depscan_LDADD = $(LIBRCSCRIPTS)
+depscan_LDADD = $(LIBRCCORE)
depscan_SOURCES = depscan.c
-runscript_LDADD = $(LIBRCSCRIPTS) -ldl
+runscript_LDADD = $(LIBRCUTIL) -ldl
runscript_SOURCES = runscript.c
diff --git a/src/core/src/depscan.c b/src/core/src/depscan.c
index d5a18a3..a1e322b 100644
--- a/src/core/src/depscan.c
+++ b/src/core/src/depscan.c
@@ -34,7 +34,9 @@
#include <fcntl.h>
#include <unistd.h>
-#include "librcscripts/rcscripts.h"
+#include "rcscripts/rccore.h"
+
+#include "librccore/internal/rccore.h"
char *svcdir_subdirs[] = {
"softscripts",
@@ -64,9 +66,9 @@ create_directory (const char *name)
return -1;
/* Check if directory exist, and is not a symlink */
- if (!is_dir (name, 0))
+ if (!rc_is_dir (name, 0))
{
- if (exists (name))
+ if (rc_file_exists (name))
{
/* Remove it if not a directory */
if (-1 == unlink (name))
@@ -76,7 +78,7 @@ create_directory (const char *name)
}
}
/* Now try to create the directory */
- if (-1 == mktree (name, 0755))
+ if (-1 == rc_mktree (name, 0755))
{
DBG_MSG ("Failed to create '%s'!\n", name);
return -1;
@@ -104,7 +106,7 @@ create_var_dirs (const char *svcdir)
while (NULL != svcdir_subdirs[i])
{
- tmp_path = strcatpaths (svcdir, svcdir_subdirs[i]);
+ tmp_path = rc_strcatpaths (svcdir, svcdir_subdirs[i]);
if (NULL == tmp_path)
{
DBG_MSG ("Failed to allocate buffer!\n");
@@ -136,7 +138,7 @@ delete_var_dirs (const char *svcdir)
return -1;
/* Just quit if svcdir do not exist */
- if (!exists (svcdir))
+ if (!rc_file_exists (svcdir))
{
DBG_MSG ("'%s' does not exist!\n", svcdir);
return 0;
@@ -144,7 +146,7 @@ delete_var_dirs (const char *svcdir)
while (NULL != svcdir_volatile_subdirs[i])
{
- tmp_path = strcatpaths (svcdir, svcdir_volatile_subdirs[i]);
+ tmp_path = rc_strcatpaths (svcdir, svcdir_volatile_subdirs[i]);
if (NULL == tmp_path)
{
DBG_MSG ("Failed to allocate buffer!\n");
@@ -152,11 +154,11 @@ delete_var_dirs (const char *svcdir)
}
/* Skip the directory if it does not exist */
- if (!exists (tmp_path))
+ if (!rc_file_exists (tmp_path))
goto _continue;
/* Check and delete all files and sub directories if needed */
- if (-1 == rmtree (tmp_path))
+ if (-1 == rc_rmtree (tmp_path))
{
DBG_MSG ("Failed to delete '%s'!\n", tmp_path);
free (tmp_path);
@@ -195,7 +197,7 @@ main (void)
exit (EXIT_FAILURE);
}
- svcdir = get_cnf_entry (RC_CONFD_FILE_NAME, SVCDIR_CONFIG_ENTRY);
+ svcdir = rc_get_cnf_entry (RC_CONFD_FILE_NAME, SVCDIR_CONFIG_ENTRY);
if (NULL == svcdir)
{
EERROR ("Failed to get config entry '%s'!\n", SVCDIR_CONFIG_ENTRY);
@@ -219,14 +221,14 @@ main (void)
exit (EXIT_FAILURE);
}
- cachefile = strcatpaths (svcdir, LEGACY_CACHE_FILE_NAME);
+ cachefile = rc_strcatpaths (svcdir, LEGACY_CACHE_FILE_NAME);
if (NULL == cachefile)
{
DBG_MSG ("Failed to allocate buffer!\n");
exit (EXIT_FAILURE);
}
- tmp_cachefile = strcatpaths (cachefile, "XXXXXX");
+ tmp_cachefile = rc_strcatpaths (cachefile, "XXXXXX");
if (NULL == tmp_cachefile)
{
DBG_MSG ("Failed to allocate buffer!\n");
@@ -300,7 +302,7 @@ main (void)
write_legacy_stage3 (cachefile_fd);
fclose (cachefile_fd);
- if ((-1 == unlink (cachefile)) && (exists (cachefile)))
+ if ((-1 == unlink (cachefile)) && (rc_file_exists (cachefile)))
{
EERROR ("Could not remove '%s'!\n", cachefile);
unlink (tmp_cachefile);
diff --git a/src/core/src/runscript.c b/src/core/src/runscript.c
index e80774f..8ab0ec2 100644
--- a/src/core/src/runscript.c
+++ b/src/core/src/runscript.c
@@ -16,16 +16,19 @@
#include <sys/wait.h>
#include <dlfcn.h>
-#include "librcscripts/rcscripts.h"
+#include "rcscripts/rcutil.h"
+#include "rcscripts/rcdefines.h"
+#include "librccore/internal/rccore.h"
#define IS_SBIN_RC() ((caller) && (0 == strcmp (caller, SBIN_RC)))
+#if defined(WANT_SELINUX)
static void (*selinux_run_init_old) (void);
static void (*selinux_run_init_new) (int argc, char **argv);
-#if defined(WANT_SELINUX)
void setup_selinux (int argc, char **argv);
#endif
+
char ** filter_environ (char *caller);
extern char **environ;
@@ -71,19 +74,19 @@ filter_environ (char *caller)
* environment should be fine */
return environ;
- if (1 == is_file (SYS_WHITELIST, 1))
- whitelist = get_list_file (whitelist, SYS_WHITELIST);
+ if (1 == rc_is_file (SYS_WHITELIST, 1))
+ whitelist = rc_get_list_file (whitelist, SYS_WHITELIST);
else
EWARN ("System environment whitelist missing!\n");
- if (1 == is_file (USR_WHITELIST, 1))
- whitelist = get_list_file (whitelist, USR_WHITELIST);
+ if (1 == rc_is_file (USR_WHITELIST, 1))
+ whitelist = rc_get_list_file (whitelist, USR_WHITELIST);
if (NULL == whitelist)
/* If no whitelist is present, revert to old behaviour */
return environ;
- if (1 != is_file (PROFILE_ENV, 1))
+ if (1 != rc_is_file (PROFILE_ENV, 1))
/* XXX: Maybe warn here? */
check_profile = 0;
@@ -111,7 +114,7 @@ filter_environ (char *caller)
snprintf (tmp_env_name, tmp_len, "export %s", env_name);
- env_var = get_cnf_entry (PROFILE_ENV, tmp_env_name);
+ env_var = rc_get_cnf_entry (PROFILE_ENV, tmp_env_name);
free (tmp_env_name);
if ((NULL == env_var) && (0 != errno) && (ENOMSG != errno))
goto error;
diff --git a/src/core/tests/Makefile.am b/src/core/tests/Makefile.am
index 2fb4ee7..2f2d305 100644
--- a/src/core/tests/Makefile.am
+++ b/src/core/tests/Makefile.am
@@ -1,6 +1,10 @@
-INCLUDES = -I$(top_srcdir)
+INCLUDES = \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir)/include
-LIBRCSCRIPTS = $(top_builddir)/librcscripts/librcscripts.la
+LIBRCUTIL = $(top_builddir)/librcutil/librcutil.la
+LIBRCCORE = $(top_builddir)/librccore/librccore.la
TESTS = \
test-dynbuf \
@@ -9,16 +13,20 @@ TESTS = \
noinst_PROGRAMS = \
test-runlevels \
+ test-services \
$(TESTS)
-test_dynbuf_LDADD = $(LIBRCSCRIPTS)
+test_dynbuf_LDADD = $(LIBRCUTIL)
test_dynbuf_SOURCES = test-dynbuf.c
-test_mmap_LDADD = $(LIBRCSCRIPTS)
+test_mmap_LDADD = $(LIBRCUTIL)
test_mmap_SOURCES = test-mmap.c
-test_regex_LDADD = $(LIBRCSCRIPTS)
+test_regex_LDADD = $(LIBRCUTIL)
test_regex_SOURCES = test-regex.c
-test_runlevels_LDADD = $(LIBRCSCRIPTS)
+test_runlevels_LDADD = $(LIBRCCORE)
test_runlevels_SOURCES = test-runlevels.c
+
+test_services_LDADD = $(LIBRCCORE)
+test_services_SOURCES = test-services.c
diff --git a/src/core/tests/test-dynbuf.c b/src/core/tests/test-dynbuf.c
index 27ff894..836a9f1 100644
--- a/src/core/tests/test-dynbuf.c
+++ b/src/core/tests/test-dynbuf.c
@@ -26,7 +26,7 @@
#include <stdlib.h>
#include <string.h>
-#include "librcscripts/rcscripts.h"
+#include "rcscripts/rcutil.h"
#define TEST_STRING1 "Hello"
#define TEST_STRING2 " "
diff --git a/src/core/tests/test-mmap.c b/src/core/tests/test-mmap.c
index 198968f..9df0622 100644
--- a/src/core/tests/test-mmap.c
+++ b/src/core/tests/test-mmap.c
@@ -1,7 +1,7 @@
/*
* test-mmap.c
*
- * Test for file_map().
+ * Test for rc_file_map().
*
* Copyright (C) 2004,2005 Martin Schlemmer <azarah@nosferatu.za.org>
*
@@ -24,7 +24,7 @@
#include <stdlib.h>
-#include "librcscripts/rcscripts.h"
+#include "rcscripts/rcutil.h"
int
main (void)
@@ -32,13 +32,13 @@ main (void)
char *buf;
size_t bufsize;
- if (-1 == file_map ("/etc/fstab", &buf, &bufsize))
+ if (-1 == rc_file_map ("/etc/fstab", &buf, &bufsize))
{
DBG_MSG ("Failed to mmap file");
exit (EXIT_FAILURE);
}
- file_unmap (buf, bufsize);
+ rc_file_unmap (buf, bufsize);
return 0;
}
diff --git a/src/core/tests/test-regex.c b/src/core/tests/test-regex.c
index b827d3b..0178051 100644
--- a/src/core/tests/test-regex.c
+++ b/src/core/tests/test-regex.c
@@ -26,7 +26,7 @@
#include <stdlib.h>
#include <string.h>
-#include "librcscripts/rcscripts.h"
+#include "rcscripts/rcutil.h"
char *test_data[] = {
/* string, pattern, match (1 = yes, 0 = no) */
diff --git a/src/core/tests/test-runlevels.c b/src/core/tests/test-runlevels.c
index dde6850..7ef5492 100644
--- a/src/core/tests/test-runlevels.c
+++ b/src/core/tests/test-runlevels.c
@@ -24,7 +24,12 @@
#include <stdlib.h>
-#include "librcscripts/rcscripts.h"
+#include "rcscripts/rccore.h"
+
+#include "librccore/api/scripts.h"
+#include "librccore/api/runlevels.h"
+#include "librccore/api/parse.h"
+#include "librccore/api/depend.h"
int
main (void)
@@ -49,7 +54,7 @@ main (void)
list_for_each_entry (script_info, &rcscript_list, node)
{
- printf (" - %s\n", gbasename (script_info->filename));
+ printf (" - %s\n", rc_basename (script_info->filename));
}
printf ("\n");
@@ -57,11 +62,11 @@ main (void)
list_for_each_entry (runlevel_info, &runlevel_list, node)
{
- EINFO ("Runlevel %s:\n", gbasename (runlevel_info->dirname));
+ EINFO ("Runlevel %s:\n", rc_basename (runlevel_info->dirname));
list_for_each_entry (script_info, &runlevel_info->entries, node)
{
- printf (" - %s\n", gbasename (script_info->filename));
+ printf (" - %s\n", rc_basename (script_info->filename));
}
}
diff --git a/src/core/tests/test-services.c b/src/core/tests/test-services.c
new file mode 100644
index 0000000..5c4d5f5
--- /dev/null
+++ b/src/core/tests/test-services.c
@@ -0,0 +1,51 @@
+/*
+ * test-services.c
+ *
+ * Test for services functionality.
+ *
+ * Copyright (C) 2004-2006 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 <stdlib.h>
+
+#include "rcscripts/rccore.h"
+
+int
+main (int argc, char **argv)
+{
+ /* Needed to init config variables, etc! */
+ rc_init ();
+
+ if (argc >= 2)
+ {
+ if (rc_service_test_state (argv[1], rc_service_started))
+ {
+ EINFO ("Service '%s': started\n", argv[1]);
+ exit (EXIT_SUCCESS);
+ }
+ else
+ {
+ EERROR ("Service '%s': stopped\n", argv[1]);
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ return 0;
+}
+
diff --git a/src/env_whitelist b/src/env_whitelist
index ef30661..780afc6 100644
--- a/src/env_whitelist
+++ b/src/env_whitelist
@@ -21,6 +21,22 @@ USER
HOME
TERM
+# Language variables
+LANG
+LC_CTYPE
+LC_NUMERIC
+LC_TIME
+LC_COLLATE
+LC_MONETARY
+LC_MESSAGES
+LC_PAPER
+LC_NAME
+LC_ADDRESS
+LC_TELEPHONE
+LC_MEASUREMENT
+LC_IDENTIFICATION
+LC_ALL
+
# From /sbin/init
PATH
INIT_VERSION