openrc/src/librc/librc.c

993 lines
22 KiB
C
Raw Normal View History

/*
2008-01-14 10:35:22 +05:30
librc
core RC functions
*/
2008-01-14 10:35:22 +05:30
/*
* Copyright 2007-2008 Roy Marples
* All rights reserved
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
const char librc_copyright[] = "Copyright (c) 2007-2008 Roy Marples";
#include "librc.h"
#ifdef __FreeBSD__
#include <sys/sysctl.h>
#endif
#include <signal.h>
#define SOFTLEVEL RC_SVCDIR "/softlevel"
2007-10-04 21:56:44 +05:30
#ifndef S_IXUGO
# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
#endif
/* File stream used for plugins to write environ vars to */
FILE *rc_environ_fd = NULL;
typedef struct rc_service_state_name {
RC_SERVICE state;
const char *name;
} rc_service_state_name_t;
/* We MUST list the states below 0x10 first
* The rest can be in any order */
static const rc_service_state_name_t rc_service_state_names[] = {
{ RC_SERVICE_STARTED, "started" },
{ RC_SERVICE_STOPPED, "stopped" },
{ RC_SERVICE_STARTING, "starting" },
{ RC_SERVICE_STOPPING, "stopping" },
{ RC_SERVICE_INACTIVE, "inactive" },
{ RC_SERVICE_WASINACTIVE, "wasinactive" },
{ RC_SERVICE_COLDPLUGGED, "coldplugged" },
{ RC_SERVICE_FAILED, "failed" },
{ RC_SERVICE_SCHEDULED, "scheduled"},
{ 0, NULL}
};
2007-10-05 15:46:14 +05:30
#define LS_INITD 0x01
#define LS_DIR 0x02
static RC_STRINGLIST *ls_dir(const char *dir, int options)
2007-10-05 15:46:14 +05:30
{
DIR *dp;
struct dirent *d;
RC_STRINGLIST *list;
struct stat buf;
size_t l;
char *file;
int r;
2007-10-05 15:46:14 +05:30
if ((dp = opendir(dir)) == NULL)
return NULL;
2007-10-05 15:46:14 +05:30
list = rc_stringlist_new();
while (((d = readdir(dp)) != NULL)) {
2007-10-05 15:46:14 +05:30
if (d->d_name[0] != '.') {
if (options & LS_INITD) {
/* Check that our file really exists.
* This is important as a service maybe in a runlevel, but
* could also have been removed. */
file = rc_strcatpaths(dir, d->d_name, NULL);
r = stat(file, &buf);
free(file);
if (r != 0)
2007-10-05 15:46:14 +05:30
continue;
/* .sh files are not init scripts */
l = strlen(d->d_name);
2007-10-05 15:46:14 +05:30
if (l > 2 && d->d_name[l - 3] == '.' &&
2008-01-11 21:21:40 +05:30
d->d_name[l - 2] == 's' &&
d->d_name[l - 1] == 'h')
2007-10-05 15:46:14 +05:30
continue;
}
if (options & LS_DIR) {
if (stat(d->d_name, &buf) == 0 &&
! S_ISDIR(buf.st_mode))
2007-10-05 15:46:14 +05:30
continue;
}
rc_stringlist_add(list, d->d_name);
2007-10-05 15:46:14 +05:30
}
}
closedir(dp);
2007-10-05 15:46:14 +05:30
return list;
2007-10-05 15:46:14 +05:30
}
static bool rm_dir(const char *pathname, bool top)
2007-10-05 16:06:53 +05:30
{
DIR *dp;
struct dirent *d;
char *tmp = NULL;
struct stat s;
bool retval = true;
2007-10-05 16:06:53 +05:30
if ((dp = opendir(pathname)) == NULL)
return false;
2007-10-05 16:06:53 +05:30
errno = 0;
while (((d = readdir(dp)) != NULL) && errno == 0) {
if (strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0) {
free(tmp);
tmp = rc_strcatpaths(pathname, d->d_name, (char *) NULL);
if (stat(tmp, &s) != 0) {
retval = false;
break;
}
if (S_ISDIR(s.st_mode)) {
if (! rm_dir(tmp, true))
2007-10-05 16:06:53 +05:30
{
retval = false;
break;
2007-10-05 16:06:53 +05:30
}
} else {
if (unlink(tmp)) {
retval = false;
break;
2007-10-05 16:06:53 +05:30
}
}
}
}
closedir(dp);
free(tmp);
if (! retval)
return false;
2007-10-05 16:06:53 +05:30
if (top && rmdir(pathname) != 0)
return false;
2007-10-05 16:06:53 +05:30
return true;
2007-10-05 16:06:53 +05:30
}
2008-02-19 21:21:59 +05:30
/* 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)
2008-02-19 21:21:59 +05:30
{
FILE *fp;
char *line;
regex_t re;
bool retval = false;
int result;
if (! (fp = fopen(file, "r")))
return false;
2008-02-19 21:21:59 +05:30
if ((result = regcomp(&re, regex, REG_EXTENDED | REG_NOSUB)) != 0) {
fclose(fp);
line = xmalloc(sizeof (char) * BUFSIZ);
regerror(result, &re, line, BUFSIZ);
fprintf(stderr, "file_regex: %s", line);
free(line);
return false;
2008-02-19 21:21:59 +05:30
}
while ((line = rc_getline(fp))) {
if (regexec(&re, line, 0, NULL, 0) == 0)
2008-02-19 21:21:59 +05:30
retval = true;
free(line);
2008-02-19 21:21:59 +05:30
if (retval)
break;
}
fclose(fp);
regfree(&re);
2008-02-19 21:21:59 +05:30
return retval;
2008-02-19 21:21:59 +05:30
}
#endif
const char *rc_sys(void)
{
#ifdef PREFIX
return RC_SYS_PREFIX;
#else
#ifdef __FreeBSD__
int jailed = 0;
size_t len = sizeof(jailed);
if (sysctlbyname("security.jail.jailed", &jailed, &len, NULL, 0) == 0)
if (jailed == 1)
return RC_SYS_JAIL;
#endif
#ifdef __linux__
if (exists("/proc/xen")) {
if (file_regex("/proc/xen/capabilities", "control_d"))
return RC_SYS_XEN0;
return RC_SYS_XENU;
} else if (file_regex("/proc/cpuinfo", "UML"))
return RC_SYS_UML;
else if (file_regex("/proc/self/status",
"(s_context|VxID):[[:space:]]*[1-9]"))
return RC_SYS_VSERVER;
else if (file_regex("/proc/self/status",
"envID:[[:space:]]*[1-9]"))
return RC_SYS_OPENVZ;
#endif
return NULL;
#endif /* PREFIX */
}
static const char *rc_parse_service_state(RC_SERVICE state)
{
int i;
for (i = 0; rc_service_state_names[i].name; i++) {
if (rc_service_state_names[i].state == state)
return rc_service_state_names[i].name;
}
return NULL;
}
bool rc_runlevel_starting(void)
{
return exists(RC_STARTING);
}
librc_hidden_def(rc_runlevel_starting)
bool rc_runlevel_stopping(void)
{
return exists(RC_STOPPING);
}
librc_hidden_def(rc_runlevel_stopping)
RC_STRINGLIST *rc_runlevel_list(void)
{
return ls_dir(RC_RUNLEVELDIR, LS_DIR);
}
2007-10-03 19:41:55 +05:30
librc_hidden_def(rc_runlevel_list)
char *rc_runlevel_get(void)
{
2007-04-11 18:14:47 +05:30
FILE *fp;
char *runlevel = NULL;
if ((fp = fopen(SOFTLEVEL, "r"))) {
runlevel = xmalloc(sizeof(char) * PATH_MAX);
if (fgets(runlevel, PATH_MAX, fp)) {
int i = strlen(runlevel) - 1;
2007-10-12 05:31:33 +05:30
if (runlevel[i] == '\n')
runlevel[i] = 0;
} else
*runlevel = '\0';
fclose(fp);
2007-04-11 18:14:47 +05:30
}
2007-10-12 05:31:33 +05:30
if (! runlevel || ! *runlevel) {
free(runlevel);
runlevel = xstrdup(RC_LEVEL_SYSINIT);
2007-10-12 05:31:33 +05:30
}
return runlevel;
}
2007-10-02 15:27:23 +05:30
librc_hidden_def(rc_runlevel_get)
bool rc_runlevel_set(const char *runlevel)
{
FILE *fp = fopen(SOFTLEVEL, "w");
2007-04-11 18:14:47 +05:30
if (! fp)
return false;
fprintf(fp, "%s", runlevel);
fclose(fp);
return true;
}
2007-10-02 15:27:23 +05:30
librc_hidden_def(rc_runlevel_set)
bool rc_runlevel_exists(const char *runlevel)
{
2007-04-11 18:14:47 +05:30
char *path;
2007-10-04 22:24:29 +05:30
struct stat buf;
bool retval = false;
2007-04-11 18:14:47 +05:30
if (! runlevel)
return false;
path = rc_strcatpaths(RC_RUNLEVELDIR, runlevel, (char *) NULL);
if (stat(path, &buf) == 0 && S_ISDIR(buf.st_mode))
2007-10-04 22:24:29 +05:30
retval = true;
free(path);
return retval;
}
librc_hidden_def(rc_runlevel_exists)
/* Resolve a service name to it's full path */
char *rc_service_resolve(const char *service)
{
2007-04-11 18:14:47 +05:30
char buffer[PATH_MAX];
char *file;
int r = 0;
2007-10-04 21:51:53 +05:30
struct stat buf;
2007-04-11 18:14:47 +05:30
if (! service)
return NULL;
2007-04-11 18:14:47 +05:30
if (service[0] == '/')
return xstrdup(service);
2007-04-11 18:14:47 +05:30
/* First check started services */
file = rc_strcatpaths(RC_SVCDIR, "started", service, (char *) NULL);
if (lstat(file, &buf) || ! S_ISLNK(buf.st_mode)) {
free(file);
file = rc_strcatpaths(RC_SVCDIR, "inactive", service, (char *) NULL);
if (lstat(file, &buf) || ! S_ISLNK(buf.st_mode)) {
free(file);
2007-04-11 18:14:47 +05:30
file = NULL;
}
}
memset(buffer, 0, sizeof(buffer));
/* Nope, so lets see if the user has written it */
#ifdef RC_LOCAL_INITDIR
snprintf(buffer, sizeof(buffer), RC_LOCAL_INITDIR "/%s", service);
if (stat(buffer, &buf) == 0)
return xstrdup(buffer);
#endif
2007-04-11 18:14:47 +05:30
if (file) {
r = readlink(file, buffer, sizeof(buffer));
free(file);
2007-04-11 18:14:47 +05:30
if (r > 0)
return xstrdup(buffer);
2007-04-11 18:14:47 +05:30
}
snprintf(buffer, sizeof(buffer), RC_INITDIR "/%s", service);
/* So we don't exist in /etc/init.d - check RC_PKG_INITDIR */
#ifdef RC_PKG_INITDIR
if (stat(buffer, &buf) != 0) {
snprintf(buffer, sizeof(buffer), RC_PKG_INITDIR "/%s", service);
if (stat(buffer, &buf) != 0)
return NULL;
}
#endif
return xstrdup(buffer);
}
librc_hidden_def(rc_service_resolve)
bool rc_service_exists(const char *service)
{
2007-04-11 18:14:47 +05:30
char *file;
2007-09-25 23:00:07 +05:30
bool retval = false;
2007-04-11 18:14:47 +05:30
int len;
2007-10-04 21:56:44 +05:30
struct stat buf;
2007-04-11 18:14:47 +05:30
if (! service)
return false;
2007-04-11 18:14:47 +05:30
len = strlen(service);
2007-04-11 18:14:47 +05:30
/* .sh files are not init scripts */
if (len > 2 && service[len - 3] == '.' &&
2008-01-11 21:21:40 +05:30
service[len - 2] == 's' &&
service[len - 1] == 'h')
return false;
2007-04-11 18:14:47 +05:30
if (! (file = rc_service_resolve(service)))
return false;
if (stat(file, &buf) == 0 && buf.st_mode & S_IXUGO)
2007-10-04 21:56:44 +05:30
retval = true;
free(file);
return retval;
}
librc_hidden_def(rc_service_exists)
2007-10-24 02:13:45 +05:30
#define OPTSTR ". '%s'; echo \"${opts}\""
RC_STRINGLIST *rc_service_extra_commands(const char *service)
{
char *svc;
2007-10-12 05:31:33 +05:30
char *cmd = NULL;
char *buffer = NULL;
RC_STRINGLIST *commands;
char *token;
char *p;
FILE *fp;
2008-01-22 15:59:15 +05:30
size_t l;
if (! (svc = rc_service_resolve(service)))
return NULL;
commands = rc_stringlist_new();
l = strlen(OPTSTR) + strlen(svc) + 1;
cmd = xmalloc(sizeof(char) * l);
snprintf(cmd, l, OPTSTR, svc);
free(svc);
if ((fp = popen(cmd, "r"))) {
p = buffer = rc_getline(fp);
while ((token = strsep(&p, " ")))
rc_stringlist_add(commands, token);
pclose(fp);
free(buffer);
}
free(cmd);
return commands;
}
librc_hidden_def(rc_service_extra_commands)
2007-10-24 02:13:45 +05:30
#define DESCSTR ". '%s'; echo \"${description%s%s}\""
char *rc_service_description(const char *service, const char *option)
{
char *svc;
char *cmd;
char *desc = NULL;
FILE *fp;
2008-01-22 15:59:15 +05:30
size_t l;
if (! (svc = rc_service_resolve(service)))
return NULL;
2007-07-11 00:44:37 +05:30
if (! option)
option = "";
l = strlen(DESCSTR) + strlen(svc) + strlen(option) + 2;
cmd = xmalloc(sizeof(char) * l);
snprintf(cmd, l, DESCSTR, svc, option ? "_" : "", option);
free(svc);
if ((fp = popen(cmd, "r"))) {
desc = rc_getline(fp);
pclose(fp);
}
free(cmd);
return desc;
}
librc_hidden_def(rc_service_description)
bool rc_service_in_runlevel(const char *service, const char *runlevel)
{
2007-04-11 18:14:47 +05:30
char *file;
2007-09-25 23:00:07 +05:30
bool retval;
2007-04-11 18:14:47 +05:30
if (! runlevel || ! service)
return false;
file = rc_strcatpaths(RC_RUNLEVELDIR, runlevel, basename_c(service),
(char *) NULL);
retval = exists(file);
free(file);
return retval;
}
librc_hidden_def(rc_service_in_runlevel)
bool rc_service_mark(const char *service, const RC_SERVICE state)
{
2007-04-11 18:14:47 +05:30
char *file;
int i = 0;
int skip_state = -1;
const char *base;
char *init = rc_service_resolve(service);
2007-04-11 18:14:47 +05:30
bool skip_wasinactive = false;
int s;
char *was;
RC_STRINGLIST *dirs;
RC_STRING *dir;
int serrno;
2007-04-11 18:14:47 +05:30
if (! init)
return false;
2007-04-11 18:14:47 +05:30
base = basename_c(service);
2007-04-11 18:14:47 +05:30
if (state != RC_SERVICE_STOPPED) {
if (! exists(init)) {
free(init);
return false;
2007-04-11 18:14:47 +05:30
}
file = rc_strcatpaths(RC_SVCDIR, rc_parse_service_state (state), base,
(char *) NULL);
if (exists(file))
unlink(file);
i = symlink(init, file);
if (i != 0) {
free(file);
free(init);
return false;
2007-04-11 18:14:47 +05:30
}
free(file);
2007-04-11 18:14:47 +05:30
skip_state = state;
}
if (state == RC_SERVICE_COLDPLUGGED || state == RC_SERVICE_FAILED) {
free(init);
return true;
2007-04-11 18:14:47 +05:30
}
/* Remove any old states now */
for (i = 0; rc_service_state_names[i].name; i++) {
s = rc_service_state_names[i].state;
if ((s != skip_state &&
2008-01-11 21:21:40 +05:30
s != RC_SERVICE_STOPPED &&
s != RC_SERVICE_COLDPLUGGED &&
s != RC_SERVICE_SCHEDULED) &&
(! skip_wasinactive || s != RC_SERVICE_WASINACTIVE))
2007-04-11 18:14:47 +05:30
{
file = rc_strcatpaths(RC_SVCDIR, rc_parse_service_state(s), base,
(char *) NULL);
if (exists(file)) {
if ((state == RC_SERVICE_STARTING ||
2008-01-11 21:21:40 +05:30
state == RC_SERVICE_STOPPING) &&
s == RC_SERVICE_INACTIVE)
2007-04-11 18:14:47 +05:30
{
was = rc_strcatpaths(RC_SVCDIR,
rc_parse_service_state(RC_SERVICE_WASINACTIVE),
base, (char *) NULL);
2007-04-11 18:14:47 +05:30
symlink(init, was);
2007-04-11 18:14:47 +05:30
skip_wasinactive = true;
free(was);
2007-04-11 18:14:47 +05:30
}
unlink(file);
2007-04-11 18:14:47 +05:30
}
free(file);
2007-04-11 18:14:47 +05:30
}
}
/* Remove the exclusive state if we're inactive */
if (state == RC_SERVICE_STARTED ||
2008-01-11 21:21:40 +05:30
state == RC_SERVICE_STOPPED ||
state == RC_SERVICE_INACTIVE)
2007-04-11 18:14:47 +05:30
{
file = rc_strcatpaths(RC_SVCDIR, "exclusive", base, (char *) NULL);
unlink(file);
free(file);
2007-04-11 18:14:47 +05:30
}
/* Remove any options and daemons the service may have stored */
if (state == RC_SERVICE_STOPPED) {
file = rc_strcatpaths(RC_SVCDIR, "options", base, (char *) NULL);
rm_dir(file, true);
free(file);
2007-04-11 18:14:47 +05:30
file = rc_strcatpaths(RC_SVCDIR, "daemons", base, (char *) NULL);
rm_dir(file, true);
free(file);
2007-04-11 18:14:47 +05:30
rc_service_schedule_clear(service);
2007-04-11 18:14:47 +05:30
}
/* These are final states, so remove us from scheduled */
if (state == RC_SERVICE_STARTED || state == RC_SERVICE_STOPPED) {
file = rc_strcatpaths(RC_SVCDIR, "scheduled", (char *) NULL);
dirs = ls_dir(file, 0);
2007-04-11 18:14:47 +05:30
TAILQ_FOREACH(dir, dirs, entries) {
was = rc_strcatpaths(file, dir->value, base, (char *) NULL);
unlink(was);
free(was);
2007-04-11 18:14:47 +05:30
/* Try and remove the dir - we don't care about errors */
was = rc_strcatpaths(file, dir->value, (char *) NULL);
2007-04-11 18:14:47 +05:30
serrno = errno;
rmdir(was);
2007-04-11 18:14:47 +05:30
errno = serrno;
free(was);
2007-04-11 18:14:47 +05:30
}
rc_stringlist_free(dirs);
2007-04-11 18:14:47 +05:30
}
free(init);
return true;
}
librc_hidden_def(rc_service_mark)
RC_SERVICE rc_service_state(const char *service)
{
int i;
int state = RC_SERVICE_STOPPED;
char *file;
RC_STRINGLIST *dirs;
RC_STRING *dir;
2007-04-11 18:14:47 +05:30
for (i = 0; rc_service_state_names[i].name; i++) {
file = rc_strcatpaths(RC_SVCDIR, rc_service_state_names[i].name,
basename_c(service), (char*) NULL);
if (exists(file)) {
if (rc_service_state_names[i].state <= 0x10)
state = rc_service_state_names[i].state;
else
state |= rc_service_state_names[i].state;
}
free(file);
}
if (state & RC_SERVICE_STOPPED) {
dirs = ls_dir(RC_SVCDIR "/scheduled", 0);
TAILQ_FOREACH (dir, dirs, entries) {
file = rc_strcatpaths(RC_SVCDIR, "scheduled",
dir->value,
service, (char *) NULL);
if (exists(file))
state |= RC_SERVICE_SCHEDULED;
free(file);
if (state & RC_SERVICE_SCHEDULED)
break;
}
rc_stringlist_free(dirs);
2007-04-11 18:14:47 +05:30
}
return state;
}
librc_hidden_def(rc_service_state)
char *rc_service_value_get(const char *service, const char *option)
{
2007-04-11 18:14:47 +05:30
FILE *fp;
char *line = NULL;
char *file = rc_strcatpaths(RC_SVCDIR, "options", service, option,
(char *) NULL);
2007-04-11 18:14:47 +05:30
if ((fp = fopen(file, "r"))) {
line = rc_getline(fp);
fclose(fp);
2007-04-11 18:14:47 +05:30
}
free(file);
return line;
}
librc_hidden_def(rc_service_value_get)
bool rc_service_value_set(const char *service, const char *option,
const char *value)
{
2007-04-11 18:14:47 +05:30
FILE *fp;
char *path = rc_strcatpaths(RC_SVCDIR, "options", service, (char *) NULL);
char *file = rc_strcatpaths(path, option, (char *) NULL);
2007-09-25 23:00:07 +05:30
bool retval = false;
2007-04-11 18:14:47 +05:30
if (mkdir(path, 0755) != 0 && errno != EEXIST) {
free(path);
free(file);
return false;
2007-04-11 18:14:47 +05:30
}
if ((fp = fopen(file, "w"))) {
2007-04-11 18:14:47 +05:30
if (value)
fprintf(fp, "%s", value);
fclose(fp);
2007-09-25 23:00:07 +05:30
retval = true;
2007-04-11 18:14:47 +05:30
}
free(path);
free(file);
return retval;
}
librc_hidden_def(rc_service_value_set)
static pid_t _exec_service(const char *service, const char *arg)
{
2007-04-11 18:14:47 +05:30
char *file;
char *fifo;
pid_t pid = -1;
sigset_t full;
sigset_t old;
struct sigaction sa;
2007-04-11 18:14:47 +05:30
file = rc_service_resolve(service);
if (! exists(file)) {
rc_service_mark(service, RC_SERVICE_STOPPED);
free(file);
return 0;
2007-04-11 18:14:47 +05:30
}
/* We create a fifo so that other services can wait until we complete */
fifo = rc_strcatpaths(RC_SVCDIR, "exclusive", basename_c(service),
(char *) NULL);
2007-04-11 18:14:47 +05:30
if (mkfifo(fifo, 0600) != 0 && errno != EEXIST) {
free(fifo);
free(file);
return -1;
2007-04-11 18:14:47 +05:30
}
/* We need to block signals until we have forked */
memset(&sa, 0, sizeof (sa));
sa.sa_handler = SIG_DFL;
sigemptyset(&sa.sa_mask);
sigfillset(&full);
sigprocmask(SIG_SETMASK, &full, &old);
if ((pid = fork()) == 0) {
/* Restore default handlers */
sigaction(SIGCHLD, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
sigaction(SIGWINCH, &sa, NULL);
/* Unmask signals */
sigprocmask(SIG_SETMASK, &old, NULL);
/* Safe to run now */
execl(file, file, arg, (char *) NULL);
fprintf(stderr, "unable to exec `%s': %s\n",
file, strerror(errno));
unlink(fifo);
_exit(EXIT_FAILURE);
2007-04-11 18:14:47 +05:30
}
2008-02-03 03:47:44 +05:30
if (pid == -1)
fprintf(stderr, "fork: %s\n",strerror (errno));
2008-02-03 03:47:44 +05:30
sigprocmask(SIG_SETMASK, &old, NULL);
free(fifo);
free(file);
2007-04-11 18:14:47 +05:30
return pid;
}
2007-04-11 18:14:47 +05:30
pid_t rc_service_stop(const char *service)
{
RC_SERVICE state = rc_service_state(service);
if (state & RC_SERVICE_FAILED)
return -1;
if (state & RC_SERVICE_STOPPED)
return 0;
return _exec_service(service, "stop");
}
librc_hidden_def(rc_service_stop)
pid_t rc_service_start(const char *service)
{
RC_SERVICE state = rc_service_state(service);
if (state & RC_SERVICE_FAILED)
return -1;
if (! state & RC_SERVICE_STOPPED)
return 0;
return _exec_service(service, "start");
}
librc_hidden_def(rc_service_start)
bool rc_service_schedule_start(const char *service,
const char *service_to_start)
{
2007-04-11 18:14:47 +05:30
char *dir;
char *init;
char *file;
bool retval;
2007-04-11 18:14:47 +05:30
/* service may be a provided service, like net */
if (! service || ! rc_service_exists(service_to_start))
return false;
2007-04-11 18:14:47 +05:30
dir = rc_strcatpaths(RC_SVCDIR, "scheduled", basename_c(service),
(char *) NULL);
if (mkdir(dir, 0755) != 0 && errno != EEXIST) {
free(dir);
return false;
2007-10-04 22:24:29 +05:30
}
2007-04-11 18:14:47 +05:30
init = rc_service_resolve(service_to_start);
file = rc_strcatpaths(dir, basename_c(service_to_start), (char *) NULL);
retval = (exists(file) || symlink(init, file) == 0);
free(init);
free(file);
free(dir);
return retval;
}
librc_hidden_def(rc_service_schedule_start)
bool rc_service_schedule_clear(const char *service)
{
char *dir = rc_strcatpaths(RC_SVCDIR, "scheduled", basename_c(service),
(char *) NULL);
bool retval;
if (! (retval = rm_dir(dir, true)) && errno == ENOENT)
retval = true;
free(dir);
return retval;
}
librc_hidden_def(rc_service_schedule_clear)
RC_STRINGLIST *rc_services_in_runlevel(const char *runlevel)
{
2007-04-11 18:14:47 +05:30
char *dir;
RC_STRINGLIST *list;
if (! runlevel) {
#ifdef RC_PKG_INITDIR
RC_STRINGLIST *pkg = ls_dir(RC_PKG_INITDIR, LS_INITD);
#endif
#ifdef RC_LOCAL_INITDIR
RC_STRINGLIST *local = ls_dir(RC_LOCAL_INITDIR, LS_INITD);
#endif
list = ls_dir(RC_INITDIR, LS_INITD);
#ifdef RC_PKG_INITDIR
if (pkg) {
TAILQ_CONCAT(list, pkg, entries);
free(pkg);
}
#endif
#ifdef RC_LOCAL_DIR
if (local) {
TAILQ_CONCAT(list, local, entries);
free(local);
}
#endif
return list;
}
2007-04-11 18:14:47 +05:30
/* These special levels never contain any services */
if (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0 ||
strcmp(runlevel, RC_LEVEL_SINGLE) == 0) {
list = rc_stringlist_new();
return list;
}
dir = rc_strcatpaths(RC_RUNLEVELDIR, runlevel, (char *) NULL);
list = ls_dir(dir, LS_INITD);
free(dir);
return list;
}
librc_hidden_def(rc_services_in_runlevel)
RC_STRINGLIST *rc_services_in_state(RC_SERVICE state)
{
char *dir = rc_strcatpaths(RC_SVCDIR, rc_parse_service_state(state),
(char *) NULL);
RC_STRINGLIST *services;
RC_STRINGLIST *list;
RC_STRINGLIST *dirs;
RC_STRING *d;
char *p;
2007-04-11 18:14:47 +05:30
if (state == RC_SERVICE_SCHEDULED) {
dirs = ls_dir(dir, 0);
list = rc_stringlist_new();
TAILQ_FOREACH(d, dirs, entries) {
p = rc_strcatpaths(dir, d->value, (char *) NULL);
services = ls_dir(p, LS_INITD);
free(p);
TAILQ_CONCAT(list, services, entries);
free(services);
2007-04-11 18:14:47 +05:30
}
rc_stringlist_free(dirs);
2007-04-11 18:14:47 +05:30
} else {
list = ls_dir(dir, LS_INITD);
2007-04-11 18:14:47 +05:30
}
free(dir);
return list;
}
librc_hidden_def(rc_services_in_state)
bool rc_service_add(const char *runlevel, const char *service)
{
2007-09-25 23:00:07 +05:30
bool retval;
2007-04-11 18:14:47 +05:30
char *init;
char *file;
char path[MAXPATHLEN] = { '\0' };
char *p;
2007-04-11 18:14:47 +05:30
if (! rc_runlevel_exists(runlevel)) {
2007-04-11 18:14:47 +05:30
errno = ENOENT;
return false;
2007-04-11 18:14:47 +05:30
}
if (rc_service_in_runlevel(service, runlevel)) {
2007-04-11 18:14:47 +05:30
errno = EEXIST;
return false;
2007-04-11 18:14:47 +05:30
}
init = rc_service_resolve(service);
/* We need to ensure that only things in /etc/init.d are added
* to the boot runlevel */
if (strcmp (runlevel, RC_LEVEL_BOOT) == 0) {
p = realpath(dirname (init), path);
free(init);
if (! *p)
return false;
retval = (strcmp(path, RC_INITDIR) == 0);
if (! retval) {
errno = EPERM;
return false;
}
init = rc_strcatpaths(RC_INITDIR, service, (char *) NULL);
}
file = rc_strcatpaths(RC_RUNLEVELDIR, runlevel, basename_c(service),
(char *) NULL);
retval = (symlink(init, file) == 0);
free(init);
free(file);
return retval;
}
librc_hidden_def(rc_service_add)
2007-09-25 23:00:07 +05:30
bool rc_service_delete (const char *runlevel, const char *service)
{
2007-04-11 18:14:47 +05:30
char *file;
2007-09-25 23:00:07 +05:30
bool retval = false;
2007-04-11 18:14:47 +05:30
if (! runlevel || ! service)
return false;
2007-04-10 16:54:58 +05:30
file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename_c(service),
2008-01-11 21:21:40 +05:30
(char *) NULL);
if (unlink(file) == 0)
2007-09-25 23:00:07 +05:30
retval = true;
free(file);
return retval;
}
librc_hidden_def(rc_service_delete)
RC_STRINGLIST *rc_services_scheduled_by(const char *service)
{
RC_STRINGLIST *dirs = ls_dir(RC_SVCDIR "/scheduled", 0);
RC_STRINGLIST *list;
RC_STRING *dir;
char *file;
2007-04-11 18:14:47 +05:30
list = rc_stringlist_new();
TAILQ_FOREACH (dir, dirs, entries) {
file = rc_strcatpaths(RC_SVCDIR, "scheduled", dir->value,
service, (char *) NULL);
if (exists(file))
rc_stringlist_add(list, file);
free(file);
2007-04-11 18:14:47 +05:30
}
rc_stringlist_free(dirs);
2007-04-11 18:14:47 +05:30
return list;
}
librc_hidden_def(rc_services_scheduled_by)
RC_STRINGLIST *rc_services_scheduled(const char *service)
{
char *dir = rc_strcatpaths(RC_SVCDIR, "scheduled", basename_c(service),
(char *) NULL);
RC_STRINGLIST *list = ls_dir(dir, LS_INITD);
free(dir);
return list;
}
librc_hidden_def(rc_services_scheduled)