Rewrite the core parts in C. We now provide librc so other programs can

query runlevels, services and state without using bash. We also provide
libeinfo so other programs can easily use our informational functions.

As such, we have dropped the requirement of using bash as the init script
shell. We now use /bin/sh and have strived to make the scripts as portable
as possible. Shells that work are bash and dash. busybox works provided
you disable s-s-d. If you have WIPE_TMP set to yes in conf.d/bootmisc you
should disable find too.
zsh and ksh do not work at this time.

Networking support is currently being re-vamped also as it was heavily bash
array based. As such, a new config format is available like so
config_eth0="1.2.3.4/24 5.6.7.8/16"
or like so
config_eth0="'1.2.3.4 netmask 255.255.255.0' '5.6.7.8 netmask 255.255.0.0'"

We will still support the old bash array format provided that /bin/sh IS
a link it bash.

ChangeLog for baselayout-1 can be found in our SVN repo.
This commit is contained in:
Roy Marples
2007-04-05 11:18:42 +00:00
commit 5af58b4514
169 changed files with 20917 additions and 0 deletions

153
src/Makefile Normal file
View File

@@ -0,0 +1,153 @@
# Copyright 1999-2007 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
CC ?= gcc
CFLAGS ?= -Wall -O2 -pipe
CFLAGS += -pedantic -std=c99 \
-Wall -Wextra -Wunused -Wimplicit -Wshadow -Wformat=2 \
-Wmissing-declarations -Wno-missing-prototypes -Wwrite-strings \
-Wbad-function-cast -Wnested-externs -Wcomment -Winline \
-Wchar-subscripts -Wcast-align -Wno-format-nonliteral
# Early GCC versions don't support these flags, so you may need to comment
# this line out
CFLAGS += -Wsequence-point -Wextra -Wdeclaration-after-statement
# For debugging. -Werror is pointless due to ISO C issues with dlsym
#CFLAGS += -ggdb
DESTDIR =
LIB = lib
LIBEINFOSOVER = 0
LIBEINFOSO = libeinfo.so.$(LIBRCSOVER)
LIBEINFOOBJS= libeinfo.o
LIBRCSOVER = 0
LIBRCSO = librc.so.$(LIBRCSOVER)
LIBRCOBJS= librc.o librc-depend.o librc-daemon.o librc-misc.o librc-strlist.o
LIB_TARGETS = $(LIBEINFOSO) $(LIBRCSO)
BIN_TARGETS = rc-status
SBIN_TARGETS = env-update fstabinfo mountinfo \
rc rc-depend rc-update runscript start-stop-daemon
SYS_WHITELIST = env_whitelist
TARGET = $(LIB_TARGETS) $(BIN_TARGETS) $(SBIN_TARGETS)
RCLINKS = einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \
eindent eoutdent eflush color_terminal \
veinfo vewarn vebegin veend vewend veindent veoutdent \
service_starting service_inactive service_started \
service_stopping service_stopped \
service_inactive service_wasinactive \
service_coldplugged \
mark_service_starting mark_service_inactive mark_service_started \
mark_service_stopping mark_service_stopped \
mark_service_inactive mark_service_wasinactive \
mark_service_coldplugged \
get_options save_options \
is_runlevel_start is_runlevel_stop service_started_daemon
# Quick hack to make my life easier on BSD and Linux
ifeq ($(OS),)
OS=$(shell uname -s)
ifneq ($(OS),Linux)
OS=BSD
endif
endif
ifeq ($(OS),Linux)
LDLIBS_RC = -ldl
LDLIBS_RS = -ldl
# Shouldn't need this, but it's the easiest workaround for silly
# Linux headers that don't work with -std=c99
override CFLAGS += -D_GNU_SOURCE
endif
ifeq ($(OS),BSD)
override LDLIBS += -lkvm
endif
HAVE_PAM =
ifdef HAVE_PAM
CFLAGS_SSD = -DHAVE_PAM
LDLIBS_SSD = -lpam
endif
# We also define _BSD_SOURCE so both Linux and the BSDs get a few
# handy functions which makes our lives a lot easier
override CFLAGS += -DLIBDIR=\"$(LIB)\"
# IMPORTANT!!!
# Remove this when releasing as it's a security risk
# However, this does save us using libtool when we're testing
# NOTE: The toplevel Makefile for baselayout will automatically
# disable then when doing `make dist`
##override LDFLAGS += -Wl,-rpath .
all: $(TARGET)
$(LIBEINFOOBJS): CFLAGS += -fPIC
$(LIBEINFOSO): LDLIBS =
$(LIBEINFOSO): $(LIBEINFOOBJS)
$(CC) -fPIC -shared -Wl,-soname,$(LIBEINFOSO) -o $(LIBEINFOSO) $(LIBEINFOOBJS)
ln -sf $(LIBEINFOSO) libeinfo.so
$(LIBRCOBJS): CFLAGS += -fPIC
$(LIBRCSO): $(LIBRCOBJS)
$(CC) -fPIC -shared -Wl,-soname,$(LIBRCSO) -o $(LIBRCSO) $(LIBRCOBJS)
ln -sf $(LIBRCSO) librc.so
splash: CFLAGS += -fPIC
splash: splash.o
$(CC) -fPIC -shared -Wl,-soname,splash.so -o splash.so splash.o
env-update: $(LIBEINFOSO) $(LIBRCSO) env-update.o
fstabinfo: $(LIBEINFOSO) fstabinfo.o
mountinfo: $(LIBEINFOSO) $(LIBRCSO) mountinfo.o
rc-depend: $(LIBEINFOSO) $(LIBRCSO) rc-depend.o
rc-status: $(LIBEINFOSO) $(LIBRCSO) rc-status.o
rc-update: $(LIBEINFOSO) $(LIBRCSO) rc-update.o
rc: LDLIBS += $(LDLIBS_RC)
rc: $(LIBEINFOSO) $(LIBRCSO) rc-plugin.o rc.o
runscript: LDLIBS += $(LDLIBS_RS)
runscript: $(LIBEINFOSO) $(LIBRCSO) rc-plugin.o runscript.o
start-stop-daemon: CFLAGS += $(CFLAGS_SSD)
start-stop-daemon: LDLIBS += $(LDLIBS_SSD)
start-stop-daemon: $(LIBEINFOSO) $(LIBRCSO) start-stop-daemon.o
links: rc
for x in $(RCLINKS) $(RCPRIVLINKS); do ln -sf rc $$x; done
install: $(TARGET)
install -m 0755 -d $(DESTDIR)/$(LIB)
install -m 0755 $(LIB_TARGETS) $(DESTDIR)/$(LIB)
ln -sf $(LIBEINFOSO) $(DESTDIR)/$(LIB)/libeinfo.so
ln -sf $(LIBRCSO) $(DESTDIR)/$(LIB)/librc.so
install -m 0755 -d $(DESTDIR)/usr/include
install -m 0644 einfo.h rc.h $(DESTDIR)/usr/include
install -m 0755 -d $(DESTDIR)/bin
install -m 0755 $(BIN_TARGETS) $(DESTDIR)/bin
install -m 0755 -d $(DESTDIR)/sbin
install -m 0755 $(SBIN_TARGETS) $(DESTDIR)/sbin
install -m 0755 -d $(DESTDIR)/$(LIB)/rcscripts/conf.d
install -m 0644 $(SYS_WHITELIST) $(DESTDIR)/$(LIB)/rcscripts/conf.d
install -m 0755 -d $(DESTDIR)/$(LIB)/rcscripts/bin
for x in $(RCLINKS); do ln -sf $(DESTDIR)/sbin/rc $(DESTDIR)/$(LIB)/rcscripts/bin/$$x; done
if test "$(HAVE_PAM)" != "" ; then \
install -m 0755 -d $(DESTDIR)/etc/pam.d ; \
install -m 0644 start-stop-daemon.pam $(DESTDIR)/etc/pam.d/start-stop-daemon ; \
fi
clean:
rm -f $(TARGET) $(RCLINKS) $(RCPRIVLINKS)
rm -f *.o *~ *.core *.so

83
src/einfo.h Normal file
View File

@@ -0,0 +1,83 @@
/*
rc.h
Header file for external applications to get RC information.
Copyright 2007 Gentoo Foundation
Released under the GPLv2
*/
#ifndef __EINFO_H__
#define __EINFO_H__
#ifdef __GNUC__
# define EINFO_PRINTF(_one, _two) __attribute__ ((__format__ (__printf__, _one, _two)))
# define EINFO_XPRINTF(_one, _two) __attribute__ ((__noreturn__, __format__ (__printf__, _one, _two)))
#endif
#include <sys/types.h>
#include <stdbool.h>
typedef enum
{
einfo_good,
einfo_warn,
einfo_bad,
einfo_hilite,
einfo_bracket,
einfo_normal
} einfo_color_t;
/* Colour codes used by the below functions. */
#define EINFO_GOOD "\033[32;01m"
#define EINFO_WARN "\033[33;01m"
#define EINFO_BAD "\033[31;01m"
#define EINFO_HILITE "\033[36;01m"
#define EINFO_BRACKET "\033[34;01m"
#define EINFO_NORMAL "\033[0m"
/* Handy macros to easily use the above in a custom manner */
#define PEINFO_GOOD if (colour_terminal ()) printf (EINFO_GOOD)
#define PEINFO_WARN if (colour_terminal ()) printf (EINFO_WARN)
#define PEINFO_BAD if (colour_terminal ()) printf (EINFO_BAD)
#define PEINFO_HILITE if (colour_terminal ()) printf (EINFO_HILITE)
#define PEINFO_BRACKET if (colour_terminal ()) printf (EINFO_BRACKET)
#define PEINFO_NORMAL if (colour_terminal ()) printf (EINFO_NORMAL)
/* We work out if the terminal supports colour or not through the use
of the TERM env var. We cache the reslt in a static bool, so
subsequent calls are very fast. */
/* The n suffix means that a newline is NOT appended to the string
The v prefix means that we only print it when RC_VERBOSE=yes */
bool colour_terminal (void);
int einfon (const char *fmt, ...) EINFO_PRINTF (1, 2);
int ewarnn (const char *fmt, ...) EINFO_PRINTF (1, 2);
int eerrorn (const char *fmt, ...) EINFO_PRINTF (1, 2);
int einfo (const char *fmt, ...) EINFO_PRINTF(1, 2);
int ewarn (const char *fmt, ...) EINFO_PRINTF (1, 2);
void ewarnx (const char *fmt, ...) EINFO_XPRINTF (1,2);
int eerror (const char *fmt, ...) EINFO_PRINTF (1,2);
void eerrorx (const char *fmt, ...) EINFO_XPRINTF (1,2);
int ebegin (const char *fmt, ...) EINFO_PRINTF (1, 2);
int eend (int retval, const char *fmt, ...) EINFO_PRINTF (2, 3);
int ewend (int retval, const char *fmt, ...) EINFO_PRINTF (2, 3);
void ebracket (int col, einfo_color_t color, const char *msg);
void eindent (void);
void eoutdent (void);
int veinfon (const char *fmt, ...) EINFO_PRINTF (1, 2);
int vewarnn (const char *fmt, ...) EINFO_PRINTF (1, 2);
int vebeginn (const char *fmt, ...) EINFO_PRINTF (1, 2);
int veendn (int retval, const char *fmt, ...) EINFO_PRINTF (2, 3);
int vewendn (int retval, const char *fmt, ...) EINFO_PRINTF (2, 3);
int veinfo (const char *fmt, ...) EINFO_PRINTF (1, 2);
int vewarn (const char *fmt, ...) EINFO_PRINTF (1, 2);
int vebegin (const char *fmt, ...) EINFO_PRINTF (1, 2);
int veend (int retval, const char *fmt, ...) EINFO_PRINTF (2, 3);
int vewend (int retval, const char *fmt, ...) EINFO_PRINTF (2, 3);
void veindent (void);
void veoutdent (void);
/* If RC_EBUFFER is set, then we buffer all the above commands.
As such, we need to flush the buffer when done. */
void eflush(void);
#endif

247
src/env-update.c Normal file
View File

@@ -0,0 +1,247 @@
/*
env-update
Create /etc/profile.env (sh), /etc/csh.env from /etc/env.d
Run ldconfig as required
Copyright 2007 Gentoo Foundation
Released under the GPLv2
*/
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "einfo.h"
#include "rc.h"
#include "rc-misc.h"
#include "strlist.h"
#define ENVDIR "/etc/env.d"
#define PROFILE_ENV "/etc/profile.env"
#define CSH_ENV "/etc/csh.env"
#define LDSOCONF "/etc/ld.so.conf"
#define NOTICE "# THIS FILE IS AUTOMATICALLY GENERATED BY env-update.\n" \
"# DO NOT EDIT THIS FILE. CHANGES TO STARTUP PROFILES\n" \
"# GO INTO %s NOT %s\n\n"
#define LDNOTICE "# ld.so.conf autogenerated by env-update; make all\n" \
"# changes to contents of /etc/env.d directory\n"
static const char *specials[] =
{
"ADA_INCLUDE_PATH",
"ADA_OBJECTS_PATH",
"CLASSPATH",
"INFOPATH",
"KDEDIRS",
"LDPATH",
"MANPATH",
"PATH",
"PKG_CONFIG_PATH",
"PRELINK_PATH",
"PRELINK_PATH_MASK",
"PYTHONPATH",
"ROOTPATH",
NULL
};
static const char *special_spaces[] =
{
"CONFIG_PROTECT",
"CONFIG_PROTECT_MASK",
NULL,
};
static char *applet = NULL;
int main (int argc, char **argv)
{
char **files = rc_ls_dir (NULL, ENVDIR, 0);
char *file;
char **envs = NULL;
char *env;
int i = 0;
FILE *fp;
bool ld = true;
char *ldent;
char **ldents = NULL;
int nents = 0;
applet = argv[0];
if (! files)
eerrorx ("%s: no files in " ENVDIR " to process", applet);
STRLIST_FOREACH (files, file, i)
{
char *path = rc_strcatpaths (ENVDIR, file, NULL);
char **entries = NULL;
char *entry;
int j;
if (! rc_is_dir (path))
entries = rc_get_config (NULL, path);
free (path);
STRLIST_FOREACH (entries, entry, j)
{
char *tmpent = rc_xstrdup (entry);
char *value = tmpent;
char *var = strsep (&value, "=");
int k;
bool isspecial = false;
bool isspecial_spaced = false;
bool replaced = false;
for (k = 0; special_spaces[k]; k++)
if (strcmp (special_spaces[k], var) == 0)
{
isspecial = true;
isspecial_spaced = true;
break;
}
if (! isspecial)
{
for (k = 0; specials[k]; k++)
if (strcmp (specials[k], var) == 0)
{
isspecial = true;
break;
}
}
/* Skip blank vars */
if (isspecial &&
(! value || strlen (value)) == 0)
{
free (tmpent);
continue;
}
STRLIST_FOREACH (envs, env, k)
{
char *tmpenv = rc_xstrdup (env);
char *tmpvalue = tmpenv;
char *tmpentry = strsep (&tmpvalue, "=");
if (strcmp (tmpentry, var) == 0)
{
if (isspecial)
{
envs[k - 1] = rc_xrealloc (envs[k - 1],
strlen (envs[k - 1]) +
strlen (entry) + 1);
sprintf (envs[k - 1] + strlen (envs[k - 1]),
"%s%s", isspecial_spaced ? " " : ":", value);
}
else
{
free (envs[k - 1]);
envs[k - 1] = strdup (entry);
}
replaced = true;
}
free (tmpenv);
if (replaced)
break;
}
if (! replaced)
envs = rc_strlist_addsort (envs, entry);
free (tmpent);
}
}
if ((fp = fopen (PROFILE_ENV, "w")) == NULL)
eerrorx ("%s: fopen `%s': %s", applet, PROFILE_ENV, strerror (errno));
fprintf (fp, NOTICE, "/etc/profile", PROFILE_ENV);
STRLIST_FOREACH (envs, env, i)
{
char *tmpent = rc_xstrdup (env);
char *value = tmpent;
char *var = strsep (&value, "=");
if (strcmp (var, "LDPATH") != 0)
fprintf (fp, "export %s='%s'\n", var, value);
free (tmpent);
}
fclose (fp);
if ((fp = fopen (CSH_ENV, "w")) == NULL)
eerrorx ("%s: fopen `%s': %s", applet, PROFILE_ENV, strerror (errno));
fprintf (fp, NOTICE, "/etc/csh.cshrc", PROFILE_ENV);
STRLIST_FOREACH (envs, env, i)
{
char *tmpent = rc_xstrdup (env);
char *value = tmpent;
char *var = strsep (&value, "=");
if (strcmp (var, "LDPATH") != 0)
fprintf (fp, "setenv %s '%s'\n", var, value);
free (tmpent);
}
fclose (fp);
ldent = rc_get_config_entry (envs, "LDPATH");
if (! ldent ||
(argc > 1 && argv[1] && strcmp (argv[1], "--no-ldconfig") == 0))
{
free (envs);
return (EXIT_SUCCESS);
}
while ((file = strsep (&ldent, ":")))
{
if (strlen (file) == 0)
continue;
ldents = rc_strlist_add (ldents, file);
nents++;
}
/* Update ld.so.conf only if different */
if (rc_exists (LDSOCONF))
{
char **lines = rc_get_list (NULL, LDSOCONF);
char *line;
ld = false;
STRLIST_FOREACH (lines, line, i)
if (i > nents || strcmp (line, ldents[i - 1]) != 0)
{
ld = true;
break;
}
if (i - 1 != nents)
ld = true;
}
if (ld)
{
int retval = 0;
if ((fp = fopen (LDSOCONF, "w")) == NULL)
eerrorx ("%s: fopen `%s': %s", applet, LDSOCONF, strerror (errno));
fprintf (fp, LDNOTICE);
STRLIST_FOREACH (ldents, ldent, i)
fprintf (fp, "%s\n", ldent);
fclose (fp);
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
ebegin ("Regenerating /var/run/ld-elf.so.hints");
retval = system ("/sbin/ldconfig -elf -i '" LDSOCONF "'");
#else
ebegin ("Regenerating /etc/ld.so.cache");
retval = system ("/sbin/ldconfig");
#endif
eend (retval, NULL);
}
return(EXIT_SUCCESS);
}

