Merge branch 'enable-disable-services'

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson
2017-07-01 18:02:52 +02:00
7 changed files with 362 additions and 28 deletions
+2
View File
@@ -51,6 +51,8 @@ General
That is: (r)esolve dependencies and only enable ssh (t)emporary
in the default runlevel when networking is available.
* `initctl add /sbin/service -n -- Service description`, for details
https://github.com/troglobit/finit/issues/69#issuecomment-287907610
Init
+75
View File
@@ -0,0 +1,75 @@
Distro Recommendations
======================
By default Finit uses the following directories for configuration files:
```
/etc/
|-- finit.d/ -- Regular (enabled) services
| |-- lighttpd.conf
| `- *.conf
|-- finit.conf -- Bootstrap tasks and services
:
```
To enable a standard (non-bootstrap) service one simply drops a small
configuration file in `/etc/finit.d/`. This works fine on system that
keep disabled services elsewhere, or generates them as needed from some
other tool.
Disitributions, however, may want a clearer separation of enabled and
available (installed but not enabled) services. They may even want to
customize the directories used, for brand labling or uniformity.
To that end Finit allows for a sub-directory `/etc/finit.d/available/`
where installed but disabled services can reside. Adding a symlink to a
configuration in this sub-directory enables the service, but it will not
be started.
To change the default configuration directory and configuration file
names the Finit `configure` script offers the following two options at
build time:
```sh
./configure --with-rcsd=/etc/init.d --with-config=/etc/init.d/init.conf
```
The resulting directory structure is depicted below. Please notice how
`/etc/finit.conf` now resides in the same sub-directory as a non-symlink
`/etc/init.d/init.conf`:
```
/etc/
|-- init.d/
| |-- available/ -- Regular (disabled) services
| | |-- httpd.conf
| | |-- ntpd.conf
| | `-- sshd.conf
| |-- sshd.conf -- Symlink to available/sshd.conf
| `- init.conf -- Bootstrap tasks and services
:
```
To facilitate the task of managing configurations, be it a service,
task, run, or other stanza, the `initctl` tool has a a few built-in
commands:
```
list List all .conf in /etc/finit.d/
enable <CONF> Enable .conf in /etc/finit.d/available/
disable <CONF> Disable .conf in /etc/finit.d/[enabled/]
reload Reload *.conf in /etc/finit.d/ (activates changes)
```
To enable a service like `sshd.conf`, above, use
initctl enable sshd
The `.conf` suffix is not needed, `initctl` adds it implicitly if it's
missing. The `disable` command works in a similar fashion.
Note, however, that `initctl` only operates on symlinks and it always
requires the `available/` sub-directory. Any non-symlink in the parent
directory, here `/etc/init.d/`, will be considered a system override by
`initctl`.
+3 -3
View File
@@ -41,9 +41,9 @@ else
finit_LDADD += -ldl
endif
initctl_SOURCES = initctl.c svc.c svc.h cond.c cond.h \
util.c util.h
initctl_CFLAGS = $(lite_CFLAGS)
initctl_SOURCES = initctl.c serv.c serv.h svc.c svc.h \
cond.c cond.h util.c util.h
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter $(lite_CFLAGS)
initctl_LDADD = $(lite_LIBS)
reboot_SOURCES = reboot.c util.c util.h
+37 -10
View File
@@ -388,13 +388,15 @@ static void tabstospaces(char *line)
static int parse_conf_dynamic(char *file, struct timeval *mtime)
{
FILE *fp = fopen(file, "r");
FILE *fp;
fp = fopen(file, "r");
if (!fp) {
_pe("Failed opening %s", file);
return 1;
}
_d("Parsing %s", file);
while (!feof(fp)) {
char line[LINE_SIZE] = "";
@@ -403,7 +405,7 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime)
chomp(line);
tabstospaces(line);
_d("dyn conf: %s", line);
_d("%s", line);
parse_dynamic(line, mtime);
}
@@ -445,13 +447,15 @@ static int parse_conf(char *file)
if (!silent)
print(0, "Loading %sconfiguration", line);
_d("Parsing %s", file);
while (!feof(fp)) {
if (!fgets(line, sizeof(line), fp))
continue;
chomp(line);
tabstospaces(line);
_d("conf: %s", line);
_d("%s", line);
parse_static(line);
parse_dynamic(line, NULL);
@@ -474,29 +478,52 @@ void conf_reload_dynamic(void)
num = scandir(dir, &e, NULL, alphasort);
if (num < 0) {
_d("No files found in %s, skipping ...", dir);
_d("Skipping %s, no files found ...", dir);
return;
}
for (i = 0; i < num; i++) {
char *name = e[i]->d_name;
char path[CMD_SIZE];
size_t len;
struct stat st;
struct timeval mtime;
snprintf(path, sizeof(path), "%s/%s", dir, name);
/* Check that it's an actual file ... */
if (stat(path, &st)) {
_d("Cannot even read .conf file %s, skipping ...", path);
/* Check that it's an actual file ... beyond any symlinks */
if (lstat(path, &st)) {
_d("Skipping %s, cannot access: %s", path, strerror(errno));
continue;
}
if (S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode))
/* Skip directories */
if (S_ISDIR(st.st_mode)) {
_d("Skipping directory %s", path);
continue;
}
/* Check for dangling symlinks */
if (S_ISLNK(st.st_mode)) {
char *rp;
rp = realpath(path, NULL);
if (!rp) {
/*
* XXX: Prune from rcsd?
* XXX: Possibly temporary service from last boot
*/
logit(LOG_WARNING, "Skipping %s, dangling symlink: %s", path, strerror(errno));
continue;
}
free(rp);
}
/* Check that file ends with '.conf' */
if (strcmp(&path[strlen(path) - 5], ".conf")) {
_d("File %s is not a .conf, skipping ... ", path);
len = strlen(path);
if (len < 6 || strcmp(&path[len - 5], ".conf")) {
_d("Skipping %s, not a valid .conf ... ", path);
continue;
}
+44 -15
View File
@@ -21,11 +21,12 @@
* THE SOFTWARE.
*/
#include "config.h"
#include <err.h>
#include <ftw.h>
#include <ctype.h>
#include <getopt.h>
#include <glob.h>
#include <paths.h>
#include <signal.h>
#include <stdio.h>
@@ -36,9 +37,9 @@
#include <sys/un.h>
#include <lite/lite.h>
#include "config.h"
#include "finit.h"
#include "cond.h"
#include "serv.h"
#include "service.h"
#include "util.h"
@@ -54,6 +55,7 @@ int verbose = 0;
int silent = 1; /* For helpers.c */
int runlevel = 0;
static int do_send(struct init_request *rq, ssize_t len)
{
int sd, result = 255;
@@ -572,53 +574,79 @@ static int show_status(char *arg)
static int usage(int rc)
{
fprintf(stderr, "Usage: %s [OPTIONS] <COMMAND>\n\n"
fprintf(stderr,
"Usage: %s [OPTIONS] <COMMAND>\n"
"\n"
"Options:\n"
" -d, --debug Debug initctl (client)\n"
" -v, --verbose Verbose output\n"
" -h, --help This help text\n\n"
" -h, --help This help text\n"
"\n"
"Commands:\n"
" debug Toggle Finit (daemon) debug\n"
" help This help text\n"
" log [NAME] Show ten last Finit, or NAME, messages from syslog\n"
" reload Reload *.conf in /etc/finit.d and activate changes\n"
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
" status | show Show status of services\n"
" version Show Finit version\n"
"\n"
" list List all .conf in /etc/finit.d/\n"
" enable <CONF> Enable .conf in /etc/finit.d/available/\n"
" disable <CONF> Disable .conf in /etc/finit.d/[enabled/]\n"
" reload Reload *.conf in /etc/finit.d/ (activates changes)\n"
"\n"
" cond set <COND> Set (assert) condition => +COND\n"
" cond clear <COND> Clear (deassert) condition => -COND\n"
" cond show Show condition status\n"
" cond dump Show all conditions and their status\n"
"\n"
" log [NAME] Show ten last Finit, or NAME, messages from syslog\n"
" start <JOB|NAME>[:ID] Start service by job# or name, with optional ID\n"
" stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name\n"
" restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name\n"
" status | show Show status of services\n"
"\n"
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
" reboot Reboot system\n"
" halt Halt system\n"
" poweroff Halt and power off system\n"
"\n"
" utmp show Raw dump of UTMP/WTMP db\n"
" version Show Finit version\n\n", prognm);
"\n", prognm);
return rc;
}
static int do_help(char *arg)
{
return usage(0);
}
int main(int argc, char *argv[])
{
int c;
command_t command[] = {
{ "debug", toggle_debug },
{ "log", do_log },
{ "help", do_help },
{ "version", show_version },
{ "list", serv_list },
{ "enable", serv_enable },
{ "disable", serv_disable },
{ "reload", do_reload },
{ "runlevel", do_runlevel },
{ "status", show_status },
{ "show", show_status }, /* Convenience alias */
{ "cond", do_cond },
{ "log", do_log },
{ "start", do_start },
{ "stop", do_stop },
{ "restart", do_restart },
{ "status", show_status },
{ "show", show_status }, /* Convenience alias */
{ "runlevel", do_runlevel },
{ "reboot", do_reboot },
{ "halt", do_halt },
{ "poweroff", do_poweroff },
{ "utmp", do_utmp },
{ "version", show_version },
{ NULL, NULL }
};
struct option long_options[] = {
@@ -652,7 +680,8 @@ int main(int argc, char *argv[])
while (optind < argc) {
strlcat(arg, argv[optind++], sizeof(arg));
strlcat(arg, " ", sizeof(arg));
if (optind < argc)
strlcat(arg, " ", sizeof(arg));
}
for (c = 0; command[c].cmd; c++) {
+163
View File
@@ -0,0 +1,163 @@
/* List and enable/disable service configurations
*
* Copyright (c) 2017 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "config.h"
#include <err.h>
#include <glob.h>
#include <stdio.h>
#include <lite/lite.h>
#define SCREEN_WIDTH 80 /* Calculate screen width as well, later */
static const char *cwd;
static const char *available = FINIT_RCSD "/available";
static const char *enabled = FINIT_RCSD "/enabled";
static void pushd(const char *dir)
{
cwd = get_current_dir_name();
chdir(dir);
}
static void popd(void)
{
chdir(cwd);
}
static int calc_width(char *arr[], size_t len)
{
int width = 0;
size_t i;
for (i = 0; i < len; i++) {
int w = strlen(arr[i]);
if (w > width)
width = w;
}
return width;
}
static void do_list(const char *heading, const char *path)
{
int num;
int width;
size_t i;
glob_t gl;
char h[SCREEN_WIDTH] = "===============================================================================";
pushd(path);
if (glob("*.conf", 0, NULL, &gl)) {
chdir(cwd);
return;
}
if (gl.gl_pathc <= 0)
goto done;
num = snprintf(h, sizeof(h) - 1, "%s", path);
h[num] = ' ';
puts(h);
width = calc_width(gl.gl_pathv, gl.gl_pathc);
num = SCREEN_WIDTH / width;
if ((num - 1) * 2 + num * width > SCREEN_WIDTH)
num--;
for (i = 0; i < gl.gl_pathc; i++) {
if (i > 0 && !(i % num))
puts("");
printf("%-*s ", width, gl.gl_pathv[i]);
}
puts("\n");
done:
globfree(&gl);
popd();
}
int serv_list(char *arg)
{
if (fisdir(available))
do_list("Available", available);
if (fisdir(enabled))
do_list("Enabled", enabled);
else if (fisdir(FINIT_RCSD))
do_list("Always enabled", FINIT_RCSD);
return 0;
}
int serv_enable(char *arg)
{
char corr[40];
char link[80];
char path[80];
if (!strstr(arg, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", arg);
arg = corr;
}
pushd(FINIT_RCSD);
snprintf(path, sizeof(path), "available/%s", arg);
if (!fexist(path))
errx(1, "Cannot find %s", path);
snprintf(link, sizeof(link), "%s/%s", FINIT_RCSD, arg);
if (fexist(link))
errx(1, "%s already exists", link);
return symlink(path, link) != 0;
}
int serv_disable(char *arg)
{
char corr[40];
char link[80];
struct stat st;
if (!strstr(arg, ".conf")) {
snprintf(corr, sizeof(corr), "%s.conf", arg);
arg = corr;
}
snprintf(link, sizeof(link), "%s/%s", FINIT_RCSD, arg);
if (stat(link, &st))
err(1, "Cannot find %s", link);
if ((st.st_mode & S_IFMT) == S_IFLNK)
errx(1, "%s is not a symlink, move manually to %s first", link, available);
return remove(link) != 0;
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+38
View File
@@ -0,0 +1,38 @@
/* List and enable/disable service configurations
*
* Copyright (c) 2017 Joachim Nilsson <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef FINIT_SERV_H_
#define FINIT_SERV_H_
int serv_list (char *arg);
int serv_enable (char *arg);
int serv_disable (char *arg);
#endif /* FINIT_SERV_H_ */
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/