diff --git a/BUSYBOX.md b/BUSYBOX.md new file mode 100644 index 0000000..db7ca51 --- /dev/null +++ b/BUSYBOX.md @@ -0,0 +1,32 @@ +# 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_IP -- The ip applet doesn't support the "scope" modifier for +"ip route add" and "ip address add". + +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..7d0ad3e --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1322 @@ +commit 248a7dcda351ef4172c28cc686bfe7b93fa522ff +Author: William Hubbs +Commit: William Hubbs + + mtab: typo fix + +commit bb451fd7b4a200b63ba355ef2ea5ba35444f49e7 +Author: William Hubbs +Commit: William Hubbs + + update ChangeLog + +commit fb23cbe204b9adeb817e2f5be7a1bb827bb9a477 +Author: William Hubbs +Commit: William Hubbs + + mtab: make /etc/mtab as a file configurable + +commit a4346bb7f5fc09e81a61032ebc05f70692a4a04c +Author: William Hubbs +Commit: William Hubbs + + start work on 0.18.3 + +commit 776346e2cd4a43ffeaf268b8a89927d9f3f9eca5 +Author: William Hubbs +Commit: William Hubbs + + update changelog + +commit ce71ffbfbe5e44bb61a3f00faf8466071dab5ea7 +Author: William Hubbs +Commit: William Hubbs + + openrc-run.sh: fix new required_* tests to exit properly + +commit d9ee8ca5842983b59cc2c6e595d21e9a26a4f000 +Author: William Hubbs +Commit: William Hubbs + + update changelog + +commit 4e44e2cd73fb7fe08fb229bc3192d14f97dc2aa2 +Author: William Hubbs +Commit: William Hubbs + + typo fix + + X-Gentoo-Bug: 563010 + X-Gentoo-Bug: https://bugs.gentoo.org/show_bug.cgi?id=563010 + +commit 88b807f9bcea5ec5bf5c5edb3c7161bf5c12d2d0 +Author: William Hubbs +Commit: William Hubbs + + increment version number + +commit feba5d86b73b0df5587b178e80e85129a60861cd +Author: William Hubbs +Commit: William Hubbs + + mountinfo: make sure the netdev variable is initialized on Linux + + This fixes the following regression: + + X-Gentoo-Bug: 562668 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=562668 + +commit 3b1e96a6a3af3de68a18558f3a6ebac31430a1cd +Author: William Hubbs +Commit: William Hubbs + + openrc-run.sh: allow spaces in required_{files,dirs} + + X-Gentoo-Bug: 562320 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=562320 + +commit 79998bdf9cb5089f423e00bfa4bc3816b64c58d4 +Author: William Hubbs +Commit: William Hubbs + + increment version number + +commit 050ddfae4ad1ad0dc5993766e0bd90739bd41de9 +Author: William Hubbs +Commit: William Hubbs + + Update ChangeLog + +commit d5116cc697c9eb275ab8497939ab41504e5ce578 +Author: William Hubbs +Commit: William Hubbs + + localmount: white space cleanup + +commit b86d170037197d7bdcda57c7d4c09c17bda97f31 +Author: Ian Stakenvicius +Commit: William Hubbs + + localmount: clean up handling of aufs branches + + X-Gentoo-Bug: 560008 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=560008 + +commit 6fa0d6318bcd5c98548cff2ff840bca116892ca5 +Author: William Hubbs +Commit: William Hubbs + + mtab: fix update logic + + This advises users to remove mtab from their runlevels if /etc/mtab is a + symlink, and it creates the symlink if /etc/mtab does not exist on a + system. + + X-Gentoo-Bug: 560060 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=560060 + +commit 80d3928b0d13f09a9c1e82bd27c9fff943d84d43 +Author: Austin S. Hemmelgarn +Commit: William Hubbs + + cgroups: Add the hugetlb, net_cls and pids controllers + + Note from WilliamH: I slightly rearranged the code and added the + settings in rc.conf. + + X-Gentoo-Bug: 555488 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=555488 + +commit 17ef205bc63a4e231dccee719394a7a8563f8c3f +Author: William Hubbs +Commit: William Hubbs + + sysfs: use printf instead of echo to write to cgroup files + + This is needed for compatibility with musl and printf is also posix. + + X-Gentoo-Bug: 562334 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=562334 + +commit b20a1951adf9a705a903fb3047b7ef26c013103c +Author: William Hubbs +Commit: William Hubbs + + rc-cgroup.sh: Do not add leading spaces to cgroup values + + We were starting the value we write to the cgroup setting file with + leading spaces and this was causing issues. This change makes sure that + we aren't adding leading spaces to the value. + + X-Gentoo-Bug: 562354 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=562354 + +commit bf0c0dd5644436efe4986c2b259b755d111266b9 +Author: William Hubbs +Commit: William Hubbs + + bootmisc: convert errors in clean_run function to warnings + + X-Gentoo-Bug: 552418 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=552418 + +commit 1558ad2b9ebf319b85876a940d31d513bf21324f +Author: William Hubbs +Commit: William Hubbs + + bootmisc: only remove temp directory if umount is successful + + Change the clean_run function to only remove the temp directory if the + umount was successful. + + X-Gentoo-Bug: 561230 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=561230 + +commit 5f4f2420364098835522da868a9e75205c9e4f9c +Author: William Hubbs +Commit: William Hubbs + + mountinfo: fix --netdev and --nonetdev on Linux + + On Linux, the --netdev and --nonetdev switches were not working. They + were both returning false. After this change, they operate based on the + presence or abscence of the _netdev option in mount options. + +commit b3f7ff901f7d3ed00b9f73c601193ac507f62eaf +Author: William Hubbs +Commit: William Hubbs + + mountinfo: read /proc/self/mounts instead of /proc/mounts on Linux + +commit a59365a582c3a8c9a8b863b572fddcb65fccadfd +Author: William Hubbs +Commit: William Hubbs + + start-stop-daemon.sh: complain in start if command is undefined + + The default start-stop-daemon start function expects the command + variable to be defined to point to the daemon we want to start. + + If the variable is undefined, this means that there will be nothing to + start, and in this case we should complain because it is possible that + the script writer made a typo in the variable name. + +commit dac5966ca40610797d2b2aabef17154ca3dc20af +Author: William Hubbs +Commit: William Hubbs + + Revert "local/netmount: remove uses of -O [no]_netdev" + + This reverts commit 2a439c85bd69efc14847b4397bd6783cac051405. + There is another use case for -O involving iscsi, so we can't remove it. + +commit 3b6a6df4b5b818e576a88444632d5c73cffd1c57 +Author: William Hubbs +Commit: William Hubbs + + openrc-run: rename some dependency variables and a function for clarity + + All of the dependency type lists had the types_ prefix in their names; + this has been changed to deptypes_ to make them more self documenting. + + Along the same lines, the setup_types function was renamed + setup_deptypes. + +commit b047ea47e97d7e8b96d6d0e064613f6860c8eb36 +Author: William Hubbs +Commit: William Hubbs + + localmount/netmount: on Linux, fail if some file systems do not mount + + The following return codes are returned by mount -a: + + 0: all file systems mounted. + 32: no file systems mounted. + 64: some file systems mounted. + + The localmount/netmount services should fail if all file systems that + should mount did not mount. + +commit b652752339690e10a55ae50d046f4cf2a98daf1a +Author: William Hubbs +Commit: William Hubbs + + Make localmount and netmount always succeed on non-linux + +commit 2a439c85bd69efc14847b4397bd6783cac051405 +Author: William Hubbs +Commit: William Hubbs + + local/netmount: remove uses of -O [no]_netdev + + This was causing an incompatibility with busybox, and we do not use it + in Gentoo. + +commit 7341cd882fba522c1f1d183603334839bd4df7fc +Author: William Hubbs +Commit: William Hubbs + + allow localmount and netmount to fail + +commit 279f1e5d1013309d99509ab4b7b57521f8a4aba4 +Author: Mike Frysinger +Commit: William Hubbs + + binfmt: fix indent on return + +commit c256a7aa80c683eca6194c80b57294e4d51c9a16 +Author: Doug Freed +Commit: Doug Freed + + savecache: clean up implementation + + X-Gentoo-Bug: 557222 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=557222 + +commit 6a422982579786cb8308af04ccca6622afa06e50 +Author: Jason Zaman +Commit: Doug Freed + + tmpfiles: run restorecon on the entire path + + The tmpfiles "d" entry will create a full path and only the last dir in + the path will have its SELinux label set correctly. This patch will + restorecon the parents as well so that the selinux labels are correct. + + eg, "d /run/libvirt/lxc", then "lxc" would have the correct SELinux + label but "libvirt" would not. + + Signed-off-by: Jason Zaman + +commit f69833a1e17d1cf65e96a34fcc0e48caf9d90d64 +Author: William Hubbs +Commit: William Hubbs + + mountinfo: fix -e and -E options + + Add the -e and -E options to getoptstring so they are recognized. + +commit eeadca0b8a5b87c26e60a58563251604350a7a3b +Author: William Hubbs +Commit: William Hubbs + + Add EERROR_QUIET and EINFO_QUIET to environment whitelist + +commit dde339070b4850dc1fadf4992cc01d3468992106 +Author: William Hubbs +Commit: William Hubbs + + Increment version + +commit 1736be3bc3ebbc1440f87b49fc353e81fb0a6037 +Author: Doug Freed +Commit: Doug Freed + + savecache: Fix unable to create cache message + + The cache is created in $RC_LIBEXECDIR, not $RC_SVCDIR, so fix the error + message when we fail to create it to match. + +commit a36a635b016a7427dd1739a26c951fedf22f0dec +Author: Mike Gilbert +Commit: Doug Freed + + tmpfiles: Recognize type 'v' (create btrfs subvol) + + This change does NOT implement btrfs subvol creation. Instead, it + treats 'v' the same as 'd', which is an acceptable fallback + according to the manual. + + Fixes #58 + +commit 9310ccc06bcadd8897aed51cd51c94fccb7d9c07 +Author: Mike Gilbert +Commit: Doug Freed + + Remove execute bit from tmpfiles.sh.in + +commit 0c2e4eb3cd7935d375b74099a3a9a5fe519e6cab +Author: William Hubbs +Commit: William Hubbs + + Update ChangeLog + +commit d2ce07e227ec95370e8aee5f1199edc6ad61aff9 +Author: William Hubbs +Commit: William Hubbs + + Add rc-sstat script + + The rc-sstat script is written to display status of s6 services and + run rc-status to display all services status. + + This currently only works on Linux. + +commit b209fe3859c05c286037843bb34058f849c54b15 +Author: Mike Gilbert +Commit: William Hubbs + + bootmisc: Don't call dmesg in systemd-nspawn containers + + This fixes #57. + +commit c94c8288cd5217b01c24d6f048c64ebbc30bee02 +Author: Mike Frysinger +Commit: Mike Frysinger + + fix link to s6 website + +commit bcb9c44e73ccf332c7c961a6f82520699c6e776d +Author: Mike Gilbert +Commit: William Hubbs + + man: Document the stopsig variable + + This variable can be used to set the signal to send if the service is + using start-stop-daemon. + + This fixes #56 + +commit bbabf546f9d72cbfc48bd839a6d01b402ee6cced +Author: William Hubbs +Commit: William Hubbs + + Document bash's handling of ulimit options + + When bash is used in posix mode for the shell, the ulimit command uses + a block size of 512 bytes for the -c and -f options. + + X-Gentoo-Bug: 549238 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=549238 + +commit a7fcc1e264b035177b4e524a40b27145baa86969 +Author: William Hubbs +Commit: William Hubbs + + S6: kick the scanner and sleep 1.5 seconds to avoid a race condition + +commit b79d058f162d8b49ccc968744e7cb1b7a7ba729c +Author: William Hubbs +Commit: William Hubbs + + s6: Use s6-svc -Dd to stop services + + This allows us to get rid of the sleep call in the stop function. Also, + we set a configurable timeout value for stopping daemons. + +commit ddce529c71c2e9f01d8e5666e27050b6ed6c6761 +Author: William Hubbs +Commit: William Hubbs + + More s6 fixes + + - When no service link is in the scan directory, show the default + stopped message. + - Do not remove the service link when stopping the service. + +commit e372f97bebd4866633ad56aa5d5b1ae59fa88118 +Author: William Hubbs +Commit: William Hubbs + + Fix the s6 handling + + This changes the default s6 service directory to /var/svc.d, also + it changes the code to work with the individual services instead of + forcing a rescan when a service is started or stopped. + +commit c2abf4b436b8cca8ebed395ff08f5fdc546eadac +Author: William Hubbs +Commit: William Hubbs + + Start work on 0.17 + +commit d247ac4cbbe0ab62564ef82a5940b4f1a03973b3 +Author: William Hubbs +Commit: William Hubbs + + Update changelog + +commit bb2d7becfd3008379f8f69b5d036922281aa211f +Author: William Hubbs +Commit: William Hubbs + + Add support for the s6 supervision suite + +commit 0f9354becfbd54f9800c93092aa26be859dcf16a +Author: William Hubbs +Commit: William Hubbs + + man: Clarify the documentation for command_args + + The command_args variable only works if using start-stop-daemon to start + the daemon. + +commit a15fa1a3b12a5372c1b3c8d5df7e489648bef913 +Author: William Hubbs +Commit: William Hubbs + + Rework supervisor integration framework + + The original way of doing this allowed users to change the supervisor in + conf.d/*. This changes this so that the supervisor setup can be done in + the service script itself. + +commit 0198affc742297b6e203bbcecc14436682119cc7 +Author: William Hubbs +Commit: William Hubbs + + The rc_supervisor variable is a service configuration variable + + This variable should not be changed globally unless you really know what + you are doing. + +commit abef2fcb2dbcc277bb05f0d9c674d4b47826f17f +Author: William Hubbs +Commit: William Hubbs + + Make the default start, stop and status functions overridable + + This will make it possible to add support for supervision suites such as + runit and s6. + +commit 0b435ddd834bd18254c4d3341acdebf0829921f5 +Author: Jakob Drexel +Commit: William Hubbs + + librc: Fix crash if the service name is the same as the including runlevel + + If a service has the same name as the runlevel it is in, openrc will + crash on changing to such runlevel. It goes in a recursive madness and + eventually gets a SEGV while in snprintf (don't know why). + + This fixes two errors: + 1. ls_dir stats files not with full path -> stat always returns != 0 + 2. ls_dir adds files to list if stat failed + + This fixes #53. + + X-Gentoo-Bug: 537304 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=537304 + +commit 9f6427ea57c6c950b4d1c356ad1bfb0aa8deb40b +Author: William Hubbs +Commit: William Hubbs + + Start work on 0.16 + +commit 60488069c08201cbfbc661a3e7bd1fafa31fceed +Author: William Hubbs +Commit: William Hubbs + + update ChangeLog + +commit 9225bfa6918cfd488c8f1055cf986b542f1f157e +Author: William Hubbs +Commit: William Hubbs + + Build: make snapshot remove .git directory from tarball + +commit 7bd456ed7bf35da2d7a53650baa33637e178064e +Author: Mike Gilbert +Commit: William Hubbs + + Disable service scripts for systemd-nspawn + + This adds the -systemd-nspawn keyword to service scripts which are not + intended to run in systemd-nspawn containers. + + This fixes #52. + + X-Gentoo-Bug: 548058 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=548058 + +commit c709e6077c6eda3f4f7e7222298213413254ee0f +Author: William Hubbs +Commit: William Hubbs + + Add support for systemd-nspawn containers + + This adds support for running OpenRC in a container created by + the systemd-nspawn utility. + + This fixes #52. + + X-Gentoo-Bug: 548058 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=548058 + +commit a27d577da8cf4f1c7f9784a043ecf02d71a81ca6 +Author: William Hubbs +Commit: William Hubbs + + Build: fix the dist target + + The dist target now creates an archive based on the version setting. + This makes it possible to create an archive identical to the ones github + generates once the release is tagged. + +commit 8b9349208696e5caed1a8b34f066cec4ee642194 +Author: William Hubbs +Commit: William Hubbs + + convert all references from runscript to openrc-run + +commit 4b1b457cd15cc1609c101b99a49a5e4c67b62567 +Author: William Hubbs +Commit: William Hubbs + + Start work on 0.15 + +commit 1d6602bb8e7062323ead03eaa0c4ae307c517b9e +Author: William Hubbs +Commit: William Hubbs + + Add ChangeLog + +commit a6391f44ee6c68d674ae8425983467b971710d5d +Author: William Hubbs +Commit: William Hubbs + + mtab: move toward requiring /etc/mtab to be a symbolic link + + This changes the mtab service in the following way: + + - If /etc/mtab is a symbolic link, success is returned. + - If /etc is not writable, we warn that we could not update /etc/mtab + and return success. + - If /etc/mtab does not exist, we create a symbolic link from + /etc/mtab to /proc/self/mounts. + - Otherwise, we warn that updating /etc/mtab as a file is + deprecated and continue to update it after outputting instructions to + the user for how to move it to a symbolic link. + +commit a8c6dbac96a20eb35ce55befe0e64d89dd30de4d +Author: William Hubbs +Commit: William Hubbs + + typo fix in NEWS + + The binfmt service should be added to the boot runlevel, not sysinit. + +commit a7c0400177e504fe07e7c39168b7a92e40ab334a +Author: William Hubbs +Commit: William Hubbs + + Update news + +commit 03803ae8e966755e8bf6d52b61209792cb830cf4 +Author: William Hubbs +Commit: William Hubbs + + start-stop-daemon: redirect stdin if --background option is used + + X-Gentoo-Bug: 498684 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=498684 + +commit 1c2f7bf607ee7e24dca0a2710cef9e9b24e819f7 +Author: William Hubbs +Commit: William Hubbs + + Convert feature removal schedule to markdown + +commit 1ebffa517f6f1dafc206d5ee943d3cacd25feaf6 +Author: William Hubbs +Commit: William Hubbs + + Convert news to markdown + +commit e6dd26d185e4a9722f4d4813a084ca77eba913c7 +Author: William Hubbs +Commit: William Hubbs + + convert README.history to markdown + +commit 628b35e1beeea6aaab181b1511b2a879bdc45b61 +Author: William Hubbs +Commit: William Hubbs + + Update busybox documentation + + Convert README.busybox to markdown and add the note on the sysctl applet + incompatibility. + +commit ebc32aadada564095b70f0ff439a9863102a2ae5 +Author: William Hubbs +Commit: William Hubbs + + Convert style guide to markdown + +commit 362dfa33804d2ba5bed241f697aac0178be07d3d +Author: William Hubbs +Commit: William Hubbs + + README.md: small formatting changes + +commit dccc0a91292240022c4b120304b9198055d0d240 +Author: William Hubbs +Commit: William Hubbs + + Update README.md format and bug reporting information + +commit c2aa56a7c49214b1fef355f79dfcd94265efe089 +Author: William Hubbs +Commit: William Hubbs + + Rename README README.md + + This fixes #26. + +commit 23d806ca24845261fd89104c16bc28a60505fe5c +Author: William Hubbs +Commit: William Hubbs + + savecache: clean up creation of cache directory + + The cache directory should be created via mkdir -p instead of + mkdir. This makes sure all parent directories are created. + + Also, we now display an error message explaining that we were unable to + create the cache directory if creation fails. + +commit de7d184909d561b68b411325d32471c047549bca +Author: William Hubbs +Commit: William Hubbs + + savecache: fix check for $RC_LIBEXECDIR writability + + We were originally checking to see if $RC_LIBEXECDIR/cache was writable. For + a new install, this check will fail since this path does not exist. This + is also incorrect because later we create $RC_LIBEXECDIR/cache. + + The correct check is checkpath -W $RC_LIBEXECDIR, and this fixes the + issue. + + X-Gentoo-Bug: 544632 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=544632 + +commit 15ab3f39c69ff8d69fd08a9cde7495a04b4dec9e +Author: William Hubbs +Commit: William Hubbs + + cgroups: use printf to write to cgroup files + + This fixes #33. + +commit ee1768a419122d288256cce1723d4997bd965eab +Author: William Hubbs +Commit: William Hubbs + + Add binfmt service to sysinit runlevel + + This makes binfmt processing behave like tmpfiles processing which + follows the same specification as systemd. + + This fixes #48. + + X-Gentoo-Bug: 545162 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=545162 + +commit 95ed0665393c353dbc24afb8c63c615402136f5d +Author: William Hubbs +Commit: William Hubbs + + procfs: do not force loading of usbcore module + + It appears that the only reason we were force loading the usbcore + module was to facilitate mounting usbfs. Since we no longer mount + usbfs, this is no longer necessary. + + X-Gentoo-Bug: 480312 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=480312 + +commit 4c5132421f37bd6831eab1f9527a197340f2e9ae +Author: William Hubbs +Commit: William Hubbs + + procfs: remove usbfs and usbdevfs support + + The usbfs and usbdevfs file systems have been deprecated since + Linux-2.6.32, so we remove the code to automount them. + + X-Gentoo-Bug: 480312 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=480312 + +commit 6d81d3be1bcba81a68086f2a17561d13e1f844e6 +Author: William Hubbs +Commit: William Hubbs + + procfs: do not test for the existence of /proc/filesystems + + The test for the existence of /proc/filesystems is redundant since we + always return success. + +commit 8d307a6fadd516f26d9c72016119277a7a5c1946 +Author: William Hubbs +Commit: William Hubbs + + procfs: remove redundant check for OpenVZ + + The check for OpenVZ is not necessary since the procfs service already + will not run on OpenVZ due to the keywords setting. + +commit 62addf118067dd2cd57c3f5fee35c9e80f9fec42 +Author: William Hubbs +Commit: William Hubbs + + Move SELinux mount to sysfs service + + The selinux file system is mounted under /sys, so move the code for it + to the appropriate service. + + X-Gentoo-Bug: 546290 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=546290 + +commit 1eab656ca1d3258ff00495710a68ad459ce50d3e +Author: William Hubbs +Commit: William Hubbs + + Fix tmpfiles processing + + Tmpfiles.d processing had /run overriding /usr/lib and /etc, but this is + not correct. The correct order, from lowest to highest, for tmpfiles + processing is: + + * /usr/lib/tmpfiles.d/*.conf + * /run/tmpfiles.d/*.conf + * /etc/tmpfiles.d + + This means /run/tmpfiles.d/*.conf can override /etc/tmpfiles.d/*.conf, + but /etc/tmpfiles.d/*.conf can override both of them. + + This fixes #49. + +commit 731a3affdce31e2971a84cde11df2d122049ec99 +Author: William Hubbs +Commit: William Hubbs + + Fix script execution in the local service + + The local service should use eval when it executes scripts since it has + the redirection set up in a variable. + + This fixes #50. + X-Gentoo-Bug: 545012 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=545012 + +commit 3e9bb3b021620654f99a0ead71ed73d34b5c6826 +Author: William Hubbs +Commit: William Hubbs + + Make sysctl on Linux respect rc_verbose setting + + We do not need to spam the console with variable settings by default. + This fixes #51. + + X-Gentoo-Bug: 541922 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=541922 + +commit c068762c4cf49e7ea9719dcab2e5f7d4c4e7e2e5 +Author: William Hubbs +Commit: Doug Freed + + Fix rc_verbose processing + + We were processing the rc_verbose setting before we sourced the + configuration file where it was set; this fixes the issue. + + Fixes #46 + +commit b8ab99b5d3ee1a93f215157c36fb120857afde64 +Author: William Hubbs +Commit: William Hubbs + + checkpath: Remove the last HAVE_SELINUX test + +commit a4cf61e8bf827dc405a547c314e840dab11fc979 +Author: William Hubbs +Commit: William Hubbs + + selinux: unconditionally include the header and provide stub methods + + If selinux is disabled, then stub methods will be provided instead of + calling the real methods. This removes some warnings about unused + parameters which used to be covered up with #ifdef HAVE_SELINUX. + + Signed-off-by: Jason Zaman + +commit d38cc8f2210e839c4935208917138e4809ece758 +Author: William Hubbs +Commit: William Hubbs + + checkpath: fix warning about selinux_on being unused + +commit f085ae400c60289f61d99e9e80ce037beedf38b4 +Author: Doug Freed +Commit: William Hubbs + + Fix some compiler warnings + + librc: Fix C90 warning (mixed declaration and code) + rc: Fix warning about discarding const qualifier + + Fixes #45. + +commit c1faafcad8197a821282b8e56a10132e27eb5d9f +Author: Will Miles +Commit: William Hubbs + + start-stop-daemon: Fix regression for --test + + The previous fix to --test (PR #34) prevented reading one too many + arguments when --exec -or --name was not specified, but created a + regression where the last argument would not print if either of those + arguments was specified. This corrects the issue. + + Fixes #41. + +commit de93587affb17675e6f7cab2b85613d61e11b98b +Author: William Hubbs +Commit: Doug Freed + + Silence warning about _DEFAULT_SOURCE for Linux/glibc + + In >=glibc-2.20, the _BSD_SOURCE macro is deprecated in favor of + _DEFAULT_SOURCE. This adds -D_DEFAULT_SOURCE to CPPFLAGS on Linux. + + Fixes #44 + +commit be497229b64613ebfbc4073985107a275d49f78e +Author: Anthony Donnelly +Commit: William Hubbs + + Fix savecore service on FreeBSD + + savecore -C only needs the dumpdevice otherwise it causes an error on startup. + + This fixes #40. + +commit e16b7183e90090ecee539697508582d208859a8b +Author: William Hubbs +Commit: William Hubbs + + mk/os-GNU.mk: fix typo + + MAX_PATH should have been PATH_MAX + +commit ccd83a5e9cc24833e1ab098cac1688f69ab6e9b6 +Author: Will Miles +Commit: William Hubbs + + savecache: Make sure cache directory exists before running checkpath + + checkpath -W can fail if the specified path doesn't actually exist yet. + In this case savecache script should attempt to create the path if it is + missing, however it is pre-empted by the checkpath call. This patch adds + an explicit existence test before executing checkpath. + + This fixes #36. + +commit 7bbb73574b44972b0c1b364e24f71623068d7a1c +Author: Robin H. Johnson +Commit: William Hubbs + + bootmisc: clean_run safety improvements. + + If /tmp or / are read-only, the clean_run function can fail in some very + bad ways. + + 1. dir=$(mktemp -d) returns an EMPTY string on error. + 2. "mount -o bind / $dir", and don't check the result of that, + 3. "rm -rf $dir/run/*", which removes the REAL /run contents + 4. box gets very weird from this point forward + + Signed-Off-By: Robin H. Johnson + Signed-Off-By: Chip Parker + Reported-by: Chip Parker + Tested-by: Chip Parker + +commit a0378f38713e630e1af9101c2ece5d27ca2130fe +Author: William Hubbs +Commit: William Hubbs + + checkpath: do not chown or chmod symbolic links + + This is another security fix. If you use chown() or chmod() on a + symbolic link, it affects the referenced file, not the symbolic link + itself. + + X-Gentoo-Bug: 540006 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=540006 + +commit 423f82bae9f91f1f5a27d30a2542d8884c6f757a +Author: William Hubbs +Commit: William Hubbs + + ChangeLog: show authors and committers + +commit 9dfb85d5d236dd126d13d039eb02a97aa0e6c8ac +Author: William Hubbs +Commit: William Hubbs + + local: fix redirections + + The local service now redirects stdout and stderr for the scripts it + runs to /dev/null unless it is run in verbose mode. + + X-Gentoo-Bug: 537444 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=537444 + +commit 6781667641580fef852ccffc2f42d060f791b354 +Author: William Hubbs +Commit: William Hubbs + + typo fix + +commit b17af3c85fc94ecc12857146ba2133a3782ead52 +Author: William Hubbs +Commit: William Hubbs + + checkpath: security fix for -m and -o options + + Do not change permissions on the target if it is a file and has multiple + hard links. This is necessary because a hard link can be an attack + vector to gain privilege escalation. + + X-Gentoo-Bug: 540006 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=540006 + +commit 3100114bc104741145fb6c1d4b1664759114cc5c +Author: William Hubbs +Commit: William Hubbs + + Add nfsclient to netmount use dependencies + + X-Gentoo-Bug: 537996 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=537996 + +commit 3f80f22e22ec16ed517397fd303c3df99f9340fc +Author: Will Miles +Commit: William Hubbs + + Prioritize local includes and libraries + + This fixes #35. + +commit 8250ac94dfc6156075081e0e2d0986cb51b3098d +Author: Consus +Commit: William Hubbs + + tmpfiles.*: Follow OpenRC's message style + + Just to be consistent. + +commit fbdd669ba7c5d1a67129236b4ffcd76198340a1b +Author: William Hubbs +Commit: William Hubbs + + Makefile: add variable for path to source tree + + Add a new variable, ${TOP}, to the top level makefile, which points to + the path of the source tree. + +commit cddb4aad08615420320f75050042d946b18d2bb5 +Author: Will Miles +Commit: William Hubbs + + Fix off-by-one error in --test argument printout in start-stop-daemon. + + Fixes #34. + +commit 3c5dc0ec7774a72e243da43ac5180ea36a311ad8 +Author: William Hubbs +Commit: William Hubbs + + tmpfiles.dev: pass --boot to tmpfiles.sh so kmod works properly + +commit 7e3a33c8f5ccae03e035cf4c9d1c3c01a0f57b1e +Author: William Hubbs +Commit: William Hubbs + + Add description for cgroup_cleanup + + X-Gentoo-Bug: 535184 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=535184 + +commit 74478830a8d035c078e986b57efd40a5c48bc896 +Author: Doug Freed +Commit: William Hubbs + + fix double free of pidfile + + This fixes a double free of the pidfile variable. For discussion of this + issue, see the bug. + + X-Gentoo-Bug: 531600 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=531600 + +commit 6a9679377f4de257f02de1d16a513df14b4c25ba +Author: William Hubbs +Commit: William Hubbs + + Do not call the shell to evaluate CHANGELOG_LIMIT + + The git log command understands dates such as "1 year ago", so there is + no need to use the date command. + +commit 72186ea3bbbf0b09b88a6f3e1fb23bf04ce1ddad +Author: William Hubbs +Commit: William Hubbs + + etc/rc and etc/rc.shutdown: change references from rc to openrc + +commit 3647db7a27f7a5ca14b33b14effeb945fd986210 +Author: William Hubbs +Commit: William Hubbs + + Add target to create ChangeLog + + This was added by request because some users are requesting a ChangeLog. + + This fixes #29. + +commit 7a92eb888794819a339babd0ee220b6aa3993db1 +Author: William Hubbs +Commit: William Hubbs + + rename git.mk to gitver.mk + + This is a more descriptive name since this file only sets the gitver + variable. + +commit 30cc3cdb76a66c7c0f89a52db4e5cff77b570e31 +Author: William Hubbs +Commit: William Hubbs + + Make sysfs behave like netmount and localmount + + sysfs now mounts all related sysfs file systems and returns success, + like netmount and localmount. + + Also, we now check to make sure the cgroups are not mounted before we + mount them. + + X-Gentoo-Bug: 530138 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=530138 + +commit dff6e4a004afeaa64f4ccb07c7d31bb821b043b4 +Author: S. Gilles +Commit: William Hubbs + + Fix mdoc warning for empty line in rc-update man page. + + X-Gentoo-Bug: 529374 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=529374 + +commit 3fad31a994f7eb14f3f001f3980eb2b0ae8fe4f1 +Author: William Hubbs +Commit: William Hubbs + + init.d: add osclock to ignore patterns + +commit 8d0ca13fbd38e782bae655eca6646dabc8d63899 +Author: William Hubbs +Commit: William Hubbs + + devfs: optionally add missing symbolic links + + If symbolic links for /dev/{fd,stdin,stdout,stderr,core} do not exist + once /dev is mounted, we should create them. + +commit 93ba67eff9333e434c969bb8131467f777546764 +Author: William Hubbs +Commit: William Hubbs + + netmount: unmount nfs file systems + +commit 1932360adca3f9fe9b47bcfad7b8bd5efbd33bee +Author: Jason Zaman +Commit: William Hubbs + + Integrate the functionality from runscript_selinux.so + + runscript used to dlopen() runscript_selinux.so. This adds equivalent + functionality directly in to runscript instead. It authenticates with + either PAM or shadow and optionally has a dep on audit. + + X-Gentoo-Bug: 517450 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=517450 + +commit be952bebb3647069fb93b9791ee3439698f697ca +Author: Alexander Vershilov +Commit: William Hubbs + + Fix incorrect handling of chroot option. + Fixes #28. + + X-Gentoo-Bug: #527370 + X-Gentoo-Bug-Url: https://bugs.gentoo.org/show_bug.cgi?id=527370 + +commit 0bfde472d0154f14ea88c0f5ddd21a510443d713 +Author: Ralph Sennhauser +Commit: William Hubbs + + Add osclock service + + This scripts sole purpose is to "provide clock" on OSs that already + take care of the clock being properly set. + +commit ba0a11fc94d303e208dd364b06c1c2a75bcdd62b +Author: Johan Bergström +Commit: William Hubbs + + Pass ncurses cflags to build + + Fixes #25 + Note from William Hubbs: + The original patch overwrote CFLAGS. I modified this patch to add the + ncurses cflags to CPPFLAGS instead of overwriting CFLAGS. + +commit 7700e6fe796cabfa22eefddc024d66257a28d4dc +Author: William Hubbs +Commit: William Hubbs + + Fix compile errors created by bundling queue.h + +commit 6ca79042b9aa9752e577346e6f355356ef8f2f9a +Author: Anthony G. Basile +Commit: William Hubbs + + helpers.h, start-stop-daemon.c: remove uneeded macros + + TAILQ_CONCAT, TAILQ_FOREACH_SAFE and LIST_FOREACH_SAFE are defined + in our bundled queue.h and are no longer required. + +commit 1e0a4bebdefd06af09ba8f2459287d3ca28f89d7 +Author: Anthony G. Basile +Commit: William Hubbs + + Bundle from NetBSD + + We are bundling this to allow building on musl-based systems since musl + does not include . + +commit ca6b86be44fc7ed618a7ab3bd021e208d38878b1 +Author: William Hubbs +Commit: William Hubbs + + Fix all tests for GNU/kFreeBSD + + It is necessary to check for both the kernel and c library because + __FreeBSD_kernel is also defined on native FreeBSD [1]. + + [1] http://sourceforge.net/p/predef/wiki/OperatingSystems/ + +commit 4ac289b5397a688393c596a9a01651c94d3b5711 +Author: Gabriele Giacone <1o5g4r8o@gmail.com> +Commit: William Hubbs + + Fix rc_svcdir for GNU/Hurd + +commit 875f03e27c3475675f7b9572b071dd8c26257be7 +Author: Svante Signell +Commit: William Hubbs + + fix defines for GNU/Hurd + +commit 203b754f843fe6af0a40e983d557a9cdbc89f84b +Author: Svante Signell +Commit: William Hubbs + + add missing files for GNU/Hurd + +commit 89c8a62a1078e770e12c47f06c8dbc9c2924e771 +Author: Gabriele Giacone <1o5g4r8o@gmail.com> +Commit: William Hubbs + + Fix rc_svcdir for GNU/kFreeBSD + +commit d8e1d9a6edf94ecac580e80e1113f4fdbdc5a23b +Author: Gabriele Giacone <1o5g4r8o@gmail.com> +Commit: William Hubbs + + Add missing files for GNU/kFreeBSD + +commit 3f82edbeb9251149c6aff071d6537379af4e5eea +Author: Svante Signell +Commit: William Hubbs + + Fix GNU/kFreeBSD port + + Check for __FreeBSD_kernel instead of __GLIBC__ in source files. + + note from William Hubbs: + I was told this is a better check for GNU/kFreeBSD than checking the + C library the source is being compiled against. + GNU/kFreeBSD than checking which library we are using. + +commit 86e9aa0d36813e2630c6613cd71c3ce8db642f71 +Author: Anthony G. Basile +Commit: Anthony G. Basile + + einfo.h, rc.h.in: simplify __BEGIN_DECLS logic + + There is no need to redefine __BEGIN_DECLS and __END_DECLS. + We simplify the logic here and avoid undefining these macros. + +commit 4a08517cac3c68c232694db7288654b58b68b8ba +Author: Anthony G. Basile +Commit: Anthony G. Basile + + einfo.h, rc.h.in: ensure __BEGIN_DECLS is defined + + Some Standard C Libraries, like musl, don't define __BEGIN_DECLS + or __END_DECLS. We add some ifdef magic to ensure these are + available. + +commit 9bf789f78890c8b5879d29acb9fb0e23285baee4 +Author: William Hubbs +Commit: William Hubbs + + Update news file wrt chroot variable + +commit f9acd65497c6e561fbf5420386a99d681fede859 +Author: Alexander Vershilov +Commit: William Hubbs + + librc:look for the pid file in a chroot if defined + + X-Gentoo-Bug: 524388 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=524388 + +commit 8c7ea4e9e8da500877a514402bbe90aababda2d6 +Author: William Hubbs +Commit: William Hubbs + + runscript.sh: add chroot support + + This adds support for a chroot variable which will be passed to the + start-stop-daemon --chroot switch to runscript.sh when starting a + daemon. This also needs to be saved so it can be used in locating the + pid file when stopping the daemon. + + X-Gentoo-Bug: 524388 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=524388 + +commit 5f1439f1aac14618592789042e05daaf80f55a15 +Author: William Hubbs +Commit: William Hubbs + + Add NEWS file + +commit 85da4a5e2621dc5f5356d440735aa058008a1b7e +Author: William Hubbs +Commit: William Hubbs + + add back nfs and nfs4 file systems + + Fix gentoo bug #427996 correctly. + We should attempt to mount the file systems, but not try to start the + daemons. The previous fix removed mounting the file systems as well as + starting the daemons. + + X-Gentoo-Bug: 508574 + X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=508574 diff --git a/FEATURE-REMOVAL-SCHEDULE b/FEATURE-REMOVAL-SCHEDULE deleted file mode 100644 index 344711f..0000000 --- a/FEATURE-REMOVAL-SCHEDULE +++ /dev/null @@ -1,72 +0,0 @@ -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. - ---------------------------- - -What: Service pause action - -When: 1.0 - -Why: ... - -Who: - ---------------------------- - -What: 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: - ---------------------------- - -What: 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: - ---------------------------- - -What: support for the opts variable in service scripts - -When: 1.0 - -Why: Depprecated in favor of extra_commands, extra_started_commands - and extra_stopped_commands. - -Who: - ---------------------------- - -What: support for local_start and local_stop - -When: 1.0 - -Why: Depprecated in favor of executable scripts in @SYSCONFDIR@/local.d - -Who: - ---------------------------- - -What: the mtab service script - -When: make warnings more visible in 1.0, remove in 2.0 - -Why: /etc/mtab should be a symbolic link to /proc/self/mounts on modern - Linux systems - -Who: - ---------------------------- diff --git a/FEATURE-REMOVAL-SCHEDULE.md b/FEATURE-REMOVAL-SCHEDULE.md new file mode 100644 index 0000000..a1b19ab --- /dev/null +++ b/FEATURE-REMOVAL-SCHEDULE.md @@ -0,0 +1,61 @@ +# 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: Depprecated in favor of extra_commands, extra_started_commands + and extra_stopped_commands. + +Who: + +## support for local_start and local_stop + +When: 1.0 + +Why: Depprecated in favor of executable scripts in @SYSCONFDIR@/local.d + +Who: + +## the mtab service script + +When: make warnings more visible in 1.0, remove in 2.0 + +Why: /etc/mtab should be a symbolic link to /proc/self/mounts on modern + Linux systems + +Who: diff --git a/HISTORY.md b/HISTORY.md new file mode 100644 index 0000000..03dde1d --- /dev/null +++ b/HISTORY.md @@ -0,0 +1,57 @@ +# 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. + +[1] http://www.gentoo.org/news/en/gwn/20040426-newsletter.xml diff --git a/Makefile b/Makefile index 119a27e..dd06bdd 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,10 @@ # Copyright (c) 2007-2009 Roy Marples # Released under the 2-clause BSD license. -include Makefile.inc +TOP:= ${dir ${realpath ${firstword ${MAKEFILE_LIST}}}} +MK= ${TOP}/mk + +include ${TOP}/Makefile.inc SUBDIR= conf.d etc init.d local.d man scripts sh src sysctl.d @@ -17,12 +20,11 @@ INSTALLAFTER= _installafter -MK= mk include ${MK}/sys.mk include ${MK}/os.mk include ${MK}/subdir.mk include ${MK}/dist.mk -include ${MK}/git.mk +include ${MK}/gitver.mk _installafter: ifeq (${MKPREFIX},yes) diff --git a/Makefile.inc b/Makefile.inc index c6dc0c1..dda4fe4 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -1,3 +1,3 @@ NAME= openrc -VERSION= 0.13.1 +VERSION= 0.18.3 PKG= ${NAME}-${VERSION} diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..d67ed60 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,97 @@ +# 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.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 b/README deleted file mode 100644 index 12eb50c..0000000 --- a/README +++ /dev/null @@ -1,94 +0,0 @@ -OpenRC README - - -Installation ------------- -make install -Yup, that simple. Works with GNU make. - -You may wish to tweak the installation with the below arguments -PROGLDFLAGS=-static -LIBNAME=lib64 -DESTDIR=/tmp/openrc-image -MKNET=no -MKPAM=pam -MKPREFIX=yes -MKPKGCONFIG=no -MKSELINUX=yes -MKSTATICLIBS=no -MKTERMCAP=ncurses -MKTERMCAP=termcap -MKTOOLS=yes -PKG_PREFIX=/usr/pkg -LOCAL_PREFIX=/usr/local -PREFIX=/usr/local - -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 debug memory under valgrind, add -DDEBUG_MEMORY to your CPPFLAGS -so that all malloc memory should be freed at exit. -If you are building OpenRC for a Gentoo Prefix installation, add -MKPREFIX=yes. - -You can also brand OpenRC if you so wish like so -BRANDING=\"Gentoo/$(uname -s)\" - -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. - -init.d.misc is 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. - -As of 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. - -As of OpenRC-0.13, two binaries have been renamed due to naming -conflicts with other software. The /sbin/rc binary was renamed to -/sbin/openrc, and /sbin/runscript was renamed to /sbin/openrc-run. - -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, they -only show in verbose mode in this release due to the level of noise they -produce. - -Also, 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. - -Reporting Bugs --------------- -Since Gentoo Linux is hosting OpenRC development, Bugs should go to -the Gentoo Bugzilla: - http://bugs.gentoo.org/ -They should be filed under the "Gentoo Hosted Projects" product and -the "openrc" component. diff --git a/README.busybox b/README.busybox deleted file mode 100644 index b084d42..0000000 --- a/README.busybox +++ /dev/null @@ -1,31 +0,0 @@ -Using Busybox as your Default Shell ------------------------------------ - - -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_IP -- The ip applet doesn't support the "scope" modifier for -"ip route add" and "ip address add". - -There is work to get most of these supported by busybox, so this file -will be updated as things change. diff --git a/README.history b/README.history deleted file mode 100644 index 2aeca1b..0000000 --- a/README.history +++ /dev/null @@ -1,55 +0,0 @@ -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. - -[1] http://www.gentoo.org/news/en/gwn/20040426-newsletter.xml diff --git a/README.md b/README.md new file mode 100644 index 0000000..7fadd23 --- /dev/null +++ b/README.md @@ -0,0 +1,99 @@ +# 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 +MKTERMCAP=ncurses +MKTERMCAP=termcap +MKTOOLS=yes +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 debug memory under valgrind, add `-DDEBUG_MEMORY` +to your `CPPFLAGS` so that all malloc memory should be freed at exit. + +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. + +`init.d.misc` is 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. + +## 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/STYLE b/STYLE deleted file mode 100644 index 44ff1d5..0000000 --- a/STYLE +++ /dev/null @@ -1,85 +0,0 @@ -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: - http://en.wikipedia.org/wiki/Indent_style#BSD_KNF_style -Basically, it's like K&R/LKML, but wrapped lines that are indented use 4 spaces. - -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 -[1], so I would like to thank the author. - -### cut here ### -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) -### cut here ### - -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://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html - 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/conf.d/Makefile b/conf.d/Makefile index 93476fc..34a3b07 100644 --- a/conf.d/Makefile +++ b/conf.d/Makefile @@ -15,7 +15,7 @@ CONF-FreeBSD= ipfw moused powerd rarpd savecore syscons -CONF-Linux= consolefont devfs dmesg hwclock keymaps killprocs modules +CONF-Linux= consolefont devfs dmesg hwclock keymaps killprocs modules mtab CONF-NetBSD= moused rarpd savecore 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/network.GNU-kFreeBSD.in b/conf.d/network.GNU-kFreeBSD.in new file mode 100644 index 0000000..9f49b81 --- /dev/null +++ b/conf.d/network.GNU-kFreeBSD.in @@ -0,0 +1,4 @@ + +# You can assign a default route +#defaultroute="192.168.0.1" +#defaultroute6="2001:a:b:c" diff --git a/conf.d/network.GNU.in b/conf.d/network.GNU.in new file mode 100644 index 0000000..78aa412 --- /dev/null +++ b/conf.d/network.GNU.in @@ -0,0 +1,4 @@ + +# You can assign a default route +#defaultroute="gw 192.168.0.1" +#defaultroute6="gw 2001:a:b:c" diff --git a/conf.d/staticroute.GNU-kFreeBSD.in b/conf.d/staticroute.GNU-kFreeBSD.in new file mode 100644 index 0000000..9f54152 --- /dev/null +++ b/conf.d/staticroute.GNU-kFreeBSD.in @@ -0,0 +1,5 @@ +# Separate multiple routes using ; or new lines. + +# Example static routes. 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" diff --git a/conf.d/staticroute.GNU.in b/conf.d/staticroute.GNU.in new file mode 100644 index 0000000..58d77e3 --- /dev/null +++ b/conf.d/staticroute.GNU.in @@ -0,0 +1,7 @@ +# Separate multiple routes using ; or new lines. +# /etc/route.conf(5) takes precedence over this configuration. + +# Example static routes. See route(8) for syntax. +# 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" diff --git a/etc/rc.conf.GNU b/etc/rc.conf.GNU new file mode 100644 index 0000000..a9f054a --- /dev/null +++ b/etc/rc.conf.GNU @@ -0,0 +1,14 @@ +############################################################################## +# GNU/Hurd SPECIFIC OPTIONS + +# This is the subsystem type. Valid options on GNU/Hurd: +# "" - nothing special +# "subhurd" - Hurd subhurds (to be checked) +# If this is commented out, automatic detection will be used. +# +# This should be set to the value representing the environment this file is +# PRESENTLY in, not the virtualization the environment is capable of. +#rc_sys="" +# This is the number of tty's used in most of the rc-scripts (like +# consolefont, numlock, etc ...) +#rc_tty_number=6? diff --git a/etc/rc.conf.Linux b/etc/rc.conf.Linux index 79bd971..f04f96e 100644 --- a/etc/rc.conf.Linux +++ b/etc/rc.conf.Linux @@ -2,14 +2,15 @@ # LINUX SPECIFIC OPTIONS # This is the subsystem type. Valid options on Linux: -# "" - nothing special -# "lxc" - Linux Containers -# "openvz" - Linux OpenVZ -# "prefix" - Prefix -# "uml" - Usermode Linux -# "vserver" - Linux vserver -# "xen0" - Xen0 Domain -# "xenU" - XenU Domain +# "" - nothing special +# "lxc" - Linux Containers +# "openvz" - Linux OpenVZ +# "prefix" - Prefix +# "uml" - Usermode Linux +# "vserver" - Linux vserver +# "systemd-nspawn" - Container created by the systemd-nspawn utility +# "xen0" - Xen0 Domain +# "xenU" - XenU Domain # If this is commented out, automatic detection will be used. # # This should be set to the value representing the environment this file is @@ -61,11 +62,20 @@ # 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 yu want all of the processes in a service's cgroup # killed when the service is stopped or restarted. diff --git a/etc/rc.conf.in b/etc/rc.conf.in index 69a5cf2..1b78c88 100644 --- a/etc/rc.conf.in +++ b/etc/rc.conf.in @@ -116,6 +116,9 @@ #SSD_NICELEVEL="-19" # 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 diff --git a/etc/rc.in b/etc/rc.in index 864bd1b..e6fb38b 100644 --- a/etc/rc.in +++ b/etc/rc.in @@ -10,9 +10,9 @@ trap : SIGINT trap "echo 'Boot interrupted'; exit 1" SIGQUIT -/sbin/rc sysinit || exit 1 -/sbin/rc boot || exit 1 -/sbin/rc default +/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 diff --git a/etc/rc.shutdown.in b/etc/rc.shutdown.in index 216e2f8..88c7b93 100644 --- a/etc/rc.shutdown.in +++ b/etc/rc.shutdown.in @@ -14,4 +14,4 @@ [ -z "$TERM" -o "$TERM" = "dumb" ] && TERM="@TERM@" && export TERM action=${1:-shutdown} -exec /sbin/rc "${action}" +exec /sbin/openrc "${action}" diff --git a/init.d/.gitignore b/init.d/.gitignore index 3a5d003..04f725d 100644 --- a/init.d/.gitignore +++ b/init.d/.gitignore @@ -23,6 +23,7 @@ mount-ro mtab numlock +osclock procfs staticroute sysfs diff --git a/init.d/Makefile b/init.d/Makefile index c2c3ea1..a662f8d 100644 --- a/init.d/Makefile +++ b/init.d/Makefile @@ -2,8 +2,8 @@ DIR= ${INITDIR} SRCS= bootmisc.in fsck.in hostname.in local.in localmount.in loopback.in \ - netmount.in root.in savecache.in swap.in swapfiles.in \ - tmpfiles.setup.in swclock.in sysctl.in urandom.in ${SRCS-${OS}} + netmount.in osclock.in root.in savecache.in swap.in swapfiles.in \ + tmpfiles.setup.in swclock.in sysctl.in urandom.in s6-svscan.in ${SRCS-${OS}} BIN= ${OBJS} # Are we installing our network scripts? @@ -21,7 +21,7 @@ SRCS-FreeBSD+= adjkerntz.in devd.in dumpon.in encswap.in ipfw.in \ mixer.in nscd.in powerd.in syscons.in -SRCS-Linux= devfs.in dmesg.in hwclock.in consolefont.in keymaps.in \ +SRCS-Linux= binfmt.in devfs.in dmesg.in hwclock.in consolefont.in keymaps.in \ killprocs.in modules.in mount-ro.in mtab.in numlock.in \ procfs.in sysfs.in termencoding.in tmpfiles.dev.in diff --git a/init.d/binfmt.in b/init.d/binfmt.in new file mode 100644 index 0000000..46e2220 --- /dev/null +++ b/init.d/binfmt.in @@ -0,0 +1,20 @@ +#!@SBINDIR@/openrc-run +# Copyright 2015 William Hubbs +# Released under the 2-clause BSD license. + +description="Register misc binary format handlers" + +depend() +{ + after procfs + use modules devfs + keyword -openvz -prefix -systemd-nspawn -vserver -lxc +} + +start() +{ + ebegin "Loading custom binary format handlers" + "$RC_LIBEXECDIR"/sh/binfmt.sh + eend $? + return 0 +} diff --git a/init.d/bootmisc.in b/init.d/bootmisc.in index 2ec075f..1a05920 100644 --- a/init.d/bootmisc.in +++ b/init.d/bootmisc.in @@ -119,11 +119,31 @@ { [ "$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) - mount --bind / $dir - rm -rf $dir/run/* - umount $dir - rm -rf $dir + 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() @@ -193,10 +213,13 @@ if yesno $log_dmesg; then if $logw || checkpath -W /var/log; then # Create an 'after-boot' dmesg log - if [ "$RC_SYS" != VSERVER -a "$RC_SYS" != OPENVZ -a "$RC_SYS" != LXC ]; then - dmesg > /var/log/dmesg - chmod 640 /var/log/dmesg - fi + case "$RC_SYS" in + VSERVER|OPENVZ|LXC|SYSTEMD-NSPAWN) ;; + *) + dmesg > /var/log/dmesg + chmod 640 /var/log/dmesg + ;; + esac fi fi diff --git a/init.d/consolefont.in b/init.d/consolefont.in index 7ec93d6..e56cc09 100644 --- a/init.d/consolefont.in +++ b/init.d/consolefont.in @@ -8,7 +8,7 @@ { need localmount termencoding after hotplug bootmisc - keyword -openvz -prefix -uml -vserver -xenu -lxc + keyword -openvz -prefix -systemd-nspawn -uml -vserver -xenu -lxc } start() diff --git a/init.d/devfs.in b/init.d/devfs.in index bcdbdcd..86bda24 100644 --- a/init.d/devfs.in +++ b/init.d/devfs.in @@ -8,7 +8,7 @@ { provide dev-mount before dev - keyword -prefix -vserver -lxc + keyword -prefix -systemd-nspawn -vserver -lxc } mount_dev() @@ -69,7 +69,14 @@ # so udev can add its start-message to dmesg [ -c /dev/kmsg ] || mknod -m 660 /dev/kmsg c 1 11 - # Mount required stuff as user may not have then in /etc/fstab + # 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 devpts" \ diff --git a/init.d/dmesg.in b/init.d/dmesg.in index 5b001fc..6e1ae53 100644 --- a/init.d/dmesg.in +++ b/init.d/dmesg.in @@ -7,7 +7,7 @@ depend() { before dev modules - keyword -lxc -prefix -vserver + keyword -lxc -prefix -systemd-nspawn -vserver } start() diff --git a/init.d/fsck.in b/init.d/fsck.in index d3b6607..79e74e5 100644 --- a/init.d/fsck.in +++ b/init.d/fsck.in @@ -9,7 +9,7 @@ depend() { use dev clock modules - keyword -jail -openvz -prefix -timeout -vserver -lxc -uml + keyword -jail -openvz -prefix -systemd-nspawn -timeout -vserver -lxc -uml } _abort() { diff --git a/init.d/hostname.in b/init.d/hostname.in index eaeb79a..5b8b75d 100644 --- a/init.d/hostname.in +++ b/init.d/hostname.in @@ -5,7 +5,7 @@ description="Sets the hostname of the machine." depend() { - keyword -prefix -lxc + keyword -prefix -systemd-nspawn -lxc } start() diff --git a/init.d/hwclock.in b/init.d/hwclock.in index 4b0da02..1dde2d6 100644 --- a/init.d/hwclock.in +++ b/init.d/hwclock.in @@ -28,7 +28,7 @@ else before * fi - keyword -openvz -prefix -uml -vserver -xenu -lxc + keyword -openvz -prefix -systemd-nspawn -uml -vserver -xenu -lxc } setupopts() diff --git a/init.d/keymaps.in b/init.d/keymaps.in index eeed22a..af42485 100644 --- a/init.d/keymaps.in +++ b/init.d/keymaps.in @@ -8,7 +8,7 @@ { need localmount termencoding after bootmisc - keyword -openvz -prefix -uml -vserver -xenu -lxc + keyword -openvz -prefix -systemd-nspawn -uml -vserver -xenu -lxc } start() diff --git a/init.d/local.in b/init.d/local.in index 180735d..254135c 100644 --- a/init.d/local.in +++ b/init.d/local.in @@ -14,12 +14,13 @@ { ebegin "Starting local" - local file has_errors=0 retval + 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}\"" - "${file}" 2>&1 >/dev/null + eval "${file}" $redirect retval=$? if [ ${retval} -ne 0 ]; then has_errors=1 @@ -52,12 +53,13 @@ { ebegin "Stopping local" - local file has_errors=0 retval + 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}\"" - "${file}" 2>&1 >/dev/null + eval "${file}" $redirect retval=$? if [ ${retval} -ne 0 ]; then has_errors=1 diff --git a/init.d/localmount.in b/init.d/localmount.in index bf3dd0f..1812089 100644 --- a/init.d/localmount.in +++ b/init.d/localmount.in @@ -9,13 +9,13 @@ need fsck use lvm modules mtab after lvm modules - keyword -jail -prefix -vserver -lxc + keyword -jail -prefix -systemd-nspawn -vserver -lxc } start() { # Mount local filesystems in /etc/fstab. - local types="noproc" x= no_netdev= + local types="noproc" x= no_netdev= rc= for x in $net_fs_list $extra_net_fs_list; do types="${types},no${x}" done @@ -29,9 +29,11 @@ ebegin "Mounting local filesystems" mount -at "$types" $no_netdev eend $? "Some local filesystem failed to mount" - - # Always return 0 - some local mounts may not be critical for boot - return 0 + rc=$? + if [ "$RC_UNAME" != Linux ]; then + rc=0 + fi + return $rc } stop() @@ -63,6 +65,33 @@ . "$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 diff --git a/init.d/loopback.in b/init.d/loopback.in index ec45a98..4602dd0 100644 --- a/init.d/loopback.in +++ b/init.d/loopback.in @@ -6,7 +6,7 @@ depend() { - keyword -jail -prefix -vserver + keyword -jail -prefix -systemd-nspawn -vserver } start() diff --git a/init.d/modules.in b/init.d/modules.in index acce97e..e10e7ae 100644 --- a/init.d/modules.in +++ b/init.d/modules.in @@ -7,7 +7,7 @@ depend() { use isapnp - keyword -openvz -prefix -vserver -lxc + keyword -openvz -prefix -systemd-nspawn -vserver -lxc } start() diff --git a/init.d/mount-ro.in b/init.d/mount-ro.in index a6438b5..d3f7acc 100644 --- a/init.d/mount-ro.in +++ b/init.d/mount-ro.in @@ -7,7 +7,7 @@ depend() { need killprocs savecache - keyword -openvz -prefix -vserver -lxc + keyword -openvz -prefix -systemd-nspawn -vserver -lxc } start() diff --git a/init.d/mtab.in b/init.d/mtab.in index e38d33e..f9febe6 100644 --- a/init.d/mtab.in +++ b/init.d/mtab.in @@ -7,33 +7,33 @@ depend() { need root - keyword -prefix + keyword -prefix -systemd-nspawn } start() { - if [ -L /etc/mtab ]; then - return 0 + 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 - - ebegin "Updating /etc/mtab" - vewarn "The support for updating /etc/mtab as a file is" - vewarn "deprecated and will be removed in the future." - vewarn "Please run the following command as root on your system." - vewarn - vewarn "ln -snf /proc/self/mounts /etc/mtab" - if ! echo 2>/dev/null >/etc/mtab; then - ewend 1 "/etc/mtab is not updateable" - return 0 - fi - - # 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~~ - eend 0 + eend $rc "/etc is not writable; unable to create /etc/mtab" + return 0 } diff --git a/init.d/netmount.in b/init.d/netmount.in index 39ab0bc..d1f3cff 100644 --- a/init.d/netmount.in +++ b/init.d/netmount.in @@ -2,28 +2,20 @@ # Copyright (c) 2007-2009 Roy Marples # Released under the 2-clause BSD license. -description="Mounts network shares, other than NFS, according to /etc/fstab." -# We skip all NFS shares in this script because they require extra -# daemons to be running on the client in order to work correctly. -# It is best to allow nfs-utils to handle all nfs shares. +description="Mounts network shares according to /etc/fstab." depend() { config /etc/fstab - use afc-client amd autofs openvpn + use afc-client amd nfsclient autofs openvpn use dns - keyword -jail -prefix -vserver -lxc + keyword -jail -prefix -systemd-nspawn -vserver -lxc } start() { local x= fs= rc= for x in $net_fs_list $extra_net_fs_list; do - case "$x" in - nfs|nfs4) - continue - ;; - esac fs="$fs${fs:+,}$x" done @@ -35,7 +27,10 @@ rc=$? fi ewend $rc "Could not mount all network filesystems" - return 0 + if [ "$RC_UNAME" != Linux ]; then + rc=0 + fi + return $rc } stop() @@ -46,14 +41,7 @@ . "$RC_LIBEXECDIR"/sh/rc-mount.sh for x in $net_fs_list $extra_net_fs_list; do - case "$x" in - nfs|nfs4) - continue - ;; - *) - fs="$fs${fs:+,}$x" - ;; - esac + fs="$fs${fs:+,}$x" done if [ -n "$fs" ]; then umount -at $fs || eerror "Failed to simply unmount filesystems" @@ -62,14 +50,7 @@ eindent fs= for x in $net_fs_list $extra_net_fs_list; do - case "$x" in - nfs|nfs4) - continue - ;; - *) - fs="$fs${fs:+|}$x" - ;; - esac + fs="$fs${fs:+|}$x" done [ -n "$fs" ] && fs="^($fs)$" do_unmount umount ${fs:+--fstype-regex} $fs --netdev diff --git a/init.d/numlock.in b/init.d/numlock.in index 1b6d0a5..cdc0da0 100644 --- a/init.d/numlock.in +++ b/init.d/numlock.in @@ -9,7 +9,7 @@ depend() { need localmount - keyword -openvz -prefix -vserver -lxc + keyword -openvz -prefix -systemd-nspawn -vserver -lxc } _setleds() diff --git a/init.d/osclock.in b/init.d/osclock.in new file mode 100644 index 0000000..ce892d2 --- /dev/null +++ b/init.d/osclock.in @@ -0,0 +1,12 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2014 Ralph Sennhauser +# Released under the 2-clause BSD license. + +# Can be used on OSs that take care of the clock. + +description="Provides clock" + +depend() +{ + provide clock +} diff --git a/init.d/procfs.in b/init.d/procfs.in index 636cd20..a9fcb9a 100644 --- a/init.d/procfs.in +++ b/init.d/procfs.in @@ -8,66 +8,20 @@ { use modules devfs need localmount - keyword -openvz -prefix -vserver -lxc + keyword -openvz -prefix -systemd-nspawn -vserver -lxc } start() { - # Make sure we insert usbcore if it's a module - if [ -f /proc/modules -a ! -d /sys/module/usbcore -a ! -d /proc/bus/usb ]; then - modprobe -q usbcore - fi - - [ -e /proc/filesystems ] || return 0 - # Setup Kernel Support for miscellaneous Binary Formats if [ -d /proc/sys/fs/binfmt_misc -a ! -e /proc/sys/fs/binfmt_misc/register ]; then + modprobe -q binfmt-misc 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 - if eend $? ; then - local fmts - ebegin "Loading custom binary format handlers" - fmts=$(grep -hsv -e '^[#;]' -e '^[[:space:]]*$' \ - /run/binfmt.d/*.conf \ - /etc/binfmt.d/*.conf \ - ""/usr/lib/binfmt.d/*.conf) - if [ -n "${fmts}" ]; then - echo "${fmts}" > /proc/sys/fs/binfmt_misc/register - fi eend $? - fi fi fi - - [ "$RC_SYS" = "OPENVZ" ] && return 0 - - # Check what USB fs the kernel support. Currently - # 2.5+ kernels, and later 2.4 kernels have 'usbfs', - # while older kernels have 'usbdevfs'. - if [ -d /proc/bus/usb -a ! -e /proc/bus/usb/devices ]; then - local usbfs=$(grep -Fow usbfs /proc/filesystems || - grep -Fow usbdevfs /proc/filesystems) - if [ -n "$usbfs" ]; then - ebegin "Mounting USB device filesystem [$usbfs]" - local usbgid="$(getent group usb | \ - sed -e 's/.*:.*:\(.*\):.*/\1/')" - mount -t $usbfs \ - -o ${usbgid:+devmode=0664,devgid=$usbgid,}noexec,nosuid \ - usbfs /proc/bus/usb - 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 - return 0 } diff --git a/init.d/root.in b/init.d/root.in index 1668368..0395dd1 100644 --- a/init.d/root.in +++ b/init.d/root.in @@ -7,7 +7,7 @@ depend() { need fsck - keyword -jail -openvz -prefix -vserver -lxc + keyword -jail -openvz -prefix -systemd-nspawn -vserver -lxc } start() diff --git a/init.d/s6-svscan.in b/init.d/s6-svscan.in new file mode 100644 index 0000000..6fdf4e1 --- /dev/null +++ b/init.d/s6-svscan.in @@ -0,0 +1,31 @@ +#!@SBINDIR@/openrc-run +# Copyright (C) 2015 William Hubbs +# Released under the 2-clause BSD license. + +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 index 9040732..5ec1028 100644 --- a/init.d/savecache.in +++ b/init.d/savecache.in @@ -7,43 +7,53 @@ start() { if [ -e "$RC_SVCDIR"/clock-skewed ]; then - ewarn "WARNING: clock skew detected!" + ewarn "Clock skew detected!" if ! yesno "${RC_GOINGDOWN}"; then eerror "Not saving deptree cache" return 1 fi fi - if ! checkpath -W "$RC_LIBEXECDIR"; then - ewarn "WARNING: ${RC_LIBEXECDIR} is not writable!" - if ! yesno "${RC_GOINGDOWN}"; then - ewarn "Unable to save deptree cache" + 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 - return 0 + 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= - if [ ! -d "$RC_LIBEXECDIR"/cache ]; then - rm -rf "$RC_LIBEXECDIR"/cache - if ! mkdir "$RC_LIBEXECDIR"/cache; then - rc=$? - if yesno "${RC_GOINGDOWN}"; then - rc=0 - fi - eend $rc - return $rc - fi - fi - local save= + local rc=0 save= for x in deptree depconfig shutdowntime softlevel nettree rc.log; do [ -e "$RC_SVCDIR/$x" ] && save="$save $RC_SVCDIR/$x" done if [ -n "$save" ]; then - cp -p $save "$RC_LIBEXECDIR"/cache 2>/dev/null + cp -p $save "$RC_LIBEXECDIR"/cache + rc=$? fi - rc=$? if yesno "${RC_GOINGDOWN}"; then - rc=0 + if [ $rc -ne 0 ]; then + eerror "Unable to save dependency cache" + fi + eend 0 fi - eend $rc + eend $rc "Unable to save dependency cache" } diff --git a/init.d/savecore.in b/init.d/savecore.in index b1b6ca3..cb00564 100644 --- a/init.d/savecore.in +++ b/init.d/savecore.in @@ -23,7 +23,7 @@ # 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_dir" $dump_device >/dev/null + savecore -C $dump_device >/dev/null else ls "$dump_dir"/bsd* > /dev/null 2>&1 fi diff --git a/init.d/swap.in b/init.d/swap.in index a64ea60..41d4eac 100644 --- a/init.d/swap.in +++ b/init.d/swap.in @@ -5,7 +5,7 @@ depend() { before localmount - keyword -jail -openvz -prefix -vserver -lxc + keyword -jail -openvz -prefix -systemd-nspawn -vserver -lxc } start() diff --git a/init.d/swapfiles.in b/init.d/swapfiles.in index 1c80500..5c80762 100644 --- a/init.d/swapfiles.in +++ b/init.d/swapfiles.in @@ -5,7 +5,7 @@ depend() { need localmount - keyword -jail -openvz -prefix -vserver -lxc + keyword -jail -openvz -prefix -systemd-nspawn -vserver -lxc } start() diff --git a/init.d/swclock.in b/init.d/swclock.in index b74d49a..b3ea17b 100644 --- a/init.d/swclock.in +++ b/init.d/swclock.in @@ -8,7 +8,7 @@ { before * provide clock - keyword -openvz -prefix -uml -vserver -xenu -lxc + keyword -openvz -prefix -systemd-nspawn -uml -vserver -xenu -lxc } # swclock is an OpenRC built in diff --git a/init.d/sysctl.GNU-kFreeBSD.in b/init.d/sysctl.GNU-kFreeBSD.in new file mode 100644 index 0000000..92d5868 --- /dev/null +++ b/init.d/sysctl.GNU-kFreeBSD.in @@ -0,0 +1,31 @@ +#!@SBINDIR@/openrc-run +# Copyright (c) 2007-2009 Roy Marples +# Released under the 2-clause BSD license. + +depend() +{ + before bootmisc logger + keyword -prefix +} + +start() +{ + [ -e /etc/sysctl.conf ] || return 0 + local retval=0 var= comments= conf= + ebegin "Configuring kernel parameters" + eindent + for conf in @SYSCONFDIR@/sysctl.conf @SYSCONFDIR@/sysctl.d/*.conf; do + if [ -r "$conf" ]; then + vebegin "applying $conf" + while read var comments; do + case "$var" in + ""|"#"*) continue;; + esac + sysctl -w "$var" >/dev/null || retval=1 + done < "$conf" + veend $retval + fi + done + eoutdent + eend $retval "Some errors were encountered" +} diff --git a/init.d/sysctl.GNU.in b/init.d/sysctl.GNU.in new file mode 100644 index 0000000..1bc325e --- /dev/null +++ b/init.d/sysctl.GNU.in @@ -0,0 +1,32 @@ +#!@PREFIX@/sbin/openrc-run +# Copyright (c) 2007-2009 Roy Marples +# Released under the 2-clause BSD license. +#FIXME: Modify for GNU/Hurd + +depend() +{ + before bootmisc logger + keyword -prefix +} + +start() +{ + [ -e /etc/sysctl.conf ] || return 0 + local retval=0 var= comments= conf= + ebegin "Configuring kernel parameters" + eindent + for conf in @SYSCONFDIR@/sysctl.conf @SYSCONFDIR@/sysctl.d/*.conf; do + if [ -r "$conf" ]; then + vebegin "applying $conf" + while read var comments; do + case "$var" in + ""|"#"*) continue;; + esac + sysctl -w "$var" >/dev/null || retval=1 + done < "$conf" + veend $retval + fi + done + eoutdent + eend $retval "Some errors were encountered" +} diff --git a/init.d/sysctl.Linux.in b/init.d/sysctl.Linux.in index a1a8897..186c6ed 100644 --- a/init.d/sysctl.Linux.in +++ b/init.d/sysctl.Linux.in @@ -5,12 +5,15 @@ depend() { before bootmisc logger - keyword -prefix -vserver + keyword -prefix -systemd-nspawn -vserver } start() { + local quiet + yesno $rc_verbose || quiet=-q + ebegin "Configuring kernel parameters" - sysctl --system + sysctl ${quiet} --system eend $? "Unable to configure some kernel parameters" } diff --git a/init.d/sysfs.in b/init.d/sysfs.in index 67485c2..f0bb313 100644 --- a/init.d/sysfs.in +++ b/init.d/sysfs.in @@ -8,7 +8,7 @@ depend() { - keyword -lxc -prefix -vserver + keyword -lxc -prefix -systemd-nspawn -vserver } mount_sys() @@ -82,6 +82,15 @@ 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 up kernel support for efivarfs # slightly complicated, as if it's build as a module but NOT yet loaded, # it will NOT appear in /proc/filesystems yet @@ -107,13 +116,14 @@ mount -n -t cgroup \ -o none,${sysfs_opts},name=openrc,release_agent="$agent" \ openrc /sys/fs/cgroup/openrc - echo 1 > /sys/fs/cgroup/openrc/notify_on_release + 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) mkdir /sys/fs/cgroup/${name} + 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} ;; @@ -129,25 +139,13 @@ restorecon -rF /sys/fs/cgroup >/dev/null 2>&1 eend $? fi - - return 0 } start() { - local retval mount_sys - retval=$? - if [ $retval -eq 0 ]; then - mount_misc - retval=$? - fi - if [ $retval -eq 0 ]; then - mount_cgroups - retval=$? - fi - + mount_misc + mount_cgroups restorecon_sys - - return $retval + return 0 } diff --git a/init.d/termencoding.in b/init.d/termencoding.in index d3aa027..59ca625 100644 --- a/init.d/termencoding.in +++ b/init.d/termencoding.in @@ -9,7 +9,7 @@ depend() { - keyword -lxc -openvz -prefix -uml -vserver -xenu + keyword -lxc -openvz -prefix -systemd-nspawn -uml -vserver -xenu use root after bootmisc } diff --git a/init.d/tmpfiles.dev.in b/init.d/tmpfiles.dev.in index 3566928..c70a29e 100644 --- a/init.d/tmpfiles.dev.in +++ b/init.d/tmpfiles.dev.in @@ -2,7 +2,7 @@ # Copyright 1999-2012 Gentoo Foundation # Released under the 2-clause BSD license. -description="set up tmpfiles.d entries" +description="Set up tmpfiles.d entries" depend() { @@ -13,8 +13,8 @@ start() { - ebegin "setting up tmpfiles.d entries for /dev" - @LIBEXECDIR@/sh/tmpfiles.sh --prefix=/dev --create ${tmpfiles_opts} + ebegin "Setting up tmpfiles.d entries for /dev" + @LIBEXECDIR@/sh/tmpfiles.sh --prefix=/dev --create --boot ${tmpfiles_opts} eend $? return 0 } diff --git a/init.d/tmpfiles.setup.in b/init.d/tmpfiles.setup.in index d5a6ecd..5872fea 100644 --- a/init.d/tmpfiles.setup.in +++ b/init.d/tmpfiles.setup.in @@ -2,7 +2,7 @@ # Copyright 1999-2012 Gentoo Foundation # Released under the 2-clause BSD license. -description="set up tmpfiles.d entries" +description="Set up tmpfiles.d entries" depend() { @@ -11,7 +11,7 @@ start() { - ebegin "setting up tmpfiles.d entries" + ebegin "Setting up tmpfiles.d entries" @LIBEXECDIR@/sh/tmpfiles.sh --exclude-prefix=/dev --create --remove --boot \ ${tmpfiles_opts} eend $? diff --git a/init.d/urandom.in b/init.d/urandom.in index ded4113..0dd2ba6 100644 --- a/init.d/urandom.in +++ b/init.d/urandom.in @@ -8,7 +8,7 @@ depend() { need localmount - keyword -jail -lxc -openvz -prefix + keyword -jail -lxc -openvz -prefix -systemd-nspawn } save_seed() diff --git a/man/Makefile b/man/Makefile index 436f647..73db2a8 100644 --- a/man/Makefile +++ b/man/Makefile @@ -1,8 +1,16 @@ +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 + +ifeq (${OS},Linux) +MAN8 += rc-sstat.8 +endif # Handy macro to create symlinks # This does rely on correctly formatting our manpages! @@ -16,8 +24,6 @@ fi; \ done; -MK= ../mk -include ${MK}/sys.mk include ${MK}/gitignore.mk all: diff --git a/man/openrc-run.8 b/man/openrc-run.8 index 3890f76..4637fd0 100644 --- a/man/openrc-run.8 +++ b/man/openrc-run.8 @@ -107,6 +107,19 @@ 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, +start-stop-daemon will be used. The only alternate supervisor we support +in this release is S6 from Skarnet software. To use this, set +supervisor=s6. +.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 @@ -114,16 +127,22 @@ .Nm start-stop-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. +List of arguments to pass to the daemon when starting via +.Nm start-stop-daemon . .It Ar command_background Set this to "true", "yes" or "1" (case-insensitive) to force the daemon into the background. This implies the "--make-pidfile" and "--pidfile" option of .Xr start-stop-daemon 8 so the pidfile variable must be set. +.It Ar chroot +.Xr start-stop-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 stopsig +Signal to send when stopping the daemon. .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). @@ -131,6 +150,12 @@ 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. .El .Sh DEPENDENCIES You should define a diff --git a/man/rc-sstat.8 b/man/rc-sstat.8 new file mode 100644 index 0000000..2f2c4a6 --- /dev/null +++ b/man/rc-sstat.8 @@ -0,0 +1,46 @@ +.\" Copyright (c) 2015 William Hubbs +.\" +.\" 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 AUTHOR 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 AUTHOR 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. +.\" +.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-update.8 b/man/rc-update.8 index dca4b90..113a533 100644 --- a/man/rc-update.8 +++ b/man/rc-update.8 @@ -87,7 +87,7 @@ .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 diff --git a/mk/depend.mk b/mk/depend.mk index 44af378..873b0ca 100644 --- a/mk/depend.mk +++ b/mk/depend.mk @@ -6,7 +6,7 @@ .depend: ${SRCS} rm -f .depend - ${CC} ${CPPFLAGS} -MM ${SRCS} > .depend + ${CC} ${LOCAL_CPPFLAGS} ${CPPFLAGS} -MM ${SRCS} > .depend depend: .depend extra_depend diff --git a/mk/dist.mk b/mk/dist.mk index 82fb8cb..a986e60 100644 --- a/mk/dist.mk +++ b/mk/dist.mk @@ -2,17 +2,22 @@ # Copyright (c) 2008 Roy Marples # Released under the 2-clause BSD license. -GITREF?= HEAD +GITREF?= ${VERSION} DISTPREFIX?= ${NAME}-${VERSION} DISTFILE?= ${DISTPREFIX}.tar.bz2 CLEANFILES+= ${NAME}-*.tar.bz2 + +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.bz2 + +changelog: + git log ${CHANGELOG_LIMIT} --format=full > ChangeLog dist: git archive --prefix=${DISTPREFIX}/ ${GITREF} | bzip2 > ${DISTFILE} @@ -29,7 +34,7 @@ mkdir /tmp/${SNAPDIR} cp -RPp * /tmp/${SNAPDIR} (cd /tmp/${SNAPDIR}; make clean) - find /tmp/${SNAPDIR} -name .svn -exec rm -rf -- {} \; 2>/dev/null || true + rm -rf /tmp/${SNAPDIR}/.git 2>/dev/null || true tar -cvjpf ${SNAPFILE} -C /tmp ${SNAPDIR} rm -rf /tmp/${SNAPDIR} ls -l ${SNAPFILE} diff --git a/mk/git.mk b/mk/git.mk deleted file mode 100644 index 62cae5a..0000000 --- a/mk/git.mk +++ /dev/null @@ -1,8 +0,0 @@ -_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/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 index 1cf006c..61a79ca 100644 --- a/mk/lib.mk +++ b/mk/lib.mk @@ -21,10 +21,10 @@ CLEANFILES+= ${OBJS} ${SOBJS} ${_LIBS} ${SHLIB_LINK} %.o: %.c - ${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@ + ${CC} ${LOCAL_CFLAGS} ${LOCAL_CPPFLAGS} ${CFLAGS} ${CPPFLAGS} -c $< -o $@ %.So: %.c - ${CC} ${PICFLAG} -DPIC ${CPPFLAGS} ${CFLAGS} -c $< -o $@ + ${CC} ${PICFLAG} -DPIC ${LOCAL_CFLAGS} ${LOCAL_CPPFLAGS} ${CPPFLAGS} ${CFLAGS} -c $< -o $@ all: depend ${_LIBS} @@ -40,7 +40,7 @@ @${ECHO} building shared library $@ @rm -f $@ ${SHLIB_LINK} @ln -fs $@ ${SHLIB_LINK} - ${CC} ${CFLAGS} ${LDFLAGS} -shared -Wl,-x \ + ${CC} ${LOCAL_CFLAGS} ${CFLAGS} ${LOCAL_LDFLAGS} ${LDFLAGS} -shared -Wl,-x \ -o $@ -Wl,-soname,${SONAME} \ ${SOBJS} ${LDADD} diff --git a/mk/os-GNU-kFreeBSD.mk b/mk/os-GNU-kFreeBSD.mk index 72fea3e..c217372 100644 --- a/mk/os-GNU-kFreeBSD.mk +++ b/mk/os-GNU-kFreeBSD.mk @@ -3,7 +3,9 @@ # Generic definitions +SFX= .GNU-kFreeBSD.in +PKG_PREFIX?= /usr + CPPFLAGS+= -D_BSD_SOURCE -D_XOPEN_SOURCE=700 LIBDL= -Wl,-Bdynamic -ldl LIBKVM?= -include ${MK}/os-BSD.mk diff --git a/mk/os-GNU.mk b/mk/os-GNU.mk new file mode 100644 index 0000000..826202c --- /dev/null +++ b/mk/os-GNU.mk @@ -0,0 +1,8 @@ +# Copyright (c) 2008 Roy Marples +# Released under the 2-clause BSD license. + +SFX= .GNU.in +PKG_PREFIX?= /usr + +CPPFLAGS+= -D_BSD_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 index dc76b96..60235b8 100644 --- a/mk/os-Linux.mk +++ b/mk/os-Linux.mk @@ -4,11 +4,24 @@ SFX= .Linux.in PKG_PREFIX?= /usr -CPPFLAGS+= -D_BSD_SOURCE -D_XOPEN_SOURCE=700 +CPPFLAGS+= -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700 LIBDL= -Wl,-Bdynamic -ldl ifeq (${MKSELINUX},yes) CPPFLAGS+= -DHAVE_SELINUX -LIBSELINUX= -lselinux +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/pam.mk b/mk/pam.mk index 15ffb54..199896c 100644 --- a/mk/pam.mk +++ b/mk/pam.mk @@ -2,6 +2,12 @@ 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 diff --git a/mk/prog.mk b/mk/prog.mk index d4c3252..1c829b1 100644 --- a/mk/prog.mk +++ b/mk/prog.mk @@ -1,4 +1,4 @@ -# rules to build a library +# rules to build a program # based on FreeBSD's bsd.prog.mk # Copyright (c) 2008 Roy Marples @@ -25,10 +25,10 @@ all: depend ${PROG} %.o: %.c - ${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@ + ${CC} ${LOCAL_CFLAGS} ${LOCAL_CPPFLAGS} ${CFLAGS} ${CPPFLAGS} -c $< -o $@ ${PROG}: ${SCRIPTS} ${OBJS} - ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJS} ${LDADD} + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJS} ${LDADD} clean: rm -f ${CLEANFILES} diff --git a/mk/termcap.mk b/mk/termcap.mk index 6f590da..2d6ad01 100644 --- a/mk/termcap.mk +++ b/mk/termcap.mk @@ -1,11 +1,12 @@ ifeq (${MKTERMCAP},ncurses) - LTERMCAP:= $(shell pkg-config ncurses --libs 2> /dev/null) +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 +CPPFLAGS+= -DHAVE_TERMCAP ${TERMCAP_CFLAGS} LDADD+= ${LIBTERMCAP} else ifeq (${MKTERMCAP},termcap) LIBTERMCAP?= -ltermcap diff --git a/runlevels/Makefile b/runlevels/Makefile index 25e3e1a..682d6e1 100644 --- a/runlevels/Makefile +++ b/runlevels/Makefile @@ -34,7 +34,8 @@ # FreeBSD specific stuff BOOT-FreeBSD+= adjkerntz dumpon syscons -BOOT-Linux+= hwclock keymaps modules mtab procfs termencoding tmpfiles.setup +BOOT-Linux+= binfmt hwclock keymaps modules mtab procfs termencoding \ + tmpfiles.setup SHUTDOWN-Linux= killprocs mount-ro SYSINIT-Linux= devfs dmesg sysfs tmpfiles.dev diff --git a/s6-guide.md b/s6-guide.md new file mode 100644 index 0000000..630d778 --- /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 Skarmet 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..8b9d7ba --- /dev/null +++ b/scripts/.gitignore @@ -0,0 +1 @@ +rc-sstat diff --git a/scripts/Makefile b/scripts/Makefile index 3d41631..9a66c65 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -1,5 +1,19 @@ +MK= ../mk +include ${MK}/os.mk + DIR= ${LIBEXECDIR}/bin BIN= on_ac_power +INSTALLAFTER = _installafter -MK= ../mk +ifeq (${OS},Linux) +SRCS+= rc-sstat.in +BIN+= rc-sstat +endif + +_installafter: +ifeq (${OS},Linux) + ${INSTALL} -d ${DESTDIR}${SBINDIR} + ln -s ${DIR}/rc-sstat ${DESTDIR}/${SBINDIR}/rc-sstat +endif + include ${MK}/scripts.mk diff --git a/scripts/rc-sstat.in b/scripts/rc-sstat.in new file mode 100644 index 0000000..8896695 --- /dev/null +++ b/scripts/rc-sstat.in @@ -0,0 +1,140 @@ +#!@SHELL@ + +# 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/sh/.gitignore b/sh/.gitignore index d5cb215..f814f4a 100644 --- a/sh/.gitignore +++ b/sh/.gitignore @@ -1,10 +1,11 @@ functions.sh gendepends.sh rc-functions.sh -runscript.sh +openrc-run.sh cgroup-release-agent.sh init.sh init-early.sh rc-cgroup.sh tmpfiles.sh migrate-to-run.sh +binfmt.sh diff --git a/sh/Makefile b/sh/Makefile index c1953f3..b9b9fb3 100644 --- a/sh/Makefile +++ b/sh/Makefile @@ -1,8 +1,8 @@ DIR= ${LIBEXECDIR}/sh SRCS= init.sh.in functions.sh.in gendepends.sh.in \ - rc-functions.sh.in runscript.sh.in tmpfiles.sh.in ${SRCS-${OS}} -INC= rc-mount.sh functions.sh rc-functions.sh -BIN= gendepends.sh init.sh runscript.sh tmpfiles.sh ${BIN-${OS}} + openrc-run.sh.in rc-functions.sh.in tmpfiles.sh.in ${SRCS-${OS}} +INC= rc-mount.sh functions.sh rc-functions.sh s6.sh start-stop-daemon.sh +BIN= gendepends.sh init.sh openrc-run.sh tmpfiles.sh ${BIN-${OS}} INSTALLAFTER= _installafter @@ -12,9 +12,9 @@ SRCS-FreeBSD= BIN-FreeBSD= -SRCS-Linux= cgroup-release-agent.sh.in init-early.sh.in migrate-to-run.sh.in \ - rc-cgroup.sh.in -BIN-Linux= cgroup-release-agent.sh init-early.sh migrate-to-run.sh \ +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= diff --git a/sh/binfmt.sh.in b/sh/binfmt.sh.in new file mode 100644 index 0000000..b636bac --- /dev/null +++ b/sh/binfmt.sh.in @@ -0,0 +1,85 @@ +#!@SHELL@ +# This is a reimplementation of the systemd binfmt.d code to register +# misc binary formats with the kernel. +# +# Copyright (c) 2015 William Hubbs +# Released under the 2-clause BSD license. +# +# 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 +# + +apply_file() { + [ $# -lt 1 ] && return 0 + FILE="$1" + LINENUM=0 + + ### FILE FORMAT ### + # See https://www.kernel.org/doc/Documentation/binfmt_misc.txt + while read 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/init.sh.GNU-kFreeBSD.in b/sh/init.sh.GNU-kFreeBSD.in new file mode 100644 index 0000000..d1a04e1 --- /dev/null +++ b/sh/init.sh.GNU-kFreeBSD.in @@ -0,0 +1,35 @@ +#!@SHELL@ +# Copyright (c) 2007-2009 Roy Marples +# Released under the 2-clause BSD license. + +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/deptree ]; 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..5ba051d --- /dev/null +++ b/sh/init.sh.GNU.in @@ -0,0 +1,38 @@ +#!@SHELL@ +# Copyright (c) 2007-2009 Roy Marples +# Copyright (c) 2014 Svante Signell +# Released under the 2-clause BSD license. + +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/deptree ]; then + cp -p "$RC_LIBEXECDIR"/cache/* "$RC_SVCDIR" 2>/dev/null +fi + +echo sysinit >"$RC_SVCDIR"/softlevel +exit 0 diff --git a/sh/openrc-run.sh.in b/sh/openrc-run.sh.in new file mode 100644 index 0000000..8aba4e0 --- /dev/null +++ b/sh/openrc-run.sh.in @@ -0,0 +1,335 @@ +#!@SHELL@ +# Shell wrapper for openrc-run + +# Copyright (c) 2007-2009 Roy Marples +# Released under the 2-clause BSD license. + +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|SYSTEMD-NSPAWN) ;; + *) 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 $*" +} +before() { + [ -n "$*" ] && echo "before $*" +} +after() { + [ -n "$*" ] && echo "after $*" +} +provide() { + [ -n "$*" ] && echo "provide $*" +} +keyword() { + [ -n "$*" ] && echo "keyword $*" +} + +# 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 +} + +# Template start / stop / status functions +# These functions select the appropriate function to call from the +# supervisor modules +start() +{ + local func=ssd_start + case "$supervisor" in + s6) func=s6_start ;; + ?*) + ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon" + ;; + esac + $func +} + +stop() +{ + local func=ssd_stop + case "$supervisor" in + s6) func=s6_stop ;; + ?*) + ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon" + ;; + esac + $func +} + +status() +{ + local func=ssd_status + case "$supervisor" in + s6) func=s6_status ;; + ?*) + ewarn "Invalid supervisor, \"$supervisor\", using start-stop-daemon" + ;; + esac + $func +} + +yesno $RC_DEBUG && set -x + +_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 any system overrides +sourcex -e "@SYSCONFDIR@/rc.conf" + +# load service supervisor functions +sourcex "@LIBEXECDIR@/sh/s6.sh" +sourcex "@LIBEXECDIR@/sh/start-stop-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 index b635340..b49c711 100644 --- a/sh/rc-cgroup.sh.in +++ b/sh/rc-cgroup.sh.in @@ -1,7 +1,9 @@ #!@SHELL@ # Copyright (c) 2012 Alexander Vershilov # Released under the 2-clause BSD license. + extra_stopped_commands="${extra_stopped_commands} cgroup_cleanup" +description_cgroup_cleanup="Kill all processes in the cgroup" cgroup_find_path() { @@ -46,25 +48,27 @@ $controller.*) if [ -n "$name" -a -f "$cgroup/$name" -a -n "$val" ]; then veinfo "$RC_SVCNAME: Setting $cgroup/$name to $val" - echo $val > "$cgroup/$name" + printf "%s" "$val" > "$cgroup/$name" fi name=$1 val= ;; *) - val="$val $1" + [ -n "$val" ] && + val="$val $1" || + val="$1" ;; esac shift done if [ -n "$name" -a -f "$cgroup/$name" -a -n "$val" ]; then veinfo "$RC_SVCNAME: Setting $cgroup/$name to $val" - echo $val > "$cgroup/$name" + printf "%s" "$val" > "$cgroup/$name" fi if [ -f "$cgroup/tasks" ]; then veinfo "$RC_SVCNAME: adding to $cgroup/tasks" - echo 0 > "$cgroup/tasks" + printf "%d" 0 > "$cgroup/tasks" fi return 0 @@ -77,14 +81,14 @@ # cgroups. But may lead to a problems where that inheriting # is needed. for d in /sys/fs/cgroup/* ; do - [ -f "${d}"/tasks ] && echo 0 > "${d}"/tasks + [ -f "${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" - [ -f "$cgroup/tasks" ] && echo 0 > "$cgroup/tasks" + [ -f "$cgroup/tasks" ] && printf "%d" 0 > "$cgroup/tasks" fi } @@ -105,11 +109,20 @@ 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 } diff --git a/sh/rc-functions.sh.in b/sh/rc-functions.sh.in index d52b82e..911d65a 100644 --- a/sh/rc-functions.sh.in +++ b/sh/rc-functions.sh.in @@ -85,7 +85,7 @@ return 1 } -# Called from runscript.sh or gendepends.sh +# Called from openrc-run.sh or gendepends.sh _depend() { depend local _rc_svcname=$(shell_var "$RC_SVCNAME") _deptype= _depends= diff --git a/sh/runscript.sh.in b/sh/runscript.sh.in deleted file mode 100644 index 9b2ae0e..0000000 --- a/sh/runscript.sh.in +++ /dev/null @@ -1,339 +0,0 @@ -#!@SHELL@ -# Shell wrapper for runscript - -# Copyright (c) 2007-2009 Roy Marples -# Released under the 2-clause BSD license. - -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" -[ "$RC_SYS" != "PREFIX" ] && sourcex -e "@LIBEXECDIR@/sh/rc-cgroup.sh" - -# 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 $*" -} -before() { - [ -n "$*" ] && echo "before $*" -} -after() { - [ -n "$*" ] && echo "after $*" -} -provide() { - [ -n "$*" ] && echo "provide $*" -} -keyword() { - [ -n "$*" ] && echo "keyword $*" -} - -# 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 -} - -# Template start / stop / status functions -start() -{ - [ -n "$command" ] || return 0 - 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 - _background="--background --make-pidfile" - fi - if yesno "$start_inactive"; then - local _inactive=false - service_inactive && _inactive=true - mark_service_inactive - fi - eval start-stop-daemon --start \ - --exec $command \ - ${procname:+--name} $procname \ - ${pidfile:+--pidfile} $pidfile \ - $_background $start_stop_daemon_args \ - -- $command_args - if eend $? "Failed to start $RC_SVCNAME"; then - service_set_value "command" "${command}" - [ -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 -} - -stop() -{ - local startcommand="$(service_get_value "command")" - local startpidfile="$(service_get_value "pidfile")" - local startprocname="$(service_get_value "procname")" - command="${startcommand:-$command}" - pidfile="${startpidfile:-$pidfile}" - procname="${startprocname:-$procname}" - [ -n "$command" -o -n "$procname" -o -n "$pidfile" ] || return 0 - ebegin "Stopping ${name:-$RC_SVCNAME}" - start-stop-daemon --stop \ - ${retry:+--retry} $retry \ - ${command:+--exec} $command \ - ${procname:+--name} $procname \ - ${pidfile:+--pidfile} $pidfile \ - ${stopsig:+--signal} $stopsig - eend $? "Failed to stop $RC_SVCNAME" -} - -status() -{ - _status -} - -yesno $RC_DEBUG && set -x - -_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 any system overrides -sourcex -e "@SYSCONFDIR@/rc.conf" - -# Apply any ulimit defined -[ -n "${rc_ulimit:-$RC_ULIMIT}" ] && ulimit ${rc_ulimit:-$RC_ULIMIT} - -# Set verbose mode -if yesno "${rc_verbose:-$RC_VERBOSE}"; then - EINFO_VERBOSE=yes - export EINFO_VERBOSE -fi - -# Apply cgroups settings if defined -if [ "$1" = "start" ] ; then - if [ "$(command -v cgroup_add_service)" = "cgroup_add_service" ]; then - 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 -fi - -# Load our script -sourcex "$RC_SERVICE" - -for _d in $required_dirs; do - if [ ! -d $_d ]; then - eerror "$RC_SVCNAME: \`$_d' is not a directory" - exit 1 - fi -done -unset _d - -for _f in $required_files; do - if [ ! -r $_f ]; then - eerror "$RC_SVCNAME: \`$_f' is not readable" - exit 1 - fi -done -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/s6.sh b/sh/s6.sh new file mode 100644 index 0000000..a45456a --- /dev/null +++ b/sh/s6.sh @@ -0,0 +1,50 @@ +# Start / stop / status functions for s6 support +# Copyright (c) 2015 William Hubbs +# Released under the 2-clause BSD license. + +[ -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 $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 -Dd -T ${s6_service_timeout_stop:-10000} "${s6_service_link}" + set -- $(s6-svstat "${s6_service_link}") + [ "$1" = "down" ] + eend $? "Failed to stop $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..ea99269 --- /dev/null +++ b/sh/start-stop-daemon.sh @@ -0,0 +1,77 @@ +# start / stop / status functions for start-stop-daemon +# Copyright (c) 2007-2009 Roy Marples +# Released under the 2-clause BSD license. + +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 + eval start-stop-daemon --start \ + --exec $command \ + ${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 $RC_SVCNAME"; then + service_set_value "command" "${command}" + [ -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 startcommand="$(service_get_value "command")" + local startpidfile="$(service_get_value "pidfile")" + local startprocname="$(service_get_value "procname")" + command="${startcommand:-$command}" + pidfile="${startpidfile:-$pidfile}" + procname="${startprocname:-$procname}" + [ -n "$command" -o -n "$procname" -o -n "$pidfile" ] || return 0 + ebegin "Stopping ${name:-$RC_SVCNAME}" + start-stop-daemon --stop \ + ${retry:+--retry} $retry \ + ${command:+--exec} $command \ + ${procname:+--name} $procname \ + ${pidfile:+--pidfile} $pidfile \ + ${stopsig:+--signal} $stopsig + + eend $? "Failed to stop $RC_SVCNAME" +} + +ssd_status() +{ + _status +} diff --git a/sh/tmpfiles.sh.in b/sh/tmpfiles.sh.in old file mode 100755 new file mode 100644 index 42a3639..43442d7 100644 --- a/sh/tmpfiles.sh.in +++ b/sh/tmpfiles.sh.in @@ -53,10 +53,18 @@ done } +splitpath() { + local path=$1 + while [ -n "$path" ]; do + echo $path + path=${path%/*} + done +} + _restorecon() { local path=$1 if [ -x /sbin/restorecon ]; then - dryrun_or_real restorecon -F "$path" + dryrun_or_real restorecon -F $(splitpath "$path") fi } @@ -80,6 +88,17 @@ fi } +_C() { + # recursively copy a file or directory + local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6 + if [ ! -e "$path" ]; then + dryrun_or_real cp -r "$arg" "$path" + _restorecon "$path" + [ $uid != '-' ] && dryrun_or_real chown "$uid" "$path" + [ $gid != '-' ] && dryrun_or_real chgrp "$gid" "$path" + [ $mode != '-' ] && dryrun_or_real chmod "$mode" "$path" + fi +} _f() { # Create a file if it doesn't exist yet @@ -111,6 +130,7 @@ if [ ! -d "$path" ]; then dryrun_or_real mkdir -p "$path" 2>/dev/null + _restorecon "$path" dryrun_or_real $CHECKPATH -dq -m "$mode" -o "$uid:$gid" "$path" fi } @@ -126,8 +146,16 @@ if [ $CREATE -gt 0 ]; then dryrun_or_real mkdir -p "$path" 2>/dev/null + _restorecon "$path" dryrun_or_real $CHECKPATH -Dq -m "$mode" -o "$uid:$gid" "$path" fi +} + +_v() { + # Create a subvolume if the path does not exist yet and the file system + # supports this (btrfs). Otherwise create a normal directory. + # TODO: Implement btrfs subvol creation. + _d "$@" } _L() { @@ -234,7 +262,7 @@ FILE= fragments= # XXX: The harcoding of /usr/lib/ is an explicit choice by upstream -tmpfiles_dirs='/usr/lib/tmpfiles.d/ /etc/tmpfiles.d/ /run/tmpfiles.d/' +tmpfiles_dirs='/usr/lib/tmpfiles.d/ /run/tmpfiles.d/ /etc/tmpfiles.d/' tmpfiles_basenames='' tmpfiles_d='' # Build a list of sorted unique basenames @@ -304,6 +332,7 @@ # But IS allowed when globs are expanded for the x/r/R/z/Z types. while read cmd path mode uid gid age arg; do LINENUM=$(( LINENUM+1 )) + FORCE=0 # Unless we have both command and path, skip this line. if [ -z "$cmd" -o -z "$path" ]; then @@ -311,13 +340,20 @@ fi case $cmd in - *!) [ "$BOOT" -eq "1" ] || continue; cmd=${cmd%!} ;; + \#*) continue ;; esac + + while [ ${#cmd} -gt 1 ]; do + case $cmd in + *!) cmd=${cmd%!}; [ "$BOOT" -eq "1" ] || continue 2 ;; + *+) cmd=${cmd%+}; FORCE=1; ;; + *) warninvalid ; continue 2 ;; + esac + done # whine about invalid entries case $cmd in - f|F|w|d|D|p|L|c|b|x|X|r|R|z|Z) ;; - \#*) continue ;; + f|F|w|d|D|v|p|L|c|C|b|x|X|r|R|z|Z) ;; *) warninvalid ; continue ;; esac @@ -325,8 +361,8 @@ if [ "$mode" = '-' -o "$mode" = '' ]; then case "$cmd" in p|f|F) mode=0644 ;; - d|D) mode=0755 ;; - z|Z|x|r|R|L) ;; + d|D|v) mode=0755 ;; + C|z|Z|x|r|R|L) ;; esac fi @@ -338,6 +374,13 @@ [ -n "$EXCLUDE" ] && checkprefix $path $EXCLUDE && continue [ -n "$PREFIX" ] && ! checkprefix $path $PREFIX && continue + + if [ $FORCE -gt 0 ]; then + case $cmd in + p|L|c|b) [ -f "$path" ] && dryrun_or_real rm -f "$path" + esac + fi + [ "$VERBOSE" -eq "1" ] && echo _$cmd "$@" _$cmd "$@" rc=$? diff --git a/src/includes/helpers.h b/src/includes/helpers.h index 94e59a8..4352858 100644 --- a/src/includes/helpers.h +++ b/src/includes/helpers.h @@ -47,25 +47,6 @@ #endif #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) - -/* Some libc implemntations don't have these */ -#ifndef TAILQ_CONCAT -#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 (0) -#endif - -#ifndef TAILQ_FOREACH_SAFE -#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = TAILQ_FIRST((head)); \ - (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ - (var) = (tvar)) -#endif #ifdef __GLIBC__ # if ! defined (__UCLIBC__) && ! defined (__dietlibc__) 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/libeinfo/Makefile b/src/libeinfo/Makefile index ec756b4..e6ccb65 100644 --- a/src/libeinfo/Makefile +++ b/src/libeinfo/Makefile @@ -4,7 +4,7 @@ INCS= einfo.h VERSION_MAP= einfo.map -CPPFLAGS+= -I../includes +LOCAL_CPPFLAGS+= -I../includes MK= ../../mk include ${MK}/lib.mk diff --git a/src/libeinfo/einfo.h b/src/libeinfo/einfo.h index 31a891f..c0325c2 100644 --- a/src/libeinfo/einfo.h +++ b/src/libeinfo/einfo.h @@ -48,7 +48,10 @@ # endif #endif -__BEGIN_DECLS +/* __BEGIN_DECLS */ +#ifdef __cplusplus +extern "C" { +#endif /*! @brief Color types to use */ typedef enum @@ -140,5 +143,9 @@ /*! @brief Prefix each einfo line with something */ void eprefix(const char * EINFO_RESTRICT); -__END_DECLS +/* __END_DECLS */ +#ifdef __cplusplus +} #endif + +#endif diff --git a/src/librc/Makefile b/src/librc/Makefile index 7307560..08c599e 100644 --- a/src/librc/Makefile +++ b/src/librc/Makefile @@ -7,7 +7,7 @@ LDADD+= ${LIBKVM} -CPPFLAGS+= -I../includes +LOCAL_CPPFLAGS+= -I../includes MK= ../../mk include ${MK}/lib.mk diff --git a/src/librc/librc-daemon.c b/src/librc/librc-daemon.c index 8793075..4986f70 100644 --- a/src/librc/librc-daemon.c +++ b/src/librc/librc-daemon.c @@ -28,9 +28,10 @@ * SUCH DAMAGE. */ +#include "queue.h" #include "librc.h" -#if defined(__linux__) || defined (__GLIBC__) +#if defined(__linux__) || (defined (__FreeBSD_kernel__) && defined(__GLIBC__)) static bool pid_is_exec(pid_t pid, const char *exec) { @@ -98,7 +99,7 @@ pid_t p; char buffer[PATH_MAX]; struct stat sb; - pid_t runscript_pid = 0; + pid_t openrc_pid = 0; char *pp; RC_PIDLIST *pids = NULL; RC_PID *pi; @@ -107,7 +108,7 @@ return NULL; /* - We never match RC_RUNSCRIPT_PID if present so we avoid the below + We never match RC_OPENRC_PID if present so we avoid the below scenario /etc/init.d/ntpd stop does @@ -117,9 +118,9 @@ nasty */ - if ((pp = getenv("RC_RUNSCRIPT_PID"))) { - if (sscanf(pp, "%d", &runscript_pid) != 1) - runscript_pid = 0; + if ((pp = getenv("RC_OPENRC_PID"))) { + if (sscanf(pp, "%d", &openrc_pid) != 1) + openrc_pid = 0; } /* @@ -145,7 +146,7 @@ while ((entry = readdir(procdir)) != NULL) { if (sscanf(entry->d_name, "%d", &p) != 1) continue; - if (runscript_pid != 0 && runscript_pid == p) + if (openrc_pid != 0 && openrc_pid == p) continue; if (pid != 0 && pid != p) continue; @@ -509,6 +510,8 @@ 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)); @@ -552,6 +555,16 @@ } } 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) { diff --git a/src/librc/librc-depend.c b/src/librc/librc-depend.c index c9df451..d7a8ae1 100644 --- a/src/librc/librc-depend.c +++ b/src/librc/librc-depend.c @@ -30,6 +30,7 @@ #include +#include "queue.h" #include "librc.h" #define GENDEP RC_LIBEXECDIR "/sh/gendepends.sh" diff --git a/src/librc/librc-misc.c b/src/librc/librc-misc.c index b907c5c..2e9de80 100644 --- a/src/librc/librc-misc.c +++ b/src/librc/librc-misc.c @@ -28,6 +28,7 @@ * SUCH DAMAGE. */ +#include "queue.h" #include "librc.h" bool diff --git a/src/librc/librc-stringlist.c b/src/librc/librc-stringlist.c index 1a59335..bf8d3f4 100644 --- a/src/librc/librc-stringlist.c +++ b/src/librc/librc-stringlist.c @@ -28,6 +28,7 @@ * SUCH DAMAGE. */ +#include "queue.h" #include "librc.h" RC_STRINGLIST * diff --git a/src/librc/librc.c b/src/librc/librc.c index d7a4849..8f04313 100644 --- a/src/librc/librc.c +++ b/src/librc/librc.c @@ -30,6 +30,7 @@ const char librc_copyright[] = "Copyright (c) 2007-2008 Roy Marples"; +#include "queue.h" #include "librc.h" #ifdef __FreeBSD__ # include @@ -100,7 +101,9 @@ continue; } if (options & LS_DIR) { - if (stat(d->d_name, &buf) == 0 && + snprintf(file, sizeof(file), "%s/%s", + dir, d->d_name); + if (stat(file, &buf) != 0 || !S_ISDIR(buf.st_mode)) continue; } @@ -293,6 +296,8 @@ 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=systemd-nspawn")) + return RC_SYS_SYSTEMD_NSPAWN; #endif return NULL; diff --git a/src/librc/librc.h b/src/librc/librc.h index 54c9a1a..0824eba 100644 --- a/src/librc/librc.h +++ b/src/librc/librc.h @@ -57,11 +57,13 @@ #include #include -#ifdef BSD +#if defined(BSD) && !defined(__GNU__) #include #include #include #include +#else +#include #endif #include "rc.h" diff --git a/src/librc/rc.h.in b/src/librc/rc.h.in index c2a919f..13e1b5b 100644 --- a/src/librc/rc.h.in +++ b/src/librc/rc.h.in @@ -27,11 +27,13 @@ #define __RC_H__ #include -#include #include #include -__BEGIN_DECLS +/* __BEGIN_DECLS */ +#ifdef __cplusplus +extern "C" { +#endif #define RC_PREFIX "@PREFIX@" #define RC_SYSCONFDIR "@SYSCONFDIR@" @@ -39,7 +41,8 @@ #define RC_LIBEXECDIR "@LIBEXECDIR@" #if defined(PREFIX) #define RC_SVCDIR RC_LIBEXECDIR "/init.d" -#elif defined(__linux__) +#elif defined(__linux__) || (defined(__FreeBSD_kernel__) && \ + defined(__GLIBC__)) || defined(__GNU__) #define RC_SVCDIR "/run/openrc" #else #define RC_SVCDIR RC_LIBEXECDIR "/init.d" @@ -73,6 +76,51 @@ # 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 { @@ -284,6 +332,7 @@ #define RC_SYS_OPENVZ "OPENVZ" #define RC_SYS_LXC "LXC" #define RC_SYS_PREFIX "PREFIX" +#define RC_SYS_SYSTEMD_NSPAWN "SYSTEMD-NSPAWN" #define RC_SYS_UML "UML" #define RC_SYS_VSERVER "VSERVER" #define RC_SYS_XEN0 "XEN0" @@ -563,5 +612,9 @@ * we have our own */ ssize_t rc_getline(char **, size_t *, FILE *); -__END_DECLS +/* __END_DECLS */ +#ifdef __cplusplus +} #endif + +#endif diff --git a/src/rc/Makefile b/src/rc/Makefile index bd8b942..65b5811 100644 --- a/src/rc/Makefile +++ b/src/rc/Makefile @@ -1,8 +1,8 @@ PROG= openrc -SRCS= checkpath.c fstabinfo.c mountinfo.c start-stop-daemon.c \ +SRCS= checkpath.c fstabinfo.c mountinfo.c openrc-run.c \ rc-applets.c rc-depend.c rc-logger.c \ rc-misc.c rc-plugin.c rc-service.c rc-status.c rc-update.c \ - runscript.c rc.c swclock.c + rc.c start-stop-daemon.c swclock.c ifeq (${MKSELINUX},yes) SRCS+= rc-selinux.c @@ -35,14 +35,14 @@ ALL_LINKS= ${BINLINKS} ${SBINLINKS} ${RC_BINLINKS} ${RC_SBINLINKS} CLEANFILES+= ${ALL_LINKS} -CPPFLAGS+= -I../includes -I../librc -I../libeinfo -LDFLAGS+= -L../librc -L../libeinfo +LOCAL_CPPFLAGS=-I../includes -I../librc -I../libeinfo +LOCAL_LDFLAGS=-L../librc -L../libeinfo LDADD+= -lutil -lrc -leinfo include ../../Makefile.inc MK= ../../mk include ${MK}/prog.mk -include ${MK}/git.mk +include ${MK}/gitver.mk include ${MK}/cc.mk include ${MK}/termcap.mk diff --git a/src/rc/builtins.h b/src/rc/builtins.h index 2b38c67..d9bb984 100644 --- a/src/rc/builtins.h +++ b/src/rc/builtins.h @@ -23,6 +23,7 @@ * SUCH DAMAGE. */ +#include "queue.h" #include "rc.h" int checkpath(int, char **); diff --git a/src/rc/checkpath.c b/src/rc/checkpath.c index 94ab474..a8a1c75 100644 --- a/src/rc/checkpath.c +++ b/src/rc/checkpath.c @@ -45,10 +45,7 @@ #include "builtins.h" #include "einfo.h" #include "rc-misc.h" - -#ifdef HAVE_SELINUX #include "rc-selinux.h" -#endif typedef enum { inode_unknown = 0, @@ -68,7 +65,7 @@ int u; memset(&st, 0, sizeof(st)); - if (stat(path, &st) || trunc) { + if (lstat(path, &st) || trunc) { if (type == inode_file) { einfo("%s: creating file", path); if (!mode) /* 664 */ @@ -133,6 +130,14 @@ } 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)); @@ -141,6 +146,14 @@ } 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)); @@ -148,10 +161,8 @@ } } -#ifdef HAVE_SELINUX if (selinux_on) selinux_util_label(path); -#endif return 0; } @@ -280,10 +291,8 @@ if (gr) gid = gr->gr_gid; -#ifdef HAVE_SELINUX if (selinux_util_open() == 1) selinux_on = true; -#endif while (optind < argc) { if (writable) @@ -293,10 +302,8 @@ optind++; } -#ifdef HAVE_SELINUX if (selinux_on) selinux_util_close(); -#endif return retval; } diff --git a/src/rc/fstabinfo.c b/src/rc/fstabinfo.c index 99eb7bc..34fea30 100644 --- a/src/rc/fstabinfo.c +++ b/src/rc/fstabinfo.c @@ -70,6 +70,7 @@ #include "builtins.h" #include "einfo.h" +#include "queue.h" #include "rc.h" #include "rc-misc.h" diff --git a/src/rc/mountinfo.c b/src/rc/mountinfo.c index ce3f27d..3f1dfb6 100644 --- a/src/rc/mountinfo.c +++ b/src/rc/mountinfo.c @@ -35,11 +35,12 @@ # include # include # define F_FLAGS f_flags -#elif defined(BSD) +#elif defined(BSD) && !defined(__GNU__) # include # define statfs statvfs # define F_FLAGS f_flag -#elif defined (__linux__) || defined (__GLIBC__) +#elif defined (__linux__) || (defined(__FreeBSD_kernel__) && \ + defined(__GLIBC__)) || defined(__GNU__) # include #endif @@ -53,6 +54,7 @@ #include "builtins.h" #include "einfo.h" +#include "queue.h" #include "rc.h" #include "rc-misc.h" @@ -168,7 +170,7 @@ return -1; } -#ifdef BSD +#if defined(BSD) && !defined(__GNU__) /* Translate the mounted options to english * This is taken directly from FreeBSD mount.c */ @@ -265,7 +267,8 @@ return list; } -#elif defined (__linux__) || defined (__GLIBC__) +#elif defined (__linux__) || (defined (__FreeBSD_kernel__) && \ + defined(__GLIBC__)) static struct mntent * getmntfile(const char *file) { @@ -295,7 +298,7 @@ int netdev; RC_STRINGLIST *list; - if ((fp = fopen("/proc/mounts", "r")) == NULL) + if ((fp = fopen("/proc/self/mounts", "r")) == NULL) eerrorx("getmntinfo: %s", strerror(errno)); list = rc_stringlist_new(); @@ -312,6 +315,8 @@ if ((ent = getmntfile(to))) { if (strstr(ent->mnt_opts, "_netdev")) netdev = 0; + else + netdev = 1; } process_mount(list, args, from, to, fst, opts, netdev); @@ -344,7 +349,7 @@ #include "_usage.h" #define extraopts "[mount1] [mount2] ..." -#define getoptstring "f:F:n:N:o:O:p:P:ist" getoptstring_COMMON +#define getoptstring "f:F:n:N:o:O:p:P:iste:E:" getoptstring_COMMON static const struct option longopts[] = { { "fstype-regex", 1, NULL, 'f'}, { "skip-fstype-regex", 1, NULL, 'F'}, diff --git a/src/rc/openrc-run.c b/src/rc/openrc-run.c new file mode 100644 index 0000000..f3a009e --- /dev/null +++ b/src/rc/openrc-run.c @@ -0,0 +1,1400 @@ +/* + * openrc-run.c + * Handle launching of init scripts. + */ + +/* + * Copyright (c) 2007-2009 Roy Marples + * + * 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 AUTHOR 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 AUTHOR 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. + */ + +#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__)) +# include +#elif defined(__NetBSD__) || defined(__OpenBSD__) +# include +#else +# include +#endif + +#include "builtins.h" +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-misc.h" +#include "rc-plugin.h" +#include "rc-selinux.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 */ + +static const char *applet; +static char *service, *runlevel, *ibsave, *prefix; +static RC_DEPTREE *deptree; +static RC_STRINGLIST *applet_list, *services, *tmplist; +static RC_STRINGLIST *restart_services, *need_services, *use_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; +static RC_STRINGLIST *deptypes_n; +static RC_STRINGLIST *deptypes_nu; +static RC_STRINGLIST *deptypes_nua; +static RC_STRINGLIST *deptypes_m; +static RC_STRINGLIST *deptypes_mua; + +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(); + +#ifdef DEBUG_MEMORY + rc_stringlist_free(deptypes_b); + rc_stringlist_free(deptypes_n); + rc_stringlist_free(deptypes_nu); + rc_stringlist_free(deptypes_nua); + rc_stringlist_free(deptypes_m); + rc_stringlist_free(deptypes_mua); + rc_deptree_free(deptree); + rc_stringlist_free(restart_services); + rc_stringlist_free(need_services); + rc_stringlist_free(use_services); + rc_stringlist_free(services); + rc_stringlist_free(applet_list); + rc_stringlist_free(tmplist); + free(ibsave); + free(service); + free(prefix); + free(runlevel); +#endif +} + +/* 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")) { + 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 { + 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_nu = rc_stringlist_new(); + rc_stringlist_add(deptypes_nu, "ineed"); + rc_stringlist_add(deptypes_nu, "iuse"); + + deptypes_nua = rc_stringlist_new(); + rc_stringlist_add(deptypes_nua, "ineed"); + rc_stringlist_add(deptypes_nua, "iuse"); + rc_stringlist_add(deptypes_nua, "iafter"); + + deptypes_m = rc_stringlist_new(); + rc_stringlist_add(deptypes_m, "needsme"); + + deptypes_mua = rc_stringlist_new(); + rc_stringlist_add(deptypes_mua, "needsme"); + rc_stringlist_add(deptypes_mua, "usesme"); + rc_stringlist_add(deptypes_mua, "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); + use_services = rc_deptree_depends(deptree, deptypes_nu, + 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_nua, 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(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_mua, 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; + } + } +#ifdef DEBUG_MEMORY + free(list); +#endif + return allow; +} + +#include "_usage.h" +#define getoptstring "dDsSvl:Z" getoptstring_COMMON +#define extraopts "stop | start | restart | describe | zap" +static 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 +}; +static 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 +}; +#include "_usage.c" + +int +openrc_run(int argc, char **argv) +{ + bool doneone = 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); + } + + 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); + + /* 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, "needsme") == 0 || + strcmp(optarg, "usesme") == 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); + 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; +} + +int +runscript(int argc, char **argv) +{ + ewarnv("runscript is deprecated; please use openrc-run instead."); + return (openrc_run(argc, argv)); +} diff --git a/src/rc/rc-applets.c b/src/rc/rc-applets.c index 8fe2d22..9b84ce4 100644 --- a/src/rc/rc-applets.c +++ b/src/rc/rc-applets.c @@ -329,7 +329,7 @@ bool ok = false; char *svcname = getenv("RC_SVCNAME"); char *service = NULL; - char *runscript_pid; + char *openrc_pid; /* char *mtime; */ pid_t pid; RC_SERVICE bit; @@ -350,7 +350,7 @@ eerrorx("%s: unknown applet", applet); /* If we're marking ourselves then we need to inform our parent - runscript process so they do not mark us based on our exit code */ + 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 @@ -359,8 +359,8 @@ * openrc@gentoo.org). */ if (ok && svcname && strcmp(svcname, service) == 0) { - runscript_pid = getenv("RC_RUNSCRIPT_PID"); - if (runscript_pid && sscanf(runscript_pid, "%d", &pid) == 1) + 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)); @@ -369,10 +369,10 @@ in control as well */ /* l = strlen(RC_SVCDIR "/exclusive") + strlen(svcname) + - strlen(runscript_pid) + 4; + strlen(openrc_pid) + 4; mtime = xmalloc(l); snprintf(mtime, l, RC_SVCDIR "/exclusive/%s.%s", - svcname, runscript_pid); + svcname, openrc_pid); if (exists(mtime) && unlink(mtime) != 0) eerror("%s: unlink: %s", applet, strerror(errno)); free(mtime); diff --git a/src/rc/rc-depend.c b/src/rc/rc-depend.c index 2392e18..8e7e388 100644 --- a/src/rc/rc-depend.c +++ b/src/rc/rc-depend.c @@ -46,6 +46,7 @@ #include "builtins.h" #include "einfo.h" +#include "queue.h" #include "rc.h" #include "rc-misc.h" diff --git a/src/rc/rc-logger.c b/src/rc/rc-logger.c index e8fb0ff..e72f181 100644 --- a/src/rc/rc-logger.c +++ b/src/rc/rc-logger.c @@ -44,7 +44,7 @@ #include #include -#if defined(__linux__) || defined(__GLIBC__) +#if defined(__linux__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) # include #elif defined(__NetBSD__) || defined(__OpenBSD__) # include @@ -54,6 +54,7 @@ #include "einfo.h" #include "rc-logger.h" +#include "queue.h" #include "rc.h" #include "rc-misc.h" diff --git a/src/rc/rc-misc.c b/src/rc/rc-misc.c index e8f6ca6..27d9f81 100644 --- a/src/rc/rc-misc.c +++ b/src/rc/rc-misc.c @@ -47,6 +47,7 @@ #include #include "einfo.h" +#include "queue.h" #include "rc.h" #include "rc-misc.h" #include "version.h" @@ -65,6 +66,7 @@ "LC_MONETARY", "LC_MESSAGES", "LC_PAPER", "LC_NAME", "LC_ADDRESS", "LC_TELEPHONE", "LC_MEASUREMENT", "LC_IDENTIFICATION", "LC_ALL", "IN_HOTPLUG", "IN_BACKGROUND", "RC_INTERFACE_KEEP_CONFIG", + "EERROR_QUIET", "EINFO_QUIET", NULL }; diff --git a/src/rc/rc-plugin.c b/src/rc/rc-plugin.c index 6175945..d981afd 100644 --- a/src/rc/rc-plugin.c +++ b/src/rc/rc-plugin.c @@ -43,6 +43,7 @@ #include #include "einfo.h" +#include "queue.h" #include "rc.h" #include "rc-misc.h" #include "rc-plugin.h" diff --git a/src/rc/rc-selinux.c b/src/rc/rc-selinux.c index eae030d..7124e83 100644 --- a/src/rc/rc-selinux.c +++ b/src/rc/rc-selinux.c @@ -1,7 +1,7 @@ /* - rc-selinux.c - SELinux helpers to get and set contexts. -*/ + * rc-selinux.c + * SELinux helpers to get and set contexts. + */ /* * Copyright (c) 2014 Jason Zaman @@ -31,23 +31,48 @@ #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 "rc-plugin.h" #include "rc-selinux.h" -#define SELINUX_LIB RC_LIBDIR "/runscript_selinux.so" - -static void (*selinux_run_init_old) (void); -static void (*selinux_run_init_new) (int argc, char **argv); - +/* 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) @@ -132,33 +157,243 @@ return 0; } -void selinux_setup(int argc, char **argv) -{ - void *lib_handle = NULL; - - if (!exists(SELINUX_LIB)) +/* + * 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; - - lib_handle = dlopen(SELINUX_LIB, RTLD_NOW | RTLD_GLOBAL); - if (!lib_handle) { - eerror("dlopen: %s", dlerror()); - return; - } - - selinux_run_init_old = (void (*)(void)) - dlfunc(lib_handle, "selinux_runscript"); - selinux_run_init_new = (void (*)(int, char **)) - dlfunc(lib_handle, "selinux_runscript2"); - - /* Use new run_init if it exists, else fall back to old */ - if (selinux_run_init_new) - selinux_run_init_new(argc, argv); - else if (selinux_run_init_old) - selinux_run_init_old(); - else - /* This shouldnt happen... probably corrupt lib */ - eerrorx - ("run_init is missing from runscript_selinux.so!"); - - dlclose(lib_handle); -} + } + + 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); + curr_t = xstrdup(context_type_get(curr_con)); + /* 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 index 8cf73b0..039890b 100644 --- a/src/rc/rc-selinux.h +++ b/src/rc/rc-selinux.h @@ -26,10 +26,24 @@ #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(int argc, char **argv); +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 index d35b36b..ff725cd 100644 --- a/src/rc/rc-service.c +++ b/src/rc/rc-service.c @@ -36,6 +36,7 @@ #include "builtins.h" #include "einfo.h" +#include "queue.h" #include "rc.h" #include "rc-misc.h" @@ -57,7 +58,7 @@ }; static const char * const longopts_help[] = { "tests if the service exists or not", - "if the service exsits then run the command", + "if the service exists then run the command", "list all available services", "resolve the service name to an init script", longopts_help_COMMON diff --git a/src/rc/rc-status.c b/src/rc/rc-status.c index 62591da..1f67b75 100644 --- a/src/rc/rc-status.c +++ b/src/rc/rc-status.c @@ -36,6 +36,7 @@ #include "builtins.h" #include "einfo.h" +#include "queue.h" #include "rc.h" #include "rc-misc.h" diff --git a/src/rc/rc-update.c b/src/rc/rc-update.c index d16af1b..48bb4dc 100644 --- a/src/rc/rc-update.c +++ b/src/rc/rc-update.c @@ -39,6 +39,7 @@ #include "builtins.h" #include "einfo.h" +#include "queue.h" #include "rc.h" #include "rc-misc.h" diff --git a/src/rc/rc.c b/src/rc/rc.c index 136e8fa..dd35482 100644 --- a/src/rc/rc.c +++ b/src/rc/rc.c @@ -60,6 +60,7 @@ #include "builtins.h" #include "einfo.h" +#include "queue.h" #include "rc.h" #include "rc-logger.h" #include "rc-misc.h" @@ -518,7 +519,7 @@ } static void -do_stop_services(const RC_STRINGLIST *types_n, const RC_STRINGLIST *start_services, +do_stop_services(RC_STRINGLIST *types_n, RC_STRINGLIST *start_services, const RC_STRINGLIST *stop_services, const RC_DEPTREE *deptree, const char *newlevel, bool parallel, bool going_down) { diff --git a/src/rc/runscript.c b/src/rc/runscript.c deleted file mode 100644 index de18f90..0000000 --- a/src/rc/runscript.c +++ /dev/null @@ -1,1394 +0,0 @@ -/* - * runscript.c - * Handle launching of init scripts. - */ - -/* - * Copyright (c) 2007-2009 Roy Marples - * - * 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 AUTHOR 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 AUTHOR 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. - */ - -#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(__GLIBC__) -# include -#elif defined(__NetBSD__) || defined(__OpenBSD__) -# include -#else -# include -#endif - -#include "builtins.h" -#include "einfo.h" -#include "rc.h" -#include "rc-misc.h" -#include "rc-plugin.h" - -#ifdef HAVE_SELINUX -#include "rc-selinux.h" -#endif - -#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 */ - -static const char *applet; -static char *service, *runlevel, *ibsave, *prefix; -static RC_DEPTREE *deptree; -static RC_STRINGLIST *applet_list, *services, *tmplist; -static RC_STRINGLIST *restart_services, *need_services, *use_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 *types_b, *types_n, *types_nu, *types_nua, *types_m; -static RC_STRINGLIST *types_mua = NULL; - -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(); - -#ifdef DEBUG_MEMORY - rc_stringlist_free(types_b); - rc_stringlist_free(types_n); - rc_stringlist_free(types_nu); - rc_stringlist_free(types_nua); - rc_stringlist_free(types_m); - rc_stringlist_free(types_mua); - rc_deptree_free(deptree); - rc_stringlist_free(restart_services); - rc_stringlist_free(need_services); - rc_stringlist_free(use_services); - rc_stringlist_free(services); - rc_stringlist_free(applet_list); - rc_stringlist_free(tmplist); - free(ibsave); - free(service); - free(prefix); - free(runlevel); -#endif -} - -/* 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 "/runscript.sh")) { - execl(RC_SVCDIR "/runscript.sh", - RC_SVCDIR "/runscript.sh", - service, arg1, arg2, (char *) NULL); - eerror("%s: exec `" RC_SVCDIR "/runscript.sh': %s", - service, strerror(errno)); - _exit(EXIT_FAILURE); - } else { - execl(RC_LIBEXECDIR "/sh/runscript.sh", - RC_LIBEXECDIR "/sh/runscript.sh", - service, arg1, arg2, (char *) NULL); - eerror("%s: exec `" RC_LIBEXECDIR "/sh/runscript.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_types(void) -{ - types_b = rc_stringlist_new(); - rc_stringlist_add(types_b, "broken"); - - types_n = rc_stringlist_new(); - rc_stringlist_add(types_n, "ineed"); - - types_nu = rc_stringlist_new(); - rc_stringlist_add(types_nu, "ineed"); - rc_stringlist_add(types_nu, "iuse"); - - types_nua = rc_stringlist_new(); - rc_stringlist_add(types_nua, "ineed"); - rc_stringlist_add(types_nua, "iuse"); - rc_stringlist_add(types_nua, "iafter"); - - types_m = rc_stringlist_new(); - rc_stringlist_add(types_m, "needsme"); - - types_mua = rc_stringlist_new(); - rc_stringlist_add(types_mua, "needsme"); - rc_stringlist_add(types_mua, "usesme"); - rc_stringlist_add(types_mua, "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 (!types_b) - setup_types(); - - services = rc_deptree_depends(deptree, types_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, types_n, - applet_list, runlevel, depoptions); - use_services = rc_deptree_depends(deptree, types_nu, - 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, types_nua, 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(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 (!types_m) - setup_types(); - - services = rc_deptree_depends(deptree, types_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, types_mua, 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; - } - } -#ifdef DEBUG_MEMORY - free(list); -#endif - return allow; -} - -#include "_usage.h" -#define getoptstring "dDsSvl:Z" getoptstring_COMMON -#define extraopts "stop | start | restart | describe | zap" -static 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 -}; -static 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 -}; -#include "_usage.c" - -int -openrc_run(int argc, char **argv) -{ - bool doneone = 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); - } - - 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_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); - } - -#ifdef HAVE_SELINUX - /* Ok, we are ready to go, so setup selinux if applicable */ - selinux_setup(argc, argv); -#endif - - 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); - - /* 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, "needsme") == 0 || - strcmp(optarg, "usesme") == 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); - 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; -} - -int -runscript(int argc, char **argv) -{ - ewarnv("runscript is deprecated; please use openrc-run instead."); - return (openrc_run(argc, argv)); -} diff --git a/src/rc/start-stop-daemon.c b/src/rc/start-stop-daemon.c index cc47c0b..7d2aba3 100644 --- a/src/rc/start-stop-daemon.c +++ b/src/rc/start-stop-daemon.c @@ -74,17 +74,9 @@ #include "builtins.h" #include "einfo.h" +#include "queue.h" #include "rc.h" #include "rc-misc.h" - -/* Some libc implementations don't define this */ -#ifndef LIST_FOREACH_SAFE -#define LIST_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = LIST_FIRST((head)); \ - (var) && ((tvar) = LIST_NEXT((var), field), 1); \ - (var) = (tvar)) -#endif - typedef struct scheduleitem { @@ -686,6 +678,7 @@ int tid = 0; char *redirect_stderr = NULL; char *redirect_stdout = NULL; + int stdin_fd; int stdout_fd; int stderr_fd; pid_t pid, spid; @@ -927,10 +920,13 @@ exec = name; if (name && start) *argv = name; - } else if (name) + } else if (name) { *--argv = name; - else if (exec) + ++argc; + } else if (exec) { *--argv = exec; + ++argc; + }; if (stop || sig != -1) { if (sig == -1) @@ -1083,7 +1079,7 @@ exit (EXIT_SUCCESS); einfon("Would start"); - while (argc-- >= 0) + while (argc-- > 0) printf(" %s", *argv++); printf("\n"); eindent(); @@ -1252,6 +1248,7 @@ setenv("PATH", newpath, 1); } + stdin_fd = devnull_fd; stdout_fd = devnull_fd; stderr_fd = devnull_fd; if (redirect_stdout) { @@ -1271,7 +1268,8 @@ applet, redirect_stderr, strerror(errno)); } - /* We don't redirect stdin as some daemons may need it */ + 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")))