48
src/env_whitelist Normal file
View File

@@ -0,0 +1,48 @@
# System environment whitelist for rc-system
# See /etc/conf.d/env_whitelist for details.
#
# Internal variables needed for operation of rc-system
# NB: Do not modify below this line if you do not know what you are doing!!
#
# Hotplug
IN_HOTPLUG
# RC network script support
IN_BACKGROUND
RC_INTERFACE_KEEP_CONFIG
# Default shell stuff
PATH
SHELL
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
INIT_HALT
INIT_VERSION
RUNLEVEL
PREVLEVEL
CONSOLE
# Allow this through too so we can prefer stuff in /lib when shutting down
# or going to single mode.
LD_LIBRARY_PATH

146
src/fstabinfo.c Normal file
View File

@@ -0,0 +1,146 @@
/*
fstabinfo.c
Gets information about /etc/fstab.
Copyright 2007 Gentoo Foundation
*/
#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Yay for linux and it's non liking of POSIX functions.
Okay, we could use getfsent but the man page says use getmntent instead
AND we don't have getfsent on uclibc or dietlibc for some odd reason. */
#ifdef __linux__
#define HAVE_GETMNTENT
#include <mntent.h>
#define GET_ENT getmntent (fp)
#define GET_ENT_FILE(_name) getmntfile (fp, _name)
#define END_ENT endmntent (fp)
#define ENT_DEVICE(_ent) ent->mnt_fsname
#define ENT_FILE(_ent) ent->mnt_dir
#define ENT_TYPE(_ent) ent->mnt_type
#define ENT_OPTS(_ent) ent->mnt_opts
#define ENT_PASS(_ent) ent->mnt_passno
#else
#define HAVE_GETFSENT
#include <fstab.h>
#define GET_ENT getfsent ()
#define GET_ENT_FILE(_name) getfsfile (_name)
#define END_ENT endfsent ()
#define ENT_DEVICE(_ent) ent->fs_spec
#define ENT_TYPE(_ent) ent->fs_vfstype
#define ENT_FILE(_ent) ent->fs_file
#define ENT_OPTS(_ent) ent->fs_mntops
#define ENT_PASS(_ent) ent->fs_passno
#endif
#include "einfo.h"
#ifdef HAVE_GETMNTENT
static struct mntent *getmntfile (FILE *fp, const char *file)
{
struct mntent *ent;
while ((ent = getmntent (fp)))
if (strcmp (file, ent->mnt_dir) == 0)
return (ent);
return (NULL);
}
#endif
int main (int argc, char **argv)
{
int i;
#ifdef HAVE_GETMNTENT
FILE *fp;
struct mntent *ent;
#else
struct fstab *ent;
#endif
int result = EXIT_FAILURE;
char *p;
char *token;
int n = 0;
for (i = 1; i < argc; i++)
{
#ifdef HAVE_GETMNTENT
fp = setmntent ("/etc/fstab", "r");
#endif
if (strcmp (argv[i], "--fstype") == 0 && i + 1 < argc)
{
i++;
p = argv[i];
while ((token = strsep (&p, ",")))
while ((ent = GET_ENT))
if (strcmp (token, ENT_TYPE (ent)) == 0)
printf ("%s\n", ENT_FILE (ent));
result = EXIT_SUCCESS;
}
if (strcmp (argv[i], "--mount-cmd") == 0 && i + 1 < argc)
{
i++;
if ((ent = GET_ENT_FILE (argv[i])) == NULL)
continue;
printf ("-o %s -t %s %s %s\n", ENT_OPTS (ent), ENT_TYPE (ent),
ENT_DEVICE (ent), ENT_FILE (ent));
result = EXIT_SUCCESS;
}
if (strcmp (argv[i], "--opts") == 0 && i + 1 < argc)
{
i++;
if ((ent = GET_ENT_FILE (argv[i])) == NULL)
continue;
printf ("%s\n", ENT_OPTS (ent));
result = EXIT_SUCCESS;
}
if (strcmp (argv[i], "--passno") == 0 && i + 1 < argc)
{
i++;
switch (argv[i][0])
{
case '=':
case '<':
case '>':
if (sscanf (argv[i] + 1, "%d", &n) != 1)
eerrorx ("%s: invalid passno %s", argv[0], argv[i] + 1);
while ((ent = GET_ENT))
{
if (((argv[i][0] == '=' && n == ENT_PASS (ent)) ||
(argv[i][0] == '<' && n > ENT_PASS (ent)) ||
(argv[i][0] == '>' && n < ENT_PASS (ent))) &&
strcmp (ENT_FILE (ent), "none") != 0)
printf ("%s\n", ENT_FILE (ent));
}
default:
if ((ent = GET_ENT_FILE (argv[i])) == NULL)
continue;
printf ("%d\n", ENT_PASS (ent));
result = EXIT_SUCCESS;
}
}
END_ENT;
if (result != EXIT_SUCCESS)
{
eerror ("%s: unknown option `%s'", basename (argv[0]), argv[i]);
break;
}
}
exit (result);
}

877
src/libeinfo.c Normal file
View File

@@ -0,0 +1,877 @@
/*
einfo.c
Gentoo informational functions
Copyright 2007 Gentoo Foundation
*/
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include "einfo.h"
#include "rc.h"
#include "rc-misc.h"
/* Incase we cannot work out how many columns from ioctl, supply a default */
#define DEFAULT_COLS 80
#define OK "ok"
#define NOT_OK "!!"
#define CHECK_VERBOSE if (! is_env ("RC_VERBOSE", "yes")) return 0
/* Number of spaces for an indent */
#define INDENT_WIDTH 2
/* How wide can the indent go? */
#define INDENT_MAX 40
#define EBUFFER_LOCK RC_SVCDIR "ebuffer/.lock"
/* A cheat sheet of colour capable terminals
This is taken from DIR_COLORS from GNU coreutils
We embed it here as we shouldn't depend on coreutils */
static const char *colour_terms[] =
{
"Eterm",
"ansi",
"color-xterm",
"con132x25",
"con132x30",
"con132x43",
"con132x60",
"con80x25",
"con80x28",
"con80x30",
"con80x43",
"con80x50",
"con80x60",
"cons25",
"console",
"cygwin",
"dtterm",
"gnome",
"konsole",
"kterm",
"linux",
"linux-c",
"mach-color",
"mlterm",
"putty",
"rxvt",
"rxvt-cygwin",
"rxvt-cygwin-native",
"rxvt-unicode",
"screen",
"screen-bce",
"screen-w",
"screen.linux",
"vt100",
"xterm",
"xterm-256color",
"xterm-color",
"xterm-debian",
NULL
};
static bool is_env (const char *var, const char *val)
{
char *v;
if (! var)
return (false);
v = getenv (var);
if (! v)
return (val == NULL ? true : false);
return (strcasecmp (v, val) == 0 ? true : false);
}
bool colour_terminal (void)
{
static int in_colour = -1;
int i = 0;
char *term;
if (in_colour == 0)
return (false);
if (in_colour == 1)
return (true);
term = getenv ("TERM");
/* If $TERM isn't set then the chances are we're in single user mode */
if (! term)
return (true);
while (colour_terms[i])
{
if (strcmp (colour_terms[i], term) == 0)
{
in_colour = 1;
return (true);
}
i++;
}
in_colour = 0;
return (false);
}
static int get_term_columns (void)
{
#ifdef TIOCGSIZE /* BSD */
struct ttysize ts;
if (ioctl(0, TIOCGSIZE, &ts) == 0)
return (ts.ts_cols);
#elif TIOCGWINSZ /* Linux */
struct winsize ws;
if (ioctl(0, TIOCGWINSZ, &ws) == 0)
return (ws.ws_col);
#endif
return (DEFAULT_COLS);
}
static int ebuffer (const char *cmd, int retval, const char *fmt, va_list ap)
{
char *file = getenv ("RC_EBUFFER");
FILE *fp;
char buffer[RC_LINEBUFFER];
int l = 1;
if (! file || ! cmd || strlen (cmd) < 4)
return (0);
if (! (fp = fopen (file, "a")))
{
fprintf (stderr, "fopen `%s': %s\n", file, strerror (errno));
return (0);
}
fprintf (fp, "%s %d ", cmd, retval);
if (fmt)
{
l = vsnprintf (buffer, sizeof (buffer), fmt, ap);
fprintf (fp, "%d %s\n", l, buffer);
}
else
fprintf (fp, "0\n");
fclose (fp);
return (l);
}
typedef struct func
{
const char *name;
int (*efunc) (const char *fmt, ...);
int (*eefunc) (int retval, const char *fmt, ...);
void (*eind) (void);
} func_t;
static const func_t funcmap[] = {
{ "einfon", &einfon, NULL, NULL },
{ "ewarnn", &ewarnn, NULL, NULL},
{ "eerrorn", &eerrorn, NULL, NULL},
{ "einfo", &einfo, NULL, NULL },
{ "ewarn", &ewarn, NULL, NULL },
{ "eerror", &eerror, NULL, NULL },
{ "ebegin", &ebegin, NULL, NULL },
{ "eend", NULL, &eend, NULL },
{ "ewend", NULL, &ewend, NULL },
{ "eindent", NULL, NULL, &eindent },
{ "eoutdent", NULL, NULL, &eoutdent },
{ "veinfon", &veinfon, NULL, NULL },
{ "vewarnn", &vewarnn, NULL, NULL },
{ "veinfo", &veinfo, NULL, NULL },
{ "vewarn", &vewarn, NULL, NULL },
{ "vebegin", &vebegin, NULL, NULL },
{ "veend", NULL, &veend, NULL },
{ "vewend", NULL, &vewend, NULL },
{ "veindent" ,NULL, NULL, &veindent },
{ "veoutdent", NULL, NULL, &veoutdent },
{ NULL, NULL, NULL, NULL },
};
void eflush (void)
{
FILE *fp;
char *file = getenv ("RC_EBUFFER");
char buffer[RC_LINEBUFFER];
char *cmd;
int retval = 0;
int length = 0;
char *token;
char *p;
struct stat buf;
pid_t pid;
char newfile[PATH_MAX];
int i = 1;
if (! file|| (stat (file, &buf) != 0))
{
errno = 0;
return;
}
/* Find a unique name for our file */
while (true)
{
snprintf (newfile, sizeof (newfile), "%s.%d", file, i);
if (stat (newfile, &buf) != 0)
{
if (rename (file, newfile))
fprintf (stderr, "rename `%s' `%s': %s\n", file, newfile,
strerror (errno));
break;
}
i++;
}
/* We fork a child process here so we don't hold anything up */
if ((pid = fork ()) == -1)
{
fprintf (stderr, "fork: %s", strerror (errno));
return;
}
if (pid != 0)
return;
/* Spin until we can lock the ebuffer */
while (true)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 20000;
select (0, NULL, NULL, NULL, &tv);
errno = 0;
if (link (newfile, EBUFFER_LOCK) == 0)
break;
if (errno != EEXIST)
fprintf (stderr, "link `%s' `%s': %s\n", newfile, EBUFFER_LOCK,
strerror (errno));
}
if (! (fp = fopen (newfile, "r")))
{
fprintf (stderr, "fopen `%s': %s\n", newfile, strerror (errno));
return;
}
unsetenv ("RC_EBUFFER");
memset (buffer, 0, RC_LINEBUFFER);
while (fgets (buffer, RC_LINEBUFFER, fp))
{
i = strlen (buffer) - 1;
if (i < 1)
continue;
if (buffer[i] == '\n')
buffer[i] = 0;
p = buffer;
cmd = strsep (&p, " ");
token = strsep (&p, " ");
if (sscanf (token, "%d", &retval) != 1)
{
fprintf (stderr, "eflush `%s': not a number", token);
continue;
}
token = strsep (&p, " ");
if (sscanf (token, "%d", &length) != 1)
{
fprintf (stderr, "eflush `%s': not a number", token);
continue;
}
i = 0;
while (funcmap[i].name)
{
if (strcmp (funcmap[i].name, cmd) == 0)
{
if (funcmap[i].efunc)
{
if (p)
funcmap[i].efunc ("%s", p);
else
funcmap[i].efunc (NULL, NULL);
}
else if (funcmap[i].eefunc)
{
if (p)
funcmap[i].eefunc (retval, "%s", p);
else
funcmap[i].eefunc (retval, NULL, NULL);
}
else if (funcmap[i].eind)
funcmap[i].eind ();
else
fprintf (stderr, "eflush `%s': no function defined\n", cmd);
break;
}
i++;
}
if (! funcmap[i].name)
fprintf (stderr, "eflush `%s': invalid function\n", cmd);
}
fclose (fp);
if (unlink (EBUFFER_LOCK))
fprintf (stderr, "unlink `%s': %s", EBUFFER_LOCK, strerror (errno));
if (unlink (newfile))
fprintf (stderr, "unlink `%s': %s", newfile, strerror (errno));
_exit (EXIT_SUCCESS);
}
#define EBUFFER(_cmd, _retval, _fmt, _ap) \
{ \
int _i = ebuffer (_cmd, _retval, _fmt, _ap); \
if (_i) \
return (_i); \
}
static void elog (int level, const char *fmt, va_list ap)
{
char *e = getenv ("RC_ELOG");
if (e)
{
closelog ();
openlog (e, LOG_PID, LOG_DAEMON);
vsyslog (level, fmt, ap);
}
}
static int _eindent (FILE *stream)
{
char *env = getenv ("RC_EINDENT");
int amount = 0;
char indent[INDENT_MAX];
if (env)
{
errno = 0;
amount = strtol (env, NULL, 0);
if (errno != 0 || amount < 0)
amount = 0;
else if (amount > INDENT_MAX)
amount = INDENT_MAX;
if (amount > 0)
memset (indent, ' ', amount);
}
/* Terminate it */
memset (indent + amount, 0, 1);
return (fprintf (stream, "%s", indent));
}
#define VEINFON(_file, _colour) \
if (colour_terminal ()) \
fprintf (_file, " " _colour "*" EINFO_NORMAL " "); \
else \
fprintf (_file, " * "); \
retval += _eindent (_file); \
retval += vfprintf (_file, fmt, ap) + 3; \
if (colour_terminal ()) \
fprintf (_file, "\033[K");
static int _veinfon (const char *fmt, va_list ap)
{
int retval = 0;
VEINFON (stdout, EINFO_GOOD);
return (retval);
}
static int _vewarnn (const char *fmt, va_list ap)
{
int retval = 0;
VEINFON (stdout, EINFO_WARN);
return (retval);
}
static int _veerrorn (const char *fmt, va_list ap)
{
int retval = 0;
VEINFON (stderr, EINFO_BAD);
return (retval);
}
int einfon (const char *fmt, ...)
{
int retval;
va_list ap;
if (! fmt || is_env ("RC_QUIET", "yes"))
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("einfon", 0, fmt, ap)))
retval = _veinfon (fmt, ap);
va_end (ap);
return (retval);
}
int ewarnn (const char *fmt, ...)
{
int retval;
va_list ap;
if (! fmt || is_env ("RC_QUIET", "yes"))
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("ewarnn", 0, fmt, ap)))
retval = _vewarnn (fmt, ap);
va_end (ap);
return (retval);
}
int eerrorn (const char *fmt, ...)
{
int retval;
va_list ap;
va_start (ap, fmt);
if (! (retval = ebuffer ("eerrorn", 0, fmt, ap)))
retval = _veerrorn (fmt, ap);
va_end (ap);
return (retval);
}
int einfo (const char *fmt, ...)
{
int retval;
va_list ap;
if (! fmt || is_env ("RC_QUIET", "yes"))
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("einfo", 0, fmt, ap)))
{
retval = _veinfon (fmt, ap);
retval += printf ("\n");
}
va_end (ap);
return (retval);
}
int ewarn (const char *fmt, ...)
{
int retval;
va_list ap;
if (! fmt || is_env ("RC_QUIET", "yes"))
return (0);
va_start (ap, fmt);
elog (LOG_WARNING, fmt, ap);
if (! (retval = ebuffer ("ewarn", 0, fmt, ap)))
{
retval = _vewarnn (fmt, ap);
retval += printf ("\n");
}
va_end (ap);
return (retval);
}
void ewarnx (const char *fmt, ...)
{
int retval;
va_list ap;
if (fmt && ! is_env ("RC_QUIET", "yes"))
{
va_start (ap, fmt);
elog (LOG_WARNING, fmt, ap);
retval = _vewarnn (fmt, ap);
va_end (ap);
retval += printf ("\n");
}
exit (EXIT_FAILURE);
}
int eerror (const char *fmt, ...)
{
int retval;
va_list ap;
if (! fmt)
return (0);
va_start (ap, fmt);
elog (LOG_ERR, fmt, ap);
retval = _veerrorn (fmt, ap);
va_end (ap);
retval += fprintf (stderr, "\n");
return (retval);
}
void eerrorx (const char *fmt, ...)
{
va_list ap;
if (fmt)
{
va_start (ap, fmt);
elog (LOG_ERR, fmt, ap);
_veerrorn (fmt, ap);
va_end (ap);
printf ("\n");
}
exit (EXIT_FAILURE);
}
int ebegin (const char *fmt, ...)
{
int retval;
va_list ap;
if (! fmt || is_env ("RC_QUIET", "yes"))
return (0);
va_start (ap, fmt);
if ((retval = ebuffer ("ebegin", 0, fmt, ap)))
{
va_end (ap);
return (retval);
}
retval = _veinfon (fmt, ap);
va_end (ap);
retval += printf (" ...");
if (colour_terminal ())
retval += printf ("\n");
return (retval);
}
static void _eend (int col, einfo_color_t color, const char *msg)
{
FILE *fp = stdout;
int i;
int cols;
if (! msg)
return;
if (color == einfo_bad)
fp = stderr;
cols = get_term_columns () - (strlen (msg) + 6);
if (cols > 0 && colour_terminal ())
{
fprintf (fp, "\033[A\033[%dC %s[ ", cols, EINFO_BRACKET);
switch (color)
{
case einfo_good:
fprintf (fp, EINFO_GOOD);
break;
case einfo_warn:
fprintf (fp, EINFO_WARN);
break;
case einfo_bad:
fprintf (fp, EINFO_BAD);
break;
case einfo_hilite:
fprintf (fp, EINFO_HILITE);
break;
case einfo_bracket:
fprintf (fp, EINFO_BRACKET);
break;
case einfo_normal:
fprintf (fp, EINFO_NORMAL);
break;
}
fprintf (fp, "%s%s ]%s\n", msg, EINFO_BRACKET, EINFO_NORMAL);
}
else
{
for (i = -1; i < cols - col; i++)
fprintf (fp, " ");
fprintf (fp, "[ %s ]\n", msg);
}
}
static int _do_eend (const char *cmd, int retval, const char *fmt, va_list ap)
{
int col = 0;
FILE *fp;
if (ebuffer (cmd, retval, fmt, ap))
return (retval);
if (fmt && retval != 0)
{
if (strcmp (cmd, "ewend") == 0)
{
col = _vewarnn (fmt, ap);
fp = stdout;
}
else
{
col = _veerrorn (fmt, ap);
fp = stderr;
}
if (colour_terminal ())
fprintf (fp, "\n");
}
_eend (col, retval == 0 ? einfo_good : einfo_bad, retval == 0 ? OK : NOT_OK);
return (retval);
}
int eend (int retval, const char *fmt, ...)
{
va_list ap;
if (is_env ("RC_QUIET", "yes"))
return (retval);
va_start (ap, fmt);
_do_eend ("eend", retval, fmt, ap);
va_end (ap);
return (retval);
}
int ewend (int retval, const char *fmt, ...)
{
va_list ap;
if (is_env ("RC_QUIET", "yes"))
return (retval);
va_start (ap, fmt);
_do_eend ("ewend", retval, fmt, ap);
va_end (ap);
return (retval);
}
void ebracket (int col, einfo_color_t color, const char *msg)
{
_eend (col, color, msg);
}
void eindent (void)
{
char *env = getenv ("RC_EINDENT");
int amount = 0;
char num[10];
if (ebuffer ("eindent", 0, NULL, NULL))
return;
if (env)
{
errno = 0;
amount = strtol (env, NULL, 0);
if (errno != 0)
amount = 0;
}
amount += INDENT_WIDTH;
if (amount > INDENT_MAX)
amount = INDENT_MAX;
snprintf (num, 10, "%08d", amount);
setenv ("RC_EINDENT", num, 1);
}
void eoutdent (void)
{
char *env = getenv ("RC_EINDENT");
int amount = 0;
char num[10];
if (ebuffer ("eoutdent", 0, NULL, NULL))
return;
if (! env)
return;
errno = 0;
amount = strtol (env, NULL, 0);
if (errno != 0)
amount = 0;
else
amount -= INDENT_WIDTH;
if (amount <= 0)
unsetenv ("RC_EINDENT");
else
{
snprintf (num, 10, "%08d", amount);
setenv ("RC_EINDENT", num, 1);
}
}
int veinfon (const char *fmt, ...)
{
int retval;
va_list ap;
CHECK_VERBOSE;
if (! fmt)
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("veinfon", 0, fmt, ap)))
retval = _veinfon (fmt, ap);
va_end (ap);
return (retval);
}
int vewarnn (const char *fmt, ...)
{
int retval;
va_list ap;
CHECK_VERBOSE;
if (! fmt)
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("vewarnn", 0, fmt, ap)))
retval = _vewarnn (fmt, ap);
va_end (ap);
return (retval);
}
int veinfo (const char *fmt, ...)
{
int retval;
va_list ap;
CHECK_VERBOSE;
if (! fmt)
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("veinfo", 0, fmt, ap)))
{
retval = _veinfon (fmt, ap);
retval += printf ("\n");
}
va_end (ap);
return (retval);
}
int vewarn (const char *fmt, ...)
{
int retval;
va_list ap;
CHECK_VERBOSE;
if (! fmt)
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("vewarn", 0, fmt, ap)))
{
retval = _vewarnn (fmt, ap);
retval += printf ("\n");
}
va_end (ap);
retval += printf ("\n");
return (retval);
}
int vebegin (const char *fmt, ...)
{
int retval;
va_list ap;
CHECK_VERBOSE;
if (! fmt)
return (0);
va_start (ap, fmt);
if (! (retval = ebuffer ("vewarn", 0, fmt, ap)))
{
retval = _veinfon (fmt, ap);
retval += printf (" ...");
if (colour_terminal ())
retval += printf ("\n");
}
va_end (ap);
return (retval);
}
int veend (int retval, const char *fmt, ...)
{
va_list ap;
CHECK_VERBOSE;
va_start (ap, fmt);
_do_eend ("veend", retval, fmt, ap);
va_end (ap);
return (retval);
}
int vewend (int retval, const char *fmt, ...)
{
va_list ap;
CHECK_VERBOSE;
va_start (ap, fmt);
_do_eend ("vewend", retval, fmt, ap);
va_end (ap);
return (retval);
}
void veindent (void)
{
if (is_env ("RC_VERBOSE", "yes"))
eindent ();
}
void veoutdent (void)
{
if (is_env ("RC_VERBOSE", "yes"))
eoutdent ();
}

