Punt the rc_strcatpaths function and use snprintf instead to save on expensive malloc calls.

This commit is contained in:
Roy Marples 2008-03-17 13:25:56 +00:00
parent 50a7697bf2
commit 4c14666423
14 changed files with 682 additions and 745 deletions

View File

@ -1,6 +1,6 @@
MAN3= einfo.3 \ MAN3= einfo.3 \
rc_config.3 rc_deptree.3 rc_find_pids.3 rc_plugin_hook.3 \ rc_config.3 rc_deptree.3 rc_find_pids.3 rc_plugin_hook.3 \
rc_runlevel.3 rc_service.3 rc_strcatpaths.3 rc_stringlist.3 rc_runlevel.3 rc_service.3 rc_stringlist.3
MAN8= rc-status.8 rc-update.8 rc.8 runscript.8 start-stop-daemon.8 MAN8= rc-status.8 rc-update.8 rc.8 runscript.8 start-stop-daemon.8
# Handy macro to create symlinks # Handy macro to create symlinks

View File

@ -1,45 +0,0 @@
.\" 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.
.\"
.Dd Feb 22, 2008
.Dt RC_STRCATPATHS 3 SMM
.Os OpenRC
.Sh NAME
.Nm rc_strcatpaths
.Nd concatenates each elements, seperating each with / as necessary
.Sh LIBRARY
Run Command library (librc, -lrc)
.Sh SYNOPSIS
.In rc.h
.Ft "char *" Fn rc_strcatpaths "const char *path" ...
.Sh DESCRIPTION
.Fn rc_strcatpaths concatenates each
.Fa path ,
seperating each with / as necessary and returns a malloced pointer
to the result. This should be freed when done.
.Sh SEE ALSO
.Xr free 3 ,
.Xr malloc 3
.Sh AUTHORS
.An "Roy Marples" Aq roy@marples.name

View File

