Add -Z, --dry-run option to show which services we would start/stop

without actually doing so.
Fixes #151.
This commit is contained in:
Roy Marples 2009-04-30 23:42:01 +01:00
parent 3d0e5175d8
commit f689187966
2 changed files with 314 additions and 263 deletions

View File

@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.Dd April 26, 2009 .Dd April 30, 2009
.Dt RUNSCRIPT 8 SMM .Dt RUNSCRIPT 8 SMM
.Os OpenRC .Os OpenRC
.Sh NAME .Sh NAME
@ -33,6 +33,7 @@
.Op Fl D , -nodeps .Op Fl D , -nodeps
.Op Fl d , -debug .Op Fl d , -debug
.Op Fl s , -ifstarted .Op Fl s , -ifstarted
.Op Fl Z , -dry-run
.Op Ar command ... .Op Ar command ...
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
@ -81,6 +82,9 @@ Turns off all informational output the service generates.
Output from any non OpenRC comands is not affected. Output from any non OpenRC comands is not affected.
.It Fl v , -verbose .It Fl v , -verbose
Turns on any extra informational output the service generates. Turns on any extra informational output the service generates.
.It Fl Z , -dry-run
Shows what services would be stopped and/or started without actually starting
or stopping them.
.El .El
.Pp .Pp
The following variables affect the service script: The following variables affect the service script:

View File