600
src/librc-daemon.c Normal file
View File

@@ -0,0 +1,600 @@
/*
librc-daemon
Finds PID for given daemon criteria
Copyright 2007 Gentoo Foundation
Released under the GPLv2
*/
#include <sys/types.h>
#include <sys/stat.h>
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__)
#include <sys/param.h>
#include <sys/user.h>
#include <sys/sysctl.h>
#include <kvm.h>
#include <limits.h>
#endif
#ifndef __linux__
#include <libgen.h>
#endif
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "einfo.h"
#include "rc.h"
#include "rc-misc.h"
#include "strlist.h"
#if defined(__linux__)
static bool pid_is_cmd (pid_t pid, const char *cmd)
{
char buffer[32];
FILE *fp;
int c;
snprintf(buffer, sizeof (buffer), "/proc/%d/stat", pid);
if ((fp = fopen (buffer, "r")) == NULL)
return (false);
while ((c = getc (fp)) != EOF && c != '(')
;
if (c != '(')
{
fclose(fp);
return (false);
}
while ((c = getc (fp)) != EOF && c == *cmd)
cmd++;
fclose (fp);
return ((c == ')' && *cmd == '\0') ? true : false);
}
static bool pid_is_exec (pid_t pid, const char *exec)
{
char cmdline[32];
char buffer[PATH_MAX];
char *p;
int fd = -1;
int r;
snprintf (cmdline, sizeof (cmdline), "/proc/%u/exe", pid);
memset (buffer, 0, sizeof (buffer));
if (readlink (cmdline, buffer, sizeof (buffer)) != -1)
{
if (strcmp (exec, buffer) == 0)
return (true);
/* We should cater for deleted binaries too */
if (strlen (buffer) > 10)
{
p = buffer + (strlen (buffer) - 10);
if (strcmp (p, " (deleted)") == 0)
{
*p = 0;
if (strcmp (buffer, exec) == 0)
return (true);
}
}
}
snprintf (cmdline, sizeof (cmdline), "/proc/%u/cmdline", pid);
if ((fd = open (cmdline, O_RDONLY)) < 0)
return (false);
r = read(fd, buffer, sizeof (buffer));
close (fd);
if (r == -1)
return 0;
buffer[r] = 0;
return (strcmp (exec, buffer) == 0 ? true : false);
}
pid_t *rc_find_pids (const char *exec, const char *cmd,
uid_t uid, pid_t pid)
{
DIR *procdir;
struct dirent *entry;
int npids = 0;
int foundany = false;
pid_t p;
pid_t *pids = NULL;
char buffer[PATH_MAX];
struct stat sb;
pid_t runscript_pid = 0;
char *pp;
if ((procdir = opendir ("/proc")) == NULL)
eerrorx ("opendir `/proc': %s", strerror (errno));
/*
We never match RC_RUNSCRIPT_PID if present so we avoid the below
scenario
/etc/init.d/ntpd stop does
start-stop-daemon --stop --name ntpd
catching /etc/init.d/ntpd stop
nasty
*/
if ((pp = getenv ("RC_RUNSCRIPT_PID")))
{
if (sscanf (pp, "%d", &runscript_pid) != 1)
runscript_pid = 0;
}
while ((entry = readdir (procdir)) != NULL)
{
if (sscanf (entry->d_name, "%d", &p) != 1)
continue;
foundany = true;
if (runscript_pid != 0 && runscript_pid == p)
continue;
if (pid != 0 && pid != p)
continue;
if (uid)
{
snprintf (buffer, sizeof (buffer), "/proc/%d", pid);
if (stat (buffer, &sb) != 0 || sb.st_uid != uid)
continue;
}
if (cmd && ! pid_is_cmd (p, cmd))
continue;
if (exec && ! cmd && ! pid_is_exec (p, exec))
continue;
pids = realloc (pids, sizeof (pid_t) * (npids + 2));
if (! pids)
eerrorx ("memory exhausted");
pids[npids] = p;
pids[npids + 1] = 0;
npids++;
}
closedir (procdir);
if (! foundany)
eerrorx ("nothing in /proc");
return (pids);
}
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
# if defined(__FreeBSD__)
# define _KINFO_PROC kinfo_proc
# define _KVM_GETPROCS kvm_getprocs
# define _KVM_GETARGV kvm_getargv
# define _GET_KINFO_UID(kp) (kp.ki_ruid)
# define _GET_KINFO_COMM(kp) (kp.ki_comm)
# define _GET_KINFO_PID(kp) (kp.ki_pid)
# else
# define _KINFO_PROC kinfo_proc2
# define _KVM_GETPROCS kvm_getprocs2
# define _KVM_GETARGV kvm_getargv2
# define _GET_KINFO_UID(kp) (kp.p_ruid)
# define _GET_KINFO_COMM(kp) (kp.p_comm)
# define _GET_KINFO_PID(kp) (kp.p_pid)
# endif
pid_t *rc_find_pids (const char *exec, const char *cmd,
uid_t uid, pid_t pid)
{
static kvm_t *kd = NULL;
char errbuf[_POSIX2_LINE_MAX];
struct _KINFO_PROC *kp;
int i;
int processes = 0;
int argc = 0;
char **argv;
pid_t *pids = NULL;
int npids = 0;
if ((kd = kvm_openfiles (NULL, NULL, NULL, O_RDONLY, errbuf)) == NULL)
eerrorx ("kvm_open: %s", errbuf);
kp = _KVM_GETPROCS (kd, KERN_PROC_PROC, 0, &processes);
for (i = 0; i < processes; i++)
{
pid_t p = _GET_KINFO_PID (kp[i]);
if (pid != 0 && pid != p)
continue;
if (uid != 0 && uid != _GET_KINFO_UID (kp[i]))
continue;
if (cmd)
{
if (! _GET_KINFO_COMM (kp[i]) ||
strcmp (cmd, _GET_KINFO_COMM (kp[i])) != 0)
continue;
}
if (exec && ! cmd)
{
if ((argv = _KVM_GETARGV (kd, &kp[i], argc)) == NULL || ! *argv)
continue;
if (strcmp (*argv, exec) != 0)
continue;
}
pids = realloc (pids, sizeof (pid_t) * (npids + 2));
if (! pids)
eerrorx ("memory exhausted");
pids[npids] = p;
pids[npids + 1] = 0;
npids++;
}
kvm_close(kd);
return (pids);
}
#else
# error "Platform not supported!"
#endif
static bool _match_daemon (const char *path, const char *file,
const char *mexec, const char *mname,
const char *mpidfile)
{
char buffer[RC_LINEBUFFER];
char *ffile = rc_strcatpaths (path, file, NULL);
FILE *fp;
int lc = 0;
int m = 0;
if (! rc_exists (ffile))
{
free (ffile);
return (false);
}
if ((fp = fopen (ffile, "r")) == NULL)
{
eerror ("fopen `%s': %s", ffile, strerror (errno));
free (ffile);
return (false);
}
if (! mname)
m += 10;
if (! mpidfile)
m += 100;
memset (buffer, 0, sizeof (buffer));
while ((fgets (buffer, RC_LINEBUFFER, fp)))
{
int lb = strlen (buffer) - 1;
if (buffer[lb] == '\n')
buffer[lb] = 0;
if (strcmp (buffer, mexec) == 0)
m += 1;
else if (mname && strcmp (buffer, mname) == 0)
m += 10;
else if (mpidfile && strcmp (buffer, mpidfile) == 0)
m += 100;
if (m == 111)
break;
lc++;
if (lc > 5)
break;
}
fclose (fp);
free (ffile);
return (m == 111 ? true : false);
}
void rc_set_service_daemon (const char *service, const char *exec,
const char *name, const char *pidfile,
bool started)
{
char *dirpath = rc_strcatpaths (RC_SVCDIR, "daemons", basename (service), NULL);
char **files = NULL;
char *file;
char *ffile = NULL;
int i;
char *mexec;
char *mname;
char *mpidfile;
int nfiles = 0;
if (! exec && ! name && ! pidfile)
return;
if (exec)
{
i = strlen (exec) + 6;
mexec = rc_xmalloc (sizeof (char *) * i);
snprintf (mexec, i, "exec=%s", exec);
}
else
mexec = strdup ("exec=");
if (name)
{
i = strlen (name) + 6;
mname = rc_xmalloc (sizeof (char *) * i);
snprintf (mname, i, "name=%s", name);
}
else
mname = strdup ("name=");
if (pidfile)
{
i = strlen (pidfile) + 9;
mpidfile = rc_xmalloc (sizeof (char *) * i);
snprintf (mpidfile, i, "pidfile=%s", pidfile);
}
else
mpidfile = strdup ("pidfile=");
/* Regardless, erase any existing daemon info */
if (rc_is_dir (dirpath))
{
char *oldfile = NULL;
files = rc_ls_dir (NULL, dirpath, 0);
STRLIST_FOREACH (files, file, i)
{
ffile = rc_strcatpaths (dirpath, file, NULL);
nfiles++;
if (! oldfile)
{
if (_match_daemon (dirpath, file, mexec, mname, mpidfile))
{
unlink (ffile);
oldfile = ffile;
nfiles--;
}
}
else
{
rename (ffile, oldfile);
free (oldfile);
oldfile = ffile;
}
}
if (ffile)
free (ffile);
free (files);
}
/* Now store our daemon info */
if (started)
{
char buffer[10];
FILE *fp;
if (! rc_is_dir (dirpath))
if (mkdir (dirpath, 0755) != 0)
eerror ("mkdir `%s': %s", dirpath, strerror (errno));
snprintf (buffer, sizeof (buffer), "%03d", nfiles + 1);
file = rc_strcatpaths (dirpath, buffer, NULL);
if ((fp = fopen (file, "w")) == NULL)
eerror ("fopen `%s': %s", file, strerror (errno));
else
{
fprintf (fp, "%s\n%s\n%s\n", mexec, mname, mpidfile);
fclose (fp);
}
free (file);
}
free (mexec);
free (mname);
free (mpidfile);
free (dirpath);
}
bool rc_service_started_daemon (const char *service, const char *exec,
int indx)
{
char *dirpath;
char *file;
int i;
char *mexec;
bool retval = false;
if (! service || ! exec)
return (false);
dirpath = rc_strcatpaths (RC_SVCDIR, "daemons", basename (service), NULL);
if (! rc_is_dir (dirpath))
{
free (dirpath);
return (false);
}
i = strlen (exec) + 6;
mexec = rc_xmalloc (sizeof (char *) * i);
snprintf (mexec, i, "exec=%s", exec);
if (indx > 0)
{
file = rc_xmalloc (sizeof (char *) * 10);
snprintf (file, sizeof (file), "%03d", indx);
retval = _match_daemon (dirpath, file, mexec, NULL, NULL);
free (file);
}
else
{
char **files = rc_ls_dir (NULL, dirpath, 0);
STRLIST_FOREACH (files, file, i)
{
retval = _match_daemon (dirpath, file, mexec, NULL, NULL);
if (retval)
break;
}
free (files);
}
free (mexec);
return (retval);
}
bool rc_service_daemons_crashed (const char *service)
{
char *dirpath;
char **files;
char *file;
char *path;
int i;
FILE *fp;
char buffer[RC_LINEBUFFER];
char *exec = NULL;
char *name = NULL;
char *pidfile = NULL;
pid_t pid = 0;
pid_t *pids = NULL;
char *p;
char *token;
bool retval = false;
if (! service)
return (false);
dirpath = rc_strcatpaths (RC_SVCDIR, "daemons", basename (service), NULL);
if (! rc_is_dir (dirpath))
{
free (dirpath);
return (false);
}
memset (buffer, 0, sizeof (buffer));
files = rc_ls_dir (NULL, dirpath, 0);
STRLIST_FOREACH (files, file, i)
{
path = rc_strcatpaths (dirpath, file, NULL);
fp = fopen (path, "r");
free (path);
if (! fp)
{
eerror ("fopen `%s': %s", file, strerror (errno));
continue;
}
while ((fgets (buffer, RC_LINEBUFFER, fp)))
{
int lb = strlen (buffer) - 1;
if (buffer[lb] == '\n')
buffer[lb] = 0;
p = buffer;
if ((token = strsep (&p, "=")) == NULL || ! p)
continue;
if (strlen (p) == 0)
continue;
if (strcmp (token, "exec") == 0)
{
if (exec)
free (exec);
exec = strdup (p);
}
else if (strcmp (token, "name") == 0)
{
if (name)
free (name);
name = strdup (p);
}
else if (strcmp (token, "pidfile") == 0)
{
if (pidfile)
free (pidfile);
pidfile = strdup (p);
}
}
fclose (fp);
pid = 0;
if (pidfile)
{
if (! rc_exists (pidfile))
{
retval = true;
break;
}
if ((fp = fopen (pidfile, "r")) == NULL)
{
eerror ("fopen `%s': %s", pidfile, strerror (errno));
retval = true;
break;
}
if (fscanf (fp, "%d", &pid) != 1)
{
eerror ("no pid found in `%s'", pidfile);
fclose (fp);
retval = true;
break;
}
fclose (fp);
free (pidfile);
pidfile = NULL;
}
if ((pids = rc_find_pids (exec, name, 0, pid)) == NULL)
{
retval = true;
break;
}
free (pids);
if (exec)
{
free (exec);
exec = NULL;
}
if (name)
{
free (name);
name = NULL;
}
}
if (exec)
{
free (exec);
exec = NULL;
}
if (name)
{
free (name);
name = NULL;
}
free (dirpath);
rc_strlist_free (files);
return (retval);
}

