Compare commits

..

186 Commits
0.26 ... 0.35

Author SHA1 Message Date
William Hubbs
f62975a1f8 Update ChangeLog 2018-02-28 16:21:52 -06:00
Jason Zaman
5bb6f9aa31 init.sh: apply SELinux label for /run early in boot
Some initramfs mount /run which then ends up with the wrong labels.
Force relabel all of /run right after its mounted to fix.
2018-02-28 12:42:58 -06:00
Jason Zaman
1ab8541a6c init-early.sh.Linux.in: apply the selinux label to /dev/console early
/dev/console is relabelled later in the devfs init script, but by then we
have already missed some of the messages, so fix that label early.
2018-02-28 11:24:55 -06:00
William Hubbs
038c03bef3 supervise-daemon: make an error message more verbose 2018-02-27 16:02:20 -06:00
William Hubbs
e51dc29e2f cgroups: fix indentation 2018-02-27 15:56:54 -06:00
William Hubbs
c6047f887a cgroups: add rc_cgroup_memory_use_hierarchy setting for cgroups v1 2018-02-26 14:33:15 -06:00
William Hubbs
0506d68427 fstabinfo: fix an error message 2018-02-26 13:46:47 -06:00
William Hubbs
c3d666acaf openrc: remove unused #define's. 2018-02-26 13:14:31 -06:00
William Hubbs
e93b1d76d1 openrc: convert another execl() call to execlp() 2018-02-26 13:13:44 -06:00
William Hubbs
acf1e43f81 openrc-init: convert execl calls to execlp 2018-02-26 12:21:14 -06:00
William Hubbs
f383fd87b1 kill_all: change execl call to execlp 2018-02-24 17:02:02 -06:00
William Hubbs
cfded513cd openrc-init: set a default path
The default path provided by the system if one isn't set only includes
"/bin:/usr/bin". This adds the default path setting from sysvinit.
2018-02-24 16:24:08 -06:00
Christian Brauner
16ff3cd8df check whether /sys/fs/cgroup is a mountpoint
The current check only tries to detect whether /sys/fs/cgroup exists and
whether it is writable or not. But when the init system doesn't mount
cgroups then /sys/fs/cgroup will just be an empty directory. When paired
with unprivileged containers that mount sysfs this will cause misleading
errors to be printed since /sys/fs/cgroup will be owned by user
nobody:nogroup in this case. Independent of this specific problem this
check will also be misleading when the /sys/fs/cgroup exists and is in
fact writable by the init system but isn't actually a mountpoint.

Note from William. "grep -qs" doesn't need to redirect output to
/dev/null since it is completely silent.

This fixes #209.
2018-02-23 15:51:12 -06:00
William Hubbs
38032626a6 improve cgroup configuration checks
make the base/controller functions return successfully if cgroups v1/v2
are not configured in the kernel
2018-02-23 15:07:04 -06:00
William Hubbs
aa7d3a7911 openrc: force deptree update for sysinit runlevel 2018-02-22 16:16:07 -06:00
William Hubbs
98262647a9 supervise-daemon: zero out the cmdline buffer when it is allocated 2018-02-22 12:54:21 -06:00
William Hubbs
5868abe97b start-stop-daemon: compiler warning cleanup 2018-02-21 18:16:56 -06:00
William Hubbs
71aad16256 openrc-run: clean up a compiler warning 2018-02-21 18:15:35 -06:00
William Hubbs
e275da84de supervise-daemon: remove references to PATH_MAX 2018-02-21 18:14:56 -06:00
William Hubbs
a6cc7f06cf rc.c: remove PATH_MAX references 2018-02-21 13:49:41 -06:00
William Hubbs
c1178c8eeb rc-selinux.c: remove references to path_max 2018-02-20 16:35:50 -06:00
William Hubbs
3c031ca978 rc-plubin.c: remove references to PATH_MAX 2018-02-20 16:01:15 -06:00
William Hubbs
8e02406d8f rc-misc.c: remove references to PATH_MAX 2018-02-20 14:22:29 -06:00
William Hubbs
8dbdabcc5e start-stop-daemon: clean up string handling 2018-02-16 14:04:40 -06:00
William Hubbs
488d8989c5 openrc-run: clean up string handling
- remove references to PATH_MAX
- use xasprintf to create strings
2018-02-15 15:26:40 -06:00
William Hubbs
9e14b35da8 rc-update: remove reference to PATH_MAX 2018-02-15 12:23:39 -06:00
William Hubbs
7b4879cb72 mountinfo: create strings with xasprintf 2018-02-14 16:56:19 -06:00
William Hubbs
74cfb455c5 kill_all: create strings with xasprintf 2018-02-14 16:50:24 -06:00
William Hubbs
0110487722 helpers.h: silence compiler warnings about xasprintf 2018-02-10 16:48:22 -06:00
William Hubbs
68b9b0bc2a xasprintf: exit if return value of vsnprintf is invalid 2018-02-10 14:49:40 -06:00
William Hubbs
4616f8f809 helpers.h: add xasprintf function
This is our own version of asprintf(). This original code was written by
Mike Frysinger, and I was able to modify it to use our memory helper
functions.

We need a version of this code because it is not available on glibc at
least without defining _GNU_SOURCE, and I would rather not do that.

This is the first step in improving string handling in OpenRC for #207.
2018-02-09 16:27:12 -06:00
William Hubbs
287d71bd25 helpers.h: style fix 2018-02-09 15:35:12 -06:00
Michael Orlitzky
c1ffe4d9f2 man/openrc-run.8: fix mountinfo args.
The "Ar" tag for the mountinfo command contained a typo (leading
space) that resulted in the tag being output verbatim; that is,

  mountinfo ... .Ar mount1 mount2

rather than e.g.

  mountinfo ... <mount1> <mount2>

This commit deletes the leading space to fix the output.

This fixes #204.
2018-02-06 13:53:11 -06:00
Michael Orlitzky
4c517bdb2b man/openrc-run.8: add missing "Xo" tags for fstabinfo and mountinfo.
The BUILTINS are all surrounded by Xo...Xc tags, but the opening "Xo"
was missing from the two commands fstabinfo and mountinfo. This commit
adds them, and thereby fixes the spacing when viewed by man.

This is for #204.
2018-02-06 13:52:36 -06:00
William Hubbs
110582491f service-script-guide.md cleanups
Refer to /var/run in the documentation instead of /run, and make it
clear at the top of the pidfile section that we use /run under Linux.

This is for #202.
2018-01-24 17:43:08 -06:00
William Hubbs
5dd1d39d20 typo fix 2018-01-24 10:56:33 -06:00
William Hubbs
1771bc2a83 checkpath: use fchown and fchmod to handle ownership and mode changes
This is related to #195.

This is an attempt to shorten the window for the first two issues
discussed by using a file descriptor which does not follow symbolic
links and using the fchmod and fchown calls instead of chown and chmod.
with.
2018-01-23 16:56:06 -06:00
William Hubbs
87c98ebb01 checkpath: fix lchown error message
X-Gentoo-Bug: 643084
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=643084
2018-01-16 13:34:37 -06:00
William Hubbs
4af5a80b0c _rc_deptree_load - return NULL if the stat() call is not successful
X-Gentoo-Bug: 643084
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=643084
2018-01-16 13:11:22 -06:00
William Hubbs
fee2ffe559 Reformat and fix titles in documentation 2018-01-12 14:10:42 -06:00
William Hubbs
8878f8916b Rename guide.md to user-guide.md to better describe its purpose 2018-01-11 10:57:05 -06:00
William Hubbs
14e3359a9e move developer documentation from guide.md to service-script-guide.md 2018-01-10 13:25:13 -06:00
William Hubbs
918d955fd2 checkpath: use lchown instead of chown
Checkpath should never follow symbolic links when changing ownership of a file.

This is for https://github.com/openrc/openrc/issues/195.
2018-01-09 17:25:28 -06:00
William Hubbs
92cfa0e543 service-script-guide.md: formatting changes
Add a title, adjust the headings and update the example that referred to
"net.lo" to refer to "loopback".
2018-01-08 15:33:03 -06:00
Michael Orlitzky
c2bd33e483 service-script-guide.md: new guide for service script authors.
This fixes #162.
2018-01-08 13:59:12 -06:00
3PO
f42ec82f21 net-online: only process symbolic links in /sys/class/net
The /sys/class/net directory contains files which are not symlinks if
the system has bonded devices [1].  We should ignore these files.

This fixes #196.

[1] https://elkano.org/blog/manage-interface-bondings-sysfs-interface/
2018-01-05 14:56:33 -06:00
William Hubbs
7affff568a init.d.examples: pid files always go in /var
This removes the @VARBASE@ substitution since it is always translated to
/var.
2017-12-20 11:01:29 -06:00
William Hubbs
4f750933fd procfs: remove the @sysconfdir@ reference 2017-12-14 17:31:57 -06:00
William Hubbs
50b69d564a hostname: replace @SYSCONFDIR@ references with /etc
I do not know of any situation where /etc/hostname is at any other
location. Also, this does not run on prefix.
2017-12-14 17:07:31 -06:00
William Hubbs
eb18f4f348 hwclock: use shell variables for configuration file path
This removes a reference to @SYSCONFDIR@.
2017-12-14 16:38:09 -06:00
William Hubbs
d5f3fe52c8 sysctl: hard code paths for sysctl files on *bsd
For *BSD,the sysctl*.conf files are always in /etc.
2017-12-14 14:18:39 -06:00
William Hubbs
bb1bc6eeb7 local: misc cleanups
Remove references to @SYSCONFDIR@ since these can be calculated at
runtime.
Also style fixes.
2017-12-14 12:52:03 -06:00
William Hubbs
53844fd0dc devfs: cleanup
clean up local definitions. Also remove @SYSCONFDIR@ substitutions since
they can be calculated at runtime.
2017-12-14 12:08:38 -06:00
bell07
14938c29ce net-online: wait for ping_test_host
The script should wait till the ping host is available or timeout reached
Closes : #179
Closes : #191
2017-12-12 16:29:34 -06:00
William Hubbs
8bf501aaf2 network: use 'command -v ip' to test for the ip executable
This is an improved test because it doesn't require the ip executable to be
in a specific path.
2017-12-12 14:23:04 -06:00
William Hubbs
9d05f68b51 Do not use file tests in net-online
https://unix.stackexchange.com/questions/252002/help-testing-special-file-in-sys-class-net

This is for #189
2017-12-12 12:45:52 -06:00
William Hubbs
ee2524cd1d net-online: clean up quoting and test for existence
This is for #178.
2017-12-11 17:41:04 -06:00
Marcel Greter
5c81661d47 Gracefully handle unreadable /sys/class/net/dev/ nodes
Fixes https://bugs.gentoo.org/629228
Fixes #189
Fixes #185
Fixes #178
2017-12-11 14:46:16 -06:00
William Hubbs
fb96c9c127 Make bash-completion and zsh-completion optional 2017-12-07 12:53:04 -06:00
William Hubbs
6c456f9383 Add zsh-completion support 2017-12-07 11:30:32 -06:00
William Hubbs
d220fc2723 add bash completion support
This fixes #188.
2017-12-06 13:25:26 -06:00
William Hubbs
a2447dfb42 rc-service: add --ifcrashed option
This works like the other --if options. If the service is crashed, run
the command.

This fixes #154.
2017-12-04 17:17:17 -06:00
William Hubbs
cee3919908 Clean up the calls to group_add_service
This function should only be called once and it does not take any
arguments.

X-Gentoo-Bug: 639166
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=639166
2017-12-01 15:43:09 -06:00
William Hubbs
0feadd2922 man/openrc-run.8: update documentation for command_user 2017-11-30 16:00:42 -06:00
Will Miles
a7c99506d9 Fix repeated dependency cache rebuild if clock skewed
rc_deptree_update_needed would return early as soon as it found
any file newer than the existing dependency cache.  Unfortunately,
the first file found may not be the newest one there; so the
clock skew workaround in rc-misc:_rc_deptree_load would be given
a timestamp that was still too old.

This fix forces a full scan of all relevant files, so as to
ensure that we return a timestamp that will allow the clock skew
fix to operate.   The runtime cost is no worse than the case where
the cache is up to date (ie. we must check every possible file).

This fixes #161.
2017-11-30 13:56:54 -06:00
Julien Reichardt
f012930775 man/openrc-run.8: remove white space
This is for #184.
2017-11-29 15:06:06 -06:00
Julien Reichardt
ddbdb69658 add more variables for start-stop-daemon and supervise-daemon options
Add the following variables to expose more arguments that can be passed
to start-stop-daemon or supervise-daemon:

- directory will be passed to --chdir
- error_log will be passed to --stderr
- output_log will be passed to --stdout
- umask will be passed to umask

This is for #184.
2017-11-29 15:06:06 -06:00
Doug Freed
c84ebb94d1 start-stop-daemon: properly handle missing pidfile
X-Gentoo-Bug: 639218
X-Gentoo-Bug-URL: https://bugs.gentoo.org/639218
2017-11-29 15:10:39 -05:00
William Hubbs
3de6395ae3 split cgroups mounting out of sysfs
This is neceessary to allow cgroups to be mounted in an lxc/lxd
container.

Fixes https://github.com/openrc/openrc/issues/187
2017-11-28 17:14:33 -06:00
William Hubbs
6bb7ebec48 fix FreeBSD build
This is for #186.
2017-11-27 11:10:00 -06:00
Doug Freed
a192caf88f rc-schedules: if given nothing to look for, stop
This avoids trying to kill everything.

X-Gentoo-Bug: 631958
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=631958
2017-11-19 11:05:30 -05:00
William Hubbs
e805c74d31 s6 supervisor fixes
Add the ability to force-kill a service if it does not go down
successfully. Also, adjust the default wait time for an s6 service to go
down to 60 seconds.
2017-11-16 15:35:35 -06:00
William Hubbs
318e873481 strongly encourage Linux users to not make /etc/mtab a flat file
The OpenRC team does not currently know of any modern linux tools that
require /etc/mtab to be a flat file, so this puts users on notice that
the mtab service will be removed in the future.
2017-11-14 15:41:03 -06:00
William Hubbs
0d15898f58 adjust mtab and localmount dependencies
localmount had mtab in its "use" dependencies; however, it makes more
sense to add "before localmount" to the mtab service and remove
"use mtab" from the localmount service.
2017-11-14 13:20:49 -06:00
William Hubbs
971e82784c rc_find_pids: namespace fix
Ignore namespaces if there are errors reading either the pid namespace
for the current process or the process we aare testing.

This fixes https://github.com/openrc/openrc/issues/180.
2017-11-13 16:54:57 -06:00
William Hubbs
90d9ea656f start-stop-daemon: do not use do_stop to verify whether a daemon is running
X-Gentoo-Bug: 636574
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=636574
2017-11-07 15:30:34 -06:00
William Hubbs
a3d1c8a0e7 fix issue with --reexec call 2017-10-30 18:21:39 -05:00
William Hubbs
913b2ca537 supervise-daemon: use RC_SVCNAME as the first argument to the daemon
This makes ps show which service the supervisor is monitoring.
2017-10-27 19:22:09 -05:00
William Hubbs
3fe99c8b82 supervise-daemon: fix logging for reexec and the child command line 2017-10-27 17:34:29 -05:00
William Hubbs
27b8183de2 log as supervise-daemon not the service 2017-10-27 15:33:47 -05:00
William Hubbs
f32d8e1bfe supervise-daemon: clarify a log message 2017-10-26 17:01:17 -05:00
William Hubbs
d019f34a83 supervise-daemon: log the command line we run to spawn the child process 2017-10-26 13:57:04 -05:00
William Hubbs
bb9c481f02 supervise-daemon: log with the service name instead of "supervise-daemon" 2017-10-26 13:36:41 -05:00
William Hubbs
82da844b42 implement "unsupervised" status
The unsupervised status is to be used when a supervisor of a supervised
service dies but leaves the service daemon itself running.
2017-10-26 13:11:12 -05:00
William Hubbs
667a09983c supervise-daemon: remove child_pid from saved options during shutdown
This allows us to detect when the supervisor dies unexpectedly because
in that case child_pid will still exist.
2017-10-26 12:58:59 -05:00
William Hubbs
cf429ee359 rc_service_value_set: remove the option if NULL is the value
This allows the equivalent of "unsetting" a value for a service.
2017-10-26 12:54:37 -05:00
William Hubbs
6f3e2e2d7d supervise-daemon.sh: fix status function with no namespaces 2017-10-25 21:20:15 -05:00
Patrick McLean
35b88fb42b cgroups_cleanup: clean up shutdown signaling
- do not sleep for the full 90 seconds if processes are dead
- re-arrange the order of signals we attempt to send to the processes
2017-10-25 21:19:15 -05:00
William Hubbs
a428c325a9 add "unsupervised" status and return code 64 to supervise-daemon status function
This is to be used if the service is being supervised and the
supervisor is somehow killed.

Currently, this is very linux specific, but I will expand to other
platforms, patches are welcome.
2017-10-25 15:09:42 -05:00
William Hubbs
3219ecd608 supervise-daemon: fix build issue for >=glibc-2.26
X-Gentoo-Bug: 635334
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=635334
2017-10-24 16:47:53 -05:00
William Hubbs
0d8dc4f798 version 0.35 2017-10-24 16:46:52 -05:00
William Hubbs
f3c70bf5b5 Update ChangeLog 2017-10-24 10:42:11 -05:00
William Hubbs
f5acc66db7 rc_find_pids: ignore pids that are not in our pid namespace
X-Gentoo-Bug: 634634
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=634634
2017-10-24 10:37:37 -05:00
William Hubbs
fdce4769f2 supervise-daemon: multiple fixes
- Harden against dying by handling all signals that would terminate the
program and adding --reexec support
- factor the supervisor into its own function
- fix test for whether we are already running
2017-10-24 10:26:18 -05:00
William Hubbs
35b1996704 supervise-daemon: elevate some log messages to warnings
Prior to this change, we were logging unexpected terminations of daemons
we were supervising at the info level. This change moves the logs to
warnings.
2017-10-18 18:07:50 -05:00
William Hubbs
3c8e7ed255 version 0.34 2017-10-13 16:10:57 -05:00
William Hubbs
acaed1f910 Update ChangeLog 2017-10-13 11:29:49 -05:00
William Hubbs
91109e31d8 update news 2017-10-12 18:54:17 -05:00
William Hubbs
2b6eeea01d man: remove service(8) man page 2017-10-12 18:47:36 -05:00
William Hubbs
a15de23e57 typo fix 2017-10-10 10:56:27 -05:00
William Hubbs
efa9ba485d init.d/sysfs.in: fix reference to RC_LIBEXECDIR
The sysfs init script referred to @LIBEXECDIR@ before this change, but
it is better to refer to RC_LIBEXECDIR so that we get rid of a sed
substitution.
2017-10-09 15:54:06 -05:00
d4ddd72701 add option to make agetty startup quiet
This fixes #150
2017-10-06 14:43:59 -05:00
William Hubbs
1e9af2cd42 fix compiler warning 2017-10-05 18:31:07 -05:00
William Hubbs
3c05db74f6 remove service binary
The service binary was just a synonym for rc-service, so use rc-service
instead of service. If you want a "service" binary, it should be
something that can determine which service manager you are running and
run the appropriate service manager commands.
2017-10-02 18:09:09 -05:00
William Hubbs
edc54b0377 version 0.33 2017-10-02 16:58:59 -05:00
William Hubbs
8e53a3fa8a Update ChangeLog 2017-10-02 11:21:29 -05:00
William Hubbs
7f3b413111 use printf consistently in cgroups handling
This makes the cgroups handling consistent between cgroups v1 and v2.
Also, it fixes #167.
2017-09-29 12:51:12 -05:00
William Hubbs
1ccba05658 sh/rc-functions.sh: add need_if_exists convenience function 2017-09-22 17:22:50 -05:00
William Hubbs
c46adf1434 man/openrc-run.8: Clarify the explanation of the need dependency 2017-09-22 16:24:20 -05:00
William Hubbs
1cac8b080c ignore sigchld when shutting down the supervised process
We need to do this to skip the zombie state for the child process since
we are not easily able to wait() for it.
2017-09-18 16:59:18 -05:00
William Hubbs
b58194ef63 typo fix 2017-09-18 13:30:56 -05:00
William Hubbs
b28c0d6f66 typo fix 2017-09-18 13:03:34 -05:00
William Hubbs
3cf19b0f30 supervise-daemon: code cleanup
Clean up the process for killing an active supervisor when stopping.
2017-09-18 12:25:37 -05:00
William Hubbs
0eb47b9af3 initialize the stop schedule 2017-09-18 10:36:17 -05:00
William Hubbs
4ab60ff109 rc-schedules.c: pass the correct pid to rc_find_pids
This is for #163.
2017-09-17 14:20:05 -05:00
Jason Zaman
db4a578273 selinux: fix const qualifier warning
rc-selinux.c: In function ‘selinux_setup’:
rc-selinux.c:361:9: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
  curr_t = context_type_get(curr_con);
         ^
2017-09-17 11:41:24 -05:00
Jason Zaman
b1c3422f45 selinux: use openrc contexts path to get contexts
The minimum requirement for libselinux is now >=2.6
The refpolicy and the gentoo policy contain the
contexts since version 2.20170204-r4
2017-09-17 11:40:07 -05:00
William Hubbs
3fafd7a76e sysfs: fix cgroup hybrid mode
In hybrid mode, we should not try to mount cgroup2 if it is not
available in the kernel.

This fixes #164.
2017-09-16 17:02:52 -05:00
William Hubbs
cd5722aca5 cgroup2_find_path: use legacy mode if cgroup2 is not in the kernel
This is related to #164.
2017-09-16 16:49:22 -05:00
William Hubbs
dcb4a4d261 version 0.32 2017-09-16 13:24:52 -05:00
William Hubbs
e312e56997 Update ChangeLog 2017-09-15 15:33:24 -05:00
William Hubbs
2f60a959b4 update news file 2017-09-15 14:25:33 -05:00
William Hubbs
25b45a5a23 cgroup_cleanup: try to remove the cgroup version 2 cgroup
If we were able to kill all the processes in the cgroup, it should be
removed.
2017-09-15 14:22:34 -05:00
William Hubbs
4651b8c7e9 rc-cgroup.sh: cgroup_cleanup fix error handling
cgroup_cleanup should warn if it is unable to clean up all processes in
the control group, but it will always return success.
2017-09-15 13:42:50 -05:00
William Hubbs
50608b54ed rc-cgroup.sh: fix signal names
The "SIG" prefix on signal names passed to kill -s isn't portable.
2017-09-15 13:28:15 -05:00
William Hubbs
b0a077a35f add quiet switch to do_stop in src-schedules.c
This allows supervise-daemon to run this code without attempting to
print some status messages used by start-stop-daemon.
2017-09-14 18:24:39 -05:00
William Hubbs
6a5ca2ab36 make the procedure for killing child processes of services configurable 2017-09-14 16:17:20 -05:00
William Hubbs
2b0345165e Make cgroup_cleanup send only one sigterm and sigkill
Instead of looping and sending multiple signals to child processes in
cgroup_cleanup, we send sigterm followed by sleeping one second then
sigkill.

This brings us more in line with systemd's "control group" killmode
setting.

Also, this commit includes several shellcheck cleanups.
2017-09-14 10:55:06 -05:00
William Hubbs
8885580986 rc-cgroup.sh: move cgroup_cleanup to the end of the file 2017-09-14 10:44:52 -05:00
William Hubbs
6d7713a758 guide.md: clarify cgroups documentation
Update the documentation to reflect cgroups version 2 support.
Also, add a section on dealing with orphaned service processes.

This fixes #94.
2017-09-14 10:39:38 -05:00
William Hubbs
457f928e79 add support for control groups version 2
This is for #94.
2017-09-14 10:38:10 -05:00
William Hubbs
a71a461e45 version 0.31 2017-09-07 11:28:09 -05:00
William Hubbs
a09b8af3f9 Update ChangeLog 2017-09-06 17:31:01 -05:00
William Hubbs
382efdbfcb add quiet parameter to run_stop_schedule 2017-09-06 17:22:35 -05:00
William Hubbs
17b5cc78d3 add retry option to supervise-daemon
The --retry option for supervise-daemon defines how the supervisor will
attempt to stop the child process it is monitoring. It is defined when
the supervisor is started since stopping the supervisor just sends a
signal to the active supervisor.

This fixes #160.
2017-09-06 17:22:21 -05:00
William Hubbs
36a0ab9054 make run_stop_schedule accept a pid instead of a pid file 2017-08-25 11:36:45 -05:00
William Hubbs
27c2bd997d version 0.30 2017-08-24 11:44:32 -05:00
William Hubbs
d7938f54f2 start-stop-daemon: move --retry processing code to a shared module
This was part of start-stop-daemon; however, it needs to be shared in
order to be used by supervise-daemon.
2017-08-24 11:34:18 -05:00
William Hubbs
cfbe9c2ede move get_pid function to a shared file 2017-08-23 14:36:50 -05:00
William Hubbs
df28002b72 Update ChangeLog 2017-08-16 11:38:37 -05:00
William Hubbs
66ed8082d0 sh/openrc-run: source service script before ulimit is processed
This is needed to allow the service script author to set a default for
rc_ulimit inside the service script.
2017-08-15 17:15:14 -05:00
William Hubbs
c2d256bafb man/openrc-run.8: document fstabinfo and mountinfo
X-Gentoo-Bug: 592374
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=592374
2017-07-26 11:31:34 -05:00
William Hubbs
f48d9c33a5 man/openrc-run.8: document _pre and _post functions
Fixes https://github.com/openrc/openrc/issues/155.
2017-07-25 13:54:12 -05:00
Jason Graham
6d4e843397 fix ENT macro usage
X-Gentoo-Bug: 624796
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=624796
2017-07-24 18:24:18 -05:00
William Hubbs
0513cd3964 version 0.29 2017-07-24 17:28:54 -05:00
John R. Graham
72bb2e57de Typo fix
X-Gentoo-Bug: 624908
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=624908
2017-07-24 17:24:13 -05:00
William Hubbs
84c5da3069 Update ChangeLog 2017-07-13 17:51:02 -05:00
William Hubbs
b35099cb70 Add comment about overriding the default efivars mount in fstab to news 2017-07-13 17:49:25 -05:00
William Hubbs
3fd3bfc76d add link to efivars issue to news file 2017-07-11 15:10:16 -05:00
William Hubbs
492a6303cb Update ChangeLog 2017-07-11 14:57:13 -05:00
William Hubbs
e7807b3136 fix sysvinit compatibility for shutdown wrapper 2017-07-11 14:41:29 -05:00
William Hubbs
03a461ac0e fix sysvinit compatibility for reboot wrapper 2017-07-11 11:53:23 -05:00
William Hubbs
7e0f76e0ad fix sysvinit compatibility for poweroff wrapper 2017-07-11 11:10:46 -05:00
William Hubbs
9812ce5b8d fix halt wrapper so it is sysvinit compatible
This makes the halt wrapper sysvinit compatible. It ignores several
command line switches which are not currently implemented; however,
those can be implemented if we need to do so.

This fixes https://github.com/openrc/openrc/issues/146.
2017-07-10 18:36:24 -05:00
Adam Borowski
12f75e4167 man: fix an unclosed .Bl/.El warning
This fixes #151.
2017-07-07 17:14:31 -05:00
Adam Borowski
260368e010 man: fix missing .Pp warnings
This fixes #151.
2017-07-07 17:13:40 -05:00
William Hubbs
f87a9eec3d init.d/sysfs: mount efivars read only
This fixes #134.
2017-06-13 13:19:36 -05:00
William Hubbs
1e837d596e fix argument parsing for the sysvinit shutdown wrapper
This fixes #140.
2017-06-12 17:58:18 -05:00
William Hubbs
dcc686e42b scripts/shutdown: fix arguments to be sysvinit shutdown compatible
This fixes #140.
2017-06-12 12:24:18 -05:00
William Hubbs
2f81c100af Fix link to shutdown for MKSYSVINIT=yes 2017-06-12 12:12:50 -05:00
Nuno Silva
a511a48d77 init.d/hostname: fix default parameter syntax
The syntax for expanding a variable with a default value is
	${parameter:-word}
not
	${parameter-word}
although the latter still works for a reason I could not explain.

This fixes #143.
2017-06-12 10:44:27 -05:00
Nuno Silva
1e5322e5c5 init.d/hostname: fix indentation
This is for #143.
2017-06-12 10:43:55 -05:00
udeved
199a210d2f scripts/Makefile: make symlinks absolute instead of relative
This closes #142.
2017-06-12 10:39:44 -05:00
udeved
5b7667af32 scripts/Makefile: respect SBINDIR with MKSYSVINIT
This is for #142.
2017-06-12 10:38:47 -05:00
Jory A. Pratt
11243f85b6 kill_all: include limits.h for PATH_MAX 2017-06-08 10:04:19 -05:00
William Hubbs
3c40826d34 version 0.28 2017-06-08 08:45:16 -05:00
William Hubbs
560d874d2f fix compile issue for musl 2017-06-08 08:43:42 -05:00
William Hubbs
e84366fd23 Update ChangeLog 2017-06-07 12:03:52 -05:00
William Hubbs
caacedc0a8 man: update openrc-shutdown man page
Add the new wtmp options and fix some cross references.
2017-06-07 11:28:01 -05:00
William Hubbs
84d140a1f6 scripts/shutdown: pass --single to openrc-shutdown
Sysvinit shutdown has a default of single user mode, but openrc-shutdown
makes you choose a default action. Because of this, the shutdown wrapper
needs to pass --single to openrc-shutdown.
2017-06-07 10:59:42 -05:00
William Hubbs
ee886c4482 openrc-shutdown: add --single option and clean up option processing 2017-06-05 15:49:22 -05:00
William Hubbs
1801561c2d init.d/bootmisc: use openrc-shutdown instead of halt to write halt record
This fixes #139 and fixes #128.
and fixes #124.
2017-06-05 16:52:50 +00:00
William Hubbs
7689106aa1 add support for writing reboot and shutdown records to wtmp 2017-06-04 20:56:03 -05:00
William Hubbs
1564e155b7 openrc-init: add optional sysvinit compatibility 2017-05-31 18:07:02 -05:00
William Hubbs
44bac3c379 Change killprocs to use kill_all instead of killall5
X-Gentoo-Bug:376977
X-Gentoo-Bug-URL:https://bugs.gentoo.org/show_bug.cgi?id=376977
2017-05-30 18:48:33 -05:00
Sergei Trofimovich
0ddee9b7d2 openrc-init: fix buffer overflow in init.ctl
How to reproduce 1-byte overflow:

