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 \
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
# 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"
#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__)
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)
{
char *line;
char *ffile = rc_strcatpaths(path, file, (char *) NULL);
char ffile[PATH_MAX];
FILE *fp;
RC_STRING *m;
snprintf(ffile, sizeof(ffile), "%s/%s", path, file);
fp = fopen(ffile, "r");
free(ffile);
if (! fp)
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,
const char *name, const char *pidfile, bool started)
{
char *dirpath;
char *file = NULL;
char dirpath[PATH_MAX];
char file[PATH_MAX];
int nfiles = 0;
char *oldfile = NULL;
char oldfile[PATH_MAX] = { '\0' };
bool retval = false;
DIR *dp;
struct dirent *d;
RC_STRINGLIST *match;
int i = 0;
char buffer[10];
FILE *fp;
if (!(argv && *argv) && ! name && ! pidfile) {
@ -369,40 +392,40 @@ bool rc_service_daemon_set(const char *service, const char *const *argv,
return false;
}
dirpath = rc_strcatpaths(RC_SVCDIR, "daemons",
basename_c(service), (char *) NULL);
match = _match_list(argv, name, pidfile);
snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s",
basename_c(service));
/* Regardless, erase any existing daemon info */
if ((dp = opendir(dirpath))) {
match = _match_list(argv, name, pidfile);
while ((d = readdir(dp))) {
if (d->d_name[0] == '.')
continue;
file = rc_strcatpaths(dirpath, d->d_name, (char *) NULL);
snprintf(file, sizeof(file), "%s/%s",
dirpath, d->d_name);
nfiles++;
if (! oldfile) {
if (! *oldfile) {
if (_match_daemon(dirpath, d->d_name, match)) {
unlink(file);
oldfile = file;
strlcpy(oldfile, file, sizeof(oldfile));
nfiles--;
}
} else {
rename(file, oldfile);
free(oldfile);
oldfile = file;
strlcpy(oldfile, file, sizeof(oldfile));
}
}
free(file);
closedir(dp);
rc_stringlist_free(match);
}
/* Now store our daemon info */
if (started) {
if (mkdir(dirpath, 0755) == 0 || errno == EEXIST) {
snprintf(buffer, sizeof(buffer), "%03d", nfiles + 1);
file = rc_strcatpaths(dirpath, buffer, (char *) NULL);
snprintf(file, sizeof(file), "%s/%03d",
dirpath, nfiles + 1);
if ((fp = fopen(file, "w"))) {
while (argv && 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);
retval = true;
}
free(file);
}
} else
retval = true;
rc_stringlist_free(match);
free(dirpath);
return retval;
}
librc_hidden_def(rc_service_daemon_set)
bool
rc_service_started_daemon (const char *service, const char *const *argv,
bool rc_service_started_daemon(const char *service, const char *const *argv,
int indx)
{
char *dirpath;
char *file;
size_t l;
char dirpath[PATH_MAX];
char file[16];
RC_STRINGLIST *match;
bool retval = false;
DIR *dp;
@ -445,17 +462,13 @@ rc_service_started_daemon (const char *service, const char *const *argv,
if (!service || !(argv && *argv))
return false;
dirpath = rc_strcatpaths(RC_SVCDIR, "daemons", basename_c(service),
(char *) NULL);
snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s",
basename_c(service));
match = _match_list(argv, NULL, NULL);
if (indx > 0) {
l = sizeof (char) * 10;
file = xmalloc(l);
snprintf(file, l, "%03d", indx);
snprintf(file, sizeof(file), "%03d", indx);
retval = _match_daemon(dirpath, file, match);
free(file);
} else {
if ((dp = opendir(dirpath))) {
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);
return retval;
}
@ -477,10 +489,10 @@ librc_hidden_def(rc_service_started_daemon)
bool rc_service_daemons_crashed(const char *service)
{
char *dirpath;
char dirpath[PATH_MAX];
DIR *dp;
struct dirent *d;
char *path;
char *path = dirpath;
FILE *fp;
char *line;
char **argv = NULL;
@ -498,21 +510,19 @@ bool rc_service_daemons_crashed(const char *service)
RC_STRING *s;
size_t i;
dirpath = rc_strcatpaths(RC_SVCDIR, "daemons", basename_c(service),
(char *) NULL);
path += snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s",
basename_c(service));
if (! (dp = opendir(dirpath))) {
free(dirpath);
if (! (dp = opendir(dirpath)))
return false;
}
while ((d = readdir(dp))) {
if (d->d_name[0] == '.')
continue;
path = rc_strcatpaths(dirpath, d->d_name, (char *) NULL);
fp = fopen(path, "r");
free(path);
snprintf(path, sizeof(dirpath) - (path - dirpath), "/%s",
d->d_name);
fp = fopen(dirpath, "r");
if (! fp)
break;

View File

@ -362,7 +362,7 @@ static RC_STRINGLIST *get_provided (const RC_DEPINFO *depinfo,
static void visit_service(const RC_DEPTREE *deptree,
const RC_STRINGLIST *types,
RC_STRINGLIST *sorted,
RC_STRINGLIST **sorted,
RC_STRINGLIST *visited,
const RC_DEPINFO *depinfo,
const char *runlevel, int options)
@ -392,7 +392,9 @@ static void visit_service (const RC_DEPTREE *deptree,
if (! options & RC_DEP_TRACE ||
strcmp(type->value, "iprovide") == 0)
{
rc_stringlist_add(sorted, service->value);
if (! *sorted)
*sorted = rc_stringlist_new();
rc_stringlist_add(*sorted, service->value);
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 */
svcname = getenv("SVCNAME");
if (! svcname || strcmp(svcname, depinfo->service) != 0)
if (! get_deptype(depinfo, "providedby"))
rc_stringlist_add(sorted, depinfo->service);
if (! get_deptype(depinfo, "providedby")) {
if (! *sorted)
*sorted = rc_stringlist_new();
rc_stringlist_add(*sorted, depinfo->service);
}
}
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 char *runlevel, int options)
{
RC_STRINGLIST *sorted = rc_stringlist_new();
RC_STRINGLIST *sorted = NULL;
RC_STRINGLIST *visited = rc_stringlist_new();
RC_DEPINFO *di;
const RC_STRING *service;
@ -493,7 +498,7 @@ RC_STRINGLIST *rc_deptree_depends (const RC_DEPTREE *deptree,
continue;
}
if (types)
visit_service (deptree, types, sorted, visited,
visit_service(deptree, types, &sorted, visited,
di, runlevel, options);
}
@ -522,12 +527,23 @@ RC_STRINGLIST *rc_deptree_order(const RC_DEPTREE *deptree,
list = rc_services_in_state(RC_SERVICE_STARTED);
list2 = rc_services_in_state (RC_SERVICE_INACTIVE);
if (list2) {
if (list) {
TAILQ_CONCAT(list, list2, entries);
free(list2);
} else
list = list2;
}
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);
} else {
list = rc_services_in_runlevel (runlevel);
@ -567,7 +583,7 @@ bool rc_newer_than(const char *source, const char *target)
bool newer = true;
DIR *dp;
struct dirent *d;
char *path;
char path[PATH_MAX];
int serrno = errno;
/* We have to exist */
@ -594,9 +610,8 @@ bool rc_newer_than(const char *source, const char *target)
if (d->d_name[0] == '.')
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);
free(path);
if (! newer)
break;
}
@ -672,6 +687,7 @@ bool rc_deptree_update_needed(void)
/* Some init scripts dependencies change depending on config files
* outside of baselayout, like syslog-ng, so we check those too. */
config = rc_config_list(RC_DEPCONFIG);
if (config) {
TAILQ_FOREACH(s, config, entries) {
if (! rc_newer_than(RC_DEPTREE_CACHE, s->value)) {
newer = true;
@ -679,6 +695,7 @@ bool rc_deptree_update_needed(void)
}
}
rc_stringlist_free(config);
}
return newer;
}

View File

@ -54,57 +54,6 @@ bool rc_yesno (const char *value)
}
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 *line = NULL;
@ -138,13 +87,11 @@ RC_STRINGLIST *rc_config_list(const char *file)
char *buffer;
char *p;
char *token;
RC_STRINGLIST *list;
RC_STRINGLIST *list = NULL;
if (!(fp = fopen(file, "r")))
return NULL;
list = rc_stringlist_new();
while ((p = buffer = rc_getline(fp))) {
/* Strip leading spaces/tabs */
while ((*p == ' ') || (*p == '\t'))
@ -159,6 +106,8 @@ RC_STRINGLIST *rc_config_list(const char *file)
if (token[strlen(token) - 1] == '\n')
token[strlen(token) - 1] = 0;
if (! list)
list = rc_stringlist_new();
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 *list = NULL;
RC_STRINGLIST *config = NULL;
RC_STRINGLIST *list;
RC_STRINGLIST *config;
char *token;
RC_STRING *line;
RC_STRING *cline;
@ -183,9 +132,11 @@ RC_STRINGLIST *rc_config_load(const char *file)
char *newline;
char *p;
config = rc_stringlist_new();
list = rc_config_list(file);
if (! list)
return NULL;
config = rc_stringlist_new();
TAILQ_FOREACH(line, list, entries) {
/* Get entry */
p = line->value;

View File

@ -72,25 +72,24 @@ static RC_STRINGLIST *ls_dir(const char *dir, int options)
{
DIR *dp;
struct dirent *d;
RC_STRINGLIST *list;
RC_STRINGLIST *list = NULL;
struct stat buf;
size_t l;
char *file;
char file[PATH_MAX];
int r;
if ((dp = opendir(dir)) == NULL)
return NULL;
list = rc_stringlist_new();
while (((d = readdir(dp)) != NULL)) {
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);
snprintf(file, sizeof(file), "%s/%s",
dir, d->d_name);
r = stat(file, &buf);
free(file);
if (r != 0)
continue;
@ -106,6 +105,8 @@ static RC_STRINGLIST *ls_dir(const char *dir, int options)
! S_ISDIR(buf.st_mode))
continue;
}
if (! list)
list = rc_stringlist_new();
rc_stringlist_add(list, d->d_name);
}
}
@ -118,7 +119,7 @@ static bool rm_dir(const char *pathname, bool top)
{
DIR *dp;
struct dirent *d;
char *tmp = NULL;
char file[PATH_MAX];
struct stat s;
bool retval = true;
@ -128,22 +129,21 @@ static bool rm_dir(const char *pathname, bool top)
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);
snprintf(file, sizeof(file), "%s/%s", pathname, d->d_name);
if (stat(tmp, &s) != 0) {
if (stat(file, &s) != 0) {
retval = false;
break;
}
if (S_ISDIR(s.st_mode)) {
if (! rm_dir(tmp, true))
if (! rm_dir(file, true))
{
retval = false;
break;
}
} else {
if (unlink(tmp)) {
if (unlink(file)) {
retval = false;
break;
}
@ -151,7 +151,6 @@ static bool rm_dir(const char *pathname, bool top)
}
}
closedir(dp);
free(tmp);
if (! retval)
return false;
@ -302,18 +301,16 @@ librc_hidden_def(rc_runlevel_set)
bool rc_runlevel_exists(const char *runlevel)
{
char *path;
char path[PATH_MAX];
struct stat buf;
bool retval = false;
if (! runlevel)
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))
retval = true;
free(path);
return retval;
return true;
return false;
}
librc_hidden_def(rc_runlevel_exists)
@ -321,8 +318,8 @@ librc_hidden_def(rc_runlevel_exists)
char *rc_service_resolve(const char *service)
{
char buffer[PATH_MAX];
char *file;
int r = 0;
char file[PATH_MAX];
int r;
struct stat buf;
if (! service)
@ -332,43 +329,41 @@ char *rc_service_resolve(const char *service)
return xstrdup(service);
/* 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)) {
free(file);
file = rc_strcatpaths(RC_SVCDIR, "inactive", service, (char *) NULL);
if (lstat(file, &buf) || ! S_ISLNK(buf.st_mode)) {
free(file);
file = NULL;
}
snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
"inactive", service);
if (lstat(file, &buf) || ! S_ISLNK(buf.st_mode))
*file = '\0';
}
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));
free(file);
if (r > 0)
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_PKG_INITDIR
if (stat(buffer, &buf) != 0) {
snprintf(buffer, sizeof(buffer), RC_PKG_INITDIR "/%s", service);
if (stat(buffer, &buf) != 0)
return NULL;
}
#ifdef RC_LOCAL_INITDIR
/* Nope, so lets see if the user has written it */
snprintf(file, sizeof(file), RC_LOCAL_INITDIR "/%s", service);
if (stat(file, &buf) == 0)
return xstrdup(file);
#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)
@ -406,7 +401,7 @@ RC_STRINGLIST *rc_service_extra_commands(const char *service)
char *svc;
char *cmd = NULL;
char *buffer = NULL;
RC_STRINGLIST *commands;
RC_STRINGLIST *commands = NULL;
char *token;
char *p;
FILE *fp;
@ -415,7 +410,6 @@ RC_STRINGLIST *rc_service_extra_commands(const char *service)
if (! (svc = rc_service_resolve(service)))
return NULL;
commands = rc_stringlist_new();
l = strlen(OPTSTR) + strlen(svc) + 1;
cmd = xmalloc(sizeof(char) * l);
@ -424,8 +418,11 @@ RC_STRINGLIST *rc_service_extra_commands(const char *service)
if ((fp = popen(cmd, "r"))) {
p = buffer = rc_getline(fp);
while ((token = strsep(&p, " ")))
while ((token = strsep(&p, " "))) {
if (! commands)
commands = rc_stringlist_new();
rc_stringlist_add(commands, token);
}
pclose(fp);
free(buffer);
}
@ -464,24 +461,17 @@ librc_hidden_def(rc_service_description)
bool rc_service_in_runlevel(const char *service, const char *runlevel)
{
char *file;
bool retval;
char file[PATH_MAX];
if (! runlevel || ! service)
return false;
file = rc_strcatpaths(RC_RUNLEVELDIR, runlevel, basename_c(service),
(char *) NULL);
retval = exists(file);
free(file);
return retval;
snprintf(file, sizeof(file), RC_RUNLEVELDIR "/%s/%s",
runlevel, basename_c(service));
return exists(file);
}
librc_hidden_def(rc_service_in_runlevel)
bool rc_service_mark(const char *service, const RC_SERVICE state)
{
char *file;
char file[PATH_MAX];
int i = 0;
int skip_state = -1;
const char *base;
@ -504,18 +494,16 @@ bool rc_service_mark(const char *service, const RC_SERVICE state)
return false;
}
file = rc_strcatpaths(RC_SVCDIR, rc_parse_service_state (state), base,
(char *) NULL);
snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
rc_parse_service_state (state), base);
if (exists(file))
unlink(file);
i = symlink(init, file);
if (i != 0) {
free(file);
free(init);
return false;
}
free(file);
skip_state = state;
}
@ -534,24 +522,22 @@ bool rc_service_mark(const char *service, const RC_SERVICE state)
s != RC_SERVICE_SCHEDULED) &&
(! skip_wasinactive || s != RC_SERVICE_WASINACTIVE))
{
file = rc_strcatpaths(RC_SVCDIR, rc_parse_service_state(s), base,
(char *) NULL);
snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
rc_parse_service_state(s), base);
if (exists(file)) {
if ((state == RC_SERVICE_STARTING ||
state == RC_SERVICE_STOPPING) &&
s == RC_SERVICE_INACTIVE)
{
was = rc_strcatpaths(RC_SVCDIR,
snprintf(was, sizeof(was),
RC_SVCDIR "/%s/%s",
rc_parse_service_state(RC_SERVICE_WASINACTIVE),
base, (char *) NULL);
base);
symlink(init, was);
skip_wasinactive = true;
free(was);
}
unlink(file);
}
free(file);
}
}
@ -560,43 +546,44 @@ bool rc_service_mark(const char *service, const RC_SERVICE state)
state == RC_SERVICE_STOPPED ||
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);
free(file);
}
/* Remove any options and daemons the service may have stored */
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);
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);
free(file);
rc_service_schedule_clear(service);
}
/* 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);
snprintf(file, sizeof(file), RC_SVCDIR "/%s", "scheduled");
dirs = ls_dir(file, 0);
if (dirs) {
TAILQ_FOREACH(dir, dirs, entries) {
was = rc_strcatpaths(file, dir->value, base, (char *) NULL);
snprintf(was, sizeof(was), "%s/%s/%s",
file, dir->value, base);
unlink(was);
free(was);
/* Try and remove the dir - we don't care about errors */
was = rc_strcatpaths(file, dir->value, (char *) NULL);
snprintf(was, sizeof(was), "%s/%s",
file, dir->value);
serrno = errno;
rmdir(was);
errno = serrno;
free(was);
}
rc_stringlist_free(dirs);
}
}
free(init);
return true;
@ -607,36 +594,37 @@ RC_SERVICE rc_service_state(const char *service)
{
int i;
int state = RC_SERVICE_STOPPED;
char *file;
char file[PATH_MAX];
RC_STRINGLIST *dirs;
RC_STRING *dir;
const char *base = basename_c(service);
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);
snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
rc_service_state_names[i].name, base);
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);
if (dirs) {
TAILQ_FOREACH (dir, dirs, entries) {
file = rc_strcatpaths(RC_SVCDIR, "scheduled",
dir->value,
service, (char *) NULL);
if (exists(file))
snprintf(file, sizeof(file),
RC_SVCDIR "/scheduled/%s/%s",
dir->value, service);
if (exists(file)) {
state |= RC_SERVICE_SCHEDULED;
free(file);
if (state & RC_SERVICE_SCHEDULED)
break;
}
}
rc_stringlist_free(dirs);
}
}
return state;
}
@ -646,14 +634,14 @@ char *rc_service_value_get(const char *service, const char *option)
{
FILE *fp;
char *line = NULL;
char *file = rc_strcatpaths(RC_SVCDIR, "options", service, option,
(char *) NULL);
char file[PATH_MAX];
snprintf(file, sizeof(file), RC_SVCDIR "/options/%s/%s",
service, option);
if ((fp = fopen(file, "r"))) {
line = rc_getline(fp);
fclose(fp);
}
free(file);
return line;
}
@ -663,33 +651,27 @@ bool rc_service_value_set(const char *service, const char *option,
const char *value)
{
FILE *fp;
char *path = rc_strcatpaths(RC_SVCDIR, "options", service, (char *) NULL);
char *file = rc_strcatpaths(path, option, (char *) NULL);
bool retval = false;
char file[PATH_MAX];
char *p = file;
if (mkdir(path, 0755) != 0 && errno != EEXIST) {
free(path);
free(file);
p += snprintf(file, sizeof(file), RC_SVCDIR "/options/%s", service);
if (mkdir(file, 0755) != 0 && errno != EEXIST)
return false;
}
if ((fp = fopen(file, "w"))) {
snprintf(p, sizeof(file) - (p - file), "/%s", option);
if (!(fp = fopen(file, "w")))
return false;
if (value)
fprintf(fp, "%s", value);
fclose(fp);
retval = true;
}
free(path);
free(file);
return retval;
return true;
}
librc_hidden_def(rc_service_value_set)
static pid_t _exec_service(const char *service, const char *arg)
{
char *file;
char *fifo;
char fifo[PATH_MAX];
pid_t pid = -1;
sigset_t full;
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 */
fifo = rc_strcatpaths(RC_SVCDIR, "exclusive", basename_c(service),
(char *) NULL);
snprintf(fifo, sizeof(fifo), RC_SVCDIR "/exclusive/%s",
basename_c(service));
if (mkfifo(fifo, 0600) != 0 && errno != EEXIST) {
free(fifo);
free(file);
return -1;
}
@ -745,7 +725,6 @@ static pid_t _exec_service(const char *service, const char *arg)
sigprocmask(SIG_SETMASK, &old, NULL);
free(fifo);
free(file);
return pid;
@ -782,28 +761,24 @@ librc_hidden_def(rc_service_start)
bool rc_service_schedule_start(const char *service,
const char *service_to_start)
{
char *dir;
char file[PATH_MAX];
char *p = file;
char *init;
char *file;
bool retval;
/* service may be a provided service, like net */
if (! service || ! rc_service_exists(service_to_start))
return false;
dir = rc_strcatpaths(RC_SVCDIR, "scheduled", basename_c(service),
(char *) NULL);
if (mkdir(dir, 0755) != 0 && errno != EEXIST) {
free(dir);
p += snprintf(file, sizeof(file), RC_SVCDIR "/scheduled/%s",
basename_c(service));
if (mkdir(file, 0755) != 0 && errno != EEXIST)
return false;
}
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);
free(init);
free(file);
free(dir);
return retval;
}
@ -811,20 +786,19 @@ 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;
char dir[PATH_MAX];
if (! (retval = rm_dir(dir, true)) && errno == ENOENT)
retval = true;
free(dir);
return retval;
snprintf(dir, sizeof(dir), RC_SVCDIR "/scheduled/%s",
basename_c(service));
if (! rm_dir(dir, true) && errno == ENOENT)
return true;
return false;
}
librc_hidden_def(rc_service_schedule_clear)
RC_STRINGLIST *rc_services_in_runlevel(const char *runlevel)
{
char *dir;
char dir[PATH_MAX];
RC_STRINGLIST *list;
if (! runlevel) {
@ -855,43 +829,47 @@ RC_STRINGLIST *rc_services_in_runlevel(const char *runlevel)
/* 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;
return NULL;
}
dir = rc_strcatpaths(RC_RUNLEVELDIR, runlevel, (char *) NULL);
snprintf(dir, sizeof(dir), RC_RUNLEVELDIR "/%s", runlevel);
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;
char dir[PATH_MAX];
char *p = dir;
p += snprintf(dir, sizeof(dir), RC_SVCDIR "/%s",
rc_parse_service_state(state));
if (state != RC_SERVICE_SCHEDULED)
return ls_dir(dir, LS_INITD);
if (state == RC_SERVICE_SCHEDULED) {
dirs = ls_dir(dir, 0);
list = rc_stringlist_new();
if (! dirs)
return NULL;
TAILQ_FOREACH(d, dirs, entries) {
p = rc_strcatpaths(dir, d->value, (char *) NULL);
services = ls_dir(p, LS_INITD);
free(p);
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);
free(services);
}
rc_stringlist_free(dirs);
} else {
list = ls_dir(dir, LS_INITD);
}
rc_stringlist_free(dirs);
free(dir);
return list;
}
librc_hidden_def(rc_services_in_state)
@ -900,9 +878,11 @@ bool rc_service_add(const char *runlevel, const char *service)
{
bool retval;
char *init;
char *file;
char file[PATH_MAX];
char path[MAXPATHLEN] = { '\0' };
char *p;
char *p = NULL;
char binit[PATH_MAX];
char *i;
if (! rc_runlevel_exists(runlevel)) {
errno = ENOENT;
@ -914,13 +894,15 @@ bool rc_service_add(const char *runlevel, const char *service)
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
* to the boot runlevel */
if (strcmp (runlevel, RC_LEVEL_BOOT) == 0) {
p = realpath(dirname (init), path);
free(init);
p = realpath(dirname (init), path);
if (! *p)
return false;
@ -929,33 +911,25 @@ bool rc_service_add(const char *runlevel, const char *service)
errno = EPERM;
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),
(char *) NULL);
retval = (symlink(init, file) == 0);
retval = (symlink(i, file) == 0);
free(init);
free(file);
return retval;
}
librc_hidden_def(rc_service_add)
bool rc_service_delete (const char *runlevel, const char *service)
{
char *file;
bool retval = false;
char file[PATH_MAX];
if (! runlevel || ! service)
return false;
file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename_c(service),
(char *) NULL);
snprintf(file, sizeof(file), RC_RUNLEVELDIR "/%s/%s",
runlevel, basename_c(service));
if (unlink(file) == 0)
retval = true;
free(file);
return retval;
return true;
return false;
}
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 *list;
RC_STRING *dir;
char *file;
char file[PATH_MAX];
if (! dirs)
return NULL;
list = rc_stringlist_new();
TAILQ_FOREACH (dir, dirs, entries) {
file = rc_strcatpaths(RC_SVCDIR, "scheduled", dir->value,
service, (char *) NULL);
if (exists(file))
snprintf(file, sizeof(file), RC_SVCDIR "/scheduled/%s/%s",
dir->value, service);
if (exists(file)) {
if (! list)
list = rc_stringlist_new();
rc_stringlist_add(list, file);
free(file);
}
}
rc_stringlist_free(dirs);
@ -982,11 +960,10 @@ 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);
char dir[PATH_MAX];
free(dir);
return list;
snprintf(dir, sizeof(dir), "RC_SVCDIR/scheduled/%s",
basename_c(service));
return ls_dir(dir, LS_INITD);
}
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_value_get)
librc_hidden_proto(rc_service_value_set)
librc_hidden_proto(rc_strcatpaths)
librc_hidden_proto(rc_stringlist_add)
librc_hidden_proto(rc_stringlist_addu)
librc_hidden_proto(rc_stringlist_delete)

