aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2009-04-17 22:55:11 +0000
committerRoy Marples <roy@marples.name>2009-04-17 22:55:11 +0000
commitee54bb937261b17c6767868607e95b2c560bd4b1 (patch)
treefadb67fcf50fe29cd10ac0d4b9ae8d76beebd970 /src
parent7138c1532c6546a6939ec8f7fa37da708edc0ac6 (diff)
Using fifos for locking can be error prone.
flocks are safer, as we only use tmpfs for our lock files. I don't know how this works for inactive just yet though ...
Diffstat (limited to 'src')
-rw-r--r--src/includes/rc-misc.h2
-rw-r--r--src/rc/rc-misc.c52
-rw-r--r--src/rc/runscript.c207
3 files changed, 97 insertions, 164 deletions
diff --git a/src/includes/rc-misc.h b/src/includes/rc-misc.h
index 113662f6..42447155 100644
--- a/src/includes/rc-misc.h
+++ b/src/includes/rc-misc.h
@@ -150,6 +150,8 @@ bool rc_conf_yesno(const char *var);
void env_filter(void);
void env_config(void);
int signal_setup(int sig, void (*handler)(int));
+int svc_lock(const char *);
+int svc_unlock(const char *, int);
pid_t exec_service(const char *, const char *);
#define service_start(service) exec_service(service, "start");
diff --git a/src/rc/rc-misc.c b/src/rc/rc-misc.c
index 0d9511f3..5ebd1ddc 100644
--- a/src/rc/rc-misc.c
+++ b/src/rc/rc-misc.c
@@ -29,6 +29,7 @@
* SUCH DAMAGE.
*/
+#include <sys/file.h>
#include <sys/types.h>
#include <sys/utsname.h>
@@ -38,6 +39,7 @@
#endif
#include <ctype.h>
+#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
@@ -270,30 +272,56 @@ signal_setup(int sig, void (*handler)(int))
return sigaction(sig, &sa, NULL);
}
+int
+svc_lock(const char *applet)
+{
+ char file[PATH_MAX];
+ int fd;
+
+ snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s", applet);
+ fd = open(file, O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
+ if (fd == -1)
+ return -1;
+ if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+int
+svc_unlock(const char *applet, int fd)
+{
+ char file[PATH_MAX];
+
+ snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s", applet);
+ close(fd);
+ unlink(file);
+ return -1;
+}
+
pid_t
exec_service(const char *service, const char *arg)
{
- char *file;
- char fifo[PATH_MAX];
+ char *file, sfd[32];
+ int fd;
pid_t pid = -1;
sigset_t full;
sigset_t old;
struct sigaction sa;
+ fd = svc_lock(basename_c(service));
+ if (fd == -1)
+ return -1;
+
file = rc_service_resolve(service);
if (!exists(file)) {
rc_service_mark(service, RC_SERVICE_STOPPED);
+ svc_unlock(basename_c(service), fd);
free(file);
return 0;
}
-
- /* We create a fifo so that other services can wait until we complete */
- snprintf(fifo, sizeof(fifo), RC_SVCDIR "/exclusive/%s",
- basename_c(service));
- if (mkfifo(fifo, 0600) != 0 && errno != EEXIST) {
- free(file);
- return -1;
- }
+ snprintf(sfd, sizeof(sfd), "%d", fd);
/* We need to block signals until we have forked */
memset(&sa, 0, sizeof (sa));
@@ -316,10 +344,10 @@ exec_service(const char *service, const char *arg)
sigprocmask(SIG_SETMASK, &old, NULL);
/* Safe to run now */
- execl(file, file, arg, (char *) NULL);
+ execl(file, file, "--lockfd", sfd, arg, (char *) NULL);
fprintf(stderr, "unable to exec `%s': %s\n",
file, strerror(errno));
- unlink(fifo);
+ svc_unlock(basename_c(service), fd);
_exit(EXIT_FAILURE);
}
diff --git a/src/rc/runscript.c b/src/rc/runscript.c
index d1215841..8475633b 100644
--- a/src/rc/runscript.c
+++ b/src/rc/runscript.c
@@ -86,8 +86,7 @@ static RC_STRINGLIST *use_services = NULL;
static RC_STRINGLIST *services = NULL;
static RC_STRINGLIST *tmplist = NULL;
static char *service = NULL;
-static char exclusive[PATH_MAX] = { '\0' };
-static char mtime_test[PATH_MAX] = { '\0' };
+static int exclusive_fd = -1;
static RC_DEPTREE *deptree = NULL;
static char *runlevel = NULL;
static bool sighup = false;
@@ -195,58 +194,6 @@ handle_signal(int sig)
errno = serrno;
}
-static time_t
-get_mtime(const char *pathname, bool follow_link)
-{
- struct stat buf;
- int retval;
-
- if (!pathname)
- return 0;
- retval = follow_link ? stat(pathname, &buf) : lstat(pathname, &buf);
- if (!retval)
- return buf.st_mtime;
- errno = 0;
- return 0;
-}
-
-static const char *const tests[] = {
- "starting", "started", "stopping", "inactive", "wasinactive", NULL
-};
-static bool
-in_control()
-{
- char file[PATH_MAX];
- time_t m;
- time_t mtime;
- int i = 0;
-
- if (sighup)
- return false;
-
- if (!*mtime_test || !exists(mtime_test))
- return false;
-
- if (rc_service_state(applet) & RC_SERVICE_STOPPED)
- return false;
-
- if (!(mtime = get_mtime(mtime_test, false)))
- return false;
-
- while (tests[i]) {
- 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;
- }
- i++;
- }
-
- return true;
-}
-
static void
unhotplug()
{
@@ -294,7 +241,7 @@ restore_state(void)
{
RC_SERVICE state;
- if (rc_in_plugin || !in_control())
+ if (rc_in_plugin || exclusive_fd == -1)
return;
state = rc_service_state(applet);
if (state & RC_SERVICE_STOPPING) {
@@ -312,11 +259,7 @@ restore_state(void)
if (rc_runlevel_starting())
rc_service_mark(applet, RC_SERVICE_FAILED);
}
-
- if (*exclusive) {
- unlink(exclusive);
- *exclusive = '\0';
- }
+ exclusive_fd = svc_unlock(applet, exclusive_fd);
}
static void
@@ -360,9 +303,6 @@ cleanup(void)
free(prefix);
free(runlevel);
#endif
-
- if (*mtime_test && !rc_in_plugin)
- unlink(mtime_test);
}
static int
@@ -539,11 +479,11 @@ svc_exec(const char *arg1, const char *arg2)
static bool
svc_wait(const char *svc)
{
- char fifo[PATH_MAX];
+ char file[PATH_MAX];
struct timespec ts;
int nloops = WAIT_MAX * (ONE_SECOND / WAIT_INTERVAL);
int sloops = (ONE_SECOND / WAIT_INTERVAL) * 5;
- bool retval = false;
+ int fd;
bool forever = false;
RC_STRINGLIST *keywords;
@@ -553,20 +493,28 @@ svc_wait(const char *svc)
forever = true;
rc_stringlist_free(keywords);
- snprintf(fifo, sizeof(fifo), RC_SVCDIR "/exclusive/%s",
+ snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s",
basename_c(svc));
ts.tv_sec = 0;
ts.tv_nsec = WAIT_INTERVAL;
while (nloops) {
- if (!exists(fifo)) {
- retval = true;
- break;
+ fd = open(file, O_RDONLY | O_NONBLOCK);
+ if (fd != -1) {
+ if (flock(fd, LOCK_SH | LOCK_NB) == 0) {
+ close(fd);
+ return true;
+ }
+ close(fd);
}
+ if (errno == ENOENT)
+ return true;
+ if (errno != EWOULDBLOCK)
+ eerrorx("%s: open `%s': %s", applet, file, strerror(errno));
if (nanosleep(&ts, NULL) == -1) {
if (errno != EINTR)
- break;
+ return false;
}
if (!forever) {
@@ -579,9 +527,7 @@ svc_wait(const char *svc)
}
}
- if (!exists(fifo))
- retval = true;
- return retval;
+ return false;
}
static RC_SERVICE
@@ -618,45 +564,6 @@ svc_status(void)
}
static void
-make_exclusive(void)
-{
- /* We create a fifo so that other services can wait until we complete */
- 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));
-
- 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));
- *mtime_test = '\0';
- return;
- }
-
- if (symlink(service, mtime_test) != 0) {
- eerror("%s: symlink `%s' to `%s': %s",
- applet, service, mtime_test, strerror(errno));
- *mtime_test = '\0';
- }
-}
-
-static void
-unlink_mtime_test(void)
-{
- if (unlink(mtime_test) != 0)
- eerror("%s: unlink `%s': %s",
- applet, mtime_test, strerror(errno));
- *mtime_test = '\0';
-}
-
-static void
get_started_services(void)
{
RC_STRINGLIST *tmp = rc_services_in_state(RC_SERVICE_INACTIVE);
@@ -722,24 +629,25 @@ svc_start(bool deps)
" next runlevel", applet);
}
+ if (exclusive_fd == -1)
+ exclusive_fd = svc_lock(applet);
+ if (exclusive_fd == -1) {
+ if (errno == EACCES)
+ eerrorx("%s: superuser access required", applet);
+ if (state & RC_SERVICE_STOPPING)
+ ewarnx("WARNING: %s is stopping", applet);
+ else
+ ewarnx("WARNING: %s is already starting", applet);
+ }
+
if (state & RC_SERVICE_STARTED) {
ewarn("WARNING: %s has already been started", applet);
return;
- } else if (state & RC_SERVICE_STARTING)
- ewarnx("WARNING: %s is already starting", applet);
- else if (state & RC_SERVICE_STOPPING)
- ewarnx("WARNING: %s is stopping", applet);
- else if (state & RC_SERVICE_INACTIVE && ! background)
+ } else if (state & RC_SERVICE_INACTIVE && ! background)
ewarnx("WARNING: %s has already started, but is inactive",
applet);
-
- if (!rc_service_mark(service, RC_SERVICE_STARTING)) {
- if (errno == EACCES)
- eerrorx("%s: superuser access required", applet);
- eerrorx("ERROR: %s has been started by something else", applet);
- }
-
- make_exclusive();
+
+ rc_service_mark(service, RC_SERVICE_STARTING);
hook_out = RC_HOOK_SERVICE_START_OUT;
rc_plugin_run(RC_HOOK_SERVICE_START_IN, applet);
@@ -838,7 +746,6 @@ svc_start(bool deps)
/* Set the state now, then unlink our exclusive so that
our scheduled list is preserved */
rc_service_mark(service, RC_SERVICE_STOPPED);
- unlink_mtime_test();
rc_stringlist_free(use_services);
use_services = NULL;
@@ -885,23 +792,18 @@ svc_start(bool deps)
if (ibsave)
unsetenv("IN_BACKGROUND");
- if (in_control()) {
- if (!started)
- eerrorx("ERROR: %s failed to start", applet);
- } else {
+ if (!started)
+ eerrorx("ERROR: %s failed to start", applet);
+ else {
if (rc_service_state(service) & RC_SERVICE_INACTIVE)
ewarnx("WARNING: %s has started, but is inactive",
applet);
- else
- ewarnx("WARNING: %s not under our control, aborting",
- applet);
}
rc_service_mark(service, RC_SERVICE_STARTED);
- unlink_mtime_test();
+ exclusive_fd = svc_unlock(applet, exclusive_fd);
hook_out = RC_HOOK_SERVICE_START_OUT;
rc_plugin_run(RC_HOOK_SERVICE_START_DONE, applet);
- unlink(exclusive);
/* Now start any scheduled services */
services = rc_services_scheduled(service);
@@ -948,28 +850,29 @@ svc_stop(bool deps)
!(state & RC_SERVICE_INACTIVE))
exit (EXIT_FAILURE);
+ if (exclusive_fd == -1)
+ exclusive_fd = svc_lock(applet);
+ if (exclusive_fd == -1) {
+ if (errno == EACCES)
+ eerrorx("%s: superuser access required", applet);
+ if (state & RC_SERVICE_STOPPING)
+ ewarnx("WARNING: %s is already stopping", applet);
+ eerrorx("ERROR: %d %s has been stopped by something else", exclusive_fd, applet);
+ }
if (state & RC_SERVICE_STOPPED) {
ewarn("WARNING: %s is already stopped", applet);
return;
- } else if (state & RC_SERVICE_STOPPING)
- ewarnx("WARNING: %s is already stopping", applet);
-
- if (!rc_service_mark(service, RC_SERVICE_STOPPING)) {
- if (errno == EACCES)
- eerrorx("%s: superuser access required", applet);
- eerrorx("ERROR: %s has been stopped by something else", applet);
}
- make_exclusive();
-
+ rc_service_mark(service, RC_SERVICE_STOPPING);
hook_out = RC_HOOK_SERVICE_STOP_OUT;
rc_plugin_run(RC_HOOK_SERVICE_STOP_IN, applet);
if (!rc_runlevel_stopping()) {
if (rc_service_in_runlevel(service, RC_LEVEL_SYSINIT))
- ewarn ("WARNING: you are stopping a sysinit service");
+ ewarn("WARNING: you are stopping a sysinit service");
else if (rc_service_in_runlevel(service, RC_LEVEL_BOOT))
- ewarn ("WARNING: you are stopping a boot service");
+ ewarn("WARNING: you are stopping a boot service");
}
if (deps && !(state & RC_SERVICE_WASINACTIVE)) {
@@ -1062,9 +965,6 @@ svc_stop(bool deps)
if (ibsave)
unsetenv("IN_BACKGROUND");
- if (!in_control())
- ewarnx("WARNING: %s not under our control, aborting", applet);
-
if (!stopped)
eerrorx("ERROR: %s failed to stop", applet);
@@ -1073,10 +973,8 @@ svc_stop(bool deps)
else
rc_service_mark(service, RC_SERVICE_STOPPED);
- unlink_mtime_test();
hook_out = RC_HOOK_SERVICE_STOP_OUT;
rc_plugin_run(RC_HOOK_SERVICE_STOP_DONE, applet);
- unlink(exclusive);
hook_out = 0;
rc_plugin_run(RC_HOOK_SERVICE_STOP_OUT, applet);
}
@@ -1148,18 +1046,20 @@ service_plugable(void)
}
#include "_usage.h"
-#define getoptstring "dDsv" getoptstring_COMMON
+#define getoptstring "dDsvl:" getoptstring_COMMON
#define extraopts "stop | start | restart | describe | zap"
static const struct option longopts[] = {
{ "debug", 0, NULL, 'd'},
{ "ifstarted", 0, NULL, 's'},
{ "nodeps", 0, NULL, 'D'},
+ { "lockfd", 1, NULL, 'l'},
longopts_COMMON
};
static const char *const longopts_help[] = {
"set xtrace when running the script",
"only run commands when started",
"ignore dependencies",
+ "fd of the exclusive lock from rc",
longopts_help_COMMON
};
#include "_usage.c"
@@ -1291,6 +1191,9 @@ runscript(int argc, char **argv)
case 'd':
setenv("RC_DEBUG", "YES", 1);
break;
+ case 'l':
+ exclusive_fd = atoi(optarg);
+ break;
case 's':
if (!(rc_service_state(service) & RC_SERVICE_STARTED))
exit(EXIT_FAILURE);