```
$ FEATURES=-test CFLAGS="-fsanitize=address -O0 -ggdb3" emerge -1 openrc

=================================================================
==1==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff0efd8710
    at pc 0x000000402076 bp 0x7fff0efd7d50 sp 0x7fff0efd7d40
WRITE of size 1 at 0x7fff0efd8710 thread T0
    #0 0x402075  (/sbin/openrc-init+0x402075)
    #1 0x3cf6e2070f in __libc_start_main (/lib64/libc.so.6+0x3cf6e2070f)
    #2 0x4013b8  (/sbin/openrc-init+0x4013b8)

Address 0x7fff0efd8710 is located in stack of thread T0 at offset 2432 in frame
    #0 0x401cfb  (/sbin/openrc-init+0x401cfb)

  This frame has 3 object(s):
    [32, 160) 'signals'
    [192, 344) 'sa'
    [384, 2432) 'buf' <== Memory access at offset 2432 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow ??:0 ??
```

The problem here is in the code handling reads from 'init.ctl':

```
int main(int argc, char **argv) {
...
    char buf[2048];
    for (;;) {
        /* This will block until a command is sent down the pipe... */
        fifo = fopen(RC_INIT_FIFO, "r");
        count = fread(buf, 1, 2048, fifo);
        buf[count] = 0;
        ...
    }
```

`buf[count] = 0;` writes outside the buffer when `fread()` returns non-truncated read.