838
src/librc-depend.c Normal file
View File

@@ -0,0 +1,838 @@
/*
librc-depend
rc service dependency and ordering
Copyright 2006-2007 Gentoo Foundation
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "einfo.h"
#include "rc.h"
#include "rc-misc.h"
#include "strlist.h"
#define GENDEP RC_LIBDIR "/sh/gendepends.sh"
/* We use this so we can pass our char array through many functions */
struct lhead
{
char **list;
};
static char *get_shell_value (char *string)
{
char *p = string;
char *e;
if (! string)
return (NULL);
if (*p == '\'')
p++;
e = p + strlen (p) - 1;
if (*e == '\n')
*e-- = 0;
if (*e == '\'')
*e-- = 0;
if (*p != 0)
return p;
return (NULL);
}
void rc_free_deptree (rc_depinfo_t *deptree)
{
rc_depinfo_t *di = deptree;
while (di)
{
rc_depinfo_t *dip = di->next;
rc_deptype_t *dt = di->depends;
free (di->service);
while (dt)
{
rc_deptype_t *dtp = dt->next;
free (dt->type);
rc_strlist_free (dt->services);
free (dt);
dt = dtp;
}
free (di);
di = dip;
}
}
rc_depinfo_t *rc_load_deptree (void)
{
FILE *fp;
rc_depinfo_t *deptree = NULL;
rc_depinfo_t *depinfo = NULL;
rc_deptype_t *deptype = NULL;
char buffer [RC_LINEBUFFER];
char *type;
char *p;
char *e;
int i;
/* Update our deptree, but only if we need too */
rc_update_deptree (false);
if (! (fp = fopen (RC_DEPTREE, "r")))
return (NULL);
while (fgets (buffer, RC_LINEBUFFER, fp))
{
p = buffer;
e = strsep (&p, "_");
if (! e || strcmp (e, "depinfo") != 0)
continue;
e = strsep (&p, "_");
if (! e || sscanf (e, "%d", &i) != 1)
continue;
if (! (type = strsep (&p, "_=")))
continue;
if (strcmp (type, "service") == 0)
{
/* Sanity */
e = get_shell_value (p);
if (! e || strlen (e) == 0)
continue;
if (! deptree)
{
deptree = rc_xmalloc (sizeof (rc_depinfo_t));
depinfo = deptree;
}
else
{
depinfo->next = rc_xmalloc (sizeof (rc_depinfo_t));
depinfo = depinfo->next;
}
memset (depinfo, 0, sizeof (rc_depinfo_t));
depinfo->service = strdup (e);
deptype = NULL;
continue;
}
e = strsep (&p, "=");
if (! e || sscanf (e, "%d", &i) != 1)
continue;
/* Sanity */
e = get_shell_value (p);
if (! e || strlen (e) == 0)
continue;
if (! deptype)
{
depinfo->depends = rc_xmalloc (sizeof (rc_deptype_t));
deptype = depinfo->depends;
memset (deptype, 0, sizeof (rc_deptype_t));
}
else
if (strcmp (deptype->type, type) != 0)
{
deptype->next = rc_xmalloc (sizeof (rc_deptype_t));
deptype = deptype->next;
memset (deptype, 0, sizeof (rc_deptype_t));
}
if (! deptype->type)
deptype->type = strdup (type);
deptype->services = rc_strlist_addsort (deptype->services, e);
}
fclose (fp);
return (deptree);
}
rc_depinfo_t *rc_get_depinfo (rc_depinfo_t *deptree, const char *service)
{
rc_depinfo_t *di;
if (! deptree || ! service)
return (NULL);
for (di = deptree; di; di = di->next)
if (strcmp (di->service, service) == 0)
return (di);
return (NULL);
}
rc_deptype_t *rc_get_deptype (rc_depinfo_t *depinfo, const char *type)
{
rc_deptype_t *dt;
if (! depinfo || !type)
return (NULL);
for (dt = depinfo->depends; dt; dt = dt->next)
if (strcmp (dt->type, type) == 0)
return (dt);
return (NULL);
}
static bool valid_service (const char *runlevel, const char *service)
{
return ((strcmp (runlevel, RC_LEVEL_BOOT) != 0 &&
rc_service_in_runlevel (service, RC_LEVEL_BOOT)) ||
rc_service_in_runlevel (service, runlevel) ||
rc_service_state (service, rc_service_coldplugged) ||
rc_service_state (service, rc_service_started));
}
static bool get_provided1 (const char *runlevel, struct lhead *providers,
rc_deptype_t *deptype,
const char *level, bool coldplugged,
bool started, bool inactive)
{
char *service;
int i;
bool retval = false;
STRLIST_FOREACH (deptype->services, service, i)
{
bool ok = true;
if (level)
ok = rc_service_in_runlevel (service, level);
else if (coldplugged)
ok = (rc_service_state (service, rc_service_coldplugged) &&
! rc_service_in_runlevel (service, runlevel) &&
! rc_service_in_runlevel (service, RC_LEVEL_BOOT));
if (! ok)
continue;
if (started)
ok = (rc_service_state (service, rc_service_starting) ||
rc_service_state (service, rc_service_started) ||
rc_service_state (service, rc_service_stopping));
else if (inactive)
ok = rc_service_state (service, rc_service_inactive);
if (! ok)
continue;
retval = true;
providers->list = rc_strlist_add (providers->list, service);
}
return (retval);
}
/* Work out if a service is provided by another service.
For example metalog provides logger.
We need to be able to handle syslogd providing logger too.
We do this by checking whats running, then what's starting/stopping,
then what's run in the runlevels and finally alphabetical order.
If there are any bugs in rc-depend, they will probably be here as
provided dependancy can change depending on runlevel state.
*/
static char **get_provided (rc_depinfo_t *deptree, rc_depinfo_t *depinfo,
const char *runlevel, int options)
{
rc_deptype_t *dt;
struct lhead providers;
char *service;
int i;
if (! deptree || ! depinfo)
return (NULL);
if (rc_service_exists (depinfo->service))
return (NULL);
dt = rc_get_deptype (depinfo, "providedby");
if (! dt)
return (NULL);
memset (&providers, 0, sizeof (struct lhead));
/* If we are stopping then all depends are true, regardless of state.
This is especially true for net services as they could force a restart
of the local dns resolver which may depend on net. */
if (options & RC_DEP_STOP)
{
STRLIST_FOREACH (dt->services, service, i)
providers.list = rc_strlist_add (providers.list, service);
return (providers.list);
}
/* If we're strict, then only use what we have in our runlevel */
if (options & RC_DEP_STRICT)
{
STRLIST_FOREACH (dt->services, service, i)
if (rc_service_in_runlevel (service, runlevel))
providers.list = rc_strlist_add (providers.list, service);
if (providers.list)
return (providers.list);
}
/* OK, we're not strict or there were no services in our runlevel.
This is now where the logic gets a little fuzzy :)
If there is >1 running service then we return NULL.
We do this so we don't hang around waiting for inactive services and
our need has already been satisfied as it's not strict.
We apply this to our runlevel, coldplugged services, then bootlevel
and finally any running.*/
#define DO \
if (providers.list && providers.list[0] && providers.list[1]) \
{ \
rc_strlist_free (providers.list); \
return (NULL); \
} \
else if (providers.list) \
return providers.list; \
/* Anything in the runlevel has to come first */
if (get_provided1 (runlevel, &providers, dt, runlevel, false, true, false))
{ DO }
if (get_provided1 (runlevel, &providers, dt, runlevel, false, false, true))
{ DO }
if (get_provided1 (runlevel, &providers, dt, runlevel, false, false, false))
return (providers.list);
/* Check coldplugged started services */
if (get_provided1 (runlevel, &providers, dt, NULL, true, true, false))
{ DO }
/* Check bootlevel if we're not in it */
if (strcmp (runlevel, RC_LEVEL_BOOT) != 0)
{
if (get_provided1 (runlevel, &providers, dt, RC_LEVEL_BOOT, false, true, false))
{ DO }
if (get_provided1 (runlevel, &providers, dt, RC_LEVEL_BOOT, false, false, true))
{ DO }
}
/* Check coldplugged inactive services */
if (get_provided1 (runlevel, &providers, dt, NULL, true, false, true))
{ DO }
/* Check manually started */
if (get_provided1 (runlevel, &providers, dt, NULL, false, true, false))
{ DO }
if (get_provided1 (runlevel, &providers, dt, NULL, false, false, true))
{ DO }
/* Nothing started then. OK, lets get the stopped services */
if (get_provided1 (runlevel, &providers, dt, NULL, true, false, false))
return (providers.list);
if ((strcmp (runlevel, RC_LEVEL_BOOT) != 0)
&& (get_provided1 (runlevel, &providers, dt, RC_LEVEL_BOOT, false, false, false)))
return (providers.list);
/* Still nothing? OK, list all services */
STRLIST_FOREACH (dt->services, service, i)
providers.list = rc_strlist_add (providers.list, service);
return (providers.list);
}
static void visit_service (rc_depinfo_t *deptree, char **types,
struct lhead *sorted, struct lhead *visited,
rc_depinfo_t *depinfo,
const char *runlevel, int options)
{
int i, j, k;
char *lp, *item;
char *service;
rc_depinfo_t *di;
rc_deptype_t *dt;
char **provides;
char *svcname;
if (! deptree || !sorted || !visited || !depinfo)
return;
/* Check if we have already visited this service or not */
STRLIST_FOREACH (visited->list, item, i)
if (strcmp (item, depinfo->service) == 0)
return;
/* Add ourselves as a visited service */
visited->list = rc_strlist_add (visited->list, depinfo->service);
STRLIST_FOREACH (types, item, i)
{
if ((dt = rc_get_deptype (depinfo, item)))
{
STRLIST_FOREACH (dt->services, service, j)
{
if (! options & RC_DEP_TRACE || strcmp (item, "iprovide") == 0)
{
sorted->list = rc_strlist_add (sorted->list, service);
continue;
}
di = rc_get_depinfo (deptree, service);
if ((provides = get_provided (deptree, di, runlevel, options)))
{
STRLIST_FOREACH (provides, lp, k)
{
di = rc_get_depinfo (deptree, lp);
if (di && (strcmp (item, "ineed") == 0 ||
valid_service (runlevel, di->service)))
visit_service (deptree, types, sorted, visited, di,
runlevel, options | RC_DEP_TRACE);
}
rc_strlist_free (provides);
}
else
if (di && (strcmp (item, "ineed") == 0 ||
valid_service (runlevel, service)))
visit_service (deptree, types, sorted, visited, di,
runlevel, options | RC_DEP_TRACE);
}
}
}
/* Now visit the stuff we provide for */
if (options & RC_DEP_TRACE && (dt = rc_get_deptype (depinfo, "iprovide")))
{
STRLIST_FOREACH (dt->services, service, i)
{
if ((di = rc_get_depinfo (deptree, service)))
if ((provides = get_provided (deptree, di, runlevel, options)))
{
STRLIST_FOREACH (provides, lp, j)
if (strcmp (lp, depinfo->service) == 0)
{
visit_service (deptree, types, sorted, visited, di,
runlevel, options | RC_DEP_TRACE);
break;
}
rc_strlist_free (provides);
}
}
}
/* We've visited everything we need, so add ourselves unless we
are also the service calling us or we are provided by something */
svcname = getenv("SVCNAME");
if (! svcname || strcmp (svcname, depinfo->service) != 0)
if (! rc_get_deptype (depinfo, "providedby"))
sorted->list = rc_strlist_add (sorted->list, depinfo->service);
}
char **rc_get_depends (rc_depinfo_t *deptree,
char **types, char **services,
const char *runlevel, int options)
{
struct lhead sorted;
struct lhead visited;
rc_depinfo_t *di;
char *service;
int i;
if (! deptree || ! types || ! services)
return (NULL);
memset (&sorted, 0, sizeof (struct lhead));
memset (&visited, 0, sizeof (struct lhead));
STRLIST_FOREACH (services, service, i)
{
di = rc_get_depinfo (deptree, service);
visit_service (deptree, types, &sorted, &visited, di, runlevel, options);
}
rc_strlist_free (visited.list);
return (sorted.list);
}
char **rc_order_services (rc_depinfo_t *deptree, const char *runlevel,
int options)
{
char **list = NULL;
char **types = NULL;
char **services = NULL;
bool reverse = false;
if (! runlevel)
return (NULL);
/* When shutting down, list all running services */
if (strcmp (runlevel, RC_LEVEL_SINGLE) == 0 ||
strcmp (runlevel, RC_LEVEL_SHUTDOWN) == 0 ||
strcmp (runlevel, RC_LEVEL_REBOOT) == 0)
{
list = rc_ls_dir (list, RC_SVCDIR_STARTING, RC_LS_INITD);
list = rc_ls_dir (list, RC_SVCDIR_INACTIVE, RC_LS_INITD);
list = rc_ls_dir (list, RC_SVCDIR_STARTED, RC_LS_INITD);
reverse = true;
}
else
{
list = rc_services_in_runlevel (runlevel);
/* Add coldplugged services */
list = rc_ls_dir (list, RC_SVCDIR_COLDPLUGGED, RC_LS_INITD);
/* If we're not the boot runlevel then add that too */
if (strcmp (runlevel, RC_LEVEL_BOOT) != 0)
{
char *path = rc_strcatpaths (RC_RUNLEVELDIR, RC_LEVEL_BOOT, NULL);
list = rc_ls_dir (list, path, RC_LS_INITD);
free (path);
}
}
/* Now we have our lists, we need to pull in any dependencies
and order them */
types = rc_strlist_add (NULL, "ineed");
types = rc_strlist_add (types, "iuse");
types = rc_strlist_add (types, "iafter");
services = rc_get_depends (deptree, types, list, runlevel,
RC_DEP_STRICT | RC_DEP_TRACE | options);
rc_strlist_free (list);
rc_strlist_free (types);
if (reverse)
rc_strlist_reverse (services);
return (services);
}
static bool is_newer_than (const char *file, const char *target)
{
struct stat buf;
int mtime;
if (stat (file, &buf) != 0 || buf.st_size == 0)
return (false);
mtime = buf.st_mtime;
if (stat (target, &buf) != 0)
return (false);
if (mtime < buf.st_mtime)
return (false);
if (rc_is_dir (target))
{
char **targets = rc_ls_dir (NULL, target, 0);
char *t;
int i;
bool newer = true;
STRLIST_FOREACH (targets, t, i)
{
char *path = rc_strcatpaths (target, t, NULL);
newer = is_newer_than (file, path);
free (path);
if (! newer)
break;
}
rc_strlist_free (targets);
return (newer);
}
return (true);
}
typedef struct deppair
{
const char *depend;
const char *addto;
} deppair_t;
static const deppair_t deppairs[] = {
{ "ineed", "needsme" },
{ "iuse", "usesme" },
{ "iafter", "ibefore" },
{ "ibefore", "iafter" },
{ "iprovide", "providedby" },
{ NULL, NULL }
};
static const char *depdirs[] =
{
RC_SVCDIR "starting",
RC_SVCDIR "started",
RC_SVCDIR "stopping",
RC_SVCDIR "inactive",
RC_SVCDIR "wasinactive",
RC_SVCDIR "failed",
RC_SVCDIR "coldplugged",
RC_SVCDIR "daemons",
RC_SVCDIR "options",
RC_SVCDIR "exclusive",
RC_SVCDIR "scheduled",
RC_SVCDIR "ebuffer",
NULL
};
/* This is a 5 phase operation
Phase 1 is a shell script which loads each init script and config in turn
and echos their dependency info to stdout
Phase 2 takes that and populates a depinfo object with that data
Phase 3 adds any provided services to the depinfo object
Phase 4 scans that depinfo object and puts in backlinks
Phase 5 saves the depinfo object to disk
*/
int rc_update_deptree (bool force)
{
char *depends;
char *service;
char *type;
char *depend;
int retval = 0;
FILE *fp;
rc_depinfo_t *deptree;
rc_depinfo_t *depinfo;
rc_depinfo_t *di;
rc_depinfo_t *last_depinfo = NULL;
rc_deptype_t *deptype;
rc_deptype_t *dt;
rc_deptype_t *last_deptype = NULL;
char buffer[RC_LINEBUFFER];
int len;
int i;
int j;
int k;
bool already_added;
/* Create base directories if needed */
for (i = 0; depdirs[i]; i++)
if (! rc_is_dir (depdirs[i]))
if (mkdir (depdirs[i], 0755) != 0)
eerrorx ("mkdir `%s': %s", depdirs[i], strerror (errno));
if (! force)
if (is_newer_than (RC_DEPTREE, RC_INITDIR) &&
is_newer_than (RC_DEPTREE, RC_CONFDIR) &&
is_newer_than (RC_DEPTREE, "/etc/rc.conf"))
return 0;
ebegin ("Caching service dependencies");
/* Some init scripts need RC_LIBDIR to source stuff
Ideally we should be setting our full env instead */
if (! getenv ("RC_LIBDIR"))
setenv ("RC_LIBDIR", RC_LIBDIR, 0);
/* Phase 1 */
if ((fp = popen (GENDEP, "r")) == NULL)
eerrorx ("popen: %s", strerror (errno));
deptree = rc_xmalloc (sizeof (rc_depinfo_t));
memset (deptree, 0, sizeof (rc_depinfo_t));
memset (buffer, 0, RC_LINEBUFFER);
/* Phase 2 */
while (fgets (buffer, RC_LINEBUFFER, fp))
{
/* Trim the newline */
if (buffer[strlen (buffer) - 1] == '\n')
buffer[strlen(buffer) -1] = 0;
depends = buffer;
service = strsep (&depends, " ");
if (! service)
continue;
type = strsep (&depends, " ");
for (depinfo = deptree; depinfo; depinfo = depinfo->next)
{
last_depinfo = depinfo;
if (depinfo->service && strcmp (depinfo->service, service) == 0)
break;
}
if (! depinfo)
{
if (! last_depinfo->service)
depinfo = last_depinfo;
else
{
last_depinfo->next = rc_xmalloc (sizeof (rc_depinfo_t));
depinfo = last_depinfo->next;
}
memset (depinfo, 0, sizeof (rc_depinfo_t));
depinfo->service = strdup (service);
}
/* We may not have any depends */
if (! type || ! depends)
continue;
last_deptype = NULL;
for (deptype = depinfo->depends; deptype; deptype = deptype->next)
{
last_deptype = deptype;
if (strcmp (deptype->type, type) == 0)
break;
}
if (! deptype)
{
if (! last_deptype)
{
depinfo->depends = rc_xmalloc (sizeof (rc_deptype_t));
deptype = depinfo->depends;
}
else
{
last_deptype->next = rc_xmalloc (sizeof (rc_deptype_t));
deptype = last_deptype->next;
}
memset (deptype, 0, sizeof (rc_deptype_t));
deptype->type = strdup (type);
}
/* Now add each depend to our type.
We do this individually so we handle multiple spaces gracefully */
while ((depend = strsep (&depends, " ")))
{
if (depend[0] == 0)
continue;
/* .sh files are not init scripts */
len = strlen (depend);
if (len > 2 &&
depend[len - 3] == '.' &&
depend[len - 2] == 's' &&
depend[len - 1] == 'h')
continue;
deptype->services = rc_strlist_addsort (deptype->services, depend);
}
}
pclose (fp);
/* Phase 3 - add our providors to the tree */
for (depinfo = deptree; depinfo; depinfo = depinfo->next)
{
if ((deptype = rc_get_deptype (depinfo, "iprovide")))
STRLIST_FOREACH (deptype->services, service, i)
{
for (di = deptree; di; di = di->next)
{
last_depinfo = di;
if (strcmp (di->service, service) == 0)
break;
}
if (! di)
{
last_depinfo->next = rc_xmalloc (sizeof (rc_depinfo_t));
di = last_depinfo->next;
memset (di, 0, sizeof (rc_depinfo_t));
di->service = strdup (service);
}
}
}
/* Phase 4 - backreference our depends */
for (depinfo = deptree; depinfo; depinfo = depinfo->next)
{
for (i = 0; deppairs[i].depend; i++)
{
deptype = rc_get_deptype (depinfo, deppairs[i].depend);
if (! deptype)
continue;
STRLIST_FOREACH (deptype->services, service, j)
{
di = rc_get_depinfo (deptree, service);
if (! di)
{
if (strcmp (deptype->type, "ineed") == 0)
{
eerror ("Service `%s' needs non existant service `%s'",
depinfo->service, service);
retval = -1;
}
continue;
}
/* Add our deptype now */
last_deptype = NULL;
for (dt = di->depends; dt; dt = dt->next)
{
last_deptype = dt;
if (strcmp (dt->type, deppairs[i].addto) == 0)
break;
}
if (! dt)
{
if (! last_deptype)
{
di->depends = rc_xmalloc (sizeof (rc_deptype_t));
dt = di->depends;
}
else
{
last_deptype->next = rc_xmalloc (sizeof (rc_deptype_t));
dt = last_deptype->next;
}
memset (dt, 0, sizeof (rc_deptype_t));
dt->type = strdup (deppairs[i].addto);
}
already_added = false;
STRLIST_FOREACH (dt->services, service, k)
if (strcmp (service, depinfo->service) == 0)
{
already_added = true;
break;
}
if (! already_added)
dt->services = rc_strlist_addsort (dt->services,
depinfo->service);
}
}
}
/* Phase 5 - save to disk
Now that we're purely in C, do we need to keep a shell parseable file?
I think yes as then it stays human readable
This works and should be entirely shell parseable provided that depend
names don't have any non shell variable characters in
*/
if ((fp = fopen (RC_DEPTREE, "w")) == NULL)
eerror ("fopen `%s': %s", RC_DEPTREE, strerror (errno));
else
{
i = 0;
for (depinfo = deptree; depinfo; depinfo = depinfo->next)
{
fprintf (fp, "depinfo_%d_service='%s'\n", i, depinfo->service);
for (deptype = depinfo->depends; deptype; deptype = deptype->next)
{
k = 0;
STRLIST_FOREACH (deptype->services, service, j)
{
fprintf (fp, "depinfo_%d_%s_%d='%s'\n", i, deptype->type,
k, service);
k++;
}
}
i++;
}
fclose (fp);
}
rc_free_deptree (deptree);
eend (retval, "Failed to update the service dependency tree");
return (retval);
}