View File

@ -27,17 +27,6 @@
#ifndef __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/queue.h>
#include <stdbool.h>
@ -444,13 +433,6 @@ void rc_stringlist_sort(RC_STRINGLIST **);
* @param list to free */
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
{
pid_t pid;

View File

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

View File

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

View File

@ -633,148 +633,14 @@ static void do_coldplug(void)
printf ("%s\n", ecolor(ECOLOR_NORMAL));
}
static bool runlevel_config(const char *service, const char *level)
static void do_newlevel(const char *newlevel)
{
char *init = rc_service_resolve(service);
char *conf;
size_t l;
bool retval;
init = dirname(init);
init = dirname(init);
l = strlen(init) + strlen(level) + strlen(service) + 10;
conf = xmalloc(sizeof(char) * l);
snprintf(conf, l, "%s/conf.d/%s.%s", init, service, level);
retval = exists(conf);
free(conf);
return retval;
}
#include "_usage.h"
#define getoptstring "o:" getoptstring_COMMON
static const struct option longopts[] = {
{ "override", 1, NULL, 'o' },
longopts_COMMON
};
static const char * const longopts_help[] = {
"override the next runlevel to change into\nwhen leaving single user or boot runlevels",
longopts_help_COMMON
};
#include "_usage.c"
int main(int argc, char **argv)
{
const char *bootlevel = NULL;
const char *sys = rc_sys();
char *newlevel = NULL;
RC_STRINGLIST *deporder = NULL;
RC_STRINGLIST *tmplist;
RC_STRING *service;
bool going_down = false;
bool interactive = false;
int depoptions = RC_DEP_STRICT | RC_DEP_TRACE;
char ksoftbuffer [PATH_MAX];
char pidstr[6];
int opt;
bool parallel;
int regen = 0;
pid_t pid;
RC_STRING *svc1;
RC_STRING *svc2 = NULL;
struct utsname uts;
const char *sys;
#ifdef __linux__
char *cmd;
char *proc;
char *p;
char *token;
#endif
applet = basename_c(argv[0]);
LIST_INIT(&service_pids);
atexit(cleanup);
if (! applet)
eerrorx("arguments required");
if (argc > 1 && (strcmp(argv[1], "--version") == 0)) {
printf("%s (OpenRC", applet);
if (sys)
printf(" [%s]", sys);
printf(") " VERSION
#ifdef BRANDING
" (" BRANDING ")"
#endif
"\n");
exit(EXIT_SUCCESS);
}
/* Run our built in applets. If we ran one, we don't return. */
run_applets(argc, argv);
argc--;
argv++;
/* Change dir to / to ensure all scripts don't use stuff in pwd */
chdir("/");
/* RUNLEVEL is set by sysvinit as is a magic number
* RC_SOFTLEVEL is set by us and is the name for this magic number
* even though all our userland documentation refers to runlevel */
RUNLEVEL = getenv("RUNLEVEL");
PREVLEVEL = getenv("PREVLEVEL");
/* Ensure our environment is pure
* Also, add our configuration to it */
env_filter();
env_config();
argc++;
argv--;
while ((opt = getopt_long(argc, argv, getoptstring,
longopts, (int *) 0)) != -1)
{
switch (opt) {
case 'o':
if (*optarg == '\0')
optarg = NULL;
exit(set_ksoftlevel(optarg) ? EXIT_SUCCESS : EXIT_FAILURE);
/* NOTREACHED */
case_RC_COMMON_GETOPT
}
}
newlevel = argv[optind++];
/* Enable logging */
setenv("EINFO_LOG", "rc", 1);
/* Export our PID */
snprintf(pidstr, sizeof(pidstr), "%d", getpid());
setenv("RC_PID", pidstr, 1);
/* Load current softlevel */
bootlevel = getenv("RC_BOOTLEVEL");
runlevel = rc_runlevel_get();
rc_logger_open(newlevel ? newlevel : runlevel);
/* Setup a signal handler */
signal_setup(SIGINT, handle_signal);
signal_setup(SIGQUIT, handle_signal);
signal_setup(SIGTERM, handle_signal);
signal_setup(SIGUSR1, handle_signal);
signal_setup(SIGWINCH, handle_signal);
if (! rc_yesno(getenv("EINFO_QUIET")))
interactive = exists(INTERACTIVE);
rc_plugin_load();
/* Check we're in the runlevel requested, ie from
* rc single
* rc shutdown
* rc reboot
*/
if (newlevel) {
if (strcmp(newlevel, RC_LEVEL_SYSINIT) == 0
#ifndef PREFIX
&& RUNLEVEL &&
@ -804,7 +670,7 @@ int main(int argc, char **argv)
uts.machine);
#endif
if (sys)
if ((sys = rc_sys()))
printf(" [%s]", sys);
printf("%s\n\n", ecolor(ECOLOR_NORMAL));
@ -876,6 +742,276 @@ int main(int argc, char **argv)
}
}
static bool runlevel_config(const char *service, const char *level)
{
char *init = rc_service_resolve(service);
char *conf;
size_t l;
bool retval;
init = dirname(init);
init = dirname(init);
l = strlen(init) + strlen(level) + strlen(service) + 10;
conf = xmalloc(sizeof(char) * l);
snprintf(conf, l, "%s/conf.d/%s.%s", init, service, level);
retval = exists(conf);
free(conf);
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"
#define getoptstring "o:" getoptstring_COMMON
static const struct option longopts[] = {
{ "override", 1, NULL, 'o' },
longopts_COMMON
};
static const char * const longopts_help[] = {
"override the next runlevel to change into\nwhen leaving single user or boot runlevels",
longopts_help_COMMON
};
#include "_usage.c"
int main(int argc, char **argv)
{
const char *bootlevel = NULL;
char *newlevel = NULL;
RC_STRINGLIST *deporder = NULL;
RC_STRINGLIST *tmplist;
RC_STRING *service;
bool going_down = false;
int depoptions = RC_DEP_STRICT | RC_DEP_TRACE;
char ksoftbuffer [PATH_MAX];
char pidstr[6];
int opt;
bool parallel;
int regen = 0;
#ifdef __linux__
char *proc;
char *p;
char *token;
#endif
applet = basename_c(argv[0]);
LIST_INIT(&service_pids);
atexit(cleanup);
if (! applet)
eerrorx("arguments required");
if (argc > 1 && (strcmp(argv[1], "--version") == 0)) {
printf("%s (OpenRC", applet);
if ((bootlevel = rc_sys()))
printf(" [%s]", bootlevel);
printf(") " VERSION
#ifdef BRANDING
" (" BRANDING ")"
#endif
"\n");
exit(EXIT_SUCCESS);
}
/* Run our built in applets. If we ran one, we don't return. */
run_applets(argc, argv);
argc--;
argv++;
/* Change dir to / to ensure all scripts don't use stuff in pwd */
chdir("/");
/* RUNLEVEL is set by sysvinit as is a magic number
* RC_SOFTLEVEL is set by us and is the name for this magic number
* even though all our userland documentation refers to runlevel */
RUNLEVEL = getenv("RUNLEVEL");
PREVLEVEL = getenv("PREVLEVEL");
/* Ensure our environment is pure
* Also, add our configuration to it */
env_filter();
env_config();
argc++;
argv--;
while ((opt = getopt_long(argc, argv, getoptstring,
longopts, (int *) 0)) != -1)
{
switch (opt) {
case 'o':
if (*optarg == '\0')
optarg = NULL;
exit(set_ksoftlevel(optarg) ? EXIT_SUCCESS : EXIT_FAILURE);
/* NOTREACHED */
case_RC_COMMON_GETOPT
}
}
newlevel = argv[optind++];
/* Enable logging */
setenv("EINFO_LOG", "rc", 1);
/* Export our PID */
snprintf(pidstr, sizeof(pidstr), "%d", getpid());
setenv("RC_PID", pidstr, 1);
/* Load current softlevel */
bootlevel = getenv("RC_BOOTLEVEL");
runlevel = rc_runlevel_get();
rc_logger_open(newlevel ? newlevel : runlevel);
/* Setup a signal handler */
signal_setup(SIGINT, handle_signal);
signal_setup(SIGQUIT, handle_signal);
signal_setup(SIGTERM, handle_signal);
signal_setup(SIGUSR1, handle_signal);
signal_setup(SIGWINCH, handle_signal);
rc_plugin_load();
/* Check we're in the runlevel requested, ie from
* rc single
* rc shutdown
* rc reboot
*/
if (newlevel)
do_newlevel(newlevel);
/* Now we start handling our children */
signal_setup(SIGCHLD, handle_signal);
@ -946,15 +1082,23 @@ int main(int argc, char **argv)
* correct order for stopping them */
stop_services = rc_services_in_state(RC_SERVICE_STARTED);
tmplist = rc_services_in_state(RC_SERVICE_INACTIVE);
if (tmplist) {
if (stop_services) {
TAILQ_CONCAT(stop_services, tmplist, entries);
free(tmplist);
} else
stop_services = tmplist;
}
tmplist = rc_services_in_state(RC_SERVICE_STARTING);
if (tmplist) {
if (stop_services) {
TAILQ_CONCAT(stop_services, tmplist, entries);
free(tmplist);
} else
stop_services = tmplist;
}
rc_stringlist_sort(&stop_services);
types_n = rc_stringlist_new();
rc_stringlist_add(types_n, "needsme");
types_nua = rc_stringlist_new();
rc_stringlist_add(types_nua, "ineed");
@ -976,10 +1120,16 @@ int main(int argc, char **argv)
start_services = rc_services_in_runlevel(bootlevel);
if (strcmp (newlevel ? newlevel : runlevel, bootlevel) != 0) {
tmplist = rc_services_in_runlevel(newlevel ? newlevel : runlevel);
if (tmplist) {
if (start_services) {
TAILQ_CONCAT(start_services, tmplist, entries);
free(tmplist);
} else
start_services = tmplist;
}
}
if (coldplugged_services)
TAILQ_FOREACH(service, coldplugged_services, entries)
rc_stringlist_addu(start_services, service->value);
}
@ -991,69 +1141,8 @@ int main(int argc, char **argv)
parallel = rc_conf_yesno("rc_parallel");
/* Now stop the services that shouldn't be running */
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);
}
}
}
if (stop_services)
do_stop_services(newlevel, parallel, going_down);
/* Wait for our services to finish */
wait_for_services();
@ -1094,15 +1183,18 @@ int main(int argc, char **argv)
hook_out = RC_HOOK_RUNLEVEL_START_OUT;
/* Re-add our coldplugged services if they stopped */
if (coldplugged_services)
TAILQ_FOREACH(service, coldplugged_services, entries)
rc_service_mark(service->value, RC_SERVICE_COLDPLUGGED);
/* Order the services to start */
if (start_services) {
rc_stringlist_sort(&start_services);
deporder = rc_deptree_depends(deptree, types_nua, start_services,
runlevel, depoptions | RC_DEP_START);
rc_stringlist_free(start_services);
start_services = deporder;
}
#ifdef __linux__
/* mark any services skipped as started */
@ -1116,46 +1208,12 @@ int main(int argc, char **argv)
}
#endif
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);
}
}
}
}
if (start_services) {
do_start_services(parallel);
/* Wait for our services to finish */
wait_for_services();
}
rc_plugin_run(RC_HOOK_RUNLEVEL_START_OUT, runlevel);
hook_out = 0;
@ -1172,14 +1230,6 @@ interactive_option:
}
#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
* 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 */