This fixes #138.
2017-05-30 16:21:23 -05:00
Sergei Trofimovich
688566c535 mk/cc.mk: make implicit function declarations fatal (#136)
Avoids issues with missing prototypes causing truncation of pointers.

Signed-off-by: Sergei Trofimovich <slyfox@gentoo.org>
2017-05-30 03:51:42 -04:00
Sergei Trofimovich
7185e242ff rc-logger.c: fix crash on fclose(NULL) (#137)
Only close the log if we successfully opened it.

Reported-by: Brian Evans <grknight@gentoo.org>
Tested-by: Brian Evans <grknight@gentoo.org>
Signed-off-by: Sergei Trofimovich <slyfox@gentoo.org>
2017-05-30 03:47:55 -04:00
William Hubbs
ec27299f4b typo fix
X-Gentoo-Bug: 618888
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=618888
2017-05-22 12:52:58 -05:00
William Hubbs
1ece16bfcd openrc-shutdown: add dry-run option 2017-05-22 12:42:37 -05:00
William Hubbs
0cfd0dd9ef openrc-shutdown: move to single user mode by default
To be more compatible with sysvinit, move to single user mode if no
options are specified on the command line.
2017-05-22 12:15:15 -05:00
William Hubbs
a77ee2e941 init: add ability to switch to single user mode 2017-05-22 11:29:23 -05:00
William Hubbs
49b8a573a1 add kill_all helper
This is similar to the sysvinit killall5 utility.  It should only be used
in service scripts, so it will not be installed in the path.

This closes #129.
2017-05-19 18:13:39 -05:00
William Hubbs
a2055af900 rc_status: calculate time differences in time_t and display seconds in uptime 2017-05-15 18:55:47 -05:00
William Hubbs
cbf96967f1 supervise-daemon: save start time and respawn count before dropping privs 2017-05-12 17:15:55 -05:00
William Hubbs
f1013037b4 version 0.27 2017-05-12 17:14:15 -05:00
114 changed files with 5059 additions and 2472 deletions

View File

@@ -1,4 +1,5 @@
# Using Busybox as your Default Shell with OpenRC
Using Busybox as your Default Shell with OpenRC
===============================================
If you have/bin/sh linked to busybox, you need to be aware of several
incompatibilities between busybox's applets and the standalone

2723
ChangeLog

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,12 @@
# Features Scheduled for Removal
Features Scheduled for Removal
==============================
The following is a list of files and features that are going to be removed in
the source tree. Every entry should contain what exactly is going away, why it
is happening, and who is going to be doing the work. When the feature is
removed, it should also be removed from this file.
## Service pause action
# Service pause action
When: 1.0
@@ -13,7 +14,7 @@ Why: The same affect can be obtained with the --nodeps option to stop.
Who:
## start-stop-daemon options --startas, --chuid , --oknodo
# start-stop-daemon options --startas, --chuid , --oknodo
When: 1.0
@@ -25,7 +26,7 @@ Why: Obsolete or replaced by other options.
Who:
## runscript and rc symbolic links
# runscript and rc symbolic links
When: 1.0
@@ -34,7 +35,7 @@ Why: Deprecated in favor of openrc-run and openrc due to naming
Who:
## support for the opts variable in service scripts
# support for the opts variable in service scripts
When: 1.0
@@ -43,7 +44,7 @@ Why: Deprecated in favor of extra_commands, extra_started_commands
Who:
## support for local_start and local_stop
# support for local_start and local_stop
When: 1.0
@@ -51,7 +52,7 @@ Why: Deprecated in favor of executable scripts in @SYSCONFDIR@/local.d
Who:
## the mtab service script
# the mtab service script
When: force /etc/mtab to link to /proc/self/mounts in 1.0, remove
service in 2.0
@@ -61,13 +62,13 @@ Why: /etc/mtab should be a symbolic link to /proc/self/mounts on modern
Who:
## C API Functions in rc.h
# C API Functions in rc.h
If you have a c program that links to librc and uses functions from
there, this section will list API functions which are deprecated and
will be removed along with the reason they are being removed.
### rc_getline()
## rc_getline()
When: 1.0

View File

@@ -1,4 +1,5 @@
# OpenRC History
OpenRC History
==============
This history of OpenRC was written by Daniel Robbins, Roy Marples, William
Hubbs and others.

View File

@@ -15,12 +15,24 @@ include ${TOP}/Makefile.inc
SUBDIR= conf.d etc init.d local.d man scripts sh src support sysctl.d
# Build bash completion or not
MKBASHCOMP?= no
ifeq (${MKBASHCOMP},yes)
SUBDIR+= bash-completion
endif
# Build pkgconfig or not
MKPKGCONFIG?= yes
ifeq (${MKPKGCONFIG},yes)
SUBDIR+= pkgconfig
endif
# Build zsh completion or not
MKZSHCOMP?= no
ifeq (${MKZSHCOMP},yes)
SUBDIR+= zsh-completion
endif
# We need to ensure that runlevels is done last
SUBDIR+= runlevels

View File

@@ -1,3 +1,3 @@
NAME= openrc
VERSION= 0.26
VERSION= 0.35
PKG= ${NAME}-${VERSION}

64
NEWS.md
View File

@@ -1,8 +1,70 @@
# OpenRC NEWS
OpenRC NEWS
===========
This file will contain a list of notable changes for each release. Note
the information in this file is in reverse order.
## OpenRC 0.35
In this version, the cgroups mounting logic has been moved from the
sysfs service to the cgroups service. This was done so cgroups can be
mounted inside an lxc/lxd container without using the other parts of the
sysfs service.
?As a result of this change, if you are upgrading, you need to add
cgroups to your sysinit runlevel by running the following command as
root:
```
# rc-update add cgroups sysinit
```
For more information, see the following issue:
https://github.com/openrc/openrc/issues/187
Consider this your second notification with regard to /etc/mtab being a
file instead of a symbolic link.
In this version, the mtab service will complain loudly if you have
mtab_is_file set to yes and recommend that you change this to no and
restart the mtab service to migrate /etc/mtab to a symbolic link.
If there is a valid technical reason to keep /etc/mtab as a flat file
instead of a symbolic link to /proc/self/mounts, we are interested and
we will keep the support in that case. Please open an issue and let us
know however. Otherwise, consider this your final notice that the mtab
service will be removed in the future.
## OpenRC 0.33
This version removes the "service" binary which was just a copy of
"rc-service" provided for compatibility.
If you still need the "service" binary, as opposed to "rc-service", it is
recommended that you use something like Debian's init-system-helpers.
Otherwise, just use "rc-service" in place of "service".
## OpenRC 0.31
This version adds support for Control Groups version 2, which is
considered stable as of Linux-4.13. Please see /etc/rc.conf for
documentation on how to configure control groups.
## OpenRC-0.28
This version mounts efivars read only due to concerns about changes in
this file system making systems unbootable. If you need to change something
in this path, you will need to re-mount it read-write, make the change
and re-mount it read-only.
Also, you can override this behavior by adding a line for efivars to
fstab if you want efivars mounted read-write.
For more information on this issue, see the following url:
https://github.com/openrc/openrc/issues/134
## OpenRC-0.25
This version contains an OpenRC-specific implementation of init for

View File

@@ -1,4 +1,5 @@
# OpenRC README
OpenRC README
=============
OpenRC is a dependency-based init system that works with the
system-provided init program, normally `/sbin/init`. Currently, it does
@@ -22,14 +23,17 @@ below arguments to the make command
PROGLDFLAGS=-static
LIBNAME=lib64
DESTDIR=/tmp/openrc-image
MKBASHCOMP=no
MKNET=no
MKPAM=pam
MKPREFIX=yes
MKPKGCONFIG=no
MKSELINUX=yes
MKSTATICLIBS=no
MKSYSVINIT=yes
MKTERMCAP=ncurses
MKTERMCAP=termcap
MKZSHCOMP=no
PKG_PREFIX=/usr/pkg
LOCAL_PREFIX=/usr/local
PREFIX=/usr/local

View File

@@ -1,4 +1,5 @@
# OpenRC Style Guide
OpenRC Coding Style Guide
=========================
This is the openrc style manual. It governs the coding style of all code
in this repository. Follow it. Contact openrc@gentoo.org for any questions

View File

@@ -1,4 +1,5 @@
# Setting up the agetty service in OpenRC
Setting up the agetty service in OpenRC
=================================xxxxxx
The agetty service is an OpenRC specific way to monitor and respawn a
getty, using agetty, on Linux. To use this method, make sure you aren't

11
bash-completion/Makefile Normal file
View File

@@ -0,0 +1,11 @@
DIR= ${BASHCOMPDIR}
CONF= openrc \
openrc-service-script \
rc-service \
rc-status \
rc-update \
MK= ../mk
include ${MK}/os.mk
include ${MK}/scripts.mk

24
bash-completion/openrc Normal file
View File

@@ -0,0 +1,24 @@
# Copyright (c) 2017 The OpenRC Authors.
# See the Authors file at the top-level directory of this distribution and
# https://github.com/OpenRC/openrc/blob/master/AUTHORS
#
# This file is part of OpenRC. It is subject to the license terms in
# the LICENSE file found in the top-level directory of this
# distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
# This file may not be copied, modified, propagated, or distributed
# except according to the terms contained in the LICENSE file.
#
# openrc completion command
#
_openrc()
{
local cur
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ ${#COMP_WORDS[*]} -le 2 ]]; then
COMPREPLY=($(compgen -W "$(rc-status --list)" -- $cur))
fi
return 0
} &&
complete -F _openrc openrc

View File

@@ -0,0 +1,29 @@
# Copyright (c) 2017 The OpenRC Authors.
# See the Authors file at the top-level directory of this distribution and
# https://github.com/OpenRC/openrc/blob/master/AUTHORS
#
# This file is part of OpenRC. It is subject to the license terms in
# the LICENSE file found in the top-level directory of this
# distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
# This file may not be copied, modified, propagated, or distributed
# except according to the terms contained in the LICENSE file.
_openrc_service_script()
{
local script="${COMP_WORDS[0]}"
local cur="${COMP_WORDS[$COMP_CWORD]}"
if [[ ( -f "${script}" || -h "${script}" ) && -r "${script}" ]] \
&& [[ "$(head -n 1 "${script}")" =~ \#\!.*/openrc-run ]]
then
[[ $COMP_CWORD -gt 1 ]] && return 1
COMPREPLY=($(opts="start stop status restart pause zap ineed needsme iuse usesme broken"; \
eval "$(grep '^opts=' "${script}")"; echo "${opts}"))
[[ -n "$COMPREPLY" ]] || COMPREPLY=(start stop restart zap)
COMPREPLY=($(compgen -W "${COMPREPLY[*]}" -- "${cur}"))
else
COMPREPLY=($(compgen -o default -- "${cur}"))
fi
return 0
}
complete -F _openrc_service_script */etc/init.d/*

113
bash-completion/rc-service Normal file
View File

@@ -0,0 +1,113 @@
# Copyright (c) 2017 The OpenRC Authors.
# See the Authors file at the top-level directory of this distribution and
# https://github.com/OpenRC/openrc/blob/master/AUTHORS
#
# This file is part of OpenRC. It is subject to the license terms in
# the LICENSE file found in the top-level directory of this
# distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
# This file may not be copied, modified, propagated, or distributed
# except according to the terms contained in the LICENSE file.
#
# rc-service completion command
#
_rc_service()
{
local cur prev numwords opts
local words i x filename
local action actionpos
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
numwords=${#COMP_WORDS[*]}
if [[ ${prev} == '>' || ${prev} == '<' ]] ; then
COMPREPLY=($(compgen -f -- ${cur}))
return 0
fi
# find action
for x in ${COMP_LINE} ; do
if [[ ${x} =~ --(list|exists|resolve) ]] || [[ ${x} =~ -(l|e|r) ]]
then
action=${x}
break
fi
done
if [[ -n ${action} ]]; then
for ((i = 0; i < ${numwords}; i++ )); do
if [[ ${COMP_WORDS[${i}]} == "${action}" ]]; then
actionpos=${i}
break
fi
done
for ((i = 1; i < ${numwords}; i++ )); do
if [[ ! ${COMP_WORDS[$i]} == -* ]]; then
break
fi
done
fi
if [[ ${COMP_CWORD} -eq 3 ]]; then
return 1
fi
# check if an option was typed
if [[ ${cur} == -* ]]; then
if [[ ${cur} == --* ]]; then
opts="--list --exists --resolve"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
elif [[ ${cur} == -* ]]; then
opts="-l -e -r"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
# NOTE: This slows things down!
# (Adapted from bash_completion by Ian Macdonald <ian@caliban.org>)
# This removes any options from the list of completions that have
# already been specified on the command line.
COMPREPLY=($(echo "${COMP_WORDS[@]}" | \
(while read -d ' ' i; do
[[ -z ${i} ]] && continue
# flatten array with spaces on either side,
# otherwise we cannot grep on word boundaries of
# first and last word
COMPREPLY=" ${COMPREPLY[@]} "
# remove word from list of completions
COMPREPLY=(${COMPREPLY/ ${i%% *} / })
done
echo ${COMPREPLY[@]})))
return 0
else
# no option was typed
if [[ ${COMP_CWORD} -eq 1 ]]; then # if first word typed
words="$(rc-service --list | grep ^${cur})" # complete for init scripts
COMPREPLY=($(for i in ${words} ; do \
[[ ${i} == ${cur}* ]] && echo ${i} ; \
done))
return 0
elif [[ ${COMP_CWORD} -eq 2 ]] && [[ ${prev} != -* ]]; then # if second word typed and we didn't type in a function
filename=$(rc-service --resolve ${prev})
opts=$(cat ${filename} | grep "^\w*()" | sed "s/().*$//") # Greps the functions included in the init script
if [[ "x${opts}" == "x" ]] ; then # if no options found loosen the grep algorhythm
opts=$(cat ${filename} | grep "\w*()" | sed "s/().*$//")
fi
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
fi
if [[ ${action} == '--exists' ]] || [[ ${action} == '-e' ]] || \
[[ ${action} == '--resolve' ]] || [[ ${action} == '-r' ]]; then
words="$(rc-service --list | grep ^${cur})"
COMPREPLY=($(for i in ${words} ; do \
[[ ${i} == ${cur}* ]] && echo ${i} ; \
done))
return 0
fi
return 0
} &&
complete -F _rc_service rc-service

31
bash-completion/rc-status Normal file
View File

@@ -0,0 +1,31 @@
# Copyright (c) 2017 The OpenRC Authors.
# See the Authors file at the top-level directory of this distribution and
# https://github.com/OpenRC/openrc/blob/master/AUTHORS
#
# This file is part of OpenRC. It is subject to the license terms in
# the LICENSE file found in the top-level directory of this
# distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
# This file may not be copied, modified, propagated, or distributed
# except according to the terms contained in the LICENSE file.
#
# rc-status completion command
#
_rcstatus()
{
local cur
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ $COMP_CWORD -eq 1 ]]; then
if [[ "${cur}" == --* ]]; then
COMPREPLY=($(compgen -W '--all --list --unused' -- ${cur}))
elif [[ "${cur}" == -* ]]; then
COMPREPLY=($(compgen -W '-a -l -u' -- ${cur}))
else
COMPREPLY=($(compgen -W "$(rc-status --list)" -- ${cur}))
fi
else
unset COMPREPLY
fi
return 0
} &&
complete -F _rcstatus rc-status

42
bash-completion/rc-update Normal file
View File

@@ -0,0 +1,42 @@
# Copyright (c) 2017 The OpenRC Authors.
# See the Authors file at the top-level directory of this distribution and
# https://github.com/OpenRC/openrc/blob/master/AUTHORS
#
# This file is part of OpenRC. It is subject to the license terms in
# the LICENSE file found in the top-level directory of this
# distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
# This file may not be copied, modified, propagated, or distributed
# except according to the terms contained in the LICENSE file.
#
# rc-update completion command
#
_rc_update()
{
local cur show
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ $COMP_CWORD -eq 1 ]]; then
if [[ "${cur}" == -* ]]; then
COMPREPLY=($(compgen -W '-a -d -s' -- ${cur}))
else
COMPREPLY=($(compgen -W 'add del show' ${cur}))
fi
else
if [[ "${COMP_WORDS[1]}" == "show" ]] || [[ "${COMP_WORDS[1]}" == "-s" ]]; then
show="TRUE"
fi
if ([[ $COMP_CWORD -eq 3 ]] && [[ -z "$show" ]]) || \
([[ $COMP_CWORD -eq 2 ]] && [[ -n "$show" ]]); then
COMPREPLY=($(compgen -W "$(rc-status --list)" -- $cur))
elif [[ $COMP_CWORD -eq 2 ]]; then
COMPREPLY=($(compgen -W "$(rc-service --list)" $cur))
elif [[ ${#COMP_WORDS[*]} -gt 2 ]] ; then
COMPREPLY=($(compgen -W "$(rc-status --list)" -- $cur))
else
unset COMPREPLY
fi
fi
return 0
} &&
complete -F _rc_update rc-update

View File

@@ -1,8 +1,11 @@
# make agetty quiet
#quiet="yes"
# Set the baud rate of the terminal line
#baud=""
# set the terminal type
#termtype="linux"
#term_type="linux"
# extra options to pass to agetty for this port
#agetty_options=""

View File

@@ -1,3 +1,6 @@
# If you wish to pass any options to killall5 during shutdown,
# If you wish to pass any options to kill_all during shutdown,
# you should do so here.
#
# The setting is called killall5_opts because the options here are meant
# to be identical to those you could pass to killall5.
killall5_opts=""

View File

@@ -3,7 +3,7 @@
#no_umounts="/dir1:/var/dir2"
#
# Mark certain mount points as critical.
# This contains aspace separated list of mount points which should be
# This contains a space separated list of mount points which should be
# considered critical. If one of these mount points cannot be mounted,
# localmount will fail.
# By default, this is empty.

View File

@@ -1,5 +1,13 @@
# As far as we are aware, there are no modern linux tools or use cases
# which require /etc/mtab to be a separate file from /proc/self/mounts,
# so this setting should be commented out.
# If it is set to yes, please comment it out and run this command:
# # rc-service mtab restart
# In the future, the mtab service will be removed since we are not aware
# of any need to manipulate /etc/mtab as a separate file from
# /proc/self/mounts.
# If you have a technical reason we should keep this support, please
# open an issue at https://github.com/openrc/openrc/issues and let us
# know about your situation.
# This setting controls whether /etc/mtab is a file or symbolic link.
# Most of the time, you shouldn't touch this. However, if the default
# breaks your system in some way, please see the NEWS.md file that comes
# with OpenRC for the actions to take.
# mtab_is_file=no

View File

@@ -191,13 +191,48 @@ rc_tty_number=12
##############################################################################
# LINUX CGROUPS RESOURCE MANAGEMENT
# If you have cgroups turned on in your kernel, this switch controls
# whether or not a group for each controller is mounted under
# /sys/fs/cgroup.
# None of the other options in this section work if this is set to "NO".
# This sets the mode used to mount cgroups.
# "hybrid" mounts cgroups version 2 on /sys/fs/cgroup/unified and
# cgroups version 1 on /sys/fs/cgroup.
# "legacy" mounts cgroups version 1 on /sys/fs/cgroup
# "unified" mounts cgroups version 2 on /sys/fs/cgroup
#rc_cgroup_mode="hybrid"
# This is a list of controllers which should be enabled for cgroups version 2.
# If hybrid mode is being used, controllers listed here will not be
# available for cgroups version 1.
# This is a global setting.
#rc_cgroup_controllers=""
# This variable contains the cgroups version 2 settings for your services.
# If this is set in this file, the settings will apply to all services.
# If you want different settings for each service, place the settings in
# /etc/conf.d/foo for service foo.
# The format is to specify the setting and value followed by a newline.
# Multiple settings and values can be specified.
# For example, you would use this to set the maximum memory and maximum
# number of pids for a service.
#rc_cgroup_settings="
#memory.max 10485760
#pids.max max
#"
#
# For more information about the adjustments that can be made with
# cgroups version 2, see Documentation/cgroups-v2.txt in the linux kernel
# source tree.
#rc_cgroup_settings=""
# This switch controls whether or not cgroups version 1 controllers are
# individually mounted under
# /sys/fs/cgroup in hybrid or legacy mode.
#rc_controller_cgroups="YES"
# The following settings allow you to set up values for the cgroup
# The following setting turns on the memory.use_hierarchy setting in the
# root memory cgroup for cgroups v1.
# It must be set to yes in this file if you want this functionality.
#rc_cggroup_memory_use_hierarchy="NO"
# The following settings allow you to set up values for the cgroups version 1
# controllers for your services.
# They can be set in this file;, however, if you do this, the settings
# will apply to all of your services.
@@ -211,8 +246,9 @@ rc_tty_number=12
# cpu.shares 512
# "
#
#For more information about the adjustments that can be made with
#cgroups, see Documentation/cgroups/* in the linux kernel source tree.
# For more information about the adjustments that can be made with
# cgroups version 1, see Documentation/cgroups-v1/* in the linux kernel
# source tree.
# Set the blkio controller settings for this service.
#rc_cgroup_blkio=""
@@ -246,10 +282,33 @@ rc_tty_number=12
# Set this to YES if you want all of the processes in a service's cgroup
# killed when the service is stopped or restarted.
# This should not be set globally because it kills all of the service's
# child processes, and most of the time this is undesirable. Please set
# it in /etc/conf.d/<service>.
# Be aware that setting this to yes means all of a service's
# child processes will be killed. Keep this in mind if you set this to
# yes here instead of for the individual services in
# /etc/conf.d/<service>.
# To perform this cleanup manually for a stopped service, you can
# execute cgroup_cleanup with /etc/init.d/<service> cgroup_cleanup or
# rc-service <service> cgroup_cleanup.
# The process followed in this cleanup is the following:
# 1. send stopsig (sigterm if it isn't set) to all processes left in the
# cgroup immediately followed by sigcont.
# 2. Send sighup to all processes in the cgroup if rc_send_sighup is
# yes.
# 3. delay for rc_timeout_stopsec seconds.
# 4. send sigkill to all processes in the cgroup unless disabled by
# setting rc_send_sigkill to no.
# rc_cgroup_cleanup="NO"
# If this is yes, we will send sighup to the processes in the cgroup
# immediately after stopsig and sigcont.
#rc_send_sighup="NO"
# This is the amount of time in seconds that we delay after sending sigcont
# and optionally sighup, before we optionally send sigkill to all
# processes in the # cgroup.
# The default is 90 seconds.
#rc_timeout_stopsec="90"
# If this is set to no, we do not send sigkill to all processes in the
# cgroup.
#rc_send_sigkill="YES"

1
init.d/.gitignore vendored
View File

@@ -1,5 +1,6 @@
agetty
binfmt
cgroups
modules-load
bootmisc
fsck

View File

@@ -21,9 +21,10 @@ SRCS-FreeBSD= hostid.in modules.in moused.in newsyslog.in pf.in rarpd.in \
SRCS-FreeBSD+= adjkerntz.in devd.in dumpon.in encswap.in ipfw.in \
modules-load.in mixer.in nscd.in powerd.in syscons.in
SRCS-Linux= agetty.in binfmt.in devfs.in dmesg.in hwclock.in consolefont.in \
keymaps.in killprocs.in modules.in modules-load.in mount-ro.in mtab.in \
numlock.in procfs.in net-online.in sysfs.in termencoding.in
SRCS-Linux= agetty.in binfmt.in devfs.in cgroups.in dmesg.in hwclock.in \
consolefont.in keymaps.in killprocs.in modules.in modules-load.in \
mount-ro.in mtab.in numlock.in procfs.in net-online.in sysfs.in \
termencoding.in
# Generic BSD scripts
SRCS-NetBSD= hostid.in moused.in newsyslog.in pf.in rarpd.in rc-enabled.in \

View File

@@ -14,8 +14,9 @@ supervisor=supervise-daemon
port="${RC_SVCNAME#*.}"
term_type="${term_type:-linux}"
command=/sbin/agetty
command_args_foreground="${agetty_options} ${port} ${baud} ${termtype}"
command_args_foreground="${agetty_options} ${port} ${baud} ${term_type}"
pidfile="/run/${RC_SVCNAME}.pid"
export EINFO_QUIET="${quiet:-yes}"
depend() {
after local

View File

@@ -241,7 +241,7 @@ stop()
{
# Write a halt record if we're shutting down
if [ "$RC_RUNLEVEL" = shutdown ]; then
[ "$RC_UNAME" = Linux ] && halt -w
[ "$RC_UNAME" = Linux ] && openrc-shutdown -w
if [ "$RC_SYS" = OPENVZ ]; then
yesno $RC_REBOOT && printf "" >/reboot
fi

149
init.d/cgroups.in Normal file
View File

@@ -0,0 +1,149 @@
#!@SBINDIR@/openrc-run
# Copyright (c) 2017 The OpenRC Authors.
# See the Authors file at the top-level directory of this distribution and
# https://github.com/OpenRC/openrc/blob/master/AUTHORS
#
# This file is part of OpenRC. It is subject to the license terms in
# the LICENSE file found in the top-level directory of this
# distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
# This file may not be copied, modified, propagated, or distributed
# except according to the terms contained in the LICENSE file.
description="Mount the control groups."
cgroup_opts=nodev,noexec,nosuid
depend()
{
keyword -docker -prefix -systemd-nspawn -vserver
after sysfs
}
cgroup1_base()
{
grep -qw cgroup /proc/filesystems || return 0
if ! mountinfo -q /sys/fs/cgroup; then
ebegin "Mounting cgroup filesystem"
local opts="${cgroup_opts},mode=755,size=${rc_cgroupsize:-10m}"
mount -n -t tmpfs -o "${opts}" cgroup_root /sys/fs/cgroup
eend $?
fi
if ! mountinfo -q /sys/fs/cgroup/openrc; then
local agent="${RC_LIBEXECDIR}/sh/cgroup-release-agent.sh"
mkdir /sys/fs/cgroup/openrc
mount -n -t cgroup \
-o none,${cgroup_opts},name=openrc,release_agent="$agent" \
openrc /sys/fs/cgroup/openrc
printf 1 > /sys/fs/cgroup/openrc/notify_on_release
fi
return 0
}
cgroup1_controllers()
{
yesno "${rc_controller_cgroups:-YES}" && [ -e /proc/cgroups ] &&
grep -qw cgroup /proc/filesystems || return 0
while read -r name _ _ enabled _; do
case "${enabled}" in
1) mountinfo -q "/sys/fs/cgroup/${name}" && continue
local x
for x in $rc_cgroup_controllers; do
[ "${name}" = "blkio" ] && [ "${x}" = "io" ] &&
continue 2
[ "${name}" = "${x}" ] &&
continue 2
done
mkdir "/sys/fs/cgroup/${name}"
mount -n -t cgroup -o "${cgroup_opts},${name}" \
"${name}" "/sys/fs/cgroup/${name}"
yesno "${rc_cgroup_memory_use_hierarchy:-no}" &&
[ "${name}" = memory ] &&
echo 1 > /sys/fs/cgroup/memory/memory.use_hierarchy
;;
esac
done < /proc/cgroups
return 0
}
cgroup2_base()
{
grep -qw cgroup2 /proc/filesystems || return 0
local base
base="$(cgroup2_find_path)"
mkdir -p "${base}"
mount -t cgroup2 none -o "${cgroup_opts},nsdelegate" "${base}" 2> /dev/null ||
mount -t cgroup2 none -o "${cgroup_opts}" "${base}"
return 0
}
cgroup2_controllers()
{
grep -qw cgroup2 /proc/filesystems || return 0
local active cgroup_path x y
cgroup_path="$(cgroup2_find_path)"
[ -z "${cgroup_path}" ] && return 0
[ -e "${cgroup_path}/cgroup.controllers" ] &&
read -r active < "${cgroup_path}/cgroup.controllers"
for x in ${rc_cgroup_controllers}; do
for y in ${active}; do
[ "$x" = "$y" ] &&
[ -e "${cgroup_path}/cgroup.subtree_control" ]&&
echo "+${x}" > "${cgroup_path}/cgroup.subtree_control"
done
done
return 0
}
cgroups_hybrid()
{
cgroup1_base
cgroup2_base
cgroup2_controllers
cgroup1_controllers
return 0
}
cgroups_legacy()
{
cgroup1_base
cgroup1_controllers
return 0
}
cgroups_unified()
{
cgroup2_base
cgroup2_controllers
return 0
}
mount_cgroups()
{
case "${rc_cgroup_mode:-hybrid}" in
hybrid) cgroups_hybrid ;;
legacy) cgroups_legacy ;;
unified) cgroups_unified ;;
esac
return 0
}
restorecon_cgroups()
{
if [ -x /sbin/restorecon ]; then
ebegin "Restoring SELinux contexts in /sys/fs/cgroup"
restorecon -rF /sys/fs/cgroup >/dev/null 2>&1
eend $?
fi
return 0
}
start()
{
# set up kernel support for cgroups
if [ -d /sys/fs/cgroup ]; then
mount_cgroups
restorecon_cgroups
fi
return 0
}

View File

@@ -20,9 +20,12 @@ depend()
mount_dev()
{
local action=--mount devfstype msg=Mounting
local action conf_d_dir devfstype msg mountopts
action=--mount
conf_d_dir="${RC_SERVICE%/*/*}/conf.d"
msg=Mounting
# Some devices require exec, Bug #92921
local mountopts="exec,nosuid,mode=0755"
mountopts="exec,nosuid,mode=0755"
if yesno ${skip_mount_dev:-no} ; then
einfo "/dev will not be mounted due to user request"
return 0
@@ -33,7 +36,7 @@ mount_dev()
msg=Remounting
fi
if fstabinfo -q /dev; then
ebegin "$msg /dev according to @SYSCONFDIR@/fstab"
ebegin "$msg /dev according to fstab"
fstabinfo -q $action /dev
eend $?
return 0
@@ -54,7 +57,7 @@ mount_dev()
ewarn "is no entry for /dev in fstab."
ewarn "This means /dev will not be mounted."
ewarn "To avoid this message, set CONFIG_DEVTMPFS or CONFIG_TMPFS to y"
ewarn "in your kernel configuration or see @SYSCONFDIR@/conf.d/devfs"
ewarn "in your kernel configuration or see ${conf_d_dir}/${RC_SVCNAME}"
fi
return 0
}

View File

@@ -11,7 +11,8 @@
description="Sets the hostname of the machine."
depend() {
depend()
{
after clock
keyword -docker -lxc -prefix -systemd-nspawn
}
@@ -19,13 +20,13 @@ depend() {
start()
{
local h source x
if [ -s @SYSCONFDIR@/hostname ] && [ -r @SYSCONFDIR@/hostname ]; then
read h x <@SYSCONFDIR@/hostname
source=" from @SYSCONFDIR@/hostname"
if [ -s /etc/hostname ] && [ -r /etc/hostname ]; then
read h x </etc/hostname
source="from /etc/hostname"
else
# HOSTNAME variable used to be defined in caps in conf.d/hostname.
# It is also a magic variable in bash.
h=${hostname-${HOSTNAME}} # checkbashisms: false positive
h=${hostname:-${HOSTNAME}} # checkbashisms: false positive (HOSTNAME var)
fi
if [ -z "$h" ]; then
einfo "Using default system hostname"

View File

@@ -105,7 +105,7 @@ start()
done
[ -n "$modname" ] &&
ewarn "The $modname module needs to be configured in" \
"@SYSCONFDIR@/conf.d/modules or built in."
"${RC_SERVICE%/*/*}/conf.d/modules or built in."
fi
fi

View File

@@ -19,9 +19,9 @@ depend()
start()
{
ebegin "Terminating remaining processes"
killall5 -15 ${killall5_opts}
kill_all 15 ${killall5_opts}
eend 0
ebegin "Killing remaining processes"
killall5 -9 ${killall5_opts}
kill_all 9 ${killall5_opts}
eend 0
}

View File

@@ -9,7 +9,10 @@
# This file may not be copied, modified, propagated, or distributed
# except according to the terms contained in the LICENSE file.
description="Executes user programs in @SYSCONFDIR@/local.d"
conf_d_dir="${RC_SERVICE%/*/*}/conf.d"
local_d_dir="${RC_SERVICE%/*/*}/local.d"
description="Executes user programs in ${local_d_dir}"
depend()
{
@@ -19,12 +22,12 @@ depend()
start()
{
ebegin "Starting local"
local file has_errors=0 redirect retval
local file has_errors redirect retval
has_errors=0
yesno $rc_verbose || redirect='> /dev/null 2>&1'
ebegin "Starting local"
eindent
for file in @SYSCONFDIR@/local.d/*.start; do
for file in "${local_d_dir}"/*.start; do
if [ -x "${file}" ]; then
vebegin "Executing \"${file}\""
eval "${file}" $redirect
@@ -38,32 +41,32 @@ start()
eoutdent
if command -v local_start >/dev/null 2>&1; then
ewarn "\"@SYSCONFDIR@/conf.d/local\" should be removed."
ewarn "\"${conf_d_dir}/local\" should be removed."
ewarn "Please move the code from the local_start function"
ewarn "to executable scripts with an .start extension"
ewarn "in \"@SYSCONFDIR@/local.d\""
ewarn "in \"${local_d_dir}\""
local_start
fi
eend ${has_errors}
# We have to end with a zero exit code, because a failed execution
# of an executable @SYSCONFDIR@/local.d/*.start file shouldn't result in
# of an executable ${local_d_dir}/*.start file shouldn't result in
# marking the local service as failed. Otherwise we are unable to
# execute any executable @SYSCONFDIR@/local.d/*.stop file, because a failed
# execute any executable ${local_d_dir}/*.stop file, because a failed
# marked service cannot be stopped (and the stop function would
# actually call the executable @SYSCONFDIR@/local.d/*.stop file(s)).
# actually call the executable ${local_d_dir}/*.stop file(s)).
return 0
}
stop()
{
ebegin "Stopping local"
local file has_errors=0 redirect retval
local file has_errors redirect retval
has_errors=0
yesno $rc_verbose || redirect='> /dev/null 2>&1'
ebegin "Stopping local"
eindent
for file in @SYSCONFDIR@/local.d/*.stop; do
for file in "${local_d_dir}"/*.stop; do
if [ -x "${file}" ]; then
vebegin "Executing \"${file}\""
eval "${file}" $redirect
@@ -77,16 +80,16 @@ stop()
eoutdent
if command -v local_stop >/dev/null 2>&1; then
ewarn "\"@SYSCONFDIR@/conf.d/local\" should be removed."
ewarn "\"${conf_d_dir}/local\" should be removed."
ewarn "Please move the code from the local_stop function"
ewarn "to executable scripts with an .stop extension"
ewarn "in \"@SYSCONFDIR@/local.d\""
ewarn "in \"${local_d_dir}\""
local_stop
fi
eend ${has_errors}
# An executable @SYSCONFDIR@/local.d/*.stop file which failed with a
# An executable ${local_d_dir}/*.stop file which failed with a
# non-zero exit status is not a reason to mark this service
# as failed, therefore we have to end with a zero exit code.
return 0

View File

@@ -14,7 +14,7 @@ description="Mounts disks and swap according to /etc/fstab."
depend()
{
need fsck
use lvm modules mtab root
use lvm modules root
after clock lvm modules root
keyword -docker -jail -lxc -prefix -systemd-nspawn -vserver
}

View File

@@ -14,6 +14,7 @@ description="Update /etc/mtab to match what the kernel knows about"
depend()
{
after clock
before localmount
need root
keyword -prefix -systemd-nspawn
}
@@ -30,6 +31,10 @@ start()
einfo "Creating mtab symbolic link"
ln -snf /proc/self/mounts /etc/mtab
else
ewarn "The ${RC_SVCNAME} service will be removed in the future."
ewarn "Please change the mtab_is_file setting to no and run"
ewarn "# rc-service mtab restart"
ewarn "to create the mtab symbolic link."
[ -L /etc/mtab ] && ewarn "Removing /etc/mtab symbolic link"
rm -f /etc/mtab
einfo "Creating mtab file"

View File

@@ -23,6 +23,7 @@ get_interfaces()
{
local ifname iftype
for ifname in /sys/class/net/*; do
[ -h "${ifname}" ] && continue
read iftype < ${ifname}/type
[ "$iftype" = "1" ] && printf "%s " ${ifname##*/}
done
@@ -31,7 +32,7 @@ get_interfaces()
start ()
{
local carriers configured dev gateway ifcount infinite
local rc state x
local carrier operstate rc
ebegin "Checking to see if the network is online"
rc=0
@@ -44,10 +45,12 @@ start ()
ifcount=0
for dev in ${interfaces}; do
: $((ifcount += 1))
read x < /sys/class/net/$dev/carrier
[ $x -eq 1 ] && : $((carriers += 1))
read x < /sys/class/net/$dev/operstate
[ "$x" = up ] && : $((configured += 1))
read carrier < /sys/class/net/$dev/carrier 2> /dev/null ||
carrier=
[ "$carrier" = 1 ] && : $((carriers += 1))
read operstate < /sys/class/net/$dev/operstate 2> /dev/null ||
operstate=
[ "$operstate" = up ] && : $((configured += 1))
done
[ $configured -eq $ifcount ] && [ $carriers -ge 1 ] && break
sleep 1
@@ -61,8 +64,12 @@ start ()
if [ $rc -eq 0 ] && yesno ${include_ping_test:-no}; then
ping_test_host="${ping_test_host:-google.com}"
if [ -n "$ping_test_host" ]; then
ping -c 1 $ping_test_host > /dev/null 2>&1
rc=$?
while $infinite || [ $timeout -gt 0 ]; do
ping -c 1 $ping_test_host > /dev/null 2>&1
rc=$?
[ $rc -eq 0 ] && break
: $((timeout -= 1))
done
fi
fi
eend $rc "The network is offline"

View File

@@ -76,7 +76,7 @@ tentative()
case "$RC_UNAME" in
Linux)
[ -x /sbin/ip ] || [ -x /bin/ip ] || return 1
[ -n "$(command -v ip)" ] || return 1
[ -n "$(ip -f inet6 addr show tentative)" ]
;;
*)
@@ -174,7 +174,7 @@ runip()
routeflush()
{
if [ "$RC_UNAME" = Linux ]; then
if [ -x /sbin/ip ] || [ -x /bin/ip ]; then
if [ -n "$(command -v ip)" ]; then
ip route flush scope global
ip route delete default 2>/dev/null
else
@@ -346,7 +346,7 @@ stop()
then
veinfo "$int"
runargs /etc/ifdown."$int" "$downcmd"
if [ -x /sbin/ip ] || [ -x /bin/ip ]; then
if [ -n "$(command -v ip)" ]; then
# We need to do this, otherwise we may
# fail to add things correctly on restart
ip address flush dev "$int" 2>/dev/null

View File

@@ -27,8 +27,8 @@ start()
[ ! -e /proc/sys/fs/binfmt_misc/register ]; then
if ! grep -qs binfmt_misc /proc/filesystems &&
modprobe -q binfmt-misc; then
ewarn "The binfmt-misc module needs to be configured in" \
"@SYSCONFDIR@/conf.d/modules or built in."
ewarn "The binfmt-misc module needs to be loaded by" \
"the modules service or built in."
fi
if grep -qs binfmt_misc /proc/filesystems; then
ebegin "Mounting misc binary format filesystem"

View File

@@ -1,4 +1,4 @@
S#!@BINDIR@/openrc-run
#!@SBINDIR@/openrc-run
# Copyright (c) 2009-2015 The OpenRC Authors.
# See the Authors file at the top-level directory of this distribution and
# https://github.com/OpenRC/openrc/blob/master/AUTHORS

View File

@@ -21,7 +21,7 @@ BSD_sysctl()
[ -e /etc/sysctl.conf ] || return 0
local retval=0 var= comments= conf=
eindent
for conf in @SYSCONFDIR@/sysctl.conf @SYSCONFDIR@/sysctl.d/*.conf; do
for conf in /etc/sysctl.conf /etc/sysctl.d/*.conf; do
if [ -r "$conf" ]; then
vebegin "applying $conf"
while read var comments; do

View File

@@ -101,53 +101,17 @@ mount_misc()
if [ -d /sys/firmware/efi/efivars ] &&
! mountinfo -q /sys/firmware/efi/efivars; then
ebegin "Mounting efivarfs filesystem"
mount -n -t efivarfs -o ${sysfs_opts} \
mount -n -t efivarfs -o ro \
efivarfs /sys/firmware/efi/efivars 2> /dev/null
eend 0
fi
}
mount_cgroups()
{
# set up kernel support for cgroups
if [ -d /sys/fs/cgroup ] && ! mountinfo -q /sys/fs/cgroup; then
if grep -qs cgroup /proc/filesystems; then
ebegin "Mounting cgroup filesystem"
local opts="${sysfs_opts},mode=755,size=${rc_cgroupsize:-10m}"
mount -n -t tmpfs -o ${opts} cgroup_root /sys/fs/cgroup
eend $?
fi
fi
mountinfo -q /sys/fs/cgroup || return 0
if ! mountinfo -q /sys/fs/cgroup/openrc; then
local agent="@LIBEXECDIR@/sh/cgroup-release-agent.sh"
mkdir /sys/fs/cgroup/openrc
mount -n -t cgroup \
-o none,${sysfs_opts},name=openrc,release_agent="$agent" \
openrc /sys/fs/cgroup/openrc
printf 1 > /sys/fs/cgroup/openrc/notify_on_release
fi
yesno ${rc_controller_cgroups:-YES} && [ -e /proc/cgroups ] || return 0
while read name hier groups enabled rest; do
case "${enabled}" in
1) mountinfo -q /sys/fs/cgroup/${name} && continue
mkdir /sys/fs/cgroup/${name}
mount -n -t cgroup -o ${sysfs_opts},${name} \
${name} /sys/fs/cgroup/${name}
;;
esac
done < /proc/cgroups
}
restorecon_sys()
{
if [ -x /sbin/restorecon ]; then
ebegin "Restoring SELinux contexts in /sys"
restorecon -F /sys/devices/system/cpu/online >/dev/null 2>&1
restorecon -rF /sys/fs/cgroup >/dev/null 2>&1
eend $?
fi
}
@@ -156,7 +120,6 @@ start()
{
mount_sys
mount_misc
mount_cgroups
restorecon_sys
return 0
}

View File

@@ -6,7 +6,7 @@ MAN3= einfo.3 \
rc_config.3 rc_deptree.3 rc_find_pids.3 rc_plugin_hook.3 \
rc_runlevel.3 rc_service.3 rc_stringlist.3
MAN8= rc-service.8 rc-status.8 rc-update.8 openrc.8 openrc-run.8 \
service.8 start-stop-daemon.8 supervise-daemon.8
start-stop-daemon.8 supervise-daemon.8
ifeq (${OS},Linux)
MAN8 += rc-sstat.8 openrc-init.8 openrc-shutdown.8

View File

@@ -8,7 +8,7 @@
.\" This file may not be copied, modified, propagated, or distributed
.\" except according to the terms contained in the LICENSE file.
.\"
.Dd December 31, 2011
.Dd November 30, 2017
.Dt openrc-run 8 SMM
.Os OpenRC
.Sh NAME
@@ -111,9 +111,12 @@ The path to the s6 service directory if you are monitoring this service
with S6. The default is /var/svc.d/${RC_SVCNAME}.
.It Ar s6_svwait_options_start
The options to pass to s6-svwait when starting the service via s6.
.It Ar s6_force_kill
Should we force-kill this service if s6_service_timeout_stop expires
but the service doesn't go down during shutdown? The default is yes.
.It Ar s6_service_timeout_stop
The amount of time, in milliseconds, s6-svc should wait for the service
to go down when stopping the service. The default is 10000.
to go down when stopping the service. The default is 60000.
.It Ar start_stop_daemon_args
List of arguments passed to start-stop-daemon when starting the daemon.
.It Ar command
@@ -139,21 +142,39 @@ List of arguments to pass to the daemon when starting via
.Nm supervise-daemon .
to force the daemon to stay in the foreground
.It Ar command_background
Set this to "true", "yes" or "1" (case-insensitive) if you want
Set this to "true", "yes" or "1" (case-insensitive) if you want
.Xr start-stop-daemon 8
to force the daemon into the background. This forces the
"--make-pidfile" and "--pidfile" options, so the pidfile variable must be set.
.It Ar command_progress
Set this to "true", "yes" or "1" (case-insensitive) if you want
Set this to "true", "yes" or "1" (case-insensitive) if you want
.Xr start-stop-daemon 8
to display a progress meter when waiting for a daemon to stop.
.It Ar command_user
If the daemon does not support changing to a different user id, you can
use this to change the user id before
use this to change the user id, and optionally group id, before
.Xr start-stop-daemon 8
or
.Xr supervise-daemon 8
launches the daemon
launches the daemon.
.It Ar output_log
This is the path to a file or named pipe where the standard output from
the service will be redirected. If you are starting this service with
.Xr start-stop-daemon 8 ,
, you must set
.Pa command_background
to true. Keep in mind that this path will be inside the chroot if the
.Pa chroot
variable is set.
.It Ar error_log
The same thing as
.Pa output_log
but for the standard error output.
.It Ar directory
.Xr start-stop-daemon 8
and
.Xr supervise-daemon 8
will chdir to this directory before starting the daemon.
.It Ar chroot
.Xr start-stop-daemon 8
and
@@ -198,6 +219,8 @@ used along with in_background_fake to support re-entrant services.
.It Ar in_background_fake
Space separated list of commands which should always succeed when
in_background is yes.
.It Ar umask
Set the umask of the daemon.
.Pp
Keep in mind that eval is used to process chroot, command, command_args_*,
command_user, pidfile and procname. This may affect how they are
@@ -217,8 +240,10 @@ that dependency type to the function, or prefix the names with ! to
remove them from the dependencies.
.Bl -tag -width "RC_DEFAULTLEVEL"
.It Ic need
The service will refuse to start until needed services have started and it
will refuse to stop until any services that need it have stopped.
The service will attempt to start any services it needs regardless of
whether they have been added to the runlevel. It will refuse to start
until all services it needs have started, and it will refuse to stop until all
services that need it have stopped.
.It Ic use
The service will attempt to start any services it uses that have been added
to the runlevel.
@@ -284,6 +309,18 @@ system.
To see how to influence dependencies in configuration files, see the
.Sx FILES
section below.
.Sh _pre AND _post FUNCTIONS
Any command defined in extra_commands, extra_started_commands or
extra_stopped_commands can have _pre and _post functions in the service
script. If the command function is called foo, the_pre and _post
functions for it should be called foo_pre and foo_post.
.Pp
These functions should be used to perform preparation before the
command is run and cleanup after the command completes. In order for
.Nm
to record the command as being run successfully, the _pre
function, command function itself and the _post function should all exit
with a zero return code.
.Sh BUILTINS
.Nm
defines some builtin functions that you can use inside your service scripts:
@@ -412,27 +449,65 @@ If -d, -f or -p is specified, checkpath checks to see if the path
exists, is the right type and has the correct owner and access modes. If
any of these tests fail, the path is created and set up as specified. If
more than one of -d, -f or -p are specified, the last one will be used.
.Pp
The argument to -m is a three or four digit octal number. If this option
is not provided, the value defaults to 0644 for files and 0775 for
directories.
.Pp
The argument to -o is a representation of the user and/or group which
should own the path. The user and group can be represented numerically
or with names, and are separated by a colon.
.Pp
The truncate options (-D and -F) cause the directory or file to be
cleared of all contents.
.Pp
If -W is specified, checkpath checks to see if the first path given on
the command line is writable. This is different from how the test
command in the shell works, because it also checks to make sure the file
system is not read only.
.Pp
Also, the -d, -f or -p options should not be specified along with this option.
.Pp
The -q option suppresses all informational output. If it is specified
twice, all error messages are suppressed as well.
.It Xo
.Ic fstabinfo
.Op Fl M , -mount
.Op Fl R , -remount
.Op Fl b , -blockdevice
.Op Fl m , -mountargs
.Op Fl o , -options
.Op Fl p , -passno Ar passno
.Op Fl t , -type Ar fstype
.Ar path
.Xc
If -b, -m, -o, -p or -t is specified,the appropriate information is
extracted from fstab. If -M or -R are given, file systems are mounted or
remounted.
.Pp
The -q option suppresses all informational output. If it is specified
twice, all error messages are suppressed as well.
.It Xo
.Ic mountinfo
.Op Fl f, -fstype-regex Ar regex
.Op Fl F, -skip-fstype-regex Ar regex
.Op Fl n, -node-regex Ar regex
.Op Fl N, -skip-node-regex Ar regex
.Op Fl o, -options-regex Ar regex
.Op Fl O, -skip-options-regex Ar regex
.Op Fl p, -point-regex Ar regex
.Op Fl P, -skip-point-regex Ar regex
.Op Fl e, -netdev
.Op Fl E, -nonetdev
.Op Fl i, -options
.Op Fl s, -fstype
.Op Fl t, -node
.Ar mount1 mount2 ...
.Xc
The f, F, n, N, o, O, p, P, e and E options specify what you want to
search for or skip in the mounted file systems. The i, s and t options
specify what you want to display. If no mount points are given, all
mount points will be considered.
.It Ic yesno Ar value
If
.Ar value
@@ -658,6 +733,8 @@ Users are encouraged to use the is_newer_than function which returns correctly.
.Xr rc_plugin_hook 3 ,
.Xr sh 1p ,
.Xr start-stop-daemon 8 ,
.Xr supervise-daemon 8 ,
.Xr uname 1
.Sh AUTHORS
.An Roy Marples <roy@marples.name>
.An William Hubbs <w.d.hubbs@gmail.com>

View File

@@ -8,7 +8,7 @@
.\" This file may not be copied, modified, propagated, or distributed
.\" except according to the terms contained in the LICENSE file.
.\"
.Dd April 6, 2017
.Dd May 22, 2017
.Dt openrc-shutdown 8 SMM
.Os OpenRC
.Sh NAME
@@ -16,22 +16,33 @@
.Nd bring the system down
.Sh SYNOPSIS
.Nm
.Op Fl d , -no-write
.Op Fl D , -dry-run
.Op Fl H , -halt
.Op Fl k , -kexec
.Op Fl p , -poweroff
.Op Fl R , -reexec
.Op Fl r , -reboot
.Op Fl s , -single
.Op Fl w , -write-only
.Sh DESCRIPTION
.Nm
is the utility that communicates with openrc-init(8) to bring down the
system or instruct openrc-init to re-execute itself. It supports the
following options:
is the utility that communicates with
.Xr openrc-init 8
to bring down the system or instruct openrc-init to re-execute itself.
It supports the following options:
.Bl -tag -width "poweroff"
.It Fl d , -no-write
Do not write the wtmp boot record.
.It Fl D , -dry-run
Print the action that would be taken without executing it. This is to
allow testing.
.It Fl H , -halt
Stop all services, kill all remaining processes and halt the system.
.It Fl k , -kexec
Stop all services, kill all processes and boot directly into a new
kernel loaded via kexec(8).
kernel loaded via
.Xr kexec 8 .
.It Fl p , -poweroff
Stop all services, kill all processes and power off the system.
.It Fl R , -reexec
@@ -39,6 +50,10 @@ instruct openrc-init to re-exec itself. This should be used after an
upgrade of OpenRC if you are using openrc-init as your init process.
.It Fl r , -reboot
Stop all services, kill all processes and reboot the system.
.It Fl s , -single
Stop all services, kill all processes and move to single user mode.
.It Fl w , -write-only
Stop all services, kill all processes and move to single user mode.
.El
.Sh SEE ALSO
.Xr openrc-init 8 ,

View File

@@ -16,6 +16,10 @@
.Nd locate and run an OpenRC service with the given arguments
.Sh SYNOPSIS
.Nm
.Op Fl c , -ifcrashed
.Ar service cmd
.Op Ar ...
.Nm
.Op Fl i , -ifexists
.Ar service cmd
.Op Ar ...

View File

@@ -1 +0,0 @@
.so rc-service.8

View File

@@ -36,6 +36,8 @@
.Ar pidfile
.Fl P , -respawn-period
.Ar seconds
.Fl R , -retry
.Ar arg
.Fl r , -chroot
.Ar chrootpath
.Fl u , -user
@@ -115,6 +117,9 @@ Modifies the scheduling priority of the daemon.
.It Fl P , -respawn-period Ar seconds
Sets the length of a respawn period. The default is 10 seconds. See the
description of --respawn-max for more information.
.It Fl R , -retry Ar timeout | Ar signal Ns / Ns Ar timeout
The retry specification can be either a timeout in seconds or multiple
signal/timeout pairs (like SIGTERM/5).
.It Fl r , -chroot Ar path
chroot to this directory before starting the daemon. All other paths, such
as the path to the daemon, chdir and pidfile, should be relative to the chroot.
@@ -130,6 +135,7 @@ The same thing as
.Fl 1 , -stdout
but with the standard error output.
.El
.El
.Sh ENVIRONMENT
.Va SSD_NICELEVEL
can also set the scheduling priority of the daemon, but the command line

View File

@@ -26,7 +26,8 @@ _CCFLAGS= -Wall -Wextra -Wimplicit -Wshadow -Wformat=2 \
-Wnested-externs \
-Winline -Wwrite-strings -Wcast-align -Wcast-qual \
-Wpointer-arith \
-Wdeclaration-after-statement -Wsequence-point
-Wdeclaration-after-statement -Wsequence-point \
-Werror=implicit-function-declaration
# We should be using -Wredundant-decls, but our library hidden proto stuff
# gives loads of warnings. I don't fully understand it (the hidden proto,

View File

@@ -34,6 +34,7 @@ PICFLAG?= -fPIC
SYSCONFDIR?= ${PREFIX}/etc
INITDIR?= ${SYSCONFDIR}/init.d
CONFDIR?= ${SYSCONFDIR}/conf.d
CONFMODE?= 0644
LOCALDIR?= ${SYSCONFDIR}/local.d
SYSCTLDIR?= ${SYSCONFDIR}/sysctl.d
@@ -59,10 +60,12 @@ MANPREFIX?= ${UPREFIX}/share
MANDIR?= ${MANPREFIX}/man
MANMODE?= 0444
BASHCOMPDIR?= ${UPREFIX}/share/bash-completion/completions
DATADIR?= ${UPREFIX}/share/openrc
DATAMODE?= 0644
DOCDIR?= ${UPREFIX}/share/doc
DOCMODE?= 0644
CONFMODE?= 0644
ZSHCOMPDIR?= ${UPREFIX}/share/zsh/site-functions

View File

@@ -1,4 +1,5 @@
# Using runit with OpenRC
Using runit with OpenRC
=======================
Beginning with OpenRC-0.21, we support using runit [1] in place of
start-stop-daemon for monitoring and restarting daemons.

View File

@@ -38,7 +38,7 @@ BOOT-FreeBSD+= adjkerntz dumpon syscons
BOOT-Linux+= binfmt hwclock keymaps modules mtab procfs termencoding
SHUTDOWN-Linux= killprocs mount-ro
SYSINIT-Linux= devfs dmesg sysfs
SYSINIT-Linux= devfs cgroups dmesg sysfs
# Generic BSD stuff
BOOT-NetBSD+= hostid newsyslog savecore syslogd

View File

@@ -1,4 +1,5 @@
# Using S6 with OpenRC
Using S6 with OpenRC
====================
Beginning with OpenRC-0.16, we support using the s6 supervision suite
from Skarnet Software in place of start-stop-daemon for monitoring
@@ -39,6 +40,10 @@ s6_service_path - the path to the s6 service directory. The default is
s6_svwait_options_start - the options to pass to s6-svwait when starting
the service. If this is not set, s6-svwait will not be called.
s6_force_kill - Should we try to force kill this service if the
s6_service_timeout_stop timeout expires when shutting down this service?
The default is yes.
s6_service_timeout_stop - the amount of time, in milliseconds, s6-svc
should wait for a service to go down when stopping.

4
scripts/.gitignore vendored
View File

@@ -1 +1,5 @@
halt
poweroff
rc-sstat
reboot
shutdown

View File

@@ -8,12 +8,23 @@ INSTALLAFTER = _installafter
ifeq (${OS},Linux)
SRCS+= rc-sstat.in
BIN+= rc-sstat
ifeq (${MKSYSVINIT},yes)
SRCS+= halt.in poweroff.in reboot.in shutdown.in
BIN+= halt poweroff reboot shutdown
endif
endif
_installafter:
ifeq (${OS},Linux)
${INSTALL} -d ${DESTDIR}${SBINDIR}
ln -sf ${DIR}/rc-sstat ${DESTDIR}/${SBINDIR}/rc-sstat
ifeq (${MKSYSVINIT},yes)
ln -sf ${DIR}/halt ${DESTDIR}/${SBINDIR}/halt
ln -sf ${DIR}/poweroff ${DESTDIR}/${SBINDIR}/poweroff
ln -sf ${DIR}/reboot ${DESTDIR}/${SBINDIR}/reboot
ln -sf ${DIR}/shutdown ${DESTDIR}/${SBINDIR}/shutdown
ln -sf openrc-init ${DESTDIR}/${SBINDIR}/init
endif
endif
include ${MK}/scripts.mk

24
scripts/halt.in Normal file
View File

@@ -0,0 +1,24 @@
#!@SHELL@
option_arg=
poweroff_arg=
while getopts :nwdfiph opt; do
case "$opt" in
n) ;;
w) poweroff_arg=--write-only ;;
d) option_arg=--no-write ;;
f) ;;
i) ;;
p) poweroff_arg=--poweroff ;;
[?]) printf "%s\n" "${0##*/}: invalid command line option" >&2
exit 1
;;
esac
done
shift $((OPTIND-1))
if [ -z "${poweroff_arg}" ]; then
poweroff_arg=--poweroff
fi
exec @SBINDIR@/openrc-shutdown ${option_arg} ${poweroff_arg} "$@"

23
scripts/poweroff.in Normal file
View File

@@ -0,0 +1,23 @@
#!@SHELL@
option_arg=
poweroff_arg=
while getopts :nwdfiph opt; do
case "$opt" in
n) ;;
w) poweroff_arg=--write-only ;;
d) option_arg=--no-write ;;
f) ;;
i) ;;
[?]) printf "%s\n" "${0##*/}: invalid command line option" >&2
exit 1
;;
esac
done
shift $((OPTIND-1))
if [ -z "${poweroff_arg}" ]; then
poweroff_arg=--poweroff
fi
exec @SBINDIR@/openrc-shutdown ${option_arg} ${poweroff_arg} "$@"

