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:
153
src/Makefile
Normal file
153
src/Makefile
Normal 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
83
src/einfo.h
Normal 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
247
src/env-update.c
Normal 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
48
src/env_whitelist
Normal 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
146
src/fstabinfo.c
Normal 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
877
src/libeinfo.c
Normal 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
600
src/librc-daemon.c
Normal 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
838
src/librc-depend.c
Normal 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
750
src/librc-misc.c
Normal 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
141
src/librc-strlist.c
Normal 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
773
src/librc.c
Normal 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
246
src/mountinfo.c
Normal 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
120
src/rc-depend.c
Normal 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
34
src/rc-misc.h
Normal 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
119
src/rc-plugin.c
Normal 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
15
src/rc-plugin.h
Normal 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
142
src/rc-status.c
Normal 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
162
src/rc-update.c
Normal 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]);
|
||||
}
|
||||
180
src/rc.h
Normal file
180
src/rc.h
Normal 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
1097
src/runscript.c
Normal file
File diff suppressed because it is too large
Load Diff
130
src/splash.c
Normal file
130
src/splash.c
Normal 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
1047
src/start-stop-daemon.c
Normal file
File diff suppressed because it is too large
Load Diff
6
src/start-stop-daemon.pam
Normal file
6
src/start-stop-daemon.pam
Normal 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
24
src/strlist.h
Normal 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__ */
|
||||
Reference in New Issue
Block a user