View File

@ -82,8 +82,8 @@ static RC_STRINGLIST *use_services = NULL;
static RC_STRINGLIST *services = NULL;
static RC_STRINGLIST *tmplist = NULL;
static char *service = NULL;
static char *exclusive = NULL;
static char *mtime_test = NULL;
static char exclusive[PATH_MAX] = { '\0' };
static char mtime_test[PATH_MAX] = { '\0' };
static RC_DEPTREE *deptree = NULL;
static char *runlevel = NULL;
static bool sighup = false;
@ -212,7 +212,7 @@ static const char *const tests[] = {
};
static bool in_control()
{
char *path;
char file[PATH_MAX];
time_t m;
time_t mtime;
int i = 0;
@ -230,15 +230,12 @@ static bool in_control()
return false;
while (tests[i]) {
path = rc_strcatpaths(RC_SVCDIR, tests[i], applet, (char *) NULL);
if (exists(path)) {
m = get_mtime(path, false);
if (mtime < m && m != 0) {
free(path);
snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s", tests[i], applet);
if (exists(file)) {
m = get_mtime(file, false);
if (mtime < m && m != 0)
return false;
}
}
free(path);
i++;
}
@ -247,10 +244,11 @@ static bool in_control()
static void uncoldplug()
{
char *cold = rc_strcatpaths(RC_SVCDIR, "coldplugged", applet, (char *) NULL);
if (exists(cold) && unlink(cold) != 0)
eerror("%s: unlink `%s': %s", applet, cold, strerror(errno));
free(cold);
char file[PATH_MAX];
snprintf(file, sizeof(file), RC_SVCDIR "/coldplugged/%s", applet);
if (exists(file) && unlink(file) != 0)
eerror("%s: unlink `%s': %s", applet, file, strerror(errno));
}
static void start_services(RC_STRINGLIST *list) {
@ -305,10 +303,10 @@ static void restore_state(void)
rc_service_mark(applet, RC_SERVICE_FAILED);
}
if (exclusive)
if (*exclusive) {
unlink(exclusive);
free(exclusive);
exclusive = NULL;
*exclusive = '\0';
}
}
static void cleanup(void)
@ -347,17 +345,12 @@ static void cleanup(void)
rc_stringlist_free(applet_list);
rc_stringlist_free(tmplist);
free (ibsave);
if (mtime_test)
{
if (! rc_in_plugin)
unlink(mtime_test);
free(mtime_test);
}
free(exclusive);
free(service);
free(prefix);
free(runlevel);
if (*mtime_test && ! rc_in_plugin)
unlink(mtime_test);
}
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)
{
char *path;
size_t l;
/* We create a fifo so that other services can wait until we complete */
if (! exclusive)
exclusive = rc_strcatpaths(RC_SVCDIR, "exclusive", applet, (char *) NULL);
if (! *exclusive)
snprintf(exclusive, sizeof(exclusive), RC_SVCDIR "/exclusive/%s",
applet);
if (mkfifo(exclusive, 0600) != 0 && errno != EEXIST &&
(errno != EACCES || geteuid () == 0))
eerrorx ("%s: unable to create fifo `%s': %s",
applet, exclusive, strerror(errno));
path = rc_strcatpaths(RC_SVCDIR, "exclusive", applet, (char *) NULL);
l = strlen (path) + 16;
mtime_test = xmalloc(sizeof (char) * l);
snprintf(mtime_test, l, "%s.%d", path, getpid());
free(path);
snprintf(mtime_test, sizeof(mtime_test), RC_SVCDIR "/exclusive/%s.%d", applet, getpid());
if (exists(mtime_test) && unlink(mtime_test) != 0) {
eerror("%s: unlink `%s': %s",
applet, mtime_test, strerror(errno));
free(mtime_test);
mtime_test = NULL;
*mtime_test = '\0';
return;
}
if (symlink(service, mtime_test) != 0) {
eerror("%s: symlink `%s' to `%s': %s",
applet, service, mtime_test, strerror(errno));
free(mtime_test);
mtime_test = NULL;
*mtime_test = '\0';
}
}
@ -618,8 +603,7 @@ static void unlink_mtime_test(void)
{
if (unlink(mtime_test) != 0)
eerror("%s: unlink `%s': %s", applet, mtime_test, strerror(errno));
free(mtime_test);
mtime_test = NULL;
*mtime_test = '\0';
}
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_free(restart_services);
restart_services = rc_services_in_state(RC_SERVICE_STARTED);
if (tmp) {
if (restart_services) {
TAILQ_CONCAT(restart_services, tmp, entries);
free(tmp);
} else
restart_services = tmp;
}
}
static void setup_types(void)
@ -713,7 +702,7 @@ static void svc_start(bool deps)
services = rc_deptree_depends(deptree, types_b, applet_list,
runlevel, 0);
if (TAILQ_FIRST(services)) {
if (services && TAILQ_FIRST(services)) {
eerrorn("ERROR: `%s' needs ", applet);
first = true;
TAILQ_FOREACH(svc, services, entries) {
@ -733,7 +722,7 @@ static void svc_start(bool deps)
use_services = rc_deptree_depends(deptree, types_nu, applet_list,
runlevel, depoptions);
if (! rc_runlevel_starting())
if (! rc_runlevel_starting() && use_services)
TAILQ_FOREACH(svc, use_services, entries)
if (rc_service_state(svc->value) & RC_SERVICE_STOPPED) {
pid_t pid = rc_service_start(svc->value);
@ -746,8 +735,7 @@ static void svc_start(bool deps)
runlevel, depoptions);
/* We use tmplist to hold our scheduled by list */
tmplist = rc_stringlist_new();
tmplist = NULL;
TAILQ_FOREACH(svc, services, entries) {
RC_SERVICE svcs = rc_service_state(svc->value);
if (svcs & RC_SERVICE_STARTED)
@ -768,14 +756,19 @@ static void svc_start(bool deps)
if (! svc_wait(svc->value))
eerror ("%s: timed out waiting for %s",
applet, svc->value);
if (! need_services)
continue;
if ((svcs = rc_service_state(svc->value)) & RC_SERVICE_STARTED)
continue;
TAILQ_FOREACH(svc2, need_services, entries) {
if (strcmp (svc->value, svc2->value) == 0) {
if (svcs & RC_SERVICE_INACTIVE ||
svcs & RC_SERVICE_WASINACTIVE)
{
if (! tmplist)
tmplist = rc_stringlist_new();
rc_stringlist_add(tmplist, svc->value);
else
} else
eerrorx("ERROR: cannot start %s as"
" %s would not start",
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
our scheduled list is preserved */
rc_service_mark(service, RC_SERVICE_STOPPED);
@ -813,14 +806,14 @@ static void svc_start(bool deps)
p += snprintf(p, len, "%s", svc->value);
}
free(tmp);
rc_stringlist_free(tmplist);
tmplist = NULL;
ewarnx("WARNING: %s is scheduled to start when %s has started",
applet, tmp);
}
rc_stringlist_free(services);
services = NULL;
rc_stringlist_free(tmplist);
tmplist = NULL;
}
if (ibsave)
@ -928,9 +921,10 @@ static void svc_stop(bool deps)
if (! types_m)
setup_types();
tmplist = rc_stringlist_new();
tmplist = NULL;
services = rc_deptree_depends(deptree, types_m, applet_list,
runlevel, depoptions);
if (services) {
TAILQ_FOREACH_REVERSE(svc, services, rc_stringlist, entries) {
RC_SERVICE svcs = rc_service_state(svc->value);
if (svcs & RC_SERVICE_STARTED ||
@ -944,13 +938,17 @@ static void svc_stop(bool deps)
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;
}
if (tmplist) {
TAILQ_FOREACH(svc, tmplist, entries) {
if (rc_service_state(svc->value) & RC_SERVICE_STOPPED)
continue;
@ -975,11 +973,13 @@ static void svc_stop(bool deps)
}
rc_stringlist_free(tmplist);
tmplist = NULL;
}
/* We now wait for other services that may use us and are stopping
This is important when a runlevel stops */
services = rc_deptree_depends(deptree, types_mua, applet_list,
runlevel, depoptions);
if (services) {
TAILQ_FOREACH(svc, services, entries) {
if (rc_service_state(svc->value) & RC_SERVICE_STOPPED)
continue;
@ -988,6 +988,7 @@ static void svc_stop(bool deps)
rc_stringlist_free (services);
services = NULL;
}
}
/* If we're stopping localmount, set LC_ALL=C so that
* bash doesn't load anything blocking the unmounting of /usr */
@ -1114,8 +1115,8 @@ int runscript(int argc, char **argv)
eerror("%s: cannot run until sysvinit completes", applet);
if (mkdir("/dev/.rcboot", 0755) != 0 && errno != EEXIST)
eerrorx("%s: mkdir `/dev/.rcboot': %s", applet, strerror(errno));
prefix = rc_strcatpaths("/dev/.rcboot", applet, (char *) NULL);
symlink(service, prefix);
snprintf(exclusive, sizeof(exclusive), "/dev/.rcboot/%s", applet);
symlink(service, exclusive);
exit (EXIT_FAILURE);
}
#endif

View File

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