2018-03-16 15:56:19 +03:00
/* vim: tabstop=4 shiftwidth=4 noexpandtab
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE . md
2018-05-01 11:12:56 +03:00
* Copyright ( C ) 2011 - 2018 K . Lange
2018-03-16 15:56:19 +03:00
* Copyright ( C ) 2012 Markus Schober
* Copyright ( C ) 2015 Dale Weiler
*
* Processes
*
* Internal format format for a process and functions to spawn
* new processes and manage the process tree .
*/
2018-03-19 05:38:11 +03:00
# include <kernel/system.h>
# include <kernel/process.h>
# include <kernel/bitset.h>
# include <kernel/logging.h>
# include <kernel/shm.h>
# include <kernel/printf.h>
2018-03-16 15:56:19 +03:00
2018-10-29 06:45:22 +03:00
# include <sys/wait.h>
2018-04-24 13:28:50 +03:00
# include <toaru/list.h>
# include <toaru/tree.h>
2018-03-16 15:56:19 +03:00
tree_t * process_tree ; /* Parent->Children tree */
list_t * process_list ; /* Flat storage */
list_t * process_queue ; /* Ready queue */
list_t * sleep_queue ;
volatile process_t * current_process = NULL ;
process_t * kernel_idle_task = NULL ;
static spin_lock_t tree_lock = { 0 } ;
static spin_lock_t process_queue_lock = { 0 } ;
static spin_lock_t wait_lock_tmp = { 0 } ;
static spin_lock_t sleep_lock = { 0 } ;
static bitset_t pid_set ;
/* Default process name string */
char * default_name = " [unnamed] " ;
int is_valid_process ( process_t * process ) {
foreach ( lnode , process_list ) {
if ( lnode - > value = = process ) {
return 1 ;
}
}
return 0 ;
}
/*
* This makes a nice 4096 - byte bitmap . It also happens
* to be pid_max on 32 - bit Linux , so that ' s kinda nice .
*/
# define MAX_PID 32768
/*
* Initialize the process tree and ready queue .
*/
void initialize_process_tree ( void ) {
process_tree = tree_create ( ) ;
process_list = list_create ( ) ;
process_queue = list_create ( ) ;
sleep_queue = list_create ( ) ;
/* Start off with enough bits for 64 processes */
bitset_init ( & pid_set , MAX_PID / 8 ) ;
/* First two bits are set by default */
bitset_set ( & pid_set , 0 ) ;
bitset_set ( & pid_set , 1 ) ;
}
/*
* Recursively print a process node to the console .
*
* @ param node Node to print .
* @ param height Current depth in the tree .
*/
void debug_print_process_tree_node ( tree_node_t * node , size_t height ) {
/* End recursion on a blank entry */
if ( ! node ) return ;
char * tmp = malloc ( 512 ) ;
memset ( tmp , 0 , 512 ) ;
char * c = tmp ;
/* Indent output */
for ( uint32_t i = 0 ; i < height ; + + i ) {
c + = sprintf ( c , " " ) ;
}
/* Get the current process */
process_t * proc = ( process_t * ) node - > value ;
/* Print the process name */
c + = sprintf ( c , " %d.%d %s " , proc - > group ? proc - > group : proc - > id , proc - > id , proc - > name ) ;
if ( proc - > description ) {
/* And, if it has one, its description */
c + = sprintf ( c , " %s " , proc - > description ) ;
}
if ( proc - > finished ) {
c + = sprintf ( c , " [zombie] " ) ;
}
/* Linefeed */
debug_print ( NOTICE , " %s " , tmp ) ;
free ( tmp ) ;
foreach ( child , node - > children ) {
/* Recursively print the children */
debug_print_process_tree_node ( child - > value , height + 1 ) ;
}
}
/*
* Print the process tree to the console .
*/
void debug_print_process_tree ( void ) {
debug_print_process_tree_node ( process_tree - > root , 0 ) ;
}
/*
* Retreive the next ready process .
* XXX : POPs from the ready queue !
*
* @ return A pointer to the next process in the queue .
*/
process_t * next_ready_process ( void ) {
if ( ! process_available ( ) ) {
return kernel_idle_task ;
}
if ( process_queue - > head - > owner ! = process_queue ) {
debug_print ( ERROR , " Erroneous process located in process queue: node 0x%x has owner 0x%x, but process_queue is 0x%x " , process_queue - > head , process_queue - > head - > owner , process_queue ) ;
process_t * proc = process_queue - > head - > value ;
debug_print ( ERROR , " PID associated with this node is %d " , proc - > id ) ;
}
node_t * np = list_dequeue ( process_queue ) ;
assert ( np & & " Ready queue is empty. " ) ;
process_t * next = np - > value ;
return next ;
}
/*
* Reinsert a process into the ready queue .
*
* @ param proc Process to reinsert
*/
void make_process_ready ( process_t * proc ) {
if ( proc - > sleep_node . owner ! = NULL ) {
if ( proc - > sleep_node . owner = = sleep_queue ) {
/* XXX can't wake from timed sleep */
if ( proc - > timed_sleep_node ) {
IRQ_OFF ;
spin_lock ( sleep_lock ) ;
list_delete ( sleep_queue , proc - > timed_sleep_node ) ;
spin_unlock ( sleep_lock ) ;
IRQ_RES ;
proc - > sleep_node . owner = NULL ;
free ( proc - > timed_sleep_node - > value ) ;
}
/* Else: I have no idea what happened. */
} else {
proc - > sleep_interrupted = 1 ;
spin_lock ( wait_lock_tmp ) ;
list_delete ( ( list_t * ) proc - > sleep_node . owner , & proc - > sleep_node ) ;
spin_unlock ( wait_lock_tmp ) ;
}
}
if ( proc - > sched_node . owner ) {
debug_print ( WARNING , " Can't make process ready without removing from owner list: %d " , proc - > id ) ;
debug_print ( WARNING , " (This is a bug) Current owner list is 0x%x (ready queue is 0x%x) " , proc - > sched_node . owner , process_queue ) ;
return ;
}
spin_lock ( process_queue_lock ) ;
list_append ( process_queue , & proc - > sched_node ) ;
spin_unlock ( process_queue_lock ) ;
}
extern void tree_remove_reparent_root ( tree_t * tree , tree_node_t * node ) ;
/*
* Delete a process from the process tree
*
* @ param proc Process to find and remove .
*/
void delete_process ( process_t * proc ) {
tree_node_t * entry = proc - > tree_entry ;
/* The process must exist in the tree, or the client is at fault */
if ( ! entry ) return ;
/* We can not remove the root, which is an error anyway */
assert ( ( entry ! = process_tree - > root ) & & " Attempted to kill init. " ) ;
if ( process_tree - > root = = entry ) {
/* We are init, don't even bother. */
return ;
}
/* Remove the entry. */
spin_lock ( tree_lock ) ;
/* Reparent everyone below me to init */
int has_children = entry - > children - > length ;
tree_remove_reparent_root ( process_tree , entry ) ;
list_delete ( process_list , list_find ( process_list , proc ) ) ;
spin_unlock ( tree_lock ) ;
if ( has_children ) {
process_t * init = process_tree - > root - > value ;
wakeup_queue ( init - > wait_queue ) ;
}
bitset_clear ( & pid_set , proc - > id ) ;
/* Uh... */
free ( proc ) ;
}
static void _kidle ( void ) {
while ( 1 ) {
IRQ_ON ;
PAUSE ;
}
}
/*
* Spawn the idle " process " .
*/
process_t * spawn_kidle ( void ) {
process_t * idle = malloc ( sizeof ( process_t ) ) ;
memset ( idle , 0x00 , sizeof ( process_t ) ) ;
idle - > id = - 1 ;
idle - > name = strdup ( " [kidle] " ) ;
idle - > is_tasklet = 1 ;
idle - > image . stack = ( uintptr_t ) malloc ( KERNEL_STACK_SIZE ) + KERNEL_STACK_SIZE ;
idle - > thread . eip = ( uintptr_t ) & _kidle ;
idle - > thread . esp = idle - > image . stack ;
idle - > thread . ebp = idle - > image . stack ;
idle - > started = 1 ;
idle - > running = 1 ;
idle - > wait_queue = list_create ( ) ;
idle - > shm_mappings = list_create ( ) ;
idle - > signal_queue = list_create ( ) ;
gettimeofday ( & idle - > start , NULL ) ;
set_process_environment ( idle , current_directory ) ;
return idle ;
}
/*
* Spawn the initial process .
*
* @ return A pointer to the new initial process entry
*/
process_t * spawn_init ( void ) {
/* We can only do this once. */
assert ( ( ! process_tree - > root ) & & " Tried to regenerate init! " ) ;
/* Allocate space for a new process */
process_t * init = malloc ( sizeof ( process_t ) ) ;
/* Set it as the root process */
tree_set_root ( process_tree , ( void * ) init ) ;
/* Set its tree entry pointer so we can keep track
* of the process ' entry in the process tree . */
init - > tree_entry = process_tree - > root ;
init - > id = 1 ; /* Init is PID 1 */
2018-10-26 14:34:15 +03:00
init - > group = 0 ; /* thread group id (real PID) */
init - > job = 1 ; /* process group id (jobs) */
init - > session = 1 ; /* session leader id */
2018-03-16 15:56:19 +03:00
init - > name = strdup ( " init " ) ; /* Um, duh. */
init - > cmdline = NULL ;
init - > user = 0 ; /* UID 0 */
init - > mask = 022 ; /* umask */
init - > status = 0 ; /* Run status */
init - > fds = malloc ( sizeof ( fd_table_t ) ) ;
init - > fds - > refs = 1 ;
init - > fds - > length = 0 ; /* Initialize the file descriptors */
init - > fds - > capacity = 4 ;
init - > fds - > entries = malloc ( sizeof ( fs_node_t * ) * init - > fds - > capacity ) ;
/* Set the working directory */
init - > wd_node = clone_fs ( fs_root ) ;
init - > wd_name = strdup ( " / " ) ;
/* Heap and stack pointers (and actuals) */
init - > image . entry = 0 ;
init - > image . heap = 0 ;
init - > image . heap_actual = 0 ;
init - > image . stack = initial_esp + 1 ;
init - > image . user_stack = 0 ;
init - > image . size = 0 ;
init - > image . shm_heap = SHM_START ; /* Yeah, a bit of a hack. */
spin_init ( init - > image . lock ) ;
/* Process is not finished */
init - > finished = 0 ;
2018-10-26 14:34:15 +03:00
init - > suspended = 0 ;
2018-03-16 15:56:19 +03:00
init - > started = 1 ;
init - > running = 1 ;
init - > wait_queue = list_create ( ) ;
init - > shm_mappings = list_create ( ) ;
init - > signal_queue = list_create ( ) ;
init - > signal_kstack = NULL ; /* None yet initialized */
init - > sched_node . prev = NULL ;
init - > sched_node . next = NULL ;
init - > sched_node . value = init ;
init - > sleep_node . prev = NULL ;
init - > sleep_node . next = NULL ;
init - > sleep_node . value = init ;
init - > timed_sleep_node = NULL ;
init - > is_tasklet = 0 ;
set_process_environment ( init , current_directory ) ;
/* What the hey, let's also set the description on this one */
init - > description = strdup ( " [init] " ) ;
list_insert ( process_list , ( void * ) init ) ;
return init ;
}
/*
* Get the next available PID
*
* @ return A usable PID for a new process .
*/
static int _next_pid = 2 ;
pid_t get_next_pid ( void ) {
if ( _next_pid > MAX_PID ) {
int index = bitset_ffub ( & pid_set ) ;
/*
* Honestly , we don ' t have the memory to really risk reaching
* the point where we have MAX_PID processes running
* concurrently , so this assertion should be " safe enough " .
*/
assert ( index ! = - 1 ) ;
bitset_set ( & pid_set , index ) ;
return index ;
}
int pid = _next_pid ;
_next_pid + + ;
assert ( ! bitset_test ( & pid_set , pid ) & & " Next PID already allocated? " ) ;
bitset_set ( & pid_set , pid ) ;
return pid ;
}
/*
* Disown a process from its parent .
*/
void process_disown ( process_t * proc ) {
assert ( process_tree - > root & & " No init, has the process tree been initialized? " ) ;
/* Find the process in the tree */
tree_node_t * entry = proc - > tree_entry ;
/* Break it of from its current parent */
spin_lock ( tree_lock ) ;
tree_break_off ( process_tree , entry ) ;
/* And insert it back elsewhere */
tree_node_insert_child_node ( process_tree , process_tree - > root , entry ) ;
spin_unlock ( tree_lock ) ;
}
/*
* Spawn a new process .
*
* @ param parent The parent process to spawn the new one off of .
* @ return A pointer to the new process .
*/
process_t * spawn_process ( volatile process_t * parent , int reuse_fds ) {
assert ( process_tree - > root & & " Attempted to spawn a process without init. " ) ;
/* Allocate a new process */
debug_print ( INFO , " process_t { " ) ;
process_t * proc = calloc ( sizeof ( process_t ) , 1 ) ;
debug_print ( INFO , " } " ) ;
proc - > id = get_next_pid ( ) ; /* Set its PID */
proc - > group = proc - > id ; /* Set the GID */
proc - > name = strdup ( parent - > name ) ; /* Use the default name */
proc - > description = NULL ; /* No description */
proc - > cmdline = parent - > cmdline ;
/* Copy permissions */
proc - > user = parent - > user ;
proc - > mask = parent - > mask ;
2018-10-26 14:34:15 +03:00
/* Until specified otherwise */
proc - > job = parent - > job ;
proc - > session = parent - > session ;
2018-03-16 15:56:19 +03:00
/* Zero out the ESP/EBP/EIP */
proc - > thread . esp = 0 ;
proc - > thread . ebp = 0 ;
proc - > thread . eip = 0 ;
proc - > thread . fpu_enabled = 0 ;
memcpy ( ( void * ) proc - > thread . fp_regs , ( void * ) parent - > thread . fp_regs , 512 ) ;
/* Set the process image information from the parent */
proc - > image . entry = parent - > image . entry ;
proc - > image . heap = parent - > image . heap ;
proc - > image . heap_actual = parent - > image . heap_actual ;
proc - > image . size = parent - > image . size ;
debug_print ( INFO , " stack { " ) ;
proc - > image . stack = ( uintptr_t ) kvmalloc ( KERNEL_STACK_SIZE ) + KERNEL_STACK_SIZE ;
debug_print ( INFO , " } " ) ;
proc - > image . user_stack = parent - > image . user_stack ;
proc - > image . shm_heap = SHM_START ; /* Yeah, a bit of a hack. */
spin_init ( proc - > image . lock ) ;
assert ( proc - > image . stack & & " Failed to allocate kernel stack for new process. " ) ;
/* Clone the file descriptors from the original process */
if ( reuse_fds ) {
proc - > fds = parent - > fds ;
proc - > fds - > refs + + ;
} else {
proc - > fds = malloc ( sizeof ( fd_table_t ) ) ;
proc - > fds - > refs = 1 ;
proc - > fds - > length = parent - > fds - > length ;
proc - > fds - > capacity = parent - > fds - > capacity ;
debug_print ( INFO , " fds / files { " ) ;
proc - > fds - > entries = malloc ( sizeof ( fs_node_t * ) * proc - > fds - > capacity ) ;
assert ( proc - > fds - > entries & & " Failed to allocate file descriptor table for new process. " ) ;
debug_print ( INFO , " --- " ) ;
for ( uint32_t i = 0 ; i < parent - > fds - > length ; + + i ) {
proc - > fds - > entries [ i ] = clone_fs ( parent - > fds - > entries [ i ] ) ;
}
debug_print ( INFO , " } " ) ;
}
/* As well as the working directory */
proc - > wd_node = clone_fs ( parent - > wd_node ) ;
proc - > wd_name = strdup ( parent - > wd_name ) ;
/* Zero out the process status */
proc - > status = 0 ;
proc - > finished = 0 ;
2018-10-26 14:34:15 +03:00
proc - > suspended = 0 ;
2018-03-16 15:56:19 +03:00
proc - > started = 0 ;
proc - > running = 0 ;
memset ( proc - > signals . functions , 0x00 , sizeof ( uintptr_t ) * NUMSIGNALS ) ;
proc - > wait_queue = list_create ( ) ;
proc - > shm_mappings = list_create ( ) ;
proc - > signal_queue = list_create ( ) ;
proc - > signal_kstack = NULL ; /* None yet initialized */
proc - > sched_node . prev = NULL ;
proc - > sched_node . next = NULL ;
proc - > sched_node . value = proc ;
proc - > sleep_node . prev = NULL ;
proc - > sleep_node . next = NULL ;
proc - > sleep_node . value = proc ;
proc - > timed_sleep_node = NULL ;
proc - > is_tasklet = 0 ;
gettimeofday ( & proc - > start , NULL ) ;
/* Insert the process into the process tree as a child
* of the parent process . */
tree_node_t * entry = tree_node_create ( proc ) ;
assert ( entry & & " Failed to allocate a process tree node for new process. " ) ;
proc - > tree_entry = entry ;
spin_lock ( tree_lock ) ;
tree_node_insert_child_node ( process_tree , parent - > tree_entry , entry ) ;
list_insert ( process_list , ( void * ) proc ) ;
spin_unlock ( tree_lock ) ;
/* Return the new process */
return proc ;
}
uint8_t process_compare ( void * proc_v , void * pid_v ) {
pid_t pid = ( * ( pid_t * ) pid_v ) ;
process_t * proc = ( process_t * ) proc_v ;
return ( uint8_t ) ( proc - > id = = pid ) ;
}
process_t * process_from_pid ( pid_t pid ) {
if ( pid < 0 ) return NULL ;
spin_lock ( tree_lock ) ;
tree_node_t * entry = tree_find ( process_tree , & pid , process_compare ) ;
spin_unlock ( tree_lock ) ;
if ( entry ) {
return ( process_t * ) entry - > value ;
}
return NULL ;
}
process_t * process_get_parent ( process_t * process ) {
process_t * result = NULL ;
spin_lock ( tree_lock ) ;
tree_node_t * entry = process - > tree_entry ;
if ( entry - > parent ) {
result = entry - > parent - > value ;
}
spin_unlock ( tree_lock ) ;
return result ;
}
/*
* Wait for children .
*
* @ param process Process doing the waiting .
* @ param pid PID to wait for
* @ param status [ out ] Where to put the status conditions of the waited - for process
* @ param options Options ( unused )
* @ return A pointer to the process that broke the wait
*/
process_t * process_wait ( process_t * process , pid_t pid , int * status , int options ) {
/* `options` is ignored */
if ( pid = = - 1 ) {
/* wait for any child process */
} else if ( pid < 0 ) {
/* wait for any porcess whose ->group == processes[abs(pid)]->group */
} else if ( pid = = 0 ) {
/* wait for any process whose ->group == process->group */
} else {
/* wait for processes[pid] */
}
return NULL ;
}
/*
* Wake up a sleeping process
*
* @ param process Process to wake up
* @ param caller Who woke it up
* @ return Don ' t know yet , but I think it should return something .
*/
int process_wake ( process_t * process , process_t * caller ) {
return 0 ;
}
/*
* Set the directory for a process .
*
* @ param proc Process to set the directory for .
* @ param directory Directory to set .
*/
void set_process_environment ( process_t * proc , page_directory_t * directory ) {
assert ( proc ) ;
assert ( directory ) ;
proc - > thread . page_directory = directory ;
}
/*
* Are there any processes available in the queue ?
* ( Queue not empty )
*
* @ return 1 if there are processes available , 0 otherwise
*/
uint8_t process_available ( void ) {
return ( process_queue - > head ! = NULL ) ;
}
/*
* Append a file descriptor to a process .
*
* @ param proc Process to append to
* @ param node The VFS node
* @ return The actual fd , for use in userspace
*/
uint32_t process_append_fd ( process_t * proc , fs_node_t * node ) {
/* Fill gaps */
for ( unsigned int i = 0 ; i < proc - > fds - > length ; + + i ) {
if ( ! proc - > fds - > entries [ i ] ) {
proc - > fds - > entries [ i ] = node ;
return i ;
}
}
/* No gaps, expand */
if ( proc - > fds - > length = = proc - > fds - > capacity ) {
proc - > fds - > capacity * = 2 ;
proc - > fds - > entries = realloc ( proc - > fds - > entries , sizeof ( fs_node_t * ) * proc - > fds - > capacity ) ;
}
proc - > fds - > entries [ proc - > fds - > length ] = node ;
proc - > fds - > length + + ;
return proc - > fds - > length - 1 ;
}
/*
* dup2 ( ) - > Move the file pointed to by ` s ( ou ) rc ( e ) ` into
* the slot pointed to be ` dest ( ination ) ` .
*
* @ param proc Process to do this for
* @ param src Source file descriptor
* @ param dest Destination file descriptor
* @ return The destination file descriptor , - 1 on failure
*/
uint32_t process_move_fd ( process_t * proc , int src , int dest ) {
if ( ( size_t ) src > proc - > fds - > length | | ( dest ! = - 1 & & ( size_t ) dest > proc - > fds - > length ) ) {
return - 1 ;
}
if ( dest = = - 1 ) {
dest = process_append_fd ( proc , NULL ) ;
}
if ( proc - > fds - > entries [ dest ] ! = proc - > fds - > entries [ src ] ) {
close_fs ( proc - > fds - > entries [ dest ] ) ;
proc - > fds - > entries [ dest ] = proc - > fds - > entries [ src ] ;
open_fs ( proc - > fds - > entries [ dest ] , 0 ) ;
}
return dest ;
}
int wakeup_queue ( list_t * queue ) {
int awoken_processes = 0 ;
while ( queue - > length > 0 ) {
spin_lock ( wait_lock_tmp ) ;
node_t * node = list_pop ( queue ) ;
spin_unlock ( wait_lock_tmp ) ;
if ( ! ( ( process_t * ) node - > value ) - > finished ) {
make_process_ready ( node - > value ) ;
}
awoken_processes + + ;
}
return awoken_processes ;
}
int wakeup_queue_interrupted ( list_t * queue ) {
int awoken_processes = 0 ;
while ( queue - > length > 0 ) {
spin_lock ( wait_lock_tmp ) ;
node_t * node = list_pop ( queue ) ;
spin_unlock ( wait_lock_tmp ) ;
if ( ! ( ( process_t * ) node - > value ) - > finished ) {
process_t * proc = node - > value ;
proc - > sleep_interrupted = 1 ;
make_process_ready ( proc ) ;
}
awoken_processes + + ;
}
return awoken_processes ;
}
int sleep_on ( list_t * queue ) {
if ( current_process - > sleep_node . owner ) {
/* uh, we can't sleep right now, we're marked as ready */
switch_task ( 0 ) ;
return 0 ;
}
current_process - > sleep_interrupted = 0 ;
spin_lock ( wait_lock_tmp ) ;
list_append ( queue , ( node_t * ) & current_process - > sleep_node ) ;
spin_unlock ( wait_lock_tmp ) ;
switch_task ( 0 ) ;
return current_process - > sleep_interrupted ;
}
int process_is_ready ( process_t * proc ) {
return ( proc - > sched_node . owner ! = NULL ) ;
}
void wakeup_sleepers ( unsigned long seconds , unsigned long subseconds ) {
IRQ_OFF ;
spin_lock ( sleep_lock ) ;
if ( sleep_queue - > length ) {
sleeper_t * proc = ( ( sleeper_t * ) sleep_queue - > head - > value ) ;
while ( proc & & ( proc - > end_tick < seconds | | ( proc - > end_tick = = seconds & & proc - > end_subtick < = subseconds ) ) ) {
if ( proc - > is_fswait ) {
proc - > is_fswait = - 1 ;
process_alert_node ( proc - > process , proc ) ;
} else {
process_t * process = proc - > process ;
process - > sleep_node . owner = NULL ;
process - > timed_sleep_node = NULL ;
if ( ! process_is_ready ( process ) ) {
make_process_ready ( process ) ;
}
}
free ( proc ) ;
free ( list_dequeue ( sleep_queue ) ) ;
if ( sleep_queue - > length ) {
proc = ( ( sleeper_t * ) sleep_queue - > head - > value ) ;
} else {
break ;
}
}
}
spin_unlock ( sleep_lock ) ;
IRQ_RES ;
}
void sleep_until ( process_t * process , unsigned long seconds , unsigned long subseconds ) {
if ( current_process - > sleep_node . owner ) {
/* Can't sleep, sleeping already */
return ;
}
process - > sleep_node . owner = sleep_queue ;
IRQ_OFF ;
spin_lock ( sleep_lock ) ;
node_t * before = NULL ;
foreach ( node , sleep_queue ) {
sleeper_t * candidate = ( ( sleeper_t * ) node - > value ) ;
if ( candidate - > end_tick > seconds | | ( candidate - > end_tick = = seconds & & candidate - > end_subtick > subseconds ) ) {
break ;
}
before = node ;
}
sleeper_t * proc = malloc ( sizeof ( sleeper_t ) ) ;
proc - > process = process ;
proc - > end_tick = seconds ;
proc - > end_subtick = subseconds ;
proc - > is_fswait = 0 ;
process - > timed_sleep_node = list_insert_after ( sleep_queue , before , proc ) ;
spin_unlock ( sleep_lock ) ;
IRQ_RES ;
}
void cleanup_process ( process_t * proc , int retval ) {
proc - > status = retval ;
proc - > finished = 1 ;
list_free ( proc - > wait_queue ) ;
free ( proc - > wait_queue ) ;
list_free ( proc - > signal_queue ) ;
free ( proc - > signal_queue ) ;
free ( proc - > wd_name ) ;
if ( proc - > node_waits ) {
list_free ( proc - > node_waits ) ;
free ( proc - > node_waits ) ;
proc - > node_waits = NULL ;
}
debug_print ( INFO , " Releasing shared memory for %d " , proc - > id ) ;
shm_release_all ( proc ) ;
free ( proc - > shm_mappings ) ;
debug_print ( INFO , " Freeing more mems %d " , proc - > id ) ;
if ( proc - > signal_kstack ) {
free ( proc - > signal_kstack ) ;
}
release_directory ( proc - > thread . page_directory ) ;
debug_print ( INFO , " Dec'ing fds for %d " , proc - > id ) ;
proc - > fds - > refs - - ;
if ( proc - > fds - > refs = = 0 ) {
debug_print ( INFO , " Reached 0, all dependencies are closed for %d's file descriptors and page directories " , proc - > id ) ;
debug_print ( INFO , " Going to clear out the file descriptors %d " , proc - > id ) ;
for ( uint32_t i = 0 ; i < proc - > fds - > length ; + + i ) {
if ( proc - > fds - > entries [ i ] ) {
close_fs ( proc - > fds - > entries [ i ] ) ;
proc - > fds - > entries [ i ] = NULL ;
}
}
debug_print ( INFO , " ... and their storage %d " , proc - > id ) ;
free ( proc - > fds - > entries ) ;
free ( proc - > fds ) ;
debug_print ( INFO , " ... and the kernel stack (hope this ain't us) %d " , proc - > id ) ;
free ( ( void * ) ( proc - > image . stack - KERNEL_STACK_SIZE ) ) ;
}
}
void reap_process ( process_t * proc ) {
debug_print ( INFO , " Reaping process %d; mem before = %d " , proc - > id , memory_use ( ) ) ;
free ( proc - > name ) ;
debug_print ( INFO , " Reaped process %d; mem after = %d " , proc - > id , memory_use ( ) ) ;
delete_process ( proc ) ;
debug_print_process_tree ( ) ;
}
static int wait_candidate ( process_t * parent , int pid , int options , process_t * proc ) {
if ( ! proc ) return 0 ;
2018-10-29 06:45:22 +03:00
if ( options & WNOKERN ) {
2018-08-12 10:36:44 +03:00
/* Skip kernel processes */
if ( proc - > is_tasklet ) return 0 ;
}
2018-03-16 15:56:19 +03:00
if ( pid < - 1 ) {
2018-10-26 14:34:15 +03:00
if ( proc - > job = = - pid | | proc - > id = = - pid ) return 1 ;
2018-03-16 15:56:19 +03:00
} else if ( pid = = 0 ) {
/* Matches our group ID */
2018-10-26 14:34:15 +03:00
if ( proc - > job = = parent - > id ) return 1 ;
2018-03-16 15:56:19 +03:00
} else if ( pid > 0 ) {
/* Specific pid */
if ( proc - > id = = pid ) return 1 ;
} else {
return 1 ;
}
return 0 ;
}
int waitpid ( int pid , int * status , int options ) {
process_t * proc = ( process_t * ) current_process ;
if ( proc - > group ) {
proc = process_from_pid ( proc - > group ) ;
}
debug_print ( INFO , " waitpid(%s%d, ..., %d) (from pid=%d.%d) " , ( pid > = 0 ) ? " " : " - " , ( pid > = 0 ) ? pid : - pid , options , current_process - > id , current_process - > group ) ;
do {
process_t * candidate = NULL ;
int has_children = 0 ;
/* First, find out if there is anyone to reap */
foreach ( node , proc - > tree_entry - > children ) {
if ( ! node - > value ) {
continue ;
}
process_t * child = ( ( tree_node_t * ) node - > value ) - > value ;
if ( wait_candidate ( proc , pid , options , child ) ) {
has_children = 1 ;
2018-10-29 06:45:22 +03:00
if ( child - > finished ) {
candidate = child ;
break ;
}
if ( ! ( options & WEXITED ) & & child - > suspended ) {
2018-03-16 15:56:19 +03:00
candidate = child ;
break ;
}
}
}
if ( ! has_children ) {
/* No valid children matching this description */
debug_print ( INFO , " No children matching description. " ) ;
return - ECHILD ;
}
if ( candidate ) {
debug_print ( INFO , " Candidate found (%x:%d), bailing early. " , candidate , candidate - > id ) ;
if ( status ) {
* status = candidate - > status ;
}
int pid = candidate - > id ;
2018-10-26 14:34:15 +03:00
if ( candidate - > finished ) {
reap_process ( candidate ) ;
}
2018-03-16 15:56:19 +03:00
return pid ;
} else {
2018-10-29 06:45:22 +03:00
if ( options & WNOHANG ) {
2018-03-16 15:56:19 +03:00
return 0 ;
}
debug_print ( INFO , " Sleeping until queue is done. " ) ;
/* Wait */
if ( sleep_on ( proc - > wait_queue ) ! = 0 ) {
debug_print ( INFO , " wait() was interrupted " ) ;
return - EINTR ;
}
}
} while ( 1 ) ;
}
int process_wait_nodes ( process_t * process , fs_node_t * nodes [ ] , int timeout ) {
assert ( ! process - > node_waits & & " Tried to wait on nodes while already waiting on nodes. " ) ;
fs_node_t * * n = nodes ;
int index = 0 ;
if ( * n ) {
do {
int result = selectcheck_fs ( * n ) ;
if ( result < 0 ) {
debug_print ( NOTICE , " An invalid descriptor was specified: %d (0x%x) (pid=%d) " , index , * n , current_process - > id ) ;
return - 1 ;
}
if ( result = = 0 ) {
return index ;
}
n + + ;
index + + ;
} while ( * n ) ;
}
if ( timeout = = 0 ) {
return - 2 ;
}
n = nodes ;
process - > node_waits = list_create ( ) ;
if ( * n ) {
do {
if ( selectwait_fs ( * n , process ) < 0 ) {
debug_print ( NOTICE , " Bad selectwait? 0x%x " , * n ) ;
}
n + + ;
} while ( * n ) ;
}
if ( timeout > 0 ) {
debug_print ( INFO , " fswait with a timeout of %d (pid=%d) " , timeout , current_process - > id ) ;
unsigned long s , ss ;
relative_time ( 0 , timeout , & s , & ss ) ;
IRQ_OFF ;
spin_lock ( sleep_lock ) ;
node_t * before = NULL ;
foreach ( node , sleep_queue ) {
sleeper_t * candidate = ( ( sleeper_t * ) node - > value ) ;
if ( candidate - > end_tick > s | | ( candidate - > end_tick = = s & & candidate - > end_subtick > ss ) ) {
break ;
}
before = node ;
}
sleeper_t * proc = malloc ( sizeof ( sleeper_t ) ) ;
proc - > process = process ;
proc - > end_tick = s ;
proc - > end_subtick = ss ;
proc - > is_fswait = 1 ;
list_insert ( ( ( process_t * ) process ) - > node_waits , proc ) ;
process - > timeout_node = list_insert_after ( sleep_queue , before , proc ) ;
spin_unlock ( sleep_lock ) ;
IRQ_RES ;
} else {
process - > timeout_node = NULL ;
}
process - > awoken_index = - 1 ;
/* Wait. */
switch_task ( 0 ) ;
return process - > awoken_index ;
}
int process_awaken_from_fswait ( process_t * process , int index ) {
process - > awoken_index = index ;
list_free ( process - > node_waits ) ;
free ( process - > node_waits ) ;
process - > node_waits = NULL ;
if ( process - > timeout_node & & process - > timeout_node - > owner = = sleep_queue ) {
sleeper_t * proc = process - > timeout_node - > value ;
if ( proc - > is_fswait ! = - 1 ) {
list_delete ( sleep_queue , process - > timeout_node ) ;
free ( process - > timeout_node - > value ) ;
free ( process - > timeout_node ) ;
}
}
process - > timeout_node = NULL ;
make_process_ready ( process ) ;
return 0 ;
}
int process_alert_node ( process_t * process , void * value ) {
if ( ! is_valid_process ( process ) ) {
debug_print ( WARNING , " Invalid process in alert from fswait. " ) ;
return 0 ;
}
if ( ! process - > node_waits ) {
return 0 ; /* Possibly already returned. Wait for another call. */
}
int index = 0 ;
foreach ( node , process - > node_waits ) {
if ( value = = node - > value ) {
return process_awaken_from_fswait ( process , index ) ;
}
index + + ;
}
return - 1 ;
}