750
src/librc-misc.c Normal file
View File

@@ -0,0 +1,750 @@
/*
rc-misc.c
rc misc functions
Copyright 2007 Gentoo Foundation
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <regex.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "einfo.h"
#include "rc-misc.h"
#include "rc.h"
#include "strlist.h"
#define ERRX eerrorx("out of memory");
#define PROFILE_ENV "/etc/profile.env"
#define SYS_WHITELIST RC_LIBDIR "conf.d/env_whitelist"
#define USR_WHITELIST "/etc/conf.d/env_whitelist"
#define RC_CONFIG "/etc/conf.d/rc"
#define PATH_PREFIX RC_LIBDIR "bin:/bin:/sbin:/usr/bin:/usr/sbin"
#ifndef S_IXUGO
# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
#endif
void *rc_xcalloc (size_t n, size_t size)
{
void *value = calloc (n, size);
if (value)
return value;
ERRX
}
void *rc_xmalloc (size_t size)
{
void *value = malloc (size);
if (value)
return (value);
ERRX
}
void *rc_xrealloc (void *ptr, size_t size)
{
void *value = realloc (ptr, size);
if (value)
return (value);
ERRX
}
char *rc_xstrdup (const char *str)
{
char *value;
if (! str)
return (NULL);
value = strdup (str);
if (value)
return (value);
ERRX
}
bool rc_is_env (const char *var, const char *val)
{
char *v;
if (! var)
return (false);
v = getenv (var);
if (! v)
return (val == NULL ? true : false);
return (strcasecmp (v, val) == 0 ? true : false);
}
char *rc_strcatpaths (const char *path1, const char *paths, ...)
{
va_list ap;
int length;
int i;
char *p;
char *path;
char *pathp;
if (! path1 || ! paths)
return (NULL);
length = strlen (path1) + strlen (paths) + 3;
i = 0;
va_start (ap, paths);
while ((p = va_arg (ap, char *)) != NULL)
length += strlen (p) + 1;
va_end (ap);
path = rc_xmalloc (length);
memset (path, 0, length);
memcpy (path, path1, strlen (path1));
pathp = path + strlen (path1) - 1;
if (*pathp != '/')
{
pathp++;
*pathp++ = '/';
}
else
pathp++;
memcpy (pathp, paths, strlen (paths));
pathp += strlen (paths);
va_start (ap, paths);
while ((p = va_arg (ap, char *)) != NULL)
{
if (*pathp != '/')
*pathp++ = '/';
i = strlen (p);
memcpy (pathp, p, i);
pathp += i;
}
va_end (ap);
*pathp++ = 0;
return (path);
}
bool rc_exists (const char *pathname)
{
struct stat buf;
if (! pathname)
return (false);
if (stat (pathname, &buf) == 0)
return (true);
errno = 0;
return (false);
}
bool rc_is_file (const char *pathname)
{
struct stat buf;
if (! pathname)
return (false);
if (stat (pathname, &buf) == 0)
return (S_ISREG (buf.st_mode));
errno = 0;
return (false);
}
bool rc_is_dir (const char *pathname)
{
struct stat buf;
if (! pathname)
return (false);
if (stat (pathname, &buf) == 0)
return (S_ISDIR (buf.st_mode));
errno = 0;
return (false);
}
bool rc_is_link (const char *pathname)
{
struct stat buf;
if (! pathname)
return (false);
if (lstat (pathname, &buf) == 0)
return (S_ISLNK (buf.st_mode));
errno = 0;
return (false);
}
bool rc_is_exec (const char *pathname)
{
struct stat buf;
if (! pathname)
return (false);
if (lstat (pathname, &buf) == 0)
return (buf.st_mode & S_IXUGO);
errno = 0;
return (false);
}
char **rc_ls_dir (char **list, const char *dir, int options)
{
DIR *dp;
struct dirent *d;
if (! dir)
return (list);
if ((dp = opendir (dir)) == NULL)
{
eerror ("failed to opendir `%s': %s", dir, strerror (errno));
return (list);
}
errno = 0;
while (((d = readdir (dp)) != NULL) && errno == 0)
{
if (d->d_name[0] != '.')
{
if (options & RC_LS_INITD)
{
int l = strlen (d->d_name);
char *init = rc_strcatpaths (RC_INITDIR, d->d_name, NULL);
bool ok = rc_exists (init);
free (init);
if (! ok)
continue;
/* .sh files are not init scripts */
if (l > 2 && d->d_name[l - 3] == '.' &&
d->d_name[l - 2] == 's' &&
d->d_name[l - 1] == 'h')
continue;
}
list = rc_strlist_addsort (list, d->d_name);
}
}
closedir (dp);
if (errno != 0)
{
eerror ("failed to readdir `%s': %s", dir, strerror (errno));
rc_strlist_free (list);
return (NULL);
}
return (list);
}
bool rc_rm_dir (const char *pathname, bool top)
{
DIR *dp;
struct dirent *d;
if (! pathname)
return (false);
if ((dp = opendir (pathname)) == NULL)
{
eerror ("failed to opendir `%s': %s", pathname, strerror (errno));
return (false);
}
errno = 0;
while (((d = readdir (dp)) != NULL) && errno == 0)
{
if (strcmp (d->d_name, ".") != 0 && strcmp (d->d_name, "..") != 0)
{
char *tmp = rc_strcatpaths (pathname, d->d_name, NULL);
if (d->d_type == DT_DIR)
{
if (! rc_rm_dir (tmp, true))
{
free (tmp);
closedir (dp);
return (false);
}
}
else
{
if (unlink (tmp))
{
eerror ("failed to unlink `%s': %s", tmp, strerror (errno));
free (tmp);
closedir (dp);
return (false);
}
}
free (tmp);
}
}
if (errno != 0)
eerror ("failed to readdir `%s': %s", pathname, strerror (errno));
closedir (dp);
if (top && rmdir (pathname) != 0)
{
eerror ("failed to rmdir `%s': %s", pathname, strerror (errno));
return false;
}
return (true);
}
char **rc_get_config (char **list, const char *file)
{
FILE *fp;
char buffer[RC_LINEBUFFER];
char *p;
char *token;
char *line;
char *linep;
char *linetok;
int i = 0;
bool replaced;
char *entry;
char *newline;
if (! (fp = fopen (file, "r")))
{
ewarn ("load_config_file `%s': %s", file, strerror (errno));
return (list);
}
while (fgets (buffer, RC_LINEBUFFER, fp))
{
p = buffer;
/* Strip leading spaces/tabs */
while ((*p == ' ') || (*p == '\t'))
p++;
if (! p || strlen (p) < 3 || p[0] == '#')
continue;
/* Get entry */
token = strsep (&p, "=");
if (! token)
continue;
entry = rc_xstrdup (token);
do
{
/* Bash variables are usually quoted */
token = strsep (&p, "\"\'");
}
while ((token) && (strlen (token) == 0));
/* Drop a newline if that's all we have */
i = strlen (token) - 1;
if (token[i] == 10)
token[i] = 0;
i = strlen (entry) + strlen (token) + 2;
newline = rc_xmalloc (i);
snprintf (newline, i, "%s=%s", entry, token);
replaced = false;
/* In shells the last item takes precedence, so we need to remove
any prior values we may already have */
STRLIST_FOREACH (list, line, i)
{
char *tmp = rc_xstrdup (line);
linep = tmp;
linetok = strsep (&linep, "=");
if (strcmp (linetok, entry) == 0)
{
/* We have a match now - to save time we directly replace it */
free (list[i - 1]);
list[i - 1] = newline;
replaced = true;
free (tmp);
break;
}
free (tmp);
}
if (! replaced)
{
list = rc_strlist_addsort (list, newline);
free (newline);
}
free (entry);
}
fclose (fp);
return (list);
}
char *rc_get_config_entry (char **list, const char *entry)
{
char *line;
int i;
char *p;
STRLIST_FOREACH (list, line, i)
{
p = strchr (line, '=');
if (p && strncmp (entry, line, p - line) == 0)
return (p += 1);
}
return (NULL);
}
char **rc_get_list (char **list, const char *file)
{
FILE *fp;
char buffer[RC_LINEBUFFER];
char *p;
char *token;
if (! (fp = fopen (file, "r")))
{
ewarn ("rc_get_list `%s': %s", file, strerror (errno));
return (list);
}
while (fgets (buffer, RC_LINEBUFFER, fp))
{
p = buffer;
/* Strip leading spaces/tabs */
while ((*p == ' ') || (*p == '\t'))
p++;
/* Get entry - we do not want comments */
token = strsep (&p, "#");
if (token && (strlen (token) > 1))
{
token[strlen (token) - 1] = 0;
list = rc_strlist_add (list, token);
}
}
fclose (fp);
return (list);
}
char **rc_filter_env (void)
{
char **env = NULL;
char **whitelist = NULL;
char *env_name = NULL;
char **profile = NULL;
int count = 0;
bool got_path = false;
char *env_var;
int env_len;
char *p;
char *token;
char *sep;
char *e;
int pplen = strlen (PATH_PREFIX);
whitelist = rc_get_list (whitelist, SYS_WHITELIST);
if (! whitelist)
ewarn ("system environment whitelist (" SYS_WHITELIST ") missing");
whitelist = rc_get_list (whitelist, USR_WHITELIST);
if (! whitelist)
return (NULL);
if (rc_is_file (PROFILE_ENV))
profile = rc_get_config (profile, PROFILE_ENV);
STRLIST_FOREACH (whitelist, env_name, count)
{
char *space = strchr (env_name, ' ');
if (space)
*space = 0;
env_var = getenv (env_name);
if (! env_var && profile)
{
env_len = strlen (env_name) + strlen ("export ") + 1;
p = rc_xmalloc (sizeof (char *) * env_len);
snprintf (p, env_len, "export %s", env_name);
env_var = rc_get_config_entry (profile, p);
free (p);
}
if (! env_var)
continue;
/* Ensure our PATH is prefixed with the system locations first
for a little extra security */
if (strcmp (env_name, "PATH") == 0 &&
strncmp (PATH_PREFIX, env_var, pplen) != 0)
{
got_path = true;
env_len = strlen (env_name) + strlen (env_var) + pplen + 2;
e = p = rc_xmalloc (sizeof (char *) * env_len);
p += sprintf (e, "%s=%s", env_name, PATH_PREFIX);
/* Now go through the env var and only add bits not in our PREFIX */
sep = env_var;
while ((token = strsep (&sep, ":")))
{
char *np = strdup (PATH_PREFIX);
char *npp = np;
char *tok = NULL;
while ((tok = strsep (&npp, ":")))
if (strcmp (tok, token) == 0)
break;
if (! tok)
p += sprintf (p, ":%s", token);
free (np);
}
*p++ = 0;
}
else
{
env_len = strlen (env_name) + strlen (env_var) + 2;
e = rc_xmalloc (sizeof (char *) * env_len);
snprintf (e, env_len, "%s=%s", env_name, env_var);
}
env = rc_strlist_add (env, e);
free (e);
}
/* We filtered the env but didn't get a PATH? Very odd.
However, we do need a path, so use a default. */
if (! got_path)
{
env_len = strlen ("PATH=") + strlen (PATH_PREFIX) + 2;
p = rc_xmalloc (sizeof (char *) * env_len);
snprintf (p, env_len, "PATH=%s", PATH_PREFIX);
env = rc_strlist_add (env, p);
free (p);
}
rc_strlist_free (whitelist);
rc_strlist_free (profile);
return (env);
}
/* Other systems may need this at some point, but for now it's Linux only */
#ifdef __linux__
static bool file_regex (const char *file, const char *regex)
{
FILE *fp;
char buffer[RC_LINEBUFFER];
regex_t re;
bool retval = false;
int result;
if (! rc_exists (file))
return (false);
if (! (fp = fopen (file, "r")))
{
ewarn ("file_regex `%s': %s", file, strerror (errno));
return (false);
}
if ((result = regcomp (&re, regex, REG_EXTENDED | REG_NOSUB)) != 0)
{
fclose (fp);
regerror (result, &re, buffer, sizeof (buffer));
eerror ("file_regex: %s", buffer);
return (false);
}
while (fgets (buffer, RC_LINEBUFFER, fp))
{
if (regexec (&re, buffer, 0, NULL, 0) == 0)
{
retval = true;
break;
}
}
fclose (fp);
regfree (&re);
return (retval);
}
#endif
char **rc_config_env (char **env)
{
char *line;
int i;
char *p;
char **config = rc_get_config (NULL, RC_CONFIG);
char *e;
char sys[6];
struct utsname uts;
bool has_net_fs_list = false;
FILE *fp;
char buffer[PATH_MAX];
STRLIST_FOREACH (config, line, i)
{
p = strchr (line, '=');
if (! p)
continue;
*p = 0;
e = getenv (line);
if (! e)
{
*p = '=';
env = rc_strlist_add (env, line);
}
else
{
int len = strlen (line) + strlen (e) + 2;
char *new = rc_xmalloc (sizeof (char *) * len);
snprintf (new, len, "%s=%s", line, e);
env = rc_strlist_add (env, new);
free (new);
}
}
rc_strlist_free (config);
i = strlen ("RC_LIBDIR=//rcscripts") + strlen (LIBDIR) + 2;
line = rc_xmalloc (sizeof (char *) * i);
snprintf (line, i, "RC_LIBDIR=/" LIBDIR "/rcscripts");
env = rc_strlist_add (env, line);
free (line);
i += strlen ("/init.d");
line = rc_xmalloc (sizeof (char *) * i);
snprintf (line, i, "RC_SVCDIR=/" LIBDIR "/rcscripts/init.d");
env = rc_strlist_add (env, line);
free (line);
env = rc_strlist_add (env, "RC_BOOTLEVEL=" RC_LEVEL_BOOT);
p = rc_get_runlevel ();
i = strlen ("RC_SOFTLEVEL=") + strlen (p) + 1;
line = rc_xmalloc (sizeof (char *) * i);
snprintf (line, i, "RC_SOFTLEVEL=%s", p);
env = rc_strlist_add (env, line);
free (line);
if (rc_exists (RC_SVCDIR "ksoftlevel"))
{
if (! (fp = fopen (RC_SVCDIR "ksoftlevel", "r")))
eerror ("fopen `%s': %s", RC_SVCDIR "ksoftlevel",
strerror (errno));
else
{
memset (buffer, 0, sizeof (buffer));
if (fgets (buffer, sizeof (buffer), fp))
{
i = strlen (buffer) - 1;
if (buffer[i] == '\n')
buffer[i] = 0;
i += strlen ("RC_DEFAULTLEVEL=") + 2;
line = rc_xmalloc (sizeof (char *) * i);
snprintf (line, i, "RC_DEFAULTLEVEL=%s", buffer);
env = rc_strlist_add (env, line);
free (line);
}
fclose (fp);
}
}
else
env = rc_strlist_add (env, "RC_DEFAULTLEVEL=" RC_LEVEL_DEFAULT);
memset (sys, 0, sizeof (sys));
/* Linux can run some funky stuff like Xen, VServer, UML, etc
We store this special system in RC_SYS so our scripts run fast */
#ifdef __linux__
if (rc_is_dir ("/proc/xen"))
{
fp = fopen ("/proc/xen/capabilities", "r");
if (fp)
{
fclose (fp);
if (file_regex ("/proc/xen/capabilities", "control_d"))
sprintf (sys, "XENU");
}
if (! sys)
sprintf (sys, "XEN0");
}
else if (file_regex ("/proc/cpuinfo", "UML"))
sprintf (sys, "UML");
else if (file_regex ("/proc/self/status",
"(s_context|VxID|envID):[[:space:]]*[1-9]"))
sprintf(sys, "VPS");
#endif
/* Only add a NET_FS list if not defined */
STRLIST_FOREACH (env, line, i)
if (strncmp (line, "RC_NET_FS_LIST=", strlen ("RC_NET_FS_LIST=")) == 0)
{
has_net_fs_list = true;
break;
}
if (! has_net_fs_list)
{
i = strlen ("RC_NET_FS_LIST=") + strlen (RC_NET_FS_LIST_DEFAULT) + 1;
line = rc_xmalloc (sizeof (char *) * i);
snprintf (line, i, "RC_NET_FS_LIST=%s", RC_NET_FS_LIST_DEFAULT);
env = rc_strlist_add (env, line);
free (line);
}
if (sys[0])
{
i = strlen ("RC_SYS=") + strlen (sys) + 2;
line = rc_xmalloc (sizeof (char *) * i);
snprintf (line, i, "RC_SYS=%s", sys);
env = rc_strlist_add (env, line);
free (line);
}
/* Some scripts may need to take a different code path if Linux/FreeBSD, etc
To save on calling uname, we store it in an environment variable */
if (uname (&uts) == 0)
{
i = strlen ("RC_UNAME=") + strlen (uts.sysname) + 2;
line = rc_xmalloc (sizeof (char *) * i);
snprintf (line, i, "RC_UNAME=%s", uts.sysname);
env = rc_strlist_add (env, line);
free (line);
}
/* Set this var to ensure that things are POSIX, which makes scripts work
on non GNU systems with less effort. */
env = rc_strlist_add (env, "POSIXLY_CORRECT=1");
return (env);
}

