-
Notifications
You must be signed in to change notification settings - Fork 0
Tools improving compatibility between different init systems
License
chris-se/cross-init-tools
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
cross-init-tools: Improve compatibility between init systems Status: experimental. It should work, but a review is required. Since this code might be executed with privileges, a proper review should look at security aspects, which hasn't been done so far. (Somebody definitely needs to review the mkdtemp part, if that is really correct in the way it's used.) Prerequisits: dbus-1 library (the low-level one) cross-init-bridge: Maps between upstart and systemd types of readiness notifications and socket activation. It automatically detects the init system it is run on. An option named --target specifies which init system the daemon was designed for (upstart, systemd) and the program translates between that and the init system currently in use (it is essentially a NOOP if both are the same). ::: Compile: gcc -Wall -o cross-init-bridge cross-init-bridge.c \ -I$DBUS_INCLUDE_PATH -L$DBUS_LIBRARY_PATH -ldbus-1 For example, under x86_64 Kubuntu Saucy Salamander, use: gcc -Wall -o cross-init-bridge cross-init-bridge.c \ -I/usr/include/dbus-1.0 \ -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include \ -ldbus-1 Under Fedora 19, use: gcc -Wall -o cross-init-bridge cross-init-bridge.c \ -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include \ -ldbus-1 Under Fedora 20, use: gcc -Wall -o cross-init-bridge cross-init-bridge.c \ -I/usr/include/dbus-1.0 -I/usr/lib64/dbus-1.0/include \ -ldbus-1 ::: Usage: For using a daemon that was designed for upstart from a systemd unit file, use: ExecStart=/.../cross-init-bridge -t upstart -- /.../daemon If the daemon uses raise(SIGSTOP) to notify upstart of its readiness, use Type=notify ExecStart=/.../cross-init-bridge -t upstart -N -- /.../daemon For using a daemon that was designed for systemd from an upstart job file, use: exec /.../cross-init-bridge -t systemd -- /.../daemon If the daemon uses systemd's sd_notify for readiness notification, use expect stop exec /.../cross-init-bridge -t systemd -N -- /.../daemon Sockets will be translated if passed from the init system (autodetected). Notifications are not autodetected, since the upstart notification type changes process semantics quite a bit so that startup is completely broken if the init system (and by extension, this bridge) and the daemon don't agree on the notification scheme. Therefore the system administrator / packager should be responsible for explicitly requesting support for that. ::: Tested on: Kubuntu 13.10 (Saucy Salamander) for Upstart (Upstart 1.10) Fedora 19 (Schrödinger's Cat) for systemd (systemd 204) (WARNING for systemd 204: see unter "Bugs") Fedora 20 (Heisenbug) for systemd (systemd 208) ::: Peculiarities: systemd allows for arbitrary messages to be passed over the notification interface. It also is not restricted to daemon startup. For this reason, a babysitter process is kept alive for the lifetime of the daemon to provide the socket. Only the first notification with READY=1 is going to be relevant, all other data coming through this socket is currently ignored - but is actually read from the socket so that the kernel buffer doesn't fill up. There is an option to end the babysitter process early, by specifying --end-after-ready on the command line. If the daemon does not use the notification socket again after it has sent READY=1 over the socket, this is fine. But if the daemon actually does use the notification socket after signaling readiness, the babysitter should continue to run, providing the socket as a black hole, for two reasons: - The daemon might not be able to properly cope with the socket gone all of a sudden (this does not happen in the systemd case, because the socket will be there for the entire lifetime of the system). - If some other process re-creates the socket afterwards and the daemon tries to access that socket again, the other process will get all the notification data. If the socket is initially created under /run, this is not too big of a problem, since usually only privileged processes allowed to do so, but if the user verb is used in the upstart job file, /run may actually not be writable for the bridge and the socket may be created in /tmp. After the babysitter exits, the socket could in principle be recreated by an attacker, leading to two possbile kinds of attacks: information disclosure (which might or might not be an issue) and possibly denial of service for the daemon if the attacker simply creates the socket but never reads from it, the daemon might just block on the socket while notifying. Also note that each instance of a systemd daemon with notifications on top of upstart will have it's own socket (in contrast to systemd itself, which uses a single global socket), since this was the easiest design. At least /tmp has to be writable, otherwise the bridge will not work, because it doesn't have anywhere to create the socket. (By default, it first tries to create it under /run, but has /tmp as a fallback, since /run is usually only writable for root.) ::: Bugs: 1. When running an upstart daemon on top of systemd with readiness notification, this doesn't work in systemd 204, at least the one that's shipped with Fedora 19. With systemd 208 everything works properly. In systemd 204, it kind of breaks the internal state of systemd, so that it doesn't detect that the process is gone. A previous version in git was somewhat better for earlier versions of systemd (internal state not totally broken, but no proper detection of service failure either), but will cause warning messages to be generated. ::: WONTFIX/CANTFIX: 1. When running systemd-type notification daemon on top of upstart, all messages through the notification interface will be ignored, except for the initial READY=1 signal. This is usually harmless (although some information the administrator is interested in might get lost, so one could think about sending it to syslog(3) instead of eating it), but systemd allows for one important message to be sent via the socket that can't be properly handled: if the process specifies that it has changed its main process id via the MAINPID= message. Upstart doesn't appear to have a mechanism to notify it of such things, so it is doubtful this can ever be supported. (And if one actually implements a mechanism in upstart for this, the ĺogical thing would be to use systemd's notification protocol for this instead of inventing yet another thing, making this bridge obsolete.) Ironically, this bridge uses systemd's MAINPID= support for precisely this when emulating upstart's protocol for systemd. 2. Upstart only supports one socket at a time for activation. Also, only AF_INET (IPv4) and AF_LOCAL (UNIX domain / abstract) sockets are supported. For example, AF_INET6 (IPv6) is not supported. Additionally, it can only create SOCK_STREAM sockets (in IPv4 that means only TCP, no UDP). The bridge will check that all sockets it receives from systemd are SOCK_STREAM of type AF_INET and/or AF_LOCAL. If that is not the case (or the FD is a FIFO, which systemd supports but Upstart doesn't), this bridge is conservative and will refuse to start the daemon. (It also only allows one socket in total.) 3. Upstart only passes the socket when activated via the socket, the socket is not passed when started manually or because of another event. systemd always passes the socket, regardless of the way the program was started. This could lead to problems in both directions: 3a. If the service was started manually or due to some other reason that was not an activation event on a the socket, the socket will not be passed to the daemon. This might not seem problematic, but if no socket is passed, for a daemon to be useful it would have to create the socket itself, which it can't, because upstart already holds it. This is not specifically tied to this bridge, and appears to be a severe limitation in upstart's socket activation design, and unless this is fixed, this bridge cannot do anything about it. (Solving this problem would also probably also make it trivial to implement multiple sockets in upstart, but it is unclear as to how easy making the required changes will be.) 3b. An upstart service might expect that if a socket has been passed, that it will always have a client on the other end of the socket. If started in a systemd environment, a manual start will still pass the socket, but no client on the other end of it. A well-written daemon should cope with this regardless (since spurious select/poll/epoll events can happen anyway), but some daemons might not work properly because of this. In the opinion of the author of the bridge this type of behaviour should be considered a bug in the respective daemon, because it could already occur with upstart on a spurious polling event. TODO: - proper build system - wait for dbus name (for systemd Type=dbus services on upstart)
About
Tools improving compatibility between different init systems
Resources
License
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published