diff --git a/libc/Makefile b/libc/Makefile index cdf88916..b684ea44 100644 --- a/libc/Makefile +++ b/libc/Makefile @@ -240,6 +240,7 @@ gethostname.o \ getlogin.o \ getlogin_r.o \ getpagesize.o \ +getpgid.o \ getpid.o \ getppid.o \ gettermmode.o \ @@ -318,6 +319,7 @@ setegid.o \ seteuid.o \ setgid.o \ setlocale.o \ +setpgid.o \ settermmode.o \ setuid.o \ sfork.o \ diff --git a/libc/getpgid.cpp b/libc/getpgid.cpp new file mode 100644 index 00000000..714c7dc5 --- /dev/null +++ b/libc/getpgid.cpp @@ -0,0 +1,34 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + getpgid.cpp + Get the process group of a process. + +*******************************************************************************/ + +#include + +#include + +DEFN_SYSCALL1(pid_t, sys_getpgid, SYSCALL_GETPGID, pid_t); + +extern "C" int getpgid(pid_t pid) +{ + return sys_getpgid(pid); +} diff --git a/libc/include/unistd.h b/libc/include/unistd.h index 43454961..d7587b37 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -272,13 +272,11 @@ long fpathconf(int, int); int getgroups(int, gid_t []); long gethostid(void); int getopt(int, char* const [], const char*); -pid_t getpgid(pid_t); pid_t getpgrp(void); pid_t getsid(pid_t); int lockf(int, int, off_t); int nice(int); int pause(void); -int setpgid(pid_t, pid_t); int setregid(gid_t, gid_t); int setreuid(uid_t, uid_t); pid_t setsid(void); @@ -331,6 +329,7 @@ uid_t geteuid(void); int gethostname(char*, size_t); char* getlogin(void); int getlogin_r(char*, size_t); +pid_t getpgid(pid_t); pid_t getpid(void); pid_t getppid(void); uid_t getuid(void); @@ -351,6 +350,7 @@ int rmdir(const char*); int setegid(gid_t); int seteuid(uid_t); int setgid(gid_t); +int setpgid(pid_t, pid_t); int setuid(uid_t); unsigned sleep(unsigned); long sysconf(int); diff --git a/libc/setpgid.cpp b/libc/setpgid.cpp new file mode 100644 index 00000000..ae863904 --- /dev/null +++ b/libc/setpgid.cpp @@ -0,0 +1,34 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + setpgid.cpp + Change the process group of a process. + +*******************************************************************************/ + +#include + +#include + +DEFN_SYSCALL2(int, sys_setpgid, SYSCALL_SETPGID, pid_t, pid_t); + +extern "C" int setpgid(pid_t pid, pid_t pgid) +{ + return sys_setpgid(pid, pgid); +} diff --git a/sortix/include/sortix/kernel/process.h b/sortix/include/sortix/kernel/process.h index e74fc5f8..6af52362 100644 --- a/sortix/include/sortix/kernel/process.h +++ b/sortix/include/sortix/kernel/process.h @@ -77,6 +77,7 @@ public: class Process { friend void Process__OnLastThreadExit(void*); +friend void Process__LastPrayerFinalize(void*); public: Process(); @@ -141,6 +142,16 @@ private: addr_t mmapfrom; int exitstatus; +public: + Process* group; + Process* groupprev; + Process* groupnext; + Process* groupfirst; + kthread_mutex_t groupchildlock; + kthread_mutex_t groupparentlock; + kthread_cond_t groupchildleft; + bool grouplimbo; + public: Thread* firstthread; kthread_mutex_t threadlock; @@ -181,10 +192,14 @@ private: CPU::InterruptRegisters* regs); void OnLastThreadExit(); void LastPrayer(); + void LastPrayerFinalize(); void NotifyChildExit(Process* child, bool zombify); void NotifyNewZombies(); void DeleteTimers(); +public: + void NotifyLeftProcessGroup(); + public: void ResetForExecute(); addr_t AllocVirtualAddr(size_t size); diff --git a/sortix/include/sortix/syscallnum.h b/sortix/include/sortix/syscallnum.h index 2442a619..dd31dfba 100644 --- a/sortix/include/sortix/syscallnum.h +++ b/sortix/include/sortix/syscallnum.h @@ -133,6 +133,8 @@ #define SYSCALL_FCHROOT 109 #define SYSCALL_FCHROOTAT 110 #define SYSCALL_MKPARTITION 111 -#define SYSCALL_MAX_NUM 112 /* index of highest constant + 1 */ +#define SYSCALL_GETPGID 112 +#define SYSCALL_SETPGID 113 +#define SYSCALL_MAX_NUM 114 /* index of highest constant + 1 */ #endif diff --git a/sortix/kernel.cpp b/sortix/kernel.cpp index eed2153e..eb2a34e2 100644 --- a/sortix/kernel.cpp +++ b/sortix/kernel.cpp @@ -383,6 +383,10 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) if ( !system ) { Panic("Could not allocate the system process"); } addr_t systemaddrspace = Memory::GetAddressSpace(); system->addrspace = systemaddrspace; + system->group = system; + system->groupprev = NULL; + system->groupnext = NULL; + system->groupfirst = system; if ( !(system->program_image_path = String::Clone("")) ) Panic("Unable to clone string for system process name"); @@ -577,6 +581,10 @@ static void BootThread(void* /*user*/) Process* init = new Process; if ( !init ) { Panic("Could not allocate init process"); } + init->group = init; + init->groupprev = NULL; + init->groupnext = NULL; + init->groupfirst = init; CurrentProcess()->AddChildProcess(init); diff --git a/sortix/process.cpp b/sortix/process.cpp index f36e9706..e161dfe5 100644 --- a/sortix/process.cpp +++ b/sortix/process.cpp @@ -120,13 +120,21 @@ Process::Process() nextsibling = NULL; firstchild = NULL; zombiechild = NULL; + group = NULL; + groupprev = NULL; + groupnext = NULL; + groupfirst = NULL; program_image_path = NULL; parentlock = KTHREAD_MUTEX_INITIALIZER; childlock = KTHREAD_MUTEX_INITIALIZER; + groupparentlock = KTHREAD_MUTEX_INITIALIZER; + groupchildlock = KTHREAD_MUTEX_INITIALIZER; + groupchildleft = KTHREAD_COND_INITIALIZER; zombiecond = KTHREAD_COND_INITIALIZER; zombiewaiting = 0; iszombie = false; nozombify = false; + grouplimbo = false; firstthread = NULL; threadlock = KTHREAD_MUTEX_INITIALIZER; ptrlock = KTHREAD_MUTEX_INITIALIZER; @@ -333,6 +341,39 @@ void Process::LastPrayer() iszombie = true; + // If this is a process group leader of a group containing other processes, + // then we have to wait until we are the only member and then terminate. + kthread_mutex_lock(&groupchildlock); + bool group_leader = group == this; + kthread_mutex_unlock(&groupchildlock); + if ( group_leader ) + { + grouplimbo = true; + NotifyLeftProcessGroup(); + return; + } + + LastPrayerFinalize(); +} + +void Process__LastPrayerFinalize(void* user) +{ + return ((Process*) user)->LastPrayerFinalize(); +} + +void Process::NotifyLeftProcessGroup() +{ + ScopedLock parentlock(&groupparentlock); + if ( !grouplimbo ) + return; + if ( groupprev || groupnext ) + return; + grouplimbo = false; + Worker::Schedule(Process__LastPrayerFinalize, this); +} + +void Process::LastPrayerFinalize() +{ bool zombify = !nozombify; // This class instance will be destroyed by our parent process when it @@ -442,6 +483,7 @@ pid_t Process::Wait(pid_t thepid, int* status, int options) return errno = EINTR, -1; } + // Remove from the list of zombies. if ( zombie->prevsibling ) zombie->prevsibling->nextsibling = zombie->nextsibling; if ( zombie->nextsibling ) @@ -451,6 +493,22 @@ pid_t Process::Wait(pid_t thepid, int* status, int options) if ( zombiechild ) zombiechild->prevsibling = NULL; + // Remove zombie from its process group. + kthread_mutex_lock(&zombie->groupchildlock); + kthread_mutex_lock(&zombie->group->groupparentlock); + assert(zombie->group); + if ( zombie->groupprev ) + zombie->groupprev->groupnext = zombie->groupnext; + else + zombie->group->groupfirst = zombie->groupnext; + if ( zombie->groupnext ) + zombie->groupnext->groupprev = zombie->groupprev; + kthread_cond_signal(&zombie->group->groupchildleft); + kthread_mutex_unlock(&zombie->group->groupparentlock); + zombie->group->NotifyLeftProcessGroup(); + zombie->group = NULL; + kthread_mutex_unlock(&zombie->groupchildlock); + thepid = zombie->pid; // It is safe to access these clocks directly as the child process is no @@ -574,6 +632,9 @@ Process* Process::Fork() { assert(CurrentProcess() == this); + // TODO: This adds the new process to the process table, but it's not ready + // and functions that access this new process will be surprised that + // it's not fully constructed and really bad things will happen. Process* clone = new Process; if ( !clone ) return NULL; @@ -610,6 +671,17 @@ Process* Process::Fork() // Remember the relation to the child process. AddChildProcess(clone); + // Add the new process to the current process group. + kthread_mutex_lock(&groupchildlock); + kthread_mutex_lock(&group->groupparentlock); + clone->group = group; + clone->groupprev = NULL; + if ( (clone->groupnext = group->groupfirst) ) + group->groupfirst->groupprev = clone; + group->groupfirst = clone; + kthread_mutex_unlock(&group->groupparentlock); + kthread_mutex_unlock(&groupchildlock); + // Initialize everything that is safe and can't fail. clone->mmapfrom = mmapfrom; @@ -921,6 +993,81 @@ pid_t sys_getppid() return CurrentProcess()->GetParentProcessId(); } +static pid_t sys_getpgid(pid_t pid) +{ + Process* process = !pid ? CurrentProcess() : Process::Get(pid); + if ( !process ) + return errno = ESRCH, -1; + + // Prevent the process group from changing while we read it. + ScopedLock childlock(&process->groupchildlock); + assert(process->group); + + return process->group->pid; +} + +static int sys_setpgid(pid_t pid, pid_t pgid) +{ + // TODO: Prevent changing the process group of zombies and other volatile + // things that are about to implode. + // TODO: Either prevent changing the process group after an exec or provide + // a version of this system call with a flags parameter that lets you + // decide if you want this behavior. This will fix a race condition + // where the shell spawns a child and both parent and child sets the + // process group, but the child sets the process group and execve's + // and the new program image exploits this 'bug' and also changes the + // process group, and then the shell gets around to change the process + // group. This probably unlikely, but correctness over all! + + // Find the processes in question. + Process* process = !pid ? CurrentProcess() : Process::Get(pid); + if ( !process ) + return errno = ESRCH, -1; + Process* group = !pgid ? process : Process::Get(pgid); + if ( !group ) + return errno = ESRCH, -1; + + // Prevent the current group from being changed while we also change it + ScopedLock childlock(&process->groupchildlock); + assert(process->group); + + // Exit early if this is a noop. + if ( process->group == group ) + return 0; + + // Prevent changing the process group of a process group leader. + if ( process->group == process ) + return errno = EPERM, -1; + + // Remove the process from its current process group. + kthread_mutex_lock(&process->group->groupparentlock); + if ( process->groupprev ) + process->groupprev->groupnext = process->groupnext; + else + process->group->groupfirst = process->groupnext; + if ( process->groupnext ) + process->groupnext->groupprev = process->groupprev; + kthread_cond_signal(&process->group->groupchildleft); + kthread_mutex_unlock(&process->group->groupparentlock); + process->group->NotifyLeftProcessGroup(); + process->group = NULL; + + // TODO: Somehow prevent joining a zombie group, or worse yet, one that is + // currently being deleted by its parent! + + // Insert the process into its new process group. + kthread_mutex_lock(&group->groupparentlock); + process->groupprev = NULL; + process->groupnext = group->groupfirst; + if ( group->groupfirst ) + group->groupfirst->groupprev = process; + group->groupfirst = process; + process->group = group; + kthread_mutex_unlock(&group->groupparentlock); + + return 0; +} + pid_t nextpidtoallocate; kthread_mutex_t pidalloclock; @@ -1055,9 +1202,11 @@ void Process::Init() Syscall::Register(SYSCALL_EXEC, (void*) sys_execve); Syscall::Register(SYSCALL_EXIT, (void*) sys_exit); Syscall::Register(SYSCALL_GET_PAGE_SIZE, (void*) sys_getpagesize); + Syscall::Register(SYSCALL_GETPGID, (void*) sys_getpgid); Syscall::Register(SYSCALL_GETPID, (void*) sys_getpid); Syscall::Register(SYSCALL_GETPPID, (void*) sys_getppid); Syscall::Register(SYSCALL_SBRK, (void*) sys_sbrk); + Syscall::Register(SYSCALL_SETPGID, (void*) sys_setpgid); Syscall::Register(SYSCALL_TFORK, (void*) sys_tfork); Syscall::Register(SYSCALL_UMASK, (void*) sys_umask); Syscall::Register(SYSCALL_WAIT, (void*) sys_waitpid);