141
src/librc-strlist.c Normal file
View File

@@ -0,0 +1,141 @@
/*
librc-strlist.h
String list functions for using char ** arrays
Copyright 2007 Gentoo Foundation
Based on a previous implementation by Martin Schlemmer
Released under the GPLv2
*/
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "rc.h"
#include "rc-misc.h"
char **rc_strlist_add (char **list, const char *item)
{
char **newlist;
int i = 0;
if (! item)
return (list);
while (list && list[i])
i++;
newlist = rc_xrealloc (list, sizeof (char *) * (i + 2));
newlist[i] = rc_xstrdup (item);
newlist[i + 1] = NULL;
return (newlist);
}
static char **_rc_strlist_addsort (char **list, const char *item,
int (*sortfunc) (const char *s1,
const char *s2))
{
char **newlist;
int i = 0;
char *tmp1;
char *tmp2;
if (! item)
return (list);
while (list && list[i])
i++;
newlist = rc_xrealloc (list, sizeof (char *) * (i + 2));
if (i == 0)
newlist[i] = NULL;
newlist[i + 1] = NULL;
i = 0;
while (newlist[i] && sortfunc (newlist[i], item) < 0)
i++;
tmp1 = newlist[i];
newlist[i] = rc_xstrdup (item);
do
{
i++;
tmp2 = newlist[i];
newlist[i] = tmp1;
tmp1 = tmp2;
} while (tmp1);
return (newlist);
}
char **rc_strlist_addsort (char **list, const char *item)
{
return (_rc_strlist_addsort (list, item, strcoll));
}
char **rc_strlist_addsortc (char **list, const char *item)
{
return (_rc_strlist_addsort (list, item, strcmp));
}
char **rc_strlist_delete (char **list, const char *item)
{
int i = 0;
if (!list || ! item)
return (list);
while (list[i])
if (strcmp (list[i], item) == 0)
{
free (list[i]);
do
{
list[i] = list[i + 1];
i++;
} while (list[i]);
}
return (list);
}
void rc_strlist_reverse (char **list)
{
char *item;
int i = 0;
int j = 0;
if (! list)
return;
while (list[j])
j++;
j--;
while (i < j && list[i] && list[j])
{
item = list[i];
list[i] = list[j];
list[j] = item;
i++;
j--;
}
}
void rc_strlist_free (char **list)
{
int i = 0;
if (! list)
return;
while (list[i])
{
free (list[i]);
list[i++] = NULL;
}
free (list);
}

773
src/librc.c Normal file
View File

@@ -0,0 +1,773 @@
/*
librc
core RC functions
Copyright 2007 Gentoo Foundation
Released under the GPLv2
*/
#include <sys/types.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#ifndef __linux__
/* Although linux should work fine, gcc likes to bitch with our default
CFLAGS so we just don't include the file and use the GNU one defined
in string.h */
#include <libgen.h>
#endif
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "einfo.h"
#include "rc.h"
#include "rc-misc.h"
#include "strlist.h"
/* usecs to wait while we poll the fifo */
#define WAIT_INTERVAL 20000
/* max secs to wait until a service comes up */
#define WAIT_MAX 60
#define SOFTLEVEL RC_SVCDIR "softlevel"
static const char *rc_service_state_names[] = {
"started",
"stopped",
"starting",
"stopping",
"inactive",
"wasinactive",
"coldplugged",
"failed",
NULL
};
bool rc_runlevel_starting (void)
{
return (rc_is_dir (RC_SVCDIR "softscripts.old"));
}
bool rc_runlevel_stopping (void)
{
return (rc_is_dir (RC_SVCDIR "softscripts.new"));
}
char **rc_get_runlevels (void)
{
char **dirs = rc_ls_dir (NULL, RC_RUNLEVELDIR, 0);
char **runlevels = NULL;
int i;
char *dir;
STRLIST_FOREACH (dirs, dir, i)
{
char *path = rc_strcatpaths (RC_RUNLEVELDIR, dir, NULL);
if (rc_is_dir (path))
runlevels = rc_strlist_addsort (runlevels, dir);
free (path);
}
rc_strlist_free (dirs);
return (runlevels);
}
char *rc_get_runlevel (void)
{
FILE *fp;
static char buffer [PATH_MAX];
if (! (fp = fopen (SOFTLEVEL, "r")))
{
strcpy (buffer, "sysinit");
return (buffer);
}
if (fgets (buffer, PATH_MAX, fp))
{
int i = strlen (buffer) - 1;
if (buffer[i] == '\n')
buffer[i] = 0;
fclose (fp);
return (buffer);
}
fclose (fp);
strcpy (buffer, "sysinit");
return (buffer);
}
void rc_set_runlevel (const char *runlevel)
{
FILE *fp = fopen (SOFTLEVEL, "w");
if (! fp)
eerrorx ("failed to open `" SOFTLEVEL "': %s", strerror (errno));
fprintf (fp, "%s", runlevel);
fclose (fp);
}
bool rc_runlevel_exists (const char *runlevel)
{
char *path;
bool retval;
if (! runlevel)
return (false);
path = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, NULL);
retval = rc_is_dir (path);
free (path);
return (retval);
}
/* Resolve a service name to it's full path */
char *rc_resolve_service (const char *service)
{
char buffer[PATH_MAX];
char *file;
int r = 0;
if (! service)
return (NULL);
if (service[0] == '/')
return (strdup (service));
file = rc_strcatpaths (RC_SVCDIR, "started", service, NULL);
if (! rc_is_link (file))
{
free (file);
file = rc_strcatpaths (RC_SVCDIR, "inactive", service, NULL);
if (! rc_is_link (file))
{
free (file);
file = NULL;
}
}
memset (buffer, 0, sizeof (buffer));
if (file)
{
r = readlink (file, buffer, sizeof (buffer));
free (file);
if (r > 0)
return strdup (buffer);
}
snprintf (buffer, sizeof (buffer), RC_INITDIR "%s", service);
return (strdup (buffer));
}
bool rc_service_exists (const char *service)
{
char *file;
bool retval = false;
int len;
if (! service)
return (false);
len = strlen (service);
/* .sh files are not init scripts */
if (len > 2 && service[len - 3] == '.' &&
service[len - 2] == 's' &&
service[len - 1] == 'h')
return (false);
file = rc_resolve_service (service);
if (rc_exists (file))
retval = rc_is_exec (file);
free (file);
return (retval);
}
bool rc_service_in_runlevel (const char *service, const char *runlevel)
{
char *file;
bool retval;
if (! runlevel || ! service)
return (false);
if (! rc_service_exists (service))
return (false);
file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename (service), NULL);
retval = rc_exists (file);
free (file);
return (retval);
}
bool rc_mark_service (const char *service, const rc_service_state_t state)
{
char *file;
int i = 0;
int skip_state = -1;
char *base;
char *init = rc_resolve_service (service);
bool skip_wasinactive = false;
if (! service)
return (false);
base = basename (service);
if (state != rc_service_stopped)
{
if (! rc_is_file(init))
{
free (init);
return (false);
}
file = rc_strcatpaths (RC_SVCDIR, rc_service_state_names[state], base, NULL);
if (rc_exists (file))
unlink (file);
i = symlink (init, file);
if (i != 0)
{
free (file);
free (init);
einfo ("%d %s %s", state, rc_service_state_names[state], base);
eerror ("symlink `%s' to `%s': %s", init, file, strerror (errno));
return (false);
}
free (file);
skip_state = state;
}
if (state == rc_service_coldplugged)
{
free (init);
return (true);
}
/* Remove any old states now */
i = 0;
while (rc_service_state_names[i])
{
if ((i != skip_state &&
i != rc_service_stopped &&
i != rc_service_coldplugged &&
i != rc_service_crashed) &&
(! skip_wasinactive || i != rc_service_wasinactive))
{
file = rc_strcatpaths (RC_SVCDIR, rc_service_state_names[i], base, NULL);
if (rc_exists (file))
{
if ((state == rc_service_starting ||
state == rc_service_stopping) &&
i == rc_service_inactive)
{
char *wasfile = rc_strcatpaths (RC_SVCDIR,
rc_service_state_names[rc_service_wasinactive],
base, NULL);
if (symlink (init, wasfile) != 0)
eerror ("symlink `%s' to `%s': %s", init, wasfile,
strerror (errno));
skip_wasinactive = true;
free (wasfile);
}
errno = 0;
if (unlink (file) != 0 && errno != ENOENT)
eerror ("failed to delete `%s': %s", file,
strerror (errno));
}
free (file);
}
i++;
}
/* Remove the exclusive state if we're inactive */
if (state == rc_service_started ||
state == rc_service_stopped ||
state == rc_service_inactive)
{
file = rc_strcatpaths (RC_SVCDIR, "exclusive", base, NULL);
if (rc_exists (file))
if (unlink (file) != 0)
eerror ("unlink `%s': %s", file, strerror (errno));
free (file);
}
/* Remove any options and daemons the service may have stored */
if (state == rc_service_stopped)
{
char *dir = rc_strcatpaths (RC_SVCDIR, "options", base, NULL);
if (rc_is_dir (dir))
rc_rm_dir (dir, true);
free (dir);
dir = rc_strcatpaths (RC_SVCDIR, "daemons", base, NULL);
if (rc_is_dir (dir))
rc_rm_dir (dir, true);
free (dir);
rc_schedule_clear (service);
}
/* These are final states, so remove us from scheduled */
if (state == rc_service_started || state == rc_service_stopped)
{
char *sdir = rc_strcatpaths (RC_SVCDIR, "scheduled", NULL);
char **dirs = rc_ls_dir (NULL, sdir, 0);
char *dir;
int serrno;
STRLIST_FOREACH (dirs, dir, i)
{
char *bdir = rc_strcatpaths (sdir, dir, NULL);
file = rc_strcatpaths (bdir, base, NULL);
if (rc_exists (file))
if (unlink (file) != 0)
eerror ("unlink `%s': %s", file, strerror (errno));
free (file);
/* Try and remove the dir - we don't care about errors */
serrno = errno;
rmdir (bdir);
errno = serrno;
free (bdir);
}
rc_strlist_free (dirs);
free (sdir);
}
free (init);
return (true);
}
bool rc_service_state (const char *service, const rc_service_state_t state)
{
char *file;
bool retval;
/* If the init script does not exist then we are stopped */
if (! rc_service_exists (service))
return (state == rc_service_stopped ? true : false);
/* We check stopped state by not being in any of the others */
if (state == rc_service_stopped)
return ( ! (rc_service_state (service, rc_service_started) ||
rc_service_state (service, rc_service_starting) ||
rc_service_state (service, rc_service_stopping) ||
rc_service_state (service, rc_service_inactive)));
/* The crashed state and scheduled states are virtual */
if (state == rc_service_crashed)
return (rc_service_daemons_crashed (service));
else if (state == rc_service_scheduled)
{
char **services = rc_services_scheduled_by (service);
retval = (services);
if (services)
free (services);
return (retval);
}
/* Now we just check if a file by the service name rc_exists
in the state dir */
file = rc_strcatpaths (RC_SVCDIR, rc_service_state_names[state],
basename (service), NULL);
retval = rc_exists (file);
free (file);
return (retval);
}
bool rc_get_service_option (const char *service, const char *option,
char *value)
{
FILE *fp;
char buffer[1024];
char *file = rc_strcatpaths (RC_SVCDIR, "options", service, option, NULL);
bool retval = false;
if (rc_exists (file))
{
if ((fp = fopen (file, "r")) == NULL)
eerror ("fopen `%s': %s", file, strerror (errno));
else
{
memset (buffer, 0, sizeof (buffer));
while (fgets (buffer, RC_LINEBUFFER, fp))
{
memcpy (value, buffer, strlen (buffer));
value += strlen (buffer);
}
fclose (fp);
retval = true;
}
}
free (file);
return (retval);
}
bool rc_set_service_option (const char *service, const char *option,
const char *value)
{
FILE *fp;
char *path = rc_strcatpaths (RC_SVCDIR, "options", service, NULL);
char *file = rc_strcatpaths (path, option, NULL);
bool retval = false;
if (! rc_is_dir (path))
{
if (mkdir (path, 0755) != 0)
{
eerror ("mkdir `%s': %s", path, strerror (errno));
free (path);
free (file);
return (false);
}
}
if ((fp = fopen (file, "w")) == NULL)
eerror ("fopen `%s': %s", file, strerror (errno));
else
{
if (value)
fprintf (fp, "%s", value);
fclose (fp);
retval = true;
}
free (path);
free (file);
return (retval);
}
static pid_t _exec_service (const char *service, const char *arg)
{
char *file;
char *fifo;
pid_t pid = -1;
pid_t savedpid;
int status;
file = rc_resolve_service (service);
if (! rc_is_file (file))
{
rc_mark_service (service, rc_service_stopped);
free (file);
return (0);
}
/* We create a fifo so that other services can wait until we complete */
fifo = rc_strcatpaths (RC_SVCDIR, "exclusive", basename (service), NULL);
if (mkfifo (fifo, 0600) != 0 && errno != EEXIST)
{
eerror ("unable to create fifo `%s': %s", fifo, strerror (errno));
free (fifo);
free (file);
return (-1);
}
if ((pid = fork ()) == 0)
{
char *myarg = strdup (arg);
int e = 0;
execl (file, file, myarg, NULL);
e = errno;
free (myarg);
unlink (fifo);
free (fifo);
eerrorx ("unable to exec `%s': %s", file, strerror (errno));
}
free (fifo);
free (file);
if (pid == -1)
{
eerror ("unable to fork: %s", strerror (errno));
return (pid);
}
if (rc_is_env ("RC_PARALLEL_STARTUP", "yes"))
return (pid);
savedpid = pid;
errno = 0;
do
{
pid = waitpid (savedpid, &status, 0);
if (pid < 0)
{
if (errno != ECHILD)
eerror ("waitpid %d: %s", savedpid, strerror (errno));
return (-1);
}
} while (! WIFEXITED (status) && ! WIFSIGNALED (status));
return (0);
}
pid_t rc_stop_service (const char *service)
{
if (rc_service_state (service, rc_service_stopped))
return (0);
return (_exec_service (service, "stop"));
}
pid_t rc_start_service (const char *service)
{
if (! rc_service_state (service, rc_service_stopped))
return (0);
return (_exec_service (service, "start"));
}
void rc_schedule_start_service (const char *service,
const char *service_to_start)
{
char *dir;
char *init;
char *file;
if (! rc_service_exists (service) || ! rc_service_exists (service_to_start))
return;
dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename (service), NULL);
if (! rc_is_dir (dir))
if (mkdir (dir, 0755) != 0)
{
eerror ("mkdir `%s': %s", dir, strerror (errno));
free (dir);
return;
}
init = rc_resolve_service (service_to_start);
file = rc_strcatpaths (dir, basename (service_to_start), NULL);
if (! rc_exists (file) && symlink (init, file) != 0)
eerror ("symlink `%s' to `%s': %s", init, file, strerror (errno));
free (init);
free (file);
free (dir);
}
void rc_schedule_clear (const char *service)
{
char *dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename (service), NULL);
if (rc_is_dir (dir))
rc_rm_dir (dir, true);
free (dir);
}
bool rc_wait_service (const char *service)
{
char *fifo = rc_strcatpaths (RC_SVCDIR, "exclusive", basename (service), NULL);
struct timeval tv;
struct timeval stopat;
struct timeval now;
bool retval = false;
if (gettimeofday (&stopat, NULL) != 0)
{
eerror ("gettimeofday: %s", strerror (errno));
return (false);
}
stopat.tv_sec += WAIT_MAX;
while (true)
{
if (! rc_exists (fifo))
{
retval = true;
break;
}
tv.tv_sec = 0;
tv.tv_usec = WAIT_INTERVAL;
if (select (0, 0, 0, 0, &tv) < 0)
{
if (errno != EINTR)
eerror ("select: %s",strerror (errno));
break;
}
/* Don't hang around forever */
if (gettimeofday (&now, NULL) != 0)
{
eerror ("gettimeofday: %s", strerror (errno));
break;
}
if (timercmp (&now, &stopat, >))
break;
}
free (fifo);
return (retval);
}
char **rc_services_in_runlevel (const char *runlevel)
{
char *dir;
char **list = NULL;
if (! runlevel)
return (rc_ls_dir (NULL, RC_INITDIR, RC_LS_INITD));
/* These special levels never contain any services */
if (strcmp (runlevel, RC_LEVEL_SYSINIT) == 0 ||
strcmp (runlevel, RC_LEVEL_SINGLE) == 0)
return (NULL);
dir = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, NULL);
if (! rc_is_dir (dir))
eerror ("runlevel `%s' does not exist", runlevel);
else
list = rc_ls_dir (list, dir, RC_LS_INITD);
free (dir);
return (list);
}
char **rc_services_in_state (rc_service_state_t state)
{
char *dir = rc_strcatpaths (RC_SVCDIR, rc_service_state_names[state], NULL);
char **list = NULL;
if (rc_is_dir (dir))
list = rc_ls_dir (list, dir, RC_LS_INITD);
free (dir);
return (list);
}
bool rc_service_add (const char *runlevel, const char *service)
{
bool retval;
char *init;
char *file;
if (! rc_runlevel_exists (runlevel))
{
errno = ENOENT;
return (false);
}
if (rc_service_in_runlevel (service, runlevel))
{
errno = EEXIST;
return (false);
}
init = rc_resolve_service (service);
file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename (service), NULL);
retval = (symlink (init, file) == 0);
free (init);
free (file);
return (retval);
}
bool rc_service_delete (const char *runlevel, const char *service)
{
char *file;
bool retval = false;
if (! runlevel || ! service)
return (false);
file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename (service), NULL);
if (unlink (file) == 0)
retval = true;
free (file);
return (retval);
}
char **rc_services_scheduled_by (const char *service)
{
char **dirs = rc_ls_dir (NULL, RC_SVCDIR "scheduled", 0);
char **list = NULL;
char *dir;
int i;
STRLIST_FOREACH (dirs, dir, i)
{
char *file = rc_strcatpaths (RC_SVCDIR "scheduled", dir, service, NULL);
if (rc_exists (file))
list = rc_strlist_add (list, file);
free (file);
}
rc_strlist_free (dirs);
return (list);
}
char **rc_services_scheduled (const char *service)
{
char *dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename (service), NULL);
char **list = NULL;
if (rc_is_dir (dir))
list = rc_ls_dir (list, dir, RC_LS_INITD);
free (dir);
return (list);
}
bool rc_allow_plug (char *service)
{
char *list;
char *p;
char *star;
char *token;
bool allow = true;
char *match = getenv ("RC_PLUG_SERVICES");
if (! match)
return true;
list = strdup (match);
p = list;
while ((token = strsep (&p, " ")))
{
bool truefalse = true;
if (token[0] == '!')
{
truefalse = false;
token++;
}
star = strchr (token, '*');
if (star)
{
if (strncmp (service, token, star - token) == 0)
{
allow = truefalse;
break;
}
}
else
{
if (strcmp (service, token) == 0)
{
allow = truefalse;
break;
}
}
}
free (list);
return (allow);
}