25
scripts/reboot.in Normal file
View File

@@ -0,0 +1,25 @@
#!@SHELL@
option_arg=
poweroff_arg=
while getopts :nwdfhik opt; do
case "$opt" in
n) ;;
w) poweroff_arg=--write-only ;;
d) option_arg=--no-write ;;
f) ;;
h) ;;
i) ;;
k) poweroff_arg=--kexec ;;
[?]) printf "%s\n" "${0##*/}: invalid command line option" >&2
exit 1
;;
esac
done
shift $((OPTIND-1))
if [ -z "${poweroff_arg}" ]; then
poweroff_arg=--reboot
fi
exec @SBINDIR@/openrc-shutdown ${option_arg} ${poweroff_arg} "$@"

29
scripts/shutdown.in Normal file
View File

@@ -0,0 +1,29 @@
#!@SHELL@
shutdown_arg=
while getopts :akrhPHfFnct: opt; do
case "$opt" in
a) ;;
k) ;;
r) shutdown_arg=--reboot ;;
h) shutdown_arg=--halt ;;
P) shutdown_arg=--poweroff ;;
H) shutdown_arg=--halt ;;
f) ;;
F) ;;
n) ;;
c) ;;
t) ;;
[?]) printf "%s\n" "${0##*/}: invalid command line option" >&2
exit 1
;;
esac
done
shift $((OPTIND-1))
if [ -z "${shutdown_arg}" ]; then
shutdown_arg=--single
fi
echo @SBINDIR@/openrc-shutdown ${shutdown_arg} "$@"
exec @SBINDIR@/openrc-shutdown ${shutdown_arg} "$@"

493
service-script-guide.md Normal file
View File

@@ -0,0 +1,493 @@
OpenRC Service Script Writing Guide
===================================
This document is aimed at developers or packagers who
write OpenRC service scripts, either for their own projects, or for
the packages they maintain. It contains advice, suggestions, tips,
tricks, hints, and counsel; cautions, warnings, heads-ups,
admonitions, proscriptions, enjoinders, and reprimands.
It is intended to prevent common mistakes that are found "in the wild"
by pointing out those mistakes and suggesting alternatives. Each
good/bad thing that you should/not do has a section devoted to it. We
don't consider anything exotic, and assume that you will use
start-stop-daemon to manage a fairly typical long-running UNIX
process.
# Syntax of Service Scripts
Service scripts are shell scripts. OpenRC aims at using only the standardized
POSIX sh subset for portability reasons. The default interpreter (build-time
toggle) is `/bin/sh`, so using for example mksh is not a problem.
OpenRC has been tested with busybox sh, ash, dash, bash, mksh, zsh and possibly
others. Using busybox sh has been difficult as it replaces commands with
builtins that don't offer the expected features.
The interpreter for service scripts is `#!/sbin/openrc-run`.
Not using this interpreter will break the use of dependencies and is not
supported. (iow: if you insist on using `#!/bin/sh` you're on your own)
A `depend` function declares the dependencies of this service script.
All scripts must have start/stop/status functions, but defaults are provided and should be used unless you have a very strong reason not to use them.
Extra functions can be added easily:
```
extra_commands="checkconfig"
checkconfig() {
doSomething
}
```
This exports the checkconfig function so that `/etc/init.d/someservice
checkconfig` will be available, and it "just" runs this function.
While commands defined in `extra_commands` are always available, commands
defined in `extra_started_commands` will only work when the service is started
and those defined in `extra_stopped_commands` will only work when the service is
stopped. This can be used for implementing graceful reload and similar
behaviour.
Adding a restart function will not work, this is a design decision within
OpenRC. Since there may be dependencies involved (e.g. network -> apache) a
restart function is in general not going to work.
restart is internally mapped to `stop()` + `start()` (plus handling dependencies).
If a service needs to behave differently when it is being restarted vs
started or stopped, it should test the `$RC_CMD` variable, for example:
```
[ "$RC_CMD" = restart ] && do_something
```
# The Depend Function
This function declares the dependencies for a service script. This
determines the order the service scripts start.
```
depend() {
need net
use dns logger netmount
want coolservice
}
```
`need` declares a hard dependency - net always needs to be started before this
service does
`use` is a soft dependency - if dns, logger or netmount is in this runlevel
start it before, but we don't care if it's not in this runlevel.
`want` is between need and use - try to start coolservice if it is
installed on the system, regardless of whether it is in the
runlevel, but we don't care if it starts.
`before` declares that we need to be started before another service
`after` declares that we need to be started after another service, without
creating a dependency (so on calling stop the two are independent)
`provide` allows multiple implementations to provide one service type, e.g.:
`provide cron` is set in all cron-daemons, so any one of them started
satisfies a cron dependency
`keyword` allows platform-specific overrides, e.g. `keyword -lxc` makes this
service script a noop in lxc containers. Useful for things like keymaps,
module loading etc. that are either platform-specific or not available
in containers/virtualization/...
FIXME: Anything missing in this list?
# The Default Functions
All service scripts are assumed to have the following functions:
```
start()
stop()
status()
```
There are default implementations in `lib/rc/sh/openrc-run.sh` - this allows very
compact service scripts. These functions can be overridden per service script as
needed.
The default functions assume the following variables to be set in the service
script:
```
command=
command_args=
pidfile=
```
Thus the 'smallest' service scripts can be half a dozen lines long
## Don't write your own start/stop functions
OpenRC is capable of stopping and starting most daemons based on the
information that you give it. For a well-behaved daemon that
backgrounds itself and writes its own PID file by default, the
following OpenRC variables are likely all that you'll need:
* command
* command_args
* pidfile
Given those three pieces of information, OpenRC will be able to start
and stop the daemon on its own. The following is taken from an
[OpenNTPD](http://www.openntpd.org/) service script:
```sh
command="/usr/sbin/ntpd"
# The special RC_SVCNAME variable contains the name of this service.
pidfile="/run/${RC_SVCNAME}.pid"
command_args="-p ${pidfile}"
```
If the daemon runs in the foreground by default but has options to
background itself and to create a pidfile, then you'll also need
* command_args_background
That variable should contain the flags needed to background your
daemon, and to make it write a PID file. Take for example the
following snippet of an
[NRPE](https://github.com/NagiosEnterprises/nrpe) service script:
```sh
command="/usr/bin/nrpe"
command_args="--config=/etc/nagios/nrpe.cfg"
command_args_background="--daemon"
pidfile="/run/${RC_SVCNAME}.pid"
```
Since NRPE runs as *root* by default, it needs no special permissions
to write to `/run/nrpe.pid`. OpenRC takes care of starting and
stopping the daemon with the appropriate arguments, even passing the
`--daemon` flag during startup to force NRPE into the background (NRPE
knows how to write its own PID file).
But what if the daemon isn't so well behaved? What if it doesn't know
how to background itself or create a pidfile? If it can do neither,
then use,
* command_background=true
which will additionally pass `--make-pidfile` to start-stop-daemon,
causing it to create the `$pidfile` for you (rather than the daemon
itself being responsible for creating the PID file).
If your daemon doesn't know how to change its own user or group, then
you can tell start-stop-daemon to launch it as an unprivileged user
with
* command_user="user:group"
Finally, if your daemon always forks into the background but fails to
create a PID file, then your only option is to use
* procname
With `procname`, OpenRC will try to find the running daemon by
matching the name of its process. That's not so reliable, but daemons
shouldn't background themselves without creating a PID file in the
first place. The next example is part of the [CA NetConsole
Daemon](https://oss.oracle.com/projects/cancd/) service script:
```sh
command="/usr/sbin/cancd"
command_args="-p ${CANCD_PORT}
-l ${CANCD_LOG_DIR}
-o ${CANCD_LOG_FORMAT}"
command_user="cancd"
# cancd daemonizes itself, but doesn't write a PID file and doesn't
# have an option to run in the foreground. So, the best we can do
# is try to match the process name when stopping it.
procname="cancd"
```
To recap, in order of preference:
1. If the daemon backgrounds itself and creates its own PID file, use
`pidfile`.
2. If the daemon does not background itself (or has an option to run
in the foreground) and does not create a PID file, then use
`command_background=true` and `pidfile`.
3. If the daemon backgrounds itself and does not create a PID file,
use `procname` instead of `pidfile`. But, if your daemon has the
option to run in the foreground, then you should do that instead
(that would be the case in the previous item).
4. The last case, where the daemon does not background itself but
does create a PID file, doesn't make much sense. If there's a way
to disable the daemon's PID file (or, to write it straight into the
garbage), then do that, and use `command_background=true`.
## Reloading your daemon's configuration
Many daemons will reload their configuration files in response to a
signal. Suppose your daemon will reload its configuration in response
to a `SIGHUP`. It's possible to add a new "reload" command to your
service script that performs this action. First, tell the service
script about the new command.
```sh
extra_started_commands="reload"
```
We use `extra_started_commands` as opposed to `extra_commands` because
the "reload" action is only valid while the daemon is running (that
is, started). Now, start-stop-daemon can be used to send the signal to
the appropriate process (assuming you've defined the `pidfile`
variable elsewhere):
```sh
reload() {
ebegin "Reloading ${RC_SVCNAME}"
start-stop-daemon --signal HUP --pidfile "${pidfile}"
eend $?
}
```
## Don't restart/reload with a broken config
Often, users will start a daemon, make some configuration change, and
then attempt to restart the daemon. If the recent configuration change
contains a mistake, the result will be that the daemon is stopped but
then cannot be started again (due to the configuration error). It's
possible to prevent that situation with a function that checks for
configuration errors, and a combination of the `start_pre` and
`stop_pre` hooks.
```sh
checkconfig() {
# However you want to check this...
}
start_pre() {
# If this isn't a restart, make sure that the user's config isn't
# busted before we try to start the daemon (this will produce
# better error messages than if we just try to start it blindly).
#
# If, on the other hand, this *is* a restart, then the stop_pre
# action will have ensured that the config is usable and we don't
# need to do that again.
if [ "${RC_CMD}" != "restart" ] ; then
checkconfig || return $?
fi
}
stop_pre() {
# If this is a restart, check to make sure the user's config
# isn't busted before we stop the running daemon.
if [ "${RC_CMD}" = "restart" ] ; then
checkconfig || return $?
fi
}
```
To prevent a *reload* with a broken config, keep it simple:
```sh
reload() {
checkconfig || return $?
ebegin "Reloading ${RC_SVCNAME}"
start-stop-daemon --signal HUP --pidfile "${pidfile}"
eend $?
}
```
## PID files should be writable only by root
PID files must be writable only by *root*, which means additionally
that they must live in a *root*-owned directory. This directory is
normally /run under Linux and /var/run under other operating systems.
Some daemons run as an unprivileged user account, and create their PID
files (as the unprivileged user) in a path like
`/var/run/foo/foo.pid`. That can usually be exploited by the unprivileged
user to kill *root* processes, since when a service is stopped, *root*
usually sends a SIGTERM to the contents of the PID file (which are
controlled by the unprivileged user). The main warning sign for that
problem is using `checkpath` to set ownership on the directory
containing the PID file. For example,
```sh
# BAD BAD BAD BAD BAD BAD BAD BAD
start_pre() {
# Ensure that the pidfile directory is writable by the foo user/group.
checkpath --directory --mode 0700 --owner foo:foo "/var/run/foo"
}
# BAD BAD BAD BAD BAD BAD BAD BAD
```
If the *foo* user owns `/var/run/foo`, then he can put whatever he wants
in the `/var/run/foo/foo.pid` file. Even if *root* owns the PID file, the
*foo* user can delete it and replace it with his own. To avoid
security concerns, the PID file must be created as *root* and live in
a *root*-owned directory. If your daemon is responsible for forking
and writing its own PID file but the PID file is still owned by the
unprivileged runtime user, then you may have an upstream issue.
Once the PID file is being created as *root* (before dropping
privileges), it can be written directly to a *root*-owned
directory. For example, the *foo* daemon might write
`/var/run/foo.pid`. No calls to checkpath are needed. Note: there is
nothing technically wrong with using a directory structure like
`/var/run/foo/foo.pid`, so long as *root* owns the PID file and the
directory containing it.
Ideally (see "Upstream your service scripts"), your service script
will be integrated upstream and the build system will determine the
appropriate directory for the pid file. For example,
```sh
pidfile="@piddir@/${RC_SVCNAME}.pid"
```
A decent example of this is the [Nagios core service
script](https://github.com/NagiosEnterprises/nagioscore/blob/master/openrc-init.in),
where the full path to the PID file is specified at build-time.
## Don't let the user control the PID file location
It's usually a mistake to let the end user control the PID file
location through a conf.d variable, for a few reasons:
1. When the PID file path is controlled by the user, you need to
ensure that its parent directory exists and is writable. This
adds unnecessary code to the service script.
2. If the PID file path changes while the service is running, then
you'll find yourself unable to stop the service.
3. The directory that should contain the PID file is best determined
by the upstream build system (see "Upstream your service scripts").
On Linux, the preferred location these days is `/run`. Other systems
still use `/var/run`, though, and a `./configure` script is the
best place to decide which one you want.
4. Nobody cares where the PID file is located, anyway.
Since OpenRC service names must be unique, a value of
```sh
pidfile="/var/run/${RC_SVCNAME}.pid"
```
guarantees that your PID file has a unique name.
## Upstream your service scripts (for packagers)
The ideal place for an OpenRC service script is **upstream**. Much like
systemd services, a well-crafted OpenRC service script should be
distribution-agnostic, and the best place for it is upstream. Why? For
two reasons. First, having it upstream means that there's a single
authoritative source for improvements. Second, a few paths in every
service script are dependent upon flags passed to the build system. For
example,
```sh
command=/usr/bin/foo
```
in an autotools-based build system should really be
```sh
command=@bindir@/foo
```
so that the user's value of `--bindir` is respected. If you keep the
service script in your own distribution's repository, then you have to
keep the command path and package synchronized yourself, and that's no
fun.
## Be wary of "need net" dependencies
There are two things you need to know about "need net" dependencies:
1. They are not satisfied by the loopback interface, so "need net"
requires some *other* interface to be up.
2. Depending on the value of `rc_depend_strict` in `rc.conf`, the
"need net" will be satisfied when either *any* non-loopback
interface is up, or when *all* non-loopback interfaces are up.
The first item means that "need net" is wrong for daemons that are
happy with `0.0.0.0`, and the second point means that "need net" is
wrong for daemons that need a particular (for example, the WAN)
interface. We'll consider the two most common users of "need net";
network clients who access some network resource, and network servers
who provide them.
### Network clients
Network clients typically want the WAN interface to be up. That may
tempt you to depend on the WAN interface; but first, you should ask
yourself a question: does anything bad happen if the WAN interface is
not available? In other words, if the administrator wants to disable
the WAN, should the service be stopped? Usually the answer to that
question is "no," and in that case, you should forego the "net"
dependency entirely.
Suppose, for example, that your service retrieves virus signature
updates from the internet. In order to do its job correctly, it needs
a (working) internet connection. However, the service itself does not
require the WAN interface to be up: if it is, great; otherwise, the
worst that will happen is that a "server unavailable" warning will be
logged. The signature update service will not crash, and—perhaps more
importantly—you don't want it to terminate if the administrator turns
off the WAN interface for a second.
### Network servers
Network servers are generally easier to handle than their client
counterparts. Most server daemons listen on `0.0.0.0` (all addresses)
by default, and are therefore satisfied to have the loopback interface
present and operational. OpenRC ships with the loopback service in the
*boot* runlevel, and therefore most server daemons require no further
network dependencies.
The exceptions to this rule are those daemons who produce negative
side-effects when the WAN is unavailable. For example, the Nagios
server daemon will generate "the sky is falling" alerts for as long as
your monitored hosts are unreachable. So in that case, you should
require some other interface (often the WAN) to be up. A "need"
dependency would be appropriate, because you want Nagios to be
stopped before the network is taken down.
If your daemon can optionally be configured to listen on a particular
interface, then please see the "Depending on a particular interface"
section.
### Depending on a particular interface
If you need to depend on one particular interface, usually it's not
easy to determine programmatically what that interface is. For
example, if your *sshd* daemon listens on `192.168.1.100` (rather than
`0.0.0.0`), then you have two problems:
1. Parsing `sshd_config` to figure that out; and
2. Determining which network service name corresponds to the
interface for `192.168.1.100`.
It's generally a bad idea to parse config files in your service
scripts, but the second problem is the harder one. Instead, the most
robust (i.e. the laziest) approach is to make the user specify the
dependency when he makes a change to sshd_config. Include something
like the following in the service configuration file,
```sh
# Specify the network service that corresponds to the "bind" setting
# in your configuration file. For example, if you bind to 127.0.0.1,
# this should be set to "loopback" which provides the loopback interface.
rc_need="loopback"
```
This is a sensible default for daemons that are happy with `0.0.0.0`,
but lets the user specify something else, like `rc_need="net.wan"` if
he needs it. The burden is on the user to determine the appropriate
service whenever he changes the daemon's configuration file.

View File

@@ -33,6 +33,9 @@ else
kmode="-a"
fi
# Set the SELinux label on console before everything so we dont lose output
[ -x /sbin/restorecon ] && /sbin/restorecon -F /dev/console
# Try and set a font and as early as we can
if service_present "$RC_DEFAULTLEVEL" consolefont ||
service_present "$RC_BOOTLEVEL" consolefont; then

View File

@@ -83,6 +83,7 @@ elif ! mountinfo -q /run; then
fi
fi
[ -x /sbin/restorecon ] && /sbin/restorecon -rF /run
checkpath -d $RC_SVCDIR
checkpath -d -m 0775 -o root:uucp /run/lock

View File

@@ -243,6 +243,9 @@ sourcex "@LIBEXECDIR@/sh/s6.sh"
sourcex "@LIBEXECDIR@/sh/start-stop-daemon.sh"
sourcex "@LIBEXECDIR@/sh/supervise-daemon.sh"
# Load our script
sourcex "$RC_SERVICE"
# Set verbose mode
if yesno "${rc_verbose:-$RC_VERBOSE}"; then
EINFO_VERBOSE=yes
@@ -255,26 +258,26 @@ for _cmd; do
[ -n "${rc_ulimit:-$RC_ULIMIT}" ] && \
ulimit ${rc_ulimit:-$RC_ULIMIT}
# Apply cgroups settings if defined
if [ "$(command -v cgroup_add_service)" = \
"cgroup_add_service" ]
if [ "$(command -v cgroup_add_service)" = "cgroup_add_service" ]
then
if [ -d /sys/fs/cgroup -a ! -w /sys/fs/cgroup ]; then
eerror "No permission to apply cgroup settings"
break
if grep -qs /sys/fs/cgroup /proc/1/mountinfo
then
if [ -d /sys/fs/cgroup -a ! -w /sys/fs/cgroup ]; then
eerror "No permission to apply cgroup settings"
break
fi
fi
cgroup_add_service /sys/fs/cgroup/openrc
cgroup_add_service /sys/fs/cgroup/systemd/system
cgroup_add_service
fi
[ "$(command -v cgroup_set_limits)" = \
"cgroup_set_limits" ] && \
cgroup_set_limits
[ "$(command -v cgroup_set_limits)" = "cgroup_set_limits" ] &&
cgroup_set_limits
[ "$(command -v cgroup2_set_limits)" = "cgroup2_set_limits" ] &&
[ "$_cmd" = start ] &&
cgroup2_set_limits
break
fi
done
# Load our script
sourcex "$RC_SERVICE"
eval "printf '%s\n' $required_dirs" | while read _d; do
if [ -n "$_d" ] && [ ! -d "$_d" ]; then
eerror "$RC_SVCNAME: \`$_d' is not a directory"
@@ -364,10 +367,14 @@ while [ -n "$1" ]; do
then
"$1"_post || exit $?
fi
[ "$(command -v cgroup_cleanup)" = "cgroup_cleanup" -a \
"$1" = "stop" ] && \
yesno "${rc_cgroup_cleanup}" && \
[ "$(command -v cgroup_cleanup)" = "cgroup_cleanup" ] &&
[ "$1" = "stop" ] &&
yesno "${rc_cgroup_cleanup}" && \
cgroup_cleanup
if [ "$(command -v cgroup2_remove)" = "cgroup2_remove" ]; then
[ "$1" = stop ] || [ -z "${command}" ] &&
cgroup2_remove
fi
shift
continue 2
else

View File

@@ -14,46 +14,56 @@ description_cgroup_cleanup="Kill all processes in the cgroup"
cgroup_find_path()
{
local OIFS n name dir result
local OIFS name dir result
[ -n "$1" ] || return 0
OIFS="$IFS"
IFS=":"
while read n name dir; do
while read -r _ name dir; do
[ "$name" = "$1" ] && result="$dir"
done < /proc/1/cgroup
IFS="$OIFS"
echo $result
printf "%s" "${result}"
}
cgroup_get_pids()
{
local p
pids=
while read p; do
[ $p -eq $$ ] || pids="${pids} ${p}"
done < /sys/fs/cgroup/openrc/${RC_SVCNAME}/tasks
[ -n "$pids" ]
local cgroup_procs p pids
cgroup_procs="$(cgroup2_find_path)"
[ -n "${cgroup_procs}" ] &&
cgroup_procs="${cgroup_procs}/${RC_SVCNAME}/cgroup.procs" ||
cgroup_procs="/sys/fs/cgroup/openrc/${RC_SVCNAME}/tasks"
[ -f "${cgroup_procs}" ] || return 0
while read -r p; do
[ "$p" -eq $$ ] || pids="${pids} ${p}"
done < "${cgroup_procs}"
printf "%s" "${pids}"
return 0
}
cgroup_running()
{
[ -d "/sys/fs/cgroup/openrc/${RC_SVCNAME}" ]
[ -d "/sys/fs/cgroup/unified/${RC_SVCNAME}" ] ||
[ -d "/sys/fs/cgroup/${RC_SVCNAME}" ] ||
[ -d "/sys/fs/cgroup/openrc/${RC_SVCNAME}" ]
}
cgroup_set_values()
{
[ -n "$1" -a -n "$2" -a -d "/sys/fs/cgroup/$1" ] || return 0
[ -n "$1" ] && [ -n "$2" ] && [ -d "/sys/fs/cgroup/$1" ] || return 0
local controller="$1" h=$(cgroup_find_path "$1")
local controller h
controller="$1"
h=$(cgroup_find_path "$1")
cgroup="/sys/fs/cgroup/${1}${h}openrc_${RC_SVCNAME}"
[ -d "$cgroup" ] || mkdir -p "$cgroup"
set -- $2
local name val
while [ -n "$1" -a "$controller" != "cpuacct" ]; do
while [ -n "$1" ] && [ "$controller" != "cpuacct" ]; do
case "$1" in
$controller.*)
if [ -n "$name" -a -w "$cgroup/$name" -a -n "$val" ]; then
if [ -n "${name}" ] && [ -w "${cgroup}/${name}" ] &&
[ -n "${val}" ]; then
veinfo "$RC_SVCNAME: Setting $cgroup/$name to $val"
printf "%s" "$val" > "$cgroup/$name"
fi
@@ -68,7 +78,7 @@ cgroup_set_values()
esac
shift
done
if [ -n "$name" -a -w "$cgroup/$name" -a -n "$val" ]; then
if [ -n "${name}" ] && [ -w "${cgroup}/${name}" ] && [ -n "${val}" ]; then
veinfo "$RC_SVCNAME: Setting $cgroup/$name to $val"
printf "%s" "$val" > "$cgroup/$name"
fi
@@ -134,21 +144,84 @@ cgroup_set_limits()
return 0
}
cgroup2_find_path()
{
if grep -qw cgroup2 /proc/filesystems; then
case "${rc_cgroup_mode:-hybrid}" in
hybrid) printf "/sys/fs/cgroup/unified" ;;
unified) printf "/sys/fs/cgroup" ;;
esac
fi
return 0
}
cgroup2_remove()
{
local cgroup_path rc_cgroup_path
cgroup_path="$(cgroup2_find_path)"
[ -z "${cgroup_path}" ] && return 0
rc_cgroup_path="${cgroup_path}/${RC_SVCNAME}"
[ ! -d "${rc_cgroup_path}" ] ||
[ ! -e "${rc_cgroup_path}"/cgroup.events ] &&
return 0
grep -qx "$$" "${rc_cgroup_path}/cgroup.procs" &&
printf "%d" 0 > "${cgroup_path}/cgroup.procs"
local key populated vvalue
while read -r key value; do
case "${key}" in
populated) populated=${value} ;;
*) ;;
esac
done < "${rc_cgroup_path}/cgroup.events"
[ "${populated}" = 1 ] && return 0
rmdir "${rc_cgroup_path}"
return 0
}
cgroup2_set_limits()
{
local cgroup_path
cgroup_path="$(cgroup2_find_path)"
[ -d "${cgroup_path}" ] || return 0
rc_cgroup_path="${cgroup_path}/${RC_SVCNAME}"
local OIFS="$IFS"
IFS="
"
[ ! -d "${rc_cgroup_path}" ] && mkdir "${rc_cgroup_path}"
printf "%d" 0 > "${rc_cgroup_path}/cgroup.procs"
echo "${rc_cgroup_settings}" | while IFS="$OIFS" read -r key value; do
[ -z "${key}" ] || [ -z "${value}" ] && continue
[ ! -e "${rc_cgroup_path}/${key}" ] && continue
veinfo "${RC_SVCNAME}: cgroups: ${key} ${value}"
printf "%s" "${value}" > "${rc_cgroup_path}/${key}"
done
IFS="$OIFS"
return 0
}
cgroup_cleanup()
{
cgroup_running || return 0
ebegin "starting cgroups cleanup"
for sig in TERM QUIT INT; do
cgroup_get_pids || { eend 0 "finished" ; return 0 ; }
for i in 0 1; do
kill -s $sig $pids
for j in 0 1 2; do
cgroup_get_pids || { eend 0 "finished" ; return 0 ; }
sleep 1
done
done 2>/dev/null
done
cgroup_get_pids || { eend 0 "finished" ; return 0; }
kill -9 $pids
eend $(cgroup_running && echo 1 || echo 0) "fail to stop all processes"
local pids loops=0
pids="$(cgroup_get_pids)"
if [ -n "${pids}" ]; then
kill -s CONT ${pids} 2> /dev/null
kill -s "${stopsig:-TERM}" ${pids} 2> /dev/null
yesno "${rc_send_sighup:-no}" &&
kill -s HUP ${pids} 2> /dev/null
kill -s "${stopsig:-TERM}" ${pids} 2> /dev/null
while [ -n "$(cgroup_get_pids)" ] &&
[ "${loops}" -lt "${rc_timeout_stopsec:-90}" ]; do
loops=$((loops+1))
sleep 1
done
pids="$(cgroup_get_pids)"
[ -n "${pids}" ] && yesno "${rc_send_sigkill:-yes}" &&
kill -s KILL ${pids} 2> /dev/null
fi
cgroup2_remove
[ -z "$(cgroup_get_pids)" ]
eend $? "Unable to stop all processes"
return 0
}

View File

