2007-04-05 16:48:42 +05:30
|
|
|
/*
|
2009-04-24 03:01:22 +05:30
|
|
|
librc
|
|
|
|
core RC functions
|
|
|
|
*/
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-01-14 10:35:22 +05:30
|
|
|
/*
|
2009-05-01 19:41:40 +05:30
|
|
|
* Copyright (c) 2007-2008 Roy Marples <roy@marples.name>
|
2007-11-14 20:52:04 +05:30
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2008-01-04 16:36:58 +05:30
|
|
|
const char librc_copyright[] = "Copyright (c) 2007-2008 Roy Marples";
|
2007-11-19 22:11:36 +05:30
|
|
|
|
2007-04-13 19:38:16 +05:30
|
|
|
#include "librc.h"
|
2008-02-19 19:45:53 +05:30
|
|
|
#ifdef __FreeBSD__
|
2009-04-24 03:01:22 +05:30
|
|
|
# include <sys/sysctl.h>
|
2008-02-19 19:45:53 +05:30
|
|
|
#endif
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-03-19 22:41:50 +05:30
|
|
|
#define RC_RUNLEVEL RC_SVCDIR "/softlevel"
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2007-10-04 21:56:44 +05:30
|
|
|
#ifndef S_IXUGO
|
2009-04-24 03:01:22 +05:30
|
|
|
# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
|
2007-10-04 21:56:44 +05:30
|
|
|
#endif
|
|
|
|
|
2007-04-20 15:09:47 +05:30
|
|
|
/* File stream used for plugins to write environ vars to */
|
|
|
|
FILE *rc_environ_fd = NULL;
|
|
|
|
|
2007-09-28 17:59:23 +05:30
|
|
|
typedef struct rc_service_state_name {
|
2008-03-16 22:30:56 +05:30
|
|
|
RC_SERVICE state;
|
2007-09-28 17:59:23 +05:30
|
|
|
const char *name;
|
|
|
|
} rc_service_state_name_t;
|
|
|
|
|
2007-09-28 20:23:38 +05:30
|
|
|
/* We MUST list the states below 0x10 first
|
|
|
|
* The rest can be in any order */
|
2007-09-28 17:59:23 +05:30
|
|
|
static const rc_service_state_name_t rc_service_state_names[] = {
|
|
|
|
{ RC_SERVICE_STARTED, "started" },
|
|
|
|
{ RC_SERVICE_STOPPED, "stopped" },
|
|
|
|
{ RC_SERVICE_STARTING, "starting" },
|
|
|
|
{ RC_SERVICE_STOPPING, "stopping" },
|
|
|
|
{ RC_SERVICE_INACTIVE, "inactive" },
|
|
|
|
{ RC_SERVICE_WASINACTIVE, "wasinactive" },
|
2008-10-10 14:38:59 +05:30
|
|
|
{ RC_SERVICE_HOTPLUGGED, "hotplugged" },
|
2007-09-28 17:59:23 +05:30
|
|
|
{ RC_SERVICE_FAILED, "failed" },
|
|
|
|
{ RC_SERVICE_SCHEDULED, "scheduled"},
|
|
|
|
{ 0, NULL}
|
2007-04-05 16:48:42 +05:30
|
|
|
};
|
|
|
|
|
2007-10-05 15:46:14 +05:30
|
|
|
#define LS_INITD 0x01
|
2009-05-02 16:56:45 +05:30
|
|
|
#define LS_DIR 0x02
|
2008-10-10 14:07:21 +05:30
|
|
|
static RC_STRINGLIST *
|
|
|
|
ls_dir(const char *dir, int options)
|
2007-10-05 15:46:14 +05:30
|
|
|
{
|
|
|
|
DIR *dp;
|
|
|
|
struct dirent *d;
|
2008-03-17 18:55:56 +05:30
|
|
|
RC_STRINGLIST *list = NULL;
|
2007-12-14 17:54:16 +05:30
|
|
|
struct stat buf;
|
2008-03-16 22:30:56 +05:30
|
|
|
size_t l;
|
2008-03-17 18:55:56 +05:30
|
|
|
char file[PATH_MAX];
|
2008-03-16 22:30:56 +05:30
|
|
|
int r;
|
2007-10-05 15:46:14 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
list = rc_stringlist_new();
|
2008-03-16 22:30:56 +05:30
|
|
|
if ((dp = opendir(dir)) == NULL)
|
2008-10-10 14:07:21 +05:30
|
|
|
return list;
|
2008-03-16 22:30:56 +05:30
|
|
|
while (((d = readdir(dp)) != NULL)) {
|
2007-10-05 15:46:14 +05:30
|
|
|
if (d->d_name[0] != '.') {
|
|
|
|
if (options & LS_INITD) {
|
2007-12-14 17:54:16 +05:30
|
|
|
/* Check that our file really exists.
|
2008-10-10 14:07:21 +05:30
|
|
|
* This is important as a service maybe in a
|
|
|
|
* runlevel, but could have been removed. */
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(file, sizeof(file), "%s/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
dir, d->d_name);
|
2008-03-16 22:30:56 +05:30
|
|
|
r = stat(file, &buf);
|
|
|
|
if (r != 0)
|
2007-10-05 15:46:14 +05:30
|
|
|
continue;
|
|
|
|
|
|
|
|
/* .sh files are not init scripts */
|
2008-03-16 22:30:56 +05:30
|
|
|
l = strlen(d->d_name);
|
2007-10-05 15:46:14 +05:30
|
|
|
if (l > 2 && d->d_name[l - 3] == '.' &&
|
2008-01-11 21:21:40 +05:30
|
|
|
d->d_name[l - 2] == 's' &&
|
|
|
|
d->d_name[l - 1] == 'h')
|
2007-10-05 15:46:14 +05:30
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (options & LS_DIR) {
|
2008-03-16 22:30:56 +05:30
|
|
|
if (stat(d->d_name, &buf) == 0 &&
|
2009-05-02 16:56:45 +05:30
|
|
|
!S_ISDIR(buf.st_mode))
|
2007-10-05 15:46:14 +05:30
|
|
|
continue;
|
|
|
|
}
|
2008-03-16 22:30:56 +05:30
|
|
|
rc_stringlist_add(list, d->d_name);
|
2007-10-05 15:46:14 +05:30
|
|
|
}
|
|
|
|
}
|
2008-03-16 22:30:56 +05:30
|
|
|
closedir(dp);
|
|
|
|
return list;
|
2007-10-05 15:46:14 +05:30
|
|
|
}
|
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
static bool
|
|
|
|
rm_dir(const char *pathname, bool top)
|
2007-10-05 16:06:53 +05:30
|
|
|
{
|
|
|
|
DIR *dp;
|
|
|
|
struct dirent *d;
|
2008-03-17 18:55:56 +05:30
|
|
|
char file[PATH_MAX];
|
2008-03-08 18:36:29 +05:30
|
|
|
struct stat s;
|
|
|
|
bool retval = true;
|
2007-10-05 16:06:53 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
if ((dp = opendir(pathname)) == NULL)
|
|
|
|
return false;
|
2007-10-05 16:06:53 +05:30
|
|
|
|
|
|
|
errno = 0;
|
2008-03-16 22:30:56 +05:30
|
|
|
while (((d = readdir(dp)) != NULL) && errno == 0) {
|
2008-10-10 14:07:21 +05:30
|
|
|
if (strcmp(d->d_name, ".") != 0 &&
|
|
|
|
strcmp(d->d_name, "..") != 0)
|
|
|
|
{
|
|
|
|
snprintf(file, sizeof(file),
|
2009-04-24 03:01:22 +05:30
|
|
|
"%s/%s", pathname, d->d_name);
|
2008-03-17 18:55:56 +05:30
|
|
|
if (stat(file, &s) != 0) {
|
2008-03-08 18:36:29 +05:30
|
|
|
retval = false;
|
|
|
|
break;
|
|
|
|
}
|
2008-03-16 22:30:56 +05:30
|
|
|
if (S_ISDIR(s.st_mode)) {
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!rm_dir(file, true))
|
2007-10-05 16:06:53 +05:30
|
|
|
{
|
2008-03-08 18:36:29 +05:30
|
|
|
retval = false;
|
|
|
|
break;
|
2007-10-05 16:06:53 +05:30
|
|
|
}
|
|
|
|
} else {
|
2008-03-17 18:55:56 +05:30
|
|
|
if (unlink(file)) {
|
2008-03-08 18:36:29 +05:30
|
|
|
retval = false;
|
|
|
|
break;
|
2007-10-05 16:06:53 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-03-16 22:30:56 +05:30
|
|
|
closedir(dp);
|
2008-03-08 18:36:29 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!retval)
|
2008-03-16 22:30:56 +05:30
|
|
|
return false;
|
2007-10-05 16:06:53 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
if (top && rmdir(pathname) != 0)
|
|
|
|
return false;
|
2007-10-05 16:06:53 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
return true;
|
2007-10-05 16:06:53 +05:30
|
|
|
}
|
|
|
|
|
2008-02-19 21:21:59 +05:30
|
|
|
/* Other systems may need this at some point, but for now it's Linux only */
|
|
|
|
#ifdef __linux__
|
2008-10-10 14:07:21 +05:30
|
|
|
static bool
|
|
|
|
file_regex(const char *file, const char *regex)
|
2008-02-19 21:21:59 +05:30
|
|
|
{
|
|
|
|
FILE *fp;
|
2008-03-18 02:57:37 +05:30
|
|
|
char *line = NULL;
|
|
|
|
size_t len = 0;
|
2008-02-19 21:21:59 +05:30
|
|
|
regex_t re;
|
|
|
|
bool retval = false;
|
|
|
|
int result;
|
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!(fp = fopen(file, "r")))
|
2008-03-16 22:30:56 +05:30
|
|
|
return false;
|
2008-02-19 21:21:59 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
if ((result = regcomp(&re, regex, REG_EXTENDED | REG_NOSUB)) != 0) {
|
|
|
|
fclose(fp);
|
2008-10-10 14:07:21 +05:30
|
|
|
line = xmalloc(sizeof(char) * BUFSIZ);
|
2008-03-16 22:30:56 +05:30
|
|
|
regerror(result, &re, line, BUFSIZ);
|
|
|
|
fprintf(stderr, "file_regex: %s", line);
|
|
|
|
free(line);
|
|
|
|
return false;
|
2008-02-19 21:21:59 +05:30
|
|
|
}
|
|
|
|
|
2008-03-18 02:57:37 +05:30
|
|
|
while ((rc_getline(&line, &len, fp))) {
|
2008-03-16 22:30:56 +05:30
|
|
|
if (regexec(&re, line, 0, NULL, 0) == 0)
|
2008-02-19 21:21:59 +05:30
|
|
|
retval = true;
|
|
|
|
if (retval)
|
|
|
|
break;
|
|
|
|
}
|
2008-03-16 22:30:56 +05:30
|
|
|
fclose(fp);
|
2008-03-18 02:57:37 +05:30
|
|
|
free(line);
|
2008-03-16 22:30:56 +05:30
|
|
|
regfree(&re);
|
2008-02-19 21:21:59 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
return retval;
|
2008-02-19 21:21:59 +05:30
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
const char *
|
2011-01-06 11:33:43 +05:30
|
|
|
rc_sys_v2(void)
|
|
|
|
{
|
|
|
|
#define __STRING_SWITCH(x) { char* __string_switch = x; if(false) {}
|
|
|
|
#define __STRING_CASE(y) else if(strcmp(__string_switch,y) == 0)
|
|
|
|
#define __STRING_SWITCH_END() }
|
|
|
|
char* systype = rc_conf_value("rc_sys");
|
|
|
|
/* New sys identification code */
|
|
|
|
if(systype) {
|
|
|
|
char* s = systype;
|
|
|
|
// Convert to uppercase
|
|
|
|
while(s && *s) {
|
|
|
|
if(islower((unsigned char)*s))
|
|
|
|
*s = toupper((unsigned char)*s);
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
// Now do detection
|
|
|
|
__STRING_SWITCH(systype)
|
|
|
|
__STRING_CASE(RC_SYS_PREFIX) { return RC_SYS_PREFIX; }
|
|
|
|
#ifdef __FreeBSD__
|
|
|
|
__STRING_CASE(RC_SYS_JAIL) { return RC_SYS_JAIL; }
|
|
|
|
#endif /* __FreeBSD__ */
|
|
|
|
#ifdef __NetBSD__
|
|
|
|
__STRING_CASE(RC_SYS_XEN0) { return RC_SYS_XEN0; }
|
|
|
|
__STRING_CASE(RC_SYS_XENU) { return RC_SYS_XENU; }
|
|
|
|
#endif /* __NetBSD__ */
|
|
|
|
#ifdef __linux__
|
|
|
|
__STRING_CASE(RC_SYS_XEN0) { return RC_SYS_XEN0; }
|
|
|
|
__STRING_CASE(RC_SYS_XENU) { return RC_SYS_XENU; }
|
|
|
|
__STRING_CASE(RC_SYS_UML) { return RC_SYS_UML; }
|
|
|
|
__STRING_CASE(RC_SYS_VSERVER) { return RC_SYS_VSERVER; }
|
|
|
|
__STRING_CASE(RC_SYS_OPENVZ) { return RC_SYS_OPENVZ; }
|
|
|
|
__STRING_CASE(RC_SYS_LXC) { return RC_SYS_LXC; }
|
|
|
|
#endif /* __linux__ */
|
|
|
|
__STRING_SWITCH_END()
|
|
|
|
}
|
|
|
|
#undef __STRING_SWITCH
|
|
|
|
#undef __STRING_CASE
|
|
|
|
#undef __STRING_SWITCH_END
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
librc_hidden_def(rc_sys_v2)
|
|
|
|
|
|
|
|
/* Old sys identification code */
|
|
|
|
const char *
|
|
|
|
rc_sys_v1(void)
|
2008-02-19 19:45:53 +05:30
|
|
|
{
|
2008-03-03 21:27:36 +05:30
|
|
|
#ifdef PREFIX
|
2008-03-16 22:30:56 +05:30
|
|
|
return RC_SYS_PREFIX;
|
2008-03-02 16:10:08 +05:30
|
|
|
#else
|
|
|
|
|
2008-02-19 19:45:53 +05:30
|
|
|
#ifdef __FreeBSD__
|
|
|
|
int jailed = 0;
|
2008-03-16 22:30:56 +05:30
|
|
|
size_t len = sizeof(jailed);
|
2008-02-19 19:45:53 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
if (sysctlbyname("security.jail.jailed", &jailed, &len, NULL, 0) == 0)
|
2008-02-19 19:45:53 +05:30
|
|
|
if (jailed == 1)
|
2008-03-16 22:30:56 +05:30
|
|
|
return RC_SYS_JAIL;
|
2008-02-19 19:45:53 +05:30
|
|
|
#endif
|
|
|
|
|
2008-03-28 21:34:48 +05:30
|
|
|
#ifdef __NetBSD__
|
|
|
|
if (exists("/kern/xen/privcmd"))
|
|
|
|
return RC_SYS_XEN0;
|
|
|
|
if (exists("/kern/xen"))
|
|
|
|
return RC_SYS_XENU;
|
|
|
|
#endif
|
|
|
|
|
2008-02-19 19:45:53 +05:30
|
|
|
#ifdef __linux__
|
2008-03-16 22:30:56 +05:30
|
|
|
if (exists("/proc/xen")) {
|
|
|
|
if (file_regex("/proc/xen/capabilities", "control_d"))
|
|
|
|
return RC_SYS_XEN0;
|
|
|
|
return RC_SYS_XENU;
|
|
|
|
} else if (file_regex("/proc/cpuinfo", "UML"))
|
|
|
|
return RC_SYS_UML;
|
|
|
|
else if (file_regex("/proc/self/status",
|
2009-04-24 03:01:22 +05:30
|
|
|
"(s_context|VxID):[[:space:]]*[1-9]"))
|
2008-03-16 22:30:56 +05:30
|
|
|
return RC_SYS_VSERVER;
|
2008-08-19 12:06:43 +05:30
|
|
|
else if (exists("/proc/vz/veinfo") && !exists("/proc/vz/version"))
|
|
|
|
return RC_SYS_OPENVZ;
|
2008-03-16 22:30:56 +05:30
|
|
|
else if (file_regex("/proc/self/status",
|
2009-04-24 03:01:22 +05:30
|
|
|
"envID:[[:space:]]*[1-9]"))
|
2008-08-19 12:06:43 +05:30
|
|
|
return RC_SYS_OPENVZ; /* old test */
|
2008-02-19 19:45:53 +05:30
|
|
|
#endif
|
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
return NULL;
|
2008-03-02 16:10:08 +05:30
|
|
|
#endif /* PREFIX */
|
2008-02-19 19:45:53 +05:30
|
|
|
}
|
2011-01-06 11:33:43 +05:30
|
|
|
librc_hidden_def(rc_sys_v1)
|
|
|
|
|
|
|
|
const char *
|
|
|
|
rc_sys(void)
|
|
|
|
{
|
2011-01-06 12:03:03 +05:30
|
|
|
if(rc_conf_value("rc_sys")) {
|
|
|
|
return rc_sys_v2();
|
|
|
|
} else {
|
|
|
|
return rc_sys_v1();
|
|
|
|
}
|
2011-01-06 11:33:43 +05:30
|
|
|
}
|
2008-03-24 11:54:46 +05:30
|
|
|
librc_hidden_def(rc_sys)
|
2008-02-19 19:45:53 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
static const char *
|
|
|
|
rc_parse_service_state(RC_SERVICE state)
|
2007-09-28 17:59:23 +05:30
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; rc_service_state_names[i].name; i++) {
|
|
|
|
if (rc_service_state_names[i].state == state)
|
2008-03-16 22:30:56 +05:30
|
|
|
return rc_service_state_names[i].name;
|
2007-09-28 17:59:23 +05:30
|
|
|
}
|
2008-03-16 22:30:56 +05:30
|
|
|
return NULL;
|
2007-09-28 17:59:23 +05:30
|
|
|
}
|
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
bool
|
|
|
|
rc_runlevel_starting(void)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2008-03-16 22:30:56 +05:30
|
|
|
return exists(RC_STARTING);
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-04-13 19:38:16 +05:30
|
|
|
librc_hidden_def(rc_runlevel_starting)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
bool
|
|
|
|
rc_runlevel_stopping(void)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2008-03-16 22:30:56 +05:30
|
|
|
return exists(RC_STOPPING);
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-04-13 19:38:16 +05:30
|
|
|
librc_hidden_def(rc_runlevel_stopping)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
RC_STRINGLIST *rc_runlevel_list(void)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2008-03-16 22:30:56 +05:30
|
|
|
return ls_dir(RC_RUNLEVELDIR, LS_DIR);
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-10-03 19:41:55 +05:30
|
|
|
librc_hidden_def(rc_runlevel_list)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
char *
|
|
|
|
rc_runlevel_get(void)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2007-04-11 18:14:47 +05:30
|
|
|
FILE *fp;
|
2007-09-25 21:08:21 +05:30
|
|
|
char *runlevel = NULL;
|
2008-10-10 14:07:21 +05:30
|
|
|
size_t i;
|
2007-09-25 21:08:21 +05:30
|
|
|
|
2008-03-19 22:41:50 +05:30
|
|
|
if ((fp = fopen(RC_RUNLEVEL, "r"))) {
|
2008-03-16 22:30:56 +05:30
|
|
|
runlevel = xmalloc(sizeof(char) * PATH_MAX);
|
|
|
|
if (fgets(runlevel, PATH_MAX, fp)) {
|
2008-10-10 14:07:21 +05:30
|
|
|
i = strlen(runlevel) - 1;
|
2007-10-12 05:31:33 +05:30
|
|
|
if (runlevel[i] == '\n')
|
|
|
|
runlevel[i] = 0;
|
|
|
|
} else
|
|
|
|
*runlevel = '\0';
|
2008-03-16 22:30:56 +05:30
|
|
|
fclose(fp);
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!runlevel || !*runlevel) {
|
2008-03-16 22:30:56 +05:30
|
|
|
free(runlevel);
|
|
|
|
runlevel = xstrdup(RC_LEVEL_SYSINIT);
|
2007-10-12 05:31:33 +05:30
|
|
|
}
|
2007-09-25 21:08:21 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
return runlevel;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-10-02 15:27:23 +05:30
|
|
|
librc_hidden_def(rc_runlevel_get)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
bool
|
|
|
|
rc_runlevel_set(const char *runlevel)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2008-03-19 22:41:50 +05:30
|
|
|
FILE *fp = fopen(RC_RUNLEVEL, "w");
|
2007-09-29 22:12:08 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!fp)
|
2008-03-16 22:30:56 +05:30
|
|
|
return false;
|
|
|
|
fprintf(fp, "%s", runlevel);
|
|
|
|
fclose(fp);
|
|
|
|
return true;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-10-02 15:27:23 +05:30
|
|
|
librc_hidden_def(rc_runlevel_set)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
bool
|
|
|
|
rc_runlevel_exists(const char *runlevel)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2008-03-17 18:55:56 +05:30
|
|
|
char path[PATH_MAX];
|
2007-10-04 22:24:29 +05:30
|
|
|
struct stat buf;
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!runlevel)
|
2008-03-16 22:30:56 +05:30
|
|
|
return false;
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(path, sizeof(path), "%s/%s", RC_RUNLEVELDIR, runlevel);
|
2008-03-16 22:30:56 +05:30
|
|
|
if (stat(path, &buf) == 0 && S_ISDIR(buf.st_mode))
|
2008-03-17 18:55:56 +05:30
|
|
|
return true;
|
|
|
|
return false;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-04-13 19:38:16 +05:30
|
|
|
librc_hidden_def(rc_runlevel_exists)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2009-05-02 16:56:45 +05:30
|
|
|
bool
|
|
|
|
rc_runlevel_stack(const char *dst, const char *src)
|
|
|
|
{
|
|
|
|
char d[PATH_MAX], s[PATH_MAX];
|
|
|
|
|
|
|
|
if (!rc_runlevel_exists(dst) || !rc_runlevel_exists(src))
|
|
|
|
return false;
|
|
|
|
snprintf(s, sizeof(s), "../%s", src);
|
|
|
|
snprintf(d, sizeof(s), "%s/%s/%s", RC_RUNLEVELDIR, dst, src);
|
|
|
|
return (symlink(s, d) == 0 ? true : false);
|
|
|
|
}
|
|
|
|
librc_hidden_def(rc_runlevel_stack)
|
|
|
|
|
|
|
|
bool
|
|
|
|
rc_runlevel_unstack(const char *dst, const char *src)
|
|
|
|
{
|
|
|
|
char path[PATH_MAX];
|
|
|
|
|
|
|
|
snprintf(path, sizeof(path), "%s/%s/%s", RC_RUNLEVELDIR, dst, src);
|
|
|
|
return (unlink(path) == 0 ? true : false);
|
|
|
|
}
|
|
|
|
librc_hidden_def(rc_runlevel_unstack)
|
|
|
|
|
|
|
|
RC_STRINGLIST *
|
|
|
|
rc_runlevel_stacks(const char *runlevel)
|
|
|
|
{
|
|
|
|
char path[PATH_MAX];
|
|
|
|
RC_STRINGLIST *dirs;
|
|
|
|
RC_STRING *d, *dn;
|
|
|
|
|
|
|
|
if (!runlevel)
|
|
|
|
return false;
|
|
|
|
snprintf(path, sizeof(path), "%s/%s", RC_RUNLEVELDIR, runlevel);
|
|
|
|
dirs = ls_dir(path, LS_DIR);
|
|
|
|
TAILQ_FOREACH_SAFE(d, dirs, entries, dn) {
|
|
|
|
if (!rc_runlevel_exists(d->value)) {
|
|
|
|
TAILQ_REMOVE(dirs, d, entries);
|
|
|
|
free(d->value);
|
|
|
|
free(d);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dirs;
|
|
|
|
}
|
|
|
|
librc_hidden_def(rc_runlevel_stacks)
|
|
|
|
|
2010-01-08 14:09:25 +05:30
|
|
|
/* Resolve a service name to its full path */
|
2008-10-10 14:07:21 +05:30
|
|
|
char *
|
|
|
|
rc_service_resolve(const char *service)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2007-04-11 18:14:47 +05:30
|
|
|
char buffer[PATH_MAX];
|
2008-03-17 18:55:56 +05:30
|
|
|
char file[PATH_MAX];
|
|
|
|
int r;
|
2007-10-04 21:51:53 +05:30
|
|
|
struct stat buf;
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!service)
|
2008-03-16 22:30:56 +05:30
|
|
|
return NULL;
|
2007-04-11 18:14:47 +05:30
|
|
|
|
|
|
|
if (service[0] == '/')
|
2008-03-16 22:30:56 +05:30
|
|
|
return xstrdup(service);
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2008-03-03 16:03:42 +05:30
|
|
|
/* First check started services */
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s", "started", service);
|
2008-03-16 22:30:56 +05:30
|
|
|
if (lstat(file, &buf) || ! S_ISLNK(buf.st_mode)) {
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
"inactive", service);
|
2008-03-17 18:55:56 +05:30
|
|
|
if (lstat(file, &buf) || ! S_ISLNK(buf.st_mode))
|
|
|
|
*file = '\0';
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
if (*file) {
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
2008-03-16 22:30:56 +05:30
|
|
|
r = readlink(file, buffer, sizeof(buffer));
|
2007-04-11 18:14:47 +05:30
|
|
|
if (r > 0)
|
2008-03-16 22:30:56 +05:30
|
|
|
return xstrdup(buffer);
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
2007-12-14 17:54:16 +05:30
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
#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
|
|
|
|
|
|
|
|
/* System scripts take precedence over 3rd party ones */
|
|
|
|
snprintf(file, sizeof(file), RC_INITDIR "/%s", service);
|
|
|
|
if (stat(file, &buf) == 0)
|
|
|
|
return xstrdup(file);
|
|
|
|
|
2008-02-28 16:38:49 +05:30
|
|
|
#ifdef RC_PKG_INITDIR
|
2008-03-17 18:55:56 +05:30
|
|
|
/* Check RC_PKG_INITDIR */
|
|
|
|
snprintf(file, sizeof(file), RC_PKG_INITDIR "/%s", service);
|
|
|
|
if (stat(file, &buf) == 0)
|
|
|
|
return xstrdup(file);
|
2008-02-28 16:38:49 +05:30
|
|
|
#endif
|
2007-12-14 17:54:16 +05:30
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
return NULL;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-09-29 22:24:58 +05:30
|
|
|
librc_hidden_def(rc_service_resolve)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
bool
|
|
|
|
rc_service_exists(const char *service)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2007-04-11 18:14:47 +05:30
|
|
|
char *file;
|
2007-09-25 23:00:07 +05:30
|
|
|
bool retval = false;
|
2007-04-11 18:14:47 +05:30
|
|
|
int len;
|
2007-10-04 21:56:44 +05:30
|
|
|
struct stat buf;
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2009-02-28 19:42:03 +05:30
|
|
|
if (!service) {
|
|
|
|
errno = EINVAL;
|
2008-03-16 22:30:56 +05:30
|
|
|
return false;
|
2009-02-28 19:42:03 +05:30
|
|
|
}
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
len = strlen(service);
|
2007-04-11 18:14:47 +05:30
|
|
|
|
|
|
|
/* .sh files are not init scripts */
|
|
|
|
if (len > 2 && service[len - 3] == '.' &&
|
2008-01-11 21:21:40 +05:30
|
|
|
service[len - 2] == 's' &&
|
2009-02-28 19:42:03 +05:30
|
|
|
service[len - 1] == 'h') {
|
|
|
|
errno = EINVAL;
|
2008-03-16 22:30:56 +05:30
|
|
|
return false;
|
2009-02-28 19:42:03 +05:30
|
|
|
}
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2009-02-28 19:42:03 +05:30
|
|
|
if (!(file = rc_service_resolve(service))) {
|
|
|
|
errno = ENOENT;
|
2008-03-16 22:30:56 +05:30
|
|
|
return false;
|
2009-02-28 19:42:03 +05:30
|
|
|
}
|
2008-01-07 02:36:47 +05:30
|
|
|
|
2009-02-28 19:42:03 +05:30
|
|
|
if (stat(file, &buf) == 0) {
|
|
|
|
if (buf.st_mode & S_IXUGO)
|
|
|
|
retval = true;
|
|
|
|
else
|
|
|
|
errno = ENOEXEC;
|
|
|
|
}
|
2008-03-16 22:30:56 +05:30
|
|
|
free(file);
|
|
|
|
return retval;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-04-13 19:38:16 +05:30
|
|
|
librc_hidden_def(rc_service_exists)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2009-09-21 23:35:47 +05:30
|
|
|
#define OPTSTR ". '%s'; echo $opts"
|
2008-10-10 14:07:21 +05:30
|
|
|
RC_STRINGLIST *
|
|
|
|
rc_service_extra_commands(const char *service)
|
2007-07-11 00:39:41 +05:30
|
|
|
{
|
|
|
|
char *svc;
|
2007-10-12 05:31:33 +05:30
|
|
|
char *cmd = NULL;
|
|
|
|
char *buffer = NULL;
|
2008-03-18 02:57:37 +05:30
|
|
|
size_t len = 0;
|
2008-03-17 18:55:56 +05:30
|
|
|
RC_STRINGLIST *commands = NULL;
|
2007-07-11 00:39:41 +05:30
|
|
|
char *token;
|
2008-01-07 17:59:30 +05:30
|
|
|
char *p;
|
2007-07-11 00:39:41 +05:30
|
|
|
FILE *fp;
|
2008-01-22 15:59:15 +05:30
|
|
|
size_t l;
|
2007-07-11 00:39:41 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!(svc = rc_service_resolve(service)))
|
2008-03-16 22:30:56 +05:30
|
|
|
return NULL;
|
|
|
|
|
|
|
|
l = strlen(OPTSTR) + strlen(svc) + 1;
|
|
|
|
cmd = xmalloc(sizeof(char) * l);
|
|
|
|
snprintf(cmd, l, OPTSTR, svc);
|
|
|
|
free(svc);
|
|
|
|
|
|
|
|
if ((fp = popen(cmd, "r"))) {
|
2008-03-18 02:57:37 +05:30
|
|
|
rc_getline(&buffer, &len, fp);
|
|
|
|
p = buffer;
|
2008-03-17 18:55:56 +05:30
|
|
|
while ((token = strsep(&p, " "))) {
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!commands)
|
2008-03-17 18:55:56 +05:30
|
|
|
commands = rc_stringlist_new();
|
2008-03-16 22:30:56 +05:30
|
|
|
rc_stringlist_add(commands, token);
|
2008-03-17 18:55:56 +05:30
|
|
|
}
|
2008-03-16 22:30:56 +05:30
|
|
|
pclose(fp);
|
|
|
|
free(buffer);
|
|
|
|
}
|
|
|
|
free(cmd);
|
|
|
|
return commands;
|
2007-07-11 00:39:41 +05:30
|
|
|
}
|
2007-11-20 23:49:48 +05:30
|
|
|
librc_hidden_def(rc_service_extra_commands)
|
2007-07-11 00:39:41 +05:30
|
|
|
|
2007-10-24 02:13:45 +05:30
|
|
|
#define DESCSTR ". '%s'; echo \"${description%s%s}\""
|
2008-10-10 14:07:21 +05:30
|
|
|
char *
|
|
|
|
rc_service_description(const char *service, const char *option)
|
2007-07-11 00:39:41 +05:30
|
|
|
{
|
|
|
|
char *svc;
|
2008-01-07 17:59:30 +05:30
|
|
|
char *cmd;
|
2007-07-11 00:39:41 +05:30
|
|
|
char *desc = NULL;
|
2008-03-18 02:57:37 +05:30
|
|
|
size_t len = 0;
|
2007-07-11 00:39:41 +05:30
|
|
|
FILE *fp;
|
2008-01-22 15:59:15 +05:30
|
|
|
size_t l;
|
2007-07-11 00:39:41 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!(svc = rc_service_resolve(service)))
|
2008-03-16 22:30:56 +05:30
|
|
|
return NULL;
|
2007-07-11 00:39:41 +05:30
|
|
|
|
2008-03-24 04:28:25 +05:30
|
|
|
if (!option)
|
2007-07-11 00:44:37 +05:30
|
|
|
option = "";
|
2007-07-11 00:39:41 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
l = strlen(DESCSTR) + strlen(svc) + strlen(option) + 2;
|
|
|
|
cmd = xmalloc(sizeof(char) * l);
|
2008-03-24 04:28:25 +05:30
|
|
|
snprintf(cmd, l, DESCSTR, svc, *option ? "_" : "", option);
|
2008-03-16 22:30:56 +05:30
|
|
|
free(svc);
|
|
|
|
if ((fp = popen(cmd, "r"))) {
|
2008-03-18 02:57:37 +05:30
|
|
|
rc_getline(&desc, &len, fp);
|
2008-03-16 22:30:56 +05:30
|
|
|
pclose(fp);
|
2007-07-11 00:39:41 +05:30
|
|
|
}
|
2008-03-16 22:30:56 +05:30
|
|
|
free(cmd);
|
|
|
|
return desc;
|
2007-07-11 00:39:41 +05:30
|
|
|
}
|
|
|
|
librc_hidden_def(rc_service_description)
|
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
bool
|
|
|
|
rc_service_in_runlevel(const char *service, const char *runlevel)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2008-03-17 18:55:56 +05:30
|
|
|
char file[PATH_MAX];
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(file, sizeof(file), RC_RUNLEVELDIR "/%s/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
runlevel, basename_c(service));
|
2008-03-17 18:55:56 +05:30
|
|
|
return exists(file);
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-04-13 19:38:16 +05:30
|
|
|
librc_hidden_def(rc_service_in_runlevel)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
bool
|
|
|
|
rc_service_mark(const char *service, const RC_SERVICE state)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2008-03-17 18:55:56 +05:30
|
|
|
char file[PATH_MAX];
|
2007-04-11 18:14:47 +05:30
|
|
|
int i = 0;
|
|
|
|
int skip_state = -1;
|
2007-12-19 18:16:08 +05:30
|
|
|
const char *base;
|
2008-03-16 22:30:56 +05:30
|
|
|
char *init = rc_service_resolve(service);
|
2007-04-11 18:14:47 +05:30
|
|
|
bool skip_wasinactive = false;
|
2008-03-16 22:30:56 +05:30
|
|
|
int s;
|
2008-03-17 20:12:10 +05:30
|
|
|
char was[PATH_MAX];
|
2008-03-16 22:30:56 +05:30
|
|
|
RC_STRINGLIST *dirs;
|
|
|
|
RC_STRING *dir;
|
|
|
|
int serrno;
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!init)
|
2008-03-16 22:30:56 +05:30
|
|
|
return false;
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
base = basename_c(service);
|
2007-09-28 17:59:23 +05:30
|
|
|
if (state != RC_SERVICE_STOPPED) {
|
2008-04-09 04:27:42 +05:30
|
|
|
if (!exists(init)) {
|
2008-03-16 22:30:56 +05:30
|
|
|
free(init);
|
|
|
|
return false;
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
rc_parse_service_state(state), base);
|
2008-03-16 22:30:56 +05:30
|
|
|
if (exists(file))
|
|
|
|
unlink(file);
|
|
|
|
i = symlink(init, file);
|
|
|
|
if (i != 0) {
|
|
|
|
free(init);
|
|
|
|
return false;
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
|
|
|
skip_state = state;
|
|
|
|
}
|
2008-03-17 20:12:10 +05:30
|
|
|
|
2008-10-10 14:38:59 +05:30
|
|
|
if (state == RC_SERVICE_HOTPLUGGED || state == RC_SERVICE_FAILED) {
|
2008-03-16 22:30:56 +05:30
|
|
|
free(init);
|
|
|
|
return true;
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove any old states now */
|
2007-09-28 17:59:23 +05:30
|
|
|
for (i = 0; rc_service_state_names[i].name; i++) {
|
2008-03-16 22:30:56 +05:30
|
|
|
s = rc_service_state_names[i].state;
|
2007-09-28 17:59:23 +05:30
|
|
|
|
|
|
|
if ((s != skip_state &&
|
2009-04-24 03:01:22 +05:30
|
|
|
s != RC_SERVICE_STOPPED &&
|
|
|
|
s != RC_SERVICE_HOTPLUGGED &&
|
|
|
|
s != RC_SERVICE_SCHEDULED) &&
|
2008-01-11 21:21:40 +05:30
|
|
|
(! skip_wasinactive || s != RC_SERVICE_WASINACTIVE))
|
2007-04-11 18:14:47 +05:30
|
|
|
{
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
rc_service_state_names[i].name, base);
|
2008-03-16 22:30:56 +05:30
|
|
|
if (exists(file)) {
|
2007-09-28 17:59:23 +05:30
|
|
|
if ((state == RC_SERVICE_STARTING ||
|
2009-04-24 03:01:22 +05:30
|
|
|
state == RC_SERVICE_STOPPING) &&
|
2008-01-11 21:21:40 +05:30
|
|
|
s == RC_SERVICE_INACTIVE)
|
2007-04-11 18:14:47 +05:30
|
|
|
{
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(was, sizeof(was),
|
2009-04-24 03:01:22 +05:30
|
|
|
RC_SVCDIR "/%s/%s",
|
|
|
|
rc_parse_service_state(RC_SERVICE_WASINACTIVE),
|
|
|
|
base);
|
2009-01-29 18:59:04 +05:30
|
|
|
if (symlink(init, was) == -1)
|
|
|
|
return false;
|
2007-04-11 18:14:47 +05:30
|
|
|
skip_wasinactive = true;
|
|
|
|
}
|
2008-04-09 04:27:42 +05:30
|
|
|
if (unlink(file) == -1) {
|
|
|
|
free(init);
|
|
|
|
return false;
|
|
|
|
}
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove the exclusive state if we're inactive */
|
2007-09-28 17:59:23 +05:30
|
|
|
if (state == RC_SERVICE_STARTED ||
|
2008-01-11 21:21:40 +05:30
|
|
|
state == RC_SERVICE_STOPPED ||
|
|
|
|
state == RC_SERVICE_INACTIVE)
|
2007-04-11 18:14:47 +05:30
|
|
|
{
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
"exclusive", base);
|
2008-03-16 22:30:56 +05:30
|
|
|
unlink(file);
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove any options and daemons the service may have stored */
|
2007-09-28 17:59:23 +05:30
|
|
|
if (state == RC_SERVICE_STOPPED) {
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
"options", base);
|
2008-03-16 22:30:56 +05:30
|
|
|
rm_dir(file, true);
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
"daemons", base);
|
2008-03-16 22:30:56 +05:30
|
|
|
rm_dir(file, true);
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
rc_service_schedule_clear(service);
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
/* These are final states, so remove us from scheduled */
|
2007-09-28 17:59:23 +05:30
|
|
|
if (state == RC_SERVICE_STARTED || state == RC_SERVICE_STOPPED) {
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(file, sizeof(file), RC_SVCDIR "/%s", "scheduled");
|
2008-03-16 22:30:56 +05:30
|
|
|
dirs = ls_dir(file, 0);
|
2008-10-10 14:07:21 +05:30
|
|
|
TAILQ_FOREACH(dir, dirs, entries) {
|
|
|
|
snprintf(was, sizeof(was), "%s/%s/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
file, dir->value, base);
|
2008-10-10 14:07:21 +05:30
|
|
|
unlink(was);
|
|
|
|
|
|
|
|
/* Try and remove the dir; we don't care about errors */
|
|
|
|
snprintf(was, sizeof(was), "%s/%s", file, dir->value);
|
|
|
|
serrno = errno;
|
|
|
|
rmdir(was);
|
|
|
|
errno = serrno;
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
2008-10-10 14:07:21 +05:30
|
|
|
rc_stringlist_free(dirs);
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
2008-03-16 22:30:56 +05:30
|
|
|
free(init);
|
|
|
|
return true;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-09-29 22:24:58 +05:30
|
|
|
librc_hidden_def(rc_service_mark)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
RC_SERVICE
|
|
|
|
rc_service_state(const char *service)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2007-09-28 20:23:38 +05:30
|
|
|
int i;
|
|
|
|
int state = RC_SERVICE_STOPPED;
|
2008-03-17 18:55:56 +05:30
|
|
|
char file[PATH_MAX];
|
2008-03-16 22:30:56 +05:30
|
|
|
RC_STRINGLIST *dirs;
|
|
|
|
RC_STRING *dir;
|
2008-03-17 18:55:56 +05:30
|
|
|
const char *base = basename_c(service);
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2007-09-28 20:23:38 +05:30
|
|
|
for (i = 0; rc_service_state_names[i].name; i++) {
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
rc_service_state_names[i].name, base);
|
2008-03-16 22:30:56 +05:30
|
|
|
if (exists(file)) {
|
2007-09-28 20:23:38 +05:30
|
|
|
if (rc_service_state_names[i].state <= 0x10)
|
|
|
|
state = rc_service_state_names[i].state;
|
|
|
|
else
|
|
|
|
state |= rc_service_state_names[i].state;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state & RC_SERVICE_STOPPED) {
|
2008-03-16 22:30:56 +05:30
|
|
|
dirs = ls_dir(RC_SVCDIR "/scheduled", 0);
|
2008-10-10 14:07:21 +05:30
|
|
|
TAILQ_FOREACH (dir, dirs, entries) {
|
|
|
|
snprintf(file, sizeof(file),
|
2009-04-24 03:01:22 +05:30
|
|
|
RC_SVCDIR "/scheduled/%s/%s",
|
|
|
|
dir->value, service);
|
2008-10-10 14:07:21 +05:30
|
|
|
if (exists(file)) {
|
|
|
|
state |= RC_SERVICE_SCHEDULED;
|
|
|
|
break;
|
2008-03-17 18:55:56 +05:30
|
|
|
}
|
2007-09-28 20:23:38 +05:30
|
|
|
}
|
2008-10-10 14:07:21 +05:30
|
|
|
rc_stringlist_free(dirs);
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
return state;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-04-13 19:38:16 +05:30
|
|
|
librc_hidden_def(rc_service_state)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
char *
|
|
|
|
rc_service_value_get(const char *service, const char *option)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2007-04-11 18:14:47 +05:30
|
|
|
FILE *fp;
|
2008-01-07 17:59:30 +05:30
|
|
|
char *line = NULL;
|
2008-03-18 02:57:37 +05:30
|
|
|
size_t len = 0;
|
2008-03-17 18:55:56 +05:30
|
|
|
char file[PATH_MAX];
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(file, sizeof(file), RC_SVCDIR "/options/%s/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
service, option);
|
2008-03-16 22:30:56 +05:30
|
|
|
if ((fp = fopen(file, "r"))) {
|
2008-03-18 02:57:37 +05:30
|
|
|
rc_getline(&line, &len, fp);
|
2008-03-16 22:30:56 +05:30
|
|
|
fclose(fp);
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
2007-09-29 22:12:08 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
return line;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-10-03 19:52:45 +05:30
|
|
|
librc_hidden_def(rc_service_value_get)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
bool
|
|
|
|
rc_service_value_set(const char *service, const char *option,
|
2009-04-24 03:01:22 +05:30
|
|
|
const char *value)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2007-04-11 18:14:47 +05:30
|
|
|
FILE *fp;
|
2008-03-17 18:55:56 +05:30
|
|
|
char file[PATH_MAX];
|
|
|
|
char *p = file;
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
p += snprintf(file, sizeof(file), RC_SVCDIR "/options/%s", service);
|
|
|
|
if (mkdir(file, 0755) != 0 && errno != EEXIST)
|
2008-03-16 22:30:56 +05:30
|
|
|
return false;
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(p, sizeof(file) - (p - file), "/%s", option);
|
|
|
|
if (!(fp = fopen(file, "w")))
|
|
|
|
return false;
|
|
|
|
if (value)
|
|
|
|
fprintf(fp, "%s", value);
|
|
|
|
fclose(fp);
|
|
|
|
return true;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-10-03 19:52:45 +05:30
|
|
|
librc_hidden_def(rc_service_value_set)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
bool
|
|
|
|
rc_service_schedule_start(const char *service, const char *service_to_start)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2008-03-17 18:55:56 +05:30
|
|
|
char file[PATH_MAX];
|
|
|
|
char *p = file;
|
2007-04-11 18:14:47 +05:30
|
|
|
char *init;
|
2007-09-29 22:12:08 +05:30
|
|
|
bool retval;
|
2007-04-11 18:14:47 +05:30
|
|
|
|
|
|
|
/* service may be a provided service, like net */
|
2008-03-16 22:30:56 +05:30
|
|
|
if (! service || ! rc_service_exists(service_to_start))
|
|
|
|
return false;
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
p += snprintf(file, sizeof(file), RC_SVCDIR "/scheduled/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
basename_c(service));
|
2008-03-17 18:55:56 +05:30
|
|
|
if (mkdir(file, 0755) != 0 && errno != EEXIST)
|
2008-03-16 22:30:56 +05:30
|
|
|
return false;
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
init = rc_service_resolve(service_to_start);
|
2008-10-10 14:07:21 +05:30
|
|
|
snprintf(p, sizeof(file) - (p - file),
|
2009-04-24 03:01:22 +05:30
|
|
|
"/%s", basename_c(service_to_start));
|
2008-03-16 22:30:56 +05:30
|
|
|
retval = (exists(file) || symlink(init, file) == 0);
|
|
|
|
free(init);
|
|
|
|
return retval;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-10-03 19:48:52 +05:30
|
|
|
librc_hidden_def(rc_service_schedule_start)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
bool
|
|
|
|
rc_service_schedule_clear(const char *service)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2008-03-17 18:55:56 +05:30
|
|
|
char dir[PATH_MAX];
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(dir, sizeof(dir), RC_SVCDIR "/scheduled/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
basename_c(service));
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!rm_dir(dir, true) && errno == ENOENT)
|
2008-03-17 18:55:56 +05:30
|
|
|
return true;
|
|
|
|
return false;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-10-03 19:48:52 +05:30
|
|
|
librc_hidden_def(rc_service_schedule_clear)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
RC_STRINGLIST *
|
|
|
|
rc_services_in_runlevel(const char *runlevel)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2008-03-17 18:55:56 +05:30
|
|
|
char dir[PATH_MAX];
|
2008-10-10 14:07:21 +05:30
|
|
|
RC_STRINGLIST *list = NULL;
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!runlevel) {
|
2008-03-03 16:03:42 +05:30
|
|
|
#ifdef RC_PKG_INITDIR
|
2008-03-16 22:30:56 +05:30
|
|
|
RC_STRINGLIST *pkg = ls_dir(RC_PKG_INITDIR, LS_INITD);
|
2008-03-03 16:03:42 +05:30
|
|
|
#endif
|
|
|
|
#ifdef RC_LOCAL_INITDIR
|
2008-03-16 22:30:56 +05:30
|
|
|
RC_STRINGLIST *local = ls_dir(RC_LOCAL_INITDIR, LS_INITD);
|
2008-02-28 16:38:49 +05:30
|
|
|
#endif
|
2007-12-14 17:54:16 +05:30
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
list = ls_dir(RC_INITDIR, LS_INITD);
|
2008-02-28 16:38:49 +05:30
|
|
|
|
|
|
|
#ifdef RC_PKG_INITDIR
|
2008-10-10 14:07:21 +05:30
|
|
|
TAILQ_CONCAT(list, pkg, entries);
|
|
|
|
free(pkg);
|
2008-03-03 16:03:42 +05:30
|
|
|
#endif
|
2008-03-19 20:45:09 +05:30
|
|
|
#ifdef RC_LOCAL_INITDIR
|
2008-10-10 14:07:21 +05:30
|
|
|
TAILQ_CONCAT(list, local, entries);
|
|
|
|
free(local);
|
2008-02-28 16:38:49 +05:30
|
|
|
#endif
|
2008-03-16 22:30:56 +05:30
|
|
|
return list;
|
2007-12-14 17:54:16 +05:30
|
|
|
}
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2007-04-11 18:14:47 +05:30
|
|
|
/* These special levels never contain any services */
|
2008-10-10 14:07:21 +05:30
|
|
|
if (strcmp(runlevel, RC_LEVEL_SINGLE) != 0) {
|
|
|
|
snprintf(dir, sizeof(dir), RC_RUNLEVELDIR "/%s", runlevel);
|
|
|
|
list = ls_dir(dir, LS_INITD);
|
2008-03-16 22:30:56 +05:30
|
|
|
}
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!list)
|
|
|
|
list = rc_stringlist_new();
|
2008-03-16 22:30:56 +05:30
|
|
|
return list;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-04-13 19:38:16 +05:30
|
|
|
librc_hidden_def(rc_services_in_runlevel)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2009-05-02 16:56:45 +05:30
|
|
|
RC_STRINGLIST *
|
|
|
|
rc_services_in_runlevel_stacked(const char *runlevel)
|
|
|
|
{
|
|
|
|
RC_STRINGLIST *list, *stacks, *sl;
|
|
|
|
RC_STRING *stack;
|
|
|
|
|
|
|
|
list = rc_services_in_runlevel(runlevel);
|
|
|
|
stacks = rc_runlevel_stacks(runlevel);
|
|
|
|
TAILQ_FOREACH (stack, stacks, entries) {
|
|
|
|
sl = rc_services_in_runlevel(stack->value);
|
|
|
|
if (list != NULL) {
|
|
|
|
TAILQ_CONCAT(list, sl, entries);
|
|
|
|
free(sl);
|
|
|
|
} else
|
|
|
|
list = sl;
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
librc_hidden_def(rc_services_in_runlevel_stacked)
|
|
|
|
|
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
RC_STRINGLIST *
|
|
|
|
rc_services_in_state(RC_SERVICE state)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2008-03-16 22:30:56 +05:30
|
|
|
RC_STRINGLIST *services;
|
2008-10-10 14:07:21 +05:30
|
|
|
RC_STRINGLIST *list;
|
2008-03-16 22:30:56 +05:30
|
|
|
RC_STRINGLIST *dirs;
|
|
|
|
RC_STRING *d;
|
2008-03-17 18:55:56 +05:30
|
|
|
char dir[PATH_MAX];
|
|
|
|
char *p = dir;
|
|
|
|
|
|
|
|
p += snprintf(dir, sizeof(dir), RC_SVCDIR "/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
rc_parse_service_state(state));
|
2008-03-17 18:55:56 +05:30
|
|
|
|
|
|
|
if (state != RC_SERVICE_SCHEDULED)
|
|
|
|
return ls_dir(dir, LS_INITD);
|
|
|
|
|
|
|
|
dirs = ls_dir(dir, 0);
|
2008-10-10 14:07:21 +05:30
|
|
|
list = rc_stringlist_new();
|
2008-03-17 18:55:56 +05:30
|
|
|
if (! dirs)
|
2008-10-10 14:07:21 +05:30
|
|
|
return list;
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
TAILQ_FOREACH(d, dirs, entries) {
|
|
|
|
snprintf(p, sizeof(dir) - (p - dir), "/%s", d->value);
|
|
|
|
services = ls_dir(dir, LS_INITD);
|
2008-10-10 14:07:21 +05:30
|
|
|
if (services) {
|
2008-03-16 22:57:13 +05:30
|
|
|
TAILQ_CONCAT(list, services, entries);
|
2008-03-16 22:30:56 +05:30
|
|
|
free(services);
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
|
|
|
}
|
2008-03-17 18:55:56 +05:30
|
|
|
rc_stringlist_free(dirs);
|
2008-03-16 22:30:56 +05:30
|
|
|
return list;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-04-13 19:38:16 +05:30
|
|
|
librc_hidden_def(rc_services_in_state)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
bool
|
|
|
|
rc_service_add(const char *runlevel, const char *service)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2007-09-25 23:00:07 +05:30
|
|
|
bool retval;
|
2007-04-11 18:14:47 +05:30
|
|
|
char *init;
|
2008-03-17 18:55:56 +05:30
|
|
|
char file[PATH_MAX];
|
2008-03-16 22:30:56 +05:30
|
|
|
char path[MAXPATHLEN] = { '\0' };
|
2008-03-17 18:55:56 +05:30
|
|
|
char *p = NULL;
|
|
|
|
char binit[PATH_MAX];
|
|
|
|
char *i;
|
2007-04-11 18:14:47 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!rc_runlevel_exists(runlevel)) {
|
2007-04-11 18:14:47 +05:30
|
|
|
errno = ENOENT;
|
2008-03-16 22:30:56 +05:30
|
|
|
return false;
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
if (rc_service_in_runlevel(service, runlevel)) {
|
2007-04-11 18:14:47 +05:30
|
|
|
errno = EEXIST;
|
2008-03-16 22:30:56 +05:30
|
|
|
return false;
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
i = init = rc_service_resolve(service);
|
|
|
|
snprintf(file, sizeof(file), RC_RUNLEVELDIR "/%s/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
runlevel, basename_c(service));
|
2007-12-14 17:54:16 +05:30
|
|
|
|
|
|
|
/* We need to ensure that only things in /etc/init.d are added
|
|
|
|
* to the boot runlevel */
|
2008-10-10 14:07:21 +05:30
|
|
|
if (strcmp(runlevel, RC_LEVEL_BOOT) == 0) {
|
2008-03-24 15:43:49 +05:30
|
|
|
p = realpath(dirname(init), path);
|
2008-10-10 14:07:21 +05:30
|
|
|
if (!*p) {
|
2008-03-24 15:43:49 +05:30
|
|
|
free(init);
|
2008-03-16 22:30:56 +05:30
|
|
|
return false;
|
2008-03-24 15:43:49 +05:30
|
|
|
}
|
|
|
|
if (strcmp(path, RC_INITDIR) != 0) {
|
|
|
|
free(init);
|
2007-12-14 17:54:16 +05:30
|
|
|
errno = EPERM;
|
2008-03-16 22:30:56 +05:30
|
|
|
return false;
|
2007-12-14 17:54:16 +05:30
|
|
|
}
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(binit, sizeof(binit), RC_INITDIR "/%s", service);
|
|
|
|
i = binit;
|
2007-12-14 17:54:16 +05:30
|
|
|
}
|
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
retval = (symlink(i, file) == 0);
|
2008-03-16 22:30:56 +05:30
|
|
|
free(init);
|
|
|
|
return retval;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-04-13 19:38:16 +05:30
|
|
|
librc_hidden_def(rc_service_add)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
bool
|
|
|
|
rc_service_delete(const char *runlevel, const char *service)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2008-03-17 18:55:56 +05:30
|
|
|
char file[PATH_MAX];
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(file, sizeof(file), RC_RUNLEVELDIR "/%s/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
runlevel, basename_c(service));
|
2008-03-16 22:30:56 +05:30
|
|
|
if (unlink(file) == 0)
|
2008-03-17 18:55:56 +05:30
|
|
|
return true;
|
|
|
|
return false;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-04-13 19:38:16 +05:30
|
|
|
librc_hidden_def(rc_service_delete)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
RC_STRINGLIST *
|
|
|
|
rc_services_scheduled_by(const char *service)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2008-03-16 22:30:56 +05:30
|
|
|
RC_STRINGLIST *dirs = ls_dir(RC_SVCDIR "/scheduled", 0);
|
2008-10-10 14:07:21 +05:30
|
|
|
RC_STRINGLIST *list = rc_stringlist_new();
|
2008-03-16 22:30:56 +05:30
|
|
|
RC_STRING *dir;
|
2008-03-17 18:55:56 +05:30
|
|
|
char file[PATH_MAX];
|
|
|
|
|
2008-03-16 22:30:56 +05:30
|
|
|
TAILQ_FOREACH (dir, dirs, entries) {
|
2008-03-17 18:55:56 +05:30
|
|
|
snprintf(file, sizeof(file), RC_SVCDIR "/scheduled/%s/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
dir->value, service);
|
2008-10-10 14:07:21 +05:30
|
|
|
if (exists(file))
|
2008-03-16 22:30:56 +05:30
|
|
|
rc_stringlist_add(list, file);
|
2007-04-11 18:14:47 +05:30
|
|
|
}
|
2008-03-16 22:30:56 +05:30
|
|
|
rc_stringlist_free(dirs);
|
|
|
|
return list;
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-04-13 19:38:16 +05:30
|
|
|
librc_hidden_def(rc_services_scheduled_by)
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-10-10 14:07:21 +05:30
|
|
|
RC_STRINGLIST *
|
|
|
|
rc_services_scheduled(const char *service)
|
2007-04-05 16:48:42 +05:30
|
|
|
{
|
2008-03-17 18:55:56 +05:30
|
|
|
char dir[PATH_MAX];
|
2007-04-05 16:48:42 +05:30
|
|
|
|
2008-03-19 17:56:09 +05:30
|
|
|
snprintf(dir, sizeof(dir), RC_SVCDIR "/scheduled/%s",
|
2009-04-24 03:01:22 +05:30
|
|
|
basename_c(service));
|
2008-03-17 18:55:56 +05:30
|
|
|
return ls_dir(dir, LS_INITD);
|
2007-04-05 16:48:42 +05:30
|
|
|
}
|
2007-04-13 19:38:16 +05:30
|
|
|
librc_hidden_def(rc_services_scheduled)
|