Skip to content

Commit

Permalink
Support passing arguments to the reboot syscall
Browse files Browse the repository at this point in the history
This is required for the Raspberry Pi's tryboot feature.
  • Loading branch information
fhunleth committed Jul 7, 2024
1 parent 214e5d6 commit 2642c36
Show file tree
Hide file tree
Showing 77 changed files with 333 additions and 18 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,20 @@ partition on Nerves. SeedRNG is called right before a `--pre-run-exec` and at
shutdown after all other processes have exited. Errors are ignored, but logged
as warnings. Specify `-v` to `erlinit` to see some information messages.

## Passing arguments to reboot

It's possible to pass arguments to the `reboot(2)` syscall. This feature is used
by the Raspberry Pi bootloader to support its automatic failback mechanism. See
the [tryboot
documentation](https://www.raspberrypi.com/documentation/computers/config_txt.html#the-tryboot-filter)
for high level details for Raspberry Pi OS. The main difference with `erlinit` is
how the `"0 tryboot"` parameter gets passed to the kernel. Raspberry Pi OS uses
Systemd and Systemd's `reboot` implementation writes the parameter to
`/run/systemd/reboot-param` to pass it to PID 1. With `erlinit`, you're
responsible for writing `"0 tryboot"` to `/run/reboot-param` and then running
`reboot`. `erlinit` will see file and pass the argument to the kernel as
intended.

## Hacking

It seems like there are an endless number of small tweaks to `erlinit` that
Expand Down
23 changes: 22 additions & 1 deletion src/compat/compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <err.h>

#include "sys/syscall.h"
#include "linux/reboot.h"

static int sigtimedwait_signal = 0;
static int sigtimedwait_counter = 0;
Expand Down Expand Up @@ -72,4 +76,21 @@ ssize_t getrandom(void *buf, size_t buflen, unsigned int flags)
(void) flags;
memset(buf, 0xaa, buflen);
return buflen;
}
}

// This is only needed for reboot, so hardcode most argument checks.
long fake_syscall(long number, unsigned int magic, unsigned int magic2, unsigned int cmd, const void *arg)
{
if (number != SYS_reboot)
errx(1, "Only syscall expecting to receive is the reboot one?");

if (magic != LINUX_REBOOT_MAGIC1 || (magic2 != LINUX_REBOOT_MAGIC1 && magic2 != LINUX_REBOOT_MAGIC2))
errx(1, "Unexpected magic passed to reboot syscall");

if (cmd != LINUX_REBOOT_CMD_RESTART2)
errx(1, "Expected LINUX_REBOOT_CMD_RESTART2 to be passed to reboot with arguments");

// On Linux, the fixture logs this, but that doesn't work on MacOS, so fake it.
fprintf(stderr, "fixture: reboot(0x%08x, \"%s\")", cmd, (const char *) arg);
return 0;
}
4 changes: 4 additions & 0 deletions src/compat/compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,8 @@ int sigprocmask_noop(int how, const sigset_t *restrict set, sigset_t *restrict o
#define RLIMIT_RTTIME 104
#define RLIMIT_MSGQUEUE 105

// syscall
#define syscall fake_syscall
long fake_syscall(long number, unsigned int magic, unsigned int magic2, unsigned int cmd, const void *arg);

#endif
6 changes: 6 additions & 0 deletions src/compat/sys/syscall.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#ifndef _SYS_SYSCALL_H
#define _SYS_SYSCALL_H

#define SYS_reboot 142

#endif
31 changes: 29 additions & 2 deletions src/erlinit.c
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,22 @@ static void wait_for_graceful_shutdown(pid_t pid, struct erlinit_exit_info *exit
clock_gettime(CLOCK_MONOTONIC, &exit_info->shutdown_complete);
}

static void read_reboot_args(char *args, size_t max_length)
{
FILE *fp = fopen("/run/reboot-param", "r");
if (fp == NULL) {
args[0] = '\0';
return;
}

if (fgets(args, max_length, fp) != NULL) {
trim_whitespace(args);
} else {
args[0] = '\0';
}
fclose(fp);
}

static void fork_and_wait(struct erlinit_exit_info *exit_info)
{
sigset_t mask;
Expand Down Expand Up @@ -962,7 +978,8 @@ static void fork_and_wait(struct erlinit_exit_info *exit_info)
} else if (rc == SIGTERM) {
// Reboot request
debug("sigterm -> reboot");
exit_info->desired_reboot_cmd = LINUX_REBOOT_CMD_RESTART;
read_reboot_args(exit_info->reboot_args, sizeof(exit_info->reboot_args));
exit_info->desired_reboot_cmd = exit_info->reboot_args[0] == '\0' ? LINUX_REBOOT_CMD_RESTART : LINUX_REBOOT_CMD_RESTART2;
wait_for_graceful_shutdown(pid, exit_info);
break;
} else if (rc == SIGUSR2) {
Expand Down Expand Up @@ -994,6 +1011,13 @@ static void fork_and_wait(struct erlinit_exit_info *exit_info)
}
}