@@ -119,6 +119,13 @@ get_bootparam_value()
echo $result
}
need_if_exists()
{
for x; do
rc-service --exists "${x}" && need "${x}"
done
}
# Called from openrc-run.sh or gendepends.sh
_get_containers() {
local c

View File

@@ -12,6 +12,20 @@
[ -z "${s6_service_path}" ] && s6_service_path="/var/svc.d/${RC_SVCNAME}"
_s6_force_kill() {
local pid
s6_service_link="${RC_SVCDIR}/s6-scan/${s6_service_path##*/}"
pid="${3%)}"
[ -z "${pid}" ] && return 0
if kill -0 "${pid}" 2> /dev/null; then
ewarn "Sending DOWN & KILL for ${RC_SVCNAME}"
s6-svc -dk "${s6_service_link}"
sleep 1
kill -0 "${pid}" 2>/dev/null && return 1
fi
return 0
}
s6_start()
{
if [ ! -d "${s6_service_path}" ]; then
@@ -41,7 +55,11 @@ s6_stop()
fi
s6_service_link="${RC_SVCDIR}/s6-scan/${s6_service_path##*/}"
ebegin "Stopping ${name:-$RC_SVCNAME}"
s6-svc -wD -d -T ${s6_service_timeout_stop:-10000} "${s6_service_link}"
s6-svc -d -wD -T ${s6_service_timeout_stop:-60000} "${s6_service_link}"
set -- $(s6-svstat "${s6_service_link}")
[ "$1" = "up" ] &&
yesno "${s6_force_kill:-yes}" &&
_s6_force_kill "$@"
set -- $(s6-svstat "${s6_service_link}")
[ "$1" = "down" ]
eend $? "Failed to stop ${name:-$RC_SVCNAME}"

View File

@@ -44,9 +44,13 @@ ssd_start()
eval start-stop-daemon --start \
--exec $command \
${chroot:+--chroot} $chroot \
${directory:+--chdir} $directory \
${output_log+--stdout} $output_log \
${error_log+--stderr} $error_log \
${procname:+--name} $procname \
${pidfile:+--pidfile} $pidfile \
${command_user+--user} $command_user \
${umask+--umask} $umask \
$_background $start_stop_daemon_args \
-- $command_args $command_args_background
if eend $? "Failed to start ${name:-$RC_SVCNAME}"; then

View File

@@ -22,13 +22,18 @@ supervise_start()
# The eval call is necessary for cases like:
# command_args="this \"is a\" test"
# to work properly.
eval supervise-daemon --start \
eval supervise-daemon "${RC_SVCNAME}" --start \
${retry:+--retry} $retry \
${directory:+--chdir} $directory \
${chroot:+--chroot} $chroot \
${output_log+--stdout} ${output_log} \
${error_log+--stderr} $error_log \
${pidfile:+--pidfile} $pidfile \
${respawn_delay:+--respawn-delay} $respawn_delay \
${respawn_max:+--respawn-max} $respawn_max \
${respawn_period:+--respawn-period} $respawn_period \
${command_user+--user} $command_user \
${umask+--umask} $umask \
$supervise_daemon_args \
$command \
-- $command_args $command_args_foreground
@@ -48,14 +53,48 @@ supervise_stop()
pidfile="${startpidfile:-$pidfile}"
[ -n "$pidfile" ] || return 0
ebegin "Stopping ${name:-$RC_SVCNAME}"
supervise-daemon --stop \
supervise-daemon "${RC_SVCNAME}" --stop \
${pidfile:+--pidfile} $chroot$pidfile \
${stopsig:+--signal} $stopsig
eend $? "Failed to stop ${name:-$RC_SVCNAME}"
}
_check_supervised()
{
local child_pid start_time
child_pid="$(service_get_value "child_pid")"
start_time="$(service_get_value "start_time")"
if [ -n "${child_pid}" ] && [ -n "${start_time}" ]; then
return 1
fi
return 0
}
supervise_status()
{
_status
if service_stopping; then
ewarn "status: stopping"
return 4
elif service_starting; then
ewarn "status: starting"
return 8
elif service_inactive; then
ewarn "status: inactive"
return 16
elif service_started; then
if service_crashed; then
if ! _check_supervised; then
eerror "status: unsupervised"
return 64
fi
eerror "status: crashed"
return 32
fi
einfo "status: started"
return 0
else
einfo "status: stopped"
return 3
fi
}

View File

@@ -28,9 +28,11 @@
#if __GNUC__ > 2 || defined(__INTEL_COMPILER)
# define _dead __attribute__((__noreturn__))
# define _unused __attribute__((__unused__))
# define _xasprintf(a, b) __attribute__((__format__(__printf__, a, b)))
#else
# define _dead
# define _unused
# define _xasprintf(a, b)
#endif
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
@@ -53,6 +55,7 @@
} while (/* CONSTCOND */ 0)
#endif
#include <stdarg.h>
#include <stdbool.h>
#include <sys/stat.h>
@@ -96,8 +99,10 @@ _unused static char *xstrdup(const char *str)
#undef ERRX
/* basename_c never modifies the argument. As such, if there is a trailing
* slash then an empty string is returned. */
/*
* basename_c never modifies the argument. As such, if there is a trailing
* slash then an empty string is returned.
*/
_unused static const char *basename_c(const char *path)
{
const char *slash = strrchr(path, '/');
@@ -121,4 +126,49 @@ _unused static bool existss(const char *pathname)
return (stat(pathname, &buf) == 0 && buf.st_size != 0);
}
/*
* This is an OpenRC specific version of the asprintf() function.
* We do this to avoid defining the _GNU_SOURCE feature test macro on
* glibc systems and to insure that we have a consistent function across
* platforms. This also allows us to call our xmalloc and xrealloc
* functions to handle memory allocation.
* this function was originally written by Mike Frysinger.
*/
_unused _xasprintf(2,3) static int xasprintf(char **strp, const char *fmt, ...)
{
va_list ap;
int len;
int memlen;
char *ret;
/*
* Start with a buffer size that should cover the vast majority of uses
* (path construction).
*/
memlen = 4096;
ret = xmalloc(memlen);
va_start(ap, fmt);
len = vsnprintf(ret, memlen, fmt, ap);
va_end(ap);
if (len >= memlen) {
/*
* Output was truncated, so increase buffer to exactly what we need.
*/
memlen = len + 1;
ret = xrealloc(ret, memlen);
va_start(ap, fmt);
len = vsnprintf(ret, len + 1, fmt, ap);
va_end(ap);
}
if (len < 0 || len >= memlen) {
/* Give up! */
fprintf(stderr, "xasprintf: unable to format a buffer\n");
free(ret);
exit(1);
}
*strp = ret;
return len;
}
#endif

View File

@@ -47,6 +47,7 @@ bool rc_conf_yesno(const char *var);
void env_filter(void);
void env_config(void);
int signal_setup(int sig, void (*handler)(int));
int signal_setup_restart(int sig, void (*handler)(int));
int svc_lock(const char *);
int svc_unlock(const char *, int);
pid_t exec_service(const char *, const char *);
@@ -71,5 +72,6 @@ bool _rc_can_find_pids(void);
RC_SERVICE lookup_service_state(const char *service);
void from_time_t(char *time_string, time_t tv);
time_t to_time_t(char *timestring);
pid_t get_pid(const char *applet, const char *pidfile);
#endif

26
src/includes/rc-wtmp.h Normal file
View File

@@ -0,0 +1,26 @@
/*
* rc-wtmp.h
* This is private to us and not for user consumption
*/
/*
* Copyright (c) 2017 The OpenRC Authors.
* See the Authors file at the top-level directory of this distribution and
* https://github.com/OpenRC/openrc/blob/master/AUTHORS
*
* This file is part of OpenRC. It is subject to the license terms in
* the LICENSE file found in the top-level directory of this
* distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
* This file may not be copied, modified, propagated, or distributed
* except according to the terms contained in the LICENSE file.
*/
#ifndef __RC_WTMP_H__
#define __RC_WTMP_H__
#include <utmp.h>
void log_wtmp(const char *user, const char *id, pid_t pid, int type,
const char *line);
#endif

View File

@@ -80,9 +80,12 @@ rc_find_pids(const char *exec, const char *const *argv, uid_t uid, pid_t pid)
DIR *procdir;
struct dirent *entry;
FILE *fp;
int rc;
bool container_pid = false;
bool openvz_host = false;
char *line = NULL;
char my_ns[30];
char proc_ns[30];
size_t len = 0;
pid_t p;
char buffer[PATH_MAX];
@@ -131,6 +134,14 @@ rc_find_pids(const char *exec, const char *const *argv, uid_t uid, pid_t pid)
}
}
memset(my_ns, 0, sizeof(my_ns));
memset(proc_ns, 0, sizeof(proc_ns));
if (exists("/proc/self/ns/pid")) {
rc = readlink("/proc/self/ns/pid", my_ns, sizeof(my_ns));
if (rc <= 0)
my_ns[0] = '\0';
}
while ((entry = readdir(procdir)) != NULL) {
if (sscanf(entry->d_name, "%d", &p) != 1)
continue;
@@ -138,6 +149,14 @@ rc_find_pids(const char *exec, const char *const *argv, uid_t uid, pid_t pid)
continue;
if (pid != 0 && pid != p)
continue;
snprintf(buffer, sizeof(buffer), "/proc/%d/ns/pid", p);
if (exists(buffer)) {
rc = readlink(buffer, proc_ns, sizeof(proc_ns));
if (rc <= 0)
proc_ns[0] = '\0';
}
if (strlen(my_ns) && strlen (proc_ns) && strcmp(my_ns, proc_ns))
continue;
if (uid) {
snprintf(buffer, sizeof(buffer), "/proc/%d", p);
if (stat(buffer, &sb) != 0 || sb.st_uid != uid)

View File

@@ -542,52 +542,41 @@ rc_deptree_order(const RC_DEPTREE *deptree, const char *runlevel, int options)
}
librc_hidden_def(rc_deptree_order)
/* Given a time, recurse the target path to find out if there are
any older (or newer) files. If false, sets the time to the
oldest (or newest) found.
*/
static bool
mtime_check(const char *source, const char *target, bool newer,
deep_mtime_check(const char *target, bool newer,
time_t *rel, char *file)
{
struct stat buf;
time_t mtime;
bool retval = true;
DIR *dp;
struct dirent *d;
char path[PATH_MAX];
int serrno = errno;
/* We have to exist */
if (stat(source, &buf) != 0)
return false;
mtime = buf.st_mtime;
/* If target does not exist, return true to mimic shell test */
if (stat(target, &buf) != 0)
return true;
if (newer) {
if (mtime < buf.st_mtime) {
if (rel == NULL)
return false;
if (*rel < buf.st_mtime) {
retval = false;
}
if (rel != NULL) {
if (*rel < buf.st_mtime) {
if (file)
strlcpy(file, target, PATH_MAX);
*rel = buf.st_mtime;
}
if (file)
strlcpy(file, target, PATH_MAX);
*rel = buf.st_mtime;
}
} else {
if (mtime > buf.st_mtime) {
if (rel == NULL)
return false;
if (*rel > buf.st_mtime) {
retval = false;
}
if (rel != NULL) {
if (*rel > buf.st_mtime) {
if (file)
strlcpy(file, target, PATH_MAX);
*rel = buf.st_mtime;
}
if (file)
strlcpy(file, target, PATH_MAX);
*rel = buf.st_mtime;
}
}
@@ -602,16 +591,38 @@ mtime_check(const char *source, const char *target, bool newer,
if (d->d_name[0] == '.')
continue;
snprintf(path, sizeof(path), "%s/%s", target, d->d_name);
if (!mtime_check(source, path, newer, rel, file)) {
if (!deep_mtime_check(path, newer, rel, file)) {
retval = false;
if (rel == NULL)
break;
}
}
closedir(dp);
return retval;
}
/* Recursively check if target is older/newer than source.
* If false, return the filename and most different time (if
* the return value arguments are non-null).
*/
static bool
mtime_check(const char *source, const char *target, bool newer,
time_t *rel, char *file)
{
struct stat buf;
time_t mtime;
bool retval = true;
/* We have to exist */
if (stat(source, &buf) != 0)
return false;
mtime = buf.st_mtime;
retval = deep_mtime_check(target,newer,&mtime,file);
if (rel) {
*rel = mtime;
}
return retval;
}
bool
rc_newer_than(const char *source, const char *target,
time_t *newest, char *file)
@@ -670,6 +681,8 @@ rc_deptree_update_needed(time_t *newest, char *file)
RC_STRINGLIST *config;
RC_STRING *s;
int i;
struct stat buf;
time_t mtime;
/* Create base directories if needed */
for (i = 0; depdirs[i]; i++)
@@ -677,42 +690,48 @@ rc_deptree_update_needed(time_t *newest, char *file)
fprintf(stderr, "mkdir `%s': %s\n", depdirs[i], strerror(errno));
/* Quick test to see if anything we use has changed and we have
* data in our deptree */
if (!existss(RC_DEPTREE_CACHE))
return true;
if (!rc_newer_than(RC_DEPTREE_CACHE, RC_INITDIR, newest, file))
return true;
if (!rc_newer_than(RC_DEPTREE_CACHE, RC_CONFDIR, newest, file))
return true;
* data in our deptree. */
if (stat(RC_DEPTREE_CACHE, &buf) == 0) {
mtime = buf.st_mtime;
} else {
/* No previous cache found.
* We still run the scan, in case of clock skew; we still need to return
* the newest time.
*/
newer = true;
mtime = time(NULL);
}
newer |= !deep_mtime_check(RC_INITDIR,true,&mtime,file);
newer |= !deep_mtime_check(RC_CONFDIR,true,&mtime,file);
#ifdef RC_PKG_INITDIR
if (!rc_newer_than(RC_DEPTREE_CACHE, RC_PKG_INITDIR, newest, file))
return true;
newer |= !deep_mtime_check(RC_PKG_INITDIR,true,&mtime,file);
#endif
#ifdef RC_PKG_CONFDIR
if (!rc_newer_than(RC_DEPTREE_CACHE, RC_PKG_CONFDIR, newest, file))
return true;
newer |= !deep_mtime_check(RC_PKG_CONFDIR,true,&mtime,file);
#endif
#ifdef RC_LOCAL_INITDIR
if (!rc_newer_than(RC_DEPTREE_CACHE, RC_LOCAL_INITDIR, newest, file))
return true;
#ifdef RC_LOCAL_INITDIRs
newer |= !deep_mtime_check(RC_LOCAL_INITDIR,true,&mtime,file);
#endif
#ifdef RC_LOCAL_CONFDIR
if (!rc_newer_than(RC_DEPTREE_CACHE, RC_LOCAL_CONFDIR, newest, file))
return true;
newer |= !deep_mtime_check(RC_LOCAL_CONFDIR,true,&mtime,file);
#endif
if (!rc_newer_than(RC_DEPTREE_CACHE, RC_CONF, newest, file))
return true;
newer |= !deep_mtime_check(RC_CONF,true,&mtime,file);
/* Some init scripts dependencies change depending on config files
* outside of baselayout, like syslog-ng, so we check those too. */
config = rc_config_list(RC_DEPCONFIG);
TAILQ_FOREACH(s, config, entries) {
if (!rc_newer_than(RC_DEPTREE_CACHE, s->value, newest, file)) {
newer = true;
break;
}
newer |= !deep_mtime_check(s->value, true, &mtime, file);
}
rc_stringlist_free(config);
/* Return newest file time, if requested */
if ((newer) && (newest != NULL)) {
*newest = mtime;
}
return newer;
}
librc_hidden_def(rc_deptree_update_needed)

View File

@@ -894,12 +894,15 @@ rc_service_value_set(const char *service, const char *option,
return false;
snprintf(p, sizeof(file) - (p - file), "/%s", option);
if (!(fp = fopen(file, "w")))
return false;
if (value)
if (value) {
if (!(fp = fopen(file, "w")))
return false;
fprintf(fp, "%s", value);
fclose(fp);
return true;
fclose(fp);
} else {
unlink(file);
}
return true;
}
librc_hidden_def(rc_service_value_set)

1
src/rc/.gitignore vendored
View File

@@ -62,3 +62,4 @@ openrc
openrc-init
openrc-run
openrc-shutdown
kill_all

View File

@@ -14,7 +14,7 @@ SRCS+= rc-selinux.c
endif
ifeq (${OS},Linux)
SRCS+= openrc-init.c openrc-shutdown.c
SRCS+= kill_all.c openrc-init.c openrc-shutdown.c rc-wtmp.c
endif
CLEANFILES= version.h rc-selinux.o
@@ -24,7 +24,7 @@ SBINDIR= ${PREFIX}/sbin
LINKDIR= ${LIBEXECDIR}
BINPROGS= rc-status
SBINPROGS = openrc openrc-run rc rc-service rc-update runscript service \
SBINPROGS = openrc openrc-run rc rc-service rc-update runscript \
start-stop-daemon supervise-daemon
RC_BINPROGS= einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \
eindent eoutdent esyslog eval_ecolors ewaitfile \
@@ -44,6 +44,7 @@ RC_SBINPROGS= mark_service_starting mark_service_started \
rc-abort swclock
ifeq (${OS},Linux)
RC_BINPROGS+= kill_all
SBINPROGS+= openrc-init openrc-shutdown
endif
@@ -99,6 +100,9 @@ checkpath: rc-selinux.o
endif
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
kill_all: kill_all.o _usage.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \
eindent eoutdent esyslog eval_ecolors ewaitfile \
veinfo vewarn vebegin veend vewend veindent veoutdent: do_e.o rc-misc.o
@@ -107,7 +111,7 @@ veinfo vewarn vebegin veend vewend veindent veoutdent: do_e.o rc-misc.o
fstabinfo: fstabinfo.o _usage.o rc-misc.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
openrc-init: openrc-init.o
openrc-init: openrc-init.o rc-wtmp.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
is_newer_than: is_newer_than.o rc-misc.o
@@ -128,7 +132,7 @@ mountinfo: mountinfo.o _usage.o rc-misc.o
openrc rc: rc.o rc-logger.o rc-misc.o rc-plugin.o _usage.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
openrc-shutdown: openrc-shutdown.o _usage.o
openrc-shutdown: openrc-shutdown.o _usage.o rc-wtmp.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
openrc-run runscript: openrc-run.o _usage.o rc-misc.o rc-plugin.o
@@ -146,16 +150,16 @@ rc-depend: rc-depend.o _usage.o rc-misc.o
rc-status: rc-status.o _usage.o rc-misc.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
rc-service service: rc-service.o _usage.o rc-misc.o
rc-service: rc-service.o _usage.o rc-misc.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
rc-update: rc-update.o _usage.o rc-misc.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
start-stop-daemon: start-stop-daemon.o _usage.o rc-misc.o
start-stop-daemon: start-stop-daemon.o _usage.o rc-misc.o rc-schedules.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
supervise-daemon: supervise-daemon.o _usage.o rc-misc.o
supervise-daemon: supervise-daemon.o _usage.o rc-misc.o rc-schedules.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
service_get_value service_set_value get_options save_options: do_value.o rc-misc.o

View File

@@ -73,25 +73,32 @@ static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode,
inode_t type, bool trunc, bool chowner, bool selinux_on)
{
struct stat st;
int fd, flags;
int fd;
int flags;
int r;
int readfd;
int readflags;
int u;
memset(&st, 0, sizeof(st));
if (lstat(path, &st) || trunc) {
flags = O_CREAT|O_NDELAY|O_WRONLY|O_NOCTTY;
readflags = O_NDELAY|O_NOCTTY|O_RDONLY;
#ifdef O_CLOEXEC
flags |= O_CLOEXEC;
readflags |= O_CLOEXEC;
#endif
#ifdef O_NOFOLLOW
flags |= O_NOFOLLOW;
readflags |= O_NOFOLLOW;
#endif
if (trunc)
flags |= O_TRUNC;
readfd = open(path, readflags);
if (readfd == -1 || (type == inode_file && trunc)) {
if (type == inode_file) {
einfo("%s: creating file", path);
if (!mode) /* 664 */
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
flags = O_CREAT|O_NDELAY|O_WRONLY|O_NOCTTY;
#ifdef O_CLOEXEC
flags |= O_CLOEXEC;
#endif
#ifdef O_NOFOLLOW
flags |= O_NOFOLLOW;
#endif
if (trunc)
flags |= O_TRUNC;
u = umask(0);
fd = open(path, flags, mode);
umask(u);
@@ -99,7 +106,9 @@ static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode,
eerror("%s: open: %s", applet, strerror(errno));
return -1;
}
close (fd);
if (readfd != -1 && trunc)
close(readfd);
readfd = fd;
} else if (type == inode_dir) {
einfo("%s: creating directory", path);
if (!mode) /* 775 */
@@ -113,7 +122,12 @@ static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode,
strerror (errno));
return -1;
}
mode = 0;
readfd = open(path, readflags);
if (readfd == -1) {
eerror("%s: unable to open directory: %s", applet,
strerror(errno));
return -1;
}
} else if (type == inode_fifo) {
einfo("%s: creating fifo", path);
if (!mode) /* 600 */
@@ -126,56 +140,76 @@ static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode,
strerror (errno));
return -1;
}
readfd = open(path, readflags);
if (readfd == -1) {
eerror("%s: unable to open fifo: %s", applet,
strerror(errno));
return -1;
}
}
} else {
}
if (fstat(readfd, &st) != -1) {
if (type != inode_dir && S_ISDIR(st.st_mode)) {
eerror("%s: is a directory", path);
close(readfd);
return 1;
}
if (type != inode_file && S_ISREG(st.st_mode)) {
eerror("%s: is a file", path);
close(readfd);
return 1;
}
if (type != inode_fifo && S_ISFIFO(st.st_mode)) {
eerror("%s: is a fifo", path);
close(readfd);
return -1;
}
}
if (mode && (st.st_mode & 0777) != mode) {
if ((type != inode_dir) && (st.st_nlink > 1)) {
eerror("%s: chmod: %s %s", applet, "Too many hard links to", path);
return -1;
if (mode && (st.st_mode & 0777) != mode) {
if ((type != inode_dir) && (st.st_nlink > 1)) {
eerror("%s: chmod: %s %s", applet, "Too many hard links to", path);
close(readfd);
return -1;
}
if (S_ISLNK(st.st_mode)) {
eerror("%s: chmod: %s %s", applet, path, " is a symbolic link");
close(readfd);
return -1;
}
einfo("%s: correcting mode", path);
if (fchmod(readfd, mode)) {
eerror("%s: chmod: %s", applet, strerror(errno));
close(readfd);
return -1;
}
}
if (S_ISLNK(st.st_mode)) {
eerror("%s: chmod: %s %s", applet, path, " is a symbolic link");
return -1;
}
einfo("%s: correcting mode", path);
if (chmod(path, mode)) {
eerror("%s: chmod: %s", applet, strerror(errno));
return -1;
}
}
if (chowner && (st.st_uid != uid || st.st_gid != gid)) {
if ((type != inode_dir) && (st.st_nlink > 1)) {
eerror("%s: chown: %s %s", applet, "Too many hard links to", path);
return -1;
}
if (S_ISLNK(st.st_mode)) {
eerror("%s: chown: %s %s", applet, path, " is a symbolic link");
return -1;
}
einfo("%s: correcting owner", path);
if (chown(path, uid, gid)) {
eerror("%s: chown: %s", applet, strerror(errno));
return -1;
if (chowner && (st.st_uid != uid || st.st_gid != gid)) {
if ((type != inode_dir) && (st.st_nlink > 1)) {
eerror("%s: chown: %s %s", applet, "Too many hard links to", path);
close(readfd);
return -1;
}
if (S_ISLNK(st.st_mode)) {
eerror("%s: chown: %s %s", applet, path, " is a symbolic link");
close(readfd);
return -1;
}
einfo("%s: correcting owner", path);
if (fchown(readfd, uid, gid)) {
eerror("%s: chown: %s", applet, strerror(errno));
close(readfd);
return -1;
}
}
if (selinux_on)
selinux_util_label(path);
} else {
eerror("fstat: %s: %s", path, strerror(errno));
close(readfd);
return -1;
}
if (selinux_on)
selinux_util_label(path);
close(readfd);
return 0;
}

View File

@@ -35,11 +35,11 @@
# define GET_ENT getmntent (fp)
# define GET_ENT_FILE(_name) getmntfile (_name)
# define END_ENT endmntent (fp)
# define ENT_BLOCKDEVICE(_ent) ent->mnt_fsname
# define ENT_FILE(_ent) ent->mnt_dir
# define ENT_TYPE(_ent) ent->mnt_type
# define ENT_OPTS(_ent) ent->mnt_opts
# define ENT_PASS(_ent) ent->mnt_passno
# define ENT_BLOCKDEVICE(_ent) (_ent)->mnt_fsname
# define ENT_FILE(_ent) (_ent)->mnt_dir
# define ENT_TYPE(_ent) (_ent)->mnt_type
# define ENT_OPTS(_ent) (_ent)->mnt_opts
# define ENT_PASS(_ent) (_ent)->mnt_passno
#else
# define HAVE_GETFSENT
# include <fstab.h>
@@ -48,11 +48,11 @@
# define GET_ENT getfsent ()
# define GET_ENT_FILE(_name) getfsfile (_name)
# define END_ENT endfsent ()
# define ENT_BLOCKDEVICE(_ent) ent->fs_spec
# define ENT_TYPE(_ent) ent->fs_vfstype
# define ENT_FILE(_ent) ent->fs_file
# define ENT_OPTS(_ent) ent->fs_mntops
# define ENT_PASS(_ent) ent->fs_passno
# define ENT_BLOCKDEVICE(_ent) (_ent)->fs_spec
# define ENT_TYPE(_ent) (_ent)->fs_vfstype
# define ENT_FILE(_ent) (_ent)->fs_file
# define ENT_OPTS(_ent) (_ent)->fs_mntops
# define ENT_PASS(_ent) (_ent)->fs_passno
#endif
#include "einfo.h"
@@ -114,24 +114,24 @@ do_mount(struct ENT *ent, bool remount)
argv[0] = UNCONST("mount");
argv[1] = UNCONST("-o");
argv[2] = ENT_OPTS(*ent);
argv[2] = ENT_OPTS(ent);
argv[3] = UNCONST("-t");
argv[4] = ENT_TYPE(*ent);
argv[4] = ENT_TYPE(ent);
if (!remount) {
argv[5] = ENT_BLOCKDEVICE(*ent);
argv[6] = ENT_FILE(*ent);
argv[5] = ENT_BLOCKDEVICE(ent);
argv[6] = ENT_FILE(ent);
argv[7] = NULL;
} else {
#ifdef __linux__
argv[5] = UNCONST("-o");
argv[6] = UNCONST("remount");
argv[7] = ENT_BLOCKDEVICE(*ent);
argv[8] = ENT_FILE(*ent);
argv[7] = ENT_BLOCKDEVICE(ent);
argv[8] = ENT_FILE(ent);
argv[9] = NULL;
#else
argv[5] = UNCONST("-u");
argv[6] = ENT_BLOCKDEVICE(*ent);
argv[7] = ENT_FILE(*ent);
argv[6] = ENT_BLOCKDEVICE(ent);
argv[7] = ENT_FILE(ent);
argv[8] = NULL;
#endif
}
@@ -141,7 +141,7 @@ do_mount(struct ENT *ent, bool remount)
/* NOTREACHED */
case 0:
execvp(argv[0], argv);
eerror("%s: execv: %s", applet, strerror(errno));
eerror("%s: execvp: %s", applet, strerror(errno));
_exit(EXIT_FAILURE);
/* NOTREACHED */
default:

260
src/rc/kill_all.c Normal file
View File

@@ -0,0 +1,260 @@
/*
* kill_all.c
* Sends a signal to all processes on the system.
*/
/*
* Copyright (c) 2017 The OpenRC Authors.
* See the Authors file at the top-level directory of this distribution and
* https://github.com/OpenRC/openrc/blob/master/AUTHORS
*
* This file is part of OpenRC. It is subject to the license terms in
* the LICENSE file found in the top-level directory of this
* distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
* This file may not be copied, modified, propagated, or distributed
* except according to the terms contained in the LICENSE file.
*/
#include <dirent.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "einfo.h"
#include "rc.h"
#include "rc-misc.h"
#include "_usage.h"
const char *applet = NULL;
const char *extraopts = "[signal number]";
const char *getoptstring = "do:" getoptstring_COMMON;
const struct option longopts[] = {
{ "dry-run", 0, NULL, 'd' },
{ "omit", 1, NULL, 'o' },
longopts_COMMON
};
const char * const longopts_help[] = {
"print what would be done",
"omit this pid (can be repeated)",
longopts_help_COMMON
};
const char *usagestring = NULL;
static int mount_proc(void)
{
pid_t pid;
pid_t rc;
int status;
if (exists("/proc/version"))
return 0;
pid = fork();
switch(pid) {
case -1:
syslog(LOG_ERR, "Unable to fork");
return -1;
break;
case 0:
/* attempt to mount /proc */
execlp("mount", "mount", "-t", "proc", "proc", "/proc", NULL);
syslog(LOG_ERR, "Unable to execute mount");
exit(1);
break;
default:
/* wait for child process */
while ((rc = wait(&status)) != pid)
if (rc < 0 && errno == ECHILD)
break;
if (rc != pid || WEXITSTATUS(status) != 0)
syslog(LOG_ERR, "mount returned non-zero exit status");
break;
}
if (! exists("/proc/version")) {
syslog(LOG_ERR, "Could not mount /proc");
return -1;
}
return 0;
}
static bool is_user_process(pid_t pid)
{
char *buf = NULL;
FILE *fp;
char *path = NULL;
pid_t temp_pid;
size_t size;
bool user_process = true;
while (pid >0 && user_process) {
if (pid == 2) {
user_process = false;
continue;
}
xasprintf(&path, "/proc/%d/status", pid);
fp = fopen(path, "r");
free(path);
/*
* if we could not open the file, the process disappeared, which
* leaves us no way to determine for sure whether it was a user
* process or kernel thread, so we say it is a kernel thread to
* avoid accidentally killing it.
*/
if (!fp) {
user_process = false;
continue;
}
temp_pid = -1;
while (! feof(fp)) {
buf = NULL;
if (getline(&buf, &size, fp) != -1) {
sscanf(buf, "PPid: %d", &temp_pid);
free(buf);
} else {
free(buf);
break;
}
}
fclose(fp);
if (temp_pid == -1) {
syslog(LOG_ERR, "Unable to read pid from /proc/%d/status", pid);
user_process = false;
continue;
}
pid = temp_pid;
}
return user_process;
}
static int signal_processes(int sig, RC_STRINGLIST *omits, bool dryrun)
{
sigset_t signals;
sigset_t oldsigs;
DIR *dir;
struct dirent *d;
char *buf = NULL;
pid_t pid;
int sendcount = 0;
kill(-1, SIGSTOP);
sigfillset(&signals);
sigemptyset(&oldsigs);
sigprocmask(SIG_SETMASK, &signals, &oldsigs);
/*
* Open the /proc directory.
* CWD must be /proc to avoid problems if / is affected by the killing
* (i.e. depends on fuse).
*/
if (chdir("/proc") == -1) {
syslog(LOG_ERR, "chdir /proc failed");
sigprocmask(SIG_SETMASK, &oldsigs, NULL);
kill(-1, SIGCONT);
return -1;
}
dir = opendir(".");
if (!dir) {
syslog(LOG_ERR, "cannot opendir(/proc)");
sigprocmask(SIG_SETMASK, &oldsigs, NULL);
kill(-1, SIGCONT);
return -1;
}
/* Walk through the directory. */
while ((d = readdir(dir)) != NULL) {
/* Is this a process? */
pid = (pid_t) atoi(d->d_name);
if (pid == 0)
continue;
/* Is this a process we have been requested to omit? */
if (buf) {
free(buf);
buf = NULL;
}
xasprintf(&buf, "%d", pid);
if (rc_stringlist_find(omits, buf))
continue;
/* Is this process in our session? */
if (getsid(getpid()) == getsid(pid))
continue;
/* Is this a kernel thread? */
if (!is_user_process(pid))
continue;
if (dryrun)
einfo("Would send signal %d to process %d", sig, pid);
else if (kill(pid, sig) == 0)
sendcount++;
}
closedir(dir);
sigprocmask(SIG_SETMASK, &oldsigs, NULL);
kill(-1, SIGCONT);
return sendcount;
}
int main(int argc, char **argv)
{
char *arg = NULL;
int opt;
bool dryrun = false;
RC_STRINGLIST *omits = rc_stringlist_new();
int sig = SIGKILL;
char *here;
char *token;
/* Ensure that we are only quiet when explicitly told to be */
unsetenv("EINFO_QUIET");
applet = basename_c(argv[0]);
rc_stringlist_addu(omits, "1");
while ((opt = getopt_long(argc, argv, getoptstring,
longopts, (int *) 0)) != -1)
{
switch (opt) {
case 'd':
dryrun = true;
break;
case 'o':
here = optarg;
while ((token = strsep(&here, ",;:"))) {
if ((pid_t) atoi(token) > 0)
rc_stringlist_addu(omits, token);
else {
eerror("Invalid omit pid value %s", token);
usage(EXIT_FAILURE);
}
}
break;
case_RC_COMMON_GETOPT
}
}
if (argc > optind) {
arg = argv[optind];
sig = atoi(arg);
if (sig <= 0 || sig > 31) {
rc_stringlist_free(omits);
eerror("Invalid signal %s", arg);
usage(EXIT_FAILURE);
}
}
openlog(applet, LOG_CONS|LOG_PID, LOG_DAEMON);
if (mount_proc() != 0) {
rc_stringlist_free(omits);
eerrorx("Unable to mount /proc file system");
}
signal_processes(sig, omits, dryrun);
rc_stringlist_free(omits);
return 0;
}

View File

@@ -248,7 +248,6 @@ find_mounts(struct args *args)
struct opt *o;
int netdev;
char *tmp;
size_t l;
if ((nmnts = getmntinfo(&mnts, MNT_NOWAIT)) == 0)
eerrorx("getmntinfo: %s", strerror (errno));
@@ -264,11 +263,7 @@ find_mounts(struct args *args)
if (! options)
options = xstrdup(o->o_name);
else {
l = strlen(options) +
strlen(o->o_name) + 2;
tmp = xmalloc(sizeof (char) * l);
snprintf(tmp, l, "%s,%s", options,
o->o_name);
xasprintf(&tmp, "%s,%s", options, o->o_name);
free(options);
options = tmp;
}
@@ -315,6 +310,7 @@ find_mounts(struct args *args)
{
FILE *fp;
char *buffer;
size_t size;
char *p;
char *from;
char *to;
@@ -329,8 +325,8 @@ find_mounts(struct args *args)
list = rc_stringlist_new();
buffer = xmalloc(sizeof(char) * PATH_MAX * 3);
while (fgets(buffer, PATH_MAX * 3, fp)) {
buffer = NULL;
while (getline(&buffer, &size, fp) != -1) {
netdev = -1;
p = buffer;
from = strsep(&p, " ");
@@ -346,6 +342,8 @@ find_mounts(struct args *args)
}
process_mount(list, args, from, to, fst, opts, netdev);
free(buffer);
buffer = NULL;
}
free(buffer);
fclose(fp);
@@ -380,7 +378,7 @@ int main(int argc, char **argv)
regex_t *skip_point_regex = NULL;
RC_STRINGLIST *nodes;
RC_STRING *s;
char real_path[PATH_MAX + 1];
char *real_path = NULL;
int opt;
int result;
char *this_path;
@@ -450,9 +448,12 @@ int main(int argc, char **argv)
eerrorx("%s: `%s' is not a mount point",
argv[0], argv[optind]);
this_path = argv[optind++];
if (realpath(this_path, real_path))
real_path = realpath(this_path, NULL);
if (real_path)
this_path = real_path;
rc_stringlist_add(args.mounts, this_path);
free(real_path);
real_path = NULL;
}
nodes = find_mounts(&args);
rc_stringlist_free(args.mounts);

View File

@@ -32,8 +32,10 @@
#include "helpers.h"
#include "rc.h"
#include "rc-wtmp.h"
#include "version.h"
static const char *path_default = "/sbin:/usr/sbin:/bin:/usr/bin";
static const char *rc_default_runlevel = "default";
static pid_t do_openrc(const char *runlevel)
@@ -52,7 +54,7 @@ static pid_t do_openrc(const char *runlevel)
sigemptyset(&signals);
sigprocmask(SIG_SETMASK, &signals, NULL);
printf("Starting %s runlevel\n", runlevel);
execl("/sbin/openrc", "/sbin/openrc", runlevel, NULL);
execlp("openrc", "openrc", runlevel, NULL);
perror("exec");
break;
default:
@@ -82,11 +84,12 @@ static void init(const char *default_runlevel)
}
pid = do_openrc(runlevel);
waitpid(pid, NULL, 0);
log_wtmp("reboot", "~~", 0, RUN_LVL, "~~");
}
static void handle_reexec(char *my_name)
{
execl(my_name, my_name, "reexec", NULL);
execlp(my_name, my_name, "reexec", NULL);
return;
}
@@ -105,6 +108,14 @@ static void handle_shutdown(const char *runlevel, int cmd)
reboot(cmd);
}
static void handle_single(void)
{
pid_t pid;
pid = do_openrc("single");
while (waitpid(pid, NULL, 0) != pid);
}
static void reap_zombies(void)
{
pid_t pid;
@@ -173,6 +184,9 @@ int main(int argc, char **argv)
sigaction(SIGINT, &sa, NULL);
reboot(RB_DISABLE_CAD);
/* set default path */
setenv("PATH", path_default, 1);
if (! reexec)
init(default_runlevel);
@@ -187,7 +201,7 @@ int main(int argc, char **argv)
perror("fopen");
continue;
}
count = fread(buf, 1, 2048, fifo);
count = fread(buf, 1, sizeof(buf) - 1, fifo);
buf[count] = 0;
fclose(fifo);
printf("PID1: Received \"%s\" from FIFO...\n", buf);
@@ -201,6 +215,8 @@ int main(int argc, char **argv)
handle_shutdown("reboot", RB_AUTOBOOT);
else if (strcmp(buf, "reexec") == 0)
handle_reexec(argv[0]);
else if (strcmp(buf, "single") == 0)
handle_single();
}
return 0;
}

