/* Copyright (C) 2008 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by David Bartley , 2008. The GNU 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 2.1 of the License, or (at your option) any later version. The GNU 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 GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include #include #include #include #include #include #include #include static pid_t __door_private_pid, __door_unref_pid; static void door_server_create_default (door_info_t *); door_server_func_t *door_server_create_proc = door_server_create_default; static int (*thr_create_ptr) (void *, size_t, void * (*)(void *), void *, long, thread_t *); /* Arguments are passed normally with the 6th argument always the subcode. */ DECLARE_INLINE_SYSCALL (int, door, long, long, long, long, long, long subcode); int door_info (int d, door_info_t *info) { return INLINE_SYSCALL (door, 6, d, (long)info, 0, 0, 0, SYS_SUB_door_info); } int door_bind (int d) { return INLINE_SYSCALL (door, 6, d, 0, 0, 0, 0, SYS_SUB_door_bind); } int door_unbind (void) { return INLINE_SYSCALL (door, 6, 0, 0, 0, 0, 0, SYS_SUB_door_unbind); } int door_revoke (int d) { return INLINE_SYSCALL (door, 6, d, 0, 0, 0, 0, SYS_SUB_door_revoke); } int door_getparam(int d, int param, size_t *out) { return INLINE_SYSCALL (door, 6, d, param, (long)out, 0, 0, SYS_SUB_door_getparam); } int door_setparam (int d, int param, size_t val) { return INLINE_SYSCALL (door, 6, d, param, val, 0, 0, SYS_SUB_door_setparam); } int door_call (int d, door_arg_t* params) { if (SINGLE_THREAD_P) return INLINE_SYSCALL (door, 6, d, (long)params, 0, 0, 0, SYS_SUB_door_call); int oldtype = LIBC_CANCEL_ASYNC (); int res = INLINE_SYSCALL (door, 6, d, (long)params, 0, 0, 0, SYS_SUB_door_call); LIBC_CANCEL_RESET (oldtype); return res; } int door_return (char *data_ptr, size_t data_size, door_desc_t *desc_ptr, uint_t num_desc) { /* Reserve space for returned client arguments. */ stack_t stack; thr_stksegment (&stack); if (stack.ss_size > DOOR_MIN_STACKSIZE) { stack.ss_size -= DOOR_MIN_STACKSIZE; // TODO: handle stacks that grow up stack.ss_sp -= DOOR_MIN_STACKSIZE; } else { stack.ss_size = 0; } door_return_desc_t drd; drd.desc_ptr = desc_ptr; drd.desc_num = num_desc; /* __door_return never returns unless there's an error. */ while (__door_return ((long)data_ptr, data_size, (long)&drd, (long)stack.ss_sp, stack.ss_size, SYS_SUB_door_return) == 0 || errno == EINTR || errno == ERESTART) ; return -1; } door_server_func_t * door_server_create (door_server_func_t *create_proc) { while (1) { door_server_func_t *cur_proc = door_server_create_proc; door_server_func_t *old_proc = atomic_compare_and_exchange_val_acq ( &door_server_create_proc, create_proc, cur_proc); if (old_proc == cur_proc) return old_proc; } } static void * door_create_default_proc (void *arg) { pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL); door_return (NULL, 0, NULL, 0); return arg; } static int door_load_libpthread (void) { if (!thr_create_ptr) { void *libpthread = __libc_dlopen ("libpthread.so.0"); if (!libpthread) return -1; thr_create_ptr = __libc_dlsym (libpthread, "thr_create"); if (!thr_create_ptr) return -1; } return 0; } static void door_server_create_default (door_info_t *info) { if (door_load_libpthread () != 0) return; /* The default server create action is to create a server thread. We use thr_create since we want to create this as a daemon thread. */ thr_create_ptr (NULL, 0, door_create_default_proc, NULL, THR_DETACHED, NULL); } static void * door_unref_proc (void *arg) { /* We may get interrupted so loop. */ while (INLINE_SYSCALL (door, 6, 0, 0, 0, 0, 0, SYS_SUB_door_unrefsys) && errno == EINTR) ; return NULL; } int door_create (void (*server_procedure)(void *cookie, char *argp, size_t arg_size, door_desc_t *dp, uint_t n_desc), void *cookie, unsigned int attributes) { // TODO: remove if (attributes & ~(DOOR_NO_CANCEL | DOOR_REFUSE_DESC | DOOR_PRIVATE | \ DOOR_UNREF | DOOR_UNREF_MULTI)) abort (); /* We lock the io list lock as fork() locks it before forking. This allows us to be safe in the face of a fork. */ _IO_list_lock (); int res = INLINE_SYSCALL (door, 6, (long)server_procedure, (long)cookie, attributes, 0, 0, SYS_SUB_door_create); pid_t pid = getpid (); if (__door_private_pid != pid && (attributes & DOOR_PRIVATE) == 0) { /* We haven't created the first server. */ (*door_server_create_proc) (NULL); __door_private_pid = pid; } if (__door_unref_pid != pid && (attributes & (DOOR_UNREF | DOOR_UNREF_MULTI))) { if (door_load_libpthread () != 0) return -1; /* We haven't created the unreferenced thread. */ thr_create_ptr (NULL, 0, door_unref_proc, NULL, THR_DAEMON, NULL); __door_unref_pid = pid; } _IO_list_unlock (); return res; } int door_ucred (ucred_t **info) { ucred_t *uc = *info; if (!uc) { uc = _ucred_alloc (); if (!uc) return -1; } int res = INLINE_SYSCALL (door, 6, (long)uc, 0, 0, 0, 0, SYS_SUB_door_ucred); if (res != 0) { if (!*info) free (uc); return -1; } *info = uc; return 0; } int door_cred (door_cred_t *info) { ucred_t *uc = alloca (ucred_size ()); int res = INLINE_SYSCALL (door, 6, (long)uc, 0, 0, 0, 0, SYS_SUB_door_ucred); if (res != 0) return -1; info->dc_euid = ucred_geteuid (uc); info->dc_ruid = ucred_getruid (uc); info->dc_egid = ucred_getegid (uc); info->dc_rgid = ucred_getrgid (uc); info->dc_pid = ucred_getpid (uc); return 0; }