diff options
Diffstat (limited to 'src')
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 |