@ -31,6 +31,30 @@
#include "librc.h" #include "librc.h"
#ifdef __GLIBC__
# if ! defined (__UCLIBC__) && ! defined (__dietlibc__)
static size_t strlcpy(char *dst, const char *src, size_t size)
{
const char *s = src;
size_t n = size;
if (n && --n)
do {
if (! (*dst++ = *src++))
break;
} while (--n);
if (! n) {
if (size)
*dst = '\0';
while (*src++);
}
return src - s - 1;
}
# endif
#endif
#if defined(__linux__) #if defined(__linux__)
static bool pid_is_cmd(pid_t pid, const char *cmd) static bool pid_is_cmd(pid_t pid, const char *cmd)
{ {
@ -289,12 +313,12 @@ static bool _match_daemon(const char *path, const char *file,
RC_STRINGLIST *match) RC_STRINGLIST *match)
{ {
char *line; char *line;
char *ffile = rc_strcatpaths(path, file, (char *) NULL); char ffile[PATH_MAX];
FILE *fp; FILE *fp;
RC_STRING *m; RC_STRING *m;
snprintf(ffile, sizeof(ffile), "%s/%s", path, file);
fp = fopen(ffile, "r"); fp = fopen(ffile, "r");
free(ffile);
if (! fp) if (! fp)
return false; return false;
@ -352,16 +376,15 @@ static RC_STRINGLIST *_match_list(const char* const* argv,
bool rc_service_daemon_set(const char *service, const char *const *argv, bool rc_service_daemon_set(const char *service, const char *const *argv,
const char *name, const char *pidfile, bool started) const char *name, const char *pidfile, bool started)
{ {
char *dirpath; char dirpath[PATH_MAX];
char *file = NULL; char file[PATH_MAX];
int nfiles = 0; int nfiles = 0;
char *oldfile = NULL; char oldfile[PATH_MAX] = { '\0' };
bool retval = false; bool retval = false;
DIR *dp; DIR *dp;
struct dirent *d; struct dirent *d;
RC_STRINGLIST *match; RC_STRINGLIST *match;
int i = 0; int i = 0;
char buffer[10];
FILE *fp; FILE *fp;
if (!(argv && *argv) && ! name && ! pidfile) { if (!(argv && *argv) && ! name && ! pidfile) {
@ -369,40 +392,40 @@ bool rc_service_daemon_set(const char *service, const char *const *argv,
return false; return false;
} }
dirpath = rc_strcatpaths(RC_SVCDIR, "daemons", snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s",
basename_c(service), (char *) NULL); basename_c(service));
match = _match_list(argv, name, pidfile);
/* Regardless, erase any existing daemon info */ /* Regardless, erase any existing daemon info */
if ((dp = opendir(dirpath))) { if ((dp = opendir(dirpath))) {
match = _match_list(argv, name, pidfile);
while ((d = readdir(dp))) { while ((d = readdir(dp))) {
if (d->d_name[0] == '.') if (d->d_name[0] == '.')
continue; continue;
file = rc_strcatpaths(dirpath, d->d_name, (char *) NULL);
snprintf(file, sizeof(file), "%s/%s",
dirpath, d->d_name);
nfiles++; nfiles++;
if (! oldfile) { if (! *oldfile) {
if (_match_daemon(dirpath, d->d_name, match)) { if (_match_daemon(dirpath, d->d_name, match)) {
unlink (file); unlink(file);
oldfile = file; strlcpy(oldfile, file, sizeof(oldfile));
nfiles--; nfiles--;
} }
} else { } else {
rename(file, oldfile); rename(file, oldfile);
free(oldfile); strlcpy(oldfile, file, sizeof(oldfile));
oldfile = file;
} }
} }
free(file);
closedir(dp); closedir(dp);
rc_stringlist_free(match);
} }
/* Now store our daemon info */ /* Now store our daemon info */
if (started) { if (started) {
if (mkdir(dirpath, 0755) == 0 || errno == EEXIST) { if (mkdir(dirpath, 0755) == 0 || errno == EEXIST) {
snprintf(buffer, sizeof(buffer), "%03d", nfiles + 1); snprintf(file, sizeof(file), "%s/%03d",
file = rc_strcatpaths(dirpath, buffer, (char *) NULL); dirpath, nfiles + 1);
if ((fp = fopen(file, "w"))) { if ((fp = fopen(file, "w"))) {
while (argv && argv[i]) { while (argv && argv[i]) {
fprintf(fp, "argv_%d=%s\n", i, argv[i]); fprintf(fp, "argv_%d=%s\n", i, argv[i]);
@ -418,25 +441,19 @@ bool rc_service_daemon_set(const char *service, const char *const *argv,
fclose(fp); fclose(fp);
retval = true; retval = true;
} }
free(file);
} }
} else } else
retval = true; retval = true;
rc_stringlist_free(match);
free(dirpath);
return retval; return retval;
} }
librc_hidden_def(rc_service_daemon_set) librc_hidden_def(rc_service_daemon_set)
bool bool rc_service_started_daemon(const char *service, const char *const *argv,
rc_service_started_daemon (const char *service, const char *const *argv, int indx)
int indx)
{ {
char *dirpath; char dirpath[PATH_MAX];
char *file; char file[16];
size_t l;
RC_STRINGLIST *match; RC_STRINGLIST *match;
bool retval = false; bool retval = false;
DIR *dp; DIR *dp;
@ -445,17 +462,13 @@ rc_service_started_daemon (const char *service, const char *const *argv,
if (!service || !(argv && *argv)) if (!service || !(argv && *argv))
return false; return false;
dirpath = rc_strcatpaths(RC_SVCDIR, "daemons", basename_c(service), snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s",
(char *) NULL); basename_c(service));
match = _match_list(argv, NULL, NULL); match = _match_list(argv, NULL, NULL);
if (indx > 0) { if (indx > 0) {
l = sizeof (char) * 10; snprintf(file, sizeof(file), "%03d", indx);
file = xmalloc(l);
snprintf(file, l, "%03d", indx);
retval = _match_daemon(dirpath, file, match); retval = _match_daemon(dirpath, file, match);
free(file);
} else { } else {
if ((dp = opendir(dirpath))) { if ((dp = opendir(dirpath))) {
while ((d = readdir(dp))) { while ((d = readdir(dp))) {
@ -469,7 +482,6 @@ rc_service_started_daemon (const char *service, const char *const *argv,
} }
} }
free(dirpath);
rc_stringlist_free(match); rc_stringlist_free(match);
return retval; return retval;
} }
@ -477,10 +489,10 @@ librc_hidden_def(rc_service_started_daemon)
bool rc_service_daemons_crashed(const char *service) bool rc_service_daemons_crashed(const char *service)
{ {
char *dirpath; char dirpath[PATH_MAX];
DIR *dp; DIR *dp;
struct dirent *d; struct dirent *d;
char *path; char *path = dirpath;
FILE *fp; FILE *fp;
char *line; char *line;
char **argv = NULL; char **argv = NULL;
@ -498,21 +510,19 @@ bool rc_service_daemons_crashed(const char *service)
RC_STRING *s; RC_STRING *s;
size_t i; size_t i;
dirpath = rc_strcatpaths(RC_SVCDIR, "daemons", basename_c(service), path += snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s",
(char *) NULL); basename_c(service));
if (! (dp = opendir(dirpath))) { if (! (dp = opendir(dirpath)))
free(dirpath);
return false; return false;
}
while ((d = readdir(dp))) { while ((d = readdir(dp))) {
if (d->d_name[0] == '.') if (d->d_name[0] == '.')
continue; continue;
path = rc_strcatpaths(dirpath, d->d_name, (char *) NULL); snprintf(path, sizeof(dirpath) - (path - dirpath), "/%s",
fp = fopen(path, "r"); d->d_name);
free(path); fp = fopen(dirpath, "r");
if (! fp) if (! fp)
break; break;

View File

@ -360,12 +360,12 @@ static RC_STRINGLIST *get_provided (const RC_DEPINFO *depinfo,
return providers; return providers;
} }
static void visit_service (const RC_DEPTREE *deptree, static void visit_service(const RC_DEPTREE *deptree,
const RC_STRINGLIST *types, const RC_STRINGLIST *types,
RC_STRINGLIST *sorted, RC_STRINGLIST **sorted,
RC_STRINGLIST *visited, RC_STRINGLIST *visited,
const RC_DEPINFO *depinfo, const RC_DEPINFO *depinfo,
const char *runlevel, int options) const char *runlevel, int options)
{ {
RC_STRING *type; RC_STRING *type;
RC_STRING *service; RC_STRING *service;
@ -392,7 +392,9 @@ static void visit_service (const RC_DEPTREE *deptree,
if (! options & RC_DEP_TRACE || if (! options & RC_DEP_TRACE ||
strcmp(type->value, "iprovide") == 0) strcmp(type->value, "iprovide") == 0)
{ {
rc_stringlist_add(sorted, service->value); if (! *sorted)
*sorted = rc_stringlist_new();
rc_stringlist_add(*sorted, service->value);
continue; continue;
} }
@ -445,8 +447,11 @@ static void visit_service (const RC_DEPTREE *deptree,
are also the service calling us or we are provided by something */ are also the service calling us or we are provided by something */
svcname = getenv("SVCNAME"); svcname = getenv("SVCNAME");
if (! svcname || strcmp(svcname, depinfo->service) != 0) if (! svcname || strcmp(svcname, depinfo->service) != 0)
if (! get_deptype(depinfo, "providedby")) if (! get_deptype(depinfo, "providedby")) {
rc_stringlist_add(sorted, depinfo->service); if (! *sorted)
*sorted = rc_stringlist_new();
rc_stringlist_add(*sorted, depinfo->service);
}
} }
RC_STRINGLIST *rc_deptree_depend(const RC_DEPTREE *deptree, RC_STRINGLIST *rc_deptree_depend(const RC_DEPTREE *deptree,
@ -478,7 +483,7 @@ RC_STRINGLIST *rc_deptree_depends (const RC_DEPTREE *deptree,
const RC_STRINGLIST *services, const RC_STRINGLIST *services,
const char *runlevel, int options) const char *runlevel, int options)
{ {
RC_STRINGLIST *sorted = rc_stringlist_new(); RC_STRINGLIST *sorted = NULL;
RC_STRINGLIST *visited = rc_stringlist_new(); RC_STRINGLIST *visited = rc_stringlist_new();
RC_DEPINFO *di; RC_DEPINFO *di;
const RC_STRING *service; const RC_STRING *service;
@ -493,11 +498,11 @@ RC_STRINGLIST *rc_deptree_depends (const RC_DEPTREE *deptree,
continue; continue;
} }
if (types) if (types)
visit_service (deptree, types, sorted, visited, visit_service(deptree, types, &sorted, visited,
di, runlevel, options); di, runlevel, options);
} }
rc_stringlist_free (visited); rc_stringlist_free(visited);
return sorted; return sorted;
} }
librc_hidden_def(rc_deptree_depends) librc_hidden_def(rc_deptree_depends)
@ -522,12 +527,23 @@ RC_STRINGLIST *rc_deptree_order(const RC_DEPTREE *deptree,
list = rc_services_in_state(RC_SERVICE_STARTED); list = rc_services_in_state(RC_SERVICE_STARTED);
list2 = rc_services_in_state (RC_SERVICE_INACTIVE); list2 = rc_services_in_state (RC_SERVICE_INACTIVE);
TAILQ_CONCAT(list, list2, entries); if (list2) {
free(list2); if (list) {
TAILQ_CONCAT(list, list2, entries);
free(list2);
} else
list = list2;
}
list2 = rc_services_in_state (RC_SERVICE_STARTING); list2 = rc_services_in_state (RC_SERVICE_STARTING);
if (list2) {
if (list) {
TAILQ_CONCAT(list, list2, entries);
free(list2);
} else
list = list2;
}
TAILQ_CONCAT(list, list2, entries); TAILQ_CONCAT(list, list2, entries);
free(list2);
} else { } else {
list = rc_services_in_runlevel (runlevel); list = rc_services_in_runlevel (runlevel);
@ -567,7 +583,7 @@ bool rc_newer_than(const char *source, const char *target)
bool newer = true; bool newer = true;
DIR *dp; DIR *dp;
struct dirent *d; struct dirent *d;
char *path; char path[PATH_MAX];
int serrno = errno; int serrno = errno;
/* We have to exist */ /* We have to exist */
@ -594,9 +610,8 @@ bool rc_newer_than(const char *source, const char *target)
if (d->d_name[0] == '.') if (d->d_name[0] == '.')
continue; continue;
path = rc_strcatpaths(target, d->d_name, (char *) NULL); snprintf(path, sizeof(path), "%s/%s", target, d->d_name);
newer = rc_newer_than(source, path); newer = rc_newer_than(source, path);
free(path);
if (! newer) if (! newer)
break; break;
} }
@ -671,14 +686,16 @@ bool rc_deptree_update_needed(void)
/* Some init scripts dependencies change depending on config files /* Some init scripts dependencies change depending on config files
* outside of baselayout, like syslog-ng, so we check those too. */ * outside of baselayout, like syslog-ng, so we check those too. */
config = rc_config_list (RC_DEPCONFIG); config = rc_config_list(RC_DEPCONFIG);
TAILQ_FOREACH(s, config, entries) { if (config) {
if (! rc_newer_than(RC_DEPTREE_CACHE, s->value)) { TAILQ_FOREACH(s, config, entries) {
newer = true; if (! rc_newer_than(RC_DEPTREE_CACHE, s->value)) {
break; newer = true;
break;
}
} }
rc_stringlist_free(config);
} }
rc_stringlist_free(config);
return newer; return newer;
} }

View File

@ -54,57 +54,6 @@ bool rc_yesno (const char *value)
} }
librc_hidden_def(rc_yesno) librc_hidden_def(rc_yesno)
char *rc_strcatpaths (const char *path1, const char *paths, ...)
{
va_list ap;
size_t length;
size_t i;
char *p;
char *path;
char *pathp;
if (! path1 || ! paths)
return NULL;
length = strlen (path1) + strlen (paths) + 1;
if (*paths != '/')
length ++;
va_start (ap, paths);
while ((p = va_arg (ap, char *)) != NULL) {
if (*p != '/')
length ++;
length += strlen (p);
}
va_end (ap);
pathp = path = xmalloc (length * sizeof (char));
memset (path, 0, length);
i = strlen (path1);
memcpy (path, path1, i);
pathp += i;
if (*paths != '/')
*pathp ++ = '/';
i = strlen (paths);
memcpy (pathp, paths, i);
pathp += i;
va_start (ap, paths);
while ((p = va_arg (ap, char *)) != NULL) {
if (*p != '/')
*pathp ++= '/';
i = strlen (p);
memcpy (pathp, p, i);
pathp += i;
}
va_end (ap);
*pathp++ = 0;
return path;
}
librc_hidden_def(rc_strcatpaths)
char *rc_getline (FILE *fp) char *rc_getline (FILE *fp)
{ {
char *line = NULL; char *line = NULL;
@ -138,13 +87,11 @@ RC_STRINGLIST *rc_config_list(const char *file)
char *buffer; char *buffer;
char *p; char *p;
char *token; char *token;
RC_STRINGLIST *list; RC_STRINGLIST *list = NULL;
if (!(fp = fopen(file, "r"))) if (!(fp = fopen(file, "r")))
return NULL; return NULL;
list = rc_stringlist_new();
while ((p = buffer = rc_getline(fp))) { while ((p = buffer = rc_getline(fp))) {
/* Strip leading spaces/tabs */ /* Strip leading spaces/tabs */
while ((*p == ' ') || (*p == '\t')) while ((*p == ' ') || (*p == '\t'))
@ -159,6 +106,8 @@ RC_STRINGLIST *rc_config_list(const char *file)
if (token[strlen(token) - 1] == '\n') if (token[strlen(token) - 1] == '\n')
token[strlen(token) - 1] = 0; token[strlen(token) - 1] = 0;
if (! list)
list = rc_stringlist_new();
rc_stringlist_add(list, token); rc_stringlist_add(list, token);
} }
} }
@ -172,8 +121,8 @@ librc_hidden_def(rc_config_list)
RC_STRINGLIST *rc_config_load(const char *file) RC_STRINGLIST *rc_config_load(const char *file)
{ {
RC_STRINGLIST *list = NULL; RC_STRINGLIST *list;
RC_STRINGLIST *config = NULL; RC_STRINGLIST *config;
char *token; char *token;
RC_STRING *line; RC_STRING *line;
RC_STRING *cline; RC_STRING *cline;
@ -183,9 +132,11 @@ RC_STRINGLIST *rc_config_load(const char *file)
char *newline; char *newline;
char *p; char *p;
config = rc_stringlist_new();
list = rc_config_list(file); list = rc_config_list(file);
if (! list)
return NULL;
config = rc_stringlist_new();
TAILQ_FOREACH(line, list, entries) { TAILQ_FOREACH(line, list, entries) {
/* Get entry */ /* Get entry */
p = line->value; p = line->value;

View File

@ -62,7 +62,7 @@ RC_STRING *rc_stringlist_addu (RC_STRINGLIST *list, const char *value)
} }
librc_hidden_def(rc_stringlist_addu) librc_hidden_def(rc_stringlist_addu)
bool rc_stringlist_delete (RC_STRINGLIST *list, const char *value) bool rc_stringlist_delete(RC_STRINGLIST *list, const char *value)
{ {
RC_STRING *s; RC_STRING *s;

View File

@ -72,25 +72,24 @@ static RC_STRINGLIST *ls_dir(const char *dir, int options)
{ {
DIR *dp; DIR *dp;
struct dirent *d; struct dirent *d;
RC_STRINGLIST *list; RC_STRINGLIST *list = NULL;
struct stat buf; struct stat buf;
size_t l; size_t l;
char *file; char file[PATH_MAX];
int r; int r;
if ((dp = opendir(dir)) == NULL) if ((dp = opendir(dir)) == NULL)
return NULL; return NULL;
list = rc_stringlist_new();
while (((d = readdir(dp)) != NULL)) { while (((d = readdir(dp)) != NULL)) {
if (d->d_name[0] != '.') { if (d->d_name[0] != '.') {
if (options & LS_INITD) { if (options & LS_INITD) {
/* Check that our file really exists. /* Check that our file really exists.
* This is important as a service maybe in a runlevel, but * This is important as a service maybe in a runlevel, but
* could also have been removed. */ * could also have been removed. */
file = rc_strcatpaths(dir, d->d_name, NULL); snprintf(file, sizeof(file), "%s/%s",
dir, d->d_name);
r = stat(file, &buf); r = stat(file, &buf);
free(file);
if (r != 0) if (r != 0)
continue; continue;
@ -106,6 +105,8 @@ static RC_STRINGLIST *ls_dir(const char *dir, int options)
! S_ISDIR(buf.st_mode)) ! S_ISDIR(buf.st_mode))
continue; continue;
} }
if (! list)
list = rc_stringlist_new();
rc_stringlist_add(list, d->d_name); rc_stringlist_add(list, d->d_name);
} }
} }
@ -118,7 +119,7 @@ static bool rm_dir(const char *pathname, bool top)
{ {
DIR *dp; DIR *dp;
struct dirent *d; struct dirent *d;
char *tmp = NULL; char file[PATH_MAX];
struct stat s; struct stat s;
bool retval = true; bool retval = true;
@ -128,22 +129,21 @@ static bool rm_dir(const char *pathname, bool top)
errno = 0; errno = 0;
while (((d = readdir(dp)) != NULL) && errno == 0) { while (((d = readdir(dp)) != NULL) && errno == 0) {
if (strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0) { if (strcmp(d->d_name, ".") != 0 && strcmp(d->d_name, "..") != 0) {
free(tmp); snprintf(file, sizeof(file), "%s/%s", pathname, d->d_name);
tmp = rc_strcatpaths(pathname, d->d_name, (char *) NULL);
if (stat(tmp, &s) != 0) { if (stat(file, &s) != 0) {
retval = false; retval = false;
break; break;
} }
if (S_ISDIR(s.st_mode)) { if (S_ISDIR(s.st_mode)) {
if (! rm_dir(tmp, true)) if (! rm_dir(file, true))
{ {
retval = false; retval = false;
break; break;
} }
} else { } else {
if (unlink(tmp)) { if (unlink(file)) {
retval = false; retval = false;
break; break;
} }
@ -151,7 +151,6 @@ static bool rm_dir(const char *pathname, bool top)
} }
} }
closedir(dp); closedir(dp);
free(tmp);
if (! retval) if (! retval)
return false; return false;
@ -302,18 +301,16 @@ librc_hidden_def(rc_runlevel_set)
bool rc_runlevel_exists(const char *runlevel) bool rc_runlevel_exists(const char *runlevel)
{ {
char *path; char path[PATH_MAX];
struct stat buf; struct stat buf;
bool retval = false;
if (! runlevel) if (! runlevel)
return false; return false;
path = rc_strcatpaths(RC_RUNLEVELDIR, runlevel, (char *) NULL); snprintf(path, sizeof(path), "%s/%s", RC_RUNLEVELDIR, runlevel);
if (stat(path, &buf) == 0 && S_ISDIR(buf.st_mode)) if (stat(path, &buf) == 0 && S_ISDIR(buf.st_mode))
retval = true; return true;
free(path); return false;
return retval;
} }
librc_hidden_def(rc_runlevel_exists) librc_hidden_def(rc_runlevel_exists)
@ -321,8 +318,8 @@ librc_hidden_def(rc_runlevel_exists)
char *rc_service_resolve(const char *service) char *rc_service_resolve(const char *service)
{ {
char buffer[PATH_MAX]; char buffer[PATH_MAX];
char *file; char file[PATH_MAX];
int r = 0; int r;
struct stat buf; struct stat buf;
if (! service) if (! service)
@ -332,43 +329,41 @@ char *rc_service_resolve(const char *service)
return xstrdup(service); return xstrdup(service);
/* First check started services */ /* First check started services */
file = rc_strcatpaths(RC_SVCDIR, "started", service, (char *) NULL); snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s", "started", service);
if (lstat(file, &buf) || ! S_ISLNK(buf.st_mode)) { if (lstat(file, &buf) || ! S_ISLNK(buf.st_mode)) {
free(file); snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
file = rc_strcatpaths(RC_SVCDIR, "inactive", service, (char *) NULL); "inactive", service);
if (lstat(file, &buf) || ! S_ISLNK(buf.st_mode)) { if (lstat(file, &buf) || ! S_ISLNK(buf.st_mode))
free(file); *file = '\0';
file = NULL;
}
} }
memset(buffer, 0, sizeof(buffer)); if (*file) {
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
if (file) {
r = readlink(file, buffer, sizeof(buffer)); r = readlink(file, buffer, sizeof(buffer));
free(file);
if (r > 0) if (r > 0)
return xstrdup(buffer); return xstrdup(buffer);
} }
snprintf(buffer, sizeof(buffer), RC_INITDIR "/%s", service);
/* So we don't exist in /etc/init.d - check RC_PKG_INITDIR */ #ifdef RC_LOCAL_INITDIR
#ifdef RC_PKG_INITDIR /* Nope, so lets see if the user has written it */
if (stat(buffer, &buf) != 0) { snprintf(file, sizeof(file), RC_LOCAL_INITDIR "/%s", service);
snprintf(buffer, sizeof(buffer), RC_PKG_INITDIR "/%s", service); if (stat(file, &buf) == 0)
if (stat(buffer, &buf) != 0) return xstrdup(file);
return NULL;
}
#endif #endif
return xstrdup(buffer); /* System scripts take precedence over 3rd party ones */
snprintf(file, sizeof(file), RC_INITDIR "/%s", service);
if (stat(file, &buf) == 0)
return xstrdup(file);
#ifdef RC_PKG_INITDIR
/* Check RC_PKG_INITDIR */
snprintf(file, sizeof(file), RC_PKG_INITDIR "/%s", service);
if (stat(file, &buf) == 0)
return xstrdup(file);
#endif
return NULL;
} }
librc_hidden_def(rc_service_resolve) librc_hidden_def(rc_service_resolve)
@ -406,7 +401,7 @@ RC_STRINGLIST *rc_service_extra_commands(const char *service)
char *svc; char *svc;
char *cmd = NULL; char *cmd = NULL;
char *buffer = NULL; char *buffer = NULL;
RC_STRINGLIST *commands; RC_STRINGLIST *commands = NULL;
char *token; char *token;
char *p; char *p;
FILE *fp; FILE *fp;
@ -415,7 +410,6 @@ RC_STRINGLIST *rc_service_extra_commands(const char *service)
if (! (svc = rc_service_resolve(service))) if (! (svc = rc_service_resolve(service)))
return NULL; return NULL;
commands = rc_stringlist_new();
l = strlen(OPTSTR) + strlen(svc) + 1; l = strlen(OPTSTR) + strlen(svc) + 1;
cmd = xmalloc(sizeof(char) * l); cmd = xmalloc(sizeof(char) * l);
@ -424,8 +418,11 @@ RC_STRINGLIST *rc_service_extra_commands(const char *service)
if ((fp = popen(cmd, "r"))) { if ((fp = popen(cmd, "r"))) {
p = buffer = rc_getline(fp); p = buffer = rc_getline(fp);
while ((token = strsep(&p, " "))) while ((token = strsep(&p, " "))) {
if (! commands)
commands = rc_stringlist_new();
rc_stringlist_add(commands, token); rc_stringlist_add(commands, token);
}
pclose(fp); pclose(fp);
free(buffer); free(buffer);
} }
@ -464,24 +461,17 @@ librc_hidden_def(rc_service_description)
bool rc_service_in_runlevel(const char *service, const char *runlevel) bool rc_service_in_runlevel(const char *service, const char *runlevel)
{ {
char *file; char file[PATH_MAX];
bool retval;
if (! runlevel || ! service) snprintf(file, sizeof(file), RC_RUNLEVELDIR "/%s/%s",
return false; runlevel, basename_c(service));
return exists(file);
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) librc_hidden_def(rc_service_in_runlevel)
bool rc_service_mark(const char *service, const RC_SERVICE state) bool rc_service_mark(const char *service, const RC_SERVICE state)
{ {
char *file; char file[PATH_MAX];
int i = 0; int i = 0;
int skip_state = -1; int skip_state = -1;
const char *base; const char *base;
@ -504,18 +494,16 @@ bool rc_service_mark(const char *service, const RC_SERVICE state)
return false; return false;
} }
file = rc_strcatpaths(RC_SVCDIR, rc_parse_service_state (state), base, snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
(char *) NULL); rc_parse_service_state (state), base);
if (exists(file)) if (exists(file))
unlink(file); unlink(file);
i = symlink(init, file); i = symlink(init, file);
if (i != 0) { if (i != 0) {
free(file);
free(init); free(init);
return false; return false;
} }
free(file);
skip_state = state; skip_state = state;
} }
@ -534,24 +522,22 @@ bool rc_service_mark(const char *service, const RC_SERVICE state)
s != RC_SERVICE_SCHEDULED) && s != RC_SERVICE_SCHEDULED) &&
(! skip_wasinactive || s != RC_SERVICE_WASINACTIVE)) (! skip_wasinactive || s != RC_SERVICE_WASINACTIVE))
{ {
file = rc_strcatpaths(RC_SVCDIR, rc_parse_service_state(s), base, snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
(char *) NULL); rc_parse_service_state(s), base);
if (exists(file)) { if (exists(file)) {
if ((state == RC_SERVICE_STARTING || if ((state == RC_SERVICE_STARTING ||
state == RC_SERVICE_STOPPING) && state == RC_SERVICE_STOPPING) &&
s == RC_SERVICE_INACTIVE) s == RC_SERVICE_INACTIVE)
{ {
was = rc_strcatpaths(RC_SVCDIR, snprintf(was, sizeof(was),
rc_parse_service_state(RC_SERVICE_WASINACTIVE), RC_SVCDIR "/%s/%s",
base, (char *) NULL); rc_parse_service_state(RC_SERVICE_WASINACTIVE),
base);
symlink(init, was); symlink(init, was);
skip_wasinactive = true; skip_wasinactive = true;
free(was);
} }
unlink(file); unlink(file);
} }
free(file);
} }
} }
@ -560,42 +546,43 @@ bool rc_service_mark(const char *service, const RC_SERVICE state)
state == RC_SERVICE_STOPPED || state == RC_SERVICE_STOPPED ||
state == RC_SERVICE_INACTIVE) state == RC_SERVICE_INACTIVE)
{ {
file = rc_strcatpaths(RC_SVCDIR, "exclusive", base, (char *) NULL); snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
"exclusive", base);
unlink(file); unlink(file);
free(file);
} }
/* Remove any options and daemons the service may have stored */ /* Remove any options and daemons the service may have stored */
if (state == RC_SERVICE_STOPPED) { if (state == RC_SERVICE_STOPPED) {
file = rc_strcatpaths(RC_SVCDIR, "options", base, (char *) NULL); snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
"options", base);
rm_dir(file, true); rm_dir(file, true);
free(file);
file = rc_strcatpaths(RC_SVCDIR, "daemons", base, (char *) NULL); snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
"daemons", base);
rm_dir(file, true); rm_dir(file, true);
free(file);
rc_service_schedule_clear(service); rc_service_schedule_clear(service);
} }
/* These are final states, so remove us from scheduled */ /* These are final states, so remove us from scheduled */
if (state == RC_SERVICE_STARTED || state == RC_SERVICE_STOPPED) { if (state == RC_SERVICE_STARTED || state == RC_SERVICE_STOPPED) {
file = rc_strcatpaths(RC_SVCDIR, "scheduled", (char *) NULL); snprintf(file, sizeof(file), RC_SVCDIR "/%s", "scheduled");
dirs = ls_dir(file, 0); dirs = ls_dir(file, 0);
if (dirs) {
TAILQ_FOREACH(dir, dirs, entries) {
snprintf(was, sizeof(was), "%s/%s/%s",
file, dir->value, base);
unlink(was);
TAILQ_FOREACH(dir, dirs, entries) { /* Try and remove the dir - we don't care about errors */
was = rc_strcatpaths(file, dir->value, base, (char *) NULL); snprintf(was, sizeof(was), "%s/%s",
unlink(was); file, dir->value);
free(was); serrno = errno;
rmdir(was);
/* Try and remove the dir - we don't care about errors */ errno = serrno;
was = rc_strcatpaths(file, dir->value, (char *) NULL); }
serrno = errno; rc_stringlist_free(dirs);
rmdir(was);
errno = serrno;
free(was);
} }
rc_stringlist_free(dirs);
} }
free(init); free(init);
@ -607,35 +594,36 @@ RC_SERVICE rc_service_state(const char *service)
{ {
int i; int i;
int state = RC_SERVICE_STOPPED; int state = RC_SERVICE_STOPPED;
char *file; char file[PATH_MAX];
RC_STRINGLIST *dirs; RC_STRINGLIST *dirs;
RC_STRING *dir; RC_STRING *dir;
const char *base = basename_c(service);
for (i = 0; rc_service_state_names[i].name; i++) { for (i = 0; rc_service_state_names[i].name; i++) {
file = rc_strcatpaths(RC_SVCDIR, rc_service_state_names[i].name, snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
basename_c(service), (char*) NULL); rc_service_state_names[i].name, base);
if (exists(file)) { if (exists(file)) {
if (rc_service_state_names[i].state <= 0x10) if (rc_service_state_names[i].state <= 0x10)
state = rc_service_state_names[i].state; state = rc_service_state_names[i].state;
else else
state |= rc_service_state_names[i].state; state |= rc_service_state_names[i].state;
} }
free(file);
} }
if (state & RC_SERVICE_STOPPED) { if (state & RC_SERVICE_STOPPED) {
dirs = ls_dir(RC_SVCDIR "/scheduled", 0); dirs = ls_dir(RC_SVCDIR "/scheduled", 0);
TAILQ_FOREACH (dir, dirs, entries) { if (dirs) {
file = rc_strcatpaths(RC_SVCDIR, "scheduled", TAILQ_FOREACH (dir, dirs, entries) {
dir->value, snprintf(file, sizeof(file),
service, (char *) NULL); RC_SVCDIR "/scheduled/%s/%s",
if (exists(file)) dir->value, service);
state |= RC_SERVICE_SCHEDULED; if (exists(file)) {
free(file); state |= RC_SERVICE_SCHEDULED;
if (state & RC_SERVICE_SCHEDULED) break;
break; }
}
rc_stringlist_free(dirs);
} }
rc_stringlist_free(dirs);
} }
return state; return state;
@ -646,14 +634,14 @@ char *rc_service_value_get(const char *service, const char *option)
{ {
FILE *fp; FILE *fp;
char *line = NULL; char *line = NULL;
char *file = rc_strcatpaths(RC_SVCDIR, "options", service, option, char file[PATH_MAX];
(char *) NULL);
snprintf(file, sizeof(file), RC_SVCDIR "/options/%s/%s",
service, option);
if ((fp = fopen(file, "r"))) { if ((fp = fopen(file, "r"))) {
line = rc_getline(fp); line = rc_getline(fp);
fclose(fp); fclose(fp);
} }
free(file);
return line; return line;
} }
@ -663,33 +651,27 @@ bool rc_service_value_set(const char *service, const char *option,
const char *value) const char *value)
{ {
FILE *fp; FILE *fp;
char *path = rc_strcatpaths(RC_SVCDIR, "options", service, (char *) NULL); char file[PATH_MAX];
char *file = rc_strcatpaths(path, option, (char *) NULL); char *p = file;
bool retval = false;
if (mkdir(path, 0755) != 0 && errno != EEXIST) { p += snprintf(file, sizeof(file), RC_SVCDIR "/options/%s", service);
free(path); if (mkdir(file, 0755) != 0 && errno != EEXIST)
free(file);
return false; return false;
}
if ((fp = fopen(file, "w"))) { snprintf(p, sizeof(file) - (p - file), "/%s", option);
if (value) if (!(fp = fopen(file, "w")))
fprintf(fp, "%s", value); return false;
fclose(fp); if (value)
retval = true; fprintf(fp, "%s", value);
} fclose(fp);
return true;
free(path);
free(file);
return retval;
} }
librc_hidden_def(rc_service_value_set) librc_hidden_def(rc_service_value_set)
static pid_t _exec_service(const char *service, const char *arg) static pid_t _exec_service(const char *service, const char *arg)
{ {
char *file; char *file;
char *fifo; char fifo[PATH_MAX];
pid_t pid = -1; pid_t pid = -1;
sigset_t full; sigset_t full;
sigset_t old; sigset_t old;
@ -703,11 +685,9 @@ static pid_t _exec_service(const char *service, const char *arg)
} }
/* We create a fifo so that other services can wait until we complete */ /* We create a fifo so that other services can wait until we complete */
fifo = rc_strcatpaths(RC_SVCDIR, "exclusive", basename_c(service), snprintf(fifo, sizeof(fifo), RC_SVCDIR "/exclusive/%s",
(char *) NULL); basename_c(service));
if (mkfifo(fifo, 0600) != 0 && errno != EEXIST) { if (mkfifo(fifo, 0600) != 0 && errno != EEXIST) {
free(fifo);
free(file); free(file);
return -1; return -1;
} }
@ -745,7 +725,6 @@ static pid_t _exec_service(const char *service, const char *arg)
sigprocmask(SIG_SETMASK, &old, NULL); sigprocmask(SIG_SETMASK, &old, NULL);
free(fifo);
free(file); free(file);
return pid; return pid;
@ -782,28 +761,24 @@ librc_hidden_def(rc_service_start)
bool rc_service_schedule_start(const char *service, bool rc_service_schedule_start(const char *service,
const char *service_to_start) const char *service_to_start)
{ {
char *dir; char file[PATH_MAX];
char *p = file;
char *init; char *init;
char *file;
bool retval; bool retval;
/* service may be a provided service, like net */ /* service may be a provided service, like net */
if (! service || ! rc_service_exists(service_to_start)) if (! service || ! rc_service_exists(service_to_start))
return false; return false;
dir = rc_strcatpaths(RC_SVCDIR, "scheduled", basename_c(service), p += snprintf(file, sizeof(file), RC_SVCDIR "/scheduled/%s",
(char *) NULL); basename_c(service));
if (mkdir(dir, 0755) != 0 && errno != EEXIST) { if (mkdir(file, 0755) != 0 && errno != EEXIST)
free(dir);
return false; return false;
}
init = rc_service_resolve(service_to_start); init = rc_service_resolve(service_to_start);
file = rc_strcatpaths(dir, basename_c(service_to_start), (char *) NULL); snprintf(p, sizeof(file) - (p - file), "/%s", basename_c(service_to_start));
retval = (exists(file) || symlink(init, file) == 0); retval = (exists(file) || symlink(init, file) == 0);
free(init); free(init);
free(file);
free(dir);
return retval; return retval;
} }
@ -811,20 +786,19 @@ librc_hidden_def(rc_service_schedule_start)
bool rc_service_schedule_clear(const char *service) bool rc_service_schedule_clear(const char *service)
{ {
char *dir = rc_strcatpaths(RC_SVCDIR, "scheduled", basename_c(service), char dir[PATH_MAX];
(char *) NULL);
bool retval;
if (! (retval = rm_dir(dir, true)) && errno == ENOENT) snprintf(dir, sizeof(dir), RC_SVCDIR "/scheduled/%s",
retval = true; basename_c(service));
free(dir); if (! rm_dir(dir, true) && errno == ENOENT)
return retval; return true;
return false;
} }
librc_hidden_def(rc_service_schedule_clear) librc_hidden_def(rc_service_schedule_clear)
RC_STRINGLIST *rc_services_in_runlevel(const char *runlevel) RC_STRINGLIST *rc_services_in_runlevel(const char *runlevel)
{ {
char *dir; char dir[PATH_MAX];
RC_STRINGLIST *list; RC_STRINGLIST *list;
if (! runlevel) { if (! runlevel) {
@ -855,43 +829,47 @@ RC_STRINGLIST *rc_services_in_runlevel(const char *runlevel)
/* These special levels never contain any services */ /* These special levels never contain any services */
if (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0 || if (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0 ||
strcmp(runlevel, RC_LEVEL_SINGLE) == 0) { strcmp(runlevel, RC_LEVEL_SINGLE) == 0) {
list = rc_stringlist_new(); return NULL;
return list;
} }
dir = rc_strcatpaths(RC_RUNLEVELDIR, runlevel, (char *) NULL); snprintf(dir, sizeof(dir), RC_RUNLEVELDIR "/%s", runlevel);
list = ls_dir(dir, LS_INITD); list = ls_dir(dir, LS_INITD);
free(dir);
return list; return list;
} }
librc_hidden_def(rc_services_in_runlevel) librc_hidden_def(rc_services_in_runlevel)
RC_STRINGLIST *rc_services_in_state(RC_SERVICE state) 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 *services;
RC_STRINGLIST *list; RC_STRINGLIST *list;
RC_STRINGLIST *dirs; RC_STRINGLIST *dirs;
RC_STRING *d; RC_STRING *d;
char *p; char dir[PATH_MAX];
char *p = dir;
if (state == RC_SERVICE_SCHEDULED) { p += snprintf(dir, sizeof(dir), RC_SVCDIR "/%s",
dirs = ls_dir(dir, 0); rc_parse_service_state(state));
list = rc_stringlist_new();
TAILQ_FOREACH(d, dirs, entries) { if (state != RC_SERVICE_SCHEDULED)
p = rc_strcatpaths(dir, d->value, (char *) NULL); return ls_dir(dir, LS_INITD);
services = ls_dir(p, LS_INITD);
free(p);
dirs = ls_dir(dir, 0);
if (! dirs)
return NULL;
TAILQ_FOREACH(d, dirs, entries) {
snprintf(p, sizeof(dir) - (p - dir), "/%s", d->value);
services = ls_dir(dir, LS_INITD);
if (! list)
services = list;
else if (services) {
TAILQ_CONCAT(list, services, entries); TAILQ_CONCAT(list, services, entries);
free(services); free(services);
} }
rc_stringlist_free(dirs);
} else {
list = ls_dir(dir, LS_INITD);
} }
rc_stringlist_free(dirs);
free(dir);
return list; return list;
} }
librc_hidden_def(rc_services_in_state) librc_hidden_def(rc_services_in_state)
@ -900,9 +878,11 @@ bool rc_service_add(const char *runlevel, const char *service)
{ {
bool retval; bool retval;
char *init; char *init;
char *file; char file[PATH_MAX];
char path[MAXPATHLEN] = { '\0' }; char path[MAXPATHLEN] = { '\0' };
char *p; char *p = NULL;
char binit[PATH_MAX];
char *i;
if (! rc_runlevel_exists(runlevel)) { if (! rc_runlevel_exists(runlevel)) {
errno = ENOENT; errno = ENOENT;
@ -914,13 +894,15 @@ bool rc_service_add(const char *runlevel, const char *service)
return false; return false;
} }
init = rc_service_resolve(service); i = init = rc_service_resolve(service);
snprintf(file, sizeof(file), RC_RUNLEVELDIR "/%s/%s",
runlevel, basename_c(service));
/* We need to ensure that only things in /etc/init.d are added /* We need to ensure that only things in /etc/init.d are added
* to the boot runlevel */ * to the boot runlevel */
if (strcmp (runlevel, RC_LEVEL_BOOT) == 0) { if (strcmp (runlevel, RC_LEVEL_BOOT) == 0) {
p = realpath(dirname (init), path);
free(init); free(init);
p = realpath(dirname (init), path);
if (! *p) if (! *p)
return false; return false;
@ -929,33 +911,25 @@ bool rc_service_add(const char *runlevel, const char *service)
errno = EPERM; errno = EPERM;
return false; return false;
} }
init = rc_strcatpaths(RC_INITDIR, service, (char *) NULL); snprintf(binit, sizeof(binit), RC_INITDIR "/%s", service);
i = binit;
} }
file = rc_strcatpaths(RC_RUNLEVELDIR, runlevel, basename_c(service), retval = (symlink(i, file) == 0);
(char *) NULL);
retval = (symlink(init, file) == 0);
free(init); free(init);
free(file);
return retval; return retval;
} }
librc_hidden_def(rc_service_add) librc_hidden_def(rc_service_add)
bool rc_service_delete (const char *runlevel, const char *service) bool rc_service_delete (const char *runlevel, const char *service)
{ {
char *file; char file[PATH_MAX];
bool retval = false;
if (! runlevel || ! service) snprintf(file, sizeof(file), RC_RUNLEVELDIR "/%s/%s",
return false; runlevel, basename_c(service));
file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename_c(service),
(char *) NULL);
if (unlink(file) == 0) if (unlink(file) == 0)
retval = true; return true;
return false;
free(file);
return retval;
} }
librc_hidden_def(rc_service_delete) librc_hidden_def(rc_service_delete)
@ -964,15 +938,19 @@ RC_STRINGLIST *rc_services_scheduled_by(const char *service)
RC_STRINGLIST *dirs = ls_dir(RC_SVCDIR "/scheduled", 0); RC_STRINGLIST *dirs = ls_dir(RC_SVCDIR "/scheduled", 0);
RC_STRINGLIST *list; RC_STRINGLIST *list;
RC_STRING *dir; RC_STRING *dir;
char *file; char file[PATH_MAX];
if (! dirs)
return NULL;
list = rc_stringlist_new();
TAILQ_FOREACH (dir, dirs, entries) { TAILQ_FOREACH (dir, dirs, entries) {
file = rc_strcatpaths(RC_SVCDIR, "scheduled", dir->value, snprintf(file, sizeof(file), RC_SVCDIR "/scheduled/%s/%s",
service, (char *) NULL); dir->value, service);
if (exists(file)) if (exists(file)) {
if (! list)
list = rc_stringlist_new();
rc_stringlist_add(list, file); rc_stringlist_add(list, file);
free(file); }
} }
rc_stringlist_free(dirs); rc_stringlist_free(dirs);
@ -982,11 +960,10 @@ librc_hidden_def(rc_services_scheduled_by)
RC_STRINGLIST *rc_services_scheduled(const char *service) RC_STRINGLIST *rc_services_scheduled(const char *service)
{ {
char *dir = rc_strcatpaths(RC_SVCDIR, "scheduled", basename_c(service), char dir[PATH_MAX];
(char *) NULL);
RC_STRINGLIST *list = ls_dir(dir, LS_INITD);
free(dir); snprintf(dir, sizeof(dir), "RC_SVCDIR/scheduled/%s",
return list; basename_c(service));
return ls_dir(dir, LS_INITD);
} }
librc_hidden_def(rc_services_scheduled) librc_hidden_def(rc_services_scheduled)

