aboutsummaryrefslogtreecommitdiff
path: root/sd.c
diff options
context:
space:
mode:
Diffstat (limited to 'sd.c')
-rw-r--r--sd.c205
1 files changed, 205 insertions, 0 deletions
diff --git a/sd.c b/sd.c
new file mode 100644
index 0000000..c987df3
--- /dev/null
+++ b/sd.c
@@ -0,0 +1,205 @@
+/*
+ * Created by yuuta on 12/23/22.
+ */
+
+#include "common.h"
+#include "log.h"
+
+#define IOCTL(a, b, ...) \
+if (ioctl(a, b, __VA_ARGS__) < 0) { LOGFV("ioctl(%d, 0x%x): %m", a, b, errno); return errno; }
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+
+static const char *conspath[] = {
+ "/proc/self/fd/0",
+ "/proc/self/fd/1",
+ "/proc/self/fd/2",
+ "/dev/tty",
+ "/dev/tty0",
+ "/dev/vc/0",
+ "/dev/systty",
+ "/dev/console"
+};
+
+static int fd_con = -1;
+static struct vt_stat vts;
+static int vt_num = -1;
+static char vt_dev[PATH_MAX + 1];
+static int vt_fd = -1;
+static pid_t chld = -1;
+
+static int vt_switch(int to) {
+ if (to) {
+ IOCTL(fd_con, VT_ACTIVATE, vt_num)
+ IOCTL(fd_con, VT_WAITACTIVE, vt_num)
+ } else {
+ IOCTL(fd_con, VT_ACTIVATE, vts.v_active)
+ IOCTL(fd_con, VT_WAITACTIVE, vts.v_active)
+ }
+ return 0;
+}
+
+static int find_current_tty(void) {
+ for (unsigned int i = 0; i < sizeof(conspath) / sizeof(char *); i++) {
+ if ((fd_con = open(conspath[i], O_RDONLY)) == -1) {
+ LOGDV("Cannot open '%s': %m.",
+ conspath[i],
+ errno);
+ continue;
+ }
+ char arg;
+ errno = 0;
+ if (!isatty(fd_con) || ioctl(fd_con, KDGKBTYPE, &arg) ||
+ ((arg != KB_101) && (arg != KB_84))) {
+ if (errno) {
+ LOGDV("Cannot open '%s': %m.",
+ conspath[i]);
+ } else {
+ LOGDV("Cannot open '%s': Not a TTY.", conspath[i]);
+ }
+ close(fd_con);
+ fd_con = -1;
+ continue;
+ }
+ LOGDV("Opened '%s': %d.", conspath[i], fd_con);
+ break;
+ }
+ if (fd_con == -1) {
+ LOGE("Cannot get the current console. Try running as root to get tty0.");
+ return 1;
+ }
+ return 0;
+}
+
+static int main_sak(void) {
+ /* We have to take the ownership here - otherwise SAK will kill the parent. */
+ if (setsid() == -1) {
+ LOGFV("Cannot set session ID: %m.", errno);
+ return errno;
+ }
+ IOCTL(vt_fd, TIOCSCTTY, 0)
+ vt_switch(1);
+
+ dprintf(vt_fd, "\033[r\033[H\033[J"
+ "Press SysRq + K to continue.\n");
+ /* Wait for signal. */
+ while (1) {
+ pause();
+ }
+}
+
+int sd_setup(void) {
+ if (find_current_tty()) {
+ return 1;
+ }
+ IOCTL(fd_con, VT_GETSTATE, &vts)
+ IOCTL(fd_con, VT_OPENQRY, &vt_num)
+ snprintf(vt_dev, PATH_MAX, "/dev/tty%d", vt_num);
+ if ((vt_fd = open(vt_dev, O_RDWR)) == -1) {
+ LOGFV("Cannot open '%s': %m.", vt_dev, errno);
+ return errno;
+ }
+ LOGDV("Opened '%s': %d.", vt_dev, vt_fd);
+
+ if (!(chld = fork())) {
+ exit(main_sak());
+ }
+ if (chld == -1) {
+ LOGFV("Cannot fork: %m.", errno);
+ return errno;
+ }
+ int chld_status = -1;
+ if (waitpid(chld, &chld_status, 0) != chld) {
+ LOGFV("Cannot wait for child: %m.", errno);
+ return errno;
+ }
+ /* It seems like we need to open(2) again, or TIOCSCTTY will fail with EIO. */
+ close(vt_fd);
+ vt_fd = -1;
+
+ if (!WIFSIGNALED(chld_status)) {
+ LOGF("Not killed by signal. Considered insecure.");
+ return 1;
+ }
+ switch (WTERMSIG(chld_status)) {
+ case SIGKILL: {
+ break;
+ }
+ case SIGINT:
+ case SIGTERM: {
+ exit(13);
+ }
+ default: {
+ /* Consider insecure.
+ * Switch back and use the insecure view. */
+ LOGF("Killed by an incorrect signal. Considered insecure.");
+ return 1;
+ }
+ }
+
+ /* Take ownership */
+ if ((vt_fd = open(vt_dev, O_RDWR)) == -1) {
+ LOGFV("Cannot open '%s': %m.", vt_dev, errno);
+ return errno;
+ }
+ LOGDV("Opened '%s': %d.", vt_dev, vt_fd);
+ if (setsid() == -1) {
+ LOGFV("Cannot set session ID: %m.", errno);
+ return errno;
+ }
+ IOCTL(vt_fd, TIOCSCTTY, 1)
+
+ int in_bak, out_bak, err_bak;
+ if ((in_bak = dup(p_env.in)) == -1 ||
+ (out_bak = dup(p_env.out)) == -1 ||
+ (err_bak = dup(p_env.err)) == -1) {
+ LOGFV("dup: %m", errno);
+ return errno;
+ }
+
+ p_env.in = in_bak;
+ p_env.out = out_bak;
+ p_env.err = err_bak;
+
+ if (dup2(vt_fd, STDIN_FILENO) == -1 ||
+ dup2(vt_fd, STDOUT_FILENO) == -1 ||
+ dup2(vt_fd, STDERR_FILENO) == -1) {
+ LOGFV("dup2: %m", errno);
+ }
+
+ return 0;
+}
+
+void sd_cleanup(void) {
+ if (vt_fd >= 0) {
+ if (ioctl(vt_fd, TIOCNOTTY) < 0) {
+ LOGEV("Cannot deallocate console: %m", errno);
+ }
+ if (p_env.in != STDIN_FILENO) p_env.in = dup2(p_env.in, STDIN_FILENO);
+ if (p_env.out != STDOUT_FILENO) p_env.out = dup2(p_env.out, STDOUT_FILENO);
+ if (p_env.err != STDERR_FILENO) p_env.err = dup2(p_env.err, STDERR_FILENO);
+ /* It should already be closed? */
+ close(vt_fd);
+ vt_fd = -1;
+ }
+ if (fd_con >= 0) {
+ if (vt_switch(0)) {
+ LOGEV("Cannot return to the previous console: %m", errno);
+ }
+ if (ioctl(fd_con, VT_DISALLOCATE, vt_num) < 0) {
+ LOGEV("Cannot deallocate console: %m", errno);
+ }
+ close(fd_con);
+ fd_con = -1;
+ }
+}