246
src/mountinfo.c Normal file
View File

@@ -0,0 +1,246 @@
/*
mountinfo.c
Obtains information about mounted filesystems.
Copyright 2007 Gentoo Foundation
*/
#include <sys/types.h>
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#elif defined(__linux__)
#include <limits.h>
#endif
#include <errno.h>
#include <limits.h>
#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "einfo.h"
#include "rc.h"
#include "rc-misc.h"
#include "strlist.h"
#if defined(__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__)
static char **find_mounts (regex_t *node_regex, regex_t *fstype_regex,
char **mounts, bool list_nodes, bool list_fstype)
{
struct statfs *mnts;
int nmnts;
int i;
char **list = NULL;
if ((nmnts = getmntinfo (&mnts, MNT_NOWAIT)) == 0)
eerrorx ("getmntinfo: %s", strerror (errno));
for (i = 0; i < nmnts; i++)
{
if (node_regex &&
regexec (node_regex, mnts[i].f_mntfromname, 0, NULL, 0) != 0)
continue;
if (fstype_regex &&
regexec (fstype_regex, mnts[i].f_fstypename, 0, NULL, 0) != 0)
continue;
if (mounts)
{
bool found = false;
int j;
char *mnt;
STRLIST_FOREACH (mounts, mnt, j)
if (strcmp (mnt, mnts[i].f_mntonname) == 0)
{
found = true;
break;
}
if (! found)
continue;
}
list = rc_strlist_addsortc (list, list_nodes ?
mnts[i].f_mntfromname :
list_fstype ? mnts[i].f_fstypename :
mnts[i].f_mntonname);
}
return (list);
}
#elif defined (__linux__)
static char **find_mounts (regex_t *node_regex, regex_t *fstype_regex,
char **mounts, bool list_nodes, bool list_fstype)
{
FILE *fp;
char buffer[PATH_MAX * 3];
char *p;
char *from;
char *to;
char *fstype;
char **list = NULL;
if ((fp = fopen ("/proc/mounts", "r")) == NULL)
eerrorx ("getmntinfo: %s", strerror (errno));
while (fgets (buffer, sizeof (buffer), fp))
{
p = buffer;
from = strsep (&p, " ");
if (node_regex &&
regexec (node_regex, from, 0, NULL, 0) != 0)
continue;
to = strsep (&p, " ");
fstype = strsep (&p, " ");
/* Skip the really silly rootfs */
if (strcmp (fstype, "rootfs") == 0)
continue;
if (fstype_regex &&
regexec (fstype_regex, fstype, 0, NULL, 0) != 0)
continue;
if (mounts)
{
bool found = false;
int j;
char *mnt;
STRLIST_FOREACH (mounts, mnt, j)
if (strcmp (mnt, to) == 0)
{
found = true;
break;
}
if (! found)
continue;
}
list = rc_strlist_addsortc (list,
list_nodes ?
list_fstype ? fstype :
from : to);
}
fclose (fp);
return (list);
}
#else
# error "Operating system not supported!"
#endif
int main (int argc, char **argv)
{
int i;
regex_t *fstype_regex = NULL;
regex_t *node_regex = NULL;
regex_t *skip_regex = NULL;
char **nodes = NULL;
char *node;
int result;
char buffer[256];
bool list_nodes = false;
bool list_fstype = false;
bool reverse = false;
char **mounts = NULL;
for (i = 1; i < argc; i++)
{
if (strcmp (argv[i], "--fstype-regex") == 0 && (i + 1 < argc))
{
i++;
if (fstype_regex)
free (fstype_regex);
fstype_regex = rc_xmalloc (sizeof (regex_t));
if ((result = regcomp (fstype_regex, argv[i],
REG_EXTENDED | REG_NOSUB)) != 0)
{
regerror (result, fstype_regex, buffer, sizeof (buffer));
eerrorx ("%s: invalid regex `%s'", argv[0], buffer);
}
continue;
}
if (strcmp (argv[i], "--node-regex") == 0 && (i + 1 < argc))
{
i++;
if (node_regex)
free (node_regex);
node_regex = rc_xmalloc (sizeof (regex_t));
if ((result = regcomp (node_regex, argv[i],
REG_EXTENDED | REG_NOSUB)) != 0)
{
regerror (result, node_regex, buffer, sizeof (buffer));
eerrorx ("%s: invalid regex `%s'", argv[0], buffer);
}
continue;
}
if (strcmp (argv[i], "--skip-regex") == 0 && (i + 1 < argc))
{
i++;
if (skip_regex)
free (skip_regex);
skip_regex = rc_xmalloc (sizeof (regex_t));
if ((result = regcomp (skip_regex, argv[i],
REG_EXTENDED | REG_NOSUB)) != 0)
{
regerror (result, skip_regex, buffer, sizeof (buffer));
eerrorx ("%s: invalid regex `%s'", argv[0], buffer);
}
continue;
}
if (strcmp (argv[i], "--fstype") == 0)
{
list_fstype = true;
continue;
}
if (strcmp (argv[i], "--node") == 0)
{
list_nodes = true;
continue;
}
if (strcmp (argv[i], "--reverse") == 0)
{
reverse = true;
continue;
}
if (argv[i][0] != '/')
eerrorx ("%s: `%s' is not a mount point", argv[0], argv[i]);
mounts = rc_strlist_add (mounts, argv[i]);
}
nodes = find_mounts (node_regex, fstype_regex, mounts,
list_nodes, list_fstype);
if (node_regex)
regfree (node_regex);
if (fstype_regex)
regfree (fstype_regex);
if (reverse)
rc_strlist_reverse (nodes);
result = EXIT_FAILURE;
STRLIST_FOREACH (nodes, node, i)
{
if (skip_regex && regexec (skip_regex, node, 0, NULL, 0) == 0)
continue;
printf ("%s\n", node);
result = EXIT_SUCCESS;
}
rc_strlist_free (nodes);
if (skip_regex)
free (skip_regex);
exit (result);
}

120
src/rc-depend.c Normal file
View File

@@ -0,0 +1,120 @@
/*
rc-depend
rc service dependency and ordering
Copyright 2006-2007 Gentoo Foundation
Released under the GPLv2
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "einfo.h"
#include "rc.h"
#include "rc-misc.h"
#include "strlist.h"
int main (int argc, char **argv)
{
char **types = NULL;
char **services = NULL;
char **depends = NULL;
rc_depinfo_t *deptree = NULL;
rc_depinfo_t *di;
char *service;
int options = RC_DEP_TRACE;
bool first = true;
int i;
bool update = false;
char *runlevel = getenv ("RC_SOFTLEVEL");
if (! runlevel)
runlevel = rc_get_runlevel ();
for (i = 1; i < argc; i++)
{
if (strcmp (argv[i], "--update") == 0)
{
if (! update)
{
rc_update_deptree (true);
update = true;
}
continue;
}
if (strcmp (argv[i], "--strict") == 0)
{
options |= RC_DEP_STRICT;
continue;
}
if (strcmp (argv[i], "--notrace") == 0)
{
options &= RC_DEP_TRACE;
continue;
}
if (argv[i][0] == '-')
{
argv[i]++;
types = rc_strlist_add (types, argv[i]);
}
else
{
if ((deptree = rc_load_deptree ()) == NULL)
eerrorx ("failed to load deptree");
di = rc_get_depinfo (deptree, argv[i]);
if (! di)
eerror ("no dependency info for service `%s'", argv[i]);
else
services = rc_strlist_add (services, argv[i]);
}
}
if (! services)
{
rc_strlist_free (types);
rc_free_deptree (deptree);
if (update)
return (EXIT_SUCCESS);
eerrorx ("no services specified");
}
/* If we don't have any types, then supply some defaults */
if (! types)
{
types = rc_strlist_add (NULL, "ineed");
rc_strlist_add (types, "iuse");
}
depends = rc_get_depends (deptree, types, services, runlevel, options);
if (depends)
{
STRLIST_FOREACH (depends, service, i)
{
if (first)
first = false;
else
printf (" ");
if (service)
printf ("%s", service);
}
printf ("\n");
}
rc_strlist_free (types);
rc_strlist_free (services);
rc_strlist_free (depends);
rc_free_deptree (deptree);
return (EXIT_SUCCESS);
}

34
src/rc-misc.h Normal file
View File

@@ -0,0 +1,34 @@
/*
rc-misc.h
This is private to us and not for user consumption
Copyright 2007 Gentoo Foundation
*/
#ifndef __RC_MISC_H__
#define __RC_MISC_H__
#ifndef LIBDIR
# define LIBDIR "lib"
#endif
#define RC_LIBDIR "/" LIBDIR "/rcscripts/"
#define RC_SVCDIR RC_LIBDIR "init.d/"
#define RC_DEPTREE RC_SVCDIR "deptree"
#define RC_RUNLEVELDIR "/etc/runlevels/"
#define RC_INITDIR "/etc/init.d/"
#define RC_CONFDIR "/etc/conf.d/"
#define RC_SVCDIR_STARTING RC_SVCDIR "starting/"
#define RC_SVCDIR_INACTIVE RC_SVCDIR "inactive/"
#define RC_SVCDIR_STARTED RC_SVCDIR "started/"
#define RC_SVCDIR_COLDPLUGGED RC_SVCDIR "coldplugged/"
#define RC_PLUGINDIR RC_LIBDIR "plugins/"
/* Max buffer to read a line from a file */
#define RC_LINEBUFFER 4096
/* Good defaults just incase user has none set */
#define RC_NET_FS_LIST_DEFAULT "afs cifs coda davfs fuse gfs ncpfs nfs nfs4 ocfs2 shfs smbfs"
#endif

119
src/rc-plugin.c Normal file
View File

@@ -0,0 +1,119 @@
/*
librc-plugin.c
Simple plugin handler
Copyright 2007 Gentoo Foundation
Released under the GPLv2
*/
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "einfo.h"
#include "rc.h"
#include "rc-misc.h"
#include "rc-plugin.h"
#include "strlist.h"
typedef struct plugin
{
char *name;
void *handle;
int (*hook) (rc_hook_t hook, const char *name);
struct plugin *next;
} plugin_t;
static plugin_t *plugins = NULL;
void rc_plugin_load (void)
{
char **files;
char *file;
int i;
plugin_t *plugin = plugins;
/* Ensure some sanity here */
rc_plugin_unload ();
if (! rc_exists (RC_PLUGINDIR))
return;
files = rc_ls_dir (NULL, RC_PLUGINDIR, 0);
STRLIST_FOREACH (files, file, i)
{
char *p = rc_strcatpaths (RC_PLUGINDIR, file, NULL);
void *h = dlopen (p, RTLD_LAZY);
char *func;
void *f;
if (! h)
{
eerror ("dlopen `%s': %s", p, dlerror ());
free (p);
continue;
}
func = file;
file = strsep (&func, ".");
func = rc_xmalloc (strlen (file) + strlen ("__hook") + 1);
sprintf (func, "_%s_hook", file);
f = dlsym (h, func);
if (! f)
{
eerror ("`%s' does not expose the symbol `%s'", p, func);
dlclose (h);
}
else
{
if (plugin)
{
plugin->next = rc_xmalloc (sizeof (plugin_t));
plugin = plugin->next;
}
else
plugin = plugins = rc_xmalloc (sizeof (plugin_t));
memset (plugin, 0, sizeof (plugin_t));
plugin->name = strdup (file);
plugin->handle = h;
plugin->hook = f;
}
free (func);
free (p);
}
rc_strlist_free (files);
}
void rc_plugin_run (rc_hook_t hook, const char *value)
{
plugin_t *plugin = plugins;
while (plugin)
{
if (plugin->hook)
plugin->hook (hook, value);
plugin = plugin->next;
}
}
void rc_plugin_unload (void)
{
plugin_t *plugin = plugins;
plugin_t *next;
while (plugin)
{
next = plugin->next;
dlclose (plugin->handle);
free (plugin->name);
free (plugin);
plugin = next;
}
plugins = NULL;
}

15
src/rc-plugin.h Normal file
View File

@@ -0,0 +1,15 @@
/*
librc-plugin.h
Private instructions to use plugins
Copyright 2007 Gentoo Foundation
Released under the GPLv2
*/
#ifndef __LIBRC_PLUGIN_H__
#define __LIBRC_PLUGIN_H__
void rc_plugin_load ();
void rc_plugin_unload ();
void rc_plugin_run (rc_hook_t, const char *value);
#endif

142
src/rc-status.c Normal file
View File