View File

@ -112,7 +112,6 @@ librc_hidden_proto(rc_service_started_daemon)
librc_hidden_proto(rc_service_state) librc_hidden_proto(rc_service_state)
librc_hidden_proto(rc_service_value_get) librc_hidden_proto(rc_service_value_get)
librc_hidden_proto(rc_service_value_set) librc_hidden_proto(rc_service_value_set)
librc_hidden_proto(rc_strcatpaths)
librc_hidden_proto(rc_stringlist_add) librc_hidden_proto(rc_stringlist_add)
librc_hidden_proto(rc_stringlist_addu) librc_hidden_proto(rc_stringlist_addu)
librc_hidden_proto(rc_stringlist_delete) librc_hidden_proto(rc_stringlist_delete)

View File

@ -27,17 +27,6 @@
#ifndef __RC_H__ #ifndef __RC_H__
#define __RC_H__ #define __RC_H__
#ifdef __GNUC__
# define GCC_VERSION (__GNUC__ * 1000 + __GNUC__MINOR)
# if (GCC_VERSION >= 3005)
# define SENTINEL __attribute__ ((__sentinel__))
# endif
# define DEPRECATED __attribute__ ((deprecated))
#endif
#ifndef SENTINEL
# define SENTINEL
#endif
#include <sys/types.h> #include <sys/types.h>
#include <sys/queue.h> #include <sys/queue.h>
#include <stdbool.h> #include <stdbool.h>
@ -444,13 +433,6 @@ void rc_stringlist_sort(RC_STRINGLIST **);
* @param list to free */ * @param list to free */
void rc_stringlist_free(RC_STRINGLIST *); void rc_stringlist_free(RC_STRINGLIST *);
/*! Concatenate paths adding '/' if needed. The resultant pointer should be
* freed when finished with.
* @param path1 starting path
* @param paths NULL terminated list of paths to add
* @return pointer to the new path */
char *rc_strcatpaths(const char *, const char *, ...) SENTINEL;
typedef struct rc_pid typedef struct rc_pid
{ {
pid_t pid; pid_t pid;

View File

@ -26,6 +26,7 @@ global:
rc_service_delete; rc_service_delete;
rc_service_description; rc_service_description;
rc_service_exists; rc_service_exists;
rc_service_extra_commands;
rc_service_in_runlevel; rc_service_in_runlevel;
rc_service_mark; rc_service_mark;
rc_service_options; rc_service_options;
@ -42,7 +43,6 @@ global:
rc_service_state; rc_service_state;
rc_service_value_get; rc_service_value_get;
rc_service_value_set; rc_service_value_set;
rc_strcatpaths;
rc_stringlist_add; rc_stringlist_add;
rc_stringlist_addu; rc_stringlist_addu;
rc_stringlist_delete; rc_stringlist_delete;

View File

@ -79,7 +79,7 @@ void rc_plugin_load(void)
DIR *dp; DIR *dp;
struct dirent *d; struct dirent *d;
PLUGIN *plugin; PLUGIN *plugin;
char *p; char file[PATH_MAX];
void *h; void *h;
int (*fptr)(RC_HOOK, const char *); int (*fptr)(RC_HOOK, const char *);
@ -96,9 +96,8 @@ void rc_plugin_load(void)
if (d->d_name[0] == '.') if (d->d_name[0] == '.')
continue; continue;
p = rc_strcatpaths(RC_PLUGINDIR, d->d_name, NULL); snprintf(file, sizeof(file), RC_PLUGINDIR "/%s", d->d_name);
h = dlopen(p, RTLD_LAZY); h = dlopen(file, RTLD_LAZY);
free(p);
if (! h) { if (! h) {
eerror("dlopen: %s", dlerror()); eerror("dlopen: %s", dlerror());
continue; continue;

View File

@ -633,6 +633,116 @@ static void do_coldplug(void)
printf ("%s\n", ecolor(ECOLOR_NORMAL)); printf ("%s\n", ecolor(ECOLOR_NORMAL));
} }
static void do_newlevel(const char *newlevel)
{
struct utsname uts;
const char *sys;
#ifdef __linux__
char *cmd;
#endif
if (strcmp(newlevel, RC_LEVEL_SYSINIT) == 0
#ifndef PREFIX
&& RUNLEVEL &&
(strcmp(RUNLEVEL, "S") == 0 ||
strcmp(RUNLEVEL, "1") == 0)
#endif
)
{
/* OK, we're either in runlevel 1 or single user mode */
/* exec init-early.sh if it exists
* This should just setup the console to use the correct
* font. Maybe it should setup the keyboard too? */
if (exists(INITEARLYSH))
run_script(INITEARLYSH);
uname(&uts);
printf("\n %sOpenRC %s" VERSION "%s is starting up %s",
ecolor(ECOLOR_GOOD), ecolor(ECOLOR_HILITE),
ecolor(ECOLOR_NORMAL), ecolor(ECOLOR_BRACKET));
#ifdef BRANDING
printf(BRANDING " (%s)", uts.machine);
#else
printf("%s %s (%s)",
uts.sysname,
uts.release,
uts.machine);
#endif
if ((sys = rc_sys()))
printf(" [%s]", sys);
printf("%s\n\n", ecolor(ECOLOR_NORMAL));
if (! rc_yesno(getenv ("EINFO_QUIET")) &&
rc_conf_yesno("rc_interactive"))
printf("Press %sI%s to enter interactive boot mode\n\n",
ecolor(ECOLOR_GOOD), ecolor(ECOLOR_NORMAL));
setenv("RC_SOFTLEVEL", newlevel, 1);
rc_plugin_run(RC_HOOK_RUNLEVEL_START_IN, newlevel);
hook_out = RC_HOOK_RUNLEVEL_START_OUT;
run_script(INITSH);
#ifdef __linux__
/* If we requested a softlevel, save it now */
set_ksoftlevel(NULL);
if ((cmd = proc_getent("softlevel"))) {
set_ksoftlevel(cmd);
free(cmd);
}
#endif
/* Setup our coldplugged services now */
do_coldplug();
rc_plugin_run(RC_HOOK_RUNLEVEL_START_OUT, newlevel);
hook_out = 0;
if (want_interactive())
mark_interactive();
exit(EXIT_SUCCESS);
} else if (strcmp(newlevel, RC_LEVEL_SINGLE) == 0) {
#ifndef PREFIX
if (! RUNLEVEL ||
(strcmp(RUNLEVEL, "S") != 0 &&
strcmp(RUNLEVEL, "1") != 0))
{
/* Remember the current runlevel for when we come back */
set_ksoftlevel(runlevel);
single_user();
}
#endif
} else if (strcmp(newlevel, RC_LEVEL_REBOOT) == 0) {
if (! RUNLEVEL ||
strcmp(RUNLEVEL, "6") != 0)
{
rc_logger_close();
execl(SHUTDOWN, SHUTDOWN, "-r", "now", (char *) NULL);
eerrorx("%s: unable to exec `" SHUTDOWN "': %s",
applet, strerror(errno));
}
} else if (strcmp(newlevel, RC_LEVEL_SHUTDOWN) == 0) {
if (! RUNLEVEL ||
strcmp(RUNLEVEL, "0") != 0)
{
rc_logger_close();
execl(SHUTDOWN, SHUTDOWN,
#ifdef __linux__
"-h",
#else
"-p",
#endif
"now", (char *) NULL);
eerrorx("%s: unable to exec `" SHUTDOWN "': %s",
applet, strerror(errno));
}
}
}
static bool runlevel_config(const char *service, const char *level) static bool runlevel_config(const char *service, const char *level)
{ {
char *init = rc_service_resolve(service); char *init = rc_service_resolve(service);
@ -651,6 +761,140 @@ static bool runlevel_config(const char *service, const char *level)
return retval; return retval;
} }
static void do_stop_services(const char *newlevel, bool going_down, bool parallel)
{
pid_t pid;
RC_STRING *service, *svc1, *svc2;
RC_STRINGLIST *deporder, *tmplist;
if (! types_n) {
types_n = rc_stringlist_new();
rc_stringlist_add(types_n, "needsme");
}
TAILQ_FOREACH_REVERSE(service, stop_services, rc_stringlist, entries)
{
if (rc_service_state(service->value) & RC_SERVICE_STOPPED)
continue;
/* We always stop the service when in these runlevels */
if (going_down) {
pid = rc_service_stop(service->value);
if (pid > 0 && ! parallel)
rc_waitpid(pid);
continue;
}
/* If we're in the start list then don't bother stopping us */
TAILQ_FOREACH(svc1, start_services, entries)
if (strcmp (svc1->value, service->value) == 0)
break;
if (svc1) {
if (newlevel && strcmp(runlevel, newlevel) != 0) {
/* So we're in the start list. But we should
* be stopped if we have a runlevel
* configuration file for either the current
* or next so we use the correct one. */
if (! runlevel_config(service->value, runlevel) &&
! runlevel_config(service->value, newlevel))
continue;
}
else
continue;
}
/* We got this far! Or last check is to see if any any service
* that going to be started depends on us */
if (! svc1) {
tmplist = rc_stringlist_new();
rc_stringlist_add(tmplist, service->value);
deporder = rc_deptree_depends(deptree, types_n, tmplist,
runlevel, RC_DEP_STRICT);
rc_stringlist_free(tmplist);
svc2 = NULL;
TAILQ_FOREACH (svc1, deporder, entries) {
TAILQ_FOREACH(svc2, start_services, entries)
if (strcmp (svc1->value, svc2->value) == 0)
break;
if (svc2)
break;
}
rc_stringlist_free(deporder);
if (svc2)
continue;
}
/* After all that we can finally stop the blighter! */
pid = rc_service_stop(service->value);
if (pid > 0) {
add_pid(pid);
if (! parallel) {
rc_waitpid(pid);
remove_pid(pid);
}
}
}
}
static void do_start_services(bool parallel)
{
RC_STRING *service;
pid_t pid;
bool interactive = false;
if (! rc_yesno(getenv("EINFO_QUIET")))
interactive = exists(INTERACTIVE);
TAILQ_FOREACH(service, start_services, entries) {
if (rc_service_state(service->value) & RC_SERVICE_STOPPED) {
if (! interactive)
interactive = want_interactive();
if (interactive) {
interactive_retry:
printf("\n");
einfo("About to start the service %s",
service->value);
eindent();
einfo("1) Start the service\t\t2) Skip the service");
einfo("3) Continue boot process\t\t4) Exit to shell");
eoutdent();
interactive_option:
switch (read_key(true)) {
case '1': break;
case '2': continue;
case '3': interactive = false; break;
case '4': sulogin(true); goto interactive_retry;
default: goto interactive_option;
}
}
pid = rc_service_start(service->value);
/* Remember the pid if we're running in parallel */
if (pid > 0) {
add_pid(pid);
if (! parallel) {
rc_waitpid(pid);
remove_pid(pid);
}
}
}
}
/* Store our interactive status for boot */
if (interactive && strcmp(runlevel, getenv("RC_BOOTLEVEL")) == 0)
mark_interactive();
else {
if (exists(INTERACTIVE))
unlink(INTERACTIVE);
}
}
#include "_usage.h" #include "_usage.h"
#define getoptstring "o:" getoptstring_COMMON #define getoptstring "o:" getoptstring_COMMON
static const struct option longopts[] = { static const struct option longopts[] = {
@ -666,25 +910,18 @@ static const char * const longopts_help[] = {
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
const char *bootlevel = NULL; const char *bootlevel = NULL;
const char *sys = rc_sys();
char *newlevel = NULL; char *newlevel = NULL;
RC_STRINGLIST *deporder = NULL; RC_STRINGLIST *deporder = NULL;
RC_STRINGLIST *tmplist; RC_STRINGLIST *tmplist;
RC_STRING *service; RC_STRING *service;
bool going_down = false; bool going_down = false;
bool interactive = false;
int depoptions = RC_DEP_STRICT | RC_DEP_TRACE; int depoptions = RC_DEP_STRICT | RC_DEP_TRACE;
char ksoftbuffer [PATH_MAX]; char ksoftbuffer [PATH_MAX];
char pidstr[6]; char pidstr[6];
int opt; int opt;
bool parallel; bool parallel;
int regen = 0; int regen = 0;
pid_t pid;
RC_STRING *svc1;
RC_STRING *svc2 = NULL;
struct utsname uts;
#ifdef __linux__ #ifdef __linux__
char *cmd;
char *proc; char *proc;
char *p; char *p;
char *token; char *token;
@ -698,8 +935,8 @@ int main(int argc, char **argv)
if (argc > 1 && (strcmp(argv[1], "--version") == 0)) { if (argc > 1 && (strcmp(argv[1], "--version") == 0)) {
printf("%s (OpenRC", applet); printf("%s (OpenRC", applet);
if (sys) if ((bootlevel = rc_sys()))
printf(" [%s]", sys); printf(" [%s]", bootlevel);
printf(") " VERSION printf(") " VERSION
#ifdef BRANDING #ifdef BRANDING
" (" BRANDING ")" " (" BRANDING ")"
@ -765,8 +1002,6 @@ int main(int argc, char **argv)
signal_setup(SIGUSR1, handle_signal); signal_setup(SIGUSR1, handle_signal);
signal_setup(SIGWINCH, handle_signal); signal_setup(SIGWINCH, handle_signal);
if (! rc_yesno(getenv("EINFO_QUIET")))
interactive = exists(INTERACTIVE);
rc_plugin_load(); rc_plugin_load();
/* Check we're in the runlevel requested, ie from /* Check we're in the runlevel requested, ie from
@ -774,107 +1009,8 @@ int main(int argc, char **argv)
* rc shutdown * rc shutdown
* rc reboot * rc reboot
*/ */
if (newlevel) { if (newlevel)
if (strcmp(newlevel, RC_LEVEL_SYSINIT) == 0 do_newlevel(newlevel);
#ifndef PREFIX
&& RUNLEVEL &&
(strcmp(RUNLEVEL, "S") == 0 ||
strcmp(RUNLEVEL, "1") == 0)
#endif
)
{
/* OK, we're either in runlevel 1 or single user mode */
/* exec init-early.sh if it exists
* This should just setup the console to use the correct
* font. Maybe it should setup the keyboard too? */
if (exists(INITEARLYSH))
run_script(INITEARLYSH);
uname(&uts);
printf("\n %sOpenRC %s" VERSION "%s is starting up %s",
ecolor(ECOLOR_GOOD), ecolor(ECOLOR_HILITE),
ecolor(ECOLOR_NORMAL), ecolor(ECOLOR_BRACKET));
#ifdef BRANDING
printf(BRANDING " (%s)", uts.machine);
#else
printf("%s %s (%s)",
uts.sysname,
uts.release,
uts.machine);
#endif
if (sys)
printf(" [%s]", sys);
printf("%s\n\n", ecolor(ECOLOR_NORMAL));
if (! rc_yesno(getenv ("EINFO_QUIET")) &&
rc_conf_yesno("rc_interactive"))
printf("Press %sI%s to enter interactive boot mode\n\n",
ecolor(ECOLOR_GOOD), ecolor(ECOLOR_NORMAL));
setenv("RC_SOFTLEVEL", newlevel, 1);
rc_plugin_run(RC_HOOK_RUNLEVEL_START_IN, newlevel);
hook_out = RC_HOOK_RUNLEVEL_START_OUT;
run_script(INITSH);
#ifdef __linux__
/* If we requested a softlevel, save it now */
set_ksoftlevel(NULL);
if ((cmd = proc_getent("softlevel"))) {
set_ksoftlevel(cmd);
free(cmd);
}
#endif
/* Setup our coldplugged services now */
do_coldplug();
rc_plugin_run(RC_HOOK_RUNLEVEL_START_OUT, newlevel);
hook_out = 0;
if (want_interactive())
mark_interactive();
exit(EXIT_SUCCESS);
} else if (strcmp(newlevel, RC_LEVEL_SINGLE) == 0) {
#ifndef PREFIX
if (! RUNLEVEL ||
(strcmp(RUNLEVEL, "S") != 0 &&
strcmp(RUNLEVEL, "1") != 0))
{
/* Remember the current runlevel for when we come back */
set_ksoftlevel(runlevel);
single_user();
}
#endif
} else if (strcmp(newlevel, RC_LEVEL_REBOOT) == 0) {
if (! RUNLEVEL ||
strcmp(RUNLEVEL, "6") != 0)
{
rc_logger_close();
execl(SHUTDOWN, SHUTDOWN, "-r", "now", (char *) NULL);
eerrorx("%s: unable to exec `" SHUTDOWN "': %s",
applet, strerror(errno));
}
} else if (strcmp(newlevel, RC_LEVEL_SHUTDOWN) == 0) {
if (! RUNLEVEL ||
strcmp(RUNLEVEL, "0") != 0)
{
rc_logger_close();
execl(SHUTDOWN, SHUTDOWN,
#ifdef __linux__
"-h",
#else
"-p",
#endif
"now", (char *) NULL);
eerrorx("%s: unable to exec `" SHUTDOWN "': %s",
applet, strerror(errno));
}
}
}
/* Now we start handling our children */ /* Now we start handling our children */
signal_setup(SIGCHLD, handle_signal); signal_setup(SIGCHLD, handle_signal);
@ -946,15 +1082,23 @@ int main(int argc, char **argv)
* correct order for stopping them */ * correct order for stopping them */
stop_services = rc_services_in_state(RC_SERVICE_STARTED); stop_services = rc_services_in_state(RC_SERVICE_STARTED);
tmplist = rc_services_in_state(RC_SERVICE_INACTIVE); tmplist = rc_services_in_state(RC_SERVICE_INACTIVE);
TAILQ_CONCAT(stop_services, tmplist, entries); if (tmplist) {
free(tmplist); if (stop_services) {
TAILQ_CONCAT(stop_services, tmplist, entries);
free(tmplist);
} else
stop_services = tmplist;
}
tmplist = rc_services_in_state(RC_SERVICE_STARTING); tmplist = rc_services_in_state(RC_SERVICE_STARTING);
TAILQ_CONCAT(stop_services, tmplist, entries); if (tmplist) {
free(tmplist); if (stop_services) {
TAILQ_CONCAT(stop_services, tmplist, entries);
free(tmplist);
} else
stop_services = tmplist;
}
rc_stringlist_sort(&stop_services); rc_stringlist_sort(&stop_services);
types_n = rc_stringlist_new();
rc_stringlist_add(types_n, "needsme");
types_nua = rc_stringlist_new(); types_nua = rc_stringlist_new();
rc_stringlist_add(types_nua, "ineed"); rc_stringlist_add(types_nua, "ineed");
@ -976,12 +1120,18 @@ int main(int argc, char **argv)
start_services = rc_services_in_runlevel(bootlevel); start_services = rc_services_in_runlevel(bootlevel);
if (strcmp (newlevel ? newlevel : runlevel, bootlevel) != 0) { if (strcmp (newlevel ? newlevel : runlevel, bootlevel) != 0) {
tmplist = rc_services_in_runlevel(newlevel ? newlevel : runlevel); tmplist = rc_services_in_runlevel(newlevel ? newlevel : runlevel);
TAILQ_CONCAT(start_services, tmplist, entries); if (tmplist) {
free(tmplist); if (start_services) {
TAILQ_CONCAT(start_services, tmplist, entries);
free(tmplist);
} else
start_services = tmplist;
}
} }
TAILQ_FOREACH(service, coldplugged_services, entries) if (coldplugged_services)
rc_stringlist_addu(start_services, service->value); TAILQ_FOREACH(service, coldplugged_services, entries)
rc_stringlist_addu(start_services, service->value);
} }
/* Save our softlevel now */ /* Save our softlevel now */
@ -991,69 +1141,8 @@ int main(int argc, char **argv)
parallel = rc_conf_yesno("rc_parallel"); parallel = rc_conf_yesno("rc_parallel");
/* Now stop the services that shouldn't be running */ /* Now stop the services that shouldn't be running */
TAILQ_FOREACH_REVERSE(service, stop_services, rc_stringlist, entries) { if (stop_services)
if (rc_service_state(service->value) & RC_SERVICE_STOPPED) do_stop_services(newlevel, parallel, going_down);
continue;
/* We always stop the service when in these runlevels */
if (going_down) {
pid = rc_service_stop(service->value);
if (pid > 0 && ! parallel)
rc_waitpid(pid);
continue;
}
/* If we're in the start list then don't bother stopping us */
TAILQ_FOREACH(svc1, start_services, entries)
if (strcmp (svc1->value, service->value) == 0)
break;
if (svc1) {
if (newlevel && strcmp(runlevel, newlevel) != 0) {
/* So we're in the start list. But we should
* be stopped if we have a runlevel
* configuration file for either the current
* or next so we use the correct one. */
if (! runlevel_config(service->value, runlevel) &&
! runlevel_config(service->value, newlevel))
continue;
}
else
continue;
}
/* We got this far! Or last check is to see if any any service
* that going to be started depends on us */
if (! svc1) {
tmplist = rc_stringlist_new();
rc_stringlist_add(tmplist, service->value);
deporder = rc_deptree_depends(deptree, types_n, tmplist,
runlevel, RC_DEP_STRICT);
rc_stringlist_free(tmplist);
svc2 = NULL;
TAILQ_FOREACH (svc1, deporder, entries) {
TAILQ_FOREACH(svc2, start_services, entries)
if (strcmp (svc1->value, svc2->value) == 0)
break;
if (svc2)
break;
}
rc_stringlist_free(deporder);
if (svc2)
continue;
}
/* After all that we can finally stop the blighter! */
pid = rc_service_stop(service->value);
if (pid > 0) {
add_pid(pid);
if (! parallel) {
rc_waitpid(pid);
remove_pid(pid);
}
}
}
/* Wait for our services to finish */ /* Wait for our services to finish */
wait_for_services(); wait_for_services();
@ -1094,15 +1183,18 @@ int main(int argc, char **argv)
hook_out = RC_HOOK_RUNLEVEL_START_OUT; hook_out = RC_HOOK_RUNLEVEL_START_OUT;
/* Re-add our coldplugged services if they stopped */ /* Re-add our coldplugged services if they stopped */
TAILQ_FOREACH(service, coldplugged_services, entries) if (coldplugged_services)
rc_service_mark(service->value, RC_SERVICE_COLDPLUGGED); TAILQ_FOREACH(service, coldplugged_services, entries)
rc_service_mark(service->value, RC_SERVICE_COLDPLUGGED);
/* Order the services to start */ /* Order the services to start */
rc_stringlist_sort(&start_services); if (start_services) {
deporder = rc_deptree_depends(deptree, types_nua, start_services, rc_stringlist_sort(&start_services);
deporder = rc_deptree_depends(deptree, types_nua, start_services,
runlevel, depoptions | RC_DEP_START); runlevel, depoptions | RC_DEP_START);
rc_stringlist_free(start_services); rc_stringlist_free(start_services);
start_services = deporder; start_services = deporder;
}
#ifdef __linux__ #ifdef __linux__
/* mark any services skipped as started */ /* mark any services skipped as started */
@ -1116,47 +1208,13 @@ int main(int argc, char **argv)
} }
#endif #endif
TAILQ_FOREACH(service, start_services, entries) { if (start_services) {
if (rc_service_state(service->value) & RC_SERVICE_STOPPED) { do_start_services(parallel);
if (! interactive)
interactive = want_interactive();
if (interactive) { /* Wait for our services to finish */
interactive_retry: wait_for_services();
printf("\n");
einfo("About to start the service %s",
service->value);
eindent();
einfo("1) Start the service\t\t2) Skip the service");
einfo("3) Continue boot process\t\t4) Exit to shell");
eoutdent();
interactive_option:
switch (read_key(true)) {
case '1': break;
case '2': continue;
case '3': interactive = false; break;
case '4': sulogin(true); goto interactive_retry;
default: goto interactive_option;
}
}
pid = rc_service_start(service->value);
/* Remember the pid if we're running in parallel */
if (pid > 0) {
add_pid(pid);
if (! parallel) {
rc_waitpid(pid);
remove_pid(pid);
}
}
}
} }
/* Wait for our services to finish */
wait_for_services();
rc_plugin_run(RC_HOOK_RUNLEVEL_START_OUT, runlevel); rc_plugin_run(RC_HOOK_RUNLEVEL_START_OUT, runlevel);
hook_out = 0; hook_out = 0;
@ -1172,14 +1230,6 @@ interactive_option:
} }
#endif #endif
/* Store our interactive status for boot */
if (interactive && strcmp(runlevel, bootlevel) == 0)
mark_interactive();
else {
if (exists(INTERACTIVE))
unlink(INTERACTIVE);
}
/* If we're in the boot runlevel and we regenerated our dependencies /* If we're in the boot runlevel and we regenerated our dependencies
* we need to delete them so that they are regenerated again in the * we need to delete them so that they are regenerated again in the
* default runlevel as they may depend on things that are now available */ * default runlevel as they may depend on things that are now available */

View File

@ -82,8 +82,8 @@ static RC_STRINGLIST *use_services = NULL;
static RC_STRINGLIST *services = NULL; static RC_STRINGLIST *services = NULL;
static RC_STRINGLIST *tmplist = NULL; static RC_STRINGLIST *tmplist = NULL;
static char *service = NULL; static char *service = NULL;
static char *exclusive = NULL; static char exclusive[PATH_MAX] = { '\0' };
static char *mtime_test = NULL; static char mtime_test[PATH_MAX] = { '\0' };
static RC_DEPTREE *deptree = NULL; static RC_DEPTREE *deptree = NULL;
static char *runlevel = NULL; static char *runlevel = NULL;
static bool sighup = false; static bool sighup = false;
@ -212,7 +212,7 @@ static const char *const tests[] = {
}; };
static bool in_control() static bool in_control()
{ {
char *path; char file[PATH_MAX];
time_t m; time_t m;
time_t mtime; time_t mtime;
int i = 0; int i = 0;
@ -230,15 +230,12 @@ static bool in_control()
return false; return false;
while (tests[i]) { while (tests[i]) {
path = rc_strcatpaths(RC_SVCDIR, tests[i], applet, (char *) NULL); snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s", tests[i], applet);
if (exists(path)) { if (exists(file)) {
m = get_mtime(path, false); m = get_mtime(file, false);
if (mtime < m && m != 0) { if (mtime < m && m != 0)
free(path);
return false; return false;
}
} }
free(path);
i++; i++;
} }
@ -247,10 +244,11 @@ static bool in_control()
static void uncoldplug() static void uncoldplug()
{ {
char *cold = rc_strcatpaths(RC_SVCDIR, "coldplugged", applet, (char *) NULL); char file[PATH_MAX];
if (exists(cold) && unlink(cold) != 0)
eerror("%s: unlink `%s': %s", applet, cold, strerror(errno)); snprintf(file, sizeof(file), RC_SVCDIR "/coldplugged/%s", applet);
free(cold); if (exists(file) && unlink(file) != 0)
eerror("%s: unlink `%s': %s", applet, file, strerror(errno));
} }
static void start_services(RC_STRINGLIST *list) { static void start_services(RC_STRINGLIST *list) {
@ -305,10 +303,10 @@ static void restore_state(void)
rc_service_mark(applet, RC_SERVICE_FAILED); rc_service_mark(applet, RC_SERVICE_FAILED);
} }
if (exclusive) if (*exclusive) {
unlink(exclusive); unlink(exclusive);
free(exclusive); *exclusive = '\0';
exclusive = NULL; }
} }
static void cleanup(void) static void cleanup(void)
@ -347,17 +345,12 @@ static void cleanup(void)
rc_stringlist_free(applet_list); rc_stringlist_free(applet_list);
rc_stringlist_free(tmplist); rc_stringlist_free(tmplist);
free (ibsave); free (ibsave);
if (mtime_test)
{
if (! rc_in_plugin)
unlink(mtime_test);
free(mtime_test);
}
free(exclusive);
free(service); free(service);
free(prefix); free(prefix);
free(runlevel); free(runlevel);
if (*mtime_test && ! rc_in_plugin)
unlink(mtime_test);
} }
static int write_prefix(const char *buffer, size_t bytes, bool *prefixed) { static int write_prefix(const char *buffer, size_t bytes, bool *prefixed) {
@ -580,37 +573,29 @@ static RC_SERVICE svc_status(void)
static void make_exclusive(void) static void make_exclusive(void)
{ {
char *path;
size_t l;
/* We create a fifo so that other services can wait until we complete */ /* We create a fifo so that other services can wait until we complete */
if (! exclusive) if (! *exclusive)
exclusive = rc_strcatpaths(RC_SVCDIR, "exclusive", applet, (char *) NULL); snprintf(exclusive, sizeof(exclusive), RC_SVCDIR "/exclusive/%s",
applet);
if (mkfifo(exclusive, 0600) != 0 && errno != EEXIST && if (mkfifo(exclusive, 0600) != 0 && errno != EEXIST &&
(errno != EACCES || geteuid () == 0)) (errno != EACCES || geteuid () == 0))
eerrorx ("%s: unable to create fifo `%s': %s", eerrorx ("%s: unable to create fifo `%s': %s",
applet, exclusive, strerror(errno)); applet, exclusive, strerror(errno));
path = rc_strcatpaths(RC_SVCDIR, "exclusive", applet, (char *) NULL); snprintf(mtime_test, sizeof(mtime_test), RC_SVCDIR "/exclusive/%s.%d", applet, getpid());
l = strlen (path) + 16;
mtime_test = xmalloc(sizeof (char) * l);
snprintf(mtime_test, l, "%s.%d", path, getpid());
free(path);
if (exists(mtime_test) && unlink(mtime_test) != 0) { if (exists(mtime_test) && unlink(mtime_test) != 0) {
eerror("%s: unlink `%s': %s", eerror("%s: unlink `%s': %s",
applet, mtime_test, strerror(errno)); applet, mtime_test, strerror(errno));
free(mtime_test); *mtime_test = '\0';
mtime_test = NULL;
return; return;
} }
if (symlink(service, mtime_test) != 0) { if (symlink(service, mtime_test) != 0) {
eerror("%s: symlink `%s' to `%s': %s", eerror("%s: symlink `%s' to `%s': %s",
applet, service, mtime_test, strerror(errno)); applet, service, mtime_test, strerror(errno));
free(mtime_test); *mtime_test = '\0';
mtime_test = NULL;
} }
} }
@ -618,8 +603,7 @@ static void unlink_mtime_test(void)
{ {
if (unlink(mtime_test) != 0) if (unlink(mtime_test) != 0)
eerror("%s: unlink `%s': %s", applet, mtime_test, strerror(errno)); eerror("%s: unlink `%s': %s", applet, mtime_test, strerror(errno));
free(mtime_test); *mtime_test = '\0';
mtime_test = NULL;
} }
static void get_started_services(void) static void get_started_services(void)
@ -627,8 +611,13 @@ static void get_started_services(void)
RC_STRINGLIST *tmp = rc_services_in_state(RC_SERVICE_INACTIVE); RC_STRINGLIST *tmp = rc_services_in_state(RC_SERVICE_INACTIVE);
rc_stringlist_free(restart_services); rc_stringlist_free(restart_services);
restart_services = rc_services_in_state(RC_SERVICE_STARTED); restart_services = rc_services_in_state(RC_SERVICE_STARTED);
TAILQ_CONCAT(restart_services, tmp, entries); if (tmp) {
free(tmp); if (restart_services) {
TAILQ_CONCAT(restart_services, tmp, entries);
free(tmp);
} else
restart_services = tmp;
}
} }
static void setup_types(void) static void setup_types(void)
@ -713,8 +702,8 @@ static void svc_start(bool deps)
services = rc_deptree_depends(deptree, types_b, applet_list, services = rc_deptree_depends(deptree, types_b, applet_list,
runlevel, 0); runlevel, 0);
if (TAILQ_FIRST(services)) { if (services && TAILQ_FIRST(services)) {
eerrorn ("ERROR: `%s' needs ", applet); eerrorn("ERROR: `%s' needs ", applet);
first = true; first = true;
TAILQ_FOREACH(svc, services, entries) { TAILQ_FOREACH(svc, services, entries) {
if (first) if (first)
@ -733,7 +722,7 @@ static void svc_start(bool deps)
use_services = rc_deptree_depends(deptree, types_nu, applet_list, use_services = rc_deptree_depends(deptree, types_nu, applet_list,
runlevel, depoptions); runlevel, depoptions);
if (! rc_runlevel_starting()) if (! rc_runlevel_starting() && use_services)
TAILQ_FOREACH(svc, use_services, entries) TAILQ_FOREACH(svc, use_services, entries)
if (rc_service_state(svc->value) & RC_SERVICE_STOPPED) { if (rc_service_state(svc->value) & RC_SERVICE_STOPPED) {
pid_t pid = rc_service_start(svc->value); pid_t pid = rc_service_start(svc->value);
@ -746,8 +735,7 @@ static void svc_start(bool deps)
runlevel, depoptions); runlevel, depoptions);
/* We use tmplist to hold our scheduled by list */ /* We use tmplist to hold our scheduled by list */
tmplist = rc_stringlist_new(); tmplist = NULL;
TAILQ_FOREACH(svc, services, entries) { TAILQ_FOREACH(svc, services, entries) {
RC_SERVICE svcs = rc_service_state(svc->value); RC_SERVICE svcs = rc_service_state(svc->value);
if (svcs & RC_SERVICE_STARTED) if (svcs & RC_SERVICE_STARTED)
@ -768,14 +756,19 @@ static void svc_start(bool deps)
if (! svc_wait(svc->value)) if (! svc_wait(svc->value))
eerror ("%s: timed out waiting for %s", eerror ("%s: timed out waiting for %s",
applet, svc->value); applet, svc->value);
if (! need_services)
continue;
if ((svcs = rc_service_state(svc->value)) & RC_SERVICE_STARTED) if ((svcs = rc_service_state(svc->value)) & RC_SERVICE_STARTED)
continue; continue;
TAILQ_FOREACH(svc2, need_services, entries) { TAILQ_FOREACH(svc2, need_services, entries) {
if (strcmp (svc->value, svc2->value) == 0) { if (strcmp (svc->value, svc2->value) == 0) {
if (svcs & RC_SERVICE_INACTIVE || if (svcs & RC_SERVICE_INACTIVE ||
svcs & RC_SERVICE_WASINACTIVE) svcs & RC_SERVICE_WASINACTIVE)
rc_stringlist_add(tmplist, svc->value); {
else if (! tmplist)
tmplist = rc_stringlist_new();
rc_stringlist_add(tmplist, svc->value);
} else
eerrorx("ERROR: cannot start %s as" eerrorx("ERROR: cannot start %s as"
" %s would not start", " %s would not start",
applet, svc->value); applet, svc->value);
@ -783,7 +776,7 @@ static void svc_start(bool deps)
} }
} }
if (TAILQ_FIRST(tmplist)) { if (tmplist && TAILQ_FIRST(tmplist)) {
/* Set the state now, then unlink our exclusive so that /* Set the state now, then unlink our exclusive so that
our scheduled list is preserved */ our scheduled list is preserved */
rc_service_mark(service, RC_SERVICE_STOPPED); rc_service_mark(service, RC_SERVICE_STOPPED);
@ -813,14 +806,14 @@ static void svc_start(bool deps)
p += snprintf(p, len, "%s", svc->value); p += snprintf(p, len, "%s", svc->value);
} }
free(tmp); free(tmp);
rc_stringlist_free(tmplist);
tmplist = NULL;
ewarnx("WARNING: %s is scheduled to start when %s has started", ewarnx("WARNING: %s is scheduled to start when %s has started",
applet, tmp); applet, tmp);
} }
rc_stringlist_free(services); rc_stringlist_free(services);
services = NULL; services = NULL;
rc_stringlist_free(tmplist);
tmplist = NULL;
} }
if (ibsave) if (ibsave)
@ -928,65 +921,73 @@ static void svc_stop(bool deps)
if (! types_m) if (! types_m)
setup_types(); setup_types();
tmplist = rc_stringlist_new(); tmplist = NULL;
services = rc_deptree_depends(deptree, types_m, applet_list, services = rc_deptree_depends(deptree, types_m, applet_list,
runlevel, depoptions); runlevel, depoptions);
TAILQ_FOREACH_REVERSE(svc, services, rc_stringlist, entries) { if (services) {
RC_SERVICE svcs = rc_service_state(svc->value); TAILQ_FOREACH_REVERSE(svc, services, rc_stringlist, entries) {
if (svcs & RC_SERVICE_STARTED || RC_SERVICE svcs = rc_service_state(svc->value);
svcs & RC_SERVICE_INACTIVE)
{
svc_wait(svc->value);
svcs = rc_service_state(svc->value);
if (svcs & RC_SERVICE_STARTED || if (svcs & RC_SERVICE_STARTED ||
svcs & RC_SERVICE_INACTIVE) svcs & RC_SERVICE_INACTIVE)
{ {
pid_t pid = rc_service_stop(svc->value); svc_wait(svc->value);
if (! rc_conf_yesno("rc_parallel")) svcs = rc_service_state(svc->value);
rc_waitpid(pid); if (svcs & RC_SERVICE_STARTED ||
rc_stringlist_add(tmplist, svc->value); svcs & RC_SERVICE_INACTIVE)
{
pid_t pid = rc_service_stop(svc->value);
if (! rc_conf_yesno("rc_parallel"))
rc_waitpid(pid);
if (! tmplist)
tmplist = rc_stringlist_new();
rc_stringlist_add(tmplist, svc->value);
}
} }
} }
rc_stringlist_free(services);
services = NULL;
} }
rc_stringlist_free(services);
services = NULL;
TAILQ_FOREACH(svc, tmplist, entries) { if (tmplist) {
if (rc_service_state(svc->value) & RC_SERVICE_STOPPED) TAILQ_FOREACH(svc, tmplist, entries) {
continue; if (rc_service_state(svc->value) & RC_SERVICE_STOPPED)
continue;
/* We used to loop 3 times here - maybe re-do this if needed */ /* We used to loop 3 times here - maybe re-do this if needed */
svc_wait(svc->value); svc_wait(svc->value);
if (! (rc_service_state(svc->value) & RC_SERVICE_STOPPED)) { if (! (rc_service_state(svc->value) & RC_SERVICE_STOPPED)) {
if (rc_runlevel_stopping()) { if (rc_runlevel_stopping()) {
/* If shutting down, we should stop even /* If shutting down, we should stop even
* if a dependant failed */ * if a dependant failed */
if (runlevel && if (runlevel &&
(strcmp(runlevel, RC_LEVEL_SHUTDOWN) == 0 || (strcmp(runlevel, RC_LEVEL_SHUTDOWN) == 0 ||
strcmp(runlevel, RC_LEVEL_REBOOT) == 0 || strcmp(runlevel, RC_LEVEL_REBOOT) == 0 ||
strcmp(runlevel, RC_LEVEL_SINGLE) == 0)) strcmp(runlevel, RC_LEVEL_SINGLE) == 0))
continue; continue;
rc_service_mark(service, RC_SERVICE_FAILED); rc_service_mark(service, RC_SERVICE_FAILED);
}
eerrorx("ERROR: cannot stop %s as %s is still up",
applet, svc->value);
} }
eerrorx("ERROR: cannot stop %s as %s is still up",
applet, svc->value);
} }
rc_stringlist_free(tmplist);
tmplist = NULL;
} }
rc_stringlist_free(tmplist);
tmplist = NULL;
/* We now wait for other services that may use us and are stopping /* We now wait for other services that may use us and are stopping
This is important when a runlevel stops */ This is important when a runlevel stops */
services = rc_deptree_depends(deptree, types_mua, applet_list, services = rc_deptree_depends(deptree, types_mua, applet_list,
runlevel, depoptions); runlevel, depoptions);
TAILQ_FOREACH(svc, services, entries) { if (services) {
if (rc_service_state(svc->value) & RC_SERVICE_STOPPED) TAILQ_FOREACH(svc, services, entries) {
continue; if (rc_service_state(svc->value) & RC_SERVICE_STOPPED)
svc_wait(svc->value); continue;
svc_wait(svc->value);
}
rc_stringlist_free (services);
services = NULL;
} }
rc_stringlist_free (services);
services = NULL;
} }
/* If we're stopping localmount, set LC_ALL=C so that /* If we're stopping localmount, set LC_ALL=C so that
@ -1114,8 +1115,8 @@ int runscript(int argc, char **argv)
eerror("%s: cannot run until sysvinit completes", applet); eerror("%s: cannot run until sysvinit completes", applet);
if (mkdir("/dev/.rcboot", 0755) != 0 && errno != EEXIST) if (mkdir("/dev/.rcboot", 0755) != 0 && errno != EEXIST)
eerrorx("%s: mkdir `/dev/.rcboot': %s", applet, strerror(errno)); eerrorx("%s: mkdir `/dev/.rcboot': %s", applet, strerror(errno));
prefix = rc_strcatpaths("/dev/.rcboot", applet, (char *) NULL); snprintf(exclusive, sizeof(exclusive), "/dev/.rcboot/%s", applet);
symlink(service, prefix); symlink(service, exclusive);
exit (EXIT_FAILURE); exit (EXIT_FAILURE);
} }
#endif #endif

View File

@ -51,6 +51,7 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <getopt.h> #include <getopt.h>
#include <limits.h>
#include <grp.h> #include <grp.h>
#include <pwd.h> #include <pwd.h>
#include <signal.h> #include <signal.h>
@ -590,6 +591,7 @@ int start_stop_daemon(int argc, char **argv)
bool setuser = false; bool setuser = false;
char *p; char *p;
char *tmp; char *tmp;
char exec_file[PATH_MAX];
struct passwd *pw; struct passwd *pw;
struct group *gr; struct group *gr;
char line[130]; char line[130];
@ -773,14 +775,13 @@ int start_stop_daemon(int argc, char **argv)
/* Validate that the binary exists if we are starting */ /* Validate that the binary exists if we are starting */
if (exec) { if (exec) {
if (ch_root) if (ch_root) {
tmp = rc_strcatpaths(ch_root, exec, (char *) NULL); snprintf(exec_file, sizeof(exec_file), "%s/%s", ch_root, exec);
else tmp = exec_file;
} else
tmp = exec; tmp = exec;
if (start && ! exists(tmp)) { if (start && ! exists(tmp)) {
eerror("%s: %s does not exist", applet, tmp); eerror("%s: %s does not exist", applet, tmp);
if (ch_root)
free(tmp);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -807,15 +808,10 @@ int start_stop_daemon(int argc, char **argv)
applet, line + 2, exec); applet, line + 2, exec);
eerror("%s: or you should specify a pidfile" eerror("%s: or you should specify a pidfile"
" or process name", applet); " or process name", applet);
if (ch_root)
free(tmp);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
} }
if (ch_root)
free(tmp);
} }
/* Add exec to our arguments */ /* Add exec to our arguments */