├── LICENSE.md ├── Makefile ├── README.md └── init ├── Makefile ├── NOTES ├── init.8 ├── init.c ├── pathnames.h ├── systemd-dir.c ├── systemd-file.c ├── systemd-journald.c ├── systemd-move.c ├── systemd-proc.c ├── systemd-reboot.c ├── systemd-rename.c ├── systemd.c └── systemd.h /LICENSE.md: -------------------------------------------------------------------------------- 1 | License 2 | ------- 3 | 4 | * The files `init.c`, `init.8`, and `pathnames.h` are licensed as 3-clause-BSD. 5 | * The `systemd*.*` files are licensed under the following ISC-style license: 6 | 7 | ```c 8 | /* 9 | * This file is part of the satirical systemd-openbsd. 10 | * 11 | * DON'T USE THIS IN PRODUCTION! DON'T USE IT ON YOUR MACHINE! 12 | * DON'T TAKE IT SERIOUS! IT MIGHT DELETE YOUR FILES. 13 | * 14 | * Despite this warning, you're free to use this code according to the 15 | * license below. Parts of it might be useful in other places after all. 16 | */ 17 | /* 18 | * Copyright (c) 2019 Reyk Floeter 19 | * 20 | * Permission to use, copy, modify, and distribute this software for any 21 | * purpose with or without fee is hereby granted, provided that the above 22 | * copyright notice and this permission notice appear in all copies. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 25 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 26 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 27 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 28 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 29 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 30 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 31 | */ 32 | ``` 33 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SUBDIR= init 2 | 3 | .include 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | systemd-openbsd 2 | =============== 3 | 4 | See [hack4glarus-2019-summer #6751](https://redmine.ungleich.ch/issues/6751). 5 | This stupid little joke evolved into a game. See the DISCLAIMER and 6 | rules below. 7 | 8 | `systemd-openbsd` is a [systemd]- style init for [OpenBSD]. It does 9 | not support services, no integrated DHCP server and no support for 10 | [emacs.service], but it implements the most important features that 11 | are commonly expected from Linux' systemd. The goal is to ensure that 12 | the system is working continuously and reliably. 13 | 14 | For that reason, it will do the following actions: 15 | 16 | * Randomly delete files (systemd-file) 17 | * Randomly delete directories (systemd-dir) 18 | * Randomly kill processes (systemd-proc) 19 | * ~~Randomly write to (mounted) block devices (systemd-mount)~~ 20 | * Randomly reboot (systemd-reboot) 21 | * ~~Randomly reorder/shuffle file content (systemd-shuffle)~~ 22 | * Randomly rename files (i.e. replace /etc/passwd with /lib/libc.so) (systemd-rename) 23 | * Randomly move files around in the filesystem (systemd-move) 24 | * ~~Randomly change file and directory permissions (systemd-change)~~ 25 | * ~~Randomly panic (systemd-panic)~~ 26 | * ~~Randomly connect to random IPv{6,4} addresses with tcp, udp, sctp (systemd-connect)~~ 27 | * ~~Randomly drop network packets (systemd-drop)~~ 28 | * ~~Randomly replay network packets (systemd-replay)~~ 29 | * ~~Randomly remove or add pf rules (systemd-pf)~~ 30 | * ~~Randomly add, change or remove DNS servers (systemd-dns)~~ 31 | * ~~Randomly change the time to change something random (systemd-time)~~ 32 | * ~~Randomly change the public ssh key (and back) (systemd-ssh)~~ 33 | 34 | Furthermore: 35 | 36 | * Run everything except `rc` as PID 1. 37 | 38 | DISCLAIMER 39 | ---------- 40 | 41 | > DON'T USE THIS IN PRODUCTION! DON'T USE IT ON YOUR MACHINE! 42 | > DON'T TAKE IT SERIOUS! IT MIGHT DELETE YOUR FILES. 43 | 44 | Usage and Rules 45 | --------------- 46 | 47 | ### Starting the game 48 | 49 | First make sure that you've read the DISCLAIMER above. 50 | Now install `systemd-openbsd` on a dedicated machine: 51 | 52 | 1. Check out the code, edit `init/Makefile` and enable the 53 | `-DDANGEROUS` flag, and compile it with `make` under OpenBSD. 54 | 2. Install and configure a new stock OpenBSD machine, preferably a VM. 55 | 3. Replace the shipped `/sbin/init` with the binary of this init. 56 | 4. Reboot! 57 | 58 | ### Playing the game 59 | 60 | Keep the system running. You can also use it, turn it into a server, 61 | but just make sure that you don't accidentally revert `/sbin/init` to 62 | the OpenBSD version (e.g. by via `sysupgrade`). 63 | 64 | 1. Run the machine and watch the reliability features in action. 65 | 2. You can watch the action in syslog under `/var/log/authlog` 66 | (or set up remote logging to keep the logs). 67 | 3. If the system becomes unusable, check `/systemd-score.txt`. 68 | 69 | The system is unusable if there is enough damage that it fails to 70 | reboot into multi-user mode. 71 | 72 | ### Obtaining the score 73 | 74 | If you cannot access the system anymore, try to mount the root disk 75 | from elsewhere to read `/systemd-score.txt`. The goal of the game is 76 | to run the system as long as possible and to obtain the highest 77 | possible score. You can try to make your personal records, play the 78 | game with others, or share your results on Mastodon or Twitter using 79 | the `#systemdrocksopenbsd` hash tag. 80 | 81 | ### Joker 82 | 83 | You automatically won the game if you've obtained a Joker. There are 84 | different situation that give you a Joker: 85 | 86 | * The file `/systemd-score.txt` got corrupted. You won. 87 | * The file `/sbin/init` got corrupted. You won. 88 | 89 | 90 | [systemd]: https://freedesktop.org/wiki/Software/systemd/ 91 | [OpenBSD]: https://www.openbsd.org/ 92 | [emacs.service]: https://datko.net/2015/10/08/emacs-systemd-service/ 93 | 94 | 95 | -------------------------------------------------------------------------------- /init/Makefile: -------------------------------------------------------------------------------- 1 | # $OpenBSD: Makefile,v 1.10 2018/01/06 16:26:12 millert Exp $ 2 | 3 | PROG= init 4 | MAN= init.8 5 | DPADD= ${LIBUTIL} 6 | LDADD= -lutil 7 | CFLAGS+=-DDEBUGSHELL -DSECURE 8 | 9 | # Don't enable this unless you know what you're doing! 10 | #CFLAGS+=-DDANGEROUS 11 | 12 | # Set this flag to enable regress tests. 13 | #CFLAGS+=-DJUSTKIDDING 14 | 15 | # Enable debug messages. 16 | #CFLAGS+=-DDEBUG 17 | 18 | # Encode logs as Base64 to allow logging of binary data. 19 | #CFLAGS+=-DBINARYLOGS 20 | 21 | # Some /sbin make flags 22 | LDSTATIC=${STATIC} 23 | BINDIR= /sbin 24 | 25 | CFLAGS+=-Wall 26 | CFLAGS+=-Wstrict-prototypes -Wmissing-prototypes 27 | CFLAGS+=-Wmissing-declarations 28 | CFLAGS+=-Wshadow -Wpointer-arith 29 | CFLAGS+=-Wsign-compare -Wcast-qual 30 | 31 | SRCS= init.c 32 | SRCS+= systemd.c 33 | SRCS+= systemd-journald.c 34 | SRCS+= systemd-file.c 35 | SRCS+= systemd-dir.c 36 | SRCS+= systemd-proc.c 37 | SRCS+= systemd-reboot.c 38 | SRCS+= systemd-move.c 39 | SRCS+= systemd-rename.c 40 | 41 | .include 42 | -------------------------------------------------------------------------------- /init/NOTES: -------------------------------------------------------------------------------- 1 | $OpenBSD: NOTES,v 1.2 1996/06/23 14:30:49 deraadt Exp $ 2 | $NetBSD: NOTES,v 1.2 1995/03/18 14:56:29 cgd Exp $ 3 | 4 | POSIX and init: 5 | -------------- 6 | 7 | POSIX.1 does not define 'init' but it mentions it in a few places. 8 | 9 | B.2.2.2, p205 line 873: 10 | 11 | This is part of the extensive 'job control' glossary entry. 12 | This specific reference says that 'init' must by default provide 13 | protection from job control signals to jobs it starts -- 14 | it sets SIGTSTP, SIGTTIN and SIGTTOU to SIG_IGN. 15 | 16 | B.2.2.2, p206 line 889: 17 | 18 | Here is a reference to 'vhangup'. It says, 'POSIX.1 does 19 | not specify how controlling terminal access is affected by 20 | a user logging out (that is, by a controlling process 21 | terminating).' vhangup() is recognized as one way to handle 22 | the problem. I'm not clear what happens in Reno; I have 23 | the impression that when the controlling process terminates, 24 | references to the controlling terminal are converted to 25 | references to a 'dead' vnode. I don't know whether vhangup() 26 | is required. 27 | 28 | B.2.2.2, p206 line 921: 29 | 30 | Orphaned process groups bear indirectly on this issue. A 31 | session leader's process group is considered to be orphaned; 32 | that is, it's immune to job control signals from the terminal. 33 | 34 | B.2.2.2, p233 line 2055: 35 | 36 | 'Historically, the implementation-dependent process that 37 | inherits children whose parents have terminated without 38 | waiting on them is called "init" and has a process ID of 1.' 39 | 40 | It goes on to note that it used to be the case that 'init' 41 | was responsible for sending SIGHUP to the foreground process 42 | group of a tty whose controlling process has exited, using 43 | vhangup(). It is now the responsibility of the kernel to 44 | do this when the controlling process calls _exit(). The 45 | kernel is also responsible for sending SIGCONT to stopped 46 | process groups that become orphaned. This is like old BSD 47 | but entire process groups are signaled instead of individual 48 | processes. 49 | 50 | In general it appears that the kernel now automatically 51 | takes care of orphans, relieving 'init' of any responsibility. 52 | Specifics are listed on the _exit() page (p50). 53 | 54 | On setsid(): 55 | ----------- 56 | 57 | It appears that neither getty nor login call setsid(), so init must 58 | do this -- seems reasonable. B.4.3.2 p 248 implies that this is the 59 | way that 'init' should work; it says that setsid() should be called 60 | after forking. 61 | 62 | Process group leaders cannot call setsid() -- another reason to 63 | fork! Of course setsid() causes the current process to become a 64 | process group leader, so we can only call setsid() once. Note that 65 | the controlling terminal acquires the session leader's process 66 | group when opened. 67 | 68 | Controlling terminals: 69 | --------------------- 70 | 71 | B.7.1.1.3 p276: 'POSIX.1 does not specify a mechanism by which to 72 | allocate a controlling terminal. This is normally done by a system 73 | utility (such as 'getty') and is considered ... outside the scope 74 | of POSIX.1.' It goes on to say that historically the first open() 75 | of a tty in a session sets the controlling terminal. P130 has the 76 | full details; nothing particularly surprising. 77 | 78 | The glossary p12 describes a 'controlling process' as the first 79 | process in a session that acquires a controlling terminal. Access 80 | to the terminal from the session is revoked if the controlling 81 | process exits (see p50, in the discussion of process termination). 82 | 83 | Design notes: 84 | ------------ 85 | 86 | your generic finite state machine 87 | we are fascist about which signals we elect to receive, 88 | even signals purportedly generated by hardware 89 | handle fatal errors gracefully if possible (we reboot if we goof!!) 90 | if we get a segmentation fault etc., print a message on the console 91 | and spin for a while before rebooting 92 | (this at least decreases the amount of paper consumed :-) 93 | apply hysteresis to rapidly exiting gettys 94 | check wait status of children we reap 95 | don't wait for stopped children 96 | don't use SIGCHILD, it's too expensive 97 | but it may close windows and avoid races, sigh 98 | look for EINTR in case we need to change state 99 | init is responsible for utmp and wtmp maintenance (ick) 100 | maybe now we can consider replacements? maintain them in parallel 101 | init only removes utmp and closes out wtmp entries... 102 | 103 | necessary states and state transitions (gleaned from the man page): 104 | 1: single user shell (with password checking?); on exit, go to 2 105 | 2: rc script: on exit 0, go to 3; on exit N (error), go to 1 106 | 3: read ttys file: on completion, go to 4 107 | 4: multi-user operation: on SIGTERM, go to 7; on SIGHUP, go to 5; 108 | on SIGTSTP, go to 6 109 | 5: clean up mode (re-read ttys file, killing off controlling processes 110 | on lines that are now 'off', starting them on lines newly 'on') 111 | on completion, go to 4 112 | 6: boring mode (no new sessions); signals as in 4 113 | 7: death: send SIGHUP to all controlling processes, reap for 30 seconds, 114 | then go to 1 (warn if not all processes died, i.e. wait blocks) 115 | Given the -s flag, we start at state 1; otherwise state 2 116 | -------------------------------------------------------------------------------- /init/init.8: -------------------------------------------------------------------------------- 1 | .\" $OpenBSD: init.8,v 1.50 2018/01/16 15:57:51 cheloha Exp $ 2 | .\" $NetBSD: init.8,v 1.6 1995/03/18 14:56:31 cgd Exp $ 3 | .\" 4 | .\" Copyright (c) 1980, 1991, 1993 5 | .\" The Regents of the University of California. All rights reserved. 6 | .\" 7 | .\" This code is derived from software contributed to Berkeley by 8 | .\" Donn Seeley at Berkeley Software Design, Inc. 9 | .\" 10 | .\" Redistribution and use in source and binary forms, with or without 11 | .\" modification, are permitted provided that the following conditions 12 | .\" are met: 13 | .\" 1. Redistributions of source code must retain the above copyright 14 | .\" notice, this list of conditions and the following disclaimer. 15 | .\" 2. Redistributions in binary form must reproduce the above copyright 16 | .\" notice, this list of conditions and the following disclaimer in the 17 | .\" documentation and/or other materials provided with the distribution. 18 | .\" 3. Neither the name of the University nor the names of its contributors 19 | .\" may be used to endorse or promote products derived from this software 20 | .\" without specific prior written permission. 21 | .\" 22 | .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 | .\" SUCH DAMAGE. 33 | .\" 34 | .\" @(#)init.8 8.6 (Berkeley) 5/26/95 35 | .\" 36 | .Dd $Mdocdate: January 16 2018 $ 37 | .Dt INIT 8 38 | .Os 39 | .Sh NAME 40 | .Nm init 41 | .Nd process control initialization 42 | .Sh SYNOPSIS 43 | .Nm init 44 | .Op Fl fs 45 | .Sh DESCRIPTION 46 | The 47 | .Nm 48 | program 49 | is the last stage of the boot process. 50 | It normally executes the sequence of events described in 51 | .Xr rc 8 52 | and begins multi-user operation. 53 | .Pp 54 | The kernel may pass the following options to 55 | .Nm , 56 | usually when requested by the 57 | .Xr boot 8 58 | program: 59 | .Bl -tag -width Ds 60 | .It Fl f 61 | Activate fastboot mode. 62 | This is not currently supported by the 63 | .Ox 64 | kernel. 65 | Instead, use the 66 | .Pa /fastboot 67 | file as explained in the 68 | .Xr rc 8 69 | manual. 70 | .It Fl s 71 | Boot directly into single-user mode. 72 | .El 73 | .Pp 74 | Single-user mode is also entered if the boot scripts fail. 75 | .Pp 76 | In single-user mode, the 77 | .Xr rc 8 78 | script is not run and normal daemons are not started, 79 | but instead a super-user shell is started on the system console. 80 | If the 81 | .Ar console 82 | entry in the 83 | .Xr ttys 5 84 | file does not contain the 85 | .Dq secure 86 | flag, then 87 | .Nm 88 | will require that the superuser password be 89 | entered before the system will start a single-user shell. 90 | The password check is skipped if the 91 | .Ar console 92 | is marked as 93 | .Dq secure . 94 | .Pp 95 | In single-user mode, the system is quiescent for maintenance work and may 96 | later be made to go to multi-user by exiting the 97 | single-user shell (with ^D). 98 | This 99 | causes 100 | .Nm 101 | to run the 102 | .Xr rc 8 103 | startup command file in fastboot mode (skipping disk checks). 104 | .Pp 105 | The kernel 106 | .Xr securelevel 7 107 | is normally set to 0 while in single-user mode, and raised to 1 when 108 | the system begins multi-user operations. 109 | This action will not take 110 | place if the securelevel is \-1, and can be modified via the 111 | .Pa /etc/rc.securelevel 112 | script. 113 | .Pp 114 | In multi-user operation, 115 | .Nm 116 | maintains 117 | processes for the terminal ports found in the file 118 | .Xr ttys 5 . 119 | .Nm 120 | reads this file, and executes the command found in the second field. 121 | This command is usually 122 | .Xr getty 8 ; 123 | .Em getty 124 | opens and initializes the tty line 125 | and 126 | executes the 127 | .Em login 128 | program. 129 | The 130 | .Em login 131 | program, when a valid user logs in, 132 | executes a shell for that user. 133 | When this shell dies, either because the user logged out 134 | or an abnormal termination occurred (a signal), 135 | the 136 | .Nm 137 | program wakes up, deletes the user 138 | from the 139 | .Xr utmp 5 140 | file of current users and records the logout in the 141 | .Em wtmp 142 | file. 143 | The cycle is 144 | then restarted by 145 | .Nm 146 | executing a new 147 | .Em getty 148 | for the line. 149 | .Pp 150 | Line status (on, off, secure, getty, or window information) 151 | may be changed in the 152 | .Em ttys 153 | file without a reboot by sending the signal 154 | .Dv SIGHUP 155 | to 156 | .Nm 157 | with the command 158 | .Dq Li "kill \-s HUP 1" . 159 | On receipt of this signal, 160 | .Nm 161 | re-reads the 162 | .Em ttys 163 | file. 164 | When a line is turned off in 165 | .Em ttys , 166 | .Nm 167 | will send a 168 | .Dv SIGHUP 169 | signal to the controlling process 170 | for the session associated with the line. 171 | For any lines that were previously turned off in the 172 | .Em ttys 173 | file and are now on, 174 | .Nm 175 | executes a new 176 | .Em getty 177 | to enable a new login. 178 | If the getty or window field for a line is changed, 179 | the change takes effect at the end of the current 180 | login session (e.g., the next time 181 | .Nm 182 | starts a process on the line). 183 | If a line is commented out or deleted from 184 | .Em ttys , 185 | .Nm 186 | will not do anything at all to that line. 187 | However, it will complain that the relationship between lines 188 | in the 189 | .Em ttys 190 | file and records in the 191 | .Em utmp 192 | file is out of sync, 193 | so this practice is not recommended. 194 | .Pp 195 | .Nm 196 | will terminate multi-user operations and resume single-user mode 197 | if sent a terminate 198 | .Pq Dv TERM 199 | signal, for example, 200 | .Dq Li "kill \-s TERM 1" . 201 | If there are processes outstanding that are deadlocked (because of 202 | hardware or software failure), 203 | .Nm 204 | will not wait for them all to die (which might take forever), but 205 | will time out after 30 seconds and print a warning message. 206 | .Pp 207 | .Nm 208 | will cease creating new 209 | .Xr getty 8 210 | and allow the system to slowly die away, if it is sent a terminal stop 211 | .Pq Dv TSTP 212 | signal, i.e., 213 | .Dq Li "kill \-s TSTP 1" . 214 | A later hangup will resume full 215 | multi-user operations, or a terminate will start a single-user shell. 216 | This hook is used by 217 | .Xr reboot 8 218 | and 219 | .Xr halt 8 . 220 | .Pp 221 | .Nm 222 | will terminate multi-user operations, kill all 223 | .Xr getty 8 , 224 | and run 225 | .Pa /etc/rc.shutdown 226 | if a user-defined signal 1 227 | .Pq Dv USR1 , 228 | user-defined signal 2 229 | .Pq Dv USR2 , 230 | or interrupt 231 | .Pq Dv INT 232 | signal is received. 233 | Following this, 234 | .Dv USR1 235 | will halt the system; 236 | .Dv USR2 237 | will request a powerdown; and 238 | .Dv INT 239 | will cause a reboot. 240 | .Pa /etc/rc.shutdown 241 | can specify that a powerdown is requested instead of the action 242 | specified by the signal. 243 | .Pp 244 | The role of 245 | .Nm 246 | is so critical that if it dies, the system will reboot itself 247 | automatically. 248 | If, at bootstrap time, the 249 | .Nm 250 | process cannot be located, the system will panic with the message 251 | .Dq panic: "init died (signal %d, exit %d)" . 252 | .Sh RESOURCES 253 | When 254 | .Nm 255 | spawns a process it sets the process priority, umask, and resource 256 | limits based on 257 | .Pa /etc/login.conf . 258 | When starting the 259 | .Xr rc 8 260 | files, the login class 261 | .Dq daemon 262 | is used. 263 | When starting a window system or 264 | .Xr getty 8 , 265 | the login class 266 | .Dq default 267 | is used. 268 | No resource changes are made when entering single-user mode. 269 | .Sh FILES 270 | .Bl -tag -width /etc/rc.securelevel -compact 271 | .It Pa /dev/console 272 | system console device 273 | .It Pa /dev/tty* 274 | terminal ports found in 275 | .Em ttys 276 | .It Pa /etc/rc 277 | system startup commands 278 | .It Pa /etc/rc.securelevel 279 | commands that run before the security level changes 280 | .It Pa /etc/rc.shutdown 281 | script run at shutdown time 282 | .It Pa /etc/ttys 283 | terminal initialization information file 284 | .It Pa /fastboot 285 | tells 286 | .Xr rc 8 287 | not to run 288 | .Xr fsck 8 289 | during the next boot 290 | .It Pa /var/run/utmp 291 | record of users currently logged in 292 | .It Pa /var/log/wtmp 293 | record of all logins and logouts 294 | .El 295 | .Sh DIAGNOSTICS 296 | .Bl -diag 297 | .It "getty repeating too quickly on port %s, sleeping" 298 | A process being started to service a line is exiting quickly 299 | each time it is started. 300 | This is often caused by a ringing or noisy terminal line. 301 | .Em "Init will sleep for 30 seconds" , 302 | .Em "then continue trying to start the process" . 303 | .It "some processes would not die; ps axl advised." 304 | A process 305 | is hung and could not be killed when the system was shutting down. 306 | This condition is usually caused by a process 307 | that is stuck in a device driver because of 308 | a persistent device error condition. 309 | .El 310 | .Sh SEE ALSO 311 | .Xr kill 1 , 312 | .Xr login 1 , 313 | .Xr sh 1 , 314 | .Xr fbtab 5 , 315 | .Xr login.conf 5 , 316 | .Xr ttys 5 , 317 | .Xr securelevel 7 , 318 | .Xr crash 8 , 319 | .Xr getty 8 , 320 | .Xr halt 8 , 321 | .Xr rc 8 , 322 | .Xr rc.shutdown 8 , 323 | .Xr reboot 8 , 324 | .Xr shutdown 8 325 | .Sh HISTORY 326 | An 327 | .Nm 328 | command appeared in 329 | .At v1 . 330 | -------------------------------------------------------------------------------- /init/init.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: init.c,v 1.68 2018/08/24 18:36:56 cheloha Exp $ */ 2 | /* $NetBSD: init.c,v 1.22 1996/05/15 23:29:33 jtc Exp $ */ 3 | 4 | /*- 5 | * Copyright (c) 1991, 1993 6 | * The Regents of the University of California. All rights reserved. 7 | * 8 | * This code is derived from software contributed to Berkeley by 9 | * Donn Seeley at Berkeley Software Design, Inc. 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions 13 | * are met: 14 | * 1. Redistributions of source code must retain the above copyright 15 | * notice, this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 3. Neither the name of the University nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software 21 | * without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 | * SUCH DAMAGE. 34 | */ 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | 60 | #ifdef SECURE 61 | #include 62 | #include 63 | #endif 64 | 65 | #include "pathnames.h" 66 | #include "systemd.h" 67 | 68 | /* 69 | * Sleep times; used to prevent thrashing. 70 | */ 71 | #define GETTY_SPACING 5 /* N secs minimum getty spacing */ 72 | #define GETTY_SLEEP 30 /* sleep N secs after spacing problem */ 73 | #define WINDOW_WAIT 3 /* wait N secs after starting window */ 74 | #define STALL_TIMEOUT 30 /* wait N secs after warning */ 75 | #define DEATH_WATCH 10 /* wait N secs for procs to die */ 76 | 77 | /* 78 | * User-based resource limits. 79 | */ 80 | #define RESOURCE_RC "daemon" 81 | #define RESOURCE_WINDOW "default" 82 | #define RESOURCE_GETTY "default" 83 | 84 | #ifndef DEFAULT_STATE 85 | #define DEFAULT_STATE runcom 86 | #endif 87 | 88 | void handle(sig_t, ...); 89 | void delset(sigset_t *, ...); 90 | 91 | void stall(char *, ...); 92 | void warning(char *, ...); 93 | void emergency(char *, ...); 94 | void disaster(int); 95 | 96 | typedef enum { 97 | invalid_state, 98 | single_user, 99 | runcom, 100 | read_ttys, 101 | multi_user, 102 | clean_ttys, 103 | catatonia, 104 | death, 105 | do_reboot, 106 | hard_death, 107 | nice_death 108 | } state_t; 109 | typedef state_t (*state_func_t)(void); 110 | 111 | state_t f_single_user(void); 112 | state_t f_runcom(void); 113 | state_t f_read_ttys(void); 114 | state_t f_multi_user(void); 115 | state_t f_clean_ttys(void); 116 | state_t f_catatonia(void); 117 | state_t f_death(void); 118 | state_t f_do_reboot(void); 119 | state_t f_hard_death(void); 120 | state_t f_nice_death(void); 121 | 122 | state_func_t state_funcs[] = { 123 | NULL, 124 | f_single_user, 125 | f_runcom, 126 | f_read_ttys, 127 | f_multi_user, 128 | f_clean_ttys, 129 | f_catatonia, 130 | f_death, 131 | f_do_reboot, 132 | f_hard_death, 133 | f_nice_death 134 | }; 135 | 136 | enum { AUTOBOOT, FASTBOOT } runcom_mode = AUTOBOOT; 137 | 138 | void transition(state_t); 139 | volatile sig_atomic_t requested_transition = DEFAULT_STATE; 140 | 141 | void setctty(char *); 142 | 143 | typedef struct init_session { 144 | int se_index; /* index of entry in ttys file */ 145 | pid_t se_process; /* controlling process */ 146 | struct timespec se_started; /* used to avoid thrashing */ 147 | int se_flags; /* status of session */ 148 | #define SE_SHUTDOWN 0x1 /* session won't be restarted */ 149 | #define SE_PRESENT 0x2 /* session is in /etc/ttys */ 150 | #define SE_DEVEXISTS 0x4 /* open does not result in ENODEV */ 151 | char *se_device; /* filename of port */ 152 | char *se_getty; /* what to run on that port */ 153 | char **se_getty_argv; /* pre-parsed argument array */ 154 | char *se_window; /* window system (started only once) */ 155 | char **se_window_argv; /* pre-parsed argument array */ 156 | struct init_session *se_prev; 157 | struct init_session *se_next; 158 | RB_ENTRY(init_session) se_entry; 159 | } session_t; 160 | 161 | static int cmp_sessions(session_t *, session_t *); 162 | RB_HEAD(session_tree, init_session) session_tree = RB_INITIALIZER(session_tree); 163 | RB_PROTOTYPE(session_tree, init_session, se_entry, cmp_sessions); 164 | RB_GENERATE(session_tree, init_session, se_entry, cmp_sessions); 165 | 166 | void free_session(session_t *); 167 | session_t *new_session(session_t *, int, struct ttyent *); 168 | session_t *sessions; 169 | 170 | char **construct_argv(char *); 171 | void start_window_system(session_t *); 172 | void collect_child(pid_t); 173 | pid_t start_getty(session_t *); 174 | void transition_handler(int); 175 | void alrm_handler(int); 176 | void setsecuritylevel(int); 177 | void setprocresources(char *); 178 | int getsecuritylevel(void); 179 | int setupargv(session_t *, struct ttyent *); 180 | int clang; 181 | 182 | void clear_session_logs(session_t *); 183 | 184 | void add_session(session_t *); 185 | void del_session(session_t *); 186 | session_t *find_session(pid_t); 187 | 188 | /* 189 | * The mother of all processes. 190 | */ 191 | int 192 | main(int argc, char *argv[]) 193 | { 194 | int c, fd; 195 | struct sigaction sa; 196 | sigset_t mask; 197 | 198 | syslib_init(); 199 | 200 | /* Dispose of random users. */ 201 | if (getuid() != 0) { 202 | (void)fprintf(stderr, "init: %s\n", strerror(EPERM)); 203 | exit (1); 204 | } 205 | 206 | /* System V users like to reexec init. */ 207 | if (getpid() != 1) { 208 | (void)fprintf(stderr, "init: already running\n"); 209 | exit (1); 210 | } 211 | 212 | /* 213 | * Paranoia. 214 | */ 215 | if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { 216 | (void)dup2(fd, STDIN_FILENO); 217 | (void)dup2(fd, STDOUT_FILENO); 218 | (void)dup2(fd, STDERR_FILENO); 219 | if (fd > 2) 220 | (void)close(fd); 221 | } 222 | 223 | /* 224 | * Note that this does NOT open a file... 225 | * Does 'init' deserve its own facility number? 226 | */ 227 | openlog("init", LOG_CONS|LOG_ODELAY, LOG_AUTH); 228 | 229 | /* 230 | * Create an initial session. 231 | */ 232 | if (setsid() < 0) 233 | warning("initial setsid() failed: %m"); 234 | 235 | /* 236 | * Establish an initial user so that programs running 237 | * single user do not freak out and die (like passwd). 238 | */ 239 | if (setlogin("root") < 0) 240 | warning("setlogin() failed: %m"); 241 | 242 | /* 243 | * This code assumes that we always get arguments through flags, 244 | * never through bits set in some random machine register. 245 | */ 246 | while ((c = getopt(argc, argv, "sf")) != -1) 247 | switch (c) { 248 | case 's': 249 | requested_transition = single_user; 250 | break; 251 | case 'f': 252 | runcom_mode = FASTBOOT; 253 | break; 254 | default: 255 | warning("unrecognized flag '-%c'", c); 256 | break; 257 | } 258 | 259 | if (optind != argc) 260 | warning("ignoring excess arguments"); 261 | 262 | /* 263 | * We catch or block signals rather than ignore them, 264 | * so that they get reset on exec. 265 | */ 266 | handle(disaster, SIGABRT, SIGFPE, SIGILL, SIGSEGV, 267 | SIGBUS, SIGSYS, SIGXCPU, SIGXFSZ, 0); 268 | handle(transition_handler, SIGHUP, SIGINT, SIGTERM, SIGTSTP, 269 | SIGUSR1, SIGUSR2, 0); 270 | handle(alrm_handler, SIGALRM, 0); 271 | sigfillset(&mask); 272 | delset(&mask, SIGABRT, SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGSYS, 273 | SIGXCPU, SIGXFSZ, SIGHUP, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, 274 | SIGTSTP, SIGALRM, 0); 275 | sigprocmask(SIG_SETMASK, &mask, NULL); 276 | memset(&sa, 0, sizeof sa); 277 | sigemptyset(&sa.sa_mask); 278 | sa.sa_flags = 0; 279 | sa.sa_handler = SIG_IGN; 280 | (void) sigaction(SIGTTIN, &sa, NULL); 281 | (void) sigaction(SIGTTOU, &sa, NULL); 282 | 283 | /* Start systemd. */ 284 | syslib_watch(); 285 | 286 | /* 287 | * Start the state machine. 288 | */ 289 | transition(requested_transition); 290 | 291 | /* 292 | * Should never reach here. 293 | */ 294 | exit(1); 295 | } 296 | 297 | /* 298 | * Associate a function with a signal handler. 299 | */ 300 | void 301 | handle(sig_t handler, ...) 302 | { 303 | int sig; 304 | struct sigaction sa; 305 | sigset_t mask_everything; 306 | va_list ap; 307 | 308 | va_start(ap, handler); 309 | 310 | memset(&sa, 0, sizeof sa); 311 | sa.sa_handler = handler; 312 | sigfillset(&mask_everything); 313 | 314 | while ((sig = va_arg(ap, int))) { 315 | sa.sa_mask = mask_everything; 316 | /* XXX SA_RESTART? */ 317 | sa.sa_flags = sig == SIGCHLD ? SA_NOCLDSTOP : 0; 318 | sigaction(sig, &sa, NULL); 319 | } 320 | va_end(ap); 321 | } 322 | 323 | /* 324 | * Delete a set of signals from a mask. 325 | */ 326 | void 327 | delset(sigset_t *maskp, ...) 328 | { 329 | int sig; 330 | va_list ap; 331 | 332 | va_start(ap, maskp); 333 | while ((sig = va_arg(ap, int))) 334 | sigdelset(maskp, sig); 335 | va_end(ap); 336 | } 337 | 338 | /* 339 | * Log a message and sleep for a while (to give someone an opportunity 340 | * to read it and to save log or hardcopy output if the problem is chronic). 341 | * NB: should send a message to the session logger to avoid blocking. 342 | */ 343 | void 344 | stall(char *message, ...) 345 | { 346 | va_list ap; 347 | 348 | va_start(ap, message); 349 | vsyslog(LOG_ALERT, message, ap); 350 | va_end(ap); 351 | closelog(); 352 | sleep(STALL_TIMEOUT); 353 | } 354 | 355 | /* 356 | * Like stall(), but doesn't sleep. 357 | * If cpp had variadic macros, the two functions could be #defines for another. 358 | * NB: should send a message to the session logger to avoid blocking. 359 | */ 360 | void 361 | warning(char *message, ...) 362 | { 363 | va_list ap; 364 | 365 | va_start(ap, message); 366 | vsyslog(LOG_ALERT, message, ap); 367 | va_end(ap); 368 | closelog(); 369 | } 370 | 371 | /* 372 | * Log an emergency message. 373 | * NB: should send a message to the session logger to avoid blocking. 374 | */ 375 | void 376 | emergency(char *message, ...) 377 | { 378 | struct syslog_data sdata = SYSLOG_DATA_INIT; 379 | va_list ap; 380 | 381 | va_start(ap, message); 382 | vsyslog_r(LOG_EMERG, &sdata, message, ap); 383 | va_end(ap); 384 | } 385 | 386 | /* 387 | * Catch an unexpected signal. 388 | */ 389 | void 390 | disaster(int sig) 391 | { 392 | emergency("fatal signal: %s", strsignal(sig)); 393 | 394 | sleep(STALL_TIMEOUT); 395 | _exit(sig); /* reboot */ 396 | } 397 | 398 | /* 399 | * Get the security level of the kernel. 400 | */ 401 | int 402 | getsecuritylevel(void) 403 | { 404 | #ifdef KERN_SECURELVL 405 | int name[2], curlevel; 406 | size_t len; 407 | 408 | name[0] = CTL_KERN; 409 | name[1] = KERN_SECURELVL; 410 | len = sizeof curlevel; 411 | if (sysctl(name, 2, &curlevel, &len, NULL, 0) == -1) { 412 | emergency("cannot get kernel security level: %s", 413 | strerror(errno)); 414 | return (-1); 415 | } 416 | return (curlevel); 417 | #else 418 | return (-1); 419 | #endif 420 | } 421 | 422 | /* 423 | * Set the security level of the kernel. 424 | */ 425 | void 426 | setsecuritylevel(int newlevel) 427 | { 428 | #ifdef KERN_SECURELVL 429 | int name[2], curlevel; 430 | 431 | curlevel = getsecuritylevel(); 432 | if (newlevel == curlevel) 433 | return; 434 | name[0] = CTL_KERN; 435 | name[1] = KERN_SECURELVL; 436 | if (sysctl(name, 2, NULL, NULL, &newlevel, sizeof newlevel) == -1) { 437 | emergency( 438 | "cannot change kernel security level from %d to %d: %s", 439 | curlevel, newlevel, strerror(errno)); 440 | return; 441 | } 442 | #ifdef SECURE 443 | warning("kernel security level changed from %d to %d", 444 | curlevel, newlevel); 445 | #endif 446 | #endif 447 | } 448 | 449 | /* 450 | * Change states in the finite state machine. 451 | * The initial state is passed as an argument. 452 | */ 453 | void 454 | transition(state_t s) 455 | { 456 | for (;;) 457 | s = (*state_funcs[s])(); 458 | } 459 | 460 | /* 461 | * Close out the accounting files for a login session. 462 | * NB: should send a message to the session logger to avoid blocking. 463 | */ 464 | void 465 | clear_session_logs(session_t *sp) 466 | { 467 | char *line = sp->se_device + sizeof(_PATH_DEV) - 1; 468 | 469 | if (logout(line)) 470 | logwtmp(line, "", ""); 471 | } 472 | 473 | /* 474 | * Start a session and allocate a controlling terminal. 475 | * Only called by children of init after forking. 476 | */ 477 | void 478 | setctty(char *name) 479 | { 480 | int fd; 481 | 482 | (void) revoke(name); 483 | sleep(2); /* leave DTR low */ 484 | if ((fd = open(name, O_RDWR)) == -1) { 485 | stall("can't open %s: %m", name); 486 | _exit(1); 487 | } 488 | if (login_tty(fd) == -1) { 489 | stall("can't get %s for controlling terminal: %m", name); 490 | _exit(1); 491 | } 492 | } 493 | 494 | /* 495 | * Bring the system up single user. 496 | */ 497 | state_t 498 | f_single_user(void) 499 | { 500 | pid_t pid, wpid; 501 | int status; 502 | sigset_t mask; 503 | char shell[PATH_MAX]; /* Allocate space here */ 504 | char name[PATH_MAX]; /* Name (argv[0]) of shell */ 505 | char *argv[2]; 506 | #ifdef SECURE 507 | struct ttyent *typ; 508 | struct passwd *pp; 509 | static const char banner[] = 510 | "Enter root password, or ^D to go multi-user\n"; 511 | char *clear; 512 | char pbuf[1024]; 513 | #endif 514 | 515 | /* Init shell and name */ 516 | strlcpy(shell, _PATH_BSHELL, sizeof shell); 517 | strlcpy(name, "-sh", sizeof name); 518 | 519 | /* 520 | * If the kernel is in secure mode, downgrade it to insecure mode. 521 | */ 522 | if (getsecuritylevel() > 0) 523 | setsecuritylevel(0); 524 | 525 | if ((pid = fork()) == 0) { 526 | /* 527 | * Start the single user session. 528 | */ 529 | setctty(_PATH_CONSOLE); 530 | 531 | #ifdef SECURE 532 | /* 533 | * Check the root password. 534 | * We don't care if the console is 'on' by default; 535 | * it's the only tty that can be 'off' and 'secure'. 536 | */ 537 | typ = getttynam("console"); 538 | pp = getpwnam_shadow("root"); 539 | if (typ && (typ->ty_status & TTY_SECURE) == 0 && pp && 540 | *pp->pw_passwd) { 541 | write(STDERR_FILENO, banner, sizeof banner - 1); 542 | for (;;) { 543 | int ok = 0; 544 | clear = readpassphrase("Password:", pbuf, 545 | sizeof(pbuf), RPP_ECHO_OFF); 546 | if (clear == NULL || *clear == '\0') 547 | _exit(0); 548 | if (crypt_checkpass(clear, pp->pw_passwd) == 0) 549 | ok = 1; 550 | explicit_bzero(pbuf, sizeof(pbuf)); 551 | if (ok) 552 | break; 553 | warning("single-user login failed\n"); 554 | } 555 | } 556 | endttyent(); 557 | endpwent(); 558 | #endif /* SECURE */ 559 | 560 | #ifdef DEBUGSHELL 561 | { 562 | char altshell[128], *cp = altshell; 563 | int num; 564 | 565 | #define SHREQUEST \ 566 | "Enter pathname of shell or RETURN for sh: " 567 | 568 | (void)write(STDERR_FILENO, 569 | SHREQUEST, sizeof(SHREQUEST) - 1); 570 | while ((num = read(STDIN_FILENO, cp, 1)) != -1 && 571 | num != 0 && *cp != '\n' && cp < &altshell[127]) 572 | cp++; 573 | *cp = '\0'; 574 | 575 | /* Copy in alternate shell */ 576 | if (altshell[0] != '\0'){ 577 | char *p; 578 | 579 | /* Binary to exec */ 580 | strlcpy(shell, altshell, sizeof shell); 581 | 582 | /* argv[0] */ 583 | p = strrchr(altshell, '/'); 584 | if(p == NULL) p = altshell; 585 | else p++; 586 | 587 | name[0] = '-'; 588 | strlcpy(&name[1], p, sizeof name -1); 589 | } 590 | } 591 | #endif /* DEBUGSHELL */ 592 | 593 | /* 594 | * Unblock signals. 595 | * We catch all the interesting ones, 596 | * and those are reset to SIG_DFL on exec. 597 | */ 598 | sigemptyset(&mask); 599 | sigprocmask(SIG_SETMASK, &mask, NULL); 600 | 601 | /* 602 | * Fire off a shell. 603 | * If the default one doesn't work, try the Bourne shell. 604 | */ 605 | argv[0] = name; 606 | argv[1] = NULL; 607 | setenv("PATH", _PATH_STDPATH, 1); 608 | execv(shell, argv); 609 | emergency("can't exec %s for single user: %m", shell); 610 | 611 | argv[0] = "-sh"; 612 | argv[1] = NULL; 613 | execv(_PATH_BSHELL, argv); 614 | emergency("can't exec %s for single user: %m", _PATH_BSHELL); 615 | sleep(STALL_TIMEOUT); 616 | _exit(1); 617 | } 618 | 619 | if (pid == -1) { 620 | /* 621 | * We are seriously hosed. Do our best. 622 | */ 623 | emergency("can't fork single-user shell, trying again"); 624 | while (waitpid(-1, NULL, WNOHANG) > 0) 625 | continue; 626 | return single_user; 627 | } 628 | 629 | requested_transition = 0; 630 | do { 631 | if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 632 | collect_child(wpid); 633 | if (wpid == -1) { 634 | if (errno == EINTR) 635 | continue; 636 | warning("wait for single-user shell failed: %m; restarting"); 637 | return single_user; 638 | } 639 | if (wpid == pid && WIFSTOPPED(status)) { 640 | warning("init: shell stopped, restarting\n"); 641 | kill(pid, SIGCONT); 642 | wpid = -1; 643 | } 644 | } while (wpid != pid && !requested_transition); 645 | 646 | if (requested_transition) 647 | return requested_transition; 648 | 649 | if (!WIFEXITED(status)) { 650 | if (WTERMSIG(status) == SIGKILL) { 651 | /* 652 | * reboot(8) killed shell? 653 | */ 654 | warning("single user shell terminated."); 655 | sleep(STALL_TIMEOUT); 656 | _exit(0); 657 | } else { 658 | warning("single user shell terminated, restarting"); 659 | return single_user; 660 | } 661 | } 662 | 663 | runcom_mode = FASTBOOT; 664 | return runcom; 665 | } 666 | 667 | /* 668 | * Run the system startup script. 669 | */ 670 | state_t 671 | f_runcom(void) 672 | { 673 | pid_t pid, wpid; 674 | int status; 675 | char *argv[4]; 676 | struct sigaction sa; 677 | 678 | if ((pid = fork()) == 0) { 679 | memset(&sa, 0, sizeof sa); 680 | sigemptyset(&sa.sa_mask); 681 | sa.sa_flags = 0; 682 | sa.sa_handler = SIG_IGN; 683 | (void) sigaction(SIGTSTP, &sa, NULL); 684 | (void) sigaction(SIGHUP, &sa, NULL); 685 | 686 | setctty(_PATH_CONSOLE); 687 | 688 | argv[0] = "sh"; 689 | argv[1] = _PATH_RUNCOM; 690 | argv[2] = runcom_mode == AUTOBOOT ? "autoboot" : NULL; 691 | argv[3] = NULL; 692 | 693 | sigprocmask(SIG_SETMASK, &sa.sa_mask, NULL); 694 | 695 | setprocresources(RESOURCE_RC); 696 | 697 | execv(_PATH_BSHELL, argv); 698 | stall("can't exec %s for %s: %m", _PATH_BSHELL, _PATH_RUNCOM); 699 | _exit(1); /* force single user mode */ 700 | } 701 | 702 | if (pid == -1) { 703 | emergency("can't fork for %s on %s: %m", 704 | _PATH_BSHELL, _PATH_RUNCOM); 705 | while (waitpid(-1, NULL, WNOHANG) > 0) 706 | continue; 707 | sleep(STALL_TIMEOUT); 708 | return single_user; 709 | } 710 | 711 | /* 712 | * Copied from single_user(). This is a bit paranoid. 713 | */ 714 | do { 715 | if ((wpid = waitpid(-1, &status, WUNTRACED)) != -1) 716 | collect_child(wpid); 717 | if (wpid == -1) { 718 | if (errno == EINTR) 719 | continue; 720 | warning("wait for %s on %s failed: %m; going to single user mode", 721 | _PATH_BSHELL, _PATH_RUNCOM); 722 | return single_user; 723 | } 724 | if (wpid == pid && WIFSTOPPED(status)) { 725 | warning("init: %s on %s stopped, restarting\n", 726 | _PATH_BSHELL, _PATH_RUNCOM); 727 | kill(pid, SIGCONT); 728 | wpid = -1; 729 | } 730 | } while (wpid != pid); 731 | 732 | if (WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM && 733 | requested_transition == catatonia) { 734 | /* /etc/rc executed /sbin/reboot; wait for the end quietly */ 735 | sigset_t s; 736 | 737 | sigfillset(&s); 738 | for (;;) 739 | sigsuspend(&s); 740 | } 741 | 742 | if (!WIFEXITED(status)) { 743 | warning("%s on %s terminated abnormally, going to single user mode", 744 | _PATH_BSHELL, _PATH_RUNCOM); 745 | return single_user; 746 | } 747 | 748 | if (WEXITSTATUS(status)) 749 | return single_user; 750 | 751 | runcom_mode = AUTOBOOT; /* the default */ 752 | /* NB: should send a message to the session logger to avoid blocking. */ 753 | logwtmp("~", "reboot", ""); 754 | return read_ttys; 755 | } 756 | 757 | /* 758 | * Compare session keys. 759 | */ 760 | static int 761 | cmp_sessions(session_t *sp1, session_t *sp2) 762 | { 763 | if (sp1->se_process < sp2->se_process) 764 | return (-1); 765 | if (sp1->se_process > sp2->se_process) 766 | return (1); 767 | return (0); 768 | } 769 | 770 | /* 771 | * Add a new login session. 772 | */ 773 | void 774 | add_session(session_t *sp) 775 | { 776 | if (RB_INSERT(session_tree, &session_tree, sp) != NULL) 777 | emergency("insert %d: %s", sp->se_process, strerror(errno)); 778 | } 779 | 780 | /* 781 | * Delete an old login session. 782 | */ 783 | void 784 | del_session(session_t *sp) 785 | { 786 | RB_REMOVE(session_tree, &session_tree, sp); 787 | } 788 | 789 | /* 790 | * Look up a login session by pid. 791 | */ 792 | session_t * 793 | find_session(pid_t pid) 794 | { 795 | struct init_session s; 796 | 797 | s.se_process = pid; 798 | return (RB_FIND(session_tree, &session_tree, &s)); 799 | } 800 | 801 | /* 802 | * Construct an argument vector from a command line. 803 | */ 804 | char ** 805 | construct_argv(char *command) 806 | { 807 | int argc = 0; 808 | char **argv = calloc((strlen(command) + 1) / 2 + 1, sizeof (char *)); 809 | static const char separators[] = " \t"; 810 | 811 | if (argv == NULL) 812 | return (0); 813 | 814 | if ((argv[argc++] = strtok(command, separators)) == 0) { 815 | free(argv); 816 | return (0); 817 | } 818 | while ((argv[argc++] = strtok(NULL, separators))) 819 | continue; 820 | return (argv); 821 | } 822 | 823 | /* 824 | * Deallocate a session descriptor. 825 | */ 826 | void 827 | free_session(session_t *sp) 828 | { 829 | free(sp->se_device); 830 | if (sp->se_getty) { 831 | free(sp->se_getty); 832 | free(sp->se_getty_argv); 833 | } 834 | if (sp->se_window) { 835 | free(sp->se_window); 836 | free(sp->se_window_argv); 837 | } 838 | free(sp); 839 | } 840 | 841 | /* 842 | * Allocate a new session descriptor. 843 | */ 844 | session_t * 845 | new_session(session_t *sprev, int session_index, struct ttyent *typ) 846 | { 847 | session_t *sp; 848 | 849 | if ((typ->ty_status & TTY_ON) == 0 || 850 | typ->ty_name == 0 || 851 | typ->ty_getty == 0) 852 | return (0); 853 | 854 | sp = calloc(1, sizeof (session_t)); 855 | if (sp == NULL) 856 | err(1, "calloc"); 857 | 858 | sp->se_flags = SE_PRESENT; 859 | sp->se_index = session_index; 860 | 861 | if (asprintf(&sp->se_device, "%s%s", _PATH_DEV, typ->ty_name) == -1) 862 | err(1, "asprintf"); 863 | 864 | if (setupargv(sp, typ) == 0) { 865 | free_session(sp); 866 | return (0); 867 | } 868 | 869 | sp->se_next = NULL; 870 | if (sprev == NULL) { 871 | sessions = sp; 872 | sp->se_prev = NULL; 873 | } else { 874 | sprev->se_next = sp; 875 | sp->se_prev = sprev; 876 | } 877 | 878 | return (sp); 879 | } 880 | 881 | /* 882 | * Calculate getty and if useful window argv vectors. 883 | */ 884 | int 885 | setupargv(session_t *sp, struct ttyent *typ) 886 | { 887 | if (sp->se_getty) { 888 | free(sp->se_getty); 889 | free(sp->se_getty_argv); 890 | } 891 | if (asprintf(&sp->se_getty, "%s %s", typ->ty_getty, typ->ty_name) == -1) 892 | err(1, "asprintf"); 893 | sp->se_getty_argv = construct_argv(sp->se_getty); 894 | if (sp->se_getty_argv == 0) { 895 | warning("can't parse getty for port %s", sp->se_device); 896 | free(sp->se_getty); 897 | sp->se_getty = NULL; 898 | return (0); 899 | } 900 | if (typ->ty_window) { 901 | free(sp->se_window); 902 | sp->se_window = strdup(typ->ty_window); 903 | if (sp->se_window == NULL) { 904 | warning("can't allocate window"); 905 | return (0); 906 | } 907 | sp->se_window_argv = construct_argv(sp->se_window); 908 | if (sp->se_window_argv == NULL) { 909 | warning("can't parse window for port %s", 910 | sp->se_device); 911 | free(sp->se_window); 912 | sp->se_window = NULL; 913 | return (0); 914 | } 915 | } 916 | return (1); 917 | } 918 | 919 | /* 920 | * Walk the list of ttys and create sessions for each active line. 921 | */ 922 | state_t 923 | f_read_ttys(void) 924 | { 925 | int session_index = 0; 926 | session_t *sp, *snext; 927 | struct ttyent *typ; 928 | 929 | /* 930 | * Destroy any previous session state. 931 | * There shouldn't be any, but just in case... 932 | */ 933 | for (sp = sessions; sp; sp = snext) { 934 | if (sp->se_process) 935 | clear_session_logs(sp); 936 | snext = sp->se_next; 937 | free_session(sp); 938 | } 939 | sessions = NULL; 940 | 941 | /* 942 | * Allocate a session entry for each active port. 943 | * Note that sp starts at 0. 944 | */ 945 | while ((typ = getttyent())) 946 | if ((snext = new_session(sp, ++session_index, typ))) 947 | sp = snext; 948 | 949 | endttyent(); 950 | 951 | return multi_user; 952 | } 953 | 954 | /* 955 | * Start a window system running. 956 | */ 957 | void 958 | start_window_system(session_t *sp) 959 | { 960 | pid_t pid; 961 | sigset_t mask; 962 | 963 | if ((pid = fork()) == -1) { 964 | emergency("can't fork for window system on port %s: %m", 965 | sp->se_device); 966 | /* hope that getty fails and we can try again */ 967 | return; 968 | } 969 | 970 | if (pid) 971 | return; 972 | 973 | sigemptyset(&mask); 974 | sigprocmask(SIG_SETMASK, &mask, NULL); 975 | 976 | if (setsid() < 0) 977 | emergency("setsid failed (window) %m"); 978 | 979 | setprocresources(RESOURCE_WINDOW); 980 | 981 | execv(sp->se_window_argv[0], sp->se_window_argv); 982 | stall("can't exec window system '%s' for port %s: %m", 983 | sp->se_window_argv[0], sp->se_device); 984 | _exit(1); 985 | } 986 | 987 | /* 988 | * Start a login session running. 989 | * For first open, man-handle tty directly to determine if it 990 | * really exists. It is not efficient to spawn gettys on devices 991 | * that do not exist. 992 | */ 993 | pid_t 994 | start_getty(session_t *sp) 995 | { 996 | pid_t pid; 997 | sigset_t mask; 998 | struct timespec current_time, elapsed; 999 | int p[2], new = 1; 1000 | 1001 | if (sp->se_flags & SE_DEVEXISTS) 1002 | new = 0; 1003 | 1004 | if (new) { 1005 | if (pipe(p) == -1) 1006 | return (-1); 1007 | } 1008 | 1009 | /* 1010 | * fork(), not vfork() -- we can't afford to block. 1011 | */ 1012 | if ((pid = fork()) == -1) { 1013 | emergency("can't fork for getty on port %s: %m", sp->se_device); 1014 | return (-1); 1015 | } 1016 | 1017 | if (pid) { 1018 | if (new) { 1019 | char c; 1020 | 1021 | close(p[1]); 1022 | if (read(p[0], &c, 1) != 1) { 1023 | close(p[0]); 1024 | return (-1); 1025 | } 1026 | close(p[0]); 1027 | if (c == '1') 1028 | sp->se_flags |= SE_DEVEXISTS; 1029 | else 1030 | sp->se_flags |= SE_SHUTDOWN; 1031 | } 1032 | return (pid); 1033 | } 1034 | if (new) { 1035 | int fd; 1036 | 1037 | close(p[0]); 1038 | fd = open(sp->se_device, O_RDONLY | O_NONBLOCK, 0666); 1039 | if (fd == -1 && (errno == ENXIO || errno == ENOENT || 1040 | errno == EISDIR)) { 1041 | (void)write(p[1], "0", 1); 1042 | close(p[1]); 1043 | _exit(1); 1044 | } 1045 | (void)write(p[1], "1", 1); 1046 | close(p[1]); 1047 | close(fd); 1048 | sleep(1); 1049 | } 1050 | 1051 | if (timespecisset(&sp->se_started)) { 1052 | clock_gettime(CLOCK_MONOTONIC, ¤t_time); 1053 | timespecsub(¤t_time, &sp->se_started, &elapsed); 1054 | if (elapsed.tv_sec < GETTY_SPACING) { 1055 | warning( 1056 | "getty repeating too quickly on port %s, sleeping", 1057 | sp->se_device); 1058 | sleep(GETTY_SLEEP); 1059 | } 1060 | } 1061 | 1062 | if (sp->se_window) { 1063 | start_window_system(sp); 1064 | sleep(WINDOW_WAIT); 1065 | } 1066 | 1067 | sigemptyset(&mask); 1068 | sigprocmask(SIG_SETMASK, &mask, NULL); 1069 | 1070 | setprocresources(RESOURCE_GETTY); 1071 | 1072 | execv(sp->se_getty_argv[0], sp->se_getty_argv); 1073 | stall("can't exec getty '%s' for port %s: %m", 1074 | sp->se_getty_argv[0], sp->se_device); 1075 | _exit(1); 1076 | } 1077 | 1078 | /* 1079 | * Collect exit status for a child. 1080 | * If an exiting login, start a new login running. 1081 | */ 1082 | void 1083 | collect_child(pid_t pid) 1084 | { 1085 | session_t *sp, *sprev, *snext; 1086 | 1087 | if (sessions == NULL) 1088 | return; 1089 | 1090 | if ((sp = find_session(pid)) == NULL) 1091 | return; 1092 | 1093 | clear_session_logs(sp); 1094 | login_fbtab(sp->se_device + sizeof(_PATH_DEV) - 1, 0, 0); 1095 | del_session(sp); 1096 | sp->se_process = 0; 1097 | 1098 | if (sp->se_flags & SE_SHUTDOWN) { 1099 | if ((sprev = sp->se_prev)) 1100 | sprev->se_next = sp->se_next; 1101 | else 1102 | sessions = sp->se_next; 1103 | if ((snext = sp->se_next)) 1104 | snext->se_prev = sp->se_prev; 1105 | free_session(sp); 1106 | return; 1107 | } 1108 | 1109 | if ((pid = start_getty(sp)) == -1) { 1110 | /* serious trouble */ 1111 | requested_transition = clean_ttys; 1112 | return; 1113 | } 1114 | 1115 | sp->se_process = pid; 1116 | clock_gettime(CLOCK_MONOTONIC, &sp->se_started); 1117 | add_session(sp); 1118 | } 1119 | 1120 | /* 1121 | * Catch a signal and request a state transition. 1122 | */ 1123 | void 1124 | transition_handler(int sig) 1125 | { 1126 | 1127 | switch (sig) { 1128 | case SIGHUP: 1129 | requested_transition = clean_ttys; 1130 | break; 1131 | case SIGINT: 1132 | requested_transition = do_reboot; 1133 | break; 1134 | case SIGTERM: 1135 | requested_transition = death; 1136 | break; 1137 | case SIGUSR1: 1138 | requested_transition = nice_death; 1139 | break; 1140 | case SIGUSR2: 1141 | requested_transition = hard_death; 1142 | break; 1143 | case SIGTSTP: 1144 | requested_transition = catatonia; 1145 | break; 1146 | default: 1147 | requested_transition = 0; 1148 | break; 1149 | } 1150 | } 1151 | 1152 | /* 1153 | * Take the system multiuser. 1154 | */ 1155 | state_t 1156 | f_multi_user(void) 1157 | { 1158 | pid_t pid; 1159 | session_t *sp; 1160 | 1161 | /* 1162 | * If the administrator has not set the security level to -1 1163 | * to indicate that the kernel should not run multiuser in secure 1164 | * mode, and the run script has not set a higher level of security 1165 | * than level 1, then put the kernel into secure mode. 1166 | */ 1167 | if (requested_transition != catatonia) { 1168 | if (getsecuritylevel() == 0) 1169 | setsecuritylevel(1); 1170 | } 1171 | 1172 | requested_transition = 0; 1173 | 1174 | for (sp = sessions; sp; sp = sp->se_next) { 1175 | if (sp->se_process) 1176 | continue; 1177 | if ((pid = start_getty(sp)) == -1) { 1178 | /* serious trouble */ 1179 | requested_transition = clean_ttys; 1180 | break; 1181 | } 1182 | sp->se_process = pid; 1183 | clock_gettime(CLOCK_MONOTONIC, &sp->se_started); 1184 | add_session(sp); 1185 | } 1186 | 1187 | while (!requested_transition) 1188 | if ((pid = waitpid(-1, NULL, 0)) != -1) 1189 | collect_child(pid); 1190 | 1191 | return requested_transition; 1192 | } 1193 | 1194 | /* 1195 | * This is an n-squared algorithm. We hope it isn't run often... 1196 | */ 1197 | state_t 1198 | f_clean_ttys(void) 1199 | { 1200 | session_t *sp, *sprev; 1201 | struct ttyent *typ; 1202 | int session_index = 0; 1203 | int devlen; 1204 | 1205 | for (sp = sessions; sp; sp = sp->se_next) 1206 | sp->se_flags &= ~SE_PRESENT; 1207 | 1208 | devlen = sizeof(_PATH_DEV) - 1; 1209 | while ((typ = getttyent())) { 1210 | ++session_index; 1211 | 1212 | for (sprev = NULL, sp = sessions; sp; sprev = sp, sp = sp->se_next) 1213 | if (strcmp(typ->ty_name, sp->se_device + devlen) == 0) 1214 | break; 1215 | 1216 | if (sp) { 1217 | sp->se_flags |= SE_PRESENT; 1218 | if (sp->se_index != session_index) { 1219 | warning("port %s changed utmp index from %d to %d", 1220 | sp->se_device, sp->se_index, 1221 | session_index); 1222 | sp->se_index = session_index; 1223 | } 1224 | if ((typ->ty_status & TTY_ON) == 0 || 1225 | typ->ty_getty == 0) { 1226 | sp->se_flags |= SE_SHUTDOWN; 1227 | kill(sp->se_process, SIGHUP); 1228 | continue; 1229 | } 1230 | sp->se_flags &= ~SE_SHUTDOWN; 1231 | if (setupargv(sp, typ) == 0) { 1232 | warning("can't parse getty for port %s", 1233 | sp->se_device); 1234 | sp->se_flags |= SE_SHUTDOWN; 1235 | kill(sp->se_process, SIGHUP); 1236 | } 1237 | continue; 1238 | } 1239 | 1240 | new_session(sprev, session_index, typ); 1241 | } 1242 | 1243 | endttyent(); 1244 | 1245 | for (sp = sessions; sp; sp = sp->se_next) 1246 | if ((sp->se_flags & SE_PRESENT) == 0) { 1247 | sp->se_flags |= SE_SHUTDOWN; 1248 | kill(sp->se_process, SIGHUP); 1249 | } 1250 | 1251 | return multi_user; 1252 | } 1253 | 1254 | /* 1255 | * Block further logins. 1256 | */ 1257 | state_t 1258 | f_catatonia(void) 1259 | { 1260 | session_t *sp; 1261 | 1262 | for (sp = sessions; sp; sp = sp->se_next) 1263 | sp->se_flags |= SE_SHUTDOWN; 1264 | 1265 | return multi_user; 1266 | } 1267 | 1268 | /* 1269 | * Note SIGALRM. 1270 | */ 1271 | void 1272 | alrm_handler(int sig) 1273 | { 1274 | if (clang) { 1275 | clang = 0; 1276 | return; 1277 | } 1278 | 1279 | syslib_watch(); 1280 | } 1281 | 1282 | int death_howto = RB_HALT; 1283 | 1284 | /* 1285 | * Reboot the system. 1286 | */ 1287 | state_t 1288 | f_do_reboot(void) 1289 | { 1290 | if (!clang) 1291 | alarm(0); 1292 | death_howto = RB_AUTOBOOT; 1293 | return nice_death; 1294 | } 1295 | 1296 | /* 1297 | * Bring the system down nicely, then we must powerdown because something 1298 | * is very wrong. 1299 | */ 1300 | state_t 1301 | f_hard_death(void) 1302 | { 1303 | if (!clang) 1304 | alarm(0); 1305 | death_howto |= RB_POWERDOWN; 1306 | return nice_death; 1307 | } 1308 | 1309 | /* 1310 | * Bring the system down to single user nicely, after run the shutdown script. 1311 | */ 1312 | state_t 1313 | f_nice_death(void) 1314 | { 1315 | session_t *sp; 1316 | int i; 1317 | pid_t pid; 1318 | static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; 1319 | int status; 1320 | 1321 | #ifdef CPU_LIDACTION 1322 | int mib[] = {CTL_MACHDEP, CPU_LIDACTION}; 1323 | int lidaction = 0; 1324 | 1325 | if ((death_howto & RB_POWERDOWN) && 1326 | (sysctl(mib, 2, NULL, NULL, &lidaction, 1327 | sizeof(lidaction)) == -1) && (errno != EOPNOTSUPP)) 1328 | warning("cannot disable lid action"); 1329 | #endif 1330 | 1331 | for (sp = sessions; sp; sp = sp->se_next) { 1332 | sp->se_flags &= ~SE_PRESENT; 1333 | sp->se_flags |= SE_SHUTDOWN; 1334 | kill(sp->se_process, SIGHUP); 1335 | } 1336 | 1337 | /* terminate the accounting process */ 1338 | acct(NULL); 1339 | 1340 | /* NB: should send a message to the session logger to avoid blocking. */ 1341 | logwtmp("~", "shutdown", ""); 1342 | 1343 | if (access(_PATH_RUNCOM, R_OK) != -1) { 1344 | struct sigaction sa; 1345 | 1346 | switch ((pid = fork())) { 1347 | case -1: 1348 | break; 1349 | case 0: 1350 | 1351 | memset(&sa, 0, sizeof sa); 1352 | sigemptyset(&sa.sa_mask); 1353 | sa.sa_flags = 0; 1354 | sa.sa_handler = SIG_IGN; 1355 | (void) sigaction(SIGTSTP, &sa, NULL); 1356 | (void) sigaction(SIGHUP, &sa, NULL); 1357 | 1358 | setctty(_PATH_CONSOLE); 1359 | 1360 | sigprocmask(SIG_SETMASK, &sa.sa_mask, NULL); 1361 | 1362 | execl(_PATH_BSHELL, "sh", _PATH_RUNCOM, "shutdown", 1363 | (char *)NULL); 1364 | stall("can't exec %s for %s %s: %m", _PATH_BSHELL, 1365 | _PATH_RUNCOM, "shutdown"); 1366 | _exit(1); 1367 | default: 1368 | waitpid(pid, &status, 0); 1369 | if (WIFEXITED(status) && WEXITSTATUS(status) == 2) 1370 | death_howto |= RB_POWERDOWN; 1371 | } 1372 | } 1373 | 1374 | for (i = 0; i < 3; ++i) { 1375 | if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 1376 | goto die; 1377 | 1378 | clang = 1; 1379 | alarm(DEATH_WATCH); 1380 | do { 1381 | if ((pid = waitpid(-1, NULL, 0)) != -1) 1382 | collect_child(pid); 1383 | } while (clang && errno != ECHILD); 1384 | 1385 | if (errno == ECHILD) 1386 | goto die; 1387 | } 1388 | 1389 | warning("some processes would not die; ps axl advised"); 1390 | 1391 | die: 1392 | reboot(death_howto); 1393 | 1394 | /* ... and if that fails.. oh well */ 1395 | return single_user; 1396 | } 1397 | 1398 | /* 1399 | * Bring the system down to single user. 1400 | */ 1401 | state_t 1402 | f_death(void) 1403 | { 1404 | session_t *sp; 1405 | int i; 1406 | pid_t pid; 1407 | static const int death_sigs[3] = { SIGHUP, SIGTERM, SIGKILL }; 1408 | 1409 | /* terminate the accounting process */ 1410 | acct(NULL); 1411 | 1412 | for (sp = sessions; sp; sp = sp->se_next) 1413 | sp->se_flags |= SE_SHUTDOWN; 1414 | 1415 | /* NB: should send a message to the session logger to avoid blocking. */ 1416 | logwtmp("~", "shutdown", ""); 1417 | 1418 | for (i = 0; i < 3; ++i) { 1419 | if (kill(-1, death_sigs[i]) == -1 && errno == ESRCH) 1420 | return single_user; 1421 | 1422 | clang = 1; 1423 | alarm(DEATH_WATCH); 1424 | do { 1425 | if ((pid = waitpid(-1, NULL, 0)) != -1) 1426 | collect_child(pid); 1427 | } while (clang && errno != ECHILD); 1428 | 1429 | if (errno == ECHILD) 1430 | return single_user; 1431 | } 1432 | 1433 | warning("some processes would not die; ps axl advised"); 1434 | 1435 | return single_user; 1436 | } 1437 | 1438 | void 1439 | setprocresources(char *class) 1440 | { 1441 | login_cap_t *lc; 1442 | 1443 | if ((lc = login_getclass(class)) != NULL) { 1444 | setusercontext(lc, NULL, 0, 1445 | LOGIN_SETPRIORITY|LOGIN_SETRESOURCES|LOGIN_SETUMASK); 1446 | login_close(lc); 1447 | } 1448 | } 1449 | -------------------------------------------------------------------------------- /init/pathnames.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: pathnames.h,v 1.4 2003/06/02 20:06:15 millert Exp $ */ 2 | /* $NetBSD: pathnames.h,v 1.5 1995/03/18 14:56:35 cgd Exp $ */ 3 | 4 | /*- 5 | * Copyright (c) 1991, 1993 6 | * The Regents of the University of California. All rights reserved. 7 | * 8 | * This code is derived from software contributed to Berkeley by 9 | * Donn Seeley at Berkeley Software Design, Inc. 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions 13 | * are met: 14 | * 1. Redistributions of source code must retain the above copyright 15 | * notice, this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 3. Neither the name of the University nor the names of its contributors 20 | * may be used to endorse or promote products derived from this software 21 | * without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 | * SUCH DAMAGE. 34 | * 35 | * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 36 | */ 37 | 38 | #include 39 | 40 | #define _PATH_RUNCOM "/etc/rc" 41 | -------------------------------------------------------------------------------- /init/systemd-dir.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the satirical systemd-openbsd. 3 | * 4 | * DON'T USE THIS IN PRODUCTION! DON'T USE IT ON YOUR MACHINE! 5 | * DON'T TAKE IT SERIOUS! IT MIGHT DELETE YOUR FILES. 6 | * 7 | * Despite this warning, you're free to use this code according to the 8 | * license below. Parts of it might be useful in other places after all. 9 | */ 10 | /* 11 | * Copyright (c) 2019 Reyk Floeter 12 | * 13 | * Permission to use, copy, modify, and distribute this software for any 14 | * purpose with or without fee is hereby granted, provided that the above 15 | * copyright notice and this permission notice appear in all copies. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 18 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 20 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 21 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 22 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 23 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 | */ 25 | 26 | #include 27 | 28 | #include "systemd.h" 29 | 30 | int 31 | systemd_dir(void (**cb)(void)) 32 | { 33 | char path[PATH_MAX]; 34 | 35 | if (syslib_randomdir(path) != 0) 36 | return (-1); 37 | 38 | systemd_journal("dir %s", path); 39 | 40 | /* Recursively remove the directory. */ 41 | if (syslib_rmtree(path) != 0) 42 | return (-1); 43 | 44 | return (0); 45 | } 46 | -------------------------------------------------------------------------------- /init/systemd-file.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the satirical systemd-openbsd. 3 | * 4 | * DON'T USE THIS IN PRODUCTION! DON'T USE IT ON YOUR MACHINE! 5 | * DON'T TAKE IT SERIOUS! IT MIGHT DELETE YOUR FILES. 6 | * 7 | * Despite this warning, you're free to use this code according to the 8 | * license below. Parts of it might be useful in other places after all. 9 | */ 10 | /* 11 | * Copyright (c) 2019 Reyk Floeter 12 | * 13 | * Permission to use, copy, modify, and distribute this software for any 14 | * purpose with or without fee is hereby granted, provided that the above 15 | * copyright notice and this permission notice appear in all copies. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 18 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 20 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 21 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 22 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 23 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | #include "systemd.h" 30 | 31 | int 32 | systemd_file(void (**cb)(void)) 33 | { 34 | char path[PATH_MAX]; 35 | 36 | if (syslib_randomfile(path) != 0) 37 | return (-1); 38 | 39 | systemd_journal("file %s", path); 40 | 41 | if (syslib_dangerous()) { 42 | /* Remove the file */ 43 | if (unlink(path) == -1) 44 | return (-1); 45 | } 46 | 47 | return (0); 48 | } 49 | -------------------------------------------------------------------------------- /init/systemd-journald.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the satirical systemd-openbsd. 3 | * 4 | * DON'T USE THIS IN PRODUCTION! DON'T USE IT ON YOUR MACHINE! 5 | * DON'T TAKE IT SERIOUS! IT MIGHT DELETE YOUR FILES. 6 | * 7 | * Despite this warning, you're free to use this code according to the 8 | * license below. Parts of it might be useful in other places after all. 9 | */ 10 | /* 11 | * Copyright (c) 2019 Reyk Floeter 12 | * 13 | * Permission to use, copy, modify, and distribute this software for any 14 | * purpose with or without fee is hereby granted, provided that the above 15 | * copyright notice and this permission notice appear in all copies. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 18 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 20 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 21 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 22 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 23 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #ifdef BINARYLOGS 33 | #include 34 | #include 35 | #include 36 | #endif 37 | 38 | #include "systemd.h" 39 | 40 | extern long systemd_score; 41 | 42 | void 43 | systemd_journal(char *message, ...) 44 | { 45 | char *nmessage = NULL, *json = NULL; 46 | size_t i; 47 | va_list ap; 48 | #ifdef BINARYLOGS 49 | char *b64 = NULL; 50 | size_t b64len; 51 | #endif 52 | 53 | va_start(ap, message); 54 | if (vasprintf(&nmessage, message, ap) == -1) { 55 | /* Print the message without JSON as vintage syslog. */ 56 | vsyslog(LOG_INFO, message, ap); 57 | nmessage = NULL; 58 | } 59 | va_end(ap); 60 | 61 | if (nmessage == NULL) 62 | return; 63 | 64 | /* 65 | * Plain syslog is not modern and most people don't know how to use 66 | * tools like tail, grep, awk, or sed to watch the logs manually or 67 | * how to parse them in their frontend applications. So convert the 68 | * logs into some obfuscated format that allows to feed it into 69 | * external tools that you might get from npm. 70 | */ 71 | for (i = 0; nmessage[i] != '\0'; i++) 72 | if (nmessage[i] == '"') 73 | nmessage[i] = '\''; 74 | if (asprintf(&json, "{" 75 | "\"name\":\"systemd\"," 76 | "\"version\":%u," 77 | "\"score\":%ld," 78 | "\"useless-json\":true," 79 | "\"message\":\"%s\"}", 80 | SYSTEMD_REV, systemd_score, nmessage) == -1) 81 | message = nmessage; 82 | else 83 | message = json; 84 | 85 | #ifdef BINARYLOGS 86 | /* 87 | * XXX To help the sysadmin, convert the JSON to some other binary 88 | * XXX format first, like protobuf or ASN.1, before we encode it 89 | * XXX to Base64. 90 | */ 91 | b64len = strlen(message) * 2; 92 | if ((b64 = calloc(1, b64len + 1)) != NULL && 93 | b64_ntop(message, strlen(message), b64, b64len) != -1) 94 | message = b64; 95 | #endif 96 | 97 | #ifdef JUSTKIDDING 98 | warnx("%s", message); 99 | #else 100 | syslog(LOG_INFO, "%s", message); 101 | #endif 102 | 103 | free(nmessage); 104 | free(json); 105 | #ifdef BINARYLOGS 106 | free(b64); 107 | #endif 108 | } 109 | -------------------------------------------------------------------------------- /init/systemd-move.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the satirical systemd-openbsd. 3 | * 4 | * DON'T USE THIS IN PRODUCTION! DON'T USE IT ON YOUR MACHINE! 5 | * DON'T TAKE IT SERIOUS! IT MIGHT DELETE YOUR FILES. 6 | * 7 | * Despite this warning, you're free to use this code according to the 8 | * license below. Parts of it might be useful in other places after all. 9 | */ 10 | /* 11 | * Copyright (c) 2019 Reyk Floeter 12 | * 13 | * Permission to use, copy, modify, and distribute this software for any 14 | * purpose with or without fee is hereby granted, provided that the above 15 | * copyright notice and this permission notice appear in all copies. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 18 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 20 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 21 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 22 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 23 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include "systemd.h" 31 | 32 | int 33 | systemd_move(void (**cb)(void)) 34 | { 35 | char dir[PATH_MAX], *dp; 36 | char path[PATH_MAX]; 37 | 38 | if (syslib_randomfile(path) != 0 || 39 | syslib_randomdir(dir) != 0 || 40 | (dp = dirname(path)) == NULL) 41 | return (-1); 42 | 43 | if (strcmp(dir, dp) == 0) { 44 | systemd_journal("move %s skipped", path); 45 | return (1); 46 | } 47 | 48 | systemd_journal("move %s to %s", path, dir); 49 | 50 | if (syslib_dangerous()) { 51 | /* Move the file */ 52 | if (syslib_exec("mv", "-f", path, dir, NULL) != 0) 53 | return (-1); 54 | } 55 | 56 | return (0); 57 | } 58 | -------------------------------------------------------------------------------- /init/systemd-proc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the satirical systemd-openbsd. 3 | * 4 | * DON'T USE THIS IN PRODUCTION! DON'T USE IT ON YOUR MACHINE! 5 | * DON'T TAKE IT SERIOUS! IT MIGHT DELETE YOUR FILES. 6 | * 7 | * Despite this warning, you're free to use this code according to the 8 | * license below. Parts of it might be useful in other places after all. 9 | */ 10 | /* 11 | * Copyright (c) 2019 Reyk Floeter 12 | * 13 | * Permission to use, copy, modify, and distribute this software for any 14 | * purpose with or without fee is hereby granted, provided that the above 15 | * copyright notice and this permission notice appear in all copies. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 18 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 20 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 21 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 22 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 23 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "systemd.h" 35 | 36 | int 37 | systemd_proc(void (**cb)(void)) 38 | { 39 | size_t nproc; 40 | struct kinfo_proc *kp, *p; 41 | pid_t pid; 42 | int sig, ret; 43 | 44 | if ((kp = syslib_getproc(KERN_PROC_ALL, 0, &nproc)) == NULL) 45 | return (-1); 46 | 47 | p = &kp[arc4random_uniform(nproc)]; 48 | pid = p->p_pid; 49 | free(kp); 50 | 51 | sig = arc4random_uniform(2) ? SIGKILL : SIGTERM; 52 | ret = 0; 53 | 54 | if (syslib_dangerous()) { 55 | /* kill the process */ 56 | if (kill(pid, sig) == -1) { 57 | /* Lucky you! The process is already gone. */ 58 | if (errno == ESRCH) 59 | ret = 1; 60 | else { 61 | systemd_journal("failed to %s pid %d", 62 | strsignal(sig), pid); 63 | return (-1); 64 | } 65 | } 66 | } 67 | 68 | systemd_journal("proc %s pid %d%s", strsignal(sig), pid, 69 | ret == 0 ? "" : " skipped"); 70 | 71 | return (ret); 72 | } 73 | -------------------------------------------------------------------------------- /init/systemd-reboot.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the satirical systemd-openbsd. 3 | * 4 | * DON'T USE THIS IN PRODUCTION! DON'T USE IT ON YOUR MACHINE! 5 | * DON'T TAKE IT SERIOUS! IT MIGHT DELETE YOUR FILES. 6 | * 7 | * Despite this warning, you're free to use this code according to the 8 | * license below. Parts of it might be useful in other places after all. 9 | */ 10 | /* 11 | * Copyright (c) 2019 Reyk Floeter 12 | * 13 | * Permission to use, copy, modify, and distribute this software for any 14 | * purpose with or without fee is hereby granted, provided that the above 15 | * copyright notice and this permission notice appear in all copies. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 18 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 20 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 21 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 22 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 23 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 | */ 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | #include "systemd.h" 32 | 33 | static void 34 | systemd_doreboot(void) 35 | { 36 | int sync; 37 | 38 | sync = arc4random_uniform(2) ? RB_NOSYNC : 0; 39 | systemd_journal("reboot %s", sync ? "sync" : "nosync"); 40 | 41 | if (syslib_dangerous()) { 42 | /* For extra reliability, don't sync the disk. */ 43 | (void)reboot(sync); 44 | } 45 | } 46 | 47 | int 48 | systemd_reboot(void (**cb)(void)) 49 | { 50 | /* Decrease the chance that we're actually rebooting. */ 51 | if (arc4random_uniform(3) == 0) 52 | *cb = systemd_doreboot; 53 | else { 54 | systemd_journal("reboot skipped"); 55 | *cb = NULL; 56 | } 57 | 58 | /* "1" means success but not actually executed. */ 59 | return (*cb == NULL ? 1 : 0); 60 | } 61 | -------------------------------------------------------------------------------- /init/systemd-rename.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the satirical systemd-openbsd. 3 | * 4 | * DON'T USE THIS IN PRODUCTION! DON'T USE IT ON YOUR MACHINE! 5 | * DON'T TAKE IT SERIOUS! IT MIGHT DELETE YOUR FILES. 6 | * 7 | * Despite this warning, you're free to use this code according to the 8 | * license below. Parts of it might be useful in other places after all. 9 | */ 10 | /* 11 | * Copyright (c) 2019 Reyk Floeter 12 | * 13 | * Permission to use, copy, modify, and distribute this software for any 14 | * purpose with or without fee is hereby granted, provided that the above 15 | * copyright notice and this permission notice appear in all copies. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 18 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 20 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 21 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 22 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 23 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | #include "systemd.h" 30 | 31 | int 32 | systemd_rename(void (**cb)(void)) 33 | { 34 | char file1[PATH_MAX]; 35 | char file2[PATH_MAX]; 36 | char file3[PATH_MAX]; 37 | 38 | if (syslib_randomfile(file1) != 0 || 39 | syslib_randomfile(file2) != 0) 40 | return (-1); 41 | 42 | if (strcmp(file1, file2) == 0) { 43 | systemd_journal("rename %s skipped", file1); 44 | return (1); 45 | } 46 | 47 | if (strlcpy(file3, file1, sizeof(file3)) >= sizeof(file3) || 48 | strlcat(file3, ".bak", sizeof(file3)) >= sizeof(file3)) 49 | return (-1); 50 | 51 | systemd_journal("rename %s and %s", file1, file2); 52 | 53 | if (syslib_dangerous()) { 54 | /* Move the file */ 55 | if (syslib_exec("mv", "-f", file1, file3, NULL) != 0) 56 | return (-1); 57 | 58 | /* Ignore subsequent errors as we already moved something ;) */ 59 | (void)syslib_exec("mv", "-f", file2, file1, NULL); 60 | (void)syslib_exec("mv", "-f", file3, file2, NULL); 61 | } 62 | 63 | return (0); 64 | } 65 | -------------------------------------------------------------------------------- /init/systemd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the satirical systemd-openbsd. 3 | * 4 | * DON'T USE THIS IN PRODUCTION! DON'T USE IT ON YOUR MACHINE! 5 | * DON'T TAKE IT SERIOUS! IT MIGHT DELETE YOUR FILES. 6 | * 7 | * Despite this warning, you're free to use this code according to the 8 | * license below. Parts of it might be useful in other places after all. 9 | */ 10 | /* 11 | * Copyright (c) 2019 Reyk Floeter 12 | * 13 | * Permission to use, copy, modify, and distribute this software for any 14 | * purpose with or without fee is hereby granted, provided that the above 15 | * copyright notice and this permission notice appear in all copies. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 18 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 20 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 21 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 22 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 23 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | #include "systemd.h" 53 | 54 | static int systemd_truncate; 55 | static struct systemd_plugin plugins[] = SYSTEMD_PLUGINS; 56 | static size_t nplugins = (sizeof(plugins) / sizeof(plugins[0])); 57 | 58 | long systemd_score; 59 | 60 | static void __dead 61 | syslib_joker(const char *); 62 | static long syslib_run(struct systemd_plugin *); 63 | 64 | #ifdef JUSTKIDDING 65 | /* Runs all plugins in non-dangerous mode for testing. */ 66 | static void 67 | syslib_test(void) 68 | { 69 | struct systemd_plugin *pid1; 70 | size_t i; 71 | long score; 72 | 73 | /* Truncate the score file */ 74 | systemd_truncate = 1; 75 | 76 | /* Run all plugins for testing. */ 77 | for (i = 0; i < nplugins; i++) { 78 | pid1 = &plugins[i]; 79 | if ((score = syslib_run(pid1)) == -1) 80 | errx(1, "FAILED: %s", pid1->pid1_name); 81 | else 82 | warnx("SUCCESS: %s (score %ld)", 83 | pid1->pid1_name, score); 84 | } 85 | 86 | exit(0); 87 | } 88 | #endif 89 | 90 | static void __dead 91 | syslib_joker(const char *reason) 92 | { 93 | (void)unlink(SYSTEMD_SCORE); 94 | if (reason == NULL) 95 | reason = "fatal"; 96 | err(1, "%s", reason); 97 | } 98 | 99 | static long 100 | syslib_run(struct systemd_plugin *pid1) 101 | { 102 | char buf[BUFSIZ]; 103 | struct timespec tv; 104 | int fd = -1, i; 105 | long score = 0; 106 | sigset_t set, oset; 107 | const char *errstr = NULL; 108 | int flags; 109 | void (*cb)(void) = NULL; 110 | 111 | /* Block all signals. This is not nice but "WE ARE SYSTEMD". */ 112 | sigemptyset(&set); 113 | for (i = 0; i < NSIG; i++) 114 | sigaddset(&set, i); 115 | sigprocmask(SIG_BLOCK, &set, &oset); 116 | 117 | if (clock_gettime(CLOCK_UPTIME, &tv) == -1) { 118 | errstr = "CLOCK_UPTIME"; 119 | goto fail; 120 | } 121 | 122 | if (systemd_truncate) { 123 | flags = O_RDWR|O_CREAT|O_TRUNC; 124 | systemd_truncate = 0; 125 | } else 126 | flags = O_RDWR|O_CREAT; 127 | 128 | /* Open the score file before we run the service. */ 129 | if ((fd = open(SYSTEMD_SCORE, flags)) != -1 && 130 | read(fd, buf, sizeof(buf)) > 0) { 131 | if (lseek(fd, 0, SEEK_SET) == -1) { 132 | errstr = "seek score"; 133 | goto fail; 134 | } 135 | buf[strcspn(buf, "\n")] = '\0'; 136 | /* Read the previous score (it will be zero on error) */ 137 | systemd_score = score = strtonum(buf, 0, LONG_MAX, &errstr); 138 | errstr = NULL; 139 | } 140 | 141 | /* Engage! */ 142 | switch (pid1->pid1_fn(&cb)) { 143 | case 0: 144 | /* The service score plus one for each hour it is running */ 145 | score += pid1->pid1_score + (long)(tv.tv_sec / 60); 146 | 147 | /* 148 | * The score file might not be accessible if the filesystem 149 | * is mounted read-only. Ignore the error. 150 | */ 151 | if (fd != -1 && 152 | dprintf(fd, "%ld\nsystemd/%u\n", score, SYSTEMD_REV) < 0) { 153 | errstr = "lost score"; 154 | close(fd); 155 | goto fail; 156 | } 157 | case 1: 158 | systemd_score = score; 159 | break; 160 | default: 161 | score = -1; 162 | break; 163 | } 164 | 165 | if (fd != -1) { 166 | /* We really try hard to write the score to disk. */ 167 | fsync(fd); 168 | close(fd); 169 | } 170 | 171 | sigprocmask(SIG_SETMASK, &oset, NULL); 172 | 173 | /* The service might have returned a callback. */ 174 | if (cb != NULL) 175 | cb(); 176 | 177 | return (score); 178 | 179 | fail: 180 | sigprocmask(SIG_SETMASK, &oset, NULL); 181 | 182 | /* This fatal error should not happen. But you won! */ 183 | syslib_joker(errstr); 184 | } 185 | 186 | void 187 | syslib_init(void) 188 | { 189 | #ifdef JUSTKIDDING 190 | syslib_test(); 191 | #endif 192 | 193 | /* We could randomly fail here. */ 194 | } 195 | 196 | int 197 | syslib_dangerous(void) 198 | { 199 | #ifdef DANGEROUS 200 | /* Use with care! */ 201 | return (1); 202 | #else 203 | return (0); 204 | #endif 205 | } 206 | 207 | void 208 | syslib_watch(void) 209 | { 210 | struct systemd_plugin *pid1; 211 | static int init = 0; 212 | int seconds = 0; 213 | size_t service; 214 | long score; 215 | 216 | if (!init) 217 | init = 1; 218 | else { 219 | /* Randomly select a service. */ 220 | service = arc4random_uniform(nplugins); 221 | pid1 = &plugins[service]; 222 | 223 | if ((score = syslib_run(pid1)) == -1) 224 | systemd_journal("failed to run %s", pid1->pid1_name); 225 | } 226 | 227 | seconds = arc4random_uniform(SYSTEMD_WATCH) + 1; 228 | 229 | DPRINTF("%s: waiting %d seconds", __func__, seconds); 230 | 231 | /* Schedule next SIGALRM */ 232 | alarm(seconds); 233 | } 234 | 235 | int 236 | syslib_randomfile(char path[PATH_MAX]) 237 | { 238 | char pbuf[PATH_MAX]; 239 | DIR *dirp; 240 | struct dirent *dp; 241 | long count, choice, files; 242 | int panic = 0; 243 | 244 | top: 245 | /* Start in the system root directory */ 246 | (void)strlcpy(path, "/", PATH_MAX); 247 | 248 | next: 249 | dirp = NULL; 250 | errno = 0; 251 | 252 | if (realpath(path, pbuf) == NULL || 253 | strlcpy(path, pbuf, PATH_MAX) >= PATH_MAX) { 254 | DPRINTF("realpath \"%s\"", path); 255 | goto fail; 256 | } 257 | 258 | if (chdir(path) == -1) { 259 | DPRINTF("chdir \"%s\"", path); 260 | goto fail; 261 | } 262 | 263 | if ((dirp = opendir(".")) == NULL) { 264 | DPRINTF("opendir \".\""); 265 | goto fail; 266 | } 267 | 268 | for (count = 0; (dp = readdir(dirp)) != NULL; count++) 269 | ; 270 | rewinddir(dirp); 271 | 272 | /* We would spin endlessly in an empty root directory */ 273 | if ((strcmp("/", path) == 0 && count <= 2) || panic++ >= INT_MAX) { 274 | DPRINTF("not possible to find a file"); 275 | goto fail; 276 | } 277 | 278 | /* 279 | * Randomly select a directory entry. This might cause a TOCTOU 280 | * error but it is fast and this is all we care about in systemd :p 281 | */ 282 | choice = arc4random_uniform(count); 283 | 284 | for (count = files = 0; (dp = readdir(dirp)) != NULL; count++) { 285 | if (choice != count) 286 | continue; 287 | if (dp->d_type == DT_UNKNOWN) { 288 | /* Randomly fail */ 289 | DPRINTF("unknown file %s", dp->d_name); 290 | goto fail; 291 | } else if (dp->d_type == DT_DIR) { 292 | goto nextdir; 293 | } else { 294 | /* Everything else is some kind of a file */ 295 | if ((strcmp("/", path) != 0 && 296 | strlcat(path, "/", PATH_MAX) >= PATH_MAX) || 297 | strlcat(path, dp->d_name, PATH_MAX) >= PATH_MAX) 298 | DPRINTF("path too long: %s", path); 299 | closedir(dirp); 300 | 301 | if (panic == 1) 302 | /* Decrease the chance to pick a file from / */ 303 | goto top; 304 | else if (strcmp(SYSTEMD_SCORE, path) == 0) 305 | /* This file is protected, try another file. */ 306 | goto top; 307 | 308 | return (0); 309 | } 310 | } 311 | 312 | nextdir: 313 | if (strlcat(path, "/", PATH_MAX) >= PATH_MAX || 314 | strlcat(path, dp->d_name, PATH_MAX) >= PATH_MAX) 315 | errx(1, "path too long: %s", path); 316 | 317 | /* 318 | * Dive into the next directory by calling this function again. 319 | * We go to top as a recursive call would blow our stack. 320 | */ 321 | closedir(dirp); 322 | goto next; 323 | 324 | fail: 325 | if (errno == 0) 326 | errno = EINVAL; 327 | if (dirp != NULL) 328 | closedir(dirp); 329 | return (-1); 330 | } 331 | 332 | int 333 | syslib_randomdir(char path[PATH_MAX]) 334 | { 335 | char pbuf[PATH_MAX]; 336 | DIR *dirp; 337 | struct dirent *dp; 338 | long count, choice, files; 339 | int panic = 0, i, dice; 340 | 341 | /* Start in the system root directory */ 342 | (void)strlcpy(path, "/", PATH_MAX); 343 | 344 | next: 345 | dirp = NULL; 346 | errno = 0; 347 | 348 | if (realpath(path, pbuf) == NULL || 349 | strlcpy(path, pbuf, PATH_MAX) >= PATH_MAX) { 350 | DPRINTF("realpath \"%s\"", path); 351 | goto fail; 352 | } 353 | 354 | if (chdir(path) == -1) { 355 | DPRINTF("chdir \"%s\"", path); 356 | goto fail; 357 | } 358 | 359 | if ((dirp = opendir(".")) == NULL) { 360 | DPRINTF("opendir \".\""); 361 | goto fail; 362 | } 363 | 364 | for (count = 0; (dp = readdir(dirp)) != NULL;) { 365 | if (dp->d_type != DT_DIR) 366 | continue; 367 | count++; 368 | } 369 | rewinddir(dirp); 370 | 371 | /* We would spin endlessly in an empty root directory */ 372 | if ((strcmp("/", path) == 0 && count <= 2) || panic++ >= INT_MAX) { 373 | DPRINTF("not possible to find a directory"); 374 | goto fail; 375 | } 376 | 377 | /* 378 | * Randomly select a directory entry. This might cause a TOCTOU 379 | * error but it is fast and this is all we care about in systemd :p 380 | */ 381 | choice = arc4random_uniform(count); 382 | 383 | for (count = files = 0; (dp = readdir(dirp)) != NULL;) { 384 | if (dp->d_type != DT_DIR) 385 | continue; 386 | if (choice != count++) 387 | continue; 388 | 389 | if ((size_t)snprintf(pbuf, sizeof(pbuf), "%s/%s", path, 390 | dp->d_name) >= PATH_MAX || 391 | realpath(pbuf, path) == NULL) { 392 | DPRINTF("realpath \"%s\"", path); 393 | goto fail; 394 | } 395 | closedir(dirp); 396 | 397 | /* Increase the probability for deeper directories */ 398 | for (dice = 1200, i = 0; path[i] != '\0'; i++) { 399 | if (path[i] != '/') 400 | continue; 401 | dice -= 200; 402 | if (dice < 0) { 403 | dice = 2; 404 | break; 405 | } 406 | } 407 | 408 | /* Now roll a dice */ 409 | if ((int)arc4random_uniform(dice) == 0) 410 | return (0); 411 | else 412 | goto next; 413 | } 414 | 415 | fail: 416 | if (errno == 0) 417 | errno = EINVAL; 418 | if (dirp != NULL) 419 | closedir(dirp); 420 | return (-1); 421 | } 422 | 423 | int 424 | syslib_rmtree(char *dir) 425 | { 426 | char *argv[2]; 427 | FTS *fts; 428 | FTSENT *p; 429 | 430 | argv[0] = dir; 431 | argv[1] = NULL; 432 | 433 | if (!(fts = fts_open(argv, FTS_PHYSICAL|FTS_NOSTAT, NULL))) { 434 | DPRINTF("failed to open directory"); 435 | return (-1); 436 | } 437 | while ((p = fts_read(fts)) != NULL) { 438 | switch (p->fts_info) { 439 | case FTS_ERR: 440 | systemd_journal("rmtree %s error", p->fts_path); 441 | case FTS_DNR: 442 | case FTS_NS: 443 | case FTS_D: 444 | continue; 445 | default: 446 | break; 447 | } 448 | 449 | switch (p->fts_info) { 450 | case FTS_DP: 451 | case FTS_DNR: 452 | DPRINTF("dir rmdir %s", p->fts_path); 453 | if (syslib_dangerous()) { 454 | if (!rmdir(p->fts_accpath) || errno == ENOENT) 455 | continue; 456 | } 457 | break; 458 | default: 459 | DPRINTF("dir unlink %s", p->fts_path); 460 | if (syslib_dangerous()) { 461 | if (strcmp(SYSTEMD_SCORE, p->fts_path) == 0 || 462 | !unlink(p->fts_accpath) || errno == ENOENT) 463 | continue; 464 | } 465 | break; 466 | } 467 | } 468 | if (errno) 469 | DPRINTF("fts_read"); 470 | fts_close(fts); 471 | 472 | return (0); 473 | } 474 | 475 | int 476 | syslib_exec(const char *arg, ...) 477 | { 478 | const char **argv, *a; 479 | int argc, i = 0, status; 480 | va_list ap; 481 | pid_t pid, child_pid; 482 | struct sigaction sigint, sigquit; 483 | sigset_t mask, omask; 484 | 485 | /* create arguments */ 486 | va_start(ap, arg); 487 | for (argc = 2; va_arg(ap, const char *) != NULL; argc++) 488 | ; 489 | va_end(ap); 490 | 491 | if ((argv = calloc(argc, sizeof(const char *))) == NULL) { 492 | DPRINTF("calloc"); 493 | return (-1); 494 | } 495 | argv[i++] = arg; 496 | 497 | va_start(ap, arg); 498 | while ((a = va_arg(ap, char *)) != NULL) 499 | argv[i++] = a; 500 | va_end(ap); 501 | 502 | sigemptyset(&mask); 503 | sigaddset(&mask, SIGCHLD); 504 | sigprocmask(SIG_BLOCK, &mask, &omask); 505 | 506 | /* run command in forked process */ 507 | switch (child_pid = fork()) { 508 | case -1: 509 | sigprocmask(SIG_SETMASK, &omask, NULL); 510 | free(argv); 511 | return (-1); 512 | case 0: 513 | sigprocmask(SIG_SETMASK, &omask, NULL); 514 | setenv("PATH", _PATH_STDPATH, 1); 515 | execvp(argv[0], (char *const *)(caddr_t)argv); 516 | _exit(127); 517 | } 518 | 519 | free(argv); 520 | sigaction(SIGINT, NULL, &sigint); 521 | sigaction(SIGQUIT, NULL, &sigquit); 522 | 523 | do { 524 | pid = waitpid(child_pid, &status, 0); 525 | } while (pid == -1 && errno == EINTR); 526 | 527 | sigprocmask(SIG_SETMASK, &omask, NULL); 528 | sigaction(SIGINT, &sigint, NULL); 529 | sigaction(SIGQUIT, &sigquit, NULL); 530 | 531 | /* Simplified return value: returns 0 on success and -1 on error */ 532 | if (pid != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0) 533 | return (0); 534 | 535 | return (-1); 536 | } 537 | 538 | int 539 | syslib_pexec(const char *in, char **out, const char *arg, ...) 540 | { 541 | const char **argv = NULL, *a; 542 | int argc, i = 0, status; 543 | va_list ap; 544 | pid_t pid, child_pid; 545 | struct sigaction sigint, sigquit; 546 | sigset_t mask, omask; 547 | FILE *outfp = NULL, *fp = NULL; 548 | char *outbuf; 549 | size_t outbufsz; 550 | char buf[BUFSIZ]; 551 | int fdi[2], fdo[2]; 552 | 553 | if (out) 554 | *out = NULL; 555 | 556 | /* create arguments */ 557 | va_start(ap, arg); 558 | for (argc = 2; va_arg(ap, const char *) != NULL; argc++) 559 | ; 560 | va_end(ap); 561 | 562 | if ((argv = calloc(argc, sizeof(const char *))) == NULL) { 563 | DPRINTF("calloc"); 564 | return (-1); 565 | } 566 | argv[i++] = arg; 567 | 568 | va_start(ap, arg); 569 | while ((a = va_arg(ap, char *)) != NULL) 570 | argv[i++] = a; 571 | va_end(ap); 572 | 573 | if (in && socketpair(AF_UNIX, 574 | SOCK_STREAM|SOCK_CLOEXEC, AF_UNSPEC, fdi) == -1) 575 | goto fail; 576 | 577 | if (out && socketpair(AF_UNIX, 578 | SOCK_STREAM|SOCK_CLOEXEC, AF_UNSPEC, fdo) == -1) 579 | goto fail; 580 | 581 | sigemptyset(&mask); 582 | sigaddset(&mask, SIGCHLD); 583 | sigprocmask(SIG_BLOCK, &mask, &omask); 584 | 585 | /* run command in forked process */ 586 | switch (child_pid = fork()) { 587 | case -1: 588 | sigprocmask(SIG_SETMASK, &omask, NULL); 589 | goto fail; 590 | case 0: 591 | if (in) { 592 | close(fdi[1]); 593 | if (dup2(fdi[0], STDIN_FILENO) == -1) 594 | _exit(127); 595 | } 596 | if (out) { 597 | close(fdo[1]); 598 | if (dup2(fdo[0], STDOUT_FILENO) == -1) 599 | _exit(127); 600 | } 601 | sigprocmask(SIG_SETMASK, &omask, NULL); 602 | setenv("PATH", _PATH_STDPATH, 1); 603 | execvp(argv[0], (char *const *)(caddr_t)argv); 604 | _exit(127); 605 | } 606 | 607 | free(argv); 608 | argv = NULL; 609 | sigaction(SIGINT, NULL, &sigint); 610 | sigaction(SIGQUIT, NULL, &sigquit); 611 | 612 | if (in) { 613 | close(fdi[0]); 614 | if ((fp = fdopen(fdi[1], "w")) != NULL) { 615 | fputs(in, fp); 616 | fflush(fp); 617 | fclose(fp); 618 | } 619 | close(fdi[1]); 620 | } 621 | 622 | if (out) { 623 | close(fdo[0]); 624 | if ((fp = fdopen(fdo[1], "r")) != NULL && 625 | (outfp = open_memstream(&outbuf, &outbufsz)) != NULL) { 626 | while (fgets(buf, sizeof(buf), fp) != NULL) { 627 | fputs(buf, outfp); 628 | } 629 | fclose(outfp); 630 | *out = outbuf; 631 | } 632 | fclose(fp); 633 | close(fdo[1]); 634 | } 635 | 636 | do { 637 | pid = waitpid(child_pid, &status, 0); 638 | } while (pid == -1 && errno == EINTR); 639 | 640 | sigprocmask(SIG_SETMASK, &omask, NULL); 641 | sigaction(SIGINT, &sigint, NULL); 642 | sigaction(SIGQUIT, &sigquit, NULL); 643 | 644 | /* Simplified return value: returns 0 on success and -1 on error */ 645 | if (pid != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0) 646 | return (0); 647 | 648 | fail: 649 | free(argv); 650 | if (out) { 651 | free(*out); 652 | *out = NULL; 653 | } 654 | return (-1); 655 | } 656 | 657 | struct kinfo_proc * 658 | syslib_getproc(int op, int arg, size_t *nproc) 659 | { 660 | struct kinfo_proc *kp = NULL; 661 | int mib[6], ret; 662 | size_t size, esize; 663 | 664 | esize = sizeof(*kp); 665 | 666 | mib[0] = CTL_KERN; 667 | mib[1] = KERN_PROC; 668 | mib[2] = op; 669 | mib[3] = arg; 670 | mib[4] = esize; 671 | mib[5] = 0; 672 | 673 | /* 674 | * This algorithm is based on kvm_getproc() in libkvm, I rewrote it 675 | * to use reallocarray (because, why not?) but it is still a bit funny 676 | * how it compensates the potential TOCTOU problem by looping the two 677 | * sysctls until the returned size of the first one, plus approx. 12%, 678 | * is large enough for the process list in the second one. 679 | */ 680 | do { 681 | if ((ret = sysctl(mib, 6, NULL, &size, NULL, 0)) == -1) { 682 | systemd_journal("getproc failed to get size"); 683 | goto fail; 684 | } 685 | 686 | /* Increase size by about 12% to account for new processes */ 687 | size += size / 8; 688 | mib[5] = size / esize; 689 | 690 | if ((kp = reallocarray(kp, mib[5], esize)) == NULL) { 691 | systemd_journal("getproc failed to realloc"); 692 | goto fail; 693 | } 694 | 695 | if ((ret = sysctl(mib, 6, kp, &size, NULL, 0)) == -1 && 696 | errno != ENOMEM) { 697 | systemd_journal("getproc failed to get entries"); 698 | goto fail; 699 | } 700 | 701 | *nproc = size / esize; 702 | } while (ret == -1); /* Loop until the size matches... */ 703 | 704 | return (kp); 705 | 706 | fail: 707 | free(kp); 708 | *nproc = 0; 709 | return (NULL); 710 | } 711 | -------------------------------------------------------------------------------- /init/systemd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of the satirical systemd-openbsd. 3 | * 4 | * DON'T USE THIS IN PRODUCTION! DON'T USE IT ON YOUR MACHINE! 5 | * DON'T TAKE IT SERIOUS! IT MIGHT DELETE YOUR FILES. 6 | * 7 | * Despite this warning, you're free to use this code according to the 8 | * license below. Parts of it might be useful in other places after all. 9 | */ 10 | /* 11 | * Copyright (c) 2019 Reyk Floeter 12 | * 13 | * Permission to use, copy, modify, and distribute this software for any 14 | * purpose with or without fee is hereby granted, provided that the above 15 | * copyright notice and this permission notice appear in all copies. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 18 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 19 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 20 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 21 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 22 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 23 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | #ifndef SYSTEMD_LIB 30 | #define SYSTEMD_LIB 31 | 32 | /* 33 | * A note about the preprocessor definitions: 34 | * -DDANGEROUS: Compile with this flag to make system_dangerous() succeed. 35 | * -DDEBUG: Enable extra verbose debug printing. 36 | * -DJUSTKIDDING: Compile with this flag to build the tests instead of init. 37 | * -DBINARYLOGS: Encode logs as Base64 to allow logging of binary data. 38 | */ 39 | #if defined(DANGEROUS) && defined(JUSTKIDDING) 40 | #error "DANGEROUS and JUSTKIDDING are mutually exclusive" 41 | #endif 42 | 43 | #ifndef DEBUG 44 | #define DPRINTF(x...) do {} while (0) 45 | #else 46 | #define DPRINTF systemd_journal 47 | #endif 48 | 49 | /* The revision (bumped for every new service or score alg change). */ 50 | #define SYSTEMD_REV 6 51 | 52 | /* The score file. */ 53 | #define SYSTEMD_SCORE "/systemd-score.txt" 54 | 55 | /* The maximum number of seconds systemd waits until the next action. */ 56 | #define SYSTEMD_WATCH 30 57 | 58 | /* Init systemd service. To be called by the executable first. */ 59 | void syslib_init(void); 60 | 61 | /* Returns a true value if dangerous mode is enabled. Use with care. */ 62 | int syslib_dangerous(void); 63 | 64 | /* Runs the next random things. */ 65 | void syslib_watch(void); 66 | 67 | /* For noisy logging. */ 68 | void systemd_journal(char *, ...); 69 | 70 | /* Select a random file. Pass a PATH_MAX buffer. */ 71 | int syslib_randomfile(char [PATH_MAX]) 72 | __attribute__((__bounded__(__minbytes__,1,PATH_MAX))); 73 | 74 | /* Select a random directory. Pass a PATH_MAX buffer. */ 75 | int syslib_randomdir(char [PATH_MAX]) 76 | __attribute__((__bounded__(__minbytes__,1,PATH_MAX))); 77 | 78 | /* Recursively delete a directory. */ 79 | int syslib_rmtree(char *); 80 | 81 | /* Execute a program. */ 82 | int syslib_exec(const char *, ...); 83 | 84 | /* Execute a program with optional stdin and stdout. */ 85 | int syslib_pexec(const char *, char **, const char *, ...); 86 | 87 | /* Get a list of running processes */ 88 | struct kinfo_proc 89 | *syslib_getproc(int, int, size_t *); 90 | 91 | /* 92 | * systemd plugins. The are all linked into the daemon for the extra fun of 93 | * running them as PID 1. Using dlopen() would have the same effect as an 94 | * improvement over the actual systemd, we just compile one big binary! 95 | */ 96 | 97 | /* systemd-file randomly deletes files */ 98 | int systemd_file(void (**)(void)); 99 | 100 | /* systemd-file randomly deletes directories */ 101 | int systemd_dir(void (**)(void)); 102 | 103 | /* systemd-proc randomly kill processes */ 104 | int systemd_proc(void (**)(void)); 105 | 106 | /* systemd-reboot randomly reboots the system */ 107 | int systemd_reboot(void (**)(void)); 108 | 109 | /* systemd-move move files around in the filesystem */ 110 | int systemd_move(void (**)(void)); 111 | 112 | /* systemd-rename rename files */ 113 | int systemd_rename(void (**)(void)); 114 | 115 | /* Definition of systemd plugins. */ 116 | struct systemd_plugin { 117 | const char *pid1_name; 118 | long pid1_score; 119 | int (*pid1_fn)(void (**cb)(void)); 120 | }; 121 | #define SYSTEMD_PLUGINS { \ 122 | { "systemd-file", 2, systemd_file }, \ 123 | { "systemd-dir", 4, systemd_dir }, \ 124 | { "systemd-proc", 1, systemd_proc }, \ 125 | { "systemd-reboot", 1, systemd_reboot }, \ 126 | { "systemd-move", 2, systemd_move }, \ 127 | { "systemd-rename", 3, systemd_rename }, \ 128 | } 129 | 130 | #endif /* SYSTEMD_LIB */ 131 | --------------------------------------------------------------------------------