/*! * Wait for signal * \param set Signals thread is waiting for * \param info Where to save caught signal * \return signal number if signal is caught, * -1 otherwise and appropriate error number is set */ int sys__sigwaitinfo ( void *p ) { sigset_t *set; siginfo_t *info; kthread_t *kthread; ksignal_handling_t *sh; ksiginfo_t *ksig, *next; int retval; set = *( (sigset_t **) p ); p += sizeof (sigset_t *); info = *( (siginfo_t **) p ); ASSERT_ERRNO_AND_EXIT ( set, EINVAL ); set = U2K_GET_ADR ( set, kthread_get_process (NULL) ); ASSERT_ERRNO_AND_EXIT ( set, EINVAL ); if ( info ) info = U2K_GET_ADR ( info, kthread_get_process (NULL) ); kthread = kthread_get_active (); sh = kthread_get_sigparams ( kthread ); /* first, search for such signal in pending signals */ ksig = list_get ( &sh->pending_signals, FIRST ); while ( ksig ) { next = list_get_next ( &ksig->list ); if ( sigtestset ( set, ksig->siginfo.si_signo ) ) { retval = ksig->siginfo.si_signo; if ( info ) *info = ksig->siginfo; list_remove ( &sh->pending_signals, 0, &ksig->list ); kfree ( ksig ); EXIT2 ( EXIT_SUCCESS, retval ); } ksig = next; } /* * if no pending signal found that matches given mask * suspend thread until signal is received */ kthread_suspend ( kthread, ksignal_received_signal, NULL ); kthreads_schedule (); EXIT ( EINTR ); /* if other events wake thread */ }
int sys__device_open ( void *p ) { char *dev_name; void **dev; dev_name = U2K_GET_ADR ( *( (char **) p ), kthread_get_process (NULL) ); p += sizeof (char *); dev = U2K_GET_ADR ( *( (void **) p ), kthread_get_process (NULL) ); *dev = k_device_open ( dev_name ); return *dev == NULL; }
/*! * Modify thread signal mask * \param how How to modify current must * \param set Mask to use in modification * \param oset Where to store old mask * \return 0 if successful, -1 otherwise and appropriate error number is set */ int sys__pthread_sigmask ( void *p ) { int how; sigset_t *set; sigset_t *oset; ksignal_handling_t *sh; int retval = EXIT_SUCCESS; how = *( (int *) p ); p += sizeof (int); set = *( (sigset_t **) p ); p += sizeof (sigset_t *); oset = *( (sigset_t **) p ); ASSERT_ERRNO_AND_EXIT ( set, EINVAL ); set = U2K_GET_ADR ( set, kthread_get_process (NULL) ); ASSERT_ERRNO_AND_EXIT ( set, EINVAL ); sh = kthread_get_sigparams ( NULL ); if (oset) oset = U2K_GET_ADR ( oset, kthread_get_process (NULL) ); if (oset) *oset = *sh->mask; switch ( how ) { case SIG_BLOCK: sigaddsets ( sh->mask, set ); break; case SIG_UNBLOCK: sigaddsets ( sh->mask, set ); break; case SIG_SETMASK: *sh->mask = *set; break; default: retval = EINVAL; } /* reevaluate pending signals with new mask */ if ( retval == EXIT_SUCCESS ) retval = ksignal_process_pending ( kthread_get_active () ); EXIT ( retval ); }
/*! * Modify thread signal mask * \param sig Signal number * \param act Signal handling behavior * \param oact Where to save old signal behavior * \return 0 if successful, -1 otherwise and appropriate error number is set */ int sys__sigaction ( void *p ) { int sig; sigaction_t *act; sigaction_t *oact; ksignal_handling_t *sh; sig = *( (int *) p ); p += sizeof (int); act = *( (sigaction_t **) p ); p += sizeof (sigaction_t *); oact = *( (sigaction_t **) p ); ASSERT_ERRNO_AND_EXIT ( sig > 0 && sig <= SIGMAX, EINVAL ); if ( act ) act = U2K_GET_ADR ( act, kthread_get_process (NULL) ); if ( oact ) oact = U2K_GET_ADR ( oact, kthread_get_process (NULL) ); sh = kthread_get_sigparams ( NULL ); if ( oact ) *oact = sh->act[sig]; if ( act ) { if ( !( act->sa_flags & SA_SIGINFO ) ) EXIT ( ENOTSUP ); if ( act->sa_sigaction == SIG_ERR || act->sa_sigaction == SIG_DFL || act->sa_sigaction == SIG_IGN || act->sa_sigaction == SIG_HOLD ) { EXIT ( ENOTSUP ); } sh->act[sig] = *act; sigdelset ( sh->mask, sig ); /* accept sig */ /* reevaluate pending signals with changed behavior */ ksignal_process_pending ( kthread_get_active () ); } EXIT ( EXIT_SUCCESS ); }
int sys__device_recv ( void *p ) { void *data; size_t size; int flags; kdevice_t *dev; data = U2K_GET_ADR ( *( (void **) p ), kthread_get_process (NULL) ); p += sizeof (void *); size = *( (size_t *) p ); p += sizeof (size_t); flags = *( (int *) p ); p += sizeof (int); dev = *( (void **) p ); return k_device_recv ( data, size, flags, dev ); }
/*! create/insert state for signal handler and similar */ void kthread_create_new_state ( kthread_t *kthread, void *start_func, void *param, void *stack, size_t stack_size, int save_old_state ) { ASSERT ( kthread ); kprocess_t *kproc = kthread_get_process ( kthread ); ASSERT ( kproc ); /* save old state if requested (put it at beginning of state list) */ if ( save_old_state ) { kthread_state_t *state = kmalloc ( sizeof (kthread_state_t) ); *state = kthread->state; list_prepend ( &kthread->states, state, &state->list ); } int stack_provided = FALSE; if ( stack && stack_size ) { stack_provided = TRUE; } else if ( kproc->smap ) { /* use process stack heap */ stack_size = kproc->thread_stack_size; stack = kprocess_stack_alloc ( kproc ); } else { /* use kernel heap */ if ( !stack_size ) stack_size = DEFAULT_THREAD_STACK_SIZE; stack = kmalloc ( stack_size ); } ASSERT ( stack && stack_size ); if ( stack_provided ) { kthread->state.stack = NULL; /* don't free it on exit */ kthread->state.stack_size = 0; } else { kthread->state.stack = stack; kthread->state.stack_size = stack_size; } /* reserve space for errno in user space */ stack_size -= sizeof (int); kthread->state.errno = kthread->state.stack + stack_size; arch_create_thread_context ( &kthread->state.context, start_func, param, kproc->proc->p.exit, stack, stack_size, kproc ); *kthread->state.errno = 0; kthread->state.exit_status = NULL; kthread->state.pparam = NULL; list_init ( &kthread->state.cleanup ); }
/*! Send signal to target thread */ int ksignal_queue ( kthread_t *kthread, siginfo_t *sig ) { int enqueue = FALSE; int retval = EXIT_SUCCESS; int schedule = FALSE; ksignal_handling_t *sh; sigaction_t *act; void (*func) (kthread_t *, void *), *param; kprocess_t *proc; siginfo_t *us; param_t param1, param2, param3; ASSERT ( kthread ); ASSERT ( kthread_check_kthread ( kthread ) ); ASSERT ( sig->si_signo > 0 && sig->si_signo <= SIGMAX ); if ( !kthread_is_alive ( kthread ) ) return ESRCH; sh = kthread_get_sigparams ( kthread ); /* is thread suspended and waits for this signal? */ if ( kthread_is_suspended ( kthread, (void **) &func, ¶m ) && ((void *) func) == ((void *) ksignal_received_signal) ) { /* thread is waiting for signal */ if ( !ksignal_received_signal ( kthread, sig ) ) { /* waited for this signal */ /* do not process this signal further */ return EXIT_SUCCESS; } } /* * If thread is in interruptable state and signal is not masked: * - process signal * - otherwise queue it */ if ( kthread_get_interruptable ( kthread ) && !sigtestset ( sh->mask, sig->si_signo ) ) { act = &sh->act[sig->si_signo]; if ( act->sa_flags != SA_SIGINFO ) return ENOTSUP; /* not supported without SA_SIGINFO! */ if ( act->sa_sigaction == SIG_ERR || act->sa_sigaction == SIG_DFL || act->sa_sigaction == SIG_IGN || act->sa_sigaction == SIG_HOLD ) { return ENOTSUP; /* not yet supported */ } if ( !kthread_is_ready ( kthread ) ) { /* * thread is suspended/blocked on something else * * 1. handle interruption: * a) we break suspend/wait state * (call cancel function) * b) set errno = EINTR * c) set return value = FAILURE * 2. create new thread state * a) process signal in this state */ void (*func) (kthread_t *, void *), *param; kthread_is_suspended (kthread, (void **) &func, ¶m); if ( func ) func ( kthread, param ); kthread_move_to_ready ( kthread, LAST ); kthread_set_errno ( kthread, EINTR ); kthread_set_syscall_retval ( kthread, EXIT_FAILURE ); /* thread is unsuspended, but signal * handler will be added first */ schedule = TRUE; } /* copy sig to user space */ proc = kthread_get_process ( kthread ); us = kprocess_stack_alloc ( proc ); ASSERT (us); /*if ( !us ) return ENOMEM;*/ *us = *sig; kthread_create_new_state ( kthread, act->sa_sigaction, K2U_GET_ADR ( us, proc ), NULL, HANDLER_STACK_SIZE, TRUE ); param1.p_ptr = proc; param2.p_ptr = us; param3.p_ptr = NULL; kthread_add_cleanup ( kthread, kprocess_stack_free, param1, param2, param3 ); /* mask signal in thread mask */ sigaddset ( sh->mask, sig->si_signo ); /* mask additional signals in thread mask */ sigaddsets ( sh->mask, &act->sa_mask ); } else { enqueue = TRUE; } if ( enqueue ) { ksignal_add_to_pending ( sh, sig ); retval = EAGAIN; } if ( schedule ) kthreads_schedule (); return retval; }
/*! Process event defined with sigevent_t */ int ksignal_process_event ( sigevent_t *evp, kthread_t *kthread, int code ) { int retval = EXIT_SUCCESS; kthread_t *target = kthread; siginfo_t sig; pid_t pid; void (*func) ( sigval_t ); ASSERT ( evp && kthread ); switch ( evp->sigev_notify ) { case SIGEV_WAKE_THREAD: func = evp->sigev_notify_function; func ( evp->sigev_value ); break; case SIGEV_NONE: break; case SIGEV_THREAD_ID: pid = evp->sigev_notify_thread_id; target = pid.ptr; if ( !target || !kthread_is_alive (target) || pid.id != kthread_get_id (target) ) return ESRCH; case SIGEV_SIGNAL: sig.si_signo = evp->sigev_signo; sig.si_value = evp->sigev_value; sig.si_code = code; sig.si_errno = 0; sig.si_pid.id = kthread_get_id ( kthread ); sig.si_pid.ptr = kthread; retval = ksignal_queue ( target, &sig ); break; case SIGEV_THREAD: if ( evp->sigev_notify_function ) { if ( !kthread_create ( evp->sigev_notify_function, evp->sigev_value.sival_ptr, 0, SCHED_FIFO, THREAD_DEF_PRIO, NULL, 0, kthread_get_process(kthread) ) ) retval = EINVAL; } else { retval = EINVAL; } break; default: retval = EINVAL; break; } return retval; }
/*! Callback function called when a signal is delivered to suspended thread */ static int ksignal_received_signal ( kthread_t *kthread, void *param ) { siginfo_t *sig; context_t *context; uint sysid; void *p; sigset_t *set; siginfo_t *info; int retval = EXIT_SUCCESS; ASSERT ( kthread ); /* thread waked by signal or other event? */ if ( param == NULL ) { kthread_set_errno ( kthread, EINTR ); kthread_set_syscall_retval ( kthread, EXIT_FAILURE ); return EXIT_FAILURE; /* other event interrupted thread */ } /* signal interrupted, but did thread waited for this signal? */ sig = param; /* get syscall which caused thread to be suspend */ context = kthread_get_context ( kthread ); sysid = arch_syscall_get_id ( context ); switch ( sysid ) { case SIGWAITINFO: /* sigwaitinfo */ p = arch_syscall_get_params ( context ); set = *( (sigset_t **) p ); p += sizeof (sigset_t *); info = *( (siginfo_t **) p ); p += sizeof (siginfo_t *); ASSERT ( set ); set = U2K_GET_ADR ( set, kthread_get_process (NULL) ); ASSERT ( set ); if ( info ) info = U2K_GET_ADR ( info, kthread_get_process (NULL) ); retval = EXIT_FAILURE; if ( sigtestset ( set, sig->si_signo ) ) { retval = sig->si_signo; kthread_set_syscall_retval ( kthread, retval ); if ( info ) *info = *sig; kthread_set_errno ( kthread, EXIT_SUCCESS ); /* resume with thread */ kthread_move_to_ready ( kthread, LAST ); kthreads_schedule (); return EXIT_SUCCESS; } else { /* not waiting for this signal */ return EXIT_FAILURE; } default: return EXIT_FAILURE; } }
/*! printf (or return) system information (and details) */ int sys__sysinfo ( void *p ) { char *buffer; size_t buf_size; char **param; /* last param is NULL */ char *param1; /* *param0; */ char usage[] = "Usage: sysinfo [programs|threads|memory]"; char look_console[] = "(sysinfo printed on console)"; buffer = *( (char **) p ); p += sizeof (char *); ASSERT_ERRNO_AND_EXIT ( buffer, EINVAL ); buffer = U2K_GET_ADR ( buffer, kthread_get_process (NULL) ); buf_size = *( (size_t *) p ); p += sizeof (size_t *); param = *( (char ***) p ); param = U2K_GET_ADR ( param, kthread_get_process (NULL) ); /* param0 = U2K_GET_ADR ( param[0], kthread_get_process (NULL) ); -- param0 should be "sysinfo" so actualy its not required */ if ( param[1] == NULL ) { /* only basic info defined in kernel/startup.c */ extern char system_info[]; if ( strlen ( system_info ) > buf_size ) EXIT ( ENOMEM ); strcpy ( buffer, system_info ); EXIT ( EXIT_SUCCESS ); } else { param1 = U2K_GET_ADR ( param[1], kthread_get_process (NULL) ); /* extended info is requested */ if ( strcmp ( "programs", param1 ) == 0 ) { EXIT ( k_list_programs ( buffer, buf_size ) ); /* TODO: "program prog_name" => print help_msg */ } else if ( strcmp ( "memory", param1 ) == 0 ) { k_memory_info (); if ( strlen ( look_console ) > buf_size ) EXIT ( ENOMEM ); strcpy ( buffer, look_console ); EXIT ( EXIT_SUCCESS ); /* TODO: "memory [segments|modules|***]" */ } else if ( strcmp ( "threads", param1 ) == 0 ) { kthread_info (); if ( strlen ( look_console ) > buf_size ) EXIT ( ENOMEM ); strcpy ( buffer, look_console ); EXIT ( EXIT_SUCCESS ); /* TODO: "thread id" */ } else { if ( strlen ( usage ) > buf_size ) EXIT ( ENOMEM ); strcpy ( buffer, usage ); EXIT ( ESRCH ); } } }
/*! Send signal to target thread */ int ksignal_queue ( kthread_t *kthread, siginfo_t *sig ) { int enqueue = FALSE; int retval = EXIT_SUCCESS; int schedule = FALSE; ksignal_handling_t *sh; sigaction_t *act; void (*func) (kthread_t *, void *), *param; kprocess_t *proc; siginfo_t *us; param_t param1, param2, param3; ksiginfo_t *ksig; ASSERT ( kthread ); ASSERT ( kthread_check_kthread ( kthread ) ); ASSERT ( sig->si_signo > 0 && sig->si_signo <= SIGMAX ); if ( !kthread_is_alive ( kthread ) ) return ESRCH; sh = kthread_get_sigparams ( kthread ); /* is thread suspended and waits for this signal? */ if ( kthread_is_suspended ( kthread, (void **) &func, ¶m ) ) { if ( ((void *) func) == ((void *) ksignal_received_signal) ) { /* thread is waiting for signal */ if ( !ksignal_received_signal ( kthread, sig ) ) { /* waited for this signal */ /* should continue with signal handler also? */ /* do not process this signal further */ return EXIT_SUCCESS; } } /* else { * thread is waiting for something else; * deal with this later (in next "if") * } */ } /* if signal is not masked in thread signal mask, deliver signal */ if ( !sigtestset ( sh->mask, sig->si_signo ) ) { act = &sh->act[sig->si_signo]; if ( act->sa_flags != SA_SIGINFO ) return ENOTSUP; /* not supported without SA_SIGINFO! */ if ( act->sa_sigaction == SIG_ERR || act->sa_sigaction == SIG_DFL || act->sa_sigaction == SIG_IGN || act->sa_sigaction == SIG_HOLD ) { return ENOTSUP; /* not yet supported */ } /* current implementation * - if thread is active or ready * -- save old context in list * -- create new context * - else * -- enqueue signal or cancel wait state (todo) * * on sys__exit check if its handler or thread!!! */ if ( !kthread_is_ready ( kthread ) ) { void (*func) (kthread_t *, void *), *param; if ( kthread_is_suspended ( kthread, (void **) &func, ¶m ) ) { /* * thread is suspended on something * else; interrupt it or not? * * -handle interruption (kernel part) * -interrupt it (resume) * -process signal * * to do above just move thread to * ready, set errno & retval */ if ( func ) func ( kthread, param ); kthread_move_to_ready ( kthread, LAST ); kthread_set_errno ( kthread, EINTR ); kthread_set_syscall_retval(kthread,EXIT_FAILURE); /* thread is unsuspended, but signal * handler will be added first */ schedule = TRUE; } else { /* what else? this is error */ ASSERT ( FALSE ); } } /* copy sig to user space */ proc = kthread_get_process ( kthread ); us = ffs_alloc(proc->stack_pool, sizeof (siginfo_t)); ASSERT (us); /*if ( !us ) return ENOMEM;*/ *us = *sig; kthread_create_new_state ( kthread, act->sa_sigaction, K2U_GET_ADR ( us, proc ), NULL, HANDLER_STACK_SIZE, TRUE ); param1.p_ptr = proc->stack_pool; param2.p_ptr = us; param3.p_ptr = NULL; kthread_add_cleanup ( kthread, kthread_param_free, param1, param2, param3 ); /* mask signal in thread mask */ sigaddset ( sh->mask, sig->si_signo ); /* mask additional signals in thread mask */ sigaddsets ( sh->mask, &act->sa_mask ); } else { enqueue = TRUE; } if ( enqueue ) { /* mask signal in thread mask */ sigaddset ( sh->mask, sig->si_signo ); /* add signal to list of pending signals */ ksig = kmalloc ( sizeof (ksiginfo_t) ); ksig->siginfo = *sig; list_append ( &sh->pending_signals, ksig, &ksig->list ); /* list_sort_add ( &sh->pending_signals, ksig, &ksig->list, ksignal_compare ); */ retval = EAGAIN; } if ( schedule ) kthreads_schedule (); return retval; }