From d869ae67bd4ed0e94254852f0a72d010cda7eb00 Mon Sep 17 00:00:00 2001 From: Astounds Date: Sun, 8 Feb 2026 16:30:31 -0500 Subject: [PATCH] initial commit --- AUTHORS | 99 ++ BUSYBOX.md | 29 + ChangeLog | 1672 +++++++++++++++++++++ FEATURE-REMOVAL-SCHEDULE.md | 78 + HISTORY.md | 62 + LICENSE | 24 + Makefile | 42 + Makefile.inc | 3 + NEWS.md | 200 +++ README.md | 92 ++ README.newnet | 36 + STYLE-GUIDE.md | 84 ++ TODO | 21 + agetty-guide.md | 18 + conf.d/Makefile | 20 + conf.d/adjkerntz | 10 + conf.d/agetty | 11 + conf.d/bootmisc | 15 + conf.d/consolefont | 18 + conf.d/devfs | 8 + conf.d/dmesg | 3 + conf.d/fsck | 40 + conf.d/hostname | 2 + conf.d/hwclock | 20 + conf.d/ipfw | 14 + conf.d/keymaps | 23 + conf.d/killprocs | 6 + conf.d/localmount | 10 + conf.d/modules | 27 + conf.d/moused | 16 + conf.d/mtab | 5 + conf.d/net-online | 18 + conf.d/netmount | 47 + conf.d/network | 80 + conf.d/powerd | 7 + conf.d/rarpd | 3 + conf.d/savecore | 25 + conf.d/staticroute | 26 + conf.d/swap | 13 + conf.d/syscons | 19 + conf.d/urandom | 5 + etc/.gitignore | 2 + etc/Makefile | 23 + etc/devd.conf | 315 ++++ etc/rc.conf | 256 ++++ etc/rc.conf.orig | 255 ++++ etc/rc.devd | 39 + etc/rc.in | 26 + etc/rc.shutdown.in | 24 + guide.md | 269 ++++ init.d/.gitignore | 48 + init.d/Makefile | 36 + init.d/adjkerntz.in | 69 + init.d/agetty.in | 33 + init.d/binfmt.in | 27 + init.d/binfmt_misc.in | 41 + init.d/bootmisc.in | 253 ++++ init.d/consolefont.in | 70 + init.d/devd.in | 29 + init.d/devdb.in | 29 + init.d/devfs.in | 127 ++ init.d/dmesg.in | 25 + init.d/dumpon.in | 33 + init.d/encswap.in | 43 + init.d/fsck.in | 131 ++ init.d/hostid.in | 88 ++ init.d/hostname.in | 45 + init.d/hwclock.in | 160 ++ init.d/ipfw.in | 166 ++ init.d/keymaps.in | 77 + init.d/killprocs.in | 27 + init.d/local.in | 93 ++ init.d/localmount.in | 133 ++ init.d/loopback.in | 35 + init.d/mixer.in | 54 + init.d/modules-load.in | 72 + init.d/modules.in | 95 ++ init.d/mount-ro.in | 59 + init.d/moused.in | 69 + init.d/mtab.in | 47 + init.d/net-online.in | 69 + init.d/netmount.in | 91 ++ init.d/network.in | 360 +++++ init.d/newsyslog.in | 26 + init.d/nscd.in | 29 + init.d/numlock.in | 49 + init.d/osclock.in | 19 + init.d/pf.in | 66 + init.d/powerd.in | 42 + init.d/rarpd.in | 30 + init.d/rc-enabled.in | 60 + init.d/root.in | 61 + init.d/rpcbind.in | 28 + init.d/runsvdir.in | 20 + init.d/s6-svscan.in | 38 + init.d/savecache.in | 66 + init.d/savecore.in | 45 + init.d/staticroute.in | 111 ++ init.d/swap-blk.in | 31 + init.d/swap.in | 37 + init.d/swclock.in | 36 + init.d/syscons.in | 91 ++ init.d/sysctl.in | 58 + init.d/sysfs.in | 162 ++ init.d/syslogd.in | 27 + init.d/termencoding.in | 55 + init.d/ttys.in | 30 + init.d/urandom.in | 53 + init.d/wscons.in | 108 ++ local.d/Makefile | 6 + local.d/README | 14 + man/Makefile | 45 + man/einfo.3 | 197 +++ man/openrc-init.8 | 46 + man/openrc-run.8 | 663 ++++++++ man/openrc-shutdown.8 | 62 + man/openrc.8 | 74 + man/rc-service.8 | 71 + man/rc-sstat.8 | 33 + man/rc-status.8 | 68 + man/rc-update.8 | 89 ++ man/rc_config.3 | 59 + man/rc_deptree.3 | 99 ++ man/rc_find_pids.3 | 56 + man/rc_plugin_hook.3 | 37 + man/rc_runlevel.3 | 52 + man/rc_service.3 | 216 +++ man/rc_stringlist.3 | 74 + man/service.8 | 1 + man/start-stop-daemon.8 | 194 +++ man/supervise-daemon.8 | 171 +++ mk/cc.mk | 44 + mk/debug.mk | 23 + mk/depend.mk | 21 + mk/dist.mk | 50 + mk/gitignore.mk | 22 + mk/gitver.mk | 8 + mk/lib.mk | 78 + mk/net.mk | 1 + mk/os-BSD.mk | 16 + mk/os-DragonFly.mk | 13 + mk/os-FreeBSD.mk | 13 + mk/os-GNU-kFreeBSD.mk | 18 + mk/os-GNU.mk | 15 + mk/os-Linux.mk | 34 + mk/os-NetBSD.mk | 14 + mk/os-prefix.mk | 8 + mk/os.mk | 19 + mk/pam.mk | 16 + mk/prog.mk | 45 + mk/scripts.mk | 65 + mk/subdir.mk | 38 + mk/sys.mk | 67 + mk/termcap.mk | 17 + pkgconfig/.gitignore | 2 + pkgconfig/Makefile | 12 + pkgconfig/einfo.pc.in | 9 + pkgconfig/openrc.pc.in | 10 + runit-guide.md | 41 + runlevels/Makefile | 95 ++ s6-guide.md | 48 + scripts/.gitignore | 5 + scripts/Makefile | 30 + scripts/halt.in | 24 + scripts/on_ac_power | 46 + scripts/poweroff.in | 23 + scripts/rc-sstat.in | 149 ++ scripts/reboot.in | 25 + scripts/shutdown.in | 29 + sh/.gitignore | 10 + sh/Makefile | 35 + sh/binfmt.sh.in | 90 ++ sh/cgroup-release-agent.sh.in | 19 + sh/functions.sh.in | 124 ++ sh/gendepends.sh.in | 129 ++ sh/init-early.sh.Linux.in | 57 + sh/init.sh.BSD.in | 65 + sh/init.sh.GNU-kFreeBSD.in | 42 + sh/init.sh.GNU.in | 44 + sh/init.sh.Linux.in | 105 ++ sh/migrate-to-run.sh.in | 36 + sh/openrc-run.sh.in | 389 +++++ sh/rc-cgroup.sh.in | 154 ++ sh/rc-functions.sh.in | 168 +++ sh/rc-mount.sh | 87 ++ sh/runit.sh | 52 + sh/runtests.sh | 34 + sh/s6.sh | 58 + sh/start-stop-daemon.sh | 95 ++ sh/supervise-daemon.sh | 61 + src/Makefile | 7 + src/includes/helpers.h | 124 ++ src/includes/hidden-visibility.h | 26 + src/includes/queue.h | 846 +++++++++++ src/includes/rc-misc.h | 75 + src/includes/rc-wtmp.h | 26 + src/libeinfo/.gitignore | 1 + src/libeinfo/Makefile | 12 + src/libeinfo/einfo.h | 140 ++ src/libeinfo/einfo.map | 35 + src/libeinfo/libeinfo.c | 1059 +++++++++++++ src/librc/.gitignore | 2 + src/librc/Makefile | 48 + src/librc/librc-daemon.c | 633 ++++++++ src/librc/librc-depend.c | 1065 +++++++++++++ src/librc/librc-misc.c | 448 ++++++ src/librc/librc-stringlist.c | 151 ++ src/librc/librc.c | 1124 ++++++++++++++ src/librc/librc.h | 122 ++ src/librc/rc.h.in | 604 ++++++++ src/librc/rc.map | 66 + src/rc/.gitignore | 65 + src/rc/Makefile | 176 +++ src/rc/_usage.c | 95 ++ src/rc/_usage.h | 56 + src/rc/checkpath.c | 296 ++++ src/rc/do_e.c | 229 +++ src/rc/do_mark_service.c | 91 ++ src/rc/do_service.c | 78 + src/rc/do_value.c | 65 + src/rc/fstabinfo.c | 339 +++++ src/rc/is_newer_than.c | 34 + src/rc/is_older_than.c | 35 + src/rc/kill_all.c | 251 ++++ src/rc/mountinfo.c | 487 ++++++ src/rc/openrc-init.c | 216 +++ src/rc/openrc-run.c | 1419 +++++++++++++++++ src/rc/openrc-shutdown.c | 170 +++ src/rc/rc-abort.c | 43 + src/rc/rc-depend.c | 177 +++ src/rc/rc-logger.c | 312 ++++ src/rc/rc-logger.h | 23 + src/rc/rc-misc.c | 476 ++++++ src/rc/rc-plugin.c | 244 +++ src/rc/rc-plugin.h | 41 + src/rc/rc-selinux.c | 398 +++++ src/rc/rc-selinux.h | 36 + src/rc/rc-service.c | 132 ++ src/rc/rc-status.c | 416 +++++ src/rc/rc-update.c | 351 +++++ src/rc/rc-wtmp.c | 51 + src/rc/rc.c | 1127 ++++++++++++++ src/rc/shell_var.c | 41 + src/rc/start-stop-daemon.c | 1392 +++++++++++++++++ src/rc/start-stop-daemon.pam | 6 + src/rc/supervise-daemon.c | 808 ++++++++++ src/rc/supervise-daemon.pam | 6 + src/rc/swclock.c | 106 ++ src/test/.gitignore | 6 + src/test/Makefile | 14 + src/test/einfo.data.list | 1 + src/test/einfo.funcs.list | 52 + src/test/rc.data.list | 3 + src/test/rc.funcs.list | 116 ++ src/test/runtests.sh | 127 ++ src/test/units/is_older_than | 83 + supervise-daemon-guide.md | 49 + support/Makefile | 20 + support/deptree2dot/Makefile | 9 + support/deptree2dot/README.md | 11 + support/deptree2dot/deptree2dot | 44 + support/init.d.examples/.gitignore | 11 + support/init.d.examples/Makefile | 13 + support/init.d.examples/README.md | 3 + support/init.d.examples/avahi-dnsconfd.in | 22 + support/init.d.examples/avahid.in | 22 + support/init.d.examples/dbus.in | 26 + support/init.d.examples/dhcpcd.in | 34 + support/init.d.examples/dnsmasq.in | 31 + support/init.d.examples/hald.in | 20 + support/init.d.examples/named.in | 119 ++ support/init.d.examples/ntpd.in | 44 + support/init.d.examples/openvpn.in | 74 + support/init.d.examples/polkitd.in | 20 + support/init.d.examples/sshd.in | 42 + support/init.d.examples/wpa_supplicant.in | 82 + support/openvpn/Makefile | 9 + support/openvpn/README.md | 8 + support/openvpn/down.sh | 34 + support/openvpn/up.sh | 80 + support/sysvinit/Makefile | 8 + support/sysvinit/README.md | 2 + support/sysvinit/inittab | 41 + sysctl.d/Makefile | 6 + sysctl.d/README | 13 + test/setup_env.sh | 23 + test/skel.runtests.sh | 5 + 287 files changed, 31875 insertions(+) create mode 100644 AUTHORS create mode 100644 BUSYBOX.md create mode 100644 ChangeLog create mode 100644 FEATURE-REMOVAL-SCHEDULE.md create mode 100644 HISTORY.md create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 Makefile.inc create mode 100644 NEWS.md create mode 100644 README.md create mode 100644 README.newnet create mode 100644 STYLE-GUIDE.md create mode 100644 TODO create mode 100644 agetty-guide.md create mode 100644 conf.d/Makefile create mode 100644 conf.d/adjkerntz create mode 100644 conf.d/agetty create mode 100644 conf.d/bootmisc create mode 100644 conf.d/consolefont create mode 100644 conf.d/devfs create mode 100644 conf.d/dmesg create mode 100644 conf.d/fsck create mode 100644 conf.d/hostname create mode 100644 conf.d/hwclock create mode 100644 conf.d/ipfw create mode 100644 conf.d/keymaps create mode 100644 conf.d/killprocs create mode 100644 conf.d/localmount create mode 100644 conf.d/modules create mode 100644 conf.d/moused create mode 100644 conf.d/mtab create mode 100644 conf.d/net-online create mode 100644 conf.d/netmount create mode 100644 conf.d/network create mode 100644 conf.d/powerd create mode 100644 conf.d/rarpd create mode 100644 conf.d/savecore create mode 100644 conf.d/staticroute create mode 100644 conf.d/swap create mode 100644 conf.d/syscons create mode 100644 conf.d/urandom create mode 100644 etc/.gitignore create mode 100644 etc/Makefile create mode 100644 etc/devd.conf create mode 100644 etc/rc.conf create mode 100644 etc/rc.conf.orig create mode 100644 etc/rc.devd create mode 100644 etc/rc.in create mode 100644 etc/rc.shutdown.in create mode 100644 guide.md create mode 100644 init.d/.gitignore create mode 100644 init.d/Makefile create mode 100644 init.d/adjkerntz.in create mode 100644 init.d/agetty.in create mode 100644 init.d/binfmt.in create mode 100644 init.d/binfmt_misc.in create mode 100644 init.d/bootmisc.in create mode 100644 init.d/consolefont.in create mode 100644 init.d/devd.in create mode 100644 init.d/devdb.in create mode 100644 init.d/devfs.in create mode 100644 init.d/dmesg.in create mode 100644 init.d/dumpon.in create mode 100644 init.d/encswap.in create mode 100644 init.d/fsck.in create mode 100644 init.d/hostid.in create mode 100644 init.d/hostname.in create mode 100644 init.d/hwclock.in create mode 100644 init.d/ipfw.in create mode 100644 init.d/keymaps.in create mode 100644 init.d/killprocs.in create mode 100644 init.d/local.in create mode 100644 init.d/localmount.in create mode 100644 init.d/loopback.in create mode 100644 init.d/mixer.in create mode 100644 init.d/modules-load.in create mode 100644 init.d/modules.in create mode 100644 init.d/mount-ro.in create mode 100644 init.d/moused.in create mode 100644 init.d/mtab.in create mode 100644 init.d/net-online.in create mode 100644 init.d/netmount.in create mode 100644 init.d/network.in create mode 100644 init.d/newsyslog.in create mode 100644 init.d/nscd.in create mode 100644 init.d/numlock.in create mode 100644 init.d/osclock.in create mode 100644 init.d/pf.in create mode 100644 init.d/powerd.in create mode 100644 init.d/rarpd.in create mode 100644 init.d/rc-enabled.in create mode 100644 init.d/root.in create mode 100644 init.d/rpcbind.in create mode 100644 init.d/runsvdir.in create mode 100644 init.d/s6-svscan.in create mode 100644 init.d/savecache.in create mode 100644 init.d/savecore.in create mode 100644 init.d/staticroute.in create mode 100644 init.d/swap-blk.in create mode 100644 init.d/swap.in create mode 100644 init.d/swclock.in create mode 100644 init.d/syscons.in create mode 100644 init.d/sysctl.in create mode 100644 init.d/sysfs.in create mode 100644 init.d/syslogd.in create mode 100644 init.d/termencoding.in create mode 100644 init.d/ttys.in create mode 100644 init.d/urandom.in create mode 100644 init.d/wscons.in create mode 100644 local.d/Makefile create mode 100644 local.d/README create mode 100644 man/Makefile create mode 100644 man/einfo.3 create mode 100644 man/openrc-init.8 create mode 100644 man/openrc-run.8 create mode 100644 man/openrc-shutdown.8 create mode 100644 man/openrc.8 create mode 100644 man/rc-service.8 create mode 100644 man/rc-sstat.8 create mode 100644 man/rc-status.8 create mode 100644 man/rc-update.8 create mode 100644 man/rc_config.3 create mode 100644 man/rc_deptree.3 create mode 100644 man/rc_find_pids.3 create mode 100644 man/rc_plugin_hook.3 create mode 100644 man/rc_runlevel.3 create mode 100644 man/rc_service.3 create mode 100644 man/rc_stringlist.3 create mode 100644 man/service.8 create mode 100644 man/start-stop-daemon.8 create mode 100644 man/supervise-daemon.8 create mode 100644 mk/cc.mk create mode 100644 mk/debug.mk create mode 100644 mk/depend.mk create mode 100644 mk/dist.mk create mode 100644 mk/gitignore.mk create mode 100644 mk/gitver.mk create mode 100644 mk/lib.mk create mode 100644 mk/net.mk create mode 100644 mk/os-BSD.mk create mode 100644 mk/os-DragonFly.mk create mode 100644 mk/os-FreeBSD.mk create mode 100644 mk/os-GNU-kFreeBSD.mk create mode 100644 mk/os-GNU.mk create mode 100644 mk/os-Linux.mk create mode 100644 mk/os-NetBSD.mk create mode 100644 mk/os-prefix.mk create mode 100644 mk/os.mk create mode 100644 mk/pam.mk create mode 100644 mk/prog.mk create mode 100644 mk/scripts.mk create mode 100644 mk/subdir.mk create mode 100644 mk/sys.mk create mode 100644 mk/termcap.mk create mode 100644 pkgconfig/.gitignore create mode 100644 pkgconfig/Makefile create mode 100644 pkgconfig/einfo.pc.in create mode 100644 pkgconfig/openrc.pc.in create mode 100644 runit-guide.md create mode 100644 runlevels/Makefile create mode 100644 s6-guide.md create mode 100644 scripts/.gitignore create mode 100644 scripts/Makefile create mode 100644 scripts/halt.in create mode 100755 scripts/on_ac_power create mode 100644 scripts/poweroff.in create mode 100644 scripts/rc-sstat.in create mode 100644 scripts/reboot.in create mode 100644 scripts/shutdown.in create mode 100644 sh/.gitignore create mode 100644 sh/Makefile create mode 100644 sh/binfmt.sh.in create mode 100644 sh/cgroup-release-agent.sh.in create mode 100644 sh/functions.sh.in create mode 100644 sh/gendepends.sh.in create mode 100644 sh/init-early.sh.Linux.in create mode 100644 sh/init.sh.BSD.in create mode 100644 sh/init.sh.GNU-kFreeBSD.in create mode 100644 sh/init.sh.GNU.in create mode 100644 sh/init.sh.Linux.in create mode 100644 sh/migrate-to-run.sh.in create mode 100644 sh/openrc-run.sh.in create mode 100644 sh/rc-cgroup.sh.in create mode 100644 sh/rc-functions.sh.in create mode 100644 sh/rc-mount.sh create mode 100644 sh/runit.sh create mode 100755 sh/runtests.sh create mode 100644 sh/s6.sh create mode 100644 sh/start-stop-daemon.sh create mode 100644 sh/supervise-daemon.sh create mode 100644 src/Makefile create mode 100644 src/includes/helpers.h create mode 100644 src/includes/hidden-visibility.h create mode 100644 src/includes/queue.h create mode 100644 src/includes/rc-misc.h create mode 100644 src/includes/rc-wtmp.h create mode 100644 src/libeinfo/.gitignore create mode 100644 src/libeinfo/Makefile create mode 100644 src/libeinfo/einfo.h create mode 100644 src/libeinfo/einfo.map create mode 100644 src/libeinfo/libeinfo.c create mode 100644 src/librc/.gitignore create mode 100644 src/librc/Makefile create mode 100644 src/librc/librc-daemon.c create mode 100644 src/librc/librc-depend.c create mode 100644 src/librc/librc-misc.c create mode 100644 src/librc/librc-stringlist.c create mode 100644 src/librc/librc.c create mode 100644 src/librc/librc.h create mode 100644 src/librc/rc.h.in create mode 100644 src/librc/rc.map create mode 100644 src/rc/.gitignore create mode 100644 src/rc/Makefile create mode 100644 src/rc/_usage.c create mode 100644 src/rc/_usage.h create mode 100644 src/rc/checkpath.c create mode 100644 src/rc/do_e.c create mode 100644 src/rc/do_mark_service.c create mode 100644 src/rc/do_service.c create mode 100644 src/rc/do_value.c create mode 100644 src/rc/fstabinfo.c create mode 100644 src/rc/is_newer_than.c create mode 100644 src/rc/is_older_than.c create mode 100644 src/rc/kill_all.c create mode 100644 src/rc/mountinfo.c create mode 100644 src/rc/openrc-init.c create mode 100644 src/rc/openrc-run.c create mode 100644 src/rc/openrc-shutdown.c create mode 100644 src/rc/rc-abort.c create mode 100644 src/rc/rc-depend.c create mode 100644 src/rc/rc-logger.c create mode 100644 src/rc/rc-logger.h create mode 100644 src/rc/rc-misc.c create mode 100644 src/rc/rc-plugin.c create mode 100644 src/rc/rc-plugin.h create mode 100644 src/rc/rc-selinux.c create mode 100644 src/rc/rc-selinux.h create mode 100644 src/rc/rc-service.c create mode 100644 src/rc/rc-status.c create mode 100644 src/rc/rc-update.c create mode 100644 src/rc/rc-wtmp.c create mode 100644 src/rc/rc.c create mode 100644 src/rc/shell_var.c create mode 100644 src/rc/start-stop-daemon.c create mode 100644 src/rc/start-stop-daemon.pam create mode 100644 src/rc/supervise-daemon.c create mode 100644 src/rc/supervise-daemon.pam create mode 100644 src/rc/swclock.c create mode 100644 src/test/.gitignore create mode 100644 src/test/Makefile create mode 100644 src/test/einfo.data.list create mode 100644 src/test/einfo.funcs.list create mode 100644 src/test/rc.data.list create mode 100644 src/test/rc.funcs.list create mode 100755 src/test/runtests.sh create mode 100755 src/test/units/is_older_than create mode 100644 supervise-daemon-guide.md create mode 100644 support/Makefile create mode 100644 support/deptree2dot/Makefile create mode 100644 support/deptree2dot/README.md create mode 100644 support/deptree2dot/deptree2dot create mode 100644 support/init.d.examples/.gitignore create mode 100644 support/init.d.examples/Makefile create mode 100644 support/init.d.examples/README.md create mode 100644 support/init.d.examples/avahi-dnsconfd.in create mode 100644 support/init.d.examples/avahid.in create mode 100644 support/init.d.examples/dbus.in create mode 100644 support/init.d.examples/dhcpcd.in create mode 100644 support/init.d.examples/dnsmasq.in create mode 100644 support/init.d.examples/hald.in create mode 100644 support/init.d.examples/named.in create mode 100644 support/init.d.examples/ntpd.in create mode 100644 support/init.d.examples/openvpn.in create mode 100644 support/init.d.examples/polkitd.in create mode 100644 support/init.d.examples/sshd.in create mode 100644 support/init.d.examples/wpa_supplicant.in create mode 100644 support/openvpn/Makefile create mode 100644 support/openvpn/README.md create mode 100755 support/openvpn/down.sh create mode 100755 support/openvpn/up.sh create mode 100644 support/sysvinit/Makefile create mode 100644 support/sysvinit/README.md create mode 100644 support/sysvinit/inittab create mode 100644 sysctl.d/Makefile create mode 100644 sysctl.d/README create mode 100755 test/setup_env.sh create mode 100755 test/skel.runtests.sh diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..659381f --- /dev/null +++ b/AUTHORS @@ -0,0 +1,99 @@ +Alessio Ababilov +Alexander Berntsen +Alexander Mezin +Alexander Tsoy +Alexander Vershilov +Alexander Vershilov +Alexander V Vershilov +Alexey Shvetsov +Alon Bar-Lev +Amadeusz Å»oÅ‚nowski +Andrew Gregory +Anthony Donnelly +Anthony G. Basile +Anthony G. Basile +Austin S. Hemmelgarn +Benda Xu +Björn Baumbach +Charlie +Chris Richards +Christian +Christian Ruppert +Christian Wetzig +Christopher Head +Consus +Daniel Mierswa +Daniel Robbins +Diego Elio Pettenò +Diego Elio Pettenò +Diego +Dirk Sondermann +Doug Freed +Doug Goldstein +Ed Wildgoose +Eray Aslan +Eugeny Shkrigunov +Fedja Beader +Flex +Gabriele Giacone <1o5g4r8o@gmail.com> +Gary +Gilles Oivier +Hank Leininger +Ian Stakenvicius +Jakob Drexel +James Le Cuirot +Jan Psota +Jason Zaman +Joe Harvell +Joe M +Johan Bergström +Jory A. Pratt +Juan RP +Kaarle Ritvanen +Kfir Lavi +Kirill Elagin +Lars Wendler +Lorand Kelemen +Marc Joliet +Marien Zwart +Michal Gorny +Mihai Moldovan +Mike Frysinger +Mike Gilbert +Nao Nakashima +Natanael Copa +Nathan Phillip Brink +Ned Ludd +Olivier Huber +Patrick Lauer +Petre Rodan +Piotr Karbowski +Ralph Sennhauser +Richard Yao +Rick Farina (ZeroChaos) +Rick Farina (Zero_Chaos) +Robin H. Johnson +Robin H. Johnson +Robin Johnson +Roy Marples +Salah Coronya +Sebastian Thorarensen +Semen Maryasin +Sergei Trofimovich +Seth Robertson +S. Gilles +Stefan Knoblich +Stef Simoens +Steve L +Steven Chamberlain +Svante Signell +Sven Vermeulen +Thomas D +Thomas Pfaff +Trevor Summers Smith +Walter +William Hubbs +William Hubbs +Will Miles +Yun Zheng Hu +Yuta SATOH diff --git a/BUSYBOX.md b/BUSYBOX.md new file mode 100644 index 0000000..c8a78e7 --- /dev/null +++ b/BUSYBOX.md @@ -0,0 +1,29 @@ +# 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 +counterparts. Since it is possible to configure busybox to not include +these applets or to prefer the standalone counterparts, OpenRC does not +attempt to support the busybox applets. + +For now, it is recommended that you disable the following busybox +configuration settings for best results with OpenRC. + +CONFIG_START_STOP_DAEMON -- The start-stop-daemon applet is not compatible with +start-stop-daemon in OpenRC. + +CONFIG_MOUNT -- The mount applet does not support the -O [no]_netdev options to +skip over or include network file systems when the -a option is present. + +CONFIG_UMOUNT -- The umount applet does not support the -O option along with -a. + +CONFIG_SWAPONOFF -- The swapon applet does not support the -e option +or recognize the nofail option in fstab. + +CONFIG_SETFONT -- The setfont applet does not support the -u option from kbd. + +CONFIG_BB_SYSCTL -- The sysctl applet does not support the --system command +line switch. + +There is work to get most of these supported by busybox, so this file +will be updated as things change. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..7a5d735 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1672 @@ +commit b35099cb707e333b6b8d30d956ffa93bcd2da0ab +Author: William Hubbs +Commit: William Hubbs + + Add comment about overriding the default efivars mount in fstab to news + +commit 3fd3bfc76dccc3752f4af949ad4076dab26357fb +Author: William Hubbs +Commit: William Hubbs + + add link to efivars issue to news file + +commit 492a6303cb8314263bfd3631e3b0de5a9df178da +Author: William Hubbs +Commit: William Hubbs + + Update ChangeLog + +commit e7807b3136d8993805082320784460f5059e6275 +Author: William Hubbs +Commit: William Hubbs + + fix sysvinit compatibility for shutdown wrapper + +commit 03a461ac0ee34b7900868cdea624c6fd868b1656 +Author: William Hubbs +Commit: William Hubbs + + fix sysvinit compatibility for reboot wrapper + +commit 7e0f76e0adc545c74a8332a6ef0811d2aa62cb81 +Author: William Hubbs +Commit: William Hubbs + + fix sysvinit compatibility for poweroff wrapper + +commit 9812ce5b8dc22fe36cc7bf75cf6e62db204ece3d +Author: William Hubbs +Commit: William Hubbs + + 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. + +commit 12f75e4167f84a9a85f69924ebdb28ad36c085cb +Author: Adam Borowski +Commit: William Hubbs + + man: fix an unclosed .Bl/.El warning + + This fixes #151. + +commit 260368e0103e95625c29760f2c2ec89143e5a233 +Author: Adam Borowski +Commit: William Hubbs + + man: fix missing .Pp warnings + + This fixes #151. + +commit f87a9eec3d23ea01578500972f1df993d5d24fba +Author: William Hubbs +Commit: William Hubbs + + init.d/sysfs: mount efivars read only + + This fixes #134. + +commit 1e837d596e483ceb5cec177a6c7faff24a42384b +Author: William Hubbs +Commit: William Hubbs + + fix argument parsing for the sysvinit shutdown wrapper + + This fixes #140. + +commit dcc686e42b406d63d52ef75de9a326f67d0a06c9 +Author: William Hubbs +Commit: William Hubbs + + scripts/shutdown: fix arguments to be sysvinit shutdown compatible + + This fixes #140. + +commit 2f81c100afdf45ebf787dfc5d3261aa6055640e4 +Author: William Hubbs +Commit: William Hubbs + + Fix link to shutdown for MKSYSVINIT=yes + +commit a511a48d77b1dcb8a3fb0dd1abddb750a152869b +Author: Nuno Silva +Commit: William Hubbs + + 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. + +commit 1e5322e5c55ec744a2cdcc3342ef6547eab7c46f +Author: Nuno Silva +Commit: William Hubbs + + init.d/hostname: fix indentation + + This is for #143. + +commit 199a210d2fbc524c9c400a06f832dabffd7ed1b3 +Author: udeved +Commit: William Hubbs + + scripts/Makefile: make symlinks absolute instead of relative + + This closes #142. + +commit 5b7667af32effddf867a5d021c66d43f0645d374 +Author: udeved +Commit: William Hubbs + + scripts/Makefile: respect SBINDIR with MKSYSVINIT + + This is for #142. + +commit 11243f85b67e5f450ddf50346ffd4a1b2c6faeb5 +Author: Jory A. Pratt +Commit: William Hubbs + + kill_all: include limits.h for PATH_MAX + +commit 3c40826d3466cdda1a46abcd5c86b661b8185f46 +Author: William Hubbs +Commit: William Hubbs + + version 0.28 + +commit 560d874d2fee63bf7ca11f17cf9933021b639a1d +Author: William Hubbs +Commit: William Hubbs + + fix compile issue for musl + +commit e84366fd232a41c3ba79ed351e93c74cef8d7c8d +Author: William Hubbs +Commit: William Hubbs + + Update ChangeLog + +commit caacedc0a82285fb2d25c6d3473f154044c7ad66 +Author: William Hubbs +Commit: William Hubbs + + man: update openrc-shutdown man page + + Add the new wtmp options and fix some cross references. + +commit 84d140a1f6abf95a4170d13527152d3ab14e6613 +Author: William Hubbs +Commit: William Hubbs + + 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. + +commit ee886c44824b1dd892eaff2c6da666286e61bc73 +Author: William Hubbs +Commit: William Hubbs + + openrc-shutdown: add --single option and clean up option processing + +commit 1801561c2d36c330df7fd02c7508f503a61ff5ba +Author: William Hubbs +Commit: William Hubbs + + init.d/bootmisc: use openrc-shutdown instead of halt to write halt record + + This fixes #139 and fixes #128. + and fixes #124. + +commit 7689106aa10f7852b707b4c21ec080ccb2767280 +Author: William Hubbs +Commit: William Hubbs + + add support for writing reboot and shutdown records to wtmp + +commit 1564e155b726308200ecd5df315c002bd8b16952 +Author: William Hubbs +Commit: William Hubbs + + openrc-init: add optional sysvinit compatibility + +commit 44bac3c3798f7eb9186c3ea8774552aa191bfae7 +Author: William Hubbs +Commit: William Hubbs + + 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 + +commit 0ddee9b7d2b8dea810e252ca6a95c457876df120 +Author: Sergei Trofimovich +Commit: William Hubbs + + 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. + +commit 688566c535111a141f77caf88db12a4338544f7b +Author: Sergei Trofimovich +Commit: Doug Freed + + mk/cc.mk: make implicit function declarations fatal (#136) + + Avoids issues with missing prototypes causing truncation of pointers. + + Signed-off-by: Sergei Trofimovich + +commit 7185e242ffaa8cd1b672fe4726502a196fd779c2 +Author: Sergei Trofimovich +Commit: Doug Freed + + rc-logger.c: fix crash on fclose(NULL) (#137) + + Only close the log if we successfully opened it. + + Reported-by: Brian Evans + Tested-by: Brian Evans + Signed-off-by: Sergei Trofimovich + +commit ec27299f4b88daa80261298fafea76ae634744d9 +Author: William Hubbs +Commit: William Hubbs + + typo fix + + X-Gentoo-Bug: 618888 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=618888 + +commit 1ece16bfcd0ab71d2f9fe17a75ee6184e0fa4828 +Author: William Hubbs +Commit: William Hubbs + + openrc-shutdown: add dry-run option + +commit 0cfd0dd9ef580ed9dc563ccc164d70efe8f299db +Author: William Hubbs +Commit: William Hubbs + + 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. + +commit a77ee2e94191ba1a286b8a6835f76556481566ba +Author: William Hubbs +Commit: William Hubbs + + init: add ability to switch to single user mode + +commit 49b8a573a195f4b2cee992cd10678694da0a6f4f +Author: William Hubbs +Commit: William Hubbs + + 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. + +commit a2055af90054f5125cc07d4851b1dc9d16815e7c +Author: William Hubbs +Commit: William Hubbs + + rc_status: calculate time differences in time_t and display seconds in uptime + +commit cbf96967f1b6dc72ae16203dfbbb844bd08e8b6b +Author: William Hubbs +Commit: William Hubbs + + supervise-daemon: save start time and respawn count before dropping privs + +commit f1013037b47cdd6344f1b3ed92b7f84d7fcca01f +Author: William Hubbs +Commit: William Hubbs + + version 0.27 + +commit e4bfb4530a86a4ccdff312c857df37fa0da36fd6 +Author: William Hubbs +Commit: William Hubbs + + update ChangeLog + +commit 78e0042eccaf5a5554b195ad391b3ab0b8974cf6 +Author: William Hubbs +Commit: William Hubbs + + man/rc-status: document changes for supervised daemons + + rc-status now shows the amount of time a supervised daemon has been + active as well as the number of times it has been respawned during the + current respawn period. + +commit 82e12e309247bc84abf29aca04b3a2dd845fa11b +Author: William Hubbs +Commit: William Hubbs + + rc-status: show uptimes and respawn counts for supervised daemons + +commit 1ebef0d7a38ec0a9635418b75c3aabb564c1577e +Author: William Hubbs +Commit: William Hubbs + + fix to_time_t to honor dst + +commit 6b4050ab9cf9d678a1d6b7af7af7494f8533dbca +Author: William Hubbs +Commit: William Hubbs + + fix from_time_t function + +commit cf5e9aa2bbcdf1783fadeab26586c1134929d928 +Author: William Hubbs +Commit: William Hubbs + + Move time_t conversions to rc-misc.c so they can be shared + +commit a3250e77d412f2290e381b9e7569930d95e4fc5b +Author: William Hubbs +Commit: William Hubbs + + supervise-daemon: save start time and respawn count + + This will allow rc-status to display an uptime and restart count for + supervised processes. + +commit df027ca4722c8755b23a65db75728b835ccca807 +Author: William Hubbs +Commit: William Hubbs + + supervise-daemon: fix our status when we give up on the child process + +commit 4c89e3f5fa1c65ccd0c843f98e4013c2085f243f +Author: William Hubbs +Commit: William Hubbs + + supervise-daemon:create multiple options from --respawn-limit + + This creates --respawn-delay, --respawn-max and --respawn-period. It was + suggested that it would be easier to follow if the options were + separated. + + This is for #126. + +commit 3673040722b75c0a4d06fbeb272f917c7d1ea7c4 +Author: William Hubbs +Commit: William Hubbs + + supervise-daemon: add a --respawn-limit option + + Allow limiting the number of times supervise-daemon will attempt to respawn a + daemon once it has died to prevent infinite respawning. Also, set a + reasonable default limit (10 times in a 5 second period). + + This is for issue #126. + +commit 96c8ba2fb5f91a711ef5bfcfd8affe0b74ad18fe +Author: William Hubbs +Commit: William Hubbs + + supervise-daemon: mark all open file descriptors FD_CLOEXEC + +commit 47cf1d0c707dc88d216bebc15be3f39d5eb47fa9 +Author: William Hubbs +Commit: William Hubbs + + supervise-daemon:remove the controlling tty in the supervisor + +commit 06a6a27e441372164872c7712b80728527a6ec05 +Author: William Hubbs +Commit: William Hubbs + + supervise-daemon: fix access to tty_fd and devnull_fd + + Both the child and supervisor need access to these file descriptors. + +commit 5de3798afc55ce147e65926f863ec9c9cef60e79 +Author: William Hubbs +Commit: William Hubbs + + supervise-daemon: mark the service started when the supervisor is active + +commit 6ac094a59cf7b51d8527af15b07feca707a635c8 +Author: William Hubbs +Commit: William Hubbs + + version 0.26 + +commit 84c81ca02d7077a619dc704ff654385846fcd2b4 +Author: William Hubbs +Commit: William Hubbs + + update ChangeLog + +commit 0e3f8720984d7d5752a78a4135cd268e4f83b3d7 +Author: William Hubbs +Commit: William Hubbs + + init: send term/kill signals as final step of shutdown + +commit 5fd3747b190887d094225547f23007d25e2d9592 +Author: William Hubbs +Commit: William Hubbs + + reword the bugs section of the openrc-init man page + +commit 4694900190a9078397bb9083328b68b489af92f4 +Author: William Hubbs +Commit: William Hubbs + + init: fix signal handling + + The only signals we handle are SIGINT and SIGCHLD, so block all others + and unblock them in the child process before we start a rurnlevel. + +commit 05738bfce120114037d4f02c67ec740813f94b89 +Author: William Hubbs +Commit: William Hubbs + + init: add re-exec capability + + This will allow the re-execution of the init process after upgrading + OpenRC. + +commit 6f88ee4ec6f59e545346a7422facc3e5b6adac04 +Author: i.Dark_Templar +Commit: William Hubbs + + bootmisc: do not remove ld-elf32.so.hints + + File /var/run/ld-elf32.so.hints is used on FreeBSD 64bit multilib + This fixes #125. + +commit cc51bdca3bac963878141da047e92f125772e14f +Author: William Hubbs +Commit: William Hubbs + + Add attribution to openrc-init.c and openrc-shutdown.c + +commit 13ca79856e5836117e469c3edbcfd4bf47b6bab0 +Author: William Hubbs +Commit: William Hubbs + + add init process + + openrc-init.c and openrc-shutdown.c are based on code which was written by + James Hammons , so I would like to publically + thank him for his work. + +commit 79a9edc73068244ad843f2edbe4206ce696c91c8 +Author: i.Dark_Templar +Commit: i.Dark_Templar + + Fix make install on FreeBSD: don't try to install /etc/init.d/modules twice + +commit 9eb669591e16c5ee0ca07babe058d0b7b2396077 +Author: Austin English +Commit: William Hubbs + + start-stop-daemon: warn if calling --start with --retry or --stop with --wait + + This fixes #122 + +commit 55a87a30ec845eb725e8a923c8f8eb7aa75baa72 +Author: William Hubbs +Commit: William Hubbs + + init.d/agetty.in: add -prefix keyword + +commit a912029462ae988ab4e2a96a0958e54a3c2e822f +Author: William Hubbs +Commit: William Hubbs + + init.d/mount-ro: change dependency on killprocs and savecache to after + + killprocs always succeeds and savecache is not required by mount-ro, so + we can just start after both of these have run. + +commit 1e9078279709df2a3617bf1460890ceb1ddfcf59 +Author: William Hubbs +Commit: William Hubbs + + agetty-guide: typo fix + +commit 51a292e09b9362f13d5747d4eedaa521ddf9ce72 +Author: William Hubbs +Commit: William Hubbs + + init.d: add agetty to ignore patterns + +commit 50fccf47d4bd2ed6e7ea6ff7f72577c8e7b95d0d +Author: William Hubbs +Commit: William Hubbs + + sh/gendepends.sh.in: fix detection of service scripts + + We do not need to care about the path on the shebang line of a service + script as long as the shebang line ends with "openrc-run". + This fixes #119 and #120. + +commit 9bd63b5d4a16601712a65eb8962214cdd4d26ce5 +Author: William Hubbs +Commit: William Hubbs + + update dependencies for clock service + + The clock services had a very long list of "before" dependencies that + referred to other services within OpenRC. For ease of maintenance, + convert these to "after clock" dependencies in the individual services. + +commit 48db17a93f5e60c0d241f8fb06bfbdd01206fb9c +Author: William Hubbs +Commit: William Hubbs + + update news file + +commit c333707cba356f4cacfd58a6fcc78f7c073dddcd +Author: William Hubbs +Commit: William Hubbs + + Remove all occurances of 'before *' from dependencies + + Using wildcards in dependencies causes issues when rc_parallel is set to + yes because it can lead to deadlocks. + All dependencies need to be explicit rather than implicit. + + This is the first stage of moving this direction. + +commit 5f5b1f7cbefd0bc14352e86a9c33260266f98d9b +Author: William Hubbs +Commit: William Hubbs + + init.d/sysfs.in: efivarfs tweaks + + Since we check for /sys/firmware/efi/efivars, we do not need to check + for /sys/firmware/efi + + Since Failing to mount efivarfs is not critical, we silence the error + message from mount. + +commit cfdf56475e600f79a433cd721cadf39114c6c58d +Author: William Hubbs +Commit: William Hubbs + + version 0.25 + +commit fde3902d069dfdce9c59555186a5541d6d99c8aa +Author: William Hubbs +Commit: William Hubbs + + update ChangeLog + +commit d7bbb0f5830e1ec4df1ec52714d70ac6b0a81878 +Author: William Hubbs +Commit: William Hubbs + + add agetty service + + The agetty service is an alternate way to manage gettys with agetty + under Linux which is separate from an external init system. + +commit 21ca2b746cce67e2b3578fb7015c9d4c243e3a0c +Author: William Hubbs +Commit: William Hubbs + + init.d/sysfs: drop modules completely from the dependencies + + This is for #112. + +commit 6a79aef0159d7035778fd852b26ecfac903cd029 +Author: William Hubbs +Commit: William Hubbs + + init.d/sysfs: Do not load efivarfs module + + My understanding is that the kernel can autoload this module. If it + doesn't, the module should be built in or loaded from an initramfs. + + This fixes https://github.com/openrc/openrc/pulls/112. + +commit 4a269674b765e5267f024fa55c8644480a7304ea +Author: William Hubbs +Commit: William Hubbs + + make sure netmount and localmount start after root + +commit eea4decdd1c84e4b8775a255d8ed85bce5eb40a5 +Author: William Hubbs +Commit: William Hubbs + + net-online: typo fix + +commit d4d0f25a4844ecaed43de913e8b729e7a2e894db +Author: William Hubbs +Commit: William Hubbs + + net-online: updates to make the service more usable + + - switch from attempting to ping the default gateway to a host outside + the local network, defaulting to google.com. + - along with this, change the name of the variable that requests a ping + test to include_ping_test so the meaning is more clear. + +commit 1cb44092fce298004ab4c4547c6fbcac29c5997f +Author: William Hubbs +Commit: William Hubbs + + sh/rc-functions.sh.in: add get_bootparam_value function + +commit 4207e46622f584eb5f0cc10bbfd36f92f001a2e2 +Author: William Hubbs +Commit: William Hubbs + + move init.d examples under support and install them + +commit f6ea16159ec8583a6f2182578334aa00578cb080 +Author: William Hubbs +Commit: William Hubbs + + scripts: make sure the rc-sstat symlink is always replaced + +commit 9047ea4cb0d0e5e27704369380e128d26c3e86b2 +Author: William Hubbs +Commit: William Hubbs + + install support files + + These files have been in the distribution for some time but haven't been + installed. They are good examples of how to do things, so we should + install them. + +commit d7f5a696c173e7af67dd4b3f90744e0a056441e1 +Author: William Hubbs +Commit: William Hubbs + + support: rename all README files to README.md + +commit 6f614cd3f33dbdea3a67ac2fb414b1130674ee04 +Author: William Hubbs +Commit: William Hubbs + + Move deptree2dot to the support folder + + Since deptree2dot and the perl requirement are completely optional, we + can move this tool to the support folder. This gives the user the option + of using it if they have perl installed, and means we do not have an + optional runtime dependency on perl. + + Documentation for this tool has also been added to the support folder. + + X-Gentoo-Bug: 600742 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=600742 + +commit 85c1930acf15b0c9d3c5537fb2b0409c6a11c982 +Author: William Pitcock +Commit: William Hubbs + + test/setup_env: ensure that eval_ecolors is available on the path. + + The test environment previously used the system default paths instead of installing the necessary $PATH environment + variable to make finding eval_ecolors work. + This closes #117. + + X-Gentoo-Bug: 374191. + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=374191. + +commit 96bd0c004c9549e5a2ef64277216a15da6f96a8e +Author: Nicolas Porcel +Commit: William Hubbs + + Fix typo in guide.md + + This fixes #115. + +commit b693af90556ac9b055ba5c6e589066c1e08b2146 +Author: William Hubbs +Commit: William Hubbs + + Revert "scripts: do not substitute for @SHELL@ in rc-sstat" + + This reverts commit e2e652e469efa5f3ebcd69828ff16d8f5ad3f1b8. + +commit 6dcb6929869c2f81c1f8d0930191f74fc6dfaa3e +Author: Doug Freed +Commit: William Hubbs + + start-stop-daemon: allow all standard signals + + Also we define the signalpair_item macro. + This fixes #113. + + X-Gentoo-Bug: 604986 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=604986 + +commit e2e652e469efa5f3ebcd69828ff16d8f5ad3f1b8 +Author: William Hubbs +Commit: William Hubbs + + scripts: do not substitute for @SHELL@ in rc-sstat + +commit b73941f0c3020e3dbd1684d09685e114c678b520 +Author: William Hubbs +Commit: William Hubbs + + mountinfo: make the path to /proc/mounts a constant + + This path should not be hard coded in the open call. + Linux prior to 2.4.19 did not have /proc/self/mounts, so for now I'm + making this value /proc/mounts everywhere, but that may change to + /proc/self/mounts on linux; I'm not sure we should care about <2.4.19. + + X-Gentoo-Bug: 604646 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=604646 + +commit c304522131a795cf882444b5f94e81db4baf65b3 +Author: Benda Xu +Commit: William Hubbs + + Clean up warnings that can use the _unused macro + + X-Gentoo-Bug: 604666 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=604666 + +commit 92325b44ba58a7ca04d88ae8ca202b402b032b43 +Author: Benda Xu +Commit: William Hubbs + + Indentation fixes + + X-Gentoo-Bug: 604666 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=604666 + +commit 074d90f5a4e3b66e532a0becde372acf38346397 +Author: Benda Xu +Commit: William Hubbs + + Drop the use of the _BSD_SOURCE macro on Linux + + X-Gentoo-Bug: 604666 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=604666 + +commit 7056b56b3ccc9cbde4ef8297b923919c49c7c242 +Author: Benda Xu +Commit: William Hubbs + + Drop the use of the _BSD_SOURCE macro on GNU/Hurd + + X-Gentoo-Bug: 604666 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=604666 + +commit d5c3b85e3fbddbba149687244d607fcdae222f95 +Author: Doug Freed +Commit: Doug Freed + + loopback: drop explicit route for BSD too + +commit 1ab2249448ba24a591b561f53aa64ff3df1e41f6 +Author: William Hubbs +Commit: William Hubbs + + version 0.24 + +commit a15cff21c63f2ad951af1c59a74fc7d37f78e91b +Author: William Hubbs +Commit: William Hubbs + + update ChangeLog + +commit 45aa36cc623eeeb15fb6827b57e0c07a37cdef41 +Author: Doug Freed +Commit: William Hubbs + + librc: detect loops in stacked runlevels and abort + + This fixes #109. + X-Gentoo-Bug: 558700 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=558700 + +commit d3f833179b39368442221c448f90b87f76d28ee8 +Author: William Hubbs +Commit: William Hubbs + + sh/init.sh.Linux.in: remove unused check for Gnu/KFreeBSD + + This script only runs on Linux, so the check will always be false. + +commit abe552b969b6601f47ba0474f683d8cd80d53c9d +Author: William Hubbs +Commit: William Hubbs + + modules: get rid of printing each module on Linux + + Now that we respect the module blacklists, don't print every module we + try to load, because it might not end up loaded due to the blacklist, + and modprobe doesn't consider that a failure. + +commit 856eafb006655b7dda630a94cbd16f5db9f781be +Author: Doug Freed +Commit: Doug Freed + + sh/init.sh.Linux.in: skip /proc test if no md5sum + + This will also warn users if md5sum is missing, which serves as a pretty + good indicator that /usr is not mounted. + +commit f27d60add9ee1ef8a90ea0034edf6f4e4e6d0ed8 +Author: Robin H. Johnson +Commit: Robin H. Johnson + + sh/openrc-run.sh: expose default start/stop/status + + Supervisor setups break easily when start/stop/status functions are not + default. + + Applications that write multiple PIDs to a pidfile (eg HAProxy as + described in bug 601540), can also benefit from being able to call the + default start/stop/status with modified environment variables. + + Expose the default start/stop/status functions as + default_start/stop/status, and use them for the defaults + start/stop/status. + + Trivial usage example: + ``` + stop() + { + t=$(mktemp) + for pid in $(cat $pidfile) ; do + echo $pid >$t + pidfile=$t default_stop + done + rm -f $t + } + ``` + + X-Gentoo-Bug: 601540 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/601540 + Signed-off-by: Robin H. Johnson + +commit 8ad460c54ce66aa0900cf872d9ebfacf0c03f9da +Author: Doug Freed +Commit: Doug Freed + + Fix typos + + Fixes #99 + +commit 72c0824961fc257b634a9439496e04d1b3392ef1 +Author: Doug Freed +Commit: Doug Freed + + localmount: add comment about types variable + +commit 5b7e3490ef2ce96c35e6c18b4c64e8c61586bb7a +Author: Alan Somers +Commit: Doug Freed + + Localmount shouldn't mount remote filesystems + + The /etc/init.d/localmount script has a syntax error that causes it to + attempt to mount remote filesystems, causing the boot to fail. The + script appends a "no" to each remote filesystem type, but it should only + be append the "no" to the beginning of the list. This patch fixes + localmount on FreeBSD 12.0. A review of the mount(8) manpage on Ubuntu + 12.04 suggests that this patch is correct for Linux, too. + +commit dd61e6bfc3fc1da011b01f4f6cf3e45e26c59dc1 +Author: William Hubbs +Commit: William Hubbs + + rc.conf: fix the commented default setting for rc_logger + + X-Gentoo-Bug: 601480 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=601480 + +commit 204971c6e2ea1e37fa037e09bff02eea3a07f843 +Author: Doug Freed +Commit: GitHub + + runlevels: remove bad trailing backslash + +commit 3552f0ae548d68effd4411ad4080e7b13fe627c5 +Author: William Hubbs +Commit: William Hubbs + + man/start-stop-daemon.8: clarify documentation about --pidfile option + + The documentation implied that if you stop a daemon we handle multiple + pids in a pid file. This is not correct. We only handle the first pid. + + X-Gentoo-Bug: https://bugs.gentoo.org/show_bug.cgi?id=601540 + +commit 42cb84882918a0c9dd93a89d92b0b4818dfb44b8 +Author: AndCycle +Commit: William Hubbs + + fix manual typo + + This fixes #105. + +commit e0ac661419042cb39c1ccf93df2981504d1e6339 +Author: William Hubbs +Commit: William Hubbs + + split tmpfiles processing into opentmpfiles + + The openntmpfiles package is designed so that it can be used on systems + independently of whether openrc is used. + +commit 6414c3bc394f86a5d6a5f02c934469e21bbbc923 +Author: Jason Zaman +Commit: William Hubbs + + selinux: fix SIGSEGV with invalid contexts + + Fixes: https://github.com/openrc/openrc/issues/104 + +commit 4f9bd7e4db185ce6debbebb5242344d8ffadc3ae +Author: William Hubbs +Commit: William Hubbs + + init.d/loopback.in: drop the route to the loopback interface on Linux + + This is related to #103. + +commit bf539f2196290864ce5c5fd0d679b74ee016e2da +Author: William Hubbs +Commit: William Hubbs + + init.d/mount-ro: do not remount /usr read only if it is premounted + + X-Gentoo-Bug: 573760 + X-Gentoo-Bug: https://bugs.gentoo.org/show_bug.cgi?id=573760 + +commit 20b60ea904612669dfb744beffcd8e7e447f69ef +Author: William Hubbs +Commit: William Hubbs + + conf.d/net-online: clarify comment about interfaces setting + + This setting refers to all interfaces that support ethernet + +commit f53c8baef3a6215077c00901759cbbcbe8f10e9b +Author: William Hubbs +Commit: William Hubbs + + init.d/net-online: remove interfaces and timeout from local declarations + + X-Gentoo-Bug: 598621 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=598621 + +commit be06cd250e12e63b8eb704bb2508e06fb9791251 +Author: William Hubbs +Commit: William Hubbs + + src/rc/rc: do not try to start services if fork fails + +commit 003657c973ea338a19f2b7294190af9d76cf5cea +Author: Robin H. Johnson +Commit: William Hubbs + + init.d/loopback: drop scope on loopback + + Busybox does not support the 'scope' argument on 'ip address add' or 'ip + route add', this is documented in BUSYBOX.md, but is no longer actually + needed, as the kernel does get it right without manual specification, + and the ifconfig variant already relies on the kernel to get it right. + This is part of #103. + + X-Gentoo-Bug: 487208 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=487208 + +commit 4fd144c0a6526963c70f18cb34a65354c2f0a48c +Author: William Hubbs +Commit: William Hubbs + + src/rc/rc-misc.c: report error if call to flock() fails + + X-Gentoo-Bug: 597390 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=597390 + +commit c44c904a61418189c989e978b0237e5b161263ef +Author: Joe Maloney +Commit: William Hubbs + + init.d.misc/wpa_supplicant: find wireless interface for FreeBSD + + This fixes #101. + +commit 78146b0e14cb57dda8a3aed3d4f8d6b1db7a3c7e +Author: Sven Wegener +Commit: William Hubbs + + do_service: Initialize idx to 0 + + If index is not explicitly specified for service_started_daemon, it will + look for daemons by random index. + + This fixes #100. + +commit deaae7ab5c499191426cec81f6e803c972f0cca3 +Author: William Hubbs +Commit: William Hubbs + + init.d/sysfs: load efivarfs module when booting in efi mode:1 + + The presence of /sys/firmware/efi is used to indicate that the system + was booted in efi mode. + +commit 3d2c2f0b871944492036d04b0c220ccba1fa2dd5 +Author: William Hubbs +Commit: William Hubbs + + init.d/sysfs: fix efivarfs module test + +commit 6a0c033a64ce18056625cd37a94b9810dc5784e3 +Author: William Hubbs +Commit: William Hubbs + + init.d/sysfs: fix efivarfs handling + + Separate loading the module, if it isn't built in or loaded, from + mounting the file system. + + This also makes sure the warning about configuring the module in + /etc/conf.d/modules or building it in is displayed only if it is loaded + successfully. + + X-Gentoo-Bug: 595836 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=595836 + +commit 6710316a18c33601e780282e72c60f09b5175280 +Author: Doug Freed +Commit: Doug Freed + + openrc-run: fix double free + +commit 61882821e0d6110a2ca2f67fad7c362983a85cf0 +Author: Doug Freed +Commit: Doug Freed + + init.d: Clean up some bad ewarn output + +commit 969546bcf0203379db286be21c7f709d27cc73b0 +Author: William Hubbs +Commit: William Hubbs + + typo fix + + X-Gentoo-Bug: 595306 + X-Gentoo-Bug: https://bugs.gentoo.org/show_bug.cgi?id=595306 + +commit d0ae7ffc2534fa65c2e8927931f5107ce4505ca6 +Author: William Hubbs +Commit: William Hubbs + + version 0.23 + +commit b71bcc242202752bc74fce3a5c629f172b04fca5 +Author: William Hubbs +Commit: William Hubbs + + update ChangeLog + +commit 24010dcb483cf7284cd8a5db111ae63f0d4e1038 +Author: William Hubbs +Commit: William Hubbs + + dist: convert to tar.gz + + This allows the "make dist" target to be used as well as the github + archive generation. + +commit 0a76627345a173fc00be9864f3f3f5c3b15319cd +Author: William Hubbs +Commit: William Hubbs + + init.d/swap: remove the case for linux + + I am removing the separate case for Linux, because we are droppping the + "-e" switch. + +commit bbf98befb86337a36ef5af7f273e503a6de4b9bd +Author: William Hubbs +Commit: William Hubbs + + sh/init.sh.Linux.in: update test for live /proc to use md5sum + + This allows us to avoid the warnings from bash-4.4 about null bytes in + command substitutions. + + If you have separate /usr, are not using an initramfs, and have a file + called /proc/self/environ on your root file system, this will break. + X-Gentoo-Bug: 594534 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=594534 + +commit 316903fbf0da6edc067a98327c8c6cb2b3cdcf93 +Author: William Hubbs +Commit: William Hubbs + + man/openrc-run.8: typo fix + +commit 66a9788435e51e658e4ae9d3ce0d0e54ea53e4f9 +Author: William Hubbs +Commit: William Hubbs + + man/openrc-run.8: Add note about eval usage + + This fixes #77. + +commit bf73363f220ff086d2559e7c2015801f80862749 +Author: William Hubbs +Commit: William Hubbs + + Add --use-blacklist to modprobe calls in modules and modules-load + + This means that we will honor the modprobe black lists. + + X-Gentoo-Bug: 594012 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=594012 + +commit d6c30ab12a3b335ac57cd1f0ac00231bb34fc0c4 +Author: William Hubbs +Commit: William Hubbs + + Revert "Remove eval calls from supervisor start functions" + + This reverts commit 0d1f1010c299a95332f224c3be9e8dfdd85eec54. + We need the eval in case someone uses something like: + command_args="this \"is a\" test" + + This is related to #77. + +commit 0d1f1010c299a95332f224c3be9e8dfdd85eec54 +Author: William Hubbs +Commit: William Hubbs + + Remove eval calls from supervisor start functions + + This fixes #77. + +commit 83bb827edf5b9be04a326d1970d6f55db239281f +Author: William Hubbs +Commit: William Hubbs + + Revert "Disable parallel startup in interactive mode" + + This reverts commit 8b4fc05ff2645b2ecb0f153492f72dd8b39ba431. + The original commit did not explain why this feature was disabled, and I + now have a request to enable it. + + This fixes #24. + +commit c146b966913ae80652e3be925d3aba60ed82f14d +Author: William Hubbs +Commit: William Hubbs + + Add command_progress variable + + If this is set to yes, 1, true, or on, start-stop-daemon will display a + progress meter while waiting for a daemon to stop. + +commit 6cabaf274defa11773094a2c85b3d0a9f0bd9b08 +Author: Doug Freed +Commit: Doug Freed + + rc-misc: allow EINFO_VERBOSE through too + +commit 1edb5f6fd9c4827e5d4ed5c854bc322ba8a7df73 +Author: Doug Freed +Commit: Doug Freed + + rc-misc: Allow EINFO_COLOR through env_filter() + + This allows rc-service -C to properly not print color + messages. + + Fixes #93 + +commit c4d7e02abd7008b8e8ad16f62c2abbb60fab252b +Author: William Hubbs +Commit: William Hubbs + + Fix permission checks for cgroups + + This is needed because containers may give read access to cgroups but + not allow the settings to be changed. + +commit a4e0d675e13f07bf880da10a4d602983a556264d +Author: William Hubbs +Commit: William Hubbs + + man/openrc-run.8: update variable documentation + + - document command_args_background and command_user.r + - clarify documentation for command_background + + This fixes #78. + +commit 8a8032478a755f6e2ceaebc5425e61c6817df936 +Author: William Hubbs +Commit: William Hubbs + + Make use of name vs RC_SVCNAME consistent in supervisor scripts + + This fixes #79. + +commit ac53c9a658589456c678b6bfe674a66a3845e564 +Author: William Hubbs +Commit: William Hubbs + + sh/init.sh: fix the test for cache restoration + + This fixes the test for cache restoration since we are no longer caching + the dependency tree. + +commit b02ff466fa75cc4b5bcfaff3f2989cc65c823f43 +Author: William Hubbs +Commit: William Hubbs + + savecache: stop saving the dependency tree + + This fixes #85. + +commit 6bd0f2d096f149906061a4ac7b66b7e85516784a +Author: William Hubbs +Commit: William Hubbs + + init.d/procfs: typo fix + +commit 63f8ae466f046dcdbb0ba13ef96e63eeec86e6e6 +Author: frickler01 +Commit: William Hubbs + + Format code blocks and variable/path notations + + Add markdown backticks for commands, variable names and path as well + as code blocks for better readability. + + This fixes #97. + +commit 841b883825ddf9982a673b3964757f6df25acd46 +Author: William Hubbs +Commit: William Hubbs + + hwclock: fix module load warning + +commit ba10793b0b85b11fae04e6526716c6f7976afde1 +Author: William Hubbs +Commit: William Hubbs + + init.d/procfs: fix binfmt_misc module load warning + + This reworks the logic so that the warning about configuring the + binfmt_misc module is only displayed if the module actually has to be + loaded. + +commit d4d559323819c8a5279bf197d8d3ff80f1e28cdc +Author: William Hubbs +Commit: William Hubbs + + sh/openrc-run.sh: read global configuration settings first + + X-Gentoo-Bug: 503134 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=503134 + +commit d5db5489be135ae9295e378e789b4b7b13367fdd +Author: William Hubbs +Commit: William Hubbs + + init.d/swap: do not unmount all tmpfs file systems + + X-Gentoo-Bug: 568162 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=568162 + +commit d06db93d5954460668d09cf6ef2fc401ee9d981c +Author: William Hubbs +Commit: William Hubbs + + remove swapfiles service + + The swapfiles service was basically a copy of the swap service, so this + commit consolidates the functionality into the swap service. + + X-Funtoo-Bug-URL: https://bugs.funtoo.org/browse/FL-2523 + X-Gentoo-Bug: 568162 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=568162 + +commit 8c14d0c476e06fff7598c526e26b6a13d53a4600 +Author: Martin Väth +Commit: Martin Väth + + Fix typo in RC_UNAME check of modules-load + + The $RC_UNAME "Linux" had been misspelled as "linux". + As a consequence, entries in e.g. /etc/modules-load.d failed to + load any module succesfully under Linux(!) + +commit 04debf6f25b3748a101b61cb85f78617dbe5be6e +Author: William Hubbs +Commit: William Hubbs + + another news typo fix + +commit c289774b00d0d7dc38fdc1f0f623569bd184a4b1 +Author: Doug Freed +Commit: Doug Freed + + modules-load: handle comments better + + This handles comments without a trailing space after the comment + character. + + Reported-By: josef64 + +commit 9dd8ee330d8a4449c937bc95fc8393a55913c8d1 +Author: William Hubbs +Commit: William Hubbs + + typo fix + +commit 5d5856c193768d24f11d5f0533e48c39526aef5c +Author: William Hubbs +Commit: William Hubbs + + Update news file + + Add information on modules-load service and more explanation about + dealing with the rc -> openrc and runscript -> openrc-run transitions. + +commit 686e172207ac9e23560da18a6f877be777ded935 +Author: William Hubbs +Commit: William Hubbs + + init.d: add modules-load to ignore patterns + +commit fef6268f8d03e3ab3e2564cbf3634d0db2bcd99e +Author: William Hubbs +Commit: William Hubbs + + modules-load.d: cleanups + + Move list of directories to a local variable and create the fn variable + to use for an individual file name rather than using path. + +commit 556dbff99d53cdcc00e6b1ec67e1679f72b6f284 +Author: William Hubbs +Commit: William Hubbs + + Add modules-load.d support + +commit 69ac78d76a31d843c004717eb6aa6a77bb4c9a8e +Author: William Hubbs +Commit: William Hubbs + + openrc-run: make runscript warning respect quiet option + + X-Gentoo-Bug: 591414 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=591414 + +commit 4018dfc8de4818101c336ff8bcf0f4762b318c6a +Author: William Hubbs +Commit: William Hubbs + + init.d/hostname: do not use localhost as a default hostname + + This allows the operating system default hostname to be used if no + hostname is configured. + +commit 353bb9bc9a0ab3c6650d72d2ceb14c990762a2a0 +Author: William Hubbs +Commit: William Hubbs + + init.d/hostname: add support for /etc/hostname + +commit 73cdf10f1f513be7b5dec4f1cc91e0c68cda689b +Author: William Hubbs +Commit: William Hubbs + + Deprecate automatic loading of modules + + In the hwclock, procfs and sysfs service scripts, we automatically + attempt to load the kernel modules we need before we take any action. We + shouldn't do this, because there are systems which do not use kernel + modules and do not have the kmod package installed. + + With this change, we continue to load the modules ourselves, but we warn + the admin that they need to be added to /etc/conf.d/modules or built + into the kernel. + + In the future, this automatic loading will be dropped. + + X-Gentoo-Bug: 342313 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=342313 + +commit 1a55d46645b376cd27f394796934150120a08387 +Author: Raymond Jennings +Commit: William Hubbs + + local.d/README: typo fix + + X-Gentoo-Bug: 591258 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=591258 + +commit cae3976ef1276ce33aa7e49474f13499a48a3fe6 +Author: William Hubbs +Commit: William Hubbs + + init.d: Add runsvdir to ignore patterns + +commit da28a3d367b6078deda6bc205806b43b971e67a9 +Author: William Hubbs +Commit: William Hubbs + + init.d: initial service adjustments for docker support + + Add -docker keyword to the same scripts that have -lxc keyword. + +commit ca8c29ee60b0e8ca89091aaf801725bd71e28001 +Author: William Hubbs +Commit: William Hubbs + + librc: fix Docker auto detection + + The original auto detection of Docker containers assumed the presence of + a container environment variable. However, Docker-1.12 does not + implement this, and I'm not sure which versions of docker implemented + it. + + The new test is for the presence of a file named .dockerenv in the + root directory. + +commit f62253b8334a85dac4671e42817b96a3bedd1881 +Author: William Hubbs +Commit: William Hubbs + + Add support for runit + + X-Gentoo-Bug: 501364 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=501364 + +commit f2c2e2dd5a5e0a22da4dcabea6615d0f4697a962 +Author: William Hubbs +Commit: William Hubbs + + init.d/sysctl.in: typo fix + +commit 94b98430cb83a8f4e62d837100fc357e9eb12ca6 +Author: Kenneth Lakin +Commit: William Hubbs + + start-stop-daemon: Add SSD_IONICELEVEL + + This is the disk IO counterpart to SSD_NICELEVEL. + Modified by William Hubbs to add the variable to the start-stop-daemon + man page. + + This fixes #69. + +commit b19d0a40d7f20987323d5af91469c720ead39561 +Author: William Hubbs +Commit: William Hubbs + + init.d/loopback: remove unnecessary stop function + +commit 0c229faf7e6a57bcff70f2143b83cb69a34c89f4 +Author: Martin Väth +Commit: William Hubbs + + tmpfiles.sh: Support lines with q Q h H + + btrfs support is not implemented yet (for q Q v), but at least tmpfiles.sh + no longer chokes about tmpfiles.d lines of recent systemd versions + + This fixes #87. + +commit 3092e310acd376fc626cc051549e02bcd7697aed +Author: Mike Gilbert +Commit: William Hubbs + + tmpfiles: Accept filenames as command line arguments + + This brings us closer to being able to use tmpfiles.sh as a full + replacement for systemd-tmpfiles. + + This closes #83. + +commit 671911762d1bcd90c10d8ac0eb30fe10be4a65f6 +Author: Mike Gilbert +Commit: William Hubbs + + tmpfiles: Process command line before gathering config files + + This is part of #83. + +commit 7d68839e9ea89b0a92aef69a9b4fd298554bb9b1 +Author: Mike Gilbert +Commit: William Hubbs + + tmpfiles: Make unrecognized options fatal + + This is part of #83. + +commit 5341a925c15934674031aebb97533b0adcd10236 +Author: Jakub Jirutka +Commit: William Hubbs + + s6-guide: fix typo + + This fixes #92. + +commit 3adb8fb389caaafbed1be13c5ac4d96214c8eed3 +Author: Doug Freed +Commit: Doug Freed + + rc-logger: refuse to cat TMPLOG into itself + + This prevents an infinite loop in case somebody decides to set + rc_log_path to match TMPLOG. + +commit 8927a37fb790e718c956376242a532ab9d1755e7 +Author: William Hubbs +Commit: William Hubbs + + etc: remove rc.conf.* file fragments + +commit b085b2cda58bc884acb959e48f14fb044c983042 +Author: William Hubbs +Commit: William Hubbs + + etc: create default rc.conf + + Before now, /etc/rc.conf was created by the build system from multiple + rc.conf.* file fragments and there was no reason for this. + +commit daf93977641201f16c477b075ce9055a1da8f7b3 +Author: William Hubbs +Commit: William Hubbs + + init.d: combine sysctl scripts + + We had separate sysctl scripts for each operating system. However, there + is no need to do this since we can detect the operating system at + runtime with $RC_UNAME. + +commit 2984504c887afc9a36610eb7c20b097f7d1e70d0 +Author: William Hubbs +Commit: William Hubbs + + conf.d: remove staticroute file fragments + +commit 35e8386c24df6483f2918979dae150421f7151df +Author: William Hubbs +Commit: William Hubbs + + conf.d: makestaticroute file static + +commit 2108285d64e2ee8cc03fbe544efc3752fe349bdd +Author: William Hubbs +Commit: William Hubbs + + conf.d: remove network file fragnents + +commit a3133fec250eca3cdfb460c2ce26c707fd593c09 +Author: William Hubbs +Commit: William Hubbs + + conf.d: make network file static + +commit 314ae3dc781d7ae8fc26c276a85b0dc6ab6bc326 +Author: William Hubbs +Commit: William Hubbs + + modules: add support for FreeBSD + + This is based on a patch submitted by + Joe Maloney . + + This fixes #91. + +commit 695be59083cdf0d2ff9296f2c210e591c51bdf40 +Author: William Hubbs +Commit: William Hubbs + + rc-status: add -m/--manual option to show manually started services + + X-Gentoo-Bug: 585906 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=585906 + +commit c962678dd6ab1314b55c2a3bcdae03902bda39b8 +Author: Doug Freed +Commit: Doug Freed + + rc: Rename some static variables to kill warnings + +commit 3a1262703fd20d2e8288d13d908fb282c77d1793 +Author: William Hubbs +Commit: William Hubbs + + Remove the DEBUG_MEMORY macro + + This fixes #43. + +commit 20035210bdf5d5729734457f35f5f32a53a5b3ad +Author: William Hubbs +Commit: William Hubbs + + make variable aflag a boolean show_all diff --git a/FEATURE-REMOVAL-SCHEDULE.md b/FEATURE-REMOVAL-SCHEDULE.md new file mode 100644 index 0000000..7058805 --- /dev/null +++ b/FEATURE-REMOVAL-SCHEDULE.md @@ -0,0 +1,78 @@ +# 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 + +When: 1.0 + +Why: The same affect can be obtained with the --nodeps option to stop. + +Who: + +## start-stop-daemon options --startas, --chuid , --oknodo + +When: 1.0 + +Why: Obsolete or replaced by other options. + +* --startas => use --name or --exec +* --chuid => use --user +* --oknodo => ignore return code instead + +Who: + +## runscript and rc symbolic links + +When: 1.0 + +Why: Deprecated in favor of openrc-run and openrc due to naming + conflicts with other software. + +Who: + +## support for the opts variable in service scripts + +When: 1.0 + +Why: Deprecated in favor of extra_commands, extra_started_commands + and extra_stopped_commands. + +Who: + +## support for local_start and local_stop + +When: 1.0 + +Why: Deprecated in favor of executable scripts in @SYSCONFDIR@/local.d + +Who: + +## the mtab service script + +When: force /etc/mtab to link to /proc/self/mounts in 1.0, remove + service in 2.0 + +Why: /etc/mtab should be a symbolic link to /proc/self/mounts on modern + Linux systems + +Who: + +## 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() + +When: 1.0 + +Why: The getline() function was standardized in POSIX.1-2008, so it + should be available on POSIX systems. + +Who: + diff --git a/HISTORY.md b/HISTORY.md new file mode 100644 index 0000000..18de8a8 --- /dev/null +++ b/HISTORY.md @@ -0,0 +1,62 @@ +# OpenRC History + +This history of OpenRC was written by Daniel Robbins, Roy Marples, William +Hubbs and others. + +The Gentoo modular init scripts were developed by Daniel Robbins for Gentoo +Linux 1.0_rc6 during most of 2001 and released in September 2001. After their +development, the dependency-based init script system was maintained by a +number of senior developers, starting with Azarah (Martin Schlemmer), with +migration to the new init system assisted by Woodchip (Donnie Davies) who +converted all ebuild init scripts to work with the new system. As Grant +Goodyear notes: + +"My recollection is that one of woodchip's more impressive early feats +was the complete replacement of all of the init scripts in Portage +for Gentoo Linux 1.0_rc6. Through 1.0_rc5 Gentoo had used fairly +standard rc scripts modified from Stampede Linux, but for 1.0_rc6 Daniel +Robbins (drobbins) and Martin Schlemmer (azarah) had created a new +dependency-based init script system that is still used today. Within a +span of days Donny rewrote every single init script in the Portage tree +and committed new masked packages to await the release of 1.0_rc6. Thanks to +woodchip (and drobbins and azarah, of course) the +transition to the new init scripts was nearly painless." [1] + +Roy Marples became a Gentoo/Linux developer in 2004 and wrote the modular +network scripts for the Gentoo baselayout package. Towards the end of 2005, +he became the primary maintainer for baselayout and the init scripts. + +At the start of 2007, He announced the ongoing development of +baselayout-2, containing a rewritten core coded in C and allowing POSIX sh +init scripts instead of forcing the use of bash. By mid 2007, He had +re-implemented the Gentoo init script design created by Daniel Robbins, +using an entirely new code base. Alpha and pre-release baselayout-2 +snapshots were added to Gentoo's Portage tree as an optional component. + +Toward the end of 2007, Roy retired as a Gentoo developer. +Baselayout-2 was still in the pre stage, and aside from the gentoo-fbsd +users, it was masked. However, He desired to keep the baselayout-2 +project moving forward as an independent project. The Gentoo Council +permitted Him to release OpenRC under the 2-clause BSD license, +managed by him as an external project. + +Around mid-2010, Roy decided to no longer maintain OpenRC. At this +point, he transferred development back to Gentoo. + +William Hubbs, and several other Gentoo developers, started working on +OpenRC around this point and brought OpenRC-0.8.x to Gentoo Linux's stable +tree in 2011. + +In 2013 the OpenRC team became independent from Gentoo again and moved primary +development to github. + +Daniel Robbins continues to maintain an independent, forked +version of OpenRC for Funtoo Linux, which includes a Funtoo-specific network +configuration system. + +On 17-Dec-2015 utc, Roy gave the OpenRC developers permission to replace his +copyrights in all source files with a generic Copyright assertion for +the OpenRC developers as long as we keep the original copyright in the +binaries and LICENSE file. + +[1] http://www.gentoo.org/news/en/gwn/20040426-newsletter.xml diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..451affc --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +Copyright (c) 2007-2008, Roy Marples +Copyright (c) 2007-2015, the OpenRC authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9f2f09d --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +# Copyright (c) 2007-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. + +TOP:= ${dir ${realpath ${firstword ${MAKEFILE_LIST}}}} +MK= ${TOP}/mk + +include ${TOP}/Makefile.inc + +SUBDIR= conf.d etc init.d man scripts sh src support sysctl.d + +# Build pkgconfig or not +MKPKGCONFIG?= yes +ifeq (${MKPKGCONFIG},yes) +SUBDIR+= pkgconfig +endif + +# We need to ensure that runlevels is done last +SUBDIR+= runlevels + +INSTALLAFTER= _installafter + +include ${MK}/sys.mk +include ${MK}/os.mk +include ${MK}/subdir.mk +include ${MK}/dist.mk +include ${MK}/gitver.mk + +_installafter: +ifeq (${MKPREFIX},yes) + ${INSTALL} -d ${DESTDIR}/${LIBEXECDIR}/init.d +else ifneq (${OS},Linux) + ${INSTALL} -d ${DESTDIR}/${LIBEXECDIR}/init.d +endif + ${INSTALL} -d ${DESTDIR}/${LIBEXECDIR}/tmp + ${ECHO} "${VERSION}${GITVER}" > ${DESTDIR}/${LIBEXECDIR}/version diff --git a/Makefile.inc b/Makefile.inc new file mode 100644 index 0000000..9be0d45 --- /dev/null +++ b/Makefile.inc @@ -0,0 +1,3 @@ +NAME= openrc +VERSION= 0.28 +PKG= ${NAME}-${VERSION} diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..053a3cd --- /dev/null +++ b/NEWS.md @@ -0,0 +1,200 @@ +# 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.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 +Linux which can be used in place of sysvinit or any other init process. +For information on its usage, see the man pages for openrc-init (8) and +openrc-shutdown (8). + +## OpenRC-0.24.1 + +This version starts cleaning up the dependencies so that rc_parallel +will work correctly. + +The first step in this process is to remove the 'before *' from the +depend functions in the clock services. This means some services not +controlled by OpenRC may now start before instead of after the clock +service. If it is important for these services to start after the clock +service, they need to have 'after clock' added to their depend +functions. + +## OpenRC-0.24 + +Since the deptree2dot tool and the perl requirement are completely +optional, the deptree2dot tool has been moved to the support directory. +As a result, the MKTOOLS=yes/no switch has been removed from the makefiles. + +This version adds the agetty service which can be used to spawn +agetty on a specific terminal. This is currently documented in the +agetty-guide.md file at the top level of this distribution. + +## OpenRC-0.23 + +The tmpfiles.d processing code, which was part of previous versions of +OpenRC, has been separated into its own package [1]. If you need to +process systemd style tmpfiles.d files, please install this package. + +[1] https://github.com/openrc/opentmpfiles + +## OpenRC-0.22 + +In previous versions of OpenRC, configuration information was processed +so that service-specific configuration stored in /etc/conf.d/* was +overridden by global configuration stored in /etc/rc.conf. This release +reverses that. Global configuration is now overridden by +service-specific configuration. + +The swapfiles service, which was basically a copy of the swap service, +has been removed. If you are only using swap partitions, this change +will not affect you. If you are using swap files, please adjust the +dependencies of the swap service as shown in /etc/conf.d/swap. + +## OpenRC-0.21 + +This version adds a daemon supervisor which can start daemons and +restart them if they crash. See supervise-daemon-guide.md in the +distribution for details on its use. + +It is now possible to mark certain mount points as critical. If these +mount points are unable to be mounted, localmount or netmount will fail. +This is handled in /etc/conf.d/localmount and /etc/conf.d/netmount. See +these files for the setup. + +The deprecation messages in 0.13.x for runscript and rc are now +made visible in preparation for the removal of these binaries in 1.0. + +The steps you should take to get rid of these warnings is to run openrc +in initialization steps instead of rc and change the shebang lines in +service scripts to refer to "openrc-run" instead of "runscript". + +In 0.21.4, a modules-load service was added. This works like the +equivalent service in systemd. It looks for files named *.conf first in +/usr/lib/modules-load.d, then /run/modules-load.d, then +/etc/modules-load.d. These files contain a list of modules, one per +line, which should be loaded into the kernel. If a file name appears in +/run/modules-load.d, it overrides a file of the same name in +/usr/lib/modules-load.d. A file appearing in /etc/modules-load.d +overrides a file of the same name in both previous directories. + +## OpenRC-0.19 + +This version adds a net-online service. By default, this +service will check all known network interfaces for a configured +interface or a carrier. It will register as started only when all +interfaces are configured and there is at least a carrier on one +interface. The behaviour of this service can be modified in +/etc/conf.d/net-online. + +Currently, this only works on Linux, but if anyone wants to port to +*bsd, that would be welcomed. + +## OpenRC-0.18.3 + +Modern Linux systems expect /etc/mtab to be a symbolic link to +/proc/self/mounts. Reasons for this change include support for mount +namespaces, which will not work if /etc/mtab is a file. +By default, the mtab service enforces this on each reboot. + +If you find that this breaks your system in some way, please do the +following: + +- Set mtab_is_file=yes in /etc/conf.d/mtab. + +- Restart mtab. This will recreate the /etc/mtab file. + +- Check for an issue on https://github.com/openrc/openrc/issues + explaining why you need /etc/mtab to be a file. If there isn't one, + please open one and explain in detail why you need this to be a file. + If there is one, please add your comments to it. Please give concrete + examples of why it is important that /etc/mtab be a file instead of a + symbolic link. Those comments will be taken into consideration for how + long to keep supporting mtab as a file or when the support can be + removed. + +## OpenRC-0.18 + +The behaviour of localmount and netmount in this version is changing. In +the past, these services always started successfully. In this version, +they will be able to fail if file systems they mount fail to mount. If +you have file systems listed in fstab which should not be mounted at +boot time, make sure to add noauto to the mount options. If you have +file systems that you want to attempt to mount at boot time but failure +should be allowed, add nofail to the mount options for these file +systems in fstab. + +## OpenRC-0.14 + +The binfmt service, which registers misc binary formats with the Linux +kernel, has been separated from the procfs service. This service will be +automatically added to the boot runlevel for new Linux installs. When +you upgrade, you will need to use rc-update to add it to your boot +runlevel. + +The procfs service no longer automounts the deprecated usbfs and +usbdevfs file systems. Nothing should be using usbdevfs any longer, and +if you still need usbfs it can be added to fstab. + +Related to the above change, the procfs service no longer attempts to +modprobe the usbcore module. If your device manager does not load it, +you will need to configure the modules service to do so. + +The override order of binfmt.d and tmpfiles.d directories has been +changed to match systemd. Files in /run/binfmt.d and /run/tmpfiles.d +override their /usr/lib counterparts, and files in the /etc counterparts +override both /usr/lib and /run. + +## OpenRC-0.13.2 + +A chroot variable has been added to the service script variables. +This fixes the support for running a service in a chroot. +This is documented in man 8 openrc-run. + +The netmount service now mounts nfs file systems. +This change was made to correct a fix for an earlier bug. + +## OpenRC-0.13 + +/sbin/rc was renamed to /sbin/openrc and /sbin/runscript was renamed to +/sbin/openrc-run due to naming conflicts with other software. + +Backward compatible symbolic links are currently in place so your +system will keep working if you are using the old names; however, it is +strongly advised that you migrate to the new names because the symbolic +links will be removed in the future. +Warnings have been added to assist with this migration; however, due to the +level of noise they produce, they only appear in verbose mode in this release. + +The devfs script now handles the initial mounting and setup of the +/dev directory. If /dev has already been mounted by the kernel or an +initramfs, devfs will remount /dev with the correct mount options +instead of mounting a second /dev over the existing mount point. + +It attempts to mount /dev from fstab first if an entry exists there. If +it doesn't it attempts to mount devtmpfs if it is configured in the +kernel. If not, it attempts to mount tmpfs. +If none of these is available, an error message is displayed and static +/dev is assumed. + +## OpenRC-0.12 + +The net.* scripts, originally from Gentoo Linux, have +been removed. If you need these scripts, look for a package called +netifrc, which is maintained by them. diff --git a/README.md b/README.md new file mode 100644 index 0000000..54f8f8f --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +# OpenRC README + +OpenRC is a dependency-based init system that works with the +system-provided init program, normally `/sbin/init`. Currently, it does +not have an init program of its own. + +## Installation + +OpenRC requires GNU make. + +Once you have GNU Make installed, the default OpenRC installation can be +executed using this command: + +make install + +## Configuration + +You may wish to configure the installation by passing one or more of the +below arguments to the make command + +``` +PROGLDFLAGS=-static +LIBNAME=lib64 +DESTDIR=/tmp/openrc-image +MKNET=no +MKPAM=pam +MKPREFIX=yes +MKPKGCONFIG=no +MKSELINUX=yes +MKSTATICLIBS=no +MKSYSVINIT=yes +MKTERMCAP=ncurses +MKTERMCAP=termcap +PKG_PREFIX=/usr/pkg +LOCAL_PREFIX=/usr/local +PREFIX=/usr/local +BRANDING=\"Gentoo/$(uname -s)\" +``` + +## Notes + +We don't support building a static OpenRC with PAM. + +You may need to use `PROGLDFLAGS=-Wl,-Bstatic` on glibc instead of just `-static`. + +If you are building OpenRC for a Gentoo Prefix installation, add `MKPREFIX=yes`. + +`PKG_PREFIX` should be set to where packages install to by default. + +`LOCAL_PREFIX` should be set when to where user maintained packages are. +Only set `LOCAL_PREFIX` if different from `PKG_PREFIX`. + +`PREFIX` should be set when OpenRC is not installed to /. + +If any of the following files exist then we do not overwrite them + +``` +/etc/devd.conf +/etc/rc +/etc/rc.shutdown +/etc/conf.d/* +``` + +`rc` and `rc.shutdown` are the hooks from the BSD init into OpenRC. + +`devd.conf` is modified from FreeBSD to call `/etc/rc.devd` which is a +generic hook into OpenRC. + +`inittab` is the same, but for SysVInit as used by most Linux distributions. +This can be found in the support folder. + +Obviously, if you're installing this onto a system that does not use +OpenRC by default then you may wish to backup the above listed files, +remove them and then install so that the OS hooks into OpenRC. + +## Reporting Bugs + +If you are using Gentoo Linux, bugs can be filed on their bugzilla under +the `gentoo hosted projects` product and the `openrc` component [1]. +Otherwise, you can report issues on our github [2]. + +Better yet, if you can contribute code, please feel free to submit pull +requests [3]. + +## IRC Channel + +We have an official irc channel, #openrc on freenode, feel free to join +us there. + +[1] https://bugs.gentoo.org/ +[2] https://github.com/openrc/openrc/issues +[3] https://github.com/openrc/openrc/pulls diff --git a/README.newnet b/README.newnet new file mode 100644 index 0000000..41db1c5 --- /dev/null +++ b/README.newnet @@ -0,0 +1,36 @@ +OpenRC Network Ideals +--------------------- + +The new style networking for OpenRC is very simplistic - provide a basic means +of configuring static interface address and routes whilst allowing the +possibility to run any command at any point. + +In a nutshell, init.d/network is a wrapper around ifconfig(8) and +init.d/staticroute is wrapper around route(8). + +In the Perfect World (TM) ifconfig should be able to configure everything +about the interface easily * . The BSD family almost get this right and Linux +epically fails. + +* Only static configuration, including link setup. +For dynamic, static, IPv4LL, arping and per ssid IPv4 setup dhcpcd-5.x +provides your needs. + +It fails because there are many tools to do the same job and often have +vastly different syntax where they could be similar. In other words, there +is no coherence. + +OpenRC-0.4.x and older (inc Gentoo baselayout-1) had a collection of scripts +for each tool and allowed a script per interface. Over the years, this design +has proven very hard to maintain as each user has their own idea of how +things should work. Also, there were (and still are) race conditions. + +So where do we go from here? +Well, it's possible to use the new network scripts using the tools +currently available. It's just harder as you have to know them and their +documentation can be lacking at times. +The correct end goal is a BSD style ifconfig tool. +I've started work on it, but the project has stalled somewhat. +It's display only right now and the source is not yet publicly available. +If you have the skills and share the vision then contact me privately and +we'll take it from there. diff --git a/STYLE-GUIDE.md b/STYLE-GUIDE.md new file mode 100644 index 0000000..6c1fcf2 --- /dev/null +++ b/STYLE-GUIDE.md @@ -0,0 +1,84 @@ +# OpenRC 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 +or fixes you might notice. + +## C CODE + +The BSD Kernel Normal Form (KNF) style is used [1]. Basically, it is like +K&R/LKML, but wrapped lines that are indented use 4 spaces. Here are the +highlights. + +- no trailing whitespace +- indented code use tabs (not line wrapped) +- cuddle the braces (except for functions) +- space after native statements and before paren (for/if/while/...) +- no space between function and paren +- pointer asterisk cuddles the variable, not the type + +``` +void foo(int c) +{ + int ret = 0; + + if (c > 1000) + return; + + while (c--) { + bar(c); + ret++; + } + + return ret; +} +``` + +## COMMIT MESSAGES + +The following is an example of a correctly formatted git commit message +for this repository. Most of this information came from this blog post +[2], so I would like to thank the author. + +``` +Capitalized, short (50 chars or less) summary + +More detailed explanatory text, if necessary. Wrap it to about 72 +characters or so. In some contexts, the first line is treated as the +subject of an email and the rest of the text as the body. The blank +line separating the summary from the body is critical (unless you omit +the body entirely); tools like rebase can get confused if you run the +two together. + +Write your commit message in the imperative: "Fix bug" and not "Fixed +bug." This convention matches up with commit messages generated by +commands like git merge and git revert. + +Further paragraphs come after blank lines. + +- Bullet points are okay, too + +- Typically a hyphen or asterisk is used for the bullet, preceded by a + single space, with blank lines in between, but conventions vary here + +- Use a hanging indent + +Reported-by: User Name +X-[Distro]-Bug: BugID +X-[Distro]-Bug-URL: URL for the bug (on the distribution's web site typically) +``` + +If you did not write the code and the patch does not include authorship +information in a format git can use, please use the --author option of the +git commit command to make the authorship correct. + +The Reported-by tag is required if the person who reported the bug is +different from the author and committer. + + The X-[Distro]-Bug/Bug-URL tags are required if this commit is related + to a bug reported to us by a specific distribution of linux or a + *BSD. Also, [Distro] should be replaced with the name of the + distribution, e.g. X-Gentoo-Bug. + +[1] http://en.wikipedia.org/wiki/Indent_style#BSD_KNF_style +[2] http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html diff --git a/TODO b/TODO new file mode 100644 index 0000000..ccc074b --- /dev/null +++ b/TODO @@ -0,0 +1,21 @@ +- ensure all forks block, restore and unblock signals. needs review + +- add support somehow for optional translations + +- oldnet[bridging]: Review setting of bridge configuration on dynamic interface add + +- Document rc-depend binary. + +- _ifindex is not a reliable means of calculating metrics: + _ifindex is used for calculating metrics for new devices but has a major + problem: Since it's only the nth entry in /proc/net/dev + And devices may be removed from that file, and reordered, you won't always + get the same result. + If you do: + - add eth0 - _ifindex (eth0=0) + - add vlan1 - _ifindex (eth0=0,vlan1=1) + - add vlan2 - _ifindex (eth0=0,vlan1=1,vlan2=2) + - rem vlan1 - _ifindex (eth0=0,vlan2=1) + - add vlan3 - _ifindex (eth0=0,vlan2=1,vlan3=2) + Now your routing table has entries for both vlan2 and vlan3 with a metric of 2. + diff --git a/agetty-guide.md b/agetty-guide.md new file mode 100644 index 0000000..919a0bb --- /dev/null +++ b/agetty-guide.md @@ -0,0 +1,18 @@ +# Setting up the agetty service in OpenRC + +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 +spawning a getty manager for this port some other way (such as through +sysvinit/inittab), then run the following commands as root. + +Note that [port] refers to the port you are spawning the getty on, for +example, tty1 or ttyS0. The full path to it, for example, /dev/tty1 +should not be used. + +``` +# cd /etc/init.d +# ln -s agetty agetty.[port] +# cd /etc/conf.d +# cp agetty agetty.[port] +#rc-update add agetty.[port] [runlevel] +``` diff --git a/conf.d/Makefile b/conf.d/Makefile new file mode 100644 index 0000000..62ffd9f --- /dev/null +++ b/conf.d/Makefile @@ -0,0 +1,20 @@ +include ../mk/net.mk + +DIR= ${CONFDIR} +CONF= bootmisc fsck hostname localmount netmount swap urandom ${CONF-${OS}} + +ifeq (${MKNET},yes) +CONF+= network staticroute +endif + +MK= ../mk +include ${MK}/os.mk + +CONF-FreeBSD= ipfw modules moused powerd rarpd savecore syscons + +CONF-Linux= agetty consolefont devfs dmesg hwclock keymaps killprocs modules \ + net-online + +CONF-NetBSD= moused rarpd savecore + +include ${MK}/scripts.mk diff --git a/conf.d/adjkerntz b/conf.d/adjkerntz new file mode 100644 index 0000000..b7291cd --- /dev/null +++ b/conf.d/adjkerntz @@ -0,0 +1,10 @@ +# Set CLOCK to "UTC" if your system clock is set to UTC (also known as +# Greenwich Mean Time). If your clock is set to the local time, then +# set CLOCK to "local". Note that if you dual boot with Windows, then +# you should set it to "local". +clock="UTC" + +# If you want to set the Hardware Clock to the current System Time +# during shutdown, then say "YES" here. +# You normally don't need to do this if you run a ntp daemon. +clock_systohc="NO" diff --git a/conf.d/agetty b/conf.d/agetty new file mode 100644 index 0000000..5cfb581 --- /dev/null +++ b/conf.d/agetty @@ -0,0 +1,11 @@ +# Set the baud rate of the terminal line +#baud="" + +# set the terminal type +#term_type="linux" + +# extra options to pass to agetty for this port +#agetty_options="" + +# make agetty quiet +#quiet="no" diff --git a/conf.d/bootmisc b/conf.d/bootmisc new file mode 100644 index 0000000..dd5b08e --- /dev/null +++ b/conf.d/bootmisc @@ -0,0 +1,15 @@ +# List of /tmp directories we should clean up +clean_tmp_dirs="/tmp" + +# Should we wipe the tmp paths completely or just selectively remove known +# locks / files / etc... ? +wipe_tmp="YES" + +# Write the initial dmesg log into /var/log/dmesg after boot +# This may be useful if you need the kernel boot log afterwards +log_dmesg="YES" + +# Save the previous dmesg log to dmesg.old +# This may be useful if you need to compare the current boot to the +# previous one. +#previous_dmesg=no diff --git a/conf.d/consolefont b/conf.d/consolefont new file mode 100644 index 0000000..46b30ed --- /dev/null +++ b/conf.d/consolefont @@ -0,0 +1,18 @@ +# The consolefont service is not activated by default. If you need to +# use it, you should run "rc-update add consolefont boot" as root. +# +# consolefont specifies the default font that you'd like GNU/Linux to use on the +# console. You can find a good selection of fonts in /usr/share/consolefonts; +# you shouldn't specify the trailing ".psf.gz", just the font name below. +# To use the default console font, comment out the CONSOLEFONT setting below. +consolefont="default8x16" + +# consoletranslation is the charset map file to use. Leave commented to use +# the default one. Have a look in /usr/share/consoletrans for a selection of +# map files you can use. +#consoletranslation="8859-1_to_uni" + +# unicodemap is the unicode map file to use. Leave commented to use the +# default one. Have a look in /usr/share/unimaps for a selection of map files +# you can use. +#unicodemap="iso01" diff --git a/conf.d/devfs b/conf.d/devfs new file mode 100644 index 0000000..51f8037 --- /dev/null +++ b/conf.d/devfs @@ -0,0 +1,8 @@ +# OpenRC will attempt each of the following in succession to mount /dev. +# +# 1. If there is an entry for /dev in fstab, it will be used. +# 2. If devtmpfs is defined in the kernel, it will be used. +# 3. If tmpfs is defined in the kernel, it will be used. +# +# Set this to yes if you do not want OpenRC to attempt to mount /dev. +# skip_mount_dev="NO" diff --git a/conf.d/dmesg b/conf.d/dmesg new file mode 100644 index 0000000..cd4b8b3 --- /dev/null +++ b/conf.d/dmesg @@ -0,0 +1,3 @@ +# Sets the level at which logging of messages is done to the +# console. See dmesg(1) for more info. +dmesg_level="1" diff --git a/conf.d/fsck b/conf.d/fsck new file mode 100644 index 0000000..d66387d --- /dev/null +++ b/conf.d/fsck @@ -0,0 +1,40 @@ +# Pass any arguments to fsck. +# By default we preen. +# GNU/Linux systems also force -C0 and -T. +# If fsck_args is not specified then GNU/Linux systems also use -A +# (and -R if / is rw) +#fsck_args="-p" + +# We can also specify the passno in /etc/fstab to check +# If you multiplex fsck (ie ln -s fsck /etc/init.d/fsck.late) then you can +# do an fsck outside of the normal scope, say for /home. +# Here are some exampes:- +#fsck_passno="=1 =2" +#fsck_passno=">1" +#fsck_passno="<2" + +# If passno is not enough granularity, you can also specify mountpoints to +# check. This should NOT be used for the default non-multiplexed fsck, or your +# system might not be checked. Additionally, it is mutually exclusive with +# the fsck_passno setting. +#fsck_mnt="" +#fsck_mnt="/home" + +# Most modern fs's don't require a full fsck on boot, but for those that do +# it may be advisable to skip this when running on battery. +# WARNING: Do not turn this off if you have any JFS partitions. +fsck_on_battery="YES" + +# fsck_shutdown causes fsck to trigger during shutdown as well as startup. +# The end result of this is that if any periodic non-root filesystem checks are +# scheduled, under normal circumstances the actual check will happen during +# shutdown rather than at next boot. +# This is useful when periodic filesystem checks are causing undesirable +# delays at startup, but such delays at shutdown are acceptable. +fsck_shutdown="NO" + +# fsck_abort_on_errors can be set to no to cause fsck to not abort on +# errors. +# This is useful when periodic filesystem checks are causing undesirable +# aborts. +fsck_abort_on_errors="YES" diff --git a/conf.d/hostname b/conf.d/hostname new file mode 100644 index 0000000..ebdc8f1 --- /dev/null +++ b/conf.d/hostname @@ -0,0 +1,2 @@ +# Set to the hostname of this machine +hostname="localhost" diff --git a/conf.d/hwclock b/conf.d/hwclock new file mode 100644 index 0000000..ce9b40a --- /dev/null +++ b/conf.d/hwclock @@ -0,0 +1,20 @@ +# Set CLOCK to "UTC" if your Hardware Clock is set to UTC (also known as +# Greenwich Mean Time). If that clock is set to the local time, then +# set CLOCK to "local". Note that if you dual boot with Windows, then +# you should set it to "local". +clock="UTC" + +# If you want the hwclock script to set the system time (software clock) +# to match the current hardware clock during bootup, leave this +# commented out. +# However, you can set this to "NO" if you are running a modern kernel +# and using NTP to synchronize your system clock. +#clock_hctosys="YES" + +# If you do not want to set the hardware clock to the current system +# time (software clock) during shutdown, set this to no. +#clock_systohc="YES" + +# If you wish to pass any other arguments to hwclock during bootup, +# you may do so here. Alpha users may wish to use --arc or --srm here. +clock_args="" diff --git a/conf.d/ipfw b/conf.d/ipfw new file mode 100644 index 0000000..b8c0005 --- /dev/null +++ b/conf.d/ipfw @@ -0,0 +1,14 @@ +# ipfw provides a stateful firewall. +# This means we allow everything out, and if we have a connection we allow it +# back in. This is very flexable and quite secure. + +# For ease of use, we allow auth and ssh ports through as well. +# To override the list of allowed ports +#ipfw_ports_in="auth ssh" + +# You may want to enable logging of denied connections +#ipfw_log_deny="YES" + +# This ports not logged +#ipfw_ports_nolog="135-139,445 1026,1027 1433,1434" + diff --git a/conf.d/keymaps b/conf.d/keymaps new file mode 100644 index 0000000..4ef6900 --- /dev/null +++ b/conf.d/keymaps @@ -0,0 +1,23 @@ +# Use keymap to specify the default console keymap. There is a complete tree +# of keymaps in /usr/share/kbd/keymaps to choose from. +keymap="us" + +# Should we first load the 'windowkeys' console keymap? Most x86 users will +# say "yes" here. Note that non-x86 users should leave it as "no". +# Loading this keymap will enable VT switching (like ALT+Left/Right) +# using the special windows keys on the linux console. +windowkeys="NO" + +# The maps to load for extended keyboards. Most users will leave this as is. +extended_keymaps="" +#extended_keymaps="backspace keypad euro2" + +# Tell dumpkeys(1) to interpret character action codes to be +# from the specified character set. +# This only matters if you set unicode="yes" in /etc/rc.conf. +# For a list of valid sets, run `dumpkeys --help` +dumpkeys_charset="" + +# Some fonts map AltGr-E to the currency symbol instead of the Euro. +# To fix this, set to "yes" +fix_euro="NO" diff --git a/conf.d/killprocs b/conf.d/killprocs new file mode 100644 index 0000000..76a2bc9 --- /dev/null +++ b/conf.d/killprocs @@ -0,0 +1,6 @@ +# 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="" diff --git a/conf.d/localmount b/conf.d/localmount new file mode 100644 index 0000000..e727719 --- /dev/null +++ b/conf.d/localmount @@ -0,0 +1,10 @@ +# Stop the unmounting of certain points. +# This could be useful for some NFS related work. +#no_umounts="/dir1:/var/dir2" +# +# Mark certain mount points as critical. +# This contains aspace 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. +#critical_mounts="/home /var" diff --git a/conf.d/modules b/conf.d/modules new file mode 100644 index 0000000..c35b9ed --- /dev/null +++ b/conf.d/modules @@ -0,0 +1,27 @@ +# Linux users can define a list of modules for a specific kernel version, +# a released kernel version, a main kernel version or all kernel versions. +# The most specific versioned variable will take precedence. +# FreeBSD users can only use the modules="foo bar" setting. +#modules_2_6_23_gentoo_r5="ieee1394 ohci1394" +#modules_2_6_23="tun ieee1394" +#modules_2_6="tun" +#modules_2="ipv6" +#modules="ohci1394" + +# Linux users can give modules a different name when they load - the new name +# will also be used to pick arguments below. +# This is not supported on FreeBSD. +#modules="dummy:dummy1" + +# Linux users can give the modules some arguments if needed, per version +# if necessary. +# Again, the most specific versioned variable will take precedence. +# This is not supported on FreeBSD. +#module_ieee1394_args="debug" +#module_ieee1394_args_2_6_23_gentoo_r5="debug2" +#module_ieee1394_args_2_6_23="debug3" +#module_ieee1394_args_2_6="debug4" +#module_ieee1394_args_2="debug5" + +# You should consult your kernel documentation and configuration +# for a list of modules and their options. diff --git a/conf.d/moused b/conf.d/moused new file mode 100644 index 0000000..2d9bf77 --- /dev/null +++ b/conf.d/moused @@ -0,0 +1,16 @@ +# See the moused man page for available settings. + +# Set to your mouse device psm[0-9] for PS/2 ports, ums[0-9] for USB ports +# Leave blank to try to autodetect it +#moused_device="/dev/psm0" + +# Any additional arguments required for a specific port +#moused_args_psm0="" +# or for all mice +#moused_args="" + +# You can also multiplex the init script for each device like so +# ln -s moused /etc/init.d/moused.ums0 +# This enables you to have a config file per mouse (forces moused_device +# to ums0 in this case) and control each mouse. +# devd can also start and stop these mice, which laptop users will find handy. diff --git a/conf.d/mtab b/conf.d/mtab new file mode 100644 index 0000000..9e16d9b --- /dev/null +++ b/conf.d/mtab @@ -0,0 +1,5 @@ +# 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 diff --git a/conf.d/net-online b/conf.d/net-online new file mode 100644 index 0000000..bacf8ef --- /dev/null +++ b/conf.d/net-online @@ -0,0 +1,18 @@ +# The interfaces setting controls which interfaces the net-online +# service considers in deciding whether the network is active. The +# default is all interfaces that support ethernet. +#interfaces="" + +# This setting controls whether a ping test is included in the test for +# network connectivity after all interfaces are active. +#include_ping_test=no + +# This setting is the host to attempt to ping if the above is yes. +# The default is hyperbola.info. +#ping_test_host=some.host.name + +# The timeout setting controls how long the net-online service waits +# for the network to be configured. +# The default is 120 seconds. +# if this is set to 0, the wait is infinite. +#timeout=120 diff --git a/conf.d/netmount b/conf.d/netmount new file mode 100644 index 0000000..e759adf --- /dev/null +++ b/conf.d/netmount @@ -0,0 +1,47 @@ +# You will need to set the dependencies in the netmount script to match +# the network configuration tools you are using. This should be done in +# this file by following the examples below, and not by changing the +# service script itself. +# +# Each of these examples is meant to be used separately. So, for +# example, do not set rc_need to something like "net.eth0 dhcpcd". +# +# If you are using newnet and configuring your interfaces with static +# addresses with the network script, you should use this setting. +# +#rc_need="network" +# +# If you are using oldnet, you must list the specific net.* services you +# need. +# +# This example assumes all of your netmounts can be reached on +# eth0. +# +#rc_need="net.eth0" +# +# This example assumes some of your netmounts are on eth1 and some +# are on eth2. +# +#rc_need="net.eth1 net.eth2" +# +# If you are using a dynamic network management tool like +# NetworkManager, dhcpcd in standalone mode, wicd, badvpn-ncd, etc, to +# manage the network interfaces with the routes to your netmounts, you +# should list that tool. +# +#rc_need="NetworkManager" +#rc_need="dhcpcd" +#rc_need="wicd" +# +# The default setting is designed to be backward compatible with our +# current setup, but you are highly discouraged from using this. In +# other words, please change it to be more suited to your system. +# +rc_need="net" +# +# Mark certain mount points as critical. +# This contains aspace separated list of mount points which should be +# considered critical. If one of these mount points cannot be mounted, +# netmount will fail. +# By default, this is empty. +#critical_mounts="/home /var" diff --git a/conf.d/network b/conf.d/network new file mode 100644 index 0000000..ff55261 --- /dev/null +++ b/conf.d/network @@ -0,0 +1,80 @@ +# Assign static IP addresses and run custom scripts per interface. +# Seperate commands with ; +# Prefix with ! to run a shell script. +# Use \$int to represent the interface +#ifconfig_eth0="192.168.0.10 netmask 255.255.255.0" + +# You also have ifup_eth0 and ifdown_eth0 to run other commands when +# eth0 is started and stopped. +# You should note that we don't stop the network at system shutdown by default. +# If you really need this, then set keep_network=NO + +# Lastly, the interfaces variable pulls in virtual interfaces that cannot +# be automatically detected. +#interfaces="br0 bond0 vlan0" + +# You can also use files instead of variables here if you like: +# /etc/ifconfig.eth0 is equivalent to ifconfig_eth0 +# /etc/ip.eth0 is equivalent to ifconfig_eth0 +# /etc/ifup.eth0 is equivalent to ifup_eth0 +# /etc/ifdown.eth0 is equivalent to ifdown_eth0 +# Any files found will automatically be put into the interfaces variable. +# You don't need to escape variables in files, so use $int instead of \$int. + +# If you require DHCP, you should install dhcpcd and add it to the boot or +# default runlevel. + +# NIS users can set the domain name here +#domainname="foobar" + +# You can add a default route. +# The way this is done is slightly different depending on the operating system. +# +# *BSD: +#defaultroute="192.168.0.1" +#defaultroute6="2001:a:b:c" +# GNU/* (ifconfig): +#defaultroute="gw 192.168.0.1" +#defaultroute6="gw 2001:a:b:c" + +# The remainder of this file applies to GNU/Linux only and shows how +# iproute2 is supported along with other examples. + +# ifconfig under GNU/Linux is not that powerful and doesn't easily handle +# multiple addresses +# On the other hand, iproute2 is quite powerful and is also supported +#ip_eth0="192.168.0.10/24; 192.168.10.10/24" + +# You can also use iproute2 to add the default route. +#defaultiproute="via 192.168.0.1" +#defaultiproute6="via 2001:a:b:c" + +# ip doesn't handle MTU like ifconfig, but we can do it like so +#ifup_eth0="ip link set \$int mtu 1500" + +# Create a bonded interface +#interfaces="bond0" +#ifup_bond0="modprobe bonding; ifconfig \$int up; ifenslave \$int bge0" +#ifconfig_bond0="192.168.0.10 netmask 255.255.255.0" +#ifdown_bond0="rmmod bonding" + +# Create tap interface and a bridge interface. +# We add the tap to the bridge. +# An external program, like dhcpcd, will configure the IP on the bridge +#interfaces="tun0 br0" +#ifup_tun0="tunctl -t \$int" +#ifdown_tun0="tunctl -d \$int" +#ifup_br0="brctl addbr \$int; brctl add \$int eth1; brtctl add \$int eth2" +#ifdown_br0="ifconfig \$int down; btctl delbr \$int" + +# Create VLAN +#interfaces="eth0_2 eth0_3 eth0_4" +#ifup_eth0="vconfig add \$int 2; vconfig add \$int 3; vconfig add \$int 4" +#ifconfig_eth0_2="192.168.2.10 netmask 255.255.255.0" +#ifconfig_eth0_3="192.168.3.10 netmask 255.255.255.0" +#ifconfig_eth0_4="192.168.4.10 netmask 255.255.255.0" +#ifdown_eth0="vconfig rem \$int.2; vconfig rem \$int.3; vconfig rem \$int.4" + +# Normally you would use wpa_supplicant to configure wireless, but you can +# use iwconfig also +#ifup_wlan0="iwconfig \$int key s:secretkey enc open essid foobar" diff --git a/conf.d/powerd b/conf.d/powerd new file mode 100644 index 0000000..df5fb70 --- /dev/null +++ b/conf.d/powerd @@ -0,0 +1,7 @@ +# Mode allowed: maximum, minimum, adaptive +# Default unless specified is adaptive +powerd_ac_mode="maximum" +#powerd_battery_mode="minimum" + +# Addiditonal arguments for powerd - see the man page for details +powerd_args="" diff --git a/conf.d/rarpd b/conf.d/rarpd new file mode 100644 index 0000000..60d71a4 --- /dev/null +++ b/conf.d/rarpd @@ -0,0 +1,3 @@ +# To start rarpd only for a given interface, set the +# following variable. Otherwise we listen on all interfaces. +#rarpd_interface="rl0" diff --git a/conf.d/savecore b/conf.d/savecore new file mode 100644 index 0000000..99eaefc --- /dev/null +++ b/conf.d/savecore @@ -0,0 +1,25 @@ +# Unless you're a kernel developer or driver writer then this won't +# be of any interest to you at all. +# The following options allow to configure the kernel's core dump +# facilities. + +# The dump_device variable is used to specify which device will be +# used by the kernel to write the dump down. This has to be a swap +# partition, and has to be at least big enough to contain the whole +# physical memory (see hw.physmem sysctl(8) variable). +# When the variable is commented out, no core dump will be enabled for +# the kernel. +#dump_device=/dev/ad0s1b + +# The dump_dir variable is used to tell savecore(8) utility where +# to save the kernel core dump once it's restored from the dump +# device. If unset, /var/crash will be used, as the default of +# FreeBSD. +#dump_dir=/var/crash + +# The dump_compress variable decide whether to compress with +# gzip(1) the dump or leave it of its original size (the size of the +# physical memory present on the system). If set to yes, the -z option +# will be passed to savecore(8) that will proceed on compressing the +# dump. +#dump_compress=NO diff --git a/conf.d/staticroute b/conf.d/staticroute new file mode 100644 index 0000000..2c50386 --- /dev/null +++ b/conf.d/staticroute @@ -0,0 +1,26 @@ +# Static routes are defined differently depending on your operating +# system, so please be sure to use the correct syntax. +# Do not use this file to define the default route. +# In all settings, multiple routes should be separated using ; or new lines. + +# Define static routes on GNU/Linux using route. See route(8) for syntax. +#staticroute="net 192.168.0.0 netmask 255.255.255.0 gw 10.73.1.1 +#net 192.168.1.0 netmask 255.255.255.0 gw 10.73.1.1" + +# Define static routes on GNU/Linux using iproute2. See ip(8) for syntax. +#staticiproute="192.168.0.0/24 via 10.73.1.1; 192.168.1.0/24 via 10.73.1.1" + +# Define static routes on GNU/Hurd. See route(8) for syntax. +# /etc/route.conf(5) takes precedence over this configuration. +# FIXME: "net ..." not supported +#staticroute="net 192.168.0.0 -netmask 255.255.255.0 --address 10.73.1.1 +#net 192.168.1.0 -netmask 255.255.255.0 --address 10.73.1.1" + +# Define static routes on GNU/KFreeBSD. See route(8) for syntax. +#staticroute="net 192.168.0.0 10.73.1.1 netmask 255.255.255.0 +#net 192.168.1.0 10.73.1.1 netmask 255.255.255.0" + +# Define static routes on other BSD systems. See route(8) for syntax. +# /etc/route.conf(5) takes precedence over this configuration. +#staticroute="net 192.168.0.0 -netmask 255.255.255.0 10.73.1.1 +#net 192.168.1.0 -netmask 255.255.255.0 10.73.1.1" diff --git a/conf.d/swap b/conf.d/swap new file mode 100644 index 0000000..17bd034 --- /dev/null +++ b/conf.d/swap @@ -0,0 +1,13 @@ +# If you are only using local swap partitions, you should not change +# this file. Otherwise, you need to uncomment the below rc_before line +# followed by the appropriate rc_need line. +#rc_before="!localmount" +# +# If you are using swap files stored on local file systems, uncomment +# this line. +#rc_need="localmount" +# +# If you are using swap files stored on network file systems or swap +# partitions stored on network block devices such as iSCSI, uncomment +# this line. +#rc_need="netmount" diff --git a/conf.d/syscons b/conf.d/syscons new file mode 100644 index 0000000..cd012df --- /dev/null +++ b/conf.d/syscons @@ -0,0 +1,19 @@ +# Example syscons config file. This is the place to set things like keymap, etc. + +# Set the video mode - you should check the vidcontrol man page for valid modes +# NOTE:- This will blank the screen after this command is run +# NOTE:- You can get more modes if you load the vesa kernel module, but this +# may require the SC_PIXEL_MODE kernel option +#allscreen_flags="VGA_80x30" + +# Set the keymap to "uk.iso". +#keymap="uk.iso" + +# Set the keyboard rate to 250ms delay, and 34 repeat rate. +#keyrate="250.34" + +# Change the behaviour of F-unction keys (see kbdcontrol(1)). +#keychange="10 'ssh myhost'" + +# See vidcontrol(1) -t +#blanktime="off" diff --git a/conf.d/urandom b/conf.d/urandom new file mode 100644 index 0000000..f721a24 --- /dev/null +++ b/conf.d/urandom @@ -0,0 +1,5 @@ +# Sometimes you want to have urandom start before "localmount" +# (say for crypt swap), so you will need to customize this +# behavior. If you have /var on a separate partition, then +# make sure this path lives on your root device somewhere. +urandom_seed="/var/lib/misc/random-seed" diff --git a/etc/.gitignore b/etc/.gitignore new file mode 100644 index 0000000..fe38bc1 --- /dev/null +++ b/etc/.gitignore @@ -0,0 +1,2 @@ +rc +rc.shutdown diff --git a/etc/Makefile b/etc/Makefile new file mode 100644 index 0000000..1695dee --- /dev/null +++ b/etc/Makefile @@ -0,0 +1,23 @@ +DIR= ${SYSCONFDIR} +SRCS= rc.in rc.shutdown.in +BIN= ${BIN-${OS}} +CONF= rc.conf ${BIN-${OS}} + +MK= ../mk +include ${MK}/os.mk + +SED_EXTRA-FreeBSD= -e 's:@TERM@:cons25:g' +BIN-FreeBSD= rc rc.shutdown rc.devd +CONF-FreeBSD= devd.conf + +SED_EXTRA-Linux= -e 's:@TERM@:wsvt25:g' +BIN-Linux= +CONF-Linux= + +SED_EXTRA-NetBSD= -e 's:@TERM@:wsvt25:g' +BIN-NetBSD= rc rc.shutdown +CONF-NetBSD= + +SED_EXTRA= ${SED_EXTRA-${OS}} + +include ${MK}/scripts.mk diff --git a/etc/devd.conf b/etc/devd.conf new file mode 100644 index 0000000..3cd262c --- /dev/null +++ b/etc/devd.conf @@ -0,0 +1,315 @@ +# $FreeBSD: src/etc/devd.conf,v 1.38 2007/06/21 22:50:36 njl Exp $ +# +# Refer to devd.conf(5) and devd(8) man pages for the details on how to +# run and configure devd. +# + +# NB: All regular expressions have an implicit ^$ around them. +# NB: device-name is shorthand for 'match device-name' + +options { + # Each directory directive adds a directory the list of directories + # that we scan for files. Files are read-in in the order that they + # are returned from readdir(3). The rule-sets are combined to + # create a DFA that's used to match events to actions. + directory "/etc/devd"; + directory "/usr/local/etc/devd"; + pid-file "/var/run/devd.pid"; + + # Setup some shorthand for regex that we use later in the file. + #XXX Yes, these are gross -- imp + set scsi-controller-regex + "(aac|adv|adw|aha|ahb|ahc|ahd|aic|amd|amr|asr|bt|ciss|ct|dpt|\ + esp|ida|iir|ips|isp|mlx|mly|mpt|ncr|ncv|nsp|stg|sym|trm|wds)\ + [0-9]+"; +}; + +# Note that the attach/detach with the highest value wins, so that one can +# override these general rules. + +# +# Configure the interface on attach. Due to a historical accident, this +# script is called pccard_ether. +# +notify 0 { + match "system" "IFNET"; + match "type" "ATTACH"; + action "/etc/rc.devd net.$subsystem start"; +}; + +notify 0 { + match "system" "IFNET"; + match "type" "DETACH"; + action "/etc/rc.devd net.$subsystem stop"; +}; + +# +# Try to configure the interface when the network comes up and deconfigure +# when it goes down +# +notify 0 { + match "system" "IFNET"; + match "type" "LINK_UP"; + media-type "ethernet"; + action "/etc/rc.devd net.$subsystem start"; +}; + +notify 0 { + match "system" "IFNET"; + match "type" "LINK_DOWN"; + media-type "ethernet"; + action "/etc/rc.devd net.$subsystem stop"; +}; +# +# Like Ethernet devices, but separate because +# they have a different media type. We may want +# to exploit this later. +# +detach 0 { + media-type "802.11"; + action "/etc/rc.devd net.$device-name stop"; +}; +attach 0 { + media-type "802.11"; + action "/etc/rc.devd net.$device-name start"; +}; +notify 0 { + match "system" "IFNET"; + match "type" "LINK_UP"; + media-type "802.11"; + action "/etc/rc.devd net.$subsystem start"; +}; +notify 0 { + match "system" "IFNET"; + match "type" "LINK_DOWN"; + media-type "802.11"; + action "/etc/rc.devd net.$subsystem stop"; +}; + +# An entry like this might be in a different file, but is included here +# as an example of how to override things. Normally 'ed50' would match +# the above attach/detach stuff, but the value of 100 makes it +# hard wired to 1.2.3.4. +attach 100 { + device-name "ed50"; + action "ifconfig $device-name inet 1.2.3.4 netmask 0xffff0000"; +}; +detach 100 { + device-name "ed50"; +}; + +# When a USB Bluetooth dongle appears activate it +attach 100 { + device-name "ubt[0-9]+"; + action "/etc/rc.d/bluetooth start $device-name"; +}; +detach 100 { + device-name "ubt[0-9]+"; + action "/etc/rc.d/bluetooth stop $device-name"; +}; + +# When a USB keyboard arrives, attach it as the console keyboard. +attach 100 { + device-name "ukbd0"; + action "/etc/rc.d/syscons setkeyboard /dev/ukbd0"; +}; +detach 100 { + device-name "ukbd0"; + action "/etc/rc.d/syscons setkeyboard /dev/kbd0"; +}; + +# The entry below starts moused when a mouse is plugged in. Moused +# stops automatically (actually it bombs :) when the device disappears. +attach 100 { + device-name "ums[0-9]+"; + action "/etc/rc.devd moused.$device-name start"; +}; + +# Firmware download into the ActiveWire board. After the firmware download is +# done the device detaches and reappears as something new and shiny +# automatically. +attach 100 { + match "vendor" "0x0854"; + match "product" "0x0100"; + match "release" "0x0000"; + action "/usr/local/bin/ezdownload -f /usr/local/share/usb/firmware/0854.0100.0_01.hex $device-name"; +}; + +# Firmware download for Entrega Serial DB25 adapter. +attach 100 { + match "vendor" "0x1645"; + match "product" "0x8001"; + match "release" "0x0101"; + action "if ! kldstat -n usio > /dev/null 2>&1 ; then kldload usio; fi /usr/sbin/ezdownload -v -f /usr/share/usb/firmware/1645.8001.0101 /dev/$device-name"; +}; + +# This entry starts the ColdSync tool in daemon mode. Make sure you have an up +# to date /usr/local/etc/palms. We override the 'listen' settings for port and +# type in /usr/local/etc/coldsync.conf. +attach 100 { + device-name "ugen[0-9]+"; + match "vendor" "0x082d"; + match "product" "0x0100"; + match "release" "0x0100"; + action "/usr/local/bin/coldsync -md -p /dev/$device-name -t usb"; +}; + +# +# Rescan scsi device-names on attach, but not detach. However, it is +# disabled by default due to reports of problems. +# +attach 0 { + device-name "$scsi-controller-regex"; +// action "camcontrol rescan all"; +}; + +# Don't even try to second guess what to do about drivers that don't +# match here. Instead, pass it off to syslog. Commented out for the +# moment, as the pnpinfo variable isn't set in devd yet. Individual +# variables within the bus supplied pnpinfo are set. +nomatch 0 { +# action "logger Unknown device: $pnpinfo $location $bus"; +}; + +# Various logging of unknown devices. +nomatch 10 { + match "bus" "uhub[0-9]+"; + action "logger Unknown USB device: vendor $vendor product $product \ + bus $bus"; +}; + +# Some PC-CARDs don't offer numerical manufacturer/product IDs, just +# show the CIS info there. +nomatch 20 { + match "bus" "pccard[0-9]+"; + match "manufacturer" "0xffffffff"; + match "product" "0xffffffff"; + action "logger Unknown PCCARD device: CISproduct $cisproduct \ + CIS-vendor $cisvendor bus $bus"; +}; + +nomatch 10 { + match "bus" "pccard[0-9]+"; + action "logger Unknown PCCARD device: manufacturer $manufacturer \ + product $product CISproduct $cisproduct CIS-vendor \ + $cisvendor bus $bus"; +}; + +nomatch 10 { + match "bus" "cardbus[0-9]+"; + action "logger Unknown Cardbus device: device $device class $class \ + vendor $vendor bus $bus"; +}; + +# Switch power profiles when the AC line state changes. +notify 10 { + match "system" "ACPI"; + match "subsystem" "ACAD"; + action "/etc/rc.d/power_profile $notify"; +}; + +# Notify all users before beginning emergency shutdown when we get +# a _CRT or _HOT thermal event and we're going to power down the system +# very soon. +notify 10 { + match "system" "ACPI"; + match "subsystem" "Thermal"; + match "notify" "0xcc"; + action "logger -p kern.emerg 'WARNING: system temperature too high, shutting down soon!'"; +}; + +# Sample ZFS problem reports handling. +notify 10 { + match "system" "ZFS"; + match "type" "zpool"; + action "logger -p kern.err 'ZFS: failed to load zpool $pool'"; +}; + +notify 10 { + match "system" "ZFS"; + match "type" "vdev"; + action "logger -p kern.err 'ZFS: vdev failure, zpool=$pool type=$type'"; +}; + +notify 10 { + match "system" "ZFS"; + match "type" "data"; + action "logger -p kern.warn 'ZFS: zpool I/O failure, zpool=$pool error=$zio_err'"; +}; + +notify 10 { + match "system" "ZFS"; + match "type" "io"; + action "logger -p kern.warn 'ZFS: vdev I/O failure, zpool=$pool path=$vdev_path offset=$zio_offset size=$zio_size error=$zio_err'"; +}; + +notify 10 { + match "system" "ZFS"; + match "type" "checksum"; + action "logger -p kern.warn 'ZFS: checksum mismatch, zpool=$pool path=$vdev_path offset=$zio_offset size=$zio_size'"; +}; + +# User requested suspend, so perform preparation steps and then execute +# the actual suspend process. +notify 10 { + match "system" "ACPI"; + match "subsystem" "Suspend"; + action "/etc/rc.suspend acpi $notify"; +}; +notify 10 { + match "system" "ACPI"; + match "subsystem" "Resume"; + action "/etc/rc.resume acpi $notify"; +}; + +/* EXAMPLES TO END OF FILE + +# The following might be an example of something that a vendor might +# install if you were to add their device. This might reside in +# /usr/local/etc/devd/deqna.conf. A deqna is, in this hypothetical +# example, a pccard ethernet-like device. Students of history may +# know other devices by this name, and will get the in-jokes in this +# entry. +nomatch 10 { + match "bus" "pccard[0-9]+"; + match "manufacturer" "0x1234"; + match "product" "0x2323"; + action "kldload if_deqna"; +}; +attach 10 { + device-name "deqna[0-9]+"; + action "/etc/pccard_ether $device-name start"; +}; +detach 10 { + device-name "deqna[0-9]+"; + action "/etc/pccard_ether $device-name stop"; +}; + +# Examples of notify hooks. A notify is a generic way for a kernel +# subsystem to send event notification to userland. +# +# Here are some examples of ACPI notify handlers. ACPI subsystems that +# generate notifies include the AC adapter, power/sleep buttons, +# control method batteries, lid switch, and thermal zones. +# +# Information returned is not always the same as the ACPI notify +# events. See the ACPI specification for more information about +# notifies. Here is the information returned for each subsystem: +# +# ACAD: AC line state (0 is offline, 1 is online) +# Button: Button pressed (0 for power, 1 for sleep) +# CMBAT: ACPI battery events +# Lid: Lid state (0 is closed, 1 is open) +# Suspend, Resume: Suspend and resume notification +# Thermal: ACPI thermal zone events +# +# This example calls a script when the AC state changes, passing the +# notify value as the first argument. If the state is 0x00, it might +# call some sysctls to implement economy mode. If 0x01, it might set +# the mode to performance. +notify 10 { + match "system" "ACPI"; + match "subsystem" "ACAD"; + action "/etc/acpi_ac $notify"; +}; +*/ diff --git a/etc/rc.conf b/etc/rc.conf new file mode 100644 index 0000000..fc7d415 --- /dev/null +++ b/etc/rc.conf @@ -0,0 +1,256 @@ +# Global OpenRC configuration settings + +# Set to "YES" if you want the rc system to try and start services +# in parallel for a slight speed improvement. When running in parallel we +# prefix the service output with its name as the output will get +# jumbled up. +# WARNING: whilst we have improved parallel, it can still potentially lock +# the boot process. Don't file bugs about this unless you can supply +# patches that fix it without breaking other things! +#rc_parallel="NO" + +# Set rc_interactive to "YES" and you'll be able to press the I key during +# boot so you can choose to start specific services. Set to "NO" to disable +# this feature. This feature is automatically disabled if rc_parallel is +# set to YES. +#rc_interactive="YES" + +# If we need to drop to a shell, you can specify it here. +# If not specified we use $SHELL, otherwise the one specified in /etc/passwd, +# otherwise /bin/sh +# GNU/Linux users could specify /sbin/sulogin +#rc_shell=/bin/sh + +# Do we allow any started service in the runlevel to satisfy the dependency +# or do we want all of them regardless of state? For example, if net.eth0 +# and net.eth1 are in the default runlevel then with rc_depend_strict="NO" +# both will be started, but services that depend on 'net' will work if either +# one comes up. With rc_depend_strict="YES" we would require them both to +# come up. +#rc_depend_strict="YES" + +# rc_hotplug controls which services we allow to be hotplugged. +# A hotplugged service is one started by a dynamic dev manager when a matching +# hardware device is found. +# Hotplugged services appear in the "hotplugged" runlevel. +# If rc_hotplug is set to any value, we compare the name of this service +# to every pattern in the value, from left to right, and we allow the +# service to be hotplugged if it matches a pattern, or if it matches no +# patterns. Patterns can include shell wildcards. +# To disable services from being hotplugged, prefix patterns with "!". +#If rc_hotplug is not set or is empty, all hotplugging is disabled. +# Example - rc_hotplug="net.wlan !net.*" +# This allows net.wlan and any service not matching net.* to be hotplugged. +# Example - rc_hotplug="!net.*" +# This allows services that do not match "net.*" to be hotplugged. + +# rc_logger launches a logging daemon to log the entire rc process to +# /var/log/rc.log +# NOTE: GNU/Linux systems require the devfs service to be started before +# logging can take place and as such cannot log the sysinit runlevel. +#rc_logger="NO" + +# Through rc_log_path you can specify a custom log file. +# The default value is: /var/log/rc.log +#rc_log_path="/var/log/rc.log" + +# If you want verbose output for OpenRC, set this to yes. If you want +# verbose output for service foo only, set it to yes in /etc/conf.d/foo. +#rc_verbose=no + +# By default we filter the environment for our running scripts. To allow other +# variables through, add them here. Use a * to allow all variables through. +#rc_env_allow="VAR1 VAR2" + +# By default we assume that all daemons will start correctly. +# However, some do not - a classic example is that they fork and return 0 AND +# then child barfs on a configuration error. Or the daemon has a bug and the +# child crashes. You can set the number of milliseconds start-stop-daemon +# waits to check that the daemon is still running after starting here. +# The default is 0 - no checking. +#rc_start_wait=100 + +# rc_nostop is a list of services which will not stop when changing runlevels. +# This still allows the service itself to be stopped when called directly. +#rc_nostop="" + +# rc will attempt to start crashed services by default. +# However, it will not stop them by default as that could bring down other +# critical services. +#rc_crashed_stop=NO +#rc_crashed_start=YES + +# Set rc_nocolor to yes if you do not want colors displayed in OpenRC +# output. +#rc_nocolor=NO + +############################################################################## +# MISC CONFIGURATION VARIABLES +# There variables are shared between many init scripts + +# Set unicode to YES to turn on unicode support for keyboards and screens. +#unicode="NO" + +# This is how long fuser should wait for a remote server to respond. The +# default is 60 seconds, but it can be adjusted here. +#rc_fuser_timeout=60 + +# Below is the default list of network fstypes. +# +# afs ceph cifs coda davfs fuse fuse.sshfs gfs glusterfs lustre ncpfs +# nfs nfs4 ocfs2 shfs smbfs +# +# If you would like to add to this list, you can do so by adding your +# own fstypes to the following variable. +#extra_net_fs_list="" + +############################################################################## +# SERVICE CONFIGURATION VARIABLES +# These variables are documented here, but should be configured in +# /etc/conf.d/foo for service foo and NOT enabled here unless you +# really want them to work on a global basis. +# If your service has characters in its name which are not legal in +# shell variable names and you configure the variables for it in this +# file, those characters should be replaced with underscores in the +# variable names as shown below. + +# Some daemons are started and stopped via start-stop-daemon. +# We can set some things on a per service basis, like the nicelevel. +#SSD_NICELEVEL="-19" +# Or the ionice level. The format is class[:data] , just like the +# --ionice start-stop-daemon parameter. +#SSD_IONICELEVEL="2:2" + +# Pass ulimit parameters +# If you are using bash in POSIX mode for your shell, note that the +# ulimit command uses a block size of 512 bytes for the -c and -f +# options +#rc_ulimit="-u 30" + +# It's possible to define extra dependencies for services like so +#rc_config="/etc/foo" +#rc_need="openvpn" +#rc_use="net.eth0" +#rc_after="clock" +#rc_before="local" +#rc_provide="!net" + +# You can also enable the above commands here for each service. Below is an +# example for service foo. +#rc_foo_config="/etc/foo" +#rc_foo_need="openvpn" +#rc_foo_after="clock" + +# Below is an example for service foo-bar. Note that the '-' is illegal +# in a shell variable name, so we convert it to an underscore. +# example for service foo-bar. +#rc_foo_bar_config="/etc/foo-bar" +#rc_foo_bar_need="openvpn" +#rc_foo_bar_after="clock" + +# You can also remove dependencies. +# This is mainly used for saying which services do NOT provide net. +#rc_net_tap0_provide="!net" + +# This is the subsystem type. +# It is used to match against keywords set by the keyword call in the +# depend function of service scripts. +# +# It should be set to the value representing the environment this file is +# PRESENTLY in, not the virtualization the environment is capable of. +# If it is commented out, automatic detection will be used. +# +# The list below shows all possible settings as well as the host +# operating systems where they can be used and autodetected. +# +# "" - nothing special +# "docker" - Docker container manager (GNU/Linux) +# "jail" - Jail (DragonflyBSD or FreeBSD) +# "lxc" - Linux Containers +# "openvz" - Linux OpenVZ +# "prefix" - Prefix +# "rkt" - CoreOS container management system (GNU/Linux) +# "subhurd" - Hurd subhurds (to be checked) +# "chroot" - Chroot container (to be checked) +# "chroot+unshare" - Chroot container using unshare command (GNU/Linux) +# "uml" - Usermode Linux +# "vserver" - Linux vserver +# "xen0" - Xen0 Domain (GNU/HyperBK, GNU/Linux, FreeBSD and NetBSD) +# "xenU" - XenU Domain (GNU/Hurd, GNU/HyperBK, GNU/Linux, FreeBSD, NetBSD and OpenBSD) +#rc_sys="" + +# if you use openrc-init, which is currently only available on GNU/Linux, +# this is the default runlevel to activate after "sysinit" and "boot" +# when booting. +#rc_default_runlevel="default" + +# on GNU/Linux and GNU/Hurd, this is the number of ttys allocated for logins +# It is used in the consolefont, keymaps, numlock and termencoding +# service scripts. +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". +#rc_controller_cgroups="YES" + +# The following settings allow you to set up values for the cgroup +# 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. +# 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 names of the settings followed by their +# values. Each variable can hold multiple settings. +# For example, you would use this to set the cpu.shares setting in the +# cpu controller to 512 for your service. +# rc_cgroup_cpu=" +# cpu.shares 512 +# " +# +#For more information about the adjustments that can be made with +#cgroups, see Documentation/cgroups/* in the linux kernel source tree. + +# Set the blkio controller settings for this service. +#rc_cgroup_blkio="" + +# Set the cpu controller settings for this service. +#rc_cgroup_cpu="" + +# Add this service to the cpuacct controller (any value means yes). +#rc_cgroup_cpuacct="" + +# Set the cpuset controller settings for this service. +#rc_cgroup_cpuset="" + +# Set the devices controller settings for this service. +#rc_cgroup_devices="" + +# Set the hugetlb controller settings for this service. +#rc_cgroup_hugetlb="" + +# Set the memory controller settings for this service. +#rc_cgroup_memory="" + +# Set the net_cls controller settings for this service. +#rc_cgroup_net_cls="" + +# Set the net_prio controller settings for this service. +#rc_cgroup_net_prio="" + +# Set the pids controller settings for this service. +#rc_cgroup_pids="" + +# 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/. +# To perform this cleanup manually for a stopped service, you can +# execute cgroup_cleanup with /etc/init.d/ cgroup_cleanup or +# rc-service cgroup_cleanup. +# rc_cgroup_cleanup="NO" diff --git a/etc/rc.conf.orig b/etc/rc.conf.orig new file mode 100644 index 0000000..3f802db --- /dev/null +++ b/etc/rc.conf.orig @@ -0,0 +1,255 @@ +# Global OpenRC configuration settings + +# Set to "YES" if you want the rc system to try and start services +# in parallel for a slight speed improvement. When running in parallel we +# prefix the service output with its name as the output will get +# jumbled up. +# WARNING: whilst we have improved parallel, it can still potentially lock +# the boot process. Don't file bugs about this unless you can supply +# patches that fix it without breaking other things! +#rc_parallel="NO" + +# Set rc_interactive to "YES" and you'll be able to press the I key during +# boot so you can choose to start specific services. Set to "NO" to disable +# this feature. This feature is automatically disabled if rc_parallel is +# set to YES. +#rc_interactive="YES" + +# If we need to drop to a shell, you can specify it here. +# If not specified we use $SHELL, otherwise the one specified in /etc/passwd, +# otherwise /bin/sh +# Linux users could specify /sbin/sulogin +#rc_shell=/bin/sh + +# Do we allow any started service in the runlevel to satisfy the dependency +# or do we want all of them regardless of state? For example, if net.eth0 +# and net.eth1 are in the default runlevel then with rc_depend_strict="NO" +# both will be started, but services that depend on 'net' will work if either +# one comes up. With rc_depend_strict="YES" we would require them both to +# come up. +#rc_depend_strict="YES" + +# rc_hotplug controls which services we allow to be hotplugged. +# A hotplugged service is one started by a dynamic dev manager when a matching +# hardware device is found. +# Hotplugged services appear in the "hotplugged" runlevel. +# If rc_hotplug is set to any value, we compare the name of this service +# to every pattern in the value, from left to right, and we allow the +# service to be hotplugged if it matches a pattern, or if it matches no +# patterns. Patterns can include shell wildcards. +# To disable services from being hotplugged, prefix patterns with "!". +#If rc_hotplug is not set or is empty, all hotplugging is disabled. +# Example - rc_hotplug="net.wlan !net.*" +# This allows net.wlan and any service not matching net.* to be hotplugged. +# Example - rc_hotplug="!net.*" +# This allows services that do not match "net.*" to be hotplugged. + +# rc_logger launches a logging daemon to log the entire rc process to +# /var/log/rc.log +# NOTE: Linux systems require the devfs service to be started before +# logging can take place and as such cannot log the sysinit runlevel. +#rc_logger="NO" + +# Through rc_log_path you can specify a custom log file. +# The default value is: /var/log/rc.log +#rc_log_path="/var/log/rc.log" + +# If you want verbose output for OpenRC, set this to yes. If you want +# verbose output for service foo only, set it to yes in /etc/conf.d/foo. +#rc_verbose=no + +# By default we filter the environment for our running scripts. To allow other +# variables through, add them here. Use a * to allow all variables through. +#rc_env_allow="VAR1 VAR2" + +# By default we assume that all daemons will start correctly. +# However, some do not - a classic example is that they fork and return 0 AND +# then child barfs on a configuration error. Or the daemon has a bug and the +# child crashes. You can set the number of milliseconds start-stop-daemon +# waits to check that the daemon is still running after starting here. +# The default is 0 - no checking. +#rc_start_wait=100 + +# rc_nostop is a list of services which will not stop when changing runlevels. +# This still allows the service itself to be stopped when called directly. +#rc_nostop="" + +# rc will attempt to start crashed services by default. +# However, it will not stop them by default as that could bring down other +# critical services. +#rc_crashed_stop=NO +#rc_crashed_start=YES + +# Set rc_nocolor to yes if you do not want colors displayed in OpenRC +# output. +#rc_nocolor=NO + +############################################################################## +# MISC CONFIGURATION VARIABLES +# There variables are shared between many init scripts + +# Set unicode to YES to turn on unicode support for keyboards and screens. +#unicode="NO" + +# This is how long fuser should wait for a remote server to respond. The +# default is 60 seconds, but it can be adjusted here. +#rc_fuser_timeout=60 + +# Below is the default list of network fstypes. +# +# afs ceph cifs coda davfs fuse fuse.sshfs gfs glusterfs lustre ncpfs +# nfs nfs4 ocfs2 shfs smbfs +# +# If you would like to add to this list, you can do so by adding your +# own fstypes to the following variable. +#extra_net_fs_list="" + +############################################################################## +# SERVICE CONFIGURATION VARIABLES +# These variables are documented here, but should be configured in +# /etc/conf.d/foo for service foo and NOT enabled here unless you +# really want them to work on a global basis. +# If your service has characters in its name which are not legal in +# shell variable names and you configure the variables for it in this +# file, those characters should be replaced with underscores in the +# variable names as shown below. + +# Some daemons are started and stopped via start-stop-daemon. +# We can set some things on a per service basis, like the nicelevel. +#SSD_NICELEVEL="-19" +# Or the ionice level. The format is class[:data] , just like the +# --ionice start-stop-daemon parameter. +#SSD_IONICELEVEL="2:2" + +# Pass ulimit parameters +# If you are using bash in POSIX mode for your shell, note that the +# ulimit command uses a block size of 512 bytes for the -c and -f +# options +#rc_ulimit="-u 30" + +# It's possible to define extra dependencies for services like so +#rc_config="/etc/foo" +#rc_need="openvpn" +#rc_use="net.eth0" +#rc_after="clock" +#rc_before="local" +#rc_provide="!net" + +# You can also enable the above commands here for each service. Below is an +# example for service foo. +#rc_foo_config="/etc/foo" +#rc_foo_need="openvpn" +#rc_foo_after="clock" + +# Below is an example for service foo-bar. Note that the '-' is illegal +# in a shell variable name, so we convert it to an underscore. +# example for service foo-bar. +#rc_foo_bar_config="/etc/foo-bar" +#rc_foo_bar_need="openvpn" +#rc_foo_bar_after="clock" + +# You can also remove dependencies. +# This is mainly used for saying which services do NOT provide net. +#rc_net_tap0_provide="!net" + +# This is the subsystem type. +# It is used to match against keywords set by the keyword call in the +# depend function of service scripts. +# +# It should be set to the value representing the environment this file is +# PRESENTLY in, not the virtualization the environment is capable of. +# If it is commented out, automatic detection will be used. +# +# The list below shows all possible settings as well as the host +# operating systems where they can be used and autodetected. +# +# "" - nothing special +# "docker" - Docker container manager (Linux) +# "jail" - Jail (DragonflyBSD or FreeBSD) +# "lxc" - Linux Containers +# "openvz" - Linux OpenVZ +# "prefix" - Prefix +# "rkt" - CoreOS container management system (Linux) +# "subhurd" - Hurd subhurds (to be checked) +# "chroot+unshare" - Container created by chroot+unshare (Linux) +# "uml" - Usermode Linux +# "vserver" - Linux vserver +# "xen0" - Xen0 Domain (Linux and NetBSD) +# "xenU" - XenU Domain (Linux and NetBSD) +#rc_sys="" + +# if you use openrc-init, which is currently only available on Linux, +# this is the default runlevel to activate after "sysinit" and "boot" +# when booting. +#rc_default_runlevel="default" + +# on Linux and Hurd, this is the number of ttys allocated for logins +# It is used in the consolefont, keymaps, numlock and termencoding +# service scripts. +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". +#rc_controller_cgroups="YES" + +# The following settings allow you to set up values for the cgroup +# 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. +# 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 names of the settings followed by their +# values. Each variable can hold multiple settings. +# For example, you would use this to set the cpu.shares setting in the +# cpu controller to 512 for your service. +# rc_cgroup_cpu=" +# cpu.shares 512 +# " +# +#For more information about the adjustments that can be made with +#cgroups, see Documentation/cgroups/* in the linux kernel source tree. + +# Set the blkio controller settings for this service. +#rc_cgroup_blkio="" + +# Set the cpu controller settings for this service. +#rc_cgroup_cpu="" + +# Add this service to the cpuacct controller (any value means yes). +#rc_cgroup_cpuacct="" + +# Set the cpuset controller settings for this service. +#rc_cgroup_cpuset="" + +# Set the devices controller settings for this service. +#rc_cgroup_devices="" + +# Set the hugetlb controller settings for this service. +#rc_cgroup_hugetlb="" + +# Set the memory controller settings for this service. +#rc_cgroup_memory="" + +# Set the net_cls controller settings for this service. +#rc_cgroup_net_cls="" + +# Set the net_prio controller settings for this service. +#rc_cgroup_net_prio="" + +# Set the pids controller settings for this service. +#rc_cgroup_pids="" + +# 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/. +# To perform this cleanup manually for a stopped service, you can +# execute cgroup_cleanup with /etc/init.d/ cgroup_cleanup or +# rc-service cgroup_cleanup. +# rc_cgroup_cleanup="NO" diff --git a/etc/rc.devd b/etc/rc.devd new file mode 100644 index 0000000..e2286d1 --- /dev/null +++ b/etc/rc.devd @@ -0,0 +1,39 @@ +#!/bin/sh +# Copyright (c) 2007-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. + +# Inform RC that we are in the background and hotplugged +IN_BACKGROUND=yes +IN_HOTPLUG=yes +export IN_BACKGROUND IN_HOTPLUG + +getmedia() { + ifconfig "$1" | while read line; do + case "${line}" in + media:" "*) echo "${line}"; return;; + esac + done +} + +# Try and create an init script for network interfaces +if [ ! -e /etc/init.d/"$1" -a ! -e /usr/local/init.d/"$1" ]; then + base=${1%%.*} + if [ "${base}" = "net" ]; then + # We only create links for pyhsical interfaces + [ -n "$(getmedia ${1#*.})" ] || exit 1 + base="net.lo0" + fi + if [ -e /etc/init.d/"${base}" -a "${base}" != "$1" ]; then + ln -s "${base}" /etc/init.d/"$1" + fi +fi + +# Run the init script +exec /etc/init.d/"$1" "$2" diff --git a/etc/rc.in b/etc/rc.in new file mode 100644 index 0000000..72f8f05 --- /dev/null +++ b/etc/rc.in @@ -0,0 +1,26 @@ +#!@SHELL@ +# Copyright (c) 2007-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. + +# If $TERM is not set then assume default of @TERM@ +# This gives us a nice colour boot :) +[ -z "$TERM" -o "$TERM" = "dumb" ] && TERM="@TERM@" && export TERM + +# Handle interrupts +trap : SIGINT +trap "echo 'Boot interrupted'; exit 1" SIGQUIT + +/sbin/openrc sysinit || exit 1 +/sbin/openrc boot || exit 1 +/sbin/openrc default + +# We don't actually care if rc default worked or not, we should exit 0 +# to allow logins +exit 0 diff --git a/etc/rc.shutdown.in b/etc/rc.shutdown.in new file mode 100644 index 0000000..de3e09b --- /dev/null +++ b/etc/rc.shutdown.in @@ -0,0 +1,24 @@ +#!@SHELL@ +# Copyright (c) 2007-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. + +# Handle interrupts +trap : SIGINT SIGQUIT + +# Try and use stuff in /lib over anywhere else so we can shutdown +# local mounts correctly. +LD_LIBRARY_PATH="/lib${LD_LIBRARY_PATH:+:}${LD_LIBRARY_PATH}" ; export LD_LIBRARY_PATH + +# If $TERM is not set then assume default of @TERM@ +# This gives us a nice colour boot :) +[ -z "$TERM" -o "$TERM" = "dumb" ] && TERM="@TERM@" && export TERM + +action=${1:-shutdown} +exec /sbin/openrc "${action}" diff --git a/guide.md b/guide.md new file mode 100644 index 0000000..c4351a2 --- /dev/null +++ b/guide.md @@ -0,0 +1,269 @@ +# Purpose and description + +OpenRC is an init system for Unixoid operating systems. It takes care of +startup and shutdown of the whole system, including services. + +It evolved out of the Gentoo "Baselayout" package which was a custom pure-shell +startup solution. (This was both hard to maintain and debug, and not very +performant) + +Most of the core parts are written in C99 for performance and flexibility +reasons, while everything else is posix sh. +The License is 2-clause BSD + +Current size is about 10k LoC C, and about 4k LoC shell. + +OpenRC is known to work on Linux, many BSDs (FreeBSD, OpenBSD, DragonFlyBSD at +least) and HURD. + +Services are stateful (i.e. `start`; `start` will lead to "it's already started") + +# Startup + +Usually PID1 (aka. `init`) calls the OpenRC binary (`/sbin/openrc` by default). +(The default setup assumes sysvinit for this) + +openrc scans the runlevels (default: `/etc/runlevels`) and builds a dependency +graph, then starts the needed service scripts, either serialized (default) or in +parallel. + +When all the init scripts are started openrc terminates. There is no persistent +daemon. (Integration with tools like monit, runit or s6 can be done) + +# Shutdown + +On change to runlevel 0/6 or running `reboot`, `halt` etc., openrc stops all +services that are started and runs the services in the `shutdown` runlevel. + +# Modifying Service Scripts + +Any service can, at any time, be started/stopped/restarted by executing +`rc-service someservice start`, `rc-service someservice stop`, etc. +Another, less preferred method, is to run the service script directly, +e.g. `/etc/init.d/service start`, `/etc/init.d/service stop`, etc. + +OpenRC will take care of dependencies, e.g starting apache will start network +first, and stopping network will stop apache first. + +There is a special command `zap` that makes OpenRC 'forget' that a service is +started; this is mostly useful to reset a crashed service to stopped state +without invoking the (possibly broken) stop function of the service script. + +Calling `openrc` without any arguments will try to reset all services so +that the current runlevel is satisfied; if you manually started apache it will be +stopped, and if squid died but is in the current runlevel it'll be restarted. + +There is a `service` helper that emulates the syntax seen on e.g. older Redhat +and Ubuntu (`service nginx start` etc.) + +# Runlevels + +OpenRC has a concept of runlevels, similar to what sysvinit historically +offered. A runlevel is basically a collection of services that needs to be +started. Instead of random numbers they are named, and users can create their +own if needed. This allows, for example, to have a default runlevel with +"everything" enabled, and a "powersaving" runlevel where some services are +disabled. + +The `rc-status` helper will print all currently active runlevels and the state +of init scripts in them: + +``` +# rc-status + * Caching service dependencies ... [ ok ] +Runlevel: default + modules [ started ] + lvm [ started ] +``` + +All runlevels are represented as folders in `/etc/runlevels/` with symlinks to +the actual init scripts. + +Calling openrc with an argument (`openrc default`) will switch to that +runlevel; this will start and stop services as needed. + +Managing runlevels is usually done through the `rc-update` helper, but could of +course be done by hand if desired. +e.g. `rc-update add nginx default` - add nginx to the default runlevel +Note: This will not auto-start nginx! You'd still have to trigger `rc` or run +the initscript by hand. + +FIXME: Document stacked runlevels + +The default startup uses the runlevels `boot`, `sysinit` and `default`, in that +order. Shutdown uses the `shutdown` runlevel. + + +# 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 initscripts 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. +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 + +# The Magic of `conf.d` + +Most service scripts need default values. It would be fragile to +explicitly source some arbitrary files. By convention `openrc-run` will source +the matching file in `/etc/conf.d/` for any script in `/etc/init.d/` + +This allows you to set random startup-related things easily. Example: + +``` +conf.d/foo: +START_OPTS="--extraparameter sausage" + +init.d/foo: +start() { + /usr/sbin/foo-daemon ${STARTOPTS} +} +``` + +The big advantage of this split is that most of the time editing of the init +script can be avoided. + +# Start-Stop-Daemon + +OpenRC has its own modified version of s-s-d, which is historically related and +mostly syntax-compatible to Debian's s-s-d, but has been rewritten from scratch. + +It helps with starting daemons, backgrounding, creating PID files and many +other convenience functions related to managing daemons. + +# `/etc/rc.conf` + +This file manages the default configuration for OpenRC, and it has examples of +per-service-script variables. + +Among these are `rc_parallel` (for parallelized startup), `rc_log` (logs all boot +messages to a file), and a few others. + +# ulimit and CGroups + +Setting `ulimit` and `nice` values per service can be done through the `rc_ulimit` +variable. + +Under Linux, OpenRC can optionally use CGroups for process management. +By default each service script's processes are migrated to their own CGroup. + +By changing certain values in the `conf.d` file limits can be enforced per +service. It is easy to find orphan processes of a service that persist after +`stop()`, but by default these will NOT be terminated. +To change this add `rc_cgroup_cleanup="yes"` in the `conf.d` files for services +where you desire this functionality. + +# Caching + +For performance reasons OpenRC keeps a cache of pre-parsed initscript metadata +(e.g. `depend`). The default location for this is `/${RC_SVCDIR}/cache`. + +The cache uses `mtime` to check for file staleness. Should any service script +change it'll re-source the relevant files and update the cache + +# Convenience functions + +OpenRC has wrappers for many common output tasks in libeinfo. +This allows to print colour-coded status notices and other things. +To make the output consistent the bundled initscripts all use ebegin/eend to +print nice messages. diff --git a/init.d/.gitignore b/init.d/.gitignore new file mode 100644 index 0000000..bad8b47 --- /dev/null +++ b/init.d/.gitignore @@ -0,0 +1,48 @@ +agetty +binfmt +modules-load +bootmisc +fsck +hostname +local +localmount +loopback +moused +netmount +network +root +savecache +swap +sysctl +urandom +devfs +dmesg +hwclock +consolefont +keymaps +killprocs +modules +mount-ro +mtab +net-online +numlock +osclock +binfmt_misc +s6-svscan +staticroute +sysfs +devdb +hostid +newsyslog +pf +rarpd +rc-enabled +rpcbind +runsvdir +savecore +swap-blk +swclock +syslogd +termencoding +ttys +wscons diff --git a/init.d/Makefile b/init.d/Makefile new file mode 100644 index 0000000..969be0f --- /dev/null +++ b/init.d/Makefile @@ -0,0 +1,36 @@ +include ../mk/net.mk + +DIR= ${INITDIR} +SRCS= bootmisc.in fsck.in hostname.in localmount.in loopback.in \ + netmount.in osclock.in root.in savecache.in swap.in \ + sysctl.in runsvdir.in urandom.in s6-svscan.in ${SRCS-${OS}} +BIN= ${OBJS} + +# Are we installing our network scripts? +ifeq (${MKNET},yes) +SRCS+= network.in staticroute.in +endif + +MK= ../mk +include ${MK}/os.mk + +# Generic BSD scripts +SRCS-FreeBSD= hostid.in modules.in moused.in newsyslog.in pf.in rarpd.in \ + rc-enabled.in rpcbind.in savecore.in syslogd.in +# These are FreeBSD specific +SRCS-FreeBSD+= adjkerntz.in devd.in dumpon.in encswap.in ipfw.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 mount-ro.in \ + numlock.in binfmt_misc.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 \ + rpcbind.in savecore.in syslogd.in +# These are NetBSD specific +SRCS-NetBSD+= devdb.in swap-blk.in ttys.in wscons.in + +include ${MK}/scripts.mk + +_installafter_: realinstall diff --git a/init.d/adjkerntz.in b/init.d/adjkerntz.in new file mode 100644 index 0000000..7f8b007 --- /dev/null +++ b/init.d/adjkerntz.in @@ -0,0 +1,69 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +extra_commands="save" + +description="Sets the local clock to UTC or Local Time." +description_save="Saves the current time in the BIOS." + +: ${clock:=${CLOCK:-UTC}} +if [ "$clock" = "UTC" ]; then + utc="UTC" +else + utc="Local Time" +fi + +depend() +{ + provide clock + # BSD adjkerntz needs to be able to write to /etc + if [ "$clock" = "UTC" -a -e /etc/wall_cmos_clock ] || + [ "$clock" != "UTC" -a ! -e /etc/wall_cmos_clock ]; then + need root + fi + keyword -jail -prefix +} + +start() +{ + ebegin "Starting the System Clock Adjuster [${utc}]" + if [ "$clock" != "UTC" ]; then + echo >/etc/wall_cmos_clock + start-stop-daemon --start --exec /sbin/adjkerntz -- -i + else + rm -f /etc/wall_cmos_clock + /sbin/adjkerntz -i + fi + eend $? +} + +save() +{ + ebegin "Setting hardware clock using the system clock [${utc}]" + adjkerntz -a + eend $? +} + +stop() +{ + # Don't tweak the hardware clock on LiveCD halt. + if yesno "${clock_systohc:-$CLOCK_SYSTOHC}"; then + [ -z "$CDBOOT" ] && save + fi + + ebegin "Stopping the System Clock Adjuster" + if start-stop-daemon --test --quiet --stop --exec /sbin/adjkerntz; then + start-stop-daemon --stop --exec /sbin/adjkerntz + eend $? + else + eend 0 + fi +} diff --git a/init.d/agetty.in b/init.d/agetty.in new file mode 100644 index 0000000..ef1b22f --- /dev/null +++ b/init.d/agetty.in @@ -0,0 +1,33 @@ +#!@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="start agetty on a terminal line" +supervisor=supervise-daemon +port="${RC_SVCNAME#*.}" +term_type="${term_type:-linux}" +command=/sbin/agetty +command_args_foreground="${agetty_options} ${port} ${baud} ${term_type}" +pidfile="/run/${RC_SVCNAME}.pid" +export EINFO_QUIET="${quiet:-yes}" + +depend() { + after * + keyword -prefix +} + +start_pre() { + if [ -z "$port" ]; then + eerror "${RC_SVCNAME} cannot be started directly. You must create" + eerror "symbolic links to it for the ports you want to start" + eerror "agetty on and add those to the appropriate runlevels." + return 1 + fi +} diff --git a/init.d/binfmt.in b/init.d/binfmt.in new file mode 100644 index 0000000..02a352e --- /dev/null +++ b/init.d/binfmt.in @@ -0,0 +1,27 @@ +#!@SBINDIR@/openrc-run +# 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. + +description="Register misc binary format handlers" + +depend() +{ + after clock binfmt_misc + use modules devfs + keyword -docker -lxc -openvz -prefix -chroot+unshare -vserver +} + +start() +{ + ebegin "Loading custom binary format handlers" + "$RC_LIBEXECDIR"/sh/binfmt.sh + eend $? + return 0 +} diff --git a/init.d/binfmt_misc.in b/init.d/binfmt_misc.in new file mode 100644 index 0000000..e685ff1 --- /dev/null +++ b/init.d/binfmt_misc.in @@ -0,0 +1,41 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Mounts binfmt_misc filesystems in /proc." + +depend() +{ + after clock + use devfs + want modules + need localmount + keyword -docker -lxc -openvz -prefix -chroot+unshare -vserver +} + +start() +{ + # Setup Kernel Support for miscellaneous Binary Formats + if [ -d /proc/sys/fs/binfmt_misc ] && + [ ! -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." + fi + if grep -qs binfmt_misc /proc/filesystems; then + ebegin "Mounting misc binary format filesystem" + mount -t binfmt_misc -o nodev,noexec,nosuid \ + binfmt_misc /proc/sys/fs/binfmt_misc + eend $? + fi + fi + return 0 +} diff --git a/init.d/bootmisc.in b/init.d/bootmisc.in new file mode 100644 index 0000000..4e98031 --- /dev/null +++ b/init.d/bootmisc.in @@ -0,0 +1,253 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +depend() +{ + need localmount + before logger + after clock root sysctl + keyword -prefix -timeout +} + +: ${wipe_tmp:=${WIPE_TMP:-yes}} +: ${log_dmesg:=${LOG_DMESG:-yes}} + +cleanup_tmp_dir() +{ + local dir="$1" + + if ! [ -d "$dir" ]; then + mkdir -p "$dir" || return $? + fi + checkpath -W "$dir" || return 1 + chmod a+rwt "$dir" 2> /dev/null + cd "$dir" || return 1 + if yesno $wipe_tmp; then + ebegin "Wiping $dir directory" + + # Faster than raw find + if ! rm -rf -- [!ajlq\.]* 2>/dev/null ; then + # Blah, too many files + find . -maxdepth 1 -name '[!ajlq\.]*' -exec rm -rf -- {} + + fi + + # pam_mktemp creates a .private directory within which + # each user gets a private directory with immutable + # bit set; remove the immutable bit before trying to + # remove it. + [ -d /tmp/.private ] && chattr -R -a /tmp/.private 2> /dev/null + + # Prune the paths that are left + find . -maxdepth 1 \ + ! -name . \ + ! -name lost+found \ + ! -name quota.user \ + ! -name aquota.user \ + ! -name quota.group \ + ! -name aquota.group \ + ! -name journal \ + -exec rm -rf -- {} + + eend 0 + else + ebegin "Cleaning $dir directory" + rm -rf -- .X*-lock esrv* kio* \ + jpsock.* .fam* .esd* \ + orbit-* ssh-* ksocket-* \ + .*-unix + eend 0 + fi +} + +cleanup_var_run_dir() +{ + ebegin "Cleaning /var/run" + for x in $(find /var/run ! -type d ! -name utmp \ + ! -name random-seed ! -name dev.db \ + ! -name ld-elf.so.hints ! -name ld-elf32.so.hints \ + ! -name ld.so.hints); + do + # Clean stale sockets + if [ -S "$x" ]; then + if command -v fuser >/dev/null 2>&1; then + fuser "$x" >/dev/null 2>&1 || rm -- "$x" + else + rm -- "$x" + fi + fi + [ ! -f "$x" ] && continue + # Do not remove pidfiles of already running daemons + case "$x" in + *.pid) + start-stop-daemon --test --quiet \ + --stop --pidfile "$x" && continue + ;; + esac + rm -f -- "$x" + done + eend 0 +} + +mkutmp() +{ + : >"$1" + # Not all systems have the utmp group + chgrp utmp "$1" 2>/dev/null + chmod 0664 "$1" +} + +migrate_to_run() +{ + src="$1" + dst="$2" + if [ -L $src -a "$(readlink -f $src)" != $dst ]; then + ewarn "$src does not point to $dst." + ewarn "Setting $src to point to $dst." + rm $src + elif [ ! -L $src -a -d $src ]; then + ebegin "Migrating $src to $dst" + cp -a $src/* $dst/ + rm -rf $src + eend $? + fi + # If $src doesn't exist at all, just run this + if [ ! -e $src ]; then + ln -s $dst $src + fi +} + +clean_run() +{ + [ "$RC_SYS" = VSERVER -o "$RC_SYS" = LXC ] && return 0 + local dir + # If / is still read-only due to a problem, this will fail! + if ! checkpath -W /; then + ewarn "/ is not writable; unable to clean up underlying /run" + return 1 + fi + if ! checkpath -W /tmp; then + ewarn "/tmp is not writable; unable to clean up underlying /run" + return 1 + fi + # Now we know that we can modify /tmp and / + # if mktemp -d fails, it returns an EMPTY string + # STDERR: mktemp: failed to create directory via template ‘/tmp/tmp.XXXXXXXXXX’: Read-only file system + # STDOUT: '' + rc=0 + dir=$(mktemp -d) + if [ -n "$dir" -a -d $dir -a -w $dir ]; then + mount --bind / $dir && rm -rf $dir/run/* || rc=1 + umount $dir && rmdir $dir + else + rc=1 + fi + if [ $rc -ne 0 ]; then + ewarn "Could not clean up underlying /run on /" + return 1 + fi +} + +start() +{ + # Remove any added console dirs + if checkpath -W "$RC_LIBEXECDIR"; then + rm -rf "$RC_LIBEXECDIR"/console/* + fi + + local logw=false runw=false extra= + # Ensure that our basic dirs exist + if [ "$RC_UNAME" = Linux ]; then + # Satisfy GNU/Linux FHS + extra=/var/lib/misc + if [ ! -d /run ]; then + extra="/var/run $extra" + fi + else + extra=/var/run + fi + for x in /var/log /tmp $extra; do + if ! [ -d $x ]; then + if ! mkdir -p $x; then + eend 1 "failed to create needed directory $x" + return 1 + fi + fi + done + + if [ "$RC_UNAME" = Linux -a -d /run ]; then + migrate_to_run /var/lock /run/lock + migrate_to_run /var/run /run + clean_run + fi + + if checkpath -W /var/run; then + ebegin "Creating user login records" + local xtra= + [ "$RC_UNAME" = NetBSD ] && xtra=x + for x in "" $xtra; do + mkutmp /var/run/utmp$x + done + [ -e /var/log/wtmp ] || mkutmp /var/log/wtmp + eend 0 + + mountinfo -q -f tmpfs /var/run || cleanup_var_run_dir + fi + + # Clean up /tmp directories + local tmp= + for tmp in ${clean_tmp_dirs:-${wipe_tmp_dirs-/tmp}}; do + mountinfo -q -f tmpfs "$tmp" || cleanup_tmp_dir "$tmp" + done + + if checkpath -W /tmp; then + # Make sure our X11 stuff have the correct permissions + # Omit the chown as bootmisc is run before network is up + # and users may be using lame LDAP auth #139411 + rm -rf /tmp/.ICE-unix /tmp/.X11-unix + mkdir -p /tmp/.ICE-unix /tmp/.X11-unix + chmod 1777 /tmp/.ICE-unix /tmp/.X11-unix + if [ -x /sbin/restorecon ]; then + restorecon /tmp/.ICE-unix /tmp/.X11-unix + fi + fi + + if yesno $log_dmesg; then + if $logw || checkpath -W /var/log; then + # Create an 'after-boot' dmesg log + case "$RC_SYS" in + VSERVER|OPENVZ|LXC|CHROOT+UNSHARE) ;; + *) + if yesno ${previous_dmesg:-no}; then + mv /var/log/dmesg /var/log/dmesg.old + fi + dmesg > /var/log/dmesg + chmod 640 /var/log/dmesg + ;; + esac + fi + fi + + return 0 +} + +stop() +{ + # Write a halt record if we're shutting down + if [ "$RC_RUNLEVEL" = shutdown ]; then + [ "$RC_UNAME" = Linux ] && openrc-shutdown -w + if [ "$RC_SYS" = OPENVZ ]; then + yesno $RC_REBOOT && printf "" >/reboot + fi + fi + + return 0 +} + +# vim: ft=sh diff --git a/init.d/consolefont.in b/init.d/consolefont.in new file mode 100644 index 0000000..160e429 --- /dev/null +++ b/init.d/consolefont.in @@ -0,0 +1,70 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Sets a font for the consoles." + +depend() +{ + need localmount termencoding + after hotplug bootmisc modules + keyword -docker -lxc -openvz -prefix -chroot+unshare -uml -vserver -xenu +} + +start() +{ + ttyn=${rc_tty_number:-${RC_TTY_NUMBER:-12}} + consolefont=${consolefont:-${CONSOLEFONT}} + unicodemap=${unicodemap:-${UNICODEMAP}} + consoletranslation=${consoletranslation:-${CONSOLETRANSLATION}} + + if [ -z "$consolefont" ]; then + ebegin "Using the default console font" + eend 0 + return 0 + fi + + if [ "$ttyn" = 0 ]; then + ebegin "Skipping font setup (rc_tty_number == 0)" + eend 0 + return 0 + fi + + local x= param= sf_param= retval=0 ttydev=/dev/tty + + # Get additional parameters + if [ -n "$consoletranslation" ]; then + param="$param -m $consoletranslation" + fi + if [ -n "${unicodemap}" ]; then + param="$param -u $unicodemap" + fi + + # Set the console font + ebegin "Setting console font [$consolefont]" + [ -d /dev/vc ] && ttydev=/dev/vc/ + x=1 + while [ $x -le $ttyn ]; do + if ! setfont $consolefont $param -C $ttydev$x >/dev/null; then + retval=1 + break + fi + : $(( x += 1 )) + done + eend $retval + + # Store the font so we can use it ASAP on boot + if [ $retval -eq 0 ] && checkpath -W "$RC_LIBEXECDIR"; then + mkdir -p "$RC_LIBEXECDIR"/console + setfont -O "$RC_LIBEXECDIR"/console/font + fi + + return $retval +} diff --git a/init.d/devd.in b/init.d/devd.in new file mode 100644 index 0000000..d1e7fd5 --- /dev/null +++ b/init.d/devd.in @@ -0,0 +1,29 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=/sbin/devd +command_args=$devd_args +name="Device State Change Daemon" + +depend() { + need localmount + after bootmisc + before net.lo0 + keyword -jail -prefix +} + +start_pre() { + sysctl hw.bus.devctl_disable=0 >/dev/null +} + +stop_post() { + sysctl hw.bus.devctl_disable=1 >/dev/null +} diff --git a/init.d/devdb.in b/init.d/devdb.in new file mode 100644 index 0000000..a6d3806 --- /dev/null +++ b/init.d/devdb.in @@ -0,0 +1,29 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2008-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. + +description="Creates the dev database" + +depend() +{ + after clock + need localmount +} + +start() +{ + ebegin "Building the dev database" + if [ /var/run/dev.db -nt /dev ]; then + : + else + dev_mkdb + fi + eend $? +} diff --git a/init.d/devfs.in b/init.d/devfs.in new file mode 100644 index 0000000..05d279e --- /dev/null +++ b/init.d/devfs.in @@ -0,0 +1,127 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Set up the /dev directory" + +depend() +{ + before dev logger + keyword -docker -lxc -prefix -chroot+unshare -vserver +} + +mount_dev() +{ + local action=--mount devfstype msg=Mounting + # Some devices require exec, Bug #92921 + local mountopts="exec,nosuid,mode=0755" + if yesno ${skip_mount_dev:-no} ; then + einfo "/dev will not be mounted due to user request" + return 0 + fi + if mountinfo -q /dev; then + action=--remount + mountopts="remount,$mountopts" + msg=Remounting + fi + if fstabinfo -q /dev; then + ebegin "$msg /dev according to @SYSCONFDIR@/fstab" + fstabinfo -q $action /dev + eend $? + return 0 + fi + if grep -q devtmpfs /proc/filesystems; then + devfstype=devtmpfs + mountopts="$mountopts,size=10M" + elif grep -q tmpfs /proc/filesystems; then + devfstype=tmpfs + mountopts="$mountopts,size=10M" + fi + if [ -n "$devfstype" ]; then + ebegin "$msg $devfstype on /dev" + mount -n -t $devfstype -o $mountopts dev /dev + eend $? + else + ewarn "This kernel does not have devtmpfs or tmpfs support, and there" + 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" + fi + return 0 +} + +seed_dev() +{ + # Seed /dev with some things that we know we need + + # creating /dev/console, /dev/tty and /dev/tty1 to be able to write + # to $CONSOLE with/without bootsplash before udevd creates it + [ -c /dev/console ] || mknod -m 600 /dev/console c 5 1 + [ -c /dev/tty1 ] || mknod -m 620 /dev/tty1 c 4 1 + [ -c /dev/tty ] || mknod -m 666 /dev/tty c 5 0 + + # udevd will dup its stdin/stdout/stderr to /dev/null + # and we do not want a file which gets buffered in ram + [ -c /dev/null ] || mknod -m 666 /dev/null c 1 3 + + # so udev can add its start-message to dmesg + [ -c /dev/kmsg ] || mknod -m 660 /dev/kmsg c 1 11 + + # extra symbolic links not provided by default + [ -e /dev/fd ] || ln -snf /proc/self/fd /dev/fd + [ -e /dev/stdin ] || ln -snf /proc/self/fd/0 /dev/stdin + [ -e /dev/stdout ] || ln -snf /proc/self/fd/1 /dev/stdout + [ -e /dev/stderr ] || ln -snf /proc/self/fd/2 /dev/stderr + [ -e /proc/kcore ] && ln -snf /proc/kcore /dev/core + + # Mount required directories as user may not have them in /etc/fstab + for x in \ + "mqueue /dev/mqueue 1777 ,nodev mqueue" \ + "devpts /dev/pts 0755 ,gid=5,mode=0620,newinstance devpts" \ + "tmpfs /dev/shm 1777 ,nodev,mode=1777 shm" \ + ; do + set -- $x + grep -Eq "[[:space:]]+$1$" /proc/filesystems || continue + mountinfo -q $2 && continue + + if [ ! -d $2 ]; then + mkdir -m $3 -p $2 >/dev/null 2>&1 || \ + ewarn "Could not create $2!" + fi + + if [ -d $2 ]; then + ebegin "Mounting $2" + if ! fstabinfo --mount $2; then + mount -n -t $1 -o noexec,nosuid$4 $5 $2 + fi + eend $? + fi + done +} + +restorecon_dev() +{ + if [ -x /sbin/restorecon ]; then + ebegin "Restoring SELinux contexts in /dev" + restorecon -rF /dev >/dev/null 2>&1 + eend $? + fi + + return 0 +} + +start() +{ + mount_dev + seed_dev + restorecon_dev + return 0 +} diff --git a/init.d/dmesg.in b/init.d/dmesg.in new file mode 100644 index 0000000..8d8604d --- /dev/null +++ b/init.d/dmesg.in @@ -0,0 +1,25 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Set the dmesg level for a cleaner boot" + +depend() +{ + before dev modules + keyword -docker -lxc -prefix -chroot+unshare -vserver +} + +start() +{ + if [ -n "$dmesg_level" ]; then + dmesg -n$dmesg_level + fi +} diff --git a/init.d/dumpon.in b/init.d/dumpon.in new file mode 100644 index 0000000..8a72e69 --- /dev/null +++ b/init.d/dumpon.in @@ -0,0 +1,33 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Configures a specific kernel dump device." + +depend() { + after clock + need swap + keyword -jail -prefix +} + +start() { + # Setup any user requested dump device + if [ -n "$dump_device" ]; then + ebegin "Activating kernel core dump device ($dump_device)" + dumpon ${dump_device} + eend $? + fi +} + +stop() { + ebegin "Deactivating kernel core dump device" + dumpon off + eend $? +} diff --git a/init.d/encswap.in b/init.d/encswap.in new file mode 100644 index 0000000..3df4a35 --- /dev/null +++ b/init.d/encswap.in @@ -0,0 +1,43 @@ +#!@SBINDIR@/openrc-run +# Copyright 1992-2012 FreeBSD Project +# Released under the 2-clause BSD license + +depend() { + before swap +} + +start() { + while read device mountpoint type options rest ; do + case ":${device}:${type}:${options}" in + :#*) + ;; + *.bde:swap:sw) + passphrase=$(dd if=/dev/random count=1 2>/dev/null | md5 -q) + device="${device%.bde}" + gbde init "${device}" -P "${passphrase}" || return 1 + gbde attach "${device}" -p "${passphrase}" || return 1 + ;; + *.eli:swap:sw) + device="${device%.eli}" + geli onetime ${geli_swap_flags} "${device}" || return 1 + ;; + esac + done < /etc/fstab +} + +stop() { + while read device mountpoint type options rest ; do + case ":${device}:${type}:${options}" in + :#*) + ;; + *.bde:swap:sw) + device="${device%.bde}" + gbde detach "${device}" + ;; + *.eli:swap:sw) + # Nothing here, because geli swap devices should be + # created with the auto-detach-on-last-close option. + ;; + esac + done < /etc/fstab +} diff --git a/init.d/fsck.in b/init.d/fsck.in new file mode 100644 index 0000000..4407707 --- /dev/null +++ b/init.d/fsck.in @@ -0,0 +1,131 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Check and repair filesystems according to /etc/fstab" +_IFS=" +" + +depend() +{ + after clock + use dev clock modules + keyword -docker -jail -lxc -openvz -prefix -chroot+unshare -timeout -vserver -uml +} + +_abort() { + yesno ${fsck_abort_on_errors:-yes} && rc-abort + return 1 +} + +# We should only reboot when first booting +_reboot() { + if [ "$RC_RUNLEVEL" = "$RC_BOOTLEVEL" ]; then + reboot "$@" + _abort || return 1 + fi +} + +_forcefsck() +{ + [ -e /forcefsck ] || get_bootparam forcefsck +} + +start() +{ + local fsck_opts= p= check_extra= + + if [ -e /fastboot ]; then + ewarn "Skipping fsck due to /fastboot" + return 0 + fi + if _forcefsck; then + fsck_opts="$fsck_opts -f" + check_extra="(check forced)" + elif ! yesno ${fsck_on_battery:-YES} && ! on_ac_power; then + ewarn "Skipping fsck due to not being on AC power" + return 0 + fi + + if [ -n "$fsck_passno" ]; then + check_extra="[passno $fsck_passno] $check_extra" + if [ -n "$fsck_mnt" ]; then + eerror "Only 1 of fsck_passno and fsck_mnt must be set!" + return 1 + fi + fi + ebegin "Checking local filesystems $check_extra" + # Append passno mounts + for p in $fsck_passno; do + local IFS="$_IFS" + case "$p" in + [0-9]*) p="=$p";; + esac + set -- "$@" $(fstabinfo --passno "$p") + unset IFS + done + # Append custom mounts + for m in $fsck_mnt ; do + local IFS="$_IFS" + set -- "$@" "$m" + unset IFS + done + + if [ "$RC_UNAME" = Linux ]; then + local skiptypes + skiptypes=$(printf 'no%s,' ${net_fs_list} ${extra_net_fs_list}) + [ "${skiptypes}" = "no," ] && skiptypes="" + fsck_opts="$fsck_opts -C0 -T -t ${skiptypes}noopts=_netdev" + if [ -z "$fsck_passno" -a -z "$fsck_mnt" ]; then + fsck_args=${fsck_args:--A -p} + if echo 2>/dev/null >/.test.$$; then + rm -f /.test.$$ + fsck_opts="$fsck_opts -R" + fi + fi + fi + + trap : INT QUIT + fsck ${fsck_args:--p} $fsck_opts "$@" + case $? in + 0) eend 0; return 0;; + 1) ewend 1 "Filesystems repaired"; return 0;; + 2|3) if [ "$RC_UNAME" = Linux ]; then + ewend 1 "Filesystems repaired, but reboot needed" + _reboot -f + else + ewend 1 "Filesystems still have errors;" \ + "manual fsck required" + _abort + fi;; + 4) if [ "$RC_UNAME" = Linux ]; then + ewend 1 "Fileystem errors left uncorrected, aborting" + _abort + else + ewend 1 "Filesystems repaired, but reboot needed" + _reboot + fi;; + 8) ewend 1 "Operational error"; return 0;; + 12) ewend 1 "fsck interrupted";; + *) eend 2 "Filesystems couldn't be fixed";; + esac + _abort || return 1 +} + +stop() +{ + # Fake function so we always shutdown correctly. + _abort() { return 0; } + _reboot() { return 0; } + _forcefsck() { return 1; } + + yesno $fsck_shutdown && start + return 0 +} diff --git a/init.d/hostid.in b/init.d/hostid.in new file mode 100644 index 0000000..107b7f5 --- /dev/null +++ b/init.d/hostid.in @@ -0,0 +1,88 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +extra_commands="reset" +: ${hostid_file:=/etc/hostid} + +depend() +{ + use root + after clock + before devd net + keyword -jail -prefix +} + +_set() +{ + local id=0 + + if [ -n "$1" ]; then + id=$(echo "$1" | md5) + id="0x${id%????????????????????????}" + fi + ebegin "Setting Host ID: $id" + sysctl -w kern.hostid="$id" >/dev/null + eend $? || return 1 + + if sysctl -n kern.hostuuid >/dev/null 2>&1; then + [ -n "$1" ] && id=$1 + ebegin "Setting Host UUID: $id" + sysctl kern.hostuuid="$id" >/dev/null + eend $? || return 1 + fi + +} + +# First we check to see if there is a system UUID +# If so then we use that and erase the hostid file, +# otherwise we generate a random UUID. +reset() +{ + local uuid= x="[0-9a-f]" y="$x$x$x$x" + + if command -v kenv >/dev/null 2>&1; then + uuid=$(kenv smbios.system.uuid 2>/dev/null) + fi + case "$uuid" in + $y$y-$y-$y-$y-$y$y$y);; + *) uuid=;; + esac + + if [ -n "$uuid" ]; then + rm -f "$hostid_file" + else + uuid=$(uuidgen) + if [ -z "$uuid" ]; then + eerror "Unable to generate a UUID" + return 1 + fi + if ! echo "$uuid" >"$hostid_file"; then + eerror "Failed to store UUID in \`$hostid_file'" + return 1 + fi + fi + + _set "$uuid" +} + +start() +{ + if [ -r "$hostid_file" ]; then + _set $(cat "$hostid_file") + else + reset + fi +} + +stop() +{ + _set +} diff --git a/init.d/hostname.in b/init.d/hostname.in new file mode 100644 index 0000000..6bbdf9a --- /dev/null +++ b/init.d/hostname.in @@ -0,0 +1,45 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Sets the hostname of the machine." + +depend() +{ + after clock + keyword -docker -lxc -prefix -chroot+unshare +} + +start() +{ + local h source x + if [ -s @SYSCONFDIR@/hostname ] && [ -r @SYSCONFDIR@/hostname ]; then + read h x <@SYSCONFDIR@/hostname + source="from @SYSCONFDIR@/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 (HOSTNAME var) + fi + if [ -z "$h" ]; then + einfo "Using default system hostname" + return 0 + fi + ebegin "Setting hostname to $h $source" + case $(uname -s) in + GNU/Linux|Linux) + sysctl -qw kernel.hostname="$h" + ;; + *) + hostname "$h" + ;; + esac + eend $? "Failed to set the hostname" +} diff --git a/init.d/hwclock.in b/init.d/hwclock.in new file mode 100644 index 0000000..88a21e9 --- /dev/null +++ b/init.d/hwclock.in @@ -0,0 +1,160 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +extra_commands="save show" + +description="Sets the local clock to UTC or Local Time." +description_save="Saves the current time in the BIOS." +description_show="Displays the current time in the BIOS." + +: ${clock_adjfile:=${CLOCK_ADJFILE}} +: ${clock_args:=${CLOCK_OPTS}} +: ${clock_systohc:=${CLOCK_SYSTOHC}} +: ${clock:=${CLOCK:-UTC}} +if [ "$clock" = "UTC" ]; then + utc="UTC" + utc_cmd="--utc" +else + utc="Local Time" + utc_cmd="--localtime" +fi + +depend() +{ + provide clock + want modules + if yesno $clock_adjfile; then + use root + fi + keyword -docker -lxc -openvz -prefix -chroot+unshare -uml -vserver -xenu +} + +setupopts() +{ + case "$(uname -m)" in + s390*) + utc="s390" + ;; + *) + if [ -e /proc/devices ] && \ + grep -q " cobd$" /proc/devices + then + utc="coLinux" + fi + ;; + esac + + case "$utc" in + UTC|Local" "Time);; + *) unset utc_cmd;; + esac +} + +# hwclock doesn't always return non zero on error +_hwclock() +{ + local err="$(hwclock "$@" 2>&1 >/dev/null)" + + [ -z "$err" ] && return 0 + echo "${err}" >&2 + return 1 +} + +get_noadjfile() +{ + if ! yesno $clock_adjfile; then + # Some implementations don't handle adjustments + if LC_ALL=C hwclock --help 2>&1 | grep -q "\-\-noadjfile"; then + echo --noadjfile + fi + fi +} + +rtc_exists() +{ + local rtc= + for rtc in /dev/rtc /dev/rtc[0-9]*; do + [ -e "$rtc" ] && break + done + [ -e "$rtc" ] +} + +start() +{ + local retval=0 errstr="" modname + setupopts + + if [ -z "$utc_cmd" ]; then + ewarn "Not setting clock for $utc system" + return 0 + fi + + ebegin "Setting system clock using the hardware clock [$utc]" + if [ -e /proc/modules ]; then + if ! rtc_exists; then + for x in rtc-cmos rtc genrtc; do + modprobe -q $x && rtc_exists && modname="$x" && break + done + [ -n "$modname" ] && + ewarn "The $modname module needs to be configured in" \ + "@SYSCONFDIR@/conf.d/modules or built in." + fi + fi + + # Always set the kernel's time zone. + _hwclock --systz $utc_cmd $(get_noadjfile) $clock_args + : $(( retval += $? )) + + if [ -e /etc/adjtime ] && yesno $clock_adjfile; then + _hwclock --adjust $utc_cmd $(get_noadjfile) + : $(( retval += $? )) + fi + + if yesno ${clock_hctosys:-YES}; then + _hwclock --hctosys $utc_cmd $(get_noadjfile) $clock_args + : $(( retval += $? )) + fi + + eend $retval "Failed to set the system clock" + + return 0 +} + +stop() +{ + # Don't tweak the hardware clock on LiveCD halt. + [ -n "$CDBOOT" ] && return 0 + yesno ${clock_systohc:-YES} || return 0 + + local retval=0 errstr="" + setupopts + + [ -z "$utc_cmd" ] && return 0 + + ebegin "Setting hardware clock using the system clock" "[$utc]" + + _hwclock --systohc $utc_cmd $(get_noadjfile) $clock_args + retval=$? + + eend $retval "Failed to sync clocks" +} + +save() +{ + clock_systohc=yes + stop +} + +show() +{ + setupopts + hwclock --show "$utc_cmd" $(get_noadjfile) $clock_args +} diff --git a/init.d/ipfw.in b/init.d/ipfw.in new file mode 100644 index 0000000..eabedbd --- /dev/null +++ b/init.d/ipfw.in @@ -0,0 +1,166 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +# This is based on /etc/rc.firewall and /etc/rc.firewall6 from FreeBSD + +ipfw_ip_in=${ipfw_ip_in-any} +ipfw_ports_in=${ipfw_ports_in-auth ssh} +ipfw_ports_nolog=${ipfw_ports_nolog-135-139,445 1026,1027 1433,1434} + +extra_commands="panic showstatus" + +depend() { + before net + provide firewall + keyword -jail +} + +ipfw() { + /sbin/ipfw -f -q "$@" +} + +have_ip6() { + sysctl net.ipv6 2>/dev/null +} + +init() { + # Load the kernel module + if ! sysctl net.inet.ip.fw.enable=1 >/dev/null 2>&1; then + if ! kldload ipfw; then + eend 1 "Unable to load firewall module" + return 1 + fi + fi + + # Now all rules and give a good base + ipfw flush + + ipfw add pass all from any to any via lo0 + ipfw add deny all from any to 127.0.0.0/8 + ipfw add deny ip from 127.0.0.0/8 to any + + if have_ip6; then + ipfw add pass ip6 from any to any via lo0 + ipfw add deny ip6 from any to ::1 + ipfw add deny ip6 from ::1 to any + + ipfw add pass ip6 from :: to ff02::/16 proto ipv6-icmp + ipfw add pass ip6 from fe80::/10 to fe80::/10 proto ipv6-icmp + ipfw add pass ip6 from fe80::/10 to ff02::/16 proto ipv6-icmp + fi +} + +start() { + local i= p= log= + ebegin "Starting firewall rules" + if ! init; then + eend 1 "Failed to flush firewall ruleset" + return 1 + fi + + # Use a stateful firewall + ipfw add check-state + ipfw add pass tcp from me to any established + + # Allow any connection out, adding state for each. + ipfw add pass tcp from me to any setup keep-state + ipfw add pass udp from me to any keep-state + ipfw add pass icmp from me to any keep-state + + if have_ip6; then + ipfw add pass tcp from me6 to any setup keep-state + ipfw add pass udp from me6 to any keep-state + ipfw add pass icmp from me6 to any keep-state + fi + + # Allow DHCP. + ipfw add pass udp from 0.0.0.0 68 to 255.255.255.255 67 out + ipfw add pass udp from any 67 to me 68 in + ipfw add pass udp from any 67 to 255.255.255.255 68 in + # Some servers will ping the IP while trying to decide if it's + # still in use. + ipfw add pass icmp from any to any icmptype 8 + + # Allow "mandatory" ICMP in. + ipfw add pass icmp from any to any icmptype 3,4,11 + + if have_ip6; then + # Allow ICMPv6 destination unreach + ipfw add pass ip6 from any to any icmp6types 1 proto ipv6-icmp + + # Allow NS/NA/toobig (don't filter it out) + ipfw add pass ip6 from any to any icmp6types 2,135,136 proto ipv6-icmp + fi + + # Add permits for this workstations published services below + # Only IPs and nets in firewall_allowservices is allowed in. + for i in $ipfw_ip_in; do + for p in $ipfw_ports_in; do + ipfw add pass tcp from $i to me $p + done + done + + # Allow all connections from trusted IPs. + # Playing with the content of firewall_trusted could seriously + # degrade the level of protection provided by the firewall. + for i in $ipfw_ip_trust; do + ipfw add pass ip from $i to me + done + + ipfw add 65000 count ip from any to any + + # Drop packets to ports where we don't want logging + for p in $ipfw_ports_nolog; do + ipfw add deny { tcp or udp } from any to any $p in + done + + # Broadcasts and muticasts + ipfw add deny ip from any to 255.255.255.255 + ipfw add deny ip from any to 224.0.0.0/24 + + # Noise from routers + ipfw add deny udp from any to any 520 in + + # Noise from webbrowsing. + # The stateful filter is a bit aggressive, and will cause some + # connection teardowns to be logged. + ipfw add deny tcp from any 80,443 to any 1024-65535 in + + # Deny and (if wanted) log the rest unconditionally. + if yesno ${ipfw_log_deny:-no}; then + log=log + sysctl net.inet.ip.fw.verbose=1 >/dev/null + fi + ipfw add deny $log ip from any to any + + eend 0 +} + +stop() { + ebegin "Stopping firewall rules" + # We don't unload the kernel module as that action + # can cause memory leaks as of FreeBSD 6.x + sysctl net.inet.ip.fw.enable=0 >/dev/null + eend $? +} + +panic() { + ebegin "Stopping firewall rules - hard" + if ! init; then + eend 1 "Failed to flush firewall ruleset" + return 1 + fi + eend 0 +} + +showstatus() { + ipfw show +} diff --git a/init.d/keymaps.in b/init.d/keymaps.in new file mode 100644 index 0000000..e00437f --- /dev/null +++ b/init.d/keymaps.in @@ -0,0 +1,77 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Applies a keymap for the consoles." + +depend() +{ + need localmount termencoding + after bootmisc clock + keyword -docker -lxc -openvz -prefix -chroot+unshare -uml -vserver -xenu +} + +start() +{ + ttyn=${rc_tty_number:-${RC_TTY_NUMBER:-12}} + : ${unicode:=$UNICODE} + : ${keymap:=$KEYMAP} + : ${extended_keymaps:=$EXTENDED_KEYMAPS} + : ${windowkeys:=$SET_WINDOWSKEYS} + : ${fix_euro:=$FIX_EURO} + : ${dumpkeys_charset:=${DUMPKEYS_CHARSET}} + + if [ -z "$keymap" ]; then + eerror "You need to setup keymap in /etc/conf.d/keymaps first" + return 1 + fi + + local ttydev=/dev/tty n= + [ -d /dev/vc ] && ttydev=/dev/vc/ + + # Force linux keycodes for PPC. + if [ -f /proc/sys/dev/mac_hid/keyboard_sends_linux_keycodes ]; then + echo 1 > /proc/sys/dev/mac_hid/keyboard_sends_linux_keycodes + fi + + local wkeys= kmode="-a" msg="ASCII" + if yesno $unicode; then + kmode="-u" + msg="UTF-8" + fi + yesno $windowkeys && wkeys="windowkeys" + + # Set terminal encoding to either ASCII or UNICODE. + # See utf-8(7) for more information. + ebegin "Setting keyboard mode [$msg]" + n=1 + while [ $n -le $ttyn ]; do + kbd_mode $kmode -C $ttydev$n + : $(( n += 1 )) + done + eend 0 + + ebegin "Loading key mappings [$keymap]" + loadkeys -q $wkeys $keymap $extended_keymaps + eend $? "Error loading key mappings" || return $? + + if yesno $fix_euro; then + ebegin "Fixing font for euro symbol" + # Fix some fonts displaying the Euro, #173528. + echo "altgr keycode 18 = U+20AC" | loadkeys -q - + eend $? + fi + + # Save the keymapping for use immediately at boot + if checkpath -W "$RC_LIBEXECDIR"; then + mkdir -p "$RC_LIBEXECDIR"/console + dumpkeys >"$RC_LIBEXECDIR"/console/keymap + fi +} diff --git a/init.d/killprocs.in b/init.d/killprocs.in new file mode 100644 index 0000000..fcf1364 --- /dev/null +++ b/init.d/killprocs.in @@ -0,0 +1,27 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Kill all processes so we can unmount disks cleanly." + +depend() +{ + keyword -prefix +} + +start() +{ + ebegin "Terminating remaining processes" + kill_all 15 ${killall5_opts} + eend 0 + ebegin "Killing remaining processes" + kill_all 9 ${killall5_opts} + eend 0 +} diff --git a/init.d/local.in b/init.d/local.in new file mode 100644 index 0000000..001a4fb --- /dev/null +++ b/init.d/local.in @@ -0,0 +1,93 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Executes user programs in @SYSCONFDIR@/local.d" + +depend() +{ + after * + keyword -timeout +} + +start() +{ + ebegin "Starting local" + + local file has_errors=0 redirect retval + yesno $rc_verbose || redirect='> /dev/null 2>&1' + eindent + for file in @SYSCONFDIR@/local.d/*.start; do + if [ -x "${file}" ]; then + vebegin "Executing \"${file}\"" + eval "${file}" $redirect + retval=$? + if [ ${retval} -ne 0 ]; then + has_errors=1 + fi + veend ${retval} "Execution of \"${file}\" failed." + fi + done + eoutdent + + if command -v local_start >/dev/null 2>&1; then + ewarn "\"@SYSCONFDIR@/conf.d/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\"" + 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 + # marking the local service as failed. Otherwise we are unable to + # execute any executable @SYSCONFDIR@/local.d/*.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)). + return 0 +} + +stop() +{ + ebegin "Stopping local" + + local file has_errors=0 redirect retval + yesno $rc_verbose || redirect='> /dev/null 2>&1' + eindent + for file in @SYSCONFDIR@/local.d/*.stop; do + if [ -x "${file}" ]; then + vebegin "Executing \"${file}\"" + eval "${file}" $redirect + retval=$? + if [ ${retval} -ne 0 ]; then + has_errors=1 + fi + veend ${retval} "Execution of \"${file}\" failed." + fi + done + eoutdent + + if command -v local_stop >/dev/null 2>&1; then + ewarn "\"@SYSCONFDIR@/conf.d/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\"" + local_stop + fi + + eend ${has_errors} + + # An executable @SYSCONFDIR@/local.d/*.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 +} diff --git a/init.d/localmount.in b/init.d/localmount.in new file mode 100644 index 0000000..69b718e --- /dev/null +++ b/init.d/localmount.in @@ -0,0 +1,133 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Mounts disks and swap according to /etc/fstab." + +depend() +{ + need fsck + use lvm modules root + after clock lvm modules root + keyword -docker -jail -lxc -prefix -chroot+unshare -vserver +} + +start() +{ + # Mount local filesystems in /etc/fstab. + # The types variable must start with no, and must be a type + local critical= types="noproc" x= no_netdev= rc= + for x in $net_fs_list $extra_net_fs_list; do + types="${types},${x}" + done + + if [ "$RC_UNAME" = Linux ]; then + no_netdev="-O no_netdev" + if mountinfo -q /usr; then + touch "$RC_SVCDIR"/usr_premounted + fi + fi + ebegin "Mounting local filesystems" + mount -at "$types" $no_netdev + eend $? "Some local filesystem failed to mount" + rc=$? + if [ -z "$critical_mounts" ]; then + rc=0 + else + for x in ${critical_mounts}; do + fstabinfo -q $x || continue + if ! mountinfo -q $x; then + critical=x + eerror "Failed to mount $x" + fi + done + [ -z "$critical" ] && rc=0 + fi + return $rc +} + +stop() +{ + yesno $RC_GOINGDOWN || return 0 + # We never unmount / or /dev or $RC_SVCDIR + + # Bug 381783 + local rc_svcdir=$(printf '%s\n' "$RC_SVCDIR" | sed 's:/lib\(32\|64\)\?/:/lib(32|64)?/:g') + + local x= no_umounts_r="/|/dev|/dev/.*|${rc_svcdir}" + no_umounts_r="${no_umounts_r}|/bin|/sbin|/lib(32|64)?|/libexec" + # RC_NO_UMOUNTS is an env var that can be set by plugins + local IFS="$IFS:" + for x in $no_umounts $RC_NO_UMOUNTS; do + no_umounts_r="$no_umounts_r|$x" + done + + if [ "$RC_UNAME" = Linux ]; then + no_umounts_r="$no_umounts_r|/proc|/proc/.*|/run|/sys|/sys/.*" + if [ -e "$rc_svcdir"/usr_premounted ]; then + no_umounts_r="$no_umounts_r|/usr" + fi + fi + no_umounts_r="^($no_umounts_r)$" + + # Flush all pending disk writes now + sync + + . "$RC_LIBEXECDIR"/sh/rc-mount.sh + + if [ "$RC_UNAME" = Linux ] && [ -d /sys/fs/aufs ] ; then + #if / is aufs we remount it noxino during shutdown + if mountinfo -q -f '^aufs$' / ; then + mount -o remount,noxino,rw / + sync + fi + + local aufs_branch aufs_mount_point aufs_si_id aufs_br_id branches + for aufs_si_dir in /sys/fs/aufs/si*; do + [ -d "${aufs_si_dir}" ] || continue + aufs_si_id="si=${aufs_si_dir#/sys/fs/aufs/si_}" + aufs_mount_point="$(mountinfo -o ${aufs_si_id})" + branches="$aufs_si_dir/br[0-9] $aufs_si_dir/br[0-9][0-9] $aufs_si_dir/br[0-9][0-9][0-9]" + for x in $branches; do + [ -e "${x}" ] || continue + aufs_branch=$(sed 's/=.*//g' $x) + eindent + if ! mount -o "remount,del:$aufs_branch" "$aufs_mount_point" > /dev/null 2>&1; then + ewarn "Failed to remove branch $aufs_branch from aufs" \ + "$aufs_mount_point" + fi + eoutdent + sync + done + done + fi + + # Umount loop devices + einfo "Unmounting loop devices" + eindent + do_unmount "umount -d" --skip-point-regex "$no_umounts_r" \ + --node-regex "^/dev/loop" + eoutdent + + # Now everything else, except network filesystems as the + # network should be down by this point. + einfo "Unmounting filesystems" + eindent + local fs= + for x in $net_fs_list $extra_net_fs_list; do + fs="$fs${fs:+|}$x" + done + [ -n "$fs" ] && fs="^($fs)$" + do_unmount umount --skip-point-regex "$no_umounts_r" \ + "${fs:+--skip-fstype-regex}" $fs --nonetdev + eoutdent + + return 0 +} diff --git a/init.d/loopback.in b/init.d/loopback.in new file mode 100644 index 0000000..da056d0 --- /dev/null +++ b/init.d/loopback.in @@ -0,0 +1,35 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2013-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. + +description="Configures the loopback interface." + +depend() +{ + after clock + keyword -jail -prefix -chroot+unshare -vserver +} + +start() +{ + if [ "$RC_UNAME" = Linux ]; then + ebegin "Bringing up network interface lo" + if command -v ip > /dev/null 2>&1; then + ip addr add 127.0.0.1/8 dev lo brd + + ip link set lo up + else + ifconfig lo 127.0.0.1 netmask 255.0.0.0 + fi + else + ebegin "Bringing up network interface lo0" + ifconfig lo0 127.0.0.1 netmask 255.0.0.0 + fi + eend $? +} diff --git a/init.d/mixer.in b/init.d/mixer.in new file mode 100644 index 0000000..4bfe275 --- /dev/null +++ b/init.d/mixer.in @@ -0,0 +1,54 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +extra_commands="restore" + +depend() +{ + need localmount + keyword -jail -prefix +} + +restore() +{ + local mixer= retval=0 + ebegin "Restoring mixer settings" + eindent + for mixer in /dev/mixer*; do + if [ -r "/var/db/${mixer#/dev/}-state" ]; then + vebegin "$mixer" + mixer -f "$mixer" \ + $(cat "/var/db/${mixer#/dev/}-state") >/dev/null + veend $? + : $(( retval += $? )) + fi + done +} + +start() +{ + restore +} + +stop() +{ + local mixer= retval=0 + ebegin "Saving mixer settings" + eindent + for mixer in /dev/mixer*; do + vebegin "$mixer" + mixer -f "$mixer" -s >/var/db/"${mixer#/dev/}"-state + veend $? + : $(( retval += $? )) + done + eoutdent + eend $retval +} diff --git a/init.d/modules-load.in b/init.d/modules-load.in new file mode 100644 index 0000000..ecabdf1 --- /dev/null +++ b/init.d/modules-load.in @@ -0,0 +1,72 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2016 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="Loads a list of modules from systemd-compatible locations." + +depend() +{ + keyword -docker -lxc -openvz -prefix -chroot+unshare -vserver +} + +find_modfiles() +{ + local dirs="/usr/lib/modules-load.d /run/modules-load.d /etc/modules-load.d" + local basenames files fn x y + for x in $dirs; do + [ ! -d $x ] && continue + for y in $x/*.conf; do + [ -f $y ] && basenames="${basenames}\n${y##*/}" + done + done + basenames=$(printf "$basenames" | sort -u) + for x in $basenames; do + for y in $dirs; do + [ -r $y/$x ] && + fn=$y/$x + done + files="$files $fn" + done + echo $files +} + +load_modules() +{ + local file m modules rc x + file=$1 + [ -z "$file" ] && return 0 + while read m x; do + case $m in + \;*) continue ;; + \#*) continue ;; + *) modules="$modules $m" + ;; + esac + done < $file + for x in $modules; do + ebegin "Loading module $x" + case "$RC_UNAME" in + FreeBSD) kldload "$x"; rc=$? ;; + Linux) modprobe --use-blacklist -q "$x"; rc=$? ;; + *) ;; + esac + eend $rc "Failed to load $x" + done +} + +start() +{ + local x + files=$(find_modfiles) + for x in $files; do + load_modules $x + done + return 0 +} diff --git a/init.d/modules.in b/init.d/modules.in new file mode 100644 index 0000000..94125e7 --- /dev/null +++ b/init.d/modules.in @@ -0,0 +1,95 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Loads a user defined list of kernel modules." + +modules+=$( + for _modules in $(ls /etc/modules.d); do + cat /etc/modules.d/$_modules + done + unset _modules +) + +depend() +{ + use isapnp + keyword -docker -lxc -openvz -prefix -chroot+unshare -vserver +} + +FreeBSD_modules() +{ + local cnt=0 x + for x in $modules; do + ebegin "Loading module $x" + kldload "$x" + eend $? "Failed to load $x" && : $(( cnt += 1 )) + done + einfo "Autoloaded $cnt module(s)" +} + +Linux_modules() +{ + # Should not fail if kernel does not have module + # support compiled in ... + [ ! -f /proc/modules ] && return 0 + + local KV x y kv_variant_list + KV=$(uname -r) + # full $KV + kv_variant_list="${KV}" + # remove any KV_EXTRA options to just get the full version + x=${KV%%-*} + # now slowly strip them + while [ -n "$x" ] && [ "$x" != "$y" ]; do + kv_variant_list="${kv_variant_list} $x" + y=$x + x=${x%.*} + done + + local list= x= xx= y= args= mpargs= a= + for x in $kv_variant_list ; do + eval list=\$modules_$(shell_var "$x") + [ -n "$list" ] && break + done + [ -z "$list" ] && list=$modules + + [ -n "$list" ] && ebegin "Loading kernel modules" + for x in $list; do + a=${x#*:} + if [ "$a" = "$x" ]; then + unset mpargs + else + x=${x%%:*} + mpargs="-o $a" + fi + aa=$(shell_var "$a") + xx=$(shell_var "$x") + for y in $kv_variant_list ; do + eval args=\$module_${aa}_args_$(shell_var "$y") + [ -n "${args}" ] && break + eval args=\$module_${xx}_args_$(shell_var "$y") + [ -n "${args}" ] && break + done + [ -z "$args" ] && eval args=\$module_${aa}_args + [ -z "$args" ] && eval args=\$module_${xx}_args + eval modprobe --use-blacklist --verbose "$mpargs" "$x" "$args" + done + [ -n "$list" ] && eend +} + +start() +{ + case "$RC_UNAME" in + FreeBSD|Linux) ${RC_UNAME}_modules ;; + *) ;; + esac + return 0 +} diff --git a/init.d/mount-ro.in b/init.d/mount-ro.in new file mode 100644 index 0000000..9b8a601 --- /dev/null +++ b/init.d/mount-ro.in @@ -0,0 +1,59 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Re-mount filesytems read-only for a clean reboot." + +depend() +{ + after killprocs savecache + keyword -docker -lxc -openvz -prefix -chroot+unshare -vserver +} + +start() +{ + local ret=0 + + # Flush all pending disk writes now + sync + + ebegin "Remounting remaining filesystems read-only" + # We need the do_unmount function + . "$RC_LIBEXECDIR"/sh/rc-mount.sh + eindent + + # Bug 381783 + local rc_svcdir=$(echo $RC_SVCDIR | sed 's:/lib\(32\|64\)\?/:/lib(32|64)?/:g') + + local m="/dev|/dev/.*|/proc|/proc.*|/sys|/sys/.*|/run|${rc_svcdir}" x= fs= + m="$m|/bin|/sbin|/lib(32|64)?|/libexec" + if [ -e "$rc_svcdir"/usr_premounted ]; then + m="$m|/usr" + fi + # RC_NO_UMOUNTS is an env var that can be set by plugins + local IFS="$IFS:" + for x in $no_umounts $RC_NO_UMOUNTS; do + m="$m|$x" + done + m="^($m)$" + fs= + for x in $net_fs_list $extra_net_fs_list; do + fs="$fs${fs:+|}$x" + done + [ -n "$fs" ] && fs="^($fs)$" + do_unmount "umount -r" \ + --skip-point-regex "$m" \ + "${fs:+--skip-fstype-regex}" $fs --nonetdev + ret=$? + + eoutdent + + eend $ret +} diff --git a/init.d/moused.in b/init.d/moused.in new file mode 100644 index 0000000..6301215 --- /dev/null +++ b/init.d/moused.in @@ -0,0 +1,69 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +mouse=${RC_SVCNAME##*.} +if [ -n "$mouse" -a "$mouse" != "moused" ]; then + moused_device=/dev/"$mouse" + pidfile=/var/run/moused-"$mouse".pid +else + pidfile=/var/run/moused.pid +fi +name="Console Mouse Daemon" +[ -n "$moused_device" ] && name="$name ($moused_device)" + +depend() +{ + need localmount + after bootmisc + keyword -jail -prefix +} + +start() +{ + ebegin "Starting $name" + + if [ -z "$moused_device" ]; then + local dev= + for dev in /dev/psm[0-9]* /dev/ums[0-9]*; do + [ -c "$dev" ] || continue + [ -e /var/run/moused-"${dev##*/}".pid ] && continue + moused_device=$dev + eindent + einfo "Using mouse on $moused_device" + eoutdent + break + done + fi + + if [ -z "$moused_device" ]; then + eend 1 "No mouse device found" + return 1 + fi + + local args= + eval args=\$moused_args_${moused_device##*/} + [ -z "$args" ] && args=$moused_args + + start-stop-daemon --start --exec /usr/sbin/moused \ + --pidfile "$pidfile" \ + -- $args -p "$moused_device" -I "$pidfile" + local retval=$? + + if [ $retval = 0 ]; then + local ttyv= + for ttyv in /dev/ttyv*; do + vidcontrol < "$ttyv" -m on + : $(( retval += $? )) + done + fi + + eend $retval "Failed to start moused" +} diff --git a/init.d/mtab.in b/init.d/mtab.in new file mode 100644 index 0000000..d72f724 --- /dev/null +++ b/init.d/mtab.in @@ -0,0 +1,47 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Update /etc/mtab to match what the kernel knows about" + +depend() +{ + after clock + need root + keyword -prefix -chroot+unshare +} + +start() +{ + local rc=0 + ebegin "Updating /etc/mtab" + if ! checkpath -W /etc; then + rc=1 + elif ! yesno ${mtab_is_file:-no}; then + [ ! -L /etc/mtab ] && [ -f /etc/mtab ] && + ewarn "Removing /etc/mtab file" + einfo "Creating mtab symbolic link" + ln -snf /proc/self/mounts /etc/mtab + else + [ -L /etc/mtab ] && ewarn "Removing /etc/mtab symbolic link" + rm -f /etc/mtab + einfo "Creating mtab file" + # With / as tmpfs we cannot umount -at tmpfs in localmount as that + # makes / readonly and dismounts all tmpfs even if in use which is + # not good. Luckily, umount uses /etc/mtab instead of /proc/mounts + # which allows this hack to work. + grep -v "^[! ]* / tmpfs " /proc/mounts > /etc/mtab + + # Remove stale backups + rm -f /etc/mtab~ /etc/mtab~~ + fi + eend $rc "/etc is not writable; unable to create /etc/mtab" + return 0 +} diff --git a/init.d/net-online.in b/init.d/net-online.in new file mode 100644 index 0000000..7bac89d --- /dev/null +++ b/init.d/net-online.in @@ -0,0 +1,69 @@ +#!@SBINDIR@/openrc-run +# 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. + +description="Delays until the network is online or a specific timeout" + +depend() +{ + after modules + need sysfs + provide network-online + keyword -docker -jail -lxc -openvz -prefix -chroot+unshare -uml -vserver +} + +get_interfaces() +{ + local ifname iftype + for ifname in /sys/class/net/*; do + read iftype < ${ifname}/type + [ "$iftype" = "1" ] && printf "%s " ${ifname##*/} + done +} + +start () +{ + local carriers configured dev gateway ifcount infinite + local rc state x + + ebegin "Checking to see if the network is online" + rc=0 + interfaces=${interfaces:-$(get_interfaces)} + timeout=${timeout:-120} + [ $timeout -eq 0 ] && infinite=true || infinite=false + while $infinite || [ $timeout -gt 0 ]; do + carriers=0 + configured=0 + 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)) + done + [ $configured -eq $ifcount ] && [ $carriers -ge 1 ] && break + sleep 1 + : $((timeout -= 1)) + done + ! $infinite && [ $timeout -eq 0 ] && rc=1 + include_ping_test=${include_ping_test:-${ping_default_gateway}} + if [ -n "${ping_default_gateway}" ]; then + ewarn "ping_default_gateway is deprecated, please use include_ping_test" + fi + if [ $rc -eq 0 ] && yesno ${include_ping_test:-no}; then + ping_test_host="${ping_test_host:-hyperbola.info}" + if [ -n "$ping_test_host" ]; then + ping -c 1 $ping_test_host > /dev/null 2>&1 + rc=$? + fi + fi + eend $rc "The network is offline" +} diff --git a/init.d/netmount.in b/init.d/netmount.in new file mode 100644 index 0000000..71658dd --- /dev/null +++ b/init.d/netmount.in @@ -0,0 +1,91 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Mounts network shares according to /etc/fstab." + +depend() +{ + local opts mywant="" + for opts in $(fstabinfo -o -t nfs,nfs4); do + case $opts in + noauto) ;; + *) mywant="$mywant nfsclient"; break ;; + esac + done + after root + config /etc/fstab + want $mywant + use afc-client amd openvpn + use dns + use root + keyword -docker -jail -lxc -prefix -chroot+unshare -vserver +} + +start() +{ + local x= fs= rc= + for x in $net_fs_list $extra_net_fs_list; do + fs="$fs${fs:+,}$x" + done + + ebegin "Mounting network filesystems" + mount -at $fs + rc=$? + if [ "$RC_UNAME" = Linux ] && [ $rc = 0 ]; then + mount -a -O _netdev + rc=$? + fi + ewend $rc "Could not mount all network filesystems" + if [ -z "$critical_mounts" ]; then + rc=0 + else + for x in ${critical_mounts}; do + fstabinfo -q $x || continue + if ! mountinfo -q $x; then + critical=x + eerror "Failed to mount $x" + fi + done + [ -z "$critical" ] && rc=0 + fi + return $rc +} + +stop() +{ + local x= fs= + + ebegin "Unmounting network filesystems" + . "$RC_LIBEXECDIR"/sh/rc-mount.sh + + for x in $net_fs_list $extra_net_fs_list; do + fs="$fs${fs:+,}$x" + done + if [ -n "$fs" ]; then + umount -at $fs || eerror "Failed to simply unmount filesystems" + fi + + eindent + fs= + for x in $net_fs_list $extra_net_fs_list; do + fs="$fs${fs:+|}$x" + done + [ -n "$fs" ] && fs="^($fs)$" + do_unmount umount ${fs:+--fstype-regex} $fs --netdev + retval=$? + + eoutdent + if [ "$RC_UNAME" = Linux ] && [ $retval = 0 ]; then + umount -a -O _netdev + retval=$? + fi + eend $retval "Failed to unmount network filesystems" +} diff --git a/init.d/network.in b/init.d/network.in new file mode 100644 index 0000000..56d3e7b --- /dev/null +++ b/init.d/network.in @@ -0,0 +1,360 @@ +#!@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 +# +# 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. + +# This script was inspired by the equivalent rc.d network from NetBSD. + +description="Configures network interfaces." +__nl=" +" + +depend() +{ + need localmount + after bootmisc clock + if [ -n "$(interfaces)" ]; then + provide net + fi + keyword -jail -prefix -vserver +} + +uniqify() +{ + local result= i= + for i; do + case " $result " in + *" $i "*);; + *) result="$result $i";; + esac + done + echo "${result# *}" +} + +reverse() +{ + local result= i= + for i; do + result="$i $result" + done + echo "${result# *}" +} + +sys_interfaces() +{ + case "$RC_UNAME" in + Linux) + local w= rest= i= cmd=$1 + while read w rest; do + i=${w%%:*} + case "$i" in + "$w") continue ;; + lo|lo0) continue ;; + *) ;; + esac + if [ "$cmd" = u ]; then + ifconfig "$i" | grep -q "[ ]*UP" || continue + fi + printf "%s " "$i" + done /dev/null); do + for f in /etc/ifconfig.${c}[0-9]*; do + [ -f "$f" ] && printf "%s" "$f{##*.} " + done + done + ;; + *) + for f in /etc/ifconfig.*; do + [ -f "$f" ] && printf "%s" "${f##*.} " + done + for f in /etc/ip.*; do + [ -f "$f" ] && printf "%s" "${f##*.} " + done + ;; + esac + echo +} + +interfaces() +{ + uniqify $(sys_interfaces "$@") $interfaces $(auto_interfaces) +} + +dumpargs() +{ + local f="$1" + + shift + case "$@" in + '') [ -f "$f" ] && cat "$f";; + *"$__nl"*) echo "$@";; + *) + ( + set -o noglob + IFS=';'; set -- $@ + IFS="$__nl"; echo "$*" + );; + esac +} + +intup=false +runip() +{ + local int="$1" err= + shift + + # Ensure we have a valid broadcast address + case "$@" in + *" broadcast "*|*" brd "*) ;; + *:*) ;; # Ignore IPv6 + *) set -- "$@" brd +;; + esac + + err=$(LC_ALL=C ip address add "$@" dev "$int" 2>&1) + if [ -z "$err" ]; then + # ip does not bring up the interface when adding addresses + if ! $intup; then + ip link set "$int" up + intup=true + fi + return 0 + fi + if [ "$err" = "RTNETLINK answers: File exists" ]; then + ip address del "$@" dev "$int" 2>/dev/null + fi + # Localise the error + ip address add "$@" dev "$int" +} + +routeflush() +{ + if [ "$RC_UNAME" = Linux ]; then + if [ -x /sbin/ip ] || [ -x /bin/ip ]; then + ip route flush scope global + ip route delete default 2>/dev/null + else + # Sadly we also delete some link routes, but + # this cannot be helped + local dest= gate= net= flags= rest= + route -n | while read dest gate net flags rest; do + [ -z "$net" ] && continue + case "$dest" in + [0-9]*) ;; + *) continue;; + esac + local xtra= netmask="netmask $net" + case "$flags" in + U) continue;; + *H*) flags=-host; netmask=;; + *!*) flags=-net; xtra=reject;; + *) flags=-net;; + esac + route del $flags $dest $netmask $xtra + done + # Erase any default dev eth0 routes + route del default 2>/dev/null + fi + else + route -qn flush + fi +} + +runargs() +{ + dumpargs "$@" | while read -r args; do + case "$args" in + ''|"#"*) ;; + *) + ( + eval vebegin "${args#*!}" + eval "${args#*!}" + veend $? + );; + esac + done +} + +start() +{ + local cr=0 r= int= intv= cmd= args= upcmd= + + if [ -z "$domainname" -a -s /etc/defaultdomain ]; then + domainname=$(cat /etc/defaultdomain) + fi + if [ -n "$domainname" ]; then + ebegin "Setting NIS domainname: $domainname" + domainname "$domainname" + eend $? + fi + + einfo "Starting network" + routeflush + eindent + for int in $(interfaces); do + local func= cf= + intv=$(shell_var "$int") + eval upcmd=\$ifup_$intv + for func in ip ifconfig; do + eval cmd=\$${func}_$intv + if [ -n "$cmd" -o -f /etc/"$func.$int" ]; then + cf=/etc/"$func.$int" + break + fi + done + [ -n "$cf" -o -n "$upcmd" -o \ + -f /etc/ifup."$int" -o -f "$cf" ] || continue + veinfo "$int" + case "$func" in + ip) func=runip; intup=false;; + esac + eindent + runargs /etc/ifup."$int" "$upcmd" + r=0 + dumpargs "$cf" "$cmd" | while read -r args; do + case "$args" in + ''|"#"*) ;; + "!"*) + ( + eval vebegin "${args#*!}" + eval "${args#*!}" + veend $? + );; + *) + ( + set -o noglob + eval set -- "$args" + vebegin "$@" + $func "$int" "$@" + veend $? + );; + esac + done + eoutdent + done + eoutdent + eend $cr + + # Wait for any inet6 tentative addresses + r=5 + while [ $r -gt 0 ]; do + tentative || break + [ $r = 5 ] && vebegin "Waiting for tentative addresses" + sleep 1 + : $(( r -= 1 )) + done + if [ $r != 5 ]; then + [ $r != 0 ] + veend $? + fi + + if [ -n "$defaultroute" ]; then + ebegin "Setting default route $defaultroute" + route add default $defaultroute + eend $? + elif [ -n "$defaultiproute" ]; then + ebegin "Setting default route $defaultiproute" + ip route add default $defaultiproute + eend $? + fi + + if [ -n "$defaultroute6" ]; then + ebegin "Setting default route $defaultroute6" + if [ "$RC_UNAME" = Linux ]; then + routecmd="route -A inet6 add" + else + routecmd="route -inet6 add" + fi + $routecmd default $defaultroute6 + eend $? + elif [ -n "$defaultiproute6" ]; then + ebegin "Setting default route $defaultiproute6" + ip -f inet6 route add default $defaultiproute6 + eend $? + fi + + return 0 +} + +stop() +{ + # Don't stop the network at shutdown. + # We don't use the noshutdown keyword so that we are started again + # correctly if we go back to multiuser. + yesno ${keep_network:-YES} && yesno $RC_GOINGDOWN && return 0 + + local int= intv= cmd= downcmd= r= + einfo "Stopping network" + routeflush + eindent + for int in $(reverse $(interfaces u)); do + case "$int" in + lo|lo0) continue ;; + *) ;; + esac + intv=$(shell_var "$int") + eval downcmd=\$ifdown_$intv + eval cmd=\$ip_$intv + [ -z "$cmd" ] && eval cmd=\$ifconfig_$intv + if [ -n "$cmd" -o -f /etc/ip."$int" -o \ + -f /etc/ifconfig."$int" -o \ + -n "$downcmd" -o -f /etc/ifdown."$int" ]; + then + veinfo "$int" + runargs /etc/ifdown."$int" "$downcmd" + if [ -x /sbin/ip ] || [ -x /bin/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 + fi + ifconfig "$int" down 2>/dev/null + ifconfig "$int" destroy 2>/dev/null + fi + done + eoutdent + eend 0 +} diff --git a/init.d/newsyslog.in b/init.d/newsyslog.in new file mode 100644 index 0000000..bcfff86 --- /dev/null +++ b/init.d/newsyslog.in @@ -0,0 +1,26 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +required_files="/etc/newsyslog.conf" + +depend() +{ + after clock + need localmount + keyword -prefix +} + +start() +{ + ebegin "Creating and/or trimming log files" + newsyslog -s $newsyslog_args + eend $? +} diff --git a/init.d/nscd.in b/init.d/nscd.in new file mode 100644 index 0000000..9bc8156 --- /dev/null +++ b/init.d/nscd.in @@ -0,0 +1,29 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=/usr/sbin/nscd +command_args=$nscd_args +pidfile=/var/run/nscd.pid +name="Name Service Cache Daemon" + +extra_started_commands="flush" + +depend() { + need localmount + use net dns ldap ypbind + after bootmisc +} + +flush() { + ebegin "Flushing $name" + nscd -I all >/dev/null + eend $? +} diff --git a/init.d/numlock.in b/init.d/numlock.in new file mode 100644 index 0000000..b8ed0bb --- /dev/null +++ b/init.d/numlock.in @@ -0,0 +1,49 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Turns numlock on for the consoles." + +ttyn=${rc_tty_number:-${RC_TTY_NUMBER:-12}} + +depend() +{ + need localmount + keyword -docker -lxc -openvz -prefix -chroot+unshare -vserver +} + +_setleds() +{ + [ -z "$1" ] && return 1 + + local dev=/dev/tty t= i=1 retval=0 + [ -d /dev/vc ] && dev=/dev/vc/ + + while [ $i -le $ttyn ]; do + setleds -D "$1"num < $dev$i || retval=1 + : $(( i += 1 )) + done + + return $retval +} + +start() +{ + ebegin "Enabling numlock on ttys" + _setleds + + eend $? "Failed to enable numlock" +} + +stop() +{ + ebegin "Disabling numlock on ttys" + _setleds - + eend $? "Failed to disable numlock" +} diff --git a/init.d/osclock.in b/init.d/osclock.in new file mode 100644 index 0000000..cd089af --- /dev/null +++ b/init.d/osclock.in @@ -0,0 +1,19 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2014-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. + +# Can be used on OSs that take care of the clock. + +description="Provides clock" + +depend() +{ + provide clock +} diff --git a/init.d/pf.in b/init.d/pf.in new file mode 100644 index 0000000..c79a156 --- /dev/null +++ b/init.d/pf.in @@ -0,0 +1,66 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +name="Packet Filter" +: ${pf_conf:=${pf_rules:-/etc/pf.conf}} +required_files=$pf_conf + +extra_commands="checkconfig showstatus" +extra_started_commands="reload" + +depend() { + need localmount + keyword -jail -prefix +} + +start() +{ + ebegin "Starting $name" + if command -v kldload >/dev/null 2>&1; then + kldload pf 2>/dev/null + fi + pfctl -q -F all + pfctl -q -f "$pf_conf" $pf_args + pfctl -q -e + eend $? +} + +stop() +{ + ebegin "Stopping $name" + pfctl -q -d + eend $? +} + +checkconfig() +{ + ebegin "Checking $name configuration" + pfctl -n -f "$pf_conf" + eend $? +} + +reload() +{ + ebegin "Reloading $name rules." + pfctl -q -n -f "$pf_conf" && \ + { + # Flush everything but existing state entries that way when + # rules are read in, it doesn't break established connections. + pfctl -q -Fnat -Fqueue -Frules -FSources -Finfo -FTables -Fosfp + pfctl -q -f "$pf_conf" $pf_args + } + eend $? +} + +showstatus() +{ + pfctl -s info +} diff --git a/init.d/powerd.in b/init.d/powerd.in new file mode 100644 index 0000000..673ebbb --- /dev/null +++ b/init.d/powerd.in @@ -0,0 +1,42 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=/usr/sbin/powerd +command_args=$powerd_args +pidfile=/var/run/powerd.pid +name="Power Control Daemon" + +depend() +{ + need localmount + use logger + after bootmisc + keyword -jail -prefix +} + +start_pre() +{ + if [ -n "$powerd_battery_mode" ]; then + command_args="$command_args -b $powerd_battery_mode" + fi + if [ -n "${powerd_ac_mode}" ]; then + command_args="$command_args -a $powerd_ac_mode" + fi +} + +stop_post() +{ + local level=$(sysctl -n dev.cpu.0.freq_levels | + sed -e 's:/.*::') + if [ -n "$level" ]; then + sysctl dev.cpu.0.freq="$level" >/dev/null + fi +} diff --git a/init.d/rarpd.in b/init.d/rarpd.in new file mode 100644 index 0000000..9dee6d6 --- /dev/null +++ b/init.d/rarpd.in @@ -0,0 +1,30 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=/usr/sbin/rarpd +command_args="-f $rarpd_args" +pidfile=/var/run/rarpd.pid +name="Reverse ARP Daemon" +required_files=/etc/ethers + +if [ -z "$rarpd_interface" ]; then + command_args="$command_args -a" +else + command_args="$command_args $rarpd_interface" +fi +command_background=YES + +depend() +{ + need localmount + after bootmisc + need net +} diff --git a/init.d/rc-enabled.in b/init.d/rc-enabled.in new file mode 100644 index 0000000..f00aacb --- /dev/null +++ b/init.d/rc-enabled.in @@ -0,0 +1,60 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +depend() +{ + need localmount net + after * + before local + keyword -prefix +} + +start() +{ + ebegin "Starting local rc services" + local svc= enabled= retval=0 service= pkgdir= + [ -n "@PKG_PREFIX@" ] && pkgdir="@PKG_PREFIX@/etc/rc.d/*" + for svc in $(rcorder /etc/rc.d/* $pkgdir 2>/dev/null); do + [ -x "$svc" ] || continue + service=${svc##*/} + + # Skip these services + for s in cleartmp moused; do + [ "$s" = "$service" ] && continue 2 + done + + # If we have an init script for this service, continue + rc-service --exists "$service" && continue + + # Ensure that the users rc.conf will start us + eval enabled=\$${svc##*/}_enable + yesno $enabled || yesno ${svc##*/} || continue + + # Good to go! + "$svc" start && started="$started $svc" + : $(( retval += $? )) + done + service_set_value started "$started" + eend $retval "Some local rc services failed to start" + return 0 +} + +stop() +{ + ebegin "Stopping local rc services" + local svc= retval=0 + for svc in $(rcorder $(service_get_value started) 2>/dev/null | sort -r); do + "$svc" stop + : $(( retval += $? )) + done + eend $retval "Some local rc services failed to stop" + return 0 +} diff --git a/init.d/root.in b/init.d/root.in new file mode 100644 index 0000000..756c916 --- /dev/null +++ b/init.d/root.in @@ -0,0 +1,61 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Mount the root fs read/write" + +depend() +{ + after clock + need fsck + keyword -docker -jail -lxc -openvz -prefix -chroot+unshare -vserver +} + +start() +{ + case ",$(fstabinfo -o /)," in + *,ro,*) + ;; + *) + # Check if the rootfs isn't already writable. + if checkpath -W /; then + rm -f /fastboot /forcefsck + else + ebegin "Remounting root filesystem read/write" + case "$RC_UNAME" in + Linux) + mount -n -o remount,rw / + ;; + *) + mount -u -o rw / + ;; + esac + eend $? "Root filesystem could not be mounted read/write" + if [ $? -eq 0 ]; then + rm -f /fastboot /forcefsck + fi + fi + ;; + esac + + ebegin "Remounting filesystems" + local mountpoint + for mountpoint in $(fstabinfo); do + case "${mountpoint}" in + /) + ;; + /*) + mountinfo -q "${mountpoint}" && \ + fstabinfo --remount "${mountpoint}" + ;; + esac + done + eend 0 +} diff --git a/init.d/rpcbind.in b/init.d/rpcbind.in new file mode 100644 index 0000000..b486ec0 --- /dev/null +++ b/init.d/rpcbind.in @@ -0,0 +1,28 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=/usr/sbin/rpcbind +command_args=$rpcbind_args +name="RPC program number mapper" + +depend() +{ + provide rpc + need localmount + use net logger dns + before inetd xinetd ntpd ntp-client +} + +stop_post() +{ + # rpcbind returns too fast, so sleep for a second + sleep 1 +} diff --git a/init.d/runsvdir.in b/init.d/runsvdir.in new file mode 100644 index 0000000..9b5d974 --- /dev/null +++ b/init.d/runsvdir.in @@ -0,0 +1,20 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2016 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. + +command=/usr/bin/runsvdir +command_background=yes +pidfile=/var/run/runsvdir.pid +command_args="-P $RC_SVCDIR/sv 'log: ...........................................................................................................................................................................................................................................................................................................................................................................................................'" + +start_pre() +{ + checkpath -m 0755 -o root:root -d ${RC_SVCDIR}/sv +} diff --git a/init.d/s6-svscan.in b/init.d/s6-svscan.in new file mode 100644 index 0000000..18ed0ad --- /dev/null +++ b/init.d/s6-svscan.in @@ -0,0 +1,38 @@ +#!@SBINDIR@/openrc-run +# 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. + +command=/bin/s6-svscan +command_args="${RC_SVCDIR}"/s6-scan +command_background=yes +pidfile=/var/run/s6-svscan.pid + +depend() +{ + need localmount +} + +start_pre() +{ + einfo "Creating s6 scan directory" + checkpath -d -m 0755 "$RC_SVCDIR"/s6-scan + return $? +} + +stop_post() +{ + ebegin "Stopping any remaining s6 services" + s6-svc -dx "${RC_SVCDIR}"/s6-scan/* 2>/dev/null || true + eend $? + + ebegin "Stopping any remaining s6 service loggers" + s6-svc -dx "${RC_SVCDIR}"/s6-scan/*/log 2>/dev/null || true + eend $? +} diff --git a/init.d/savecache.in b/init.d/savecache.in new file mode 100644 index 0000000..949600c --- /dev/null +++ b/init.d/savecache.in @@ -0,0 +1,66 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Saves the caches OpenRC uses to non volatile storage" + +start() +{ + if [ -e "$RC_SVCDIR"/clock-skewed ]; then + ewarn "Clock skew detected!" + if ! yesno "${RC_GOINGDOWN}"; then + eerror "Not saving deptree cache" + return 1 + fi + fi + if [ ! -d "$RC_LIBEXECDIR"/cache ]; then + if ! checkpath -W "$RC_LIBEXECDIR"; then + eerror "${RC_LIBEXECDIR} is not writable!" + eerror "Unable to save dependency cache" + if yesno "${RC_GOINGDOWN}"; then + return 0 + fi + return 1 + fi + rm -rf "$RC_LIBEXECDIR"/cache + if ! mkdir -p "$RC_LIBEXECDIR"/cache; then + eerror "Unable to create $RC_LIBEXECDIR/cache" + eerror "Unable to save dependency cache" + if yesno "${RC_GOINGDOWN}"; then + return 0 + fi + return 1 + fi + fi + if ! checkpath -W "$RC_LIBEXECDIR"/cache; then + eerror "${RC_LIBEXECDIR}/cache is not writable!" + eerror "Unable to save dependency cache" + if yesno "${RC_GOINGDOWN}"; then + return 0 + fi + return 1 + fi + ebegin "Saving dependency cache" + local rc=0 save= + for x in shutdowntime softlevel rc.log; do + [ -e "$RC_SVCDIR/$x" ] && save="$save $RC_SVCDIR/$x" + done + if [ -n "$save" ]; then + cp -p $save "$RC_LIBEXECDIR"/cache + rc=$? + fi + if yesno "${RC_GOINGDOWN}"; then + if [ $rc -ne 0 ]; then + eerror "Unable to save dependency cache" + fi + eend 0 + fi + eend $rc "Unable to save dependency cache" +} diff --git a/init.d/savecore.in b/init.d/savecore.in new file mode 100644 index 0000000..b568d5a --- /dev/null +++ b/init.d/savecore.in @@ -0,0 +1,45 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Saves a kernel dump." + +depend() +{ + need dumpon localmount + after clock + before encswap + keyword -jail -prefix +} + +start() +{ + : ${dump_dir:=/var/crash} + if ! [ -d "$dump_dir" ]; then + mkdir -p "$dump_dir" + chmod 700 "$dump_dir" + fi + + if [ "$RC_UNAME" = FreeBSD ]; then + # Don't quote ${dump_device}, so that if it's unset, + # savecore will check on the partitions listed in fstab + # without errors in the output + savecore -C $dump_device >/dev/null + else + ls "$dump_dir"/bsd* > /dev/null 2>&1 + fi + [ $? = 0 ] || return 0 + + local sopts="$dump_dir $dump_device" + yesno $dump_compress && sopts="-z $sopts" + ebegin "Saving kernel core dump in $dump_dir" + savecore $sopts >/dev/null + eend $? +} diff --git a/init.d/staticroute.in b/init.d/staticroute.in new file mode 100644 index 0000000..d970b51 --- /dev/null +++ b/init.d/staticroute.in @@ -0,0 +1,111 @@ +S#!@BINDIR@/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 +# +# 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. + +# This script was inspired by the equivalent rc.d staticroute from NetBSD. + +description="Configures static routes." +__nl=" +" +depend() +{ + after clock + provide net + use network + keyword -jail -prefix -vserver +} + +pre_flight_checks() +{ + route=route + [ -s /etc/route.conf ] && return 0 + + if [ -n "$staticiproute" ]; then + route="ip route" + staticroute="$staticiproute" + fi +} + +dump_args() +{ + # Route configuration file, as used by the NetBSD RC system + if [ -s /etc/route.conf ]; then + cat /etc/route.conf + return $? + fi + + case "$staticroute" in + *"$__nl"*) + echo "$staticroute" + ;; + *) + ( + set -o noglob + IFS=';'; set -- $staticroute + IFS="$__nl"; echo "$*" + ) + ;; + esac +} + +do_routes() +{ + local xtra= family= + [ "$RC_UNAME" != Linux ] && xtra=-q + + ebegin "$1 static routes" + eindent + pre_flight_checks + dump_args | while read args; do + [ -z "$args" ] && continue + case "$args" in + "#"*) + ;; + "+"*) + [ $2 = "add" ] && eval ${args#*+} + ;; + "-"*) + [ $2 = "del" -o $2 = "delete" ] && eval ${args#*-} + ;; + *) + veinfo "$args" + case "$route" in + "ip route") + ip route $2 $args + ;; + *) + # Linux route does cannot work it out ... + if [ "$RC_UNAME" = Linux ]; then + case "$args" in + *:*) family="-A inet6";; + *) family=;; + esac + fi + route $family $xtra $2 -$args + ;; + esac + veend $? + esac + done + eoutdent + eend 0 +} + +start() +{ + do_routes "Adding" "add" +} + +stop() +{ + local cmd="delete" + [ "$RC_UNAME" = Linux ] && cmd="del" + do_routes "Deleting" "$cmd" +} diff --git a/init.d/swap-blk.in b/init.d/swap-blk.in new file mode 100644 index 0000000..415cfaf --- /dev/null +++ b/init.d/swap-blk.in @@ -0,0 +1,31 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +depend() +{ + after clock + before fsck + keyword -jail -prefix +} + +start() +{ + ebegin "Activating block swap devices" + swapctl -A -t blk >/dev/null + eend 0 # If swapon has nothing todo it errors, so always return 0 +} + +stop() +{ + ebegin "Deactivating block swap devices" + swapctl -U -t blk >/dev/null + eend 0 +} diff --git a/init.d/swap.in b/init.d/swap.in new file mode 100644 index 0000000..f9736e3 --- /dev/null +++ b/init.d/swap.in @@ -0,0 +1,37 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +depend() +{ + after clock + before localmount + keyword -docker -jail -lxc -openvz -prefix -chroot+unshare -vserver +} + +start() +{ + ebegin "Activating swap devices" + case "$RC_UNAME" in + NetBSD|OpenBSD) swapctl -A -t noblk >/dev/null;; + *) swapon -a >/dev/null;; + esac + eend 0 # If swapon has nothing todo it errors, so always return 0 +} + +stop() +{ + ebegin "Deactivating swap devices" + case "$RC_UNAME" in + NetBSD|OpenBSD) swapctl -U -t noblk >/dev/null;; + *) swapoff -a >/dev/null;; + esac + eend 0 +} diff --git a/init.d/swclock.in b/init.d/swclock.in new file mode 100644 index 0000000..044e916 --- /dev/null +++ b/init.d/swclock.in @@ -0,0 +1,36 @@ +#!@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 +# +# 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="Sets the local clock to the mtime of a given file." + +depend() +{ + provide clock + keyword -docker -lxc -openvz -prefix -chroot+unshare -uml -vserver -xenu +} + +# swclock is an OpenRC built in + +start() +{ + ebegin "Setting the local clock based on last shutdown time" + if ! swclock 2> /dev/null; then + swclock --warn @SBINDIR@/openrc-run + fi + eend $? +} + +stop() +{ + ebegin "Saving the shutdown time" + swclock --save + eend $? +} diff --git a/init.d/syscons.in b/init.d/syscons.in new file mode 100644 index 0000000..9fde54e --- /dev/null +++ b/init.d/syscons.in @@ -0,0 +1,91 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +depend() { + after clock + need localmount + keyword -jail -prefix +} + +start() { + if [ -n "$allscreen_flags" ]; then + ebegin "Setting mode to $allscreen_flags for all screens" + for v in /dev/ttyv*; do + vidcontrol $allscreen_flags <$v + done + eend $? + fi + + if [ -n "$keymap" ]; then + ebegin "Setting keymap to $keymap" + kbdcontrol -l $keymap \`$2'" + kbdcontrol -f "$1" "$2" /dev/null || retval=1 + done < "$conf" + veend $retval + fi + done + eoutdent + return $retval +} + +Linux_sysctl() +{ + local quiet + yesno $rc_verbose || quiet=-q + + sysctl ${quiet} --system +} + +start() +{ + local rc=0 + + ebegin "Configuring kernel parameters" + case "$RC_UNAME" in + *BSD|GNU) BSD_sysctl; rc=$? ;; + Linux) Linux_sysctl; rc=$? ;; + esac + eend $rc "Unable to configure some kernel parameters" +} diff --git a/init.d/sysfs.in b/init.d/sysfs.in new file mode 100644 index 0000000..48bd6ba --- /dev/null +++ b/init.d/sysfs.in @@ -0,0 +1,162 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +description="Mount the sys filesystem." + +sysfs_opts=nodev,noexec,nosuid + +depend() +{ + keyword -docker -lxc -prefix -chroot+unshare -vserver +} + +mount_sys() +{ + grep -Eq "[[:space:]]+sysfs$" /proc/filesystems || return 1 + mountinfo -q /sys && return 0 + + if [ ! -d /sys ]; then + if ! mkdir -m 0755 /sys; then + ewarn "Could not create /sys!" + return 1 + fi + fi + + ebegin "Mounting /sys" + if ! fstabinfo --mount /sys; then + mount -n -t sysfs -o ${sysfs_opts} sysfs /sys + fi + eend $? +} + +mount_misc() +{ + # Setup Kernel Support for securityfs + if [ -d /sys/kernel/security ] && \ + ! mountinfo -q /sys/kernel/security; then + if grep -qs securityfs /proc/filesystems; then + ebegin "Mounting security filesystem" + mount -n -t securityfs -o ${sysfs_opts} \ + securityfs /sys/kernel/security + eend $? + fi + fi + + # Setup Kernel Support for debugfs + if [ -d /sys/kernel/debug ] && ! mountinfo -q /sys/kernel/debug; then + if grep -qs debugfs /proc/filesystems; then + ebegin "Mounting debug filesystem" + mount -n -t debugfs -o ${sysfs_opts} debugfs /sys/kernel/debug + eend $? + fi + fi + + # Setup Kernel Support for configfs + if [ -d /sys/kernel/config ] && ! mountinfo -q /sys/kernel/config; then + if grep -qs configfs /proc/filesystems; then + ebegin "Mounting config filesystem" + mount -n -t configfs -o ${sysfs_opts} configfs /sys/kernel/config + eend $? + fi + fi + + # set up kernel support for fusectl + if [ -d /sys/fs/fuse/connections ] \ + && ! mountinfo -q /sys/fs/fuse/connections; then + if grep -qs fusectl /proc/filesystems; then + ebegin "Mounting fuse control filesystem" + mount -n -t fusectl -o ${sysfs_opts} \ + fusectl /sys/fs/fuse/connections + eend $? + fi + fi + + # Setup Kernel Support for SELinux + if [ -d /sys/fs/selinux ] && ! mountinfo -q /sys/fs/selinux; then + if grep -qs selinuxfs /proc/filesystems; then + ebegin "Mounting SELinux filesystem" + mount -t selinuxfs selinuxfs /sys/fs/selinux + eend $? + fi + fi + + # Setup Kernel Support for persistent storage + if [ -d /sys/fs/pstore ] && ! mountinfo -q /sys/fs/pstore; then + if grep -qs 'pstore$' /proc/filesystems; then + ebegin "Mounting persistent storage (pstore) filesystem" + mount -t pstore pstore -o ${sysfs_opts} /sys/fs/pstore + eend $? + fi + fi + + # set up kernel support for efivarfs + if [ -d /sys/firmware/efi/efivars ] && + ! mountinfo -q /sys/firmware/efi/efivars; then + ebegin "Mounting efivarfs filesystem" + 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 +} + +start() +{ + mount_sys + mount_misc + mount_cgroups + restorecon_sys + return 0 +} diff --git a/init.d/syslogd.in b/init.d/syslogd.in new file mode 100644 index 0000000..504b953 --- /dev/null +++ b/init.d/syslogd.in @@ -0,0 +1,27 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=/usr/sbin/syslogd +command_args=$syslogd_args +case "$RC_UNAME" in + FreeBSD|DragonFly) pidfile=/var/run/syslog.pid;; + *) pidfile=/var/run/syslogd.pid;; +esac +name="System Logger Daemon" + +depend() +{ + provide logger + use net newsyslog + need localmount + after bootmisc clock + keyword -prefix +} diff --git a/init.d/termencoding.in b/init.d/termencoding.in new file mode 100644 index 0000000..7c6086f --- /dev/null +++ b/init.d/termencoding.in @@ -0,0 +1,55 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2008-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. + +description="Configures terminal encoding." + +ttyn=${rc_tty_number:-${RC_TTY_NUMBER:-12}} +: ${unicode:=${UNICODE}} + +depend() +{ + keyword -docker -lxc -openvz -prefix -chroot+unshare -uml -vserver -xenu + use root + after bootmisc clock +} + +start() +{ + local ttydev=/dev/tty n= + [ -d /dev/vc ] && ttydev=/dev/vc/ + + # Set terminal encoding to either ASCII or UNICODE. + # See utf-8(7) for more information. + local termencoding="%@" termmsg="ASCII" + if yesno ${unicode}; then + termencoding="%G" + termmsg="UTF-8" + fi + + ebegin "Setting terminal encoding [$termmsg]" + n=1 + while [ ${n} -le "$ttyn" ]; do + printf "\033%s" "$termencoding" >$ttydev$n + : $(( n += 1 )) + done + + # Save the encoding for use immediately at boot + if checkpath -W "$RC_LIBEXECDIR"; then + mkdir -p "$RC_LIBEXECDIR"/console + if yesno ${unicode:-${UNICODE}}; then + echo "" > "$RC_LIBEXECDIR"/console/unicode + else + rm -f "$RC_LIBEXECDIR"/console/unicode + fi + fi + + eend 0 +} diff --git a/init.d/ttys.in b/init.d/ttys.in new file mode 100644 index 0000000..e32f3e6 --- /dev/null +++ b/init.d/ttys.in @@ -0,0 +1,30 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2008-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. + +depend() +{ + after clock fsck + keyword -prefix +} + +start() +{ + ebegin "Setting tty flags" + ttyflags -a + eend $? || return $? + + if [ -c /dev/ttyp0 ]; then + chmod 666 /dev/tty[p-uw-zP-T][0-9a-zA-Z] + fi + if [ -c /dev/ttyv1 ]; then + chmod 666 /dev/ttyv[0-9a-zA-Z] + fi +} diff --git a/init.d/urandom.in b/init.d/urandom.in new file mode 100644 index 0000000..9e0e851 --- /dev/null +++ b/init.d/urandom.in @@ -0,0 +1,53 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +: ${urandom_seed:=${URANDOM_SEED:-/var/lib/misc/random-seed}} +description="Initializes the random number generator." + +depend() +{ + after clock + need localmount + keyword -docker -jail -lxc -openvz -prefix -chroot+unshare +} + +save_seed() +{ + local psz=1 + + if [ -e /proc/sys/kernel/random/poolsize ]; then + : $(( psz = $(cat /proc/sys/kernel/random/poolsize) / 4096 )) + fi + + ( # sub shell to prevent umask pollution + umask 077 + dd if=/dev/urandom of="$urandom_seed" count=${psz} 2>/dev/null + ) +} + +start() +{ + [ -c /dev/urandom ] || return + if [ -f "$urandom_seed" ]; then + ebegin "Initializing random number generator" + cat "$urandom_seed" > /dev/urandom + eend $? "Error initializing random number generator" + fi + rm -f "$urandom_seed" && save_seed + return 0 +} + +stop() +{ + ebegin "Saving random seed" + save_seed + eend $? "Failed to save random seed" +} diff --git a/init.d/wscons.in b/init.d/wscons.in new file mode 100644 index 0000000..1f4acf0 --- /dev/null +++ b/init.d/wscons.in @@ -0,0 +1,108 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +depend() +{ + after clock + need localmount + keyword -prefix +} + +start() +{ + wscfg=/usr/sbin/wsconscfg + wsfld=/usr/sbin/wsfontload + wsctl=/sbin/wsconsctl + config=/etc/wscons.conf + + # args mean: + # screen idx scr emul + # font name width height enc file + while read type arg1 arg2 arg3 arg4 arg5; do + case "$type" in + \#*|"") + continue + ;; + + font) + cmd=$wsfld + [ "$arg2" != "-" ] && cmd="$cmd -w $arg2" + [ "$arg3" != "-" ] && cmd="$cmd -h $arg3" + [ "$arg4" != "-" ] && cmd="$cmd -e $arg4" + cmd="$cmd -N $arg1 $arg5" + eval "$cmd" + ;; + + screen) + cmd=$wscfg + [ "$arg2" != "-" ] && cmd="$cmd -t $arg2" + [ "$arg3" != "-" ] && cmd="$cmd -e $arg3" + cmd="$cmd $arg1" + eval "$cmd" + ;; + + keyboard) + cmd=$wscfg + case "$arg1" in + -|auto) + cmd="$cmd -k" + ;; + *) + cmd="$cmd -k $arg1" + ;; + esac + $cmd + ;; + + encoding) + eval $wsctl -w "\"encoding=$arg1\"" + ;; + + mapfile) + local entry= + while read entry; do + case "$entry" in + \#*|"") + continue + ;; + *) + cmd="$wsctl -w \"map+=$entry\"" + eval "$cmd >/dev/null" + ;; + esac + done < "$arg1" + ;; + + mux) + eval "$wscfg -m $arg1" + ;; + + setvar) + case "$arg1" in + keyboard) + cmd="$wsctl -kw $arg2" + ;; + display) + cmd="$wsctl -dw $arg2" + ;; + mouse) + cmd="$wsctl -mw $arg2" + ;; + *) + cmd="$wsctl -w $arg1" + ;; + esac + eval "$cmd" + ;; + + esac + done < "$config" +} diff --git a/local.d/Makefile b/local.d/Makefile new file mode 100644 index 0000000..7a7d31d --- /dev/null +++ b/local.d/Makefile @@ -0,0 +1,6 @@ +DIR= ${LOCALDIR} +CONF= README + +MK= ../mk +include ${MK}/os.mk +include ${MK}/scripts.mk diff --git a/local.d/README b/local.d/README new file mode 100644 index 0000000..068b7d9 --- /dev/null +++ b/local.d/README @@ -0,0 +1,14 @@ +This directory should contain programs or scripts which are to be run +when the local service is started or stopped. + +If a file in this directory is executable and it has a .start extension, +it will be run when the local service is started. If a file is +executable and it has a .stop extension, it will be run when the local +service is stopped. + +All files are processed in lexical order. + +Keep in mind that files in this directory are processed sequentially, +and the local service is not considered started or stopped until +everything is processed, so if you have a process which takes a long +time to run, it can delay your boot or shutdown processing. diff --git a/man/Makefile b/man/Makefile new file mode 100644 index 0000000..a72b7e7 --- /dev/null +++ b/man/Makefile @@ -0,0 +1,45 @@ +MK= ../mk +include ${MK}/sys.mk +include ${MK}/os.mk + +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 + +ifeq (${OS},Linux) +MAN8 += rc-sstat.8 openrc-init.8 openrc-shutdown.8 +endif + +# Handy macro to create symlinks +# This does rely on correctly formatting our manpages! +MAKE_LINKS= suffix=$${man\#*.}; \ + prefix=$${man%%.*}; \ + for link in `sed -e 's/ ,//g' \ + -n -e '/^\.Sh NAME$$/,/\.Sh/ s/\.Nm //p' $${man}`; do \ + if test "$${link}" != "$${prefix}" ; then \ + ln -sf $${man} \ + ${DESTDIR}/${MANDIR}/man$${suffix}/$${link}.$${suffix} ; \ + fi; \ + done; + +include ${MK}/gitignore.mk + +all: + +install: + ${INSTALL} -d ${DESTDIR}/${MANDIR}/man3 + for man in ${MAN3}; do \ + ${INSTALL} -m ${MANMODE} "$$man" ${DESTDIR}/${MANDIR}/man3 || exit $$?; \ + ${MAKE_LINKS} \ + done + ${INSTALL} -d ${DESTDIR}/${MANDIR}/man8 + for man in ${MAN8}; do \ + ${INSTALL} -m ${MANMODE} "$$man" ${DESTDIR}/${MANDIR}/man8 || exit $$?; \ + ${MAKE_LINKS} \ + done + +check test:: + +clean: diff --git a/man/einfo.3 b/man/einfo.3 new file mode 100644 index 0000000..53ee2d1 --- /dev/null +++ b/man/einfo.3 @@ -0,0 +1,197 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd Mar 16, 2008 +.Dt EINFO 3 SMM +.Os OpenRC +.Sh NAME +.Nm einfo , ewarn , eerror , ebegin , +.Nm einfon , ewarnn , eerrorn , ebeginn , +.Nm einfov , ewarnv , ebeginv , +.Nm einfovn , ewarnvn , ebeginvn , +.Nm ewarnx , eerrorx , +.Nm eend , ewend , +.Nm eendv , ewendv , +.Nm ebracket , +.Nm eindent , eoutdent , +.Nm eindentv , eoutdentv , +.Nm eprefix +.Nd colorful informational output +.Sh LIBRARY +Enhanced Information output library (libeinfo, -leinfo) +.Sh SYNOPSIS +.In einfo.h +.Ft int Fn einfo "const char * restrict format" ... +.Ft int Fn ewarn "const char * restrict format" ... +.Ft int Fn eerror "const char * restrict format" ... +.Ft int Fn ebegin "const char * restrict format" ... +.Ft int Fn einfon "const char * restrict format" ... +.Ft int Fn ewarnn "const char * restrict format" ... +.Ft int Fn eerrorn "const char * restrict format" ... +.Ft int Fn ebeginn "const char * restrict format" ... +.Ft int Fn einfov "const char * restrict format" ... +.Ft int Fn ewarnv "const char * restrict format" ... +.Ft int Fn ebeginv "const char * restrict format" ... +.Ft int Fn einfovn "const char * restrict format" ... +.Ft int Fn ewarnvn "const char * restrict format" ... +.Ft int Fn ebeginvn "const char * restrict format" ... +.Ft int Fn ewarnx "const char * restrict format" ... +.Ft int Fn eerrorx "const char * restrict format" ... +.Ft int Fn eend "int retval" "const char * restrict format" ... +.Ft int Fn ewend "int retval" "const char * restrict format" ... +.Ft int Fn eendv "int retval" "const char * restrict format" ... +.Ft int Fn ewendv "int retval" "const char * restrict format" ... +.Ft void Fn ebracket "int col" "ECOLOR color" "const char * restrict msg" +.Ft void Fn eindent void +.Ft void Fn eoutdent void +.Ft void Fn eindentv void +.Ft void Fn eoutdentv void +.Ft void Fn eprefix "const char * prefix" +.Sh DESCRIPTION +The +.Fn einfo +family of functions provide a simple informational output that is colorised. +Basically +.Fn einfo , +.Fn ewarn +and +.Fn eerror +behave exactly like +.Fn printf +but prefix the output with a colored *. The function called denotes the color +used with +.Fn einfo +being green, +.Fn ewarn +being yellow and +.Fn eerror +being red. +einfo goes to stdout and the others go to stderr. +The number of real characters printed is returned. +.Fn ebegin +is identical to +.Fn einfo +except that 3 dots are appended to the output. +.Pp +.Fn einfov , +.Fn ewarnv +and +.Fn ebeginv +work the same way to +.Fn einfo , +.Fn ewarn , +and +.Fn ebegin +respectively, but only work when +.Va EINFO_VERBOSE +is true. You can also make the +.Fn einfo , +.Fn ewarn , +and +.Fn ebegin +functions silent by setting +.Va EINFO_QUIET +to true. +.Pp +These functions are designed to output a whole line, so they also +append a newline to the string. To stop this behaviour, you can use the +functions +.Fn einfon , +.Fn ewarnn , +.Fn eerrorn , +.Fn einfovn , +.Fn ewarnvn , +and +.Fn ebeginvn . +.Pp +.Fn eend , +.Fn ewend , +.Fn eendv +and +.Fn ewendv +are the counterparts to the above functions. If +.Fa retval +is zero then ok in green is printed in a bracket at the end of the prior +line. Otherwise we print the formatted string using +.Fn error +(or +.Fn ewarn +if +.Fn ewend +is called) !! in red (or yellow if +.Fn ewend +is called) is printed in a bracket at the end of the line. +The value of +.Fa retval +is returned. +.Pp +.Fn ebracket +does the same as +.Fn eend +but prints +.Fa msg +instead of ok or !! in the color +.Fa color +at the column +.Fa col . +.Pp +.Fn eindent +indents subsequent calls to the above functions by 3 characters. +.Fn eoutdent +removes an +.Fn eindent . +.Fn eindentv +and +.Fn eoutdentv +only work when +.Va EINFO_VERBOSE +is true. +.Pp +.Fn eprefix +prefixes the string +.Fa prefix +to the above functions. +.Sh IMPLEMENTATION NOTES +einfo can optionally be linked against the +.Lb libtermcap +so that we can correctly query the connected console for our color and +cursor escape codes. +If not, then we have a hard coded list of terminals we know about that support +the commonly used codes for color and cursor position. +.Sh ENVIRONMENT +.Va EINFO_QUIET +when set to true makes the +.Fn einfo +and +.Fn einfon +family of functions quiet, so nothing is printed. +.Va EERROR_QUIET +when set to true makes the +.Fn eerror +and +.Fn eerrorn +family of functions quiet, so nothing is printed. +.Pp +.Va EINFO_VERBOSE +when set to true makes the +.Fn einfov +and +.Fn einfovn +family of functions work, so they do print. +.Sh FILES +.Pa /etc/init.d/functions.sh +is provided by OpenRC, which allows shell scripts to use the above functions. +For historical reasons our verbose functions are prefixed with v instead of +suffixed. So einfov becomes veinfo, einfovn becomes veinfon. +Rinse and repeat for the other verbose functions. +.Sh SEE ALSO +.Xr printf 3 , +.Sh AUTHORS +.An Roy Marples diff --git a/man/openrc-init.8 b/man/openrc-init.8 new file mode 100644 index 0000000..8cd4d02 --- /dev/null +++ b/man/openrc-init.8 @@ -0,0 +1,46 @@ +.\" 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. +.\" +.Dd April 6, 2017 +.Dt openrc-init 8 SMM +.Os OpenRC +.Sh NAME +.Nm openrc-init +.Nd the parent of all processes +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +is an init process which can be an alternative to sysvinit or any other +init process. +.Pp +To use +.Nm +configure your boot loader to invoke it or symlink it to /sbin/init. +Also, you will need to use +.Xr openrc-shutdown 8 , +to halt, reboot or poweroff the system. +.Pp +The default runlevel is read from the init command line, the +rc_default_runlevel setting in rc.conf, the kernel command line, or it is +assumed to be "default" if it is not set in any of these places. +.Pp +.Nm +doesn't manage getty's directly, so you will need to manage them another +way. For example, you can use the agetty service script as described in +agetty-guide.md in this distribution. +.Sh BUGS +This was first released as part of OpenRC 0.25. +I do not know of any specific issues. However, since this is the first +release of openrc-init, please test and report any issues you find. +.Sh SEE ALSO +.Xr openrc-shutdown 8 , +.Sh AUTHORS +.An William Hubbs diff --git a/man/openrc-run.8 b/man/openrc-run.8 new file mode 100644 index 0000000..278aa0e --- /dev/null +++ b/man/openrc-run.8 @@ -0,0 +1,663 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd December 31, 2011 +.Dt openrc-run 8 SMM +.Os OpenRC +.Sh NAME +.Nm openrc-run +.Nd a means of hooking shell commands into a service +.Sh SYNOPSIS +.Nm +.Op Fl D , -nodeps +.Op Fl d , -debug +.Op Fl s , -ifstarted +.Op Fl S , -ifstopped +.Op Fl Z , -dry-run +.Op Ar command ... +.Sh DESCRIPTION +.Nm +is basically an interpreter for shell scripts which provides an easy interface +to the often complex system commands and daemons. +When a service runs a command it first loads its multiplexed configuration +file, then its master configuration file, then +.Pa /etc/rc.conf +and finally the script itself. At this point +.Nm +then runs the command given. +.Pp +Commands are defined as shell functions within the script. Here is a list of +some functions that all scripts have by default: +.Bl -tag -width "RC_DEFAULTLEVEL" +.It Ar describe +Describes what the service does and each command the service defines. +.It Ar start +First we ensure that any services we depend on are started. If any needed +services fail to start then we exit with a suitable error, otherwise call the +supplied start function if it exists. +.It Ar stop +First we ensure that any services that depend on us are stopped. If any +services that need us fail to stop then we exit with a suitable error, +otherwise call the supplied stop function if it exists. +.It Ar restart +Stops and starts the service, including dependencies. This cannot be +overridden. See the description of the RC_CMD variable below for the +method to make your service behave differently when restart is being +executed. +.It Ar status +Shows the status of the service. The return code matches the status, with the +exception of "started" returning 0 to match standard command behaviour. +.It Ar zap +Resets the service state to stopped and removes all saved data about the +service. +.El +.Pp +The following options affect how the service is run: +.Bl -tag -width "RC_DEFAULTLEVEL" +.It Fl d , -debug +Set xtrace on in the shell to assist in debugging. +.It Fl D , -nodeps +Ignore all dependency information the service supplies. +.It Fl s , -ifstarted +Only run the command if the service has been started. +.It Fl S , -ifstopped +Only run the command if the service has been stopped. +.It Fl q , -quiet +Turns off all informational output the service generates. +Output from any non OpenRC commands is not affected. +.It Fl v , -verbose +Turns on any extra informational output the service generates. +.It Fl Z , -dry-run +Shows which services would be stopped and/or started without actually stopping +or starting them. +.El +.Pp +The following variables affect the service script: +.Bl -tag -width "RC_DEFAULTLEVEL" +.It Ar extra_commands +Space separated list of extra commands the service defines. These should +not depend on the service being stopped or started. +.It Ar extra_started_commands +Space separated list of extra commands the service defines. These only work if +the service has already been started. +.It Ar extra_stopped_commands +Space separated list of extra commands the service defines. These only work if +the service has already been stopped. +.It Ar description +String describing the service. +.It Ar description_$command +String describing the extra command. +.It Ar supervisor +Supervisor to use to monitor this daemon. If this is unset or invalid, +start-stop-daemon will be used. +Currently, we support s6 from skarnet software, and supervise-daemon +which is a light-weight supervisor internal to OpenRC. +To use s6, set +supervisor=s6. +or set +supervisor=supervise-daemon +to use supervise-daemon. +Note that supervise-daemon is still in early development, so it is +considered experimental. +.It Ar s6_service_path +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_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. +.It Ar start_stop_daemon_args +List of arguments passed to start-stop-daemon when starting the daemon. +.It Ar command +Daemon to start or stop via +.Nm start-stop-daemon +or +.Nm supervise-daemon +if no start or stop function is defined by the service. +.It Ar command_args +List of arguments to pass to the daemon when starting via +.Nm start-stop-daemon . +.It Ar command_args_background +This variable should be used if the daemon you are starting with +.Xr start-stop-daemon 8 +runs in the foreground by default but has its own command line options +to request that it background and write a pid file. It should be set to +those options. It should not be used at the same time as +command_background, because command_background requests that +.Xr start-stop-daemon 8 +go into the background before executing the daemon. +.It Ar command_args_foreground +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 +.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 +.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 +.Xr start-stop-daemon 8 +or +.Xr supervise-daemon 8 +launches the daemon +.It Ar chroot +.Xr start-stop-daemon 8 +and +.Xr supervise-daemon 8 +will chroot into this path before writing the pid file or starting the daemon. +.It Ar pidfile +Pidfile to use for the above defined command. +.It Ar name +Display name used for the above defined command. +.It Ar procname +Process name to match when signaling the daemon. +.It Ar stopsig +Signal to send when stopping the daemon. +.It Ar respawn_delay +Respawn delay +.Xr supervise-daemon 8 +will use for this daemon. See +.Xr supervise-daemon 8 +for more information about this setting. +.It Ar respawn_max +Respawn max +.Xr supervise-daemon 8 +will use for this daemon. See +.Xr supervise-daemon 8 +for more information about this setting. +.It Ar respawn_period +Respawn period +.Xr supervise-daemon 8 +will use for this daemon. See +.Xr supervise-daemon 8 +for more information about this setting. +.It Ar retry +Retry schedule to use when stopping the daemon. It can either be a +timeout in seconds or multiple signal/timeout pairs (like SIGTERM/5). +.It Ar required_dirs +A list of directories which must exist for the service to start. +.It Ar required_files +A list of files which must exist for the service to start. +.It Ar start_inactive +Set to yes to have the service marked inactive when it starts. This is +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. +.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 +evaluated depending on how they are quoted. +.El +.Sh DEPENDENCIES +You should define a +.Ic depend +function for the service so that +.Nm +will start and stop it in the right order in relation to other services. +As it's a function it can be very flexible, see the example below. +Here is a list of the functions you can use in a +.Ic depend +function. You simply pass the names of the services you want to add to +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. +.It Ic use +The service will attempt to start any services it uses that have been added +to the runlevel. +.It Ic want +The service will attempt to start any services it wants, regardless of +whether they have been added to the runlevel. +.It Ic after +The service will start after these services and stop before these services. +.It Ic before +The service will start before these services and stop after these services. +.It Ic provide +The service provides this virtual service. For example, named provides dns. +Note that it is not legal to have a virtual and real service with the +same name. If you do this, you will receive an error message, and you +must rename either the real or virtual service. +.It Ic config +We should recalculate our dependencies if the listed files have changed. +.It Ic keyword +Tags a service with a keyword. These are the keywords we currently understand: +.Bl -tag -width indent +.It Dv -shutdown +Don't stop this service when shutting the system down. +This is normally quite safe as remaining daemons will be sent a SIGTERM just +before final shutdown. +Network related services such as the network and dhcpcd init scripts normally +have this keyword. +.It Dv -stop +Don't stop this service when changing runlevels, even if not present. +This includes shutting the system down. +.It Dv -timeout +Other services should wait indefinitely for this service to start. Use +this keyword if your service may take longer than 60 seconds to start. +.It Dv -jail +When in a jail, exclude this service from any dependencies. The service can +still be run directly. Set via +.Ic rc_sys +in +.Pa /etc/rc.conf +.It Dv -lxc +Same as -jail, but for Linux Resource Containers (LXC). +.It Dv -openvz +Same as -jail, but for OpenVZ systems. +.It Dv -prefix +Same as -jail, but for Prefix systems. +.It Dv -rkt +Same as -jail, but for RKT systems. +.It Dv -uml +Same as -jail, but for UML systems. +.It Dv -vserver +Same as -jail, but for VServer systems. +.It Dv -xen0 +Same as -jail, but for Xen DOM0 systems. +.It Dv -xenu +Same as -jail, but for Xen DOMU systems. +.It Dv -docker +Same as -jail, but for docker systems. +.It Dv -containers +Same as -jail, but for all relevant container types on the operating +system. +.El +.El +.Pp +To see how to influence dependencies in configuration files, see the +.Sx FILES +section below. +.Sh BUILTINS +.Nm +defines some builtin functions that you can use inside your service scripts: +.Bl -tag -width indent +.It Ic einfo Op Ar string +Output a green asterisk followed by the string. +.It Ic ewarn Op Ar string +Output a yellow asterisk followed by the string. +.It Ic eerror Op Ar string +Output a red asterisk followed by the string to stderr. +.It Ic ebegin Op Ar string +Same as einfo, but append 3 dots to the end. +.It Ic eend Ar retval Op Ar string +If +.Ar retval +does not equal 0 then output the string using +.Ic eerror +and !! in square brackets +at the end of the line. +Otherwise output ok in square brackets at the end of the line. +The value of +.Ar retval +is returned. +.It Ic ewend Ar retval Op Ar string +Same as +.Ic eend , +but use +.Ic ewarn +instead of +.Ic eerror . +.El +.Pp +You can prefix the above commands with the letter +.Ic v , +which means they only +output when the environment variable +.Va EINFO_VERBOSE +is true. +.Bl -tag -width indent +.It Ic ewaitfile Ar timeout Ar file1 Ar file2 ... +Wait for +.Ar timeout +seconds until all files exist. +Returns 0 if all files exist, otherwise non zero. +If +.Ar timeout +is less than 1 then we wait indefinitely. +.It Ic is_newer_than Ar file1 Ar file2 ... +If +.Ar file1 +is newer than +.Ar file2 +return 0, otherwise 1. +If +.Ar file2 +is a directory, then check all its contents too. +.It Ic is_older_than Ar file1 Ar file2 ... +If +.Ar file1 +is newer than +.Ar file2 +return 0, otherwise 1. +If +.Ar file2 +is a directory, then check all its contents too. +.It Ic service_set_value Ar name Ar value +Saves the +.Ar name +.Ar value +for later retrieval. Saved values are lost when the service stops. +.It Ic service_get_value Ar name +Returns the saved value called +.Ar name . +.It Ic service_started Op Ar service +If the service is started, return 0 otherwise 1. +.It Ic service_starting Op Ar service +If the service is starting, return 0 otherwise 1. +.It Ic service_inactive Op Ar service +If the service is inactive, return 0 otherwise 1. +.It Ic service_stopping Op Ar service +If the service is stopping, return 0 otherwise 1. +.It Ic service_stopped Op Ar service +If the service is stopped, return 0 otherwise 1. +.It Ic service_coldplugged Op Ar service +If the service is coldplugged, return 0 otherwise 1. +.It Ic service_wasinactive Op Ar service +If the service was inactive, return 0 otherwise 1. +.It Xo +.Ic service_started_daemon +.Op Ar service +.Ar daemon +.Op Ar index +.Xc +If the service has started the daemon using +.Nm start-stop-daemon , +return 0 otherwise 1. +If an index is specified, it has to be the nth daemon started by the service. +.It Ic mark_service_started Op Ar service +Mark the service as started. +.It Ic mark_service_starting Op Ar service +Mark the service as starting. +.It Ic mark_service_inactive Op Ar service +Mark the service as inactive. +.It Ic mark_service_stopping Op Ar service +Mark the service as stopping. +.It Ic mark_service_stopped Op Ar service +Mark the service as stopped. +.It Ic mark_service_coldplugged Op Ar service +Mark the service as coldplugged. +.It Ic mark_service_wasinactive Op Ar service +Mark the service as inactive. +.It Xo +.Ic checkpath +.Op Fl D , -directory-truncate +.Op Fl d , -directory +.Op Fl F , -file-truncate +.Op Fl f , -file +.Op Fl p , -pipe +.Op Fl m , -mode Ar mode +.Op Fl o , -owner Ar owner +.Op Fl W , -writable +.Op Fl q , -quiet +.Ar path ... +.Xc +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 Ic yesno Ar value +If +.Ar value +matches YES, TRUE, ON or 1 regardless of case then we return 0, otherwise 1. +.El +.Sh ENVIRONMENT +.Nm +sets the following environment variables for use in the service scripts: +.Bl -tag -width "RC_DEFAULTLEVEL" +.It Va RC_SVCNAME +Name of the service. +.It Va RC_SERVICE +Full path to the service. +.It Va RC_RUNLEVEL +Current runlevel that OpenRC is in. Note that, in OpenRC, the reboot +runlevel is mapped to the shutdown runlevel. This was done because most +services do not need to know if a system is shutting down or rebooting. +If you are writing a service that does need to know this, see the +RC_REBOOT variable. +.It Va RC_REBOOT +This variable contains YES if the system is rebooting. If your service +needs to know the system is rebooting, you should test this variable. +.It Va RC_BOOTLEVEL +Boot runlevel chosen. Default is boot. +.It Va RC_DEFAULTLEVEL +Default runlevel chosen. Default is default. +.It Va RC_SYS +A special variable to describe the system more. +Possible values are OPENVZ, XENU, XEN0, UML and VSERVER. +.It Va RC_PREFIX +In a Gentoo Prefix installation, this variable contains the prefix +offset. Otherwise it is undefined. +.It Va RC_UNAME +The result of `uname -s`. +.It Va RC_CMD +This contains the name of the command the service script is executing, such +as start, stop, restart etc. One example of using this is to make a +service script behave differently when restart is being executed. +.It Va RC_GOINGDOWN +This variable contains YES if the system is going into single user mode +or shutting down. +.It Va RC_LIBEXECDIR +The value of libexecdir which OpenRC was configured with during build +time. +.It Va RC_NO_UMOUNTS +This variable is used by plugins to contain a list of directories which +should not be unmounted. +.El +.Sh FILES +.Pp +Configuration files, relative to the location of the service. +If a file ending with .${RC_RUNLEVEL} exists then we use that instead. +.Bl -ohang +.It Pa ../conf.d/${RC_SVCNAME%%.*} +multiplexed configuration file. +Example: if ${RC_SVCNAME} is net.eth1 then look for +.Pa ../conf.d/net . +.It Pa ../conf.d/${RC_SVCNAME} +service configuration file. +.It Pa /etc/rc.conf +host configuration file. +.El +.Pp +With the exception of +.Pa /etc/rc.conf , +the configuration files can also influence the dependencies of the service +through variables. Simply prefix the name of the dependency with rc_. +Examples: +.Bd -literal -offset indent +# Whilst most services don't bind to a specific interface, our +# openvpn configuration requires a specific interface, namely bge0. +rc_need="net.bge0" +# To put it in /etc/rc.conf you would do it like this +rc_openvpn_need="net.bge0" + +# Services should not depend on the tap1 interface for network, +# but we need to add net.tap1 to the default runlevel to start it. +rc_provide="!net" +# To put it in /etc/conf.d/net you would do it like this +rc_provide_tap1="!net" +# To put in in /etc/rc.conf you would do it like this +rc_net_tap1_provide="!net" + +# It's also possible to negate keywords. This is mainly useful for prefix +# users testing OpenRC. +rc_keyword="!-prefix" +# This can also be used to block a script from runining in all +# containers except one or two +rc_keyword="!-containers !-docker" +.Ed +.Sh EXAMPLES +.Pp +An example service script for foo. +.Bd -literal -offset indent +#!/sbin/openrc-run +command=/usr/bin/foo +command_args="${foo_args} --bar" +pidfile=/var/run/foo.pid +name="FooBar Daemon" + +description="FooBar is a daemon that eats and drinks" +extra_commands="show" +extra_started_commands="drink eat" +description_drink="Opens mouth and reflexively swallows" +description_eat="Chews food in mouth" +description_show="Shows what's in the tummy" + +_need_dbus() +{ + grep -q dbus /etc/foo/plugins +} + +depend() +{ + # We write a pidfile and to /var/cache, so we need localmount. + need localmount + # We can optionally use the network, but it's not essential. + use net + # We should be after bootmisc so that /var/run is cleaned before + # we put our pidfile there. + after bootmisc + + # Foo may use a dbus plugin. + # However, if we add the dbus plugin whilst foo is running and + # stop dbus, we don't need to stop foo as foo didn't use dbus. + config /etc/foo/plugins + local _need= + if service_started; then + _need=`service_get_value need` + else + if _need_dbus; then + _need="${_need} dbus" + fi + fi + need ${_need} +} + +# This function does any pre-start setup. If it fails, the service will +# not be started. +# If you need this function to behave differently for a restart command, +# you should check the value of RC_CMD for "restart". +# This also applies to start_post, stop_pre and stop_post. +start_pre() +{ + if [ "$RC_CMD" = restart ]; then + # This block will only execute for a restart command. Use a + # structure like this if you need special processing for a + # restart which you do not need for a normal start. + # The function can also fail from here, which will mean that a + # restart can fail. + # This logic can also be used in start_post, stop_pre and + # stop_post. + fi + # Ensure that our dirs are correct + checkpath --directory --owner foo:foo --mode 0775 \\ + /var/run/foo /var/cache/foo +} + +start_post() +{ + # Save our need + if _need_dbus; then + service_set_value need dbus + fi +} + +stop_post() { + # Clean any spills + rm -rf /var/cache/foo/* +} + +drink() +{ + ebegin "Starting to drink" + ${command} --drink beer + eend $? "Failed to drink any beer :(" +} + +eat() +{ + local result=0 retval= ate= food= + ebegin "Starting to eat" + + if yesno "${foo_diet}"; then + eend 1 "We are on a diet!" + return 1 + fi + + for food in /usr/share/food/*; do + veinfo "Eating `basename ${food}`" + ${command} --eat ${food} + retval=$? + : $(( result += retval )) + [ ${retval} = 0 ] && ate="${ate} `basename ${food}`" + done + + if eend ${result} "Failed to eat all the food"; then + service_set_value ate "${ate}" + fi +} + +show() +{ + einfo "Foo has eaten: `service_get_value ate`" +} + +.Ed +.Sh BUGS +Because of the way we load our configuration files and the need to handle +more than one service directory, you can only use symlinks in service +directories to other services in the same directory. +You cannot symlink to a service in a different directory even if it is +another service directory. +.Pp +is_older_than should return 0 on success. +Instead we return 1 to be compliant with Gentoo baselayout. +Users are encouraged to use the is_newer_than function which returns correctly. +.Sh SEE ALSO +.Xr einfo 3 , +.Xr openrc 8 , +.Xr rc-status 8 , +.Xr rc-update 8 , +.Xr rc_plugin_hook 3 , +.Xr sh 1p , +.Xr start-stop-daemon 8 , +.Xr uname 1 +.Sh AUTHORS +.An Roy Marples diff --git a/man/openrc-shutdown.8 b/man/openrc-shutdown.8 new file mode 100644 index 0000000..5db2133 --- /dev/null +++ b/man/openrc-shutdown.8 @@ -0,0 +1,62 @@ +.\" 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. +.\" +.Dd May 22, 2017 +.Dt openrc-shutdown 8 SMM +.Os OpenRC +.Sh NAME +.Nm openrc-shutdown +.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 +.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 +.Xr kexec 8 . +.It Fl p , -poweroff +Stop all services, kill all processes and power off the system. +.It Fl R , -reexec +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 , +.Xr kexec 8 , +.Sh AUTHORS +.An William Hubbs diff --git a/man/openrc.8 b/man/openrc.8 new file mode 100644 index 0000000..e883392 --- /dev/null +++ b/man/openrc.8 @@ -0,0 +1,74 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd May 2, 2009 +.Dt OPENRC 8 SMM +.Os OpenRC +.Sh NAME +.Nm openrc +.Nd stops and starts services for the specified runlevel +.Sh SYNOPSIS +.Nm +.Op Fl n , -no-stop +.Op Fl o , -override +.Op Ar runlevel +.Sh DESCRIPTION +.Nm +first stops any services that are not in the specified runlevel unless +--no-stop is specified, then starts any services in the runlevel and +stacked runlevels added by +.Nm rc-update +that are not currently started. +If no runlevel is specified, we use the current runlevel. +.Pp +There are some special runlevels that you should be aware of: +.Bl -tag -width "shutdown" +.It Ar sysinit +Brings up any system specific stuff such as +.Pa /dev , +.Pa /proc +and optionally +.Pa /sys +for GNU/Linux based systems. It also mounts +.Pa /lib/rc/init.d +as a ramdisk using tmpfs where available unless / is mounted rw at boot. +.Nm +uses +.Pa /lib/rc/init.d +to hold state information about the services it runs. +sysinit always runs when the host first starts should not be run again. +.It Ar boot +Generally the only services you should add to the boot runlevel are those +which deal with the mounting of filesystems, set the initial state of attached +peripherals and logging. +Hotplugged services are added to the boot runlevel by the system. +All services in the boot and sysinit runlevels are automatically included +in all other runlevels except for those listed here. +.It Ar single +Stops all services except for those in the sysinit runlevel. +.It Ar reboot +Changes to the shutdown runlevel and then reboots the host. +.It Ar shutdown +Changes to the shutdown runlevel and then halts the host. +.El +.Pp +You should not call any of these runlevels yourself. +Instead you should use +.Xr init 8 +and +.Xr shutdown 8 +and let them call these special runlevels. +.Sh SEE ALSO +.Xr rc-status 8 , +.Xr rc-update 8 , +.Xr init 8 , +.Xr shutdown 8 +.Sh AUTHORS +.An Roy Marples Aq roy@marples.name diff --git a/man/rc-service.8 b/man/rc-service.8 new file mode 100644 index 0000000..80deb5e --- /dev/null +++ b/man/rc-service.8 @@ -0,0 +1,71 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd May 1, 2009 +.Dt RC-SERVICE 8 SMM +.Os OpenRC +.Sh NAME +.Nm rc-service +.Nd locate and run an OpenRC service with the given arguments +.Sh SYNOPSIS +.Nm +.Op Fl i , -ifexists +.Ar service cmd +.Op Ar ... +.Nm +.Op Fl I , -ifinactive +.Ar service cmd +.Op Ar ... +.Nm +.Op Fl N , -ifnotstarted +.Ar service cmd +.Op Ar ... +.Nm +.Fl e , -exists +.Ar service +.Nm +.Fl l , -list +.Nm +.Fl r , -resolve +.Ar service +.Sh DESCRIPTION +Service scripts could be in different places on different systems. +.Nm +locates the specified service and runs it with the given arguments. +If +.Fl i , -ifexists +is given then +.Nm +returns 0 even if the service does not exist. +If +.Fl I , -ifinactive +or +.Fl N , -ifnotstarted +is given then +.Nm +returns 0 if the service exists but is in the wrong state. +.Pp +If given the +.Fl l , -list +argument then +.Nm +will list all available services. +.Pp +.Fl e , -exists +return 0 if it can find +.Ar service , +otherwise -1. +.Fl r , -resolve +does the same and also prints the full path of the service to stdout. +.Sh SEE ALSO +.Xr openrc 8 , +.Xr stdout 3 +.Sh AUTHORS +.An Roy Marples diff --git a/man/rc-sstat.8 b/man/rc-sstat.8 new file mode 100644 index 0000000..855fbcc --- /dev/null +++ b/man/rc-sstat.8 @@ -0,0 +1,33 @@ +.\" 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. +.\" +.Dd April 24, 2008 +.Dt RC-sstat 8 SMM +.Os OpenRC +.Sh NAME +.Nm rc-sstat +.Nd show status info about services supervised by s6 then rc-status +info +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +gathers and displays information about the status of services supervised +by s6 then runs rc-status to show info about nnormal OpenRC services. +.Pp +.Sh EXIT STATUS +.Nm +exits 1 if there is an internal error or exits with the same exit codes +as rc-status. +.Sh SEE ALSO +.Xr rc-status 8 , +.Xr rc-update 8 +.Sh AUTHORS +.An William Hubbs diff --git a/man/rc-status.8 b/man/rc-status.8 new file mode 100644 index 0000000..6cf69a5 --- /dev/null +++ b/man/rc-status.8 @@ -0,0 +1,68 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd April 24, 2008 +.Dt RC-STATUS 8 SMM +.Os OpenRC +.Sh NAME +.Nm rc-status +.Nd show status info about runlevels +.Sh SYNOPSIS +.Nm +.Op Fl aclsuC +.Op Ar runlevel +.Sh DESCRIPTION +.Nm +gathers and displays information about the status of services +in different runlevels. The default behavior is to show information +about the current runlevel and any unassigned services that are not stopped, +but any runlevel can be quickly examined. +.Pp +If an active service is being supervised by +.Xr supervise-daemon 8, +the amount of time the daemon has been active along with the number of +times it has been respawned in the current respawn period will be +displayed. +.Pp +The options are as follows: +.Bl -tag -width ".Fl test , test string" +.It Fl a , -all +Show all runlevels and their services. +.It Fl c , -crashed +List all services that have crashed. +.It Fl l , -list +List all defined runlevels. +.It Fl m , -manual +Show all manually started services. +.It Fl r , -runlevel +Print the current runlevel name. +.It Fl s , -servicelist +Show all services. +.It Fl u , -unused +Show services not assigned to any runlevel. +.It Fl C , -nocolor +Disable color output. +.It Ar runlevel +Show information only for the named +.Ar runlevel . +.El +.Sh EXIT STATUS +.Nm +exits 0, except when checking for crashed services and it doesn't find any. +.Sh IMPLEMENTATION NOTES +.Nm +tries to list services within each runlevel in the presently resolved +dependency order if the dependency tree is available. +.Sh SEE ALSO +.Xr openrc 8 , +.Xr rc-update 8 +.Xr supervise-daemon 8 +.Sh AUTHORS +.An Roy Marples diff --git a/man/rc-update.8 b/man/rc-update.8 new file mode 100644 index 0000000..4aa2242 --- /dev/null +++ b/man/rc-update.8 @@ -0,0 +1,89 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd Jan 13, 2014 +.Dt RC-UPDATE 8 SMM +.Os OpenRC +.Sh NAME +.Nm rc-update +.Nd add and remove services to and from a runlevel +.Sh SYNOPSIS +.Nm +.Op Fl s , -stack +.Ar add +.Ar service +.Op Ar runlevel ... +.Nm +.Op Fl s , -stack +.Op Fl a , -all +.Ar delete +.Ar service +.Op Ar runlevel ... +.Nm +.Op Fl u , -update +.Op Fl v , -verbose +.Ar show +.Op Ar runlevel ... +.Sh DESCRIPTION +OpenRC uses named runlevels. Rather than editing some obscure +file or managing a directory of symlinks, +.Nm +exists to quickly add or delete services to and from from different runlevels. +All services must reside in the +.Pa /etc/init.d +or +.Pa /usr/local/etc/init.d +directories. +They must also be standard OpenRC scripts, meaning they must use +openrc-run. +.Pp +.Bl -tag -width "Fl a , -delete service" +.It Ar add Ar service +Add the +.Ar service +to the +.Ar runlevel +or the current one if none given. +Services added to the boot runlevel must exist in +.Pa /etc/init.d . +.It Ar delete Ar service +Delete the +.Ar service +from the +.Ar runlevel +or the current one if none given. +.It Ar show +Show all enabled services and the runlevels they belong to. If you specify +runlevels to show, then only those will be included in the output. +.It Fl v , -verbose +Show all services. +.It Fl u , -update +Forces an update of the dependency tree cache. +This may be needed in the event of clock skew (a file in /etc is newer than the +system clock). +.El +.Pp +If the +.Fl s , -stack +option is given then we either add or remove the runlevel from the runlevel. +This allows inheritance of runlevels. +.Pp +If the +.Fl a, -all +option is given, we remove the service from all runlevels. This is +useful, for example, to clean up the dangling symlinks after a service +is removed. +.Sh SEE ALSO +.Xr openrc 8 , +.Xr openrc-run 8 , +.Xr rc-status 8 +.Sh AUTHORS +.An Roy Marples +.An The OpenRC Team diff --git a/man/rc_config.3 b/man/rc_config.3 new file mode 100644 index 0000000..f658942 --- /dev/null +++ b/man/rc_config.3 @@ -0,0 +1,59 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd Mar 17, 2008 +.Dt RC_CONFIG 3 SMM +.Os OpenRC +.Sh NAME +.Nm rc_config_list , rc_config_load , rc_config_value , rc_yesno +.Nd functions to query OpenRC service configurations +.Sh LIBRARY +Run Command library (librc, -lrc) +.Sh SYNOPSIS +.In rc.h +.Ft "RC_STRINGLIST *" Fn rc_config_list "const char *file" +.Ft "RC_STRINGLIST *" Fn rc_config_load "const char *file" +.Ft "char *" Fn rc_config_value "const char *const *list" "const char *entry" +.Ft bool Fn rc_yesno "const char *value" +.Sh DESCRIPTION +These functions provide an easy means of querying OpenRC configuration files. +.Pp +.Fn rc_config_list +returns a list of non comment lines in +.Fa file . +.Fn rc_config_load +does the same, but attempts to parse the line as if it was +a shell assignment. +.Fn rc_config_value +returns the value of +.Fa entry +found in +.Fa list . +.Pp +Each list should be freed using +.Fn rc_stringlist_free +when done. +.Pp +.Fn rc_yesno +returns if +.Fa value +is true, yes, on or 1 regardless of case, otherwise false. +If +.Fa value +is also not false, no, off or 0 regardless of case then +.Va errno +is set to +.Va EINVAL . +.Sh SEE ALSO +.Xr malloc 3 , +.Xr rc_stringlist_free 3 , +.Xr sh 1 +.Sh AUTHORS +.An Roy Marples diff --git a/man/rc_deptree.3 b/man/rc_deptree.3 new file mode 100644 index 0000000..86a9bd3 --- /dev/null +++ b/man/rc_deptree.3 @@ -0,0 +1,99 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd Mar 16, 2008 +.Dt RC_DEPTREE 3 SMM +.Os OpenRC +.Sh NAME +.Nm rc_deptree_update , rc_deptree_update_needed , rc_deptree_load , +.Nm rc_deptree_depend , rc_deptree_depends , rc_deptree_order , +.Nm rc_deptree_free +.Nd RC dependency tree functions +.Sh LIBRARY +Run Command library (librc, -lrc) +.Sh SYNOPSIS +.In rc.h +.Ft bool Fn rc_deptree_update void +.Ft bool Fn rc_deptree_update_needed void +.Ft RC_DEPTREE Fn rc_deptree_load void +.Ft "RC_STRINGLIST *" Fo rc_deptree_depend +.Fa "const RC_DEPTREE *deptree" +.Fa "const char *type" +.Fa "const char *service" +.Fc +.Ft bool Fo rc_deptree_depends +.Fa "const RC_DEPTREE *deptree" +.Fa "const char *const *types" +.Fa "const char *const *services" +.Fa "const char *runlevel" +.Fa "int options" +.Fc +.Ft "RC_STRINGLIST *" Fo rc_deptree_order +.Fa "const RC_DEPTREE *deptree" +.Fa "const char *runlevel" +.Fa "int options" +.Fc +.Ft void Fn rc_deptree_free "RC_DEPTREE *deptree" +.Sh DESCRIPTION +These functions provide a means of querying the dependencies of OpenRC +services. +.Pp +.Fn rc_deptree_update +updates the service dependency tree, normally +.Pa /lib/rc/init.d/deptree . +.Fn rc_deptree_update_needed +checks to see if the dependency tree needs updated based on the mtime of it +compared to +.Pa /etc/init.d , +.Pa /etc/conf.d , +.Pa /usr/local/etc/init.d , +.Pa /usr/local/etc/conf.d , +.Pa /etc/rc.conf +and any files specified by a service. +.Pp +.Fn rc_deptree_load +loads the deptree and returns a pointer to it which needs to be freed by +.Fn rc_deptree_free +when done. +.Pp +.Fn rc_deptree_depend , +.Fn rc_deptree_depends +and +.Fn rc_deptree_order +return a list of services from the +.Fa deptree +based on the +.Fa type +or +.Fa types +of dependency. +.Fa options +can be a bitmask of +.Va RC_DEP_TRACE +and +.Va RC_DEP_STRICT . +.Va RC_DEP_TRACE +follows each services dependencies right down to the first service needed and +.Va RC_DEP_STRICT +only lists services actually needed or in the +.Va runlevel . +.Sh IMPLEMENTATION NOTES +Each function that returns +.Fr "RC_STRINGLIST *" +should be freed by calling +.Fn rc_stringlist_free +when done. +.Sh SEE ALSO +.Xr malloc 3 , +.Xr free 3 , +.Xr rc_stringlist_free 3 , +.Xr openrc-run 8 +.Sh AUTHORS +.An Roy Marples diff --git a/man/rc_find_pids.3 b/man/rc_find_pids.3 new file mode 100644 index 0000000..18e3f5d --- /dev/null +++ b/man/rc_find_pids.3 @@ -0,0 +1,56 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd Mar 17, 2008 +.Dt RC_FIND_PIDS 3 SMM +.Os OpenRC +.Sh NAME +.Nm rc_find_pids +.Nd finds the pids of processes that match the given criteria +.Sh LIBRARY +Run Command library (librc, -lrc) +.Sh SYNOPSIS +.In rc.h +.Ft "RC_PIDLIST *" Fo rc_find_pids +.Fa "const char *const *argv" +.Fa "const char *cmd" +.Fa "uid_t uid" +.Fa "pid_t pid" +.Fc +.Sh DESCRIPTION +.Fn rc_find_pids +returns RC_PIDLIST, a structure based on the LIST macro from +.Xr queue 3 +which contains all the pids found matching the given criteria. +If +.Fa pid +is given then only that pid is returned if it is running. Otherise we check +all instances of +.Fa argv +with a process name of +.Fa cmd +owned by +.Fa uid , +all of which are optional. +.Pp +The returned list should be freed when done. +.Sh IMPLEMENTATION NOTES +On BSD systems we use +.Lb libkvm +and on GNU/Linux systems we use the +.Pa /proc +filesystem to find our processes. +.Pp +Each RC_PID should be freed in the list as well as the list itself when done. +.Sh SEE ALSO +.Xr free 3 , +.Xr queue 3 +.Sh AUTHORS +.An Roy Marples diff --git a/man/rc_plugin_hook.3 b/man/rc_plugin_hook.3 new file mode 100644 index 0000000..1d2c941 --- /dev/null +++ b/man/rc_plugin_hook.3 @@ -0,0 +1,37 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd Mar 16, 2008 +.Dt RC_PLUGIN_HOOK 3 SMM +.Os OpenRC +.Sh NAME +.Nm rc_plugin_hook +.Nd hooks plugins into OpenRC services +.Sh LIBRARY +Run Command library (librc, -lrc) +.Sh SYNOPSIS +.In rc.h +.Ft int Fn rc_plugin_hook "RC_HOOK hook" "const char *name" +.Sh DESCRIPTION +.Fn rc_plugin_hook +is called for each shareable object found in +.Pa /lib/rc/plugins . +.Fa hook +is set to the hook running, and +.Fa name +is set to the name of the runlevel or name of the service. +.Pp +Plugins can affect the parent environment by writing NULL separated strings to +.Va rc_environ_fd . +.Sh SEE ALSO +.Xr openrc 8 , +.Xr openrc-run 8 +.Sh AUTHORS +.An Roy Marples diff --git a/man/rc_runlevel.3 b/man/rc_runlevel.3 new file mode 100644 index 0000000..47c5d21 --- /dev/null +++ b/man/rc_runlevel.3 @@ -0,0 +1,52 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd Mar 16, 2008 +.Dt RC_RUNLEVEL 3 SMM +.Os OpenRC +.Sh NAME +.Nm rc_runlevel_get , rc_runlevel_exists , rc_runlevel_list , rc_runlevel_set , +.Nm rc_runlevel_starting , rc_runlevel_stopping +.Nd RC runlevel functions +.Sh LIBRARY +Run Command library (librc, -lrc) +.Sh SYNOPSIS +.In rc.h +.Ft "char *" Fn rc_runlevel_get void +.Ft bool Fn rc_runlevel_exists +.Ft "RC_STRINGLIST *" Fn rc_runlevel_list void +.Ft bool Fn rc_runlevel_set "const char *runlevel" +.Ft bool Fn rc_runlevel_starting void +.Ft bool Fn rc_runlevel_stopping void +.Sh DESCRIPTION +These functions provide a means of querying OpenRC to find out which runlevel +we are in and what services are in which runlevel. +.Sh IMPLEMENTATION NOTES +Each function that returns +.Fr "char *" +returns a malloced NULL terminated string that should be freed when done. +.Pp +Each function that returns +.Fr "RC_STRINGLIST *" +should by freed by calling +.Fn rc_stringlist_free +when done. +.Sh FILES +.Pa /etc/init.d/functions.sh +is provided by OpenRC, which allows shell scripts to use the above functions. +For historical reasons our verbose functions are prefixed with v instead of +suffixed. So einfov becomes veinfo, einfovn becomes veinfon. +Rinse and repeat for the other verbose functions. +.Sh SEE ALSO +.Xr malloc 3 , +.Xr free 3 +.Xr rc_stringlist_free 3 +.Sh AUTHORS +.An Roy Marples diff --git a/man/rc_service.3 b/man/rc_service.3 new file mode 100644 index 0000000..591af58 --- /dev/null +++ b/man/rc_service.3 @@ -0,0 +1,216 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd Mar 16, 2008 +.Dt RC_SERVICE 3 SMM +.Os OpenRC +.Sh NAME +.Nm rc_service_add , rc_service_delete , rc_service_daemon_set , +.Nm rc_service_description , rc_service_exists , rc_service_in_runlevel , +.Nm rc_service_mark , rc_service_extra_commands , rc_service_plugable , +.Nm rc_service_resolve , rc_service_schedule_start , rc_services_scheduled_by , +.Nm rc_service_schedule_clear , rc_service_state , +.Nm rc_service_started_daemon , rc_service_value_get , rc_service_value_set , +.Nm rc_services_in_runlevel , rc_services_in_state , rc_services_scheduled , +.Nm rc_service_daemons_crashed +.Nd functions to query OpenRC services +.Sh LIBRARY +Run Command library (librc, -lrc) +.Sh SYNOPSIS +.In rc.h +.Ft bool Fn rc_service_add "const char *runlevel" "const char *service" +.Ft bool Fn rc_service_delete "const char *runlevel" "const char *service" +.Ft bool Fo rc_service_daemon_set +.Fa "const char *service" +.Fa "const char *exec" +.Fa "const char *name" +.Fa "const char *pidfile" +.Fa "bool started" +.Fc +.Ft "char *" Fo rc_service_description +.Fa "const char *service" +.Fa "const char *option" +.Fc +.Ft bool Fn rc_service_exists "const char *service" +.Ft bool Fn rc_service_in_runlevel "const char *service" "const char *runlevel" +.Ft bool Fn rc_service_mark "const char *service" "RC_SERVICE state" +.Ft "RC_STRINGLIST *" Fn rc_service_extra_commands "const char *service" +.Ft bool Fn rc_service_plugable "const char *service" +.Ft "char *" rc_service_resolve "const char *service" +.Ft bool Fo rc_service_schedule_start +.Fa "const char *service" +.Fa "const char *service_to_start" +.Fc +.Ft "RC_STRINGLIST *" Fn rc_services_scheduled_by "const char *service" +.Ft bool Fn rc_service_schedule_clear "const char *service" +.Ft RC_SERVICE Fn rc_service_state "const char *service" +.Ft bool Fo rc_service_started_daemon +.Fa "const char *service" +.Fa "const char *exec" +.Fa "int indx" +.Fc +.Ft "char *" Fn rc_service_value_get "const char *service" "const char *option" +.Ft bool Fo rc_service_value_set +.Fa "const char *service" +.Fa "const char *option" +.Fa "const char *value" +.Fc +.Ft "RC_STRINGLIST *" Fn rc_services_in_runlevel "const char *runlevel" +.Ft "RC_STRINGLIST *" Fn rc_services_in_state "RC_SERVICE state" +.Ft "RC_STRINGLIST *" Fn rc_services_scheduled "const char *service" +.Ft bool Fn rc_service_daemons_crashed "const char *service" +.Sh DESCRIPTION +These functions provide a means of querying OpenRC services to find out the +state of each one, to start and stop it, and any other functions related +to it. +.Pp +Most functions should be self descriptive as to what they do and what they +return based on names and arguments. +.Pp +.Fn rc_service_add +adds the +.Fa service +to the +.Fa runlevel . +.Pp +.Fn rc_service_delete +deletes the +.Fa service +from the +.Fa runlevel . +.Pp +.Fn rc_service_daemon_set +saves the arguments in the +.Fa service +state data so that +.Fn rc_service_daemons_crashed +can check to see if they are still running or not. +.Pp +.Fn rc_service_description +returns the +.Va description +variable of the +.Fa service . +If +.Fa option +is not null then we return the +.Fa description_$option +variable instead. +.Pp +.Fn rc_service_exists +returns true if the +.Fa service +exists, otherwise false. +.Pp +.Fn rc_service_in_runlevel +returns true if the +.Fa service +is in the +.Fa runlevel , +otherwise false. +.Pp +.Fn rc_service_mark +puts the +.Fa service +into the given +.Fa state . +If the state is RC_SERVICE_STOPPED then all data associated with the +.Fa service +is lost. +.Fn rc_service_extra_commands +returns a list of extra commands the +.Fa service +supports beyond the default ones. See +.Nm openrc-run +for default commands. +.Pp +.Fn rc_service_plugable +returns true if the service is allowed to be plugged by +.Pa rc.conf . +Default is true. +.Pp +.Fn rc_service_resolve +resolves +.Fa service +to the full path of service that was started, or would be started. +.Pp +When +.Fa service +starts, it starts +.Fa service_to_start +afterswards as directed by +.Fn rc_service_schedule_start . +.Fn rc_services_scheduled +returns a list of services that will be started when +.Fa service +starts. +.Fn rc_service_schedule_clear +clears these scheduled services for +.Fa service . +.Pp +.Fn rc_service_state returns the state of +.Fa service . +The return value is a bitmask, where more than one state can apply. +.Pp +.Fn rc_service_started_daemon +checks to see if +.Fa service +started +.Fa exec +using +.Nm start-stop-daemon . +If +.Fa indx +is greater than zero, then it must also be the nth daemon started by +.Fa service . +.Fn rc_service_value_set +saves the +.Fa value +under the name +.Fa option . +.Fn rc_service_value_get +returns the value of the saved +.Fa option . +.Pp +.Fn rc_services_in_runlevel +returns a list of services in +.Fa runlevel . +If +.Fa runlevel +is not specified, then it returns a list of all available services. +.Pp +.Fn rc_services_in_state +returns a list of all the services in +.Fa state . +.Sh IMPLEMENTATION NOTES +Each function that returns +.Fr "char *" +returns a malloced NULL terminated string that should be freed when done. +.Pp +Each function that returns +.Fr "RC_STRINGLIST *" +should be freed using +.Fn rc_stringlist_free +when done. +.Pp +When a function fails it should either return false or NULL and set +.Va errno +unless specified otherwise as above. +.Sh FILES +.Pa /lib/rc/init.d +normally holds the volatile state data for services on a RAM backed disk. +.Sh SEE ALSO +.Xr errno 3 , +.Xr malloc 3 , +.Xr free 3 +.Xr rc_stringlist_free 3 , +.Xr start-stop-daemon 8 +.Sh AUTHORS +.An Roy Marples diff --git a/man/rc_stringlist.3 b/man/rc_stringlist.3 new file mode 100644 index 0000000..ad53cd4 --- /dev/null +++ b/man/rc_stringlist.3 @@ -0,0 +1,74 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd Mar 16, 2008 +.Dt RC_STRLIST 3 SMM +.Os OpenRC +.Sh NAME +.Nm rc_stringlist_add , rc_stringlist_addu , rc_stringlist_delete , +.Nm rc_stringlist_free , rc_stringlist_new , rc_stringlist_sort +.Nd RC string list functions +.Sh LIBRARY +Run Command library (librc, -lrc) +.Sh SYNOPSIS +.In rc.h +.Ft "RC_STRINGLIST *" Fn rc_stringlist_new void +.Ft "RC_STRING *" Fn rc_stringlist_add "RC_STRINGLIST *list" "const char *item" +.Ft "RC_STRING *" Fn rc_stringlist_addu "RC_STRINGLIST *list" "const char *item" +.Ft bool Fn rc_stringlist_delete RC_STRINGLIST "const char *item" +.Ft void Fn rc_stringlist_free "RC_STRINGLIST *list" +.Ft void Fn rc_stringlist_sort "RC_STRINGLIST *list" +.Sh DESCRIPTION +These functions provide an easy means of manipulating string lists. They are +basically wrappers around TAILQ macros found in +.Xr queue 3 . +.Pp +.Fn rc_stringlist_new +creates a new list head to store the list. +.Pp +.Fn rc_stringlist_add +adds a malloced copy of +.Fa item +to +.Fa list . +It returns a pointer to the new item on success, or NULL on failure and sets +.Va errno +accordingly. +.Fn rc_stringlist_addu +only works if +.Fa list +does not already contain +.Fa item . +.Pp +.Fn rc_stringlist_delete +removes and frees +.Fa item +from +.Fa list , +retuning true on success, otherwise false. +.Pp +.Fn rc_stringlist_sort +sorts the +.Fa list +according to C locale. +.Pp +.Fn rc_stringlist_free +frees each item on +.Fa list +and the +.Fa list +itself. +.Sh SEE ALSO +.Xr malloc 3 , +.Xr free 3 , +.Xr queue 3 , +.Xr strcmp 3 +.Sh AUTHORS +.An Roy Marples diff --git a/man/service.8 b/man/service.8 new file mode 100644 index 0000000..cd9dfa8 --- /dev/null +++ b/man/service.8 @@ -0,0 +1 @@ +.so rc-service.8 diff --git a/man/start-stop-daemon.8 b/man/start-stop-daemon.8 new file mode 100644 index 0000000..1506a1a --- /dev/null +++ b/man/start-stop-daemon.8 @@ -0,0 +1,194 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd December 14, 2009 +.Dt START-STOP-DAEMON 8 SMM +.Os OpenRC +.Sh NAME +.Nm start-stop-daemon +.Nd ensures that daemons start and stop +.Sh SYNOPSIS +.Nm +.Fl S , -start +.Ar daemon +.Op Fl - +.Op Ar arguments +.Nm +.Fl K , -stop +.Ar daemon +.Nm +.Fl s , -signal +.Ar signal +.Ar daemon +.Sh DESCRIPTION +.Nm +provides a consistent method of starting, stopping and signaling daemons. +If neither +.Fl K , -stop +nor +.Fl s , -signal +are provided, then we assume we are starting the daemon. +If a daemon cannot background by itself, nor create a pidfile, +.Nm +can do it for the daemon in a secure fashion. +.Pp +If +.Nm +is used in an OpenRC service, then OpenRC can in turn check to see if the +daemon is still running. If not, then the service is marked as crashed. +.Pp +Here are the options to specify the daemon and how it should start or stop: +.Bl -tag -width indent +.It Fl x , -exec Ar daemon +The +.Ar daemon +we start or stop. +If this option is not specified, then the first non option argument +is used. +.It Fl p , -pidfile Ar pidfile +When starting, we expect the daemon to create a valid +.Ar pidfile +within a reasonable amount of time. When stopping we only stop the first pid +listed in the +.Ar pidfile . +.It Fl n , -name Ar name +Match the process +.Ar name +instead of a pidfile or executable. +.It Fl i , -interpreted +When matching process name, we should ensure that the correct interpreter +is also matched. +So if the daemon foo starts off like so +.D1 #!/usr/bin/perl -w +then +.Nm +matches the process +.D1 /usr/bin/perl -w foo +If an interpreted daemon changes its process name then this won't work. +.It Fl u , -user Ar user Ns Op : Ns Ar group +Start the daemon as the +.Ar user +and update $HOME accordingly or stop daemons +owned by the user. You can optionally append a +.Ar group +name here also. +.It Fl t , -test +Print the action(s) that would be taken, but don't actually do anything. +The return value is set as if the command was taken and worked. +.It Fl v , -verbose +Print the action(s) that are taken just before doing them. +.It Fl P , -progress +Echo a . to the console for each second elapsed whilst waiting. +.El +.Pp +These options are only used for starting daemons: +.Bl -tag -width indent +.It Fl a , -startas Ar name +Change the process name of the daemon to +.Ar name . +This just changes the first argument passed to the daemon. +.It Fl b , -background +Force the daemon into the background. Some daemons don't create pidfiles, so a +good trick is to get the daemon to run in the foreground, and use the this +option along with +.Fl m , -make-pidfile +to create a working pidfile. +.It Fl d , -chdir Ar path +chdir to this directory before starting the daemon. +.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. +.It Fl c , -chuid Ar user +Same as the +.Fl u , -user +option. +.It Fl e , -env Ar VAR=VALUE +Set the environment variable VAR to VALUE. +.It Fl g , -group Ar group +Start the daemon as in the group. +.It Fl k , -umask Ar mode +Set the umask of the daemon. +.It Fl m , -make-pidfile +Saves the pid of the daemon in the file specified by the +.Fl p , -pidfile +option. Only useful when used with daemons that run in the foreground and +forced into the background with the +.Fl -b , -background +option. +.It Fl I , -ionice Ar class Ns Op : Ns Ar data +Modifies the IO scheduling priority of the daemon. +Class can be 0 for none, 1 for real time, 2 for best effort and 3 for idle. +Data can be from 0 to 7 inclusive. +.It Fl N , -nicelevel Ar level +Modifies the scheduling priority of the daemon. +.It Fl 1 , -stdout Ar logfile +Redirect the standard output of the process to logfile when started with +.Fl background . +Must be an absolute pathname, but relative to the path optionally given with +.Fl r , -chroot . +The logfile can also be a named pipe. +.It Fl w , -wait Ar milliseconds +Wait +.Ar milliseconds +after starting and check that daemon is still running. +Useful for daemons that check configuration after forking or stopping race +conditions where the pidfile is written out after forking. +.It Fl 2 , -stderr Ar logfile +The same thing as +.Fl 1 , -stdout +but with the standard error output. +.El +.Pp +These options are only used for stopping daemons: +.Bl -tag -width indent +.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). +.El +.Sh ENVIRONMENT +.Va SSD_IONICELEVEL +can also set the IO scheduling priority of the daemon, but the command line +option takes precedence. +.Pp +.Va SSD_NICELEVEL +can also set the scheduling priority of the daemon, but the command line +option takes precedence. +.Pp +.Va SSD_STARTWAIT +As the +.Fl w , -wait option above. +.Pa /etc/rc.conf +.Nm +waits for to check the daemon is still running. +.Sh NOTE +.Nm +uses +.Xr getopt 3 +to parse its options, which allows it to accept the `--' option which will +cause it to stop processing options at that point. Any subsequent arguments +are passed as arguments to the daemon to start and used when finding a daemon +to stop or signal. +.Sh SEE ALSO +.Xr chdir 2 , +.Xr chroot 2 , +.Xr getopt 3 , +.Xr nice 2 , +.Xr rc_find_pids 3 +.Sh BUGS +.Nm +cannot stop an interpreted daemon that no longer exists without a pidfile. +.Sh HISTORY +.Nm +first appeared in Debian. +.Pp +This is a complete re-implementation with the process finding code in the +OpenRC library (librc, -lrc) so other programs can make use of it. +.Sh AUTHORS +.An Roy Marples diff --git a/man/supervise-daemon.8 b/man/supervise-daemon.8 new file mode 100644 index 0000000..34a4e93 --- /dev/null +++ b/man/supervise-daemon.8 @@ -0,0 +1,171 @@ +.\" Copyright (c) 2007-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. +.\" +.Dd April 27, 2016 +.Dt supervise-DAEMON 8 SMM +.Os OpenRC +.Sh NAME +.Nm supervise-daemon +.Nd starts a daemon and restarts it if it crashes +.Sh SYNOPSIS +.Nm +.Fl D , -respawn-delay +.Ar seconds +.Fl d , -chdir +.Ar path +.Fl e , -env +.Ar var=value +.Fl g , -group +.Ar group +.Fl I , -ionice +.Ar arg +.Fl k , -umask +.Ar value +.Fl m , -respawn-max +.Ar count +.Fl N , -nicelevel +.Ar level +.Fl p , -pidfile +.Ar pidfile +.Fl P , -respawn-period +.Ar seconds +.Fl r , -chroot +.Ar chrootpath +.Fl u , -user +.Ar user +.Fl 1 , -stdout +.Ar logfile +.Fl 2 , -stderr +.Ar logfile +.Fl S , -start +.Ar daemon +.Op Fl - +.Op Ar arguments +.Nm +.Fl K , -stop +.Ar daemon +.Fl p , -pidfile +.Ar pidfile +.Fl r , -chroot +.Ar chrootpath +.Sh DESCRIPTION +.Nm +provides a consistent method of starting, stopping and restarting +daemons. If +.Fl K , -stop +is not provided, then we assume we are starting the daemon. +.Nm +only works with daemons which do not fork. Also, it uses its own pid +file, so the daemon should not write a pid file, or the pid file passed +to +.Nm +should not be the one the daemon writes. +.Pp +Here are the options to specify the daemon and how it should start or stop: +.Bl -tag -width indent +.It Fl p , -pidfile Ar pidfile +When starting, we write a +.Ar pidfile +so we know which supervisor to stop. When stopping we only stop the pid(s) +listed in the +.Ar pidfile . +.It Fl u , -user Ar user Ns Op : Ns Ar group +Start the daemon as the +.Ar user +and update $HOME accordingly or stop daemons +owned by the user. You can optionally append a +.Ar group +name here also. +.It Fl v , -verbose +Print the action(s) that are taken just before doing them. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl D , -respawn-delay Ar seconds +wait this number of seconds before restarting a daemon after it crashes. +The default is 0. +.It Fl d , -chdir Ar path +chdir to this directory before starting the daemon. +.It Fl e , -env Ar VAR=VALUE +Set the environment variable VAR to VALUE. +.It Fl g , -group Ar group +Start the daemon as in the group. +.It Fl I , -ionice Ar class Ns Op : Ns Ar data +Modifies the IO scheduling priority of the daemon. +Class can be 0 for none, 1 for real time, 2 for best effort and 3 for idle. +Data can be from 0 to 7 inclusive. +.It Fl k , -umask Ar mode +Set the umask of the daemon. +.It Fl m , -respawn-max Ar count +Sets the maximum number of times a daemon will be respawned during a +respawn period. If a daemon dies more than this number of times during a +respawn period, +.Nm +will give up trying to respawn it and exit. The default is 10, and 0 +means unlimited. +.It Fl N , -nicelevel Ar level +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 , -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. +.It Fl u , -user Ar user +Start the daemon as the specified user. +.It Fl 1 , -stdout Ar logfile +Redirect the standard output of the process to logfile. +Must be an absolute pathname, but relative to the path optionally given with +.Fl r , -chroot . +The logfile can also be a named pipe. +.It Fl 2 , -stderr Ar logfile +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 +option takes precedence. +.Sh NOTE +.Nm +uses +.Xr getopt 3 +to parse its options, which allows it to accept the `--' option which will +cause it to stop processing options at that point. Any subsequent arguments +are passed as arguments to the daemon to start and used when finding a daemon +to stop or signal. +.Sh NOTE +If respawn-delay, respawn-max and respawn-period are not set correctly, +it is possible to trigger a situation in which the supervisor will +infinitely try to respawn a daemon. To avoid this, if you change the +values of --respawn-delay, --respawn-max or --respawn-period, always +make sure the settings mmake sense. For example, a respawn period of 5 +seconds with a respawn max of 10 and a respawn delay of 1 second leads +to infinite respawning since there can never be 10 respawns within 5 +seconds. +.Sh SEE ALSO +.Xr chdir 2 , +.Xr chroot 2 , +.Xr getopt 3 , +.Xr nice 2 , +.Xr rc_find_pids 3 +.Sh BUGS +.Nm +cannot stop an interpreted daemon that no longer exists without a pidfile. +.Sh HISTORY +.Nm +first appeared in Debian. +.Pp +This is a complete re-implementation with the process finding code in the +OpenRC library (librc, -lrc) so other programs can make use of it. +.Sh AUTHORS +.An William Hubbs diff --git a/mk/cc.mk b/mk/cc.mk new file mode 100644 index 0000000..6d47aa6 --- /dev/null +++ b/mk/cc.mk @@ -0,0 +1,44 @@ +# Copyright (c) 2008-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. + +# Setup some good default CFLAGS +CFLAGS?= -O2 -g + +# Default to using the C99 standard +CSTD?= c99 +ifneq (${CSTD},) +CFLAGS+= -std=${CSTD} +endif + +# Try and use some good cc flags if we're building from git +# We don't use -pedantic as it will warn about our perfectly valid +# use of %m in our logger. +_CCFLAGS= -Wall -Wextra -Wimplicit -Wshadow -Wformat=2 \ + -Wmissing-prototypes -Wmissing-declarations \ + -Wmissing-noreturn -Wmissing-format-attribute \ + -Wnested-externs \ + -Winline -Wwrite-strings -Wcast-align -Wcast-qual \ + -Wpointer-arith \ + -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, +# not the warning) so we just silence the warning. + +_CC_FLAGS_SH= for f in ${_CCFLAGS}; do \ + if echo "int main(void) { return 0;} " | \ + ${CC} $$f -S -xc -o /dev/null - ; \ + then printf "%s" "$$f "; fi \ + done; +_CC_FLAGS:= $(shell ${_CC_FLAGS_SH}) +CFLAGS+= ${_CC_FLAGS} + +include ${MK}/debug.mk diff --git a/mk/debug.mk b/mk/debug.mk new file mode 100644 index 0000000..5dd785b --- /dev/null +++ b/mk/debug.mk @@ -0,0 +1,23 @@ +# rules to enable debugging support +# Copyright (c) 2008-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. + +_RC_DEBUG_SH= case "${DEBUG}" in "") echo "";; *) echo "-DRC_DEBUG";; esac +_RC_DEBUG:= $(shell ${_RC_DEBUG_SH}) +CPPFLAGS+= ${_RC_DEBUG} + +# Should we enable this with a different flag? +_LD_DEBUG_SH= case "${DEBUG}" in "") echo "";; *) echo "-Wl,--rpath=../librc -Wl,--rpath=../libeinfo";; esac +_LD_DEBUG:= $(shell ${_LD_DEBUG_SH}) +LDFLAGS+= ${_LD_DEBUG} + +_GGDB_SH= case "${DEBUG}" in "") echo "";; *) echo "-ggdb";; esac +_GGDB:= $(shell ${_GGDB_SH}) +CFLAGS+= ${_GGDB} diff --git a/mk/depend.mk b/mk/depend.mk new file mode 100644 index 0000000..9dc118f --- /dev/null +++ b/mk/depend.mk @@ -0,0 +1,21 @@ +# Generate .depend +# Copyright (c) 2008-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. + +CLEANFILES+= .depend +IGNOREFILES+= .depend + +.depend: ${SRCS} + rm -f .depend + ${CC} ${LOCAL_CPPFLAGS} ${CPPFLAGS} -MM ${SRCS} > .depend + +depend: .depend extra_depend + +-include .depend diff --git a/mk/dist.mk b/mk/dist.mk new file mode 100644 index 0000000..7d6e139 --- /dev/null +++ b/mk/dist.mk @@ -0,0 +1,50 @@ +# rules to make a distribution tarball from a git repo +# Copyright (c) 2008-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. + +GITREF?= ${VERSION} +DISTPREFIX?= ${NAME}-${VERSION} +DISTFILE?= ${DISTPREFIX}.tar.gz + +CLEANFILES+= ${NAME}-*.tar.gz + +CHANGELOG_LIMIT?= --after="1 year ago" + +_SNAP_SH= date -u +%Y%m%d%H%M +_SNAP:= $(shell ${_SNAP_SH}) +SNAP= ${_SNAP} +SNAPDIR= ${DISTPREFIX}-${SNAP} +SNAPFILE= ${SNAPDIR}.tar.gz + +changelog: + git log ${CHANGELOG_LIMIT} --format=full > ChangeLog + +dist: + git archive --prefix=${DISTPREFIX}/ ${GITREF} --output=${DISTFILE} + +distcheck: dist + rm -rf ${DISTPREFIX} + tar xf ${DISTFILE} + MAKEFLAGS= $(MAKE) -C ${DISTPREFIX} + MAKEFLAGS= $(MAKE) -C ${DISTPREFIX} check + rm -rf ${DISTPREFIX} + +snapshot: + rm -rf /tmp/${SNAPDIR} + mkdir /tmp/${SNAPDIR} + cp -RPp * /tmp/${SNAPDIR} + (cd /tmp/${SNAPDIR}; make clean) + rm -rf /tmp/${SNAPDIR}/.git 2>/dev/null || true + tar -cvzpf ${SNAPFILE} -C /tmp ${SNAPDIR} + rm -rf /tmp/${SNAPDIR} + ls -l ${SNAPFILE} + +snap: snapshot + diff --git a/mk/gitignore.mk b/mk/gitignore.mk new file mode 100644 index 0000000..2929aac --- /dev/null +++ b/mk/gitignore.mk @@ -0,0 +1,22 @@ +# rules to make .gitignore files +# Copyright (c) 2008-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. + +IGNOREFILES+= ${CLEANFILES} + +.PHONY: .gitignore + +.gitignore: + @if test -n "${IGNOREFILES}"; then \ + echo "Ignoring ${IGNOREFILES}"; \ + echo ${IGNOREFILES} | tr ' ' '\n' > .gitignore; \ + fi + +ignore: .gitignore diff --git a/mk/gitver.mk b/mk/gitver.mk new file mode 100644 index 0000000..62cae5a --- /dev/null +++ b/mk/gitver.mk @@ -0,0 +1,8 @@ +_GITVER_SH= if git rev-parse --short HEAD >/dev/null 2>&1; then \ + printf "."; \ + git rev-parse --short HEAD; \ + else \ + echo ""; \ + fi +_GITVER:= $(shell ${_GITVER_SH}) +GITVER= ${_GITVER} diff --git a/mk/lib.mk b/mk/lib.mk new file mode 100644 index 0000000..efd9ea8 --- /dev/null +++ b/mk/lib.mk @@ -0,0 +1,78 @@ +# rules to build a library +# based on FreeBSD's bsd.lib.mk + +# Copyright (c) 2008-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. + +SHLIB_NAME= lib${LIB}.so.${SHLIB_MAJOR} +SHLIB_LINK= lib${LIB}.so +SONAME?= ${SHLIB_NAME} + +SOBJS+= ${SRCS:.c=.So} + +MKSTATICLIBS?= yes +ifeq (${MKSTATICLIBS},yes) +OBJS+= ${SRCS:.c=.o} +_LIBS+= lib${LIB}.a +endif + +_LIBS+= ${SHLIB_NAME} + +CLEANFILES+= ${OBJS} ${SOBJS} ${_LIBS} ${SHLIB_LINK} + +%.o: %.c + ${CC} ${LOCAL_CFLAGS} ${LOCAL_CPPFLAGS} ${CFLAGS} ${CPPFLAGS} -c $< -o $@ + +%.So: %.c + ${CC} ${PICFLAG} -DPIC ${LOCAL_CFLAGS} ${LOCAL_CPPFLAGS} ${CPPFLAGS} ${CFLAGS} -c $< -o $@ + +all: depend ${_LIBS} + +lib${LIB}.a: ${OBJS} ${STATICOBJS} + @${ECHO} building static library $@ + ${AR} rc $@ $^ + ${RANLIB} $@ + +${SHLIB_NAME}: ${VERSION_MAP} +LDFLAGS+= -Wl,--version-script=${VERSION_MAP} + +${SHLIB_NAME}: ${SOBJS} + @${ECHO} building shared library $@ + @rm -f $@ ${SHLIB_LINK} + @ln -fs $@ ${SHLIB_LINK} + ${CC} ${LOCAL_CFLAGS} ${CFLAGS} ${LOCAL_LDFLAGS} ${LDFLAGS} -shared -Wl,-x \ + -o $@ -Wl,-soname,${SONAME} \ + ${SOBJS} ${LDADD} + +install: all +ifeq (${MKSTATICLIBS},yes) + ${INSTALL} -d ${DESTDIR}${LIBDIR} + ${INSTALL} -m ${LIBMODE} lib${LIB}.a ${DESTDIR}${LIBDIR} +endif + ${INSTALL} -d ${DESTDIR}${SHLIBDIR} + ${INSTALL} -m ${LIBMODE} ${SHLIB_NAME} ${DESTDIR}${SHLIBDIR} + ln -fs ${SHLIB_NAME} ${DESTDIR}${SHLIBDIR}/${SHLIB_LINK} + ${INSTALL} -d ${DESTDIR}${INCDIR} + for x in ${INCS}; do ${INSTALL} -m ${INCMODE} $$x ${DESTDIR}${INCDIR}; done + +check test:: + +clean: + rm -f ${OBJS} ${SOBJS} ${_LIBS} ${SHLIB_LINK} ${CLEANFILES} + +extra_depend: + @TMP=depend.$$$$; \ + ${SED} -e 's/^\([^\.]*\).o[ ]*:/\1.o \1.So:/' .depend > $${TMP}; \ + mv $${TMP} .depend + +include ${MK}/sys.mk +include ${MK}/os.mk +include ${MK}/depend.mk +include ${MK}/gitignore.mk diff --git a/mk/net.mk b/mk/net.mk new file mode 100644 index 0000000..abc198c --- /dev/null +++ b/mk/net.mk @@ -0,0 +1 @@ +MKNET?= yes diff --git a/mk/os-BSD.mk b/mk/os-BSD.mk new file mode 100644 index 0000000..49f7a6b --- /dev/null +++ b/mk/os-BSD.mk @@ -0,0 +1,16 @@ +# Copyright (c) 2008-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. + +# Generic definitions + +PKG_PREFIX?= /usr/local +SFX= .BSD.in + +LIBKVM?= -lkvm diff --git a/mk/os-DragonFly.mk b/mk/os-DragonFly.mk new file mode 100644 index 0000000..e0a64c9 --- /dev/null +++ b/mk/os-DragonFly.mk @@ -0,0 +1,13 @@ +# Copyright (c) 2013-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. + +# Generic definitions + +include ${MK}/os-BSD.mk diff --git a/mk/os-FreeBSD.mk b/mk/os-FreeBSD.mk new file mode 100644 index 0000000..0e4fd10 --- /dev/null +++ b/mk/os-FreeBSD.mk @@ -0,0 +1,13 @@ +# Copyright (c) 2008-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. + +# Generic definitions + +include ${MK}/os-BSD.mk diff --git a/mk/os-GNU-kFreeBSD.mk b/mk/os-GNU-kFreeBSD.mk new file mode 100644 index 0000000..4fc934e --- /dev/null +++ b/mk/os-GNU-kFreeBSD.mk @@ -0,0 +1,18 @@ +# Copyright (c) 2013-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. + +# Generic definitions + +SFX= .GNU-kFreeBSD.in +PKG_PREFIX?= /usr + +CPPFLAGS+= -D_BSD_SOURCE -D_XOPEN_SOURCE=700 +LIBDL= -Wl,-Bdynamic -ldl +LIBKVM?= diff --git a/mk/os-GNU.mk b/mk/os-GNU.mk new file mode 100644 index 0000000..d5c4172 --- /dev/null +++ b/mk/os-GNU.mk @@ -0,0 +1,15 @@ +# Copyright (c) 2007-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. + +SFX= .GNU.in +PKG_PREFIX?= /usr + +CPPFLAGS+= -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700 -DMAXPATHLEN=4096 -DPATH_MAX=4096 +LIBDL= -Wl,-Bdynamic -ldl diff --git a/mk/os-Linux.mk b/mk/os-Linux.mk new file mode 100644 index 0000000..3a3c516 --- /dev/null +++ b/mk/os-Linux.mk @@ -0,0 +1,34 @@ +# Copyright (c) 2008-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. + +SFX= .Linux.in +PKG_PREFIX?= /usr + +CPPFLAGS+= -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700 +LIBDL= -Wl,-Bdynamic -ldl + +ifeq (${MKSELINUX},yes) +CPPFLAGS+= -DHAVE_SELINUX +LIBSELINUX?= -lselinux +LDADD += $(LIBSELINUX) + +ifneq (${MKPAM},pam) +# if using selinux but not pam then we need crypt +LIBCRYPT?= -lcrypt +LDADD += $(LIBCRYPT) +endif + +endif + +ifeq (${MKAUDIT},yes) +LIBAUDIT?= -laudit +CPPFLAGS+= -DHAVE_AUDIT +LDADD+= ${LIBAUDIT} +endif diff --git a/mk/os-NetBSD.mk b/mk/os-NetBSD.mk new file mode 100644 index 0000000..2bd2643 --- /dev/null +++ b/mk/os-NetBSD.mk @@ -0,0 +1,14 @@ +# Copyright (c) 2008-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. + +# Generic definitions + +PKG_PREFIX?= /usr/pkg +include ${MK}/os-BSD.mk diff --git a/mk/os-prefix.mk b/mk/os-prefix.mk new file mode 100644 index 0000000..af08c99 --- /dev/null +++ b/mk/os-prefix.mk @@ -0,0 +1,8 @@ +# Copyright (c) 2012 William Hubbs +# Released under the 2-clause BSD license. + +ifeq (${MKPREFIX},yes) +CPPFLAGS+= -DPREFIX +PKG_PREFIX?= $(PREFIX)/usr +SED_EXTRA= -e '/_PATH=.*usr.bin/d' +endif diff --git a/mk/os.mk b/mk/os.mk new file mode 100644 index 0000000..bac0388 --- /dev/null +++ b/mk/os.mk @@ -0,0 +1,19 @@ +# Copyright (c) 2008-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. + +# Generic definitions + +_OS_SH= uname -s | tr '/' '-' +_OS:= $(shell ${_OS_SH}) +OS?= ${_OS} +include ${MK}/os-prefix.mk +include ${MK}/os-${OS}.mk + +RC_LIB= /$(LIBNAME)/rc diff --git a/mk/pam.mk b/mk/pam.mk new file mode 100644 index 0000000..199896c --- /dev/null +++ b/mk/pam.mk @@ -0,0 +1,16 @@ +ifeq (${MKPAM},pam) +LIBPAM?= -lpam +CPPFLAGS+= -DHAVE_PAM +LDADD+= ${LIBPAM} + +ifeq (${MKSELINUX},yes) +# with selinux, pam_misc is needed too +LIBPAM_MISC?= -lpam_misc +LDADD+= ${LIBPAM_MISC} +endif + +PAMDIR?= /etc/pam.d +PAMMODE?= 0644 +else ifneq (${MKPAM},) +$(error if MKPAM is defined, it must be "pam") +endif diff --git a/mk/prog.mk b/mk/prog.mk new file mode 100644 index 0000000..a1573fa --- /dev/null +++ b/mk/prog.mk @@ -0,0 +1,45 @@ +# rules to build a program +# based on FreeBSD's bsd.prog.mk + +# Copyright (c) 2008-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. + +OBJS+= ${SRCS:.c=.o} + +# This is currently hardcoded for NetBSD which has two dynamic linkers +# and we need to use the one in /libexec instead of /usr/libexec +_DYNLINK_SH= if test "${PREFIX}" = "" && test -e /libexec/ld.elf_so; then \ + echo "-Wl,-dynamic-linker=/libexec/ld.elf_so"; \ + else \ + echo ""; \ + fi +_DYNLINK:= $(shell ${_DYNLINK_SH}) +LDFLAGS+= ${_DYNLINK} +LDFLAGS+= ${PROGLDFLAGS} + +CLEANFILES+= ${OBJS} ${PROG} + +all: depend ${PROG} + +%.o: %.c + ${CC} ${LOCAL_CFLAGS} ${LOCAL_CPPFLAGS} ${CFLAGS} ${CPPFLAGS} -c $< -o $@ + +${PROG}: ${SCRIPTS} ${OBJS} + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJS} ${LDADD} + +clean: + rm -f ${CLEANFILES} + +extra_depend: + +include ${MK}/sys.mk +include ${MK}/os.mk +include ${MK}/depend.mk +include ${MK}/gitignore.mk diff --git a/mk/scripts.mk b/mk/scripts.mk new file mode 100644 index 0000000..0cbd3bf --- /dev/null +++ b/mk/scripts.mk @@ -0,0 +1,65 @@ +# Install rules for our scripts +# Copyright (c) 2007-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. + +include ${MK}/sys.mk +include ${MK}/os.mk + +OBJS+= ${SRCS:.in=} + +_PKG_SED_SH= if test "${PREFIX}" = "${PKG_PREFIX}"; then echo "-e 's:@PKG_PREFIX@::g'"; else echo "-e 's:@PKG_PREFIX@:${PKG_PREFIX}:g'"; fi +_PKG_SED:= $(shell ${_PKG_SED_SH}) +_LCL_SED_SH= if test "${PREFIX}" = "${LOCAL_PREFIX}"; then echo "-e 's:@LOCAL_PREFIX@::g'"; else echo "-e 's:@LOCAL_PREFIX@:${LOCAL_PREFIX}:g'"; fi +_LCL_SED:= $(shell ${_LCL_SED_SH}) + +SED_REPLACE= -e 's:@SHELL@:${SH}:g' -e 's:@LIB@:${LIBNAME}:g' -e 's:@SYSCONFDIR@:${SYSCONFDIR}:g' -e 's:@LIBEXECDIR@:${LIBEXECDIR}:g' -e 's:@PREFIX@:${PREFIX}:g' -e 's:@BINDIR@:${BINDIR}:g' -e 's:@SBINDIR@:${SBINDIR}:g' ${_PKG_SED} ${_LCL_SED} + +# Tweak our shell scripts +%.sh: %.sh.in + ${SED} ${SED_REPLACE} ${SED_EXTRA} $< > $@ + +%: %.in + ${SED} ${SED_REPLACE} ${SED_EXTRA} $< > $@ + +all: ${OBJS} ${TARGETS} + +realinstall: ${BIN} ${CONF} ${INC} + @if test -n "${DIR}"; then \ + ${ECHO} ${INSTALL} -d ${DESTDIR}/${DIR}; \ + ${INSTALL} -d ${DESTDIR}/${DIR} || exit $$?; \ + fi + @if test -n "${BIN}"; then \ + ${ECHO} ${INSTALL} -m ${BINMODE} ${BIN} ${DESTDIR}/${DIR}; \ + ${INSTALL} -m ${BINMODE} ${BIN} ${DESTDIR}/${DIR} || exit $$?; \ + fi + @if test -n "${INC}"; then \ + ${ECHO} ${INSTALL} -m ${INCMODE} ${INC} ${DESTDIR}/${DIR}; \ + ${INSTALL} -m ${INCMODE} ${INC} ${DESTDIR}/${DIR} || exit $$?; \ + fi + @for x in ${CONF}; do \ + if ! test -e ${DESTDIR}/${PREFIX}${DIR}/$$x; then \ + ${ECHO} ${INSTALL} -m ${CONFMODE} $$x ${DESTDIR}/${DIR}; \ + ${INSTALL} -m ${CONFMODE} $$x ${DESTDIR}/${DIR} || exit $$?; \ + fi; \ + done + +install: all realinstall ${INSTALLAFTER} + +check test:: + @if test -e runtests.sh ; then ./runtests.sh || exit $$? ; fi + +# A lot of scripts don't have anything to clean +# Also, some rm implentation require a file argument regardless of error +# so we ensure that it has a bogus argument +CLEANFILES+= ${OBJS} +clean: + @if test -n "${CLEANFILES}"; then echo "rm -f ${CLEANFILES}"; rm -f ${CLEANFILES}; fi + +include ${MK}/gitignore.mk diff --git a/mk/subdir.mk b/mk/subdir.mk new file mode 100644 index 0000000..b79c94b --- /dev/null +++ b/mk/subdir.mk @@ -0,0 +1,38 @@ +# Recursive rules +# Adapted from FreeBSDs bsd.subdir.mk +# Copyright (c) 2007-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. + +_+_ ?= + +ECHODIR ?= echo +_SUBDIR = @${_+_}for x in ${SUBDIR}; do \ + if test -d $$x; then \ + ${ECHODIR} "===> ${DIRPRFX}$$x (${@:realinstall=install})"; \ + cd $$x; \ + ${MAKE} ${@:realinstall=install} \ + DIRPRFX=${DIRPRFX}$$x/ || exit $$?; \ + cd ..; \ + fi; \ +done + +all: + ${_SUBDIR} +clean: + @if test -n "${CLEANFILES}"; then echo "rm -f ${CLEANFILES}"; rm -f ${CLEANFILES}; fi + ${_SUBDIR} +realinstall: + ${_SUBDIR} +install: realinstall ${INSTALLAFTER} +check test:: + ${_SUBDIR} +depend: + ${_SUBDIR} +ignore: + ${_SUBDIR} diff --git a/mk/sys.mk b/mk/sys.mk new file mode 100644 index 0000000..f7074d0 --- /dev/null +++ b/mk/sys.mk @@ -0,0 +1,67 @@ +# Generic system definitions +# Copyright (c) 2008-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. + +AR?= ar +CP?= cp +PKG_CONFIG?= pkg-config +ECHO?= echo +INSTALL?= install +RANLIB?= ranlib +SED?= sed +SH= /bin/sh + +PREFIX?= +ifeq (${PREFIX},) +UPREFIX= /usr +else +UPREFIX= ${PREFIX} +ifeq (${MKPREFIX},yes) +UPREFIX= ${PREFIX}/usr +endif +endif +LOCAL_PREFIX= $(UPREFIX)/local + +PICFLAG?= -fPIC + +SYSCONFDIR?= ${PREFIX}/etc +INITDIR?= ${SYSCONFDIR}/init.d +CONFDIR?= ${SYSCONFDIR}/conf.d +SYSCTLDIR?= ${SYSCONFDIR}/sysctl.d + +BINDIR?= ${PREFIX}/bin +BINMODE?= 0755 + +SBINDIR?= ${PREFIX}/sbin +SBINMODE?= 0755 + +INCDIR?= ${UPREFIX}/include +INCMODE?= 0644 + +_LIBNAME_SH= case `readlink /lib` in /lib64|lib64) echo "lib64";; *) echo "lib";; esac +_LIBNAME:= $(shell ${_LIBNAME_SH}) +LIBNAME?= ${_LIBNAME} +LIBDIR?= ${UPREFIX}/${LIBNAME} +LIBMODE?= 0644 +SHLIBDIR?= ${PREFIX}/${LIBNAME} + +LIBEXECDIR?= ${PREFIX}/libexec/rc + +MANPREFIX?= ${UPREFIX}/share +MANDIR?= ${MANPREFIX}/man +MANMODE?= 0644 + +DATADIR?= ${UPREFIX}/share/openrc +DATAMODE?= 0644 + +DOCDIR?= ${UPREFIX}/share/doc +DOCMODE?= 0644 + +CONFMODE?= 0644 diff --git a/mk/termcap.mk b/mk/termcap.mk new file mode 100644 index 0000000..4890e35 --- /dev/null +++ b/mk/termcap.mk @@ -0,0 +1,17 @@ +ifeq (${MKTERMCAP},ncurses) +TERMCAP_CFLAGS:= $(shell ${PKG_CONFIG} ncurses --cflags 2> /dev/null) +LTERMCAP:= $(shell ${PKG_CONFIG} ncurses --libs 2> /dev/null) +ifeq ($(LTERMCAP),) +LIBTERMCAP?= -lncurses +else +LIBTERMCAP?= $(LTERMCAP) +endif +CPPFLAGS+= -DHAVE_TERMCAP ${TERMCAP_CFLAGS} +LDADD+= ${LIBTERMCAP} +else ifeq (${MKTERMCAP},termcap) +LIBTERMCAP?= -ltermcap +CPPFLAGS+= -DHAVE_TERMCAP +LDADD+= ${LIBTERMCAP} +else ifneq (${MKTERMCAP},) +$(error If MKTERMCAP is defined, it must be ncurses or termcap) +endif diff --git a/pkgconfig/.gitignore b/pkgconfig/.gitignore new file mode 100644 index 0000000..e50bf9c --- /dev/null +++ b/pkgconfig/.gitignore @@ -0,0 +1,2 @@ +einfo.pc +openrc.pc diff --git a/pkgconfig/Makefile b/pkgconfig/Makefile new file mode 100644 index 0000000..4bf28cf --- /dev/null +++ b/pkgconfig/Makefile @@ -0,0 +1,12 @@ +DIR= ${LIBDIR}/pkgconfig +SRCS= einfo.pc.in openrc.pc.in +INC= einfo.pc openrc.pc + +.DEFAULT: + ${SED} -n -e 's/^VERSION=[[:space:]]*\([^[:space:]]*\).*/#define VERSION "\1${GITVER}\"/p' ../../Makefile > version.h + +SED_EXTRA= -e 's:@VERSION@:${VERSION}:g' + +MK= ../mk +include ../Makefile.inc +include ${MK}/scripts.mk diff --git a/pkgconfig/einfo.pc.in b/pkgconfig/einfo.pc.in new file mode 100644 index 0000000..b5242a6 --- /dev/null +++ b/pkgconfig/einfo.pc.in @@ -0,0 +1,9 @@ +prefix=@PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@LIB@ +includedir=/usr/include + +Name: einfo +Description: Pretty console informational display +Version: @VERSION@ +Libs: -L${libdir} -leinfo diff --git a/pkgconfig/openrc.pc.in b/pkgconfig/openrc.pc.in new file mode 100644 index 0000000..9a8a916 --- /dev/null +++ b/pkgconfig/openrc.pc.in @@ -0,0 +1,10 @@ +prefix=@PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@LIB@ +includedir=/usr/include + +Name: OpenRC +Description: Universal init system +Version: @VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lrc diff --git a/runit-guide.md b/runit-guide.md new file mode 100644 index 0000000..93a3eec --- /dev/null +++ b/runit-guide.md @@ -0,0 +1,41 @@ +# 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. + +## Setup + +Documenting runit in detail is beyond the scope of this guide. It will +document how to set up OpenRC services to communicate with runit. + +### Use Default start, stop and status functions + +If you write your own start, stop and status functions in your service +script, none of this will work. You must allow OpenRC to use the default +functions. + +### Dependencies + +All OpenRC service scripts that want their daemons monitored by runit +should have the following line added to their dependencies to make sure +the runit scan directory is being monitored. + +need runsvdir + +### Variable Settings + +The most important setting is the supervisor variable. At the top of +your service script, you should set this variable as follows: + +supervisor=runit + +The second variable you need is runit_service. This is the path to the +runit service you wish to control via OpenRC. The default is +/etc/sv/${RC_SVCNAME}. This means that for an OpenRC service +/etc/init.d/foo, you will need to create the same runit service in +/etc/sv/foo. + +This is very early support, so feel free to file bugs if you have +issues. + +[1] http://www.smarden.org/runit diff --git a/runlevels/Makefile b/runlevels/Makefile new file mode 100644 index 0000000..12d3d94 --- /dev/null +++ b/runlevels/Makefile @@ -0,0 +1,95 @@ +include ../mk/net.mk + +BOOT= bootmisc hostname \ + root sysctl urandom ${BOOT-${OS}} +DEFAULT= +NONETWORK= +SHUTDOWN= ${SHUTDOWN-${OS}} +SYSINIT= ${SYSINIT-${OS}} + +LEVELDIR= ${DESTDIR}/${SYSCONFDIR}/runlevels +SYSINITDIR= ${LEVELDIR}/sysinit +BOOTDIR= ${LEVELDIR}/boot +DEFAULTDIR= ${LEVELDIR}/default +NONETWORKDIR= ${LEVELDIR}/nonetwork +SHUTDOWNDIR= ${LEVELDIR}/shutdown + +ifeq (${MKNET},yes) +BOOT+= network staticroute +endif + +INITFILES= ../init.d + +MK= ../mk +include ${MK}/sys.mk +include ${MK}/os.mk +include ${MK}/gitignore.mk + +BOOT-${OS}= +SHUTDOWN-${OS}= +SYSINIT-${OS}= + +BOOT-BSD= hostid newsyslog savecore syslogd + +# Generic BSD stuff +BOOT-FreeBSD+= hostid newsyslog savecore syslogd +# FreeBSD specific stuff +BOOT-FreeBSD+= adjkerntz dumpon syscons + +BOOT-Linux+= +SHUTDOWN-Linux= +SYSINIT-Linux= + +# Generic BSD stuff +BOOT-NetBSD+= hostid newsyslog savecore syslogd +# NetBSD specific stuff +BOOT-NetBSD+= devdb ttys wscons + +all: + +install: + if ! test -d "${SYSINITDIR}"; then \ + ${INSTALL} -d ${SYSINITDIR} || exit $$?; \ + for x in ${SYSINIT}; do \ + if test "${MKPREFIX}" = yes; then \ + grep -q "keyword .*-prefix" ${INITFILES}/"$$x" && continue; \ + fi; \ + ln -snf ${INITDIR}/"$$x" ${SYSINITDIR}/"$$x" || exit $$?; done \ + fi + if ! test -d "${BOOTDIR}"; then \ + ${INSTALL} -d ${BOOTDIR} || exit $$?; \ + for x in ${BOOT}; do \ + if test "${MKPREFIX}" = yes; then \ + grep -q "keyword .*-prefix" ${INITFILES}/"$$x" && continue; \ + fi; \ + ln -snf ${INITDIR}/"$$x" ${BOOTDIR}/"$$x" || exit $$?; \ + done \ + fi + if ! test -d "${DEFAULTDIR}"; then \ + ${INSTALL} -d ${DEFAULTDIR} || exit $$?; \ + for x in ${DEFAULT}; do \ + if test "${MKPREFIX}" = yes; then \ + grep -q "keyword .*-prefix" ${INITFILES}/"$$x" && continue; \ + fi; \ + ln -snf ${INITDIR}/"$$x" ${DEFAULTDIR}/"$$x" || exit $$?; done \ + fi + if ! test -d "${NONETWORKDIR}"; then \ + ${INSTALL} -d ${NONETWORKDIR} || exit $$?; \ + for x in ${NONETWORK}; do \ + if test "${MKPREFIX}" = yes; then \ + grep -q "keyword .*-prefix" ${INITFILES}/"$$x" && continue; \ + fi; \ + ln -snf ${INITDIR}/"$$x" ${NONETWORKDIR}/"$$x" || exit $$?; done \ + fi + if ! test -d "${SHUTDOWNDIR}"; then \ + ${INSTALL} -d ${SHUTDOWNDIR} || exit $$?; \ + for x in ${SHUTDOWN}; do \ + if test "${MKPREFIX}" = yes; then \ + grep -q "keyword .*-prefix" ${INITFILES}/"$$x" && continue; \ + fi; \ + ln -snf ${INITDIR}/"$$x" ${SHUTDOWNDIR}/"$$x" || exit $$?; done \ + fi + +check test:: + +clean: diff --git a/s6-guide.md b/s6-guide.md new file mode 100644 index 0000000..52262b3 --- /dev/null +++ b/s6-guide.md @@ -0,0 +1,48 @@ +# 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 +daemons [1]. + +## Setup + +Documenting s6 in detail is beyond the scope of this guide. It will +document how to set up OpenRC services to communicate with s6. + +### Use Default start, stop and status functions + +If you write your own start, stop and status functions in your service +script, none of this will work. You must allow OpenRC to use the default +functions. + +### Dependencies + +All OpenRC service scripts that want their daemons monitored by s6 +should have the following line added to their dependencies to make sure +the s6 scan directory is being monitored. + +need s6-svscan + +### Variable Settings + +The most important setting is the supervisor variable. At the top of +your service script, you should set this variable as follows: + +supervisor=s6 + +Several other variables affect s6 services. They are documented on the +openrc-run man page, but I will list them here for convenience: + +s6_service_path - the path to the s6 service directory. The default is +/var/svc.d/$RC_SVCNAME. + +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_service_timeout_stop - the amount of time, in milliseconds, s6-svc +should wait for a service to go down when stopping. + +This is very early support, so feel free to file bugs if you have +issues. + +[1] http://www.skarnet.org/software/s6 diff --git a/scripts/.gitignore b/scripts/.gitignore new file mode 100644 index 0000000..e26c51a --- /dev/null +++ b/scripts/.gitignore @@ -0,0 +1,5 @@ +halt +poweroff +rc-sstat +reboot +shutdown diff --git a/scripts/Makefile b/scripts/Makefile new file mode 100644 index 0000000..d2215b3 --- /dev/null +++ b/scripts/Makefile @@ -0,0 +1,30 @@ +MK= ../mk +include ${MK}/os.mk + +DIR= ${LIBEXECDIR}/bin +BIN= on_ac_power +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 diff --git a/scripts/halt.in b/scripts/halt.in new file mode 100644 index 0000000..257cc2a --- /dev/null +++ b/scripts/halt.in @@ -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} "$@" diff --git a/scripts/on_ac_power b/scripts/on_ac_power new file mode 100755 index 0000000..33fb63a --- /dev/null +++ b/scripts/on_ac_power @@ -0,0 +1,46 @@ +#!/bin/sh +# Detect AC power or not in a portable way +# Exit 0 if on AC power, 1 if not and 255 if we don't know how to work it out + +# Copyright (c) 2007-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. + +if [ -f /proc/acpi/ac_adapter/*/state ]; then + cat /proc/acpi/ac_adapter/*/state | while read line; do + case "$line" in + "state:"*"off-line") exit 128;; + esac + done +elif [ -f /sys/class/power_supply/*/online ]; then + cat /sys/class/power_supply/*/online | while read line; do + [ "${line}" = 0 ] && exit 128 + done +elif [ -f /proc/pmu/info ]; then + cat /proc/pmu/info | while read line; do + case "$line" in + "AC Power"*": 0") exit 128;; + esac + done +elif command -v envstat >/dev/null 2>&1; then + # NetBSD has envstat + envstat -d acpiacad0 2>/dev/null | while read line; do + case "$line" in + "connected:"*"OFF") exit 128;; + esac + done +elif sysctl -q hw.acpi.acline >/dev/null 2>/dev/null; then + case $(sysctl -n hw.acpi.acline) in + 0) exit 1;; + *) exit 0;; + esac +else + exit 255 +fi +[ $? != 128 ] diff --git a/scripts/poweroff.in b/scripts/poweroff.in new file mode 100644 index 0000000..5632f67 --- /dev/null +++ b/scripts/poweroff.in @@ -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} "$@" diff --git a/scripts/rc-sstat.in b/scripts/rc-sstat.in new file mode 100644 index 0000000..a204dbd --- /dev/null +++ b/scripts/rc-sstat.in @@ -0,0 +1,149 @@ +#!@SHELL@ +# 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. + +# Define variables +scandir="/run/openrc/s6-scan" +statfile=/dev/shm/s6-svstat.${USER} + +color_red='\E[01;31m' +color_green='\E[32m' +color_yellow='\E[01;33m' + +# Time Modules +uptimeModules() { + # Given a single integer argument representing seconds of uptime... + # convert uptime to a friendly human readable string: '2d 16h 58m 46s' + # define a variable to keep track of the longest length uptime string + uSec=${1:-0} + + uDay=$(( $uSec / 86400 )) + uSec=$(( $uSec % 86400 )) + uHour=$(( $uSec / 3600 )) + uSec=$(( $uSec % 3600 )) + uMin=$(( $uSec / 60 )) + uSec=$(( $uSec % 60 )) + + [ $uDay -ne 0 ] && pDay="${uDay}d " || pDay="" + [ $uHour -ne 0 ] && pHour="${uHour}h " || pHour="" + [ $uMin -ne 0 ] && pMin="${uMin}m " || pMin="" + [ $uSec -ne 0 ] && pSec="${uSec}s " || pSec="" + + parsedUptime="$( echo ${pDay}${pHour}${pMin}${pSec} | sed 's#[ \t]*$##' )" + uCharCount=${#parsedUptime} +} + +# Make sure we are running as root +if [ $(id -u) != 0 ]; then + printf "This command must be run as root\n" + exit 1 +fi + +# Make sure scandir exists +if [ ! -d $scandir ]; then + printf "%s\n" "$scandir does not exist" + exit 1 +fi + +# Make sure s6-svscan is running +if ! pgrep s6-svscan >/dev/null ; then + printf "s6-svscan is not running\n" + exit 1 +fi + +# If TERM is undefined (launching sstat through an ssh command) then make it vt100 +if [ -z $TERM -o $TERM = "dumb" ]; then + export TERM=vt100 +fi + +# Gather list of candidate services s6-supervise may be supervising +# filter for folders and symlinks at /run/openrc/s6-scan/* ommiting output starting with '.' +services="$(find $scandir -maxdepth 1 -mindepth 1 \( -type d -or -type l \) | awk -F'/' '{ if ( $NF !~ "^\\." ) print $NF}')" +if [ -z "$services" ]; then + printf "s6 found no services configured for supervision\n" + exit 1 +fi + +# Gather status for each service from s6-svstat +# write to tmp file in memory for non I/O bound repeatative access +rm -f $statfile 2>/dev/null +for service in $services ; do + echo "$service $(s6-svstat ${scandir}/${service})" >> $statfile +done + +# Define longest string from parsed uptime (default to 7 to match string length of 'Up Time') +timeStringLength=7 +for uptime in $(awk '$2 == "up" {print $5}' $statfile | sort -run) +do + uptimeModules $uptime + [ ${uCharCount} -gt $timeStringLength ] && timeStringLength=$uCharCount +done + + +# Print the status header like so... +# Service Name State PID Up Time Start Time +#---------------------------- ----- ----- -------------- ------------------- +printf "\n" +printf "%28s %5s %5s %${timeStringLength}s %19s\n" "Service Name" "State" "PID" "Up Time" "Start Time" +for dashes in 28 5 5 $timeStringLength 19 ; do + printf "%0.s-" $(seq 1 $dashes) ; echo -n ' ' +done && printf "\n" + + +# sshd up (pid 26300) 80373 seconds +cat $statfile | \ +while read line +do + set $line + + service=$1 + state=$2 + pid=${4/)/} + time=$5 + + # call function to convert time in seconds and define additional variables + uptimeModules $time + + if [ "$state" = up ]; then + if [ $time -lt 30 ]; then + # uptime < 30 seconds, color the whole line yellow + echo -en "$color_yellow" + # 1st 4 columns are printed with printf for space padding + printf "%28s %5s %5s %${timeStringLength}s" $service $state $pid "$parsedUptime" + # 4th column is output from date -d + echo -e " $(date -d "${time} seconds ago" "+%F %T")" + # reset terminal colors + tput sgr0 + else + printf "%28s" $service + # uptime > 30 seconds, color just the "state" value green + echo -en "$color_green" + printf " %5s" $state + # reset terminal colors + tput sgr0 + printf " %5s" $pid + printf " %${timeStringLength}s" "$parsedUptime" + echo -e " $(date -d "${time} seconds ago" "+%F %T")" + fi + else + printf "%28s" $service + echo -en "$color_red" + printf " %5s" $state + tput sgr0 + echo "" + fi +done + +# Cleanup +rm -f $statfile 2>/dev/null + +printf "\n\n" + +rc-status diff --git a/scripts/reboot.in b/scripts/reboot.in new file mode 100644 index 0000000..c805ceb --- /dev/null +++ b/scripts/reboot.in @@ -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} "$@" diff --git a/scripts/shutdown.in b/scripts/shutdown.in new file mode 100644 index 0000000..7d68bf0 --- /dev/null +++ b/scripts/shutdown.in @@ -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} "$@" diff --git a/sh/.gitignore b/sh/.gitignore new file mode 100644 index 0000000..8a00710 --- /dev/null +++ b/sh/.gitignore @@ -0,0 +1,10 @@ +functions.sh +gendepends.sh +rc-functions.sh +openrc-run.sh +cgroup-release-agent.sh +init.sh +init-early.sh +rc-cgroup.sh +migrate-to-run.sh +binfmt.sh diff --git a/sh/Makefile b/sh/Makefile new file mode 100644 index 0000000..4d7148f --- /dev/null +++ b/sh/Makefile @@ -0,0 +1,35 @@ +DIR= ${LIBEXECDIR}/sh +SRCS= init.sh.in functions.sh.in gendepends.sh.in \ + openrc-run.sh.in rc-functions.sh.in ${SRCS-${OS}} +INC= rc-mount.sh functions.sh rc-functions.sh runit.sh s6.sh \ + start-stop-daemon.sh supervise-daemon.sh +BIN= gendepends.sh init.sh openrc-run.sh ${BIN-${OS}} + +INSTALLAFTER= _installafter + +MK= ../mk +include ${MK}/os.mk + +SRCS-FreeBSD= +BIN-FreeBSD= + +SRCS-Linux= binfmt.sh.in cgroup-release-agent.sh.in init-early.sh.in \ + migrate-to-run.sh.in rc-cgroup.sh.in +BIN-Linux= binfmt.sh cgroup-release-agent.sh init-early.sh migrate-to-run.sh \ + rc-cgroup.sh + +SRCS-NetBSD= +BIN-NetBSD= + +include ${MK}/scripts.mk + +%.sh: %.sh${SFX} + ${SED} ${SED_REPLACE} ${SED_EXTRA} $< > $@ + +_installafter: + ${INSTALL} -d ${DESTDIR}/${INITDIR} + @# Put functions.sh into init for backwards compat + ln -snf ${LIBEXECDIR}/sh/functions.sh ${DESTDIR}/${INITDIR} || exit $$? + +check test:: + ./runtests.sh diff --git a/sh/binfmt.sh.in b/sh/binfmt.sh.in new file mode 100644 index 0000000..be0ed48 --- /dev/null +++ b/sh/binfmt.sh.in @@ -0,0 +1,90 @@ +#!@SHELL@ +# This is a reimplementation of the systemd binfmt.d code to register +# misc binary formats with the kernel. +# +# See the binfmt.d manpage as well: +# http://0pointer.de/public/systemd-man/binfmt.d.html +# This script should match the manpage as of 2015/03/31 + +# 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. +apply_file() { + [ $# -lt 1 ] && return 0 + FILE="$1" + LINENUM=0 + + ### FILE FORMAT ### + # See https://www.kernel.org/doc/Documentation/binfmt_misc.txt + while read -r line; do + LINENUM=$(( LINENUM+1 )) + case $line in + \#*) continue ;; + \;*) continue ;; + esac + + echo "${line}" > /proc/sys/fs/binfmt_misc/register + rc=$? + if [ $rc -ne 0 ]; then + printf "binfmt: invalid entry on line %d of \`%s'\n" \ + "$LINENUM" "$FILE" >&2 + error=1 + fi + done <$FILE + return $rc +} + +[ -e /proc/sys/fs/binfmt_misc/register ] || exit 0 +error=0 +if [ $# -gt 0 ]; then + while [ $# -gt 0 ]; do + apply_file "$1" + shift + done +else + # The hardcoding of these paths is intentional; we are following the + # systemd spec. + binfmt_dirs='/usr/lib/binfmt.d/ /run/binfmt.d/ /etc/binfmt.d/' + binfmt_basenames='' + binfmt_d='' + + # Build a list of sorted unique basenames + # directories declared later in the binfmt_d list will override earlier + # directories, on a per file basename basis. + # `/run/binfmt.d/foo.conf' supersedes `/usr/lib/binfmt.d/foo.conf'. + # `/run/binfmt.d/foo.conf' will always be read after `/etc/binfmt.d/bar.conf' + for d in ${binfmt_dirs} ; do + [ -d $d ] && for f in ${d}/*.conf ; do + case "${f##*/}" in + systemd.conf|systemd-*.conf) continue;; + esac + [ -e $f ] && binfmt_basenames="${binfmt_basenames}\n${f##*/}" + done # for f in ${d} + done # for d in ${binfmt_dirs} + binfmt_basenames="$(printf "${binfmt_basenames}\n" | sort -u )" + + for b in $binfmt_basenames ; do + real_f='' + for d in $binfmt_dirs ; do + f=${d}/${b} + [ -e "${f}" ] && real_f=$f + done + [ -e "${real_f}" ] && binfmt_d="${binfmt_d} ${real_f}" + done + + # loop through the gathered fragments, sorted globally by filename. + # `/run/binfmt.d/foo.conf' will always be read after `/etc/binfmt.d/bar.conf' + for FILE in $binfmt_d ; do + apply_file "$FILE" + done +fi + +exit $error + +# vim: set ts=2 sw=2 sts=2 noet ft=sh: diff --git a/sh/cgroup-release-agent.sh.in b/sh/cgroup-release-agent.sh.in new file mode 100644 index 0000000..394b7ec --- /dev/null +++ b/sh/cgroup-release-agent.sh.in @@ -0,0 +1,19 @@ +#!@SHELL@ +# This is run by the kernel after the last task is removed from a +# control group in the openrc hierarchy. + +# Copyright (c) 2007-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. + +cgroup=/sys/fs/cgroup/openrc +PATH=/bin:/usr/bin:/sbin:/usr/sbin +if [ -d ${cgroup}/"$1" ]; then + rmdir ${cgroup}/"$1" +fi diff --git a/sh/functions.sh.in b/sh/functions.sh.in new file mode 100644 index 0000000..9283ec0 --- /dev/null +++ b/sh/functions.sh.in @@ -0,0 +1,124 @@ +# Allow any sh script to work with einfo functions and friends +# We also provide a few helpful functions for other programs to use + +# Copyright (c) 2007-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. + +RC_GOT_FUNCTIONS="yes" + +eindent() +{ + : $(( EINFO_INDENT = ${EINFO_INDENT:-0} + 2 )) + [ "$EINFO_INDENT" -gt 40 ] && EINFO_INDENT=40 + export EINFO_INDENT +} + +eoutdent() +{ + : $(( EINFO_INDENT = ${EINFO_INDENT:-0} - 2 )) + [ "$EINFO_INDENT" -lt 0 ] && EINFO_INDENT=0 + return 0 +} + +yesno() +{ + [ -z "$1" ] && return 1 + + # Check the value directly so people can do: + # yesno ${VAR} + case "$1" in + [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) return 0;; + [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) return 1;; + esac + + # Check the value of the var so people can do: + # yesno VAR + # Note: this breaks when the var contains a double quote. + local value= + eval value=\"\$$1\" + case "$value" in + [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) return 0;; + [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) return 1;; + *) vewarn "\$$1 is not set properly"; return 1;; + esac +} + +rc_runlevel() +{ + rc-status --runlevel +} + +_sanitize_path() +{ + local IFS=":" p= path= + for p in $PATH; do + case "$p" in + @LIBEXECDIR@/bin|@LIBEXECDIR@/sbin);; + @BINDIR@|@SBINDIR@|/usr/bin|/usr/sbin);; + @PKG_PREFIX@/bin|@PKG_PREFIX@/sbin);; + @LOCAL_PREFIX@/bin|@LOCAL_PREFIX@/sbin);; + *) path="$path${path:+:}$p";; + esac + done + echo "$path" +} + +# Allow our scripts to support zsh +if [ -n "$ZSH_VERSION" ]; then + emulate sh + NULLCMD=: + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +fi + +# Make a sane PATH +_PREFIX=@PREFIX@ +_PKG_PREFIX=@PKG_PREFIX@ +_LOCAL_PREFIX=@LOCAL_PREFIX@ +_LOCAL_PREFIX=${_LOCAL_PREFIX:-/usr/local} +_PATH=@LIBEXECDIR@/bin +case "$_PREFIX" in + "$_PKG_PREFIX"|"$_LOCAL_PREFIX") ;; + *) _PATH="$_PATH:$_PREFIX/bin:$_PREFIX/sbin";; +esac +_PATH="$_PATH":/bin:/sbin:/usr/bin:/usr/sbin + +if [ -n "$_PKG_PREFIX" ]; then + _PATH="$_PATH:$_PKG_PREFIX/bin:$_PKG_PREFIX/sbin" +fi +if [ -n "$_LOCAL_PREFIX" ]; then + _PATH="$_PATH:$_LOCAL_PREFIX/bin:$_LOCAL_PREFIX/sbin" +fi +_path="$(_sanitize_path "$PATH")" +PATH="$_PATH${_path:+:}$_path" ; export PATH +unset _sanitize_path _PREFIX _PKG_PREFIX _LOCAL_PREFIX _PATH _path + +for arg; do + case "$arg" in + --nocolor|--nocolour|-C) + EINFO_COLOR="NO" ; export EINFO_COLOR + ;; + esac +done + +if [ -t 1 ] && yesno "${EINFO_COLOR:-YES}"; then + if [ -z "$GOOD" ]; then + eval $(eval_ecolors) + fi +else + # We need to have shell stub functions so our init scripts can remember + # the last ecmd + for _e in ebegin eend error errorn einfo einfon ewarn ewarnn ewend \ + vebegin veend veinfo vewarn vewend; do + eval "$_e() { local _r; command $_e \"\$@\"; _r=\$?; \ + EINFO_LASTCMD=$_e; export EINFO_LASTCMD ; return \$_r; }" + done + unset _e +fi diff --git a/sh/gendepends.sh.in b/sh/gendepends.sh.in new file mode 100644 index 0000000..45147e6 --- /dev/null +++ b/sh/gendepends.sh.in @@ -0,0 +1,129 @@ +#!@SHELL@ +# Shell wrapper to list our dependencies + +# Copyright (c) 2007-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. + +. @LIBEXECDIR@/sh/functions.sh +. @LIBEXECDIR@/sh/rc-functions.sh + +config() { + [ -n "$*" ] && echo "$RC_SVCNAME config $*" >&3 +} +need() { + [ -n "$*" ] && echo "$RC_SVCNAME ineed $*" >&3 +} +use() { + [ -n "$*" ] && echo "$RC_SVCNAME iuse $*" >&3 +} +want() { + [ -n "$*" ] && echo "$RC_SVCNAME iwant $*" >&3 +} +before() { + [ -n "$*" ] && echo "$RC_SVCNAME ibefore $*" >&3 +} +after() { + [ -n "$*" ] && echo "$RC_SVCNAME iafter $*" >&3 +} +provide() { + [ -n "$*" ] && echo "$RC_SVCNAME iprovide $*" >&3 +} +keyword() { + local c x + set -- $* + while [ -n "$*" ]; do + case "$1" in + -containers) x="$(_get_containers)" ;; + !-containers) x="$(_get_containers_remove)" ;; + *) x=$1 ;; + esac + c="${c}${x} " + shift + done + [ -n "$c" ] && echo "$RC_SVCNAME keyword $c" >&3 +} +depend() { + : +} + +_done_dirs= +for _dir in \ +@SYSCONFDIR@/init.d \ +@PKG_PREFIX@/etc/init.d \ +@LOCAL_PREFIX@/etc/init.d +do + [ -d "$_dir" ] || continue + + # Don't do the same dir twice + for _d in $_done_dirs; do + [ "$_d" = "$_dir" ] && continue 2 + done + unset _d + _done_dirs="$_done_dirs $_dir" + + cd "$_dir" + for RC_SERVICE in *; do + [ -x "$RC_SERVICE" -a -f "$RC_SERVICE" ] || continue + + # Only generate dependencies for OpenRC scripts + read one two three <"$RC_SERVICE" + case "$one" in + \#*/openrc-run) ;; + \#*/runscript) ;; + \#!) + case "$two" in + */openrc-run) ;; + */runscript) ;; + *) + continue + ;; + esac + ;; + *) + continue + ;; + esac + unset one two three + + RC_SVCNAME=${RC_SERVICE##*/} ; export RC_SVCNAME + + # Compat + SVCNAME=$RC_SVCNAME ; export SVCNAME + + ( + # Save stdout in fd3, then remap it to stderr + exec 3>&1 1>&2 + + _rc_c=${RC_SVCNAME%%.*} + if [ -n "$_rc_c" -a "$_rc_c" != "$RC_SVCNAME" ]; then + if [ -e "$_dir/../conf.d/$_rc_c" ]; then + . "$_dir/../conf.d/$_rc_c" + fi + fi + unset _rc_c + + if [ -e "$_dir/../conf.d/$RC_SVCNAME" ]; then + . "$_dir/../conf.d/$RC_SVCNAME" + fi + + [ -e @SYSCONFDIR@/rc.conf ] && . @SYSCONFDIR@/rc.conf + if [ -d "@SYSCONFDIR@/rc.conf.d" ]; then + for _f in "@SYSCONFDIR@"/rc.conf.d/*.conf; do + [ -e "$_f" ] && . "$_f" + done + fi + + if . "$_dir/$RC_SVCNAME"; then + echo "$RC_SVCNAME" >&3 + _depend + fi + ) + done +done diff --git a/sh/init-early.sh.Linux.in b/sh/init-early.sh.Linux.in new file mode 100644 index 0000000..1898c44 --- /dev/null +++ b/sh/init-early.sh.Linux.in @@ -0,0 +1,57 @@ +#!@SHELL@ +# Copyright (c) 2007-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. + +: ${CONSOLE:=/dev/console} +: ${RC_LIBEXECDIR:=@LIBEXECDIR@} + +service_present() +{ + local p="@SYSCONFDIR@/runlevels/$1/$2" + # fail if the file doesn't exist + [ ! -e "$p" ] && return 1 + # succeed if $RC_SYS empty, can't check further, assume script will run + [ -z "$RC_SYS" ] && return 0 + # fail if file contains "-$RC_SYS", because then it won't run + egrep -qi "^[[:space:]]*keyword[[:space:]].*-$RC_SYS\>" "$p" && return 1 + # succeed otherwise + return 0 +} + +if [ -e "$RC_LIBEXECDIR"/console/unicode ]; then + termencoding="%G" + kmode="-u" +else + termencoding="(K" + kmode="-a" +fi + +# Try and set a font and as early as we can +if service_present "$RC_DEFAULTLEVEL" consolefont || + service_present "$RC_BOOTLEVEL" consolefont; then + printf "\033%s" "$termencoding" >"$CONSOLE" 2>/dev/null + if [ -r "$RC_LIBEXECDIR"/console/font ] && \ + command -v setfont > /dev/null 2>&1; then + [ -c "$CONSOLE" ] && cons="-C $CONSOLE" + setfont $cons "$RC_LIBEXECDIR"/console/font 2>/dev/null + fi +fi + +# Try and set a keyboard map as early as possible +if service_present "$RC_DEFAULTLEVEL" keymaps || + service_present "$RC_BOOTLEVEL" keymaps; then + kbd_mode $kmode -C "$CONSOLE" 2>/dev/null + if [ -r "$RC_LIBEXECDIR"/console/keymap ]; then + loadkeys -q "$RC_LIBEXECDIR"/console/keymap 2>/dev/null + fi +fi + +# Ensure we exit 0 so the boot continues +exit 0 diff --git a/sh/init.sh.BSD.in b/sh/init.sh.BSD.in new file mode 100644 index 0000000..64282e9 --- /dev/null +++ b/sh/init.sh.BSD.in @@ -0,0 +1,65 @@ +#!@SHELL@ +# Copyright (c) 2007-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. + +# This basically mounts $svcdir as a ramdisk, but preserving its content +# which allows us to run depscan.sh +# FreeBSD has a nice ramdisk - we don't set a size as we should always +# be fairly small and we unmount them after the boot level is done anyway +# NOTE we don't set a size for Linux either +# FreeBSD-7 supports tmpfs now :) +mount_svcdir() +{ + if ! fstabinfo --mount "$RC_SVCDIR"; then + if ! mount -t tmpfs -o rw,noexec,nosuid none \ + "$RC_SVCDIR" 2>/dev/null + then + mdconfig -a -t malloc -s "${rc_svcsize:-1024}"k -u 0 + newfs -b 4096 -i 1024 -n /dev/md0 + mount -o rw,noexec,nosuid /dev/md0 "$RC_SVCDIR" + fi + fi +} + +. "$RC_LIBEXECDIR"/sh/functions.sh +[ -r "@SYSCONFDIR@/rc.conf" ] && . "@SYSCONFDIR@/rc.conf" +if [ -d "@SYSCONFDIR@/rc.conf.d" ]; then + for _f in "@SYSCONFDIR@"/rc.conf.d/*.conf; do + [ -r "$_f" ] && . "$_f" + done +fi + +# Disable devd until we need it +if [ -z "$RC_SYS" -a "$RC_UNAME" = "FreeBSD" ]; then + sysctl hw.bus.devctl_disable=1 >/dev/null +fi + +# mount $RC_SVCDIR as something we can write to if it's not rw +# On vservers, / is always rw at this point, so we need to clean out +# the old service state data +: ${RC_LIBEXECDIR:=@LIBEXECDIR@} +: ${RC_SVCDIR:=@LIBEXECDIR@/init.d} +case "$(openrc --sys)" in + OPENVZ|VSERVER) rm -rf "$RC_SVCDIR"/*;; + *) if mountinfo --quiet "$RC_SVCDIR"; then + rm -rf "$RC_SVCDIR"/* + else + mount_svcdir + fi + ;; +esac +retval=$? + +if [ -e "$RC_LIBEXECDIR"/cache/softlevel ]; then + cp -p "$RC_LIBEXECDIR"/cache/* "$RC_SVCDIR" 2>/dev/null +fi + +echo sysinit >"$RC_SVCDIR"/softlevel +exit $retval diff --git a/sh/init.sh.GNU-kFreeBSD.in b/sh/init.sh.GNU-kFreeBSD.in new file mode 100644 index 0000000..5eb8064 --- /dev/null +++ b/sh/init.sh.GNU-kFreeBSD.in @@ -0,0 +1,42 @@ +#!@SHELL@ +# Copyright (c) 2007-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. + +if [ ! -d /run ]; then + ebegin "Creating /run" + mkdir -p /run + eend $? +fi + +if [ -L $RC_SVCDIR ]; then + rm $RC_SVCDIR +fi + +ebegin "Mounting /run" +if ! fstabinfo --mount /run; then + mount -t tmpfs -o mode=0755,noexec,nosuid,size=10% tmpfs /run + if [ $? != 0 ]; then + eerror "Unable to mount tmpfs on /run." + eerror "Can't continue." + exit 1 + fi +fi +eend + +ebegin "Creating $RC_SVCDIR" +mkdir -p $RC_SVCDIR +eend $? + +if [ -e "$RC_LIBEXECDIR"/cache/softlevel ]; then + cp -p "$RC_LIBEXECDIR"/cache/* "$RC_SVCDIR" 2>/dev/null +fi + +echo sysinit >"$RC_SVCDIR"/softlevel +exit 0 diff --git a/sh/init.sh.GNU.in b/sh/init.sh.GNU.in new file mode 100644 index 0000000..08a492a --- /dev/null +++ b/sh/init.sh.GNU.in @@ -0,0 +1,44 @@ +#!@SHELL@ +# Copyright (c) 2007-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. + +if [ ! -d /run ]; then + ebegin "Creating /run" + mkdir -p /run + eend $? +fi + +if [ -L $RC_SVCDIR ]; then + rm $RC_SVCDIR +fi + +if ! mountinfo -q /run; then + ebegin "Mounting /run" + if ! fstabinfo --mount /run; then + mount -t tmpfs -o mode=0755,no-suid,size=10% tmpfs /run + if [ $? != 0 ]; then + eerror "Unable to mount tmpfs on /run." + eerror "Can't continue." + exit 1 + fi + fi + eend +fi + +ebegin "Creating $RC_SVCDIR" +mkdir -p $RC_SVCDIR +eend $? + +if [ -e "$RC_LIBEXECDIR"/cache/softlevel ]; then + cp -p "$RC_LIBEXECDIR"/cache/* "$RC_SVCDIR" 2>/dev/null +fi + +echo sysinit >"$RC_SVCDIR"/softlevel +exit 0 diff --git a/sh/init.sh.Linux.in b/sh/init.sh.Linux.in new file mode 100644 index 0000000..53438e3 --- /dev/null +++ b/sh/init.sh.Linux.in @@ -0,0 +1,105 @@ +#!@SHELL@ +# Copyright (c) 1999-2007 Gentoo Foundation +# Copyright (c) 2007-2009 Roy Marples +# Released under the 2-clause BSD license. + +. "$RC_LIBEXECDIR"/sh/functions.sh +[ -r "@SYSCONFDIR@/rc.conf" ] && . "@SYSCONFDIR@/rc.conf" +if [ -d "@SYSCONFDIR@/rc.conf.d" ]; then + for _f in "@SYSCONFDIR@"/rc.conf.d/*.conf; do + [ -e "$_f" ] && . "$_f" + done +fi + +# check for md5sum, and probably /usr too +if command -v md5sum >/dev/null; then + got_md5sum=true +else + eerror "md5sum is missing, which suggests /usr is not mounted" + eerror "If you have separate /usr, it must be mounted by initramfs" + eerror "If not, you should check coreutils is installed correctly" + got_md5sum=false +fi + +# By default VServer already has /proc mounted, but OpenVZ does not! +# However, some of our users have an old proc image in /proc +# NFC how they managed that, but the end result means we have to test if +# /proc actually works or not. We do this by comparing two reads of +# /proc/self/environ for which we have set the variable VAR to two +# different values. If the comparison comes back equal, we know that +# /proc is not working. +mountproc=true +f=/proc/self/environ +if [ -e $f ]; then + if $got_md5sum && [ "$(VAR=a md5sum $f)" = "$(VAR=b md5sum $f)" ]; then + eerror "You have cruft in /proc that should be deleted" + else + # If they don't have md5sum, this will fail in pretty ways if + # /proc isn't really mounted. Oh well, their system is busted + # anyway, and they get to keep the pieces. + einfo "/proc is already mounted" + mountproc=false + fi +fi +unset f + +if $mountproc; then + ebegin "Mounting /proc" + if ! fstabinfo --mount /proc; then + mount -n -t proc -o noexec,nosuid,nodev proc /proc + mount -n /proc -o remount,gid=26,hidepid=2 + fi + eend $? +fi + +# /run is a new directory for storing volatile runtime data. +# Read more about /run at https://lwn.net/Articles/436012 +sys="$(openrc --sys)" + +if [ ! -d /run ]; then + if [ "$sys" = VSERVER ]; then + if [ -e /run ]; then + rm -rf /run + fi + mkdir /run + else + eerror "The /run directory does not exist. Unable to continue." + return 1 + fi +fi + +if [ "$sys" = VSERVER ]; then + rm -rf /run/* +elif ! mountinfo -q /run; then + ebegin "Mounting /run" + rc=0 + if ! fstabinfo --mount /run; then + mount -t tmpfs -o mode=0755,nodev,size=10% tmpfs /run + rc=$? + fi + if [ $rc != 0 ]; then + eerror "Unable to mount tmpfs on /run." + eerror "Can't continue." + exit 1 + fi +fi + +checkpath -d $RC_SVCDIR +checkpath -d -m 0775 -o root:uucp /run/lock + +# Try to mount xenfs as early as possible, otherwise rc_sys() will always +# return RC_SYS_XENU and will think that we are in a domU while it's not. +if grep -Eq "[[:space:]]+xenfs$" /proc/filesystems; then + ebegin "Mounting xenfs" + if ! fstabinfo --mount /proc/xen; then + mount -n -t xenfs xenfs /proc/xen -o nosuid,nodev,noexec + fi + eend $? +fi + +if [ -e "$RC_LIBEXECDIR"/cache/softlevel ]; then + cp -p "$RC_LIBEXECDIR"/cache/* "$RC_SVCDIR" 2>/dev/null +fi + +echo sysinit >"$RC_SVCDIR"/softlevel +exit 0 diff --git a/sh/migrate-to-run.sh.in b/sh/migrate-to-run.sh.in new file mode 100644 index 0000000..2a31b6d --- /dev/null +++ b/sh/migrate-to-run.sh.in @@ -0,0 +1,36 @@ +#!@SHELL@ +# Copyright (c) 2012-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. + +. "@LIBEXECDIR@/sh/functions.sh" + +if [ -e /run/openrc/softlevel ]; then + einfo "The OpenRC dependency data has already been migrated." + exit 0 +fi + +if [ ! -d /run ]; then + eerror "/run is not a directory." + eerror "moving /run to /run.pre-openrc" + mv /run /run.pre-openrc + mkdir /run +fi + +rm -rf /run/openrc + +if ! mountinfo -q -f tmpfs /run; then + ln -s "@LIBEXECDIR@"/init.d /run/openrc +else + cp -a "@LIBEXECDIR@/init.d" /run/openrc + rc-update -u +fi + +einfo "The OpenRC dependency data was migrated successfully." +exit 0 diff --git a/sh/openrc-run.sh.in b/sh/openrc-run.sh.in new file mode 100644 index 0000000..e48f78b --- /dev/null +++ b/sh/openrc-run.sh.in @@ -0,0 +1,389 @@ +#!@SHELL@ +# Shell wrapper for openrc-run + +# Copyright (c) 2007-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. + +verify_boot() +{ + if [ ! -e ${RC_SVCDIR}/softlevel ]; then + eerror "You are attempting to run an openrc service on a" + eerror "system which openrc did not boot." + eerror "You may be inside a chroot or you may have used" + eerror "another initialization system to boot this system." + eerror "In this situation, you will get unpredictable results!" + eerror + eerror "If you really want to do this, issue the following command:" + eerror "touch ${RC_SVCDIR}/softlevel" + exit 1 + fi + return 0 +} + +sourcex() +{ + if [ "$1" = "-e" ]; then + shift + [ -e "$1" ] || return 1 + fi + if ! . "$1"; then + eerror "$RC_SVCNAME: error loading $1" + exit 1 + fi +} + +sourcex "@LIBEXECDIR@/sh/functions.sh" +sourcex "@LIBEXECDIR@/sh/rc-functions.sh" +case $RC_SYS in + PREFIX|CHROOT+UNSHARE) ;; + *) sourcex -e "@LIBEXECDIR@/sh/rc-cgroup.sh";; +esac + +# Support LiveCD foo +if sourcex -e "/sbin/livecd-functions.sh"; then + livecd_read_commandline +fi + +if [ -z "$1" -o -z "$2" ]; then + eerror "$RC_SVCNAME: not enough arguments" + exit 1 +fi + +# So daemons know where to recall us if needed +RC_SERVICE="$1" ; export RC_SERVICE +shift + +# Compat +SVCNAME=$RC_SVCNAME ; export SVCNAME + +# Dependency function +config() { + [ -n "$*" ] && echo "config $*" +} +need() { + [ -n "$*" ] && echo "need $*" +} +use() { + [ -n "$*" ] && echo "use $*" +} +want() { + [ -n "$*" ] && echo "want $*" +} +before() { + [ -n "$*" ] && echo "before $*" +} +after() { + [ -n "$*" ] && echo "after $*" +} +provide() { + [ -n "$*" ] && echo "provide $*" +} +keyword() { + local c x + set -- $* + while [ -n "$*" ]; do + case "$1" in + -containers) x="$(_get_containers)" ;; + !-containers) x="$(_get_containers_remove)" ;; + *) x=$1 ;; + esac + c="${c}${x} " + shift + done + [ -n "$c" ] && echo "keyword $c" +} + +# Describe the init script to the user +describe() +{ + if [ -n "$description" ]; then + einfo "$description" + else + ewarn "No description for $RC_SVCNAME" + fi + + local svc= desc= + for svc in ${extra_commands:-$opts} $extra_started_commands \ + $extra_stopped_commands; do + eval desc=\$description_$svc + if [ -n "$desc" ]; then + einfo "$HILITE$svc$NORMAL: $desc" + else + ewarn "$HILITE$svc$NORMAL: no description" + fi + done +} + +# Report 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 + eerror "status: crashed" + return 32 + fi + einfo "status: started" + return 0 + else + einfo "status: stopped" + return 3 + fi +} + +# These functions select the appropriate function to call from the +# supervisor modules +default_start() +{ + local func=ssd_start + case "$supervisor" in + runit) func=runit_start ;; + s6) func=s6_start ;; + supervise-daemon) func=supervise_start ;; + ?*) + ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon" + ;; + esac + $func +} + +default_stop() +{ + local func=ssd_stop + case "$supervisor" in + runit) func=runit_stop ;; + s6) func=s6_stop ;; + supervise-daemon) func=supervise_stop ;; + ?*) + ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon" + ;; + esac + $func +} + +default_status() +{ + local func=ssd_status + case "$supervisor" in + runit) func=runit_status ;; + s6) func=s6_status ;; + supervise-daemon) func=supervise_status ;; + ?*) + ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon" + ;; + esac + $func +} + +# Template start / stop / status functions +# package init scripts may override these, but the bodies are as minimal as +# possible, so that the init scripts can creatively wrap default_* +# functions. +start() +{ + default_start +} + +stop() +{ + default_stop +} + +status() +{ + default_status +} + +# Start debug output +yesno $RC_DEBUG && set -x + +# Load configuration settings. First the global ones, then any +# service-specific settings. +sourcex -e "@SYSCONFDIR@/rc.conf" +if [ -d "@SYSCONFDIR@/rc.conf.d" ]; then + for _f in "@SYSCONFDIR@"/rc.conf.d/*.conf; do + sourcex -e "$_f" + done +fi + +_conf_d=${RC_SERVICE%/*}/../conf.d +# If we're net.eth0 or openvpn.work then load net or openvpn config +_c=${RC_SVCNAME%%.*} +if [ -n "$_c" -a "$_c" != "$RC_SVCNAME" ]; then + if ! sourcex -e "$_conf_d/$_c.$RC_RUNLEVEL"; then + sourcex -e "$_conf_d/$_c" + fi +fi +unset _c + +# Overlay with our specific config +if ! sourcex -e "$_conf_d/$RC_SVCNAME.$RC_RUNLEVEL"; then + sourcex -e "$_conf_d/$RC_SVCNAME" +fi +unset _conf_d + +# load service supervisor functions +sourcex "@LIBEXECDIR@/sh/runit.sh" +sourcex "@LIBEXECDIR@/sh/s6.sh" +sourcex "@LIBEXECDIR@/sh/start-stop-daemon.sh" +sourcex "@LIBEXECDIR@/sh/supervise-daemon.sh" + +# Set verbose mode +if yesno "${rc_verbose:-$RC_VERBOSE}"; then + EINFO_VERBOSE=yes + export EINFO_VERBOSE +fi + +for _cmd; do + if [ "$_cmd" != status -a "$_cmd" != describe ]; then + # Apply any ulimit defined + [ -n "${rc_ulimit:-$RC_ULIMIT}" ] && \ + ulimit ${rc_ulimit:-$RC_ULIMIT} + # Apply cgroups settings if defined + 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 + fi + cgroup_add_service /sys/fs/cgroup/openrc + cgroup_add_service /sys/fs/cgroup/systemd/system + fi + [ "$(command -v cgroup_set_limits)" = \ + "cgroup_set_limits" ] && \ + cgroup_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" + exit 1 + fi +done +[ $? -ne 0 ] && exit 1 +unset _d + +eval "printf '%s\n' $required_files" | while read _f; do + if [ -n "$_f" ] && [ ! -r "$_f" ]; then + eerror "$RC_SVCNAME: \`$_f' is not readable" + exit 1 + fi +done +[ $? -ne 0 ] && exit 1 +unset _f + +if [ -n "$opts" ]; then + ewarn "Use of the opts variable is deprecated and will be" + ewarn "removed in the future." + ewarn "Please use extra_commands, extra_started_commands or extra_stopped_commands." +fi + +while [ -n "$1" ]; do + # Special case depend + if [ "$1" = depend ]; then + shift + + # Enter the dir of the init script to fix the globbing + # bug 412677 + cd ${RC_SERVICE%/*} + _depend + cd / + continue + fi + # See if we have the required function and run it + for _cmd in describe start stop status ${extra_commands:-$opts} \ + $extra_started_commands $extra_stopped_commands + do + if [ "$_cmd" = "$1" ]; then + if [ "$(command -v "$1")" = "$1" ]; then + # If we're in the background, we may wish to + # fake some commands. We do this so we can + # "start" ourselves from inactive which then + # triggers other services to start which + # depend on us. + # A good example of this is openvpn. + if yesno $IN_BACKGROUND; then + for _cmd in $in_background_fake; do + if [ "$_cmd" = "$1" ]; then + shift + continue 3 + fi + done + fi + # Check to see if we need to be started before + # we can run this command + for _cmd in $extra_started_commands; do + if [ "$_cmd" = "$1" ]; then + if verify_boot && ! service_started; then + eerror "$RC_SVCNAME: cannot \`$1' as it has not been started" + exit 1 + fi + fi + done + # Check to see if we need to be stopped before + # we can run this command + for _cmd in $extra_stopped_commands; do + if [ "$_cmd" = "$1" ]; then + if verify_boot && ! service_stopped; then + eerror "$RC_SVCNAME: cannot \`$1' as it has not been stopped" + exit 1 + fi + fi + done + unset _cmd + case $1 in + start|stop|status) verify_boot;; + esac + if [ "$(command -v "$1_pre")" = "$1_pre" ] + then + "$1"_pre || exit $? + fi + "$1" || exit $? + if [ "$(command -v "$1_post")" = "$1_post" ] + then + "$1"_post || exit $? + fi + [ "$(command -v cgroup_cleanup)" = "cgroup_cleanup" -a \ + "$1" = "stop" ] && \ + yesno "${rc_cgroup_cleanup}" && \ + cgroup_cleanup + shift + continue 2 + else + if [ "$_cmd" = "start" -o "$_cmd" = "stop" ] + then + shift + continue 2 + else + eerror "$RC_SVCNAME: function \`$1' defined but does not exist" + exit 1 + fi + fi + fi + done + eerror "$RC_SVCNAME: unknown function \`$1'" + exit 1 +done + +exit 0 diff --git a/sh/rc-cgroup.sh.in b/sh/rc-cgroup.sh.in new file mode 100644 index 0000000..5987f96 --- /dev/null +++ b/sh/rc-cgroup.sh.in @@ -0,0 +1,154 @@ +#!@SHELL@ +# Copyright (c) 2012-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. + +extra_stopped_commands="${extra_stopped_commands} cgroup_cleanup" +description_cgroup_cleanup="Kill all processes in the cgroup" + +cgroup_find_path() +{ + local OIFS n name dir result + [ -n "$1" ] || return 0 + OIFS="$IFS" + IFS=":" + while read n name dir; do + [ "$name" = "$1" ] && result="$dir" + done < /proc/1/cgroup + IFS="$OIFS" + echo $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" ] +} + +cgroup_running() +{ + [ -d "/sys/fs/cgroup/openrc/${RC_SVCNAME}" ] +} + +cgroup_set_values() +{ + [ -n "$1" -a -n "$2" -a -d "/sys/fs/cgroup/$1" ] || return 0 + + local 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 + case "$1" in + $controller.*) + if [ -n "$name" -a -w "$cgroup/$name" -a -n "$val" ]; then + veinfo "$RC_SVCNAME: Setting $cgroup/$name to $val" + printf "%s" "$val" > "$cgroup/$name" + fi + name=$1 + val= + ;; + *) + [ -n "$val" ] && + val="$val $1" || + val="$1" + ;; + esac + shift + done + if [ -n "$name" -a -w "$cgroup/$name" -a -n "$val" ]; then + veinfo "$RC_SVCNAME: Setting $cgroup/$name to $val" + printf "%s" "$val" > "$cgroup/$name" + fi + + if [ -w "$cgroup/tasks" ]; then + veinfo "$RC_SVCNAME: adding to $cgroup/tasks" + printf "%d" 0 > "$cgroup/tasks" + fi + + return 0 +} + +cgroup_add_service() +{ + # relocate starting process to the top of the cgroup + # it prevents from unwanted inheriting of the user + # cgroups. But may lead to a problems where that inheriting + # is needed. + for d in /sys/fs/cgroup/* ; do + [ -w "${d}"/tasks ] && printf "%d" 0 > "${d}"/tasks + done + + openrc_cgroup=/sys/fs/cgroup/openrc + if [ -d "$openrc_cgroup" ]; then + cgroup="$openrc_cgroup/$RC_SVCNAME" + mkdir -p "$cgroup" + [ -w "$cgroup/tasks" ] && printf "%d" 0 > "$cgroup/tasks" + fi +} + +cgroup_set_limits() +{ + local blkio="${rc_cgroup_blkio:-$RC_CGROUP_BLKIO}" + [ -n "$blkio" ] && cgroup_set_values blkio "$blkio" + + local cpu="${rc_cgroup_cpu:-$RC_CGROUP_CPU}" + [ -n "$cpu" ] && cgroup_set_values cpu "$cpu" + + local cpuacct="${rc_cgroup_cpuacct:-$RC_CGROUP_CPUACCT}" + [ -n "$cpuacct" ] && cgroup_set_values cpuacct "$cpuacct" + + local cpuset="${rc_cgroup_cpuset:-$RC_CGROUP_cpuset}" + [ -n "$cpuset" ] && cgroup_set_values cpuset "$cpuset" + + local devices="${rc_cgroup_devices:-$RC_CGROUP_DEVICES}" + [ -n "$devices" ] && cgroup_set_values devices "$devices" + + local hugetlb="${rc_cgroup_hugetlb:-$RC_CGROUP_HUGETLB}" + [ -n "$hugetlb" ] && cgroup_set_values hugetlb "$hugetlb" + + local memory="${rc_cgroup_memory:-$RC_CGROUP_MEMORY}" + [ -n "$memory" ] && cgroup_set_values memory "$memory" + + local net_cls="${rc_cgroup_net_cls:-$RC_CGROUP_NET_CLS}" + [ -n "$net_cls" ] && cgroup_set_values net_cls "$net_cls" + + local net_prio="${rc_cgroup_net_prio:-$RC_CGROUP_NET_PRIO}" + [ -n "$net_prio" ] && cgroup_set_values net_prio "$net_prio" + + local pids="${rc_cgroup_pids:-$RC_CGROUP_PIDS}" + [ -n "$pids" ] && cgroup_set_values pids "$pids" + + 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" +} diff --git a/sh/rc-functions.sh.in b/sh/rc-functions.sh.in new file mode 100644 index 0000000..40958fa --- /dev/null +++ b/sh/rc-functions.sh.in @@ -0,0 +1,168 @@ +# Copyright (c) 2007 Gentoo Foundation +# Copyright (c) 2007-2009 Roy Marples +# Released under the 2-clause BSD license. + +has_addon() +{ + [ -e /@LIB@/rc/addons/"$1".sh -o -e /@LIB@/rcscripts/addons/"$1".sh ] +} + +_addon_warn() +{ + eindent + ewarn "$RC_SVCNAME uses addon code which is deprecated" + ewarn "and may not be available in the future." + eoutdent +} + +import_addon() +{ + if [ -e /@LIB@/rc/addons/"$1".sh ]; then + _addon_warn + . /@LIB@/rc/addons/"$1".sh + elif [ -e /@LIB@/rcscripts/addons/"$1".sh ]; then + _addon_warn + . /@LIB@/rcscripts/addons/"$1".sh + else + return 1 + fi +} + +start_addon() +{ + ( import_addon "$1-start" ) +} + +stop_addon() +{ + ( import_addon "$1-stop" ) +} + +net_fs_list="afs ceph cifs coda davfs fuse fuse.sshfs gfs glusterfs lustre +ncpfs nfs nfs4 ocfs2 shfs smbfs" +is_net_fs() +{ + [ -z "$1" ] && return 1 + + # Check OS specific flags to see if we're local or net mounted + mountinfo --quiet --netdev "$1" && return 0 + mountinfo --quiet --nonetdev "$1" && return 1 + + # Fall back on fs types + local t=$(mountinfo --fstype "$1") + for x in $net_fs_list $extra_net_fs_list; do + [ "$x" = "$t" ] && return 0 + done + return 1 +} + +is_union_fs() +{ + [ ! -x /sbin/unionctl ] && return 1 + unionctl "$1" --list >/dev/null 2>&1 +} + +get_bootparam() +{ + local match="$1" + [ -z "$match" -o ! -r /proc/cmdline ] && return 1 + + set -- $(cat /proc/cmdline) + while [ -n "$1" ]; do + [ "$1" = "$match" ] && return 0 + case "$1" in + gentoo=*) + local params="${1##*=}" + local IFS=, x= + for x in $params; do + [ "$x" = "$match" ] && return 0 + done + ;; + esac + shift + done + + return 1 +} + +get_bootparam_value() +{ + local match="$1" which_value="$2" sep="$3" result value + if [ -n "$match" -a -r /proc/cmdline ]; then + set -- $(cat /proc/cmdline) + while [ -n "$1" ]; do + case "$1" in + $match=*) + value="${1##*=}" + case "$which_value" in + all) + [ -z "$sep" ] && sep=' ' + if [ -z "$result" ]; then + result="$value" + else + result="${result}${sep}${value}" + fi + ;; + last) + result="$value" + ;; + *) + result="$value" + break + ;; + esac + ;; + esac + shift + done + fi + echo $result +} + +# Called from openrc-run.sh or gendepends.sh +_get_containers() { + local c + case "${RC_UNAME}" in + FreeBSD) + c="-jail" + ;; + Linux) + c="-docker -lxc -openvz -rkt -chroot+unshare -uml -vserver" + ;; + esac + echo $c +} + +_get_containers_remove() { + local c + for x in $(_get_containers); do + c="${c}!${x} " + done + echo $c +} + +_depend() { + depend + local _rc_svcname=$(shell_var "$RC_SVCNAME") _deptype= _depends= + + # Add any user defined depends + for _deptype in config:CONFIG need:NEED use:USE want:WANT \ + after:AFTER before:BEFORE \ + provide:PROVIDE keyword:KEYWORD; do + IFS=: + set -- $_deptype + unset IFS + eval _depends=\$rc_${_rc_svcname}_$1 + [ -z "$_depends" ] && eval _depends=\$rc_$1 + [ -z "$_depends" ] && eval _depends=\$RC_${_rc_svcname}_$2 + [ -z "$_depends" ] && eval _depends=\$RC_$2 + + $1 $_depends + done +} + +# Add our sbin to $PATH +case "$PATH" in + "$RC_LIBEXECDIR"/sbin|"$RC_LIBEXECDIR"/sbin:*);; + *) PATH="$RC_LIBEXECDIR/sbin:$PATH" ; export PATH ;; +esac diff --git a/sh/rc-mount.sh b/sh/rc-mount.sh new file mode 100644 index 0000000..f443d4c --- /dev/null +++ b/sh/rc-mount.sh @@ -0,0 +1,87 @@ +# Copyright (c) 2007-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. + +# Declare this here so that no formatting doesn't affect the embedded newline +__IFS=" +" + +# Handy function to handle all our unmounting needs +# mountinfo is a C program to actually find our mounts on our supported OS's +# We rely on fuser being present, so if it's not then don't unmount anything. +# This isn't a real issue for the BSD's, but it is for Linux. +do_unmount() +{ + local cmd="$1" retval=0 retry= pids=- + local f_opts="-m -c" f_kill="-s " mnt= + if [ "$RC_UNAME" = "Linux" ]; then + f_opts="-m" + f_kill="-" + fi + + shift + local IFS="$__IFS" + set -- $(mountinfo "$@") + unset IFS + for mnt; do + # Unmounting a shared mount can unmount other mounts, so + # we need to check the mount is still valid + mountinfo --quiet "$mnt" || continue + # Ensure we interpret all characters properly. + mnt=$(printf "$mnt") + + case "$cmd" in + umount) + ebegin "Unmounting $mnt" + ;; + *) + ebegin "Remounting $mnt read only" + ;; + esac + + retry=4 # Effectively TERM, sleep 1, TERM, sleep 1, KILL, sleep 1 + while ! LC_ALL=C $cmd "$mnt" 2>/dev/null; do + if command -v fuser >/dev/null 2>&1; then + pids="$(timeout -k 10 -s KILL "${rc_fuser_timeout:-60}" \ + fuser $f_opts "$mnt" 2>/dev/null)" + fi + case " $pids " in + *" $$ "*) + eend 1 "failed because we are using" \ + "$mnt" + retry=0;; + " - ") + eend 1 + retry=0;; + " ") + eend 1 "in use but fuser finds nothing" + retry=0;; + *) + if [ $retry -le 0 ]; then + eend 1 + else + local sig="TERM" + : $(( retry -= 1 )) + [ $retry = 1 ] && sig="KILL" + fuser $f_kill$sig -k $f_opts \ + "$mnt" >/dev/null 2>&1 + sleep 1 + fi + ;; + esac + [ $retry -le 0 ] && break + done + if [ $retry -le 0 ]; then + retval=1 + else + eend 0 + fi + done + return $retval +} diff --git a/sh/runit.sh b/sh/runit.sh new file mode 100644 index 0000000..3cef0f3 --- /dev/null +++ b/sh/runit.sh @@ -0,0 +1,52 @@ +# Copyright (c) 2016 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. +# Released under the 2-clause BSD license. + +runit_start() +{ + local service_path service_link + service_path="${runit_service:-/etc/sv/${RC_SVCNAME}}" + if [ ! -d "${service_path}" ]; then + eerror "Runit service ${service_path} not found" + return 1 + fi + service_link="${RC_SVCDIR}/sv/${service_path##*/}" + ebegin "Starting ${name:-$RC_SVCNAME}" + ln -snf "${service_path}" "${service_link}" + sv start "${service_link}" > /dev/null 2>&1 + eend $? "Failed to start ${name:-$RC_SVCNAME}" +} + +runit_stop() +{ + local service_path service_link + service_path="${runit_service:-/etc/sv/${RC_SVCNAME}}" + if [ ! -d "${service_path}" ]; then + eerror "Runit service ${service_path} not found" + return 1 + fi + service_link="${RC_SVCDIR}/sv/${service_path##*/}" + ebegin "Stopping ${name:-$RC_SVCNAME}" + sv stop "${service_link}" > /dev/null 2>&1 && + rm "${service_link}" + eend $? "Failed to stop ${name:-$RC_SVCNAME}" +} + +runit_status() +{ + local service_path service_link + service_path="${runit_service:-/etc/sv/${RC_SVCNAME}}" + if [ ! -d "${service_path}" ]; then + eerror "Runit service ${service_path} not found" + return 1 + fi + service_link="${RC_SVCDIR}/sv/${service_path##*/}" + sv status "${service_link}" +} diff --git a/sh/runtests.sh b/sh/runtests.sh new file mode 100755 index 0000000..b42a357 --- /dev/null +++ b/sh/runtests.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# Copyright (c) 2008-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. + +: ${top_srcdir:=..} +. $top_srcdir/test/setup_env.sh + +ret=0 + +tret=0 +ebegin "Testing yesno()" +for f in yes YES Yes true TRUE True 1 ; do + if ! yesno $f; then + : $(( tret += 1 )) + echo "!$f!" + fi +done +for f in no NO No false FALSE False 0 ; do + if yesno $f; then + : $(( tret += 1 )) + echo "!$f!" + fi +done +eend $tret +: $(( ret += $tret )) + +exit $ret diff --git a/sh/s6.sh b/sh/s6.sh new file mode 100644 index 0000000..d1b9c10 --- /dev/null +++ b/sh/s6.sh @@ -0,0 +1,58 @@ +# Start / stop / status functions for s6 support + +# 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. + +[ -z "${s6_service_path}" ] && s6_service_path="/var/svc.d/${RC_SVCNAME}" + +s6_start() +{ + if [ ! -d "${s6_service_path}" ]; then + eerror "${s6_service_path} does not exist." + return 1 + fi + s6_service_link="${RC_SVCDIR}/s6-scan/${s6_service_path##*/}" + ebegin "Starting ${name:-$RC_SVCNAME}" + ln -sf "${s6_service_path}" "${s6_service_link}" + s6-svscanctl -na "${RC_SVCDIR}"/s6-scan + sleep 1.5 + s6-svc -u "${s6_service_link}" + if [ -n "$s6_svwait_options_start" ]; then + s6-svwait ${s6_svwait_options_start} "${s6_service_link}" + fi + sleep 1.5 + set -- $(s6-svstat "${s6_service_link}") + [ "$1" = "up" ] + eend $? "Failed to start ${name:-$RC_SVCNAME}" +} + +s6_stop() +{ + if [ ! -d "${s6_service_path}" ]; then + eerror "${s6_service_path} does not exist." + return 1 + 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}" + set -- $(s6-svstat "${s6_service_link}") + [ "$1" = "down" ] + eend $? "Failed to stop ${name:-$RC_SVCNAME}" +} + +s6_status() +{ + s6_service_link="${RC_SVCDIR}/s6-scan/${s6_service_path##*/}" + if [ -L "${s6_service_link}" ]; then + s6-svstat "${s6_service_link}" + else + _status + fi +} diff --git a/sh/start-stop-daemon.sh b/sh/start-stop-daemon.sh new file mode 100644 index 0000000..0793b19 --- /dev/null +++ b/sh/start-stop-daemon.sh @@ -0,0 +1,95 @@ +# start / stop / status functions for start-stop-daemon + +# Copyright (c) 2007-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. + +ssd_start() +{ + if [ -z "$command" ]; then + ewarn "The command variable is undefined." + ewarn "There is nothing for ${name:-$RC_SVCNAME} to start." + ewarn "If this is what you intend, please write a start function." + ewarn "This will become a failure in a future release." + return 0 + fi + + local _background= + ebegin "Starting ${name:-$RC_SVCNAME}" + if yesno "${command_background}"; then + if [ -z "${pidfile}" ]; then + eend 1 "command_background option used but no pidfile specified" + return 1 + fi + if [ -n "${command_args_background}" ]; then + eend 1 "command_background used with command_args_background" + return 1 + fi + _background="--background --make-pidfile" + fi + if yesno "$start_inactive"; then + local _inactive=false + service_inactive && _inactive=true + mark_service_inactive + fi + #the eval call is necessary for cases like: + # command_args="this \"is a\" test" + # to work properly. + eval start-stop-daemon --start \ + --exec $command \ + ${chroot:+--chroot} $chroot \ + ${procname:+--name} $procname \ + ${pidfile:+--pidfile} $pidfile \ + ${command_user+--user} $command_user \ + $_background $start_stop_daemon_args \ + -- $command_args $command_args_background + if eend $? "Failed to start ${name:-$RC_SVCNAME}"; then + service_set_value "command" "${command}" + [ -n "${chroot}" ] && service_set_value "chroot" "${chroot}" + [ -n "${pidfile}" ] && service_set_value "pidfile" "${pidfile}" + [ -n "${procname}" ] && service_set_value "procname" "${procname}" + return 0 + fi + if yesno "$start_inactive"; then + if ! $_inactive; then + mark_service_stopped + fi + fi + return 1 +} + +ssd_stop() +{ + local _progress= + local startcommand="$(service_get_value "command")" + local startchroot="$(service_get_value "chroot")" + local startpidfile="$(service_get_value "pidfile")" + local startprocname="$(service_get_value "procname")" + command="${startcommand:-$command}" + chroot="${startchroot:-$chroot}" + pidfile="${startpidfile:-$pidfile}" + procname="${startprocname:-$procname}" + [ -n "$command" -o -n "$procname" -o -n "$pidfile" ] || return 0 + yesno "${command_progress}" && _progress=--progress + ebegin "Stopping ${name:-$RC_SVCNAME}" + start-stop-daemon --stop \ + ${retry:+--retry} $retry \ + ${command:+--exec} $command \ + ${procname:+--name} $procname \ + ${pidfile:+--pidfile} $chroot$pidfile \ + ${stopsig:+--signal} $stopsig \ + ${_progress} + + eend $? "Failed to stop ${name:-$RC_SVCNAME}" +} + +ssd_status() +{ + _status +} diff --git a/sh/supervise-daemon.sh b/sh/supervise-daemon.sh new file mode 100644 index 0000000..8add214 --- /dev/null +++ b/sh/supervise-daemon.sh @@ -0,0 +1,61 @@ +# start / stop / status functions for supervise-daemon + +# Copyright (c) 2016 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. + +supervise_start() +{ + if [ -z "$command" ]; then + ewarn "The command variable is undefined." + ewarn "There is nothing for ${name:-$RC_SVCNAME} to start." + return 1 + fi + + ebegin "Starting ${name:-$RC_SVCNAME}" + # The eval call is necessary for cases like: + # command_args="this \"is a\" test" + # to work properly. + eval supervise-daemon --start \ + ${chroot:+--chroot} $chroot \ + ${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 \ + $supervise_daemon_args \ + $command \ + -- $command_args $command_args_foreground + rc=$? + if [ $rc = 0 ]; then + [ -n "${chroot}" ] && service_set_value "chroot" "${chroot}" + [ -n "${pidfile}" ] && service_set_value "pidfile" "${pidfile}" + fi + eend $rc "failed to start ${name:-$RC_SVCNAME}" +} + +supervise_stop() +{ + local startchroot="$(service_get_value "chroot")" + local startpidfile="$(service_get_value "pidfile")" + chroot="${startchroot:-$chroot}" + pidfile="${startpidfile:-$pidfile}" + [ -n "$pidfile" ] || return 0 + ebegin "Stopping ${name:-$RC_SVCNAME}" + supervise-daemon --stop \ + ${pidfile:+--pidfile} $chroot$pidfile \ + ${stopsig:+--signal} $stopsig + + eend $? "Failed to stop ${name:-$RC_SVCNAME}" +} + +supervise_status() +{ + _status +} diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..e375034 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,7 @@ +# Copyright (c) 2007-2008 Roy Marples +# Released under the 2-clause BSD license. + +SUBDIR= test libeinfo librc rc + +MK= ../mk +include ${MK}/subdir.mk diff --git a/src/includes/helpers.h b/src/includes/helpers.h new file mode 100644 index 0000000..1a00d3d --- /dev/null +++ b/src/includes/helpers.h @@ -0,0 +1,124 @@ +/* + * helpers.h + * This is private to us and not for user consumption + */ + +/* + * Copyright (c) 2007-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. + */ + +#ifndef __HELPERS_H__ +#define __HELPERS_H__ + +#define ERRX fprintf (stderr, "out of memory\n"); exit (1) + +#define UNCONST(a) ((void *)(unsigned long)(const void *)(a)) + +#ifdef lint +# define _unused +#endif +#if __GNUC__ > 2 || defined(__INTEL_COMPILER) +# define _dead __attribute__((__noreturn__)) +# define _unused __attribute__((__unused__)) +#else +# define _dead +# define _unused +#endif + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +#ifdef __GLIBC__ +# if ! defined (__UCLIBC__) && ! defined (__dietlibc__) +# define strlcpy(dst, src, size) snprintf(dst, size, "%s", src) +# endif +#endif + +#ifndef timespecsub +#define timespecsub(tsp, usp, vsp) \ + do { \ + (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ + (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ + if ((vsp)->tv_nsec < 0) { \ + (vsp)->tv_sec--; \ + (vsp)->tv_nsec += 1000000000L; \ + } \ + } while (/* CONSTCOND */ 0) +#endif + +#include +#include + +_unused static void *xmalloc (size_t size) +{ + void *value = malloc(size); + + if (value) + return (value); + + ERRX; + /* NOTREACHED */ +} + +_unused static void *xrealloc(void *ptr, size_t size) +{ + void *value = realloc(ptr, size); + + if (value) + return (value); + + ERRX; + /* NOTREACHED */ +} + +_unused static char *xstrdup(const char *str) +{ + char *value; + + if (! str) + return (NULL); + + value = strdup(str); + + if (value) + return (value); + + ERRX; + /* NOTREACHED */ +} + +#undef ERRX + +/* 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, '/'); + + if (slash) + return (++slash); + return (path); +} + +_unused static bool exists(const char *pathname) +{ + struct stat buf; + + return (stat(pathname, &buf) == 0); +} + +_unused static bool existss(const char *pathname) +{ + struct stat buf; + + return (stat(pathname, &buf) == 0 && buf.st_size != 0); +} + +#endif diff --git a/src/includes/hidden-visibility.h b/src/includes/hidden-visibility.h new file mode 100644 index 0000000..9d397c2 --- /dev/null +++ b/src/includes/hidden-visibility.h @@ -0,0 +1,26 @@ +/* + * Written by Mike Frysinger + * Placed in the Public Domain + */ + +#ifndef _HIDDEN_VISIBILITY_H_ +#define _HIDDEN_VISIBILITY_H_ + +#if defined(__ELF__) && defined(__GNUC__) +# define __hidden_asmname(name) __hidden_asmname1 (__USER_LABEL_PREFIX__, name) +# define __hidden_asmname1(prefix, name) __hidden_asmname2(prefix, name) +# define __hidden_asmname2(prefix, name) #prefix name +# define __hidden_proto(name, internal) \ + extern __typeof (name) name __asm__ (__hidden_asmname (#internal)) \ + __attribute__ ((visibility ("hidden"))); +# define __hidden_ver(local, internal, name) \ + extern __typeof (name) __EI_##name __asm__(__hidden_asmname (#internal)); \ + extern __typeof (name) __EI_##name __attribute__((alias (__hidden_asmname1 (,#local)))) +# define hidden_proto(name) __hidden_proto(name, __RC_##name) +# define hidden_def(name) __hidden_ver(__RC_##name, name, name); +#else +# define hidden_proto(name) +# define hidden_def(name) +#endif + +#endif diff --git a/src/includes/queue.h b/src/includes/queue.h new file mode 100644 index 0000000..67f801d --- /dev/null +++ b/src/includes/queue.h @@ -0,0 +1,846 @@ +/* $NetBSD: queue.h,v 1.67 2014/05/17 21:22:56 rmind Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular queues. + * + * A singly-linked list is headed by a single forward pointer. The + * elements are singly linked for minimum space and pointer manipulation + * overhead at the expense of O(n) removal for arbitrary elements. New + * elements can be added to the list after an existing element or at the + * head of the list. Elements being removed from the head of the list + * should use the explicit macro for this purpose for optimum + * efficiency. A singly-linked list may only be traversed in the forward + * direction. Singly-linked lists are ideal for applications with large + * datasets and few or no removals or for implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * A circle queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the list. + * A circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +/* + * Include the definition of NULL only on NetBSD because sys/null.h + * is not available elsewhere. This conditional makes the header + * portable and it can simply be dropped verbatim into any system. + * The caveat is that on other systems some other header + * must provide NULL before the macros can be used. + */ +#ifdef __NetBSD__ +#include +#endif + +#if defined(QUEUEDEBUG) +# if defined(_KERNEL) +# define QUEUEDEBUG_ABORT(...) panic(__VA_ARGS__) +# else +# include +# define QUEUEDEBUG_ABORT(...) err(1, __VA_ARGS__) +# endif +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = (head)->slh_first; \ + (var) != SLIST_END(head); \ + (var) = (var)->field.sle_next) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) != SLIST_END(head) && \ + ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) do { \ + (head)->slh_first = SLIST_END(head); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_AFTER(slistelm, field) do { \ + (slistelm)->field.sle_next = \ + SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (/*CONSTCOND*/0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while(curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (/*CONSTCOND*/0) + + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods. + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) ((head)->lh_first == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for ((var) = ((head)->lh_first); \ + (var) != LIST_END(head); \ + (var) = ((var)->field.le_next)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) != LIST_END(head) && \ + ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define LIST_MOVE(head1, head2) do { \ + LIST_INIT((head2)); \ + if (!LIST_EMPTY((head1))) { \ + (head2)->lh_first = (head1)->lh_first; \ + LIST_INIT((head1)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * List functions. + */ +#if defined(QUEUEDEBUG) +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \ + if ((head)->lh_first && \ + (head)->lh_first->field.le_prev != &(head)->lh_first) \ + QUEUEDEBUG_ABORT("LIST_INSERT_HEAD %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_OP(elm, field) \ + if ((elm)->field.le_next && \ + (elm)->field.le_next->field.le_prev != \ + &(elm)->field.le_next) \ + QUEUEDEBUG_ABORT("LIST_* forw %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + if (*(elm)->field.le_prev != (elm)) \ + QUEUEDEBUG_ABORT("LIST_* back %p %s:%d", (elm), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \ + (elm)->field.le_next = (void *)1L; \ + (elm)->field.le_prev = (void *)1L; +#else +#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_LIST_OP(elm, field) +#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) +#endif + +#define LIST_INIT(head) do { \ + (head)->lh_first = LIST_END(head); \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + if (((elm)->field.le_next = (listelm)->field.le_next) != \ + LIST_END(head)) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_LIST_OP((listelm), field) \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (/*CONSTCOND*/0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.le_next = (head)->lh_first) != LIST_END(head))\ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (/*CONSTCOND*/0) + +#define LIST_REMOVE(elm, field) do { \ + QUEUEDEBUG_LIST_OP((elm), field) \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->sqh_first); \ + (var) != SIMPLEQ_END(head); \ + (var) = ((var)->field.sqe_next)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \ + for ((var) = ((head)->sqh_first); \ + (var) != SIMPLEQ_END(head) && \ + ((next = ((var)->field.sqe_next)), 1); \ + (var) = (next)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + SIMPLEQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->sqh_first; \ + while (curelm->field.sqe_next != (elm)) \ + curelm = curelm->field.sqe_next; \ + if ((curelm->field.sqe_next = \ + curelm->field.sqe_next->field.sqe_next) == NULL) \ + (head)->sqh_last = &(curelm)->field.sqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_CONCAT(head1, head2) do { \ + if (!SIMPLEQ_EMPTY((head2))) { \ + *(head1)->sqh_last = (head2)->sqh_first; \ + (head1)->sqh_last = (head2)->sqh_last; \ + SIMPLEQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +#define SIMPLEQ_LAST(head, type, field) \ + (SIMPLEQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->sqh_last) - offsetof(struct type, field)))) + +/* + * Tail queue definitions. + */ +#define _TAILQ_HEAD(name, type, qual) \ +struct name { \ + qual type *tqh_first; /* first element */ \ + qual type *qual *tqh_last; /* addr of last next element */ \ +} +#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) + +#define TAILQ_HEAD_INITIALIZER(head) \ + { TAILQ_END(head), &(head).tqh_first } + +#define _TAILQ_ENTRY(type, qual) \ +struct { \ + qual type *tqe_next; /* next element */ \ + qual type *qual *tqe_prev; /* address of previous next element */\ +} +#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) + +/* + * Tail queue access methods. + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) (NULL) +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) (TAILQ_FIRST(head) == TAILQ_END(head)) + + +#define TAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var) != TAILQ_END(head); \ + (var) = ((var)->field.tqe_next)) + +#define TAILQ_FOREACH_SAFE(var, head, field, next) \ + for ((var) = ((head)->tqh_first); \ + (var) != TAILQ_END(head) && \ + ((next) = TAILQ_NEXT(var, field), 1); (var) = (next)) + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last));\ + (var) != TAILQ_END(head); \ + (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \ + for ((var) = TAILQ_LAST((head), headname); \ + (var) != TAILQ_END(head) && \ + ((prev) = TAILQ_PREV((var), headname, field), 1); (var) = (prev)) + +/* + * Tail queue functions. + */ +#if defined(QUEUEDEBUG) +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \ + if ((head)->tqh_first && \ + (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ + QUEUEDEBUG_ABORT("TAILQ_INSERT_HEAD %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \ + if (*(head)->tqh_last != NULL) \ + QUEUEDEBUG_ABORT("TAILQ_INSERT_TAIL %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_OP(elm, field) \ + if ((elm)->field.tqe_next && \ + (elm)->field.tqe_next->field.tqe_prev != \ + &(elm)->field.tqe_next) \ + QUEUEDEBUG_ABORT("TAILQ_* forw %p %s:%d", (elm), \ + __FILE__, __LINE__); \ + if (*(elm)->field.tqe_prev != (elm)) \ + QUEUEDEBUG_ABORT("TAILQ_* back %p %s:%d", (elm), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \ + if ((elm)->field.tqe_next == NULL && \ + (head)->tqh_last != &(elm)->field.tqe_next) \ + QUEUEDEBUG_ABORT("TAILQ_PREREMOVE head %p elm %p %s:%d",\ + (head), (elm), __FILE__, __LINE__); +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \ + (elm)->field.tqe_next = (void *)1L; \ + (elm)->field.tqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) +#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) +#define QUEUEDEBUG_TAILQ_OP(elm, field) +#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) +#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) +#endif + +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = TAILQ_END(head); \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \ + if (((elm)->field.tqe_next = (head)->tqh_first) != TAILQ_END(head))\ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \ + (elm)->field.tqe_next = TAILQ_END(head); \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != \ + TAILQ_END(head)) \ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + QUEUEDEBUG_TAILQ_OP((listelm), field) \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \ + QUEUEDEBUG_TAILQ_OP((elm), field) \ + if (((elm)->field.tqe_next) != TAILQ_END(head)) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ +} while (/*CONSTCOND*/0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != \ + TAILQ_END(head)) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ +} while (/*CONSTCOND*/0) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_END(head) NULL +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) +#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head)) + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + (head)->stqh_first = NULL; \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (head)->stqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.stqe_next = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &(elm)->field.stqe_next; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ + (head)->stqh_last = &(elm)->field.stqe_next; \ + (listelm)->field.stqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ + (head)->stqh_last = &(head)->stqh_first; \ +} while (/*CONSTCOND*/0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if ((head)->stqh_first == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (curelm->field.stqe_next != (elm)) \ + curelm = curelm->field.stqe_next; \ + if ((curelm->field.stqe_next = \ + curelm->field.stqe_next->field.stqe_next) == NULL) \ + (head)->stqh_last = &(curelm)->field.stqe_next; \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->stqh_first); \ + (var); \ + (var) = ((var)->field.stqe_next)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (/*CONSTCOND*/0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->stqh_last) - offsetof(struct type, field)))) + + +#ifndef _KERNEL +/* + * Circular queue definitions. Do not use. We still keep the macros + * for compatibility but because of pointer aliasing issues their use + * is discouraged! + */ + +/* + * __launder_type(): We use this ugly hack to work around the the compiler + * noticing that two types may not alias each other and elide tests in code. + * We hit this in the CIRCLEQ macros when comparing 'struct name *' and + * 'struct type *' (see CIRCLEQ_HEAD()). Modern compilers (such as GCC + * 4.8) declare these comparisons as always false, causing the code to + * not run as designed. + * + * This hack is only to be used for comparisons and thus can be fully const. + * Do not use for assignment. + * + * If we ever choose to change the ABI of the CIRCLEQ macros, we could fix + * this by changing the head/tail sentinal values, but see the note above + * this one. + */ +static __inline const void * __launder_type(const void *); +static __inline const void * +__launder_type(const void *__x) +{ + __asm __volatile("" : "+r" (__x)); + return __x; +} + +#if defined(QUEUEDEBUG) +#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) \ + if ((head)->cqh_first != CIRCLEQ_ENDC(head) && \ + (head)->cqh_first->field.cqe_prev != CIRCLEQ_ENDC(head)) \ + QUEUEDEBUG_ABORT("CIRCLEQ head forw %p %s:%d", (head), \ + __FILE__, __LINE__); \ + if ((head)->cqh_last != CIRCLEQ_ENDC(head) && \ + (head)->cqh_last->field.cqe_next != CIRCLEQ_ENDC(head)) \ + QUEUEDEBUG_ABORT("CIRCLEQ head back %p %s:%d", (head), \ + __FILE__, __LINE__); +#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) \ + if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) { \ + if ((head)->cqh_last != (elm)) \ + QUEUEDEBUG_ABORT("CIRCLEQ elm last %p %s:%d", \ + (elm), __FILE__, __LINE__); \ + } else { \ + if ((elm)->field.cqe_next->field.cqe_prev != (elm)) \ + QUEUEDEBUG_ABORT("CIRCLEQ elm forw %p %s:%d", \ + (elm), __FILE__, __LINE__); \ + } \ + if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) { \ + if ((head)->cqh_first != (elm)) \ + QUEUEDEBUG_ABORT("CIRCLEQ elm first %p %s:%d", \ + (elm), __FILE__, __LINE__); \ + } else { \ + if ((elm)->field.cqe_prev->field.cqe_next != (elm)) \ + QUEUEDEBUG_ABORT("CIRCLEQ elm prev %p %s:%d", \ + (elm), __FILE__, __LINE__); \ + } +#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) \ + (elm)->field.cqe_next = (void *)1L; \ + (elm)->field.cqe_prev = (void *)1L; +#else +#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) +#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) +#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) +#endif + +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = CIRCLEQ_END(head); \ + (head)->cqh_last = CIRCLEQ_END(head); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == CIRCLEQ_ENDC(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = CIRCLEQ_END(head); \ + if ((head)->cqh_last == CIRCLEQ_ENDC(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + (elm)->field.cqe_next = CIRCLEQ_END(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == CIRCLEQ_ENDC(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ + QUEUEDEBUG_CIRCLEQ_ELM((head), (elm), field) \ + if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ + QUEUEDEBUG_CIRCLEQ_POSTREMOVE((elm), field) \ +} while (/*CONSTCOND*/0) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for ((var) = ((head)->cqh_first); \ + (var) != CIRCLEQ_ENDC(head); \ + (var) = ((var)->field.cqe_next)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = ((head)->cqh_last); \ + (var) != CIRCLEQ_ENDC(head); \ + (var) = ((var)->field.cqe_prev)) + +/* + * Circular queue access methods. + */ +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +/* For comparisons */ +#define CIRCLEQ_ENDC(head) (__launder_type(head)) +/* For assignments */ +#define CIRCLEQ_END(head) ((void *)(head)) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#define CIRCLEQ_EMPTY(head) \ + (CIRCLEQ_FIRST(head) == CIRCLEQ_ENDC(head)) + +#define CIRCLEQ_LOOP_NEXT(head, elm, field) \ + (((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \ + ? ((head)->cqh_first) \ + : (elm->field.cqe_next)) +#define CIRCLEQ_LOOP_PREV(head, elm, field) \ + (((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \ + ? ((head)->cqh_last) \ + : (elm->field.cqe_prev)) +#endif /* !_KERNEL */ + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/src/includes/rc-misc.h b/src/includes/rc-misc.h new file mode 100644 index 0000000..95ccbc3 --- /dev/null +++ b/src/includes/rc-misc.h @@ -0,0 +1,75 @@ +/* + * rc-misc.h + * This is private to us and not for user consumption +*/ + +/* + * Copyright (c) 2007-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. + */ + +#ifndef __RC_MISC_H__ +#define __RC_MISC_H__ + +#include +#include +#include +#include +#include +#include +#include + +#include "helpers.h" + +#define RC_LEVEL_BOOT "boot" +#define RC_LEVEL_DEFAULT "default" + +#define RC_DEPTREE_CACHE RC_SVCDIR "/deptree" +#define RC_DEPTREE_SKEWED RC_SVCDIR "/clock-skewed" +#define RC_KRUNLEVEL RC_SVCDIR "/krunlevel" +#define RC_STARTING RC_SVCDIR "/rc.starting" +#define RC_STOPPING RC_SVCDIR "/rc.stopping" + +#define RC_SVCDIR_STARTING RC_SVCDIR "/starting" +#define RC_SVCDIR_INACTIVE RC_SVCDIR "/inactive" +#define RC_SVCDIR_STARTED RC_SVCDIR "/started" +#define RC_SVCDIR_COLDPLUGGED RC_SVCDIR "/coldplugged" + +char *rc_conf_value(const char *var); +bool rc_conf_yesno(const char *var); +void env_filter(void); +void env_config(void); +int signal_setup(int sig, void (*handler)(int)); +int svc_lock(const char *); +int svc_unlock(const char *, int); +pid_t exec_service(const char *, const char *); + +/* + * Check whether path is writable or not, + * this also works properly with read-only filesystems + */ +int is_writable(const char *); + +#define service_start(service) exec_service(service, "start"); +#define service_stop(service) exec_service(service, "stop"); + +int parse_mode(mode_t *, char *); + +/* Handy function so we can wrap einfo around our deptree */ +RC_DEPTREE *_rc_deptree_load (int, int *); + +/* Test to see if we can see pid 1 or not */ +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); + +#endif diff --git a/src/includes/rc-wtmp.h b/src/includes/rc-wtmp.h new file mode 100644 index 0000000..6645774 --- /dev/null +++ b/src/includes/rc-wtmp.h @@ -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 + +void log_wtmp(const char *user, const char *id, pid_t pid, int type, + const char *line); + +#endif diff --git a/src/libeinfo/.gitignore b/src/libeinfo/.gitignore new file mode 100644 index 0000000..373284d --- /dev/null +++ b/src/libeinfo/.gitignore @@ -0,0 +1 @@ +libeinfo.so.1 diff --git a/src/libeinfo/Makefile b/src/libeinfo/Makefile new file mode 100644 index 0000000..e6ccb65 --- /dev/null +++ b/src/libeinfo/Makefile @@ -0,0 +1,12 @@ +LIB= einfo +SHLIB_MAJOR= 1 +SRCS= libeinfo.c +INCS= einfo.h +VERSION_MAP= einfo.map + +LOCAL_CPPFLAGS+= -I../includes + +MK= ../../mk +include ${MK}/lib.mk +include ${MK}/cc.mk +include ${MK}/termcap.mk diff --git a/src/libeinfo/einfo.h b/src/libeinfo/einfo.h new file mode 100644 index 0000000..2b32580 --- /dev/null +++ b/src/libeinfo/einfo.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2007-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. + */ + +#ifndef __EINFO_H__ +#define __EINFO_H__ + +#if defined(__GNUC__) +# define EINFO_PRINTF(a, b) __attribute__((__format__(__printf__, a, b))) +# define EINFO_XPRINTF(a, b) __attribute__((__noreturn__,__format__(__printf__, a, b))) +#else +# define EINFO_PRINTF(a, b) +# define EINFO_XPRINTF(a, b) +#endif + +#include +#include + +/* Although OpenRC requires C99, linking to us should not. */ +#ifdef restrict +# define EINFO_RESTRICT restrict +#else +# ifdef __restrict +# define EINFO_RESTRICT __restrict +# else +# define EINFO_RESTRICT +# endif +#endif + +/* __BEGIN_DECLS */ +#ifdef __cplusplus +extern "C" { +#endif + +/*! @brief Color types to use */ +typedef enum +{ + ECOLOR_NORMAL = 1, + ECOLOR_GOOD = 2, + ECOLOR_WARN = 3, + ECOLOR_BAD = 4, + ECOLOR_HILITE = 5, + ECOLOR_BRACKET = 6, + ECOLOR_HYPERBOLA = 7, +} ECOLOR; + +/*! @brief Returns the ASCII code for the color */ +const char *ecolor(ECOLOR); + +/*! @brief Writes to syslog. */ +void elog(int, const char * EINFO_RESTRICT, ...) EINFO_PRINTF(2, 3); + +/*! + * @brief Display informational messages. + * + * The einfo family of functions display messages in a consistent manner + * across applications. Basically they prefix the message with + * " * ". If the terminal can handle color then we color the * based on + * the command used. Otherwise we are identical to the printf function. + * + * - einfo - green + * - ewarn - yellow + * - eerror - red + * + * The n suffix denotes that no new line should be printed. + * The v suffix means only print if EINFO_VERBOSE is yes. + * The x suffix means function will exit() returning failure. + */ +/*@{*/ +int einfon(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int ewarnn(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int eerrorn(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int einfo(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int ewarn(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +void ewarnx(const char * __EINFO_RESTRICT, ...) EINFO_XPRINTF(1, 2); +int eerror(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +void eerrorx(const char * __EINFO_RESTRICT, ...) EINFO_XPRINTF(1, 2); + +int einfovn(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int ewarnvn(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int ebeginvn(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int eendvn(int, const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(2, 3); +int ewendvn(int, const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(2, 3); +int einfov(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int ewarnv(const char * __EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +/*@}*/ + +/*! @ingroup ebegin + * @brief Display informational messages that may take some time. + * + * Similar to einfo, but we add ... to the end of the message */ +/*@{*/ +int ebeginv(const char * EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +int ebegin(const char * EINFO_RESTRICT, ...) EINFO_PRINTF(1, 2); +/*@}*/ + +/*! @ingroup eend + * @brief End an ebegin. + * + * If you ebegin, you should eend also. + * eend places [ ok ] or [ !! ] at the end of the terminal line depending on + * retval (0 or ok, anything else for !!) + * + * ebracket allows you to specifiy the position, color and message */ +/*@{*/ +int eend(int, const char * EINFO_RESTRICT, ...) EINFO_PRINTF(2, 3); +int ewend(int, const char * EINFO_RESTRICT, ...) EINFO_PRINTF(2, 3); +void ebracket(int, ECOLOR, const char * EINFO_RESTRICT); + +int eendv(int, const char * EINFO_RESTRICT, ...) EINFO_PRINTF(2, 3); +int ewendv(int, const char * EINFO_RESTRICT, ...) EINFO_PRINTF(2, 3); +/*@}*/ + +/*! @ingroup eindent + * @brief Indents the einfo lines. + * + * For each indent you should outdent when done */ +/*@{*/ +void eindent(void); +void eoutdent(void); +void eindentv(void); +void eoutdentv(void); + +/*! @brief Prefix each einfo line with something */ +void eprefix(const char * EINFO_RESTRICT); + +/* __END_DECLS */ +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libeinfo/einfo.map b/src/libeinfo/einfo.map new file mode 100644 index 0000000..428a895 --- /dev/null +++ b/src/libeinfo/einfo.map @@ -0,0 +1,35 @@ +EINFO_1.0 { +global: + ecolor; + elog; + einfon; + ewarnn; + eerrorn; + einfo; + ewarn; + ewarnx; + eerror; + eerrorx; + einfovn; + ewarnvn; + ebeginvn; + eendvn; + ewendvn; + einfov; + ewarnv; + ebeginv; + ebegin; + eend; + ewend; + ebracket; + eendv; + ewendv; + eindent; + eoutdent; + eindentv; + eoutdentv; + eprefix; + +local: + *; +}; diff --git a/src/libeinfo/libeinfo.c b/src/libeinfo/libeinfo.c new file mode 100644 index 0000000..4ed1c6c --- /dev/null +++ b/src/libeinfo/libeinfo.c @@ -0,0 +1,1059 @@ +/* + einfo.c + Informational functions +*/ + +/* + * Copyright (c) 2007-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. + */ + +const char libeinfo_copyright[] = "Copyright (c) 2007-2008 Roy Marples"; + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_TERMCAP +# include +#endif +#include + +#include "einfo.h" +#include "helpers.h" +#include "hidden-visibility.h" + +hidden_proto(ecolor) +hidden_proto(ebegin) +hidden_proto(ebeginv) +hidden_proto(ebracket) +hidden_proto(eend) +hidden_proto(eendv) +hidden_proto(eerror) +hidden_proto(eerrorn) +hidden_proto(eerrorx) +hidden_proto(eindent) +hidden_proto(eindentv) +hidden_proto(einfo) +hidden_proto(einfon) +hidden_proto(einfov) +hidden_proto(einfovn) +hidden_proto(elog) +hidden_proto(eoutdent) +hidden_proto(eoutdentv) +hidden_proto(eprefix) +hidden_proto(ewarn) +hidden_proto(ewarnn) +hidden_proto(ewarnv) +hidden_proto(ewarnvn) +hidden_proto(ewarnx) +hidden_proto(ewend) +hidden_proto(ewendv) + +/* Incase we cannot work out how many columns from ioctl, supply a default */ +#define DEFAULT_COLS 80 + +#define OK "ok" +#define NOT_OK "!!" + +/* Number of spaces for an indent */ +#define INDENT_WIDTH 2 + +/* How wide can the indent go? */ +#define INDENT_MAX 40 + +/* Default colours */ +#define GOOD 2 +#define WARN 3 +#define BAD 1 +#define HILITE 6 +#define BRACKET 4 +#define HYPERBOLA 7 + +/* We fallback to these escape codes if termcap isn't available + * like say /usr isn't mounted */ +#define AF "\033[3%dm" +#define CE "\033[K" +#define CH "\033[%dC" +#define MD "\033[1m" +#define ME "\033[m" +#define UP "\033[A" + +#define _GET_CAP(_d, _c) strlcpy(_d, tgoto(_c, 0, 0), sizeof(_d)); +#define _ASSIGN_CAP(_v) do { \ + _v = p; \ + p += strlcpy(p, tmp, sizeof(ebuffer) - (p - ebuffer)) + 1; \ + } while (0) + +/* A pointer to a string to prefix to einfo/ewarn/eerror messages */ +static const char *_eprefix = NULL; + +/* Buffers and structures to hold the final colours */ +static char ebuffer[100]; +struct ecolor { + ECOLOR color; + int def; + const char *name; +}; +static char nullstr = '\0'; + +static const struct ecolor ecolors[] = { + { ECOLOR_GOOD, GOOD, "good" }, + { ECOLOR_WARN, WARN, "warn" }, + { ECOLOR_BAD, BAD, "bad" }, + { ECOLOR_HILITE, HILITE, "hilite" }, + { ECOLOR_BRACKET, BRACKET, "bracket" }, + { ECOLOR_HYPERBOLA, HYPERBOLA, "hyperbola" }, + { ECOLOR_NORMAL, 0, NULL }, +}; +static const char *ecolors_str[ARRAY_SIZE(ecolors)]; + +static char *flush = NULL; +static char *up = NULL; +static char *goto_column = NULL; + +static const char *term = NULL; +static bool term_is_cons25 = false; + +/* Termcap buffers and pointers + * Static buffers suck hard, but some termcap implementations require them */ +#ifdef HAVE_TERMCAP +static char termcapbuf[2048]; +static char tcapbuf[512]; +#else +/* No curses support, so we hardcode a list of colour capable terms + * Only terminals without "color" in the name need to be explicitly listed */ +static const char *const color_terms[] = { + "Eterm", + "ansi", + "con132x25", + "con132x30", + "con132x43", + "con132x60", + "con80x25", + "con80x28", + "con80x30", + "con80x43", + "con80x50", + "con80x60", + "cons25", + "console", + "cygwin", + "dtterm", + "gnome", + "konsole", + "kterm", + "linux", + "linux-c", + "mlterm", + "putty", + "rxvt", + "rxvt-cygwin", + "rxvt-cygwin-native", + "rxvt-unicode", + "screen", + "screen-bce", + "screen-w", + "screen.linux", + "vt100", + "vt220", + "wsvt25", + "xterm", + "xterm-debian", + NULL +}; +#endif + +/* strlcat and strlcpy are nice, shame glibc does not define them */ +#ifdef __GLIBC__ +# if ! defined (__UCLIBC__) && ! defined (__dietlibc__) +static size_t +strlcat(char *dst, const char *src, size_t size) +{ + char *d = dst; + const char *s = src; + size_t src_n = size; + size_t dst_n; + + while (src_n-- != 0 && *d != '\0') + d++; + dst_n = d - dst; + src_n = size - dst_n; + + if (src_n == 0) + return dst_n + strlen(src); + + while (*s != '\0') { + if (src_n != 1) { + *d++ = *s; + src_n--; + } + s++; + } + *d = '\0'; + + return dst_n + (s - src); +} +# endif +#endif + +static bool +yesno(const char *value) +{ + if (!value) { + errno = ENOENT; + return false; + } + + if (strcasecmp(value, "yes") == 0 || + strcasecmp(value, "y") == 0 || + strcasecmp(value, "true") == 0 || + strcasecmp(value, "on") == 0 || + strcasecmp(value, "1") == 0) + return true; + + if (strcasecmp(value, "no") != 0 && + strcasecmp(value, "n") != 0 && + strcasecmp(value, "false") != 0 && + strcasecmp(value, "off") != 0 && + strcasecmp(value, "0") != 0) + errno = EINVAL; + + return false; +} + +static bool +noyes(const char *value) +{ + int serrno = errno; + bool retval; + + errno = 0; + retval = yesno(value); + if (errno == 0) { + retval = !retval; + errno = serrno; + } + + return retval; +} + +static bool +is_quiet(void) +{ + return yesno(getenv("EINFO_QUIET")); +} + +static bool +is_really_quiet(void) +{ + return yesno(getenv("EERROR_QUIET")); +} + +static bool +is_verbose(void) +{ + return yesno(getenv ("EINFO_VERBOSE")); +} + +/* Fake tgoto call - very crapy, but works for our needs */ +#ifndef HAVE_TERMCAP +static char * +tgoto(const char *cap, int col, int line) +{ + static char buf[20]; + char *p, *e, c, dbuf[6]; + int oncol = 0, which = line, i; + + p = buf; + e = p + sizeof(buf); + while ((c = *cap++)) { + if (c != '%' || ((c = *cap++) == '%')) { + *p++ = c; + if (p >= e) { + errno = E2BIG; + return NULL; + } + continue; + } + switch (c) { + case '3': + case '2': + case 'd': + i = 0; + do + dbuf[i++] = which % 10 | '0'; + while ((which /= 10)); + if (c != 'd') { + c -= '0'; + if (i > c) { + errno = EINVAL; + return NULL; + } + while (i < c) + dbuf[i++] = '0'; + } + if (p + i >= e) { + errno = E2BIG; + return NULL; + } + do + *p++ = dbuf[--i]; + while (i); + break; + case 'r': + oncol = 0; + break; + case 'i': + col++; + line++; + which++; + continue; + default: + errno = EINVAL; + return NULL; + } + + oncol = 1 - oncol; + which = oncol ? col : line; + } + *p = '\0'; + return buf; +} +#endif + +static bool +colour_terminal(FILE * EINFO_RESTRICT f) +{ + static int in_colour = -1; + char *e, *ee, *end, *d, *p; + int c; + const char *_af = NULL, *_ce = NULL, *_ch = NULL; + const char *_md = NULL, *_me = NULL, *_up = NULL; + const char *bold; + char tmp[100]; + unsigned int i = 0; +#ifdef HAVE_TERMCAP + char *bp; +#endif + + if (f && !isatty(fileno(f))) + return false; + + if (noyes(getenv("EINFO_COLOR"))) + return false; + + if (in_colour == 0) + return false; + if (in_colour == 1) + return true; + + term_is_cons25 = false; + if (!term) { + term = getenv("TERM"); + if (!term) + return false; + } + if (strcmp(term, "cons25") == 0) + term_is_cons25 = true; + +#ifdef HAVE_TERMCAP + /* Check termcap to see if we can do colour or not */ + if (tgetent(termcapbuf, term) == 1) { + bp = tcapbuf; + _af = tgetstr("AF", &bp); + _ce = tgetstr("ce", &bp); + _ch = tgetstr("ch", &bp); + /* Our ch use also works with RI .... for now */ + if (!_ch) + _ch = tgetstr("RI", &bp); + _md = tgetstr("md", &bp); + _me = tgetstr("me", &bp); + _up = tgetstr("up", &bp); + } + + /* Cheat here as vanilla BSD has the whole termcap info in /usr + * which is not available to us when we boot */ + if (term_is_cons25 || strcmp(term, "wsvt25") == 0) { +#else + if (strstr(term, "color")) + in_colour = 1; + + while (color_terms[i] && in_colour != 1) { + if (strcmp(color_terms[i], term) == 0) { + in_colour = 1; + } + i++; + } + + if (in_colour != 1) { + in_colour = 0; + return false; + } +#endif + if (!_af) + _af = AF; + if (!_ce) + _ce = CE; + if (!_ch) + _ch = CH; + if (!_md) + _md = MD; + if (!_me) + _me = ME; + if (!_up) + _up = UP; +#ifdef HAVE_TERMCAP + } + + if (!_af || !_ce || !_me || !_md || !_up) { + in_colour = 0; + return false; + } + + /* Many termcap databases don't have ch or RI even though they + * do work */ + if (!_ch) + _ch = CH; +#endif + + /* Now setup our colours */ + p = ebuffer; + for (i = 0; i < ARRAY_SIZE(ecolors); ++i) { + tmp[0] = '\0'; + if (ecolors[i].name) { + bold = _md; + c = ecolors[i].def; + + /* See if the user wants to override the colour + * We use a :col;bold: format like 2;1: for bold green + * and 1;0: for a normal red */ + if ((e = getenv("EINFO_COLOR"))) { + ee = strstr(e, ecolors[i].name); + if (ee) + ee += strlen(ecolors[i].name); + + if (ee && *ee == '=') { + d = strdup(ee + 1); + if (d) { + end = strchr(d, ':'); + if (end) + *end = '\0'; + c = atoi(d); + end = strchr(d, ';'); + if (end && *++end == '0') + bold = _me; + free(d); + } + } + } + strlcpy(tmp, tgoto(bold, 0, 0), sizeof(tmp)); + strlcat(tmp, tgoto(_af, 0, c & 0x07), sizeof(tmp)); + } else + _GET_CAP(tmp, _me); + + if (tmp[0]) + _ASSIGN_CAP(ecolors_str[i]); + else + ecolors_str[i] = &nullstr; + } + + _GET_CAP(tmp, _ce); + _ASSIGN_CAP(flush); + _GET_CAP(tmp, _up); + _ASSIGN_CAP(up); + strlcpy(tmp, _ch, sizeof(tmp)); + _ASSIGN_CAP(goto_column); + + in_colour = 1; + return true; +} + +static int +get_term_columns(FILE * EINFO_RESTRICT stream) +{ + struct winsize ws; + char *env = getenv("COLUMNS"); + char *p; + int i; + + if (env) { + i = strtoimax(env, &p, 10); + if (!*p) + return i; + } + + if (ioctl(fileno(stream), TIOCGWINSZ, &ws) == 0) + return ws.ws_col; + + return DEFAULT_COLS; +} + +void +eprefix(const char *EINFO_RESTRICT prefix) +{ + _eprefix = prefix; +} +hidden_def(eprefix) + +static void EINFO_PRINTF(2, 0) +elogv(int level, const char *EINFO_RESTRICT fmt, va_list ap) +{ + char *e = getenv("EINFO_LOG"); + va_list apc; + + if (fmt && e) { + closelog(); + openlog(e, LOG_PID, LOG_DAEMON); + va_copy(apc, ap); + vsyslog(level, fmt, apc); + va_end(apc); + closelog(); + } +} + +void +elog(int level, const char *EINFO_RESTRICT fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + elogv(level, fmt, ap); + va_end(ap); +} +hidden_def(elog) + +static int +_eindent(FILE * EINFO_RESTRICT stream) +{ + char *env = getenv("EINFO_INDENT"); + int amount = 0; + char indent[INDENT_MAX]; + + if (env) { + errno = 0; + amount = strtoimax(env, NULL, 0); + if (errno != 0 || amount < 0) + amount = 0; + else if (amount > INDENT_MAX) + amount = INDENT_MAX; + + if (amount > 0) + memset(indent, ' ', (size_t)amount); + } + + /* Terminate it */ + memset(indent + amount, 0, 1); + return fprintf(stream, "%s", indent); +} + +static const char * +_ecolor(FILE * EINFO_RESTRICT f, ECOLOR color) +{ + unsigned int i; + + if (!colour_terminal(f)) + return ""; + + for (i = 0; i < ARRAY_SIZE(ecolors); ++i) + if (ecolors[i].color == color) + return ecolors_str[i]; + return ""; +} +hidden_def(ecolor) + +const char * +ecolor(ECOLOR color) +{ + FILE *f = stdout; + + /* Try and guess a valid tty */ + if (!isatty(fileno(f))) { + f = stderr; + if (!isatty(fileno(f))) { + f = stdin; + if (!isatty(fileno(f))) + f = NULL; + } + } + + return _ecolor(f, color); +} + +#define LASTCMD(_cmd) { \ + unsetenv("EINFO_LASTCMD"); \ + setenv("EINFO_LASTCMD", _cmd, 1); \ + } + +static int EINFO_PRINTF(3, 0) + _einfo(FILE *f, ECOLOR color, const char *EINFO_RESTRICT fmt, va_list va) +{ + int retval = 0; + char *last = getenv("EINFO_LASTCMD"); + va_list ap; + + if (last && + !colour_terminal(f) && + strcmp(last, "ewarn") != 0 && + last[strlen(last) - 1] == 'n') + fprintf(f, "\n"); + if (_eprefix) + fprintf(f, "%s%s%s|", _ecolor(f, color), _eprefix, _ecolor(f, ECOLOR_NORMAL)); + fprintf(f, " %s*%s ", _ecolor(f, color), _ecolor(f, ECOLOR_NORMAL)); + retval += _eindent(f); + va_copy(ap, va); + retval += vfprintf(f, fmt, ap) + 3; + va_end(ap); \ + if (colour_terminal(f)) + fprintf(f, "%s", flush); + return retval; +} + +#define _einfovn(fmt, ap) _einfo(stdout, ECOLOR_GOOD, fmt, ap) +#define _ewarnvn(fmt, ap) _einfo(stderr, ECOLOR_WARN, fmt, ap) +#define _eerrorvn(fmt, ap) _einfo(stderr, ECOLOR_BAD, fmt, ap) + +int +einfon(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || is_quiet()) + return 0; + va_start(ap, fmt); + retval = _einfovn(fmt, ap); + va_end(ap); + LASTCMD("einfon"); + return retval; +} +hidden_def(einfon) + +int +ewarnn(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || is_quiet()) + return 0; + va_start(ap, fmt); + retval = _ewarnvn(fmt, ap); + va_end(ap); + LASTCMD("ewarnn"); + return retval; +} +hidden_def(ewarnn) + +int +eerrorn(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || is_really_quiet()) + return 0; + va_start(ap, fmt); + retval = _eerrorvn(fmt, ap); + va_end(ap); + LASTCMD("errorn"); + return retval; +} +hidden_def(eerrorn) + +int +einfo(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || is_quiet()) + return 0; + va_start(ap, fmt); + retval = _einfovn(fmt, ap); + retval += printf("\n"); + va_end(ap); + LASTCMD("einfo"); + return retval; +} +hidden_def(einfo) + +int +ewarn(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || is_quiet()) + return 0; + va_start(ap, fmt); + elogv(LOG_WARNING, fmt, ap); + retval = _ewarnvn(fmt, ap); + retval += fprintf(stderr, "\n"); + va_end(ap); + LASTCMD("ewarn"); + return retval; +} +hidden_def(ewarn) + +void +ewarnx(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (fmt && !is_quiet()) { + va_start(ap, fmt); + elogv(LOG_WARNING, fmt, ap); + retval = _ewarnvn(fmt, ap); + va_end(ap); + retval += fprintf(stderr, "\n"); + } + exit(EXIT_FAILURE); +} +hidden_def(ewarnx) + +int +eerror(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || is_really_quiet()) + return 0; + va_start(ap, fmt); + elogv(LOG_ERR, fmt, ap); + retval = _eerrorvn(fmt, ap); + va_end(ap); + retval += fprintf(stderr, "\n"); + LASTCMD("eerror"); + return retval; +} +hidden_def(eerror) + +void +eerrorx(const char *EINFO_RESTRICT fmt, ...) +{ + va_list ap; + + if (fmt && !is_really_quiet()) { + va_start(ap, fmt); + elogv(LOG_ERR, fmt, ap); + _eerrorvn(fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + } + exit(EXIT_FAILURE); +} +hidden_def(eerrorx) + +int +ebegin(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || is_quiet()) + return 0; + va_start(ap, fmt); + retval = _einfovn(fmt, ap); + va_end(ap); + retval += printf(" ..."); + if (colour_terminal(stdout)) + retval += printf("\n"); + LASTCMD("ebegin"); + return retval; +} +hidden_def(ebegin) + +static void +_eend(FILE * EINFO_RESTRICT fp, int col, ECOLOR color, const char *msg) +{ + int i; + int cols; + + if (!msg) + return; + + cols = get_term_columns(fp) - (strlen(msg) + 5); + + /* cons25 is special - we need to remove one char, otherwise things + * do not align properly at all. */ + if (!term) { + term = getenv("TERM"); + if (term && strcmp(term, "cons25") == 0) + term_is_cons25 = true; + else + term_is_cons25 = false; + } + if (term_is_cons25) + cols--; + + if (cols > 0 && colour_terminal(fp)) { + fprintf(fp, "%s%s %s[%s %s %s]%s\n", up, tgoto(goto_column, 0, cols), + ecolor(ECOLOR_BRACKET), ecolor(color), msg, + ecolor(ECOLOR_BRACKET), ecolor(ECOLOR_NORMAL)); + } else { + if (col > 0) + for (i = 0; i < cols - col; i++) + fprintf(fp, " "); + fprintf(fp, " [ %s ]\n", msg); + } +} + +static int EINFO_PRINTF(3, 0) +_do_eend(const char *cmd, int retval, + const char *EINFO_RESTRICT fmt, va_list ap) +{ + int col = 0; + FILE *fp = stdout; + va_list apc; + + if (fmt && *fmt != '\0' && retval != 0) { + fp = stderr; + va_copy(apc, ap); + if (strcmp(cmd, "ewend") == 0) + col = _ewarnvn(fmt, apc); + else + col = _eerrorvn(fmt, apc); + col += fprintf(fp, "\n"); + va_end(apc); + } + _eend(fp, col, + retval == 0 ? ECOLOR_GOOD : ECOLOR_BAD, + retval == 0 ? OK : NOT_OK); + return retval; +} + +int +eend(int retval, const char *EINFO_RESTRICT fmt, ...) +{ + va_list ap; + + if (is_quiet()) + return retval; + va_start(ap, fmt); + _do_eend("eend", retval, fmt, ap); + va_end(ap); + LASTCMD("eend"); + return retval; +} +hidden_def(eend) + +int +ewend(int retval, const char *EINFO_RESTRICT fmt, ...) +{ + va_list ap; + + if (is_quiet()) + return retval; + va_start(ap, fmt); + _do_eend("ewend", retval, fmt, ap); + va_end(ap); + LASTCMD("ewend"); + return retval; +} +hidden_def(ewend) + +void +ebracket(int col, ECOLOR color, const char *msg) +{ + _eend(stdout, col, color, msg); +} +hidden_def(ebracket) + +void +eindent(void) +{ + char *env = getenv("EINFO_INDENT"); + int amount = 0; + char num[10]; + + if (env) { + errno = 0; + amount = strtoimax(env, NULL, 0); + if (errno != 0) + amount = 0; + } + amount += INDENT_WIDTH; + if (amount > INDENT_MAX) + amount = INDENT_MAX; + snprintf(num, 10, "%08d", amount); + setenv("EINFO_INDENT", num, 1); +} +hidden_def(eindent) + +void eoutdent(void) +{ + char *env = getenv("EINFO_INDENT"); + int amount = 0; + char num[10]; + int serrno = errno; + + if (!env) + return; + errno = 0; + amount = strtoimax(env, NULL, 0); + if (errno != 0) + amount = 0; + else + amount -= INDENT_WIDTH; + if (amount <= 0) + unsetenv("EINFO_INDENT"); + else { + snprintf(num, 10, "%08d", amount); + setenv("EINFO_INDENT", num, 1); + } + errno = serrno; +} +hidden_def(eoutdent) + +int +einfovn(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || !is_verbose()) + return 0; + va_start(ap, fmt); + retval = _einfovn(fmt, ap); + va_end(ap); + LASTCMD("einfovn"); + return retval; +} +hidden_def(einfovn) + +int +ewarnvn(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || !is_verbose()) + return 0; + va_start(ap, fmt); + retval = _ewarnvn(fmt, ap); + va_end(ap); + LASTCMD("ewarnvn"); + return retval; +} +hidden_def(ewarnvn) + +int +einfov(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || !is_verbose()) + return 0; + va_start(ap, fmt); + retval = _einfovn(fmt, ap); + retval += printf("\n"); + va_end(ap); + LASTCMD("einfov"); + return retval; +} +hidden_def(einfov) + +int +ewarnv(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || !is_verbose()) + return 0; + va_start(ap, fmt); + retval = _ewarnvn(fmt, ap); + retval += printf("\n"); + va_end(ap); + LASTCMD("ewarnv"); + return retval; +} +hidden_def(ewarnv) + +int +ebeginv(const char *EINFO_RESTRICT fmt, ...) +{ + int retval; + va_list ap; + + if (!fmt || !is_verbose()) + return 0; + + va_start(ap, fmt); + retval = _einfovn(fmt, ap); + retval += printf(" ..."); + if (colour_terminal(stdout)) + retval += printf("\n"); + va_end(ap); + LASTCMD("ebeginv"); + return retval; +} +hidden_def(ebeginv) + +int +eendv(int retval, const char *EINFO_RESTRICT fmt, ...) +{ + va_list ap; + + if (!is_verbose()) + return 0; + va_start(ap, fmt); + _do_eend("eendv", retval, fmt, ap); + va_end(ap); + LASTCMD("eendv"); + return retval; +} +hidden_def(eendv) + +int +ewendv(int retval, const char *EINFO_RESTRICT fmt, ...) +{ + va_list ap; + + if (!is_verbose()) + return 0; + va_start(ap, fmt); + _do_eend("ewendv", retval, fmt, ap); + va_end(ap); + LASTCMD("ewendv"); + return retval; +} +hidden_def(ewendv) + +void +eindentv(void) +{ + if (is_verbose()) + eindent(); +} +hidden_def(eindentv) + +void +eoutdentv(void) +{ + if (is_verbose()) + eoutdent(); +} +hidden_def(eoutdentv) diff --git a/src/librc/.gitignore b/src/librc/.gitignore new file mode 100644 index 0000000..e7fafe8 --- /dev/null +++ b/src/librc/.gitignore @@ -0,0 +1,2 @@ +librc.so.1 +rc.h diff --git a/src/librc/Makefile b/src/librc/Makefile new file mode 100644 index 0000000..08c599e --- /dev/null +++ b/src/librc/Makefile @@ -0,0 +1,48 @@ +LIB= rc +SHLIB_MAJOR= 1 +SRCS= librc.c librc-daemon.c librc-depend.c librc-misc.c \ + librc-stringlist.c +INCS= rc.h +VERSION_MAP= rc.map + +LDADD+= ${LIBKVM} + +LOCAL_CPPFLAGS+= -I../includes + +MK= ../../mk +include ${MK}/lib.mk +include ${MK}/cc.mk + +# Massage our header file for our dirs +SED_CMD= -e 's:@PREFIX@:${PREFIX}:g' +SED_CMD+= -e 's:@LIB@:${LIBNAME}:g' +SED_CMD+= -e 's:@SYSCONFDIR@:${SYSCONFDIR}:g' +SED_CMD+= -e 's:@LIBEXECDIR@:${LIBEXECDIR}:g' +SED_CMD+= -e 's:@BINDIR@:${BINDIR}:g' +SED_CMD+= -e 's:@SBINDIR@:${SBINDIR}:g' + +_PKG_PREFIX= -e 's:.*@PKG_PREFIX@.*:\#undef RC_PKG_PREFIX:g' +ifneq (${PKG_PREFIX},) +ifneq (${PKG_PREFIX},/) +ifneq (${PKG_PREFIX},${PREFIX}) +_PKG_PREFIX= -e 's:@PKG_PREFIX@:${PKG_PREFIX}:g' +endif +endif +endif +SED_CMD+= ${_PKG_PREFIX} + +_LCL_PREFIX= -e 's:@LOCAL_PREFIX@::g' +ifneq (${LOCAL_PREFIX},) +ifneq (${LOCAL_PREFIX},/) +ifneq (${LOCAL_PREFIX},${PREFIX}) +_LCL_PREFIX= -e 's:@LOCAL_PREFIX@:${LOCAL_PREFIX}:g' +endif +endif +endif +SED_CMD+= ${_LCL_PREFIX} + +%.h: %.h.in + ${SED} ${SED_CMD} $< > $@ +${SRCS}: rc.h + +CLEANFILES+= rc.h diff --git a/src/librc/librc-daemon.c b/src/librc/librc-daemon.c new file mode 100644 index 0000000..56aaa91 --- /dev/null +++ b/src/librc/librc-daemon.c @@ -0,0 +1,633 @@ +/* + * librc-daemon + * Finds PID for given daemon criteria +*/ + +/* + * Copyright (c) 2007-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. + */ + +#include "queue.h" +#include "librc.h" + +#if defined(__linux__) || (defined (__FreeBSD_kernel__) && defined(__GLIBC__)) \ + || defined(__GNU__) +static bool +pid_is_exec(pid_t pid, const char *exec) +{ + char buffer[32]; + FILE *fp; + int c; + bool retval = false; + + exec = basename_c(exec); + snprintf(buffer, sizeof(buffer), "/proc/%d/stat", pid); + if ((fp = fopen(buffer, "r"))) { + while ((c = getc(fp)) != EOF && c != '(') + ; + if (c == '(') { + while ((c = getc(fp)) != EOF && c == *exec) + exec++; + if (c == ')' && *exec == '\0') + retval = true; + } + fclose(fp); + } + return retval; +} + +static bool +pid_is_argv(pid_t pid, const char *const *argv) +{ + char cmdline[32]; + int fd; + char buffer[PATH_MAX]; + char *p; + ssize_t bytes; + + snprintf(cmdline, sizeof(cmdline), "/proc/%u/cmdline", pid); + if ((fd = open(cmdline, O_RDONLY)) < 0) + return false; + bytes = read(fd, buffer, sizeof(buffer)); + close(fd); + if (bytes == -1) + return false; + + buffer[bytes] = '\0'; + p = buffer; + while (*argv) { + if (strcmp(*argv, p) != 0) + return false; + argv++; + p += strlen(p) + 1; + if ((unsigned)(p - buffer) > sizeof(buffer)) + return false; + } + return true; +} + +RC_PIDLIST * +rc_find_pids(const char *exec, const char *const *argv, uid_t uid, pid_t pid) +{ + DIR *procdir; + struct dirent *entry; + FILE *fp; + bool container_pid = false; + bool openvz_host = false; + char *line = NULL; + size_t len = 0; + pid_t p; + char buffer[PATH_MAX]; + struct stat sb; + pid_t openrc_pid = 0; + char *pp; + RC_PIDLIST *pids = NULL; + RC_PID *pi; + + if ((procdir = opendir("/proc")) == NULL) + return NULL; + + /* + We never match RC_OPENRC_PID if present so we avoid the below + scenario + + /etc/init.d/ntpd stop does + start-stop-daemon --stop --name ntpd + catching /etc/init.d/ntpd stop + + nasty + */ + + if ((pp = getenv("RC_OPENRC_PID"))) { + if (sscanf(pp, "%d", &openrc_pid) != 1) + openrc_pid = 0; + } + + /* + If /proc/self/status contains EnvID: 0, then we are an OpenVZ host, + and we will need to filter out processes that are inside containers + from our list of pids. + */ + + if (exists("/proc/self/status")) { + fp = fopen("/proc/self/status", "r"); + if (fp) { + while (! feof(fp)) { + rc_getline(&line, &len, fp); + if (strncmp(line, "envID:\t0", 8) == 0) { + openvz_host = true; + break; + } + } + fclose(fp); + } + } + + while ((entry = readdir(procdir)) != NULL) { + if (sscanf(entry->d_name, "%d", &p) != 1) + continue; + if (openrc_pid != 0 && openrc_pid == p) + continue; + if (pid != 0 && pid != p) + continue; + if (uid) { + snprintf(buffer, sizeof(buffer), "/proc/%d", p); + if (stat(buffer, &sb) != 0 || sb.st_uid != uid) + continue; + } + if (exec && !pid_is_exec(p, exec)) + continue; + if (argv && + !pid_is_argv(p, (const char *const *)argv)) + continue; + /* If this is an OpenVZ host, filter out container processes */ + if (openvz_host) { + snprintf(buffer, sizeof(buffer), "/proc/%d/status", p); + if (exists(buffer)) { + fp = fopen(buffer, "r"); + if (! fp) + continue; + while (! feof(fp)) { + rc_getline(&line, &len, fp); + if (strncmp(line, "envID:", 6) == 0) { + container_pid = ! (strncmp(line, "envID:\t0", 8) == 0); + break; + } + } + fclose(fp); + } + } + if (container_pid) + continue; + if (!pids) { + pids = xmalloc(sizeof(*pids)); + LIST_INIT(pids); + } + pi = xmalloc(sizeof(*pi)); + pi->pid = p; + LIST_INSERT_HEAD(pids, pi, entries); + } + if (line != NULL) + free(line); + closedir(procdir); + return pids; +} +librc_hidden_def(rc_find_pids) + +#elif BSD + +# if defined(__NetBSD__) || defined(__OpenBSD__) +# define _KVM_GETPROC2 +# define _KINFO_PROC kinfo_proc2 +# define _KVM_GETARGV kvm_getargv2 +# define _GET_KINFO_UID(kp) (kp.p_ruid) +# define _GET_KINFO_COMM(kp) (kp.p_comm) +# define _GET_KINFO_PID(kp) (kp.p_pid) +# define _KVM_PATH NULL +# define _KVM_FLAGS KVM_NO_FILES +# else +# ifndef KERN_PROC_PROC +# define KERN_PROC_PROC KERN_PROC_ALL +# endif +# define _KINFO_PROC kinfo_proc +# define _KVM_GETARGV kvm_getargv +# if defined(__DragonFly__) +# define _GET_KINFO_UID(kp) (kp.kp_ruid) +# define _GET_KINFO_COMM(kp) (kp.kp_comm) +# define _GET_KINFO_PID(kp) (kp.kp_pid) +# else +# define _GET_KINFO_UID(kp) (kp.ki_ruid) +# define _GET_KINFO_COMM(kp) (kp.ki_comm) +# define _GET_KINFO_PID(kp) (kp.ki_pid) +# endif +# define _KVM_PATH _PATH_DEVNULL +# define _KVM_FLAGS O_RDONLY +# endif + +RC_PIDLIST * +rc_find_pids(const char *exec, const char *const *argv, uid_t uid, pid_t pid) +{ + static kvm_t *kd = NULL; + char errbuf[_POSIX2_LINE_MAX]; + struct _KINFO_PROC *kp; + int i; + int processes = 0; + int pargc = 0; + char **pargv; + RC_PIDLIST *pids = NULL; + RC_PID *pi; + pid_t p; + const char *const *arg; + int match; + + if ((kd = kvm_openfiles(_KVM_PATH, _KVM_PATH, + NULL, _KVM_FLAGS, errbuf)) == NULL) + { + fprintf(stderr, "kvm_open: %s\n", errbuf); + return NULL; + } + +#ifdef _KVM_GETPROC2 + kp = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*kp), &processes); +#else + kp = kvm_getprocs(kd, KERN_PROC_PROC, 0, &processes); +#endif + if ((kp == NULL && processes > 0) || (kp != NULL && processes < 0)) { + fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(kd)); + kvm_close(kd); + return NULL; + } + + if (exec) + exec = basename_c(exec); + for (i = 0; i < processes; i++) { + p = _GET_KINFO_PID(kp[i]); + if (pid != 0 && pid != p) + continue; + if (uid != 0 && uid != _GET_KINFO_UID(kp[i])) + continue; + if (exec) { + if (!_GET_KINFO_COMM(kp[i]) || + strcmp(exec, _GET_KINFO_COMM(kp[i])) != 0) + continue; + } + if (argv && *argv) { + pargv = _KVM_GETARGV(kd, &kp[i], pargc); + if (!pargv || !*pargv) + continue; + arg = argv; + match = 1; + while (*arg && *pargv) + if (strcmp(*arg++, *pargv++) != 0) { + match = 0; + break; + } + if (!match) + continue; + } + if (!pids) { + pids = xmalloc(sizeof(*pids)); + LIST_INIT(pids); + } + pi = xmalloc(sizeof(*pi)); + pi->pid = p; + LIST_INSERT_HEAD(pids, pi, entries); + } + kvm_close(kd); + + return pids; +} +librc_hidden_def(rc_find_pids) + +#else +# error "Platform not supported!" +#endif + +static bool +_match_daemon(const char *path, const char *file, RC_STRINGLIST *match) +{ + char *line = NULL; + size_t len = 0; + char ffile[PATH_MAX]; + FILE *fp; + RC_STRING *m; + + snprintf(ffile, sizeof(ffile), "%s/%s", path, file); + fp = fopen(ffile, "r"); + + if (!fp) + return false; + + while ((rc_getline(&line, &len, fp))) { + TAILQ_FOREACH(m, match, entries) + if (strcmp(line, m->value) == 0) { + TAILQ_REMOVE(match, m, entries); + break; + } + if (!TAILQ_FIRST(match)) + break; + } + fclose(fp); + free(line); + if (TAILQ_FIRST(match)) + return false; + return true; +} + +static RC_STRINGLIST * +_match_list(const char *exec, const char *const *argv, const char *pidfile) +{ + RC_STRINGLIST *match = rc_stringlist_new(); + int i = 0; + size_t l; + char *m; + + if (exec) { + l = strlen(exec) + 6; + m = xmalloc(sizeof(char) * l); + snprintf(m, l, "exec=%s", exec); + rc_stringlist_add(match, m); + free(m); + } + + while (argv && argv[i]) { + l = strlen(*argv) + strlen("argv_=") + 16; + m = xmalloc(sizeof(char) * l); + snprintf(m, l, "argv_0=%s", argv[i++]); + rc_stringlist_add(match, m); + free(m); + } + + if (pidfile) { + l = strlen(pidfile) + 9; + m = xmalloc(sizeof(char) * l); + snprintf(m, l, "pidfile=%s", pidfile); + rc_stringlist_add(match, m); + free(m); + } + + return match; +} + +bool +rc_service_daemon_set(const char *service, const char *exec, + const char *const *argv, + const char *pidfile, bool started) +{ + char dirpath[PATH_MAX]; + char file[PATH_MAX]; + int nfiles = 0; + char oldfile[PATH_MAX] = { '\0' }; + bool retval = false; + DIR *dp; + struct dirent *d; + RC_STRINGLIST *match; + int i = 0; + FILE *fp; + + if (!exec && !pidfile) { + errno = EINVAL; + return false; + } + + snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s", + basename_c(service)); + + /* Regardless, erase any existing daemon info */ + if ((dp = opendir(dirpath))) { + match = _match_list(exec, argv, pidfile); + while ((d = readdir(dp))) { + if (d->d_name[0] == '.') + continue; + + snprintf(file, sizeof(file), "%s/%s", + dirpath, d->d_name); + nfiles++; + + if (!*oldfile) { + if (_match_daemon(dirpath, d->d_name, match)) { + unlink(file); + strlcpy(oldfile, file, sizeof(oldfile)); + nfiles--; + } + } else { + rename(file, oldfile); + strlcpy(oldfile, file, sizeof(oldfile)); + } + } + closedir(dp); + rc_stringlist_free(match); + } + + /* Now store our daemon info */ + if (started) { + if (mkdir(dirpath, 0755) == 0 || errno == EEXIST) { + snprintf(file, sizeof(file), "%s/%03d", + dirpath, nfiles + 1); + if ((fp = fopen(file, "w"))) { + fprintf(fp, "exec="); + if (exec) + fprintf(fp, "%s", exec); + while (argv && argv[i]) { + fprintf(fp, "\nargv_%d=%s", i, argv[i]); + i++; + } + fprintf(fp, "\npidfile="); + if (pidfile) + fprintf(fp, "%s", pidfile); + fprintf(fp, "\n"); + fclose(fp); + retval = true; + } + } + } else + retval = true; + + return retval; +} +librc_hidden_def(rc_service_daemon_set) + +bool +rc_service_started_daemon(const char *service, + const char *exec, const char *const *argv, int indx) +{ + char dirpath[PATH_MAX]; + char file[16]; + RC_STRINGLIST *match; + bool retval = false; + DIR *dp; + struct dirent *d; + + if (!service || !exec) + return false; + + snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s", + basename_c(service)); + match = _match_list(exec, argv, NULL); + + if (indx > 0) { + snprintf(file, sizeof(file), "%03d", indx); + retval = _match_daemon(dirpath, file, match); + } else { + if ((dp = opendir(dirpath))) { + while ((d = readdir(dp))) { + if (d->d_name[0] == '.') + continue; + retval = _match_daemon(dirpath, d->d_name, match); + if (retval) + break; + } + closedir(dp); + } + } + + rc_stringlist_free(match); + return retval; +} +librc_hidden_def(rc_service_started_daemon) + +bool +rc_service_daemons_crashed(const char *service) +{ + char dirpath[PATH_MAX]; + DIR *dp; + struct dirent *d; + char *path = dirpath; + FILE *fp; + char *line = NULL; + size_t len = 0; + char **argv = NULL; + char *exec = NULL; + char *name = NULL; + char *pidfile = NULL; + pid_t pid = 0; + RC_PIDLIST *pids; + RC_PID *p1; + RC_PID *p2; + char *p; + char *token; + bool retval = false; + RC_STRINGLIST *list = NULL; + RC_STRING *s; + size_t i; + char *ch_root; + char *spidfile; + + path += snprintf(dirpath, sizeof(dirpath), RC_SVCDIR "/daemons/%s", + basename_c(service)); + + if (!(dp = opendir(dirpath))) + return false; + + while ((d = readdir(dp))) { + if (d->d_name[0] == '.') + continue; + + snprintf(path, sizeof(dirpath) - (path - dirpath), "/%s", + d->d_name); + fp = fopen(dirpath, "r"); + if (!fp) + break; + + while ((rc_getline(&line, &len, fp))) { + p = line; + if ((token = strsep(&p, "=")) == NULL || !p) + continue; + + if (!*p) + continue; + + if (strcmp(token, "exec") == 0) { + if (exec) + free(exec); + exec = xstrdup(p); + } else if (strncmp(token, "argv_", 5) == 0) { + if (!list) + list = rc_stringlist_new(); + rc_stringlist_add(list, p); + } else if (strcmp(token, "name") == 0) { + if (name) + free(name); + name = xstrdup(p); + } else if (strcmp(token, "pidfile") == 0) { + pidfile = xstrdup(p); + break; + } + } + fclose(fp); + + ch_root = rc_service_value_get(basename_c(service), "chroot"); + spidfile = pidfile; + if (ch_root && pidfile) { + spidfile = xmalloc(strlen(ch_root) + strlen(pidfile) + 1); + strcpy(spidfile, ch_root); + strcat(spidfile, pidfile); + free(pidfile); + pidfile = spidfile; + } + + pid = 0; + if (pidfile) { + retval = true; + if ((fp = fopen(pidfile, "r"))) { + if (fscanf(fp, "%d", &pid) == 1) + retval = false; + fclose(fp); + } + free(pidfile); + pidfile = NULL; + + /* We have the pid, so no need to match + on exec or name */ + free(exec); + exec = NULL; + free(name); + name = NULL; + } else { + if (exec) { + if (!list) + list = rc_stringlist_new(); + if (!TAILQ_FIRST(list)) + rc_stringlist_add(list, exec); + + free(exec); + exec = NULL; + } + + if (list) { + /* We need to flatten our linked list + into an array */ + i = 0; + TAILQ_FOREACH(s, list, entries) + i++; + argv = xmalloc(sizeof(char *) * (i + 1)); + i = 0; + TAILQ_FOREACH(s, list, entries) + argv[i++] = s->value; + argv[i] = '\0'; + } + } + + if (!retval) { + if (pid != 0) { + if (kill(pid, 0) == -1 && errno == ESRCH) + retval = true; + } else if ((pids = rc_find_pids(exec, + (const char *const *)argv, + 0, pid))) + { + p1 = LIST_FIRST(pids); + while (p1) { + p2 = LIST_NEXT(p1, entries); + free(p1); + p1 = p2; + } + free(pids); + } else + retval = true; + } + rc_stringlist_free(list); + list = NULL; + free(argv); + argv = NULL; + free(exec); + exec = NULL; + free(name); + name = NULL; + if (retval) + break; + } + closedir(dp); + free(line); + + return retval; +} +librc_hidden_def(rc_service_daemons_crashed) diff --git a/src/librc/librc-depend.c b/src/librc/librc-depend.c new file mode 100644 index 0000000..1c99399 --- /dev/null +++ b/src/librc/librc-depend.c @@ -0,0 +1,1065 @@ +/* + * librc-depend + * rc service dependency and ordering + */ + +/* + * Copyright (c) 2007-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. + */ + +#include + +#include "queue.h" +#include "librc.h" + +#define GENDEP RC_LIBEXECDIR "/sh/gendepends.sh" + +#define RC_DEPCONFIG RC_SVCDIR "/depconfig" + +static const char *bootlevel = NULL; + +static char * +get_shell_value(char *string) +{ + char *p = string; + char *e; + + if (! string) + return NULL; + + if (*p == '\'') + p++; + + e = p + strlen(p) - 1; + if (*e == '\n') + *e-- = 0; + if (*e == '\'') + *e-- = 0; + + if (*p != 0) + return p; + + return NULL; +} + +void +rc_deptree_free(RC_DEPTREE *deptree) +{ + RC_DEPINFO *di; + RC_DEPINFO *di2; + RC_DEPTYPE *dt; + RC_DEPTYPE *dt2; + + if (!deptree) + return; + + di = TAILQ_FIRST(deptree); + while (di) { + di2 = TAILQ_NEXT(di, entries); + dt = TAILQ_FIRST(&di->depends); + while (dt) { + dt2 = TAILQ_NEXT(dt, entries); + rc_stringlist_free(dt->services); + free(dt->type); + free(dt); + dt = dt2; + } + free(di->service); + free(di); + di = di2; + } + free(deptree); +} +librc_hidden_def(rc_deptree_free) + +static RC_DEPINFO * +get_depinfo(const RC_DEPTREE *deptree, const char *service) +{ + RC_DEPINFO *di; + + TAILQ_FOREACH(di, deptree, entries) + if (strcmp(di->service, service) == 0) + return di; + return NULL; +} + +static RC_DEPTYPE * +get_deptype(const RC_DEPINFO *depinfo, const char *type) +{ + RC_DEPTYPE *dt; + + TAILQ_FOREACH(dt, &depinfo->depends, entries) + if (strcmp(dt->type, type) == 0) + return dt; + return NULL; +} + +RC_DEPTREE * +rc_deptree_load(void) { + return rc_deptree_load_file(RC_DEPTREE_CACHE); +} +librc_hidden_def(rc_deptree_load) + +RC_DEPTREE * +rc_deptree_load_file(const char *deptree_file) +{ + FILE *fp; + RC_DEPTREE *deptree; + RC_DEPINFO *depinfo = NULL; + RC_DEPTYPE *deptype = NULL; + char *line = NULL; + size_t len = 0; + char *type; + char *p; + char *e; + int i; + + if (!(fp = fopen(deptree_file, "r"))) + return NULL; + + deptree = xmalloc(sizeof(*deptree)); + TAILQ_INIT(deptree); + while ((rc_getline(&line, &len, fp))) + { + p = line; + e = strsep(&p, "_"); + if (!e || strcmp(e, "depinfo") != 0) + continue; + e = strsep(&p, "_"); + if (!e || sscanf(e, "%d", &i) != 1) + continue; + if (!(type = strsep(&p, "_="))) + continue; + if (strcmp(type, "service") == 0) { + /* Sanity */ + e = get_shell_value(p); + if (! e || *e == '\0') + continue; + depinfo = xmalloc(sizeof(*depinfo)); + TAILQ_INIT(&depinfo->depends); + depinfo->service = xstrdup(e); + TAILQ_INSERT_TAIL(deptree, depinfo, entries); + deptype = NULL; + continue; + } + e = strsep(&p, "="); + if (!e || sscanf(e, "%d", &i) != 1) + continue; + /* Sanity */ + e = get_shell_value(p); + if (!e || *e == '\0') + continue; + if (!deptype || strcmp(deptype->type, type) != 0) { + deptype = xmalloc(sizeof(*deptype)); + deptype->services = rc_stringlist_new(); + deptype->type = xstrdup(type); + TAILQ_INSERT_TAIL(&depinfo->depends, deptype, entries); + } + rc_stringlist_add(deptype->services, e); + } + fclose(fp); + free(line); + + return deptree; +} +librc_hidden_def(rc_deptree_load_file) + +static bool +valid_service(const char *runlevel, const char *service, const char *type) +{ + RC_SERVICE state; + + if (!runlevel || + strcmp(type, "ineed") == 0 || + strcmp(type, "needsme") == 0 || + strcmp(type, "iwant") == 0 || + strcmp(type, "wantsme") == 0) + return true; + + if (rc_service_in_runlevel(service, runlevel)) + return true; + if (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0) + return false; + if (strcmp(runlevel, RC_LEVEL_SHUTDOWN) == 0 && + strcmp(type, "iafter") == 0) + return false; + if (strcmp(runlevel, bootlevel) != 0) { + if (rc_service_in_runlevel(service, bootlevel)) + return true; + } + + state = rc_service_state(service); + if (state & RC_SERVICE_HOTPLUGGED || + state & RC_SERVICE_STARTED) + return true; + + return false; +} + +static bool +get_provided1(const char *runlevel, RC_STRINGLIST *providers, + RC_DEPTYPE *deptype, const char *level, + bool hotplugged, RC_SERVICE state) +{ + RC_STRING *service; + RC_SERVICE st; + bool retval = false; + bool ok; + const char *svc; + + TAILQ_FOREACH(service, deptype->services, entries) { + ok = true; + svc = service->value; + st = rc_service_state(svc); + + if (level) + ok = rc_service_in_runlevel(svc, level); + else if (hotplugged) + ok = (st & RC_SERVICE_HOTPLUGGED && + !rc_service_in_runlevel(svc, runlevel) && + !rc_service_in_runlevel(svc, bootlevel)); + if (!ok) + continue; + switch (state) { + case RC_SERVICE_STARTED: + ok = (st & RC_SERVICE_STARTED); + break; + case RC_SERVICE_INACTIVE: + case RC_SERVICE_STARTING: + case RC_SERVICE_STOPPING: + ok = (st & RC_SERVICE_STARTING || + st & RC_SERVICE_STOPPING || + st & RC_SERVICE_INACTIVE); + break; + default: + break; + } + if (!ok) + continue; + retval = true; + rc_stringlist_add(providers, svc); + } + + return retval; +} + +/* Work out if a service is provided by another service. + For example metalog provides logger. + We need to be able to handle syslogd providing logger too. + We do this by checking whats running, then what's starting/stopping, + then what's run in the runlevels and finally alphabetical order. + + If there are any bugs in rc-depend, they will probably be here as + provided dependancy can change depending on runlevel state. + */ +static RC_STRINGLIST * +get_provided(const RC_DEPINFO *depinfo, const char *runlevel, int options) +{ + RC_DEPTYPE *dt; + RC_STRINGLIST *providers = rc_stringlist_new(); + RC_STRING *service; + + dt = get_deptype(depinfo, "providedby"); + if (!dt) + return providers; + + /* If we are stopping then all depends are true, regardless of state. + This is especially true for net services as they could force a restart + of the local dns resolver which may depend on net. */ + if (options & RC_DEP_STOP) { + TAILQ_FOREACH(service, dt->services, entries) + rc_stringlist_add(providers, service->value); + return providers; + } + + /* If we're strict or starting, then only use what we have in our + * runlevel and bootlevel. If we starting then check hotplugged too. */ + if (options & RC_DEP_STRICT || options & RC_DEP_START) { + TAILQ_FOREACH(service, dt->services, entries) + if (rc_service_in_runlevel(service->value, runlevel) || + rc_service_in_runlevel(service->value, bootlevel) || + (options & RC_DEP_START && + rc_service_state(service->value) & RC_SERVICE_HOTPLUGGED)) + rc_stringlist_add(providers, service->value); + if (TAILQ_FIRST(providers)) + return providers; + } + + /* OK, we're not strict or there were no services in our runlevel. + * This is now where the logic gets a little fuzzy :) + * If there is >1 running service then we return NULL. + * We do this so we don't hang around waiting for inactive services and + * our need has already been satisfied as it's not strict. + * We apply this to these states in order:- + * started, starting | stopping | inactive, stopped + * Our sub preference in each of these is in order:- + * runlevel, hotplugged, bootlevel, any + */ +#define DO \ + if (TAILQ_FIRST(providers)) { \ + if (TAILQ_NEXT(TAILQ_FIRST(providers), entries)) { \ + rc_stringlist_free(providers); \ + providers = rc_stringlist_new(); \ + } \ + return providers; \ + } + + /* Anything running has to come first */ + if (get_provided1(runlevel, providers, dt, runlevel, false, RC_SERVICE_STARTED)) + { DO } + if (get_provided1(runlevel, providers, dt, NULL, true, RC_SERVICE_STARTED)) + { DO } + if (bootlevel && strcmp(runlevel, bootlevel) != 0 && + get_provided1(runlevel, providers, dt, bootlevel, false, RC_SERVICE_STARTED)) + { DO } + if (get_provided1(runlevel, providers, dt, NULL, false, RC_SERVICE_STARTED)) + { DO } + + /* Check starting services */ + if (get_provided1(runlevel, providers, dt, runlevel, false, RC_SERVICE_STARTING)) + return providers; + if (get_provided1(runlevel, providers, dt, NULL, true, RC_SERVICE_STARTING)) + return providers; + if (bootlevel && strcmp(runlevel, bootlevel) != 0 && + get_provided1(runlevel, providers, dt, bootlevel, false, RC_SERVICE_STARTING)) + return providers; + if (get_provided1(runlevel, providers, dt, NULL, false, RC_SERVICE_STARTING)) + return providers; + + /* Nothing started then. OK, lets get the stopped services */ + if (get_provided1(runlevel, providers, dt, runlevel, false, RC_SERVICE_STOPPED)) + return providers; + if (get_provided1(runlevel, providers, dt, NULL, true, RC_SERVICE_STOPPED)) + { DO } + if (bootlevel && (strcmp(runlevel, bootlevel) != 0) && + get_provided1(runlevel, providers, dt, bootlevel, false, RC_SERVICE_STOPPED)) + return providers; + + /* Still nothing? OK, list our first provided service. */ + service = TAILQ_FIRST(dt->services); + if (service != NULL) + rc_stringlist_add(providers, service->value); + + return providers; +} + +static void +visit_service(const RC_DEPTREE *deptree, + const RC_STRINGLIST *types, + RC_STRINGLIST *sorted, + RC_STRINGLIST *visited, + const RC_DEPINFO *depinfo, + const char *runlevel, int options) +{ + RC_STRING *type; + RC_STRING *service; + RC_DEPTYPE *dt; + RC_DEPINFO *di; + RC_STRINGLIST *provided; + RC_STRING *p; + const char *svcname; + + /* Check if we have already visited this service or not */ + TAILQ_FOREACH(type, visited, entries) + if (strcmp(type->value, depinfo->service) == 0) + return; + /* Add ourselves as a visited service */ + rc_stringlist_add(visited, depinfo->service); + + TAILQ_FOREACH(type, types, entries) + { + if (!(dt = get_deptype(depinfo, type->value))) + continue; + + TAILQ_FOREACH(service, dt->services, entries) { + if (!(options & RC_DEP_TRACE) || + strcmp(type->value, "iprovide") == 0) + { + rc_stringlist_add(sorted, service->value); + continue; + } + + if (!(di = get_depinfo(deptree, service->value))) + continue; + provided = get_provided(di, runlevel, options); + + if (TAILQ_FIRST(provided)) { + TAILQ_FOREACH(p, provided, entries) { + di = get_depinfo(deptree, p->value); + if (di && valid_service(runlevel, di->service, type->value)) + visit_service(deptree, types, sorted, visited, di, + runlevel, options | RC_DEP_TRACE); + } + } + else if (di && valid_service(runlevel, service->value, type->value)) + visit_service(deptree, types, sorted, visited, di, + runlevel, options | RC_DEP_TRACE); + + rc_stringlist_free(provided); + } + } + + /* Now visit the stuff we provide for */ + if (options & RC_DEP_TRACE && + (dt = get_deptype(depinfo, "iprovide"))) + { + TAILQ_FOREACH(service, dt->services, entries) { + if (!(di = get_depinfo(deptree, service->value))) + continue; + provided = get_provided(di, runlevel, options); + TAILQ_FOREACH(p, provided, entries) + if (strcmp(p->value, depinfo->service) == 0) { + visit_service(deptree, types, sorted, visited, di, + runlevel, options | RC_DEP_TRACE); + break; + } + rc_stringlist_free(provided); + } + } + + /* We've visited everything we need, so add ourselves unless we + are also the service calling us or we are provided by something */ + svcname = getenv("RC_SVCNAME"); + if (!svcname || strcmp(svcname, depinfo->service) != 0) { + if (!get_deptype(depinfo, "providedby")) + rc_stringlist_add(sorted, depinfo->service); + } +} + +RC_STRINGLIST * +rc_deptree_depend(const RC_DEPTREE *deptree, + const char *service, const char *type) +{ + RC_DEPINFO *di; + RC_DEPTYPE *dt; + RC_STRINGLIST *svcs; + RC_STRING *svc; + + svcs = rc_stringlist_new(); + if (!(di = get_depinfo(deptree, service)) || + !(dt = get_deptype(di, type))) + { + errno = ENOENT; + return svcs; + } + + /* For consistency, we copy the array */ + TAILQ_FOREACH(svc, dt->services, entries) + rc_stringlist_add(svcs, svc->value); + return svcs; +} +librc_hidden_def(rc_deptree_depend) + +RC_STRINGLIST * +rc_deptree_depends(const RC_DEPTREE *deptree, + const RC_STRINGLIST *types, + const RC_STRINGLIST *services, + const char *runlevel, int options) +{ + RC_STRINGLIST *sorted = rc_stringlist_new(); + RC_STRINGLIST *visited = rc_stringlist_new(); + RC_DEPINFO *di; + const RC_STRING *service; + + bootlevel = getenv("RC_BOOTLEVEL"); + if (!bootlevel) + bootlevel = RC_LEVEL_BOOT; + TAILQ_FOREACH(service, services, entries) { + if (!(di = get_depinfo(deptree, service->value))) { + errno = ENOENT; + continue; + } + if (types) + visit_service(deptree, types, sorted, visited, + di, runlevel, options); + } + rc_stringlist_free(visited); + return sorted; +} +librc_hidden_def(rc_deptree_depends) + +RC_STRINGLIST * +rc_deptree_order(const RC_DEPTREE *deptree, const char *runlevel, int options) +{ + RC_STRINGLIST *list; + RC_STRINGLIST *list2; + RC_STRINGLIST *types; + RC_STRINGLIST *services; + + bootlevel = getenv("RC_BOOTLEVEL"); + if (! bootlevel) + bootlevel = RC_LEVEL_BOOT; + + /* When shutting down, list all running services */ + if (strcmp(runlevel, RC_LEVEL_SINGLE) == 0 || + strcmp(runlevel, RC_LEVEL_SHUTDOWN) == 0) + { + list = rc_services_in_state(RC_SERVICE_STARTED); + list2 = rc_services_in_state(RC_SERVICE_INACTIVE); + TAILQ_CONCAT(list, list2, entries); + free(list2); + list2 = rc_services_in_state(RC_SERVICE_STARTING); + TAILQ_CONCAT(list, list2, entries); + free(list2); + } else { + list = rc_services_in_runlevel(RC_LEVEL_SYSINIT); + if (strcmp(runlevel, RC_LEVEL_SYSINIT) != 0) { + list2 = rc_services_in_runlevel(runlevel); + TAILQ_CONCAT(list, list2, entries); + free(list2); + list2 = rc_services_in_state(RC_SERVICE_HOTPLUGGED); + TAILQ_CONCAT(list, list2, entries); + free(list2); + /* If we're not the boot runlevel then add that too */ + if (strcmp(runlevel, bootlevel) != 0) { + list2 = rc_services_in_runlevel(bootlevel); + TAILQ_CONCAT(list, list2, entries); + free(list2); + } + } + } + + /* Now we have our lists, we need to pull in any dependencies + and order them */ + types = rc_stringlist_new(); + rc_stringlist_add(types, "ineed"); + rc_stringlist_add(types, "iuse"); + rc_stringlist_add(types, "iwant"); + rc_stringlist_add(types, "iafter"); + services = rc_deptree_depends(deptree, types, list, runlevel, + RC_DEP_STRICT | RC_DEP_TRACE | options); + rc_stringlist_free(list); + rc_stringlist_free(types); + return services; +} +librc_hidden_def(rc_deptree_order) + +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; + 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; + retval = false; + } + if (rel != NULL) { + if (*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; + retval = false; + } + if (rel != NULL) { + if (*rel > buf.st_mtime) { + if (file) + strlcpy(file, target, PATH_MAX); + *rel = buf.st_mtime; + } + } + } + + /* If not a dir then reset errno */ + if (!(dp = opendir(target))) { + errno = serrno; + return retval; + } + + /* Check all the entries in the dir */ + while ((d = readdir(dp))) { + if (d->d_name[0] == '.') + continue; + snprintf(path, sizeof(path), "%s/%s", target, d->d_name); + if (!mtime_check(source, path, newer, rel, file)) { + retval = false; + if (rel == NULL) + break; + } + } + closedir(dp); + return retval; +} + +bool +rc_newer_than(const char *source, const char *target, + time_t *newest, char *file) +{ + + return mtime_check(source, target, true, newest, file); +} +librc_hidden_def(rc_newer_than) + +bool +rc_older_than(const char *source, const char *target, + time_t *oldest, char *file) +{ + return mtime_check(source, target, false, oldest, file); +} +librc_hidden_def(rc_older_than) + +typedef struct deppair +{ + const char *depend; + const char *addto; +} DEPPAIR; + +static const DEPPAIR deppairs[] = { + { "ineed", "needsme" }, + { "iuse", "usesme" }, + { "iwant", "wantsme" }, + { "iafter", "ibefore" }, + { "ibefore", "iafter" }, + { "iprovide", "providedby" }, + { NULL, NULL } +}; + +static const char *const depdirs[] = +{ + RC_SVCDIR, + RC_SVCDIR "/starting", + RC_SVCDIR "/started", + RC_SVCDIR "/stopping", + RC_SVCDIR "/inactive", + RC_SVCDIR "/wasinactive", + RC_SVCDIR "/failed", + RC_SVCDIR "/hotplugged", + RC_SVCDIR "/daemons", + RC_SVCDIR "/options", + RC_SVCDIR "/exclusive", + RC_SVCDIR "/scheduled", + RC_SVCDIR "/tmp", + NULL +}; + +bool +rc_deptree_update_needed(time_t *newest, char *file) +{ + bool newer = false; + RC_STRINGLIST *config; + RC_STRING *s; + int i; + + /* Create base directories if needed */ + for (i = 0; depdirs[i]; i++) + if (mkdir(depdirs[i], 0755) != 0 && errno != EEXIST) + 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; +#ifdef RC_PKG_INITDIR + if (!rc_newer_than(RC_DEPTREE_CACHE, RC_PKG_INITDIR, newest, file)) + return true; +#endif +#ifdef RC_PKG_CONFDIR + if (!rc_newer_than(RC_DEPTREE_CACHE, RC_PKG_CONFDIR, newest, file)) + return true; +#endif +#ifdef RC_LOCAL_INITDIR + if (!rc_newer_than(RC_DEPTREE_CACHE, RC_LOCAL_INITDIR, newest, file)) + return true; +#endif +#ifdef RC_LOCAL_CONFDIR + if (!rc_newer_than(RC_DEPTREE_CACHE, RC_LOCAL_CONFDIR, newest, file)) + return true; +#endif + if (!rc_newer_than(RC_DEPTREE_CACHE, RC_CONF, newest, file)) + return true; + + /* 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; + } + } + rc_stringlist_free(config); + return newer; +} +librc_hidden_def(rc_deptree_update_needed) + +/* This is a 7 phase operation + Phase 1 is a shell script which loads each init script and config in turn + and echos their dependency info to stdout + Phase 2 takes that and populates a depinfo object with that data + Phase 3 adds any provided services to the depinfo object + Phase 4 scans that depinfo object and puts in backlinks + Phase 5 removes broken before dependencies + Phase 6 looks for duplicate services indicating a real and virtual service + with the same names + Phase 7 saves the depinfo object to disk + */ +bool +rc_deptree_update(void) +{ + FILE *fp; + RC_DEPTREE *deptree, *providers; + RC_DEPINFO *depinfo = NULL, *depinfo_np, *di; + RC_DEPTYPE *deptype = NULL, *dt_np, *dt, *provide; + RC_STRINGLIST *config, *dupes, *types, *sorted, *visited; + RC_STRING *s, *s2, *s2_np, *s3, *s4; + char *line = NULL; + size_t len = 0; + char *depend, *depends, *service, *type, *nosys, *onosys; + size_t i, k, l; + bool retval = true; + const char *sys = rc_sys(); + struct utsname uts; + int serrno; + + /* Some init scripts need RC_LIBEXECDIR to source stuff + Ideally we should be setting our full env instead */ + if (!getenv("RC_LIBEXECDIR")) + setenv("RC_LIBEXECDIR", RC_LIBEXECDIR, 0); + + if (uname(&uts) == 0) + setenv("RC_UNAME", uts.sysname, 1); + /* Phase 1 - source all init scripts and print dependencies */ + if (!(fp = popen(GENDEP, "r"))) + return false; + + deptree = xmalloc(sizeof(*deptree)); + TAILQ_INIT(deptree); + config = rc_stringlist_new(); + while ((rc_getline(&line, &len, fp))) + { + depends = line; + service = strsep(&depends, " "); + if (!service || !*service) + continue; + + type = strsep(&depends, " "); + if (!depinfo || strcmp(depinfo->service, service) != 0) { + deptype = NULL; + depinfo = get_depinfo(deptree, service); + if (!depinfo) { + depinfo = xmalloc(sizeof(*depinfo)); + TAILQ_INIT(&depinfo->depends); + depinfo->service = xstrdup(service); + TAILQ_INSERT_TAIL(deptree, depinfo, entries); + } + } + + /* We may not have any depends */ + if (!type || !depends) + continue; + + /* Get the type */ + if (strcmp(type, "config") != 0) { + if (!deptype || strcmp(deptype->type, type) != 0) + deptype = get_deptype(depinfo, type); + if (!deptype) { + deptype = xmalloc(sizeof(*deptype)); + deptype->type = xstrdup(type); + deptype->services = rc_stringlist_new(); + TAILQ_INSERT_TAIL(&depinfo->depends, deptype, entries); + } + } + + /* Now add each depend to our type. + We do this individually so we handle multiple spaces gracefully */ + while ((depend = strsep(&depends, " "))) + { + if (depend[0] == 0) + continue; + + if (strcmp(type, "config") == 0) { + rc_stringlist_addu(config, depend); + continue; + } + + /* Don't provide ourself */ + if (strcmp(type, "iprovide") == 0 && + strcmp(depend, service) == 0) + continue; + + /* .sh files are not init scripts */ + l = strlen(depend); + if (l > 2 && + depend[l - 3] == '.' && + depend[l - 2] == 's' && + depend[l - 1] == 'h') + continue; + + /* Remove our dependency if instructed */ + if (depend[0] == '!') { + rc_stringlist_delete(deptype->services, depend + 1); + continue; + } + + rc_stringlist_add(deptype->services, depend); + + /* We need to allow `after *; before local;` to work. + * Conversely, we need to allow 'before *; after modules' also */ + /* If we're before something, remove us from the after list */ + if (strcmp(type, "ibefore") == 0) { + if ((dt = get_deptype(depinfo, "iafter"))) + rc_stringlist_delete(dt->services, depend); + } + /* If we're after something, remove us from the before list */ + if (strcmp(type, "iafter") == 0 || + strcmp(type, "ineed") == 0 || + strcmp(type, "iwant") == 0 || + strcmp(type, "iuse") == 0) { + if ((dt = get_deptype(depinfo, "ibefore"))) + rc_stringlist_delete(dt->services, depend); + } + } + } + free(line); + pclose(fp); + + /* Phase 2 - if we're a special system, remove services that don't + * work for them. This doesn't stop them from being run directly. */ + if (sys) { + len = strlen(sys); + nosys = xmalloc(len + 2); + nosys[0] = '-'; + for (i = 0; i < len; i++) + nosys[i + 1] = (char)tolower((unsigned char)sys[i]); + nosys[i + 1] = '\0'; + + onosys = xmalloc(len + 3); + onosys[0] = 'n'; + onosys[1] = 'o'; + for (i = 0; i < len; i++) + onosys[i + 2] = (char)tolower((unsigned char)sys[i]); + onosys[i + 2] = '\0'; + + TAILQ_FOREACH_SAFE(depinfo, deptree, entries, depinfo_np) + if ((deptype = get_deptype(depinfo, "keyword"))) + TAILQ_FOREACH(s, deptype->services, entries) + if (strcmp(s->value, nosys) == 0 || + strcmp(s->value, onosys) == 0) + { + provide = get_deptype(depinfo, "iprovide"); + TAILQ_REMOVE(deptree, depinfo, entries); + TAILQ_FOREACH(di, deptree, entries) { + TAILQ_FOREACH_SAFE(dt, &di->depends, entries, dt_np) { + rc_stringlist_delete(dt->services, depinfo->service); + if (provide) + TAILQ_FOREACH(s2, provide->services, entries) + rc_stringlist_delete(dt->services, s2->value); + if (!TAILQ_FIRST(dt->services)) { + TAILQ_REMOVE(&di->depends, dt, entries); + free(dt->type); + free(dt->services); + free(dt); + } + } + } + } + free(nosys); + free(onosys); + } + + /* Phase 3 - add our providers to the tree */ + providers = xmalloc(sizeof(*providers)); + TAILQ_INIT(providers); + TAILQ_FOREACH(depinfo, deptree, entries) + if ((deptype = get_deptype(depinfo, "iprovide"))) + TAILQ_FOREACH(s, deptype->services, entries) { + TAILQ_FOREACH(di, providers, entries) + if (strcmp(di->service, s->value) == 0) + break; + if (!di) { + di = xmalloc(sizeof(*di)); + TAILQ_INIT(&di->depends); + di->service = xstrdup(s->value); + TAILQ_INSERT_TAIL(providers, di, entries); + } + } + TAILQ_CONCAT(deptree, providers, entries); + free(providers); + + /* Phase 4 - backreference our depends */ + TAILQ_FOREACH(depinfo, deptree, entries) + for (i = 0; deppairs[i].depend; i++) { + deptype = get_deptype(depinfo, deppairs[i].depend); + if (!deptype) + continue; + TAILQ_FOREACH(s, deptype->services, entries) { + di = get_depinfo(deptree, s->value); + if (!di) { + if (strcmp(deptype->type, "ineed") == 0) { + fprintf(stderr, + "Service `%s' needs non" + " existent service `%s'\n", + depinfo->service, s->value); + dt = get_deptype(depinfo, "broken"); + if (!dt) { + dt = xmalloc(sizeof(*dt)); + dt->type = xstrdup("broken"); + dt->services = rc_stringlist_new(); + TAILQ_INSERT_TAIL(&depinfo->depends, dt, entries); + } + rc_stringlist_addu(dt->services, s->value); + } + continue; + } + + dt = get_deptype(di, deppairs[i].addto); + if (!dt) { + dt = xmalloc(sizeof(*dt)); + dt->type = xstrdup(deppairs[i].addto); + dt->services = rc_stringlist_new(); + TAILQ_INSERT_TAIL(&di->depends, dt, entries); + } + rc_stringlist_addu(dt->services, depinfo->service); + } + } + + + /* Phase 5 - Remove broken before directives */ + types = rc_stringlist_new(); + rc_stringlist_add(types, "ineed"); + rc_stringlist_add(types, "iwant"); + rc_stringlist_add(types, "iuse"); + rc_stringlist_add(types, "iafter"); + TAILQ_FOREACH(depinfo, deptree, entries) { + deptype = get_deptype(depinfo, "ibefore"); + if (!deptype) + continue; + sorted = rc_stringlist_new(); + visited = rc_stringlist_new(); + visit_service(deptree, types, sorted, visited, depinfo, + NULL, 0); + rc_stringlist_free(visited); + TAILQ_FOREACH_SAFE(s2, deptype->services, entries, s2_np) { + TAILQ_FOREACH(s3, sorted, entries) { + di = get_depinfo(deptree, s3->value); + if (!di) + continue; + if (strcmp(s2->value, s3->value) == 0) { + dt = get_deptype(di, "iafter"); + if (dt) + rc_stringlist_delete(dt->services, depinfo->service); + break; + } + dt = get_deptype(di, "iprovide"); + if (!dt) + continue; + TAILQ_FOREACH(s4, dt->services, entries) { + if (strcmp(s4->value, s2->value) == 0) + break; + } + if (s4) { + di = get_depinfo(deptree, s4->value); + if (di) { + dt = get_deptype(di, "iafter"); + if (dt) + rc_stringlist_delete(dt->services, depinfo->service); + } + break; + } + } + if (s3) + rc_stringlist_delete(deptype->services, s2->value); + } + rc_stringlist_free(sorted); + } + rc_stringlist_free(types); + + /* Phase 6 - Print errors for duplicate services */ + dupes = rc_stringlist_new(); + TAILQ_FOREACH(depinfo, deptree, entries) { + serrno = errno; + errno = 0; + rc_stringlist_addu(dupes,depinfo->service); + if (errno == EEXIST) { + fprintf(stderr, + "Error: %s is the name of a real and virtual service.\n", + depinfo->service); + } + errno = serrno; + } + rc_stringlist_free(dupes); + + /* Phase 7 - save to disk + Now that we're purely in C, do we need to keep a shell parseable file? + I think yes as then it stays human readable + This works and should be entirely shell parseable provided that depend + names don't have any non shell variable characters in + */ + if ((fp = fopen(RC_DEPTREE_CACHE, "w"))) { + i = 0; + TAILQ_FOREACH(depinfo, deptree, entries) { + fprintf(fp, "depinfo_%zu_service='%s'\n", + i, depinfo->service); + TAILQ_FOREACH(deptype, &depinfo->depends, entries) { + k = 0; + TAILQ_FOREACH(s, deptype->services, entries) { + fprintf(fp, + "depinfo_%zu_%s_%zu='%s'\n", + i, deptype->type, k, s->value); + k++; + } + } + i++; + } + fclose(fp); + } else { + fprintf(stderr, "fopen `%s': %s\n", + RC_DEPTREE_CACHE, strerror(errno)); + retval = false; + } + + /* Save our external config files to disk */ + if (TAILQ_FIRST(config)) { + if ((fp = fopen(RC_DEPCONFIG, "w"))) { + TAILQ_FOREACH(s, config, entries) + fprintf(fp, "%s\n", s->value); + fclose(fp); + } else { + fprintf(stderr, "fopen `%s': %s\n", + RC_DEPCONFIG, strerror(errno)); + retval = false; + } + } else { + unlink(RC_DEPCONFIG); + } + + rc_stringlist_free(config); + rc_deptree_free(deptree); + return retval; +} +librc_hidden_def(rc_deptree_update) diff --git a/src/librc/librc-misc.c b/src/librc/librc-misc.c new file mode 100644 index 0000000..9d514bc --- /dev/null +++ b/src/librc/librc-misc.c @@ -0,0 +1,448 @@ +/* + * rc-misc.c + * rc misc functions +*/ + +/* + * Copyright (c) 2007-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. + */ + +#include + +#include "queue.h" +#include "librc.h" +#include "helpers.h" + +bool +rc_yesno(const char *value) +{ + if (!value) { + errno = ENOENT; + return false; + } + + if (strcasecmp(value, "yes") == 0 || + strcasecmp(value, "y") == 0 || + strcasecmp(value, "true") == 0 || + strcasecmp(value, "1") == 0) + return true; + + if (strcasecmp(value, "no") != 0 && + strcasecmp(value, "n") != 0 && + strcasecmp(value, "false") != 0 && + strcasecmp(value, "0") != 0) + errno = EINVAL; + + return false; +} +librc_hidden_def(rc_yesno) + + +/** + * Read the entire @file into the buffer and set @len to the + * size of the buffer when finished. For C strings, this will + * be strlen(buffer) + 1. + * Don't forget to free the buffer afterwards! + */ +bool +rc_getfile(const char *file, char **buffer, size_t *len) +{ + bool ret = false; + FILE *fp; + int fd; + struct stat st; + size_t done, left; + + fp = fopen(file, "re"); + if (!fp) + return false; + + /* assume fileno() never fails */ + fd = fileno(fp); + + if (fstat(fd, &st)) + goto finished; + + left = st.st_size; + *len = left + 1; /* NUL terminator */ + *buffer = xrealloc(*buffer, *len); + while (left) { + done = fread(*buffer, sizeof(*buffer[0]), left, fp); + if (done == 0 && ferror(fp)) + goto finished; + left -= done; + } + ret = true; + + finished: + if (!ret) { + free(*buffer); + *len = 0; + } else + (*buffer)[*len - 1] = '\0'; + fclose(fp); + return ret; +} +librc_hidden_def(rc_getfile) + +ssize_t +rc_getline(char **line, size_t *len, FILE *fp) +{ + char *p; + size_t last = 0; + + while (!feof(fp)) { + if (*line == NULL || last != 0) { + *len += BUFSIZ; + *line = xrealloc(*line, *len); + } + p = *line + last; + memset(p, 0, BUFSIZ); + if (fgets(p, BUFSIZ, fp) == NULL) + break; + last += strlen(p); + if (last && (*line)[last - 1] == '\n') { + (*line)[last - 1] = '\0'; + break; + } + } + return last; +} +librc_hidden_def(rc_getline) + +char * +rc_proc_getent(const char *ent _unused) +{ +#ifdef __linux__ + FILE *fp; + char *proc, *p, *value = NULL; + size_t i, len; + + if (!exists("/proc/cmdline")) + return NULL; + + if (!(fp = fopen("/proc/cmdline", "r"))) + return NULL; + + proc = NULL; + i = 0; + if (rc_getline(&proc, &i, fp) == -1 || proc == NULL) + return NULL; + + if (proc != NULL) { + len = strlen(ent); + + while ((p = strsep(&proc, " "))) { + if (strncmp(ent, p, len) == 0 && (p[len] == '\0' || p[len] == ' ' || p[len] == '=')) { + p += len; + + if (*p == '=') + p++; + + value = xstrdup(p); + } + } + } + + if (!value) + errno = ENOENT; + + fclose(fp); + free(proc); + + return value; +#else + return NULL; +#endif +} +librc_hidden_def(rc_proc_getent) + +RC_STRINGLIST * +rc_config_list(const char *file) +{ + FILE *fp; + char *buffer = NULL; + size_t len = 0; + char *p; + char *token; + RC_STRINGLIST *list = rc_stringlist_new(); + + if (!(fp = fopen(file, "r"))) + return list; + + while ((rc_getline(&buffer, &len, fp))) { + p = buffer; + /* Strip leading spaces/tabs */ + while ((*p == ' ') || (*p == '\t')) + p++; + + /* Get entry - we do not want comments */ + token = strsep(&p, "#"); + if (token && (strlen(token) > 1)) { + /* If not variable assignment then skip */ + if (strchr(token, '=')) { + /* Stip the newline if present */ + if (token[strlen(token) - 1] == '\n') + token[strlen(token) - 1] = 0; + + rc_stringlist_add(list, token); + } + } + } + fclose(fp); + free(buffer); + + return list; +} +librc_hidden_def(rc_config_list) + +static void rc_config_set_value(RC_STRINGLIST *config, char *value) +{ + RC_STRING *cline; + char *entry; + size_t i = 0; + char *newline; + char *p = value; + bool replaced; + char *token; + + if (! p) + return; + if (strncmp(p, "export ", 7) == 0) + p += 7; + if (! (token = strsep(&p, "="))) + return; + + entry = xstrdup(token); + /* Preserve shell coloring */ + if (*p == '$') + token = value; + else + do { + /* Bash variables are usually quoted */ + token = strsep(&p, "\"\'"); + } while (token && *token == '\0'); + + /* Drop a newline if that's all we have */ + if (token) { + i = strlen(token) - 1; + if (token[i] == '\n') + token[i] = 0; + + i = strlen(entry) + strlen(token) + 2; + newline = xmalloc(sizeof(char) * i); + snprintf(newline, i, "%s=%s", entry, token); + } else { + i = strlen(entry) + 2; + newline = xmalloc(sizeof(char) * i); + snprintf(newline, i, "%s=", entry); + } + + replaced = false; + /* In shells the last item takes precedence, so we need to remove + any prior values we may already have */ + TAILQ_FOREACH(cline, config, entries) { + i = strlen(entry); + if (strncmp(entry, cline->value, i) == 0 && cline->value[i] == '=') { + /* We have a match now - to save time we directly replace it */ + free(cline->value); + cline->value = newline; + replaced = true; + break; + } + } + + if (!replaced) { + rc_stringlist_add(config, newline); + free(newline); + } + free(entry); +} + +/* + * Override some specific rc.conf options on the kernel command line. + * I only know how to do this in Linux, so if someone wants to supply + * a patch for this on *BSD or tell me how to write the code to do this, + * any suggestions are welcome. + */ +static RC_STRINGLIST *rc_config_kcl(RC_STRINGLIST *config) +{ +#ifdef __linux__ + RC_STRINGLIST *overrides; + RC_STRING *cline, *override, *config_np; + char *tmp = NULL; + char *value = NULL; + size_t varlen = 0; + size_t len = 0; + + overrides = rc_stringlist_new(); + + /* A list of variables which may be overridden on the kernel command line */ + rc_stringlist_add(overrides, "rc_parallel"); + + TAILQ_FOREACH(override, overrides, entries) { + varlen = strlen(override->value); + value = rc_proc_getent(override->value); + + /* No need to continue if there's nothing to override */ + if (!value) { + free(value); + continue; + } + + if (value != NULL) { + len = varlen + strlen(value) + 2; + tmp = xmalloc(sizeof(char) * len); + snprintf(tmp, len, "%s=%s", override->value, value); + } + + /* + * Whenever necessary remove the old config entry first to prevent + * duplicates + */ + TAILQ_FOREACH_SAFE(cline, config, entries, config_np) { + if (strncmp(override->value, cline->value, varlen) == 0 + && cline->value[varlen] == '=') { + rc_stringlist_delete(config, cline->value); + break; + } + } + + /* Add the option (var/value) to the current config */ + rc_stringlist_add(config, tmp); + + free(tmp); + free(value); + } + + rc_stringlist_free(overrides); +#endif + return config; +} + +static RC_STRINGLIST * rc_config_directory(RC_STRINGLIST *config) +{ + DIR *dp; + struct dirent *d; + RC_STRINGLIST *rc_conf_d_files = rc_stringlist_new(); + RC_STRING *fname; + RC_STRINGLIST *rc_conf_d_list; + char path[PATH_MAX]; + RC_STRING *line; + + if ((dp = opendir(RC_CONF_D)) != NULL) { + while ((d = readdir(dp)) != NULL) { + if (fnmatch("*.conf", d->d_name, FNM_PATHNAME) == 0) { + rc_stringlist_addu(rc_conf_d_files, d->d_name); + } + } + closedir(dp); + + if (rc_conf_d_files) { + rc_stringlist_sort(&rc_conf_d_files); + TAILQ_FOREACH(fname, rc_conf_d_files, entries) { + if (! fname->value) + continue; + sprintf(path, "%s/%s", RC_CONF_D, fname->value); + rc_conf_d_list = rc_config_list(path); + TAILQ_FOREACH(line, rc_conf_d_list, entries) + if (line->value) + rc_config_set_value(config, line->value); + rc_stringlist_free(rc_conf_d_list); + } + rc_stringlist_free(rc_conf_d_files); + } + } + return config; +} + +RC_STRINGLIST * +rc_config_load(const char *file) +{ + RC_STRINGLIST *list; + RC_STRINGLIST *config; + RC_STRING *line; + + list = rc_config_list(file); + config = rc_stringlist_new(); + TAILQ_FOREACH(line, list, entries) { + rc_config_set_value(config, line->value); + } + rc_stringlist_free(list); + + return config; +} +librc_hidden_def(rc_config_load) + +char * +rc_config_value(RC_STRINGLIST *list, const char *entry) +{ + RC_STRING *line; + char *p; + size_t len; + + len = strlen(entry); + TAILQ_FOREACH(line, list, entries) { + p = strchr(line->value, '='); + if (p != NULL) { + if (strncmp(entry, line->value, len) == 0 && line->value[len] == '=') + return ++p; + } + } + return NULL; +} +librc_hidden_def(rc_config_value) + +/* Global for caching the strings loaded from rc.conf to avoid reparsing for + * each rc_conf_value call */ +static RC_STRINGLIST *rc_conf = NULL; + +static void +_free_rc_conf(void) +{ + rc_stringlist_free(rc_conf); +} + +char * +rc_conf_value(const char *setting) +{ + RC_STRINGLIST *old; + RC_STRING *s; + char *p; + + if (! rc_conf) { + rc_conf = rc_config_load(RC_CONF); + atexit(_free_rc_conf); + + /* Support old configs. */ + if (exists(RC_CONF_OLD)) { + old = rc_config_load(RC_CONF_OLD); + TAILQ_CONCAT(rc_conf, old, entries); + free(old); + } + + rc_conf = rc_config_directory(rc_conf); + rc_conf = rc_config_kcl(rc_conf); + + /* Convert old uppercase to lowercase */ + TAILQ_FOREACH(s, rc_conf, entries) { + p = s->value; + while (p && *p && *p != '=') { + if (isupper((unsigned char)*p)) + *p = tolower((unsigned char)*p); + p++; + } + } + } + + return rc_config_value(rc_conf, setting); +} +librc_hidden_def(rc_conf_value) diff --git a/src/librc/librc-stringlist.c b/src/librc/librc-stringlist.c new file mode 100644 index 0000000..58c6ef8 --- /dev/null +++ b/src/librc/librc-stringlist.c @@ -0,0 +1,151 @@ +/* + * librc-strlist.h + * String list functions to make using queue(3) a little easier. +*/ + +/* + * Copyright (c) 2007-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. + */ + +#include "queue.h" +#include "librc.h" + +RC_STRINGLIST * +rc_stringlist_new(void) +{ + RC_STRINGLIST *l = xmalloc(sizeof(*l)); + TAILQ_INIT(l); + return l; +} +librc_hidden_def(rc_stringlist_new) + +RC_STRING * +rc_stringlist_add(RC_STRINGLIST *list, const char *value) +{ + RC_STRING *s = xmalloc(sizeof(*s)); + + s->value = xstrdup(value); + TAILQ_INSERT_TAIL(list, s, entries); + return s; +} +librc_hidden_def(rc_stringlist_add) + +RC_STRING * +rc_stringlist_addu(RC_STRINGLIST *list, const char *value) +{ + RC_STRING *s; + + TAILQ_FOREACH(s, list, entries) + if (strcmp(s->value, value) == 0) { + errno = EEXIST; + return NULL; + } + + return rc_stringlist_add(list, value); +} +librc_hidden_def(rc_stringlist_addu) + +bool +rc_stringlist_delete(RC_STRINGLIST *list, const char *value) +{ + RC_STRING *s; + + TAILQ_FOREACH(s, list, entries) + if (strcmp(s->value, value) == 0) { + TAILQ_REMOVE(list, s, entries); + free(s->value); + free(s); + return true; + } + + errno = EEXIST; + return false; +} +librc_hidden_def(rc_stringlist_delete) + +RC_STRING * +rc_stringlist_find(RC_STRINGLIST *list, const char *value) +{ + RC_STRING *s; + + if (list) { + TAILQ_FOREACH(s, list, entries) + if (strcmp(s->value, value) == 0) + return s; + } + return NULL; +} +librc_hidden_def(rc_stringlist_find) + +RC_STRINGLIST * +rc_stringlist_split(const char *value, const char *sep) +{ + RC_STRINGLIST *list = rc_stringlist_new(); + char *d = xstrdup(value); + char *p = d, *token; + + while ((token = strsep(&p, sep))) + rc_stringlist_add(list, token); + free(d); + + return list; +} +librc_hidden_def(rc_stringlist_split) + +void +rc_stringlist_sort(RC_STRINGLIST **list) +{ + RC_STRINGLIST *l = *list; + RC_STRINGLIST *new = rc_stringlist_new(); + RC_STRING *s; + RC_STRING *sn; + RC_STRING *n; + RC_STRING *last; + + TAILQ_FOREACH_SAFE(s, l, entries, sn) { + TAILQ_REMOVE(l, s, entries); + last = NULL; + TAILQ_FOREACH(n, new, entries) { + if (strcmp(s->value, n->value) < 0) + break; + last = n; + } + if (last) + TAILQ_INSERT_AFTER(new, last, s, entries); + else + TAILQ_INSERT_HEAD(new, s, entries); + } + + /* Now we've sorted the list, copy across the new head */ + free(l); + *list = new; +} +librc_hidden_def(rc_stringlist_sort) + +void +rc_stringlist_free(RC_STRINGLIST *list) +{ + RC_STRING *s1; + RC_STRING *s2; + + if (!list) + return; + + s1 = TAILQ_FIRST(list); + while (s1) { + s2 = TAILQ_NEXT(s1, entries); + free(s1->value); + free(s1); + s1 = s2; + } + free(list); +} +librc_hidden_def(rc_stringlist_free) diff --git a/src/librc/librc.c b/src/librc/librc.c new file mode 100644 index 0000000..d2d99f6 --- /dev/null +++ b/src/librc/librc.c @@ -0,0 +1,1124 @@ +/* + * librc + * core RC functions + */ + +/* + * Copyright (c) 2007-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. + */ + +const char librc_copyright[] = "Copyright (c) 2007-2008 Roy Marples"; + +#include "queue.h" +#include "librc.h" +#include +#ifdef __FreeBSD__ +# include +#endif + +#define RC_RUNLEVEL RC_SVCDIR "/softlevel" + +#ifndef S_IXUGO +# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH) +#endif + +/* File stream used for plugins to write environ vars to */ +FILE *rc_environ_fd = NULL; + +typedef struct rc_service_state_name { + RC_SERVICE state; + const char *name; +} rc_service_state_name_t; + +/* We MUST list the states below 0x10 first + * The rest can be in any order */ +static const rc_service_state_name_t rc_service_state_names[] = { + { RC_SERVICE_STARTED, "started" }, + { RC_SERVICE_STOPPED, "stopped" }, + { RC_SERVICE_STARTING, "starting" }, + { RC_SERVICE_STOPPING, "stopping" }, + { RC_SERVICE_INACTIVE, "inactive" }, + { RC_SERVICE_WASINACTIVE, "wasinactive" }, + { RC_SERVICE_HOTPLUGGED, "hotplugged" }, + { RC_SERVICE_FAILED, "failed" }, + { RC_SERVICE_SCHEDULED, "scheduled"}, + { 0, NULL} +}; + +#define LS_INITD 0x01 +#define LS_DIR 0x02 +static RC_STRINGLIST * +ls_dir(const char *dir, int options) +{ + DIR *dp; + struct dirent *d; + RC_STRINGLIST *list = NULL; + struct stat buf; + size_t l; + char file[PATH_MAX]; + int r; + + list = rc_stringlist_new(); + if ((dp = opendir(dir)) == NULL) + return list; + while (((d = readdir(dp)) != NULL)) { + if (d->d_name[0] != '.') { + if (options & LS_INITD) { + /* Check that our file really exists. + * This is important as a service maybe in a + * runlevel, but could have been removed. */ + snprintf(file, sizeof(file), "%s/%s", + dir, d->d_name); + r = stat(file, &buf); + if (r != 0) + continue; + + /* .sh files are not init scripts */ + l = strlen(d->d_name); + if (l > 2 && d->d_name[l - 3] == '.' && + d->d_name[l - 2] == 's' && + d->d_name[l - 1] == 'h') + continue; + } + if (options & LS_DIR) { + snprintf(file, sizeof(file), "%s/%s", + dir, d->d_name); + if (stat(file, &buf) != 0 || + !S_ISDIR(buf.st_mode)) + continue; + } + rc_stringlist_add(list, d->d_name); + } + } + closedir(dp); + return list; +} + +static bool +rm_dir(const char *pathname, bool top) +{ + DIR *dp; + struct dirent *d; + char file[PATH_MAX]; + struct stat s; + bool retval = true; + + if ((dp = opendir(pathname)) == NULL) + return false; + + errno = 0; + while (((d = readdir(dp)) != NULL) && errno == 0) { + if (strcmp(d->d_name, ".") != 0 && + strcmp(d->d_name, "..") != 0) + { + snprintf(file, sizeof(file), + "%s/%s", pathname, d->d_name); + if (stat(file, &s) != 0) { + retval = false; + break; + } + if (S_ISDIR(s.st_mode)) { + if (!rm_dir(file, true)) + { + retval = false; + break; + } + } else { + if (unlink(file)) { + retval = false; + break; + } + } + } + } + closedir(dp); + + if (!retval) + return false; + + if (top && rmdir(pathname) != 0) + return false; + + return true; +} + +/* Other systems may need this at some point, but for now it's Linux only */ +#ifdef __linux__ +static bool +file_regex(const char *file, const char *regex) +{ + FILE *fp; + char *line = NULL; + size_t len = 0; + regex_t re; + bool retval = true; + int result; + + if (!(fp = fopen(file, "r"))) + return false; + + if ((result = regcomp(&re, regex, REG_EXTENDED | REG_NOSUB)) != 0) { + fclose(fp); + line = xmalloc(sizeof(char) * BUFSIZ); + regerror(result, &re, line, BUFSIZ); + fprintf(stderr, "file_regex: %s", line); + free(line); + return false; + } + + while ((rc_getline(&line, &len, fp))) { + char *str = line; + /* some /proc files have \0 separated content so we have to + loop through the 'line' */ + do { + if (regexec(&re, str, 0, NULL, 0) == 0) + goto found; + str += strlen(str) + 1; + /* len is the size of allocated buffer and we don't + want call regexec BUFSIZE times. find next str */ + while (str < line + len && *str == '\0') + str++; + } while (str < line + len); + } + retval = false; +found: + fclose(fp); + free(line); + regfree(&re); + + return retval; +} +#endif + + +static const char * +get_systype(void) +{ + char *systype = rc_conf_value("rc_sys"); + if (systype) { + char *s = systype; + /* Convert to uppercase */ + while (s && *s) { + if (islower((unsigned char) *s)) + *s = toupper((unsigned char) *s); + s++; + } + } + return systype; +} + +static const char * +detect_prefix(const char *systype) +{ +#ifdef PREFIX + return RC_SYS_PREFIX; +#else + if (systype) { + if (strcmp(systype, RC_SYS_NONE) == 0) + return NULL; + if (strcmp(systype, RC_SYS_PREFIX) == 0) + return RC_SYS_PREFIX; + } + + return NULL; +#endif +} + +static const char * +detect_container(const char *systype _unused) +{ +#ifdef __FreeBSD__ + if (systype) { + if (strcmp(systype, RC_SYS_NONE) == 0) + return NULL; + if (strcmp(systype, RC_SYS_JAIL) == 0) + return RC_SYS_JAIL; + } + + int jailed = 0; + size_t len = sizeof(jailed); + + if (sysctlbyname("security.jail.jailed", &jailed, &len, NULL, 0) == 0) + if (jailed == 1) + return RC_SYS_JAIL; +#endif + +#ifdef __linux__ + if (systype) { + if (strcmp(systype, RC_SYS_NONE) == 0) + return NULL; + if (strcmp(systype, RC_SYS_UML) == 0) + return RC_SYS_UML; + if (strcmp(systype, RC_SYS_VSERVER) == 0) + return RC_SYS_VSERVER; + if (strcmp(systype, RC_SYS_OPENVZ) == 0) + return RC_SYS_OPENVZ; + if (strcmp(systype, RC_SYS_LXC) == 0) + return RC_SYS_LXC; + if (strcmp(systype, RC_SYS_RKT) == 0) + return RC_SYS_RKT; + if (strcmp(systype, RC_SYS_SYSTEMD_NSPAWN) == 0) + return RC_SYS_SYSTEMD_NSPAWN; + if (strcmp(systype, RC_SYS_DOCKER) == 0) + return RC_SYS_DOCKER; + } + if (file_regex("/proc/cpuinfo", "UML")) + return RC_SYS_UML; + else if (file_regex("/proc/self/status", + "(s_context|VxID):[[:space:]]*[1-9]")) + return RC_SYS_VSERVER; + else if (exists("/proc/vz/veinfo") && !exists("/proc/vz/version")) + return RC_SYS_OPENVZ; + else if (file_regex("/proc/self/status", + "envID:[[:space:]]*[1-9]")) + return RC_SYS_OPENVZ; /* old test */ + else if (file_regex("/proc/1/environ", "container=lxc")) + return RC_SYS_LXC; + else if (file_regex("/proc/1/environ", "container=rkt")) + return RC_SYS_RKT; + else if (file_regex("/proc/1/environ", "container=chroot+unshare")) + return RC_SYS_SYSTEMD_NSPAWN; + else if (exists("/.dockerenv")) + return RC_SYS_DOCKER; + /* old test, I'm not sure when this was valid. */ + else if (file_regex("/proc/1/environ", "container=docker")) + return RC_SYS_DOCKER; +#endif + + return NULL; +} + +static const char * +detect_vm(const char *systype _unused) +{ +#ifdef __NetBSD__ + if (systype) { + if (strcmp(systype, RC_SYS_NONE) == 0) + return NULL; + if (strcmp(systype, RC_SYS_XEN0) == 0) + return RC_SYS_XEN0; + if (strcmp(systype, RC_SYS_XENU) == 0) + return RC_SYS_XENU; + } + if (exists("/kern/xen/privcmd")) + return RC_SYS_XEN0; + if (exists("/kern/xen")) + return RC_SYS_XENU; +#endif + +#ifdef __linux__ + if (systype) { + if (strcmp(systype, RC_SYS_NONE) == 0) + return NULL; + if (strcmp(systype, RC_SYS_XEN0) == 0) + return RC_SYS_XEN0; + if (strcmp(systype, RC_SYS_XENU) == 0) + return RC_SYS_XENU; + } + if (exists("/proc/xen")) { + if (file_regex("/proc/xen/capabilities", "control_d")) + return RC_SYS_XEN0; + return RC_SYS_XENU; + } +#endif + + return NULL; +} + +const char * +rc_sys(void) +{ + const char *systype; + const char *sys; + + systype = get_systype(); + sys = detect_prefix(systype); + if (!sys) { + sys = detect_container(systype); + if (!sys) { + sys = detect_vm(systype); + } + } + + return sys; +} +librc_hidden_def(rc_sys) + +static const char * +rc_parse_service_state(RC_SERVICE state) +{ + int i; + + for (i = 0; rc_service_state_names[i].name; i++) { + if (rc_service_state_names[i].state == state) + return rc_service_state_names[i].name; + } + return NULL; +} + +/* Returns a list of all the chained runlevels used by the + * specified runlevel in dependency order, including the + * specified runlevel. */ +static void +get_runlevel_chain(const char *runlevel, RC_STRINGLIST *level_list, RC_STRINGLIST *ancestor_list) +{ + char path[PATH_MAX]; + RC_STRINGLIST *dirs; + RC_STRING *d, *parent; + const char *nextlevel; + + /* + * If we haven't been passed a runlevel or a level list, or + * if the passed runlevel doesn't exist then we're done already! + */ + if (!runlevel || !level_list || !rc_runlevel_exists(runlevel)) + return; + + /* + * We want to add this runlevel to the list but if + * it is already in the list it needs to go at the + * end again. + */ + if (rc_stringlist_find(level_list, runlevel)) + rc_stringlist_delete(level_list, runlevel); + rc_stringlist_add(level_list, runlevel); + + /* + * We can now do exactly the above procedure for our chained + * runlevels. + */ + snprintf(path, sizeof(path), "%s/%s", RC_RUNLEVELDIR, runlevel); + dirs = ls_dir(path, LS_DIR); + TAILQ_FOREACH(d, dirs, entries) { + nextlevel = d->value; + + /* Check for loop */ + if (rc_stringlist_find(ancestor_list, nextlevel)) { + fprintf(stderr, "Loop detected in stacked runlevels attempting to enter runlevel %s!\n", + nextlevel); + fprintf(stderr, "Ancestors:\n"); + TAILQ_FOREACH(parent, ancestor_list, entries) + fprintf(stderr, "\t%s\n", parent->value); + exit(1); + } + + /* Add new ancestor */ + rc_stringlist_add(ancestor_list, nextlevel); + + get_runlevel_chain(nextlevel, level_list, ancestor_list); + + rc_stringlist_delete(ancestor_list, nextlevel); + } + rc_stringlist_free(dirs); +} + +bool +rc_runlevel_starting(void) +{ + return exists(RC_STARTING); +} +librc_hidden_def(rc_runlevel_starting) + +bool +rc_runlevel_stopping(void) +{ + return exists(RC_STOPPING); +} +librc_hidden_def(rc_runlevel_stopping) + +RC_STRINGLIST *rc_runlevel_list(void) +{ + return ls_dir(RC_RUNLEVELDIR, LS_DIR); +} +librc_hidden_def(rc_runlevel_list) + +char * +rc_runlevel_get(void) +{ + FILE *fp; + char *runlevel = NULL; + size_t i; + + if ((fp = fopen(RC_RUNLEVEL, "r"))) { + runlevel = xmalloc(sizeof(char) * PATH_MAX); + if (fgets(runlevel, PATH_MAX, fp)) { + i = strlen(runlevel) - 1; + if (runlevel[i] == '\n') + runlevel[i] = 0; + } else + *runlevel = '\0'; + fclose(fp); + } + + if (!runlevel || !*runlevel) { + free(runlevel); + runlevel = xstrdup(RC_LEVEL_SYSINIT); + } + + return runlevel; +} +librc_hidden_def(rc_runlevel_get) + +bool +rc_runlevel_set(const char *runlevel) +{ + FILE *fp = fopen(RC_RUNLEVEL, "w"); + + if (!fp) + return false; + fprintf(fp, "%s", runlevel); + fclose(fp); + return true; +} +librc_hidden_def(rc_runlevel_set) + +bool +rc_runlevel_exists(const char *runlevel) +{ + char path[PATH_MAX]; + struct stat buf; + + if (!runlevel || strcmp(runlevel, ".") == 0 || strcmp(runlevel, "..") == 0) + return false; + snprintf(path, sizeof(path), "%s/%s", RC_RUNLEVELDIR, runlevel); + if (stat(path, &buf) == 0 && S_ISDIR(buf.st_mode)) + return true; + return false; +} +librc_hidden_def(rc_runlevel_exists) + +bool +rc_runlevel_stack(const char *dst, const char *src) +{ + char d[PATH_MAX], s[PATH_MAX]; + + if (!rc_runlevel_exists(dst) || !rc_runlevel_exists(src)) + return false; + snprintf(s, sizeof(s), "../%s", src); + snprintf(d, sizeof(s), "%s/%s/%s", RC_RUNLEVELDIR, dst, src); + return (symlink(s, d) == 0 ? true : false); +} +librc_hidden_def(rc_runlevel_stack) + +bool +rc_runlevel_unstack(const char *dst, const char *src) +{ + char path[PATH_MAX]; + + snprintf(path, sizeof(path), "%s/%s/%s", RC_RUNLEVELDIR, dst, src); + return (unlink(path) == 0 ? true : false); +} +librc_hidden_def(rc_runlevel_unstack) + +RC_STRINGLIST * +rc_runlevel_stacks(const char *runlevel) +{ + RC_STRINGLIST *stack, *ancestor_list; + stack = rc_stringlist_new(); + ancestor_list = rc_stringlist_new(); + rc_stringlist_add(ancestor_list, runlevel); + get_runlevel_chain(runlevel, stack, ancestor_list); + rc_stringlist_free(ancestor_list); + return stack; +} +librc_hidden_def(rc_runlevel_stacks) + +/* Resolve a service name to its full path */ +char * +rc_service_resolve(const char *service) +{ + char buffer[PATH_MAX]; + char file[PATH_MAX]; + int r; + struct stat buf; + + if (!service) + return NULL; + + if (service[0] == '/') + return xstrdup(service); + + /* First check started services */ + snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s", "started", service); + if (lstat(file, &buf) || ! S_ISLNK(buf.st_mode)) { + snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s", + "inactive", service); + if (lstat(file, &buf) || ! S_ISLNK(buf.st_mode)) + *file = '\0'; + } + + if (*file) { + memset(buffer, 0, sizeof(buffer)); + r = readlink(file, buffer, sizeof(buffer)); + if (r > 0) + return xstrdup(buffer); + } + +#ifdef RC_LOCAL_INITDIR + /* Nope, so lets see if the user has written it */ + snprintf(file, sizeof(file), RC_LOCAL_INITDIR "/%s", service); + if (stat(file, &buf) == 0) + return xstrdup(file); +#endif + + /* System scripts take precedence over 3rd party ones */ + snprintf(file, sizeof(file), RC_INITDIR "/%s", service); + if (stat(file, &buf) == 0) + return xstrdup(file); + +#ifdef RC_PKG_INITDIR + /* Check RC_PKG_INITDIR */ + snprintf(file, sizeof(file), RC_PKG_INITDIR "/%s", service); + if (stat(file, &buf) == 0) + return xstrdup(file); +#endif + + return NULL; +} +librc_hidden_def(rc_service_resolve) + +bool +rc_service_exists(const char *service) +{ + char *file; + bool retval = false; + size_t len; + struct stat buf; + + if (!service) { + errno = EINVAL; + return false; + } + + len = strlen(service); + + /* .sh files are not init scripts */ + if (len > 2 && service[len - 3] == '.' && + service[len - 2] == 's' && + service[len - 1] == 'h') { + errno = EINVAL; + return false; + } + + if (!(file = rc_service_resolve(service))) { + errno = ENOENT; + return false; + } + + if (stat(file, &buf) == 0) { + if (buf.st_mode & S_IXUGO) + retval = true; + else + errno = ENOEXEC; + } + free(file); + return retval; +} +librc_hidden_def(rc_service_exists) + +#define OPTSTR \ +". '%s'; echo $extra_commands $extra_started_commands $extra_stopped_commands" + +RC_STRINGLIST * +rc_service_extra_commands(const char *service) +{ + char *svc; + char *cmd = NULL; + char *buffer = NULL; + size_t len = 0; + RC_STRINGLIST *commands = NULL; + char *token; + char *p; + FILE *fp; + size_t l; + + if (!(svc = rc_service_resolve(service))) + return NULL; + + l = strlen(OPTSTR) + strlen(svc) + 1; + cmd = xmalloc(sizeof(char) * l); + snprintf(cmd, l, OPTSTR, svc); + free(svc); + + if ((fp = popen(cmd, "r"))) { + rc_getline(&buffer, &len, fp); + p = buffer; + commands = rc_stringlist_new(); + + while ((token = strsep(&p, " "))) + if (token[0] != '\0') + rc_stringlist_add(commands, token); + + pclose(fp); + free(buffer); + } + + free(cmd); + return commands; +} +librc_hidden_def(rc_service_extra_commands) + +#define DESCSTR ". '%s'; echo \"${description%s%s}\"" +char * +rc_service_description(const char *service, const char *option) +{ + char *svc; + char *cmd; + char *desc = NULL; + size_t len = 0; + FILE *fp; + size_t l; + + if (!(svc = rc_service_resolve(service))) + return NULL; + + if (!option) + option = ""; + + l = strlen(DESCSTR) + strlen(svc) + strlen(option) + 2; + cmd = xmalloc(sizeof(char) * l); + snprintf(cmd, l, DESCSTR, svc, *option ? "_" : "", option); + free(svc); + if ((fp = popen(cmd, "r"))) { + rc_getline(&desc, &len, fp); + pclose(fp); + } + free(cmd); + return desc; +} +librc_hidden_def(rc_service_description) + +bool +rc_service_in_runlevel(const char *service, const char *runlevel) +{ + char file[PATH_MAX]; + + snprintf(file, sizeof(file), RC_RUNLEVELDIR "/%s/%s", + runlevel, basename_c(service)); + return exists(file); +} +librc_hidden_def(rc_service_in_runlevel) + +bool +rc_service_mark(const char *service, const RC_SERVICE state) +{ + char file[PATH_MAX]; + int i = 0; + int skip_state = -1; + const char *base; + char *init = rc_service_resolve(service); + bool skip_wasinactive = false; + int s; + char was[PATH_MAX]; + RC_STRINGLIST *dirs; + RC_STRING *dir; + int serrno; + + if (!init) + return false; + + base = basename_c(service); + if (state != RC_SERVICE_STOPPED) { + if (!exists(init)) { + free(init); + return false; + } + + snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s", + rc_parse_service_state(state), base); + if (exists(file)) + unlink(file); + i = symlink(init, file); + if (i != 0) { + free(init); + return false; + } + skip_state = state; + } + + if (state == RC_SERVICE_HOTPLUGGED || state == RC_SERVICE_FAILED) { + free(init); + return true; + } + + /* Remove any old states now */ + for (i = 0; rc_service_state_names[i].name; i++) { + s = rc_service_state_names[i].state; + + if ((s != skip_state && + s != RC_SERVICE_STOPPED && + s != RC_SERVICE_HOTPLUGGED && + s != RC_SERVICE_SCHEDULED) && + (! skip_wasinactive || s != RC_SERVICE_WASINACTIVE)) + { + snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s", + rc_service_state_names[i].name, base); + if (exists(file)) { + if ((state == RC_SERVICE_STARTING || + state == RC_SERVICE_STOPPING) && + s == RC_SERVICE_INACTIVE) + { + snprintf(was, sizeof(was), + RC_SVCDIR "/%s/%s", + rc_parse_service_state(RC_SERVICE_WASINACTIVE), + base); + if (symlink(init, was) == -1) + return false; + skip_wasinactive = true; + } + if (unlink(file) == -1) { + free(init); + return false; + } + } + } + } + + /* Remove the exclusive state if we're inactive */ + if (state == RC_SERVICE_STARTED || + state == RC_SERVICE_STOPPED || + state == RC_SERVICE_INACTIVE) + { + snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s", + "exclusive", base); + unlink(file); + } + + /* Remove any options and daemons the service may have stored */ + if (state == RC_SERVICE_STOPPED) { + snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s", + "options", base); + rm_dir(file, true); + + snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s", + "daemons", base); + rm_dir(file, true); + + rc_service_schedule_clear(service); + } + + /* These are final states, so remove us from scheduled */ + if (state == RC_SERVICE_STARTED || state == RC_SERVICE_STOPPED) { + snprintf(file, sizeof(file), RC_SVCDIR "/%s", "scheduled"); + dirs = ls_dir(file, 0); + TAILQ_FOREACH(dir, dirs, entries) { + snprintf(was, sizeof(was), "%s/%s/%s", + file, dir->value, base); + unlink(was); + + /* Try and remove the dir; we don't care about errors */ + snprintf(was, sizeof(was), "%s/%s", file, dir->value); + serrno = errno; + rmdir(was); + errno = serrno; + } + rc_stringlist_free(dirs); + } + free(init); + return true; +} +librc_hidden_def(rc_service_mark) + +RC_SERVICE +rc_service_state(const char *service) +{ + int i; + int state = RC_SERVICE_STOPPED; + char file[PATH_MAX]; + RC_STRINGLIST *dirs; + RC_STRING *dir; + const char *base = basename_c(service); + + for (i = 0; rc_service_state_names[i].name; i++) { + snprintf(file, sizeof(file), RC_SVCDIR "/%s/%s", + rc_service_state_names[i].name, base); + if (exists(file)) { + if (rc_service_state_names[i].state <= 0x10) + state = rc_service_state_names[i].state; + else + state |= rc_service_state_names[i].state; + } + } + + if (state & RC_SERVICE_STOPPED) { + dirs = ls_dir(RC_SVCDIR "/scheduled", 0); + TAILQ_FOREACH(dir, dirs, entries) { + snprintf(file, sizeof(file), + RC_SVCDIR "/scheduled/%s/%s", + dir->value, service); + if (exists(file)) { + state |= RC_SERVICE_SCHEDULED; + break; + } + } + rc_stringlist_free(dirs); + } + + return state; +} +librc_hidden_def(rc_service_state) + +char * +rc_service_value_get(const char *service, const char *option) +{ + char *buffer = NULL; + size_t len = 0; + char file[PATH_MAX]; + + snprintf(file, sizeof(file), RC_SVCDIR "/options/%s/%s", + service, option); + rc_getfile(file, &buffer, &len); + + return buffer; +} +librc_hidden_def(rc_service_value_get) + +bool +rc_service_value_set(const char *service, const char *option, + const char *value) +{ + FILE *fp; + char file[PATH_MAX]; + char *p = file; + + p += snprintf(file, sizeof(file), RC_SVCDIR "/options/%s", service); + if (mkdir(file, 0755) != 0 && errno != EEXIST) + return false; + + snprintf(p, sizeof(file) - (p - file), "/%s", option); + if (!(fp = fopen(file, "w"))) + return false; + if (value) + fprintf(fp, "%s", value); + fclose(fp); + return true; +} +librc_hidden_def(rc_service_value_set) + + +bool +rc_service_schedule_start(const char *service, const char *service_to_start) +{ + char file[PATH_MAX]; + char *p = file; + char *init; + bool retval; + + /* service may be a provided service, like net */ + if (! service || ! rc_service_exists(service_to_start)) + return false; + + p += snprintf(file, sizeof(file), RC_SVCDIR "/scheduled/%s", + basename_c(service)); + if (mkdir(file, 0755) != 0 && errno != EEXIST) + return false; + + init = rc_service_resolve(service_to_start); + snprintf(p, sizeof(file) - (p - file), + "/%s", basename_c(service_to_start)); + retval = (exists(file) || symlink(init, file) == 0); + free(init); + return retval; +} +librc_hidden_def(rc_service_schedule_start) + +bool +rc_service_schedule_clear(const char *service) +{ + char dir[PATH_MAX]; + + snprintf(dir, sizeof(dir), RC_SVCDIR "/scheduled/%s", + basename_c(service)); + if (!rm_dir(dir, true) && errno == ENOENT) + return true; + return false; +} +librc_hidden_def(rc_service_schedule_clear) + +RC_STRINGLIST * +rc_services_in_runlevel(const char *runlevel) +{ + char dir[PATH_MAX]; + RC_STRINGLIST *list = NULL; + + if (!runlevel) { +#ifdef RC_PKG_INITDIR + RC_STRINGLIST *pkg = ls_dir(RC_PKG_INITDIR, LS_INITD); +#endif +#ifdef RC_LOCAL_INITDIR + RC_STRINGLIST *local = ls_dir(RC_LOCAL_INITDIR, LS_INITD); +#endif + + list = ls_dir(RC_INITDIR, LS_INITD); + +#ifdef RC_PKG_INITDIR + TAILQ_CONCAT(list, pkg, entries); + free(pkg); +#endif +#ifdef RC_LOCAL_INITDIR + TAILQ_CONCAT(list, local, entries); + free(local); +#endif + return list; + } + + /* These special levels never contain any services */ + if (strcmp(runlevel, RC_LEVEL_SINGLE) != 0) { + snprintf(dir, sizeof(dir), RC_RUNLEVELDIR "/%s", runlevel); + list = ls_dir(dir, LS_INITD); + } + if (!list) + list = rc_stringlist_new(); + return list; +} +librc_hidden_def(rc_services_in_runlevel) + +RC_STRINGLIST * +rc_services_in_runlevel_stacked(const char *runlevel) +{ + RC_STRINGLIST *list, *stacks, *sl; + RC_STRING *stack; + + list = rc_services_in_runlevel(runlevel); + stacks = rc_runlevel_stacks(runlevel); + TAILQ_FOREACH(stack, stacks, entries) { + sl = rc_services_in_runlevel(stack->value); + TAILQ_CONCAT(list, sl, entries); + free(sl); + } + return list; +} +librc_hidden_def(rc_services_in_runlevel_stacked) + +RC_STRINGLIST * +rc_services_in_state(RC_SERVICE state) +{ + RC_STRINGLIST *services; + RC_STRINGLIST *list; + RC_STRINGLIST *dirs; + RC_STRING *d; + char dir[PATH_MAX]; + char *p = dir; + + p += snprintf(dir, sizeof(dir), RC_SVCDIR "/%s", + rc_parse_service_state(state)); + + if (state != RC_SERVICE_SCHEDULED) + return ls_dir(dir, LS_INITD); + + dirs = ls_dir(dir, 0); + list = rc_stringlist_new(); + if (! dirs) + return list; + + TAILQ_FOREACH(d, dirs, entries) { + snprintf(p, sizeof(dir) - (p - dir), "/%s", d->value); + services = ls_dir(dir, LS_INITD); + if (services) { + TAILQ_CONCAT(list, services, entries); + free(services); + } + } + rc_stringlist_free(dirs); + return list; +} +librc_hidden_def(rc_services_in_state) + +bool +rc_service_add(const char *runlevel, const char *service) +{ + bool retval; + char *init; + char file[PATH_MAX]; + char path[MAXPATHLEN] = { '\0' }; + char *p = NULL; + char binit[PATH_MAX]; + char *i; + + if (!rc_runlevel_exists(runlevel)) { + errno = ENOENT; + return false; + } + + if (rc_service_in_runlevel(service, runlevel)) { + errno = EEXIST; + return false; + } + + i = init = rc_service_resolve(service); + snprintf(file, sizeof(file), RC_RUNLEVELDIR "/%s/%s", + runlevel, basename_c(service)); + + /* We need to ensure that only things in /etc/init.d are added + * to the boot runlevel */ + if (strcmp(runlevel, RC_LEVEL_BOOT) == 0) { + p = realpath(dirname(init), path); + if (!*p) { + free(init); + return false; + } + if (strcmp(path, RC_INITDIR) != 0) { + free(init); + errno = EPERM; + return false; + } + snprintf(binit, sizeof(binit), RC_INITDIR "/%s", service); + i = binit; + } + + retval = (symlink(i, file) == 0); + free(init); + return retval; +} +librc_hidden_def(rc_service_add) + +bool +rc_service_delete(const char *runlevel, const char *service) +{ + char file[PATH_MAX]; + + snprintf(file, sizeof(file), RC_RUNLEVELDIR "/%s/%s", + runlevel, basename_c(service)); + if (unlink(file) == 0) + return true; + return false; +} +librc_hidden_def(rc_service_delete) + +RC_STRINGLIST * +rc_services_scheduled_by(const char *service) +{ + RC_STRINGLIST *dirs = ls_dir(RC_SVCDIR "/scheduled", 0); + RC_STRINGLIST *list = rc_stringlist_new(); + RC_STRING *dir; + char file[PATH_MAX]; + + TAILQ_FOREACH(dir, dirs, entries) { + snprintf(file, sizeof(file), RC_SVCDIR "/scheduled/%s/%s", + dir->value, service); + if (exists(file)) + rc_stringlist_add(list, file); + } + rc_stringlist_free(dirs); + return list; +} +librc_hidden_def(rc_services_scheduled_by) + +RC_STRINGLIST * +rc_services_scheduled(const char *service) +{ + char dir[PATH_MAX]; + + snprintf(dir, sizeof(dir), RC_SVCDIR "/scheduled/%s", + basename_c(service)); + return ls_dir(dir, LS_INITD); +} +librc_hidden_def(rc_services_scheduled) diff --git a/src/librc/librc.h b/src/librc/librc.h new file mode 100644 index 0000000..c4c3418 --- /dev/null +++ b/src/librc/librc.h @@ -0,0 +1,122 @@ +/* + * librc.h + * Internal header file to setup build env for files in librc.so + */ + +/* + * Copyright (c) 2007-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. + */ + +#ifndef _LIBRC_H_ +#define _LIBRC_H_ + +#define _IN_LIBRC + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BSD) && !defined(__GNU__) +#include +#include +#include +#include +#else +#include +#endif + +#include "rc.h" +#include "rc-misc.h" + +#include "hidden-visibility.h" +#define librc_hidden_proto(x) hidden_proto(x) +#define librc_hidden_def(x) hidden_def(x) + +librc_hidden_proto(rc_conf_value) +librc_hidden_proto(rc_config_list) +librc_hidden_proto(rc_config_load) +librc_hidden_proto(rc_config_value) +librc_hidden_proto(rc_deptree_depend) +librc_hidden_proto(rc_deptree_depends) +librc_hidden_proto(rc_deptree_free) +librc_hidden_proto(rc_deptree_load) +librc_hidden_proto(rc_deptree_load_file) +librc_hidden_proto(rc_deptree_order) +librc_hidden_proto(rc_deptree_update) +librc_hidden_proto(rc_deptree_update_needed) +librc_hidden_proto(rc_find_pids) +librc_hidden_proto(rc_getfile) +librc_hidden_proto(rc_getline) +librc_hidden_proto(rc_newer_than) +librc_hidden_proto(rc_proc_getent) +librc_hidden_proto(rc_older_than) +librc_hidden_proto(rc_runlevel_exists) +librc_hidden_proto(rc_runlevel_get) +librc_hidden_proto(rc_runlevel_list) +librc_hidden_proto(rc_runlevel_set) +librc_hidden_proto(rc_runlevel_stack) +librc_hidden_proto(rc_runlevel_stacks) +librc_hidden_proto(rc_runlevel_starting) +librc_hidden_proto(rc_runlevel_stopping) +librc_hidden_proto(rc_runlevel_unstack) +librc_hidden_proto(rc_service_add) +librc_hidden_proto(rc_service_daemons_crashed) +librc_hidden_proto(rc_service_daemon_set) +librc_hidden_proto(rc_service_delete) +librc_hidden_proto(rc_service_description) +librc_hidden_proto(rc_service_exists) +librc_hidden_proto(rc_service_extra_commands) +librc_hidden_proto(rc_service_in_runlevel) +librc_hidden_proto(rc_service_mark) +librc_hidden_proto(rc_service_resolve) +librc_hidden_proto(rc_service_schedule_clear) +librc_hidden_proto(rc_service_schedule_start) +librc_hidden_proto(rc_services_in_runlevel) +librc_hidden_proto(rc_services_in_runlevel_stacked) +librc_hidden_proto(rc_services_in_state) +librc_hidden_proto(rc_services_scheduled) +librc_hidden_proto(rc_services_scheduled_by) +librc_hidden_proto(rc_service_started_daemon) +librc_hidden_proto(rc_service_state) +librc_hidden_proto(rc_service_value_get) +librc_hidden_proto(rc_service_value_set) +librc_hidden_proto(rc_stringlist_add) +librc_hidden_proto(rc_stringlist_addu) +librc_hidden_proto(rc_stringlist_delete) +librc_hidden_proto(rc_stringlist_find) +librc_hidden_proto(rc_stringlist_free) +librc_hidden_proto(rc_stringlist_new) +librc_hidden_proto(rc_stringlist_split) +librc_hidden_proto(rc_stringlist_sort) +librc_hidden_proto(rc_sys) +librc_hidden_proto(rc_yesno) + +#endif diff --git a/src/librc/rc.h.in b/src/librc/rc.h.in new file mode 100644 index 0000000..b658355 --- /dev/null +++ b/src/librc/rc.h.in @@ -0,0 +1,604 @@ +/* + * Copyright (c) 2007-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. + */ + +#ifndef __RC_H__ +#define __RC_H__ + +#include +#include +#include + +/* __BEGIN_DECLS */ +#ifdef __cplusplus +extern "C" { +#endif + +#define RC_PREFIX "@PREFIX@" +#define RC_SYSCONFDIR "@SYSCONFDIR@" +#define RC_LIBDIR "@PREFIX@/@LIB@/rc" +#define RC_LIBEXECDIR "@LIBEXECDIR@" +#if defined(PREFIX) +#define RC_SVCDIR RC_LIBEXECDIR "/init.d" +#elif defined(__linux__) || (defined(__FreeBSD_kernel__) && \ + defined(__GLIBC__)) || defined(__GNU__) +#define RC_SVCDIR "/run/openrc" +#else +#define RC_SVCDIR RC_LIBEXECDIR "/init.d" +#endif +#define RC_RUNLEVELDIR RC_SYSCONFDIR "/runlevels" +#define RC_INITDIR RC_SYSCONFDIR "/init.d" +#define RC_CONFDIR RC_SYSCONFDIR "/conf.d" +#define RC_PLUGINDIR RC_LIBDIR "/plugins" + +#define RC_INIT_FIFO RC_SVCDIR"/init.ctl" +#define RC_PROFILE_ENV RC_SYSCONFDIR "/profile.env" +#define RC_SYS_WHITELIST RC_LIBEXECDIR "/conf.d/env_whitelist" +#define RC_USR_WHITELIST RC_SYSCONFDIR "/conf.d/env_whitelist" +#define RC_CONF RC_SYSCONFDIR "/rc.conf" +#define RC_CONF_D RC_SYSCONFDIR "/rc.conf.d" +#define RC_CONF_OLD RC_SYSCONFDIR "/conf.d/rc" + +#define RC_PATH_PREFIX RC_LIBEXECDIR "/bin:/bin:/sbin:/usr/bin:/usr/sbin" + +/* PKG_PREFIX is where packages are installed if different from the base OS + * On Gentoo this is normally unset, on FreeBSD /usr/local and on NetBSD + * /usr/pkg. */ +#define RC_PKG_PREFIX "@PKG_PREFIX@" +#ifdef RC_PKG_PREFIX +# define RC_PKG_INITDIR RC_PKG_PREFIX "/etc/init.d" +# define RC_PKG_CONFDIR RC_PKG_PREFIX "/etc/conf.d" +#endif + +/* LOCAL_PREFIX is for user written stuff, which the base OS and package + * manger don't touch. */ +#define RC_LOCAL_PREFIX "@LOCAL_PREFIX@" +#ifdef RC_LOCAL_PREFIX +# define RC_LOCAL_INITDIR RC_LOCAL_PREFIX "/etc/init.d" +# define RC_LOCAL_CONFDIR RC_LOCAL_PREFIX "/etc/conf.d" +#endif + +#ifndef _SYS_QUEUE_H_ + +/* + * The following are copied directly from our imported queue.h. + */ + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * Tail queue definitions. + */ +#define _TAILQ_HEAD(name, type, qual) \ +struct name { \ + qual type *tqh_first; /* first element */ \ + qual type *qual *tqh_last; /* addr of last next element */ \ +} +#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) + +#define TAILQ_HEAD_INITIALIZER(head) \ + { TAILQ_END(head), &(head).tqh_first } + +#define _TAILQ_ENTRY(type, qual) \ +struct { \ + qual type *tqe_next; /* next element */ \ + qual type *qual *tqe_prev; /* address of previous next element */\ +} +#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) + +#endif /* _SYS_QUEUE_H_ */ + +/* A doubly linked list using queue(3) for ease of use */ +typedef struct rc_string { + char *value; + TAILQ_ENTRY(rc_string) entries; +} RC_STRING; +typedef TAILQ_HEAD(rc_stringlist, rc_string) RC_STRINGLIST; + +/*! @name Reserved runlevel names */ +#define RC_LEVEL_SYSINIT "sysinit" +#define RC_LEVEL_SINGLE "single" +#define RC_LEVEL_SHUTDOWN "shutdown" + +/*! Return the current runlevel. + * @return the current runlevel */ +char *rc_runlevel_get(void); + +/*! Checks if the runlevel exists or not + * @param runlevel to check + * @return true if the runlevel exists, otherwise false */ +bool rc_runlevel_exists(const char *); + +/*! Stack a runlevel onto another + * @param runlevel to stack onto + * @param runlevel being stacked + * @return true if successful, otherwise false */ +bool rc_runlevel_stack(const char *, const char *); + +/*! Unstack a runlevel from another + * @param runlevel to unstack from + * @param runlevel being unstacked + * @return true if successful, otherwise false */ +bool rc_runlevel_unstack(const char *, const char *); + +/*! Return a NULL terminated list of runlevel stacks in the runlevels + * @return a NULL terminated list of runlevels */ +RC_STRINGLIST *rc_runlevel_stacks(const char *); + +/*! Return a NULL terminated list of runlevels + * @return a NULL terminated list of runlevels */ +RC_STRINGLIST *rc_runlevel_list(void); + +/*! Set the runlevel. + * This just changes the stored runlevel and does not start or stop any + * services. + * @param runlevel to store */ +bool rc_runlevel_set(const char *); + +/*! Is the runlevel starting? + * @return true if yes, otherwise false */ +bool rc_runlevel_starting(void); + +/*! Is the runlevel stopping? + * @return true if yes, otherwise false */ +bool rc_runlevel_stopping(void); + +/*! @name RC + * A service can be given as a full path or just its name. + * If it's just a name then we try to resolve the service to a full path. + * This should allow the use if local init.d directories in the future. */ + +/*! @brief States a service can be in */ +typedef enum +{ + /* These are actual states + * The service has to be in one only at all times */ + RC_SERVICE_STOPPED = 0x0001, + RC_SERVICE_STARTED = 0x0002, + RC_SERVICE_STOPPING = 0x0004, + RC_SERVICE_STARTING = 0x0008, + RC_SERVICE_INACTIVE = 0x0010, + + /* Service may or may not have been hotplugged */ + RC_SERVICE_HOTPLUGGED = 0x0100, + + /* Optional states service could also be in */ + RC_SERVICE_FAILED = 0x0200, + RC_SERVICE_SCHEDULED = 0x0400, + RC_SERVICE_WASINACTIVE = 0x0800 +} RC_SERVICE; + +/*! Add the service to the runlevel + * @param runlevel to add to + * @param service to add + * @return true if successful, otherwise false */ +bool rc_service_add(const char *, const char *); + +/*! Remove the service from the runlevel + * @param runlevel to remove from + * @param service to remove + * @return true if sucessful, otherwise false */ +bool rc_service_delete(const char *, const char *); + +/*! Save the arguments to find a running daemon + * @param service to save arguments for + * @param exec that we started + * @param name of the process (optional) + * @param pidfile of the process (optional) + * @param started if true, add the arguments otherwise remove existing matching arguments */ +bool rc_service_daemon_set(const char *, const char *, const char *const *, const char *, + bool); + +/*! Returns a description of what the service and/or option does. + * @param service to check + * @param option to check (if NULL, service description) + * @return a newly allocated pointer to the description */ +char *rc_service_description(const char *, const char *); + +/*! Checks if a service exists or not. + * @param service to check + * @return true if service exists, otherwise false */ +bool rc_service_exists(const char *); + +/*! Checks if a service is in a runlevel + * @param service to check + * @param runlevel it should be in + * @return true if service is in the runlevel, otherwise false */ +bool rc_service_in_runlevel(const char *, const char *); + +/*! Marks the service state + * @param service to mark + * @param state service should be in + * @return true if service state change was successful, otherwise false */ +bool rc_service_mark(const char *, RC_SERVICE); + +/*! Lists the extra commands a service has + * @param service to load the commands from + * @return NULL terminated string list of commands */ +RC_STRINGLIST *rc_service_extra_commands(const char *); + +/*! Resolves a service name to its full path. + * @param service to check + * @return pointer to full path of service */ +char *rc_service_resolve(const char *); + +/*! Schedule a service to be started when another service starts + * @param service that starts the scheduled service when started + * @param service_to_start service that will be started */ +bool rc_service_schedule_start(const char *, const char *); + +/*! Return a NULL terminated list of services that are scheduled to start + * when the given service has started + * @param service to check + * @return NULL terminated list of services scheduled to start */ +RC_STRINGLIST *rc_services_scheduled_by(const char *); + +/*! Clear the list of services scheduled to be started by this service + * @param service to clear + * @return true if no errors, otherwise false */ +bool rc_service_schedule_clear(const char *); + +/*! Checks if a service in in a state + * @param service to check + * @return state of the service */ +RC_SERVICE rc_service_state(const char *); + +/*! Check if the service started the daemon + * @param service to check + * @param exec to check + * @param argv to check + * @param indx of the daemon (optional - 1st daemon, 2nd daemon, etc) + * @return true if started by this service, otherwise false */ +bool rc_service_started_daemon(const char *, const char *, + const char *const *, int); + +/*! Return a saved value for a service + * @param service to check + * @param option to load + * @return saved value */ +char *rc_service_value_get(const char *, const char *); + +/*! Save a persistent value for a service + * @param service to save for + * @param option to save + * @param value of the option + * @return true if saved, otherwise false */ +bool rc_service_value_set(const char *, const char *, const char *); + +/*! List the services in a runlevel + * @param runlevel to list + * @return NULL terminated list of services */ +RC_STRINGLIST *rc_services_in_runlevel(const char *); + +/*! List the stacked services in a runlevel + * @param runlevel to list + * @return NULL terminated list of services */ +RC_STRINGLIST *rc_services_in_runlevel_stacked(const char *); + +/*! List the services in a state + * @param state to list + * @return NULL terminated list of services */ +RC_STRINGLIST *rc_services_in_state(RC_SERVICE); + +/*! List the services shceduled to start when this one does + * @param service to check + * @return NULL terminated list of services */ +RC_STRINGLIST *rc_services_scheduled(const char *); + +/*! Checks that all daemons started with start-stop-daemon by the service + * are still running. + * @param service to check + * @return true if all daemons started are still running, otherwise false */ +bool rc_service_daemons_crashed(const char *); + +/*! @name System types + * OpenRC can support some special sub system types, normally virtualization. + * Some services cannot work in these systems, or we do something else. */ +#define RC_SYS_DOCKER "DOCKER" +#define RC_SYS_JAIL "JAIL" +#define RC_SYS_NONE "" +#define RC_SYS_OPENVZ "OPENVZ" +#define RC_SYS_LXC "LXC" +#define RC_SYS_PREFIX "PREFIX" +#define RC_SYS_RKT "RKT" +#define RC_SYS_SYSTEMD_NSPAWN "CHROOT+UNSHARE" +#define RC_SYS_UML "UML" +#define RC_SYS_VSERVER "VSERVER" +#define RC_SYS_XEN0 "XEN0" +#define RC_SYS_XENU "XENU" + +/*! Returns the type of subsystem + * @return string from RC_SYS_* types or NULL if none detected */ +const char *rc_sys(void); + +/*! @name Dependency options + * These options can change the services found by the rc_get_depinfo and + * rc_get_depends functions. */ +/*! Trace provided services */ +#define RC_DEP_TRACE (1<<0) +/*! Only use services added to runlevels */ +#define RC_DEP_STRICT (1<<1) +/*! Runlevel is starting */ +#define RC_DEP_START (1<<2) +/*! Runlevel is stopping */ +#define RC_DEP_STOP (1<<3) + +/*! @name Dependencies + * We analyse each init script and cache the resultant dependency tree. + * This tree can be accessed using the below functions. */ + +#ifdef _IN_LIBRC +/*! @name Dependency structures + * private to librc */ + +/*! Singly linked list of dependency types that list the services the + * type is for */ +typedef struct rc_deptype +{ + /*! ineed, iuse, iafter, etc */ + char *type; + /*! list of services */ + RC_STRINGLIST *services; + /*! list of types */ + TAILQ_ENTRY(rc_deptype) entries; +} RC_DEPTYPE; + +/*! Singly linked list of services and their dependencies */ +typedef struct rc_depinfo +{ + /*! Name of service */ + char *service; + /*! Dependencies */ + TAILQ_HEAD(, rc_deptype) depends; + /*! List of entries */ + TAILQ_ENTRY(rc_depinfo) entries; +} RC_DEPINFO; + +typedef TAILQ_HEAD(,rc_depinfo) RC_DEPTREE; +#else +/* Handles to internal structures */ +typedef void *RC_DEPTREE; +#endif + +/*! Check to see if source is newer than target. + * If target is a directory then we traverse it and its children. + * @param source + * @param target + * @param mtime of newest target + * @param filename of the newest target (needs mtime param) + * @return true if source is newer than target, otherwise false */ +bool rc_newer_than(const char *, const char *, time_t *, char *); + +/*! Check to see if source is older than target. + * If target is a directory then we traverse it and its children. + * @param source + * @param target + * @param mtime of oldest target + * @param filename of the oldest target (needs mtime param) + * @return true if source is older than target, otherwise false */ +bool rc_older_than(const char *, const char *, time_t *, char *); + +/*! Read variables/values from /proc/cmdline + * @param value + * @return pointer to the value, otherwise NULL */ +char *rc_proc_getent(const char *); + +/*! Update the cached dependency tree if it's older than any init script, + * its configuration file or an external configuration file the init script + * has specified. + * time_t returns the time of the newest file that the dependency tree + * will be checked against. + * @return true if successful, otherwise false */ +bool rc_deptree_update(void); + +/*! Check if the cached dependency tree is older than any init script, + * its configuration file or an external configuration file the init script + * has specified. + * @param mtime of newest file + * @param buffer of PATH_MAX to store newest file + * @return true if it needs updating, otherwise false */ +bool rc_deptree_update_needed(time_t *, char *); + +/*! Load the cached dependency tree and return a pointer to it. + * This pointer should be freed with rc_deptree_free when done. + * @return pointer to the dependency tree */ +RC_DEPTREE *rc_deptree_load(void); + +/*! Load a cached dependency tree from the specified file and return a pointer + * to it. This pointer should be freed with rc_deptree_free when done. + * @return pointer to the dependency tree */ +RC_DEPTREE *rc_deptree_load_file(const char *); + +/*! List the depend for the type of service + * @param deptree to search + * @param type to use (keywords, etc) + * @param service to check + * @return NULL terminated list of services in order */ +RC_STRINGLIST *rc_deptree_depend(const RC_DEPTREE *, const char *, const char *); + +/*! List all the services in order that the given services have + * for the given types and options. + * @param deptree to search + * @param types to use (ineed, iuse, etc) + * @param services to check + * @param options to pass + * @return NULL terminated list of services in order */ +RC_STRINGLIST *rc_deptree_depends(const RC_DEPTREE *, const RC_STRINGLIST *, + const RC_STRINGLIST *, const char *, int); + +/*! List all the services that should be stoppned and then started, in order, + * for the given runlevel, including sysinit and boot services where + * approriate. + * @param deptree to search + * @param runlevel to change into + * @param options to pass + * @return NULL terminated list of services in order */ +RC_STRINGLIST *rc_deptree_order(const RC_DEPTREE *, const char *, int); + +/*! Free a deptree and its information + * @param deptree to free */ +void rc_deptree_free(RC_DEPTREE *); + +/*! @name Plugins + * For each plugin loaded we will call rc_plugin_hook with the below + * enum and either the runlevel name or service name. + * + * Plugins are called when rc does something. This does not indicate an + * end result and the plugin should use the above functions to query things + * like service status. + * + * The service hooks have extra ones - now and done. This is because after + * start_in we may start other services before we start the service in + * question. now shows we really will start the service now and done shows + * when we have done it as may start scheduled services at this point. */ +/*! Points at which a plugin can hook into RC */ +typedef enum +{ + RC_HOOK_RUNLEVEL_STOP_IN = 1, + RC_HOOK_RUNLEVEL_STOP_OUT = 4, + RC_HOOK_RUNLEVEL_START_IN = 5, + RC_HOOK_RUNLEVEL_START_OUT = 8, + /*! We send the abort if an init script requests we abort and drop + * into single user mode if system not fully booted */ + RC_HOOK_ABORT = 99, + RC_HOOK_SERVICE_STOP_IN = 101, + RC_HOOK_SERVICE_STOP_NOW = 102, + RC_HOOK_SERVICE_STOP_DONE = 103, + RC_HOOK_SERVICE_STOP_OUT = 104, + RC_HOOK_SERVICE_START_IN = 105, + RC_HOOK_SERVICE_START_NOW = 106, + RC_HOOK_SERVICE_START_DONE = 107, + RC_HOOK_SERVICE_START_OUT = 108 +} RC_HOOK; + +/*! Plugin entry point + * @param hook point + * @param name of runlevel or service + * @return 0 for success otherwise -1 */ +int rc_plugin_hook(RC_HOOK, const char *); + +/*! Plugins should write FOO=BAR to this fd to set any environment + * variables they wish. Variables should be separated by NULLs. */ +extern FILE *rc_environ_fd; + + +/*! Return a NULL terminated list of non comment lines from a file. */ +RC_STRINGLIST *rc_config_list(const char *); + +/*! Return a NULL terminated list of key=value lines from a file. */ +RC_STRINGLIST *rc_config_load(const char *); + +/*! Return the value of the entry from a key=value list. */ +char *rc_config_value(RC_STRINGLIST *, const char *); + +/*! Return the value of the entry from rc.conf. */ +char *rc_conf_value(const char *); + +/*! Check if a variable is a boolean and return its value. + * If variable is not a boolean then we set errno to be ENOENT when it does + * not exist or EINVAL if it's not a boolean. + * @param variable to check + * @return true if it matches true, yes or 1, false if otherwise. */ +bool rc_yesno(const char *); + +/*! @name String List functions + * Every string list should be released with a call to rc_stringlist_free. */ + +/*! Create a new stringlinst + * @return pointer to new list */ +RC_STRINGLIST *rc_stringlist_new(void); + +/*! Duplicate the item, add it to end of the list and return a pointer to it. + * @param list to add the item too + * @param item to add. + * @return pointer to newly added item */ +RC_STRING *rc_stringlist_add(RC_STRINGLIST *, const char *); + +/*! If the item does not exist in the list, duplicate it, add it to the + * list and then return a pointer to it. + * @param list to add the item too + * @param item to add. + * @return pointer to newly added item */ +RC_STRING *rc_stringlist_addu(RC_STRINGLIST *, const char *); + +/*! Free the item and remove it from the list. Return 0 on success otherwise -1. + * @param list to add the item too + * @param item to add. + * @return true on success, otherwise false */ +bool rc_stringlist_delete(RC_STRINGLIST *, const char *); + +/*! Find the item on the list. + * @param list to search + * @param item to find. + * @return pointer to item */ +RC_STRING *rc_stringlist_find(RC_STRINGLIST *, const char *); + +/*! Split a string into a stringlist based on separator. + * @param string to split + * @param separator + * @return new list */ +RC_STRINGLIST *rc_stringlist_split(const char *, const char *); + + +/*! Sort the list according to C locale + * @param list to sort */ +void rc_stringlist_sort(RC_STRINGLIST **); + +/*! Frees each item on the list and the list itself. + * @param list to free */ +void rc_stringlist_free(RC_STRINGLIST *); + +typedef struct rc_pid +{ + pid_t pid; + LIST_ENTRY(rc_pid) entries; +} RC_PID; +typedef LIST_HEAD(rc_pidlist, rc_pid) RC_PIDLIST; + +/*! Find processes based on criteria. + * All of these are optional. + * pid overrides anything else. + * If both exec and cmd are given then we ignore exec. + * @param exec to check for + * @param argv to check for + * @param uid to check for + * @param pid to check for + * @return NULL terminated list of pids */ +RC_PIDLIST *rc_find_pids(const char *, const char *const *, uid_t, pid_t); + +/* Basically the same as rc_getline() below, it just returns multiple lines */ +bool rc_getfile(const char *, char **, size_t *); + +/* getline is a handy glibc function that not all libcs have, so + * we have our own */ +ssize_t rc_getline(char **, size_t *, FILE *); + +/* __END_DECLS */ +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/librc/rc.map b/src/librc/rc.map new file mode 100644 index 0000000..0012d8a --- /dev/null +++ b/src/librc/rc.map @@ -0,0 +1,66 @@ +RC_1.0 { +global: + rc_conf_value; + rc_config_list; + rc_config_load; + rc_config_value; + rc_deptree_depend; + rc_deptree_depends; + rc_deptree_free; + rc_deptree_load; + rc_deptree_load_file; + rc_deptree_order; + rc_deptree_update; + rc_deptree_update_needed; + rc_environ_fd; + rc_find_pids; + rc_getfile; + rc_getline; + rc_newer_than; + rc_older_than; + rc_proc_getent; + rc_runlevel_exists; + rc_runlevel_get; + rc_runlevel_list; + rc_runlevel_set; + rc_runlevel_stack; + rc_runlevel_stacks; + rc_runlevel_starting; + rc_runlevel_stopping; + rc_runlevel_unstack; + rc_service_add; + rc_service_daemons_crashed; + rc_service_daemon_set; + rc_service_delete; + rc_service_description; + rc_service_exists; + rc_service_extra_commands; + rc_service_in_runlevel; + rc_service_mark; + rc_service_options; + rc_service_resolve; + rc_service_schedule_clear; + rc_service_schedule_start; + rc_services_in_runlevel; + rc_services_in_runlevel_stacked; + rc_services_in_state; + rc_services_scheduled; + rc_services_scheduled_by; + rc_service_started_daemon; + rc_service_state; + rc_service_value_get; + rc_service_value_set; + rc_stringlist_add; + rc_stringlist_addu; + rc_stringlist_delete; + rc_stringlist_find; + rc_stringlist_split; + rc_stringlist_new; + rc_stringlist_sort; + rc_stringlist_free; + rc_sys; + rc_yesno; + +local: + *; +}; diff --git a/src/rc/.gitignore b/src/rc/.gitignore new file mode 100644 index 0000000..91d5707 --- /dev/null +++ b/src/rc/.gitignore @@ -0,0 +1,65 @@ +version.h +rc-status +rc-service +rc-update +runscript +service +start-stop-daemon +supervise-daemon +einfon +einfo +ewarnn +ewarn +eerrorn +eerror +ebegin +eend +ewend +eindent +eoutdent +esyslog +eval_ecolors +ewaitfile +veinfo +vewarn +vebegin +veend +vewend +veindent +veoutdent +service_starting +service_started +service_stopping +service_stopped +service_inactive +service_wasinactive +service_hotplugged +service_started_daemon +service_crashed +checkpath +fstabinfo +mountinfo +swclock +rc-depend +service_get_value +service_set_value +get_options +save_options +shell_var +is_newer_than +is_older_than +mark_service_starting +mark_service_started +mark_service_stopping +mark_service_stopped +mark_service_inactive +mark_service_wasinactive +mark_service_hotplugged +mark_service_failed +rc-abort +rc +openrc +openrc-init +openrc-run +openrc-shutdown +kill_all diff --git a/src/rc/Makefile b/src/rc/Makefile new file mode 100644 index 0000000..e39889b --- /dev/null +++ b/src/rc/Makefile @@ -0,0 +1,176 @@ +include ../../Makefile.inc +MK= ../../mk +include ${MK}/os.mk + +SRCS= checkpath.c do_e.c do_mark_service.c do_service.c \ + do_value.c fstabinfo.c is_newer_than.c is_older_than.c \ + mountinfo.c openrc-run.c rc-abort.c rc.c \ + rc-depend.c rc-logger.c rc-misc.c rc-plugin.c \ + rc-service.c rc-status.c rc-update.c \ + shell_var.c start-stop-daemon.c supervise-daemon.c _usage.c + +ifeq (${MKSELINUX},yes) +SRCS+= rc-selinux.c +endif + +ifeq (${OS},Linux) +SRCS+= kill_all.c openrc-init.c openrc-shutdown.c rc-wtmp.c +endif + +CLEANFILES= version.h rc-selinux.o + +BINDIR= ${PREFIX}/bin +SBINDIR= ${PREFIX}/sbin +LINKDIR= ${LIBEXECDIR} + +BINPROGS= rc-status +SBINPROGS = openrc openrc-run rc rc-service rc-update runscript service \ + start-stop-daemon supervise-daemon +RC_BINPROGS= einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \ + eindent eoutdent esyslog eval_ecolors ewaitfile \ + veinfo vewarn vebegin veend vewend veindent veoutdent \ + checkpath fstabinfo mountinfo rc-depend \ + is_newer_than is_older_than \ + service_get_value service_set_value get_options save_options \ + service_starting service_started \ + service_stopping service_stopped \ + service_inactive service_wasinactive \ + service_hotplugged service_started_daemon service_crashed \ + shell_var +RC_SBINPROGS= mark_service_starting mark_service_started \ + mark_service_stopping mark_service_stopped \ + mark_service_inactive mark_service_wasinactive \ + mark_service_hotplugged mark_service_failed \ + rc-abort + +ifeq (${OS},Linux) +RC_BINPROGS+= kill_all +SBINPROGS+= openrc-init openrc-shutdown +endif + +ALL_PROGS= ${BINPROGS} ${SBINPROGS} ${RC_BINPROGS} ${RC_SBINPROGS} +CLEANFILES+= ${ALL_PROGS} + +LOCAL_CPPFLAGS=-I../includes -I../librc -I../libeinfo +LOCAL_LDFLAGS=-L../librc -L../libeinfo +LDADD+= -lutil -lrc -leinfo + +include ${MK}/prog.mk +include ${MK}/gitver.mk +include ${MK}/cc.mk + +include ${MK}/termcap.mk +LDADD+= ${LIBDL} ${LIBKVM} +include ${MK}/pam.mk + +${SRCS}: version.h + +.PHONY: version.h.tmp +version.h.tmp: + echo "#define VERSION \"${VERSION}${GITVER}\"" >$@ + if test -n "${BRANDING}"; then \ + echo "#define BRANDING \"${BRANDING}\"" >> $@; \ + fi + +version.h: version.h.tmp + cmp -s $@.tmp $@ && rm $@.tmp || mv $@.tmp $@ + +install: all + ${INSTALL} -d ${DESTDIR}${SBINDIR} + ${INSTALL} -m ${BINMODE} ${SBINPROGS} ${DESTDIR}${SBINDIR} + ${INSTALL} -d ${DESTDIR}${BINDIR} + ${INSTALL} -m ${BINMODE} ${BINPROGS} ${DESTDIR}${BINDIR} + ${INSTALL} -d ${DESTDIR}${LINKDIR}/bin + ${INSTALL} -m ${BINMODE} ${RC_BINPROGS} ${DESTDIR}${LINKDIR}/bin + ${INSTALL} -d ${DESTDIR}${LINKDIR}/sbin + ${INSTALL} -m ${BINMODE} ${RC_SBINPROGS} ${DESTDIR}${LINKDIR}/sbin + if test "${MKPAM}" = pam; then \ + ${INSTALL} -d ${DESTDIR}${PAMDIR}; \ + ${INSTALL} -m ${PAMMODE} start-stop-daemon.pam ${DESTDIR}${PAMDIR}/start-stop-daemon; \ + ${INSTALL} -m ${PAMMODE} supervise-daemon.pam ${DESTDIR}${PAMDIR}/supervise-daemon; \ + fi + +check test:: + +all: ${ALL_PROGS} + +checkpath: checkpath.o _usage.o rc-misc.o +ifeq (${MKSELINUX},yes) +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 + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} + +fstabinfo: fstabinfo.o _usage.o rc-misc.o + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} + +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 + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} + +is_older_than: is_older_than.o rc-misc.o + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} + +mark_service_starting mark_service_started \ +mark_service_stopping mark_service_stopped \ +mark_service_inactive mark_service_wasinactive \ +mark_service_hotplugged mark_service_failed: do_mark_service.o rc-misc.o + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} + +mountinfo: mountinfo.o _usage.o rc-misc.o + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} + +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 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 +ifeq (${MKSELINUX},yes) +openrc-run runscript: rc-selinux.o +endif + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} + +rc-abort: rc-abort.o + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ -leinfo + +rc-depend: rc-depend.o _usage.o rc-misc.o + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} + +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 + ${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 + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} + +supervise-daemon: supervise-daemon.o _usage.o rc-misc.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 + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} + +service_starting service_started \ +service_stopping service_stopped \ +service_inactive service_wasinactive \ +service_hotplugged service_started_daemon \ +service_crashed: do_service.o rc-misc.o + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} + +shell_var: shell_var.o + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ diff --git a/src/rc/_usage.c b/src/rc/_usage.c new file mode 100644 index 0000000..0d3d5ed --- /dev/null +++ b/src/rc/_usage.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2007-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. + */ + +#include +#include +#include +#include "rc.h" +#include "rc-misc.h" +#include "_usage.h" +#include "version.h" + +#if lint +# define _noreturn +#endif +#if __GNUC__ > 2 || defined(__INTEL_COMPILER) +# define _noreturn __attribute__ ((__noreturn__)) +#else +# define _noreturn +#endif + +void set_quiet_options(void) +{ + static int qcount = 0; + + qcount ++; + switch (qcount) { + case 1: + setenv ("EINFO_QUIET", "YES", 1); + break; + case 2: + setenv ("EERROR_QUIET", "YES", 1); + break; + } +} + +_noreturn void show_version(void) +{ + const char *systype = NULL; + + printf("%s (OpenRC", applet); + if ((systype = rc_sys())) + printf(" [%s]", systype); + printf(") %s", VERSION); +#ifdef BRANDING + printf(" (%s)", BRANDING); +#endif + printf("\n"); + exit(EXIT_SUCCESS); +} + +_noreturn void usage(int exit_status) +{ + const char * const has_arg[] = { "", "", "[arg]" }; + int i; + int len; + char *lo; + char *p; + char *token; + char val[4] = "-?,"; + + if (usagestring) + printf("%s", usagestring); + else + printf("Usage: %s [options] ", applet); + + if (extraopts) + printf("%s", extraopts); + + printf("\n\nOptions: [ %s ]\n", getoptstring); + for (i = 0; longopts[i].name; ++i) { + val[1] = longopts[i].val; + len = printf(" %3s --%s %s", isprint(longopts[i].val) ? val : "", + longopts[i].name, has_arg[longopts[i].has_arg]); + + lo = p = xstrdup(longopts_help[i]); + while ((token = strsep(&p, "\n"))) { + len = 36 - len; + if (len > 0) + printf("%*s", len, ""); + puts(token); + len = 0; + } + free(lo); + } + exit(exit_status); +} diff --git a/src/rc/_usage.h b/src/rc/_usage.h new file mode 100644 index 0000000..fd20971 --- /dev/null +++ b/src/rc/_usage.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2007-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. + */ + +#include + +#define getoptstring_COMMON "ChqVv" + +#define longopts_COMMON \ + { "help", 0, NULL, 'h'}, \ + { "nocolor", 0, NULL, 'C'}, \ + { "version", 0, NULL, 'V'}, \ + { "verbose", 0, NULL, 'v'}, \ + { "quiet", 0, NULL, 'q'}, \ + { NULL, 0, NULL, 0 } + +#define longopts_help_COMMON \ + "Display this help output", \ + "Disable color output", \ + "Display software version", \ + "Run verbosely", \ + "Run quietly (repeat to suppress errors)" + +#define case_RC_COMMON_getopt_case_C setenv ("EINFO_COLOR", "NO", 1); +#define case_RC_COMMON_getopt_case_h usage (EXIT_SUCCESS); +#define case_RC_COMMON_getopt_case_V if (argc == 2) show_version(); +#define case_RC_COMMON_getopt_case_v setenv ("EINFO_VERBOSE", "YES", 1); +#define case_RC_COMMON_getopt_case_q set_quiet_options(); +#define case_RC_COMMON_getopt_default usage (EXIT_FAILURE); + +#define case_RC_COMMON_GETOPT \ + case 'C': case_RC_COMMON_getopt_case_C; break; \ + case 'h': case_RC_COMMON_getopt_case_h; break; \ + case 'V': case_RC_COMMON_getopt_case_V; break; \ + case 'v': case_RC_COMMON_getopt_case_v; break; \ + case 'q': case_RC_COMMON_getopt_case_q; break; \ + default: case_RC_COMMON_getopt_default; break; + +extern const char *applet; +extern const char *extraopts; +extern const char *getoptstring; +extern const struct option longopts[]; +extern const char * const longopts_help[]; +extern const char *usagestring; + +void set_quiet_options(void); +void show_version(void); +void usage(int exit_status); diff --git a/src/rc/checkpath.c b/src/rc/checkpath.c new file mode 100644 index 0000000..b150994 --- /dev/null +++ b/src/rc/checkpath.c @@ -0,0 +1,296 @@ +/* + * checkpath.c + * Checks for the existance of a file or directory and creates it + * if necessary. It can also correct its ownership. + */ + +/* + * Copyright (c) 2007-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. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "rc.h" +#include "rc-misc.h" +#include "rc-selinux.h" +#include "_usage.h" + +typedef enum { + inode_unknown = 0, + inode_file = 1, + inode_dir = 2, + inode_fifo = 3, +} inode_t; + +const char *applet = NULL; +const char *extraopts ="path1 [path2] [...]"; +const char *getoptstring = "dDfFpm:o:W" getoptstring_COMMON; +const struct option longopts[] = { + { "directory", 0, NULL, 'd'}, + { "directory-truncate", 0, NULL, 'D'}, + { "file", 0, NULL, 'f'}, + { "file-truncate", 0, NULL, 'F'}, + { "pipe", 0, NULL, 'p'}, + { "mode", 1, NULL, 'm'}, + { "owner", 1, NULL, 'o'}, + { "writable", 0, NULL, 'W'}, + longopts_COMMON +}; +const char * const longopts_help[] = { + "Create a directory if not exists", + "Create/empty directory", + "Create a file if not exists", + "Truncate file", + "Create a named pipe (FIFO) if not exists", + "Mode to check", + "Owner to check (user:group)", + "Check whether the path is writable or not", + longopts_help_COMMON +}; +const char *usagestring = NULL; + +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 r; + int u; + + memset(&st, 0, sizeof(st)); + if (lstat(path, &st) || 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); + if (fd == -1) { + eerror("%s: open: %s", applet, strerror(errno)); + return -1; + } + close (fd); + } else if (type == inode_dir) { + einfo("%s: creating directory", path); + if (!mode) /* 775 */ + mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH; + u = umask(0); + /* We do not recursively create parents */ + r = mkdir(path, mode); + umask(u); + if (r == -1 && errno != EEXIST) { + eerror("%s: mkdir: %s", applet, + strerror (errno)); + return -1; + } + mode = 0; + } else if (type == inode_fifo) { + einfo("%s: creating fifo", path); + if (!mode) /* 600 */ + mode = S_IRUSR | S_IWUSR; + u = umask(0); + r = mkfifo(path, mode); + umask(u); + if (r == -1 && errno != EEXIST) { + eerror("%s: mkfifo: %s", applet, + strerror (errno)); + return -1; + } + } + } else { + if (type != inode_dir && S_ISDIR(st.st_mode)) { + eerror("%s: is a directory", path); + return 1; + } + if (type != inode_file && S_ISREG(st.st_mode)) { + eerror("%s: is a file", path); + return 1; + } + if (type != inode_fifo && S_ISFIFO(st.st_mode)) { + eerror("%s: is a fifo", 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); + 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 (selinux_on) + selinux_util_label(path); + + return 0; +} + +static int parse_owner(struct passwd **user, struct group **group, + const char *owner) +{ + char *u = xstrdup (owner); + char *g = strchr (u, ':'); + int id = 0; + int retval = 0; + + if (g) + *g++ = '\0'; + + if (user && *u) { + if (sscanf(u, "%d", &id) == 1) + *user = getpwuid((uid_t) id); + else + *user = getpwnam(u); + if (*user == NULL) + retval = -1; + } + + if (group && g && *g) { + if (sscanf(g, "%d", &id) == 1) + *group = getgrgid((gid_t) id); + else + *group = getgrnam(g); + if (*group == NULL) + retval = -1; + } + + free(u); + return retval; +} + +int main(int argc, char **argv) +{ + int opt; + uid_t uid = geteuid(); + gid_t gid = getgid(); + mode_t mode = 0; + struct passwd *pw = NULL; + struct group *gr = NULL; + inode_t type = inode_unknown; + int retval = EXIT_SUCCESS; + bool trunc = false; + bool chowner = false; + bool writable = false; + bool selinux_on = false; + + applet = basename_c(argv[0]); + while ((opt = getopt_long(argc, argv, getoptstring, + longopts, (int *) 0)) != -1) + { + switch (opt) { + case 'D': + trunc = true; + case 'd': + type = inode_dir; + break; + case 'F': + trunc = true; + case 'f': + type = inode_file; + break; + case 'p': + type = inode_fifo; + break; + case 'm': + if (parse_mode(&mode, optarg) != 0) + eerrorx("%s: invalid mode `%s'", + applet, optarg); + break; + case 'o': + chowner = true; + if (parse_owner(&pw, &gr, optarg) != 0) + eerrorx("%s: owner `%s' not found", + applet, optarg); + break; + case 'W': + writable = true; + break; + + case_RC_COMMON_GETOPT + } + } + + if (optind >= argc) + usage(EXIT_FAILURE); + + if (writable && type != inode_unknown) + eerrorx("%s: -W cannot be specified along with -d, -f or -p", applet); + + if (pw) { + uid = pw->pw_uid; + gid = pw->pw_gid; + } + if (gr) + gid = gr->gr_gid; + + if (selinux_util_open() == 1) + selinux_on = true; + + while (optind < argc) { + if (writable) + exit(!is_writable(argv[optind])); + if (do_check(argv[optind], uid, gid, mode, type, trunc, chowner, selinux_on)) + retval = EXIT_FAILURE; + optind++; + } + + if (selinux_on) + selinux_util_close(); + + return retval; +} diff --git a/src/rc/do_e.c b/src/rc/do_e.c new file mode 100644 index 0000000..9299665 --- /dev/null +++ b/src/rc/do_e.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2007-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. + */ + +#define SYSLOG_NAMES + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "helpers.h" + +/* usecs to wait while we poll the file existance */ +#define WAIT_INTERVAL 20000000 + +const char *applet = NULL; + +static int syslog_decode(char *name, CODE *codetab) +{ + CODE *c; + + if (isdigit((unsigned char)*name)) + return atoi(name); + + for (c = codetab; c->c_name; c++) + if (! strcasecmp(name, c->c_name)) + return c->c_val; + + return -1; +} + +int main(int argc, char **argv) +{ + int retval = EXIT_SUCCESS; + int i; + size_t l = 0; + char *message = NULL; + char *p; + int level = 0; + struct timespec ts; + struct timeval stop, now; + int (*e) (const char *, ...) EINFO_PRINTF(1, 2) = NULL; + int (*ee) (int, const char *, ...) EINFO_PRINTF(2, 3) = NULL; + + applet = basename_c(argv[0]); + argc--; + argv++; + + if (strcmp(applet, "eval_ecolors") == 0) { + printf("GOOD='%s'\nWARN='%s'\nBAD='%s'\nHILITE='%s'\nBRACKET='%s'\nHYPERBOLA='%s'\nNORMAL='%s'\n", + ecolor(ECOLOR_GOOD), + ecolor(ECOLOR_WARN), + ecolor(ECOLOR_BAD), + ecolor(ECOLOR_HILITE), + ecolor(ECOLOR_BRACKET), + ecolor(ECOLOR_HYPERBOLA), + ecolor(ECOLOR_NORMAL)); + exit(EXIT_SUCCESS); + } + + if (argc > 0) { + if (strcmp(applet, "eend") == 0 || + strcmp(applet, "ewend") == 0 || + strcmp(applet, "veend") == 0 || + strcmp(applet, "vweend") == 0 || + strcmp(applet, "ewaitfile") == 0) + { + errno = 0; + retval = (int)strtoimax(argv[0], &p, 0); + if (!p || *p != '\0') + errno = EINVAL; + if (errno) + retval = EXIT_FAILURE; + else { + argc--; + argv++; + } + } else if (strcmp(applet, "esyslog") == 0 || + strcmp(applet, "elog") == 0) { + p = strchr(argv[0], '.'); + if (!p || + (level = syslog_decode(p + 1, prioritynames)) == -1) + eerrorx("%s: invalid log level `%s'", applet, argv[0]); + + if (argc < 3) + eerrorx("%s: not enough arguments", applet); + + unsetenv("EINFO_LOG"); + setenv("EINFO_LOG", argv[1], 1); + + argc -= 2; + argv += 2; + } + } + + if (strcmp(applet, "ewaitfile") == 0) { + if (errno) + eerrorx("%s: invalid timeout", applet); + if (argc == 0) + eerrorx("%s: not enough arguments", applet); + + gettimeofday(&stop, NULL); + /* retval stores the timeout */ + stop.tv_sec += retval; + ts.tv_sec = 0; + ts.tv_nsec = WAIT_INTERVAL; + for (i = 0; i < argc; i++) { + ebeginv("Waiting for %s", argv[i]); + for (;;) { + if (exists(argv[i])) + break; + if (nanosleep(&ts, NULL) == -1) + return EXIT_FAILURE; + gettimeofday(&now, NULL); + if (retval <= 0) + continue; + if (timercmp(&now, &stop, <)) + continue; + eendv(EXIT_FAILURE, + "timed out waiting for %s", argv[i]); + return EXIT_FAILURE; + } + eendv(EXIT_SUCCESS, NULL); + } + return EXIT_SUCCESS; + } + + if (argc > 0) { + for (i = 0; i < argc; i++) + l += strlen(argv[i]) + 1; + + message = xmalloc(l); + p = message; + + for (i = 0; i < argc; i++) { + if (i > 0) + *p++ = ' '; + l = strlen(argv[i]); + memcpy(p, argv[i], l); + p += l; + } + *p = 0; + } + + if (strcmp(applet, "einfo") == 0) + e = einfo; + else if (strcmp(applet, "einfon") == 0) + e = einfon; + else if (strcmp(applet, "ewarn") == 0) + e = ewarn; + else if (strcmp(applet, "ewarnn") == 0) + e = ewarnn; + else if (strcmp(applet, "eerror") == 0) { + e = eerror; + retval = 1; + } else if (strcmp(applet, "eerrorn") == 0) { + e = eerrorn; + retval = 1; + } else if (strcmp(applet, "ebegin") == 0) + e = ebegin; + else if (strcmp(applet, "eend") == 0) + ee = eend; + else if (strcmp(applet, "ewend") == 0) + ee = ewend; + else if (strcmp(applet, "esyslog") == 0) { + elog(retval, "%s", message); + retval = 0; + } else if (strcmp(applet, "veinfo") == 0) + e = einfov; + else if (strcmp(applet, "veinfon") == 0) + e = einfovn; + else if (strcmp(applet, "vewarn") == 0) + e = ewarnv; + else if (strcmp(applet, "vewarnn") == 0) + e = ewarnvn; + else if (strcmp(applet, "vebegin") == 0) + e = ebeginv; + else if (strcmp(applet, "veend") == 0) + ee = eendv; + else if (strcmp(applet, "vewend") == 0) + ee = ewendv; + else if (strcmp(applet, "eindent") == 0) + eindent(); + else if (strcmp(applet, "eoutdent") == 0) + eoutdent(); + else if (strcmp(applet, "veindent") == 0) + eindentv(); + else if (strcmp(applet, "veoutdent") == 0) + eoutdentv(); + else { + eerror("%s: unknown applet", applet); + retval = EXIT_FAILURE; + } + + if (message) { + if (e) + e("%s", message); + else if (ee) + ee(retval, "%s", message); + } else { + if (e) + e(NULL); + else if (ee) + ee(retval, NULL); + } + + free(message); + return retval; +} diff --git a/src/rc/do_mark_service.c b/src/rc/do_mark_service.c new file mode 100644 index 0000000..fea31ee --- /dev/null +++ b/src/rc/do_mark_service.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2007-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. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "rc.h" +#include "rc-misc.h" + +const char *applet = NULL; + +int main(int argc, char **argv) +{ + bool ok = false; + char *svcname = getenv("RC_SVCNAME"); + char *service = NULL; + char *openrc_pid; + /* char *mtime; */ + pid_t pid; + RC_SERVICE bit; + /* size_t l; */ + + applet = basename_c(argv[0]); + if (argc > 1) + service = argv[1]; + else + service = svcname; + + if (service == NULL || *service == '\0') + eerrorx("%s: no service specified", applet); + + if (!strncmp(applet, "mark_", 5) && + (bit = lookup_service_state(applet + 5))) + ok = rc_service_mark(service, bit); + else + eerrorx("%s: unknown applet", applet); + + /* If we're marking ourselves then we need to inform our parent + openrc-run process so they do not mark us based on our exit code */ + /* + * FIXME: svcname and service are almost always equal except called from a + * shell with just argv[1] - So that doesn't seem to do what Roy initially + * expected. + * See 20120424041423.GA23657@odin.qasl.de (Tue, 24 Apr 2012 06:14:23 +0200, + * openrc@gentoo.org). + */ + if (ok && svcname && strcmp(svcname, service) == 0) { + openrc_pid = getenv("RC_OPENRC_PID"); + if (openrc_pid && sscanf(openrc_pid, "%d", &pid) == 1) + if (kill(pid, SIGHUP) != 0) + eerror("%s: failed to signal parent %d: %s", + applet, pid, strerror(errno)); + + /* Remove the exclusive time test. This ensures that it's not + in control as well */ + /* + l = strlen(RC_SVCDIR "/exclusive") + strlen(svcname) + + strlen(openrc_pid) + 4; + mtime = xmalloc(l); + snprintf(mtime, l, RC_SVCDIR "/exclusive/%s.%s", + svcname, openrc_pid); + if (exists(mtime) && unlink(mtime) != 0) + eerror("%s: unlink: %s", applet, strerror(errno)); + free(mtime); + */ + } + + return ok ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/rc/do_service.c b/src/rc/do_service.c new file mode 100644 index 0000000..a36a09c --- /dev/null +++ b/src/rc/do_service.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2007-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. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "rc.h" +#include "rc-misc.h" + +const char *applet = NULL; + +int main(int argc, char **argv) +{ + bool ok = false; + char *service; + char *exec; + int idx = 0; + RC_SERVICE state, bit; + + applet = basename_c(argv[0]); + if (argc > 1) + service = argv[1]; + else + service = getenv("RC_SVCNAME"); + + if (service == NULL || *service == '\0') + eerrorx("%s: no service specified", applet); + + state = rc_service_state(service); + bit = lookup_service_state(applet); + if (bit) { + ok = (state & bit); + } else if (strcmp(applet, "service_started_daemon") == 0) { + service = getenv("RC_SVCNAME"); + exec = argv[1]; + if (argc > 3) { + service = argv[1]; + exec = argv[2]; + sscanf(argv[3], "%d", &idx); + } else if (argc == 3) { + if (sscanf(argv[2], "%d", &idx) != 1) { + service = argv[1]; + exec = argv[2]; + } + } + ok = rc_service_started_daemon(service, exec, NULL, idx); + + } else if (strcmp(applet, "service_crashed") == 0) { + ok = (_rc_can_find_pids() && + rc_service_daemons_crashed(service) && + errno != EACCES); + } else + eerrorx("%s: unknown applet", applet); + + return ok ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/rc/do_value.c b/src/rc/do_value.c new file mode 100644 index 0000000..e6aa3ef --- /dev/null +++ b/src/rc/do_value.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2016 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. + */ + +#define SYSLOG_NAMES + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "rc.h" +#include "rc-misc.h" + +const char *applet = NULL; + +int main(int argc, char **argv) +{ + bool ok = false; + char *service = getenv("RC_SVCNAME"); + char *option; + + applet = basename_c(argv[0]); + if (service == NULL) + eerrorx("%s: no service specified", applet); + + if (argc < 2 || ! argv[1] || *argv[1] == '\0') + eerrorx("%s: no option specified", applet); + + if (strcmp(applet, "service_get_value") == 0 || + strcmp(applet, "get_options") == 0) + { + option = rc_service_value_get(service, argv[1]); + if (option) { + printf("%s", option); + free(option); + ok = true; + } + } else if (strcmp(applet, "service_set_value") == 0 || + strcmp(applet, "save_options") == 0) + ok = rc_service_value_set(service, argv[1], argv[2]); + else + eerrorx("%s: unknown applet", applet); + + return ok ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/rc/fstabinfo.c b/src/rc/fstabinfo.c new file mode 100644 index 0000000..75c8bc2 --- /dev/null +++ b/src/rc/fstabinfo.c @@ -0,0 +1,339 @@ +/* + * fstabinfo.c + * Gets information about /etc/fstab. + */ + +/* + * Copyright (c) 2007-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. + */ + +#include + +#include +#include +#include +#include +#include +#include + +/* Yay for linux and its non liking of POSIX functions. + Okay, we could use getfsent but the man page says use getmntent instead + AND we don't have getfsent on uclibc or dietlibc for some odd reason. */ +#ifdef __linux__ +# define HAVE_GETMNTENT +# include +# define ENT mntent +# define START_ENT fp = setmntent ("/etc/fstab", "r"); +# 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 +#else +# define HAVE_GETFSENT +# include +# define ENT fstab +# define START_ENT +# 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 +#endif + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-misc.h" +#include "_usage.h" + +const char *applet = NULL; +const char *extraopts = NULL; +const char *getoptstring = "MRbmop:t:" getoptstring_COMMON; +const struct option longopts[] = { + { "mount", 0, NULL, 'M' }, + { "remount", 0, NULL, 'R' }, + { "blockdevice", 0, NULL, 'b' }, + { "mountargs", 0, NULL, 'm' }, + { "options", 0, NULL, 'o' }, + { "passno", 1, NULL, 'p' }, + { "fstype", 1, NULL, 't' }, + longopts_COMMON +}; +const char * const longopts_help[] = { + "Mounts the filesytem from the mountpoint", + "Remounts the filesystem based on the information in fstab", + "Extract the block device", + "Show arguments needed to mount the entry", + "Extract the options field", + "Extract or query the pass number field", + "List entries with matching file system type", + longopts_help_COMMON +}; +const char *usagestring = NULL; + +#ifdef HAVE_GETMNTENT +static struct mntent * +getmntfile(const char *file) +{ + struct mntent *ent; + FILE *fp; + + START_ENT; + while ((ent = getmntent(fp))) + if (strcmp(file, ent->mnt_dir) == 0) + break; + END_ENT; + + return ent; +} +#endif + +extern const char *applet; + +static int +do_mount(struct ENT *ent, bool remount) +{ + char *argv[10]; + pid_t pid; + int status; + + argv[0] = UNCONST("mount"); + argv[1] = UNCONST("-o"); + argv[2] = ENT_OPTS(*ent); + argv[3] = UNCONST("-t"); + argv[4] = ENT_TYPE(*ent); + if (!remount) { + 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[9] = NULL; +#else + argv[5] = UNCONST("-u"); + argv[6] = ENT_BLOCKDEVICE(*ent); + argv[7] = ENT_FILE(*ent); + argv[8] = NULL; +#endif + } + switch (pid = vfork()) { + case -1: + eerrorx("%s: vfork: %s", applet, strerror(errno)); + /* NOTREACHED */ + case 0: + execvp(argv[0], argv); + eerror("%s: execv: %s", applet, strerror(errno)); + _exit(EXIT_FAILURE); + /* NOTREACHED */ + default: + waitpid(pid, &status, 0); + if (WIFEXITED(status)) + return WEXITSTATUS(status); + else + return -1; + /* NOTREACHED */ + } +} + +#define OUTPUT_FILE (1 << 1) +#define OUTPUT_MOUNTARGS (1 << 2) +#define OUTPUT_OPTIONS (1 << 3) +#define OUTPUT_PASSNO (1 << 4) +#define OUTPUT_BLOCKDEV (1 << 5) +#define OUTPUT_MOUNT (1 << 6) +#define OUTPUT_REMOUNT (1 << 7) + +int main(int argc, char **argv) +{ + struct ENT *ent; + int result = EXIT_SUCCESS; + char *token; + int i, p; + int opt; + int output = OUTPUT_FILE; + RC_STRINGLIST *files = rc_stringlist_new(); + RC_STRING *file, *file_np; + bool filtered = false; + +#ifdef HAVE_GETMNTENT + FILE *fp; +#endif + + /* fail if there is no /etc/fstab */ + if (!exists("/etc/fstab")) + eerrorx("/etc/fstab does not exist"); + /* Ensure that we are only quiet when explicitly told to be */ + unsetenv("EINFO_QUIET"); + + applet = basename_c(argv[0]); + while ((opt = getopt_long(argc, argv, getoptstring, + longopts, (int *) 0)) != -1) + { + switch (opt) { + case 'M': + output = OUTPUT_MOUNT; + break; + case 'R': + output = OUTPUT_REMOUNT; + break; + case 'b': + output = OUTPUT_BLOCKDEV; + break; + case 'o': + output = OUTPUT_OPTIONS; + break; + case 'm': + output = OUTPUT_MOUNTARGS; + break; + + case 'p': + switch (optarg[0]) { + case '=': + case '<': + case '>': + if (sscanf(optarg + 1, "%d", &i) != 1) + eerrorx("%s: invalid passno %s", + argv[0], optarg + 1); + + filtered = true; + opt = optarg[0]; + START_ENT; + while ((ent = GET_ENT)) { + if (strcmp(ENT_FILE(ent), "none") == 0) + continue; + p = ENT_PASS(ent); + if ((opt == '=' && i == p) || + (opt == '<' && i > p && p != 0) || + (opt == '>' && i < p && p != 0)) + rc_stringlist_add(files, + ENT_FILE(ent)); + } + END_ENT; + break; + + default: + rc_stringlist_add(files, optarg); + output = OUTPUT_PASSNO; + break; + } + break; + + case 't': + filtered = true; + while ((token = strsep(&optarg, ","))) { + START_ENT; + while ((ent = GET_ENT)) + if (strcmp(token, ENT_TYPE(ent)) == 0) + rc_stringlist_add(files, + ENT_FILE(ent)); + END_ENT; + } + break; + + case_RC_COMMON_GETOPT + } + } + + if (optind < argc) { + if (TAILQ_FIRST(files)) { + TAILQ_FOREACH_SAFE(file, files, entries, file_np) { + for (i = optind; i < argc; i++) + if (strcmp(argv[i], file->value) == 0) + break; + if (i >= argc) + rc_stringlist_delete(files, + file->value); + } + } else { + while (optind < argc) + rc_stringlist_add(files, argv[optind++]); + } + } else if (!filtered) { + START_ENT; + while ((ent = GET_ENT)) + rc_stringlist_add(files, ENT_FILE(ent)); + END_ENT; + + if (!TAILQ_FIRST(files)) + eerrorx("%s: empty fstab", argv[0]); + } + + if (!TAILQ_FIRST(files)) { + rc_stringlist_free(files); + return (EXIT_FAILURE); + } + + /* Ensure we always display something */ + START_ENT; + TAILQ_FOREACH(file, files, entries) { + if (!(ent = GET_ENT_FILE(file->value))) { + result = EXIT_FAILURE; + continue; + } + + /* mount or remount? */ + switch (output) { + case OUTPUT_MOUNT: + result += do_mount(ent, false); + break; + + case OUTPUT_REMOUNT: + result += do_mount(ent, true); + break; + } + + /* No point in outputting if quiet */ + if (rc_yesno(getenv("EINFO_QUIET"))) + continue; + + switch (output) { + case OUTPUT_BLOCKDEV: + printf("%s\n", ENT_BLOCKDEVICE(ent)); + break; + + case OUTPUT_MOUNTARGS: + printf("-o %s -t %s %s %s\n", + ENT_OPTS(ent), + ENT_TYPE(ent), + ENT_BLOCKDEVICE(ent), + file->value); + break; + + case OUTPUT_OPTIONS: + printf("%s\n", ENT_OPTS(ent)); + break; + + case OUTPUT_FILE: + printf("%s\n", file->value); + break; + + case OUTPUT_PASSNO: + printf("%d\n", ENT_PASS(ent)); + break; + } + } + END_ENT; + + rc_stringlist_free(files); + exit(result); + /* NOTREACHED */ +} diff --git a/src/rc/is_newer_than.c b/src/rc/is_newer_than.c new file mode 100644 index 0000000..4093ea8 --- /dev/null +++ b/src/rc/is_newer_than.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016 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 +#include +#include +#include + +#include "rc.h" +#include "rc-misc.h" + +int main(int argc, char **argv) +{ + int i; + + if (argc < 3) + return EXIT_FAILURE; + + /* This test is correct as it's not present in baselayout */ + for (i = 2; i < argc; ++i) + if (!rc_newer_than(argv[1], argv[i], NULL, NULL)) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} diff --git a/src/rc/is_older_than.c b/src/rc/is_older_than.c new file mode 100644 index 0000000..aea5377 --- /dev/null +++ b/src/rc/is_older_than.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016 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 +#include +#include +#include + +#include "rc.h" +#include "rc-misc.h" + +int main(int argc, char **argv) +{ + int i; + + if (argc < 3) + return EXIT_FAILURE; + + /* This test is perverted - historically the baselayout function + * returns 0 on *failure*, which is plain wrong */ + for (i = 2; i < argc; ++i) + if (!rc_newer_than(argv[1], argv[i], NULL, NULL)) + return EXIT_SUCCESS; + + return EXIT_FAILURE; +} diff --git a/src/rc/kill_all.c b/src/rc/kill_all.c new file mode 100644 index 0000000..0b5c4b8 --- /dev/null +++ b/src/rc/kill_all.c @@ -0,0 +1,251 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 */ + execl("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[PATH_MAX+1]; + FILE *fp; + char path[PATH_MAX+1]; + pid_t temp_pid; + bool user_process = true; + + while (pid >0 && user_process) { + if (pid == 2) { + user_process = false; + continue; + } + snprintf(path, sizeof(path), "/proc/%d/status", pid); + fp = fopen(path, "r"); + /* + * 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[0] = 0; + if (fgets(buf, sizeof(buf), fp)) + sscanf(buf, "PPid: %d", &temp_pid); + else + 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[PATH_MAX+1]; + 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? */ + sprintf(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; +} diff --git a/src/rc/mountinfo.c b/src/rc/mountinfo.c new file mode 100644 index 0000000..d9c25a3 --- /dev/null +++ b/src/rc/mountinfo.c @@ -0,0 +1,487 @@ +/* + * mountinfo.c + * Obtains information about mounted filesystems. + */ + +/* + * Copyright 2007-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. + */ + +#include +#include + +#if defined(__DragonFly__) || defined(__FreeBSD__) +# include +# include +# define F_FLAGS f_flags +#elif defined(BSD) && !defined(__GNU__) +# include +# define statfs statvfs +# define F_FLAGS f_flag +#elif defined(__linux__) || (defined(__FreeBSD_kernel__) && \ + defined(__GLIBC__)) || defined(__GNU__) +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-misc.h" +#include "_usage.h" + +const char *applet = NULL; +const char *procmounts = "/proc/mounts"; +const char *extraopts = "[mount1] [mount2] ..."; +const char *getoptstring = "f:F:n:N:o:O:p:P:iste:E:" getoptstring_COMMON; +const struct option longopts[] = { + { "fstype-regex", 1, NULL, 'f'}, + { "skip-fstype-regex", 1, NULL, 'F'}, + { "node-regex", 1, NULL, 'n'}, + { "skip-node-regex", 1, NULL, 'N'}, + { "options-regex", 1, NULL, 'o'}, + { "skip-options-regex", 1, NULL, 'O'}, + { "point-regex", 1, NULL, 'p'}, + { "skip-point-regex", 1, NULL, 'P'}, + { "options", 0, NULL, 'i'}, + { "fstype", 0, NULL, 's'}, + { "node", 0, NULL, 't'}, + { "netdev", 0, NULL, 'e'}, + { "nonetdev", 0, NULL, 'E'}, + longopts_COMMON +}; +const char * const longopts_help[] = { + "fstype regex to find", + "fstype regex to skip", + "node regex to find", + "node regex to skip", + "options regex to find", + "options regex to skip", + "point regex to find", + "point regex to skip", + "print options", + "print fstype", + "print node", + "is it a network device", + "is it not a network device", + longopts_help_COMMON +}; +const char *usagestring = NULL; + +typedef enum { + mount_from, + mount_to, + mount_fstype, + mount_options +} mount_type; + +typedef enum { + net_ignore, + net_yes, + net_no +} net_opts; + +struct args { + regex_t *node_regex; + regex_t *skip_node_regex; + regex_t *fstype_regex; + regex_t *skip_fstype_regex; + regex_t *options_regex; + regex_t *skip_options_regex; + RC_STRINGLIST *mounts; + mount_type mount_type; + net_opts netdev; +}; + +static int +process_mount(RC_STRINGLIST *list, struct args *args, + char *from, char *to, char *fstype, char *options, + int netdev) +{ + char *p; + RC_STRING *s; + + errno = ENOENT; + +#ifdef __linux__ + /* Skip the really silly rootfs */ + if (strcmp(fstype, "rootfs") == 0) + return -1; +#endif + + if (args->netdev == net_yes && + (netdev != -1 || TAILQ_FIRST(args->mounts))) + { + if (netdev != 0) + return 1; + } else if (args->netdev == net_no && + (netdev != -1 || TAILQ_FIRST(args->mounts))) + { + if (netdev != 1) + return 1; + } else { + if (args->node_regex && + regexec(args->node_regex, from, 0, NULL, 0) != 0) + return 1; + if (args->skip_node_regex && + regexec(args->skip_node_regex, from, 0, NULL, 0) == 0) + return 1; + + if (args->fstype_regex && + regexec(args->fstype_regex, fstype, 0, NULL, 0) != 0) + return -1; + if (args->skip_fstype_regex && + regexec(args->skip_fstype_regex, fstype, 0, NULL, 0) == 0) + return -1; + + if (args->options_regex && + regexec(args->options_regex, options, 0, NULL, 0) != 0) + return -1; + if (args->skip_options_regex && + regexec(args->skip_options_regex, options, 0, NULL, 0) == 0) + return -1; + } + + if (TAILQ_FIRST(args->mounts)) { + TAILQ_FOREACH(s, args->mounts, entries) + if (strcmp(s->value, to) == 0) + break; + if (! s) + return -1; + } + + switch (args->mount_type) { + case mount_from: + p = from; + break; + case mount_to: + p = to; + break; + case mount_fstype: + p = fstype; + break; + case mount_options: + p = options; + break; + default: + p = NULL; + errno = EINVAL; + break; + } + + if (p) { + errno = 0; + rc_stringlist_add(list, p); + return 0; + } + + return -1; +} + +#if defined(BSD) && !defined(__GNU__) + +/* Translate the mounted options to english + * This is taken directly from FreeBSD mount.c */ +static struct opt { + int o_opt; + const char *o_name; +} optnames[] = { + { MNT_ASYNC, "asynchronous" }, + { MNT_EXPORTED, "NFS exported" }, + { MNT_LOCAL, "local" }, + { MNT_NOATIME, "noatime" }, + { MNT_NOEXEC, "noexec" }, + { MNT_NOSUID, "nosuid" }, +#ifdef MNT_NOSYMFOLLOW + { MNT_NOSYMFOLLOW, "nosymfollow" }, +#endif + { MNT_QUOTA, "with quotas" }, + { MNT_RDONLY, "read-only" }, + { MNT_SYNCHRONOUS, "synchronous" }, + { MNT_UNION, "union" }, +#ifdef MNT_NOCLUSTERR + { MNT_NOCLUSTERR, "noclusterr" }, +#endif +#ifdef MNT_NOCLUSTERW + { MNT_NOCLUSTERW, "noclusterw" }, +#endif +#ifdef MNT_SUIDDIR + { MNT_SUIDDIR, "suiddir" }, +#endif + { MNT_SOFTDEP, "soft-updates" }, +#ifdef MNT_MULTILABEL + { MNT_MULTILABEL, "multilabel" }, +#endif +#ifdef MNT_ACLS + { MNT_ACLS, "acls" }, +#endif +#ifdef MNT_GJOURNAL + { MNT_GJOURNAL, "gjournal" }, +#endif + { 0, NULL } +}; + +static RC_STRINGLIST * +find_mounts(struct args *args) +{ + struct statfs *mnts; + int nmnts; + int i; + RC_STRINGLIST *list; + char *options = NULL; + uint64_t flags; + struct opt *o; + int netdev; + char *tmp; + size_t l; + + if ((nmnts = getmntinfo(&mnts, MNT_NOWAIT)) == 0) + eerrorx("getmntinfo: %s", strerror (errno)); + + list = rc_stringlist_new(); + for (i = 0; i < nmnts; i++) { + netdev = 0; + flags = mnts[i].F_FLAGS & MNT_VISFLAGMASK; + for (o = optnames; flags && o->o_opt; o++) { + if (flags & o->o_opt) { + if (o->o_opt == MNT_LOCAL) + netdev = 1; + 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); + free(options); + options = tmp; + } + } + flags &= ~o->o_opt; + } + + process_mount(list, args, + mnts[i].f_mntfromname, + mnts[i].f_mntonname, + mnts[i].f_fstypename, + options, + netdev); + + free(options); + options = NULL; + } + + return list; +} + +#elif defined(__linux__) || (defined(__FreeBSD_kernel__) && \ + defined(__GLIBC__)) || defined(__GNU__) +static struct mntent * +getmntfile(const char *file) +{ + struct mntent *ent = NULL; + FILE *fp; + + if (!exists("/etc/fstab")) + return NULL; + + fp = setmntent("/etc/fstab", "r"); + while ((ent = getmntent(fp))) + if (strcmp(file, ent->mnt_dir) == 0) + break; + endmntent(fp); + + return ent; +} + +static RC_STRINGLIST * +find_mounts(struct args *args) +{ + FILE *fp; + char *buffer; + char *p; + char *from; + char *to; + char *fst; + char *opts; + struct mntent *ent; + int netdev; + RC_STRINGLIST *list; + + if ((fp = fopen(procmounts, "r")) == NULL) + eerrorx("getmntinfo: %s", strerror(errno)); + + list = rc_stringlist_new(); + + buffer = xmalloc(sizeof(char) * PATH_MAX * 3); + while (fgets(buffer, PATH_MAX * 3, fp)) { + netdev = -1; + p = buffer; + from = strsep(&p, " "); + to = strsep(&p, " "); + fst = strsep(&p, " "); + opts = strsep(&p, " "); + + if ((ent = getmntfile(to))) { + if (strstr(ent->mnt_opts, "_netdev")) + netdev = 0; + else + netdev = 1; + } + + process_mount(list, args, from, to, fst, opts, netdev); + } + free(buffer); + fclose(fp); + + return list; +} + +#else +# error "Operating system not supported!" +#endif + +static regex_t * +get_regex(const char *string) +{ + regex_t *reg = xmalloc(sizeof (*reg)); + int result; + char buffer[256]; + + if ((result = regcomp(reg, string, REG_EXTENDED | REG_NOSUB)) != 0) + { + regerror(result, reg, buffer, sizeof(buffer)); + eerrorx("%s: invalid regex `%s'", applet, buffer); + } + + return reg; +} + +int main(int argc, char **argv) +{ + struct args args; + regex_t *point_regex = NULL; + regex_t *skip_point_regex = NULL; + RC_STRINGLIST *nodes; + RC_STRING *s; + char real_path[PATH_MAX + 1]; + int opt; + int result; + char *this_path; + +#define DO_REG(_var) \ + if (_var) free(_var); \ + _var = get_regex(optarg); +#define REG_FREE(_var) \ + if (_var) { regfree(_var); free(_var); } + + applet = basename_c(argv[0]); + memset (&args, 0, sizeof(args)); + args.mount_type = mount_to; + args.netdev = net_ignore; + args.mounts = rc_stringlist_new(); + + while ((opt = getopt_long(argc, argv, getoptstring, + longopts, (int *) 0)) != -1) + { + switch (opt) { + case 'e': + args.netdev = net_yes; + break; + case 'E': + args.netdev = net_no; + break; + case 'f': + DO_REG(args.fstype_regex); + break; + case 'F': + DO_REG(args.skip_fstype_regex); + break; + case 'n': + DO_REG(args.node_regex); + break; + case 'N': + DO_REG(args.skip_node_regex); + break; + case 'o': + DO_REG(args.options_regex); + break; + case 'O': + DO_REG(args.skip_options_regex); + break; + case 'p': + DO_REG(point_regex); + break; + case 'P': + DO_REG(skip_point_regex); + break; + case 'i': + args.mount_type = mount_options; + break; + case 's': + args.mount_type = mount_fstype; + break; + case 't': + args.mount_type = mount_from; + break; + + case_RC_COMMON_GETOPT + } + } + + while (optind < argc) { + if (argv[optind][0] != '/') + eerrorx("%s: `%s' is not a mount point", + argv[0], argv[optind]); + this_path = argv[optind++]; + if (realpath(this_path, real_path)) + this_path = real_path; + rc_stringlist_add(args.mounts, this_path); + } + nodes = find_mounts(&args); + rc_stringlist_free(args.mounts); + + REG_FREE(args.fstype_regex); + REG_FREE(args.skip_fstype_regex); + REG_FREE(args.node_regex); + REG_FREE(args.skip_node_regex); + REG_FREE(args.options_regex); + REG_FREE(args.skip_options_regex); + + result = EXIT_FAILURE; + + /* We should report the mounts in reverse order to ease unmounting */ + TAILQ_FOREACH_REVERSE(s, nodes, rc_stringlist, entries) { + if (point_regex && + regexec(point_regex, s->value, 0, NULL, 0) != 0) + continue; + if (skip_point_regex && + regexec(skip_point_regex, s->value, 0, NULL, 0) == 0) + continue; + if (! rc_yesno(getenv("EINFO_QUIET"))) + printf("%s\n", s->value); + result = EXIT_SUCCESS; + } + rc_stringlist_free(nodes); + + REG_FREE(point_regex); + REG_FREE(skip_point_regex); + + return result; +} diff --git a/src/rc/openrc-init.c b/src/rc/openrc-init.c new file mode 100644 index 0000000..0016dc2 --- /dev/null +++ b/src/rc/openrc-init.c @@ -0,0 +1,216 @@ +/* + * openrc-init.c + * This is the init process (pid 1) for OpenRC. + * + * This is based on code written by James Hammons , so + * I would like to publically thank him for his work. + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "helpers.h" +#include "rc.h" +#include "rc-wtmp.h" +#include "version.h" + +static const char *rc_default_runlevel = "default"; + +static void do_openrc(const char *runlevel) +{ + pid_t pid; + sigset_t all_signals; + sigset_t our_signals; + + sigfillset(&all_signals); + /* block all signals */ + sigprocmask(SIG_BLOCK, &all_signals, &our_signals); + pid = fork(); + switch(pid) { + case -1: + perror("fork"); + exit(1); + break; + case 0: + setsid(); + /* unblock all signals */ + sigprocmask(SIG_UNBLOCK, &all_signals, NULL); + printf("Starting %s runlevel\n", runlevel); + execl("/sbin/openrc", "/sbin/openrc", runlevel, NULL); + perror("exec"); + exit(1); + break; + default: + /* restore our signal mask */ + sigprocmask(SIG_SETMASK, &our_signals, NULL); + while (waitpid(pid, NULL, 0) != pid) + if (errno == ECHILD) + break; + break; + } +} + +static void init(const char *default_runlevel) +{ + const char *runlevel = NULL; + do_openrc("sysinit"); + do_openrc("boot"); + if (default_runlevel) + runlevel = default_runlevel; + else + runlevel = rc_conf_value("rc_default_runlevel"); + if (!runlevel) + runlevel = rc_default_runlevel; + if (!rc_runlevel_exists(runlevel)) { + printf("%s is an invalid runlevel\n", runlevel); + runlevel = rc_default_runlevel; + } + do_openrc(runlevel); + log_wtmp("reboot", "~~", 0, RUN_LVL, "~~"); +} + +static void handle_reexec(char *my_name) +{ + execl(my_name, my_name, "reexec", NULL); + return; +} + +static void handle_shutdown(const char *runlevel, int cmd) +{ + do_openrc(runlevel); + printf("Sending the final term signal\n"); + kill(-1, SIGTERM); + sleep(3); + printf("Sending the final kill signal\n"); + kill(-1, SIGKILL); + sync(); + reboot(cmd); +} + +static void handle_single(void) +{ + do_openrc("single"); +} + +static void reap_zombies(void) +{ + pid_t pid; + + for (;;) { + pid = waitpid(-1, NULL, WNOHANG); + if (pid == 0) + break; + else if (pid == -1) { + if (errno == ECHILD) + break; + perror("waitpid"); + continue; + } + } +} + +static void signal_handler(int sig) +{ + switch(sig) { + case SIGINT: + handle_shutdown("reboot", RB_AUTOBOOT); + break; + case SIGCHLD: + reap_zombies(); + break; + default: + printf("Unknown signal received, %d\n", sig); + break; + } +} + +int main(int argc, char **argv) +{ + char *default_runlevel; + char buf[2048]; + int count; + FILE *fifo; + bool reexec = false; + sigset_t signals; + struct sigaction sa; + + if (getpid() != 1) + return 1; + + printf("OpenRC init version %s starting\n", VERSION); + + if (argc > 1) + default_runlevel = argv[1]; + else + default_runlevel = NULL; + + if (default_runlevel && strcmp(default_runlevel, "reexec") == 0) + reexec = true; + + /* block all signals we do not handle */ + sigfillset(&signals); + sigdelset(&signals, SIGCHLD); + sigdelset(&signals, SIGINT); + sigprocmask(SIG_SETMASK, &signals, NULL); + + /* install signal handler */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = signal_handler; + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + reboot(RB_DISABLE_CAD); + + if (! reexec) + init(default_runlevel); + + if (mkfifo(RC_INIT_FIFO, 0600) == -1 && errno != EEXIST) + perror("mkfifo"); + + for (;;) { + /* This will block until a command is sent down the pipe... */ + fifo = fopen(RC_INIT_FIFO, "r"); + if (!fifo) { + if (errno != EINTR) + perror("fopen"); + continue; + } + count = fread(buf, 1, sizeof(buf) - 1, fifo); + buf[count] = 0; + fclose(fifo); + printf("PID1: Received \"%s\" from FIFO...\n", buf); + if (strcmp(buf, "halt") == 0) + handle_shutdown("shutdown", RB_HALT_SYSTEM); + else if (strcmp(buf, "kexec") == 0) + handle_shutdown("reboot", RB_KEXEC); + else if (strcmp(buf, "poweroff") == 0) + handle_shutdown("shutdown", RB_POWER_OFF); + else if (strcmp(buf, "reboot") == 0) + 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; +} diff --git a/src/rc/openrc-run.c b/src/rc/openrc-run.c new file mode 100644 index 0000000..bb79f42 --- /dev/null +++ b/src/rc/openrc-run.c @@ -0,0 +1,1419 @@ +/* + * openrc-run.c + * Handle launching of init scripts. + */ + +/* + * Copyright (c) 2007-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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) \ + || defined(__GNU__) +# include +#elif defined(__NetBSD__) || defined(__OpenBSD__) +# include +#else +# include +#endif + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-misc.h" +#include "rc-plugin.h" +#include "rc-selinux.h" +#include "_usage.h" + +#define PREFIX_LOCK RC_SVCDIR "/prefix.lock" + +#define WAIT_INTERVAL 20000000 /* usecs to poll the lock file */ +#define WAIT_TIMEOUT 60 /* seconds until we timeout */ +#define WARN_TIMEOUT 10 /* warn about this every N seconds */ + +const char *applet = NULL; +const char *extraopts = "stop | start | restart | describe | zap"; +const char *getoptstring = "dDsSvl:Z" getoptstring_COMMON; +const struct option longopts[] = { + { "debug", 0, NULL, 'd'}, + { "dry-run", 0, NULL, 'Z'}, + { "ifstarted", 0, NULL, 's'}, + { "ifstopped", 0, NULL, 'S'}, + { "nodeps", 0, NULL, 'D'}, + { "lockfd", 1, NULL, 'l'}, + longopts_COMMON +}; +const char *const longopts_help[] = { + "set xtrace when running the script", + "show what would be done", + "only run commands when started", + "only run commands when stopped", + "ignore dependencies", + "fd of the exclusive lock from rc", + longopts_help_COMMON +}; +const char *usagestring = NULL; + +static char *service, *runlevel, *ibsave, *prefix; +static RC_DEPTREE *deptree; +static RC_STRINGLIST *applet_list, *services, *tmplist; +static RC_STRINGLIST *restart_services; +static RC_STRINGLIST *need_services; +static RC_STRINGLIST *use_services; +static RC_STRINGLIST *want_services; +static RC_HOOK hook_out; +static int exclusive_fd = -1, master_tty = -1; +static bool sighup, in_background, deps, dry_run; +static pid_t service_pid; +static int signal_pipe[2] = { -1, -1 }; + +static RC_STRINGLIST *deptypes_b; /* broken deps */ +static RC_STRINGLIST *deptypes_n; /* needed deps */ +static RC_STRINGLIST *deptypes_nw; /* need+want deps */ +static RC_STRINGLIST *deptypes_nwu; /* need+want+use deps */ +static RC_STRINGLIST *deptypes_nwua; /* need+want+use+after deps */ +static RC_STRINGLIST *deptypes_m; /* needed deps for stopping */ +static RC_STRINGLIST *deptypes_mwua; /* need+want+use+after deps for stopping */ + +static void +handle_signal(int sig) +{ + int serrno = errno; + char signame[10] = { '\0' }; + struct winsize ws; + + switch (sig) { + case SIGHUP: + sighup = true; + break; + + case SIGCHLD: + if (signal_pipe[1] > -1) { + if (write(signal_pipe[1], &sig, sizeof(sig)) == -1) + eerror("%s: send: %s", + service, strerror(errno)); + } else + rc_waitpid(-1); + break; + + case SIGWINCH: + if (master_tty >= 0) { + ioctl(fileno(stdout), TIOCGWINSZ, &ws); + ioctl(master_tty, TIOCSWINSZ, &ws); + } + break; + + case SIGINT: + if (!signame[0]) + snprintf(signame, sizeof(signame), "SIGINT"); + /* FALLTHROUGH */ + case SIGTERM: + if (!signame[0]) + snprintf(signame, sizeof(signame), "SIGTERM"); + /* FALLTHROUGH */ + case SIGQUIT: + if (!signame[0]) + snprintf(signame, sizeof(signame), "SIGQUIT"); + /* Send the signal to our children too */ + if (service_pid > 0) + kill(service_pid, sig); + eerrorx("%s: caught %s, aborting", applet, signame); + /* NOTREACHED */ + + default: + eerror("%s: caught unknown signal %d", applet, sig); + } + + /* Restore errno */ + errno = serrno; +} + +static void +unhotplug() +{ + char file[PATH_MAX]; + + snprintf(file, sizeof(file), RC_SVCDIR "/hotplugged/%s", applet); + if (exists(file) && unlink(file) != 0) + eerror("%s: unlink `%s': %s", applet, file, strerror(errno)); +} + +static void +start_services(RC_STRINGLIST *list) +{ + RC_STRING *svc; + RC_SERVICE state = rc_service_state (service); + + if (!list) + return; + + if (state & RC_SERVICE_INACTIVE || + state & RC_SERVICE_WASINACTIVE || + state & RC_SERVICE_STARTING || + state & RC_SERVICE_STARTED) + { + TAILQ_FOREACH(svc, list, entries) { + if (!(rc_service_state(svc->value) & + RC_SERVICE_STOPPED)) + continue; + if (state & RC_SERVICE_INACTIVE || + state & RC_SERVICE_WASINACTIVE) + { + rc_service_schedule_start(service, + svc->value); + ewarn("WARNING: %s will start when %s has started", + svc->value, applet); + } else + service_start(svc->value); + } + } +} + +static void +restore_state(void) +{ + RC_SERVICE state; + + if (rc_in_plugin || exclusive_fd == -1) + return; + state = rc_service_state(applet); + if (state & RC_SERVICE_STOPPING) { + if (state & RC_SERVICE_WASINACTIVE) + rc_service_mark(applet, RC_SERVICE_INACTIVE); + else + rc_service_mark(applet, RC_SERVICE_STARTED); + if (rc_runlevel_stopping()) + rc_service_mark(applet, RC_SERVICE_FAILED); + } else if (state & RC_SERVICE_STARTING) { + if (state & RC_SERVICE_WASINACTIVE) + rc_service_mark(applet, RC_SERVICE_INACTIVE); + else + rc_service_mark(applet, RC_SERVICE_STOPPED); + if (rc_runlevel_starting()) + rc_service_mark(applet, RC_SERVICE_FAILED); + } + exclusive_fd = svc_unlock(applet, exclusive_fd); +} + +static void +cleanup(void) +{ + restore_state(); + + if (!rc_in_plugin) { + if (hook_out) { + rc_plugin_run(hook_out, applet); + if (hook_out == RC_HOOK_SERVICE_START_DONE) + rc_plugin_run(RC_HOOK_SERVICE_START_OUT, + applet); + else if (hook_out == RC_HOOK_SERVICE_STOP_DONE) + rc_plugin_run(RC_HOOK_SERVICE_STOP_OUT, + applet); + } + + if (restart_services) + start_services(restart_services); + } + + rc_plugin_unload(); + + rc_stringlist_free(deptypes_b); + rc_stringlist_free(deptypes_n); + rc_stringlist_free(deptypes_nw); + rc_stringlist_free(deptypes_nwu); + rc_stringlist_free(deptypes_nwua); + rc_stringlist_free(deptypes_m); + rc_stringlist_free(deptypes_mwua); + rc_deptree_free(deptree); + rc_stringlist_free(restart_services); + rc_stringlist_free(need_services); + rc_stringlist_free(use_services); + rc_stringlist_free(want_services); + rc_stringlist_free(services); + rc_stringlist_free(applet_list); + rc_stringlist_free(tmplist); + free(ibsave); + free(service); + free(prefix); + free(runlevel); +} + +/* Buffer and lock all output messages so that we get readable content */ +/* FIXME: Use a dynamic lock file that contains the tty/pts as well. + * For example openrc-pts8.lock or openrc-tty1.lock. + * Using a static lock file makes no sense, esp. in multi-user environments. + * Why don't we use (f)printf, as it is thread-safe through POSIX already? + * Bug: 360013 + */ +static int +write_prefix(const char *buffer, size_t bytes, bool *prefixed) +{ + size_t i, j; + const char *ec = ecolor(ECOLOR_HILITE); + const char *ec_normal = ecolor(ECOLOR_NORMAL); + ssize_t ret = 0; + int fd = fileno(stdout), lock_fd = -1; + + /* + * Lock the prefix. + * open() may fail here when running as user, as RC_SVCDIR may not be writable. + */ + lock_fd = open(PREFIX_LOCK, O_WRONLY | O_CREAT, 0664); + + if (lock_fd != -1) { + while (flock(lock_fd, LOCK_EX) != 0) { + if (errno != EINTR) { + ewarnv("flock() failed: %s", strerror(errno)); + break; + } + } + } + else + ewarnv("Couldn't open the prefix lock, please make sure you have enough permissions"); + + for (i = 0; i < bytes; i++) { + /* We don't prefix eend calls (cursor up) */ + if (buffer[i] == '\033' && !*prefixed) { + for (j = i + 1; j < bytes; j++) { + if (buffer[j] == 'A') + *prefixed = true; + if (isalpha((unsigned int)buffer[j])) + break; + } + } + + if (!*prefixed) { + ret += write(fd, ec, strlen(ec)); + ret += write(fd, prefix, strlen(prefix)); + ret += write(fd, ec_normal, strlen(ec_normal)); + ret += write(fd, "|", 1); + *prefixed = true; + } + + if (buffer[i] == '\n') + *prefixed = false; + ret += write(fd, buffer + i, 1); + } + + /* Release the lock */ + close(lock_fd); + + return ret; +} + +static int +svc_exec(const char *arg1, const char *arg2) +{ + int ret, fdout = fileno(stdout); + struct termios tt; + struct winsize ws; + int i; + int flags = 0; + struct pollfd fd[2]; + int s; + char *buffer; + size_t bytes; + bool prefixed = false; + int slave_tty; + sigset_t sigchldmask; + sigset_t oldmask; + + /* Setup our signal pipe */ + if (pipe(signal_pipe) == -1) + eerrorx("%s: pipe: %s", service, applet); + for (i = 0; i < 2; i++) + if ((flags = fcntl(signal_pipe[i], F_GETFD, 0) == -1 || + fcntl(signal_pipe[i], F_SETFD, flags | FD_CLOEXEC) == -1)) + eerrorx("%s: fcntl: %s", service, strerror(errno)); + + /* Open a pty for our prefixed output + * We do this instead of mapping pipes to stdout, stderr so that + * programs can tell if they're attached to a tty or not. + * The only loss is that we can no longer tell the difference + * between the childs stdout or stderr */ + master_tty = slave_tty = -1; + if (prefix && isatty(fdout)) { + tcgetattr(fdout, &tt); + ioctl(fdout, TIOCGWINSZ, &ws); + + /* If the below call fails due to not enough ptys then we don't + * prefix the output, but we still work */ + openpty(&master_tty, &slave_tty, NULL, &tt, &ws); + if (master_tty >= 0 && + (flags = fcntl(master_tty, F_GETFD, 0)) == 0) + fcntl(master_tty, F_SETFD, flags | FD_CLOEXEC); + + if (slave_tty >=0 && + (flags = fcntl(slave_tty, F_GETFD, 0)) == 0) + fcntl(slave_tty, F_SETFD, flags | FD_CLOEXEC); + } + + service_pid = fork(); + if (service_pid == -1) + eerrorx("%s: fork: %s", service, strerror(errno)); + if (service_pid == 0) { + if (slave_tty >= 0) { + dup2(slave_tty, STDOUT_FILENO); + dup2(slave_tty, STDERR_FILENO); + } + + if (exists(RC_SVCDIR "/openrc-run.sh")) { + if (arg2) + einfov("Executing: %s %s %s %s %s", + RC_SVCDIR "/openrc-run.sh", RC_SVCDIR "/openrc-run.sh", + service, arg1, arg2); + else + einfov("Executing: %s %s %s %s", + RC_SVCDIR "/openrc-run.sh", RC_SVCDIR "/openrc-run.sh", + service, arg1); + execl(RC_SVCDIR "/openrc-run.sh", + RC_SVCDIR "/openrc-run.sh", + service, arg1, arg2, (char *) NULL); + eerror("%s: exec `" RC_SVCDIR "/openrc-run.sh': %s", + service, strerror(errno)); + _exit(EXIT_FAILURE); + } else { + if (arg2) + einfov("Executing: %s %s %s %s %s", + RC_LIBEXECDIR "/sh/openrc-run.sh", + RC_LIBEXECDIR "/sh/openrc-run.sh", + service, arg1, arg2); + else + einfov("Executing: %s %s %s %s", + RC_LIBEXECDIR "/sh/openrc-run.sh", + RC_LIBEXECDIR "/sh/openrc-run.sh", + service, arg1); + execl(RC_LIBEXECDIR "/sh/openrc-run.sh", + RC_LIBEXECDIR "/sh/openrc-run.sh", + service, arg1, arg2, (char *) NULL); + eerror("%s: exec `" RC_LIBEXECDIR "/sh/openrc-run.sh': %s", + service, strerror(errno)); + _exit(EXIT_FAILURE); + } + } + + buffer = xmalloc(sizeof(char) * BUFSIZ); + fd[0].fd = signal_pipe[0]; + fd[0].events = fd[1].events = POLLIN; + fd[0].revents = fd[1].revents = 0; + if (master_tty >= 0) { + fd[1].fd = master_tty; + fd[1].events = POLLIN; + fd[1].revents = 0; + } + + for (;;) { + if ((s = poll(fd, master_tty >= 0 ? 2 : 1, -1)) == -1) { + if (errno != EINTR) { + eerror("%s: poll: %s", + service, strerror(errno)); + break; + } + } + + if (s > 0) { + if (fd[1].revents & (POLLIN | POLLHUP)) { + bytes = read(master_tty, buffer, BUFSIZ); + write_prefix(buffer, bytes, &prefixed); + } + + /* Only SIGCHLD signals come down this pipe */ + if (fd[0].revents & (POLLIN | POLLHUP)) + break; + } + } + + free(buffer); + + sigemptyset (&sigchldmask); + sigaddset (&sigchldmask, SIGCHLD); + sigprocmask (SIG_BLOCK, &sigchldmask, &oldmask); + + close(signal_pipe[0]); + close(signal_pipe[1]); + signal_pipe[0] = signal_pipe[1] = -1; + + sigprocmask (SIG_SETMASK, &oldmask, NULL); + + if (master_tty >= 0) { + /* Why did we do this? */ + /* signal (SIGWINCH, SIG_IGN); */ + close(master_tty); + master_tty = -1; + } + + ret = rc_waitpid(service_pid); + ret = WEXITSTATUS(ret); + if (ret != 0 && errno == ECHILD) + /* killall5 -9 could cause this */ + ret = 0; + service_pid = 0; + + return ret; +} + +static bool +svc_wait(const char *svc) +{ + char file[PATH_MAX]; + int fd; + bool forever = false; + RC_STRINGLIST *keywords; + struct timespec interval, timeout, warn; + + /* Some services don't have a timeout, like fsck */ + keywords = rc_deptree_depend(deptree, svc, "keyword"); + if (rc_stringlist_find(keywords, "-timeout") || + rc_stringlist_find(keywords, "notimeout")) + forever = true; + rc_stringlist_free(keywords); + + snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s", + basename_c(svc)); + + interval.tv_sec = 0; + interval.tv_nsec = WAIT_INTERVAL; + timeout.tv_sec = WAIT_TIMEOUT; + timeout.tv_nsec = 0; + warn.tv_sec = WARN_TIMEOUT; + warn.tv_nsec = 0; + for (;;) { + fd = open(file, O_RDONLY | O_NONBLOCK); + if (fd != -1) { + if (flock(fd, LOCK_SH | LOCK_NB) == 0) { + close(fd); + return true; + } + close(fd); + } + if (errno == ENOENT) + return true; + if (errno != EWOULDBLOCK) + eerrorx("%s: open `%s': %s", applet, file, + strerror(errno)); + if (nanosleep(&interval, NULL) == -1) { + if (errno != EINTR) + return false; + } + if (!forever) { + timespecsub(&timeout, &interval, &timeout); + if (timeout.tv_sec <= 0) + return false; + timespecsub(&warn, &interval, &warn); + if (warn.tv_sec <= 0) { + ewarn("%s: waiting for %s (%d seconds)", + applet, svc, (int)timeout.tv_sec); + warn.tv_sec = WARN_TIMEOUT; + warn.tv_nsec = 0; + } + } + } + return false; +} + +static void +get_started_services(void) +{ + RC_STRINGLIST *tmp = rc_services_in_state(RC_SERVICE_INACTIVE); + + rc_stringlist_free(restart_services); + restart_services = rc_services_in_state(RC_SERVICE_STARTED); + TAILQ_CONCAT(restart_services, tmp, entries); + free(tmp); +} + +static void +setup_deptypes(void) +{ + deptypes_b = rc_stringlist_new(); + rc_stringlist_add(deptypes_b, "broken"); + + deptypes_n = rc_stringlist_new(); + rc_stringlist_add(deptypes_n, "ineed"); + + deptypes_nw = rc_stringlist_new(); + rc_stringlist_add(deptypes_nw, "ineed"); + rc_stringlist_add(deptypes_nw, "iwant"); + + deptypes_nwu = rc_stringlist_new(); + rc_stringlist_add(deptypes_nwu, "ineed"); + rc_stringlist_add(deptypes_nwu, "iwant"); + rc_stringlist_add(deptypes_nwu, "iuse"); + + deptypes_nwua = rc_stringlist_new(); + rc_stringlist_add(deptypes_nwua, "ineed"); + rc_stringlist_add(deptypes_nwua, "iwant"); + rc_stringlist_add(deptypes_nwua, "iuse"); + rc_stringlist_add(deptypes_nwua, "iafter"); + + deptypes_m = rc_stringlist_new(); + rc_stringlist_add(deptypes_m, "needsme"); + + deptypes_mwua = rc_stringlist_new(); + rc_stringlist_add(deptypes_mwua, "needsme"); + rc_stringlist_add(deptypes_mwua, "wantsme"); + rc_stringlist_add(deptypes_mwua, "usesme"); + rc_stringlist_add(deptypes_mwua, "beforeme"); +} + +static void +svc_start_check(void) +{ + RC_SERVICE state; + + state = rc_service_state(service); + + if (in_background) { + if (!(state & (RC_SERVICE_INACTIVE | RC_SERVICE_STOPPED))) + exit(EXIT_FAILURE); + if (rc_yesno(getenv("IN_HOTPLUG"))) + rc_service_mark(service, RC_SERVICE_HOTPLUGGED); + if (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0) + ewarnx("WARNING: %s will be started in the" + " next runlevel", applet); + } + + if (exclusive_fd == -1) + exclusive_fd = svc_lock(applet); + if (exclusive_fd == -1) { + if (errno == EACCES) + eerrorx("%s: superuser access required", applet); + if (state & RC_SERVICE_STOPPING) + ewarnx("WARNING: %s is stopping", applet); + else + ewarnx("WARNING: %s is already starting", applet); + } + fcntl(exclusive_fd, F_SETFD, + fcntl(exclusive_fd, F_GETFD, 0) | FD_CLOEXEC); + + if (state & RC_SERVICE_STARTED) { + ewarn("WARNING: %s has already been started", applet); + exit(EXIT_SUCCESS); + } + else if (state & RC_SERVICE_INACTIVE && !in_background) + ewarnx("WARNING: %s has already started, but is inactive", + applet); + + rc_service_mark(service, RC_SERVICE_STARTING); + hook_out = RC_HOOK_SERVICE_START_OUT; + rc_plugin_run(RC_HOOK_SERVICE_START_IN, applet); +} + +static void +svc_start_deps(void) +{ + bool first; + RC_STRING *svc, *svc2; + RC_SERVICE state; + int depoptions = RC_DEP_TRACE, n; + size_t len; + char *p, *tmp; + pid_t pid; + + errno = 0; + if (rc_conf_yesno("rc_depend_strict") || errno == ENOENT) + depoptions |= RC_DEP_STRICT; + + if (!deptree && ((deptree = _rc_deptree_load(0, NULL)) == NULL)) + eerrorx("failed to load deptree"); + if (!deptypes_b) + setup_deptypes(); + + services = rc_deptree_depends(deptree, deptypes_b, applet_list, + runlevel, 0); + if (TAILQ_FIRST(services)) { + eerrorn("ERROR: %s needs service(s) ", applet); + first = true; + TAILQ_FOREACH(svc, services, entries) { + if (first) + first = false; + else + fprintf(stderr, ", "); + fprintf(stderr, "%s", svc->value); + } + fprintf(stderr, "\n"); + exit(EXIT_FAILURE); + } + rc_stringlist_free(services); + services = NULL; + + need_services = rc_deptree_depends(deptree, deptypes_n, + applet_list, runlevel, depoptions); + want_services = rc_deptree_depends(deptree, deptypes_nw, + applet_list, runlevel, depoptions); + use_services = rc_deptree_depends(deptree, deptypes_nwu, + applet_list, runlevel, depoptions); + + if (!rc_runlevel_starting()) { + TAILQ_FOREACH(svc, use_services, entries) { + state = rc_service_state(svc->value); + /* Don't stop failed services again. + * If you remove this check, ensure that the + * exclusive file isn't created. */ + if (state & RC_SERVICE_FAILED && + rc_runlevel_starting()) + continue; + if (state & RC_SERVICE_STOPPED) { + if (dry_run) { + printf(" %s", svc->value); + continue; + } + pid = service_start(svc->value); + if (!rc_conf_yesno("rc_parallel")) + rc_waitpid(pid); + } + } + } + + if (dry_run) + return; + + /* Now wait for them to start */ + services = rc_deptree_depends(deptree, deptypes_nwua, applet_list, + runlevel, depoptions); + /* We use tmplist to hold our scheduled by list */ + tmplist = rc_stringlist_new(); + TAILQ_FOREACH(svc, services, entries) { + state = rc_service_state(svc->value); + if (state & RC_SERVICE_STARTED) + continue; + + /* Don't wait for services which went inactive but are + * now in starting state which we are after */ + if (state & RC_SERVICE_STARTING && + state & RC_SERVICE_WASINACTIVE) + { + if (!rc_stringlist_find(need_services, svc->value) && + !rc_stringlist_find(want_services, svc->value) && + !rc_stringlist_find(use_services, svc->value)) + continue; + } + + if (!svc_wait(svc->value)) + eerror("%s: timed out waiting for %s", + applet, svc->value); + state = rc_service_state(svc->value); + if (state & RC_SERVICE_STARTED) + continue; + if (rc_stringlist_find(need_services, svc->value)) { + if (state & RC_SERVICE_INACTIVE || + state & RC_SERVICE_WASINACTIVE) + { + rc_stringlist_add(tmplist, svc->value); + } else if (!TAILQ_FIRST(tmplist)) + eerrorx("ERROR: cannot start %s as" + " %s would not start", + applet, svc->value); + } + } + + if (TAILQ_FIRST(tmplist)) { + /* Set the state now, then unlink our exclusive so that + our scheduled list is preserved */ + rc_service_mark(service, RC_SERVICE_STOPPED); + + rc_stringlist_free(use_services); + use_services = NULL; + len = 0; + n = 0; + TAILQ_FOREACH(svc, tmplist, entries) { + rc_service_schedule_start(svc->value, service); + use_services = rc_deptree_depend(deptree, + "iprovide", svc->value); + TAILQ_FOREACH(svc2, use_services, entries) + rc_service_schedule_start(svc2->value, service); + rc_stringlist_free(use_services); + use_services = NULL; + len += strlen(svc->value) + 2; + n++; + } + + len += 5; + tmp = p = xmalloc(sizeof(char) * len); + TAILQ_FOREACH(svc, tmplist, entries) { + if (p != tmp) + p += snprintf(p, len, ", "); + p += snprintf(p, len - (p - tmp), + "%s", svc->value); + } + rc_stringlist_free(tmplist); + tmplist = NULL; + ewarnx("WARNING: %s will start when %s has started", applet, tmp); + free(tmp); + } + + rc_stringlist_free(tmplist); + tmplist = NULL; + rc_stringlist_free(services); + services = NULL; +} + +static void svc_start_real() +{ + bool started; + RC_STRING *svc, *svc2; + + if (ibsave) + setenv("IN_BACKGROUND", ibsave, 1); + hook_out = RC_HOOK_SERVICE_START_DONE; + rc_plugin_run(RC_HOOK_SERVICE_START_NOW, applet); + started = (svc_exec("start", NULL) == 0); + if (ibsave) + unsetenv("IN_BACKGROUND"); + + if (rc_service_state(service) & RC_SERVICE_INACTIVE) + ewarnx("WARNING: %s has started, but is inactive", applet); + else if (!started) + eerrorx("ERROR: %s failed to start", applet); + + rc_service_mark(service, RC_SERVICE_STARTED); + exclusive_fd = svc_unlock(applet, exclusive_fd); + hook_out = RC_HOOK_SERVICE_START_OUT; + rc_plugin_run(RC_HOOK_SERVICE_START_DONE, applet); + + /* Now start any scheduled services */ + services = rc_services_scheduled(service); + TAILQ_FOREACH(svc, services, entries) + if (rc_service_state(svc->value) & RC_SERVICE_STOPPED) + service_start(svc->value); + rc_stringlist_free(services); + services = NULL; + + /* Do the same for any services we provide */ + if (deptree) { + tmplist = rc_deptree_depend(deptree, "iprovide", applet); + TAILQ_FOREACH(svc, tmplist, entries) { + services = rc_services_scheduled(svc->value); + TAILQ_FOREACH(svc2, services, entries) + if (rc_service_state(svc2->value) & + RC_SERVICE_STOPPED) + service_start(svc2->value); + rc_stringlist_free(services); + services = NULL; + } + rc_stringlist_free(tmplist); + tmplist = NULL; + } + + hook_out = 0; + rc_plugin_run(RC_HOOK_SERVICE_START_OUT, applet); +} + +static void +svc_start(void) +{ + if (dry_run) + einfon("start:"); + else + svc_start_check(); + if (deps) + svc_start_deps(); + if (dry_run) + printf(" %s\n", applet); + else + svc_start_real(); +} + +static int +svc_stop_check(RC_SERVICE *state) +{ + *state = rc_service_state(service); + + if (rc_runlevel_stopping() && *state & RC_SERVICE_FAILED) + exit(EXIT_FAILURE); + + if (in_background && + !(*state & RC_SERVICE_STARTED) && + !(*state & RC_SERVICE_INACTIVE)) + exit(EXIT_FAILURE); + + if (exclusive_fd == -1) + exclusive_fd = svc_lock(applet); + if (exclusive_fd == -1) { + if (errno == EACCES) + eerrorx("%s: superuser access required", applet); + if (*state & RC_SERVICE_STOPPING) + ewarnx("WARNING: %s is already stopping", applet); + eerrorx("ERROR: %s stopped by something else", applet); + } + fcntl(exclusive_fd, F_SETFD, + fcntl(exclusive_fd, F_GETFD, 0) | FD_CLOEXEC); + + if (*state & RC_SERVICE_STOPPED) { + ewarn("WARNING: %s is already stopped", applet); + return 1; + } + + rc_service_mark(service, RC_SERVICE_STOPPING); + hook_out = RC_HOOK_SERVICE_STOP_OUT; + rc_plugin_run(RC_HOOK_SERVICE_STOP_IN, applet); + + if (!rc_runlevel_stopping()) { + if (rc_service_in_runlevel(service, RC_LEVEL_SYSINIT)) + ewarn("WARNING: you are stopping a sysinit service"); + else if (rc_service_in_runlevel(service, RC_LEVEL_BOOT)) + ewarn("WARNING: you are stopping a boot service"); + } + + return 0; +} + +static void +svc_stop_deps(RC_SERVICE state) +{ + int depoptions = RC_DEP_TRACE; + RC_STRING *svc; + pid_t pid; + + if (state & RC_SERVICE_WASINACTIVE) + return; + + errno = 0; + if (rc_conf_yesno("rc_depend_strict") || errno == ENOENT) + depoptions |= RC_DEP_STRICT; + + if (!deptree && ((deptree = _rc_deptree_load(0, NULL)) == NULL)) + eerrorx("failed to load deptree"); + + if (!deptypes_m) + setup_deptypes(); + + services = rc_deptree_depends(deptree, deptypes_m, applet_list, + runlevel, depoptions); + tmplist = rc_stringlist_new(); + TAILQ_FOREACH_REVERSE(svc, services, rc_stringlist, entries) { + state = rc_service_state(svc->value); + /* Don't stop failed services again. + * If you remove this check, ensure that the + * exclusive file isn't created. */ + if (state & RC_SERVICE_FAILED && + rc_runlevel_stopping()) + continue; + if (state & RC_SERVICE_STARTED || + state & RC_SERVICE_INACTIVE) + { + if (dry_run) { + printf(" %s", svc->value); + continue; + } + svc_wait(svc->value); + state = rc_service_state(svc->value); + if (state & RC_SERVICE_STARTED || + state & RC_SERVICE_INACTIVE) + { + pid = service_stop(svc->value); + if (!rc_conf_yesno("rc_parallel")) + rc_waitpid(pid); + rc_stringlist_add(tmplist, svc->value); + } + } + } + rc_stringlist_free(services); + services = NULL; + if (dry_run) + return; + + TAILQ_FOREACH(svc, tmplist, entries) { + if (rc_service_state(svc->value) & RC_SERVICE_STOPPED) + continue; + svc_wait(svc->value); + if (rc_service_state(svc->value) & RC_SERVICE_STOPPED) + continue; + if (rc_runlevel_stopping()) { + /* If shutting down, we should stop even + * if a dependant failed */ + if (runlevel && + (strcmp(runlevel, + RC_LEVEL_SHUTDOWN) == 0 || + strcmp(runlevel, + RC_LEVEL_SINGLE) == 0)) + continue; + rc_service_mark(service, RC_SERVICE_FAILED); + } + eerrorx("ERROR: cannot stop %s as %s " + "is still up", applet, svc->value); + } + rc_stringlist_free(tmplist); + tmplist = NULL; + + /* We now wait for other services that may use us and are + * stopping. This is important when a runlevel stops */ + services = rc_deptree_depends(deptree, deptypes_mwua, applet_list, + runlevel, depoptions); + TAILQ_FOREACH(svc, services, entries) { + if (rc_service_state(svc->value) & RC_SERVICE_STOPPED) + continue; + svc_wait(svc->value); + } + rc_stringlist_free(services); + services = NULL; +} + +static void +svc_stop_real(void) +{ + bool stopped; + + /* If we're stopping localmount, set LC_ALL=C so that + * bash doesn't load anything blocking the unmounting of /usr */ + if (strcmp(applet, "localmount") == 0) + setenv("LC_ALL", "C", 1); + + if (ibsave) + setenv("IN_BACKGROUND", ibsave, 1); + hook_out = RC_HOOK_SERVICE_STOP_DONE; + rc_plugin_run(RC_HOOK_SERVICE_STOP_NOW, applet); + stopped = (svc_exec("stop", NULL) == 0); + if (ibsave) + unsetenv("IN_BACKGROUND"); + + if (!stopped) + eerrorx("ERROR: %s failed to stop", applet); + + if (in_background) + rc_service_mark(service, RC_SERVICE_INACTIVE); + else + rc_service_mark(service, RC_SERVICE_STOPPED); + + hook_out = RC_HOOK_SERVICE_STOP_OUT; + rc_plugin_run(RC_HOOK_SERVICE_STOP_DONE, applet); + hook_out = 0; + rc_plugin_run(RC_HOOK_SERVICE_STOP_OUT, applet); +} + +static int +svc_stop(void) +{ + RC_SERVICE state; + + state = 0; + if (dry_run) + einfon("stop:"); + else + if (svc_stop_check(&state) == 1) + return 1; /* Service has been stopped already */ + if (deps) + svc_stop_deps(state); + if (dry_run) + printf(" %s\n", applet); + else + svc_stop_real(); + + return 0; +} + +static void +svc_restart(void) +{ + /* This is hairly and a better way needs to be found I think! + * The issue is this - openvpn need net and dns. net can restart + * dns via resolvconf, so you could have openvpn trying to restart + * dnsmasq which in turn is waiting on net which in turn is waiting + * on dnsmasq. + * The work around is for resolvconf to restart its services with + * --nodeps which means just that. + * The downside is that there is a small window when our status is + * invalid. + * One workaround would be to introduce a new status, + * or status locking. */ + if (!deps) { + RC_SERVICE state = rc_service_state(service); + if (state & RC_SERVICE_STARTED || state & RC_SERVICE_INACTIVE) + svc_exec("stop", "start"); + else + svc_exec("start", NULL); + return; + } + + if (!(rc_service_state(service) & RC_SERVICE_STOPPED)) { + get_started_services(); + svc_stop(); + if (dry_run) + ewarn("Cannot calculate restart start dependencies" + " on a dry-run"); + } + + svc_start(); + start_services(restart_services); + rc_stringlist_free(restart_services); + restart_services = NULL; +} + +static bool +service_plugable(void) +{ + char *list, *p, *token; + bool allow = true, truefalse; + char *match = rc_conf_value("rc_hotplug"); + + if (!match) + match = rc_conf_value("rc_plug_services"); + if (!match) + return false; + + list = xstrdup(match); + p = list; + while ((token = strsep(&p, " "))) { + if (token[0] == '!') { + truefalse = false; + token++; + } else + truefalse = true; + + if (fnmatch(token, applet, 0) == 0) { + allow = truefalse; + break; + } + } + free(list); + return allow; +} + +int main(int argc, char **argv) +{ + bool doneone = false; + bool runscript = false; + int retval, opt, depoptions = RC_DEP_TRACE; + RC_STRING *svc; + char path[PATH_MAX], lnk[PATH_MAX]; + char *dir, *save = NULL, *saveLnk = NULL; + char pidstr[10]; + size_t l = 0, ll; + const char *file; + struct stat stbuf; + + /* Show help if insufficient args */ + if (argc < 2 || !exists(argv[1])) { + fprintf(stderr, "openrc-run should not be run directly\n"); + exit(EXIT_FAILURE); + } + + applet = basename_c(argv[0]); + if (strcmp(applet, "runscript") == 0) + runscript = true; + + if (stat(argv[1], &stbuf) != 0) { + fprintf(stderr, "openrc-run `%s': %s\n", + argv[1], strerror(errno)); + exit(EXIT_FAILURE); + } + + atexit(cleanup); + + /* We need to work out the real full path to our service. + * 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)) { + fprintf(stderr, "realpath: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + memset(lnk, 0, sizeof(lnk)); + if (readlink(argv[1], lnk, sizeof(lnk)-1)) { + dir = dirname(path); + if (strchr(lnk, '/')) { + save = xstrdup(dir); + saveLnk = xstrdup(lnk); + dir = dirname(saveLnk); + if (strcmp(dir, save) == 0) + file = basename_c(argv[1]); + else + file = basename_c(lnk); + dir = save; + } else + file = basename_c(argv[1]); + ll = strlen(dir) + strlen(file) + 2; + service = xmalloc(ll); + snprintf(service, ll, "%s/%s", dir, file); + if (stat(service, &stbuf) != 0) { + free(service); + service = xstrdup(lnk); + } + free(save); + free(saveLnk); + } + if (!service) + service = xstrdup(path); + applet = basename_c(service); + + if (argc < 3) + usage(EXIT_FAILURE); + + /* Change dir to / to ensure all init scripts don't use stuff in pwd */ + if (chdir("/") == -1) + eerror("chdir: %s", strerror(errno)); + + if ((runlevel = xstrdup(getenv("RC_RUNLEVEL"))) == NULL) { + env_filter(); + env_config(); + runlevel = rc_runlevel_get(); + } + + setenv("EINFO_LOG", service, 1); + setenv("RC_SVCNAME", applet, 1); + + /* 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()); + setenv("RC_OPENRC_PID", pidstr, 1); + /* + * RC_RUNSCRIPT_PID is deprecated, but we will keep it for a while + * for safety. + */ + setenv("RC_RUNSCRIPT_PID", pidstr, 1); + + /* eprefix is kinda klunky, but it works for our purposes */ + if (rc_conf_yesno("rc_parallel")) { + /* Get the longest service name */ + services = rc_services_in_runlevel(NULL); + TAILQ_FOREACH(svc, services, entries) { + ll = strlen(svc->value); + if (ll > l) + l = ll; + } + rc_stringlist_free(services); + services = NULL; + ll = strlen(applet); + if (ll > l) + l = ll; + + /* Make our prefix string */ + prefix = xmalloc(sizeof(char) * l + 1); + ll = strlen(applet); + memcpy(prefix, applet, ll); + memset(prefix + ll, ' ', l - ll); + memset(prefix + l, 0, 1); + eprefix(prefix); + } + + /* Ok, we are ready to go, so setup selinux if applicable */ + selinux_setup(argv); + + deps = true; + + /* Punt the first arg as its our service name */ + argc--; + argv++; + + /* Right then, parse any options there may be */ + while ((opt = getopt_long(argc, argv, getoptstring, + longopts, (int *)0)) != -1) + switch (opt) { + case 'd': + setenv("RC_DEBUG", "YES", 1); + break; + case 'l': + exclusive_fd = atoi(optarg); + fcntl(exclusive_fd, F_SETFD, + fcntl(exclusive_fd, F_GETFD, 0) | FD_CLOEXEC); + break; + case 's': + if (!(rc_service_state(service) & RC_SERVICE_STARTED)) + exit(EXIT_FAILURE); + break; + case 'S': + if (!(rc_service_state(service) & RC_SERVICE_STOPPED)) + exit(EXIT_FAILURE); + break; + case 'D': + deps = false; + break; + case 'Z': + dry_run = true; + break; + case_RC_COMMON_GETOPT + } + + /* If we're changing runlevels and not called by rc then we cannot + work with any dependencies */ + if (deps && getenv("RC_PID") == NULL && + (rc_runlevel_starting() || rc_runlevel_stopping())) + deps = false; + + /* Save the IN_BACKGROUND env flag so it's ONLY passed to the service + that is being called and not any dependents */ + if (getenv("IN_BACKGROUND")) { + ibsave = xstrdup(getenv("IN_BACKGROUND")); + in_background = rc_yesno(ibsave); + unsetenv("IN_BACKGROUND"); + } + + if (rc_yesno(getenv("IN_HOTPLUG"))) { + if (!service_plugable()) + eerrorx("%s: not allowed to be hotplugged", applet); + in_background = true; + } + + /* Setup a signal handler */ + signal_setup(SIGHUP, handle_signal); + signal_setup(SIGINT, handle_signal); + signal_setup(SIGQUIT, handle_signal); + signal_setup(SIGTERM, handle_signal); + signal_setup(SIGCHLD, handle_signal); + + /* Load our plugins */ + rc_plugin_load(); + + applet_list = rc_stringlist_new(); + rc_stringlist_add(applet_list, applet); + + if (runscript) + ewarn("%s uses runscript, please convert to openrc-run.", service); + + /* Now run each option */ + retval = EXIT_SUCCESS; + while (optind < argc) { + optarg = argv[optind++]; + + /* Abort on a sighup here */ + if (sighup) + exit (EXIT_FAILURE); + + /* Export the command we're running. + This is important as we stamp on the restart function now but + some start/stop routines still need to behave differently if + restarting. */ + unsetenv("RC_CMD"); + setenv("RC_CMD", optarg, 1); + + doneone = true; + + if (strcmp(optarg, "describe") == 0 || + strcmp(optarg, "help") == 0 || + strcmp(optarg, "depend") == 0) + { + save = prefix; + eprefix(NULL); + prefix = NULL; + svc_exec(optarg, NULL); + eprefix(save); + prefix = save; + } else if (strcmp(optarg, "ineed") == 0 || + strcmp(optarg, "iuse") == 0 || + strcmp(optarg, "iwant") == 0 || + strcmp(optarg, "needsme") == 0 || + strcmp(optarg, "usesme") == 0 || + strcmp(optarg, "wantsme") == 0 || + strcmp(optarg, "iafter") == 0 || + strcmp(optarg, "ibefore") == 0 || + strcmp(optarg, "iprovide") == 0) + { + errno = 0; + if (rc_conf_yesno("rc_depend_strict") || + errno == ENOENT) + depoptions |= RC_DEP_STRICT; + + if (!deptree && + ((deptree = _rc_deptree_load(0, NULL)) == NULL)) + eerrorx("failed to load deptree"); + + tmplist = rc_stringlist_new(); + rc_stringlist_add(tmplist, optarg); + services = rc_deptree_depends(deptree, tmplist, + applet_list, + runlevel, depoptions); + rc_stringlist_free(tmplist); + tmplist = NULL; + TAILQ_FOREACH(svc, services, entries) + printf("%s ", svc->value); + printf ("\n"); + rc_stringlist_free(services); + services = NULL; + } else if (strcmp (optarg, "status") == 0) { + save = prefix; + eprefix(NULL); + prefix = NULL; + retval = svc_exec("status", NULL); + } else { + if (strcmp(optarg, "conditionalrestart") == 0 || + strcmp(optarg, "condrestart") == 0) + { + if (rc_service_state(service) & + RC_SERVICE_STARTED) + svc_restart(); + } else if (strcmp(optarg, "restart") == 0) { + svc_restart(); + } else if (strcmp(optarg, "start") == 0) { + svc_start(); + } else if (strcmp(optarg, "stop") == 0 || strcmp(optarg, "pause") == 0) { + if (strcmp(optarg, "pause") == 0) { + ewarn("WARNING: 'pause' is deprecated; please use '--nodeps stop'"); + deps = false; + } + if (deps && in_background) + get_started_services(); + if (svc_stop() == 1) + continue; /* Service has been stopped already */ + if (deps) { + if (!in_background && + !rc_runlevel_stopping() && + rc_service_state(service) & + RC_SERVICE_STOPPED) + unhotplug(); + + if (in_background && + rc_service_state(service) & + RC_SERVICE_INACTIVE) + { + TAILQ_FOREACH(svc, + restart_services, + entries) + if (rc_service_state(svc->value) & + RC_SERVICE_STOPPED) + rc_service_schedule_start(service, svc->value); + } + } + } else if (strcmp(optarg, "zap") == 0) { + einfo("Manually resetting %s to stopped state", + applet); + if (!rc_service_mark(applet, + RC_SERVICE_STOPPED)) + eerrorx("rc_service_mark: %s", + strerror(errno)); + unhotplug(); + } else + retval = svc_exec(optarg, NULL); + + /* We should ensure this list is empty after + * an action is done */ + rc_stringlist_free(restart_services); + restart_services = NULL; + } + + if (!doneone) + usage(EXIT_FAILURE); + } + + return retval; +} diff --git a/src/rc/openrc-shutdown.c b/src/rc/openrc-shutdown.c new file mode 100644 index 0000000..b17a63d --- /dev/null +++ b/src/rc/openrc-shutdown.c @@ -0,0 +1,170 @@ +/* + * openrc-shutdown.c + * If you are using OpenRC's provided init, this will shut down or + * reboot your system. + * + * This is based on code written by James Hammons , so + * I would like to publically thank him for his work. + */ + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 = "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'}, + { "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, --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; + } + + ignored = fwrite(cmd, 1, strlen(cmd), fifo); + if (ignored != strlen(cmd)) + printf("Error writing to init fifo\n"); + fclose(fifo); +} + +int main(int argc, char **argv) +{ + int opt; + int cmd_count = 0; + + applet = basename_c(argv[0]); + 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': + do_kexec = true; + cmd_count++; + break; + case 'p': + do_poweroff = true; + cmd_count++; + break; + case 'R': + do_reexec = true; + cmd_count++; + break; + case 'r': + 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); + } + if (do_halt) + send_cmd("halt"); + else if (do_kexec) + send_cmd("kexec"); + else if (do_poweroff) + send_cmd("poweroff"); + else if (do_reboot) + 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; +} diff --git a/src/rc/rc-abort.c b/src/rc/rc-abort.c new file mode 100644 index 0000000..c81abe7 --- /dev/null +++ b/src/rc/rc-abort.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2007-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. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" + +int main(void) +{ + const char *p = getenv("RC_PID"); + int pid; + + if (p && sscanf(p, "%d", &pid) == 1) { + if (kill(pid, SIGUSR1) != 0) + eerrorx("rc-abort: failed to signal parent %d: %s", + pid, strerror(errno)); + return EXIT_SUCCESS; + } + + return EXIT_FAILURE; +} diff --git a/src/rc/rc-depend.c b/src/rc/rc-depend.c new file mode 100644 index 0000000..51c6a5e --- /dev/null +++ b/src/rc/rc-depend.c @@ -0,0 +1,177 @@ +/* + * rc-depend + * rc service dependency and ordering + */ + +/* + * Copyright (c) 2007-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. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-misc.h" +#include "_usage.h" + +const char *applet = NULL; +const char *extraopts = NULL; +const char *getoptstring = "aot:suTF:" getoptstring_COMMON; +const struct option longopts[] = { + { "starting", 0, NULL, 'a'}, + { "stopping", 0, NULL, 'o'}, + { "type", 1, NULL, 't'}, + { "notrace", 0, NULL, 'T'}, + { "strict", 0, NULL, 's'}, + { "update", 0, NULL, 'u'}, + { "deptree-file", 1, NULL, 'F'}, + longopts_COMMON +}; +const char * const longopts_help[] = { + "Order services as if runlevel is starting", + "Order services as if runlevel is stopping", + "Type(s) of dependency to list", + "Don't trace service dependencies", + "Only use what is in the runlevels", + "Force an update of the dependency tree", + "File to load cached deptree from", + longopts_help_COMMON +}; +const char *usagestring = NULL; + +int main(int argc, char **argv) +{ + RC_STRINGLIST *list; + RC_STRINGLIST *types; + RC_STRINGLIST *services; + RC_STRINGLIST *depends; + RC_STRING *s; + RC_DEPTREE *deptree = NULL; + int options = RC_DEP_TRACE, update = 0; + bool first = true; + char *runlevel = xstrdup(getenv("RC_RUNLEVEL")); + int opt; + char *token; + char *deptree_file = NULL; + + applet = basename_c(argv[0]); + types = rc_stringlist_new(); + while ((opt = getopt_long(argc, argv, getoptstring, + longopts, (int *) 0)) != -1) + { + switch (opt) { + case 'a': + options |= RC_DEP_START; + break; + case 'o': + options |= RC_DEP_STOP; + break; + case 's': + options |= RC_DEP_STRICT; + break; + case 't': + while ((token = strsep(&optarg, ","))) + rc_stringlist_add(types, token); + break; + case 'u': + update = 1; + break; + case 'T': + options &= RC_DEP_TRACE; + break; + case 'F': + deptree_file = xstrdup(optarg); + break; + + case_RC_COMMON_GETOPT + } + } + + if (deptree_file) { + if (!(deptree = rc_deptree_load_file(deptree_file))) + eerrorx("failed to load deptree"); + } else { + if (!(deptree = _rc_deptree_load(update, NULL))) + eerrorx("failed to load deptree"); + } + + if (!runlevel) + runlevel = rc_runlevel_get(); + + services = rc_stringlist_new(); + while (optind < argc) { + list = rc_stringlist_new(); + rc_stringlist_add(list, argv[optind]); + errno = 0; + depends = rc_deptree_depends(deptree, NULL, list, runlevel, 0); + if (!depends && errno == ENOENT) + eerror("no dependency info for service `%s'", + argv[optind]); + else + rc_stringlist_add(services, argv[optind]); + + rc_stringlist_free(depends); + rc_stringlist_free(list); + optind++; + } + if (!TAILQ_FIRST(services)) { + rc_stringlist_free(services); + rc_stringlist_free(types); + rc_deptree_free(deptree); + free(runlevel); + if (update) + return EXIT_SUCCESS; + eerrorx("no services specified"); + } + + /* If we don't have any types, then supply some defaults */ + if (!TAILQ_FIRST(types)) { + rc_stringlist_add(types, "ineed"); + rc_stringlist_add(types, "iuse"); + } + + depends = rc_deptree_depends(deptree, types, services, + runlevel, options); + + if (TAILQ_FIRST(depends)) { + TAILQ_FOREACH(s, depends, entries) { + if (first) + first = false; + else + printf (" "); + printf ("%s", s->value); + + } + printf ("\n"); + } + + rc_stringlist_free(types); + rc_stringlist_free(services); + rc_stringlist_free(depends); + rc_deptree_free(deptree); + free(runlevel); + return EXIT_SUCCESS; +} diff --git a/src/rc/rc-logger.c b/src/rc/rc-logger.c new file mode 100644 index 0000000..062ce3d --- /dev/null +++ b/src/rc/rc-logger.c @@ -0,0 +1,312 @@ +/* + * rc-logger.c + * Spawns a logging daemon to capture stdout and stderr so we can log + * them to a buffer and/or files. + */ + +/* + * Copyright (c) 2007-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. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) \ + || defined(__GNU__) +# include +#elif defined(__NetBSD__) || defined(__OpenBSD__) +# include +#else +# include +#endif + +#include "einfo.h" +#include "rc-logger.h" +#include "queue.h" +#include "rc.h" +#include "rc-misc.h" + +#define TMPLOG RC_SVCDIR "/rc.log" +#define DEFAULTLOG "/var/log/rc.log" + +static int signal_pipe[2] = { -1, -1 }; +static int fd_stdout = -1; +static int fd_stderr = -1; +static const char *runlevel = NULL; +static bool in_escape = false; +static bool in_term = false; + +static char *logbuf = NULL; +static size_t logbuf_size = 0; +static size_t logbuf_len = 0; + +pid_t rc_logger_pid = -1; +int rc_logger_tty = -1; +bool rc_in_logger = false; + +static void +write_log(int logfd, const char *buffer, size_t bytes) +{ + const char *p = buffer; + + while ((size_t)(p - buffer) < bytes) { + switch (*p) { + case '\r': + goto cont; + case '\033': + in_escape = true; + in_term = false; + goto cont; + case '\n': + in_escape = in_term = false; + break; + case '[': + if (in_escape) + in_term = true; + break; + } + + if (!in_escape) { + if (write(logfd, p++, 1) == -1) + eerror("write: %s", strerror(errno)); + continue; + } + + if (! in_term || isalpha((unsigned char)*p)) + in_escape = in_term = false; +cont: + p++; + } +} + +static void +write_time(FILE *f, const char *s) +{ + time_t now = time(NULL); + struct tm *tm = localtime(&now); + + fprintf(f, "\nrc %s logging %s at %s\n", runlevel, s, asctime(tm)); + fflush(f); +} + +void +rc_logger_close(void) +{ + int sig = SIGTERM; + + if (signal_pipe[1] > -1) { + if (write(signal_pipe[1], &sig, sizeof(sig)) == -1) + eerror("write: %s", strerror(errno)); + close(signal_pipe[1]); + signal_pipe[1] = -1; + } + + if (rc_logger_pid > 0) + waitpid(rc_logger_pid, 0, 0); + + if (fd_stdout > -1) + dup2(fd_stdout, STDOUT_FILENO); + if (fd_stderr > -1) + dup2(fd_stderr, STDERR_FILENO); +} + +void +rc_logger_open(const char *level) +{ + int slave_tty; + struct termios tt; + struct winsize ws; + char buffer[BUFSIZ]; + struct pollfd fd[2]; + int s = 0; + size_t bytes; + int i; + FILE *log = NULL; + FILE *plog = NULL; + const char *logfile; + int log_error = 0; + + if (!rc_conf_yesno("rc_logger")) + return; + + if (pipe(signal_pipe) == -1) + eerrorx("pipe: %s", strerror(errno)); + for (i = 0; i < 2; i++) + if ((s = fcntl (signal_pipe[i], F_GETFD, 0) == -1 || + fcntl (signal_pipe[i], F_SETFD, s | FD_CLOEXEC) == -1)) + eerrorx("fcntl: %s", strerror (errno)); + + if (isatty(STDOUT_FILENO)) { + tcgetattr(STDOUT_FILENO, &tt); + ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); + if (openpty(&rc_logger_tty, &slave_tty, NULL, &tt, &ws)) + return; + } else + if (openpty(&rc_logger_tty, &slave_tty, NULL, NULL, NULL)) + return; + + if ((s = fcntl(rc_logger_tty, F_GETFD, 0)) == 0) + fcntl(rc_logger_tty, F_SETFD, s | FD_CLOEXEC); + + if ((s = fcntl(slave_tty, F_GETFD, 0)) == 0) + fcntl(slave_tty, F_SETFD, s | FD_CLOEXEC); + + rc_logger_pid = fork(); + switch (rc_logger_pid) { + case -1: + eerror("fork: %s", strerror(errno)); + break; + case 0: + rc_in_logger = true; + close(signal_pipe[1]); + signal_pipe[1] = -1; + + runlevel = level; + if ((log = fopen(TMPLOG, "ae"))) + write_time(log, "started"); + else { + free(logbuf); + logbuf_size = BUFSIZ * 10; + logbuf = xmalloc(sizeof (char) * logbuf_size); + logbuf_len = 0; + } + + fd[0].fd = signal_pipe[0]; + fd[0].events = fd[1].events = POLLIN; + fd[0].revents = fd[1].revents = 0; + if (rc_logger_tty >= 0) + fd[1].fd = rc_logger_tty; + for (;;) { + if ((s = poll(fd, + rc_logger_tty >= 0 ? 2 : 1, -1)) == -1) + { + eerror("poll: %s", strerror(errno)); + break; + } else if (s == 0) + continue; + + if (fd[1].revents & (POLLIN | POLLHUP)) { + memset(buffer, 0, BUFSIZ); + bytes = read(rc_logger_tty, buffer, BUFSIZ); + if (write(STDOUT_FILENO, buffer, bytes) == -1) + eerror("write: %s", strerror(errno)); + + if (log) + write_log(fileno (log), buffer, bytes); + else { + if (logbuf_size - logbuf_len < bytes) { + logbuf_size += BUFSIZ * 10; + logbuf = xrealloc(logbuf, + sizeof(char ) * + logbuf_size); + } + + memcpy(logbuf + logbuf_len, + buffer, bytes); + logbuf_len += bytes; + } + } + + /* Only SIGTERMS signals come down this pipe */ + if (fd[0].revents & (POLLIN | POLLHUP)) + break; + } + if (logbuf) { + if ((log = fopen(TMPLOG, "ae"))) { + write_time(log, "started"); + write_log(fileno(log), logbuf, logbuf_len); + } + free(logbuf); + } + if (log) { + write_time(log, "stopped"); + fclose(log); + } + + /* Append the temporary log to the real log */ + logfile = rc_conf_value("rc_log_path"); + if (logfile == NULL) + logfile = DEFAULTLOG; + if (!strcmp(logfile, TMPLOG)) { + eerror("Cowardly refusing to concatenate a logfile into itself."); + eerrorx("Please change rc_log_path to something other than %s to get rid of this message", TMPLOG); + } + + if ((plog = fopen(logfile, "ae"))) { + if ((log = fopen(TMPLOG, "re"))) { + while ((bytes = fread(buffer, sizeof(*buffer), BUFSIZ, log)) > 0) { + if (fwrite(buffer, sizeof(*buffer), bytes, plog) < bytes) { + log_error = 1; + eerror("Error: write(%s) failed: %s", logfile, strerror(errno)); + break; + } + } + fclose(log); + } else { + log_error = 1; + eerror("Error: fopen(%s) failed: %s", TMPLOG, strerror(errno)); + } + + fclose(plog); + } else { + /* + * logfile or its basedir may be read-only during sysinit and + * shutdown so skip the error in this case + */ + if (errno != EROFS && ((strcmp(level, RC_LEVEL_SHUTDOWN) != 0) && (strcmp(level, RC_LEVEL_SYSINIT) != 0))) { + log_error = 1; + eerror("Error: fopen(%s) failed: %s", logfile, strerror(errno)); + } + } + + /* Try to keep the temporary log in case of errors */ + if (!log_error) { + if (errno != EROFS && ((strcmp(level, RC_LEVEL_SHUTDOWN) != 0) && (strcmp(level, RC_LEVEL_SYSINIT) != 0))) + if (unlink(TMPLOG) == -1) + eerror("Error: unlink(%s) failed: %s", TMPLOG, strerror(errno)); + } else if (exists(TMPLOG)) + eerrorx("Warning: temporary logfile left behind: %s", TMPLOG); + + exit(0); + /* NOTREACHED */ + + default: + setpgid(rc_logger_pid, 0); + fd_stdout = dup(STDOUT_FILENO); + fd_stderr = dup(STDERR_FILENO); + if ((s = fcntl(fd_stdout, F_GETFD, 0)) == 0) + fcntl(fd_stdout, F_SETFD, s | FD_CLOEXEC); + + if ((s = fcntl(fd_stderr, F_GETFD, 0)) == 0) + fcntl(fd_stderr, F_SETFD, s | FD_CLOEXEC); + dup2(slave_tty, STDOUT_FILENO); + dup2(slave_tty, STDERR_FILENO); + if (slave_tty != STDIN_FILENO && + slave_tty != STDOUT_FILENO && + slave_tty != STDERR_FILENO) + close(slave_tty); + close(signal_pipe[0]); + signal_pipe[0] = -1; + break; + } +} diff --git a/src/rc/rc-logger.h b/src/rc/rc-logger.h new file mode 100644 index 0000000..bf6e3e5 --- /dev/null +++ b/src/rc/rc-logger.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2007-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. + */ + +#ifndef RC_LOGGER_H +#define RC_LOGGER_H + +pid_t rc_logger_pid; +int rc_logger_tty; +extern bool rc_in_logger; + +void rc_logger_open(const char *runlevel); +void rc_logger_close(void); + +#endif diff --git a/src/rc/rc-misc.c b/src/rc/rc-misc.c new file mode 100644 index 0000000..a6cc788 --- /dev/null +++ b/src/rc/rc-misc.c @@ -0,0 +1,476 @@ +/* + * rc-misc.c + * rc misc functions + */ + +/* + * Copyright (c) 2007-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. + */ + +#include +#include +#include + +#ifdef __linux__ +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-misc.h" +#include "version.h" + +extern char **environ; + +bool +rc_conf_yesno(const char *setting) +{ + return rc_yesno(rc_conf_value (setting)); +} + +static const char *const env_whitelist[] = { + "EERROR_QUIET", "EINFO_QUIET", + "IN_BACKGROUND", "IN_HOTPLUG", + "LANG", "LC_MESSAGES", "TERM", + "EINFO_COLOR", "EINFO_VERBOSE", + NULL +}; + +void +env_filter(void) +{ + RC_STRINGLIST *env_allow; + RC_STRINGLIST *profile; + RC_STRINGLIST *env_list; + RC_STRING *env; + char *e; + size_t i = 0; + + /* Add the user defined list of vars */ + env_allow = rc_stringlist_split(rc_conf_value("rc_env_allow"), " "); + /* + * If '*' is an entry in rc_env_allow, do nothing as we are to pass + * through all environment variables. + */ + if (rc_stringlist_find(env_allow, "*")) + return; + profile = rc_config_load(RC_PROFILE_ENV); + + /* Copy the env and work from this so we can manipulate it safely */ + env_list = rc_stringlist_new(); + while (environ && environ[i]) { + env = rc_stringlist_add(env_list, environ[i++]); + e = strchr(env->value, '='); + if (e) + *e = '\0'; + } + + TAILQ_FOREACH(env, env_list, entries) { + /* Check the whitelist */ + for (i = 0; env_whitelist[i]; i++) { + if (strcmp(env_whitelist[i], env->value) == 0) + break; + } + if (env_whitelist[i]) + continue; + + /* Check our user defined list */ + if (rc_stringlist_find(env_allow, env->value)) + continue; + + /* OK, not allowed! */ + unsetenv(env->value); + } + + /* Now add anything missing from the profile */ + TAILQ_FOREACH(env, profile, entries) { + e = strchr(env->value, '='); + *e = '\0'; + if (!getenv(env->value)) + setenv(env->value, e + 1, 1); + } + + rc_stringlist_free(env_list); + rc_stringlist_free(env_allow); + rc_stringlist_free(profile); +} + +void +env_config(void) +{ + size_t pplen = strlen(RC_PATH_PREFIX); + char *path; + char *p; + char *e; + size_t l; + struct utsname uts; + FILE *fp; + char *token; + char *np; + char *npp; + char *tok; + const char *sys = rc_sys(); + char buffer[PATH_MAX]; + + /* Ensure our PATH is prefixed with the system locations first + for a little extra security */ + path = getenv("PATH"); + if (! path) + setenv("PATH", RC_PATH_PREFIX, 1); + else if (strncmp (RC_PATH_PREFIX, path, pplen) != 0) { + l = strlen(path) + pplen + 3; + e = p = xmalloc(sizeof(char) * l); + p += snprintf(p, l, "%s", RC_PATH_PREFIX); + + /* Now go through the env var and only add bits not in our + * PREFIX */ + while ((token = strsep(&path, ":"))) { + np = npp = xstrdup(RC_PATH_PREFIX); + while ((tok = strsep(&npp, ":"))) + if (strcmp(tok, token) == 0) + break; + if (! tok) + p += snprintf(p, l - (p - e), ":%s", token); + free (np); + } + *p++ = '\0'; + unsetenv("PATH"); + setenv("PATH", e, 1); + free(e); + } + + setenv("RC_VERSION", VERSION, 1); + setenv("RC_LIBEXECDIR", RC_LIBEXECDIR, 1); + setenv("RC_SVCDIR", RC_SVCDIR, 1); + setenv("RC_TMPDIR", RC_SVCDIR "/tmp", 1); + setenv("RC_BOOTLEVEL", RC_LEVEL_BOOT, 1); + e = rc_runlevel_get(); + setenv("RC_RUNLEVEL", e, 1); + free(e); + + if ((fp = fopen(RC_KRUNLEVEL, "r"))) { + memset(buffer, 0, sizeof (buffer)); + if (fgets(buffer, sizeof (buffer), fp)) { + l = strlen (buffer) - 1; + if (buffer[l] == '\n') + buffer[l] = 0; + setenv("RC_DEFAULTLEVEL", buffer, 1); + } + fclose(fp); + } else + setenv("RC_DEFAULTLEVEL", RC_LEVEL_DEFAULT, 1); + + if (sys) + setenv("RC_SYS", sys, 1); + +#ifdef PREFIX + setenv("RC_PREFIX", RC_PREFIX, 1); +#endif + + /* Some scripts may need to take a different code path if + Linux/FreeBSD, etc + To save on calling uname, we store it in an environment variable */ + if (uname(&uts) == 0) + setenv("RC_UNAME", uts.sysname, 1); + + /* Be quiet or verbose as necessary */ + if (rc_conf_yesno("rc_quiet")) + setenv("EINFO_QUIET", "YES", 1); + if (rc_conf_yesno("rc_verbose")) + setenv("EINFO_VERBOSE", "YES", 1); + + errno = 0; + if ((! rc_conf_yesno("rc_color") && errno == 0) || + rc_conf_yesno("rc_nocolor")) + setenv("EINFO_COLOR", "NO", 1); +} + +int +signal_setup(int sig, void (*handler)(int)) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof (sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = handler; + return sigaction(sig, &sa, NULL); +} + +int +svc_lock(const char *applet) +{ + char file[PATH_MAX]; + int fd; + + snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s", applet); + fd = open(file, O_WRONLY | O_CREAT | O_NONBLOCK, 0664); + if (fd == -1) + return -1; + if (flock(fd, LOCK_EX | LOCK_NB) == -1) { + eerror("Call to flock failed: %s", strerror(errno)); + close(fd); + return -1; + } + return fd; +} + +int +svc_unlock(const char *applet, int fd) +{ + char file[PATH_MAX]; + + snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s", applet); + close(fd); + unlink(file); + return -1; +} + +pid_t +exec_service(const char *service, const char *arg) +{ + char *file, sfd[32]; + int fd; + pid_t pid = -1; + sigset_t full; + sigset_t old; + struct sigaction sa; + + fd = svc_lock(basename_c(service)); + if (fd == -1) + return -1; + + file = rc_service_resolve(service); + if (!exists(file)) { + rc_service_mark(service, RC_SERVICE_STOPPED); + svc_unlock(basename_c(service), fd); + free(file); + return 0; + } + snprintf(sfd, sizeof(sfd), "%d", fd); + + /* We need to block signals until we have forked */ + memset(&sa, 0, sizeof (sa)); + sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + sigfillset(&full); + sigprocmask(SIG_SETMASK, &full, &old); + + if ((pid = fork()) == 0) { + /* Restore default handlers */ + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGWINCH, &sa, NULL); + + /* Unmask signals */ + sigprocmask(SIG_SETMASK, &old, NULL); + + /* Safe to run now */ + execl(file, file, "--lockfd", sfd, arg, (char *) NULL); + fprintf(stderr, "unable to exec `%s': %s\n", + file, strerror(errno)); + svc_unlock(basename_c(service), fd); + _exit(EXIT_FAILURE); + } + + if (pid == -1) { + fprintf(stderr, "fork: %s\n",strerror (errno)); + svc_unlock(basename_c(service), fd); + } else + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); + + sigprocmask(SIG_SETMASK, &old, NULL); + free(file); + return pid; +} + +int +parse_mode(mode_t *mode, char *text) +{ + char *p; + unsigned long l; + + /* Check for a numeric mode */ + if ((*text - '0') < 8) { + l = strtoul(text, &p, 8); + if (*p || l > 07777U) { + errno = EINVAL; + return -1; + } + *mode = (mode_t) l; + return 0; + } + + /* We currently don't check g+w type stuff */ + errno = EINVAL; + return -1; +} + +int +is_writable(const char *path) +{ + if (access(path, W_OK) == 0) + return 1; + + return 0; +} + +RC_DEPTREE * _rc_deptree_load(int force, int *regen) +{ + int fd; + int retval; + int serrno = errno; + int merrno; + time_t t; + char file[PATH_MAX]; + struct stat st; + struct utimbuf ut; + FILE *fp; + + t = 0; + if (rc_deptree_update_needed(&t, file) || force != 0) { + /* Test if we have permission to update the deptree */ + fd = open(RC_DEPTREE_CACHE, O_WRONLY); + merrno = errno; + errno = serrno; + if (fd == -1 && merrno == EACCES) + return rc_deptree_load(); + close(fd); + + if (regen) + *regen = 1; + ebegin("Caching service dependencies"); + retval = rc_deptree_update() ? 0 : -1; + eend (retval, "Failed to update the dependency tree"); + + if (retval == 0) { + stat(RC_DEPTREE_CACHE, &st); + if (st.st_mtime < t) { + eerror("Clock skew detected with `%s'", file); + eerrorn("Adjusting mtime of `" RC_DEPTREE_CACHE + "' to %s", ctime(&t)); + fp = fopen(RC_DEPTREE_SKEWED, "w"); + if (fp != NULL) { + fprintf(fp, "%s\n", file); + fclose(fp); + } + ut.actime = t; + ut.modtime = t; + utime(RC_DEPTREE_CACHE, &ut); + } else { + if (exists(RC_DEPTREE_SKEWED)) + unlink(RC_DEPTREE_SKEWED); + } + } + if (force == -1 && regen != NULL) + *regen = retval; + } + return rc_deptree_load(); +} + +bool _rc_can_find_pids(void) +{ + RC_PIDLIST *pids; + RC_PID *pid; + RC_PID *pid2; + bool retval = false; + + if (geteuid() == 0) + return true; + + /* If we cannot see process 1, then we don't test to see if + * services crashed or not */ + pids = rc_find_pids(NULL, NULL, 0, 1); + if (pids) { + pid = LIST_FIRST(pids); + if (pid) { + retval = true; + while (pid) { + pid2 = LIST_NEXT(pid, entries); + free(pid); + pid = pid2; + } + } + free(pids); + } + return retval; +} + +static const struct { + const char * const name; + RC_SERVICE bit; +} service_bits[] = { + { "service_started", RC_SERVICE_STARTED, }, + { "service_stopped", RC_SERVICE_STOPPED, }, + { "service_inactive", RC_SERVICE_INACTIVE, }, + { "service_starting", RC_SERVICE_STARTING, }, + { "service_stopping", RC_SERVICE_STOPPING, }, + { "service_hotplugged", RC_SERVICE_HOTPLUGGED, }, + { "service_wasinactive", RC_SERVICE_WASINACTIVE, }, + { "service_failed", RC_SERVICE_FAILED, }, +}; + +RC_SERVICE lookup_service_state(const char *service) +{ + size_t i; + for (i = 0; i < ARRAY_SIZE(service_bits); ++i) + if (!strcmp(service, service_bits[i].name)) + return service_bits[i].bit; + return 0; +} + +void from_time_t(char *time_string, time_t tv) +{ + strftime(time_string, 20, "%Y-%m-%d %H:%M:%S", localtime(&tv)); +} + +time_t to_time_t(char *timestring) +{ + int check = 0; + int year = 0; + int month = 0; + int day = 0; + int hour = 0; + int min = 0; + int sec = 0; + struct tm breakdown = {0}; + time_t result = -1; + + check = sscanf(timestring, "%4d-%2d-%2d %2d:%2d:%2d", + &year, &month, &day, &hour, &min, &sec); + if (check == 6) { + breakdown.tm_year = year - 1900; /* years since 1900 */ + breakdown.tm_mon = month - 1; + breakdown.tm_mday = day; + breakdown.tm_hour = hour; + breakdown.tm_min = min; + breakdown.tm_sec = sec; + breakdown.tm_isdst = -1; + result = mktime(&breakdown); + } + return result; +} diff --git a/src/rc/rc-plugin.c b/src/rc/rc-plugin.c new file mode 100644 index 0000000..e4650b1 --- /dev/null +++ b/src/rc/rc-plugin.c @@ -0,0 +1,244 @@ +/* + * rc-plugin.c + * Simple plugin handler + */ + +/* + * Copyright (c) 2007-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. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-misc.h" +#include "rc-plugin.h" + +#define RC_PLUGIN_HOOK "rc_plugin_hook" + +bool rc_in_plugin = false; + +typedef struct plugin +{ + char *name; + void *handle; + int (*hook)(RC_HOOK, const char *); + TAILQ_ENTRY(plugin) entries; +} PLUGIN; +TAILQ_HEAD(, plugin) plugins; + +#ifndef __FreeBSD__ +dlfunc_t +dlfunc(void * __restrict handle, const char * __restrict symbol) +{ + union { + void *d; + dlfunc_t f; + } rv; + + rv.d = dlsym(handle, symbol); + return rv.f; +} +#endif + +void +rc_plugin_load(void) +{ + DIR *dp; + struct dirent *d; + PLUGIN *plugin; + char file[PATH_MAX]; + void *h; + int (*fptr)(RC_HOOK, const char *); + + /* Don't load plugins if we're in one */ + if (rc_in_plugin) + return; + + TAILQ_INIT(&plugins); + + if (!(dp = opendir(RC_PLUGINDIR))) + return; + + while ((d = readdir(dp))) { + if (d->d_name[0] == '.') + continue; + + snprintf(file, sizeof(file), RC_PLUGINDIR "/%s", d->d_name); + h = dlopen(file, RTLD_LAZY); + if (h == NULL) { + eerror("dlopen: %s", dlerror()); + continue; + } + + fptr = (int (*)(RC_HOOK, const char *)) + dlfunc(h, RC_PLUGIN_HOOK); + if (fptr == NULL) { + eerror("%s: cannot find symbol `%s'", + d->d_name, RC_PLUGIN_HOOK); + dlclose(h); + } else { + plugin = xmalloc(sizeof(*plugin)); + plugin->name = xstrdup(d->d_name); + plugin->handle = h; + plugin->hook = fptr; + TAILQ_INSERT_TAIL(&plugins, plugin, entries); + } + } + closedir(dp); +} + +int +rc_waitpid(pid_t pid) +{ + int status; + + while (waitpid(pid, &status, 0) == -1) { + if (errno != EINTR) { + status = -1; + break; + } + } + return status; +} + +void +rc_plugin_run(RC_HOOK hook, const char *value) +{ + PLUGIN *plugin; + struct sigaction sa; + sigset_t empty; + sigset_t full; + sigset_t old; + int i; + int flags; + int pfd[2]; + pid_t pid; + char *buffer; + char *token; + char *p; + ssize_t nr; + int retval; + + /* Don't run plugins if we're in one */ + if (rc_in_plugin) + return; + + /* We need to block signals until we have forked */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + sigemptyset(&empty); + sigfillset(&full); + + TAILQ_FOREACH(plugin, &plugins, entries) { + /* We create a pipe so that plugins can affect our environment + * vars, which in turn influence our scripts. */ + if (pipe(pfd) == -1) { + eerror("pipe: %s", strerror(errno)); + return; + } + + /* Stop any scripts from inheriting us. + * This is actually quite important as without this, the splash + * plugin will probably hang when running in silent mode. */ + for (i = 0; i < 2; i++) + if ((flags = fcntl (pfd[i], F_GETFD, 0)) < 0 || + fcntl (pfd[i], F_SETFD, flags | FD_CLOEXEC) < 0) + eerror("fcntl: %s", strerror(errno)); + + sigprocmask(SIG_SETMASK, &full, &old); + + /* We run the plugin in a new process so we never crash + * or otherwise affected by it */ + if ((pid = fork()) == -1) { + eerror("fork: %s", strerror(errno)); + break; + } + + if (pid == 0) { + /* Restore default handlers */ + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGWINCH, &sa, NULL); + sigprocmask(SIG_SETMASK, &old, NULL); + + rc_in_plugin = true; + close(pfd[0]); + rc_environ_fd = fdopen(pfd[1], "w"); + retval = plugin->hook(hook, value); + fclose(rc_environ_fd); + rc_environ_fd = NULL; + + /* Just in case the plugin sets this to false */ + rc_in_plugin = true; + exit(retval); + } + + sigprocmask(SIG_SETMASK, &old, NULL); + close(pfd[1]); + buffer = xmalloc(sizeof(char) * BUFSIZ); + memset(buffer, 0, BUFSIZ); + + while ((nr = read(pfd[0], buffer, BUFSIZ)) > 0) { + p = buffer; + while (*p && p - buffer < nr) { + token = strsep(&p, "="); + if (token) { + unsetenv(token); + if (*p) { + setenv(token, p, 1); + p += strlen(p) + 1; + } else + p++; + } + } + } + + free(buffer); + close(pfd[0]); + + rc_waitpid(pid); + } +} + +void +rc_plugin_unload(void) +{ + PLUGIN *plugin = TAILQ_FIRST(&plugins); + PLUGIN *next; + + while (plugin) { + next = TAILQ_NEXT(plugin, entries); + dlclose(plugin->handle); + free(plugin->name); + free(plugin); + plugin = next; + } + TAILQ_INIT(&plugins); +} diff --git a/src/rc/rc-plugin.h b/src/rc/rc-plugin.h new file mode 100644 index 0000000..00ea7b3 --- /dev/null +++ b/src/rc/rc-plugin.h @@ -0,0 +1,41 @@ +/* + * rc-plugin.h + * Private instructions to use plugins + */ + +/* + * Copyright (c) 2007-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. + */ + +#ifndef __LIBRC_PLUGIN_H__ +#define __LIBRC_PLUGIN_H__ + +/* A simple flag to say if we're in a plugin proccess or not. + * Mainly used in atexit code. */ +extern bool rc_in_plugin; + +int rc_waitpid(pid_t pid); +void rc_plugin_load(void); +void rc_plugin_unload(void); +void rc_plugin_run(RC_HOOK, const char *value); + +/* dlfunc defines needed to avoid ISO errors. FreeBSD has this right :) */ +#if !defined(__FreeBSD__) && !defined(__DragonFly__) +struct __dlfunc_arg { + int __dlfunc_dummy; +}; + +typedef void (*dlfunc_t)(struct __dlfunc_arg); + +dlfunc_t dlfunc (void * __restrict handle, const char * __restrict symbol); +#endif + +#endif diff --git a/src/rc/rc-selinux.c b/src/rc/rc-selinux.c new file mode 100644 index 0000000..2eb631a --- /dev/null +++ b/src/rc/rc-selinux.c @@ -0,0 +1,398 @@ +/* + * rc-selinux.c + * SELinux helpers to get and set contexts. + */ + +/* + * Copyright (c) 2014-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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-misc.h" +#include "rc-plugin.h" +#include "rc-selinux.h" + +/* the context files for selinux */ +#define RUN_INIT_FILE "run_init_type" +#define INITRC_FILE "initrc_context" + +#ifdef HAVE_AUDIT +#include +#endif + +/* PAM or shadow for authentication */ +#ifdef HAVE_PAM +# define PAM_SERVICE_NAME "run_init" /* the name of this program for PAM */ +# include +# include +#else +# define PASSWORD_PROMPT "Password:" +# include +# include +# include +#endif + + +/* The handle for the fcontext lookups */ +static struct selabel_handle *hnd = NULL; + +int selinux_util_label(const char *path) +{ + int retval = 0; + int enforce; + struct stat st; + security_context_t con; + + enforce = security_getenforce(); + if (retval < 0) + return retval; + + if (!hnd) + return (enforce) ? -1 : 0; + + retval = lstat(path, &st); + if (retval < 0) { + if (errno == ENOENT) + return 0; + return (enforce) ? -1 : 0; + } + + /* lookup the context */ + retval = selabel_lookup_raw(hnd, &con, path, st.st_mode); + if (retval < 0) { + if (errno == ENOENT) + return 0; + return (enforce) ? -1 : 0; + } + + /* apply the context */ + retval = lsetfilecon(path, con); + freecon(con); + if (retval < 0) { + if (errno == ENOENT) + return 0; + if (errno == ENOTSUP) + return 0; + return (enforce) ? -1 : 0; + } + + return 0; +} + +/* + * Open the label handle + * returns 1 on success, 0 if no selinux, negative on error + */ +int selinux_util_open(void) +{ + int retval = 0; + + retval = is_selinux_enabled(); + if (retval <= 0) + return retval; + + hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); + if (!hnd) + return -2; + + return 1; +} + +/* + * Close the label handle + * returns 1 on success, 0 if no selinux, negative on error + */ +int selinux_util_close(void) +{ + int retval = 0; + + retval = is_selinux_enabled(); + if (retval <= 0) + return retval; + + if (hnd) { + selabel_close(hnd); + hnd = NULL; + } + + return 0; +} + +/* + * This will check the users password and return 0 on success or -1 on fail + * + * We ask for the password to make sure it is intended vs run by malicious software. + * Actual authorization is covered by the policy itself. + */ +static int check_password(char *username) +{ + int ret = 1; +#ifdef HAVE_PAM + pam_handle_t *pamh; + int pam_err = 0; + const struct pam_conv pconv = { + misc_conv, + NULL + }; + + pam_err = pam_start(PAM_SERVICE_NAME, username, &pconv, &pamh); + if (pam_err != PAM_SUCCESS) { + ret = -1; + goto outpam; + } + + pam_err = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK); + if (pam_err != PAM_SUCCESS) { + ret = -1; + goto outpam; + } + + ret = 0; +outpam: + pam_end(pamh, pam_err); + pamh = NULL; + +#else /* authenticating via /etc/shadow instead */ + struct spwd *spw; + char *password; + char *attempt; + + spw = getspnam(username); + if (!spw) { + eerror("Failed to read shadow entry"); + ret = -1; + goto outshadow; + } + + attempt = getpass(PASSWORD_PROMPT); + if (!attempt) { + ret = -1; + goto outshadow; + } + + if (*spw->sp_pwdp == '\0' && *attempt == '\0') { + ret = -1; + goto outshadow; + } + + /* salt must be at least two characters long */ + if (!(spw->sp_pwdp[0] && spw->sp_pwdp[1])) { + ret = -1; + goto outshadow; + } + + /* encrypt the password attempt */ + password = crypt(attempt, spw->sp_pwdp); + + if (password && strcmp(password, spw->sp_pwdp) == 0) + ret = 0; + else + ret = -1; +outshadow: +#endif + return ret; +} + +/* Authenticates the user, returns 0 on success, 1 on fail */ +static int check_auth() +{ + struct passwd *pw; + uid_t uid; + +#ifdef HAVE_AUDIT + uid = audit_getloginuid(); + if (uid == (uid_t) -1) + uid = getuid(); +#else + uid = getuid(); +#endif + + pw = getpwuid(uid); + if (!pw) { + eerror("cannot find your entry in the passwd file."); + return (-1); + } + + printf("Authenticating %s.\n", pw->pw_name); + + /* do the actual check */ + if (check_password(pw->pw_name) == 0) { + return 0; + } + + eerrorx("Authentication failed for %s", pw->pw_name); + return 1; +} + +/* + * Read the context from the given context file. context must be free'd by the user. + */ +static int read_context_file(const char *filename, char **context) +{ + int ret = -1; + FILE *fp; + char filepath[PATH_MAX]; + 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); + + fp = fopen(filepath, "r"); + if (fp == NULL) { + eerror("Failed to open context file: %s", filename); + return -1; + } + + while ((read = getline(&line, &len, fp)) != -1) { + /* cut off spaces before the string */ + p = line; + while (isspace(*p) && *p != '\0') + p++; + + /* empty string, skip */ + if (*p == '\0') + continue; + + /* cut off spaces after the string */ + p2 = p; + while (!isspace(*p2) && *p2 != '\0') + p2++; + *p2 = '\0'; + + *context = xstrdup(p); + ret = 0; + break; + } + + free(line); + fclose(fp); + return ret; +} + +void selinux_setup(char **argv) +{ + char *new_context = NULL; + char *curr_context = NULL; + context_t curr_con; + char *curr_t = NULL; + char *run_init_t = NULL; + + /* Return, if selinux is disabled. */ + if (is_selinux_enabled() < 1) { + return; + } + + if (read_context_file(RUN_INIT_FILE, &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); + } + + /* Get our current context. */ + if (getcon(&curr_context) < 0) { + if (errno == ENOENT) { + /* should only hit this if proc is not mounted. this + * happens on Gentoo right after init starts, when + * the init script processing starts. + */ + goto out; + } else { + perror("getcon"); + exit(1); + } + } + + /* extract the type from the context */ + curr_con = context_new(curr_context); + if (!curr_con) { + free(curr_context); + goto out; + } + + curr_t = 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); + + /* if we are not in the run_init domain, we should not do anything */ + if (strncmp(run_init_t, curr_t, strlen(run_init_t)) != 0) { + goto out; + } + + free(curr_t); + free(run_init_t); + + if (check_auth() != 0) { + eerrorx("Authentication failed."); + } + + /* Get the context for the script to be run in. */ + if (read_context_file(INITRC_FILE, &new_context) != 0) { + /* assume a reasonable default, rather than bailing out */ + new_context = xstrdup("system_u:system_r:initrc_t"); + ewarn("Assuming SELinux initrc context is %s", new_context); + } + + /* Set the new context */ + if (setexeccon(new_context) < 0) { + eerrorx("Could not set SELinux exec context to %s.", new_context); + } + + free(new_context); + + /* + * exec will recycle ptys so try and use open_init_pty if it exists + * which will open the pty with initrc_devpts_t, if it doesnt exist, + * fall back to plain exec + */ + if (!access("/usr/sbin/open_init_pty", X_OK)) { + if (execvp("/usr/sbin/open_init_pty", argv)) { + perror("execvp"); + exit(-1); + } + } else if (execvp(argv[1], argv + 1)) { + perror("execvp"); + exit(-1); + } + +out: + free(run_init_t); + free(curr_t); +} diff --git a/src/rc/rc-selinux.h b/src/rc/rc-selinux.h new file mode 100644 index 0000000..c3258d9 --- /dev/null +++ b/src/rc/rc-selinux.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014-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. + */ + +#ifndef RC_SELINUX_UTIL_H +#define RC_SELINUX_UTIL_H + +#ifdef HAVE_SELINUX + +int selinux_util_open(void); +int selinux_util_label(const char *path); +int selinux_util_close(void); + +void selinux_setup(char **argv); + +#else + +/* always return false for selinux_util_open() */ +#define selinux_util_open() (0) +#define selinux_util_label(x) do { } while (0) +#define selinux_util_close() do { } while (0) + +#define selinux_setup(x) do { } while (0) + +#endif + + +#endif diff --git a/src/rc/rc-service.c b/src/rc/rc-service.c new file mode 100644 index 0000000..d0a6499 --- /dev/null +++ b/src/rc/rc-service.c @@ -0,0 +1,132 @@ +/* + * rc-service.c + * Finds all OpenRC services + */ + +/* + * Copyright (c) 2008-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. + */ + +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-misc.h" +#include "_usage.h" + +const char *applet = NULL; +const char *extraopts = NULL; +const char *getoptstring = "e:ilr:IN" getoptstring_COMMON; +const struct option longopts[] = { + { "exists", 1, NULL, 'e' }, + { "ifexists", 0, NULL, 'i' }, + { "ifinactive", 0, NULL, 'I' }, + { "ifnotstarted", 0, NULL, 'N' }, + { "list", 0, NULL, 'l' }, + { "resolve", 1, NULL, 'r' }, + longopts_COMMON +}; +const char * const longopts_help[] = { + "tests if the service exists or not", + "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", + "list all available services", + "resolve the service name to an init script", + longopts_help_COMMON +}; +const char *usagestring = "" \ + "Usage: rc-service [options] [-i] ...\n" \ + " or: rc-service [options] -e \n" \ + " or: rc-service [options] -l\n" \ + " or: rc-service [options] -r "; + +int main(int argc, char **argv) +{ + int opt; + char *service; + RC_STRINGLIST *list; + RC_STRING *s; + RC_SERVICE state; + bool if_exists = false; + bool if_inactive = false; + bool if_notstarted = false; + + applet = basename_c(argv[0]); + /* Ensure that we are only quiet when explicitly told to be */ + unsetenv("EINFO_QUIET"); + + while ((opt = getopt_long(argc, argv, getoptstring, + longopts, (int *) 0)) != -1) + { + switch (opt) { + case 'e': + service = rc_service_resolve(optarg); + opt = service ? EXIT_SUCCESS : EXIT_FAILURE; + free(service); + return opt; + /* NOTREACHED */ + case 'i': + if_exists = true; + break; + case 'I': + if_inactive = true; + break; + case 'N': + if_notstarted = true; + break; + case 'l': + list = rc_services_in_runlevel(NULL); + if (TAILQ_FIRST(list) == NULL) + return EXIT_FAILURE; + rc_stringlist_sort(&list); + TAILQ_FOREACH(s, list, entries) + printf("%s\n", s->value); + rc_stringlist_free(list); + return EXIT_SUCCESS; + /* NOTREACHED */ + case 'r': + service = rc_service_resolve(optarg); + if (service == NULL) + return EXIT_FAILURE; + printf("%s\n", service); + free(service); + return EXIT_SUCCESS; + /* NOTREACHED */ + + case_RC_COMMON_GETOPT + } + } + + argc -= optind; + argv += optind; + if (*argv == NULL) + eerrorx("%s: you need to specify a service", applet); + if ((service = rc_service_resolve(*argv)) == NULL) { + if (if_exists) + return 0; + eerrorx("%s: service `%s' does not exist", applet, *argv); + } + state = rc_service_state(*argv); + if (if_inactive && ! (state & RC_SERVICE_INACTIVE)) + return 0; + if (if_notstarted && (state & RC_SERVICE_STARTED)) + return 0; + *argv = service; + execv(*argv, argv); + eerrorx("%s: %s", applet, strerror(errno)); + /* NOTREACHED */ +} diff --git a/src/rc/rc-status.c b/src/rc/rc-status.c new file mode 100644 index 0000000..9a094d4 --- /dev/null +++ b/src/rc/rc-status.c @@ -0,0 +1,416 @@ +/* + * rc-status.c + * Display the status of the services in runlevels + */ + +/* + * Copyright (c) 2007-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. + */ + +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-misc.h" +#include "_usage.h" + +const char *applet = NULL; +const char *extraopts = NULL; +const char *getoptstring = "aclmrsu" getoptstring_COMMON; +const struct option longopts[] = { + {"all", 0, NULL, 'a'}, + {"crashed", 0, NULL, 'c'}, + {"list", 0, NULL, 'l'}, + {"manual", 0, NULL, 'm'}, + {"runlevel", 0, NULL, 'r'}, + {"servicelist", 0, NULL, 's'}, + {"unused", 0, NULL, 'u'}, + longopts_COMMON +}; +const char * const longopts_help[] = { + "Show services from all run levels", + "Show crashed services", + "Show list of run levels", + "Show manually started services", + "Show the name of the current runlevel", + "Show service list", + "Show services not assigned to any runlevel", + longopts_help_COMMON +}; +const char *usagestring = "" \ + "Usage: rc-status [options] ...\n" \ + " or: rc-status [options] [-a | -c | -l | -m | -r | -s | -u]"; + +static bool test_crashed = false; +static RC_DEPTREE *deptree; +static RC_STRINGLIST *types; + +static RC_STRINGLIST *levels, *services, *tmp, *alist; +static RC_STRINGLIST *sservices, *nservices, *needsme; + +static void +print_level(const char *prefix, const char *level) +{ + if (prefix) + printf("%s ", prefix); + printf ("Runlevel: "); + if (isatty(fileno(stdout))) + printf("%s%s%s\n", + ecolor(ECOLOR_HILITE), + level, + ecolor(ECOLOR_NORMAL)); + else + printf("%s\n", level); +} + +static void get_uptime(const char *service, char *uptime, int uptime_size) +{ + RC_SERVICE state = rc_service_state(service); + char *start_count; + time_t now; + char *start_time_string; + time_t start_time; + 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) { + start_count = rc_service_value_get(service, "start_count"); + start_time_string = rc_service_value_get(service, "start_time"); + if (start_count && start_time_string) { + start_time = to_time_t(start_time_string); + now = time(NULL); + 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_secs > (time_t) 3600) { + diff_hours = diff_secs / (time_t) 3600; + diff_secs %= diff_hours * (time_t) 3600; + } + if (diff_secs > (time_t) 60) { + diff_mins = diff_secs / (time_t) 60; + diff_secs %= diff_mins * (time_t) 60; + } + 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, + "%02ld:%02ld:%02ld (%s)", + diff_hours, diff_mins, diff_secs, start_count); + } + } +} + +static void +print_service(const char *service) +{ + char status[60]; + char uptime [40]; + int cols = printf(" %s", service); + const char *c = ecolor(ECOLOR_GOOD); + RC_SERVICE state = rc_service_state(service); + ECOLOR color = ECOLOR_BAD; + + if (state & RC_SERVICE_STOPPING) + snprintf(status, sizeof(status), "stopping "); + else if (state & RC_SERVICE_STARTING) { + snprintf(status, sizeof(status), "starting "); + color = ECOLOR_WARN; + } else if (state & RC_SERVICE_INACTIVE) { + snprintf(status, sizeof(status), "inactive "); + color = ECOLOR_WARN; + } else if (state & RC_SERVICE_STARTED) { + errno = 0; + if (test_crashed && + rc_service_daemons_crashed(service) && + errno != EACCES) + { + snprintf(status, sizeof(status), " crashed "); + } else { + get_uptime(service, uptime, 40); + snprintf(status, sizeof(status), " started %s", uptime); + color = ECOLOR_GOOD; + } + } else if (state & RC_SERVICE_SCHEDULED) { + snprintf(status, sizeof(status), "scheduled"); + color = ECOLOR_WARN; + } else + snprintf(status, sizeof(status), " stopped "); + + errno = 0; + if (c && *c && isatty(fileno(stdout))) + printf("\n"); + ebracket(cols, color, status); +} + +static void +print_services(const char *runlevel, RC_STRINGLIST *svcs) +{ + RC_STRINGLIST *l = NULL; + RC_STRING *s; + char *r = NULL; + + if (!svcs) + return; + if (!deptree) + deptree = _rc_deptree_load(0, NULL); + if (!deptree) { + TAILQ_FOREACH(s, svcs, entries) + if (!runlevel || + rc_service_in_runlevel(s->value, runlevel)) + print_service(s->value); + return; + } + if (!types) { + types = rc_stringlist_new(); + rc_stringlist_add(types, "ineed"); + rc_stringlist_add(types, "iuse"); + rc_stringlist_add(types, "iafter"); + } + if (!runlevel) + r = rc_runlevel_get(); + l = rc_deptree_depends(deptree, types, svcs, r ? r : runlevel, + RC_DEP_STRICT | RC_DEP_TRACE | RC_DEP_START); + free(r); + if (!l) + return; + TAILQ_FOREACH(s, l, entries) { + if (!rc_stringlist_find(svcs, s->value)) + continue; + if (!runlevel || rc_service_in_runlevel(s->value, runlevel)) + print_service(s->value); + } + rc_stringlist_free(l); +} + +static void +print_stacked_services(const char *runlevel) +{ + RC_STRINGLIST *stackedlevels, *servicelist; + RC_STRING *stackedlevel; + + stackedlevels = rc_runlevel_stacks(runlevel); + TAILQ_FOREACH(stackedlevel, stackedlevels, entries) { + if (rc_stringlist_find(levels, stackedlevel->value) != NULL) + continue; + print_level("Stacked", stackedlevel->value); + servicelist = rc_services_in_runlevel(stackedlevel->value); + print_services(stackedlevel->value, servicelist); + rc_stringlist_free(servicelist); + } + rc_stringlist_free(stackedlevels); + stackedlevels = NULL; +} + +int main(int argc, char **argv) +{ + RC_STRING *s, *l, *t, *level; + bool show_all = false; + char *p, *runlevel = NULL; + int opt, retval = 0; + + test_crashed = _rc_can_find_pids(); + + applet = basename_c(argv[0]); + while ((opt = getopt_long(argc, argv, getoptstring, longopts, + (int *) 0)) != -1) + switch (opt) { + case 'a': + show_all = true; + levels = rc_runlevel_list(); + break; + case 'c': + services = rc_services_in_state(RC_SERVICE_STARTED); + retval = 1; + TAILQ_FOREACH(s, services, entries) + if (rc_service_daemons_crashed(s->value)) { + printf("%s\n", s->value); + retval = 0; + } + goto exit; + /* NOTREACHED */ + case 'l': + levels = rc_runlevel_list(); + TAILQ_FOREACH(l, levels, entries) + printf("%s\n", l->value); + goto exit; + case 'm': + services = rc_services_in_runlevel(NULL); + levels = rc_runlevel_list(); + TAILQ_FOREACH_SAFE(s, services, entries, t) { + TAILQ_FOREACH(l, levels, entries) + if (rc_service_in_runlevel(s->value, l->value)) { + TAILQ_REMOVE(services, s, entries); + free(s->value); + free(s); + break; + } + } + TAILQ_FOREACH_SAFE(s, services, entries, t) + if (rc_service_state(s->value) & + (RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED)) { + TAILQ_REMOVE(services, s, entries); + free(s->value); + free(s); + } + print_services(NULL, services); + goto exit; + case 'r': + runlevel = rc_runlevel_get(); + printf("%s\n", runlevel); + goto exit; + /* NOTREACHED */ + case 's': + services = rc_services_in_runlevel(NULL); + print_services(NULL, services); + goto exit; + /* NOTREACHED */ + case 'u': + services = rc_services_in_runlevel(NULL); + levels = rc_runlevel_list(); + TAILQ_FOREACH_SAFE(s, services, entries, t) { + TAILQ_FOREACH(l, levels, entries) + if (rc_service_in_runlevel(s->value, l->value)) { + TAILQ_REMOVE(services, s, entries); + free(s->value); + free(s); + break; + } + } + print_services(NULL, services); + goto exit; + /* NOTREACHED */ + + case_RC_COMMON_GETOPT + } + + if (!levels) + levels = rc_stringlist_new(); + opt = (optind < argc) ? 0 : 1; + while (optind < argc) { + if (rc_runlevel_exists(argv[optind])) { + rc_stringlist_add(levels, argv[optind++]); + opt++; + } else + eerror("runlevel `%s' does not exist", argv[optind++]); + } + if (opt == 0) + exit(EXIT_FAILURE); + if (!TAILQ_FIRST(levels)) { + runlevel = rc_runlevel_get(); + rc_stringlist_add(levels, runlevel); + } + + /* Output the services in the order in which they would start */ + deptree = _rc_deptree_load(0, NULL); + + TAILQ_FOREACH(l, levels, entries) { + print_level(NULL, l->value); + services = rc_services_in_runlevel(l->value); + print_services(l->value, services); + print_stacked_services(l->value); + rc_stringlist_free(nservices); + nservices = NULL; + rc_stringlist_free(services); + services = NULL; + } + + if (show_all || argc < 2) { + /* Show hotplugged services */ + print_level("Dynamic", "hotplugged"); + services = rc_services_in_state(RC_SERVICE_HOTPLUGGED); + print_services(NULL, services); + rc_stringlist_free(services); + services = NULL; + + /* Show manually started and unassigned depended services */ + if (show_all) { + rc_stringlist_free(levels); + levels = rc_stringlist_new(); + if (!runlevel) + runlevel = rc_runlevel_get(); + rc_stringlist_add(levels, runlevel); + } + rc_stringlist_add(levels, RC_LEVEL_SYSINIT); + rc_stringlist_add(levels, RC_LEVEL_BOOT); + services = rc_services_in_runlevel(NULL); + sservices = rc_stringlist_new(); + TAILQ_FOREACH(l, levels, entries) { + nservices = rc_services_in_runlevel_stacked(l->value); + TAILQ_CONCAT(sservices, nservices, entries); + free(nservices); + } + TAILQ_FOREACH_SAFE(s, services, entries, t) { + if ((rc_stringlist_find(sservices, s->value) || + (rc_service_state(s->value) & ( RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED)))) { + TAILQ_REMOVE(services, s, entries); + free(s->value); + free(s); + } + } + needsme = rc_stringlist_new(); + rc_stringlist_add(needsme, "needsme"); + rc_stringlist_add(needsme, "wantsme"); + nservices = rc_stringlist_new(); + alist = rc_stringlist_new(); + l = rc_stringlist_add(alist, ""); + p = l->value; + TAILQ_FOREACH(level, levels, entries) { + TAILQ_FOREACH_SAFE(s, services, entries, t) { + l->value = s->value; + setenv("RC_SVCNAME", l->value, 1); + tmp = rc_deptree_depends(deptree, needsme, alist, level->value, RC_DEP_TRACE); + if (TAILQ_FIRST(tmp)) { + TAILQ_REMOVE(services, s, entries); + TAILQ_INSERT_TAIL(nservices, s, entries); + } + rc_stringlist_free(tmp); + } + } + l->value = p; + /* + * we are unsetting RC_SVCNAME because last loaded service will not + * be added to the list + */ + unsetenv("RC_SVCNAME"); + print_level("Dynamic", "needed/wanted"); + print_services(NULL, nservices); + print_level("Dynamic", "manual"); + print_services(NULL, services); + } + +exit: + free(runlevel); + rc_stringlist_free(alist); + rc_stringlist_free(needsme); + rc_stringlist_free(sservices); + rc_stringlist_free(nservices); + rc_stringlist_free(services); + rc_stringlist_free(types); + rc_stringlist_free(levels); + rc_deptree_free(deptree); + + return retval; +} diff --git a/src/rc/rc-update.c b/src/rc/rc-update.c new file mode 100644 index 0000000..9bc1fe5 --- /dev/null +++ b/src/rc/rc-update.c @@ -0,0 +1,351 @@ +/* + * rc-update + * Manage init scripts and runlevels + */ + +/* + * Copyright (c) 2007-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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-misc.h" +#include "_usage.h" + +const char *applet = NULL; +const char *extraopts = NULL; +const char *usagestring = "" \ + "Usage: rc-update [options] add [...]\n" \ + " or: rc-update [options] del [...]\n" \ + " or: rc-update [options] [show [...]]"; +const char *getoptstring = "asu" getoptstring_COMMON; +const struct option longopts[] = { + { "all", 0, NULL, 'a' }, + { "stack", 0, NULL, 's' }, + { "update", 0, NULL, 'u' }, + longopts_COMMON +}; +const char * const longopts_help[] = { + "Process all runlevels", + "Stack a runlevel instead of a service", + "Force an update of the dependency tree", + longopts_help_COMMON +}; + +/* Return the number of changes made: + * -1 = no changes (error) + * 0 = no changes (nothing to do) + * 1+ = number of runlevels updated + */ +static int +add(const char *runlevel, const char *service) +{ + int retval = -1; + + if (!rc_service_exists(service)) { + if (errno == ENOEXEC) + eerror("%s: service `%s' is not executeable", + applet, service); + else + eerror("%s: service `%s' does not exist", + applet, service); + } else if (rc_service_in_runlevel(service, runlevel)) { + einfo("%s: %s already installed in runlevel `%s'; skipping", + applet, service, runlevel); + retval = 0; + } else if (rc_service_add(runlevel, service)) { + einfo("service %s added to runlevel %s", service, runlevel); + retval = 1; + } else + eerror("%s: failed to add service `%s' to runlevel `%s': %s", + applet, service, runlevel, strerror (errno)); + + return retval; +} + +static int +delete(const char *runlevel, const char *service) +{ + int retval = -1; + + errno = 0; + if (rc_service_delete(runlevel, service)) { + einfo("service %s removed from runlevel %s", + service, runlevel); + return 1; + } + + if (errno == ENOENT) + eerror("%s: service `%s' is not in the runlevel `%s'", + applet, service, runlevel); + else + eerror("%s: failed to remove service `%s' from runlevel `%s': %s", + applet, service, runlevel, strerror (errno)); + + return retval; +} + +static int +addstack(const char *runlevel, const char *stack) +{ + if (!rc_runlevel_exists(runlevel)) { + eerror("%s: runlevel `%s' does not exist", applet, runlevel); + return -1; + } + if (!rc_runlevel_exists(stack)) { + eerror("%s: runlevel `%s' does not exist", applet, stack); + return -1; + } + if (strcmp(runlevel, stack) == 0) { + eerror("%s: cannot stack `%s' onto itself", applet, stack); + return -1; + } + if (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0 || + strcmp(stack, RC_LEVEL_SYSINIT) == 0 || + strcmp(runlevel, RC_LEVEL_BOOT) == 0 || + strcmp(stack, RC_LEVEL_BOOT) == 0 || + strcmp(runlevel, RC_LEVEL_SINGLE) == 0 || + strcmp(stack, RC_LEVEL_SINGLE) == 0 || + strcmp(runlevel, RC_LEVEL_SHUTDOWN) == 0 || + strcmp(stack, RC_LEVEL_SHUTDOWN) == 0) + { + eerror("%s: cannot stack the %s runlevel", + applet, RC_LEVEL_SYSINIT); + return -1; + } + if (!rc_runlevel_stack(runlevel, stack)) { + eerror("%s: failed to stack `%s' to `%s': %s", + applet, stack, runlevel, strerror(errno)); + return -1; + } + einfo("runlevel %s added to runlevel %s", stack, runlevel); + return 1; +} + +static int +delstack(const char *runlevel, const char *stack) +{ + if (rc_runlevel_unstack(runlevel, stack)) { + einfo("runlevel %s removed from runlevel %s", stack, runlevel); + return 1; + } + + if (errno == ENOENT) + eerror("%s: runlevel `%s' is not in the runlevel `%s'", + applet, stack, runlevel); + else + eerror("%s: failed to remove runlevel `%s' from runlevel `%s': %s", + applet, stack, runlevel, strerror (errno)); + + return -1; +} + +static void +show(RC_STRINGLIST *runlevels, bool verbose) +{ + RC_STRINGLIST *services = rc_services_in_runlevel(NULL); + RC_STRING *service; + RC_STRING *runlevel; + RC_STRINGLIST *in; + bool inone; + char buffer[PATH_MAX]; + size_t l; + + rc_stringlist_sort(&services); + TAILQ_FOREACH(service, services, entries) { + in = rc_stringlist_new(); + inone = false; + + TAILQ_FOREACH(runlevel, runlevels, entries) { + if (rc_service_in_runlevel(service->value, + runlevel->value)) + { + rc_stringlist_add(in, runlevel->value); + inone = true; + } else { + l = strlen(runlevel->value); + memset (buffer, ' ', l); + buffer[l] = 0; + rc_stringlist_add (in, buffer); + } + } + + if (inone || verbose) { + printf(" %20s |", service->value); + TAILQ_FOREACH(runlevel, in, entries) + printf (" %s", runlevel->value); + printf ("\n"); + } + rc_stringlist_free(in); + } + + rc_stringlist_free (services); +} + +#define DOADD (1 << 1) +#define DODELETE (1 << 2) +#define DOSHOW (1 << 3) + +int main(int argc, char **argv) +{ + RC_DEPTREE *deptree; + RC_STRINGLIST *runlevels; + RC_STRING *runlevel; + char *service = NULL; + char *p; + int action = 0; + bool verbose = false, stack = false, all_runlevels = false; + int opt; + int retval = EXIT_FAILURE; + int num_updated = 0; + int (*actfunc)(const char *, const char *); + int ret; + + applet = basename_c(argv[0]); + while ((opt = getopt_long(argc, argv, getoptstring, + longopts, (int *)0)) != -1) + switch (opt) { + case 'a': + all_runlevels = true; + break; + case 's': + stack = true; + break; + case 'u': + deptree = _rc_deptree_load(-1, &ret); + if (deptree) + rc_deptree_free(deptree); + return ret; + case_RC_COMMON_GETOPT + } + + verbose = rc_yesno(getenv ("EINFO_VERBOSE")); + + if ((action & DOSHOW && action != DOSHOW) || + (action & DOADD && action != DOADD) || + (action & DODELETE && action != DODELETE)) + eerrorx("%s: cannot mix commands", applet); + + /* We need to be backwards compatible */ + if (optind < argc) { + if (strcmp(argv[optind], "add") == 0) + action = DOADD; + else if (strcmp(argv[optind], "delete") == 0 || + strcmp(argv[optind], "del") == 0) + action = DODELETE; + else if (strcmp(argv[optind], "show") == 0) + action = DOSHOW; + if (action) + optind++; + else + eerrorx("%s: invalid command `%s'", + applet, argv[optind]); + } + if (!action) + action = DOSHOW; + + runlevels = rc_stringlist_new(); + + if (optind >= argc) { + if (!(action & DOSHOW)) + eerrorx("%s: no service specified", applet); + } else { + service = argv[optind]; + optind++; + + while (optind < argc) + if (rc_runlevel_exists(argv[optind])) + rc_stringlist_add(runlevels, argv[optind++]); + else { + rc_stringlist_free(runlevels); + eerrorx ("%s: `%s' is not a valid runlevel", + applet, argv[optind]); + } + } + + retval = EXIT_SUCCESS; + if (action & DOSHOW) { + if (service) + rc_stringlist_add(runlevels, service); + if (!TAILQ_FIRST(runlevels)) { + free(runlevels); + runlevels = rc_runlevel_list(); + } + + rc_stringlist_sort(&runlevels); + show (runlevels, verbose); + } else { + if (!service) + eerror ("%s: no service specified", applet); + else { + if (action & DOADD) { + if (all_runlevels) { + rc_stringlist_free(runlevels); + eerrorx("%s: the -a option is invalid with add", applet); + } + actfunc = stack ? addstack : add; + } else if (action & DODELETE) { + actfunc = stack ? delstack : delete; + } else { + rc_stringlist_free(runlevels); + eerrorx("%s: invalid action", applet); + } + + if (!TAILQ_FIRST(runlevels)) { + if (all_runlevels) { + free(runlevels); + runlevels = rc_runlevel_list(); + } else { + p = rc_runlevel_get(); + rc_stringlist_add(runlevels, p); + free(p); + } + } + + if (!TAILQ_FIRST(runlevels)) { + free(runlevels); + eerrorx("%s: no runlevels found", applet); + } + + TAILQ_FOREACH(runlevel, runlevels, entries) { + if (!rc_runlevel_exists(runlevel->value)) { + eerror ("%s: runlevel `%s' does not exist", + applet, runlevel->value); + continue; + } + + ret = actfunc(runlevel->value, service); + if (ret < 0) + retval = EXIT_FAILURE; + num_updated += ret; + } + + if (retval == EXIT_SUCCESS && + num_updated == 0 && action & DODELETE) + ewarnx("%s: service `%s' not found in any" + " of the specified runlevels", + applet, service); + } + } + + rc_stringlist_free(runlevels); + return retval; +} diff --git a/src/rc/rc-wtmp.c b/src/rc/rc-wtmp.c new file mode 100644 index 0000000..40cc280 --- /dev/null +++ b/src/rc/rc-wtmp.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/src/rc/rc.c b/src/rc/rc.c new file mode 100644 index 0000000..c4a88a7 --- /dev/null +++ b/src/rc/rc.c @@ -0,0 +1,1127 @@ +/* + * rc.c + * rc - manager for init scripts which control the startup, shutdown + * and the running of daemons. + * + * Also a multicall binary for various commands that can be used in shell + * scripts to query service state, mark service state and provide the + * einfo family of informational functions. + */ + +/* + * Copyright (c) 2007-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. + */ + +const char rc_copyright[] = "Copyright (c) 2007-2008 Roy Marples"; + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-logger.h" +#include "rc-misc.h" +#include "rc-plugin.h" + +#include "version.h" +#include "_usage.h" + +const char *extraopts = NULL; +const char *getoptstring = "a:no:s:S" getoptstring_COMMON; +const struct option longopts[] = { + { "no-stop", 0, NULL, 'n' }, + { "override", 1, NULL, 'o' }, + { "service", 1, NULL, 's' }, + { "sys", 0, NULL, 'S' }, + longopts_COMMON +}; +const char * const longopts_help[] = { + "do not stop any services", + "override the next runlevel to change into\n" + "when leaving single user or boot runlevels", + "runs the service specified with the rest\nof the arguments", + "output the RC system type, if any", + longopts_help_COMMON +}; +const char *usagestring = "" \ + "Usage: openrc [options] []"; + +#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" + +const char *applet = NULL; +static RC_STRINGLIST *main_hotplugged_services; +static RC_STRINGLIST *main_stop_services; +static RC_STRINGLIST *main_start_services; +static RC_STRINGLIST *main_types_nw; +static RC_STRINGLIST *main_types_nwua; +static RC_DEPTREE *main_deptree; +static char *runlevel; +static RC_HOOK hook_out; + +struct termios *termios_orig = NULL; + +RC_PIDLIST service_pids; + +static void +clean_failed(void) +{ + DIR *dp; + struct dirent *d; + size_t l; + char *path; + + /* Clean the failed services state dir now */ + if ((dp = opendir(RC_SVCDIR "/failed"))) { + while ((d = readdir(dp))) { + if (d->d_name[0] == '.' && + (d->d_name[1] == '\0' || + (d->d_name[1] == '.' && d->d_name[2] == '\0'))) + continue; + + l = strlen(RC_SVCDIR "/failed/") + + strlen(d->d_name) + 1; + path = xmalloc(sizeof(char) * l); + snprintf(path, l, RC_SVCDIR "/failed/%s", d->d_name); + if (path) { + if (unlink(path)) + eerror("%s: unlink `%s': %s", + applet, path, strerror(errno)); + free(path); + } + } + closedir(dp); + } +} + +static void +cleanup(void) +{ + RC_PID *p1 = LIST_FIRST(&service_pids); + RC_PID *p2; + + if (!rc_in_logger && !rc_in_plugin && + applet && (strcmp(applet, "rc") == 0 || strcmp(applet, "openrc") == 0)) + { + if (hook_out) + rc_plugin_run(hook_out, runlevel); + + rc_plugin_unload(); + + if (termios_orig) { + tcsetattr(STDIN_FILENO, TCSANOW, termios_orig); + free(termios_orig); + } + + /* Clean runlevel start, stop markers */ + rmdir(RC_STARTING); + rmdir(RC_STOPPING); + clean_failed(); + rc_logger_close(); + } + + while (p1) { + p2 = LIST_NEXT(p1, entries); + free(p1); + p1 = p2; + } + + rc_stringlist_free(main_hotplugged_services); + rc_stringlist_free(main_stop_services); + rc_stringlist_free(main_start_services); + rc_stringlist_free(main_types_nw); + rc_stringlist_free(main_types_nwua); + rc_deptree_free(main_deptree); + free(runlevel); +} + +static char +read_key(bool block) +{ + struct termios termios; + char c = 0; + int fd = STDIN_FILENO; + + if (!isatty(fd)) + return false; + + /* Now save our terminal settings. We need to restore them at exit as + we will be changing it for non-blocking reads for Interactive */ + if (!termios_orig) { + termios_orig = xmalloc(sizeof(*termios_orig)); + tcgetattr(fd, termios_orig); + } + + tcgetattr(fd, &termios); + termios.c_lflag &= ~(ICANON | ECHO); + if (block) + termios.c_cc[VMIN] = 1; + else { + termios.c_cc[VMIN] = 0; + termios.c_cc[VTIME] = 0; + } + tcsetattr(fd, TCSANOW, &termios); + if (read(fd, &c, 1) == -1) + eerror("read: %s", strerror(errno)); + tcsetattr(fd, TCSANOW, termios_orig); + return c; +} + +static bool +want_interactive(void) +{ + char c; + static bool gotinteractive; + static bool interactive; + + if (rc_yesno(getenv("EINFO_QUIET"))) + return false; + if (!gotinteractive) { + gotinteractive = true; + interactive = rc_conf_yesno("rc_interactive"); + } + if (!interactive) + return false; + c = read_key(false); + return (c == 'I' || c == 'i') ? true : false; +} + +static void +mark_interactive(void) +{ + FILE *fp = fopen(INTERACTIVE, "w"); + if (fp) + fclose(fp); +} + +static void +run_program(const char *prog) +{ + struct sigaction sa; + sigset_t full; + sigset_t old; + pid_t pid; + + /* We need to block signals until we have forked */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + sigfillset(&full); + sigprocmask(SIG_SETMASK, &full, &old); + pid = fork(); + + if (pid == -1) + eerrorx("%s: fork: %s", applet, strerror(errno)); + if (pid == 0) { + /* Restore default handlers */ + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGWINCH, &sa, NULL); + + /* Unmask signals */ + sigprocmask(SIG_SETMASK, &old, NULL); + + if (termios_orig) + tcsetattr(STDIN_FILENO, TCSANOW, termios_orig); + + execl(prog, prog, (char *)NULL); + eerror("%s: unable to exec `%s': %s", applet, prog, + strerror(errno)); + _exit(EXIT_FAILURE); + } + + /* Unmask signals and wait for child */ + sigprocmask(SIG_SETMASK, &old, NULL); + if (rc_waitpid(pid) == -1) + eerrorx("%s: failed to exec `%s'", applet, prog); +} + +static void +open_shell(void) +{ + const char *shell; + struct passwd *pw; + +#ifdef __linux__ + const char *sys = rc_sys(); + + /* 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", + applet, strerror(errno)); + } +#endif + + shell = rc_conf_value("rc_shell"); + /* No shell set, so obey env, then passwd, then default to /bin/sh */ + if (shell == NULL) { + shell = getenv("SHELL"); + if (shell == NULL) { + pw = getpwuid(getuid()); + if (pw) + shell = pw->pw_shell; + if (shell == NULL) + shell = "/bin/sh"; + } + } + run_program(shell); +} + +static bool +set_krunlevel(const char *level) +{ + FILE *fp; + + if (!level || + strcmp(level, getenv ("RC_BOOTLEVEL")) == 0 || + strcmp(level, RC_LEVEL_SINGLE) == 0 || + strcmp(level, RC_LEVEL_SYSINIT) == 0) + { + if (exists(RC_KRUNLEVEL) && + unlink(RC_KRUNLEVEL) != 0) + eerror("unlink `%s': %s", RC_KRUNLEVEL, + strerror(errno)); + return false; + } + + if (!(fp = fopen(RC_KRUNLEVEL, "w"))) { + eerror("fopen `%s': %s", RC_KRUNLEVEL, strerror(errno)); + return false; + } + + fprintf(fp, "%s", level); + fclose(fp); + return true; +} + +static size_t +get_krunlevel(char *buffer, int buffer_len) +{ + FILE *fp; + size_t i = 0; + + if (!exists(RC_KRUNLEVEL)) + return 0; + if (!(fp = fopen(RC_KRUNLEVEL, "r"))) { + eerror("fopen `%s': %s", RC_KRUNLEVEL, strerror(errno)); + return 0; + } + + if (fgets(buffer, buffer_len, fp)) { + i = strlen(buffer); + if (buffer[i - 1] == '\n') + buffer[i - 1] = 0; + } + fclose(fp); + return i; +} + +static void +add_pid(pid_t pid) +{ + RC_PID *p = xmalloc(sizeof(*p)); + p->pid = pid; + LIST_INSERT_HEAD(&service_pids, p, entries); +} + +static void +remove_pid(pid_t pid) +{ + RC_PID *p; + + LIST_FOREACH(p, &service_pids, entries) + if (p->pid == pid) { + LIST_REMOVE(p, entries); + free(p); + return; + } +} + +static void +wait_for_services(void) +{ + for (;;) { + while (waitpid(0, 0, 0) != -1) + ; + if (errno != EINTR) + break; + } +} + +static void +handle_signal(int sig) +{ + int serrno = errno; + char signame[10] = { '\0' }; + pid_t pid; + RC_PID *pi; + int status = 0; + struct winsize ws; + sigset_t sset; + + switch (sig) { + case SIGCHLD: + do { + pid = waitpid(-1, &status, WNOHANG); + if (pid < 0) { + if (errno != ECHILD) + eerror("waitpid: %s", strerror(errno)); + return; + } + } while (!WIFEXITED(status) && !WIFSIGNALED(status)); + + /* Remove that pid from our list */ + if (pid > 0) + remove_pid(pid); + break; + + case SIGWINCH: + if (rc_logger_tty >= 0) { + ioctl(STDIN_FILENO, TIOCGWINSZ, &ws); + ioctl(rc_logger_tty, TIOCSWINSZ, &ws); + } + break; + + case SIGINT: + if (!signame[0]) + snprintf(signame, sizeof(signame), "SIGINT"); + /* FALLTHROUGH */ + case SIGTERM: + if (!signame[0]) + snprintf(signame, sizeof(signame), "SIGTERM"); + /* FALLTHROUGH */ + case SIGQUIT: + if (!signame[0]) + snprintf(signame, sizeof(signame), "SIGQUIT"); + eerrorx("%s: caught %s, aborting", applet, signame); + /* NOTREACHED */ + case SIGUSR1: + eerror("rc: Aborting!"); + + /* Block child signals */ + sigemptyset(&sset); + sigaddset(&sset, SIGCHLD); + sigprocmask(SIG_BLOCK, &sset, NULL); + + /* Kill any running services we have started */ + LIST_FOREACH(pi, &service_pids, entries) + kill(pi->pid, SIGTERM); + + /* Notify plugins we are aborting */ + rc_plugin_run(RC_HOOK_ABORT, NULL); + + exit(EXIT_FAILURE); + /* NOTREACHED */ + + default: + eerror("%s: caught unknown signal %d", applet, sig); + } + + /* Restore errno */ + errno = serrno; +} + +static void +do_sysinit() +{ + struct utsname uts; + const char *sys; + + /* exec init-early.sh if it exists + * This should just setup the console to use the correct + * font. Maybe it should setup the keyboard too? */ + if (exists(INITEARLYSH)) + run_program(INITEARLYSH); + + uname(&uts); + printf("\n %sOpenRC %s" VERSION "%s is starting up %s", + ecolor(ECOLOR_GOOD), ecolor(ECOLOR_HILITE), + ecolor(ECOLOR_NORMAL), ecolor(ECOLOR_HYPERBOLA)); +#ifdef BRANDING + printf(BRANDING " (%s)", uts.machine); +#else + printf("%s %s (%s)", + uts.sysname, + uts.release, + uts.machine); +#endif + + if ((sys = rc_sys())) + printf(" [%s]", sys); + + printf("%s\n\n", ecolor(ECOLOR_NORMAL)); + + if (!rc_yesno(getenv ("EINFO_QUIET")) && + rc_conf_yesno("rc_interactive")) + printf("Press %sI%s to enter interactive boot mode\n\n", + ecolor(ECOLOR_GOOD), ecolor(ECOLOR_NORMAL)); + + setenv("RC_RUNLEVEL", RC_LEVEL_SYSINIT, 1); + run_program(INITSH); + + /* init may have mounted /proc so we can now detect or real + * sys */ + if ((sys = rc_sys())) + setenv("RC_SYS", sys, 1); +} + +static bool +runlevel_config(const char *service, const char *level) +{ + char *init = rc_service_resolve(service); + char *conf, *dir; + size_t l; + bool retval; + + dir = dirname(init); + dir = dirname(init); + l = strlen(dir) + strlen(level) + strlen(service) + 10; + conf = xmalloc(sizeof(char) * l); + snprintf(conf, l, "%s/conf.d/%s.%s", dir, service, level); + retval = exists(conf); + free(conf); + free(init); + return retval; +} + +static void +do_stop_services(RC_STRINGLIST *types_nw, RC_STRINGLIST *start_services, + const RC_STRINGLIST *stop_services, const RC_DEPTREE *deptree, + const char *newlevel, bool parallel, bool going_down) +{ + pid_t pid; + RC_STRING *service, *svc1, *svc2; + RC_STRINGLIST *deporder, *tmplist, *kwords; + RC_SERVICE state; + RC_STRINGLIST *nostop; + bool crashed, nstop; + + if (!types_nw) { + types_nw = rc_stringlist_new(); + rc_stringlist_add(types_nw, "needsme"); + rc_stringlist_add(types_nw, "wantsme"); + } + + crashed = rc_conf_yesno("rc_crashed_stop"); + + nostop = rc_stringlist_split(rc_conf_value("rc_nostop"), " "); + TAILQ_FOREACH_REVERSE(service, stop_services, rc_stringlist, entries) + { + state = rc_service_state(service->value); + if (state & RC_SERVICE_STOPPED || state & RC_SERVICE_FAILED) + continue; + + /* Sometimes we don't ever want to stop a service. */ + if (rc_stringlist_find(nostop, service->value)) { + rc_service_mark(service->value, RC_SERVICE_FAILED); + continue; + } + kwords = rc_deptree_depend(deptree, service->value, "keyword"); + if (rc_stringlist_find(kwords, "-stop") || + rc_stringlist_find(kwords, "nostop") || + (going_down && + (rc_stringlist_find(kwords, "-shutdown") || + rc_stringlist_find(kwords, "noshutdown")))) + nstop = true; + else + nstop = false; + rc_stringlist_free(kwords); + if (nstop) { + rc_service_mark(service->value, RC_SERVICE_FAILED); + continue; + } + + /* If the service has crashed, skip futher checks and just stop + it */ + if (crashed && + rc_service_daemons_crashed(service->value)) + goto stop; + + /* If we're in the start list then don't bother stopping us */ + svc1 = rc_stringlist_find(start_services, service->value); + if (svc1) { + if (newlevel && strcmp(runlevel, newlevel) != 0) { + /* So we're in the start list. But we should + * be stopped if we have a runlevel + * configuration file for either the current + * or next so we use the correct one. */ + if (!runlevel_config(service->value,runlevel) && + !runlevel_config(service->value,newlevel)) + continue; + } + else + continue; + } + + /* We got this far. Last check is to see if any any service + * that going to be started depends on us */ + if (!svc1) { + tmplist = rc_stringlist_new(); + rc_stringlist_add(tmplist, service->value); + deporder = rc_deptree_depends(deptree, types_nw, + tmplist, newlevel ? newlevel : runlevel, + RC_DEP_STRICT | RC_DEP_TRACE); + rc_stringlist_free(tmplist); + svc2 = NULL; + TAILQ_FOREACH(svc1, deporder, entries) { + svc2 = rc_stringlist_find(start_services, + svc1->value); + if (svc2) + break; + } + rc_stringlist_free(deporder); + + if (svc2) + continue; + } + +stop: + /* After all that we can finally stop the blighter! */ + pid = service_stop(service->value); + if (pid > 0) { + add_pid(pid); + if (!parallel) { + rc_waitpid(pid); + remove_pid(pid); + } + } + } + + rc_stringlist_free(nostop); +} + +static void +do_start_services(const RC_STRINGLIST *start_services, bool parallel) +{ + RC_STRING *service; + pid_t pid; + bool interactive = false; + RC_SERVICE state; + bool crashed = false; + + if (!rc_yesno(getenv("EINFO_QUIET"))) + interactive = exists(INTERACTIVE); + errno = 0; + crashed = rc_conf_yesno("rc_crashed_start"); + if (errno == ENOENT) + crashed = true; + + TAILQ_FOREACH(service, start_services, entries) { + state = rc_service_state(service->value); + if (state & RC_SERVICE_FAILED) + continue; + if (!(state & RC_SERVICE_STOPPED)) { + if (crashed && + rc_service_daemons_crashed(service->value)) + rc_service_mark(service->value, + RC_SERVICE_STOPPED); + else + continue; + } + if (!interactive) + interactive = want_interactive(); + + if (interactive) { + interactive_retry: + printf("\n"); + einfo("About to start the service %s", + service->value); + eindent(); + einfo("1) Start the service\t\t2) Skip the service"); + einfo("3) Continue boot process\t\t4) Exit to shell"); + eoutdent(); + interactive_option: + switch (read_key(true)) { + case '1': break; + case '2': continue; + case '3': interactive = false; break; + case '4': open_shell(); goto interactive_retry; + default: goto interactive_option; + } + } + + pid = service_start(service->value); + if (pid == -1) + break; + /* Remember the pid if we're running in parallel */ + if (pid > 0) { + add_pid(pid); + if (!parallel) { + rc_waitpid(pid); + remove_pid(pid); + } + } + } + + /* Store our interactive status for boot */ + if (interactive && + (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0 || + strcmp(runlevel, getenv("RC_BOOTLEVEL")) == 0)) + mark_interactive(); + else { + if (exists(INTERACTIVE)) + unlink(INTERACTIVE); + } + +} + +#ifdef RC_DEBUG +static void +handle_bad_signal(int sig) +{ + char pid[10]; + int status; + pid_t crashed_pid = getpid(); + + switch (fork()) { + case -1: + _exit(sig); + /* NOTREACHED */ + case 0: + sprintf(pid, "%i", crashed_pid); + printf("\nAuto launching gdb!\n\n"); + _exit(execlp("gdb", "gdb", "--quiet", "--pid", pid, + "-ex", "bt full", NULL)); + /* NOTREACHED */ + default: + wait(&status); + } + _exit(1); + /* NOTREACHED */ +} +#endif + +int main(int argc, char **argv) +{ + const char *bootlevel = NULL; + char *newlevel = NULL; + const char *systype = NULL; + RC_STRINGLIST *deporder = NULL; + RC_STRINGLIST *tmplist; + RC_STRING *service; + bool going_down = false; + int depoptions = RC_DEP_STRICT | RC_DEP_TRACE; + char krunlevel [PATH_MAX]; + char pidstr[10]; + int opt; + bool parallel; + int regen = 0; + bool nostop = false; +#ifdef __linux__ + char *proc; + char *p; + char *token; +#endif + +#ifdef RC_DEBUG + signal_setup(SIGBUS, handle_bad_signal); + signal_setup(SIGILL, handle_bad_signal); + signal_setup(SIGSEGV, handle_bad_signal); +#endif + + applet = basename_c(argv[0]); + LIST_INIT(&service_pids); + atexit(cleanup); + if (!applet) + eerrorx("arguments required"); + + argc--; + argv++; + + /* Change dir to / to ensure all scripts don't use stuff in pwd */ + if (chdir("/") == -1) + eerror("chdir: %s", strerror(errno)); + + /* Ensure our environment is pure + * Also, add our configuration to it */ + env_filter(); + env_config(); + + /* complain about old configuration settings if they exist */ + if (exists(RC_CONF_OLD)) { + ewarn("%s still exists on your system and should be removed.", + RC_CONF_OLD); + ewarn("Please migrate to the appropriate settings in %s", RC_CONF); + } + + argc++; + argv--; + while ((opt = getopt_long(argc, argv, getoptstring, + longopts, (int *) 0)) != -1) + { + switch (opt) { + case 'n': + nostop = true; + break; + case 'o': + if (*optarg == '\0') + optarg = NULL; + if (!rc_runlevel_exists(optarg)) { + eerror("runlevel `%s' does not exist", optarg); + exit(EXIT_FAILURE); + } + if (!set_krunlevel(optarg)) + exit(EXIT_FAILURE); + einfo("Overriding next runlevel to %s", optarg); + exit(EXIT_SUCCESS); + /* NOTREACHED */ + case 's': + newlevel = rc_service_resolve(optarg); + if (!newlevel) + eerrorx("%s: service `%s' does not exist", + applet, optarg); + argv += optind - 1; + *argv = newlevel; + execv(*argv, argv); + eerrorx("%s: %s", applet, strerror(errno)); + /* NOTREACHED */ + case 'S': + systype = rc_sys(); + if (systype) + printf("%s\n", systype); + exit(EXIT_SUCCESS); + /* NOTREACHED */ + case_RC_COMMON_GETOPT + } + } + + if (strcmp(applet, "rc") == 0) + ewarn("rc is deprecated, please use openrc instead."); + newlevel = argv[optind++]; + /* To make life easier, we only have the shutdown runlevel as + * nothing really needs to know that we're rebooting. + * But for those that do, you can test against RC_REBOOT. */ + if (newlevel) { + if (strcmp(newlevel, "reboot") == 0) { + newlevel = UNCONST(RC_LEVEL_SHUTDOWN); + setenv("RC_REBOOT", "YES", 1); + } + } + + /* Enable logging */ + setenv("EINFO_LOG", "openrc", 1); + + /* Export our PID */ + snprintf(pidstr, sizeof(pidstr), "%d", getpid()); + setenv("RC_PID", pidstr, 1); + + /* Create a list of all services which should be started for the new or + * current runlevel including those in boot, sysinit and hotplugged + * runlevels. Clearly, some of these will already be started so we + * won't actually be starting them all. + */ + bootlevel = getenv("RC_BOOTLEVEL"); + runlevel = rc_runlevel_get(); + + rc_logger_open(newlevel ? newlevel : runlevel); + + /* Setup a signal handler */ + signal_setup(SIGINT, handle_signal); + signal_setup(SIGQUIT, handle_signal); + signal_setup(SIGTERM, handle_signal); + signal_setup(SIGUSR1, handle_signal); + signal_setup(SIGWINCH, handle_signal); + + /* Run any special sysinit foo */ + if (newlevel && strcmp(newlevel, RC_LEVEL_SYSINIT) == 0) { + do_sysinit(); + free(runlevel); + runlevel = rc_runlevel_get(); + } + + rc_plugin_load(); + + /* Now we start handling our children */ + signal_setup(SIGCHLD, handle_signal); + + if (newlevel && + (strcmp(newlevel, RC_LEVEL_SHUTDOWN) == 0 || + strcmp(newlevel, RC_LEVEL_SINGLE) == 0)) + { + going_down = true; + if (!exists(RC_KRUNLEVEL)) + set_krunlevel(runlevel); + rc_runlevel_set(newlevel); + setenv("RC_RUNLEVEL", newlevel, 1); + setenv("RC_GOINGDOWN", "YES", 1); + } else { + /* We should not use krunevel in sysinit or boot runlevels */ + if (!newlevel || + (strcmp(newlevel, RC_LEVEL_SYSINIT) != 0 && + strcmp(newlevel, getenv("RC_BOOTLEVEL")) != 0)) + { + if (get_krunlevel(krunlevel, sizeof(krunlevel))) { + newlevel = krunlevel; + set_krunlevel(NULL); + } + } + + if (newlevel) { + if (strcmp(runlevel, newlevel) != 0 && + !rc_runlevel_exists(newlevel)) + eerrorx("%s: not a valid runlevel", newlevel); + +#ifdef __linux__ + if (strcmp(newlevel, RC_LEVEL_SYSINIT) == 0) { + /* If we requested a runlevel, save it now */ + p = rc_proc_getent("rc_runlevel"); + if (p == NULL) + p = rc_proc_getent("softlevel"); + if (p != NULL) { + set_krunlevel(p); + free(p); + } + } +#endif + } + } + + if (going_down) { +#ifdef __FreeBSD__ + /* FIXME: we shouldn't have todo this */ + /* For some reason, wait_for_services waits for the logger + * proccess to finish as well, but only on FreeBSD. + * We cannot allow this so we stop logging now. */ + rc_logger_close(); +#endif + + rc_plugin_run(RC_HOOK_RUNLEVEL_STOP_IN, newlevel); + } else { + rc_plugin_run(RC_HOOK_RUNLEVEL_STOP_IN, runlevel); + } + hook_out = RC_HOOK_RUNLEVEL_STOP_OUT; + + /* Check if runlevel is valid if we're changing */ + if (newlevel && strcmp(runlevel, newlevel) != 0 && !going_down) { + if (!rc_runlevel_exists(newlevel)) + eerrorx("%s: is not a valid runlevel", newlevel); + } + + /* Load our deptree */ + if ((main_deptree = _rc_deptree_load(0, ®en)) == NULL) + eerrorx("failed to load deptree"); + if (exists(RC_DEPTREE_SKEWED)) + ewarn("WARNING: clock skew detected!"); + + /* Clean the failed services state dir */ + clean_failed(); + + if (mkdir(RC_STOPPING, 0755) != 0) { + if (errno == EACCES) + eerrorx("%s: superuser access required", applet); + eerrorx("%s: failed to create stopping dir `%s': %s", + applet, RC_STOPPING, strerror(errno)); + } + + /* Create a list of all services which we could stop (assuming + * they won't be active in the new or current runlevel) including + * all those services which have been started, are inactive or + * are currently starting. Clearly, some of these will be listed + * in the new or current runlevel so we won't actually be stopping + * them all. + */ + main_stop_services = rc_services_in_state(RC_SERVICE_STARTED); + tmplist = rc_services_in_state(RC_SERVICE_INACTIVE); + TAILQ_CONCAT(main_stop_services, tmplist, entries); + free(tmplist); + tmplist = rc_services_in_state(RC_SERVICE_STARTING); + TAILQ_CONCAT(main_stop_services, tmplist, entries); + free(tmplist); + if (main_stop_services) + rc_stringlist_sort(&main_stop_services); + + main_types_nwua = rc_stringlist_new(); + rc_stringlist_add(main_types_nwua, "ineed"); + rc_stringlist_add(main_types_nwua, "iwant"); + rc_stringlist_add(main_types_nwua, "iuse"); + rc_stringlist_add(main_types_nwua, "iafter"); + + if (main_stop_services) { + tmplist = rc_deptree_depends(main_deptree, main_types_nwua, main_stop_services, + runlevel, depoptions | RC_DEP_STOP); + rc_stringlist_free(main_stop_services); + main_stop_services = tmplist; + } + + /* Create a list of all services which should be started for the new or + * current runlevel including those in boot, sysinit and hotplugged + * runlevels. Clearly, some of these will already be started so we + * won't actually be starting them all. + */ + main_hotplugged_services = rc_services_in_state(RC_SERVICE_HOTPLUGGED); + main_start_services = rc_services_in_runlevel_stacked(newlevel ? + newlevel : runlevel); + if (strcmp(newlevel ? newlevel : runlevel, RC_LEVEL_SHUTDOWN) != 0 && + strcmp(newlevel ? newlevel : runlevel, RC_LEVEL_SYSINIT) != 0) + { + tmplist = rc_services_in_runlevel(RC_LEVEL_SYSINIT); + TAILQ_CONCAT(main_start_services, tmplist, entries); + free(tmplist); + /* If we are NOT headed for the single-user runlevel... */ + if (strcmp(newlevel ? newlevel : runlevel, + RC_LEVEL_SINGLE) != 0) + { + /* If we are NOT headed for the boot runlevel... */ + if (strcmp(newlevel ? newlevel : runlevel, + bootlevel) != 0) + { + tmplist = rc_services_in_runlevel(bootlevel); + TAILQ_CONCAT(main_start_services, tmplist, entries); + free(tmplist); + } + if (main_hotplugged_services) { + TAILQ_FOREACH(service, main_hotplugged_services, + entries) + rc_stringlist_addu(main_start_services, + service->value); + } + } + } + + parallel = rc_conf_yesno("rc_parallel"); + + /* Now stop the services that shouldn't be running */ + if (main_stop_services && !nostop) + do_stop_services(main_types_nw, main_start_services, main_stop_services, main_deptree, newlevel, parallel, going_down); + + /* Wait for our services to finish */ + wait_for_services(); + + /* Notify the plugins we have finished */ + rc_plugin_run(RC_HOOK_RUNLEVEL_STOP_OUT, + going_down ? newlevel : runlevel); + hook_out = 0; + + rmdir(RC_STOPPING); + + /* Store the new runlevel */ + if (newlevel) { + rc_runlevel_set(newlevel); + free(runlevel); + runlevel = xstrdup(newlevel); + setenv("RC_RUNLEVEL", runlevel, 1); + } + +#ifdef __linux__ + /* We can't log beyond this point as the shutdown runlevel + * will mount / readonly. */ + if (strcmp(runlevel, RC_LEVEL_SHUTDOWN) == 0) + rc_logger_close(); +#endif + + mkdir(RC_STARTING, 0755); + rc_plugin_run(RC_HOOK_RUNLEVEL_START_IN, runlevel); + hook_out = RC_HOOK_RUNLEVEL_START_OUT; + + /* Re-add our hotplugged services if they stopped */ + if (main_hotplugged_services) + TAILQ_FOREACH(service, main_hotplugged_services, entries) + rc_service_mark(service->value, RC_SERVICE_HOTPLUGGED); + +#ifdef __linux__ + /* If the "noinit" parameter was passed on the kernel command line then + * mark the specified services as started so they will not be started + * by us. */ + proc = p = rc_proc_getent("noinit"); + if (proc) { + while ((token = strsep(&p, ","))) + rc_service_mark(token, RC_SERVICE_STARTED); + free(proc); + } +#endif + + /* If we have a list of services to start then... */ + if (main_start_services) { + /* Get a list of the chained runlevels which compose the target runlevel */ + RC_STRINGLIST *runlevel_chain = rc_runlevel_stacks(runlevel); + + /* Loop through them in reverse order. */ + RC_STRING *rlevel; + TAILQ_FOREACH_REVERSE(rlevel, runlevel_chain, rc_stringlist, entries) + { + /* Get a list of all the services in that runlevel */ + RC_STRINGLIST *run_services = rc_services_in_runlevel(rlevel->value); + + /* Start those services. */ + rc_stringlist_sort(&run_services); + deporder = rc_deptree_depends(main_deptree, main_types_nwua, run_services, rlevel->value, depoptions | RC_DEP_START); + rc_stringlist_free(run_services); + run_services = deporder; + do_start_services(run_services, parallel); + + /* Wait for our services to finish */ + wait_for_services(); + + /* Free the list of services, we're done with it. */ + rc_stringlist_free(run_services); + } + rc_stringlist_free(runlevel_chain); + } + +#ifdef __linux__ + /* If the "noinit" parameter was passed on the kernel command line then + * mark the specified services as stopped so that our records reflect + * reality. */ + proc = p = rc_proc_getent("noinit"); + if (proc) { + while ((token = strsep(&p, ","))) + rc_service_mark(token, RC_SERVICE_STOPPED); + free(proc); + } + +#endif + + rc_plugin_run(RC_HOOK_RUNLEVEL_START_OUT, runlevel); + hook_out = 0; + + /* If we're in the boot runlevel and we regenerated our dependencies + * we need to delete them so that they are regenerated again in the + * default runlevel as they may depend on things that are now + * available */ + if (regen && strcmp(runlevel, bootlevel) == 0) + unlink(RC_DEPTREE_CACHE); + + return EXIT_SUCCESS; +} diff --git a/src/rc/shell_var.c b/src/rc/shell_var.c new file mode 100644 index 0000000..a13f58e --- /dev/null +++ b/src/rc/shell_var.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2007-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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + int i; + char *p; + int c; + + for (i = 1; i < argc; i++) { + p = argv[i]; + if (i != 1) + putchar(' '); + while (*p) { + c = (unsigned char)*p++; + if (! isalnum(c)) + c = '_'; + putchar(c); + } + } + putchar('\n'); + return EXIT_SUCCESS; +} diff --git a/src/rc/start-stop-daemon.c b/src/rc/start-stop-daemon.c new file mode 100644 index 0000000..c88bc96 --- /dev/null +++ b/src/rc/start-stop-daemon.c @@ -0,0 +1,1392 @@ +/* + start-stop-daemon + * Starts, stops, tests and signals daemons + * + * This is essentially a ground up re-write of Debians + * start-stop-daemon for cleaner code and to integrate into our RC + * system so we can monitor daemons a little. + */ + +/* + * Copyright (c) 2007-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 +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include /* For io priority */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_PAM +#include + +/* We are not supporting authentication conversations */ +static struct pam_conv conv = { NULL, NULL}; +#endif + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-misc.h" +#include "_usage.h" +#include "helpers.h" + +const char *applet = NULL; +const char *extraopts = NULL; +const char *getoptstring = "I:KN:PR:Sa:bc:d:e:g:ik:mn:op:s:tu:r:w:x:1:2:" \ + getoptstring_COMMON; +const struct option longopts[] = { + { "ionice", 1, NULL, 'I'}, + { "stop", 0, NULL, 'K'}, + { "nicelevel", 1, NULL, 'N'}, + { "retry", 1, NULL, 'R'}, + { "start", 0, NULL, 'S'}, + { "startas", 1, NULL, 'a'}, + { "background", 0, NULL, 'b'}, + { "chuid", 1, NULL, 'c'}, + { "chdir", 1, NULL, 'd'}, + { "env", 1, NULL, 'e'}, + { "umask", 1, NULL, 'k'}, + { "group", 1, NULL, 'g'}, + { "interpreted", 0, NULL, 'i'}, + { "make-pidfile", 0, NULL, 'm'}, + { "name", 1, NULL, 'n'}, + { "oknodo", 0, NULL, 'o'}, + { "pidfile", 1, NULL, 'p'}, + { "signal", 1, NULL, 's'}, + { "test", 0, NULL, 't'}, + { "user", 1, NULL, 'u'}, + { "chroot", 1, NULL, 'r'}, + { "wait", 1, NULL, 'w'}, + { "exec", 1, NULL, 'x'}, + { "stdout", 1, NULL, '1'}, + { "stderr", 1, NULL, '2'}, + { "progress", 0, NULL, 'P'}, + longopts_COMMON +}; +const char * const longopts_help[] = { + "Set an ionice class:data when starting", + "Stop daemon", + "Set a nicelevel when starting", + "Retry schedule to use when stopping", + "Start daemon", + "deprecated, use --exec or --name", + "Force daemon to background", + "deprecated, use --user", + "Change the PWD", + "Set an environment string", + "Set the umask for the daemon", + "Change the process group", + "Match process name by interpreter", + "Create a pidfile", + "Match process name", + "deprecated", + "Match pid found in this file", + "Send a different signal", + "Test actions, don't do them", + "Change the process user", + "Chroot to this directory", + "Milliseconds to wait for daemon start", + "Binary to start/stop", + "Redirect stdout to file", + "Redirect stderr to file", + "Print dots each second while waiting", + longopts_help_COMMON +}; +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; + +extern char **environ; + +#if !defined(SYS_ioprio_set) && defined(__NR_ioprio_set) +# define SYS_ioprio_set __NR_ioprio_set +#endif +#if !defined(__DragonFly__) +static inline int ioprio_set(int which _unused, + int who _unused, + int ioprio _unused) +{ +#ifdef SYS_ioprio_set + return syscall(SYS_ioprio_set, which, who, ioprio); +#else + return 0; +#endif +} +#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) +{ + free(changeuser); + free(nav); + 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) +{ + int status; + int serrno = errno; + char signame[10] = { '\0' }; + + switch (sig) { + case SIGINT: + if (!signame[0]) + snprintf(signame, sizeof(signame), "SIGINT"); + /* FALLTHROUGH */ + case SIGTERM: + if (!signame[0]) + snprintf(signame, sizeof(signame), "SIGTERM"); + /* FALLTHROUGH */ + case SIGQUIT: + if (!signame[0]) + snprintf(signame, sizeof(signame), "SIGQUIT"); + eerrorx("%s: caught %s, aborting", applet, signame); + /* NOTREACHED */ + + case SIGCHLD: + for (;;) { + if (waitpid(-1, &status, WNOHANG) < 0) { + if (errno != ECHILD) + eerror("%s: waitpid: %s", + applet, strerror(errno)); + break; + } + } + break; + + default: + eerror("%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 (!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; +} + +int main(int argc, char **argv) +{ + int devnull_fd = -1; +#ifdef TIOCNOTTY + int tty_fd = -1; +#endif + +#ifdef HAVE_PAM + pam_handle_t *pamh = NULL; + int pamr; + const char *const *pamenv = NULL; +#endif + + int opt; + bool start = false; + bool stop = false; + bool oknodo = false; + bool test = false; + char *exec = NULL; + char *startas = NULL; + char *name = NULL; + char *pidfile = NULL; + char *retry = NULL; + int sig = -1; + int nicelevel = 0, ionicec = -1, ioniced = 0; + bool background = false; + bool makepidfile = false; + bool interpreted = false; + bool progress = false; + uid_t uid = 0; + gid_t gid = 0; + char *home = NULL; + int tid = 0; + char *redirect_stderr = NULL; + char *redirect_stdout = NULL; + int stdin_fd; + int stdout_fd; + int stderr_fd; + pid_t pid, spid; + int i; + char *svcname = getenv("RC_SVCNAME"); + RC_STRINGLIST *env_list; + RC_STRING *env; + char *tmp, *newpath, *np; + char *p; + char *token; + char exec_file[PATH_MAX]; + struct passwd *pw; + struct group *gr; + char line[130]; + FILE *fp; + size_t len; + mode_t numask = 022; + char **margv; + unsigned int start_wait = 0; + + applet = basename_c(argv[0]); + TAILQ_INIT(&schedule); + atexit(cleanup); + + signal_setup(SIGINT, handle_signal); + signal_setup(SIGQUIT, handle_signal); + signal_setup(SIGTERM, handle_signal); + + if ((tmp = getenv("SSD_NICELEVEL"))) + if (sscanf(tmp, "%d", &nicelevel) != 1) + eerror("%s: invalid nice level `%s' (SSD_NICELEVEL)", + applet, tmp); + if ((tmp = getenv("SSD_IONICELEVEL"))) { + int n = sscanf(tmp, "%d:%d", &ionicec, &ioniced); + if (n != 1 && n != 2) + eerror("%s: invalid ionice level `%s' (SSD_IONICELEVEL)", + applet, tmp); + if (ionicec == 0) + ioniced = 0; + else if (ionicec == 3) + ioniced = 7; + ionicec <<= 13; /* class shift */ + } + + /* Get our user name and initial dir */ + p = getenv("USER"); + home = getenv("HOME"); + if (home == NULL || p == NULL) { + pw = getpwuid(getuid()); + if (pw != NULL) { + if (p == NULL) + setenv("USER", pw->pw_name, 1); + if (home == NULL) { + setenv("HOME", pw->pw_dir, 1); + home = pw->pw_dir; + } + } + } + + while ((opt = getopt_long(argc, argv, getoptstring, longopts, + (int *) 0)) != -1) + switch (opt) { + case 'I': /* --ionice */ + if (sscanf(optarg, "%d:%d", &ionicec, &ioniced) == 0) + eerrorx("%s: invalid ionice `%s'", + applet, optarg); + if (ionicec == 0) + ioniced = 0; + else if (ionicec == 3) + ioniced = 7; + ionicec <<= 13; /* class shift */ + break; + + case 'K': /* --stop */ + stop = true; + break; + + case 'N': /* --nice */ + if (sscanf(optarg, "%d", &nicelevel) != 1) + eerrorx("%s: invalid nice level `%s'", + applet, optarg); + break; + + case 'P': /* --progress */ + progress = true; + break; + + case 'R': /* --retry | */ + retry = optarg; + break; + + case 'S': /* --start */ + start = true; + break; + + case 'b': /* --background */ + background = true; + break; + + case 'c': /* --chuid | */ + /* DEPRECATED */ + ewarn("WARNING: -c/--chuid is deprecated and will be removed in the future, please use -u/--user instead"); + case 'u': /* --user | */ + { + p = optarg; + tmp = strsep(&p, ":"); + changeuser = xstrdup(tmp); + if (sscanf(tmp, "%d", &tid) != 1) + pw = getpwnam(tmp); + else + pw = getpwuid((uid_t)tid); + + if (pw == NULL) + eerrorx("%s: user `%s' not found", + applet, tmp); + uid = pw->pw_uid; + home = pw->pw_dir; + unsetenv("HOME"); + if (pw->pw_dir) + setenv("HOME", pw->pw_dir, 1); + unsetenv("USER"); + if (pw->pw_name) + setenv("USER", pw->pw_name, 1); + if (gid == 0) + gid = pw->pw_gid; + + if (p) { + tmp = strsep (&p, ":"); + if (sscanf(tmp, "%d", &tid) != 1) + gr = getgrnam(tmp); + else + gr = getgrgid((gid_t) tid); + + if (gr == NULL) + eerrorx("%s: group `%s'" + " not found", + applet, tmp); + gid = gr->gr_gid; + } + } + break; + + case 'd': /* --chdir /new/dir */ + ch_dir = optarg; + break; + + case 'e': /* --env */ + putenv(optarg); + break; + + case 'g': /* --group | */ + if (sscanf(optarg, "%d", &tid) != 1) + gr = getgrnam(optarg); + else + gr = getgrgid((gid_t)tid); + if (gr == NULL) + eerrorx("%s: group `%s' not found", + applet, optarg); + gid = gr->gr_gid; + break; + + case 'i': /* --interpreted */ + interpreted = true; + break; + + case 'k': + if (parse_mode(&numask, optarg)) + eerrorx("%s: invalid mode `%s'", + applet, optarg); + break; + + case 'm': /* --make-pidfile */ + makepidfile = true; + break; + + case 'n': /* --name */ + name = optarg; + break; + + case 'o': /* --oknodo */ + /* DEPRECATED */ + ewarn("WARNING: -o/--oknodo is deprecated and will be removed in the future"); + oknodo = true; + break; + + case 'p': /* --pidfile */ + pidfile = optarg; + break; + + case 's': /* --signal */ + sig = parse_signal(optarg); + break; + + case 't': /* --test */ + test = true; + break; + + case 'r': /* --chroot /new/root */ + ch_root = optarg; + break; + + case 'a': /* --startas */ + /* DEPRECATED */ + ewarn("WARNING: -a/--startas is deprecated and will be removed in the future, please use -x/--exec or -n/--name instead"); + startas = optarg; + break; + case 'w': + if (sscanf(optarg, "%d", &start_wait) != 1) + eerrorx("%s: `%s' not a number", + applet, optarg); + break; + case 'x': /* --exec */ + exec = optarg; + break; + + case '1': /* --stdout /path/to/stdout.lgfile */ + redirect_stdout = optarg; + break; + + case '2': /* --stderr /path/to/stderr.logfile */ + redirect_stderr = optarg; + break; + + case_RC_COMMON_GETOPT + } + + endpwent(); + argc -= optind; + argv += optind; + + /* Allow start-stop-daemon --signal HUP --exec /usr/sbin/dnsmasq + * instead of forcing --stop --oknodo as well */ + if (!start && + !stop && + sig != SIGINT && + sig != SIGTERM && + sig != SIGQUIT && + sig != SIGKILL) + oknodo = true; + + if (!exec) + exec = startas; + else if (!name) + name = startas; + + if (!exec) { + exec = *argv; + if (!exec) + exec = name; + if (name && start) + *argv = name; + } else if (name) { + *--argv = name; + ++argc; + } else if (exec) { + *--argv = exec; + ++argc; + }; + + if (stop || sig != -1) { + if (sig == -1) + sig = SIGTERM; + if (!*argv && !pidfile && !name && !uid) + eerrorx("%s: --stop needs --exec, --pidfile," + " --name or --user", applet); + if (background) + eerrorx("%s: --background is only relevant with" + " --start", applet); + if (makepidfile) + eerrorx("%s: --make-pidfile is only relevant with" + " --start", applet); + if (redirect_stdout || redirect_stderr) + eerrorx("%s: --stdout and --stderr are only relevant" + " with --start", applet); + if (start_wait) + ewarn("using --wait with --stop has no effect," + " use --retry instead"); + } else { + if (!exec) + eerrorx("%s: nothing to start", applet); + if (makepidfile && !pidfile) + eerrorx("%s: --make-pidfile is only relevant with" + " --pidfile", applet); + if ((redirect_stdout || redirect_stderr) && !background) + eerrorx("%s: --stdout and --stderr are only relevant" + " with --background", applet); + } + + /* 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); + } + } + if (start && !exists(exec_file)) { + eerror("%s: %s does not exist", applet, + *exec_file ? exec_file : exec); + exit(EXIT_FAILURE); + + } + if (start && retry) + ewarn("using --retry with --start has no effect," + " use --wait instead"); + + /* If we don't have a pidfile we should check if it's interpreted + * or not. If it we, we need to pass the interpreter through + * to our daemon calls to find it correctly. */ + if (interpreted && !pidfile) { + fp = fopen(exec_file, "r"); + if (fp) { + p = fgets(line, sizeof(line), fp); + fclose(fp); + if (p != NULL && line[0] == '#' && line[1] == '!') { + p = line + 2; + /* Strip leading spaces */ + while (*p == ' ' || *p == '\t') + p++; + /* Remove the trailing newline */ + len = strlen(p) - 1; + if (p[len] == '\n') + p[len] = '\0'; + token = strsep(&p, " "); + strncpy(exec_file, token, sizeof(exec_file)); + opt = 0; + for (nav = argv; *nav; nav++) + opt++; + nav = xmalloc(sizeof(char *) * (opt + 3)); + nav[0] = exec_file; + len = 1; + if (p) + nav[len++] = p; + for (i = 0; i < opt; i++) + nav[i + len] = argv[i]; + nav[i + len] = '\0'; + } + } + } + margv = nav ? nav : argv; + + if (stop || sig != -1) { + if (sig == -1) + sig = SIGTERM; + if (!stop) + oknodo = true; + if (retry) + parse_schedule(retry, sig); + else if (test || oknodo) + parse_schedule("0", sig); + else + parse_schedule(NULL, sig); + i = run_stop_schedule(exec, (const char *const *)margv, + pidfile, uid, test, progress); + + if (i < 0) + /* We failed to stop something */ + exit(EXIT_FAILURE); + if (test || oknodo) + return i > 0 ? EXIT_SUCCESS : EXIT_FAILURE; + + /* Even if we have not actually killed anything, we should + * remove information about it as it may have unexpectedly + * crashed out. We should also return success as the end + * result would be the same. */ + if (pidfile && exists(pidfile)) + unlink(pidfile); + if (svcname) + rc_service_daemon_set(svcname, exec, + (const char *const *)argv, + pidfile, false); + exit(EXIT_SUCCESS); + } + + if (pidfile) + pid = get_pid(pidfile); + else + pid = 0; + + if (do_stop(exec, (const char * const *)margv, pid, uid, + 0, test) > 0) + eerrorx("%s: %s is already running", applet, exec); + + if (test) { + if (rc_yesno(getenv("EINFO_QUIET"))) + exit (EXIT_SUCCESS); + + einfon("Would start"); + while (argc-- > 0) + printf(" %s", *argv++); + printf("\n"); + eindent(); + if (uid != 0) + einfo("as user id %d", uid); + if (gid != 0) + einfo("as group id %d", gid); + if (ch_root) + einfo("in root `%s'", ch_root); + if (ch_dir) + einfo("in dir `%s'", ch_dir); + if (nicelevel != 0) + einfo("with a priority of %d", nicelevel); + if (name) + einfo ("with a process name of %s", name); + eoutdent(); + exit(EXIT_SUCCESS); + } + + ebeginv("Detaching to start `%s'", exec); + eindentv(); + + /* Remove existing pidfile */ + if (pidfile) + unlink(pidfile); + + if (background) + signal_setup(SIGCHLD, handle_signal); + + if ((pid = fork()) == -1) + eerrorx("%s: fork: %s", applet, strerror(errno)); + + /* Child process - lets go! */ + if (pid == 0) { + pid_t mypid = getpid(); + umask(numask); + +#ifdef TIOCNOTTY + tty_fd = open("/dev/tty", O_RDWR); +#endif + + devnull_fd = open("/dev/null", O_RDWR); + + if (nicelevel) { + if (setpriority(PRIO_PROCESS, mypid, nicelevel) == -1) + eerrorx("%s: setpritory %d: %s", + applet, nicelevel, + strerror(errno)); + } + + if (ionicec != -1 && + ioprio_set(1, mypid, ionicec | ioniced) == -1) + eerrorx("%s: ioprio_set %d %d: %s", applet, + ionicec, ioniced, strerror(errno)); + + if (ch_root && chroot(ch_root) < 0) + eerrorx("%s: chroot `%s': %s", + applet, ch_root, strerror(errno)); + + if (ch_dir && chdir(ch_dir) < 0) + eerrorx("%s: chdir `%s': %s", + applet, ch_dir, strerror(errno)); + + if (makepidfile && pidfile) { + fp = fopen(pidfile, "w"); + if (! fp) + eerrorx("%s: fopen `%s': %s", applet, pidfile, + strerror(errno)); + fprintf(fp, "%d\n", mypid); + fclose(fp); + } + +#ifdef HAVE_PAM + if (changeuser != NULL) { + pamr = pam_start("start-stop-daemon", + changeuser, &conv, &pamh); + + if (pamr == PAM_SUCCESS) + pamr = pam_acct_mgmt(pamh, PAM_SILENT); + if (pamr == PAM_SUCCESS) + pamr = pam_open_session(pamh, PAM_SILENT); + if (pamr != PAM_SUCCESS) + eerrorx("%s: pam error: %s", + applet, pam_strerror(pamh, pamr)); + } +#endif + + if (gid && setgid(gid)) + eerrorx("%s: unable to set groupid to %d", + applet, gid); + if (changeuser && initgroups(changeuser, gid)) + eerrorx("%s: initgroups (%s, %d)", + applet, changeuser, gid); + if (uid && setuid(uid)) + eerrorx ("%s: unable to set userid to %d", + applet, uid); + + /* Close any fd's to the passwd database */ + endpwent(); + +#ifdef TIOCNOTTY + ioctl(tty_fd, TIOCNOTTY, 0); + close(tty_fd); +#endif + + /* Clean the environment of any RC_ variables */ + env_list = rc_stringlist_new(); + i = 0; + while (environ[i]) + rc_stringlist_add(env_list, environ[i++]); + +#ifdef HAVE_PAM + if (changeuser != NULL) { + pamenv = (const char *const *)pam_getenvlist(pamh); + if (pamenv) { + while (*pamenv) { + /* Don't add strings unless they set a var */ + if (strchr(*pamenv, '=')) + putenv(xstrdup(*pamenv)); + else + unsetenv(*pamenv); + pamenv++; + } + } + } +#endif + + TAILQ_FOREACH(env, env_list, entries) { + if ((strncmp(env->value, "RC_", 3) == 0 && + strncmp(env->value, "RC_SERVICE=", 10) != 0 && + strncmp(env->value, "RC_SVCNAME=", 10) != 0) || + strncmp(env->value, "SSD_NICELEVEL=", 14) == 0 || + strncmp(env->value, "SSD_IONICELEVEL=", 16) == 0) + { + p = strchr(env->value, '='); + *p = '\0'; + unsetenv(env->value); + continue; + } + } + rc_stringlist_free(env_list); + + /* For the path, remove the rcscript bin dir from it */ + if ((token = getenv("PATH"))) { + len = strlen(token); + newpath = np = xmalloc(len + 1); + while (token && *token) { + p = strchr(token, ':'); + if (p) { + *p++ = '\0'; + while (*p == ':') + p++; + } + if (strcmp(token, RC_LIBEXECDIR "/bin") != 0 && + strcmp(token, RC_LIBEXECDIR "/sbin") != 0) + { + len = strlen(token); + if (np != newpath) + *np++ = ':'; + memcpy(np, token, len); + np += len; + } + token = p; + } + *np = '\0'; + unsetenv("PATH"); + setenv("PATH", newpath, 1); + } + + stdin_fd = devnull_fd; + stdout_fd = devnull_fd; + stderr_fd = devnull_fd; + if (redirect_stdout) { + if ((stdout_fd = open(redirect_stdout, + O_WRONLY | O_CREAT | O_APPEND, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) + eerrorx("%s: unable to open the logfile" + " for stdout `%s': %s", + applet, redirect_stdout, strerror(errno)); + } + if (redirect_stderr) { + if ((stderr_fd = open(redirect_stderr, + O_WRONLY | O_CREAT | O_APPEND, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) + eerrorx("%s: unable to open the logfile" + " for stderr `%s': %s", + applet, redirect_stderr, strerror(errno)); + } + + if (background) + dup2(stdin_fd, STDIN_FILENO); + if (background || redirect_stdout || rc_yesno(getenv("EINFO_QUIET"))) + dup2(stdout_fd, STDOUT_FILENO); + if (background || redirect_stderr || rc_yesno(getenv("EINFO_QUIET"))) + dup2(stderr_fd, STDERR_FILENO); + + for (i = getdtablesize() - 1; i >= 3; --i) + close(i); + + setsid(); + execvp(exec, argv); +#ifdef HAVE_PAM + if (changeuser != NULL && pamr == PAM_SUCCESS) + pam_close_session(pamh, PAM_SILENT); +#endif + eerrorx("%s: failed to exec `%s': %s", + applet, exec,strerror(errno)); + } + + /* Parent process */ + if (!background) { + /* As we're not backgrounding the process, wait for our pid + * to return */ + i = 0; + spid = pid; + + do { + pid = waitpid(spid, &i, 0); + if (pid < 1) { + eerror("waitpid %d: %s", + spid, strerror(errno)); + return -1; + } + } while (!WIFEXITED(i) && !WIFSIGNALED(i)); + if (!WIFEXITED(i) || WEXITSTATUS(i) != 0) { + eerror("%s: failed to start `%s'", applet, exec); + exit(EXIT_FAILURE); + } + pid = spid; + } + + /* Wait a little bit and check that process is still running + We do this as some badly written daemons fork and then barf */ + if (start_wait == 0 && + ((p = getenv("SSD_STARTWAIT")) || + (p = rc_conf_value("rc_start_wait")))) + { + if (sscanf(p, "%u", &start_wait) != 1) + start_wait = 0; + } + + if (start_wait > 0) { + struct timespec ts; + bool alive = false; + + ts.tv_sec = start_wait / 1000; + ts.tv_nsec = (start_wait % 1000) * ONE_MS; + if (nanosleep(&ts, NULL) == -1) { + if (errno == EINTR) + eerror("%s: caught an interrupt", applet); + else { + eerror("%s: nanosleep: %s", + applet, strerror(errno)); + return 0; + } + } + if (background) { + if (kill(pid, 0) == 0) + alive = true; + } else { + if (pidfile) { + pid = get_pid(pidfile); + if (pid == -1) { + eerrorx("%s: did not " + "create a valid" + " pid in `%s'", + applet, pidfile); + } + } else + pid = 0; + if (do_stop(exec, (const char *const *)margv, + pid, uid, 0, test) > 0) + alive = true; + } + + if (!alive) + eerrorx("%s: %s died", applet, exec); + } + + if (svcname) + rc_service_daemon_set(svcname, exec, + (const char *const *)margv, pidfile, true); + + exit(EXIT_SUCCESS); + /* NOTREACHED */ +} diff --git a/src/rc/start-stop-daemon.pam b/src/rc/start-stop-daemon.pam new file mode 100644 index 0000000..a1bada2 --- /dev/null +++ b/src/rc/start-stop-daemon.pam @@ -0,0 +1,6 @@ +#%PAM-1.0 + +auth required pam_permit.so +account required pam_permit.so +password required pam_deny.so +session optional pam_limits.so diff --git a/src/rc/supervise-daemon.c b/src/rc/supervise-daemon.c new file mode 100644 index 0000000..e3e534f --- /dev/null +++ b/src/rc/supervise-daemon.c @@ -0,0 +1,808 @@ +/* + * supervise-daemon + * This is an experimental supervisor for daemons. + * It will start a deamon and make sure it restarts if it crashes. + */ + +/* + * Copyright (c) 2016 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 +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +#include /* For io priority */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_PAM +#include + +/* We are not supporting authentication conversations */ +static struct pam_conv conv = { NULL, NULL}; +#endif + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-misc.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:" \ + getoptstring_COMMON; +const struct option longopts[] = { + { "respawn-delay", 1, NULL, 'D'}, + { "chdir", 1, NULL, 'd'}, + { "env", 1, NULL, 'e'}, + { "group", 1, NULL, 'g'}, + { "ionice", 1, NULL, 'I'}, + { "stop", 0, NULL, 'K'}, + { "umask", 1, NULL, 'k'}, + { "respawn-max", 1, NULL, 'm'}, + { "nicelevel", 1, NULL, 'N'}, + { "pidfile", 1, NULL, 'p'}, + { "respawn-period", 1, NULL, 'P'}, + { "chroot", 1, NULL, 'r'}, + { "start", 0, NULL, 'S'}, + { "user", 1, NULL, 'u'}, + { "stdout", 1, NULL, '1'}, + { "stderr", 1, NULL, '2'}, + longopts_COMMON +}; +const char * const longopts_help[] = { + "Set a respawn delay", + "Change the PWD", + "Set an environment string", + "Change the process group", + "Set an ionice class:data when starting", + "Stop daemon", + "Set the umask for the daemon", + "set maximum number of respawn attempts", + "Set a nicelevel when starting", + "Match pid found in this file", + "Set respawn time period", + "Chroot to this directory", + "Start daemon", + "Change the process user", + "Redirect stdout to file", + "Redirect stderr to file", + longopts_help_COMMON +}; +const char *usagestring = NULL; + +static int nicelevel = 0; +static int ionicec = -1; +static int ioniced = 0; +static char *changeuser, *ch_root, *ch_dir; +static uid_t uid = 0; +static gid_t gid = 0; +static int devnull_fd = -1; +static int stdin_fd; +static int stdout_fd; +static int stderr_fd; +static char *redirect_stderr = NULL; +static char *redirect_stdout = NULL; +static bool exiting = false; +#ifdef TIOCNOTTY +static int tty_fd = -1; +#endif + +extern char **environ; + +#if !defined(SYS_ioprio_set) && defined(__NR_ioprio_set) +# define SYS_ioprio_set __NR_ioprio_set +#endif +#if !defined(__DragonFly__) +static inline int ioprio_set(int which _unused, int who _unused, + int ioprio _unused) +{ +#ifdef SYS_ioprio_set + return syscall(SYS_ioprio_set, which, who, ioprio); +#else + return 0; +#endif +} +#endif + +static void cleanup(void) +{ + free(changeuser); +} + +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; +} + +static void child_process(char *exec, char **argv, char *svcname, + int start_count) +{ + RC_STRINGLIST *env_list; + RC_STRING *env; + int i; + char *p; + char *token; + size_t len; + char *newpath; + char *np; + char **c; + char cmdline[PATH_MAX]; + time_t start_time; + char start_count_string[20]; + char start_time_string[20]; + +#ifdef HAVE_PAM + pam_handle_t *pamh = NULL; + int pamr; + const char *const *pamenv = NULL; +#endif + + 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", start_count); + rc_service_value_set(svcname, "start_count", start_count_string); + } + + if (nicelevel) { + if (setpriority(PRIO_PROCESS, getpid(), nicelevel) == -1) + eerrorx("%s: setpriority %d: %s", applet, nicelevel, + strerror(errno)); + } + + if (ionicec != -1 && ioprio_set(1, getpid(), ionicec | ioniced) == -1) + eerrorx("%s: ioprio_set %d %d: %s", applet, ionicec, ioniced, + strerror(errno)); + + if (ch_root && chroot(ch_root) < 0) + eerrorx("%s: chroot `%s': %s", applet, ch_root, strerror(errno)); + + if (ch_dir && chdir(ch_dir) < 0) + eerrorx("%s: chdir `%s': %s", applet, ch_dir, strerror(errno)); + +#ifdef HAVE_PAM + if (changeuser != NULL) { + pamr = pam_start("supervise-daemon", + changeuser, &conv, &pamh); + + if (pamr == PAM_SUCCESS) + pamr = pam_acct_mgmt(pamh, PAM_SILENT); + if (pamr == PAM_SUCCESS) + pamr = pam_open_session(pamh, PAM_SILENT); + if (pamr != PAM_SUCCESS) + eerrorx("%s: pam error: %s", applet, pam_strerror(pamh, pamr)); + } +#endif + + if (gid && setgid(gid)) + eerrorx("%s: unable to set groupid to %d", applet, gid); + if (changeuser && initgroups(changeuser, gid)) + eerrorx("%s: initgroups (%s, %d)", applet, changeuser, gid); + if (uid && setuid(uid)) + eerrorx ("%s: unable to set userid to %d", applet, uid); + + /* Close any fd's to the passwd database */ + endpwent(); + + /* remove the controlling tty */ +#ifdef TIOCNOTTY + ioctl(tty_fd, TIOCNOTTY, 0); + close(tty_fd); +#endif + + /* Clean the environment of any RC_ variables */ + env_list = rc_stringlist_new(); + i = 0; + while (environ[i]) + rc_stringlist_add(env_list, environ[i++]); + +#ifdef HAVE_PAM + if (changeuser != NULL) { + pamenv = (const char *const *)pam_getenvlist(pamh); + if (pamenv) { + while (*pamenv) { + /* Don't add strings unless they set a var */ + if (strchr(*pamenv, '=')) + putenv(xstrdup(*pamenv)); + else + unsetenv(*pamenv); + pamenv++; + } + } + } +#endif + + TAILQ_FOREACH(env, env_list, entries) { + if ((strncmp(env->value, "RC_", 3) == 0 && + strncmp(env->value, "RC_SERVICE=", 10) != 0 && + strncmp(env->value, "RC_SVCNAME=", 10) != 0) || + strncmp(env->value, "SSD_NICELEVEL=", 14) == 0) + { + p = strchr(env->value, '='); + *p = '\0'; + unsetenv(env->value); + continue; + } + } + rc_stringlist_free(env_list); + + /* For the path, remove the rcscript bin dir from it */ + if ((token = getenv("PATH"))) { + len = strlen(token); + newpath = np = xmalloc(len + 1); + while (token && *token) { + p = strchr(token, ':'); + if (p) { + *p++ = '\0'; + while (*p == ':') + p++; + } + if (strcmp(token, RC_LIBEXECDIR "/bin") != 0 && + strcmp(token, RC_LIBEXECDIR "/sbin") != 0) + { + len = strlen(token); + if (np != newpath) + *np++ = ':'; + memcpy(np, token, len); + np += len; + } + token = p; + } + *np = '\0'; + unsetenv("PATH"); + setenv("PATH", newpath, 1); + } + + stdin_fd = devnull_fd; + stdout_fd = devnull_fd; + stderr_fd = devnull_fd; + if (redirect_stdout) { + if ((stdout_fd = open(redirect_stdout, + O_WRONLY | O_CREAT | O_APPEND, + S_IRUSR | S_IWUSR)) == -1) + eerrorx("%s: unable to open the logfile" + " for stdout `%s': %s", + applet, redirect_stdout, strerror(errno)); + } + if (redirect_stderr) { + if ((stderr_fd = open(redirect_stderr, + O_WRONLY | O_CREAT | O_APPEND, + S_IRUSR | S_IWUSR)) == -1) + eerrorx("%s: unable to open the logfile" + " for stderr `%s': %s", + applet, redirect_stderr, strerror(errno)); + } + + dup2(stdin_fd, STDIN_FILENO); + if (redirect_stdout || rc_yesno(getenv("EINFO_QUIET"))) + dup2(stdout_fd, STDOUT_FILENO); + if (redirect_stderr || rc_yesno(getenv("EINFO_QUIET"))) + dup2(stderr_fd, STDERR_FILENO); + + 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); + execvp(exec, argv); + +#ifdef HAVE_PAM + if (changeuser != NULL && pamr == PAM_SUCCESS) + pam_close_session(pamh, PAM_SILENT); +#endif + eerrorx("%s: failed to exec `%s': %s", applet, exec,strerror(errno)); +} + +static void handle_signal(int sig) +{ + int serrno = errno; + char signame[10] = { '\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; + } + + 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 (!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; +} + +int main(int argc, char **argv) +{ + int opt; + bool start = false; + bool stop = false; + char *exec = NULL; + char *pidfile = NULL; + char *home = NULL; + int tid = 0; + pid_t child_pid, pid; + char *svcname = getenv("RC_SVCNAME"); + 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; + struct passwd *pw; + struct group *gr; + FILE *fp; + mode_t numask = 022; + + applet = basename_c(argv[0]); + atexit(cleanup); + + signal_setup(SIGINT, handle_signal); + signal_setup(SIGQUIT, handle_signal); + signal_setup(SIGTERM, handle_signal); + openlog(applet, LOG_PID, LOG_DAEMON); + + if ((tmp = getenv("SSD_NICELEVEL"))) + if (sscanf(tmp, "%d", &nicelevel) != 1) + eerror("%s: invalid nice level `%s' (SSD_NICELEVEL)", + applet, tmp); + + /* Get our user name and initial dir */ + p = getenv("USER"); + home = getenv("HOME"); + if (home == NULL || p == NULL) { + pw = getpwuid(getuid()); + if (pw != NULL) { + if (p == NULL) + setenv("USER", pw->pw_name, 1); + if (home == NULL) { + setenv("HOME", pw->pw_dir, 1); + home = pw->pw_dir; + } + } + } + + while ((opt = getopt_long(argc, argv, getoptstring, longopts, + (int *) 0)) != -1) + switch (opt) { + case 'D': /* --respawn-delay time */ + n = sscanf(optarg, "%d", &respawn_delay); + if (n != 1 || respawn_delay < 1) + eerrorx("Invalid respawn-delay value '%s'", optarg); + break; + + case 'I': /* --ionice */ + if (sscanf(optarg, "%d:%d", &ionicec, &ioniced) == 0) + eerrorx("%s: invalid ionice `%s'", + applet, optarg); + if (ionicec == 0) + ioniced = 0; + else if (ionicec == 3) + ioniced = 7; + ionicec <<= 13; /* class shift */ + break; + + case 'K': /* --stop */ + stop = true; + break; + + case 'N': /* --nice */ + if (sscanf(optarg, "%d", &nicelevel) != 1) + eerrorx("%s: invalid nice level `%s'", + applet, optarg); + break; + + case 'P': /* --respawn-period time */ + n = sscanf(optarg, "%d", &respawn_period); + if (n != 1 || respawn_delay < 1) + eerrorx("Invalid respawn-delay value '%s'", optarg); + break; + + case 'S': /* --start */ + start = true; + break; + + case 'd': /* --chdir /new/dir */ + ch_dir = optarg; + break; + + case 'e': /* --env */ + putenv(optarg); + break; + + case 'g': /* --group | */ + if (sscanf(optarg, "%d", &tid) != 1) + gr = getgrnam(optarg); + else + gr = getgrgid((gid_t)tid); + if (gr == NULL) + eerrorx("%s: group `%s' not found", + applet, optarg); + gid = gr->gr_gid; + break; + + case 'k': + if (parse_mode(&numask, optarg)) + eerrorx("%s: invalid mode `%s'", + applet, optarg); + break; + + case 'm': /* --respawn-max count */ + n = sscanf(optarg, "%d", &respawn_max); + if (n != 1 || respawn_max < 1) + eerrorx("Invalid respawn-max value '%s'", optarg); + break; + + case 'p': /* --pidfile */ + pidfile = optarg; + break; + + case 'r': /* --chroot /new/root */ + ch_root = optarg; + break; + + case 'u': /* --user | */ + { + p = optarg; + tmp = strsep(&p, ":"); + changeuser = xstrdup(tmp); + if (sscanf(tmp, "%d", &tid) != 1) + pw = getpwnam(tmp); + else + pw = getpwuid((uid_t)tid); + + if (pw == NULL) + eerrorx("%s: user `%s' not found", + applet, tmp); + uid = pw->pw_uid; + home = pw->pw_dir; + unsetenv("HOME"); + if (pw->pw_dir) + setenv("HOME", pw->pw_dir, 1); + unsetenv("USER"); + if (pw->pw_name) + setenv("USER", pw->pw_name, 1); + if (gid == 0) + gid = pw->pw_gid; + + if (p) { + tmp = strsep (&p, ":"); + if (sscanf(tmp, "%d", &tid) != 1) + gr = getgrnam(tmp); + else + gr = getgrgid((gid_t) tid); + + if (gr == NULL) + eerrorx("%s: group `%s'" + " not found", + applet, tmp); + gid = gr->gr_gid; + } + } + break; + + case '1': /* --stdout /path/to/stdout.lgfile */ + redirect_stdout = optarg; + break; + + case '2': /* --stderr /path/to/stderr.logfile */ + redirect_stderr = optarg; + break; + + case_RC_COMMON_GETOPT + } + + if (!pidfile) + 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); + } + } + if (start && !exists(exec_file)) + eerrorx("%s: %s does not exist", applet, + *exec_file ? exec_file : exec); + + if (stop) { + pid = get_pid(pidfile); + if (pid == -1) + i = pid; + else + i = kill(pid, SIGTERM); + if (i != 0) + /* We failed to stop something */ + exit(EXIT_FAILURE); + + /* Even if we have not actually killed anything, we should + * remove information about it as it may have unexpectedly + * crashed out. We should also return success as the end + * result would be the same. */ + 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); + } + + 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); +} diff --git a/src/rc/supervise-daemon.pam b/src/rc/supervise-daemon.pam new file mode 100644 index 0000000..a1bada2 --- /dev/null +++ b/src/rc/supervise-daemon.pam @@ -0,0 +1,6 @@ +#%PAM-1.0 + +auth required pam_permit.so +account required pam_permit.so +password required pam_deny.so +session optional pam_limits.so diff --git a/src/rc/swclock.c b/src/rc/swclock.c new file mode 100644 index 0000000..8245f11 --- /dev/null +++ b/src/rc/swclock.c @@ -0,0 +1,106 @@ +/* + * swclock.c + * Sets the system time from the mtime of the given file. + * This is useful for systems who do not have a working RTC and rely on ntp. + * OpenRC relies on the correct system time for a lot of operations + * so this is needed quite early. + */ + +/* + * Copyright (c) 2007-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. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "rc.h" +#include "rc-misc.h" +#include "_usage.h" + +#define RC_SHUTDOWNTIME RC_SVCDIR "/shutdowntime" + +const char *applet = NULL; +const char *extraopts = "file"; +const char *getoptstring = "sw" getoptstring_COMMON; +const struct option longopts[] = { + { "save", 0, NULL, 's' }, + { "warn", 0, NULL, 'w' }, + longopts_COMMON +}; +const char * const longopts_help[] = { + "saves the time", + "no error if no reference file", + longopts_help_COMMON +}; +const char *usagestring = NULL; + +int main(int argc, char **argv) +{ + int opt, sflag = 0, wflag = 0; + const char *file = RC_SHUTDOWNTIME; + struct stat sb; + struct timeval tv; + + applet = basename_c(argv[0]); + while ((opt = getopt_long(argc, argv, getoptstring, + longopts, (int *) 0)) != -1) + { + switch (opt) { + case 's': + sflag = 1; + break; + case 'w': + wflag = 1; + break; + case_RC_COMMON_GETOPT + } + } + + if (optind < argc) + file = argv[optind++]; + + if (sflag) { + if (stat(file, &sb) == -1) { + opt = open(file, O_WRONLY | O_CREAT, 0644); + if (opt == -1) + eerrorx("swclock: open: %s", strerror(errno)); + close(opt); + } else + if (utime(file, NULL) == -1) + eerrorx("swclock: utime: %s", strerror(errno)); + return 0; + } + + if (stat(file, &sb) == -1) { + if (wflag != 0 && errno == ENOENT) + ewarn("swclock: `%s': %s", file, strerror(errno)); + else + eerrorx("swclock: `%s': %s", file, strerror(errno)); + return 0; + } + + tv.tv_sec = sb.st_mtime; + tv.tv_usec = 0; + + if (settimeofday(&tv, NULL) == -1) + eerrorx("swclock: settimeofday: %s", strerror(errno)); + + return 0; +} diff --git a/src/test/.gitignore b/src/test/.gitignore new file mode 100644 index 0000000..1b79b9f --- /dev/null +++ b/src/test/.gitignore @@ -0,0 +1,6 @@ +einfo.data.out +einfo.funcs.out +librc.funcs.hidden.out +librc.funcs.hidden.list +rc.data.out +rc.funcs.out diff --git a/src/test/Makefile b/src/test/Makefile new file mode 100644 index 0000000..b57b890 --- /dev/null +++ b/src/test/Makefile @@ -0,0 +1,14 @@ +all: + +install: + +ignore: + +check test:: + ./runtests.sh + +verbose-test: + VERBOSE=yes ./runtests.sh + +clean: + rm -rf *.out tmp-* diff --git a/src/test/einfo.data.list b/src/test/einfo.data.list new file mode 100644 index 0000000..c638b2f --- /dev/null +++ b/src/test/einfo.data.list @@ -0,0 +1 @@ +EINFO_1.0 diff --git a/src/test/einfo.funcs.list b/src/test/einfo.funcs.list new file mode 100644 index 0000000..5dbbd96 --- /dev/null +++ b/src/test/einfo.funcs.list @@ -0,0 +1,52 @@ +ebegin +ebegin@@EINFO_1.0 +ebeginv +ebeginv@@EINFO_1.0 +ebracket +ebracket@@EINFO_1.0 +ecolor +ecolor@@EINFO_1.0 +eend +eend@@EINFO_1.0 +eendv +eendv@@EINFO_1.0 +eerror +eerror@@EINFO_1.0 +eerrorn +eerrorn@@EINFO_1.0 +eerrorx +eerrorx@@EINFO_1.0 +eindent +eindent@@EINFO_1.0 +eindentv +eindentv@@EINFO_1.0 +einfo +einfo@@EINFO_1.0 +einfon +einfon@@EINFO_1.0 +einfov +einfov@@EINFO_1.0 +einfovn +einfovn@@EINFO_1.0 +elog +elog@@EINFO_1.0 +eoutdent +eoutdent@@EINFO_1.0 +eoutdentv +eoutdentv@@EINFO_1.0 +eprefix +eprefix@@EINFO_1.0 +ewarn +ewarn@@EINFO_1.0 +ewarnn +ewarnn@@EINFO_1.0 +ewarnv +ewarnv@@EINFO_1.0 +ewarnvn +ewarnvn@@EINFO_1.0 +ewarnx +ewarnx@@EINFO_1.0 +ewend +ewend@@EINFO_1.0 +ewendv +ewendv@@EINFO_1.0 diff --git a/src/test/rc.data.list b/src/test/rc.data.list new file mode 100644 index 0000000..3c27c95 --- /dev/null +++ b/src/test/rc.data.list @@ -0,0 +1,3 @@ +RC_1.0 +rc_environ_fd +rc_environ_fd@@RC_1.0 diff --git a/src/test/rc.funcs.list b/src/test/rc.funcs.list new file mode 100644 index 0000000..c87e470 --- /dev/null +++ b/src/test/rc.funcs.list @@ -0,0 +1,116 @@ +rc_conf_value +rc_conf_value@@RC_1.0 +rc_config_list +rc_config_list@@RC_1.0 +rc_config_load +rc_config_load@@RC_1.0 +rc_config_value +rc_config_value@@RC_1.0 +rc_deptree_depend +rc_deptree_depend@@RC_1.0 +rc_deptree_depends +rc_deptree_depends@@RC_1.0 +rc_deptree_free +rc_deptree_free@@RC_1.0 +rc_deptree_load +rc_deptree_load@@RC_1.0 +rc_deptree_load_file +rc_deptree_load_file@@RC_1.0 +rc_deptree_order +rc_deptree_order@@RC_1.0 +rc_deptree_update +rc_deptree_update@@RC_1.0 +rc_deptree_update_needed +rc_deptree_update_needed@@RC_1.0 +rc_find_pids +rc_find_pids@@RC_1.0 +rc_getfile +rc_getfile@@RC_1.0 +rc_getline +rc_getline@@RC_1.0 +rc_newer_than +rc_newer_than@@RC_1.0 +rc_older_than +rc_older_than@@RC_1.0 +rc_proc_getent +rc_proc_getent@@RC_1.0 +rc_runlevel_exists +rc_runlevel_exists@@RC_1.0 +rc_runlevel_get +rc_runlevel_get@@RC_1.0 +rc_runlevel_list +rc_runlevel_list@@RC_1.0 +rc_runlevel_set +rc_runlevel_set@@RC_1.0 +rc_runlevel_stack +rc_runlevel_stack@@RC_1.0 +rc_runlevel_stacks +rc_runlevel_stacks@@RC_1.0 +rc_runlevel_starting +rc_runlevel_starting@@RC_1.0 +rc_runlevel_stopping +rc_runlevel_stopping@@RC_1.0 +rc_runlevel_unstack +rc_runlevel_unstack@@RC_1.0 +rc_service_add +rc_service_add@@RC_1.0 +rc_service_daemon_set +rc_service_daemon_set@@RC_1.0 +rc_service_daemons_crashed +rc_service_daemons_crashed@@RC_1.0 +rc_service_delete +rc_service_delete@@RC_1.0 +rc_service_description +rc_service_description@@RC_1.0 +rc_service_exists +rc_service_exists@@RC_1.0 +rc_service_extra_commands +rc_service_extra_commands@@RC_1.0 +rc_service_in_runlevel +rc_service_in_runlevel@@RC_1.0 +rc_service_mark +rc_service_mark@@RC_1.0 +rc_service_resolve +rc_service_resolve@@RC_1.0 +rc_service_schedule_clear +rc_service_schedule_clear@@RC_1.0 +rc_service_schedule_start +rc_service_schedule_start@@RC_1.0 +rc_service_started_daemon +rc_service_started_daemon@@RC_1.0 +rc_service_state +rc_service_state@@RC_1.0 +rc_service_value_get +rc_service_value_get@@RC_1.0 +rc_service_value_set +rc_service_value_set@@RC_1.0 +rc_services_in_runlevel +rc_services_in_runlevel@@RC_1.0 +rc_services_in_runlevel_stacked +rc_services_in_runlevel_stacked@@RC_1.0 +rc_services_in_state +rc_services_in_state@@RC_1.0 +rc_services_scheduled +rc_services_scheduled@@RC_1.0 +rc_services_scheduled_by +rc_services_scheduled_by@@RC_1.0 +rc_stringlist_add +rc_stringlist_add@@RC_1.0 +rc_stringlist_addu +rc_stringlist_addu@@RC_1.0 +rc_stringlist_delete +rc_stringlist_delete@@RC_1.0 +rc_stringlist_find +rc_stringlist_find@@RC_1.0 +rc_stringlist_free +rc_stringlist_free@@RC_1.0 +rc_stringlist_new +rc_stringlist_new@@RC_1.0 +rc_stringlist_sort +rc_stringlist_sort@@RC_1.0 +rc_stringlist_split +rc_stringlist_split@@RC_1.0 +rc_sys +rc_sys@@RC_1.0 +rc_yesno +rc_yesno@@RC_1.0 diff --git a/src/test/runtests.sh b/src/test/runtests.sh new file mode 100755 index 0000000..4b723c4 --- /dev/null +++ b/src/test/runtests.sh @@ -0,0 +1,127 @@ +#!/bin/sh + +top_srcdir=${top_srcdir:-../..} +. ${top_srcdir}/test/setup_env.sh + +libeinfo_srcdir="${srcdir}/../libeinfo" +libeinfo_builddir="${builddir}/../libeinfo" +librc_srcdir="${srcdir}/../librc" +librc_builddir="${builddir}/../librc" +rc_srcdir="${srcdir}/../rc" +rc_builddir="${builddir}/../rc" + +checkit() { + local base=$1; shift + echo "$@" | tr ' ' '\n' > ${base}.out + diff -u ${base}.list ${base}.out + eend $? + : $(( ret += $? )) +} + +ret=0 + +ebegin "Checking exported symbols in libeinfo.so (data)" +checkit einfo.data $( +readelf -Ws ${libeinfo_builddir}/libeinfo.so \ + | awk '$4 == "OBJECT" && $5 == "GLOBAL" && $7 != "UND" {print $NF}' \ + | LC_ALL=C sort -u +) + +ebegin "Checking exported symbols in libeinfo.so (functions)" +checkit einfo.funcs $( +readelf -Ws ${libeinfo_builddir}/libeinfo.so \ + | awk '$4 == "FUNC" && $5 == "GLOBAL" && $7 != "UND" {print $NF}' \ + | LC_ALL=C sort -u \ + | egrep -v \ + -e '^_(init|fini)$' +) + +ebegin "Checking exported symbols in librc.so (data)" +checkit rc.data $( +readelf -Ws ${librc_builddir}/librc.so \ + | awk '$4 == "OBJECT" && $5 == "GLOBAL" && $7 != "UND" {print $NF}' \ + | LC_ALL=C sort -u +) + +ebegin "Checking exported symbols in librc.so (functions)" +checkit rc.funcs $( +readelf -Ws ${librc_builddir}/librc.so \ + | awk '$4 == "FUNC" && $5 == "GLOBAL" && $7 != "UND" {print $NF}' \ + | LC_ALL=C sort -u \ + | egrep -v \ + -e '^_(init|fini)$' +) + +ebegin "Checking hidden functions in librc.so" +sed -n '/^librc_hidden_proto/s:.*(\(.*\))$:\1:p' ${librc_srcdir}/librc.h \ + | LC_ALL=C sort -u \ + > librc.funcs.hidden.list +readelf -Wr $(grep -l '#include[[:space:]]"librc\.h"' ${librc_srcdir}/*.c | sed 's:\.c$:.o:') \ + | egrep -v -e 'R_PARISC_(DP|SEG)REL' \ + | awk '$5 ~ /^rc_/ {print $5}' \ + | LC_ALL=C sort -u \ + | egrep -v '^rc_environ_fd$|^rc_config_directory|^rc_config_set_value' \ + > librc.funcs.hidden.out +syms=$(diff -u librc.funcs.hidden.list librc.funcs.hidden.out | sed -n '/^+[^+]/s:^+::p') +[ -z "${syms}" ] +eend $? "Missing hidden defs:"$'\n'"${syms}" +: $(( ret += $? )) + +ebegin "Checking trailing whitespace in code" +# XXX: Should we check man pages too ? +out=$(cd ${top_srcdir}; find */ \ + '(' -name '*.[ch]' -o -name '*.in' -o -name '*.sh' ')' \ + -exec grep -n -E '[[:space:]]+$' {} +) +[ -z "${out}" ] +eend $? "Trailing whitespace needs to be deleted:"$'\n'"${out}" + +ebegin "Checking trailing newlines in code" +out=$(cd ${top_srcdir}; + for f in `find */ -name '*.[ch]'` ; do + sed -n -e :a -e '/^\n*$/{$q1;N;ba' -e '}' $f || echo $f + done) +[ -z "${out}" ] +eend $? "Trailing newlines need to be deleted:"$'\n'"${out}" + +ebegin "Checking for obsolete functions" +out=$(cd ${top_srcdir}; find src -name '*.[ch]' \ + ! -name queue.h \ + -exec grep -n -E '\<(malloc|memory|sys/(errno|fcntl|signal|stropts|termios|unistd))\.h\>' {} +) +[ -z "${out}" ] +eend $? "Avoid these obsolete functions:"$'\n'"${out}" + +ebegin "Checking for x* func usage" +out=$(cd ${top_srcdir}; find src -name '*.[ch]' \ + ! -name queue.h \ + -exec grep -n -E '\<(malloc|strdup)[[:space:]]*\(' {} + \ + | grep -v \ + -e src/includes/helpers.h \ + -e src/libeinfo/libeinfo.c) +[ -z "${out}" ] +eend $? "These need to be using the x* variant:"$'\n'"${out}" + +ebegin "Checking spacing style" +out=$(cd ${top_srcdir}; find src -name '*.[ch]' \ + ! -name queue.h \ + -exec grep -n -E \ + -e '\<(for|if|switch|while)\(' \ + -e '\<(for|if|switch|while) \( ' \ + -e ' ;' \ + -e '[[:space:]]$' \ + -e '\){' \ + -e '(^|[^:])//' \ + {} +) +[ -z "${out}" ] +eend $? "These lines violate style rules:"$'\n'"${out}" + +einfo "Running unit tests" +eindent +for u in units/*; do + [ -x "${u}" -a -f "${u}" ] || continue + ebegin "$(basename "${u}")" + ./"${u}" + eend $? + : $(( ret += $? )) +done + +exit ${ret} diff --git a/src/test/units/is_older_than b/src/test/units/is_older_than new file mode 100755 index 0000000..47a62d7 --- /dev/null +++ b/src/test/units/is_older_than @@ -0,0 +1,83 @@ +#!/bin/sh +# unit test for is_older_than code of baselayout (2008/06/19) +# Author: Matthias Schwarzott + +TMPDIR=tmp-"$(basename "$0")" + +# Please note that we added this unit test because the function +# should really be called is_newer_than as it's what it's really testing. +# Or more perversly, returning 0 on failure and 1 and success. + +# bool is_older_than(reference, files/dirs to check) +# +# return 0 if any of the files/dirs are newer than +# the reference file +# +# EXAMPLE: if is_older_than a.out *.o ; then ... +ref_is_older_than() +{ + local x= ref="$1" + shift + + for x; do + [ "${x}" -nt "${ref}" ] && return 0 + if [ -d "${x}" ]; then + ref_is_older_than "${ref}" "${x}"/* && return 0 + fi + done + return 1 +} + +do_test() +{ + local r1= r2= + + ref_is_older_than "$@" + r1=$? + is_older_than "$@" + r2=$? + + [ -n "${VERBOSE}" ] && echo "reference = $r1 | OpenRC = $r2" + [ $r1 = $r2 ] +} + +echo_cmd() +{ + [ -n "${VERBOSE}" ] && echo "$@" + "$@" +} + +test_it() +{ + do_test "${TMPDIR}"/ref "${TMPDIR}"/dir1 "${TMPDIR}"/dir2 +} + +run_test() +{ + echo_cmd mkdir -p "${TMPDIR}"/dir1 "${TMPDIR}"/dir2 + echo_cmd touch "${TMPDIR}"/dir1/f1 "${TMPDIR}"/dir1/f2 \ + "${TMPDIR}"/dir1/f3 "${TMPDIR}"/dir2/f1 \ + "${TMPDIR}"/dir2/f2 "${TMPDIR}"/dir2/f3 + echo_cmd sleep 1 + echo_cmd touch "${TMPDIR}"/ref + test_it || return 1 + + echo_cmd sleep 1 + echo_cmd touch "${TMPDIR}"/dir1/f2 + test_it || return 1 + + echo_cmd sleep 1 + echo_cmd touch "${TMPDIR}"/ref + test_it || return 1 + + echo_cmd sleep 1 + echo_cmd touch "${TMPDIR}"/dir2/f2 + test_it || return 1 +} + +rm -rf "${TMPDIR}" +mkdir "${TMPDIR}" +run_test +retval=$? +rm -rf "${TMPDIR}" +exit ${retval} diff --git a/supervise-daemon-guide.md b/supervise-daemon-guide.md new file mode 100644 index 0000000..4ac7365 --- /dev/null +++ b/supervise-daemon-guide.md @@ -0,0 +1,49 @@ +# 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 +terminates unexpectedly. + +The following is a brief guide on using this capability. + +## Use Default start, stop and status functions + +If you write your own start, stop and status functions in your service +script, none of this will work. You must allow OpenRC to use the default +functions. + +## Daemons must not fork + +Any deamon that you would like to have monitored by supervise-daemon +must not fork. Instead, it must stay in the foreground. If the daemon +itself forks, the supervisor will be unable to monitor it. + +If the daemon can be configured to not fork, this should be done in the +daemon's configuration file, or by adding a command line option that +instructs it not to fork to the command_args_foreground variable shown +below. + +## Variable Settings + +The most important setting is the supervisor variable. At the top of +your service script, you should set this variable as follows: + +supervisor=supervise-daemon + +Several other variables affect the way services behave under +supervise-daemon. They are documented on the openrc-run man page, but I +will list them here for convenience: + +pidfile=/pid/of/supervisor.pid + +If you are using start-stop-daemon to monitor your scripts, the pidfile +is the path to the pidfile the daemon creates. If, on the other hand, +you are using supervise-daemon, this is the path to the pidfile the +supervisor creates. + +command_args_foreground should be used if the daemon you want to monitor +forks and goes to the background by default. This should be set to the +command line option that instructs the daemon to stay in the foreground. + +This is very early support, so feel free to file bugs if you have +issues. diff --git a/support/Makefile b/support/Makefile new file mode 100644 index 0000000..c8d6ffd --- /dev/null +++ b/support/Makefile @@ -0,0 +1,20 @@ +# 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. + +MK= ../mk +include ${MK}/os.mk + +SUBDIR= deptree2dot init.d.examples openvpn + +ifeq (${OS},Linux) +SUBDIR+= sysvinit +endif + +include ${MK}/subdir.mk diff --git a/support/deptree2dot/Makefile b/support/deptree2dot/Makefile new file mode 100644 index 0000000..b7f6e39 --- /dev/null +++ b/support/deptree2dot/Makefile @@ -0,0 +1,9 @@ +MK= ../../mk +include ${MK}/os.mk + +DIR= ${DATADIR}/support/deptree2dot +BIN= deptree2dot +INC= README.md + + +include ${MK}/scripts.mk diff --git a/support/deptree2dot/README.md b/support/deptree2dot/README.md new file mode 100644 index 0000000..3df9a52 --- /dev/null +++ b/support/deptree2dot/README.md @@ -0,0 +1,11 @@ +# 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 +processed by graphviz. + +Example usage: + +$ chmod +x deptree2dot +$deptree2dot > deptree.dot +$deptree2dot | dot -Tpng -o deptree.png diff --git a/support/deptree2dot/deptree2dot b/support/deptree2dot/deptree2dot new file mode 100644 index 0000000..07ba17a --- /dev/null +++ b/support/deptree2dot/deptree2dot @@ -0,0 +1,44 @@ +#!/usr/bin/perl -w +# -*- cperl -*- +# Copyright © 2012 Diego Elio Pettenò +# Released under the 2-clause BSD license. +# +#Example usage: +#deptree2dot > deptree.dot +#deptree2dot | dot -Tpng -o deptree.png + +my $deptree = defined($ARGV[0]) ? $ARGV[0] : "/run/openrc/deptree"; + +open DEPTREE, $deptree or exit 1; + +print "digraph deptree {\n"; + +my @deptree; + +while(my $line = readline(DEPTREE)) { + $line =~ /^depinfo_([0-9]+)_([a-z]+)(?:_[0-9]+)?='(.*)'\n$/; + my $index = $1; + my $prop = $2; + my $value = $3; $value =~ s/[-\.:~]/_/g; + + if ( $prop eq "service" ) { + $deptree[$index] = $value; + printf "%s [shape=box];\n", $value; + } else { + my $service = $deptree[$index]; + + if ( $prop eq "ineed" ) { + printf "%s -> %s;\n", $service, $value; + } elsif ( $prop eq "iuse" ) { + printf "%s -> %s [color=blue];\n", $service, $value; + } elsif ( $prop eq "ibefore" ) { + printf "%s -> %s [style=dotted];\n", $service, $value; + } elsif ( $prop eq "iafter" ) { + printf "%s -> %s [style=dotted color=purple];\n", $value, $service; + } elsif ( $prop eq "iprovide" ) { + printf "%s -> %s [color=red];\n", $value, $service; + } + } +} + +print "}\n"; diff --git a/support/init.d.examples/.gitignore b/support/init.d.examples/.gitignore new file mode 100644 index 0000000..9f1ce28 --- /dev/null +++ b/support/init.d.examples/.gitignore @@ -0,0 +1,11 @@ +avahi-dnsconfd +avahid +dhcpcd +dbus +hald +named +ntpd +openvpn +polkitd +sshd +wpa_supplicant diff --git a/support/init.d.examples/Makefile b/support/init.d.examples/Makefile new file mode 100644 index 0000000..b65c1fd --- /dev/null +++ b/support/init.d.examples/Makefile @@ -0,0 +1,13 @@ +DIR= ${DATADIR}/support/init.d.examples +INC= README.md +SRCS= avahi-dnsconfd.in avahid.in dhcpcd.in dbus.in \ + hald.in named.in ntpd.in \ + openvpn.in polkitd.in sshd.in wpa_supplicant.in +BIN= ${OBJS} + +MK= ../../mk + +SED_EXTRA+= -e 's:@VARBASE@:/var:g' + +include ${MK}/os.mk +include ${MK}/scripts.mk diff --git a/support/init.d.examples/README.md b/support/init.d.examples/README.md new file mode 100644 index 0000000..2c32fd6 --- /dev/null +++ b/support/init.d.examples/README.md @@ -0,0 +1,3 @@ +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. diff --git a/support/init.d.examples/avahi-dnsconfd.in b/support/init.d.examples/avahi-dnsconfd.in new file mode 100644 index 0000000..b87b6d5 --- /dev/null +++ b/support/init.d.examples/avahi-dnsconfd.in @@ -0,0 +1,22 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=@PKG_PREFIX@/sbin/avahi-dnsconfd +command_args="$avahi_dnsconfd_args -D" +pidfile=@VARBASE@/run/avahi-dnsconfd.pid +name="Avahi DNS Configuration Daemon" + +depend() +{ + use dns + need localmount dbus + after bootmisc +} diff --git a/support/init.d.examples/avahid.in b/support/init.d.examples/avahid.in new file mode 100644 index 0000000..b809d7b --- /dev/null +++ b/support/init.d.examples/avahid.in @@ -0,0 +1,22 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=@PKG_PREFIX@/sbin/avahi-daemon +command_args="$avahid_args -D" +pidfile=@VARBASE@/run/avahi-daemon/pid +name="Avahi Service Advertisement Daemon" + +depend() +{ + use dns + need localmount dbus + after bootmisc +} diff --git a/support/init.d.examples/dbus.in b/support/init.d.examples/dbus.in new file mode 100644 index 0000000..0275767 --- /dev/null +++ b/support/init.d.examples/dbus.in @@ -0,0 +1,26 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=@PKG_PREFIX@/bin/dbus-daemon +pidfile=@VARBASE@/run/dbus/pid +command_args="${dbusd_args---system}" +name="Message Bus Daemon" + +depend() +{ + need localmount net + after bootmisc +} + +start_pre() +{ + mkdir -p $(dirname $pidfile) +} diff --git a/support/init.d.examples/dhcpcd.in b/support/init.d.examples/dhcpcd.in new file mode 100644 index 0000000..1eee3d1 --- /dev/null +++ b/support/init.d.examples/dhcpcd.in @@ -0,0 +1,34 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=/sbin/dhcpcd +pidfile=/var/run/dhcpcd.pid +command_args=-q +name="DHCP Client Daemon" + +depend() +{ + provide net + need localmount + use logger + after bootmisc modules + before dns +} + +stop_pre() +{ + # When shutting down, kill dhcpcd but preserve network + # We do this as /var/run/dhcpcd could be cleaned out when we + # return to multiuser. + if yesno $RC_GOINGDOWN; then + : ${stopsig:=SIGKILL} + fi +} diff --git a/support/init.d.examples/dnsmasq.in b/support/init.d.examples/dnsmasq.in new file mode 100644 index 0000000..f711b2e --- /dev/null +++ b/support/init.d.examples/dnsmasq.in @@ -0,0 +1,31 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=@PKG_PREFIX@/sbin/dnsmasq +command_args=$dnsmasq_args +pidfile=@VARBASE@/run/dnsmasq.pid +required_files=/etc/dnsmasq.conf + +extra_started_commands="reload" + +depend() +{ + provide dns + need localmount net + after bootmisc +} + +reload() +{ + ebegin "Reloading $RC_SVCNAME" + start-stop-daemon --signal SIGHUP --pidfile "$pidfile" + eend $? +} diff --git a/support/init.d.examples/hald.in b/support/init.d.examples/hald.in new file mode 100644 index 0000000..bde173c --- /dev/null +++ b/support/init.d.examples/hald.in @@ -0,0 +1,20 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=@PKG_PREFIX@/sbin/hald +pidfile=@VARBASE@/run/hald/hald.pid +command_args=$hald_args +name="Hardware Abstraction Layer Daemon" + +depend() +{ + need dbus +} diff --git a/support/init.d.examples/named.in b/support/init.d.examples/named.in new file mode 100644 index 0000000..1348787 --- /dev/null +++ b/support/init.d.examples/named.in @@ -0,0 +1,119 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=/usr/sbin/named +command_args=$named_args +pidfile=@VARBASE@/run/named.pid +name="Domain Name server" +extra_started_commands="reload" + +namedb=/etc/namedb +uid=named +case "$RC_UNAME" in + FreeBSD) + uid=bind + pidfile=@VARBASE@/run/named/pid + ;; + Linux) + uid=bind + ;; +esac +: ${named_uid:=${uid}} + +depend() +{ + provide dns + need localmount + after bootmisc +} + +start_pre() +{ + if [ -n "$named_chroot" ]; then + # Create (or update) the chroot directory structure + if [ -r /etc/mtree/BIND.chroot.dist ]; then + mtree -deU -f /etc/mtree/BIND.chroot.dist -p "$named_chroot" + else + ewarn "/etc/mtree/BIND.chroot.dist missing," + ewarn "chroot directory structure not updated" + fi + + if [ ! -d "$named_chroot"/. ]; then + eerror "chroot directory $named_chroot missing" + exit 1 + fi + + # Create /etc/namedb symlink + if [ ! -L "$namedb" ]; then + if [ -d "$namedb" ]; then + ewarn "named chroot: $namedb is a directory!" + elif [ -e "$namedb" ]; then + ewarn "named chroot: $namedb exists!" + else + ln -s "$named_chroot$namedb" "$namedb" + fi + else + # Make sure it points to the right place. + ln -shf "$named_chroot$namedb" "$namedb" + fi + + case "$RC_UNAME" in + *BSD|DragonFly) + # Mount a devfs in the chroot directory if needed + umount "$named_chroot"/dev 2>/dev/null + mount -t devfs dev "$named_chroot"/dev + devfs -m "$named_chroot"/dev \ + ruleset devfsrules_hide_all + devfs -m "$named_chroot"/dev \ + rule apply path null unhide + devfs -m "$named_chroot"/dev \ + rule apply path random unhide + ;; + esac + + # Copy local timezone information if it is not up to date. + if [ -r /etc/localtime ]; then + cmp -s /etc/localtime "$named_chroot/etc/localtime" || + cp -p /etc/localtime "$named_chroot/etc/localtime" + fi + + command_args="$command_args -t $named_chroot" + + ln -fs "$named_chroot$pidfile" "$pidfile" + fi + + if [ ! -s "$named_chroot$namedb/rndc.conf" ]; then + local confgen="${command%/named}/rndc-confgen -a -b256 -u $named_uid \ + -c $named_chrootdir/etc/namedb/rndc.key" + if [ -s "$named_chroot$namedb/rndc.key" ]; then + local getuser="stat -f%Su" + [ "$RC_UNAME" = Linux ] && getuser="stat -c%U" + case $(${getuser} "$named_chroot$namedb"/rndc.key) in + root|"$named_uid");; + *) $confgen;; + esac + else + $confgen + fi + fi +} + +reload() +{ + rndc reload +} + +stop_post() +{ + if [ -n "$named_chroot" -a -c "$named_chroot"/dev/null ]; then + umount "$named_chroot"/dev 2>/dev/null || true + fi +} diff --git a/support/init.d.examples/ntpd.in b/support/init.d.examples/ntpd.in new file mode 100644 index 0000000..786c022 --- /dev/null +++ b/support/init.d.examples/ntpd.in @@ -0,0 +1,44 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +: ${ntpd_config:=/etc/ntp.conf} +: ${ntpd_drift:=/var/db/ntpd.drift} + +command=/usr/sbin/ntpd +required_files=$ntpd_config +pidfile=/var/run/ntpd.pid +command_args="$ntpd_args -c $ntpd_config -f $ntpd_drift -p $pidfile" +name="Network Time Protocol Daemon" + +depend() +{ + use dns + need localmount + after bootmisc ntp-client +} + +start_pre() +{ + if [ -n "$ntpd_chroot" ]; then + case "$RC_UNAME" in + *BSD|DragonFly) + if [ ! -c "$ntpd_chroot/dev/clockctl" ]; then + rm -f "$ntpd_chroot/dev/clockctl" + (cd /dev; /bin/pax -rw -pe clockctl \ + "$ntpd_chroot/dev") + fi + ;; + esac + ln -fs "$ntpd_chroot$ntpd_drift" "$ntpd_drift" + + command_args="$command_args -u ntpd:ntpd -i $ntpd_chroot" + fi +} diff --git a/support/init.d.examples/openvpn.in b/support/init.d.examples/openvpn.in new file mode 100644 index 0000000..95e9482 --- /dev/null +++ b/support/init.d.examples/openvpn.in @@ -0,0 +1,74 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +vpn=${RC_SVCNAME#*.} +name="OpenVPN" +[ "$vpn" != openvpn ] && name="$name ($vpn)" +command=@PKG_PREFIX@/sbin/openvpn + +pidfile=@VARBASE@/run/"$RC_SVCNAME".pid +: ${openvpn_dir:=@PKG_PREFIX@/etc/openvpn} +: ${openvpn_config:=$openvpn_dir/$vpn.conf} +command_args="$openvpn_args --daemon --config $openvpn_config" +command_args="$command_args --writepid $pidfile" +required_dirs=$openvpn_dir +required_files=$openvpn_config + +# If we're an openvpn client, then supply a nice default config +# You can find sample up/down scripts in the OpenRC support/openvpn dir +if yesno $openvpn_client; then + : ${openvpn_up:=${openvpn_dir}/up.sh} + : ${openvpn_down:=${openvpn_dir}/down.sh} + command_args="$command_args --nobind --up-delay --up-restart --down-pre" + command_args="$command_args --up $openvpn_up" + command_args="$command_args --down $openvpn_down" + required_files="$required_files $openvpn_up $openvpn_down" + + in_background_fake="start stop" + start_inactive=YES +fi + +depend() +{ + need localmount net + use dns + after bootmisc +} + +start_pre() +{ + # Linux has good dynamic tun/tap creation + if [ "$RC_UNAME" = Linux ]; then + if [ ! -e /dev/net/tun ]; then + if ! modprobe tun; then + eerror "TUN/TAP support is not available in this kernel" + return 1 + fi + fi + if [ -h /dev/net/tun -a -c /dev/misc/net/tun ]; then + ebegin "Detected broken /dev/net/tun symlink, fixing..." + rm -f /dev/net/tun + ln -s /dev/misc/net/tun /dev/net/tun + eend $? + fi + else + if command -v kldload >/dev/null 2>&1; then + # Hammer the modules home by default + sysctl -a | grep -q '\.tun\.' || kldload if_tun + sysctl -a | grep -q '\.tap\.' || kldload if_tap + fi + fi + + # If the config file does not specify the cd option, we do + if ! grep -q "^[ \t]*cd[ \t].*" "$openvpn_config"; then + command_args="$command_args --cd $openvpn_dir" + fi +} diff --git a/support/init.d.examples/polkitd.in b/support/init.d.examples/polkitd.in new file mode 100644 index 0000000..a36e339 --- /dev/null +++ b/support/init.d.examples/polkitd.in @@ -0,0 +1,20 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=@PKG_PREFIX@/sbin/polkitd +pidfile=@VARBASE@/run/polkitd/polkitd.pid +command_args="$polkitd_args" +name="PolicyKit Daemon" + +depend() +{ + need dbus +} diff --git a/support/init.d.examples/sshd.in b/support/init.d.examples/sshd.in new file mode 100644 index 0000000..d89224d --- /dev/null +++ b/support/init.d.examples/sshd.in @@ -0,0 +1,42 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=/usr/sbin/sshd +command_args=$sshd_args +pidfile=@VARBASE@/run/sshd.pid +required_files=/etc/ssh/sshd_config + +depend() +{ + use logger dns + need net +} + +start_pre() +{ + if [ ! -e /etc/ssh/ssh_host_key ]; then + ebegin "Generating Hostkey" + ssh-keygen -t rsa1 -b 1024 -f /etc/ssh/ssh_host_key -N '' + eend $? || return 1 + fi + if [ ! -e /etc/ssh/ssh_host_dsa_key ]; then + ebegin "Generating DSA Hostkey" + ssh-keygen -d -f /etc/ssh/ssh_host_dsa_key -N '' + eend $? || return 1 + fi + if [ ! -e /etc/ssh/ssh_host_rsa_key ]; then + ebegin "Generating RSA Hostkey" + ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N '' + eend $? || return 1 + fi + + $command -t +} diff --git a/support/init.d.examples/wpa_supplicant.in b/support/init.d.examples/wpa_supplicant.in new file mode 100644 index 0000000..7b7ddc5 --- /dev/null +++ b/support/init.d.examples/wpa_supplicant.in @@ -0,0 +1,82 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-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. + +command=/usr/sbin/wpa_supplicant +: ${wpa_supplicant_conf:=/etc/wpa_supplicant.conf} +wpa_supplicant_if=${wpa_supplicant_if:+-i}$wpa_supplicant_if +command_args="$wpa_supplicant_args -B -c$wpa_supplicant_conf $wpa_supplicant_if" +name="WPA Supplicant Daemon" + +depend() +{ + need localmount + use logger + after bootmisc modules + before dns dhcpcd net + keyword -shutdown +} + +find_wireless() +{ + local iface= + + case "$RC_UNAME" in + Linux) + for iface in /sys/class/net/*; do + if [ -e "$iface"/wireless -o \ + -e "$iface"/phy80211 ] + then + echo "${iface##*/}" + return 0 + fi + done + ;; + FreeBSD) + for iface in $(sysctl -b net.wlan.devices 2>/dev/null); do + echo "${iface##*/}" + done + ;; + *) + for iface in /dev/net/* $(ifconfig -l 2>/dev/null); do + if ifconfig "${iface##*/}" 2>/dev/null | \ + grep -q "[ ]*ssid " + then + echo "${iface##*/}" + return 0 + fi + done + ;; + esac + + return 1 +} + +append_wireless() +{ + local iface= i= + + iface=$(find_wireless) + if [ -n "$iface" ]; then + for i in $iface; do + command_args="$command_args -i$i" + done + else + eerror "Could not find a wireless interface" + fi +} + +start_pre() +{ + case " $command_args" in + *" -i"*) ;; + *) append_wireless;; + esac +} diff --git a/support/openvpn/Makefile b/support/openvpn/Makefile new file mode 100644 index 0000000..d836f08 --- /dev/null +++ b/support/openvpn/Makefile @@ -0,0 +1,9 @@ +MK= ../../mk +include ${MK}/os.mk + +DIR= ${DATADIR}/support/openvpn +BIN= down.sh up.sh +INC= README.md + + +include ${MK}/scripts.mk diff --git a/support/openvpn/README.md b/support/openvpn/README.md new file mode 100644 index 0000000..9ff435d --- /dev/null +++ b/support/openvpn/README.md @@ -0,0 +1,8 @@ +These handy scripts setup any dns information that OpenVPN may push. +They also handle the interaction with OpenRC so that the OpenVPN service +can become "inactive". This means that when it starts, it goes inactive and +OpenRC continues on its merry way booting the system. When OpenVPN connects +to an endpoint it then re-starts the OpenVPN service and starts up any +services that depend on us. A similar thing happens when we shut down. + +Of course, this is all optional. diff --git a/support/openvpn/down.sh b/support/openvpn/down.sh new file mode 100755 index 0000000..2ffce8d --- /dev/null +++ b/support/openvpn/down.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# Copyright (c) 2007-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. + +# If we have a service specific script, run this now +[ -x "${RC_SVCNAME}"-down.sh ] && "${RC_SVCNAME}"-down.sh + +# Restore resolv.conf to how it was +if command -v resolvconf >/dev/null 2>&1; then + resolvconf -d "${dev}" +elif [ -e /etc/resolv.conf-"${dev}".sv ]; then + # Important that we copy instead of move incase resolv.conf is + # a symlink and not an actual file + cp -p /etc/resolv.conf-"${dev}".sv /etc/resolv.conf + rm -f /etc/resolv.conf-"${dev}".sv +fi + +# Re-enter the init script to stop any dependant services +if [ -x "${RC_SERVICE}" ]; then + if "${RC_SERVICE}" --quiet status; then + IN_BACKGROUND=YES + export IN_BACKGROUND + "${RC_SERVICE}" --quiet stop + fi +fi + +exit 0 diff --git a/support/openvpn/up.sh b/support/openvpn/up.sh new file mode 100755 index 0000000..ce5f296 --- /dev/null +++ b/support/openvpn/up.sh @@ -0,0 +1,80 @@ +#!/bin/sh +# Copyright (c) 2007-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. + +# Setup our resolv.conf +# Vitally important that we use the domain entry in resolv.conf so we +# can setup the nameservers are for the domain ONLY in resolvconf if +# we're using a decent dns cache/forwarder like dnsmasq and NOT nscd/libc. +# nscd/libc users will get the VPN nameservers before their other ones +# and will use the first one that responds - maybe the LAN ones? +# non resolvconf users just the the VPN resolv.conf + +# FIXME:- if we have >1 domain, then we have to use search :/ +# We need to add a flag to resolvconf to say +# "these nameservers should only be used for the listed search domains +# if other global nameservers are present on other interfaces" +# This however, will break compatibility with Debians resolvconf +# A possible workaround would be to just list multiple domain lines +# and try and let resolvconf handle it + +NS= +DOMAIN= +SEARCH= +i=1 +while true; do + eval opt=\$foreign_option_${i} + [ -z "${opt}" ] && break + if [ "${opt}" != "${opt#dhcp-option DOMAIN *}" ]; then + if [ -z "${DOMAIN}" ]; then + DOMAIN="${opt#dhcp-option DOMAIN *}" + else + SEARCH="${SEARCH:+ }${opt#dhcp-option DOMAIN *}" + fi + elif [ "${opt}" != "${opt#dhcp-option DNS *}" ]; then + NS="${NS}nameserver ${opt#dhcp-option DNS *}\n" + fi + : $(( i += 1 )) +done + +if [ -n "${NS}" ]; then + DNS="# Generated by openvpn for interface ${dev}\n" + if [ -n "${SEARCH}" ]; then + DNS="${DNS}search ${DOMAIN} ${SEARCH}\n" + else + DNS="${DNS}domain ${DOMAIN}\n" + fi + DNS="${DNS}${NS}" + if command -v resolvconf >/dev/null 2>&1; then + printf "${DNS}" | resolvconf -a "${dev}" + else + # Preserve the existing resolv.conf + if [ -e /etc/resolv.conf ]; then + cp -p /etc/resolv.conf /etc/resolv.conf-"${dev}".sv + fi + (umask 022; printf "${DNS}" > /etc/resolv.conf) + fi +fi + +# Below section is OpenRC specific + +# If we have a service specific script, run this now +[ -x "${RC_SVCNAME}"-up.sh ] && "${RC_SVCNAME}"-up.sh + +# Re-enter the init script to start any dependant services +if [ -x "${RC_SERVICE}" ]; then + if ! "${RC_SERVICE}" --quiet status; then + IN_BACKGROUND=YES + export IN_BACKGROUND + "${RC_SERVICE}" --quiet start + fi +fi + +exit 0 diff --git a/support/sysvinit/Makefile b/support/sysvinit/Makefile new file mode 100644 index 0000000..b207161 --- /dev/null +++ b/support/sysvinit/Makefile @@ -0,0 +1,8 @@ +MK= ../../mk +include ${MK}/os.mk + +DIR= ${DATADIR}/support/sysvinit +INC= inittab README.md + + +include ${MK}/scripts.mk diff --git a/support/sysvinit/README.md b/support/sysvinit/README.md new file mode 100644 index 0000000..a51c0d2 --- /dev/null +++ b/support/sysvinit/README.md @@ -0,0 +1,2 @@ +Here's a sample inittab for use with sysvinit for Linux based systems. +We don't install it by default as sysvinit packages normally own this file. diff --git a/support/sysvinit/inittab b/support/sysvinit/inittab new file mode 100644 index 0000000..2579097 --- /dev/null +++ b/support/sysvinit/inittab @@ -0,0 +1,41 @@ +# /etc/inittab: This file describes how the INIT process should set up +# the system in a certain run-level. + +# Default runlevel. +id:3:initdefault: + +# System initialization, mount local filesystems, etc. +si::sysinit:/sbin/openrc sysinit + +# Further system initialization, brings up the boot runlevel. +rc::bootwait:/sbin/openrc boot + +l0:0:wait:/sbin/openrc shutdown +l0s:0:wait:/sbin/halt -dhip +l1:S1:wait:/sbin/openrc single +l2:2:wait:/sbin/openrc nonetwork +l3:3:wait:/sbin/openrc default +l4:4:wait:/sbin/openrc default +l5:5:wait:/sbin/openrc default +l6:6:wait:/sbin/openrc reboot +l6r:6:wait:/sbin/reboot -d +#z6:6:respawn:/sbin/sulogin + +# new-style single-user +su0:S:wait:/sbin/openrc single +su1:S:wait:/sbin/sulogin + +# TERMINALS +c1:12345:respawn:/sbin/agetty 38400 tty1 linux +c2:2345:respawn:/sbin/agetty 38400 tty2 linux +c3:2345:respawn:/sbin/agetty 38400 tty3 linux +c4:2345:respawn:/sbin/agetty 38400 tty4 linux +c5:2345:respawn:/sbin/agetty 38400 tty5 linux +c6:2345:respawn:/sbin/agetty 38400 tty6 linux + +# SERIAL CONSOLES +#s0:12345:respawn:/sbin/agetty 9600 ttyS0 vt100 +#s1:12345:respawn:/sbin/agetty 9600 ttyS1 vt100 + +# What to do at the "Three Finger Salute". +ca:12345:ctrlaltdel:/sbin/shutdown -r now diff --git a/sysctl.d/Makefile b/sysctl.d/Makefile new file mode 100644 index 0000000..feaf918 --- /dev/null +++ b/sysctl.d/Makefile @@ -0,0 +1,6 @@ +DIR= ${SYSCTLDIR} +CONF= README + +MK= ../mk +include ${MK}/os.mk +include ${MK}/scripts.mk diff --git a/sysctl.d/README b/sysctl.d/README new file mode 100644 index 0000000..ca3e030 --- /dev/null +++ b/sysctl.d/README @@ -0,0 +1,13 @@ +Kernel system variables configuration files + +Files found under the /etc/sysctl.d directory that end with .conf are +parsed within sysctl(8) at boot time. If you want to set kernel variables +you can either edit /etc/sysctl.conf or make a new file. + +The filename isn't important, but don't make it a package name as it may clash +with something the package builder needs later. The file name must end +with .conf, or it will not be read. + +The recommended location for local system settings is /etc/sysctl.d/local.conf +but as long as you follow the rules for the name of the file, anything will +work. see the sysctl.conf(5) man page for details of the format. diff --git a/test/setup_env.sh b/test/setup_env.sh new file mode 100755 index 0000000..881984f --- /dev/null +++ b/test/setup_env.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +if [ -z "${top_srcdir}" ] ; then + echo "You must set top_srcdir before sourcing this file" 1>&2 + exit 1 +fi + +srcdir=${srcdir:-.} +top_builddir=${top_builddir:-${top_srcdir}} +builddir=${builddir:-${srcdir}} + +LD_LIBRARY_PATH=${top_builddir}/src/libeinfo:${top_builddir}/src/librc:${LD_LIBRARY_PATH} +PATH=${top_builddir}/src/rc:${PATH} +export LD_LIBRARY_PATH PATH + +if [ ! -f ${top_srcdir}/sh/functions.sh ] ; then + echo "functions.sh not yet created !?" 1>&2 + exit 1 +elif ! . ${top_srcdir}/sh/functions.sh; then + echo "Sourcing functions.sh failed !?" 1>&2 + exit 1 +fi + diff --git a/test/skel.runtests.sh b/test/skel.runtests.sh new file mode 100755 index 0000000..cda2002 --- /dev/null +++ b/test/skel.runtests.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# top_srcdir=${top_srcdir:-SET/THIS/PATH/OK!?} +. ${top_srcdir}/test/setup_env.sh +