Add setpgid(2) and getpgid(2).

This commit is contained in:
Jonas 'Sortie' Termansen 2013-06-12 01:02:01 +02:00
parent 2cb3f2860a
commit be0ece3fe0
8 changed files with 247 additions and 3 deletions

View File

@ -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 \

34
libc/getpgid.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
getpgid.cpp
Get the process group of a process.
*******************************************************************************/
#include <sys/syscall.h>
#include <unistd.h>
DEFN_SYSCALL1(pid_t, sys_getpgid, SYSCALL_GETPGID, pid_t);
extern "C" int getpgid(pid_t pid)
{
return sys_getpgid(pid);
}

View File

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

34
libc/setpgid.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
setpgid.cpp
Change the process group of a process.
*******************************************************************************/
#include <sys/syscall.h>
#include <unistd.h>
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);
}

View File

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

View File

@ -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

View File

@ -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("<kernel process>")) )
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);

View File

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