@@ -0,0 +1,142 @@
/*
rc-status
Display the status of the services in runlevels
Copyright 2007 Gentoo Foundation
Released under the GPLv2
*/
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "einfo.h"
#include "rc.h"
#include "rc-misc.h"
#include "strlist.h"
static void print_level (char *level)
{
printf ("Runlevel: ");
PEINFO_HILITE;
printf ("%s\n", level);
PEINFO_NORMAL;
}
static void print_service (char *service)
{
char status[10];
int cols = printf (" %s\n", service);
einfo_color_t color = einfo_bad;
if (rc_service_state (service, rc_service_stopping))
snprintf (status, sizeof (status), "stopping ");
else if (rc_service_state (service, rc_service_starting))
{
snprintf (status, sizeof (status), "starting ");
color = einfo_warn;
}
else if (rc_service_state (service, rc_service_inactive))
{
snprintf (status, sizeof (status), "inactive ");
color = einfo_warn;
}
else if (geteuid () == 0 && rc_service_state (service, rc_service_crashed))
snprintf (status, sizeof (status), " crashed ");
else if (rc_service_state (service, rc_service_started))
{
snprintf (status, sizeof (status), " started ");
color = einfo_good;
}
else if (rc_service_state (service, rc_service_scheduled))
{
snprintf (status, sizeof (status), "scheduled");
color = einfo_warn;
}
else
snprintf (status, sizeof (status), " stopped ");
ebracket (cols, color, status);
}
int main (int argc, char **argv)
{
char **levels = NULL;
char **services = NULL;
char *level;
char *service;
char c;
int option_index = 0;
int i;
int j;
const struct option longopts[] =
{
{"all", no_argument, NULL, 'a'},
{"list", no_argument, NULL, 'l'},
{"servicelist", no_argument, NULL, 's'},
{"unused", no_argument, NULL, 'u'},
{NULL, 0, NULL, 0}
};
while ((c = getopt_long(argc, argv, "alsu", longopts, &option_index)) != -1)
switch (c)
{
case 'a':
levels = rc_get_runlevels ();
break;
case 'l':
levels = rc_get_runlevels ();
STRLIST_FOREACH (levels, level, i)
printf ("%s\n", level);
rc_strlist_free (levels);
exit (EXIT_SUCCESS);
case 's':
services = rc_services_in_runlevel (NULL);
STRLIST_FOREACH (services, service, i)
print_service (service);
rc_strlist_free (services);
exit (EXIT_SUCCESS);
case 'u':
services = rc_services_in_runlevel (NULL);
levels = rc_get_runlevels ();
STRLIST_FOREACH (services, service, i)
{
bool found = false;
STRLIST_FOREACH (levels, level, j)
if (rc_service_in_runlevel (service, level))
{
found = true;
break;
}
if (! found)
print_service (service);
}
rc_strlist_free (levels);
rc_strlist_free (services);
exit (EXIT_SUCCESS);
case '?':
exit (EXIT_FAILURE);
default:
exit (EXIT_FAILURE);
}
while (optind < argc)
levels = rc_strlist_add (levels, argv[optind++]);
if (! levels)
levels = rc_strlist_add (NULL, rc_get_runlevel ());
STRLIST_FOREACH (levels, level, i)
{
print_level (level);
services = rc_services_in_runlevel (level);
STRLIST_FOREACH (services, service, j)
print_service (service);
rc_strlist_free (services);
}
rc_strlist_free (levels);
return (EXIT_SUCCESS);
}

162
src/rc-update.c Normal file
View File

@@ -0,0 +1,162 @@
/*
rc-update
Manage init scripts and runlevels
Copyright 2007 Gentoo Foundation
Released under the GPLv2
*/
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "einfo.h"
#include "rc.h"
#include "rc-misc.h"
#include "strlist.h"
static char *applet = NULL;
static bool add (const char *runlevel, const char *service)
{
bool retval = true;
if (! rc_runlevel_exists (runlevel))
{
ewarn ("runlevel `%s' does not exist", runlevel);
return (false);
}
if (rc_service_in_runlevel (service, runlevel))
{
ewarn ("%s already installed in runlevel `%s'; skipping",
service, runlevel);
return (false);
}
if (rc_service_add (runlevel, service))
einfo ("%s added to runlevel %s", service, runlevel);
else
{
eerror ("%s: failed to add service `%s' to runlevel `%s': %s",
applet, service, runlevel, strerror (errno));
retval = false;
}
return (retval);
}
int main (int argc, char **argv)
{
int i;
int j;
char *service;
char **runlevels = NULL;
char *runlevel;
applet = argv[0];
if (argc < 2 ||
strcmp (argv[1], "show") == 0 ||
strcmp (argv[1], "-s") == 0)
{
bool verbose = false;
char **services = rc_services_in_runlevel (NULL);
for (i = 2; i < argc; i++)
{
if (strcmp (argv[i], "--verbose") == 0 ||
strcmp (argv[i], "-v") == 0)
verbose = true;
else
runlevels = rc_strlist_add (runlevels, argv[i]);
}
if (! runlevels)
runlevels = rc_get_runlevels ();
STRLIST_FOREACH (services, service, i)
{
char **in = NULL;
bool inone = false;
STRLIST_FOREACH (runlevels, runlevel, j)
{
if (rc_service_in_runlevel (service, runlevel))
{
in = rc_strlist_add (in, runlevel);
inone = true;
}
else
{
char buffer[PATH_MAX];
memset (buffer, ' ', strlen (runlevel));
buffer[strlen (runlevel)] = 0;
in = rc_strlist_add (in, buffer);
}
}
if (! inone && ! verbose)
continue;
printf (" %20s |", service);
STRLIST_FOREACH (in, runlevel, j)
printf (" %s", runlevel);
printf ("\n");
}
return (EXIT_SUCCESS);
}
if (geteuid () != 0)
eerrorx ("%s: must be root to add or delete services from runlevels",
applet);
if (! (service = argv[2]))
eerrorx ("%s: no service specified", applet);
if (strcmp (argv[1], "add") == 0 ||
strcmp (argv[1], "-a") == 0)
{
if (! service)
eerrorx ("%s: no service specified", applet);
if (! rc_service_exists (service))
eerrorx ("%s: service `%s' does not exist", applet, service);
if (argc < 4)
add (rc_get_runlevel (), service);
for (i = 3; i < argc; i++)
add (argv[i], service);
return (EXIT_SUCCESS);
}
if (strcmp (argv[1], "delete") == 0 ||
strcmp (argv[1], "del") == 0 ||
strcmp (argv[1], "-d") == 0)
{
for (i = 3; i < argc; i++)
runlevels = rc_strlist_add (runlevels, argv[i]);
if (! runlevels)
runlevels = rc_strlist_add (runlevels, rc_get_runlevel ());
STRLIST_FOREACH (runlevels, runlevel, i)
{
if (rc_service_in_runlevel (service, runlevel))
{
if (rc_service_delete (runlevel, service))
einfo ("%s removed from runlevel %s", service, runlevel);
else
eerror ("%s: failed to remove service `%s' from runlevel `%s': %s",
applet, service, runlevel, strerror (errno));
}
}
return (EXIT_SUCCESS);
}
eerrorx ("%s: unknown command `%s'", applet, argv[1]);
}

1174
src/rc.c Normal file

File diff suppressed because it is too large Load Diff

180
src/rc.h Normal file
View File

@@ -0,0 +1,180 @@
/*
rc.h
Header file for external applications to get RC information.
Copyright 2007 Gentoo Foundation
Released under the GPLv2
*/
#ifndef __RC_H__
#define __RC_H__
#include <sys/types.h>
#include <stdbool.h>
/* Special level names */
#define RC_LEVEL_SYSINIT "sysinit"
#define RC_LEVEL_BOOT "boot"
#define RC_LEVEL_SINGLE "single"
#define RC_LEVEL_SHUTDOWN "shutdown"
#define RC_LEVEL_REBOOT "reboot"
#define RC_LEVEL_DEFAULT "default"
typedef enum
{
rc_service_started,
rc_service_stopped,
rc_service_starting,
rc_service_stopping,
rc_service_inactive,
rc_service_wasinactive,
rc_service_coldplugged,
rc_service_failed,
rc_service_scheduled,
rc_service_crashed
} rc_service_state_t;
char *rc_resolve_service (const char *service);
bool rc_service_exists (const char *service);
bool rc_service_in_runlevel (const char *service, const char *runlevel);
bool rc_service_state (const char *service, rc_service_state_t state);
bool rc_mark_service (const char *service, rc_service_state_t state);
pid_t rc_stop_service (const char *service);
pid_t rc_start_service (const char *service);
void rc_schedule_start_service (const char *service,
const char *service_to_start);
char **rc_services_scheduled_by (const char *service);
void rc_schedule_clear (const char *service);
bool rc_wait_service (const char *service);
bool rc_get_service_option (const char *service, const char *option,
char *value);
bool rc_set_service_option (const char *service, const char *option,
const char *value);
void rc_set_service_daemon (const char *service, const char *exec,
const char *name, const char *pidfile,
bool started);
bool rc_service_started_daemon (const char *service, const char *exec,
int indx);
bool rc_allow_plug (char *service);
char *rc_get_runlevel (void);
void rc_set_runlevel (const char *runlevel);
bool rc_runlevel_exists (const char *runlevel);
char **rc_get_runlevels (void);
bool rc_runlevel_starting (void);
bool rc_runlevel_stopping (void);
bool rc_service_add (const char *runlevel, const char *service);
bool rc_service_delete (const char *runlevel, const char *service);
char **rc_services_in_runlevel (const char *runlevel);
char **rc_services_in_state (rc_service_state_t state);
char **rc_services_scheduled (const char *service);
/* Find pids based on criteria - free the pointer returned after use */
pid_t *rc_find_pids (const char *exec, const char *cmd,
uid_t uid, pid_t pid);
/* Checks that all daemons started with start-stop-daemon by the service
are still running. If so, return false otherwise true.
You should check that the service has been started before calling this. */
bool rc_service_daemons_crashed (const char *service);
/* Dependency tree structs and functions. */
typedef struct rc_deptype
{
char *type;
char **services;
struct rc_deptype *next;
} rc_deptype_t;
typedef struct rc_depinfo
{
char *service;
rc_deptype_t *depends;
struct rc_depinfo *next;
} rc_depinfo_t;
/* Options for rc_dep_depends and rc_order_services.
When changing runlevels, you should use RC_DEP_START and RC_DEP_STOP for
the start and stop lists as we tweak the provided services for this. */
#define RC_DEP_TRACE 0x01
#define RC_DEP_STRICT 0x02
#define RC_DEP_START 0x04
#define RC_DEP_STOP 0x08
int rc_update_deptree (bool force);
rc_depinfo_t *rc_load_deptree (void);
rc_depinfo_t *rc_get_depinfo (rc_depinfo_t *deptree, const char *service);
rc_deptype_t *rc_get_deptype (rc_depinfo_t *depinfo, const char *type);
char **rc_get_depends (rc_depinfo_t *deptree, char **types,
char **services, const char *runlevel, int options);
/* List all the services that should be started, in order, the the
given runlevel, including sysinit and boot services where
approriate.
If reboot, shutdown or single are given then we list all the services
we that we need to shutdown in order. */
char **rc_order_services (rc_depinfo_t *deptree, const char *runlevel,
int options);
void rc_free_deptree (rc_depinfo_t *deptree);
/* Plugin handler
For each plugin loaded we will call it's _name_hook with the below
enum and either the runlevel name or service name. For example
int _splash_hook (rc_hook_t hook, const char *name);
Plugins are called when rc does something. This does not indicate an
end result and the plugin should use the above functions to query things
like service status. */
typedef enum
{
rc_hook_runlevel_stop_in = 1,
rc_hook_runlevel_stop_out,
rc_hook_runlevel_start_in,
rc_hook_runlevel_start_out,
rc_hook_service_stop_in,
rc_hook_service_stop_out,
rc_hook_service_start_in,
rc_hook_service_start_out
} rc_hook_t;
/* RC utility functions.
Although not directly related to RC in general, they are used by RC
itself and the supporting applications. */
void *rc_xcalloc (size_t n, size_t size);
void *rc_xmalloc (size_t size);
void *rc_xrealloc (void *ptr, size_t size);
char *rc_xstrdup (const char *str);
/* Concat paths adding '/' if needed. */
char *rc_strcatpaths (const char *path1, const char *paths, ...);
bool rc_is_env (const char *variable, const char *value);
bool rc_exists (const char *pathname);
bool rc_is_file (const char *pathname);
bool rc_is_link (const char *pathname);
bool rc_is_dir (const char *pathname);
bool rc_is_exec (const char *pathname);
#define RC_LS_INITD 0x01
char **rc_ls_dir (char **list, const char *dir, int options);
bool rc_rm_dir (const char *pathname, bool top);
/* Config file functions */
char **rc_get_list (char **list, const char *file);
char **rc_get_config (char **list, const char *file);
char *rc_get_config_entry (char **list, const char *entry);
/* Make an environment list which filters out all unwanted values
and loads it up with our RC config */
char **rc_filter_env (void);
char **rc_config_env (char **env);
/* Handy functions for dealing with string arrays of char ** */
char **rc_strlist_add (char **list, const char *item);
char **rc_strlist_addsort (char **list, const char *item);
char **rc_strlist_addsortc (char **list, const char *item);
char **rc_strlist_delete (char **list, const char *item);
void rc_strlist_reverse (char **list);
void rc_strlist_free (char **list);
#endif

1097
src/runscript.c Normal file

File diff suppressed because it is too large Load Diff

130
src/splash.c Normal file
View File

@@ -0,0 +1,130 @@
/*
splash.c
Splash plugin for the Gentoo RC sytsem.
splashutils needs to be re-written to support our new system.
Until then, we provide this compatible module which calls the
legacy bash scripts which is nasty. And slow.
For any themes that use scripts, such as the live-cd theme,
they will have to source /sbin/splash-functions.sh themselves like so
if ! type splash >/dev/null 2>/dev/null ; then
. /sbin/splash-functions.sh
fi
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <rc.h>
#ifndef LIBDIR
# define LIBDIR "lib"
#endif
#define SPLASH_CACHEDIR "/" LIBDIR "/splash/cache"
#define SPLASH_CMD "bash -c 'export SOFTLEVEL='%s'; export BOOTLEVEL=${RC_BOOTLEVEL}; export DEFAULTLEVEL=${RC_DEFAULTLEVEL}; export svcdir=${RC_SVCDIR}; add_suffix() { echo \"$@\"; }; . /etc/init.d/functions.sh; . /sbin/splash-functions.sh; splash %s %s %s'"
int _splash_hook (rc_hook_t hook, const char *name);
static int _do_splash (const char *cmd, const char *arg1, const char *arg2)
{
char *c;
int l;
char *soft = getenv ("RC_SOFTLEVEL");
if (! cmd || ! soft)
return (-1);
l = strlen (SPLASH_CMD) + strlen (soft) + strlen (cmd);
if (arg1)
l += strlen (arg1);
if (arg2)
l += strlen (arg2);
c = malloc (sizeof (char *) * l);
if (! c)
return (-1);
snprintf (c, l, SPLASH_CMD,
arg1 ? strcmp (arg1, RC_LEVEL_SYSINIT) == 0 ? RC_LEVEL_BOOT : soft : soft,
cmd, arg1 ? arg1 : "", arg2 ? arg2 : "");
l = system (c);
free (c);
return (l);
}
int _splash_hook (rc_hook_t hook, const char *name)
{
switch (hook)
{
case rc_hook_runlevel_stop_in:
if (strcmp (name, RC_LEVEL_SYSINIT) != 0)
return (_do_splash ("rc_init", name, NULL));
break;
case rc_hook_runlevel_start_out:
if (strcmp (name, RC_LEVEL_SYSINIT) == 0)
return (_do_splash ("rc_init", name, NULL));
else
return (_do_splash ("rc_exit", name, NULL));
default: ;
}
/* We don't care about splash unless we're changing runlevels */
if (! rc_runlevel_starting () &&
! rc_runlevel_stopping ())
return (0);
switch (hook)
{
case rc_hook_service_stop_in:
/* We need to stop localmount from unmounting our cache dir.
Luckily plugins can add to the unmount list. */
if (name && strcmp (name, "localmount") == 0)
{
char *umounts = getenv ("RC_NO_UMOUNTS");
char *new;
int i = strlen (SPLASH_CACHEDIR) + 1;
if (umounts)
i += strlen (umounts) + 1;
new = malloc (sizeof (char *) * i);
if (new)
{
if (umounts)
snprintf (new, i, "%s:%s", umounts, SPLASH_CACHEDIR);
else
snprintf (new, i, "%s", SPLASH_CACHEDIR);
}
/* We unsetenv first as some libc's leak memory if we overwrite
a var with a bigger value */
if (umounts)
unsetenv ("RC_NO_UMOUNTS");
setenv ("RC_NO_UMOUNTS", new, 1);
free (new);
}
return (_do_splash ("svc_stop", name, NULL));
case rc_hook_service_stop_out:
if (rc_service_state (name, rc_service_stopped))
return (_do_splash ("svc_stopped", name, "0"));
else
return (_do_splash ("svc_started", name, "1"));
case rc_hook_service_start_in:
return (_do_splash ("svc_start", name, NULL));
case rc_hook_service_start_out:
if (rc_service_state (name, rc_service_stopped))
return (_do_splash ("svc_started", name, "1"));
else
return (_do_splash ("svc_started", name, "0"));
default: ;
}
return (0);
}

1047
src/start-stop-daemon.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
#%PAM-1.0
auth sufficient pam_rootok.so
account required pam_permit.so
password required pam_deny.so
session optional pam_limits.so

24
src/strlist.h Normal file
View File

@@ -0,0 +1,24 @@
/*
strlist.h
String list macros for making char ** arrays
Copyright 2007 Gentoo Foundation
Based on a previous implementation by Martin Schlemmer
Released under the GPLv2
*/
#ifndef __STRLIST_H__
#define __STRLIST_H__
/* FIXME: We should replace the macro with an rc_strlist_foreach
function, but I'm unsure how to go about this. */
/* 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 STRLIST_FOREACH(_list, _pos, _counter) \
if ((_list) && _list[0] && ((_counter = 0) == 0)) \
while ((_pos = _list[_counter++]))
#endif /* __STRLIST_H__ */