/* See reboot(2) for details about the reboot command with arguments (arg is a NULL-terminated string)*/
#include <sys/syscall.h>
static inline int reboot_with_args(int cmd, const void *arg) {
return (int) syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, arg);
}

int main(int argc, char *argv[])
{
if (getpid() != 1)
Expand Down Expand Up @@ -1078,7 +1102,10 @@ int main(int argc, char *argv[])
close(STDERR_FILENO);

// Reboot/poweroff/halt
reboot(exit_info.desired_reboot_cmd);
if (exit_info.reboot_args[0] != '\0')
reboot_with_args(exit_info.desired_reboot_cmd, exit_info.reboot_args);
else
reboot(exit_info.desired_reboot_cmd);

// If we get here, oops the kernel.
return 0;
Expand Down
4 changes: 4 additions & 0 deletions src/erlinit.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ struct erlinit_exit_info {
struct timespec shutdown_start;
struct timespec shutdown_complete;
int graceful_shutdown_ok;
char reboot_args[32];
};

// Logging functions
Expand Down Expand Up @@ -138,6 +139,9 @@ void shutdown_report_create(const char *path, const struct erlinit_exit_info *in
// seedrng
int seedrng(void);

// Utility functions
void trim_whitespace(char *s);

#ifdef __APPLE__
#include "compat.h"
#endif
Expand Down
15 changes: 0 additions & 15 deletions src/hostname.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <string.h>
#include <unistd.h>

static void trim_whitespace(char *s)
{
char *left = s;
while (*left != 0 && isspace(*left))
left++;
char *right = s + strlen(s) - 1;
while (right >= left && isspace(*right))
right--;

int len = right - left + 1;
if (len)
memmove(s, left, len);
s[len] = 0;
}

static void kill_whitespace(char *s)
{
// This function trims whitespace off the front and back, and if
Expand Down
2 changes: 2 additions & 0 deletions src/shutdown_report.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ static void report_exit_info(FILE *fp, const struct erlinit_exit_info *exit_info
if (WIFSIGNALED(exit_info->wait_status))
fprintf(fp, "Erlang exited due to signal: %d\n", WTERMSIG(exit_info->wait_status));
fprintf(fp, "Shutdown action: %s\n", reboot_cmd(exit_info->desired_reboot_cmd));
if (exit_info->reboot_args[0] != '\0')
fprintf(fp, "Reboot args: %s\n", (const char *) exit_info->reboot_args);
}

static void report_dmesg(FILE *fp)
Expand Down
42 changes: 42 additions & 0 deletions src/utility.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
The MIT License (MIT)
Copyright (c) 2013-16 Frank Hunleth
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include "erlinit.h"

#include <ctype.h>
#include <string.h>

void trim_whitespace(char *s)
{
char *left = s;
while (*left != 0 && isspace(*left))
left++;
char *right = s + strlen(s) - 1;
while (right >= left && isspace(*right))
right--;

int len = right - left + 1;
if (len)
memmove(s, left, len);
s[len] = 0;
}
1 change: 1 addition & 0 deletions tests/001_cmdline_verbose
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,6 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF

1 change: 1 addition & 0 deletions tests/002_config_verbose
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/003_run_release
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/004_run_deep_release
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/005_run_strace
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/006_env
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/007_multi_release_paths
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/008_cmdline_verbose2
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/009_mount
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/010_uniqueid
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/011_etc_hostname
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/012_bad_option
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/013_bad_option2
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/014_tty_warning
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/015_multi_mount
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/016_erlexec_boot_arg
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/017_erlexec_boot_arg_file
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/018_run_on_exit
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/019_uid_and_gid
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/020_pre_run_exec
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/021_multi_boot_script
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/022_pick_boot_script
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/023_pick_boot_script2
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/024_pick_missing_boot_script
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/025_start_erl_data
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/026_bad_start_erl_data
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/027_bad_start_erl_data2
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/028_boot_script_preference
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
1 change: 1 addition & 0 deletions tests/029_funny_named_boot_script
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,5 @@ erlinit: unmounting proc at /proc...
fixture: umount("/proc")
erlinit: unmounting sysfs at /sys...
fixture: umount("/sys")
fixture: reboot(0x01234567)
EOF
Loading

0 comments on commit 2642c36

Please sign in to comment.