@ -75,31 +75,18 @@
#define WAIT_TIMEOUT 60 /* seconds until we timeout */ #define WAIT_TIMEOUT 60 /* seconds until we timeout */
#define WARN_TIMEOUT 10 /* warn about this every N seconds */ #define WARN_TIMEOUT 10 /* warn about this every N seconds */
static const char *applet = NULL; static const char *applet;
static RC_STRINGLIST *applet_list = NULL; static char *service, *runlevel, *ibsave, *prefix;;
static RC_STRINGLIST *restart_services = NULL; static RC_DEPTREE *deptree;
static RC_STRINGLIST *need_services = NULL; static RC_STRINGLIST *applet_list, *services, *tmplist;
static RC_STRINGLIST *use_services = NULL; static RC_STRINGLIST *restart_services, *need_services, *use_services;
static RC_STRINGLIST *services = NULL; static RC_HOOK hook_out;
static RC_STRINGLIST *tmplist = NULL; static int exclusive_fd = -1, master_tty = -1;
static char *service = NULL; static bool sighup, in_background, deps, dry_run;
static int exclusive_fd = -1; static pid_t service_pid;
static RC_DEPTREE *deptree = NULL;
static char *runlevel = NULL;
static bool sighup = false;
static char *ibsave = NULL;
static bool in_background = false;
static RC_HOOK hook_out = 0;
static pid_t service_pid = 0;
static char *prefix = NULL;
static int signal_pipe[2] = { -1, -1 }; static int signal_pipe[2] = { -1, -1 };
static int master_tty = -1;
static RC_STRINGLIST *types_b = NULL; static RC_STRINGLIST *types_b, *types_n, *types_nu, *types_nua, *types_m;
static RC_STRINGLIST *types_n = NULL;
static RC_STRINGLIST *types_nu = NULL;
static RC_STRINGLIST *types_nua = NULL;
static RC_STRINGLIST *types_m = NULL;
static RC_STRINGLIST *types_mua = NULL; static RC_STRINGLIST *types_mua = NULL;
#ifdef __linux__ #ifdef __linux__
@ -569,26 +556,16 @@ setup_types(void)
} }
static void static void
svc_start(bool deps) svc_start_check(void)
{ {
bool started;
bool background = false;
RC_STRING *svc;
RC_STRING *svc2;
int depoptions = RC_DEP_TRACE;
RC_SERVICE state; RC_SERVICE state;
bool first;
int n;
size_t len;
char *p;
char *tmp;
state = rc_service_state(service); state = rc_service_state(service);
if (rc_yesno(getenv("IN_HOTPLUG")) || in_background) { if (in_background) {
if (!(state & (RC_SERVICE_INACTIVE | RC_SERVICE_STOPPED))) if (!(state & (RC_SERVICE_INACTIVE | RC_SERVICE_STOPPED)))
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
background = true; if (rc_yesno(getenv("IN_HOTPLUG")))
rc_service_mark(service, RC_SERVICE_HOTPLUGGED); rc_service_mark(service, RC_SERVICE_HOTPLUGGED);
if (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0) if (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0)
ewarnx("WARNING: %s will be started in the" ewarnx("WARNING: %s will be started in the"
@ -611,19 +588,30 @@ svc_start(bool deps)
if (state & RC_SERVICE_STARTED) { if (state & RC_SERVICE_STARTED) {
ewarn("WARNING: %s has already been started", applet); ewarn("WARNING: %s has already been started", applet);
return; return;
} else if (state & RC_SERVICE_INACTIVE && ! background) } else if (state & RC_SERVICE_INACTIVE && !in_background)
ewarnx("WARNING: %s has already started, but is inactive", ewarnx("WARNING: %s has already started, but is inactive",
applet); applet);
rc_service_mark(service, RC_SERVICE_STARTING); rc_service_mark(service, RC_SERVICE_STARTING);
hook_out = RC_HOOK_SERVICE_START_OUT; hook_out = RC_HOOK_SERVICE_START_OUT;
rc_plugin_run(RC_HOOK_SERVICE_START_IN, applet); rc_plugin_run(RC_HOOK_SERVICE_START_IN, applet);
}
static void
svc_start_deps(void)
{
bool first;
RC_STRING *svc, *svc2;
RC_SERVICE state;
int depoptions = RC_DEP_TRACE, n;
size_t len;
char *p, *tmp;
pid_t pid;
errno = 0; errno = 0;
if (rc_conf_yesno("rc_depend_strict") || errno == ENOENT) if (rc_conf_yesno("rc_depend_strict") || errno == ENOENT)
depoptions |= RC_DEP_STRICT; depoptions |= RC_DEP_STRICT;
if (deps) {
if (!deptree && ((deptree = _rc_deptree_load(0, NULL)) == NULL)) if (!deptree && ((deptree = _rc_deptree_load(0, NULL)) == NULL))
eerrorx("failed to load deptree"); eerrorx("failed to load deptree");
if (!types_b) if (!types_b)
@ -648,11 +636,9 @@ svc_start(bool deps)
services = NULL; services = NULL;
need_services = rc_deptree_depends(deptree, types_n, need_services = rc_deptree_depends(deptree, types_n,
applet_list, runlevel, applet_list, runlevel, depoptions);
depoptions);
use_services = rc_deptree_depends(deptree, types_nu, use_services = rc_deptree_depends(deptree, types_nu,
applet_list, runlevel, applet_list, runlevel, depoptions);
depoptions);
if (!rc_runlevel_starting()) { if (!rc_runlevel_starting()) {
TAILQ_FOREACH(svc, use_services, entries) { TAILQ_FOREACH(svc, use_services, entries) {
@ -664,13 +650,20 @@ svc_start(bool deps)
rc_runlevel_starting()) rc_runlevel_starting())
continue; continue;
if (state & RC_SERVICE_STOPPED) { if (state & RC_SERVICE_STOPPED) {
pid_t pid = service_start(svc->value); if (dry_run) {
printf(" %s", svc->value);
continue;
}
pid = service_start(svc->value);
if (!rc_conf_yesno("rc_parallel")) if (!rc_conf_yesno("rc_parallel"))
rc_waitpid(pid); rc_waitpid(pid);
} }
} }
} }
if (dry_run)
return;
/* Now wait for them to start */ /* Now wait for them to start */
services = rc_deptree_depends(deptree, types_nua, applet_list, services = rc_deptree_depends(deptree, types_nua, applet_list,
runlevel, depoptions); runlevel, depoptions);
@ -686,10 +679,8 @@ svc_start(bool deps)
if (state & RC_SERVICE_STARTING && if (state & RC_SERVICE_STARTING &&
state & RC_SERVICE_WASINACTIVE) state & RC_SERVICE_WASINACTIVE)
{ {
if (!rc_stringlist_find(need_services, if (!rc_stringlist_find(need_services, svc->value) &&
svc->value) && !rc_stringlist_find(use_services, svc->value))
!rc_stringlist_find(use_services,
svc->value))
continue; continue;
} }
@ -723,11 +714,9 @@ svc_start(bool deps)
TAILQ_FOREACH(svc, tmplist, entries) { TAILQ_FOREACH(svc, tmplist, entries) {
rc_service_schedule_start(svc->value, service); rc_service_schedule_start(svc->value, service);
use_services = rc_deptree_depend(deptree, use_services = rc_deptree_depend(deptree,
"iprovide", "iprovide", svc->value);
svc->value);
TAILQ_FOREACH(svc2, use_services, entries) TAILQ_FOREACH(svc2, use_services, entries)
rc_service_schedule_start(svc2->value, rc_service_schedule_start(svc2->value, service);
service);
rc_stringlist_free(use_services); rc_stringlist_free(use_services);
use_services = NULL; use_services = NULL;
len += strlen(svc->value) + 2; len += strlen(svc->value) + 2;
@ -753,6 +742,11 @@ svc_start(bool deps)
services = NULL; services = NULL;
} }
static void svc_start_real()
{
bool started;
RC_STRING *svc, *svc2;
if (ibsave) if (ibsave)
setenv("IN_BACKGROUND", ibsave, 1); setenv("IN_BACKGROUND", ibsave, 1);
hook_out = RC_HOOK_SERVICE_START_DONE; hook_out = RC_HOOK_SERVICE_START_DONE;
@ -803,20 +797,31 @@ svc_start(bool deps)
} }
static void static void
svc_stop(bool deps) svc_start(void)
{ {
bool stopped; if (dry_run)
RC_SERVICE state = rc_service_state(service); einfon("start:");
int depoptions = RC_DEP_TRACE; else
RC_STRING *svc; svc_start_check();
if (deps)
svc_start_deps();
if (dry_run)
printf(" %s\n", applet);
else
svc_start_real();
}
static void
svc_stop_check(RC_SERVICE *state)
{
*state = rc_service_state(service);
if (rc_runlevel_stopping() && state & RC_SERVICE_FAILED) if (rc_runlevel_stopping() && *state & RC_SERVICE_FAILED)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
if (rc_yesno(getenv("IN_HOTPLUG")) || in_background) if (in_background &&
if (!(state & RC_SERVICE_STARTED) && !(*state & RC_SERVICE_STARTED) &&
!(state & RC_SERVICE_INACTIVE)) !(*state & RC_SERVICE_INACTIVE))
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
if (exclusive_fd == -1) if (exclusive_fd == -1)
@ -824,14 +829,14 @@ svc_stop(bool deps)
if (exclusive_fd == -1) { if (exclusive_fd == -1) {
if (errno == EACCES) if (errno == EACCES)
eerrorx("%s: superuser access required", applet); eerrorx("%s: superuser access required", applet);
if (state & RC_SERVICE_STOPPING) if (*state & RC_SERVICE_STOPPING)
ewarnx("WARNING: %s is already stopping", applet); ewarnx("WARNING: %s is already stopping", applet);
eerrorx("ERROR: %s has been stopped by something else", applet); eerrorx("ERROR: %s stopped by something else", applet);
} }
fcntl(exclusive_fd, F_SETFD, fcntl(exclusive_fd, F_SETFD,
fcntl(exclusive_fd, F_GETFD, 0) | FD_CLOEXEC); fcntl(exclusive_fd, F_GETFD, 0) | FD_CLOEXEC);
if (state & RC_SERVICE_STOPPED) { if (*state & RC_SERVICE_STOPPED) {
ewarn("WARNING: %s is already stopped", applet); ewarn("WARNING: %s is already stopped", applet);
return; return;
} }
@ -846,8 +851,18 @@ svc_stop(bool deps)
else if (rc_service_in_runlevel(service, RC_LEVEL_BOOT)) 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");
} }
}
static void
svc_stop_deps(RC_SERVICE state)
{
int depoptions = RC_DEP_TRACE;
RC_STRING *svc;
pid_t pid;
if (state & RC_SERVICE_WASINACTIVE)
return;
if (deps && !(state & RC_SERVICE_WASINACTIVE)) {
errno = 0; errno = 0;
if (rc_conf_yesno("rc_depend_strict") || errno == ENOENT) if (rc_conf_yesno("rc_depend_strict") || errno == ENOENT)
depoptions |= RC_DEP_STRICT; depoptions |= RC_DEP_STRICT;
@ -872,12 +887,16 @@ svc_stop(bool deps)
if (state & RC_SERVICE_STARTED || if (state & RC_SERVICE_STARTED ||
state & RC_SERVICE_INACTIVE) state & RC_SERVICE_INACTIVE)
{ {
if (dry_run) {
printf(" %s", svc->value);
continue;
}
svc_wait(svc->value); svc_wait(svc->value);
state = rc_service_state(svc->value); state = rc_service_state(svc->value);
if (state & RC_SERVICE_STARTED || if (state & RC_SERVICE_STARTED ||
state & RC_SERVICE_INACTIVE) state & RC_SERVICE_INACTIVE)
{ {
pid_t pid = service_stop(svc->value); pid = service_stop(svc->value);
if (!rc_conf_yesno("rc_parallel")) if (!rc_conf_yesno("rc_parallel"))
rc_waitpid(pid); rc_waitpid(pid);
rc_stringlist_add(tmplist, svc->value); rc_stringlist_add(tmplist, svc->value);
@ -886,6 +905,8 @@ svc_stop(bool deps)
} }
rc_stringlist_free(services); rc_stringlist_free(services);
services = NULL; services = NULL;
if (dry_run)
return;
TAILQ_FOREACH(svc, tmplist, entries) { TAILQ_FOREACH(svc, tmplist, entries) {
if (rc_service_state(svc->value) & RC_SERVICE_STOPPED) if (rc_service_state(svc->value) & RC_SERVICE_STOPPED)
@ -910,7 +931,6 @@ svc_stop(bool deps)
rc_stringlist_free(tmplist); rc_stringlist_free(tmplist);
tmplist = NULL; tmplist = NULL;
/* We now wait for other services that may use us and are /* We now wait for other services that may use us and are
* stopping. This is important when a runlevel stops */ * stopping. This is important when a runlevel stops */
services = rc_deptree_depends(deptree, types_mua, applet_list, services = rc_deptree_depends(deptree, types_mua, applet_list,
@ -924,6 +944,11 @@ svc_stop(bool deps)
services = NULL; services = NULL;
} }
static void
svc_stop_real(void)
{
bool stopped;
/* If we're stopping localmount, set LC_ALL=C so that /* If we're stopping localmount, set LC_ALL=C so that
* bash doesn't load anything blocking the unmounting of /usr */ * bash doesn't load anything blocking the unmounting of /usr */
if (strcmp(applet, "localmount") == 0) if (strcmp(applet, "localmount") == 0)
@ -952,7 +977,25 @@ svc_stop(bool deps)
} }
static void static void
svc_restart(bool deps) svc_stop(void)
{
RC_SERVICE state;
state = 0;
if (dry_run)
einfon("stop:");
else
svc_stop_check(&state);
if (deps)
svc_stop_deps(state);
if (dry_run)
printf(" %s\n", applet);
else
svc_stop_real();
}
static void
svc_restart(void)
{ {
/* This is hairly and a better way needs to be found I think! /* This is hairly and a better way needs to be found I think!
* The issue is this - openvpn need net and dns. net can restart * The issue is this - openvpn need net and dns. net can restart
@ -976,10 +1019,13 @@ svc_restart(bool deps)
if (!(rc_service_state(service) & RC_SERVICE_STOPPED)) { if (!(rc_service_state(service) & RC_SERVICE_STOPPED)) {
get_started_services(); get_started_services();
svc_stop(deps); svc_stop();
if (dry_run)
ewarn("Cannot calculate restart start dependencies"
" on a dry-run");
} }
svc_start(deps); svc_start();
start_services(restart_services); start_services(restart_services);
rc_stringlist_free(restart_services); rc_stringlist_free(restart_services);
restart_services = NULL; restart_services = NULL;
@ -1018,10 +1064,11 @@ service_plugable(void)
} }
#include "_usage.h" #include "_usage.h"
#define getoptstring "dDsvl:" getoptstring_COMMON #define getoptstring "dDsvl:Z" getoptstring_COMMON
#define extraopts "stop | start | restart | describe | zap" #define extraopts "stop | start | restart | describe | zap"
static const struct option longopts[] = { static const struct option longopts[] = {
{ "debug", 0, NULL, 'd'}, { "debug", 0, NULL, 'd'},
{ "dry-run", 0, NULL, 'Z'},
{ "ifstarted", 0, NULL, 's'}, { "ifstarted", 0, NULL, 's'},
{ "nodeps", 0, NULL, 'D'}, { "nodeps", 0, NULL, 'D'},
{ "lockfd", 1, NULL, 'l'}, { "lockfd", 1, NULL, 'l'},
@ -1029,6 +1076,7 @@ static const struct option longopts[] = {
}; };
static const char *const longopts_help[] = { static const char *const longopts_help[] = {
"set xtrace when running the script", "set xtrace when running the script",
"show what would be done",
"only run commands when started", "only run commands when started",
"ignore dependencies", "ignore dependencies",
"fd of the exclusive lock from rc", "fd of the exclusive lock from rc",
@ -1039,19 +1087,12 @@ static const char *const longopts_help[] = {
int int
runscript(int argc, char **argv) runscript(int argc, char **argv)
{ {
bool deps = true;
bool doneone = false; bool doneone = false;
char pidstr[10]; int retval, opt, depoptions = RC_DEP_TRACE;
int retval;
int opt;
RC_STRING *svc; RC_STRING *svc;
char path[PATH_MAX]; char path[PATH_MAX], lnk[PATH_MAX], *dir, *save = NULL, pidstr[10];
char lnk[PATH_MAX]; size_t l = 0, ll;
size_t l = 0;
size_t ll;
char *dir, *save = NULL;
const char *file; const char *file;
int depoptions = RC_DEP_TRACE;
struct stat stbuf; struct stat stbuf;
/* Show help if insufficient args */ /* Show help if insufficient args */
@ -1153,6 +1194,8 @@ runscript(int argc, char **argv)
setup_selinux(argc, argv); setup_selinux(argc, argv);
#endif #endif
deps = true;
/* Punt the first arg as it's our service name */ /* Punt the first arg as it's our service name */
argc--; argc--;
argv++; argv++;
@ -1176,7 +1219,10 @@ runscript(int argc, char **argv)
case 'D': case 'D':
deps = false; deps = false;
break; break;
case_RC_COMMON_GETOPT case 'Z':
dry_run = true;
break;
case_RC_COMMON_GETOPT;
} }
/* If we're changing runlevels and not called by rc then we cannot /* If we're changing runlevels and not called by rc then we cannot
@ -1196,6 +1242,7 @@ runscript(int argc, char **argv)
if (rc_yesno(getenv("IN_HOTPLUG"))) { if (rc_yesno(getenv("IN_HOTPLUG"))) {
if (!service_plugable()) if (!service_plugable())
eerrorx("%s: not allowed to be hotplugged", applet); eerrorx("%s: not allowed to be hotplugged", applet);
in_background = true;
} }
/* Setup a signal handler */ /* Setup a signal handler */
@ -1277,15 +1324,15 @@ runscript(int argc, char **argv)
{ {
if (rc_service_state(service) & if (rc_service_state(service) &
RC_SERVICE_STARTED) RC_SERVICE_STARTED)
svc_restart(deps); svc_restart();
} else if (strcmp(optarg, "restart") == 0) { } else if (strcmp(optarg, "restart") == 0) {
svc_restart(deps); svc_restart();
} else if (strcmp(optarg, "start") == 0) { } else if (strcmp(optarg, "start") == 0) {
svc_start(deps); svc_start();
} else if (strcmp(optarg, "stop") == 0) { } else if (strcmp(optarg, "stop") == 0) {
if (deps && in_background) if (deps && in_background)
get_started_services(); get_started_services();
svc_stop(deps); svc_stop();
if (deps) { if (deps) {
if (!in_background && if (!in_background &&
!rc_runlevel_stopping() && !rc_runlevel_stopping() &&