View File

@@ -109,7 +109,7 @@ static void
handle_signal(int sig)
{
int serrno = errno;
char signame[10] = { '\0' };
char *signame = NULL;
struct winsize ws;
switch (sig) {
@@ -134,20 +134,22 @@ handle_signal(int sig)
break;
case SIGINT:
if (!signame[0])
snprintf(signame, sizeof(signame), "SIGINT");
if (!signame)
xasprintf(&signame, "SIGINT");
/* FALLTHROUGH */
case SIGTERM:
if (!signame[0])
snprintf(signame, sizeof(signame), "SIGTERM");
if (!signame)
xasprintf(&signame, "SIGTERM");
/* FALLTHROUGH */
case SIGQUIT:
if (!signame[0])
snprintf(signame, sizeof(signame), "SIGQUIT");
if (!signame)
xasprintf(&signame, "SIGQUIT");
/* Send the signal to our children too */
if (service_pid > 0)
kill(service_pid, sig);
eerrorx("%s: caught %s, aborting", applet, signame);
eerror("%s: caught %s, aborting", applet, signame);
free(signame);
exit(EXIT_FAILURE);
/* NOTREACHED */
default:
@@ -161,11 +163,12 @@ handle_signal(int sig)
static void
unhotplug()
{
char file[PATH_MAX];
char *file = NULL;
snprintf(file, sizeof(file), RC_SVCDIR "/hotplugged/%s", applet);
xasprintf(&file, RC_SVCDIR "/hotplugged/%s", applet);
if (exists(file) && unlink(file) != 0)
eerror("%s: unlink `%s': %s", applet, file, strerror(errno));
free(file);
}
static void
@@ -485,7 +488,7 @@ svc_exec(const char *arg1, const char *arg2)
static bool
svc_wait(const char *svc)
{
char file[PATH_MAX];
char *file = NULL;
int fd;
bool forever = false;
RC_STRINGLIST *keywords;
@@ -498,8 +501,7 @@ svc_wait(const char *svc)
forever = true;
rc_stringlist_free(keywords);
snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s",
basename_c(svc));
xasprintf(&file, RC_SVCDIR "/exclusive/%s", basename_c(svc));
interval.tv_sec = 0;
interval.tv_nsec = WAIT_INTERVAL;
@@ -512,23 +514,29 @@ svc_wait(const char *svc)
if (fd != -1) {
if (flock(fd, LOCK_SH | LOCK_NB) == 0) {
close(fd);
free(file);
return true;
}
close(fd);
}
if (errno == ENOENT)
if (errno == ENOENT) {
free(file);
return true;
if (errno != EWOULDBLOCK)
eerrorx("%s: open `%s': %s", applet, file,
}
if (errno != EWOULDBLOCK) {
eerror("%s: open `%s': %s", applet, file,
strerror(errno));
free(file);
exit(EXIT_FAILURE);
}
if (nanosleep(&interval, NULL) == -1) {
if (errno != EINTR)
return false;
goto finish;
}
if (!forever) {
timespecsub(&timeout, &interval, &timeout);
if (timeout.tv_sec <= 0)
return false;
goto finish;
timespecsub(&warn, &interval, &warn);
if (warn.tv_sec <= 0) {
ewarn("%s: waiting for %s (%d seconds)",
@@ -538,6 +546,8 @@ svc_wait(const char *svc)
}
}
}
finish:
free(file);
return false;
}
@@ -1105,9 +1115,10 @@ int main(int argc, char **argv)
bool runscript = false;
int retval, opt, depoptions = RC_DEP_TRACE;
RC_STRING *svc;
char path[PATH_MAX], lnk[PATH_MAX];
char *path = NULL;
char *lnk = NULL;
char *dir, *save = NULL, *saveLnk = NULL;
char pidstr[10];
char *pidstr = NULL;
size_t l = 0, ll;
const char *file;
struct stat stbuf;
@@ -1134,11 +1145,13 @@ int main(int argc, char **argv)
* This works fine, provided that we ONLY allow multiplexed services
* to exist in the same directory as the master link.
* Also, the master link as to be a real file in the init dir. */
if (!realpath(argv[1], path)) {
path = realpath(argv[1], NULL);
if (!path) {
fprintf(stderr, "realpath: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
memset(lnk, 0, sizeof(lnk));
lnk = xmalloc(4096);
memset(lnk, 0, 4096);
if (readlink(argv[1], lnk, sizeof(lnk)-1)) {
dir = dirname(path);
if (strchr(lnk, '/')) {
@@ -1153,8 +1166,7 @@ int main(int argc, char **argv)
} else
file = basename_c(argv[1]);
ll = strlen(dir) + strlen(file) + 2;
service = xmalloc(ll);
snprintf(service, ll, "%s/%s", dir, file);
xasprintf(&service, "%s/%s", dir, file);
if (stat(service, &stbuf) != 0) {
free(service);
service = xstrdup(lnk);
@@ -1162,6 +1174,7 @@ int main(int argc, char **argv)
free(save);
free(saveLnk);
}
free(lnk);
if (!service)
service = xstrdup(path);
applet = basename_c(service);
@@ -1185,7 +1198,7 @@ int main(int argc, char **argv)
/* Set an env var so that we always know our pid regardless of any
subshells the init script may create so that our mark_service_*
functions can always instruct us of this change */
snprintf(pidstr, sizeof(pidstr), "%d", (int) getpid());
xasprintf(&pidstr, "%d", (int) getpid());
setenv("RC_OPENRC_PID", pidstr, 1);
/*
* RC_RUNSCRIPT_PID is deprecated, but we will keep it for a while

View File

@@ -27,42 +27,67 @@
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include "einfo.h"
#include "rc.h"
#include "helpers.h"
#include "_usage.h"
#include "rc-wtmp.h"
const char *applet = NULL;
const char *extraopts = NULL;
const char *getoptstring = "HkpRr" getoptstring_COMMON;
const char *getoptstring = "dDHKpRrsw" getoptstring_COMMON;
const struct option longopts[] = {
{ "no-write", no_argument, NULL, 'd'},
{ "dry-run", no_argument, NULL, 'D'},
{ "halt", no_argument, NULL, 'H'},
{ "kexec", no_argument, NULL, 'k'},
{ "kexec", no_argument, NULL, 'K'},
{ "poweroff", no_argument, NULL, 'p'},
{ "reexec", no_argument, NULL, 'R'},
{ "reboot", no_argument, NULL, 'r'},
{ "single", no_argument, NULL, 's'},
{ "write-only", no_argument, NULL, 'w'},
longopts_COMMON
};
const char * const longopts_help[] = {
"do not write wtmp record",
"print actions instead of executing them",
"halt the system",
"reboot the system using kexec",
"power off the system",
"re-execute init (use after upgrading)",
"reboot the system",
"single user mode",
"write wtmp boot record and exit",
longopts_help_COMMON
};
const char *usagestring = NULL;
const char *exclusive = "Select one of "
"--halt, --kexec, --poweroff, --reexec or --reboot";
"--halt, --kexec, --poweroff, --reexec, --reboot, --single or --write-only";
static bool do_dryrun = false;
static bool do_halt = false;
static bool do_kexec = false;
static bool do_poweroff = false;
static bool do_reboot = false;
static bool do_reexec = false;
static bool do_single = false;
static bool do_wtmp = true;
static bool do_wtmp_only = false;
static void send_cmd(const char *cmd)
{
FILE *fifo;
size_t ignored;
if (do_dryrun) {
einfo("Would send %s to init", cmd);
return;
}
if (do_wtmp && (do_halt || do_kexec || do_reboot || do_poweroff))
log_wtmp("shutdown", "~~", 0, RUN_LVL, "~~");
fifo = fopen(RC_INIT_FIFO, "w");
if (!fifo) {
perror("fopen");
return;
@@ -78,24 +103,23 @@ int main(int argc, char **argv)
{
int opt;
int cmd_count = 0;
bool do_halt = false;
bool do_kexec = false;
bool do_poweroff = false;
bool do_reboot = false;
bool do_reexec = false;
applet = basename_c(argv[0]);
if (geteuid() != 0)
eerrorx("%s: you must be root\n", applet);
while ((opt = getopt_long(argc, argv, getoptstring,
longopts, (int *) 0)) != -1)
{
switch (opt) {
case 'd':
do_wtmp = false;
break;
case 'D':
do_dryrun = true;
break;
case 'H':
do_halt = true;
cmd_count++;
break;
case 'k':
case 'K':
do_kexec = true;
cmd_count++;
break;
@@ -111,9 +135,19 @@ if (geteuid() != 0)
do_reboot = true;
cmd_count++;
break;
case 's':
do_single = true;
cmd_count++;
break;
case 'w':
do_wtmp_only = true;
cmd_count++;
break;
case_RC_COMMON_GETOPT
}
}
if (geteuid() != 0 && ! do_dryrun)
eerrorx("%s: you must be root\n", applet);
if (cmd_count != 1) {
eerror("%s: %s\n", applet, exclusive);
usage(EXIT_FAILURE);
@@ -128,5 +162,9 @@ if (geteuid() != 0)
send_cmd("reboot");
else if (do_reexec)
send_cmd("reexec");
else if (do_wtmp_only)
log_wtmp("shutdown", "~~", 0, RUN_LVL, "~~");
else if (do_single)
send_cmd("single");
return 0;
}

View File

@@ -261,12 +261,12 @@ rc_logger_open(const char *level)
break;
}
}
fclose(log);
} else {
log_error = 1;
eerror("Error: fopen(%s) failed: %s", TMPLOG, strerror(errno));
}
fclose(log);
fclose(plog);
} else {
/*

View File

@@ -131,7 +131,8 @@ env_config(void)
char *npp;
char *tok;
const char *sys = rc_sys();
char buffer[PATH_MAX];
char *buffer = NULL;
size_t size = 0;
/* Ensure our PATH is prefixed with the system locations first
for a little extra security */
@@ -170,8 +171,7 @@ env_config(void)
free(e);
if ((fp = fopen(RC_KRUNLEVEL, "r"))) {
memset(buffer, 0, sizeof (buffer));
if (fgets(buffer, sizeof (buffer), fp)) {
if (getline(&buffer, &size, fp) != -1) {
l = strlen (buffer) - 1;
if (buffer[l] == '\n')
buffer[l] = 0;
@@ -181,6 +181,7 @@ env_config(void)
} else
setenv("RC_DEFAULTLEVEL", RC_LEVEL_DEFAULT, 1);
free(buffer);
if (sys)
setenv("RC_SYS", sys, 1);
@@ -217,14 +218,27 @@ signal_setup(int sig, void (*handler)(int))
return sigaction(sig, &sa, NULL);
}
int
signal_setup_restart(int sig, void (*handler)(int))
{
struct sigaction sa;
memset(&sa, 0, sizeof (sa));
sigemptyset(&sa.sa_mask);
sa.sa_handler = handler;
sa.sa_flags = SA_RESTART;
return sigaction(sig, &sa, NULL);
}
int
svc_lock(const char *applet)
{
char file[PATH_MAX];
char *file = NULL;
int fd;
snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s", applet);
xasprintf(&file, RC_SVCDIR "/exclusive/%s", applet);
fd = open(file, O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
free(file);
if (fd == -1)
return -1;
if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
@@ -238,11 +252,12 @@ svc_lock(const char *applet)
int
svc_unlock(const char *applet, int fd)
{
char file[PATH_MAX];
char *file = NULL;
snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s", applet);
xasprintf(&file, RC_SVCDIR "/exclusive/%s", applet);
close(fd);
unlink(file);
free(file);
return -1;
}
@@ -346,7 +361,7 @@ RC_DEPTREE * _rc_deptree_load(int force, int *regen)
int serrno = errno;
int merrno;
time_t t;
char file[PATH_MAX];
char *file = NULL;
struct stat st;
struct utimbuf ut;
FILE *fp;
@@ -368,7 +383,10 @@ RC_DEPTREE * _rc_deptree_load(int force, int *regen)
eend (retval, "Failed to update the dependency tree");
if (retval == 0) {
stat(RC_DEPTREE_CACHE, &st);
if (stat(RC_DEPTREE_CACHE, &st) != 0) {
eerror("stat(%s): %s", RC_DEPTREE_CACHE, strerror(errno));
return NULL;
}
if (st.st_mtime < t) {
eerror("Clock skew detected with `%s'", file);
eerrorn("Adjusting mtime of `" RC_DEPTREE_CACHE
@@ -474,3 +492,27 @@ time_t to_time_t(char *timestring)
}
return result;
}
pid_t get_pid(const char *applet,const char *pidfile)
{
FILE *fp;
pid_t pid;
if (! pidfile)
return -1;
if ((fp = fopen(pidfile, "r")) == NULL) {
ewarnv("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
return -1;
}
if (fscanf(fp, "%d", &pid) != 1) {
ewarnv("%s: no pid found in `%s'", applet, pidfile);
fclose(fp);
return -1;
}
fclose(fp);
return pid;
}

View File

@@ -68,7 +68,7 @@ rc_plugin_load(void)
DIR *dp;
struct dirent *d;
PLUGIN *plugin;
char file[PATH_MAX];
char *file = NULL;
void *h;
int (*fptr)(RC_HOOK, const char *);
@@ -85,8 +85,9 @@ rc_plugin_load(void)
if (d->d_name[0] == '.')
continue;
snprintf(file, sizeof(file), RC_PLUGINDIR "/%s", d->d_name);
xasprintf(&file, RC_PLUGINDIR "/%s", d->d_name);
h = dlopen(file, RTLD_LAZY);
free(file);
if (h == NULL) {
eerror("dlopen: %s", dlerror());
continue;

422
src/rc/rc-schedules.c Normal file
View File

@@ -0,0 +1,422 @@
/*
* The functions in this file control the stopping of daemons by
* start-stop-daemon and supervise-daemon.
*/
/*
* Copyright (c) 2015 The OpenRC Authors.
* See the Authors file at the top-level directory of this distribution and
* https://github.com/OpenRC/openrc/blob/master/AUTHORS
*
* This file is part of OpenRC. It is subject to the license terms in
* the LICENSE file found in the top-level directory of this
* distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
* This file may not be copied, modified, propagated, or distributed
* except according to the terms contained in the LICENSE file.
*/
/* nano seconds */
#define POLL_INTERVAL 20000000
#define WAIT_PIDFILE 500000000
#define ONE_SECOND 1000000000
#define ONE_MS 1000000
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "einfo.h"
#include "queue.h"
#include "rc.h"
#include "rc-misc.h"
#include "rc-schedules.h"
#include "helpers.h"
typedef struct scheduleitem {
enum {
SC_TIMEOUT,
SC_SIGNAL,
SC_GOTO,
SC_FOREVER,
} type;
int value;
struct scheduleitem *gotoitem;
TAILQ_ENTRY(scheduleitem) entries;
} SCHEDULEITEM;
static TAILQ_HEAD(, scheduleitem) schedule;
void free_schedulelist(void)
{
SCHEDULEITEM *s1 = TAILQ_FIRST(&schedule);
SCHEDULEITEM *s2;
while (s1) {
s2 = TAILQ_NEXT(s1, entries);
free(s1);
s1 = s2;
}
TAILQ_INIT(&schedule);
}
int parse_signal(const char *applet, const char *sig)
{
typedef struct signalpair
{
const char *name;
int signal;
} SIGNALPAIR;
#define signalpair_item(name) { #name, SIG##name },
static const SIGNALPAIR signallist[] = {
signalpair_item(HUP)
signalpair_item(INT)
signalpair_item(QUIT)
signalpair_item(ILL)
signalpair_item(TRAP)
signalpair_item(ABRT)
signalpair_item(BUS)
signalpair_item(FPE)
signalpair_item(KILL)
signalpair_item(USR1)
signalpair_item(SEGV)
signalpair_item(USR2)
signalpair_item(PIPE)
signalpair_item(ALRM)
signalpair_item(TERM)
signalpair_item(CHLD)
signalpair_item(CONT)
signalpair_item(STOP)
signalpair_item(TSTP)
signalpair_item(TTIN)
signalpair_item(TTOU)
signalpair_item(URG)
signalpair_item(XCPU)
signalpair_item(XFSZ)
signalpair_item(VTALRM)
signalpair_item(PROF)
#ifdef SIGWINCH
signalpair_item(WINCH)
#endif
#ifdef SIGIO
signalpair_item(IO)
#endif
#ifdef SIGPWR
signalpair_item(PWR)
#endif
signalpair_item(SYS)
{ "NULL", 0 },
};
unsigned int i = 0;
const char *s;
if (!sig || *sig == '\0')
return -1;
if (sscanf(sig, "%u", &i) == 1) {
if (i < NSIG)
return i;
eerrorx("%s: `%s' is not a valid signal", applet, sig);
}
if (strncmp(sig, "SIG", 3) == 0)
s = sig + 3;
else
s = NULL;
for (i = 0; i < ARRAY_SIZE(signallist); ++i)
if (strcmp(sig, signallist[i].name) == 0 ||
(s && strcmp(s, signallist[i].name) == 0))
return signallist[i].signal;
eerrorx("%s: `%s' is not a valid signal", applet, sig);
/* NOTREACHED */
}
static SCHEDULEITEM *parse_schedule_item(const char *applet, const char *string)
{
const char *after_hyph;
int sig;
SCHEDULEITEM *item = xmalloc(sizeof(*item));
item->value = 0;
item->gotoitem = NULL;
if (strcmp(string,"forever") == 0)
item->type = SC_FOREVER;
else if (isdigit((unsigned char)string[0])) {
item->type = SC_TIMEOUT;
errno = 0;
if (sscanf(string, "%d", &item->value) != 1)
eerrorx("%s: invalid timeout value in schedule `%s'",
applet, string);
} else if ((after_hyph = string + (string[0] == '-')) &&
((sig = parse_signal(applet, after_hyph)) != -1))
{
item->type = SC_SIGNAL;
item->value = (int)sig;
} else
eerrorx("%s: invalid schedule item `%s'", applet, string);
return item;
}
void parse_schedule(const char *applet, const char *string, int timeout)
{
char buffer[20];
const char *slash;
int count = 0;
SCHEDULEITEM *repeatat = NULL;
size_t len;
SCHEDULEITEM *item;
TAILQ_INIT(&schedule);
if (string)
for (slash = string; *slash; slash++)
if (*slash == '/')
count++;
free_schedulelist();
if (count == 0) {
item = xmalloc(sizeof(*item));
item->type = SC_SIGNAL;
item->value = timeout;
item->gotoitem = NULL;
TAILQ_INSERT_TAIL(&schedule, item, entries);
item = xmalloc(sizeof(*item));
item->type = SC_TIMEOUT;
item->gotoitem = NULL;
TAILQ_INSERT_TAIL(&schedule, item, entries);
if (string) {
if (sscanf(string, "%d", &item->value) != 1)
eerrorx("%s: invalid timeout in schedule",
applet);
} else
item->value = 5;
return;
}
while (string != NULL) {
if ((slash = strchr(string, '/')))
len = slash - string;
else
len = strlen(string);
if (len >= (ptrdiff_t)sizeof(buffer))
eerrorx("%s: invalid schedule item, far too long",
applet);
memcpy(buffer, string, len);
buffer[len] = 0;
string = slash ? slash + 1 : NULL;
item = parse_schedule_item(applet, buffer);
TAILQ_INSERT_TAIL(&schedule, item, entries);
if (item->type == SC_FOREVER) {
if (repeatat)
eerrorx("%s: invalid schedule, `forever' "
"appears more than once", applet);
repeatat = item;
continue;
}
}
if (repeatat) {
item = xmalloc(sizeof(*item));
item->type = SC_GOTO;
item->value = 0;
item->gotoitem = repeatat;
TAILQ_INSERT_TAIL(&schedule, item, entries);
}
return;
}
/* return number of processes killed, -1 on error */
int do_stop(const char *applet, const char *exec, const char *const *argv,
pid_t pid, uid_t uid,int sig, bool test, bool quiet)
{
RC_PIDLIST *pids;
RC_PID *pi;
RC_PID *np;
bool killed;
int nkilled = 0;
if (pid > 0)
pids = rc_find_pids(NULL, NULL, 0, pid);
else
pids = rc_find_pids(exec, argv, uid, 0);
if (!pids)
return 0;
LIST_FOREACH_SAFE(pi, pids, entries, np) {
if (test) {
einfo("Would send signal %d to PID %d", sig, pi->pid);
nkilled++;
} else {
if (!quiet)
ebeginv("Sending signal %d to PID %d", sig, pi->pid);
errno = 0;
killed = (kill(pi->pid, sig) == 0 ||
errno == ESRCH ? true : false);
if (! quiet)
eendv(killed ? 0 : 1,
"%s: failed to send signal %d to PID %d: %s",
applet, sig, pi->pid, strerror(errno));
if (!killed) {
nkilled = -1;
} else {
if (nkilled != -1)
nkilled++;
}
}
free(pi);
}
free(pids);
return nkilled;
}
int run_stop_schedule(const char *applet,
const char *exec, const char *const *argv,
pid_t pid, uid_t uid,
bool test, bool progress, bool quiet)
{
SCHEDULEITEM *item = TAILQ_FIRST(&schedule);
int nkilled = 0;
int tkilled = 0;
int nrunning = 0;
long nloops, nsecs;
struct timespec ts;
const char *const *p;
bool progressed = false;
if (!(pid > 0 || exec || uid || (argv && *argv)))
return 0;
if (exec)
einfov("Will stop %s", exec);
if (pid > 0)
einfov("Will stop PID %d", pid);
if (uid)
einfov("Will stop processes owned by UID %d", uid);
if (argv && *argv) {
einfovn("Will stop processes of `");
if (rc_yesno(getenv("EINFO_VERBOSE"))) {
for (p = argv; p && *p; p++) {
if (p != argv)
printf(" ");
printf("%s", *p);
}
printf("'\n");
}
}
while (item) {
switch (item->type) {
case SC_GOTO:
item = item->gotoitem;
continue;
case SC_SIGNAL:
nrunning = 0;
nkilled = do_stop(applet, exec, argv, pid, uid, item->value, test,
quiet);
if (nkilled == 0) {
if (tkilled == 0) {
if (progressed)
printf("\n");
eerror("%s: no matching processes found", applet);
}
return tkilled;
}
else if (nkilled == -1)
return 0;
tkilled += nkilled;
break;
case SC_TIMEOUT:
if (item->value < 1) {
item = NULL;
break;
}
ts.tv_sec = 0;
ts.tv_nsec = POLL_INTERVAL;
for (nsecs = 0; nsecs < item->value; nsecs++) {
for (nloops = 0;
nloops < ONE_SECOND / POLL_INTERVAL;
nloops++)
{
if ((nrunning = do_stop(applet, exec, argv,
pid, uid, 0, test, quiet)) == 0)
return 0;
if (nanosleep(&ts, NULL) == -1) {
if (progressed) {
printf("\n");
progressed = false;
}
if (errno == EINTR)
eerror("%s: caught an"
" interrupt", applet);
else {
eerror("%s: nanosleep: %s",
applet, strerror(errno));
return 0;
}
}
}
if (progress) {
printf(".");
fflush(stdout);
progressed = true;
}
}
break;
default:
if (progressed) {
printf("\n");
progressed = false;
}
eerror("%s: invalid schedule item `%d'",
applet, item->type);
return 0;
}
if (item)
item = TAILQ_NEXT(item, entries);
}
if (test || (tkilled > 0 && nrunning == 0))
return nkilled;
if (progressed)
printf("\n");
if (! quiet) {
if (nrunning == 1)
eerror("%s: %d process refused to stop", applet, nrunning);
else
eerror("%s: %d process(es) refused to stop", applet, nrunning);
}
return -nrunning;
}

26
src/rc/rc-schedules.h Normal file
View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2017 The OpenRC Authors.
* See the Authors file at the top-level directory of this distribution and
* https://github.com/OpenRC/openrc/blob/master/AUTHORS
*
* This file is part of OpenRC. It is subject to the license terms in
* the LICENSE file found in the top-level directory of this
* distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
* This file may not be copied, modified, propagated, or distributed
* except according to the terms contained in the LICENSE file.
*/
#ifndef __RC_SCHEDULES_H
#define __RC_SCHEDULES_H
void free_schedulelist(void);
int parse_signal(const char *applet, const char *sig);
void parse_schedule(const char *applet, const char *string, int timeout);
int do_stop(const char *applet, const char *exec, const char *const *argv,
pid_t pid, uid_t uid,int sig, bool test, bool quiet);
int run_stop_schedule(const char *applet,
const char *exec, const char *const *argv,
pid_t pid, uid_t uid,
bool test, bool progress, bool quiet);
#endif

View File

@@ -39,7 +39,6 @@
#include "rc-selinux.h"
/* the context files for selinux */
#define RUN_INIT_FILE "run_init_type"
#define INITRC_FILE "initrc_context"
#ifdef HAVE_AUDIT
@@ -257,19 +256,19 @@ static int read_context_file(const char *filename, char **context)
{
int ret = -1;
FILE *fp;
char filepath[PATH_MAX];
char *filepath = NULL;
char *line = NULL;
char *p;
char *p2;
size_t len = 0;
ssize_t read;
memset(filepath, '\0', PATH_MAX);
snprintf(filepath, PATH_MAX - 1, "%s/%s", selinux_contexts_path(), filename);
xasprintf(&filepath, "%s/%s", selinux_contexts_path(), filename);
fp = fopen(filepath, "r");
if (fp == NULL) {
eerror("Failed to open context file: %s", filename);
free(filepath);
return -1;
}
@@ -295,10 +294,31 @@ static int read_context_file(const char *filename, char **context)
}
free(line);
free(filepath);
fclose(fp);
return ret;
}
static int read_run_init_context(char **context)
{
int ret = -1;
RC_STRINGLIST *list;
char *value = NULL;
list = rc_config_list(selinux_openrc_contexts_path());
if (list == NULL)
return ret;
value = rc_config_value(list, "run_init");
if (value != NULL && strlen(value) > 0) {
*context = xstrdup(value);
ret = 0;
}
rc_stringlist_free(list);
return ret;
}
void selinux_setup(char **argv)
{
char *new_context = NULL;
@@ -312,7 +332,7 @@ void selinux_setup(char **argv)
return;
}
if (read_context_file(RUN_INIT_FILE, &run_init_t) != 0) {
if (read_run_init_context(&run_init_t) != 0) {
/* assume a reasonable default, rather than bailing out */
run_init_t = xstrdup("run_init_t");
ewarn("Assuming SELinux run_init type is %s", run_init_t);
@@ -339,14 +359,13 @@ void selinux_setup(char **argv)
goto out;
}
curr_t = context_type_get(curr_con);
curr_t = xstrdup(context_type_get(curr_con));
if (!curr_t) {
context_free(curr_con);
free(curr_context);
goto out;
}
curr_t = xstrdup(curr_t);
/* dont need them anymore so free() now */
context_free(curr_con);
free(curr_context);

View File

@@ -29,9 +29,10 @@
const char *applet = NULL;
const char *extraopts = NULL;
const char *getoptstring = "e:ilr:IN" getoptstring_COMMON;
const char *getoptstring = "ce:ilr:IN" getoptstring_COMMON;
const struct option longopts[] = {
{ "exists", 1, NULL, 'e' },
{ "ifcrashed", 0, NULL, 'c' },
{ "ifexists", 0, NULL, 'i' },
{ "ifinactive", 0, NULL, 'I' },
{ "ifnotstarted", 0, NULL, 'N' },
@@ -41,6 +42,7 @@ const struct option longopts[] = {
};
const char * const longopts_help[] = {
"tests if the service exists or not",
"if the service is crashed then run the command",
"if the service exists then run the command",
"if the service is inactive then run the command",
"if the service is not started then run the command",
@@ -61,6 +63,7 @@ int main(int argc, char **argv)
RC_STRINGLIST *list;
RC_STRING *s;
RC_SERVICE state;
bool if_crashed = false;
bool if_exists = false;
bool if_inactive = false;
bool if_notstarted = false;
@@ -79,6 +82,9 @@ int main(int argc, char **argv)
free(service);
return opt;
/* NOTREACHED */
case 'c':
if_crashed = true;
break;
case 'i':
if_exists = true;
break;
@@ -121,6 +127,8 @@ int main(int argc, char **argv)
eerrorx("%s: service `%s' does not exist", applet, *argv);
}
state = rc_service_state(*argv);
if (if_crashed && ! (rc_service_daemons_crashed(*argv) && errno != EACCES))
return 0;
if (if_inactive && ! (state & RC_SERVICE_INACTIVE))
return 0;
if (if_notstarted && (state & RC_SERVICE_STARTED))

View File

@@ -83,11 +83,11 @@ static void get_uptime(const char *service, char *uptime, int uptime_size)
time_t now;
char *start_time_string;
time_t start_time;
double time_diff;
double diff_tmp;
double diff_days;
double diff_hours;
double diff_mins;
time_t time_diff;
time_t diff_days = (time_t) 0;
time_t diff_hours = (time_t) 0;
time_t diff_mins = (time_t) 0;
time_t diff_secs = (time_t) 0;
uptime[0] = '\0';
if (state & RC_SERVICE_STARTED) {
@@ -96,26 +96,29 @@ static void get_uptime(const char *service, char *uptime, int uptime_size)
if (start_count && start_time_string) {
start_time = to_time_t(start_time_string);
now = time(NULL);
time_diff = difftime(now, start_time);
diff_tmp = time_diff;
if (diff_tmp > 86400.0) {
diff_days = diff_tmp / 86400.0;
diff_tmp -= diff_days * 86400.0;
time_diff = (time_t) difftime(now, start_time);
diff_secs = time_diff;
if (diff_secs > (time_t) 86400) {
diff_days = diff_secs / (time_t) 86400;
diff_secs %= diff_days * (time_t) 86400;
}
if (diff_tmp > 3600.0) {
diff_hours = diff_tmp / 3600.0;
diff_tmp -= diff_hours * 3600.0;
if (diff_secs > (time_t) 3600) {
diff_hours = diff_secs / (time_t) 3600;
diff_secs %= diff_hours * (time_t) 3600;
}
if (diff_tmp > 60.0) {
diff_mins = diff_tmp / 60.0;
diff_tmp -= diff_mins * 60.0;
if (diff_secs > (time_t) 60) {
diff_mins = diff_secs / (time_t) 60;
diff_secs %= diff_mins * (time_t) 60;
}
if ((int) diff_days > 0)
snprintf(uptime, uptime_size, "%.0f days %02.0f:%02.0f (%s)",
diff_days, diff_hours, diff_mins, start_count);
if (diff_days > 0)
snprintf(uptime, uptime_size,
"%ld day(s) %02ld:%02ld:%02ld (%s)",
diff_days, diff_hours, diff_mins, diff_secs,
start_count);
else
snprintf(uptime, uptime_size, "%02.0f:%02.0f (%s)",
diff_hours, diff_mins, start_count);
snprintf(uptime, uptime_size,
"%02ld:%02ld:%02ld (%s)",
diff_hours, diff_mins, diff_secs, start_count);
}
}
}
@@ -125,6 +128,8 @@ print_service(const char *service)
{
char status[60];
char uptime [40];
char *child_pid = NULL;
char *start_time = NULL;
int cols = printf(" %s", service);
const char *c = ecolor(ECOLOR_GOOD);
RC_SERVICE state = rc_service_state(service);
@@ -144,7 +149,14 @@ print_service(const char *service)
rc_service_daemons_crashed(service) &&
errno != EACCES)
{
snprintf(status, sizeof(status), " crashed ");
child_pid = rc_service_value_get(service, "child_pid");
start_time = rc_service_value_get(service, "start_time");
if (start_time && child_pid)
snprintf(status, sizeof(status), " unsupervised ");
else
snprintf(status, sizeof(status), " crashed ");
free(child_pid);
free(start_time);
} else {
get_uptime(service, uptime, 40);
snprintf(status, sizeof(status), " started %s", uptime);

View File

@@ -166,7 +166,7 @@ show(RC_STRINGLIST *runlevels, bool verbose)
RC_STRING *runlevel;
RC_STRINGLIST *in;
bool inone;
char buffer[PATH_MAX];
char *buffer = NULL;
size_t l;
rc_stringlist_sort(&services);
@@ -182,9 +182,11 @@ show(RC_STRINGLIST *runlevels, bool verbose)
inone = true;
} else {
l = strlen(runlevel->value);
buffer = xmalloc(l+1);
memset (buffer, ' ', l);
buffer[l] = 0;
rc_stringlist_add (in, buffer);
free(buffer);
}
}

51
src/rc/rc-wtmp.c Normal file
View File

@@ -0,0 +1,51 @@
/*
* rc-wtmp.c
* This file contains routines to deal with the wtmp file.
*/
/*
* Copyright 2017 The OpenRC Authors.
* See the Authors file at the top-level directory of this distribution and
* https://github.com/OpenRC/openrc/blob/master/AUTHORS
*
* This file is part of OpenRC. It is subject to the license terms in
* the LICENSE file found in the top-level directory of this
* distribution and at https://github.com/OpenRC/openrc/blob/master/LICENSE
* This file may not be copied, modified, propagated, or distributed
* except according to the terms contained in the LICENSE file.
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include "rc-wtmp.h"
void log_wtmp(const char *user, const char *id, pid_t pid, int type,
const char *line)
{
struct timeval tv;
struct utmp utmp;
struct utsname uname_buf;
memset(&utmp, 0, sizeof(utmp));
gettimeofday(&tv, NULL);
utmp.ut_tv.tv_sec = tv.tv_sec;
utmp.ut_tv.tv_usec = tv.tv_usec;
utmp.ut_pid = pid;
utmp.ut_type = type;
strncpy(utmp.ut_name, user, sizeof(utmp.ut_name));
strncpy(utmp.ut_id , id , sizeof(utmp.ut_id ));
strncpy(utmp.ut_line, line, sizeof(utmp.ut_line));
/* Put the OS version in place of the hostname */
if (uname(&uname_buf) == 0)
strncpy(utmp.ut_host, uname_buf.release, sizeof(utmp.ut_host));
updwtmp(WTMP_FILE, &utmp);
}

View File

@@ -78,9 +78,6 @@ const char *usagestring = "" \
#define INITSH RC_LIBEXECDIR "/sh/init.sh"
#define INITEARLYSH RC_LIBEXECDIR "/sh/init-early.sh"
#define SHUTDOWN "/sbin/shutdown"
#define SULOGIN "/sbin/sulogin"
#define INTERACTIVE RC_SVCDIR "/interactive"
#define DEVBOOT "/dev/.rcboot"
@@ -288,8 +285,8 @@ open_shell(void)
/* VSERVER systems cannot really drop to shells */
if (sys && strcmp(sys, RC_SYS_VSERVER) == 0)
{
execl("/sbin/halt", "/sbin/halt", "-f", (char *) NULL);
eerrorx("%s: unable to exec `/sbin/halt': %s",
execlp("halt", "halt", "-f", (char *) NULL);
eerrorx("%s: unable to exec `halt -f': %s",
applet, strerror(errno));
}
#endif
@@ -336,26 +333,26 @@ set_krunlevel(const char *level)
return true;
}
static size_t
get_krunlevel(char *buffer, int buffer_len)
static char *get_krunlevel(void)
{
char *buffer = NULL;
FILE *fp;
size_t i = 0;
if (!exists(RC_KRUNLEVEL))
return 0;
return NULL;
if (!(fp = fopen(RC_KRUNLEVEL, "r"))) {
eerror("fopen `%s': %s", RC_KRUNLEVEL, strerror(errno));
return 0;
return NULL;
}
if (fgets(buffer, buffer_len, fp)) {
if (getline(&buffer, &i, fp) != -1) {
i = strlen(buffer);
if (buffer[i - 1] == '\n')
buffer[i - 1] = 0;
}
fclose(fp);
return i;
return buffer;
}
static void
@@ -505,6 +502,9 @@ do_sysinit()
* sys */
if ((sys = rc_sys()))
setenv("RC_SYS", sys, 1);
/* force an update of the dependency tree */
if ((main_deptree = _rc_deptree_load(1, NULL)) == NULL)
eerrorx("failed to load deptree");
}
static bool
@@ -743,7 +743,7 @@ int main(int argc, char **argv)
RC_STRING *service;
bool going_down = false;
int depoptions = RC_DEP_STRICT | RC_DEP_TRACE;
char krunlevel [PATH_MAX];
char *krunlevel = NULL;
char pidstr[10];
int opt;
bool parallel;
@@ -892,7 +892,8 @@ int main(int argc, char **argv)
(strcmp(newlevel, RC_LEVEL_SYSINIT) != 0 &&
strcmp(newlevel, getenv("RC_BOOTLEVEL")) != 0))
{
if (get_krunlevel(krunlevel, sizeof(krunlevel))) {
krunlevel = get_krunlevel();
if (krunlevel) {
newlevel = krunlevel;
set_krunlevel(NULL);
}

View File

@@ -19,10 +19,6 @@
* except according to the terms contained in the LICENSE file.
*/
/* nano seconds */
#define POLL_INTERVAL 20000000
#define WAIT_PIDFILE 500000000
#define ONE_SECOND 1000000000
#define ONE_MS 1000000
#include <sys/types.h>
@@ -63,6 +59,7 @@ static struct pam_conv conv = { NULL, NULL};
#include "queue.h"
#include "rc.h"
#include "rc-misc.h"
#include "rc-schedules.h"
#include "_usage.h"
#include "helpers.h"
@@ -130,20 +127,6 @@ const char * const longopts_help[] = {
};
const char *usagestring = NULL;
typedef struct scheduleitem
{
enum
{
SC_TIMEOUT,
SC_SIGNAL,
SC_GOTO,
SC_FOREVER
} type;
int value;
struct scheduleitem *gotoitem;
TAILQ_ENTRY(scheduleitem) entries;
} SCHEDULEITEM;
TAILQ_HEAD(, scheduleitem) schedule;
static char **nav;
static char *changeuser, *ch_root, *ch_dir;
@@ -166,20 +149,6 @@ static inline int ioprio_set(int which _unused,
}
#endif
static void
free_schedulelist(void)
{
SCHEDULEITEM *s1 = TAILQ_FIRST(&schedule);
SCHEDULEITEM *s2;
while (s1) {
s2 = TAILQ_NEXT(s1, entries);
free(s1);
s1 = s2;
}
TAILQ_INIT(&schedule);
}
static void
cleanup(void)
{
@@ -188,385 +157,6 @@ cleanup(void)
free_schedulelist();
}
static int
parse_signal(const char *sig)
{
typedef struct signalpair
{
const char *name;
int signal;
} SIGNALPAIR;
#define signalpair_item(name) { #name, SIG##name },
static const SIGNALPAIR signallist[] = {
signalpair_item(HUP)
signalpair_item(INT)
signalpair_item(QUIT)
signalpair_item(ILL)
signalpair_item(TRAP)
signalpair_item(ABRT)
signalpair_item(BUS)
signalpair_item(FPE)
signalpair_item(KILL)
signalpair_item(USR1)
signalpair_item(SEGV)
signalpair_item(USR2)
signalpair_item(PIPE)
signalpair_item(ALRM)
signalpair_item(TERM)
signalpair_item(CHLD)
signalpair_item(CONT)
signalpair_item(STOP)
signalpair_item(TSTP)
signalpair_item(TTIN)
signalpair_item(TTOU)
signalpair_item(URG)
signalpair_item(XCPU)
signalpair_item(XFSZ)
signalpair_item(VTALRM)
signalpair_item(PROF)
#ifdef SIGWINCH
signalpair_item(WINCH)
#endif
#ifdef SIGIO
signalpair_item(IO)
#endif
#ifdef SIGPWR
signalpair_item(PWR)
#endif
signalpair_item(SYS)
{ "NULL", 0 },
};
unsigned int i = 0;
const char *s;
if (!sig || *sig == '\0')
return -1;
if (sscanf(sig, "%u", &i) == 1) {
if (i < NSIG)
return i;
eerrorx("%s: `%s' is not a valid signal", applet, sig);
}
if (strncmp(sig, "SIG", 3) == 0)
s = sig + 3;
else
s = NULL;
for (i = 0; i < ARRAY_SIZE(signallist); ++i)
if (strcmp(sig, signallist[i].name) == 0 ||
(s && strcmp(s, signallist[i].name) == 0))
return signallist[i].signal;
eerrorx("%s: `%s' is not a valid signal", applet, sig);
/* NOTREACHED */
}
static SCHEDULEITEM *
parse_schedule_item(const char *string)
{
const char *after_hyph;
int sig;
SCHEDULEITEM *item = xmalloc(sizeof(*item));
item->value = 0;
item->gotoitem = NULL;
if (strcmp(string,"forever") == 0)
item->type = SC_FOREVER;
else if (isdigit((unsigned char)string[0])) {
item->type = SC_TIMEOUT;
errno = 0;
if (sscanf(string, "%d", &item->value) != 1)
eerrorx("%s: invalid timeout value in schedule `%s'",
applet, string);
} else if ((after_hyph = string + (string[0] == '-')) &&
((sig = parse_signal(after_hyph)) != -1))
{
item->type = SC_SIGNAL;
item->value = (int)sig;
} else
eerrorx("%s: invalid schedule item `%s'", applet, string);
return item;
}
static void
parse_schedule(const char *string, int timeout)
{
char buffer[20];
const char *slash;
int count = 0;
SCHEDULEITEM *repeatat = NULL;
size_t len;
SCHEDULEITEM *item;
if (string)
for (slash = string; *slash; slash++)
if (*slash == '/')
count++;
free_schedulelist();
if (count == 0) {
item = xmalloc(sizeof(*item));
item->type = SC_SIGNAL;
item->value = timeout;
item->gotoitem = NULL;
TAILQ_INSERT_TAIL(&schedule, item, entries);
item = xmalloc(sizeof(*item));
item->type = SC_TIMEOUT;
item->gotoitem = NULL;
TAILQ_INSERT_TAIL(&schedule, item, entries);
if (string) {
if (sscanf(string, "%d", &item->value) != 1)
eerrorx("%s: invalid timeout in schedule",
applet);
} else
item->value = 5;
return;
}
while (string != NULL) {
if ((slash = strchr(string, '/')))
len = slash - string;
else
len = strlen(string);
if (len >= (ptrdiff_t)sizeof(buffer))
eerrorx("%s: invalid schedule item, far too long",
applet);
memcpy(buffer, string, len);
buffer[len] = 0;
string = slash ? slash + 1 : NULL;
item = parse_schedule_item(buffer);
TAILQ_INSERT_TAIL(&schedule, item, entries);
if (item->type == SC_FOREVER) {
if (repeatat)
eerrorx("%s: invalid schedule, `forever' "
"appears more than once", applet);
repeatat = item;
continue;
}
}
if (repeatat) {
item = xmalloc(sizeof(*item));
item->type = SC_GOTO;
item->value = 0;
item->gotoitem = repeatat;
TAILQ_INSERT_TAIL(&schedule, item, entries);
}
return;
}
static pid_t
get_pid(const char *pidfile)
{
FILE *fp;
pid_t pid;
if (! pidfile)
return -1;
if ((fp = fopen(pidfile, "r")) == NULL) {
ewarnv("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
return -1;
}
if (fscanf(fp, "%d", &pid) != 1) {
ewarnv("%s: no pid found in `%s'", applet, pidfile);
fclose(fp);
return -1;
}
fclose(fp);
return pid;
}
/* return number of processed killed, -1 on error */
static int
do_stop(const char *exec, const char *const *argv,
pid_t pid, uid_t uid,int sig, bool test)
{
RC_PIDLIST *pids;
RC_PID *pi;
RC_PID *np;
bool killed;
int nkilled = 0;
if (pid)
pids = rc_find_pids(NULL, NULL, 0, pid);
else
pids = rc_find_pids(exec, argv, uid, pid);
if (!pids)
return 0;
LIST_FOREACH_SAFE(pi, pids, entries, np) {
if (test) {
einfo("Would send signal %d to PID %d", sig, pi->pid);
nkilled++;
} else {
ebeginv("Sending signal %d to PID %d", sig, pi->pid);
errno = 0;
killed = (kill(pi->pid, sig) == 0 ||
errno == ESRCH ? true : false);
eendv(killed ? 0 : 1,
"%s: failed to send signal %d to PID %d: %s",
applet, sig, pi->pid, strerror(errno));
if (!killed) {
nkilled = -1;
} else {
if (nkilled != -1)
nkilled++;
}
}
free(pi);
}
free(pids);
return nkilled;
}
static int
run_stop_schedule(const char *exec, const char *const *argv,
const char *pidfile, uid_t uid,
bool test, bool progress)
{
SCHEDULEITEM *item = TAILQ_FIRST(&schedule);
int nkilled = 0;
int tkilled = 0;
int nrunning = 0;
long nloops, nsecs;
struct timespec ts;
pid_t pid = 0;
const char *const *p;
bool progressed = false;
if (exec)
einfov("Will stop %s", exec);
if (pidfile)
einfov("Will stop PID in pidfile `%s'", pidfile);
if (uid)
einfov("Will stop processes owned by UID %d", uid);
if (argv && *argv) {
einfovn("Will stop processes of `");
if (rc_yesno(getenv("EINFO_VERBOSE"))) {
for (p = argv; p && *p; p++) {
if (p != argv)
printf(" ");
printf("%s", *p);
}
printf("'\n");
}
}
if (pidfile) {
pid = get_pid(pidfile);
if (pid == -1)
return 0;
}
while (item) {
switch (item->type) {
case SC_GOTO:
item = item->gotoitem;
continue;
case SC_SIGNAL:
nrunning = 0;
nkilled = do_stop(exec, argv, pid, uid, item->value, test);
if (nkilled == 0) {
if (tkilled == 0) {
if (progressed)
printf("\n");
eerror("%s: no matching processes found", applet);
}
return tkilled;
}
else if (nkilled == -1)
return 0;
tkilled += nkilled;
break;
case SC_TIMEOUT:
if (item->value < 1) {
item = NULL;
break;
}
ts.tv_sec = 0;
ts.tv_nsec = POLL_INTERVAL;
for (nsecs = 0; nsecs < item->value; nsecs++) {
for (nloops = 0;
nloops < ONE_SECOND / POLL_INTERVAL;
nloops++)
{
if ((nrunning = do_stop(exec, argv,
pid, uid, 0, test)) == 0)
return 0;
if (nanosleep(&ts, NULL) == -1) {
if (progressed) {
printf("\n");
progressed = false;
}
if (errno == EINTR)
eerror("%s: caught an"
" interrupt", applet);
else {
eerror("%s: nanosleep: %s",
applet, strerror(errno));
return 0;
}
}
}
if (progress) {
printf(".");
fflush(stdout);
progressed = true;
}
}
break;
default:
if (progressed) {
printf("\n");
progressed = false;
}
eerror("%s: invalid schedule item `%d'",
applet, item->type);
return 0;
}
if (item)
item = TAILQ_NEXT(item, entries);
}
if (test || (tkilled > 0 && nrunning == 0))
return nkilled;
if (progressed)
printf("\n");
if (nrunning == 1)
eerror("%s: %d process refused to stop", applet, nrunning);
else
eerror("%s: %d process(es) refused to stop", applet, nrunning);
return -nrunning;
}
static void
handle_signal(int sig)
{
@@ -664,6 +254,7 @@ int main(int argc, char **argv)
#endif
int opt;
size_t size = 0;
bool start = false;
bool stop = false;
bool oknodo = false;
@@ -689,6 +280,7 @@ int main(int argc, char **argv)
int stdout_fd;
int stderr_fd;
pid_t pid, spid;
RC_PIDLIST *pids;
int i;
char *svcname = getenv("RC_SVCNAME");
RC_STRINGLIST *env_list;
@@ -696,10 +288,10 @@ int main(int argc, char **argv)
char *tmp, *newpath, *np;
char *p;
char *token;
char exec_file[PATH_MAX];
char *exec_file = NULL;
struct passwd *pw;
struct group *gr;
char line[130];
char *line = NULL;
FILE *fp;
size_t len;
mode_t numask = 022;
@@ -707,7 +299,6 @@ int main(int argc, char **argv)
unsigned int start_wait = 0;
applet = basename_c(argv[0]);
TAILQ_INIT(&schedule);
atexit(cleanup);
signal_setup(SIGINT, handle_signal);
@@ -876,7 +467,7 @@ int main(int argc, char **argv)
break;
case 's': /* --signal <signal> */
sig = parse_signal(optarg);
sig = parse_signal(applet, optarg);
break;
case 't': /* --test */
@@ -987,26 +578,22 @@ int main(int argc, char **argv)
if (*exec == '/' || *exec == '.') {
/* Full or relative path */
if (ch_root)
snprintf(exec_file, sizeof(exec_file),
"%s/%s", ch_root, exec);
xasprintf(&exec_file, "%s/%s", ch_root, exec);
else
snprintf(exec_file, sizeof(exec_file),
"%s", exec);
xasprintf(&exec_file, "%s", exec);
} else {
/* Something in $PATH */
p = tmp = xstrdup(getenv("PATH"));
*exec_file = '\0';
exec_file = NULL;
while ((token = strsep(&p, ":"))) {
if (ch_root)
snprintf(exec_file, sizeof(exec_file),
"%s/%s/%s",
ch_root, token, exec);
xasprintf(&exec_file, "%s/%s/%s", ch_root, token, exec);
else
snprintf(exec_file, sizeof(exec_file),
"%s/%s", token, exec);
if (exists(exec_file))
xasprintf(&exec_file, "%s/%s", token, exec);
if (exec_file && exists(exec_file))
break;
*exec_file = '\0';
free(exec_file);
exec_file = NULL;
}
free(tmp);
}
@@ -1014,6 +601,7 @@ int main(int argc, char **argv)
if (start && !exists(exec_file)) {
eerror("%s: %s does not exist", applet,
*exec_file ? exec_file : exec);
free(exec_file);
exit(EXIT_FAILURE);
}
@@ -1027,7 +615,10 @@ int main(int argc, char **argv)
if (interpreted && !pidfile) {
fp = fopen(exec_file, "r");
if (fp) {
p = fgets(line, sizeof(line), fp);
line = NULL;
if (getline(&line, &size, fp) == -1)
eerrorx("%s: %s", applet, strerror(errno));
p = line;
fclose(fp);
if (p != NULL && line[0] == '#' && line[1] == '!') {
p = line + 2;
@@ -1039,7 +630,8 @@ int main(int argc, char **argv)
if (p[len] == '\n')
p[len] = '\0';
token = strsep(&p, " ");
strncpy(exec_file, token, sizeof(exec_file));
free(exec_file);
xasprintf(&exec_file, "%s", token);
opt = 0;
for (nav = argv; *nav; nav++)
opt++;
@@ -1062,13 +654,20 @@ int main(int argc, char **argv)
if (!stop)
oknodo = true;
if (retry)
parse_schedule(retry, sig);
parse_schedule(applet, retry, sig);
else if (test || oknodo)
parse_schedule("0", sig);
parse_schedule(applet, "0", sig);
else
parse_schedule(NULL, sig);
i = run_stop_schedule(exec, (const char *const *)margv,
pidfile, uid, test, progress);
parse_schedule(applet, NULL, sig);
if (pidfile) {
pid = get_pid(applet, pidfile);
if (pid == -1)
exit(EXIT_FAILURE);
} else {
pid = 0;
}
i = run_stop_schedule(applet, exec, (const char *const *)margv,
pid, uid, test, progress, false);
if (i < 0)
/* We failed to stop something */
@@ -1090,14 +689,18 @@ int main(int argc, char **argv)
}
if (pidfile)
pid = get_pid(pidfile);
pid = get_pid(applet, pidfile);
else
pid = 0;
if (do_stop(exec, (const char * const *)margv, pid, uid,
0, test) > 0)
if (pid)
pids = rc_find_pids(NULL, NULL, 0, pid);
else
pids = rc_find_pids(exec, (const char * const *) argv, uid, 0);
if (pids)
eerrorx("%s: %s is already running", applet, exec);
free(pids);
if (test) {
if (rc_yesno(getenv("EINFO_QUIET")))
exit (EXIT_SUCCESS);
@@ -1365,7 +968,7 @@ int main(int argc, char **argv)
alive = true;
} else {
if (pidfile) {
pid = get_pid(pidfile);
pid = get_pid(applet, pidfile);
if (pid == -1) {
eerrorx("%s: did not "
"create a valid"
@@ -1374,8 +977,8 @@ int main(int argc, char **argv)
}
} else
pid = 0;
if (do_stop(exec, (const char *const *)margv,
pid, uid, 0, test) > 0)
if (do_stop(applet, exec, (const char *const *)margv,
pid, uid, 0, test, false) > 0)
alive = true;
}

View File

@@ -61,12 +61,13 @@ static struct pam_conv conv = { NULL, NULL};
#include "queue.h"
#include "rc.h"
#include "rc-misc.h"
#include "rc-schedules.h"
#include "_usage.h"
#include "helpers.h"
const char *applet = NULL;
const char *extraopts = NULL;
const char *getoptstring = "D:d:e:g:I:Kk:m:N:p:r:Su:1:2:" \
const char *getoptstring = "D:d:e:g:I:Kk:m:N:p:R:r:Su:1:2:3" \
getoptstring_COMMON;
const struct option longopts[] = {
{ "respawn-delay", 1, NULL, 'D'},
@@ -80,11 +81,13 @@ const struct option longopts[] = {
{ "nicelevel", 1, NULL, 'N'},
{ "pidfile", 1, NULL, 'p'},
{ "respawn-period", 1, NULL, 'P'},
{ "retry", 1, NULL, 'R'},
{ "chroot", 1, NULL, 'r'},
{ "start", 0, NULL, 'S'},
{ "user", 1, NULL, 'u'},
{ "stdout", 1, NULL, '1'},
{ "stderr", 1, NULL, '2'},
{ "reexec", 0, NULL, '3'},
longopts_COMMON
};
const char * const longopts_help[] = {
@@ -99,11 +102,13 @@ const char * const longopts_help[] = {
"Set a nicelevel when starting",
"Match pid found in this file",
"Set respawn time period",
"Retry schedule to use when stopping",
"Chroot to this directory",
"Start daemon",
"Change the process user",
"Redirect stdout to file",
"Redirect stderr to file",
"reexec (used internally)",
longopts_help_COMMON
};
const char *usagestring = NULL;
@@ -124,6 +129,13 @@ static bool exiting = false;
#ifdef TIOCNOTTY
static int tty_fd = -1;
#endif
static pid_t child_pid;
static int respawn_count = 0;
static int respawn_delay = 0;
static int respawn_max = 10;
static int respawn_period = 5;
static char *pidfile = NULL;
static char *svcname = NULL;
extern char **environ;
@@ -147,32 +159,89 @@ static void cleanup(void)
free(changeuser);
}
static pid_t get_pid(const char *pidfile)
static void re_exec_supervisor(void)
{
FILE *fp;
pid_t pid;
if (! pidfile)
return -1;
if ((fp = fopen(pidfile, "r")) == NULL) {
ewarnv("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
return -1;
}
if (fscanf(fp, "%d", &pid) != 1) {
ewarnv("%s: no pid found in `%s'", applet, pidfile);
fclose(fp);
return -1;
}
fclose(fp);
return pid;
syslog(LOG_WARNING, "Re-executing for %s", svcname);
execlp("supervise-daemon", "supervise-daemon", svcname, "--reexec",
(char *) NULL);
syslog(LOG_ERR, "Unable to execute supervise-daemon: %s",
strerror(errno));
exit(EXIT_FAILURE);
}
static void child_process(char *exec, char **argv, char *svcname,
int start_count)
static void handle_signal(int sig)
{
int serrno = errno;
syslog(LOG_WARNING, "caught signal %d", sig);
if (sig == SIGTERM)
exiting = true;
/* Restore errno */
errno = serrno;
if (! exiting)
re_exec_supervisor();
}
static char * expand_home(const char *home, const char *path)
{
char *opath, *ppath, *p, *nh;
size_t len;
struct passwd *pw;
if (!path || *path != '~')
return xstrdup(path);
opath = ppath = xstrdup(path);
if (ppath[1] != '/' && ppath[1] != '\0') {
p = strchr(ppath + 1, '/');
if (p)
*p = '\0';
pw = getpwnam(ppath + 1);
if (pw) {
home = pw->pw_dir;
ppath = p;
if (ppath)
*ppath = '/';
} else
home = NULL;
} else
ppath++;
if (!home) {
free(opath);
return xstrdup(path);
}
if (!ppath) {
free(opath);
return xstrdup(home);
}
len = strlen(ppath) + strlen(home) + 1;
nh = xmalloc(len);
snprintf(nh, len, "%s%s", home, ppath);
free(opath);
return nh;
}
static char *make_cmdline(char **argv)
{
char **c;
char *cmdline = NULL;
size_t len = 0;
for (c = argv; c && *c; c++)
len += (strlen(*c) + 1);
cmdline = xmalloc(len);
memset(cmdline, 0, len);
for (c = argv; c && *c; c++) {
strcat(cmdline, *c);
strcat(cmdline, " ");
}
return cmdline;
}
static void child_process(char *exec, char **argv)
{
RC_STRINGLIST *env_list;
RC_STRING *env;
@@ -182,8 +251,7 @@ static void child_process(char *exec, char **argv, char *svcname,
size_t len;
char *newpath;
char *np;
char **c;
char cmdline[PATH_MAX];
char *cmdline = NULL;
time_t start_time;
char start_count_string[20];
char start_time_string[20];
@@ -196,6 +264,16 @@ static void child_process(char *exec, char **argv, char *svcname,
setsid();
if (svcname) {
start_time = time(NULL);
from_time_t(start_time_string, start_time);
rc_service_value_set(svcname, "start_time", start_time_string);
sprintf(start_count_string, "%i", respawn_count);
rc_service_value_set(svcname, "start_count", start_count_string);
sprintf(start_count_string, "%d", getpid());
rc_service_value_set(svcname, "child_pid", start_count_string);
}
if (nicelevel) {
if (setpriority(PRIO_PROCESS, getpid(), nicelevel) == -1)
eerrorx("%s: setpriority %d: %s", applet, nicelevel,
@@ -333,22 +411,9 @@ static void child_process(char *exec, char **argv, char *svcname,
for (i = getdtablesize() - 1; i >= 3; --i)
fcntl(i, F_SETFD, FD_CLOEXEC);
*cmdline = '\0';
c = argv;
while (*c) {
strcat(cmdline, *c);
strcat(cmdline, " ");
c++;
}
syslog(LOG_INFO, "Running command line: %s", cmdline);
if (svcname) {
start_time = time(NULL);
from_time_t(start_time_string, start_time);
rc_service_value_set(svcname, "start_time", start_time_string);
sprintf(start_count_string, "%i", start_count);
rc_service_value_set(svcname, "start_count", start_count_string);
}
cmdline = make_cmdline(argv);
syslog(LOG_INFO, "Child command line: %s", cmdline);
free(cmdline);
execvp(exec, argv);
#ifdef HAVE_PAM
@@ -358,110 +423,167 @@ sprintf(start_count_string, "%i", start_count);
eerrorx("%s: failed to exec `%s': %s", applet, exec,strerror(errno));
}
static void handle_signal(int sig)
static void supervisor(char *exec, char **argv)
{
int serrno = errno;
char signame[10] = { '\0' };
FILE *fp;
int i;
int nkilled;
time_t respawn_now= 0;
time_t first_spawn= 0;
switch (sig) {
case SIGINT:
snprintf(signame, sizeof(signame), "SIGINT");
break;
case SIGTERM:
snprintf(signame, sizeof(signame), "SIGTERM");
break;
case SIGQUIT:
snprintf(signame, sizeof(signame), "SIGQUIT");
break;
#ifndef RC_DEBUG
signal_setup_restart(SIGHUP, handle_signal);
signal_setup_restart(SIGINT, handle_signal);
signal_setup_restart(SIGQUIT, handle_signal);
signal_setup_restart(SIGILL, handle_signal);
signal_setup_restart(SIGABRT, handle_signal);
signal_setup_restart(SIGFPE, handle_signal);
signal_setup_restart(SIGSEGV, handle_signal);
signal_setup_restart(SIGPIPE, handle_signal);
signal_setup_restart(SIGALRM, handle_signal);
signal_setup(SIGTERM, handle_signal);
signal_setup_restart(SIGUSR1, handle_signal);
signal_setup_restart(SIGUSR2, handle_signal);
signal_setup_restart(SIGBUS, handle_signal);
#ifdef SIGPOLL
signal_setup_restart(SIGPOLL, handle_signal);
#endif
signal_setup_restart(SIGPROF, handle_signal);
signal_setup_restart(SIGSYS, handle_signal);
signal_setup_restart(SIGTRAP, handle_signal);
signal_setup_restart(SIGVTALRM, handle_signal);
signal_setup_restart(SIGXCPU, handle_signal);
signal_setup_restart(SIGXFSZ, handle_signal);
#ifdef SIGEMT
signal_setup_restart(SIGEMT, handle_signal);
#endif
signal_setup_restart(SIGIO, handle_signal);
#ifdef SIGPWR
signal_setup_restart(SIGPWR, handle_signal);
#endif
#ifdef SIGUNUSED
signal_setup_restart(SIGUNUSED, handle_signal);
#endif
#ifdef SIGRTMIN
for (i = SIGRTMIN; i <= SIGRTMAX; i++)
signal_setup_restart(i, handle_signal);
#endif
#endif
fp = fopen(pidfile, "w");
if (! fp)
eerrorx("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
fprintf(fp, "%d\n", getpid());
fclose(fp);
if (svcname)
rc_service_daemon_set(svcname, exec, (const char * const *) argv,
pidfile, true);
/* remove the controlling tty */
#ifdef TIOCNOTTY
ioctl(tty_fd, TIOCNOTTY, 0);
close(tty_fd);
#endif
/*
* Supervisor main loop
*/
i = 0;
while (!exiting) {
wait(&i);
if (exiting) {
signal_setup(SIGCHLD, SIG_IGN);
syslog(LOG_INFO, "stopping %s, pid %d", exec, child_pid);
nkilled = run_stop_schedule(applet, exec, NULL, child_pid, 0,
false, false, true);
if (nkilled > 0)
syslog(LOG_INFO, "killed %d processes", nkilled);
} else {
sleep(respawn_delay);
if (respawn_max > 0 && respawn_period > 0) {
respawn_now = time(NULL);
if (first_spawn == 0)
first_spawn = respawn_now;
if (respawn_now - first_spawn > respawn_period) {
respawn_count = 0;
first_spawn = 0;
} else
respawn_count++;
if (respawn_count >= respawn_max) {
syslog(LOG_WARNING,
"respawned \"%s\" too many times, exiting", exec);
exiting = true;
continue;
}
}
if (WIFEXITED(i))
syslog(LOG_WARNING, "%s, pid %d, exited with return code %d",
exec, child_pid, WEXITSTATUS(i));
else if (WIFSIGNALED(i))
syslog(LOG_WARNING, "%s, pid %d, terminated by signal %d",
exec, child_pid, WTERMSIG(i));
child_pid = fork();
if (child_pid == -1)
eerrorx("%s: fork: %s", applet, strerror(errno));
if (child_pid == 0)
child_process(exec, argv);
}
}
if (*signame != 0) {
syslog(LOG_INFO, "%s: caught signal %s, exiting", applet, signame);
exiting = true;
} else
syslog(LOG_INFO, "%s: caught unknown signal %d", applet, sig);
/* Restore errno */
errno = serrno;
}
static char * expand_home(const char *home, const char *path)
{
char *opath, *ppath, *p, *nh;
size_t len;
struct passwd *pw;
if (!path || *path != '~')
return xstrdup(path);
opath = ppath = xstrdup(path);
if (ppath[1] != '/' && ppath[1] != '\0') {
p = strchr(ppath + 1, '/');
if (p)
*p = '\0';
pw = getpwnam(ppath + 1);
if (pw) {
home = pw->pw_dir;
ppath = p;
if (ppath)
*ppath = '/';
} else
home = NULL;
} else
ppath++;
if (!home) {
free(opath);
return xstrdup(path);
if (pidfile && exists(pidfile))
unlink(pidfile);
if (svcname) {
rc_service_daemon_set(svcname, exec, (const char *const *)argv,
pidfile, false);
rc_service_mark(svcname, RC_SERVICE_STOPPED);
rc_service_value_set(svcname, "child_pid", NULL);
}
if (!ppath) {
free(opath);
return xstrdup(home);
}
len = strlen(ppath) + strlen(home) + 1;
nh = xmalloc(len);
snprintf(nh, len, "%s%s", home, ppath);
free(opath);
return nh;
exit(EXIT_SUCCESS);
}
int main(int argc, char **argv)
{
int opt;
char **c;
int x;
bool start = false;
bool stop = false;
bool reexec = false;
char *exec = NULL;
char *pidfile = NULL;
char *retry = NULL;
int sig = SIGTERM;
char *home = NULL;
int tid = 0;
pid_t child_pid, pid;
char *svcname = getenv("RC_SVCNAME");
pid_t pid;
char *tmp;
char *p;
char *token;
int i;
int n;
char exec_file[PATH_MAX];
int respawn_count = 0;
int respawn_delay = 0;
int respawn_max = 10;
int respawn_period = 5;
time_t respawn_now= 0;
time_t first_spawn= 0;
char *exec_file = NULL;
char *varbuf = NULL;
struct timespec ts;
struct passwd *pw;
struct group *gr;
FILE *fp;
mode_t numask = 022;
int child_argc = 0;
char **child_argv = NULL;
char *str = NULL;
char *cmdline = NULL;
applet = basename_c(argv[0]);
atexit(cleanup);
signal_setup(SIGINT, handle_signal);
signal_setup(SIGQUIT, handle_signal);
signal_setup(SIGTERM, handle_signal);
svcname = getenv("RC_SVCNAME");
if (!svcname)
eerrorx("%s: The RC_SVCNAME environment variable is not set", applet);
openlog(applet, LOG_PID, LOG_DAEMON);
if (argc >= 1 && svcname && strcmp(argv[1], svcname))
eerrorx("%s: the first argument is %s and must be %s",
applet, argv[1], svcname);
if ((tmp = getenv("SSD_NICELEVEL")))
if (sscanf(tmp, "%d", &nicelevel) != 1)
eerror("%s: invalid nice level `%s' (SSD_NICELEVEL)",
@@ -482,6 +604,11 @@ int main(int argc, char **argv)
}
}
cmdline = make_cmdline(argv);
if (svcname) {
argc--;
argv++;
}
while ((opt = getopt_long(argc, argv, getoptstring, longopts,
(int *) 0)) != -1)
switch (opt) {
@@ -514,8 +641,8 @@ int main(int argc, char **argv)
case 'P': /* --respawn-period time */
n = sscanf(optarg, "%d", &respawn_period);
if (n != 1 || respawn_delay < 1)
eerrorx("Invalid respawn-delay value '%s'", optarg);
if (n != 1 || respawn_period < 1)
eerrorx("Invalid respawn-period value '%s'", optarg);
break;
case 'S': /* --start */
@@ -557,6 +684,9 @@ int main(int argc, char **argv)
pidfile = optarg;
break;
case 'R': /* --retry <schedule>|timeout */
retry = optarg;
break;
case 'r': /* --chroot /new/root */
ch_root = optarg;
break;
@@ -608,78 +738,182 @@ int main(int argc, char **argv)
case '2': /* --stderr /path/to/stderr.logfile */
redirect_stderr = optarg;
break;
case '3': /* --reexec */
reexec = true;
break;
case_RC_COMMON_GETOPT
}
if (!pidfile)
if (!pidfile && !reexec)
eerrorx("%s: --pidfile must be specified", applet);
endpwent();
argc -= optind;
argv += optind;
exec = *argv;
if (start) {
if (!exec)
eerrorx("%s: nothing to start", applet);
if (respawn_delay * respawn_max > respawn_period) {
ewarn("%s: Please increase the value of --respawn-period to more "
"than %d to avoid infinite respawning", applet,
respawn_delay * respawn_max);
}
}
/* Expand ~ */
if (ch_dir && *ch_dir == '~')
ch_dir = expand_home(home, ch_dir);
if (ch_root && *ch_root == '~')
ch_root = expand_home(home, ch_root);
if (exec) {
if (*exec == '~')
exec = expand_home(home, exec);
/* Validate that the binary exists if we are starting */
if (*exec == '/' || *exec == '.') {
/* Full or relative path */
if (ch_root)
snprintf(exec_file, sizeof(exec_file),
"%s/%s", ch_root, exec);
else
snprintf(exec_file, sizeof(exec_file),
"%s", exec);
} else {
/* Something in $PATH */
p = tmp = xstrdup(getenv("PATH"));
*exec_file = '\0';
while ((token = strsep(&p, ":"))) {
if (ch_root)
snprintf(exec_file, sizeof(exec_file),
"%s/%s/%s",
ch_root, token, exec);
else
snprintf(exec_file, sizeof(exec_file),
"%s/%s", token, exec);
if (exists(exec_file))
break;
*exec_file = '\0';
}
free(tmp);
umask(numask);
if (reexec) {
str = rc_service_value_get(svcname, "argc");
sscanf(str, "%d", &child_argc);
child_argv = xmalloc((child_argc + 1) * sizeof(char *));
memset(child_argv, 0, (child_argc + 1) * sizeof(char *));
for (x = 0; x < child_argc; x++) {
xasprintf(&varbuf, "argv_%d", x);
str = rc_service_value_get(svcname, varbuf);
child_argv[x] = str;
free(varbuf);
varbuf = NULL;
}
}
if (start && !exists(exec_file))
eerrorx("%s: %s does not exist", applet,
*exec_file ? exec_file : exec);
free(str);
str = rc_service_value_get(svcname, "child_pid");
sscanf(str, "%d", &child_pid);
free(str);
exec = rc_service_value_get(svcname, "exec");
pidfile = rc_service_value_get(svcname, "pidfile");
retry = rc_service_value_get(svcname, "retry");
if (retry) {
parse_schedule(applet, retry, sig);
rc_service_value_set(svcname, "retry", retry);
} else
parse_schedule(applet, NULL, sig);
if (stop) {
pid = get_pid(pidfile);
if (pid == -1)
i = pid;
else
str = rc_service_value_get(svcname, "respawn_delay");
sscanf(str, "%d", &respawn_delay);
str = rc_service_value_get(svcname, "respawn_max");
sscanf(str, "%d", &respawn_max);
supervisor(exec, child_argv);
} else if (start) {
if (exec) {
if (*exec == '~')
exec = expand_home(home, exec);
/* Validate that the binary exists if we are starting */
if (*exec == '/' || *exec == '.') {
/* Full or relative path */
if (ch_root)
xasprintf(&exec_file, "%s/%s", ch_root, exec);
else
xasprintf(&exec_file, "%s", exec);
} else {
/* Something in $PATH */
p = tmp = xstrdup(getenv("PATH"));
exec_file = NULL;
while ((token = strsep(&p, ":"))) {
if (ch_root)
xasprintf(&exec_file, "%s/%s/%s", ch_root, token, exec);
else
xasprintf(&exec_file, "%s/%s", token, exec);
if (exec_file && exists(exec_file))
break;
free(exec_file);
exec_file = NULL;
}
free(tmp);
}
if (!exists(exec_file)) {
eerror("%s: %s does not exist", applet,
*exec_file ? exec_file : exec);
free(exec_file);
exit(EXIT_FAILURE);
}
} else
eerrorx("%s: nothing to start", applet);
pid = get_pid(applet, pidfile);
if (pid != -1)
if (do_stop(applet, exec, (const char * const *)argv, pid, uid,
0, false, true) > 0)
eerrorx("%s: %s is already running", applet, exec);
if (respawn_delay * respawn_max > respawn_period)
ewarn("%s: Please increase the value of --respawn-period to more "
"than %d to avoid infinite respawning", applet,
respawn_delay * respawn_max);
if (retry) {
parse_schedule(applet, retry, sig);
rc_service_value_set(svcname, "retry", retry);
} else
parse_schedule(applet, NULL, sig);
einfov("Detaching to start `%s'", exec);
syslog(LOG_INFO, "Supervisor command line: %s", cmdline);
free(cmdline);
cmdline = NULL;
/* Remove existing pidfile */
if (pidfile)
unlink(pidfile);
/* Make sure we can write a pid file */
fp = fopen(pidfile, "w");
if (! fp)
eerrorx("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
fclose(fp);
rc_service_value_set(svcname, "pidfile", pidfile);
varbuf = NULL;
xasprintf(&varbuf, "%i", respawn_delay);
rc_service_value_set(svcname, "respawn_delay", varbuf);
xasprintf(&varbuf, "%i", respawn_max);
rc_service_value_set(svcname, "respawn_max", varbuf);
xasprintf(&varbuf, "%i", respawn_period);
rc_service_value_set(svcname, "respawn_period", varbuf);
child_pid = fork();
if (child_pid == -1)
eerrorx("%s: fork: %s", applet, strerror(errno));
if (child_pid != 0)
/* first parent process, do nothing. */
exit(EXIT_SUCCESS);
#ifdef TIOCNOTTY
tty_fd = open("/dev/tty", O_RDWR);
#endif
devnull_fd = open("/dev/null", O_RDWR);
child_pid = fork();
if (child_pid == -1)
eerrorx("%s: fork: %s", applet, strerror(errno));
else if (child_pid != 0) {
c = argv;
x = 0;
while (c && *c) {
varbuf = NULL;
xasprintf(&varbuf, "argv_%-d",x);
rc_service_value_set(svcname, varbuf, *c);
free(varbuf);
varbuf = NULL;
x++;
c++;
}
xasprintf(&varbuf, "%d", x);
rc_service_value_set(svcname, "argc", varbuf);
rc_service_value_set(svcname, "exec", exec);
supervisor(exec, argv);
} else
child_process(exec, argv);
} else if (stop) {
pid = get_pid(applet, pidfile);
if (pid != -1) {
i = kill(pid, SIGTERM);
if (i != 0)
/* We failed to stop something */
exit(EXIT_FAILURE);
if (i != 0)
/* We failed to send the signal */
ewarn("Unable to shut down the supervisor");
else {
/* wait for the supervisor to go down */
while (kill(pid, 0) == 0) {
ts.tv_sec = 0;
ts.tv_nsec = 1;
nanosleep(&ts, NULL);
}
}
}
/* Even if we have not actually killed anything, we should
* remove information about it as it may have unexpectedly
@@ -695,113 +929,4 @@ int main(int argc, char **argv)
}
exit(EXIT_SUCCESS);
}
pid = get_pid(pidfile);
if (pid != -1)
if (kill(pid, 0) == 0)
eerrorx("%s: %s is already running", applet, exec);
einfov("Detaching to start `%s'", exec);
eindentv();
/* Remove existing pidfile */
if (pidfile)
unlink(pidfile);
/*
* Make sure we can write a pid file
*/
fp = fopen(pidfile, "w");
if (! fp)
eerrorx("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
fclose(fp);
child_pid = fork();
if (child_pid == -1)
eerrorx("%s: fork: %s", applet, strerror(errno));
/* first parent process, do nothing. */
if (child_pid != 0)
exit(EXIT_SUCCESS);
#ifdef TIOCNOTTY
tty_fd = open("/dev/tty", O_RDWR);
#endif
devnull_fd = open("/dev/null", O_RDWR);
child_pid = fork();
if (child_pid == -1)
eerrorx("%s: fork: %s", applet, strerror(errno));
if (child_pid != 0) {
/* this is the supervisor */
umask(numask);
fp = fopen(pidfile, "w");
if (! fp)
eerrorx("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
fprintf(fp, "%d\n", getpid());
fclose(fp);
if (svcname)
rc_service_daemon_set(svcname, exec,
(const char * const *) argv, pidfile, true);
/* remove the controlling tty */
#ifdef TIOCNOTTY
ioctl(tty_fd, TIOCNOTTY, 0);
close(tty_fd);
#endif
/*
* Supervisor main loop
*/
i = 0;
while (!exiting) {
wait(&i);
if (exiting) {
syslog(LOG_INFO, "stopping %s, pid %d", exec, child_pid);
kill(child_pid, SIGTERM);
} else {
sleep(respawn_delay);
if (respawn_max > 0 && respawn_period > 0) {
respawn_now = time(NULL);
if (first_spawn == 0)
first_spawn = respawn_now;
if (respawn_now - first_spawn > respawn_period) {
respawn_count = 0;
first_spawn = 0;
} else
respawn_count++;
if (respawn_count >= respawn_max) {
syslog(LOG_INFO, "respawned \"%s\" too many times, "
"exiting", exec);
exiting = true;
continue;
}
}
if (WIFEXITED(i))
syslog(LOG_INFO, "%s, pid %d, exited with return code %d",
exec, child_pid, WEXITSTATUS(i));
else if (WIFSIGNALED(i))
syslog(LOG_INFO, "%s, pid %d, terminated by signal %d",
exec, child_pid, WTERMSIG(i));
child_pid = fork();
if (child_pid == -1)
eerrorx("%s: fork: %s", applet, strerror(errno));
if (child_pid == 0)
child_process(exec, argv, svcname, respawn_count);
}
}
if (pidfile && exists(pidfile))
unlink(pidfile);
if (svcname) {
rc_service_daemon_set(svcname, exec,
(const char *const *)argv,
pidfile, false);
rc_service_mark(svcname, RC_SERVICE_STOPPED);
}
exit(EXIT_SUCCESS);
} else if (child_pid == 0)
child_process(exec, argv, svcname, respawn_count);
}

View File

@@ -1,4 +1,5 @@
# Using supervise-daemon
Using supervise-daemon
======================
Beginning with OpenRC-0.21 we have our own daemon supervisor,
supervise-daemon., which can start a daemon and restart it if it

View File

@@ -1,4 +1,5 @@
# deptree2dot - Graph the OpenRC Dependency Tree
deptree2dot - Graph the OpenRC Dependency Tree
==============================================
This utility can be used to graph the OpenRC dependency tree. It
requires perl5.x and converts the tree to a .dot file which can be

View File

@@ -7,7 +7,5 @@ BIN= ${OBJS}
MK= ../../mk
SED_EXTRA+= -e 's:@VARBASE@:/var:g'
include ${MK}/os.mk
include ${MK}/scripts.mk

View File

@@ -1,3 +1,6 @@
Example OpenRC Service Scripts
##############################
The service scripts in this directory are meant as examples only.
They are not installed by default as the scripts will need tweaking on a
per distro basis. They are also non essential to the operation of the system.

View File

@@ -11,7 +11,7 @@
command=@PKG_PREFIX@/sbin/avahi-dnsconfd
command_args="$avahi_dnsconfd_args -D"
pidfile=@VARBASE@/run/avahi-dnsconfd.pid
pidfile=/var/run/avahi-dnsconfd.pid
name="Avahi DNS Configuration Daemon"
depend()

View File

@@ -11,7 +11,7 @@
command=@PKG_PREFIX@/sbin/avahi-daemon
command_args="$avahid_args -D"
pidfile=@VARBASE@/run/avahi-daemon/pid
pidfile=/var/run/avahi-daemon/pid
name="Avahi Service Advertisement Daemon"
depend()

View File

@@ -10,7 +10,7 @@
# except according to the terms contained in the LICENSE file.
command=@PKG_PREFIX@/bin/dbus-daemon
pidfile=@VARBASE@/run/dbus/pid
pidfile=/var/run/dbus/pid
command_args="${dbusd_args---system}"
name="Message Bus Daemon"

Some files were not shown because too many files have changed in this diff Show More