mirror of
https://github.com/troglobit/finit.git
synced 2026-10-12 10:12:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55479cc511 | ||
|
|
b702f7eeef | ||
|
|
0e37bb0924 | ||
|
|
360f3e72bc | ||
|
|
c1023c03c7 | ||
|
|
e94d2a8c08 | ||
|
|
f7c0366fa2 | ||
|
|
8992e7bf2b | ||
|
|
95518df62d | ||
|
|
85fe95835a | ||
|
|
baf9c4f61f | ||
|
|
b439ac670d | ||
|
|
bb3d9c8670 | ||
|
|
2f9b97b3a7 | ||
|
|
c59f7d23f4 | ||
|
|
b0663d04e4 | ||
|
|
6f26f2f9d0 | ||
|
|
e7cca05fe3 | ||
|
|
f3669c7537 | ||
|
|
a9456cec41 | ||
|
|
61a9926442 | ||
|
|
7784480abe | ||
|
|
db50c09ec5 | ||
|
|
0f1f51b5ad | ||
|
|
d7401b2a96 | ||
|
|
c0515d112e | ||
|
|
9a8b55737f | ||
|
|
d7d8b8487d | ||
|
|
7f04382eee | ||
|
|
bf004d9ec4 | ||
|
|
23e0928e5c | ||
|
|
d6b2ac94dd | ||
|
|
7362e6d00a | ||
|
|
d94d11e00a | ||
|
|
f06a0fcb71 | ||
|
|
cae61ad6df | ||
|
|
27e4af5ed8 | ||
|
|
f10a724503 | ||
|
|
33037b012a | ||
|
|
0c75e59b77 | ||
|
|
8e55a76732 | ||
|
|
32b9096e31 | ||
|
|
f51723e8b7 | ||
|
|
9e31bd8a23 | ||
|
|
e841984a4e | ||
|
|
594476c4fc | ||
|
|
9c423dfca4 | ||
|
|
77cd0f0243 | ||
|
|
5dc4e7c058 | ||
|
|
3c015f384d | ||
|
|
5117b723c2 | ||
|
|
b55a609834 | ||
|
|
60eccbac31 | ||
|
|
b182e6bcc5 | ||
|
|
b8bf26b107 | ||
|
|
14c0d80f63 | ||
|
|
1f55f677d1 | ||
|
|
40267a5f0f | ||
|
|
9f50b033d9 | ||
|
|
bbaa7a1403 | ||
|
|
b68c5c1291 | ||
|
|
44007c86f2 | ||
|
|
cd73c1c951 | ||
|
|
5406af2054 | ||
|
|
3c800f6508 | ||
|
|
e0fb2b036d | ||
|
|
0ee4657f1c | ||
|
|
ec2382b6d4 | ||
|
|
0fc2df1a65 | ||
|
|
6c563a2b22 | ||
|
|
6a2911223d | ||
|
|
7f499ed1d0 | ||
|
|
2d2807bc65 | ||
|
|
4af7974fb9 | ||
|
|
3fd45eac7c | ||
|
|
bb4578292f | ||
|
|
656fa3d0e7 | ||
|
|
9a75813350 | ||
|
|
5af1f05606 | ||
|
|
fcdb5c2965 | ||
|
|
af3e560d79 | ||
|
|
b6e695b656 | ||
|
|
123df18847 | ||
|
|
dae21d9d64 | ||
|
|
bf4a0abe5c | ||
|
|
6826d7ee3f | ||
|
|
168da2a5c6 | ||
|
|
c82dc54205 | ||
|
|
eb0a759371 | ||
|
|
830abc1971 | ||
|
|
4c3a11131f | ||
|
|
796701c854 | ||
|
|
ab760aa3ff | ||
|
|
2b9eecbef3 | ||
|
|
b8190c8a2a | ||
|
|
adb4670d2f | ||
|
|
d31575538c | ||
|
|
faf0c680b8 | ||
|
|
250e26f2f6 | ||
|
|
b45c3d5cb8 | ||
|
|
6b2678c2ed | ||
|
|
0d6f203e0b | ||
|
|
781ba66905 | ||
|
|
9a97ab29dc | ||
|
|
b2b17ea4fb | ||
|
|
bfa99266e7 | ||
|
|
c76a58ef7a | ||
|
|
51c5f3cbfa | ||
|
|
a6290da210 | ||
|
|
949f02eff4 | ||
|
|
7970e45f45 | ||
|
|
a36dafaa6c | ||
|
|
c092d22d48 |
+6
-6
@@ -33,12 +33,12 @@ addons:
|
||||
branch_pattern: dev
|
||||
|
||||
install:
|
||||
- wget https://github.com/troglobit/libuev/releases/download/v1.5.2/libuev-1.5.2.tar.xz
|
||||
- wget https://github.com/troglobit/libite/releases/download/v1.9.2/libite-1.9.2.tar.xz
|
||||
- tar xf libuev-1.5.2.tar.xz
|
||||
- tar xf libite-1.9.2.tar.xz
|
||||
- (cd libuev-1.5.2 && ./configure --prefix=/tmp && make && make install-strip)
|
||||
- (cd libite-1.9.2 && ./configure --prefix=/tmp && make && make install-strip)
|
||||
- wget https://github.com/troglobit/libuev/releases/download/v2.1.0/libuev-2.1.0.tar.xz
|
||||
- wget https://github.com/troglobit/libite/releases/download/v2.0.1/libite-2.0.1.tar.xz
|
||||
- tar xf libuev-2.1.0.tar.xz
|
||||
- tar xf libite-2.0.1.tar.xz
|
||||
- (cd libuev-2.1.0 && ./configure --prefix=/tmp && make && make install-strip)
|
||||
- (cd libite-2.0.1 && ./configure --prefix=/tmp && make && make install-strip)
|
||||
|
||||
# Custom build script for the time being, no "make test" yet
|
||||
script:
|
||||
|
||||
@@ -3,6 +3,75 @@ Change Log
|
||||
|
||||
All relevant changes are documented in this file.
|
||||
|
||||
|
||||
[3.1][] - 2017-12-xx
|
||||
--------------------
|
||||
|
||||
Finit v3.1 require libuEv v2.1.0, or later, and libite v2.0.1, or later.
|
||||
|
||||
### Changes
|
||||
|
||||
* Support for `rlimit` per service/run/task/inetd/tty, issue #45
|
||||
* Support for auto-detecting serial console using Linux SysFS, the new
|
||||
`tty @console` eliminates the need to keep track of different console
|
||||
devices across embedded platforms: `/dev/ttyS0`, `/dev/ttyAMA0`, etc.
|
||||
* Support for calling run/tasks on Finit internal HOOK points, issue #18
|
||||
* Removed support for long-since deprecated `console DEV` setting
|
||||
* Cosmetic change to login, pressing enter at the `Press enter to ...`
|
||||
prompt will now replace that line with the login issue text
|
||||
* Calling `initctl` without any arguments or options now defaults to
|
||||
show status of all enabled services, and run/task/inetd jobs
|
||||
* Cosmetic change to boot messages, removed `Loading plugins ...`, start
|
||||
of inetd services, and `Loading configuration ...`. No end user knows
|
||||
what those plugins and configurations are, i.e. internal state+config
|
||||
* Change kernel WDT timeout (3 --> 30 sec) for built-in watchdog daemon
|
||||
* Handle `/etc/` OverlayFS, reload /etc/finit.d/*.conf after `mount -a`
|
||||
* initctl: Add support for printing previous runlevel
|
||||
* initctl: Support short forms of all commands
|
||||
* initctl: Support for `initctl touch <CONF>` to be used with `reload`
|
||||
* initctl: Improved output of `initctl show <SVC>`
|
||||
* Support reloading `/etc/finit.conf`. The main finit.conf file
|
||||
previously did not support reloading at runtime, as of v3.1 all
|
||||
configuration directives supported in `/etc/finit.d/*.conf` are now
|
||||
supported in `/etc/finit.conf`
|
||||
* Change `.conf` dependency + reload handling. Finit no longer relies
|
||||
on mtime of `.conf` files, instead an inotify handler tracks file
|
||||
changes for time *insensitive* dependency tracking
|
||||
* Change condition handling to not rely on mtime but a generation id.
|
||||
|
||||
### Fixes
|
||||
|
||||
* Reset TTY before restarting it. A program may manipulate the TTY in
|
||||
various ways before the user logs out, Finit needs to reset the TTY to
|
||||
a sane state before restarting it. Issue #84
|
||||
* On .conf parse errors, do *not* default to set TTY speed 38400, reuse
|
||||
current TTY speed instead
|
||||
* Fix run/tasks, must be guaranteed to run once per declared runlevel.
|
||||
All run/tasks on `[S]` with a condition `<...>` failed to run. Finit
|
||||
now tracks run/tasks more carefully, waiting for them to finish before
|
||||
switching to the configured runlevel at boot. Issue #86
|
||||
* Allow inetd services to be registered with a unique ID, e.g. `:161`,
|
||||
issue #87. Found by Westermo
|
||||
* inetd: drop UDP packets from blocked interfaces, issue #88
|
||||
* Handle obscure inter-plugin dependency issue by calling the netlink
|
||||
plugin before the pidfile plugin on `HOOK_SVC_RECONF` events
|
||||
* Handle event loop failure modes, issue found by Westermo
|
||||
* Handle API socket errors more gracefully, restart socket
|
||||
* Fix regression in condition handling, reconf condition must be kept
|
||||
as a reference point to previous reconfiguration, or bootstrap.
|
||||
* Fix nasty race condition with internal service stop, abort kill if the
|
||||
service has already terminated, otherwise we may do `kill(0, SIGKILL)`
|
||||
* Fix reconfiguration issue with (very quick) systems that don't have
|
||||
highres timers
|
||||
* Fix formatting of runlevel string in `initctl show`
|
||||
* Allow running `initctl` with `STDOUT` redirected
|
||||
* Fix regression in `initctl start/stop <ID>`, using name worked not id
|
||||
* Fix error handling in `initctl start/stop` without any argument
|
||||
* Fix issue #81 properly, remove use of SYSV shm IPC completely. Finit
|
||||
now use the API socket for all communication between PID 1 and initctl
|
||||
* Fix segfault on x86_64 when started with kernel cmdline `--debug`
|
||||
|
||||
|
||||
[3.0][] - 2017-10-19
|
||||
--------------------
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ advanced uses as well:
|
||||
|
||||
service /usr/sbin/sshd -D
|
||||
|
||||
Dependencies are handled using [conditions](doc/conditions.md). On of
|
||||
Dependencies are handled using [conditions](doc/conditions.md). One of
|
||||
the most common conditions is to wait for basic networking to become
|
||||
available:
|
||||
|
||||
@@ -124,6 +124,10 @@ to create its PID file, by default `/var/run/syslogd.pid`.
|
||||
service [2345] log <svc/sbin/syslogd> /usr/sbin/ntpd -n -N -p pool.ntp.org
|
||||
service [S12345] /sbin/syslogd -n -- Syslog daemon
|
||||
|
||||
Notice the `log` keyword, BusyBox `ntpd` uses `stderr` for logging when
|
||||
run in the foreground. With `log` Finit redirects `stdout` + `stderr`
|
||||
to the system log daemon using the command line `logger(1)` tool.
|
||||
|
||||
A service, or task, can have multiple dependencies listed. Here we wait
|
||||
for *both* `syslogd` to have started and basic networking to be up:
|
||||
|
||||
@@ -179,6 +183,15 @@ tty [12345] /dev/ttyAMA0 115200 noclear vt100
|
||||
tty [12345] /sbin/getty -L 115200 /dev/ttyAMA0 vt100
|
||||
```
|
||||
|
||||
Users of embedded systems may want to enable automatic serial console
|
||||
with the special `@console` device. This works regardless weather the
|
||||
system uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit
|
||||
figures it out by querying sysfs: `/sys/class/tty/console/active`.
|
||||
|
||||
```conf
|
||||
tty [12345] @console 115200 linux noclear
|
||||
```
|
||||
|
||||
For more information, see [doc/config.md](doc/config.md#syntax).
|
||||
|
||||
|
||||
@@ -346,28 +359,39 @@ not return until the given command has fully completed.
|
||||
|
||||
```shell
|
||||
~ $ initctl -h
|
||||
Usage: initctl [OPTIONS] <COMMAND>
|
||||
Usage: initctl [OPTIONS] [COMMAND]
|
||||
|
||||
Options:
|
||||
-d, --debug Debug initctl (client)
|
||||
-v, --verbose Verbose output
|
||||
-h, --help This help text
|
||||
|
||||
Commands:
|
||||
debug Toggle Finit (daemon) debug
|
||||
help This help text
|
||||
reload Reload *.conf in /etc/finit.d/ and activate changes
|
||||
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
|
||||
status | show Show status of services
|
||||
version Show Finit version
|
||||
|
||||
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)
|
||||
|
||||
cond set <COND> Set (assert) condition => +COND
|
||||
cond clear <COND> Clear (deassert) condition => -COND
|
||||
cond show Show condition status
|
||||
cond dump Dump all conditions and their status
|
||||
|
||||
log [NAME] Show ten last Finit, or NAME, messages from syslog
|
||||
start <JOB|NAME>[:ID] Start service by job# or name, with optional ID
|
||||
stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name
|
||||
restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name
|
||||
reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name
|
||||
version Show Finit version
|
||||
status | show Show status of services, default command
|
||||
|
||||
runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot
|
||||
reboot Reboot system
|
||||
halt Halt system
|
||||
poweroff Halt and power off system
|
||||
|
||||
utmp show Raw dump of UTMP/WTMP db
|
||||
```
|
||||
|
||||
For services *not* supporting `SIGHUP` the `<!>` notation in the .conf
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
AC_INIT(Finit, 3.0, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
|
||||
AC_INIT(Finit, 3.1-rc1, https://github.com/troglobit/finit/issues, finit, http://troglobit.com/finit.html)
|
||||
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz subdir-objects])
|
||||
AM_SILENT_RULES([yes])
|
||||
|
||||
@@ -33,8 +33,8 @@ AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT32_T
|
||||
|
||||
# Check for required libraries
|
||||
PKG_CHECK_MODULES([uev], [libuev >= 1.5.2])
|
||||
PKG_CHECK_MODULES([lite], [libite >= 1.9.2])
|
||||
PKG_CHECK_MODULES([uev], [libuev >= 2.1.0])
|
||||
PKG_CHECK_MODULES([lite], [libite >= 2.0.1])
|
||||
|
||||
# Check for configured Finit features
|
||||
AC_ARG_ENABLE(emergency_shell,
|
||||
|
||||
+3
-8
@@ -21,9 +21,9 @@ can also be used.
|
||||
General
|
||||
-------
|
||||
|
||||
* Remove shm, let `initctl` call `api.c` for each `svc_t` instead of
|
||||
opening up the inner parts of PID 1 to other processes. Also, this
|
||||
would prevent issues like #81 from happening.
|
||||
* Add support for `rescue` on the kernel command line, start rescue mode
|
||||
if enabled with `configure` script. Could mean skip `/etc/finit.conf`
|
||||
et al and jump straight to a single getty login on console.
|
||||
* Add support for timed shutdown in Finit, including cancelled shutdown.
|
||||
* SysV init and systemd use SIGUSR1 to restart their FIFO/D-Bus. Add
|
||||
API restart to SIGHUP callback for the same functionality in Finit.
|
||||
@@ -33,11 +33,6 @@ General
|
||||
i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task,
|
||||
e.g, <sys/key/ctrlaltdel> and <sys/key/signal> like SIGPWR handling.
|
||||
* Add `IFF_RUNNING` support to netlink plugin along `IFF_UP`
|
||||
* Allow scripts, instead of C code, as process callbacks
|
||||
* Add support for a pipe/popen where we can listen for `STOP`, `START`
|
||||
and `RELOAD` on `STDOUT` from a script ... call it for example
|
||||
`.monitor=` in `svc_t` ... the script named as the basename of the
|
||||
service it monitors + `.sh`.
|
||||
* Implement `initctl stop|start|restart|reload|status <SVC>` and
|
||||
`service <SVC> stop|start|restart|reload|status` on top
|
||||
* Add support for JSON output, or similar, from `initctl show`, e.g.
|
||||
|
||||
@@ -26,6 +26,12 @@ optional plugins to enable. It depends on two external libraries:
|
||||
|
||||
PKG_CONFIG_LIBDIR=/usr/local/lib/pkgconfig ./configure
|
||||
|
||||
The configure script checks for all dependencies, including the correct
|
||||
version of the above mentioned libraries. Currently requried versions:
|
||||
|
||||
- libite v2.0.1
|
||||
- libuEv v2.1.0
|
||||
|
||||
|
||||
Configure
|
||||
---------
|
||||
|
||||
+29
-23
@@ -13,7 +13,7 @@ Introduction
|
||||
|
||||
Finit can be configured using only the original `/etc/finit.conf` file
|
||||
or in combination with `/etc/finit.d/*.conf`. Finit 3 can even start a
|
||||
system using only /etc/finit.d/*.conf`, highly useful for package-based
|
||||
system using only `/etc/finit.d/*.conf`, highly useful for package-based
|
||||
Linux distributions -- each package can provide its own "script" file.
|
||||
|
||||
- `/etc/finit.conf`: main configuration file, read only once at boot
|
||||
@@ -51,15 +51,15 @@ Syntax
|
||||
* `network <PATH>`
|
||||
Script or program to bring up networking, with optional arguments
|
||||
|
||||
* `rlimit <hard|soft> RESOURCE <LIMIT|infinity>`
|
||||
Modify the specified resource's hard or soft limit to be the
|
||||
specifed limit. `RESOURCE` is a lower-case string matching the
|
||||
constants in `setrlimit(2)` with the `RLIMIT_` prefix
|
||||
removed. E.g. to select `RLIMIT_CPU`, `RESOURCE` would be
|
||||
`cpu`. `LIMIT` is an integer whose unit depends on the resource
|
||||
being modified, see `setrlimit(2)` for more information. The special
|
||||
limit `infinity` means the there should be no limit on the resource,
|
||||
i.e. `RLIM_INFINITY` is passed to `setrlimit(2)`.
|
||||
* `rlimit <hard|soft> RESOURCE <LIMIT|unlimited>`
|
||||
Set the hard or soft limit for a resource. `RESOURCE` is a lower-case
|
||||
string matching the `RLIMIT_` constants from `setrlimit(2)`, without
|
||||
the prefix. E.g. to set `RLIMIT_CPU`, use `cpu`.
|
||||
|
||||
The limit is an integer that depends on the resource being modified,
|
||||
see the man page, or the kernel `/proc/PID/limits` file, for more
|
||||
information. Finit versions before v3.1 used `infinity` for
|
||||
`unlimited`, which is still a supported keyword.
|
||||
|
||||
```shell
|
||||
# No process is allowed more than 8MB of address space
|
||||
@@ -162,9 +162,9 @@ Syntax
|
||||
tty [12345] /dev/ttyAMA0 115200 noclear vt220
|
||||
```
|
||||
|
||||
The second variant is for using an external getty, like agetty or the
|
||||
BusyBox getty.
|
||||
|
||||
The second `tty` syntax variant is for using an external getty, like
|
||||
agetty or the BusyBox getty.
|
||||
|
||||
By default both variants *clear* the TTY and *wait* for the user to
|
||||
press enter before starting getty.
|
||||
|
||||
@@ -185,7 +185,20 @@ Syntax
|
||||
Notice the ordering, the `TERM` option to the built-in getty must be
|
||||
the last argument.
|
||||
|
||||
On really bare bones systems Finit offers a fallback shell:
|
||||
Embedded systems may want to enable automatic `DEV` by supplying the
|
||||
special `@console` device. This works regardless weather the system
|
||||
uses `ttyS0`, `ttyAMA0`, `ttyMXC0`, or anything else. Finit figures
|
||||
it out by querying sysfs: `/sys/class/tty/console/active`.
|
||||
|
||||
**Example:**
|
||||
```conf
|
||||
tty [12345] @console 115200 noclear vt220
|
||||
```
|
||||
|
||||
On really bare bones systems Finit offers a fallback shell, which
|
||||
should not be enabled on production systems since. This because it
|
||||
may give a user root access without having to log in. However, for
|
||||
bringup and system debugging it can come in handy:
|
||||
|
||||
```shell
|
||||
configure --enable-fallback-shell
|
||||
@@ -197,16 +210,10 @@ Syntax
|
||||
service [12345] /bin/sh -l
|
||||
```
|
||||
|
||||
* `console <DEV>`
|
||||
Use this, along with a matching `tty DEV` line, to mark this TTY as a
|
||||
special "console" port with `prctl()`. Useful in use-cases when
|
||||
all logins except the console must be stopped, e.g. when performing a
|
||||
flash upgrade on an embedded system.
|
||||
|
||||
When running <kbd>make install</kbd> no default `/etc/finit.conf` will
|
||||
be installed since system requirements differ too much. Try out the
|
||||
Debian 6.0 example `/usr/share/doc/finit/finit.conf` configuration that
|
||||
is capable of service monitoring SSH, sysklogd, gdm and a console getty!
|
||||
is capable of service monitoring SSH, sysklogd, gdm and getty!
|
||||
|
||||
Every `run`, `task`, `service`, or `inetd` can also list the privileges
|
||||
the `/path/to/cmd` should be executed with. Simply prefix the path with
|
||||
@@ -272,9 +279,8 @@ the above `finit.conf` settings described above, only the following:
|
||||
- `inetd`
|
||||
- `rlimit`
|
||||
- `tty`
|
||||
- `console`
|
||||
|
||||
**NOTE:*** The `/etc/finit.d` directory was previously the default Finit
|
||||
**NOTE:** The `/etc/finit.d` directory was previously the default Finit
|
||||
`runparts` directory. Finit no longer has a default `runparts`, make
|
||||
sure to update your setup, or the finit configuration, accordingly.
|
||||
|
||||
|
||||
+2
-1
@@ -106,9 +106,10 @@ static void setup(void *arg)
|
||||
_d("Setting up FHS structure in /var ...");
|
||||
umask(0);
|
||||
makedir("/var/cache", 0755);
|
||||
makedir("/var/db", 0755); /* _PATH_VARDB on some systems */
|
||||
makedir("/var/games", 0755);
|
||||
makedir("/var/lib", 0755);
|
||||
makedir("/var/lib/misc", 0755);
|
||||
makedir("/var/lib/misc", 0755); /* _PATH_VARDB on some systems */
|
||||
makedir("/var/lib/alarm", 0755);
|
||||
makedir("/var/lib/urandom",0755);
|
||||
if (fisdir("/run")) {
|
||||
|
||||
+2
-1
@@ -29,6 +29,7 @@
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "service.h"
|
||||
#include "conf.h"
|
||||
|
||||
#define DAEMON "dbus-daemon"
|
||||
#define ARGS "--nofork --system"
|
||||
@@ -58,7 +59,7 @@ static void setup(void *arg)
|
||||
|
||||
/* Register service with Finit */
|
||||
snprintf(line, sizeof(line), "[S12345] %s %s -- %s", cmd, ARGS, DESC);
|
||||
if (service_register(SVC_TYPE_SERVICE, line, NULL))
|
||||
if (service_register(SVC_TYPE_SERVICE, line, global_rlimit, NULL))
|
||||
_pe("Failed registering %s", DAEMON);
|
||||
free(cmd);
|
||||
|
||||
|
||||
+5
-6
@@ -92,17 +92,16 @@ static void pidfile_callback(void *arg, int fd, int events)
|
||||
*
|
||||
* We must wait for the service to create/touch its pidfile.
|
||||
*/
|
||||
static void pidfile_reconf(void *_null)
|
||||
static void pidfile_reconf(void *arg)
|
||||
{
|
||||
static char cond[MAX_COND_LEN];
|
||||
svc_t *svc;
|
||||
(void)(_null);
|
||||
svc_t *svc, *iter = NULL;
|
||||
int restart = 0;
|
||||
|
||||
do {
|
||||
restart = 0;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
mkcond(cond, sizeof(cond), svc->cmd);
|
||||
if (svc->state == SVC_RUNNING_STATE &&
|
||||
!svc_is_changed(svc) &&
|
||||
@@ -114,7 +113,7 @@ static void pidfile_reconf(void *_null)
|
||||
}
|
||||
|
||||
if (restart)
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK | SVC_TYPE_INETD);
|
||||
|
||||
} while (restart);
|
||||
}
|
||||
@@ -148,7 +147,7 @@ static plugin_t plugin = {
|
||||
.cb = pidfile_callback,
|
||||
.flags = PLUGIN_IO_READ,
|
||||
},
|
||||
.depends = { "bootmisc", },
|
||||
.depends = { "bootmisc", "netlink" },
|
||||
};
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ static plugin_t plugin = {
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
if (plugin.io.fd)
|
||||
if (plugin.io.fd > 0)
|
||||
close(plugin.io.fd);
|
||||
|
||||
plugin.io.fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||
|
||||
+4
-3
@@ -11,10 +11,10 @@ endif
|
||||
sbin_PROGRAMS = finit initctl reboot
|
||||
finit_SOURCES = api.c \
|
||||
cond.c cond-w.c cond.h \
|
||||
client.c \
|
||||
telinit.c \
|
||||
conf.c conf.h \
|
||||
exec.c finit.c finit.h \
|
||||
getty.c \
|
||||
getty.c stty.c \
|
||||
helpers.c helpers.h \
|
||||
log.c log.h \
|
||||
mdadm.c mount.c pid.c \
|
||||
@@ -43,7 +43,8 @@ else
|
||||
finit_LDADD += -ldl
|
||||
endif
|
||||
|
||||
initctl_SOURCES = initctl.c serv.c serv.h svc.c svc.h \
|
||||
initctl_SOURCES = initctl.c client.c client.h \
|
||||
serv.c serv.h svc.h \
|
||||
cond.c cond.h util.c util.h
|
||||
initctl_CFLAGS = -W -Wall -Wextra -Wno-unused-parameter -std=gnu99
|
||||
initctl_CFLAGS += $(lite_CFLAGS)
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
@@ -38,11 +39,12 @@
|
||||
#include "helpers.h"
|
||||
#include "log.h"
|
||||
#include "plugin.h"
|
||||
#include "private.h"
|
||||
#include "sig.h"
|
||||
#include "service.h"
|
||||
#include "util.h"
|
||||
|
||||
uev_t api_watcher;
|
||||
static uev_t api_watcher;
|
||||
|
||||
static int call(int (*action)(svc_t *), char *buf, size_t len)
|
||||
{
|
||||
@@ -86,15 +88,42 @@ static int do_start (char *buf, size_t len) { return call(service_start, buf,
|
||||
static int do_stop (char *buf, size_t len) { return call(service_stop, buf, len); }
|
||||
static int do_restart(char *buf, size_t len) { return call(service_restart, buf, len); }
|
||||
|
||||
#ifdef INETD_ENABLED
|
||||
static int do_query_inetd(char *buf, size_t len)
|
||||
static char query_buf[368];
|
||||
static int missing(char *job, int id)
|
||||
{
|
||||
char buf[20];
|
||||
char idstr[10] = "";
|
||||
|
||||
if (!job)
|
||||
job = "";
|
||||
if (id > 1)
|
||||
snprintf(idstr, sizeof(idstr), ":%d", id);
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s%s ", job, idstr);
|
||||
strlcat(query_buf, buf, sizeof(query_buf));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_query(struct init_request *rq, size_t len)
|
||||
{
|
||||
memset(query_buf, 0, sizeof(query_buf));
|
||||
if (svc_parse_jobstr(rq->data, strlen(rq->data) + 1, NULL, missing)) {
|
||||
memcpy(rq->data, query_buf, sizeof(rq->data));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static svc_t *do_find(char *buf, size_t len)
|
||||
{
|
||||
int id = 1;
|
||||
char *ptr, *input = sanitize(buf, len);
|
||||
svc_t *svc;
|
||||
char *ptr, *input;
|
||||
|
||||
input = sanitize(buf, len);
|
||||
if (!input)
|
||||
return -1;
|
||||
return NULL;
|
||||
|
||||
ptr = strchr(input, ':');
|
||||
if (ptr) {
|
||||
@@ -102,11 +131,20 @@ static int do_query_inetd(char *buf, size_t len)
|
||||
id = atonum(ptr);
|
||||
}
|
||||
|
||||
svc = svc_find_by_jobid(atonum(input), id);
|
||||
if (!svc || !svc_is_inetd(svc)) {
|
||||
_e("Cannot %s svc %s ...", !svc ? "find" : "query, not an inetd", input);
|
||||
if (isdigit(input[0]))
|
||||
return svc_find_by_jobid(atonum(input), id);
|
||||
|
||||
return svc_find_by_nameid(input, id);
|
||||
}
|
||||
|
||||
#ifdef INETD_ENABLED
|
||||
static int do_query_inetd(char *buf, size_t len)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
svc = do_find(buf, len);
|
||||
if (!svc || !svc_is_inetd(svc))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return inetd_filter_str(&svc->inetd, buf, len);
|
||||
}
|
||||
@@ -163,20 +201,38 @@ static int do_handle_emit(char *buf, size_t len)
|
||||
return result;
|
||||
}
|
||||
|
||||
static void send_svc(int sd, svc_t *svc)
|
||||
{
|
||||
svc_t empty;
|
||||
size_t len;
|
||||
|
||||
if (!svc) {
|
||||
empty.pid = -1;
|
||||
svc = ∅
|
||||
}
|
||||
|
||||
len = write(sd, svc, sizeof(*svc));
|
||||
if (len != sizeof(*svc))
|
||||
_d("Failed sending svc_t to client");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* In contrast to the SysV compat handling in plugins/initctl.c, when
|
||||
* `initctl runlevel 0` is issued we default to POWERDOWN the system
|
||||
* instead of just halting.
|
||||
*/
|
||||
static void cb(uev_t *w, void *arg, int events)
|
||||
static void api_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
int sd, lvl;
|
||||
svc_t *svc;
|
||||
static svc_t *iter = NULL;
|
||||
struct init_request rq;
|
||||
|
||||
sd = accept(w->fd, NULL, NULL);
|
||||
if (sd < 0) {
|
||||
_pe("Failed serving API request");
|
||||
return;
|
||||
goto error;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
@@ -228,37 +284,46 @@ static void cb(uev_t *w, void *arg, int events)
|
||||
break;
|
||||
|
||||
case INIT_CMD_DEBUG:
|
||||
_d("debug");
|
||||
log_debug();
|
||||
break;
|
||||
|
||||
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
|
||||
_d("reload");
|
||||
service_reload_dynamic();
|
||||
break;
|
||||
|
||||
case INIT_CMD_START_SVC:
|
||||
_d("start %s", rq.data);
|
||||
result = do_start(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_STOP_SVC:
|
||||
_d("stop %s", rq.data);
|
||||
result = do_stop(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_RESTART_SVC:
|
||||
_d("restart %s", rq.data);
|
||||
result = do_restart(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
#ifdef INETD_ENABLED
|
||||
case INIT_CMD_QUERY_INETD:
|
||||
_d("query inetd");
|
||||
result = do_query_inetd(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
#endif
|
||||
|
||||
case INIT_CMD_EMIT:
|
||||
_d("emit %s", rq.data);
|
||||
result = do_handle_emit(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_GET_RUNLEVEL:
|
||||
rq.runlevel = runlevel;
|
||||
_d("get runlevel");
|
||||
rq.runlevel = runlevel;
|
||||
rq.sleeptime = prevlevel;
|
||||
break;
|
||||
|
||||
case INIT_CMD_ACK:
|
||||
@@ -266,18 +331,41 @@ static void cb(uev_t *w, void *arg, int events)
|
||||
goto leave;
|
||||
|
||||
case INIT_CMD_WDOG_HELLO:
|
||||
_d("wdog hello");
|
||||
if (rq.runlevel <= 0) {
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (wdogpid > 0 && wdogpid != rq.runlevel) {
|
||||
_d("Sending SIGTERM to %d", wdogpid);
|
||||
kill(wdogpid, SIGTERM);
|
||||
do_sleep(1);
|
||||
}
|
||||
wdogpid = rq.runlevel;
|
||||
break;
|
||||
|
||||
case INIT_CMD_SVC_ITER:
|
||||
_d("svc iter, first: %d", rq.runlevel);
|
||||
/*
|
||||
* XXX: This severly limits the number of
|
||||
* simultaneous client connections, but will
|
||||
* have to do for now.
|
||||
*/
|
||||
svc = svc_iterator(&iter, rq.runlevel);
|
||||
send_svc(sd, svc);
|
||||
goto leave;
|
||||
|
||||
case INIT_CMD_SVC_QUERY:
|
||||
_d("svc query: %s", rq.data);
|
||||
result = do_query(&rq, len);
|
||||
break;
|
||||
|
||||
case INIT_CMD_SVC_FIND:
|
||||
_d("svc find: %s", rq.data);
|
||||
send_svc(sd, do_find(rq.data, sizeof(rq.data)));
|
||||
goto leave;
|
||||
|
||||
default:
|
||||
_d("Unsupported cmd: %d", rq.cmd);
|
||||
break;
|
||||
@@ -292,8 +380,17 @@ static void cb(uev_t *w, void *arg, int events)
|
||||
_d("Failed sending ACK/NACK back to client");
|
||||
}
|
||||
|
||||
if (UEV_ERROR == events)
|
||||
goto error;
|
||||
|
||||
leave:
|
||||
close(sd);
|
||||
return;
|
||||
|
||||
error:
|
||||
api_exit();
|
||||
if (api_init(w->ctx))
|
||||
_e("Unrecoverable error on API socket");
|
||||
}
|
||||
|
||||
int api_init(uev_ctx_t *ctx)
|
||||
@@ -305,6 +402,7 @@ int api_init(uev_ctx_t *ctx)
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
|
||||
_d("Setting up external API socket ...");
|
||||
sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (-1 == sd) {
|
||||
_pe("Failed starting external API socket");
|
||||
@@ -320,7 +418,7 @@ int api_init(uev_ctx_t *ctx)
|
||||
goto error;
|
||||
|
||||
umask(oldmask);
|
||||
if (!uev_io_init(ctx, &api_watcher, cb, NULL, sd, UEV_READ))
|
||||
if (!uev_io_init(ctx, &api_watcher, api_cb, NULL, sd, UEV_READ))
|
||||
return 0;
|
||||
|
||||
error:
|
||||
|
||||
+109
-103
@@ -1,7 +1,6 @@
|
||||
/* Simple telinit client
|
||||
/* External client API, using UNIX domain socket.
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2015 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2015-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
|
||||
@@ -22,133 +21,140 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <lite/lite.h>
|
||||
#include "client.h"
|
||||
|
||||
#include "finit.h"
|
||||
#include "util.h"
|
||||
static int sd = -1;
|
||||
|
||||
static int do_send(struct init_request *rq)
|
||||
int client_connect(void)
|
||||
{
|
||||
static int fd = -1;
|
||||
int result = 255;
|
||||
ssize_t len;
|
||||
struct sockaddr_un sun = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
|
||||
if (fd == -1) {
|
||||
if (!fexist(FINIT_FIFO)) {
|
||||
fprintf(stderr, "%s does not support %s!\n", prognm, FINIT_FIFO);
|
||||
return 1;
|
||||
}
|
||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (-1 == sd)
|
||||
goto error;
|
||||
|
||||
fd = open(FINIT_FIFO, O_RDWR);
|
||||
if (-1 == fd) {
|
||||
perror("Failed opening " FINIT_FIFO);
|
||||
return 1;
|
||||
}
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1) {
|
||||
close(sd);
|
||||
goto error;
|
||||
}
|
||||
|
||||
len = write(fd, rq, sizeof(*rq));
|
||||
if (sizeof(*rq) == len)
|
||||
result = 0;
|
||||
|
||||
return result;
|
||||
return sd;
|
||||
error:
|
||||
perror("Failed connecting to finit");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int do_reload(void)
|
||||
int client_disconnect(void)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_RELOAD,
|
||||
};
|
||||
int rc;
|
||||
|
||||
return do_send(&rq);
|
||||
}
|
||||
|
||||
static int set_env(int runlevel)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_SETENV,
|
||||
};
|
||||
|
||||
if (runlevel == 0) {
|
||||
strlcpy(rq.data, "INIT_HALT=POWERDOWN", sizeof(rq.data));
|
||||
return do_send(&rq);
|
||||
if (sd < 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_runlevel(int runlevel)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_RUNLVL,
|
||||
.runlevel = runlevel,
|
||||
};
|
||||
|
||||
return set_env(runlevel) || do_send(&rq);
|
||||
}
|
||||
|
||||
/* telinit q | telinit <0-9> */
|
||||
static int usage(int rc)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
|
||||
"Options:\n"
|
||||
" -h, --help This help text\n"
|
||||
" -V, --version Show Finit version\n\n"
|
||||
"Commands:\n"
|
||||
" 0 Power-off the system, same as initctl poweroff\n"
|
||||
" 6 Reboot the system, same as initctl reboot\n"
|
||||
" 2, 3, 4, 5 Change runlevel. Starts services in new runlevel, stops any\n"
|
||||
" services in prev. runlevel that are not allowed in new.\n"
|
||||
" q, Q Reload *.conf in /etc/finit.d/, same as initctl reload or\n"
|
||||
" sending SIGHUP to PID 1\n"
|
||||
" 1, s, S Enter system rescue mode, runlevel 1\n"
|
||||
"\n", prognm);
|
||||
rc = close(sd);
|
||||
sd = -1;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int client(int argc, char *argv[])
|
||||
int client_send(struct init_request *rq, ssize_t len)
|
||||
{
|
||||
int c;
|
||||
struct option long_options[] = {
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"version", 0, NULL, 'v'},
|
||||
{NULL, 0, NULL, 0}
|
||||
int sd, result = 255;
|
||||
|
||||
sd = client_connect();
|
||||
if (-1 == sd)
|
||||
return -1;
|
||||
|
||||
if (write(sd, rq, len) != len)
|
||||
goto error;
|
||||
|
||||
if (read(sd, rq, len) != len)
|
||||
goto error;
|
||||
|
||||
result = 0;
|
||||
goto exit;
|
||||
error:
|
||||
perror("Failed communicating with finit");
|
||||
exit:
|
||||
client_disconnect();
|
||||
return result;
|
||||
}
|
||||
|
||||
svc_t *client_svc_iterator(int first)
|
||||
{
|
||||
int sd = -1;
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_SVC_ITER,
|
||||
};
|
||||
static svc_t svc;
|
||||
|
||||
progname(argv[0]);
|
||||
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case 'v':
|
||||
return puts("v" VERSION) == EOF;
|
||||
sd = client_connect();
|
||||
if (sd == -1)
|
||||
return NULL;
|
||||
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
}
|
||||
}
|
||||
if (first)
|
||||
rq.runlevel = 1;
|
||||
else
|
||||
rq.runlevel = 0;
|
||||
|
||||
if (optind < argc) {
|
||||
int req = (int)argv[optind][0];
|
||||
if (write(sd, &rq, sizeof(rq)) != sizeof(rq))
|
||||
goto error;
|
||||
if (read(sd, &svc, sizeof(svc)) != sizeof(svc))
|
||||
goto error;
|
||||
|
||||
if (isdigit(req))
|
||||
return set_runlevel(req);
|
||||
client_disconnect();
|
||||
if (svc.pid < 0)
|
||||
return NULL;
|
||||
|
||||
if (req == 'q' || req == 'Q')
|
||||
return do_reload();
|
||||
return &svc;
|
||||
error:
|
||||
perror("Failed communicating with finit");
|
||||
client_disconnect();
|
||||
sd = -1;
|
||||
|
||||
if (req == 's' || req == 'S')
|
||||
return set_runlevel(1);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return usage(1);
|
||||
svc_t *client_svc_find(char *arg)
|
||||
{
|
||||
int sd = -1;
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_SVC_FIND,
|
||||
};
|
||||
static svc_t svc;
|
||||
|
||||
sd = client_connect();
|
||||
if (sd == -1)
|
||||
return NULL;
|
||||
|
||||
strlcpy(rq.data, arg, sizeof(rq.data));
|
||||
if (write(sd, &rq, sizeof(rq)) != sizeof(rq))
|
||||
goto error;
|
||||
if (read(sd, &svc, sizeof(svc)) != sizeof(svc))
|
||||
goto error;
|
||||
|
||||
client_disconnect();
|
||||
if (svc.pid < 0)
|
||||
return NULL;
|
||||
|
||||
return &svc;
|
||||
error:
|
||||
client_disconnect();
|
||||
perror("Failed communicating with finit");
|
||||
sd = -1;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/* External client API, using UNIX domain socket.
|
||||
*
|
||||
* Copyright (c) 2015-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_CLIENT_H_
|
||||
#define FINIT_CLIENT_H_
|
||||
|
||||
#include "finit.h"
|
||||
#include "svc.h"
|
||||
|
||||
typedef struct {
|
||||
char *cmd;
|
||||
int (*cb)(char *arg);
|
||||
} command_t;
|
||||
|
||||
int client_connect (void);
|
||||
int client_disconnect (void);
|
||||
|
||||
int client_send (struct init_request *rq, ssize_t len);
|
||||
svc_t *client_svc_iterator (int first);
|
||||
svc_t *client_svc_find (char *arg);
|
||||
|
||||
#endif /* FINIT_CLIENT_H_ */
|
||||
+54
-14
@@ -1,6 +1,6 @@
|
||||
/* Condition engine (write)
|
||||
*
|
||||
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
||||
* Copyright (c) 2015-2017 Tobias Waldekranz <tobias@waldekranz.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
|
||||
@@ -25,16 +25,53 @@
|
||||
#include <libgen.h>
|
||||
#include <lite/lite.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "service.h"
|
||||
|
||||
static int cond_set_gen(const char *path, unsigned int gen)
|
||||
{
|
||||
FILE *fp;
|
||||
int ret;
|
||||
|
||||
fp = fopen(path, "w");
|
||||
if (!fp)
|
||||
return -1;
|
||||
|
||||
ret = fprintf(fp, "%u\n", gen);
|
||||
fclose(fp);
|
||||
|
||||
return (ret > 0) ? 0 : ret;
|
||||
}
|
||||
|
||||
static void cond_bump_reconf(void)
|
||||
{
|
||||
unsigned int rgen;
|
||||
|
||||
/*
|
||||
* If %COND_RECONF does not exist, cond_get_gen() returns 0
|
||||
* meaning that rgen++ is always what we want.
|
||||
*/
|
||||
rgen = cond_get_gen(COND_RECONF);
|
||||
rgen++;
|
||||
|
||||
cond_set_gen(COND_RECONF, rgen);
|
||||
}
|
||||
|
||||
int cond_set_path(const char *path, enum cond_state new)
|
||||
{
|
||||
char buf[MAX_ARG_LEN], *dir;
|
||||
enum cond_state old;
|
||||
unsigned int rgen;
|
||||
|
||||
_d("%s", path);
|
||||
|
||||
rgen = cond_get_gen(COND_RECONF);
|
||||
if (!rgen) {
|
||||
_e("Unable to read configuration generation (%s)", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
old = cond_get_path(path);
|
||||
|
||||
@@ -50,8 +87,7 @@ int cond_set_path(const char *path, enum cond_state new)
|
||||
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
|
||||
return 0;
|
||||
}
|
||||
touch(path);
|
||||
utimensat(0, path, NULL, 0);
|
||||
cond_set_gen(path, rgen);
|
||||
break;
|
||||
|
||||
case COND_OFF:
|
||||
@@ -88,14 +124,14 @@ static int svc_has_cond(svc_t *svc)
|
||||
|
||||
static void cond_update(const char *name)
|
||||
{
|
||||
svc_t *svc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
_d("%s", name);
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
|
||||
continue;
|
||||
|
||||
_d("%s: match <%s> %s", name ?: "nil", svc->cond, svc->cmd);
|
||||
_d("%s: match <%s> %s(%s)", name ?: "nil", svc->cond, svc->desc, svc->cmd);
|
||||
service_step(svc);
|
||||
}
|
||||
}
|
||||
@@ -121,8 +157,8 @@ void cond_clear(const char *name)
|
||||
void cond_reload(void)
|
||||
{
|
||||
_d("");
|
||||
cond_set_path(COND_RECONF, COND_ON);
|
||||
|
||||
cond_bump_reconf();
|
||||
cond_update(NULL);
|
||||
}
|
||||
|
||||
@@ -149,15 +185,19 @@ static int reassert(const char *fpath, const struct stat *sb, int tflg, struct F
|
||||
*/
|
||||
void cond_reassert(const char *type)
|
||||
{
|
||||
_d("%s", type ?: "reload done");
|
||||
if (!type) {
|
||||
cond_set_path(COND_RECONF, COND_OFF);
|
||||
return;
|
||||
}
|
||||
|
||||
_d("%s", type);
|
||||
nftw(cond_path(type), reassert, 20, FTW_DEPTH);
|
||||
}
|
||||
|
||||
void cond_init(void)
|
||||
{
|
||||
if (makepath(COND_PATH) && errno != EEXIST) {
|
||||
_pe("Failed creating condition base directory '%s'", COND_PATH);
|
||||
return;
|
||||
}
|
||||
|
||||
cond_bump_reconf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
|
||||
+24
-15
@@ -1,6 +1,6 @@
|
||||
/* Condition engine (read)
|
||||
*
|
||||
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.com>
|
||||
* Copyright (c) 2015-2017 Tobias Waldekranz <tobias@waldekranz.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
|
||||
@@ -28,14 +28,6 @@
|
||||
#include "cond.h"
|
||||
#include "service.h"
|
||||
|
||||
static inline int timespec_newer(const struct timespec *a,
|
||||
const struct timespec *b)
|
||||
{
|
||||
if (a->tv_sec != b->tv_sec)
|
||||
return a->tv_sec > b->tv_sec;
|
||||
|
||||
return a->tv_nsec > b->tv_nsec;
|
||||
}
|
||||
|
||||
const char *condstr(enum cond_state s)
|
||||
{
|
||||
@@ -57,18 +49,35 @@ const char *cond_path(const char *name)
|
||||
return file;
|
||||
}
|
||||
|
||||
unsigned int cond_get_gen(const char *path)
|
||||
{
|
||||
unsigned int gen;
|
||||
FILE *fp;
|
||||
int ret;
|
||||
|
||||
fp = fopen(path, "r");
|
||||
if (!fp)
|
||||
return 0;
|
||||
|
||||
ret = fscanf(fp, "%u", &gen);
|
||||
fclose(fp);
|
||||
|
||||
return (ret == 1) ? gen : 0;
|
||||
}
|
||||
|
||||
enum cond_state cond_get_path(const char *path)
|
||||
{
|
||||
struct stat st, st_reconf;
|
||||
int cgen, rgen;
|
||||
|
||||
if (stat(path, &st))
|
||||
rgen = cond_get_gen(COND_RECONF);
|
||||
if (!rgen)
|
||||
return COND_OFF;
|
||||
|
||||
if (stat(COND_RECONF, &st_reconf) ||
|
||||
timespec_newer(&st.st_mtim, &st_reconf.st_mtim))
|
||||
return COND_ON;
|
||||
cgen = cond_get_gen(path);
|
||||
if (!cgen)
|
||||
return COND_OFF;
|
||||
|
||||
return COND_FLUX;
|
||||
return (cgen == rgen) ? COND_ON : COND_FLUX;
|
||||
}
|
||||
|
||||
enum cond_state cond_get(const char *name)
|
||||
|
||||
@@ -15,6 +15,7 @@ typedef enum cond_state {
|
||||
|
||||
const char *condstr (enum cond_state s);
|
||||
const char *cond_path (const char *name);
|
||||
unsigned int cond_get_gen (const char *path);
|
||||
enum cond_state cond_get_path(const char *path);
|
||||
enum cond_state cond_get (const char *name);
|
||||
enum cond_state cond_get_agg (const char *names);
|
||||
@@ -25,6 +26,7 @@ void cond_set (const char *name);
|
||||
void cond_clear (const char *name);
|
||||
void cond_reload (void);
|
||||
void cond_reassert(const char *pat);
|
||||
void cond_init (void);
|
||||
|
||||
#endif /* FINIT_COND_H_ */
|
||||
|
||||
|
||||
+314
-110
@@ -25,8 +25,10 @@
|
||||
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/resource.h>
|
||||
#include <lite/lite.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "finit.h"
|
||||
@@ -35,11 +37,22 @@
|
||||
#include "tty.h"
|
||||
#include "helpers.h"
|
||||
|
||||
#define BOOTSTRAP (runlevel == 0)
|
||||
#define MATCH_CMD(l, c, x) \
|
||||
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
||||
|
||||
static int parse_conf(char *file);
|
||||
struct rlimit global_rlimit[RLIMIT_NLIMITS];
|
||||
|
||||
struct conf_change {
|
||||
TAILQ_ENTRY(conf_change) link;
|
||||
char *name;
|
||||
};
|
||||
|
||||
static uev_t w1, w2, w3;
|
||||
static TAILQ_HEAD(head, conf_change) conf_change_list = TAILQ_HEAD_INITIALIZER(conf_change_list);
|
||||
|
||||
static int parse_conf(char *file);
|
||||
static void drop_changes(void);
|
||||
|
||||
void conf_parse_cmdline(void)
|
||||
{
|
||||
@@ -163,59 +176,74 @@ static const struct rlimit_name rlimit_names[] = {
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
void conf_parse_rlimit(char *line)
|
||||
int str2rlim(char *str)
|
||||
{
|
||||
struct rlimit rlim;
|
||||
rlim_t new, *set;
|
||||
const struct rlimit_name *name;
|
||||
const struct rlimit_name *rn;
|
||||
|
||||
for (rn = rlimit_names; rn->name; rn++) {
|
||||
if (!strcmp(str, rn->name))
|
||||
return rn->val;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *rlim2str(int rlim)
|
||||
{
|
||||
const struct rlimit_name *rn;
|
||||
|
||||
for (rn = rlimit_names; rn->name; rn++) {
|
||||
if (rn->val == rlim)
|
||||
return rn->name;
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
void conf_parse_rlimit(char *line, struct rlimit arr[])
|
||||
{
|
||||
char *level, *limit, *val;
|
||||
int resource = -1;
|
||||
rlim_t cfg, *set;
|
||||
|
||||
char *tok = strtok(line, " \t");
|
||||
level = strtok(line, " \t");
|
||||
if (!level)
|
||||
goto error;
|
||||
|
||||
if (tok && !strcmp(tok, "soft"))
|
||||
set = &rlim.rlim_cur;
|
||||
else if (tok && !strcmp(tok, "hard"))
|
||||
set = &rlim.rlim_max;
|
||||
limit = strtok(NULL, " \t");
|
||||
if (!limit)
|
||||
goto error;
|
||||
|
||||
resource = str2rlim(limit);
|
||||
if (resource < 0 || resource > RLIMIT_NLIMITS)
|
||||
goto error;
|
||||
|
||||
val = strtok(NULL, " \t");
|
||||
if (!val)
|
||||
goto error;
|
||||
|
||||
if (!strcmp(level, "soft"))
|
||||
set = &arr[resource].rlim_cur;
|
||||
else if (!strcmp(level, "hard"))
|
||||
set = &arr[resource].rlim_max;
|
||||
else
|
||||
goto error;
|
||||
|
||||
tok = strtok(NULL, " \t");
|
||||
if (!tok)
|
||||
goto error;
|
||||
|
||||
for (name = rlimit_names; name->name; name++) {
|
||||
if (!strcmp(tok, name->name))
|
||||
resource = name->val;
|
||||
}
|
||||
|
||||
if (resource < 0)
|
||||
goto fail;
|
||||
|
||||
tok = strtok(NULL, " \t");
|
||||
if (!tok)
|
||||
goto fail;
|
||||
|
||||
if (!strcmp(tok, "infinity")) {
|
||||
new = RLIM_INFINITY;
|
||||
/* Official keyword from v3.1 is `unlimited`, from prlimit(1) */
|
||||
if (!strcmp(val, "unlimited") || !strcmp(val, "infinity")) {
|
||||
cfg = RLIM_INFINITY;
|
||||
} else {
|
||||
const char *err = NULL;
|
||||
|
||||
new = strtonum(tok, 0, (long long)2 << 31, &err);
|
||||
if (err)
|
||||
goto fail;
|
||||
cfg = strtonum(val, 0, (long long)2 << 31, &err);
|
||||
if (err) {
|
||||
logit(LOG_WARNING, "rlimit: invalid %s value: %s",
|
||||
rlim2str(resource), val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (getrlimit(resource, &rlim))
|
||||
goto fail;
|
||||
|
||||
*set = new;
|
||||
if (setrlimit(resource, &rlim))
|
||||
goto fail;
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
logit(LOG_WARNING, "rlimit: Failed setting rlimit %s", name->name ? : "unknown");
|
||||
*set = cfg;
|
||||
return;
|
||||
error:
|
||||
logit(LOG_WARNING, "rlimit: parse error");
|
||||
@@ -226,13 +254,13 @@ static void parse_static(char *line)
|
||||
char *x;
|
||||
char cmd[CMD_SIZE];
|
||||
|
||||
if (MATCH_CMD(line, "host ", x)) {
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "host ", x)) {
|
||||
if (hostname) free(hostname);
|
||||
hostname = strdup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "mknod ", x)) {
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "mknod ", x)) {
|
||||
char *dev = strip_line(x);
|
||||
|
||||
strcpy(cmd, "mknod ");
|
||||
@@ -242,13 +270,13 @@ static void parse_static(char *line)
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "network ", x)) {
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "network ", x)) {
|
||||
if (network) free(network);
|
||||
network = strdup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "runparts ", x)) {
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "runparts ", x)) {
|
||||
if (runparts) free(runparts);
|
||||
runparts = strdup(strip_line(x));
|
||||
return;
|
||||
@@ -273,10 +301,12 @@ static void parse_static(char *line)
|
||||
return;
|
||||
}
|
||||
|
||||
/* The desired runlevel to start when leaving bootstrap (S).
|
||||
/*
|
||||
* The desired runlevel to start when leaving bootstrap (S).
|
||||
* Finit supports 1-9, but most systems only use 1-6, where
|
||||
* 6 is reserved for reboot */
|
||||
if (MATCH_CMD(line, "runlevel ", x)) {
|
||||
* 6 is reserved for reboot and 0 for halt/poweroff.
|
||||
*/
|
||||
if (BOOTSTRAP && MATCH_CMD(line, "runlevel ", x)) {
|
||||
char *token = strip_line(x);
|
||||
const char *err = NULL;
|
||||
|
||||
@@ -289,7 +319,7 @@ static void parse_static(char *line)
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_dynamic(char *line, struct timeval *mtime)
|
||||
static void parse_dynamic(char *line, struct rlimit rlimit[], char *file)
|
||||
{
|
||||
char *x;
|
||||
char cmd[CMD_SIZE];
|
||||
@@ -315,26 +345,26 @@ static void parse_dynamic(char *line, struct timeval *mtime)
|
||||
|
||||
/* Monitored daemon, will be respawned on exit */
|
||||
if (MATCH_CMD(line, "service ", x)) {
|
||||
service_register(SVC_TYPE_SERVICE, x, mtime);
|
||||
service_register(SVC_TYPE_SERVICE, x, rlimit, file);
|
||||
return;
|
||||
}
|
||||
|
||||
/* One-shot task, will not be respawned */
|
||||
if (MATCH_CMD(line, "task ", x)) {
|
||||
service_register(SVC_TYPE_TASK, x, mtime);
|
||||
service_register(SVC_TYPE_TASK, x, rlimit, file);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Like task but waits for completion, useful w/ [S] */
|
||||
if (MATCH_CMD(line, "run ", x)) {
|
||||
service_register(SVC_TYPE_RUN, x, mtime);
|
||||
service_register(SVC_TYPE_RUN, x, rlimit, file);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Classic inetd service */
|
||||
if (MATCH_CMD(line, "inetd ", x)) {
|
||||
#ifdef INETD_ENABLED
|
||||
service_register(SVC_TYPE_INETD, x, mtime);
|
||||
service_register(SVC_TYPE_INETD, x, rlimit, file);
|
||||
#else
|
||||
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
|
||||
#endif
|
||||
@@ -343,20 +373,13 @@ static void parse_dynamic(char *line, struct timeval *mtime)
|
||||
|
||||
/* Read resource limits */
|
||||
if (MATCH_CMD(line, "rlimit ", x)) {
|
||||
conf_parse_rlimit(x);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Special TTY console powers */
|
||||
if (MATCH_CMD(line, "console ", x)) {
|
||||
if (console) free(console);
|
||||
console = strdup(strip_line(x));
|
||||
conf_parse_rlimit(x, rlimit);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Regular or serial TTYs to run getty */
|
||||
if (MATCH_CMD(line, "tty ", x)) {
|
||||
tty_register(strip_line(x), mtime);
|
||||
tty_register(strip_line(x), rlimit, file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -372,9 +395,10 @@ static void tabstospaces(char *line)
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_conf_dynamic(char *file, struct timeval *mtime)
|
||||
static int parse_conf_dynamic(char *file)
|
||||
{
|
||||
FILE *fp;
|
||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||
|
||||
fp = fopen(file, "r");
|
||||
if (!fp) {
|
||||
@@ -382,7 +406,10 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime)
|
||||
return 1;
|
||||
}
|
||||
|
||||
_d("Parsing %s", file);
|
||||
/* Prepare default limits for each service */
|
||||
memcpy(rlimit, global_rlimit, sizeof(rlimit));
|
||||
|
||||
_d("Parsing %s <<<<<<", file);
|
||||
while (!feof(fp)) {
|
||||
char line[LINE_SIZE] = "";
|
||||
|
||||
@@ -393,7 +420,7 @@ static int parse_conf_dynamic(char *file, struct timeval *mtime)
|
||||
tabstospaces(line);
|
||||
_d("%s", line);
|
||||
|
||||
parse_dynamic(line, mtime);
|
||||
parse_dynamic(line, rlimit, file);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
@@ -405,34 +432,19 @@ static int parse_conf(char *file)
|
||||
{
|
||||
FILE *fp;
|
||||
char line[LINE_SIZE] = "";
|
||||
char *x;
|
||||
|
||||
/*
|
||||
* Get current global limits, which may be overridden from both
|
||||
* finit.conf, for Finit and its services like inetd+getty, and
|
||||
* *.conf in finit.d/, for each service(s) listed there.
|
||||
*/
|
||||
for (int i = 0; i < RLIMIT_NLIMITS; i++)
|
||||
getrlimit(i, &global_rlimit[i]);
|
||||
|
||||
fp = fopen(file, "r");
|
||||
if (!fp)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* If not standard finit.conf, then we want to show just the base name
|
||||
* Loading configuration ............. vs
|
||||
* Loading services configuration ....
|
||||
*/
|
||||
if (!string_match (file, FINIT_CONF)) {
|
||||
/* Remove leading path */
|
||||
x = strrchr(file, '/');
|
||||
if (!x) x = file;
|
||||
else x++;
|
||||
|
||||
/* Remove ending .conf */
|
||||
strlcpy(line, x, sizeof(line));
|
||||
x = strstr(line, ".conf");
|
||||
if (x) *x = 0;
|
||||
|
||||
/* Add empty space. */
|
||||
strcat(line, " ");
|
||||
}
|
||||
|
||||
print(0, "Loading %sconfiguration", line);
|
||||
|
||||
_d("Parsing %s", file);
|
||||
while (!feof(fp)) {
|
||||
if (!fgets(line, sizeof(line), fp))
|
||||
@@ -443,43 +455,56 @@ static int parse_conf(char *file)
|
||||
_d("%s", line);
|
||||
|
||||
parse_static(line);
|
||||
parse_dynamic(line, NULL);
|
||||
parse_dynamic(line, global_rlimit, NULL);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
/* Set global limits */
|
||||
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||
if (setrlimit(i, &global_rlimit[i]) == -1)
|
||||
logit(LOG_WARNING, "rlimit: Failed setting %s", rlim2str(i));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reload all *.conf in /etc/finit.d/ */
|
||||
int conf_reload_dynamic(void)
|
||||
/*
|
||||
* Reload /etc/finit.conf and all *.conf in /etc/finit.d/
|
||||
*/
|
||||
int conf_reload(void)
|
||||
{
|
||||
int i, num;
|
||||
char *dir = rcsd;
|
||||
struct dirent **e;
|
||||
|
||||
/* Mark and sweep */
|
||||
svc_mark_dynamic();
|
||||
tty_mark();
|
||||
|
||||
num = scandir(dir, &e, NULL, alphasort);
|
||||
/* First, read /etc/finit.conf */
|
||||
parse_conf(FINIT_CONF);
|
||||
|
||||
/* Next, read all *.conf in /etc/finit.d/ */
|
||||
num = scandir(rcsd, &e, NULL, alphasort);
|
||||
if (num < 0) {
|
||||
_d("Skipping %s, no files found ...", dir);
|
||||
return 1;
|
||||
_d("Skipping %s, no files found ...", rcsd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Update global limits in case operator used prlimit(1) on us */
|
||||
for (i = 0; i < RLIMIT_NLIMITS; i++)
|
||||
getrlimit(i, &global_rlimit[i]);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
char *name = e[i]->d_name;
|
||||
char path[LINE_SIZE];
|
||||
size_t len;
|
||||
struct stat st;
|
||||
struct timeval mtime;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
||||
snprintf(path, sizeof(path), "%s/%s", rcsd, name);
|
||||
|
||||
/* Check that it's an actual file ... beyond any symlinks */
|
||||
if (lstat(path, &st)) {
|
||||
lost:
|
||||
_d("Skipping %s, cannot access: %s", path, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
@@ -496,10 +521,6 @@ int conf_reload_dynamic(void)
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -514,27 +535,210 @@ int conf_reload_dynamic(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stat(path, &st))
|
||||
goto lost; /* Weird, lost file between here and lstat() */
|
||||
|
||||
TIMESPEC_TO_TIMEVAL(&mtime, &st.st_mtim);
|
||||
parse_conf_dynamic(path, &mtime);
|
||||
parse_conf_dynamic(path);
|
||||
}
|
||||
|
||||
while (num--)
|
||||
free(e[num]);
|
||||
free(e);
|
||||
|
||||
/* Drop record of all .conf changes */
|
||||
drop_changes();
|
||||
|
||||
/*
|
||||
* Set host name, from %DEFHOST, *.conf or /etc/hostname. The
|
||||
* latter wins, if neither exists we default to "noname"
|
||||
*/
|
||||
set_hostname(&hostname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conf_parse_config(void)
|
||||
static struct conf_change *conf_find(char *file)
|
||||
{
|
||||
struct conf_change *node, *tmp;
|
||||
|
||||
TAILQ_FOREACH_SAFE(node, &conf_change_list, link, tmp) {
|
||||
if (string_compare(node->name, file))
|
||||
return node;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void drop_change(struct conf_change *node)
|
||||
{
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
TAILQ_REMOVE(&conf_change_list, node, link);
|
||||
free(node->name);
|
||||
free(node);
|
||||
}
|
||||
|
||||
|
||||
static void drop_changes(void)
|
||||
{
|
||||
struct conf_change *node, *tmp;
|
||||
|
||||
TAILQ_FOREACH_SAFE(node, &conf_change_list, link, tmp)
|
||||
drop_change(node);
|
||||
}
|
||||
|
||||
static int do_change(char *name, uint32_t mask)
|
||||
{
|
||||
struct conf_change *node;
|
||||
|
||||
node = conf_find(name);
|
||||
if (mask & (IN_DELETE | IN_MOVED_FROM)) {
|
||||
drop_change(node);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (node) {
|
||||
_d("Event already registered for %s ...", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
node = malloc(sizeof(*node));
|
||||
if (!node)
|
||||
return 1;
|
||||
|
||||
node->name = strdup(name);
|
||||
if (!node->name) {
|
||||
free(node);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TAILQ_INSERT_HEAD(&conf_change_list, node,link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conf_changed(char *file)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
if (file && (ptr = strrchr(file, '/')))
|
||||
file = ++ptr;
|
||||
|
||||
if (conf_find(file))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void conf_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1) + 1];
|
||||
struct inotify_event *ev;
|
||||
ssize_t sz, len;
|
||||
|
||||
sz = read(w->fd, ev_buf, sizeof(ev_buf) - 1);
|
||||
if (sz <= 0) {
|
||||
_pe("invalid inotify event");
|
||||
return;
|
||||
}
|
||||
ev_buf[sz] = 0;
|
||||
ev = (struct inotify_event *)ev_buf;
|
||||
|
||||
if (arg) {
|
||||
do_change(arg, ev->mask);
|
||||
return;
|
||||
}
|
||||
|
||||
for (ev = (void *)ev_buf; sz > (ssize_t)sizeof(*ev);
|
||||
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
|
||||
if (do_change(ev->name, ev->mask)) {
|
||||
_pe("conf_monitor: Out of memory");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int add_watcher(uev_ctx_t *ctx, uev_t *w, char *path, uint32_t opt)
|
||||
{
|
||||
struct stat st;
|
||||
uint32_t mask = IN_CREATE | IN_DELETE | IN_MODIFY | IN_ATTRIB | IN_MOVE;
|
||||
char *arg = NULL;
|
||||
int fd, wd;
|
||||
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
if (stat(path, &st)) {
|
||||
_d("No such file or directory, skipping %s", path);
|
||||
w->fd = -1;
|
||||
return 0;
|
||||
}
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
arg = strrchr(path, '/');
|
||||
if (!arg)
|
||||
arg = path;
|
||||
else
|
||||
arg++;
|
||||
}
|
||||
|
||||
if (w->fd >= 0)
|
||||
close(w->fd);
|
||||
|
||||
fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
_pe("Failed creating inotify descriptor");
|
||||
w->fd = -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only forward error, don't report error,
|
||||
* user may not have @path and that's OK
|
||||
*/
|
||||
wd = inotify_add_watch(fd, path, mask | opt);
|
||||
if (wd < 0) {
|
||||
w->fd = -1;
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (uev_io_init(ctx, w, conf_cb, arg, fd, UEV_READ)) {
|
||||
_pe("Failed setting up I/O callback for %s watcher", path);
|
||||
w->fd = -1;
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up inotify watcher and load all *.conf in /etc/finit.d/
|
||||
*/
|
||||
int conf_monitor(uev_ctx_t *ctx)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/*
|
||||
* If only one watcher fails, that's OK. A user may have only
|
||||
* one of /etc/finit.conf or /etc/finit.d in use, and may also
|
||||
* have or not have symlinks in place. We need to monitor for
|
||||
* changes to either symlink or target.
|
||||
*/
|
||||
rc += add_watcher(ctx, &w1, FINIT_RCSD, 0);
|
||||
rc += add_watcher(ctx, &w2, FINIT_RCSD "/available", IN_DONT_FOLLOW);
|
||||
rc += add_watcher(ctx, &w3, FINIT_CONF, 0);
|
||||
|
||||
return rc + conf_reload();
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare .conf parser and load all .conf files
|
||||
*/
|
||||
int conf_init(void)
|
||||
{
|
||||
hostname = strdup(DEFHOST);
|
||||
w1.fd = w2.fd = w3.fd = -1;
|
||||
|
||||
return parse_conf(FINIT_CONF) || conf_reload_dynamic();
|
||||
return conf_monitor(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+10
-2
@@ -26,11 +26,19 @@
|
||||
|
||||
#include "svc.h"
|
||||
|
||||
extern struct rlimit global_rlimit[];
|
||||
|
||||
int str2rlim(char *str);
|
||||
char *rlim2str(int rlim);
|
||||
|
||||
int conf_init (void);
|
||||
void conf_reload (void);
|
||||
int conf_changed (char *file);
|
||||
int conf_monitor (uev_ctx_t *ctx);
|
||||
|
||||
void conf_parse_cmdline (void);
|
||||
int conf_parse_runlevels (char *runlevels);
|
||||
void conf_parse_cond (svc_t *svc, char *cond);
|
||||
int conf_parse_config (void);
|
||||
void conf_reload_dynamic (void);
|
||||
|
||||
#endif /* FINIT_CONF_H_ */
|
||||
|
||||
|
||||
+107
-17
@@ -30,17 +30,18 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||
#include <termios.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "sig.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
#define NUM_ARGS 16
|
||||
|
||||
int getty(char *tty, char *baud, char *term, char *user);
|
||||
|
||||
|
||||
/* Wait for process completion, returns status of waitpid(2) syscall */
|
||||
int complete(char *cmd, int pid)
|
||||
@@ -234,30 +235,77 @@ int exec_runtask(char *cmd, char *args[])
|
||||
return execvp(_PATH_BSHELL, argv);
|
||||
}
|
||||
|
||||
static void prepare_tty(char *tty, char *procname, int console)
|
||||
static void prepare_tty(char *tty, speed_t speed, char *procname, struct rlimit rlimit[])
|
||||
{
|
||||
struct sigaction sa;
|
||||
struct termios term;
|
||||
|
||||
/*
|
||||
* Reset to sane defaults in case of messup from prev. session
|
||||
*/
|
||||
stty(STDIN_FILENO, speed);
|
||||
|
||||
/*
|
||||
* Disable ISIG (INTR, QUIT, SUSP) before handing over to getty.
|
||||
* It is up to the getty process to allow them again.
|
||||
*/
|
||||
tcdrain(STDIN_FILENO);
|
||||
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||
term.c_lflag &= ~ISIG;
|
||||
term.c_cc[VEOF] = _POSIX_VDISABLE;
|
||||
term.c_cc[VINTR] = _POSIX_VDISABLE;
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &term);
|
||||
}
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
sig_unblock();
|
||||
|
||||
/*
|
||||
* Ignore a few signals, needed to prevent Ctrl-C at login:
|
||||
* prompt and to prevent QUIT from dumping core.
|
||||
*/
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigaction(SIGHUP, &sa, NULL);
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
sigaction(SIGQUIT, &sa, NULL);
|
||||
|
||||
/* Set configured limits */
|
||||
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||
if (setrlimit(i, &rlimit[i]) == -1)
|
||||
logit(LOG_WARNING, "%s: rlimit: Failed setting %s", tty, rlim2str(i));
|
||||
}
|
||||
|
||||
/* Create new session and process group */
|
||||
setsid();
|
||||
|
||||
/* Set INIT_PROCESS UTMP entry */
|
||||
/* Finit is responsible for the UTMP INIT_PROCESS record */
|
||||
utmp_set_init(tty, 0);
|
||||
|
||||
if (console)
|
||||
procname = "console";
|
||||
prctl(PR_SET_NAME, procname, 0, 0, 0);
|
||||
}
|
||||
|
||||
static int activate_console(int noclear, int nowait)
|
||||
{
|
||||
int ret = 0;
|
||||
struct termios term;
|
||||
|
||||
if (!noclear)
|
||||
(void)write(STDERR_FILENO, "\e[r\e[H\e[J", 9);
|
||||
|
||||
if (nowait)
|
||||
return 1;
|
||||
|
||||
/* Disable ECHO, XON/OFF while waiting for <CR> */
|
||||
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||
term.c_iflag &= ~(IXON|IXOFF);
|
||||
term.c_lflag &= ~ECHO;
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &term);
|
||||
}
|
||||
|
||||
while (!fexist(SYNC_SHUTDOWN)) {
|
||||
char c;
|
||||
static const char clr[] = "\r\e[2K";
|
||||
static const char msg[] = "\nPlease press Enter to activate this console.";
|
||||
|
||||
if (fexist(SYNC_STOPPED)) {
|
||||
@@ -265,26 +313,63 @@ static int activate_console(int noclear, int nowait)
|
||||
continue;
|
||||
}
|
||||
|
||||
(void)write(STDERR_FILENO, msg, sizeof(msg));
|
||||
(void)write(STDERR_FILENO, clr, strlen(clr));
|
||||
(void)write(STDERR_FILENO, msg, strlen(msg));
|
||||
while (read(STDIN_FILENO, &c, 1) == 1 && c != '\n')
|
||||
continue;
|
||||
|
||||
if (fexist(SYNC_STOPPED))
|
||||
continue;
|
||||
|
||||
return 1;
|
||||
(void)write(STDERR_FILENO, clr, strlen(clr));
|
||||
ret = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
/* Restore ECHO, XON/OFF */
|
||||
if (!tcgetattr(STDIN_FILENO, &term)) {
|
||||
term.c_iflag |= IXON|IXOFF;
|
||||
term.c_lflag |= ECHO;
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &term);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
pid_t run_getty(char *tty, char *speed, char *term, int noclear, int nowait, int console)
|
||||
/*
|
||||
* Start a getty on @tty
|
||||
*
|
||||
* At the login: prompt, no signals are allowed, both Ctrl-C and Ctrl-D
|
||||
* should be disabled. Ctrl-S and Ctrl-Q are optional, but most getty
|
||||
* allow them.
|
||||
*
|
||||
* Prior to getty is called and login: is printed, Finit may display the
|
||||
* "Please press Enter ..." if @nowait is unset. This mode must be RAW,
|
||||
* only accepting <CR> and not echoing anything, this also means no
|
||||
* signals are allowed. For ease of implementation Finit will call the
|
||||
* stty() function to reset the TTY and then force RAW mode until a <CR>
|
||||
* has been received. This is handled in the same fashion for both this
|
||||
* function and run_getty2(), which is used for external getty.
|
||||
*
|
||||
* When handing over to /bin/login, Ctrl-C and Ctrl-D must be enabled
|
||||
* since /bin/login usually only disables ECHO until a password line has
|
||||
* been entered. Upon starting the user's $SHELL the ISIG flag is reset
|
||||
*/
|
||||
pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[])
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
prepare_tty(tty, "finit-getty", console);
|
||||
speed_t speed = B38400;
|
||||
|
||||
if (baud) {
|
||||
speed = stty_parse_speed(baud);
|
||||
if (B0 == speed)
|
||||
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
|
||||
}
|
||||
|
||||
prepare_tty(tty, speed, "finit-getty", rlimit);
|
||||
if (activate_console(noclear, nowait))
|
||||
_exit(getty(tty, speed, term, NULL));
|
||||
}
|
||||
@@ -292,7 +377,7 @@ pid_t run_getty(char *tty, char *speed, char *term, int noclear, int nowait, int
|
||||
return pid;
|
||||
}
|
||||
|
||||
pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, int console)
|
||||
pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[])
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
@@ -321,7 +406,8 @@ pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, in
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
|
||||
prepare_tty(tty, "getty", console);
|
||||
/* Dunno speed, tell stty() to not mess with it */
|
||||
prepare_tty(tty, B0, "getty", rlimit);
|
||||
|
||||
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
||||
_pe("Failed TIOCSCTTY");
|
||||
@@ -351,14 +437,18 @@ int run_parts(char *dir, char *cmd)
|
||||
for (i = 0; i < num; i++) {
|
||||
int j = 0;
|
||||
pid_t pid = 0;
|
||||
mode_t mode;
|
||||
struct stat st;
|
||||
char *args[NUM_ARGS];
|
||||
char *name = e[i]->d_name;
|
||||
char path[LINE_SIZE];
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
||||
mode = fmode(path);
|
||||
if (!S_ISEXEC(mode) || S_ISDIR(mode)) {
|
||||
if (stat(path, &st)) {
|
||||
_d("Failed stat(%s): %s", path, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode)) {
|
||||
_d("Skipping %s ...", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
+99
-65
@@ -36,6 +36,7 @@
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
@@ -57,7 +58,6 @@ char *network = NULL;
|
||||
char *hostname = NULL;
|
||||
char *rcsd = FINIT_RCSD;
|
||||
char *runparts = NULL;
|
||||
char *console = NULL;
|
||||
|
||||
uev_ctx_t *ctx = NULL; /* Main loop context */
|
||||
|
||||
@@ -108,7 +108,7 @@ static int fismnt(char *dir)
|
||||
*/
|
||||
static int fsck(int pass)
|
||||
{
|
||||
int save;
|
||||
// int save;
|
||||
struct fstab *fs;
|
||||
|
||||
if (!setfsent()) {
|
||||
@@ -116,8 +116,8 @@ static int fsck(int pass)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((save = log_is_debug()))
|
||||
log_debug();
|
||||
// if ((save = log_is_debug()))
|
||||
// log_debug();
|
||||
|
||||
while ((fs = getfsent())) {
|
||||
char cmd[80];
|
||||
@@ -143,8 +143,8 @@ static int fsck(int pass)
|
||||
run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
|
||||
}
|
||||
|
||||
if (save)
|
||||
log_debug();
|
||||
// if (save)
|
||||
// log_debug();
|
||||
endfsent();
|
||||
|
||||
return 0;
|
||||
@@ -241,11 +241,80 @@ static void emergency_shell(void)
|
||||
#endif /* EMERGENCY_SHELL */
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle bootstrap transition to configured runlevel, start TTYs
|
||||
*
|
||||
* This is the final stage of bootstrap. It changes to the default
|
||||
* (configured) runlevel, calls all external start scripts and final
|
||||
* bootstrap hooks before bringing up TTYs.
|
||||
*
|
||||
* We must ensure that all declared `task [S]` and `run [S]` jobs in
|
||||
* finit.conf, or *.conf in finit.d/, run to completion before we
|
||||
* finalize the bootstrap process by calling this function.
|
||||
*/
|
||||
static void finalize(void)
|
||||
{
|
||||
/*
|
||||
* Network stuff
|
||||
*/
|
||||
_d("Setting up networking ...");
|
||||
networking();
|
||||
umask(022);
|
||||
|
||||
/* Hooks that rely on loopback, or basic networking being up. */
|
||||
_d("Calling all network up hooks ...");
|
||||
plugin_run_hooks(HOOK_NETWORK_UP);
|
||||
|
||||
/*
|
||||
* Start all tasks/services in the configured runlevel
|
||||
*/
|
||||
_d("Change to default runlevel, start all services ...");
|
||||
service_runlevel(cfglevel);
|
||||
|
||||
/* Clean up bootstrap-only tasks/services that never started */
|
||||
_d("Clean up all bootstrap-only tasks/services ...");
|
||||
svc_prune_bootstrap();
|
||||
|
||||
/* All services/tasks/inetd/etc. in configure runlevel have started */
|
||||
_d("Running svc up hooks ...");
|
||||
plugin_run_hooks(HOOK_SVC_UP);
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
/*
|
||||
* Run startup scripts in the runparts directory, if any.
|
||||
*/
|
||||
if (runparts && fisdir(runparts)) {
|
||||
_d("Running startup scripts in %s ...", runparts);
|
||||
run_parts(runparts, NULL);
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/* Convenient SysV compat for when you just don't care ... */
|
||||
if (!access(FINIT_RC_LOCAL, X_OK)) {
|
||||
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/* Hooks that should run at the very end */
|
||||
_d("Calling all system up hooks ...");
|
||||
plugin_run_hooks(HOOK_SYSTEM_UP);
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
/* Enable silent mode before starting TTYs */
|
||||
_d("Going silent ...");
|
||||
log_silent();
|
||||
|
||||
/* Delayed start of TTYs at bootstrap */
|
||||
_d("Launching all getty services ...");
|
||||
tty_runlevel();
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
char *path;
|
||||
char cmd[256];
|
||||
int ret, udev = 0;
|
||||
int udev = 0;
|
||||
uev_t timer; /* Bootstrap timer, on timeout call finalize() */
|
||||
uev_ctx_t loop;
|
||||
|
||||
/*
|
||||
@@ -298,16 +367,13 @@ int main(int argc, char* argv[])
|
||||
* Load plugins early, finit.conf may contain references to
|
||||
* features implemented by plugins.
|
||||
*/
|
||||
ret = plugin_init(&loop, PLUGIN_PATH);
|
||||
plugin_init(&loop, PLUGIN_PATH);
|
||||
|
||||
/*
|
||||
* Hello world.
|
||||
*/
|
||||
banner();
|
||||
|
||||
/* Show results after banner() */
|
||||
print(ret, "Loading plugins");
|
||||
|
||||
/*
|
||||
* Check file filesystems in /etc/fstab
|
||||
*/
|
||||
@@ -329,9 +395,8 @@ int main(int argc, char* argv[])
|
||||
makedir("/dev/pts", 0755);
|
||||
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620");
|
||||
|
||||
/* Some systems rely on /dev/shm being there, for `mount -a` below */
|
||||
makedir("/dev/shm", 0755);
|
||||
if (!fismnt("/dev/shm"))
|
||||
mount("shm", "/dev/shm", "tmpfs", 0, NULL);
|
||||
|
||||
/*
|
||||
* New tmpfs based /run for volatile runtime data
|
||||
@@ -361,7 +426,7 @@ int main(int argc, char* argv[])
|
||||
|
||||
/* Register udevd as a monitored service, started much later */
|
||||
snprintf(cmd, sizeof(cmd), "[12345] %s -- Device event manager daemon", path);
|
||||
if (service_register(SVC_TYPE_SERVICE, cmd, NULL)) {
|
||||
if (service_register(SVC_TYPE_SERVICE, cmd, global_rlimit, NULL)) {
|
||||
_pe("Failed registering %s", path);
|
||||
udev = 0;
|
||||
}
|
||||
@@ -386,18 +451,14 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse /etc/finit.conf and all *.conf in /etc/finit.d/ to
|
||||
* figure out how to bootstrap the system.
|
||||
* Initialize .conf system and load static /etc/finit.conf
|
||||
*/
|
||||
conf_parse_config();
|
||||
conf_init();
|
||||
|
||||
/*
|
||||
* Start built-in watchdog as soon as possible, if enabled
|
||||
*/
|
||||
watchdog(argv[0]);
|
||||
|
||||
/* Set hostname as soon as possible, for syslog et al. */
|
||||
set_hostname(&hostname);
|
||||
wdogpid = watchdog(argv[0]);
|
||||
|
||||
/*
|
||||
* Mount filesystems
|
||||
@@ -413,6 +474,9 @@ int main(int argc, char* argv[])
|
||||
if (fexist("/lib/udev/udev-finish"))
|
||||
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
|
||||
|
||||
/* Bootstrap conditions, needed for hooks */
|
||||
cond_init();
|
||||
|
||||
_d("Root FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_ROOTFS_UP);
|
||||
|
||||
@@ -426,6 +490,12 @@ int main(int argc, char* argv[])
|
||||
/* Base FS up, enable standard SysV init signals */
|
||||
sig_setup(&loop);
|
||||
|
||||
/*
|
||||
* Set up inotify watcher for /etc/finit.d and read all .conf
|
||||
* files to figure out how to bootstrap the system.
|
||||
*/
|
||||
conf_monitor(&loop);
|
||||
|
||||
_d("Base FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_BASEFS_UP);
|
||||
|
||||
@@ -436,56 +506,20 @@ int main(int argc, char* argv[])
|
||||
sm_init(&sm);
|
||||
sm_step(&sm);
|
||||
|
||||
/*
|
||||
* Network stuff
|
||||
*/
|
||||
networking();
|
||||
umask(022);
|
||||
|
||||
/* Hooks that rely on loopback, or basic networking being up. */
|
||||
plugin_run_hooks(HOOK_NETWORK_UP);
|
||||
|
||||
/*
|
||||
* Start all tasks/services in the configured runlevel
|
||||
*/
|
||||
service_runlevel(cfglevel);
|
||||
|
||||
/* Clean up bootstrap-only tasks/services that never started */
|
||||
svc_prune_bootstrap();
|
||||
|
||||
_d("Running svc up hooks ...");
|
||||
plugin_run_hooks(HOOK_SVC_UP);
|
||||
|
||||
/*
|
||||
* Run startup scripts in the runparts directory, if any.
|
||||
*/
|
||||
if (runparts && fisdir(runparts)) {
|
||||
_d("Running startup scripts in %s ...", runparts);
|
||||
run_parts(runparts, NULL);
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/* Convenient SysV compat for when you just don't care ... */
|
||||
if (!access(FINIT_RC_LOCAL, X_OK)) {
|
||||
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/* Hooks that should run at the very end */
|
||||
plugin_run_hooks(HOOK_SYSTEM_UP);
|
||||
|
||||
/* Enable silent mode before starting TTYs */
|
||||
log_silent();
|
||||
|
||||
/* Delayed start of TTYs at bootstrap */
|
||||
tty_runlevel();
|
||||
|
||||
/* Start new initctl API responder */
|
||||
api_init(&loop);
|
||||
|
||||
/*
|
||||
* Wait for all SVC_TYPE_RUNTASK to have completed their work in
|
||||
* [S], or timeout, before calling finalize()
|
||||
*/
|
||||
_d("Starting bootstrap finalize timer ...");
|
||||
uev_timer_init(&loop, &timer, service_bootstrap_cb, finalize, 1000, 1000);
|
||||
|
||||
/*
|
||||
* Enter main loop to monior /dev/initctl and services
|
||||
*/
|
||||
_d("Entering main loop ...");
|
||||
return uev_run(&loop, 0);
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -78,6 +78,9 @@
|
||||
#define INIT_CMD_EMIT 15
|
||||
#define INIT_CMD_GET_RUNLEVEL 16
|
||||
#define INIT_CMD_WDOG_HELLO 128 /* Watchdog register and hello */
|
||||
#define INIT_CMD_SVC_ITER 129
|
||||
#define INIT_CMD_SVC_QUERY 130
|
||||
#define INIT_CMD_SVC_FIND 131
|
||||
#define INIT_CMD_NACK 254
|
||||
#define INIT_CMD_ACK 255
|
||||
|
||||
@@ -98,7 +101,6 @@ extern char *sdown;
|
||||
extern char *network;
|
||||
extern char *hostname;
|
||||
extern char *runparts;
|
||||
extern char *console;
|
||||
extern uev_ctx_t *ctx;
|
||||
|
||||
#endif /* FINIT_H_ */
|
||||
|
||||
+9
-117
@@ -64,42 +64,6 @@ static int readch(char *tty)
|
||||
return ch1 & 0xFF;
|
||||
}
|
||||
|
||||
static void stty(int fd, speed_t speed)
|
||||
{
|
||||
struct termios term;
|
||||
|
||||
tcdrain(fd);
|
||||
if (tcgetattr(fd, &term))
|
||||
return;
|
||||
|
||||
cfsetispeed(&term, speed);
|
||||
cfsetospeed(&term, speed);
|
||||
tcsetattr(fd, TCSAFLUSH, &term);
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
|
||||
/* Disable modem specific flags */
|
||||
term.c_cflag &= ~(0|CSTOPB|PARENB|PARODD);
|
||||
term.c_cflag &= ~CRTSCTS;
|
||||
term.c_cflag |= CLOCAL;
|
||||
|
||||
/* Timeouts, minimum chars and default flags */
|
||||
term.c_cc[VTIME] = 0;
|
||||
term.c_cc[VMIN] = 1;
|
||||
term.c_iflag = ICRNL|IXON|IXOFF;
|
||||
term.c_oflag = OPOST|ONLCR;
|
||||
term.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
|
||||
term.c_lflag |= ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHOKE;
|
||||
|
||||
/* Reset special characters to defaults */
|
||||
term.c_cc[VINTR] = CTRL('C');
|
||||
term.c_cc[VQUIT] = CTRL('\\');
|
||||
term.c_cc[VEOF] = CTRL('D');
|
||||
term.c_cc[VEOL] = '\n';
|
||||
term.c_cc[VKILL] = CTRL('U');
|
||||
term.c_cc[VERASE] = CERASE;
|
||||
tcsetattr(fd, TCSANOW, &term);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse and display a line from /etc/issue
|
||||
*/
|
||||
@@ -162,7 +126,6 @@ static void do_issue(char *tty)
|
||||
*/
|
||||
uname(&uts);
|
||||
|
||||
print("\n");
|
||||
fp = fopen("/etc/issue", "r");
|
||||
if (fp) {
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
@@ -244,72 +207,10 @@ static int do_login(char *name)
|
||||
return 1; /* We shouldn't get here ... */
|
||||
}
|
||||
|
||||
static speed_t do_parse_speed(char *baud)
|
||||
{
|
||||
char *ptr;
|
||||
size_t i;
|
||||
unsigned long val;
|
||||
struct { unsigned long val; speed_t speed; } v2s[] = {
|
||||
{ 0, B0 },
|
||||
{ 50, B50 },
|
||||
{ 75, B75 },
|
||||
{ 110, B110 },
|
||||
{ 134, B134 },
|
||||
{ 150, B150 },
|
||||
{ 200, B200 },
|
||||
{ 300, B300 },
|
||||
{ 600, B600 },
|
||||
{ 1200, B1200 },
|
||||
{ 1800, B1800 },
|
||||
{ 2400, B2400 },
|
||||
{ 4800, B4800 },
|
||||
{ 9600, B9600 },
|
||||
{ 19200, B19200 },
|
||||
{ 38400, B38400 },
|
||||
{ 57600, B57600 },
|
||||
{ 115200, B115200 },
|
||||
{ 230400, B230400 },
|
||||
{ 460800, B460800 },
|
||||
{ 500000, B500000 },
|
||||
{ 576000, B576000 },
|
||||
{ 921600, B921600 },
|
||||
{ 1000000, B1000000 },
|
||||
{ 1152000, B1152000 },
|
||||
{ 1500000, B1500000 },
|
||||
{ 2000000, B2000000 },
|
||||
{ 2500000, B2500000 },
|
||||
{ 3000000, B3000000 },
|
||||
{ 3500000, B3500000 },
|
||||
{ 4000000, B4000000 },
|
||||
};
|
||||
|
||||
errno = 0;
|
||||
val = strtoul(baud, &ptr, 10);
|
||||
if (errno || ptr == baud)
|
||||
return B0;
|
||||
|
||||
for (i = 0; i < sizeof(v2s) / sizeof(v2s[0]); i++) {
|
||||
if (v2s[i].val == val)
|
||||
return v2s[i].speed;
|
||||
}
|
||||
|
||||
return B0;
|
||||
}
|
||||
|
||||
int getty(char *tty, char *baud, char *term, char *user)
|
||||
int getty(char *tty, speed_t speed, char *term, char *user)
|
||||
{
|
||||
int fd;
|
||||
char name[30];
|
||||
speed_t speed = B38400;
|
||||
struct sigaction sa;
|
||||
|
||||
if (baud) {
|
||||
speed = do_parse_speed(baud);
|
||||
if (speed == B0) {
|
||||
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Detach from initial controlling TTY */
|
||||
vhangup();
|
||||
@@ -325,29 +226,20 @@ int getty(char *tty, char *baud, char *term, char *user)
|
||||
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
||||
warn("Failed TIOCSCTTY");
|
||||
|
||||
/*
|
||||
* Ignore a few signals, needed to prevent Ctrl-C at login:
|
||||
* prompt and to prevent QUIT from dumping core.
|
||||
*/
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigaction(SIGHUP, &sa, NULL);
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
sigaction(SIGQUIT, &sa, NULL);
|
||||
|
||||
/* Set up TTY */
|
||||
stty(fd, speed);
|
||||
close(fd);
|
||||
/* The getty process is responsible for the UTMP login record */
|
||||
utmp_set_login(tty, NULL);
|
||||
if (term)
|
||||
setenv("TERM", term, 1);
|
||||
|
||||
if (!user)
|
||||
do_getty(tty, name, sizeof(name));
|
||||
else
|
||||
strlcpy(name, user, sizeof(name));
|
||||
|
||||
/* Set up TTY, re-enabling ISIG et al. */
|
||||
stty(fd, speed);
|
||||
close(fd);
|
||||
|
||||
if (term)
|
||||
setenv("TERM", term, 1);
|
||||
|
||||
return do_login(name);
|
||||
}
|
||||
|
||||
|
||||
+10
-3
@@ -26,7 +26,10 @@
|
||||
#define FINIT_HELPERS_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||
#include <termios.h>
|
||||
#include <lite/lite.h>
|
||||
#include "log.h"
|
||||
|
||||
@@ -48,6 +51,11 @@ struct fstab *getfsent (void);
|
||||
|
||||
char *strip_line (char *line);
|
||||
|
||||
int getty (char *tty, speed_t speed, char *term, char *user);
|
||||
|
||||
int stty (int fd, speed_t speed);
|
||||
speed_t stty_parse_speed(char *baud);
|
||||
|
||||
int pid_alive (pid_t pid);
|
||||
char *pid_get_name (pid_t pid, char *name, size_t len);
|
||||
|
||||
@@ -56,7 +64,6 @@ void print (int action, const char *fmt, ...);
|
||||
void print_desc (char *action, char *desc);
|
||||
int print_result (int fail);
|
||||
|
||||
int start_process (char *cmd, char *args[], int console);
|
||||
int getuser (char *username, char **home);
|
||||
int getgroup (char *group);
|
||||
void set_hostname (char **hostname);
|
||||
@@ -65,8 +72,8 @@ int complete (char *cmd, int pid);
|
||||
int run (char *cmd);
|
||||
int run_interactive (char *cmd, char *fmt, ...);
|
||||
int exec_runtask (char *cmd, char *args[]);
|
||||
pid_t run_getty (char *tty, char *speed, char *term, int noclear, int nowait, int console);
|
||||
pid_t run_getty2 (char *tty, char *cmd, char *args[], int noclear, int nowait, int console);
|
||||
pid_t run_getty (char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[]);
|
||||
pid_t run_getty2 (char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]);
|
||||
int run_parts (char *dir, char *cmd);
|
||||
|
||||
static inline void create(char *path, mode_t mode, uid_t uid, gid_t gid)
|
||||
|
||||
+28
-5
@@ -70,6 +70,22 @@ static int inetd_dgram_peek(int sd, char *ifname)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
|
||||
static void inetd_dgram_drop(int sd, const char *ifname)
|
||||
{
|
||||
char pkt_interface[17];
|
||||
char buf[BUFSIZ];
|
||||
|
||||
do {
|
||||
inetd_dgram_peek(sd, pkt_interface);
|
||||
if(string_compare(pkt_interface, ifname)) {
|
||||
recv(sd, buf, sizeof(buf), 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
} while (0);
|
||||
}
|
||||
|
||||
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
|
||||
static int inetd_stream_peek(int sd, char *ifname)
|
||||
{
|
||||
@@ -129,6 +145,8 @@ static int get_stdin(svc_t *svc)
|
||||
logit(LOG_INFO, "Service %s on %s:%d is not allowed", svc->inetd.name, ifname, svc->inetd.port);
|
||||
if (svc->inetd.type == SOCK_STREAM)
|
||||
close(stdin);
|
||||
else
|
||||
inetd_dgram_drop(stdin, ifname);
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -142,7 +160,12 @@ static void socket_cb(uev_t *w, void *arg, int events)
|
||||
svc_t *svc = (svc_t *)arg, *task;
|
||||
int stdin;
|
||||
|
||||
_d("Got event on %s socket ...", svc->cmd);
|
||||
_d("%s: Got socket event ...", svc->cmd);
|
||||
if (UEV_ERROR == events) {
|
||||
logit(LOG_INFO, "%s: Socket error, aborting: %m", svc->cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
stdin = get_stdin(svc);
|
||||
if (stdin < 0) {
|
||||
logit(LOG_CRIT, "%s: Unable to accept incoming connection", svc->cmd);
|
||||
@@ -202,10 +225,10 @@ static void socket_cb(uev_t *w, void *arg, int events)
|
||||
*/
|
||||
int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name)
|
||||
{
|
||||
svc_t *svc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
char pname[NI_MAXHOST];
|
||||
|
||||
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) {
|
||||
for (svc = svc_inetd_iterator(&iter, 1); svc; svc = svc_inetd_iterator(&iter, 0)) {
|
||||
inetd_t *i = &svc->inetd;
|
||||
|
||||
if (!i->builtin || i->type != SOCK_DGRAM)
|
||||
@@ -642,9 +665,9 @@ int inetd_filter_str(inetd_t *inetd, char *str, size_t len)
|
||||
|
||||
svc_t *inetd_find_svc(char *path, char *service, char *proto)
|
||||
{
|
||||
svc_t *svc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) {
|
||||
for (svc = svc_inetd_iterator(&iter, 1); svc; svc = svc_inetd_iterator(&iter, 0)) {
|
||||
if (strncmp(path, svc->cmd, strlen(svc->cmd)))
|
||||
continue;
|
||||
|
||||
|
||||
+86
-148
@@ -33,11 +33,9 @@
|
||||
#include <time.h>
|
||||
#include <utmp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "client.h"
|
||||
#include "cond.h"
|
||||
#include "serv.h"
|
||||
#include "service.h"
|
||||
@@ -45,78 +43,25 @@
|
||||
|
||||
#define _PATH_COND _PATH_VARRUN "finit/cond/"
|
||||
|
||||
typedef struct {
|
||||
char *cmd;
|
||||
int (*cb)(char *arg);
|
||||
} command_t;
|
||||
|
||||
int verbose = 0;
|
||||
int runlevel = 0;
|
||||
|
||||
|
||||
static int do_send(struct init_request *rq, ssize_t len)
|
||||
static int runlevel_get(int *prevlevel)
|
||||
{
|
||||
int sd, result = 255;
|
||||
struct sockaddr_un sun = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (-1 == sd)
|
||||
return -1;
|
||||
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
||||
goto error;
|
||||
|
||||
if (write(sd, rq, len) != len)
|
||||
goto error;
|
||||
|
||||
if (read(sd, rq, len) != len)
|
||||
goto error;
|
||||
|
||||
result = 0;
|
||||
goto exit;
|
||||
error:
|
||||
perror("Failed communicating with finit");
|
||||
exit:
|
||||
close(sd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int runlevel_get(void)
|
||||
{
|
||||
int sd, result = 255;
|
||||
int result;
|
||||
struct init_request rq;
|
||||
struct sockaddr_un sun = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
|
||||
memset(&rq, 0, sizeof(rq));
|
||||
rq.cmd = INIT_CMD_GET_RUNLEVEL;
|
||||
rq.magic = INIT_MAGIC;
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (-1 == sd)
|
||||
return -1;
|
||||
result = client_send(&rq, sizeof(rq));
|
||||
if (!result) {
|
||||
result = rq.runlevel;
|
||||
if (prevlevel)
|
||||
*prevlevel = rq.sleeptime;
|
||||
}
|
||||
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
||||
goto error;
|
||||
|
||||
if (write(sd, &rq, sizeof(rq)) != sizeof(rq))
|
||||
goto error;
|
||||
|
||||
if (read(sd, &rq, sizeof(rq)) != sizeof(rq))
|
||||
goto error;
|
||||
|
||||
result = rq.runlevel;
|
||||
goto exit;
|
||||
error:
|
||||
perror("Failed communicating with finit");
|
||||
result = -1;
|
||||
exit:
|
||||
close(sd);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -127,7 +72,7 @@ static int toggle_debug(char *arg)
|
||||
.cmd = INIT_CMD_DEBUG,
|
||||
};
|
||||
|
||||
return do_send(&rq, sizeof(rq));
|
||||
return client_send(&rq, sizeof(rq));
|
||||
}
|
||||
|
||||
static int do_log(char *svc)
|
||||
@@ -146,6 +91,9 @@ static int do_log(char *svc)
|
||||
return system(cmd);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: Convert to read UTMP instead, like runlevel(8) does
|
||||
*/
|
||||
static int do_runlevel(char *arg)
|
||||
{
|
||||
struct init_request rq = {
|
||||
@@ -154,14 +102,25 @@ static int do_runlevel(char *arg)
|
||||
.runlevel = (int)arg[0],
|
||||
};
|
||||
|
||||
/* Not compatible with the SysV runlevel(8) command, it prints
|
||||
* "PREVLEVEL RUNLEVEL", we just print the current runlevel. */
|
||||
if (!rq.runlevel) {
|
||||
printf("%d\n", runlevel_get());
|
||||
char prev;
|
||||
int runlevel, prevlevel;
|
||||
|
||||
runlevel = runlevel_get(&prevlevel);
|
||||
if (255 == runlevel) {
|
||||
printf("unknown\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
prev = prevlevel + '0';
|
||||
if (prev <= '0' || prev > '9')
|
||||
prev = 'N';
|
||||
|
||||
printf("%c %d\n", prev , runlevel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return do_send(&rq, sizeof(rq));
|
||||
return client_send(&rq, sizeof(rq));
|
||||
}
|
||||
|
||||
static int do_svc(int cmd, char *arg)
|
||||
@@ -173,27 +132,12 @@ static int do_svc(int cmd, char *arg)
|
||||
|
||||
strlcpy(rq.data, arg, sizeof(rq.data));
|
||||
|
||||
return do_send(&rq, sizeof(rq));
|
||||
return client_send(&rq, sizeof(rq));
|
||||
}
|
||||
|
||||
static int do_emit (char *arg) { return do_svc(INIT_CMD_EMIT, arg); }
|
||||
static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD, arg); }
|
||||
|
||||
static int missing(char *job, int id)
|
||||
{
|
||||
char idstr[10] = "";
|
||||
|
||||
if (!job)
|
||||
job = "";
|
||||
if (id > 1)
|
||||
snprintf(idstr, sizeof(idstr), ":%d", id);
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "No such service %s%s\n", job, idstr);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a wrapper for do_svc() that adds a simple sanity check of
|
||||
* the service(s) provided as argument. If a service does not exist
|
||||
@@ -201,28 +145,20 @@ static int missing(char *job, int id)
|
||||
*/
|
||||
static int do_startstop(int cmd, char *arg)
|
||||
{
|
||||
int ret;
|
||||
char *svcs;
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_SVC_QUERY
|
||||
};
|
||||
|
||||
if (!arg) {
|
||||
fprintf(stderr, "Usage: initctl %s <JOB|NAME>[:ID]",
|
||||
strlcpy(rq.data, arg, sizeof(rq.data));
|
||||
if (client_send(&rq, sizeof(rq))) {
|
||||
fprintf(stderr, "No such job(s) or service(s): %s\n\n", rq.data);
|
||||
fprintf(stderr, "Usage: initctl %s <JOB|NAME>[:ID]\n",
|
||||
cmd == INIT_CMD_START_SVC ? "start" :
|
||||
(cmd == INIT_CMD_STOP_SVC ? "stop" : "restart"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
svcs = strdup(arg);
|
||||
if (!svcs) {
|
||||
perror("Failed validating service(s) name");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Include \0 in len, needed by parser */
|
||||
ret = svc_parse_jobstr(svcs, strlen(svcs) + 1, NULL, missing);
|
||||
free(svcs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return do_svc(cmd, arg);
|
||||
}
|
||||
|
||||
@@ -265,6 +201,9 @@ static int do_cond_magic(char op, char *cond)
|
||||
{
|
||||
char event[368]; /* sizeof(init_request.data) */
|
||||
|
||||
if (!cond || strlen(cond) < 1)
|
||||
return 1;
|
||||
|
||||
event[0] = op;
|
||||
strlcpy(&event[1], cond, sizeof(event) - 1);
|
||||
|
||||
@@ -281,7 +220,7 @@ static int dump_one_cond(const char *fpath, const struct stat *sb, int tflag, st
|
||||
if (tflag != FTW_F)
|
||||
return 0;
|
||||
|
||||
if (!strcmp(fpath, _PATH_COND"reconf"))
|
||||
if (!strcmp(fpath, _PATH_COND "reconf"))
|
||||
return 0;
|
||||
|
||||
len = strlen(_PATH_COND);
|
||||
@@ -309,7 +248,7 @@ static int do_cond_show(char *arg)
|
||||
|
||||
printheader(NULL, "PID SERVICE STATUS CONDITION (+ ON, ~ FLUX, - OFF)", 0);
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
if (!svc->cond[0])
|
||||
continue;
|
||||
|
||||
@@ -334,9 +273,9 @@ static int do_cond(char *cmd)
|
||||
int c;
|
||||
char *arg;
|
||||
command_t command[] = {
|
||||
{ "show", do_cond_show },
|
||||
{ "set", do_cond_set },
|
||||
{ "clear", do_cond_clear },
|
||||
{ "show", do_cond_show },
|
||||
{ "dump", do_cond_dump },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
@@ -346,7 +285,7 @@ static int do_cond(char *cmd)
|
||||
*arg++ = 0;
|
||||
|
||||
for (c = 0; command[c].cmd; c++) {
|
||||
if (!strcmp(command[c].cmd, cmd))
|
||||
if (string_match(command[c].cmd, cmd))
|
||||
return command[c].cb(arg);
|
||||
}
|
||||
|
||||
@@ -431,7 +370,7 @@ static int show_version(char *arg)
|
||||
char *runlevel_string(int runlevel, int levels)
|
||||
{
|
||||
int i, pos = 1;
|
||||
static char lvl[20];
|
||||
static char lvl[21];
|
||||
|
||||
memset(lvl, 0, sizeof(lvl));
|
||||
lvl[0] = '[';
|
||||
@@ -459,50 +398,42 @@ char *runlevel_string(int runlevel, int levels)
|
||||
return lvl;
|
||||
}
|
||||
|
||||
/* In verbose mode we skip the header and each service description.
|
||||
/*
|
||||
* In verbose mode we skip the header and each service description.
|
||||
* This in favor of having all info on one line so a machine can more
|
||||
* easily parse it. */
|
||||
* easily parse it.
|
||||
*/
|
||||
static int show_status(char *arg)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
/* Fetch UTMP runlevel, needed for svc_status() call below */
|
||||
runlevel = runlevel_get();
|
||||
runlevel = runlevel_get(NULL);
|
||||
|
||||
if (arg && arg[0]) {
|
||||
int id = 1;
|
||||
char *ptr, *next;
|
||||
|
||||
ptr = strchr(arg, ':');
|
||||
if (ptr) {
|
||||
*ptr++ = 0;
|
||||
next = strchr(ptr, ' ');
|
||||
if (next)
|
||||
*next = 0;
|
||||
id = atonum(ptr);
|
||||
}
|
||||
|
||||
if (isdigit(arg[0]))
|
||||
svc = svc_find_by_jobid(atonum(arg), id);
|
||||
else
|
||||
svc = svc_find_by_nameid(arg, id);
|
||||
|
||||
svc = client_svc_find(arg);
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
printf("%s\n", svc_status(svc));
|
||||
return 0;
|
||||
printf("Service : %s\n", svc->cmd);
|
||||
printf("Description : %s\n", svc->desc);
|
||||
printf("PID : %d\n", svc->pid);
|
||||
printf("Runlevels : %s\n", runlevel_string(runlevel, svc->runlevels));
|
||||
printf("Status : %s\n", svc_status(svc));
|
||||
printf("\n");
|
||||
|
||||
return do_log(svc->cmd);
|
||||
}
|
||||
|
||||
if (!verbose)
|
||||
printheader(NULL, "# STATUS PID RUNLEVELS SERVICE DESCRIPTION", 0);
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
for (svc = client_svc_iterator(1); svc; svc = client_svc_iterator(0)) {
|
||||
char jobid[10], args[512] = "", *lvls;
|
||||
|
||||
if (svc_is_unique(svc))
|
||||
snprintf(jobid, sizeof(jobid), "%d", svc->job);
|
||||
else
|
||||
// if (svc_is_unique(svc))
|
||||
// snprintf(jobid, sizeof(jobid), "%d", svc->job);
|
||||
// else
|
||||
snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);
|
||||
|
||||
printf("%-5s %7s ", jobid, svc_status(svc));
|
||||
@@ -539,7 +470,7 @@ static int show_status(char *arg)
|
||||
};
|
||||
|
||||
snprintf(rq.data, sizeof(rq.data), "%s", jobid);
|
||||
if (do_send(&rq, sizeof(rq))) {
|
||||
if (client_send(&rq, sizeof(rq))) {
|
||||
snprintf(args, sizeof(args), "Unknown inetd");
|
||||
info = args;
|
||||
} else {
|
||||
@@ -548,7 +479,7 @@ static int show_status(char *arg)
|
||||
info = rq.data;
|
||||
info[len - 1] = 0;
|
||||
|
||||
if (strcmp("internal", svc->cmd)) {
|
||||
if (!string_match("internal", svc->cmd)) {
|
||||
char *ptr;
|
||||
|
||||
ptr = strchr(info, ' ');
|
||||
@@ -579,7 +510,7 @@ static int show_status(char *arg)
|
||||
static int usage(int rc)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Usage: %s [OPTIONS] <COMMAND>\n"
|
||||
"Usage: %s [OPTIONS] [COMMAND]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" -v, --verbose Verbose output\n"
|
||||
@@ -593,7 +524,9 @@ static int usage(int rc)
|
||||
" 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"
|
||||
" touch <CONF> Mark .conf in /etc/finit.d/ for reload\n"
|
||||
" reload Reload *.conf in /etc/finit.d/ (activates changes)\n"
|
||||
// " reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name\n"
|
||||
"\n"
|
||||
" cond set <COND> Set (assert) condition => +COND\n"
|
||||
" cond clear <COND> Clear (deassert) condition => -COND\n"
|
||||
@@ -604,7 +537,7 @@ static int usage(int rc)
|
||||
" 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"
|
||||
" status | show Show status of services, default command\n"
|
||||
"\n"
|
||||
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
||||
" reboot Reboot system\n"
|
||||
@@ -625,6 +558,7 @@ static int do_help(char *arg)
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
char *cmd, arg[120];
|
||||
command_t command[] = {
|
||||
{ "debug", toggle_debug },
|
||||
{ "help", do_help },
|
||||
@@ -633,6 +567,7 @@ int main(int argc, char *argv[])
|
||||
{ "list", serv_list },
|
||||
{ "enable", serv_enable },
|
||||
{ "disable", serv_disable },
|
||||
{ "touch", serv_touch },
|
||||
{ "reload", do_reload },
|
||||
|
||||
{ "cond", do_cond },
|
||||
@@ -673,20 +608,23 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
screen_init();
|
||||
if (optind < argc) {
|
||||
char *cmd = argv[optind++];
|
||||
char arg[120] = "";
|
||||
|
||||
while (optind < argc) {
|
||||
strlcat(arg, argv[optind++], sizeof(arg));
|
||||
if (optind < argc)
|
||||
strlcat(arg, " ", sizeof(arg));
|
||||
}
|
||||
if (optind >= argc)
|
||||
return show_status(NULL);
|
||||
|
||||
for (c = 0; command[c].cmd; c++) {
|
||||
if (!strcmp(command[c].cmd, cmd))
|
||||
return command[c].cb(arg);
|
||||
}
|
||||
memset(arg, 0, sizeof(arg));
|
||||
cmd = argv[optind++];
|
||||
while (optind < argc) {
|
||||
strlcat(arg, argv[optind++], sizeof(arg));
|
||||
if (optind < argc)
|
||||
strlcat(arg, " ", sizeof(arg));
|
||||
}
|
||||
|
||||
for (c = 0; command[c].cmd; c++) {
|
||||
if (!string_match(command[c].cmd, cmd))
|
||||
continue;
|
||||
|
||||
return command[c].cb(arg);
|
||||
}
|
||||
|
||||
return usage(1);
|
||||
|
||||
@@ -63,7 +63,7 @@ void log_open(void)
|
||||
closelog();
|
||||
|
||||
if (debug)
|
||||
opts |= LOG_CONS | LOG_PERROR;
|
||||
opts |= LOG_PERROR; /* LOG_CONS | */
|
||||
openlog("finit", opts, LOG_DAEMON);
|
||||
setlogmask(LOG_UPTO(loglevel));
|
||||
|
||||
@@ -105,45 +105,43 @@ int log_is_debug(void)
|
||||
return debug;
|
||||
}
|
||||
|
||||
static void early_logit(int prio, const char *fmt, va_list ap)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
if (!debug && prio > loglevel)
|
||||
return;
|
||||
|
||||
fp = fopen("/dev/kmsg", "w");
|
||||
if (fp) {
|
||||
fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio);
|
||||
vfprintf(fp, fmt, ap);
|
||||
fclose(fp);
|
||||
|
||||
if (debug)
|
||||
vfprintf(stderr, fmt, ap);
|
||||
} else {
|
||||
vfprintf(stderr, fmt, ap);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Log to /dev/kmsg until syslogd has started, then openlog()
|
||||
* and continue logging as a regular daemon.
|
||||
*/
|
||||
void logit(int prio, const char *fmt, ...)
|
||||
{
|
||||
FILE *fp;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (!up) {
|
||||
if (!fexist("/dev/log")) {
|
||||
early_logit(prio, fmt, ap);
|
||||
goto done;
|
||||
}
|
||||
|
||||
log_open();
|
||||
if (up || fexist("/dev/log")) {
|
||||
if (!up)
|
||||
log_open();
|
||||
vsyslog(prio, fmt, ap);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!debug && prio > loglevel)
|
||||
goto done;
|
||||
|
||||
fp = fopen("/dev/kmsg", "w");
|
||||
if (!fp) {
|
||||
vfprintf(stderr, fmt, ap);
|
||||
goto done;
|
||||
}
|
||||
|
||||
fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio);
|
||||
vfprintf(fp, fmt, ap);
|
||||
fclose(fp);
|
||||
|
||||
if (debug) {
|
||||
va_end(ap);
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
}
|
||||
|
||||
vsyslog(prio, fmt, ap);
|
||||
done:
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
+38
-3
@@ -1,6 +1,6 @@
|
||||
/* Plugin based services architecture for finit
|
||||
*
|
||||
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-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
|
||||
@@ -30,10 +30,12 @@
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
#include "config.h"
|
||||
#include "cond.h"
|
||||
#include "finit.h"
|
||||
#include "private.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "private.h"
|
||||
#include "service.h"
|
||||
|
||||
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0)
|
||||
#define SEARCH_PLUGIN(str) \
|
||||
@@ -163,6 +165,15 @@ plugin_t *plugin_find(char *name)
|
||||
}
|
||||
|
||||
/* Private daemon API *******************************************************/
|
||||
#define CHOOSE(x, y) y
|
||||
static const char *hook_cond[] = HOOK_TYPES;
|
||||
#undef CHOOSE
|
||||
|
||||
const char *plugin_hook_str(hook_point_t no)
|
||||
{
|
||||
return hook_cond[no];
|
||||
}
|
||||
|
||||
int plugin_exists(hook_point_t no)
|
||||
{
|
||||
plugin_t *p, *tmp;
|
||||
@@ -178,14 +189,34 @@ int plugin_exists(hook_point_t no)
|
||||
/* Some hooks are called with a fixed argument, like HOOK_SVC_LOST */
|
||||
void plugin_run_hook(hook_point_t no, void *arg)
|
||||
{
|
||||
static int last = -1;
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
/*
|
||||
* End recursion: any plugin hook => start service => SVC start
|
||||
* hook => start service ... err ... wait a second
|
||||
*/
|
||||
if (HOOK_SVC_START == last) {
|
||||
_d("End plugin recursion in our lifetime!");
|
||||
return;
|
||||
}
|
||||
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (p->hook[no].cb) {
|
||||
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg);
|
||||
p->hook[no].cb(arg ? arg : p->hook[no].arg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Guard against infinite recursion */
|
||||
if (HOOK_SVC_START == no)
|
||||
last = no;
|
||||
|
||||
cond_set(hook_cond[no]);
|
||||
service_step_all(SVC_TYPE_RUNTASK);
|
||||
|
||||
if (HOOK_SVC_START == no)
|
||||
last = -1;
|
||||
}
|
||||
|
||||
/* Regular hooks are called with the registered plugin's argument */
|
||||
@@ -194,7 +225,11 @@ void plugin_run_hooks(hook_point_t no)
|
||||
plugin_run_hook(no, NULL);
|
||||
}
|
||||
|
||||
/* Generic libev I/O callback, looks up correct plugin and calls its callback */
|
||||
/*
|
||||
* Generic libev I/O callback, looks up correct plugin and calls its
|
||||
* callback. libuEv might return UEV_ERROR in events, it is up to the
|
||||
* plugin callback to handle this.
|
||||
*/
|
||||
static void generic_io_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
plugin_t *p = (plugin_t *)arg;
|
||||
|
||||
+35
-21
@@ -1,6 +1,6 @@
|
||||
/* Plugin based services architecture for finit
|
||||
*
|
||||
* Copyright (c) 2012 Joachim Nilsson <troglobit@gmail.com>
|
||||
* Copyright (c) 2012-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
|
||||
@@ -34,8 +34,16 @@
|
||||
#endif
|
||||
|
||||
#define PLUGIN_DEP_MAX 10
|
||||
|
||||
/*
|
||||
* Event flags for I/O plugins
|
||||
*/
|
||||
#define PLUGIN_IO_ERROR UEV_ERROR
|
||||
#define PLUGIN_IO_READ UEV_READ
|
||||
#define PLUGIN_IO_WRITE UEV_WRITE
|
||||
#define PLUGIN_IO_PRI UEV_PRI
|
||||
#define PLUGIN_IO_HUP UEV_HUP
|
||||
#define PLUGIN_IO_RDHUP UEV_RDHUP
|
||||
|
||||
#define PLUGIN_INIT(x) static void __attribute__ ((constructor)) x(void)
|
||||
#define PLUGIN_EXIT(x) static void __attribute__ ((destructor)) x(void)
|
||||
@@ -43,28 +51,34 @@
|
||||
#define PLUGIN_ITERATOR(x, tmp) TAILQ_FOREACH_SAFE(x, &plugins, link, tmp)
|
||||
|
||||
/*
|
||||
* Predefined hook points for easier plugin debugging
|
||||
* Predefined hook points and corresponding conditions in Finit,
|
||||
* for use by plugins and scripts. Recommended to use the task
|
||||
* or run stanzas: task <hook/mount/error> /bin/rescue.sh
|
||||
*/
|
||||
typedef enum {
|
||||
/* Bootstrap hooks */
|
||||
HOOK_BANNER = 0,
|
||||
HOOK_ROOTFS_UP,
|
||||
HOOK_MOUNT_ERROR,
|
||||
HOOK_BASEFS_UP,
|
||||
HOOK_NETWORK_UP,
|
||||
HOOK_SVC_UP,
|
||||
HOOK_SYSTEM_UP,
|
||||
#define HOOK_TYPES { \
|
||||
/* Bootstrap hooks, runlevel [S] */ \
|
||||
CHOOSE(HOOK_BANNER = 0, "hook/sys/banner"), \
|
||||
CHOOSE(HOOK_ROOTFS_UP, "hook/mount/root"), \
|
||||
CHOOSE(HOOK_MOUNT_ERROR, "hook/mount/error"), \
|
||||
CHOOSE(HOOK_BASEFS_UP, "hook/mount/all"), \
|
||||
CHOOSE(HOOK_NETWORK_UP, "hook/net/up"), \
|
||||
CHOOSE(HOOK_SVC_UP, "hook/svc/up"), \
|
||||
CHOOSE(HOOK_SYSTEM_UP, "hook/sys/up"), \
|
||||
\
|
||||
/* Runtime hooks, runlevel [S1-9] */ \
|
||||
CHOOSE(HOOK_SVC_RECONF, "hook/svc/reconf"), \
|
||||
CHOOSE(HOOK_SVC_LOST, "hook/svc/lost"), \
|
||||
CHOOSE(HOOK_SVC_START, "hook/svc/start"), \
|
||||
CHOOSE(HOOK_RUNLEVEL_CHANGE, "hook/sys/runlevel"), \
|
||||
\
|
||||
/* Shutdown hooks, runlevel [06] */ \
|
||||
CHOOSE(HOOK_SHUTDOWN, "hook/sys/shutdown"), \
|
||||
CHOOSE(HOOK_MAX_NUM, "nop") \
|
||||
}
|
||||
|
||||
/* Runtime hooks */
|
||||
HOOK_SVC_RECONF,
|
||||
HOOK_SVC_LOST,
|
||||
HOOK_SVC_START,
|
||||
HOOK_RUNLEVEL_CHANGE,
|
||||
|
||||
/* Shutdown hooks */
|
||||
HOOK_SHUTDOWN,
|
||||
HOOK_MAX_NUM
|
||||
} hook_point_t;
|
||||
#define CHOOSE(x, y) x
|
||||
typedef enum HOOK_TYPES hook_point_t;
|
||||
#undef CHOOSE
|
||||
|
||||
/**
|
||||
* plugin_t - Finit &plugin_t object
|
||||
|
||||
@@ -40,6 +40,7 @@ int client (int argc, char *argv[]);
|
||||
|
||||
void service_monitor (pid_t lost);
|
||||
|
||||
const char *plugin_hook_str(hook_point_t no);
|
||||
int plugin_exists (hook_point_t no);
|
||||
void plugin_run_hook (hook_point_t no, void *arg);
|
||||
void plugin_run_hooks (hook_point_t no);
|
||||
|
||||
+25
@@ -24,6 +24,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <fcntl.h> /* Definition of AT_* constants */
|
||||
#include <glob.h>
|
||||
#include <stdio.h>
|
||||
#include <lite/lite.h>
|
||||
@@ -168,6 +169,30 @@ int serv_disable(char *arg)
|
||||
return remove(link) != 0;
|
||||
}
|
||||
|
||||
int serv_touch(char *arg)
|
||||
{
|
||||
char corr[40];
|
||||
|
||||
if (!strstr(arg, ".conf")) {
|
||||
snprintf(corr, sizeof(corr), "%s.conf", arg);
|
||||
arg = corr;
|
||||
}
|
||||
|
||||
pushd(FINIT_RCSD);
|
||||
if (!fexist(arg)) {
|
||||
popd();
|
||||
if (!strstr(arg, "finit.conf"))
|
||||
errx(1, "Service %s is not enabled", arg);
|
||||
arg = FINIT_CONF;
|
||||
}
|
||||
|
||||
/* libite:touch() follows symlinks */
|
||||
if (utimensat(AT_FDCWD, arg, NULL, AT_SYMLINK_NOFOLLOW))
|
||||
err(1, "Failed marking %s for reload", arg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
int serv_list (char *arg);
|
||||
int serv_enable (char *arg);
|
||||
int serv_disable (char *arg);
|
||||
int serv_touch (char *arg);
|
||||
|
||||
#endif /* FINIT_SERV_H_ */
|
||||
|
||||
|
||||
+173
-72
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <ctype.h> /* isblank() */
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/wait.h>
|
||||
#include <net/if.h>
|
||||
#include <lite/lite.h>
|
||||
@@ -46,55 +47,33 @@
|
||||
|
||||
static void svc_set_state(svc_t *svc, svc_state_t new);
|
||||
|
||||
/**
|
||||
* service_enabled - Should the service run?
|
||||
* @svc: Pointer to &svc_t object
|
||||
*
|
||||
* Returns:
|
||||
* 1, if the service is allowed to run in the current runlevel and the
|
||||
* user has not manually requested that this service should not run. 0
|
||||
* otherwise.
|
||||
*/
|
||||
int service_enabled(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return 0;
|
||||
|
||||
if (!svc_in_runlevel(svc, runlevel))
|
||||
return 0;
|
||||
|
||||
if (svc_is_removed(svc) || svc_is_blocked(svc))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* service_timeout_cb - libuev callback wrapper for service timeouts
|
||||
* @w: Watcher
|
||||
* @arg: Callback argument, from init
|
||||
* @events: Error, or ready to read/write (N/A for relative timers)
|
||||
*
|
||||
* Calls the callback registered with the call to
|
||||
* service_timeout_after().
|
||||
* Run callback registered when calling service_timeout_after().
|
||||
*/
|
||||
static void service_timeout_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
svc_t *svc = arg;
|
||||
|
||||
/* Ignore any UEV_ERROR, we're a one-shot cb so just run it. */
|
||||
svc->timer_cb(svc);
|
||||
}
|
||||
|
||||
/**
|
||||
* service_timeout_after - Call a function after some time has elapsed
|
||||
* @param svc Service to use as argument to the callback
|
||||
* @param timeout Timeout, in milliseconds
|
||||
* @param cb Callback function
|
||||
* @svc: Service to use as argument to the callback
|
||||
* @timeout: Timeout, in milliseconds
|
||||
* @cb: Callback function
|
||||
*
|
||||
* After @param timeout milliseconds has elapsed, calls @param cb with
|
||||
* @param svc as the argument.
|
||||
* After @timeout milliseconds has elapsed, call @cb() with @svc as the
|
||||
* argument.
|
||||
*
|
||||
* @return 0 on success, non-zero on error.
|
||||
* Returns:
|
||||
* POSIX OK(0) on success, non-zero on error.
|
||||
*/
|
||||
static int service_timeout_after(svc_t *svc, int timeout, void (*cb)(svc_t *svc))
|
||||
{
|
||||
@@ -107,11 +86,12 @@ static int service_timeout_after(svc_t *svc, int timeout, void (*cb)(svc_t *svc)
|
||||
|
||||
/**
|
||||
* service_timeout_cancel - Cancel timeout associated with service
|
||||
* @param svc Service whose timeout to cancel
|
||||
* @svc: Service whose timeout to cancel
|
||||
*
|
||||
* If a timeout is associated with @param svc, cancel it.
|
||||
* If a timeout is associated with @svc, cancel it.
|
||||
*
|
||||
* @return 0 on success, non-zero on error.
|
||||
* Returns:
|
||||
* POSIX OK(0) on success, non-zero on error.
|
||||
*/
|
||||
static int service_timeout_cancel(svc_t *svc)
|
||||
{
|
||||
@@ -160,16 +140,16 @@ static int service_start(svc_t *svc)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (svc_is_daemon(svc) || svc_is_inetd(svc))
|
||||
#ifdef INETD_ENABLED
|
||||
if (svc_is_inetd(svc))
|
||||
return inetd_start(&svc->inetd);
|
||||
#endif
|
||||
|
||||
if (svc_is_daemon(svc))
|
||||
print_desc("Starting ", svc->desc);
|
||||
else
|
||||
print_desc("", svc->desc);
|
||||
|
||||
#ifdef INETD_ENABLED
|
||||
if (svc_is_inetd(svc))
|
||||
return print_result(inetd_start(&svc->inetd));
|
||||
#endif
|
||||
|
||||
/* Declare we're waiting for svc to create its pidfile */
|
||||
svc_starting(svc);
|
||||
|
||||
@@ -191,6 +171,14 @@ static int service_start(svc_t *svc)
|
||||
#endif
|
||||
char *args[MAX_NUM_SVC_ARGS];
|
||||
|
||||
/* Set configured limits */
|
||||
for (int i = 0; i < RLIMIT_NLIMITS; i++) {
|
||||
if (setrlimit(i, &svc->rlimit[i]) == -1)
|
||||
logit(LOG_WARNING,
|
||||
"%s: rlimit: Failed setting %s",
|
||||
svc->cmd, rlim2str(i));
|
||||
}
|
||||
|
||||
/* Set desired user+group */
|
||||
if (gid >= 0)
|
||||
setgid(gid);
|
||||
@@ -286,7 +274,7 @@ static int service_start(svc_t *svc)
|
||||
|
||||
if (svc->inetd.cmd)
|
||||
status = svc->inetd.cmd(svc->inetd.type);
|
||||
else if (svc->type & SVC_TYPE_RUNTASK)
|
||||
else if (svc_is_runtask(svc))
|
||||
status = exec_runtask(svc->cmd, args);
|
||||
else
|
||||
status = execv(svc->cmd, args);
|
||||
@@ -323,8 +311,6 @@ static int service_start(svc_t *svc)
|
||||
close(svc->stdin_fd);
|
||||
#endif
|
||||
|
||||
plugin_run_hook(HOOK_SVC_START, (void *)(uintptr_t)pid);
|
||||
|
||||
if (SVC_TYPE_RUN == svc->type) {
|
||||
result = WEXITSTATUS(complete(svc->cmd, pid));
|
||||
if (!svc_clean_bootstrap(svc)) {
|
||||
@@ -334,8 +320,17 @@ static int service_start(svc_t *svc)
|
||||
}
|
||||
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
print_result(result);
|
||||
|
||||
return print_result(result);
|
||||
/*
|
||||
* Only run hook on successful start, and *after* having printed
|
||||
* the result, otherwise any hook tasks may overwrite it and the
|
||||
* result would be like double "[ OK ]" but only one service.
|
||||
*/
|
||||
if (!result)
|
||||
plugin_run_hook(HOOK_SVC_START, (void *)(uintptr_t)pid);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -348,10 +343,16 @@ static void service_kill(svc_t *svc)
|
||||
{
|
||||
service_timeout_cancel(svc);
|
||||
|
||||
if (svc->pid <= 1) {
|
||||
/* Avoid killing ourselves or all processes ... */
|
||||
_d("%s: Aborting SIGKILL, already terminated.", svc->cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (runlevel != 1)
|
||||
print_desc("Killing ", svc->desc);
|
||||
|
||||
_d("Sending SIGKILL to pid:%d name:%s", svc->pid, pid_get_name(svc->pid, NULL, 0));
|
||||
_d("%s: Sending SIGKILL to pid:%d", pid_get_name(svc->pid, NULL, 0), svc->pid);
|
||||
kill(svc->pid, SIGKILL);
|
||||
|
||||
/* Let SIGKILLs stand out, show result as [WARN] */
|
||||
@@ -470,9 +471,10 @@ void service_runlevel(int newlevel)
|
||||
|
||||
/**
|
||||
* service_register - Register service, task or run commands
|
||||
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
|
||||
* @svcline: A complete command line with -- separated description text
|
||||
* @mtime: The modification time if service is loaded from /etc/finit.d
|
||||
* @type: %SVC_TYPE_SERVICE(0), %SVC_TYPE_TASK(1), %SVC_TYPE_RUN(2)
|
||||
* @cfg: Configuration, complete command, with -- for description text
|
||||
* @rlimit: Limits for this service/task/run/inetd, may be global limits
|
||||
* @file: The file name service was loaded from
|
||||
*
|
||||
* This function is used to register commands to be run on different
|
||||
* system runlevels with optional username. The @type argument details
|
||||
@@ -519,10 +521,10 @@ void service_runlevel(int newlevel)
|
||||
* Returns:
|
||||
* POSIX OK(0) on success, or non-zero errno exit status on failure.
|
||||
*/
|
||||
int service_register(int type, char *svcline, struct timeval *mtime)
|
||||
int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
|
||||
{
|
||||
int i = 0;
|
||||
int id = 1; /* Default to ID:1 */
|
||||
int id = -1;
|
||||
#ifdef INETD_ENABLED
|
||||
int forking = 0;
|
||||
#endif
|
||||
@@ -533,12 +535,12 @@ int service_register(int type, char *svcline, struct timeval *mtime)
|
||||
svc_t *svc;
|
||||
plugin_t *plugin = NULL;
|
||||
|
||||
if (!svcline) {
|
||||
if (!cfg) {
|
||||
_e("Invalid input argument");
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
line = strdup(svcline);
|
||||
line = strdup(cfg);
|
||||
if (!line)
|
||||
return 1;
|
||||
|
||||
@@ -554,7 +556,7 @@ int service_register(int type, char *svcline, struct timeval *mtime)
|
||||
cmd = strtok(line, " ");
|
||||
if (!cmd) {
|
||||
incomplete:
|
||||
_e("Incomplete service '%s', cannot register", svcline);
|
||||
_e("Incomplete service '%s', cannot register", cfg);
|
||||
free(line);
|
||||
return errno = ENOENT;
|
||||
}
|
||||
@@ -632,11 +634,15 @@ int service_register(int type, char *svcline, struct timeval *mtime)
|
||||
if (svc)
|
||||
goto inetd_setup;
|
||||
|
||||
id = svc_next_id(cmd);
|
||||
if (id <= 0)
|
||||
id = svc_next_id(cmd);
|
||||
}
|
||||
recreate:
|
||||
#endif
|
||||
|
||||
if (id <= 0)
|
||||
id = 1;
|
||||
|
||||
svc = svc_find(cmd, id);
|
||||
if (!svc) {
|
||||
_d("Creating new svc for %s id #%d type %d", cmd, id, type);
|
||||
@@ -717,11 +723,17 @@ recreate:
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* Set configured limits */
|
||||
memcpy(svc->rlimit, rlimit, sizeof(svc->rlimit));
|
||||
|
||||
/* New, recently modified or unchanged ... used on reload. */
|
||||
svc_check_dirty(svc, mtime);
|
||||
free(line);
|
||||
if (file && conf_changed(file))
|
||||
svc_mark_dirty(svc);
|
||||
else
|
||||
svc_mark_clean(svc);
|
||||
|
||||
/* Free duped line, from above */
|
||||
free(line);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -772,33 +784,33 @@ void service_monitor(pid_t lost)
|
||||
static void service_retry(svc_t *svc)
|
||||
{
|
||||
int timeout;
|
||||
int *restart_counter = (int *)&svc->restart_counter;
|
||||
char *restart_cnt = (char *)&svc->restart_cnt;
|
||||
|
||||
service_timeout_cancel(svc);
|
||||
|
||||
if (svc->state != SVC_HALTED_STATE ||
|
||||
svc->block != SVC_BLOCK_RESTARTING) {
|
||||
_d("%s not crashing anymore", svc->desc);
|
||||
*restart_counter = 0;
|
||||
*restart_cnt = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (*restart_counter >= RESPAWN_MAX) {
|
||||
_e("%s keeps crashing, not restarting", svc->desc);
|
||||
if (*restart_cnt >= RESPAWN_MAX) {
|
||||
logit(LOG_ERR, "%s keeps crashing, not restarting", svc->desc);
|
||||
svc_crashing(svc);
|
||||
*restart_counter = 0;
|
||||
*restart_cnt = 0;
|
||||
service_step(svc);
|
||||
return;
|
||||
}
|
||||
|
||||
(*restart_counter)++;
|
||||
(*restart_cnt)++;
|
||||
|
||||
_d("%s crashed, trying to start it again, attempt %d", svc->desc, *restart_counter);
|
||||
_d("%s crashed, trying to start it again, attempt %d", svc->desc, *restart_cnt);
|
||||
svc_unblock(svc);
|
||||
service_step(svc);
|
||||
|
||||
/* Wait 2s for the first 5 respawns, then back off to 5s */
|
||||
timeout = ((*restart_counter) <= (RESPAWN_MAX / 2)) ? 2000 : 5000;
|
||||
timeout = ((*restart_cnt) <= (RESPAWN_MAX / 2)) ? 2000 : 5000;
|
||||
service_timeout_after(svc, timeout, service_retry);
|
||||
}
|
||||
|
||||
@@ -818,14 +830,14 @@ static void svc_set_state(svc_t *svc, svc_state_t new)
|
||||
void service_step(svc_t *svc)
|
||||
{
|
||||
int err;
|
||||
int *restart_counter = (int *)&svc->restart_counter;
|
||||
char *restart_cnt = (char *)&svc->restart_cnt;
|
||||
svc_cmd_t enabled;
|
||||
svc_state_t old_state;
|
||||
cond_state_t cond;
|
||||
|
||||
restart:
|
||||
old_state = svc->state;
|
||||
enabled = service_enabled(svc);
|
||||
enabled = svc_enabled(svc);
|
||||
|
||||
_d("%20s(%4d): %8s %3sabled/%-7s cond:%-4s", svc->cmd, svc->pid,
|
||||
svc_status(svc), enabled ? "en" : "dis", svc_dirtystr(svc),
|
||||
@@ -880,20 +892,25 @@ restart:
|
||||
if (!enabled) {
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
} else if (cond_get_agg(svc->cond) == COND_ON) {
|
||||
/* wait until all processes has been stopped before continuing... */
|
||||
/* wait until all processes have been stopped before continuing... */
|
||||
if (sm_is_in_teardown(&sm))
|
||||
break;
|
||||
|
||||
/*
|
||||
* Make state transition *before* service_start(), because
|
||||
* of HOOK_SVC_START, which may call service_step()
|
||||
*/
|
||||
svc_mark_clean(svc);
|
||||
svc_set_state(svc, SVC_RUNNING_STATE);
|
||||
|
||||
err = service_start(svc);
|
||||
if (err) {
|
||||
(*restart_counter)++;
|
||||
(*restart_cnt)++;
|
||||
svc_set_state(svc, SVC_READY_STATE);
|
||||
|
||||
if (!svc_is_inetd_conn(svc))
|
||||
break;
|
||||
}
|
||||
|
||||
svc_mark_clean(svc);
|
||||
svc_set_state(svc, SVC_RUNNING_STATE);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -923,6 +940,11 @@ restart:
|
||||
svc_set_state(svc, SVC_STOPPING_STATE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (svc_is_runtask(svc)) {
|
||||
svc->once++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cond = cond_get_agg(svc->cond);
|
||||
@@ -963,7 +985,7 @@ restart:
|
||||
}
|
||||
|
||||
if (!svc->pid) {
|
||||
(*restart_counter)++;
|
||||
(*restart_cnt)++;
|
||||
svc_set_state(svc, SVC_READY_STATE);
|
||||
break;
|
||||
}
|
||||
@@ -998,6 +1020,85 @@ void service_step_all(int types)
|
||||
svc_foreach_type(types, service_step);
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_clean_runtask - Clear once flag of runtasks
|
||||
*
|
||||
* XXX: runtasks should be stopped before calling this
|
||||
*/
|
||||
void service_runtask_clean(void)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (!svc_is_runtask(svc))
|
||||
continue;
|
||||
|
||||
svc->once = 0;
|
||||
if (svc->state == SVC_DONE_STATE)
|
||||
svc_set_state(svc, SVC_HALTED_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* service_runtask_completed - Have run/task completed in current runlevel
|
||||
* @skip: Hardcoded list of conditions to skip
|
||||
*
|
||||
* This function checks if all run/task have run once in the current
|
||||
* runlevel. E.g., at bootstrap we must wait for these scripts or
|
||||
* programs to complete their run before switching to the configured
|
||||
* runlevel.
|
||||
*
|
||||
* Setting @skip means skipping tasks with %HOOK_SVC_UP, %HOOK_SYSTEM_UP
|
||||
* set in their condition mask. These tasks cannot run until finalize()
|
||||
*
|
||||
* Returns:
|
||||
* %TRUE(1) or %FALSE(0)
|
||||
*/
|
||||
int service_runtask_completed(int skip)
|
||||
{
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (!svc_is_runtask(svc))
|
||||
continue;
|
||||
|
||||
if (!svc_enabled(svc))
|
||||
continue;
|
||||
|
||||
if (strstr(svc->cond, plugin_hook_str(HOOK_SVC_UP)) ||
|
||||
strstr(svc->cond, plugin_hook_str(HOOK_SYSTEM_UP))) {
|
||||
_d("Skipping %s(%s), post-strap hook", svc->desc, svc->cmd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!svc->once) {
|
||||
_d("%s has not yet completed ...", svc->cmd);
|
||||
return 0;
|
||||
}
|
||||
_d("%s has completed ...", svc->cmd);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void service_bootstrap_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
static int cnt = 10;
|
||||
void (*finalize)(void) = arg;
|
||||
|
||||
_d("Checking completion of all run/task ... %d", cnt);
|
||||
if (cnt-- > 0 && !service_runtask_completed(!runlevel))
|
||||
return;
|
||||
|
||||
if (cnt > 0)
|
||||
_d("All run/task have completed, resuming bootstrap.");
|
||||
else
|
||||
_d("Timeout, resuming bootstrap.");
|
||||
cnt = 10;
|
||||
uev_timer_stop(w);
|
||||
finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
|
||||
+4
-2
@@ -28,15 +28,17 @@
|
||||
#include "svc.h"
|
||||
|
||||
void service_runlevel (int newlevel);
|
||||
int service_register (int type, char *line, struct timeval *mtime);
|
||||
int service_register (int type, char *line, struct rlimit rlimit[], char *file);
|
||||
void service_unregister (svc_t *svc);
|
||||
int service_enabled (svc_t *svc);
|
||||
|
||||
void service_runtask_clean (void);
|
||||
void service_reload_dynamic (void);
|
||||
|
||||
void service_step (svc_t *svc);
|
||||
void service_step_all (int types);
|
||||
|
||||
void service_bootstrap_cb (uev_t *w, void *arg, int events);
|
||||
|
||||
#endif /* FINIT_SERVICE_H_ */
|
||||
|
||||
/**
|
||||
|
||||
@@ -224,6 +224,11 @@ void do_shutdown(shutop_t op)
|
||||
static void sighup_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
_d("...");
|
||||
if (UEV_ERROR == events) {
|
||||
_e("Unrecoverable error in signal watcher");
|
||||
return;
|
||||
}
|
||||
|
||||
/* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */
|
||||
service_reload_dynamic();
|
||||
}
|
||||
@@ -234,6 +239,11 @@ static void sighup_cb(uev_t *w, void *arg, int events)
|
||||
static void sigint_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
_d("...");
|
||||
if (UEV_ERROR == events) {
|
||||
_e("Unrecoverable error in signal watcher");
|
||||
return;
|
||||
}
|
||||
|
||||
halt = SHUT_REBOOT;
|
||||
service_runlevel(6);
|
||||
}
|
||||
@@ -244,6 +254,11 @@ static void sigint_cb(uev_t *w, void *arg, int events)
|
||||
static void sigusr1_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
_d("...");
|
||||
if (UEV_ERROR == events) {
|
||||
_e("Unrecoverable error in signal watcher");
|
||||
return;
|
||||
}
|
||||
|
||||
halt = SHUT_HALT;
|
||||
service_runlevel(0);
|
||||
}
|
||||
@@ -254,6 +269,11 @@ static void sigusr1_cb(uev_t *w, void *arg, int events)
|
||||
static void sigusr2_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
_d("...");
|
||||
if (UEV_ERROR == events) {
|
||||
_e("Unrecoverable error in signal watcher");
|
||||
return;
|
||||
}
|
||||
|
||||
halt = SHUT_OFF;
|
||||
service_runlevel(0);
|
||||
}
|
||||
@@ -264,6 +284,11 @@ static void sigusr2_cb(uev_t *w, void *arg, int events)
|
||||
static void sigterm_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
_d("...");
|
||||
if (UEV_ERROR == events) {
|
||||
_e("Unrecoverable error in signal watcher");
|
||||
return;
|
||||
}
|
||||
|
||||
halt = SHUT_REBOOT;
|
||||
service_runlevel(6);
|
||||
}
|
||||
@@ -275,6 +300,11 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
if (UEV_ERROR == events) {
|
||||
_e("Unrecoverable error in signal watcher");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reap all the children! */
|
||||
do {
|
||||
pid = waitpid(-1, NULL, WNOHANG);
|
||||
@@ -290,6 +320,11 @@ static void sigchld_cb(uev_t *w, void *arg, int events)
|
||||
*/
|
||||
static void sigstop_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
if (UEV_ERROR == events) {
|
||||
_e("Unrecoverable error in signal watcher");
|
||||
return;
|
||||
}
|
||||
|
||||
touch(SYNC_STOPPED);
|
||||
stopped++;
|
||||
}
|
||||
@@ -299,6 +334,11 @@ static void sigstop_cb(uev_t *w, void *arg, int events)
|
||||
*/
|
||||
static void sigcont_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
if (UEV_ERROR == events) {
|
||||
_e("Unrecoverable error in signal watcher");
|
||||
return;
|
||||
}
|
||||
|
||||
stopped = 0;
|
||||
erase(SYNC_STOPPED);
|
||||
}
|
||||
|
||||
@@ -95,7 +95,8 @@ void sm_step(sm_t *sm)
|
||||
restart:
|
||||
old_state = sm->state;
|
||||
|
||||
_d("state: %s", sm_status(sm->state));
|
||||
_d("state: %s, runlevel: %d, newlevel: %d, teardown: %d, reload: %d",
|
||||
sm_status(sm->state), runlevel, sm->newlevel, sm->in_teardown, sm->reload);
|
||||
|
||||
switch (sm->state) {
|
||||
case SM_BOOTSTRAP_STATE:
|
||||
@@ -143,7 +144,10 @@ restart:
|
||||
|
||||
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
||||
if (runlevel != 0 && runlevel != 6)
|
||||
conf_reload_dynamic();
|
||||
conf_reload();
|
||||
|
||||
/* Reset once flag of runtasks */
|
||||
service_runtask_clean();
|
||||
|
||||
_d("Stopping services services not allowed in new runlevel ...");
|
||||
sm->in_teardown = 1;
|
||||
@@ -195,7 +199,7 @@ restart:
|
||||
|
||||
case SM_RELOAD_CHANGE_STATE:
|
||||
/* First reload all *.conf in /etc/finit.d/ */
|
||||
conf_reload_dynamic();
|
||||
conf_reload();
|
||||
|
||||
/*
|
||||
* Then, mark all affected service conditions as in-flux and
|
||||
@@ -231,9 +235,6 @@ restart:
|
||||
_d("Calling reconf hooks ...");
|
||||
plugin_run_hooks(HOOK_SVC_RECONF);
|
||||
|
||||
/* Done, lift reconf condition */
|
||||
cond_reassert(NULL);
|
||||
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
|
||||
_d("Reconfiguration done");
|
||||
|
||||
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
/* Reset terminal line settings (stty sane)
|
||||
*
|
||||
* Copyright (c) 2016-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 <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h> /* _POSIX_VDISABLE */
|
||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||
#include <termios.h>
|
||||
|
||||
speed_t stty_parse_speed(char *baud)
|
||||
{
|
||||
char *ptr;
|
||||
size_t i;
|
||||
unsigned long val;
|
||||
struct { unsigned long val; speed_t speed; } v2s[] = {
|
||||
{ 0, B0 },
|
||||
{ 50, B50 },
|
||||
{ 75, B75 },
|
||||
{ 110, B110 },
|
||||
{ 134, B134 },
|
||||
{ 150, B150 },
|
||||
{ 200, B200 },
|
||||
{ 300, B300 },
|
||||
{ 600, B600 },
|
||||
{ 1200, B1200 },
|
||||
{ 1800, B1800 },
|
||||
{ 2400, B2400 },
|
||||
{ 4800, B4800 },
|
||||
{ 9600, B9600 },
|
||||
{ 19200, B19200 },
|
||||
{ 38400, B38400 },
|
||||
{ 57600, B57600 },
|
||||
{ 115200, B115200 },
|
||||
{ 230400, B230400 },
|
||||
{ 460800, B460800 },
|
||||
{ 500000, B500000 },
|
||||
{ 576000, B576000 },
|
||||
{ 921600, B921600 },
|
||||
{ 1000000, B1000000 },
|
||||
{ 1152000, B1152000 },
|
||||
{ 1500000, B1500000 },
|
||||
{ 2000000, B2000000 },
|
||||
{ 2500000, B2500000 },
|
||||
{ 3000000, B3000000 },
|
||||
{ 3500000, B3500000 },
|
||||
{ 4000000, B4000000 },
|
||||
};
|
||||
|
||||
errno = 0;
|
||||
val = strtoul(baud, &ptr, 10);
|
||||
if (errno || ptr == baud)
|
||||
return B0;
|
||||
|
||||
for (i = 0; i < sizeof(v2s) / sizeof(v2s[0]); i++) {
|
||||
if (v2s[i].val == val)
|
||||
return v2s[i].speed;
|
||||
}
|
||||
|
||||
return B0;
|
||||
}
|
||||
|
||||
void stty(int fd, speed_t speed)
|
||||
{
|
||||
struct termios term;
|
||||
|
||||
tcdrain(fd);
|
||||
if (tcgetattr(fd, &term))
|
||||
return;
|
||||
|
||||
if (speed != B0) {
|
||||
cfsetispeed(&term, speed);
|
||||
cfsetospeed(&term, speed);
|
||||
tcsetattr(fd, TCSAFLUSH, &term);
|
||||
}
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
|
||||
/* Disable modem specific flags */
|
||||
term.c_cflag &= ~(0|CSTOPB|PARENB|PARODD);
|
||||
term.c_cflag &= ~CRTSCTS;
|
||||
term.c_cflag |= CLOCAL;
|
||||
|
||||
/* Timeouts, minimum chars and default flags */
|
||||
term.c_cc[VTIME] = 0;
|
||||
term.c_cc[VMIN] = 1;
|
||||
term.c_iflag = ICRNL|IXON|IXOFF;
|
||||
term.c_oflag = OPOST|ONLCR;
|
||||
term.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
|
||||
term.c_lflag |= ICANON|ISIG|ECHO|ECHOE|ECHOK|ECHOKE;
|
||||
|
||||
/* Reset special characters to defaults */
|
||||
term.c_cc[VINTR] = CTRL('C');
|
||||
term.c_cc[VQUIT] = CTRL('\\');
|
||||
term.c_cc[VEOF] = CTRL('D');
|
||||
term.c_cc[VEOL] = '\n';
|
||||
term.c_cc[VKILL] = CTRL('U');
|
||||
term.c_cc[VERASE] = CERASE;
|
||||
tcsetattr(fd, TCSANOW, &term);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <sys/time.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
#include "finit.h"
|
||||
#include "svc.h"
|
||||
@@ -35,38 +36,7 @@
|
||||
|
||||
/* Each svc_t needs a unique job# */
|
||||
static int jobcounter = 1;
|
||||
static svc_t *workaround = NULL;
|
||||
|
||||
static svc_t *__connect_shm(void)
|
||||
{
|
||||
svc_t *list;
|
||||
|
||||
list = finit_svc_connect();
|
||||
if (!list) {
|
||||
if (workaround)
|
||||
return workaround;
|
||||
|
||||
/* Linux not built with CONFIG_SYSVIPC, or libc does not support shmat()/shmget() */
|
||||
if (ENOSYS == errno)
|
||||
warn("Kernel does support SYSV shmat() IPC, error %d", errno);
|
||||
|
||||
/* Try to prevent PID 1 from aborting, issue #81 */
|
||||
if (getpid() == 1) {
|
||||
warnx("Implementing PID 1 workaround, initctl tool will not work ...");
|
||||
if (!workaround)
|
||||
workaround = calloc(MAX_NUM_SVC, sizeof(svc_t));
|
||||
|
||||
list = workaround;
|
||||
}
|
||||
}
|
||||
|
||||
if (!list) {
|
||||
warn("Failed connecting to shared memory, error %d", errno);
|
||||
abort();
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
static TAILQ_HEAD(head, svc) svc_list = TAILQ_HEAD_INITIALIZER(svc_list);
|
||||
|
||||
/**
|
||||
* svc_new - Create a new service
|
||||
@@ -79,11 +49,12 @@ static svc_t *__connect_shm(void)
|
||||
*/
|
||||
svc_t *svc_new(char *cmd, int id, int type)
|
||||
{
|
||||
int i, job = -1;
|
||||
svc_t *svc, *list = __connect_shm();
|
||||
int job = -1;
|
||||
char *desc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
/* Find first job n:o if registering multiple instances */
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (!strcmp(svc->cmd, cmd)) {
|
||||
job = svc->job;
|
||||
break;
|
||||
@@ -92,32 +63,26 @@ svc_t *svc_new(char *cmd, int id, int type)
|
||||
if (job == -1)
|
||||
job = jobcounter++;
|
||||
|
||||
for (i = 0; i < MAX_NUM_SVC; i++) {
|
||||
svc_t *svc = &list[i];
|
||||
svc = calloc(1, sizeof(*svc));
|
||||
if (!svc)
|
||||
return NULL;
|
||||
|
||||
if (svc->type == SVC_TYPE_FREE) {
|
||||
char *desc;
|
||||
svc->type = type;
|
||||
svc->job = job;
|
||||
svc->id = id;
|
||||
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||
|
||||
memset(svc, 0, sizeof(*svc));
|
||||
svc->type = type;
|
||||
svc->job = job;
|
||||
svc->id = id;
|
||||
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||
/* Default description, if missing */
|
||||
desc = rindex(cmd, '/');
|
||||
if (desc)
|
||||
desc++;
|
||||
else
|
||||
desc = cmd;
|
||||
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
||||
|
||||
/* Default description, if missing */
|
||||
desc = rindex(cmd, '/');
|
||||
if (desc)
|
||||
desc++;
|
||||
else
|
||||
desc = cmd;
|
||||
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
||||
TAILQ_INSERT_TAIL(&svc_list, svc, link);
|
||||
|
||||
return svc;
|
||||
}
|
||||
}
|
||||
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
return svc;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,50 +94,55 @@ svc_t *svc_new(char *cmd, int id, int type)
|
||||
*/
|
||||
int svc_del(svc_t *svc)
|
||||
{
|
||||
svc->type = SVC_TYPE_FREE;
|
||||
TAILQ_REMOVE(&svc_list, svc, link);
|
||||
memset(svc, 0, sizeof(*svc));
|
||||
free(svc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_iterator - Naive iterator over all registered services.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
* @iter: Iterator, must be a valid pointer
|
||||
* @first: If set, get first &svc_t, otherwise get next
|
||||
*
|
||||
* Returns:
|
||||
* The first &svc_t when @first is set, otherwise the next &svc_t until
|
||||
* the end when %NULL is returned.
|
||||
* An &svc_t pointer, or %NULL when no more entries can be found.
|
||||
*/
|
||||
svc_t *svc_iterator(int first)
|
||||
{
|
||||
static int i = 0;
|
||||
svc_t *list = __connect_shm();
|
||||
|
||||
if (first)
|
||||
i = 0;
|
||||
|
||||
while (i < MAX_NUM_SVC) {
|
||||
svc_t *svc = &list[i++];
|
||||
|
||||
if (svc->type != SVC_TYPE_FREE)
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_inetd_iterator - Naive iterator over all registered inetd services.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
*
|
||||
* Returns:
|
||||
* The first inetd &svc_t when @first is set, otherwise the next
|
||||
* inetd &svc_t until the end when %NULL is returned.
|
||||
*/
|
||||
svc_t *svc_inetd_iterator(int first)
|
||||
svc_t *svc_iterator(svc_t **iter, int first)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
||||
if (!iter) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (first)
|
||||
svc = TAILQ_FIRST(&svc_list);
|
||||
else
|
||||
svc = *iter;
|
||||
|
||||
if (svc)
|
||||
*iter = TAILQ_NEXT(svc, link);
|
||||
|
||||
return svc;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_inetd_iterator - Naive iterator over all registered inetd services.
|
||||
* @iter: Iterator, must be a valid pointer
|
||||
* @first: If set, get first &svc_t, otherwise get next
|
||||
*
|
||||
* Returns:
|
||||
* The first inetd &svc_t when %NULL is given as argument, otherwise the
|
||||
* next inetd &svc_t until the end when %NULL is returned.
|
||||
*/
|
||||
svc_t *svc_inetd_iterator(svc_t **iter, int first)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
|
||||
if (svc_is_inetd(svc))
|
||||
return svc;
|
||||
}
|
||||
@@ -181,42 +151,22 @@ svc_t *svc_inetd_iterator(int first)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_dynamic_iterator - Naive iterator over all registered dynamic services.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
*
|
||||
* Returns:
|
||||
* The first dynamically loaded &svc_t when @first is set, otherwise the
|
||||
* next dynamically loaded &svc_t until the end when %NULL is returned.
|
||||
*/
|
||||
svc_t *svc_dynamic_iterator(int first)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
||||
if (svc->mtime.tv_sec)
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_named_iterator - Iterates over all instances of a service.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
* @iter: Iterator, must be a valid pointer
|
||||
* @first: If set, get first &svc_t, otherwise get next
|
||||
* @cmd: Service name to look for.
|
||||
*
|
||||
* Returns:
|
||||
* The first matching &svc_t when @first is set, otherwise the next
|
||||
* &svc_t instance with the same @cmd until the end when %NULL is
|
||||
* The first matching &svc_t when %NULL is given as argument, otherwise
|
||||
* the next &svc_t with the same @cmd name until the end when %NULL is
|
||||
* returned.
|
||||
*/
|
||||
svc_t *svc_named_iterator(int first, char *cmd)
|
||||
svc_t *svc_named_iterator(svc_t **iter, int first, char *cmd)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
||||
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
|
||||
char *name = basename(svc->cmd);
|
||||
|
||||
if (!strncmp(name, cmd, strlen(name)))
|
||||
@@ -229,19 +179,20 @@ svc_t *svc_named_iterator(int first, char *cmd)
|
||||
|
||||
/**
|
||||
* svc_job_iterator - Iterates over all instances of a service.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
* @iter: Iterator, must be a valid pointer
|
||||
* @first: If set, get first &svc_t, otherwise get next
|
||||
* @job: Job to look for.
|
||||
*
|
||||
* Returns:
|
||||
* The first matching &svc_t when @first is set, otherwise the next
|
||||
* &svc_t instance with the same @job until the end when %NULL is
|
||||
* The first matching &svc_t when %NULL is given as argument, otherwise
|
||||
* the next &svc_t with the same @job ID until the end when %NULL is
|
||||
* returned.
|
||||
*/
|
||||
svc_t *svc_job_iterator(int first, int job)
|
||||
svc_t *svc_job_iterator(svc_t **iter, int first, int job)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
||||
for (svc = svc_iterator(iter, first); svc; svc = svc_iterator(iter, 0)) {
|
||||
if (svc->job == job)
|
||||
return svc;
|
||||
}
|
||||
@@ -256,28 +207,12 @@ svc_t *svc_job_iterator(int first, int job)
|
||||
*/
|
||||
void svc_foreach(void (*cb)(svc_t *))
|
||||
{
|
||||
svc_t *svc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
if (!cb)
|
||||
return;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0))
|
||||
cb(svc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_foreach_dynamic - Run a callback for each registered dynamic service
|
||||
* @cb: Callback to run for each dynamic service
|
||||
*/
|
||||
void svc_foreach_dynamic(void (*cb)(svc_t *))
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
if (!cb)
|
||||
return;
|
||||
|
||||
for (svc = svc_dynamic_iterator(1); svc; svc = svc_dynamic_iterator(0))
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0))
|
||||
cb(svc);
|
||||
}
|
||||
|
||||
@@ -289,12 +224,12 @@ void svc_foreach_dynamic(void (*cb)(svc_t *))
|
||||
*/
|
||||
void svc_foreach_type(int types, void (*cb)(svc_t *))
|
||||
{
|
||||
svc_t *svc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
if (!cb)
|
||||
return;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (!(svc->type & types))
|
||||
continue;
|
||||
|
||||
@@ -312,9 +247,9 @@ void svc_foreach_type(int types, void (*cb)(svc_t *))
|
||||
*/
|
||||
svc_t *svc_stop_completed(void)
|
||||
{
|
||||
svc_t *svc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->state == SVC_STOPPING_STATE)
|
||||
return svc;
|
||||
}
|
||||
@@ -331,9 +266,9 @@ svc_t *svc_stop_completed(void)
|
||||
*/
|
||||
svc_t *svc_find(char *cmd, int id)
|
||||
{
|
||||
svc_t *svc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd)))
|
||||
return svc;
|
||||
}
|
||||
@@ -350,9 +285,9 @@ svc_t *svc_find(char *cmd, int id)
|
||||
*/
|
||||
svc_t *svc_find_by_pid(pid_t pid)
|
||||
{
|
||||
svc_t *svc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->pid == pid)
|
||||
return svc;
|
||||
}
|
||||
@@ -370,9 +305,9 @@ svc_t *svc_find_by_pid(pid_t pid)
|
||||
*/
|
||||
svc_t *svc_find_by_jobid(int job, int id)
|
||||
{
|
||||
svc_t *svc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->job == job && svc->id == id)
|
||||
return svc;
|
||||
}
|
||||
@@ -391,9 +326,9 @@ svc_t *svc_find_by_jobid(int job, int id)
|
||||
svc_t *svc_find_by_nameid(char *name, int id)
|
||||
{
|
||||
char *ptr;
|
||||
svc_t *svc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
ptr = strrchr(svc->cmd, '/');
|
||||
if (ptr)
|
||||
ptr++;
|
||||
@@ -419,12 +354,10 @@ svc_t *svc_find_by_nameid(char *name, int id)
|
||||
*/
|
||||
void svc_mark_dynamic(void)
|
||||
{
|
||||
svc_t *svc = svc_dynamic_iterator(1);
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
while (svc) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0))
|
||||
*((int *)&svc->dirty) = -1;
|
||||
svc = svc_dynamic_iterator(0);
|
||||
}
|
||||
}
|
||||
|
||||
void svc_mark_dirty(svc_t *svc)
|
||||
@@ -437,17 +370,6 @@ void svc_mark_clean(svc_t *svc)
|
||||
*((int *)&svc->dirty) = 0;
|
||||
}
|
||||
|
||||
void svc_check_dirty(svc_t *svc, struct timeval *mtime)
|
||||
{
|
||||
if (mtime && timercmp(&svc->mtime, mtime, !=))
|
||||
svc_mark_dirty(svc);
|
||||
else
|
||||
svc_mark_clean(svc);
|
||||
|
||||
svc->mtime.tv_sec = mtime ? mtime->tv_sec : 0;
|
||||
svc->mtime.tv_usec = mtime ? mtime->tv_usec : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_clean_dynamic - Stop and cleanup stale services removed from /etc/finit.d
|
||||
* @cb: Callback to run for each stale service
|
||||
@@ -457,14 +379,13 @@ void svc_check_dirty(svc_t *svc, struct timeval *mtime)
|
||||
*/
|
||||
void svc_clean_dynamic(void (*cb)(svc_t *))
|
||||
{
|
||||
svc_t *svc = svc_dynamic_iterator(1);
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
while (svc) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (svc->dirty == -1 && cb) {
|
||||
cb(svc);
|
||||
svc_mark_clean(svc);
|
||||
}
|
||||
svc = svc_dynamic_iterator(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -495,85 +416,44 @@ int svc_clean_bootstrap(svc_t *svc)
|
||||
*/
|
||||
void svc_prune_bootstrap(void)
|
||||
{
|
||||
svc_t *svc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (!svc->pid)
|
||||
svc_clean_bootstrap(svc);
|
||||
}
|
||||
}
|
||||
|
||||
char *svc_status(svc_t *svc)
|
||||
/**
|
||||
* svc_enabled - Should the service run?
|
||||
* @svc: Pointer to &svc_t object
|
||||
*
|
||||
* Returns:
|
||||
* 1, if the service is allowed to run in the current runlevel and the
|
||||
* user has not manually requested that this service should not run. 0
|
||||
* otherwise.
|
||||
*/
|
||||
int svc_enabled(svc_t *svc)
|
||||
{
|
||||
switch (svc->state) {
|
||||
case SVC_HALTED_STATE:
|
||||
switch (svc->block) {
|
||||
case SVC_BLOCK_NONE:
|
||||
return "halted";
|
||||
if (!svc)
|
||||
return 0;
|
||||
|
||||
case SVC_BLOCK_MISSING:
|
||||
return "missing";
|
||||
if (!svc_in_runlevel(svc, runlevel))
|
||||
return 0;
|
||||
|
||||
case SVC_BLOCK_CRASHING:
|
||||
return "crashed";
|
||||
if (svc_is_removed(svc) || svc_is_blocked(svc))
|
||||
return 0;
|
||||
|
||||
case SVC_BLOCK_USER:
|
||||
return "stopped";
|
||||
|
||||
case SVC_BLOCK_BUSY:
|
||||
return "busy";
|
||||
|
||||
case SVC_BLOCK_RESTARTING:
|
||||
return "restart";
|
||||
}
|
||||
|
||||
case SVC_DONE_STATE:
|
||||
return "done";
|
||||
|
||||
case SVC_STOPPING_STATE:
|
||||
switch (svc->type) {
|
||||
case SVC_TYPE_INETD_CONN:
|
||||
case SVC_TYPE_RUN:
|
||||
case SVC_TYPE_TASK:
|
||||
return "active";
|
||||
|
||||
default:
|
||||
return "stopping";
|
||||
}
|
||||
|
||||
case SVC_WAITING_STATE:
|
||||
return "waiting";
|
||||
|
||||
case SVC_READY_STATE:
|
||||
return "ready";
|
||||
|
||||
case SVC_RUNNING_STATE:
|
||||
return "running";
|
||||
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *svc_dirtystr(svc_t *svc)
|
||||
{
|
||||
if (svc_is_removed(svc))
|
||||
return "removed";
|
||||
if (svc_is_updated(svc))
|
||||
return "updated";
|
||||
if (svc_is_changed(svc))
|
||||
return "UNKNOWN";
|
||||
|
||||
return "clean";
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Same base service, return unique ID */
|
||||
int svc_next_id(char *cmd)
|
||||
{
|
||||
int id = 0;
|
||||
svc_t *svc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) {
|
||||
if (!strcmp(svc->cmd, cmd) && id < svc->id)
|
||||
id = svc->id;
|
||||
}
|
||||
@@ -583,12 +463,10 @@ int svc_next_id(char *cmd)
|
||||
|
||||
int svc_is_unique(svc_t *svc)
|
||||
{
|
||||
svc_t *list = __connect_shm();
|
||||
int i, unique = 1;
|
||||
|
||||
for (i = 0; i < MAX_NUM_SVC; i++) {
|
||||
svc_t *s = &list[i];
|
||||
svc_t *s, *iter = NULL;
|
||||
int unique = 1;
|
||||
|
||||
for (s = svc_iterator(&iter, 1); s; s = svc_iterator(&iter, 0)) {
|
||||
if (svc->type == SVC_TYPE_FREE)
|
||||
continue;
|
||||
|
||||
@@ -619,21 +497,21 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
|
||||
token = strtok_r(input, " ", &pos);
|
||||
while (token) {
|
||||
int id = 1;
|
||||
svc_t *svc;
|
||||
svc_t *svc, *iter = NULL;
|
||||
char *ptr = strchr(token, ':');
|
||||
|
||||
if (isdigit(token[0])) {
|
||||
int job = atonum(token);
|
||||
|
||||
if (!ptr) {
|
||||
svc = svc_job_iterator(1, job);
|
||||
svc = svc_job_iterator(&iter, 1, job);
|
||||
if (!svc && not_found)
|
||||
result += not_found(NULL, job);
|
||||
|
||||
while (svc) {
|
||||
if (found)
|
||||
result += found(svc);
|
||||
svc = svc_job_iterator(0, job);
|
||||
svc = svc_job_iterator(&iter, 0, job);
|
||||
}
|
||||
} else {
|
||||
*ptr++ = 0;
|
||||
@@ -648,14 +526,14 @@ int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_foun
|
||||
}
|
||||
} else {
|
||||
if (!ptr) {
|
||||
svc = svc_named_iterator(1, token);
|
||||
svc = svc_named_iterator(&iter, 1, token);
|
||||
if (!svc && not_found)
|
||||
result += not_found(token, id);
|
||||
|
||||
while (svc) {
|
||||
if (found)
|
||||
result += found(svc);
|
||||
svc = svc_named_iterator(0, token);
|
||||
svc = svc_named_iterator(&iter, 0, token);
|
||||
}
|
||||
} else {
|
||||
*ptr++ = 0;
|
||||
|
||||
@@ -26,9 +26,10 @@
|
||||
#define FINIT_SVC_H_
|
||||
|
||||
#include <sys/ipc.h> /* IPC_CREAT */
|
||||
#include <sys/shm.h> /* shmat() */
|
||||
#include <sys/resource.h>
|
||||
#include <sys/types.h> /* pid_t */
|
||||
#include <lite/lite.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
#include "inetd.h"
|
||||
#include "helpers.h"
|
||||
@@ -65,13 +66,11 @@ typedef enum {
|
||||
SVC_BLOCK_RESTARTING,
|
||||
} svc_block_t;
|
||||
|
||||
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
|
||||
#define MAX_ARG_LEN 64
|
||||
#define MAX_STR_LEN 64
|
||||
#define MAX_COND_LEN (MAX_ARG_LEN * 3)
|
||||
#define MAX_USER_LEN 16
|
||||
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */
|
||||
#define MAX_NUM_SVC 64 /* Enough? */
|
||||
#define MAX_NUM_SVC_ARGS 32
|
||||
|
||||
/*
|
||||
@@ -81,24 +80,28 @@ typedef enum {
|
||||
* initctl <stop|start|restart> service
|
||||
*/
|
||||
typedef struct svc {
|
||||
TAILQ_ENTRY(svc) link;
|
||||
|
||||
/* Instance specifics */
|
||||
int job, id; /* JOB:ID */
|
||||
|
||||
/* Limits and scoping */
|
||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||
|
||||
/* Service details */
|
||||
pid_t pid;
|
||||
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
||||
svc_type_t type;
|
||||
struct timeval mtime; /* Modification time for .conf from /etc/finit.d/ */
|
||||
const int dirty; /* Set if old mtime != new mtime => reloaded,
|
||||
* or -1 when marked for removal */
|
||||
svc_type_t type; /* Service, run, task, inetd, ... */
|
||||
const int dirty; /* -1: removal, 0: unmodified, 1: modified */
|
||||
int starting; /* ... waiting for pidfile to be re-asserted */
|
||||
int runlevels;
|
||||
int sighup; /* This service supports SIGHUP :) */
|
||||
svc_block_t block; /* Reason that this service is currently stopped */
|
||||
char cond[MAX_COND_LEN];
|
||||
|
||||
/* Incremented for each restart by service monitor. */
|
||||
const unsigned int restart_counter;
|
||||
/* Counters */
|
||||
char once; /* run/task, (at least) once per runlevel */
|
||||
const char restart_cnt; /* Incremented for each restart by service monitor. */
|
||||
|
||||
/* For inetd services */
|
||||
inetd_t inetd;
|
||||
@@ -124,23 +127,6 @@ typedef struct svc {
|
||||
void (*timer_cb)(struct svc *svc);
|
||||
} svc_t;
|
||||
|
||||
typedef struct svc_map svc_map_t;
|
||||
|
||||
/* Put services array in shm */
|
||||
static inline svc_t *finit_svc_connect(void)
|
||||
{
|
||||
static void *ptr = (void *)-1;
|
||||
|
||||
if ((void *)-1 == ptr) {
|
||||
ptr = shmat(shmget(FINIT_SHM_ID, sizeof(svc_t) * MAX_NUM_SVC,
|
||||
0600 | IPC_CREAT), NULL, 0);
|
||||
if ((void *)-1 == ptr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (svc_t *)ptr;
|
||||
}
|
||||
|
||||
svc_t *svc_new (char *cmd, int id, int type);
|
||||
int svc_del (svc_t *svc);
|
||||
|
||||
@@ -149,28 +135,24 @@ svc_t *svc_find_by_pid (pid_t pid);
|
||||
svc_t *svc_find_by_jobid (int job, int id);
|
||||
svc_t *svc_find_by_nameid (char *name, int id);
|
||||
|
||||
svc_t *svc_iterator (int first);
|
||||
svc_t *svc_inetd_iterator (int first);
|
||||
svc_t *svc_dynamic_iterator (int first);
|
||||
svc_t *svc_named_iterator (int first, char *cmd);
|
||||
svc_t *svc_job_iterator (int first, int job);
|
||||
svc_t *svc_iterator (svc_t **iter, int first);
|
||||
svc_t *svc_inetd_iterator (svc_t **iter, int first);
|
||||
svc_t *svc_named_iterator (svc_t **iter, int first, char *cmd);
|
||||
svc_t *svc_job_iterator (svc_t **iter, int first, int job);
|
||||
|
||||
void svc_foreach (void (*cb)(svc_t *));
|
||||
void svc_foreach_dynamic (void (*cb)(svc_t *));
|
||||
void svc_foreach_type (int types, void (*cb)(svc_t *));
|
||||
|
||||
svc_t *svc_stop_completed (void);
|
||||
|
||||
void svc_mark_dynamic (void);
|
||||
void svc_check_dirty (svc_t *svc, struct timeval *mtime);
|
||||
void svc_mark_dirty (svc_t *svc);
|
||||
void svc_mark_clean (svc_t *svc);
|
||||
void svc_clean_dynamic (void (*cb)(svc_t *));
|
||||
int svc_clean_bootstrap (svc_t *svc);
|
||||
void svc_prune_bootstrap (void);
|
||||
|
||||
char *svc_status (svc_t *svc);
|
||||
const char *svc_dirtystr (svc_t *svc);
|
||||
int svc_enabled (svc_t *svc);
|
||||
int svc_next_id (char *cmd);
|
||||
int svc_is_unique (svc_t *svc);
|
||||
|
||||
@@ -183,7 +165,6 @@ static inline void svc_starting (svc_t *svc) { svc->starting = 1; }
|
||||
static inline void svc_started (svc_t *svc) { svc->starting = 0; }
|
||||
static inline int svc_is_starting (svc_t *svc) { return 0 != svc->starting; }
|
||||
|
||||
static inline int svc_is_dynamic (svc_t *svc) { return svc && 0 != svc->mtime.tv_sec; }
|
||||
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
|
||||
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
||||
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
||||
@@ -191,6 +172,7 @@ static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty
|
||||
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
||||
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
|
||||
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
|
||||
static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); }
|
||||
|
||||
static inline int svc_is_blocked (svc_t *svc) { return svc->block != SVC_BLOCK_NONE; }
|
||||
static inline int svc_is_busy (svc_t *svc) { return svc->block == SVC_BLOCK_BUSY; }
|
||||
@@ -202,6 +184,70 @@ static inline void svc_missing (svc_t *svc) { svc->block = SVC_BLOCK_MISSING
|
||||
static inline void svc_restarting (svc_t *svc) { svc->block = SVC_BLOCK_RESTARTING; }
|
||||
static inline void svc_crashing (svc_t *svc) { svc->block = SVC_BLOCK_CRASHING; }
|
||||
|
||||
static inline char *svc_status(svc_t *svc)
|
||||
{
|
||||
switch (svc->state) {
|
||||
case SVC_HALTED_STATE:
|
||||
switch (svc->block) {
|
||||
case SVC_BLOCK_NONE:
|
||||
return "halted";
|
||||
|
||||
case SVC_BLOCK_MISSING:
|
||||
return "missing";
|
||||
|
||||
case SVC_BLOCK_CRASHING:
|
||||
return "crashed";
|
||||
|
||||
case SVC_BLOCK_USER:
|
||||
return "stopped";
|
||||
|
||||
case SVC_BLOCK_BUSY:
|
||||
return "busy";
|
||||
|
||||
case SVC_BLOCK_RESTARTING:
|
||||
return "restart";
|
||||
}
|
||||
|
||||
case SVC_DONE_STATE:
|
||||
return "done";
|
||||
|
||||
case SVC_STOPPING_STATE:
|
||||
switch (svc->type) {
|
||||
case SVC_TYPE_INETD_CONN:
|
||||
case SVC_TYPE_RUN:
|
||||
case SVC_TYPE_TASK:
|
||||
return "active";
|
||||
|
||||
default:
|
||||
return "stopping";
|
||||
}
|
||||
|
||||
case SVC_WAITING_STATE:
|
||||
return "waiting";
|
||||
|
||||
case SVC_READY_STATE:
|
||||
return "ready";
|
||||
|
||||
case SVC_RUNNING_STATE:
|
||||
return "running";
|
||||
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char *svc_dirtystr(svc_t *svc)
|
||||
{
|
||||
if (svc_is_removed(svc))
|
||||
return "removed";
|
||||
if (svc_is_updated(svc))
|
||||
return "updated";
|
||||
if (svc_is_changed(svc))
|
||||
return "UNKNOWN";
|
||||
|
||||
return "clean";
|
||||
}
|
||||
|
||||
#endif /* FINIT_SVC_H_ */
|
||||
|
||||
/**
|
||||
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
/* Simple telinit client
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2015 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" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "util.h"
|
||||
|
||||
static int do_send(struct init_request *rq)
|
||||
{
|
||||
static int fd = -1;
|
||||
int result = 255;
|
||||
ssize_t len;
|
||||
|
||||
if (fd == -1) {
|
||||
if (!fexist(FINIT_FIFO)) {
|
||||
fprintf(stderr, "%s does not support %s!\n", prognm, FINIT_FIFO);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fd = open(FINIT_FIFO, O_RDWR);
|
||||
if (-1 == fd) {
|
||||
perror("Failed opening " FINIT_FIFO);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
len = write(fd, rq, sizeof(*rq));
|
||||
if (sizeof(*rq) == len)
|
||||
result = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int do_reload(void)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_RELOAD,
|
||||
};
|
||||
|
||||
return do_send(&rq);
|
||||
}
|
||||
|
||||
static int set_env(int runlevel)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_SETENV,
|
||||
};
|
||||
|
||||
if (runlevel == 0) {
|
||||
strlcpy(rq.data, "INIT_HALT=POWERDOWN", sizeof(rq.data));
|
||||
return do_send(&rq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_runlevel(int runlevel)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_RUNLVL,
|
||||
.runlevel = runlevel,
|
||||
};
|
||||
|
||||
return set_env(runlevel) || do_send(&rq);
|
||||
}
|
||||
|
||||
/* telinit q | telinit <0-9> */
|
||||
static int usage(int rc)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
|
||||
"Options:\n"
|
||||
" -h, --help This help text\n"
|
||||
" -V, --version Show Finit version\n\n"
|
||||
"Commands:\n"
|
||||
" 0 Power-off the system, same as initctl poweroff\n"
|
||||
" 6 Reboot the system, same as initctl reboot\n"
|
||||
" 2, 3, 4, 5 Change runlevel. Starts services in new runlevel, stops any\n"
|
||||
" services in prev. runlevel that are not allowed in new.\n"
|
||||
" q, Q Reload *.conf in /etc/finit.d/, same as initctl reload or\n"
|
||||
" sending SIGHUP to PID 1\n"
|
||||
" 1, s, S Enter system rescue mode, runlevel 1\n"
|
||||
"\n", prognm);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int client(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
struct option long_options[] = {
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"version", 0, NULL, 'v'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
progname(argv[0]);
|
||||
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case 'v':
|
||||
return puts("v" VERSION) == EOF;
|
||||
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
int req = (int)argv[optind][0];
|
||||
|
||||
if (isdigit(req))
|
||||
return set_runlevel(req);
|
||||
|
||||
if (req == 'q' || req == 'Q')
|
||||
return do_reload();
|
||||
|
||||
if (req == 's' || req == 'S')
|
||||
return set_runlevel(1);
|
||||
}
|
||||
|
||||
return usage(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -45,12 +45,28 @@ static LIST_HEAD(, tty_node) tty_list = LIST_HEAD_INITIALIZER();
|
||||
|
||||
static char *canonicalize(char *tty)
|
||||
{
|
||||
char buf[42];
|
||||
struct stat st;
|
||||
static char path[80];
|
||||
|
||||
if (!tty)
|
||||
return NULL;
|
||||
|
||||
/* Auto-detect serial console, for embedded devices mostly */
|
||||
if (!strcmp(tty, "@console")) {
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/sys/class/tty/console/active", "r");
|
||||
if (!fp) {
|
||||
_e("Cannot find system console, is sysfs not mounted?");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fgets(buf, sizeof(buf), fp))
|
||||
tty = chomp(buf);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
strlcpy(path, tty, sizeof(path));
|
||||
if (stat(path, &st)) {
|
||||
if (!strncmp(path, _PATH_DEV, strlen(_PATH_DEV))) {
|
||||
@@ -74,22 +90,8 @@ void tty_mark(void)
|
||||
{
|
||||
tty_node_t *tty;
|
||||
|
||||
LIST_FOREACH(tty, &tty_list, link) {
|
||||
if (tty->mtime.tv_sec)
|
||||
tty->dirty = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void tty_check(tty_node_t *tty, struct timeval *mtime)
|
||||
{
|
||||
if (mtime && timercmp(&tty->mtime, mtime, !=))
|
||||
tty->dirty = 1; /* Modified, restart */
|
||||
else
|
||||
tty->dirty = 0; /* Not modified */
|
||||
|
||||
/* Update mtime, if given */
|
||||
tty->mtime.tv_sec = mtime ? mtime->tv_sec : 0;
|
||||
tty->mtime.tv_usec = mtime ? mtime->tv_usec : 0;
|
||||
LIST_FOREACH(tty, &tty_list, link)
|
||||
tty->dirty = -1;
|
||||
}
|
||||
|
||||
void tty_sweep(void)
|
||||
@@ -97,23 +99,24 @@ void tty_sweep(void)
|
||||
tty_node_t *tty, *tmp;
|
||||
|
||||
LIST_FOREACH_SAFE(tty, &tty_list, link, tmp) {
|
||||
if (tty->mtime.tv_sec && tty->dirty) {
|
||||
_d("TTY %s dirty, stopping ...", tty->data.name);
|
||||
tty_stop(&tty->data);
|
||||
if (!tty->dirty)
|
||||
continue;
|
||||
|
||||
if (tty->dirty == -1) {
|
||||
_d("TTY %s removed, cleaning up.", tty->data.name);
|
||||
LIST_REMOVE(tty, link);
|
||||
tty_unregister(tty);
|
||||
}
|
||||
_d("TTY %s dirty, stopping ...", tty->data.name);
|
||||
tty_stop(&tty->data);
|
||||
|
||||
if (tty->dirty == -1) {
|
||||
_d("TTY %s removed, cleaning up.", tty->data.name);
|
||||
tty_unregister(tty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tty_register - Register a getty on a device
|
||||
* @line: Configuration, text after initial "tty"
|
||||
* @mtime: Modification time, to propagate to lower layers
|
||||
* @line: Configuration, text after initial "tty"
|
||||
* @rlimit: Limits for this service/task/run/inetd, may be global limits
|
||||
* @file: The file name TTY was loaded from
|
||||
*
|
||||
* A Finit tty line can use the internal getty implementation or an
|
||||
* external one, like the BusyBox getty for instance. This function
|
||||
@@ -131,7 +134,7 @@ void tty_sweep(void)
|
||||
* Different getty implementations prefer the TTY device argument in
|
||||
* different order, so take care to investigate this first.
|
||||
*/
|
||||
int tty_register(char *line, struct timeval *mtime)
|
||||
int tty_register(char *line, struct rlimit rlimit[], char *file)
|
||||
{
|
||||
tty_node_t *entry;
|
||||
int insert = 0, noclear = 0, nowait = 0;
|
||||
@@ -168,6 +171,8 @@ int tty_register(char *line, struct timeval *mtime)
|
||||
if (!dev && !cmd) {
|
||||
if (args[i][0] == '[')
|
||||
runlevels = line;
|
||||
if (!strcmp(args[i], "@console"))
|
||||
dev = args[i];
|
||||
if (!strncmp(args[i], "/dev", 4))
|
||||
dev = args[i];
|
||||
if (!strncmp(args[i], "tty", 3))
|
||||
@@ -261,7 +266,13 @@ int tty_register(char *line, struct timeval *mtime)
|
||||
if (insert)
|
||||
LIST_INSERT_HEAD(&tty_list, entry, link);
|
||||
|
||||
tty_check(entry, mtime);
|
||||
/* Register configured limits */
|
||||
memcpy(entry->data.rlimit, rlimit, sizeof(entry->data.rlimit));
|
||||
|
||||
if (file && conf_changed(file))
|
||||
entry->dirty = 1; /* Modified, restart */
|
||||
else
|
||||
entry->dirty = 0; /* Not modified */
|
||||
_d("TTY %s is %sdirty", dev, entry->dirty ? "" : "NOT ");
|
||||
|
||||
return 0;
|
||||
@@ -274,6 +285,8 @@ int tty_unregister(tty_node_t *tty)
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
LIST_REMOVE(tty, link);
|
||||
|
||||
if (tty->data.name)
|
||||
free(tty->data.name);
|
||||
if (tty->data.baud)
|
||||
@@ -361,7 +374,6 @@ static int tty_exist(char *dev)
|
||||
|
||||
void tty_start(finit_tty_t *tty)
|
||||
{
|
||||
int is_console = 0;
|
||||
char *dev;
|
||||
|
||||
if (tty->pid) {
|
||||
@@ -375,18 +387,15 @@ void tty_start(finit_tty_t *tty)
|
||||
return;
|
||||
}
|
||||
|
||||
if (console && !strcmp(dev, console))
|
||||
is_console = 1;
|
||||
|
||||
if (tty_exist(dev)) {
|
||||
_d("%s: Not a valid TTY: %s", dev, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tty->cmd)
|
||||
tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, is_console);
|
||||
tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit);
|
||||
else
|
||||
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, is_console);
|
||||
tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, tty->rlimit);
|
||||
}
|
||||
|
||||
void tty_stop(finit_tty_t *tty)
|
||||
@@ -491,11 +500,13 @@ void tty_reload(char *dev)
|
||||
|
||||
if (dev) {
|
||||
tty = tty_find(dev);
|
||||
if (!tty)
|
||||
if (!tty) {
|
||||
logit(LOG_WARNING, "No TTY registered for %s", dev);
|
||||
else
|
||||
tty_action(tty);
|
||||
return;
|
||||
}
|
||||
|
||||
tty_action(tty);
|
||||
tty->dirty = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#define FINIT_TTY_H_
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/resource.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
#define TTY_MAX_ARGS 16
|
||||
@@ -43,6 +44,9 @@ typedef struct {
|
||||
char *args[TTY_MAX_ARGS];
|
||||
|
||||
int pid;
|
||||
|
||||
/* Limits and scoping */
|
||||
struct rlimit rlimit[RLIMIT_NLIMITS];
|
||||
} finit_tty_t;
|
||||
|
||||
typedef struct tty_node {
|
||||
@@ -50,7 +54,6 @@ typedef struct tty_node {
|
||||
finit_tty_t data;
|
||||
|
||||
/* XXX: Yes, TTYs should be refactored into a separate SVC type. */
|
||||
struct timeval mtime; /* Modification time for .conf from /etc/finit.d/ */
|
||||
int dirty; /* Set if old mtime != new mtime => reloaded,
|
||||
* or -1 when marked for removal */
|
||||
} tty_node_t;
|
||||
@@ -58,10 +61,9 @@ typedef struct tty_node {
|
||||
//extern LIST_HEAD(, tty_node) tty_list;
|
||||
|
||||
void tty_mark (void);
|
||||
void tty_check (tty_node_t *tty, struct timeval *mtime);
|
||||
void tty_sweep (void);
|
||||
|
||||
int tty_register (char *line, struct timeval *mtime);
|
||||
int tty_register (char *line, struct rlimit rlimit[], char *file);
|
||||
int tty_unregister (tty_node_t *tty);
|
||||
|
||||
tty_node_t *tty_find (char *dev);
|
||||
|
||||
+4
-1
@@ -67,7 +67,7 @@ char *sanitize(char *arg, size_t len)
|
||||
return arg;
|
||||
}
|
||||
|
||||
if (i > 1 && arg[i] == 0)
|
||||
if (i > 0 && arg[i] == 0)
|
||||
return arg;
|
||||
|
||||
return NULL;
|
||||
@@ -75,6 +75,9 @@ char *sanitize(char *arg, size_t len)
|
||||
|
||||
void screen_init(void)
|
||||
{
|
||||
if (!isatty(STDOUT_FILENO))
|
||||
return;
|
||||
|
||||
initscr(&screen_rows, &screen_cols);
|
||||
}
|
||||
|
||||
|
||||
+16
-49
@@ -21,54 +21,17 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <paths.h>
|
||||
#include <signal.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
int running = 1;
|
||||
int handover = 0;
|
||||
|
||||
static int notify(void)
|
||||
{
|
||||
int sd, result = 255;
|
||||
ssize_t len;
|
||||
struct sockaddr_un sun = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
struct init_request rq;
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (-1 == sd)
|
||||
return -1;
|
||||
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
||||
goto exit;
|
||||
|
||||
rq.cmd = INIT_CMD_WDOG_HELLO;
|
||||
rq.magic = INIT_MAGIC;
|
||||
rq.runlevel = getpid();
|
||||
len = sizeof(rq);
|
||||
if (write(sd, &rq, len) != len)
|
||||
goto exit;
|
||||
|
||||
if (read(sd, &rq, len) != len)
|
||||
goto exit;
|
||||
|
||||
result = 0;
|
||||
exit:
|
||||
close(sd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void sighandler(int signo)
|
||||
{
|
||||
if (signo == SIGTERM)
|
||||
@@ -93,27 +56,29 @@ static int init(char *progname, char *devnode, int timeout)
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int loop(int fd)
|
||||
static int loop(int fd, int period)
|
||||
{
|
||||
int done = 0;
|
||||
int dummy = 0;
|
||||
|
||||
while (running) {
|
||||
do_sleep(1);
|
||||
ioctl(fd, WDIOC_KEEPALIVE, &done);
|
||||
|
||||
if (!done && !notify())
|
||||
done = 1;
|
||||
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
||||
sleep(period);
|
||||
}
|
||||
|
||||
if (handover)
|
||||
if (handover) {
|
||||
ioctl(fd, WDIOC_KEEPALIVE, &dummy);
|
||||
return !write(fd, "V", 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void watchdog(char *progname)
|
||||
int watchdog(char *progname)
|
||||
{
|
||||
if (fork() == 0) {
|
||||
int pid;
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
int fd, ret;
|
||||
|
||||
fd = init(progname, WDT_DEVNODE, WDT_TIMEOUT);
|
||||
@@ -122,11 +87,13 @@ void watchdog(char *progname)
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
ret = loop(fd);
|
||||
ret = loop(fd, WDT_TIMEOUT / 2);
|
||||
close(fd);
|
||||
|
||||
_exit(ret);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-3
@@ -24,12 +24,12 @@
|
||||
#include "config.h" /* Generated by configure script */
|
||||
|
||||
#define WDT_DEVNODE "/dev/watchdog"
|
||||
#define WDT_TIMEOUT 3
|
||||
#define WDT_TIMEOUT 30
|
||||
|
||||
#ifdef BUILTIN_WATCHDOG
|
||||
void watchdog(char *progname);
|
||||
int watchdog(char *progname);
|
||||
#else
|
||||
#define watchdog(progname)
|
||||
#define watchdog(progname) 0
|
||||
#endif /* BUILTIN_WATCHDOG */
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user