From 6e51c1ae51b1dde72f63ca2b7bf5dada71b79bad Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Sat, 15 Jun 2024 22:34:51 +0000 Subject: [PATCH] Add init groups. Every process now has an init process like it has a session, and each session belong to an init. Orphaned processes are reparented to its init process. All descendent processes are SIGKILL'd when an init process exits and creating additional processes/threads fails. Add setinit(2) for becoming the init process for your child processes and add getinit(2) for locating your init process. Add TIOCSCTTY force flag that releases a terminal from its current session and makes it the controlling terminal for the current session. This ioctl is essential to transferring the controlling terminal to a nested init, which has its own session. Add TIOCUCTTY that releases the terminal as the controlling terminal for its current session. Remove INIT_PID as it is replaced by getinit(2). --- Makefile | 2 +- init/init.8 | 5 +- init/init.c | 17 ++- kernel/include/sortix/ioctl.h | 3 +- kernel/include/sortix/kernel/process.h | 8 +- kernel/include/sortix/kernel/scheduler.h | 2 - kernel/include/sortix/kernel/syscall.h | 4 +- kernel/include/sortix/syscall.h | 4 +- kernel/kernel.cpp | 11 +- kernel/process.cpp | 135 ++++++++++++++++++----- kernel/psctl.cpp | 22 +++- kernel/scheduler.cpp | 11 -- kernel/syscall.cpp | 4 +- kernel/tty.cpp | 48 ++++++-- libc/Makefile | 4 + libc/include/unistd.h | 4 +- libc/unistd/getinit.2 | 1 + libc/unistd/getinit.c | 29 +++++ libc/unistd/setinit.2 | 75 +++++++++++++ libc/unistd/setinit.c | 29 +++++ share/man/man7/following-development.7 | 15 +++ sysinstall/sysinstall.c | 1 - terminal/terminal.c | 2 +- utils/getty.c | 2 +- utils/halt.c | 9 +- utils/poweroff.c | 9 +- utils/pty.c | 2 +- utils/reboot.c | 9 +- 28 files changed, 377 insertions(+), 90 deletions(-) create mode 120000 libc/unistd/getinit.2 create mode 100644 libc/unistd/getinit.c create mode 100644 libc/unistd/setinit.2 create mode 100644 libc/unistd/setinit.c diff --git a/Makefile b/Makefile index d65cc070..4f3c624f 100644 --- a/Makefile +++ b/Makefile @@ -232,7 +232,7 @@ sysroot-system: sysroot-fsh sysroot-base-headers echo 'ID=sortix' && \ echo 'VERSION_ID="$(VERSION)"' && \ echo 'PRETTY_NAME="Sortix $(VERSION)"' && \ - echo 'SORTIX_ABI=2.0' && \ + echo 'SORTIX_ABI=2.1' && \ true) > "$(SYSROOT)/etc/sortix-release" echo /etc/sortix-release >> "$(SYSROOT)/tix/manifest/system" ln -sf sortix-release "$(SYSROOT)/etc/os-release" diff --git a/init/init.8 b/init/init.8 index 26106934..2ef54ce2 100644 --- a/init/init.8 +++ b/init/init.8 @@ -213,12 +213,9 @@ daemon is meant to start the installation's local daemon requirements. .Sh ENVIRONMENT .Nm sets the following environment variables. -.Bl -tag -width "INIT_PID" +.Bl -tag -width "LOGNAME" .It Ev HOME root's home directory -.It Ev INIT_PID -.Nm Ns 's -process id .It Ev LOGNAME root .It Ev PATH diff --git a/init/init.c b/init/init.c index 55f1b474..5e8990bc 100644 --- a/init/init.c +++ b/init/init.c @@ -2532,7 +2532,6 @@ static void daemon_start(struct daemon* daemon) char ppid_str[sizeof(pid_t) * 3]; snprintf(ppid_str, sizeof(ppid_str), "%" PRIiPID, ppid); if ( (!daemon->need_tty && setenv("READYFD", "3", 1)) < 0 || - setenv("INIT_PID", ppid_str, 1) < 0 || setenv("LOGNAME", pwd->pw_name, 1) < 0 || setenv("USER", pwd->pw_name, 1) < 0 || setenv("HOME", home, 1) < 0 || @@ -2628,7 +2627,6 @@ static void daemon_start(struct daemon* daemon) // TODO: Also unset other things. if ( !daemon->need_tty ) unsetenv("READYFD"); - unsetenv("INIT_PID"); unsetenv("LOGNAME"); unsetenv("USER"); unsetenv("HOME"); @@ -4302,9 +4300,12 @@ int main(int argc, char* argv[]) } // Prevent recursive init without care. - if ( getenv("INIT_PID") ) + if ( getinit(0) != getpid() ) fatal("System is already managed by an init process"); + if ( !isatty(0) || !isatty(1) || !isatty(2) ) + fatal("stdin, stdout, and stderr must be the terminal"); + // Register handler that shuts down the system when init exits. if ( atexit(niht) != 0 ) fatal("atexit: %m"); @@ -4460,6 +4461,9 @@ int main(int argc, char* argv[]) chain_location_dev_made = true; close(new_dev_fd); close(old_dev_fd); + int tty_fd = open("/dev/tty", O_RDWR | O_CLOEXEC); + if ( tty_fd < 0 ) + fatal("/dev/tty: %m"); // TODO: Forward the early init log to the chain init. // Run the chain booted operating system. pid_t child_pid = fork(); @@ -4472,6 +4476,10 @@ int main(int argc, char* argv[]) fatal("chroot: %s: %m", chain_location); if ( chdir("/") < 0 ) fatal("chdir: %s: %m", chain_location); + if ( setinit() < 0 ) + fatal("setinit: %m"); + if ( ioctl(tty_fd, TIOCSCTTY, 1) < 0 ) + fatal("ioctl: TIOCSCTTY: %m"); const char* program = next_argv[0]; char verbose_opt[] = {'-', "sqv"[verbosity], '\0'}; // Chain boot the operating system upgrade if needed. @@ -4502,6 +4510,9 @@ int main(int argc, char* argv[]) } sigprocmask(SIG_BLOCK, &handled_signals, NULL); forward_signal_pid = -1; // Racy with waitpid. + if ( ioctl(tty_fd, TIOCSCTTY, 1) < 0 ) + fatal("ioctl: TIOCSCTTY: %m"); + close(tty_fd); if ( WIFEXITED(status) ) return WEXITSTATUS(status); else if ( WIFSIGNALED(status) ) diff --git a/kernel/include/sortix/ioctl.h b/kernel/include/sortix/ioctl.h index 01454017..c91eb592 100644 --- a/kernel/include/sortix/ioctl.h +++ b/kernel/include/sortix/ioctl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017 Jonas 'Sortie' Termansen. + * Copyright (c) 2016, 2017, 2024 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -39,6 +39,7 @@ #define TIOCGNAME __IOCTL(6, __IOCTL_TYPE_PTR) #define TIOCGPTN __IOCTL(7, __IOCTL_TYPE_PTR) #define TIOCGDISPLAYS __IOCTL(8, __IOCTL_TYPE_PTR) +#define TIOCUCTTY __IOCTL(9, __IOCTL_TYPE_INT) #define IOC_TYPE(x) ((x) >> 0 & 0xFF) #define IOC_TYPE_BLOCK_DEVICE 1 diff --git a/kernel/include/sortix/kernel/process.h b/kernel/include/sortix/kernel/process.h index e5919234..7b8004ce 100644 --- a/kernel/include/sortix/kernel/process.h +++ b/kernel/include/sortix/kernel/process.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2016, 2021 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2016, 2021, 2024 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -124,12 +124,17 @@ public: Process* sessionprev; Process* sessionnext; Process* sessionfirst; + Process* init; + Process* initprev; + Process* initnext; + Process* initfirst; kthread_mutex_t childlock; kthread_mutex_t parentlock; kthread_cond_t zombiecond; bool iszombie; bool nozombify; bool limbo; + bool is_init_exiting; int exit_code; public: @@ -178,6 +183,7 @@ public: int prot); void GroupRemoveMember(Process* child); void SessionRemoveMember(Process* child); + void InitRemoveMember(Process* child); public: Process* Fork(); diff --git a/kernel/include/sortix/kernel/scheduler.h b/kernel/include/sortix/kernel/scheduler.h index 644d1301..357b4af4 100644 --- a/kernel/include/sortix/kernel/scheduler.h +++ b/kernel/include/sortix/kernel/scheduler.h @@ -41,8 +41,6 @@ void SetThreadState(Thread* thread, ThreadState state, bool wake_only = false); void SetSignalPending(Thread* thread, unsigned long is_pending); ThreadState GetThreadState(Thread* thread); void SetIdleThread(Thread* thread); -void SetInitProcess(Process* init); -Process* GetInitProcess(); Process* GetKernelProcess(); void InterruptYieldCPU(struct interrupt_context* intctx, void* user); void ThreadExitCPU(struct interrupt_context* intctx, void* user); diff --git a/kernel/include/sortix/kernel/syscall.h b/kernel/include/sortix/kernel/syscall.h index 17bc22f8..a666526d 100644 --- a/kernel/include/sortix/kernel/syscall.h +++ b/kernel/include/sortix/kernel/syscall.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2016, 2021, 2022, 2023 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2016, 2021-2024 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -97,6 +97,7 @@ int sys_getentropy(void*, size_t); uid_t sys_geteuid(void); gid_t sys_getgid(void); int sys_gethostname(char*, size_t); +pid_t sys_getinit(pid_t); size_t sys_getpagesize(void); int sys_getpeername(int, void*, size_t*); pid_t sys_getpgid(pid_t); @@ -151,6 +152,7 @@ int sys_setegid(gid_t); int sys_seteuid(uid_t); int sys_setgid(gid_t); int sys_sethostname(const char*, size_t); +int sys_setinit(void); int sys_setpgid(pid_t, pid_t); int sys_setpriority(int, id_t, int); pid_t sys_setsid(void); diff --git a/kernel/include/sortix/syscall.h b/kernel/include/sortix/syscall.h index 900397e9..3037db48 100644 --- a/kernel/include/sortix/syscall.h +++ b/kernel/include/sortix/syscall.h @@ -190,6 +190,8 @@ #define SYSCALL_SETDNSCONFIG 167 #define SYSCALL_FUTEX 168 #define SYSCALL_MEMUSAGE 169 -#define SYSCALL_MAX_NUM 170 /* index of highest constant + 1 */ +#define SYSCALL_GETINIT 170 +#define SYSCALL_SETINIT 171 +#define SYSCALL_MAX_NUM 172 /* index of highest constant + 1 */ #endif diff --git a/kernel/kernel.cpp b/kernel/kernel.cpp index 63666966..55452c20 100644 --- a/kernel/kernel.cpp +++ b/kernel/kernel.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2018, 2021-2023 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2018, 2021-2024 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -381,6 +381,10 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p) system->sessionprev = NULL; system->sessionnext = NULL; system->sessionfirst = system; + system->init = NULL; + system->initprev = NULL; + system->initnext = NULL; + system->initfirst = NULL; if ( !(system->program_image_path = String::Clone("")) ) Panic("Unable to clone string for system process name"); @@ -676,6 +680,10 @@ static void BootThread(void* /*user*/) init->sessionprev = NULL; init->sessionnext = NULL; init->sessionfirst = init; + init->init = init; + init->initprev = NULL; + init->initnext = NULL; + init->initfirst = init; kthread_mutex_unlock(&process_family_lock); // TODO: Why don't we fork from pid=0 and this is done for us? @@ -685,7 +693,6 @@ static void BootThread(void* /*user*/) mtable.Reset(); init->BootstrapDirectories(droot); init->addrspace = initaddrspace; - Scheduler::SetInitProcess(init); Thread* initthread = RunKernelThread(init, InitThread, NULL, "main"); if ( !initthread ) diff --git a/kernel/process.cpp b/kernel/process.cpp index 99e641c9..4f67f462 100644 --- a/kernel/process.cpp +++ b/kernel/process.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2016, 2021-2022 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2016, 2021-2024 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -73,8 +73,6 @@ kthread_mutex_t process_family_lock = KTHREAD_MUTEX_INITIALIZER; // The system is shutting down and creation of additional processes and threads // should be prevented. Protected by process_family_lock. -static bool is_init_exiting = false; - Process::Process() { program_image_path = NULL; @@ -130,10 +128,15 @@ Process::Process() sessionprev = NULL; sessionnext = NULL; sessionfirst = NULL; + init = NULL; + initprev = NULL; + initnext = NULL; + initfirst = NULL; zombiecond = KTHREAD_COND_INITIALIZER; iszombie = false; nozombify = false; limbo = false; + is_init_exiting = false; exit_code = -1; firstthread = NULL; @@ -168,9 +171,11 @@ Process::~Process() // process_family_lock taken alarm_timer.Detach(); delete[] program_image_path; assert(!zombiechild); + assert(!init); assert(!session); assert(!group); assert(!parent); + assert(!initfirst); assert(!sessionfirst); assert(!groupfirst); assert(!firstchild); @@ -208,9 +213,6 @@ void Process::BootstrapDirectories(Ref root) void Process::OnLastThreadExit() { - Process* init = Scheduler::GetInitProcess(); - assert(init); - // Child processes can't be reparented away if we're init. The system is // about to shut down, so broadcast SIGKILL every process and wait for every // single process to exit. The operating system is finished when init has @@ -221,14 +223,21 @@ void Process::OnLastThreadExit() // Forbid any more processes and threads from being created, so this // loop will always terminate. is_init_exiting = true; - kthread_mutex_lock(&ptrlock); - for ( pid_t pid = ptable->Next(0); 0 < pid; pid = ptable->Next(pid) ) + Process* process = firstchild; + while ( process ) { - Process* process = ptable->Get(pid); - if ( process->pid != 0 && process != init ) + if ( process->pid != 0 ) process->DeliverSignal(SIGKILL); + if ( process->init == process ) + process->is_init_exiting = true; + if ( process->firstchild ) + process = process->firstchild; + while ( process && process != this && !process->nextsibling ) + process = process->parent; + if ( process == this ) + break; + process = process->nextsibling; } - kthread_mutex_unlock(&ptrlock); // NotifyChildExit always signals zombiecond for init when // is_init_exiting is true. while ( firstchild ) @@ -342,12 +351,10 @@ void Process::LastPrayer() iszombie = true; // Init is nice and will gladly raise our orphaned children and zombies. - Process* init = Scheduler::GetInitProcess(); - assert(init); - // Child processes can't be reparented away if we're init. OnLastThreadExit // must have already killed all the child processes and prevented more from // being created. + assert(init); if ( init == this ) { assert(is_init_exiting); @@ -386,6 +393,9 @@ void Process::LastPrayer() // Remove ourself from our session. if ( session ) session->SessionRemoveMember(this); + // Remove ourself from our init. + if ( init ) + init->InitRemoveMember(this); bool zombify = !nozombify; @@ -403,7 +413,7 @@ void Process::WaitedFor() // process_family_lock taken { parent = NULL; limbo = false; - if ( groupfirst || sessionfirst ) + if ( groupfirst || sessionfirst || initfirst ) limbo = true; if ( !limbo ) delete this; @@ -460,9 +470,23 @@ void Process::SessionRemoveMember(Process* child) // process_family_lock taken delete this; } +void Process::InitRemoveMember(Process* child) // process_family_lock taken +{ + assert(child->init == this); + if ( child->initprev ) + child->initprev->initnext = child->initnext; + else + initfirst = child->initnext; + if ( child->initnext ) + child->initnext->initprev = child->initprev; + child->init = NULL; + if ( IsLimboDone() ) + delete this; +} + bool Process::IsLimboDone() // process_family_lock taken { - return limbo && !groupfirst && !sessionfirst; + return limbo && !groupfirst && !sessionfirst && !initfirst; } // process_family_lock taken @@ -491,7 +515,7 @@ void Process::NotifyChildExit(Process* child, bool zombify) // when init is exiting, because OnLastThreadExit needs to be able to catch // every child exiting. DeliverSignal(SIGCHLD); - if ( zombify || (is_init_exiting && Scheduler::GetInitProcess() == this) ) + if ( zombify || is_init_exiting ) kthread_cond_broadcast(&zombiecond); } @@ -704,7 +728,7 @@ Process* Process::Fork() kthread_mutex_lock(&process_family_lock); // Forbid the creation of new processes if init has exited. - if ( is_init_exiting ) + if ( init->is_init_exiting ) { kthread_mutex_unlock(&process_family_lock); clone->AbortConstruction(); @@ -741,6 +765,13 @@ Process* Process::Fork() session->sessionfirst->sessionprev = clone; session->sessionfirst = clone; + // Add the new process to the current init. + clone->init = init; + clone->initprev = NULL; + if ( (clone->initnext = init->initfirst) ) + init->initfirst->initprev = clone; + init->initfirst = clone; + kthread_mutex_unlock(&process_family_lock); // Initialize everything that is safe and can't fail. @@ -1487,14 +1518,12 @@ pid_t sys_tfork(int flags, struct tfork* user_regs) stack_aligned = (stack_aligned + 16) & ~(stack_alignment-1); stack_aligned_size &= 0xFFFFFFF0; + Process* parent_process = CurrentProcess(); Process* child_process; if ( making_thread ) - child_process = CurrentProcess(); - else if ( !(child_process = CurrentProcess()->Fork()) ) - { - delete[] newkernelstack; - return -1; - } + child_process = parent_process; + else if ( !(child_process = parent_process->Fork()) ) + return delete[] newkernelstack, -1; struct thread_registers cpuregs; memset(&cpuregs, 0, sizeof(cpuregs)); @@ -1550,7 +1579,7 @@ pid_t sys_tfork(int flags, struct tfork* user_regs) // Forbid the creation of new threads if init has exited. ScopedLock process_family_lock_lock(&process_family_lock); - if ( is_init_exiting ) + if ( child_process->init->is_init_exiting ) return errno = EPERM, -1; // If the thread could not be created, make the process commit suicide @@ -1604,7 +1633,8 @@ pid_t sys_getpgid(pid_t pid) pid_t sys_getsid(pid_t pid) { ScopedLock lock(&process_family_lock); - Process* process = !pid ? CurrentProcess() : CurrentProcess()->GetPTable()->Get(pid); + Process* process = + !pid ? CurrentProcess() : CurrentProcess()->GetPTable()->Get(pid); if ( !process ) return errno = ESRCH, -1; if ( !process->session ) @@ -1612,6 +1642,16 @@ pid_t sys_getsid(pid_t pid) return process->session->pid; } +pid_t sys_getinit(pid_t pid) +{ + ScopedLock lock(&process_family_lock); + Process* process = + !pid ? CurrentProcess() : CurrentProcess()->GetPTable()->Get(pid); + if ( !process->init ) + return errno = ESRCH, -1; + return process->init->pid; +} + int sys_setpgid(pid_t pid, pid_t pgid) { if ( pid < 0 || pgid < 0 ) @@ -1710,6 +1750,49 @@ pid_t sys_setsid(void) return process->pid; } +int sys_setinit(void) +{ + Process* process = CurrentProcess(); + + ScopedLock lock(&process_family_lock); + + // Test if already a process group leader. + if ( process->group == process ) + return errno = EPERM, -1; + + // Remove the process from its current process group. + if ( process->group ) + process->group->GroupRemoveMember(process); + + // Remove the process from its current session. + if ( process->session ) + process->session->SessionRemoveMember(process); + + // Remove the process from its current init. + if ( process->init ) + process->init->InitRemoveMember(process); + + // Insert the process into its new init. + process->initprev = NULL; + process->initnext = NULL; + process->initfirst = process; + process->init = process; + + // Insert the process into its new session. + process->sessionprev = NULL; + process->sessionnext = NULL; + process->sessionfirst = process; + process->session = process; + + // Insert the process into its new process group. + process->groupprev = NULL; + process->groupnext = NULL; + process->groupfirst = process; + process->group = process; + + return process->pid; +} + size_t sys_getpagesize(void) { return Page::Size(); diff --git a/kernel/psctl.cpp b/kernel/psctl.cpp index 60be3147..b785a104 100644 --- a/kernel/psctl.cpp +++ b/kernel/psctl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, 2022 Jonas 'Sortie' Termansen. + * Copyright (c) 2015, 2016, 2022, 2024 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -110,11 +110,21 @@ int sys_psctl(pid_t pid, int request, void* ptr) psst.sid_next = -1; } psst.sid_first = process->sessionfirst ? process->sessionfirst->pid : -1; - // TODO: Implement init groupings. - psst.init = 1; - psst.init_prev = ptable->Prev(pid); - psst.init_next = ptable->Next(pid); - psst.init_first = pid == 1 ? 1 : -1; + + if ( process->init ) + { + Process* init = process->init; + psst.init = init->pid; + psst.init_prev = process->initprev ? process->initprev->pid : -1; + psst.init_next = process->initnext ? process->initnext->pid : -1; + } + else + { + psst.init = -1; + psst.init_prev = -1; + psst.init_next = -1; + } + psst.init_first = process->initfirst ? process->initfirst->pid : -1; kthread_mutex_lock(&process->idlock); psst.uid = process->uid; psst.euid = process->euid; diff --git a/kernel/scheduler.cpp b/kernel/scheduler.cpp index 06a4ec61..09fa44fc 100644 --- a/kernel/scheduler.cpp +++ b/kernel/scheduler.cpp @@ -268,7 +268,6 @@ static void SwitchRegisters(struct interrupt_context* intctx, static Thread* idle_thread; static Thread* first_runnable_thread; static Thread* true_current_thread; -static Process* init_process; static void SwitchThread(struct interrupt_context* intctx, Thread* old_thread, @@ -407,16 +406,6 @@ void SetIdleThread(Thread* thread) true_current_thread = thread; } -void SetInitProcess(Process* init) -{ - init_process = init; -} - -Process* GetInitProcess() -{ - return init_process; -} - Process* GetKernelProcess() { if ( !idle_thread ) diff --git a/kernel/syscall.cpp b/kernel/syscall.cpp index a7dcfed2..dbf7be45 100644 --- a/kernel/syscall.cpp +++ b/kernel/syscall.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2016, 2021-2022 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2016, 2021-2024 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -204,6 +204,8 @@ void* syscall_list[SYSCALL_MAX_NUM + 1] = [SYSCALL_SETDNSCONFIG] = (void*) sys_setdnsconfig, [SYSCALL_FUTEX] = (void*) sys_futex, [SYSCALL_MEMUSAGE] = (void*) sys_memusage, + [SYSCALL_GETINIT] = (void*) sys_getinit, + [SYSCALL_SETINIT] = (void*) sys_setinit, [SYSCALL_MAX_NUM] = (void*) sys_bad_syscall, }; } /* extern "C" */ diff --git a/kernel/tty.cpp b/kernel/tty.cpp index 1dc9214a..477729e3 100644 --- a/kernel/tty.cpp +++ b/kernel/tty.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, 2014, 2015, 2016, 2021 Jonas 'Sortie' Termansen. + * Copyright (c) 2012-2016, 2021, 2024 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -852,12 +852,23 @@ int TTY::ioctl(ioctx_t* ctx, int cmd, uintptr_t arg) return errno = EIO, -1; if ( cmd == TIOCSCTTY ) { + // TODO: After releasing Sortix 1.1, restore the invalid arguments check + // that is temporarily omitted since some Sortix 1.1 commits invoke the + // ioctl without an argument which was accidentally undefined. For now + // the kernel should be able to largely run older userspaces without + // issue, however terminal(1) did this which was fairly essential. + //if ( ((int) arg) & ~1 ) + // return errno = EINVAL, -1; + bool force = arg & 1; + if ( !force && 0 <= sid ) + return errno = EPERM, -1; ScopedLock family_lock(&process_family_lock); - if ( 0 <= sid ) - return errno = EPERM, -1; Process* process = CurrentProcess(); - if ( !process->sessionfirst ) + if ( !force && !process->sessionfirst ) return errno = EPERM, -1; + Process* session = process->session; + // TODO: Don't do this if the sesion already has a tty. + Process* group = process->group; if ( (ctx->dflags & (O_READ | O_WRITE)) != (O_READ | O_WRITE) ) return errno = EPERM, -1; Ref vnode(new Vnode(Ref(this), Ref(NULL), 0, 0)); @@ -866,11 +877,34 @@ int TTY::ioctl(ioctx_t* ctx, int cmd, uintptr_t arg) Ref desc(new Descriptor(vnode, O_READ | O_WRITE)); if ( !desc ) return -1; - sid = process->pid; - foreground_pgid = process->pid; - process->SetTTY(desc); + if ( 0 <= sid ) + { + Process* owner = process->GetPTable()->Get(sid); + if ( owner ) + owner->SetTTY(Ref()); + // TODO: SIGHUP? + } + sid = session->pid; + foreground_pgid = group->pid; + session->SetTTY(desc); return 0; } + else if ( cmd == TIOCUCTTY ) + { + if ( ((int) arg) & ~1 ) + return errno = EINVAL, -1; + if ( 0 <= sid ) + { + ScopedLock family_lock(&process_family_lock); + Process* owner = CurrentProcess()->GetPTable()->Get(sid); + // TODO: If !(arg & 1) fail if not the right session owner? + if ( owner ) + owner->SetTTY(Ref()); + // TODO: SIGHUP? + } + sid = -1; + foreground_pgid = -1; + } else if ( cmd == TIOCSPTLCK ) { // TODO: Figure out what locked ptys are and implement it if sensible. diff --git a/libc/Makefile b/libc/Makefile index f990d65e..79f4eb35 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -713,6 +713,7 @@ unistd/getentropy.o \ unistd/geteuid.o \ unistd/getgid.o \ unistd/gethostname.o \ +unistd/getinit.o \ unistd/getlogin.o \ unistd/getlogin_r.o \ unistd/getpagesize.o \ @@ -741,6 +742,7 @@ unistd/setegid.o \ unistd/seteuid.o \ unistd/setgid.o \ unistd/sethostname.o \ +unistd/setinit.o \ unistd/setpgid.o \ unistd/setsid.o \ unistd/setuid.o \ @@ -779,6 +781,8 @@ MANPAGES2=\ scram/scram.2 \ sys/dnsconfig/getdnsconfig.2 \ sys/dnsconfig/setdnsconfig.2 \ +unistd/setinit.2 \ +unistd/getinit.2 \ MANPAGES3=\ time/add_leap_seconds.3 \ diff --git a/libc/include/unistd.h b/libc/include/unistd.h index 41e57789..492be007 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen. + * Copyright (c) 2011-2016, 2024 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -562,8 +562,10 @@ int exit_thread(int, int, const struct exit_thread*); int fchdirat(int, const char*); int fchroot(int); int fchrootat(int, const char*); +pid_t getinit(pid_t); int memstat(size_t* memused, size_t* memtotal); int mkpartition(int fd, off_t start, off_t length); +pid_t setinit(void); pid_t sfork(int flags); pid_t tfork(int flags, struct tfork* regs); int truncateat(int dirfd, const char*, off_t); diff --git a/libc/unistd/getinit.2 b/libc/unistd/getinit.2 new file mode 120000 index 00000000..e304056e --- /dev/null +++ b/libc/unistd/getinit.2 @@ -0,0 +1 @@ +setinit.2 \ No newline at end of file diff --git a/libc/unistd/getinit.c b/libc/unistd/getinit.c new file mode 100644 index 00000000..23908e98 --- /dev/null +++ b/libc/unistd/getinit.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * unistd/getinit.c + * Get the current init. + */ + +#include + +#include + +DEFN_SYSCALL1(pid_t, sys_getinit, SYSCALL_GETINIT, pid_t); + +pid_t getinit(pid_t pid) +{ + return sys_getinit(pid); +} diff --git a/libc/unistd/setinit.2 b/libc/unistd/setinit.2 new file mode 100644 index 00000000..2e9afa8f --- /dev/null +++ b/libc/unistd/setinit.2 @@ -0,0 +1,75 @@ +.Dd June 18, 2024 +.Dt SETINIT 2 +.Os +.Sh NAME +.Nm setinit +.Nd become and locate init +.Sh SYNOPSIS +.In unistd.h +.Ft int +.Fn getinit "pid_t pid" +.Ft int +.Fn setinit void +.Sh DESCRIPTION +.Fn setinit +sets the current process as the init process for itself and its subsequently +created descendant processes. +.Fn setinit +runs +.Xr setsid 2 +to create a new session (and process group) and can fail for the same reasons as +.Xr setsid 2 . +.Pp +.Fn getinit +returns the init process for the process specified in +.Fa pid , +or the current process if +.Fa pid +is zero. +.Pp +Orphaned descendant processes are reparented to their init process. +If an init process exits, all descendant processes atomically receive the +.Dv SIGKILL +signal and become unable to create new processes and threads. +.Sh RETURN VALUES +.Fn setinit +returns the pid of the init process (the current process) on success, or -1 on +error and +.Va error +is set appropriately. +.Pp +.Fn getinit +returns the returns the pid of the init process, or -1 on +error and +.Va error +is set appropriately. +.Sh ERRORS +.Fn setinit +will fail if: +.Bl -tag -width "12345678" +.It Er EPERM +The process is already a process group leader, a session leader, or an init +process. +.El +.Pp +.Fn getinit +will fail if: +.Bl -tag -width "12345678" +.It Er ESRCH +The process specified in +.Fa pid +does not exist. +.El +.Sh SEE ALSO +.Xr getpgrp 2 , +.Xr getsid 2 , +.Xr psctl 2 , +.Xr setpgrp 2 , +.Xr setsid 2 , +.Xr init 8 +.Sh HISTORY +The +.Fn getinit +and +.Fn setinit +system calls originally appeared in Sortix 1.1. diff --git a/libc/unistd/setinit.c b/libc/unistd/setinit.c new file mode 100644 index 00000000..965b4dd8 --- /dev/null +++ b/libc/unistd/setinit.c @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 Jonas 'Sortie' Termansen. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * unistd/setinit.c + * Become init. + */ + +#include + +#include + +DEFN_SYSCALL0(pid_t, sys_setinit, SYSCALL_SETINIT); + +pid_t setinit(void) +{ + return sys_setinit(); +} diff --git a/share/man/man7/following-development.7 b/share/man/man7/following-development.7 index a0c998eb..bc22561e 100644 --- a/share/man/man7/following-development.7 +++ b/share/man/man7/following-development.7 @@ -69,6 +69,21 @@ releasing Sortix x.y, foo." to allow the maintainer to easily .Xr grep 1 for it after a release. .Sh CHANGES +.Ss Add init groups +The +.Xr setinit 2 +and +.Xr getinit 2 +system calls have been added for nested init groups. +The +.Dv TIOCSCTTY +.Xr ioctl 2 +for acquiring a controlling terminal has gained a force flag essential to +transferring terminals into nested sessions, and the new +.Dv TIOCUCTTY +.Xr ioctl 2 +releases a controlling terminal. +This is a minor compatible ABI change. .Ss Add tix-repository(8) The new .Xr tix-repository 8 diff --git a/sysinstall/sysinstall.c b/sysinstall/sysinstall.c index 24e3eb1e..fe27edf2 100644 --- a/sysinstall/sysinstall.c +++ b/sysinstall/sysinstall.c @@ -1751,7 +1751,6 @@ int main(void) unmount_all_but_root(); unsetenv("SYSINSTALL_TARGET"); unsetenv("SHLVL"); - unsetenv("INIT_PID"); exit(execute((const char*[]) { "chroot", "-d", fs, "/sbin/init", NULL }, "f")); } diff --git a/terminal/terminal.c b/terminal/terminal.c index c0b01c56..e1d0af8d 100644 --- a/terminal/terminal.c +++ b/terminal/terminal.c @@ -1210,7 +1210,7 @@ int main(int argc, char* argv[]) warn("setsid"); _exit(1); } - if ( ioctl(slave_fd, TIOCSCTTY) < 0 ) + if ( ioctl(slave_fd, TIOCSCTTY, 0) < 0 ) { warn("ioctl: TIOCSCTTY"); _exit(1); diff --git a/utils/getty.c b/utils/getty.c index 31424d94..064ebb30 100644 --- a/utils/getty.c +++ b/utils/getty.c @@ -71,7 +71,7 @@ int main(int argc, char* argv[]) } if ( setsid() < 0 ) err(1, "setsid"); - if ( ioctl(tty, TIOCSCTTY) < 0 ) + if ( ioctl(tty, TIOCSCTTY, 0) < 0 ) err(1, "ioctl: TIOCSCTTY"); if ( close(0) < 0 || close(1) < 0 || close(2) < 0 ) err(1, "close"); diff --git a/utils/halt.c b/utils/halt.c index 133f8c2b..291f8772 100644 --- a/utils/halt.c +++ b/utils/halt.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Jonas 'Sortie' Termansen. + * Copyright (c) 2021, 2022, 2024 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -23,6 +23,7 @@ #include #include #include +#include int main(int argc, char* argv[]) { @@ -38,11 +39,7 @@ int main(int argc, char* argv[]) if ( optind < argc ) errx(1, "extra operand: %s", argv[optind]); - pid_t init_pid = 1; - // TODO: Use a more reliable getinit() approach that also works in sshd. - if ( getenv("INIT_PID") ) - init_pid = atoll(getenv("INIT_PID")); - + pid_t init_pid = getinit(0); if ( kill(init_pid, SIGQUIT) < 0 ) err(1, "kill: %" PRIdPID, init_pid); diff --git a/utils/poweroff.c b/utils/poweroff.c index 9e26762b..560d45ef 100644 --- a/utils/poweroff.c +++ b/utils/poweroff.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Jonas 'Sortie' Termansen. + * Copyright (c) 2021, 2022, 2024 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -23,6 +23,7 @@ #include #include #include +#include int main(int argc, char* argv[]) { @@ -38,11 +39,7 @@ int main(int argc, char* argv[]) if ( optind < argc ) errx(1, "extra operand: %s", argv[optind]); - pid_t init_pid = 1; - // TODO: Use a more reliable getinit() approach that also works in sshd. - if ( getenv("INIT_PID") ) - init_pid = atoll(getenv("INIT_PID")); - + pid_t init_pid = getinit(0); if ( kill(init_pid, SIGTERM) < 0 ) err(1, "kill: %" PRIdPID, init_pid); diff --git a/utils/pty.c b/utils/pty.c index 91e210a3..1e92d98e 100644 --- a/utils/pty.c +++ b/utils/pty.c @@ -288,7 +288,7 @@ int main(int argc, char* argv[]) warn("setsid"); _exit(1); } - if ( ioctl(slave_fd, TIOCSCTTY) < 0 ) + if ( ioctl(slave_fd, TIOCSCTTY, 0) < 0 ) { warn("ioctl: TIOCSCTTY"); _exit(1); diff --git a/utils/reboot.c b/utils/reboot.c index ec374026..1bb7abf0 100644 --- a/utils/reboot.c +++ b/utils/reboot.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 Jonas 'Sortie' Termansen. + * Copyright (c) 2021, 2022, 2024 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -23,6 +23,7 @@ #include #include #include +#include int main(int argc, char* argv[]) { @@ -38,11 +39,7 @@ int main(int argc, char* argv[]) if ( optind < argc ) errx(1, "extra operand: %s", argv[optind]); - pid_t init_pid = 1; - // TODO: Use a more reliable getinit() approach that also works in sshd. - if ( getenv("INIT_PID") ) - init_pid = atoll(getenv("INIT_PID")); - + pid_t init_pid = getinit(0); if ( kill(init_pid, SIGINT) < 0 ) err(1, "kill: %" PRIdPID, init_pid);