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);