diff options
author | Austin Foxley <austinf@cetoncorp.com> | 2009-10-17 14:32:36 -0700 |
---|---|---|
committer | Austin Foxley <austinf@cetoncorp.com> | 2009-10-17 14:32:36 -0700 |
commit | 57e8823548ad6e65d33b2153edeb18fb0edc20e6 (patch) | |
tree | 8cfc6fea89ec4e90c94b5764233ee2b2ed9cc54d | |
parent | 9a737ab7a40984cfdfffd014562a220a3736a10f (diff) |
cancellation support for a large amount of the required syscalls
Signed-off-by: Austin Foxley <austinf@cetoncorp.com>
25 files changed, 1084 insertions, 279 deletions
diff --git a/libc/inet/socketcalls.c b/libc/inet/socketcalls.c index 8bb09cabb..66dff4a30 100644 --- a/libc/inet/socketcalls.c +++ b/libc/inet/socketcalls.c @@ -33,27 +33,41 @@ extern int __socketcall(int call, unsigned long *args) attribute_hidden; #define SYS_RECVMSG 17 #endif +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ +#include <sysdep-cancel.h> +#include <pthreadP.h> +#else +#define SINGLE_THREAD_P 1 +#endif #ifdef L_accept -# ifdef __NR_accept -_syscall3(int, accept, int, call, struct sockaddr *, addr, socklen_t *,addrlen) -# elif defined(__NR_socketcall) -int accept(int s, struct sockaddr *addr, socklen_t * addrlen) +extern __typeof(accept) __libc_accept; +#ifdef __NR_accept +#define __NR___libc_accept __NR_accept +_syscall3(int, __libc_accept, int, call, struct sockaddr *, addr, socklen_t *,addrlen) +#elif defined(__NR_socketcall) +int __libc_accept(int s, struct sockaddr *addr, socklen_t * addrlen) { unsigned long args[3]; args[0] = s; args[1] = (unsigned long) addr; args[2] = (unsigned long) addrlen; - return __socketcall(SYS_ACCEPT, args); + + if (SINGLE_THREAD_P) + return __socketcall(SYS_ACCEPT, args); + +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ + int oldtype = LIBC_CANCEL_ASYNC (); + int result = __socketcall(SYS_ACCEPT, args); + LIBC_CANCEL_RESET (oldtype); + return result; +#endif + } -# endif -# ifndef __LINUXTHREADS_OLD__ -libc_hidden_def(accept) -# else +#endif +weak_alias(__libc_accept,accept) libc_hidden_weak(accept) -strong_alias(accept,__libc_accept) -# endif #endif #ifdef L_bind @@ -74,25 +88,32 @@ libc_hidden_def(bind) #endif #ifdef L_connect -# ifdef __NR_connect -_syscall3(int, connect, int, sockfd, const struct sockaddr *, saddr, socklen_t, addrlen) -# elif defined(__NR_socketcall) -int connect(int sockfd, const struct sockaddr *saddr, socklen_t addrlen) +extern __typeof(connect) __libc_connect; +#ifdef __NR_connect +#define __NR___libc_connect __NR_connect +_syscall3(int, __libc_connect, int, sockfd, const struct sockaddr *, saddr, socklen_t, addrlen) +#elif defined(__NR_socketcall) +int __libc_connect(int sockfd, const struct sockaddr *saddr, socklen_t addrlen) { unsigned long args[3]; args[0] = sockfd; args[1] = (unsigned long) saddr; args[2] = addrlen; + +if (SINGLE_THREAD_P) return __socketcall(SYS_CONNECT, args); + +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ + int oldtype = LIBC_CANCEL_ASYNC (); + int result = __socketcall(SYS_CONNECT, args); + LIBC_CANCEL_RESET (oldtype); + return result; +#endif } -# endif -# ifndef __LINUXTHREADS_OLD__ -libc_hidden_def(connect) -# else +#endif +weak_alias(__libc_connect,connect) libc_hidden_weak(connect) -strong_alias(connect,__libc_connect) -# endif #endif #ifdef L_getpeername @@ -164,12 +185,14 @@ libc_hidden_def(listen) #endif #ifdef L_recv -# ifdef __NR_recv -_syscall4(ssize_t, recv, int, sockfd, __ptr_t, buffer, size_t, len, +extern __typeof(recv) __libc_recv; +#ifdef __NR_recv +#define __NR___libc_recv __NR_recv +_syscall4(ssize_t, __libc_recv, int, sockfd, __ptr_t, buffer, size_t, len, int, flags) -# elif defined(__NR_socketcall) +#elif defined(__NR_socketcall) /* recv, recvfrom added by bir7@leland.stanford.edu */ -ssize_t recv(int sockfd, __ptr_t buffer, size_t len, int flags) +ssize_t __libc_recv(int sockfd, __ptr_t buffer, size_t len, int flags) { unsigned long args[4]; @@ -177,29 +200,36 @@ ssize_t recv(int sockfd, __ptr_t buffer, size_t len, int flags) args[1] = (unsigned long) buffer; args[2] = len; args[3] = flags; - return (__socketcall(SYS_RECV, args)); + + if (SINGLE_THREAD_P) + return (__socketcall(SYS_RECV, args)); + +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ + int oldtype = LIBC_CANCEL_ASYNC (); + int result = __socketcall(SYS_RECV, args); + LIBC_CANCEL_RESET (oldtype); + return result; +#endif } -# elif defined(__NR_recvfrom) -ssize_t recv(int sockfd, __ptr_t buffer, size_t len, int flags) +#elif defined(__NR_recvfrom) +ssize_t __libc_recv(int sockfd, __ptr_t buffer, size_t len, int flags) { return (recvfrom(sockfd, buffer, len, flags, NULL, NULL)); } -# endif -# ifndef __LINUXTHREADS_OLD__ -libc_hidden_def(recv) -# else +#endif +weak_alias(__libc_recv,recv) libc_hidden_weak(recv) -strong_alias(recv,__libc_recv) -# endif #endif #ifdef L_recvfrom -# ifdef __NR_recvfrom -_syscall6(ssize_t, recvfrom, int, sockfd, __ptr_t, buffer, size_t, len, +extern __typeof(recvfrom) __libc_recvfrom; +#ifdef __NR_recvfrom +#define __NR___libc_recvfrom __NR_recvfrom +_syscall6(ssize_t, __libc_recvfrom, int, sockfd, __ptr_t, buffer, size_t, len, int, flags, struct sockaddr *, to, socklen_t *, tolen) -# elif defined(__NR_socketcall) +#elif defined(__NR_socketcall) /* recv, recvfrom added by bir7@leland.stanford.edu */ -ssize_t recvfrom(int sockfd, __ptr_t buffer, size_t len, int flags, +ssize_t __libc_recvfrom(int sockfd, __ptr_t buffer, size_t len, int flags, struct sockaddr *to, socklen_t * tolen) { unsigned long args[6]; @@ -210,45 +240,59 @@ ssize_t recvfrom(int sockfd, __ptr_t buffer, size_t len, int flags, args[3] = flags; args[4] = (unsigned long) to; args[5] = (unsigned long) tolen; + +if (SINGLE_THREAD_P) return (__socketcall(SYS_RECVFROM, args)); + +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ + int oldtype = LIBC_CANCEL_ASYNC (); + int result = __socketcall(SYS_RECVFROM, args); + LIBC_CANCEL_RESET (oldtype); + return result; +#endif } -# endif -# ifndef __LINUXTHREADS_OLD__ -libc_hidden_def(recvfrom) -# else +#endif +weak_alias(__libc_recvfrom,recvfrom) libc_hidden_weak(recvfrom) -strong_alias(recvfrom,__libc_recvfrom) -# endif #endif #ifdef L_recvmsg -# ifdef __NR_recvmsg -_syscall3(ssize_t, recvmsg, int, sockfd, struct msghdr *, msg, int, flags) -# elif defined(__NR_socketcall) -ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags) +extern __typeof(recvmsg) __libc_recvmsg; +#ifdef __NR_recvmsg +#define __NR___libc_recvmsg __NR_recvmsg +_syscall3(ssize_t, __libc_recvmsg, int, sockfd, struct msghdr *, msg, int, flags) +#elif defined(__NR_socketcall) +ssize_t __libc_recvmsg(int sockfd, struct msghdr *msg, int flags) { unsigned long args[3]; args[0] = sockfd; args[1] = (unsigned long) msg; args[2] = flags; - return (__socketcall(SYS_RECVMSG, args)); + +if (SINGLE_THREAD_P) + return (__socketcall(SYS_RECVMSG, args)); + +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ + int oldtype = LIBC_CANCEL_ASYNC (); + int result = __socketcall(SYS_RECVMSG, args); + LIBC_CANCEL_RESET (oldtype); + return result; +#endif } -# endif -# ifndef __LINUXTHREADS_OLD__ -libc_hidden_def(recvmsg) -# else +#endif +weak_alias(__libc_recvmsg,recvmsg) libc_hidden_weak(recvmsg) -strong_alias(recvmsg,__libc_recvmsg) -# endif #endif #ifdef L_send -# ifdef __NR_send -_syscall4(ssize_t, send, int, sockfd, const void *, buffer, size_t, len, int, flags) -# elif defined(__NR_socketcall) +extern __typeof(send) __libc_send; +#ifdef __NR_send +#define __NR___libc_send __NR_send +_syscall4(ssize_t, __libc_send, int, sockfd, const void *, buffer, size_t, len, int, flags) +#elif defined(__NR_socketcall) /* send, sendto added by bir7@leland.stanford.edu */ -ssize_t send(int sockfd, const void *buffer, size_t len, int flags) +ssize_t __libc_send(int sockfd, const void *buffer, size_t len, int flags) { unsigned long args[4]; @@ -256,51 +300,65 @@ ssize_t send(int sockfd, const void *buffer, size_t len, int flags) args[1] = (unsigned long) buffer; args[2] = len; args[3] = flags; + +if (SINGLE_THREAD_P) return (__socketcall(SYS_SEND, args)); + +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ + int oldtype = LIBC_CANCEL_ASYNC (); + int result = __socketcall(SYS_SEND, args); + LIBC_CANCEL_RESET (oldtype); + return result; +#endif } -# elif defined(__NR_sendto) -ssize_t send(int sockfd, const void *buffer, size_t len, int flags) +#elif defined(__NR_sendto) +ssize_t __libc_send(int sockfd, const void *buffer, size_t len, int flags) { return (sendto(sockfd, buffer, len, flags, NULL, 0)); } -# endif -# ifndef __LINUXTHREADS_OLD__ -libc_hidden_def(send) -# else +#endif +weak_alias(__libc_send,send) libc_hidden_weak(send) -strong_alias(send,__libc_send) -# endif #endif #ifdef L_sendmsg -# ifdef __NR_sendmsg -_syscall3(ssize_t, sendmsg, int, sockfd, const struct msghdr *, msg, int, flags) -# elif defined(__NR_socketcall) -ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) +extern __typeof(sendmsg) __libc_sendmsg; +#ifdef __NR_sendmsg +#define __NR___libc_sendmsg __NR_sendmsg +_syscall3(ssize_t, __libc_sendmsg, int, sockfd, const struct msghdr *, msg, int, flags) +#elif defined(__NR_socketcall) +ssize_t __libc_sendmsg(int sockfd, const struct msghdr *msg, int flags) { unsigned long args[3]; args[0] = sockfd; args[1] = (unsigned long) msg; args[2] = flags; + +if (SINGLE_THREAD_P) return (__socketcall(SYS_SENDMSG, args)); + +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ + int oldtype = LIBC_CANCEL_ASYNC (); + int result = __socketcall(SYS_SENDMSG, args); + LIBC_CANCEL_RESET (oldtype); + return result; +#endif } -# endif -# ifndef __LINUXTHREADS_OLD__ -libc_hidden_def(sendmsg) -# else +#endif +weak_alias(__libc_sendmsg,sendmsg) libc_hidden_weak(sendmsg) -strong_alias(sendmsg,__libc_sendmsg) -# endif #endif #ifdef L_sendto -# ifdef __NR_sendto -_syscall6(ssize_t, sendto, int, sockfd, const void *, buffer, +extern __typeof(sendto) __libc_sendto; +#ifdef __NR_sendto +#define __NR___libc_sendto __NR_sendto +_syscall6(ssize_t, __libc_sendto, int, sockfd, const void *, buffer, size_t, len, int, flags, const struct sockaddr *, to, socklen_t, tolen) -# elif defined(__NR_socketcall) +#elif defined(__NR_socketcall) /* send, sendto added by bir7@leland.stanford.edu */ -ssize_t sendto(int sockfd, const void *buffer, size_t len, int flags, +ssize_t __libc_sendto(int sockfd, const void *buffer, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) { unsigned long args[6]; @@ -311,15 +369,20 @@ ssize_t sendto(int sockfd, const void *buffer, size_t len, int flags, args[3] = flags; args[4] = (unsigned long) to; args[5] = tolen; + +if (SINGLE_THREAD_P) return (__socketcall(SYS_SENDTO, args)); + +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ + int oldtype = LIBC_CANCEL_ASYNC (); + int result = __socketcall(SYS_SENDTO, args); + LIBC_CANCEL_RESET (oldtype); + return result; +#endif } -# endif -# ifndef __LINUXTHREADS_OLD__ -libc_hidden_def(sendto) -# else +#endif +weak_alias(__libc_sendto,sendto) libc_hidden_weak(sendto) -strong_alias(sendto,__libc_sendto) -# endif #endif #ifdef L_setsockopt diff --git a/libc/misc/sysvipc/msgq.c b/libc/misc/sysvipc/msgq.c index e43a9ed04..d67645a4d 100644 --- a/libc/misc/sysvipc/msgq.c +++ b/libc/misc/sysvipc/msgq.c @@ -1,6 +1,11 @@ #include <errno.h> #include <sys/msg.h> #include "ipc.h" +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ +#include "sysdep-cancel.h" +#else +#define SINGLE_THREAD_P 1 +#endif #ifdef L_msgctl @@ -43,31 +48,65 @@ struct new_msg_buf{ #ifdef L_msgrcv #ifdef __NR_msgrcv -_syscall5(int, msgrcv, int, msqid, void *, msgp, size_t, msgsz, long int, msgtyp, int, msgflg) -#else -int msgrcv (int msqid, void *msgp, size_t msgsz, - long int msgtyp, int msgflg) +#define __NR___syscall_msgrcv __NR_msgrcv +static inline _syscall5(int, __syscall_msgrcv, int, msqid, void *, msgp, + size_t, msgsz, long int, msgtyp, int, msgflg) +#endif +static inline int do_msgrcv (int msqid, void *msgp, size_t msgsz, + long int msgtyp, int msgflg) { +#ifdef __NR_msgrcv + return __syscall_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg); +#else struct new_msg_buf temp; temp.r_msgtyp = msgtyp; temp.oldmsg = msgp; return __syscall_ipc(IPCOP_msgrcv ,msqid ,msgsz ,msgflg ,&temp, 0); +#endif } +int msgrcv (int msqid, void *msgp, size_t msgsz, + long int msgtyp, int msgflg) +{ + if (SINGLE_THREAD_P) + return do_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg); +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ + int oldtype = LIBC_CANCEL_ASYNC (); + int result = do_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg); + LIBC_CANCEL_RESET (oldtype); + return result; #endif +} #endif #ifdef L_msgsnd #ifdef __NR_msgsnd -_syscall4(int, msgsnd, int, msqid, const void *, msgp, size_t, msgsz, int, msgflg) -#else +#define __NR___syscall_msgsnd __NR_msgsnd +static inline _syscall4(int, __syscall_msgsnd, int, msqid, const void *, msgp, + size_t, msgsz, int, msgflg) +#endif /* Send message to message queue. */ -int msgsnd (int msqid, const void *msgp, size_t msgsz, int msgflg) +static inline int do_msgsnd (int msqid, const void *msgp, size_t msgsz, + int msgflg) { +#ifdef __NR_msgsnd + return __syscall_msgsnd(msqid, msgp, msgsz, msgflg); +#else return __syscall_ipc(IPCOP_msgsnd, msqid, msgsz, msgflg, (void *)msgp, 0); +#endif } +int msgsnd (int msqid, const void *msgp, size_t msgsz, int msgflg) +{ + if (SINGLE_THREAD_P) + return do_msgsnd(msqid, msgp, msgsz, msgflg); +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ + int oldtype = LIBC_CANCEL_ASYNC (); + int result = do_msgsnd(msqid, msgp, msgsz, msgflg); + LIBC_CANCEL_RESET (oldtype); + return result; #endif +} #endif diff --git a/libc/signal/sigpause.c b/libc/signal/sigpause.c index e465fac7a..8c4e83e76 100644 --- a/libc/signal/sigpause.c +++ b/libc/signal/sigpause.c @@ -23,7 +23,9 @@ #define __FAVOR_BSD #include <signal.h> #include <stddef.h> /* For NULL. */ -#include <string.h> +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ +#include <sysdep-cancel.h> +#endif #include "sigset-cvt-mask.h" @@ -45,6 +47,9 @@ int __sigpause (int sig_or_mask, int is_sig) else sigset_set_old_mask (&set, sig_or_mask); + /* Note the sigpause() is a cancellation point. But since we call + sigsuspend() which itself is a cancellation point we do not have + to do anything here. */ return sigsuspend (&set); } libc_hidden_def(__sigpause) @@ -56,5 +61,18 @@ libc_hidden_def(__sigpause) the BSD version. So make this the default. */ int sigpause (int mask) { +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ + if (SINGLE_THREAD_P) + return __sigpause (mask, 0); + + int oldtype = LIBC_CANCEL_ASYNC (); + + int result = __sigpause (mask, 0); + + LIBC_CANCEL_RESET (oldtype); + + return result; +#else return __sigpause (mask, 0); +#endif } diff --git a/libc/signal/sigwait.c b/libc/signal/sigwait.c index 6bca1b1e5..917e3d7be 100644 --- a/libc/signal/sigwait.c +++ b/libc/signal/sigwait.c @@ -1,7 +1,8 @@ /* vi: set sw=4 ts=4: */ /* sigwait * - * Copyright (C) 2003 by Erik Andersen <andersen@uclibc.org> + * Copyright (C) 2006 by Steven J. Hill <sjhill@realitydiluted.com> + * Copyright (C) 2003-2005 by Erik Andersen <andersen@uclibc.org> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,19 +23,86 @@ #include <signal.h> #include <string.h> -#if defined __UCLIBC_HAS_REALTIME__ +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ +# include <sysdep-cancel.h> -int sigwait(const sigset_t *set, int *sig) +# ifdef __NR_rt_sigtimedwait + +/* Return any pending signal or wait for one for the given time. */ +static int do_sigwait(const sigset_t *set, int *sig) +{ + int ret; + +# ifdef SIGCANCEL + sigset_t tmpset; + if (set != NULL + && (__builtin_expect (__sigismember (set, SIGCANCEL), 0) +# ifdef SIGSETXID + || __builtin_expect (__sigismember (set, SIGSETXID), 0) +# endif + )) + { + /* Create a temporary mask without the bit for SIGCANCEL set. */ + // We are not copying more than we have to. + memcpy(&tmpset, set, _NSIG / 8); + __sigdelset(&tmpset, SIGCANCEL); +# ifdef SIGSETXID + __sigdelset(&tmpset, SIGSETXID); +# endif + set = &tmpset; + } +# endif + + /* XXX The size argument hopefully will have to be changed to the + real size of the user-level sigset_t. */ + INTERNAL_SYSCALL_DECL(err); + do + ret = INTERNAL_SYSCALL (rt_sigtimedwait, err, 4, set, NULL, + NULL, _NSIG / 8); + while (INTERNAL_SYSCALL_ERROR_P (ret, err) + && INTERNAL_SYSCALL_ERRNO (ret, err) == EINTR); + if (! INTERNAL_SYSCALL_ERROR_P (ret, err)) + { + *sig = ret; + ret = 0; + } +else + ret = INTERNAL_SYSCALL_ERRNO (ret, err); + + return ret; +} + +int sigwait (const sigset_t *set, int *sig) +{ + if(SINGLE_THREAD_P) + return do_sigwait(set, sig); + + int oldtype = LIBC_CANCEL_ASYNC(); + + int result = do_sigwait(set, sig); + + LIBC_CANCEL_RESET(oldtype); + + return result; +} +# else /* __NR_rt_sigtimedwait */ +# error We must have rt_sigtimedwait defined!!! +# endif +#else /* __UCLIBC_HAS_THREADS_NATIVE__ */ + +# if defined __UCLIBC_HAS_REALTIME__ + +int sigwait (const sigset_t *set, int *sig) { - int ret = sigwaitinfo(set, NULL); - if (ret != -1) { + int ret = 1; + if ((ret = sigwaitinfo(set, NULL)) != -1) { *sig = ret; return 0; } return 1; } -#else /* __UCLIBC_HAS_REALTIME__ */ +# else /* __UCLIBC_HAS_REALTIME__ */ /* variant without REALTIME extensions */ static smallint was_sig; /* obviously not thread-safe */ @@ -94,4 +162,5 @@ int sigwait (const sigset_t *set, int *sig) *sig = was_sig; return was_sig == -1 ? -1 : 0; } -#endif /* __UCLIBC_HAS_REALTIME__ */ +# endif /* __UCLIBC_HAS_REALTIME__ */ +#endif /* __UCLIBC_HAS_THREADS_NATIVE__ */ diff --git a/libc/stdlib/system.c b/libc/stdlib/system.c index 99f7970c8..aa1f83676 100644 --- a/libc/stdlib/system.c +++ b/libc/stdlib/system.c @@ -10,8 +10,15 @@ #include <unistd.h> #include <sys/wait.h> #include <stdlib.h> +#ifdef __UCLIBC_HAS_THREADS_NATIVE__ +#include <sched.h> +#include <errno.h> +#include <bits/libc-lock.h> +#include <sysdep-cancel.h> +#endif +#if !defined __UCLIBC_HAS_THREADS_NATIVE__ /* uClinux-2.0 has vfork, but Linux 2.0 doesn't */ #include <sys/syscall.h> #ifndef __NR_vfork @@ -61,4 +68,198 @@ int __libc_system(const char *command) signal(SIGCHLD, save_chld); return wait_val; } +#else +/* We have to and actually can handle cancelable system(). The big + problem: we have to kill the child process if necessary. To do + this a cleanup handler has to be registered and is has to be able + to find the PID of the child. The main problem is to reliable have + the PID when needed. It is not necessary for the parent thread to + return. It might still be in the kernel when the cancellation + request comes. Therefore we have to use the clone() calls ability + to have the kernel write the PID into the user-level variable. */ + +libc_hidden_proto(sigaction) +libc_hidden_proto(waitpid) + +#if defined __ia64__ +# define FORK() \ + INLINE_SYSCALL (clone2, 6, CLONE_PARENT_SETTID | SIGCHLD, NULL, 0, \ + &pid, NULL, NULL) +#elif defined __sparc__ +# define FORK() \ + INLINE_CLONE_SYSCALL (CLONE_PARENT_SETTID | SIGCHLD, 0, &pid, NULL, NULL) +#elif defined __s390__ +# define FORK() \ + INLINE_SYSCALL (clone, 3, 0, CLONE_PARENT_SETTID | SIGCHLD, &pid) +#else +# define FORK() \ + INLINE_SYSCALL (clone, 3, CLONE_PARENT_SETTID | SIGCHLD, 0, &pid) +#endif + +static void cancel_handler (void *arg); + +# define CLEANUP_HANDLER \ + __libc_cleanup_region_start (1, cancel_handler, &pid) + +# define CLEANUP_RESET \ + __libc_cleanup_region_end (0) + +static struct sigaction intr, quit; +static int sa_refcntr; +__libc_lock_define_initialized (static, lock); + +# define DO_LOCK() __libc_lock_lock (lock) +# define DO_UNLOCK() __libc_lock_unlock (lock) +# define INIT_LOCK() ({ __libc_lock_init (lock); sa_refcntr = 0; }) +# define ADD_REF() sa_refcntr++ +# define SUB_REF() --sa_refcntr + +/* Execute LINE as a shell command, returning its status. */ +static int +do_system (const char *line) +{ + int status, save; + pid_t pid; + struct sigaction sa; + sigset_t omask; + + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + __sigemptyset (&sa.sa_mask); + + DO_LOCK (); + if (ADD_REF () == 0) + { + if (sigaction (SIGINT, &sa, &intr) < 0) + { + SUB_REF (); + goto out; + } + if (sigaction (SIGQUIT, &sa, &quit) < 0) + { + save = errno; + SUB_REF (); + goto out_restore_sigint; + } + } + DO_UNLOCK (); + + /* We reuse the bitmap in the 'sa' structure. */ + __sigaddset (&sa.sa_mask, SIGCHLD); + save = errno; + if (sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask) < 0) + { + { + DO_LOCK (); + if (SUB_REF () == 0) + { + save = errno; + (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); + out_restore_sigint: + (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL); + __set_errno (save); + } + out: + DO_UNLOCK (); + return -1; + } + } + + CLEANUP_HANDLER; + + pid = FORK (); + if (pid == (pid_t) 0) + { + /* Child side. */ + const char *new_argv[4]; + new_argv[0] = "/bin/sh"; + new_argv[1] = "-c"; + new_argv[2] = line; + new_argv[3] = NULL; + + /* Restore the signals. */ + (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL); + (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); + (void) sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL); + INIT_LOCK (); + + /* Exec the shell. */ + (void) execve ("/bin/sh", (char *const *) new_argv, __environ); + _exit (127); + } + else if (pid < (pid_t) 0) + /* The fork failed. */ + status = -1; + else + /* Parent side. */ + { + /* Note the system() is a cancellation point. But since we call + waitpid() which itself is a cancellation point we do not + have to do anything here. */ + if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid) + status = -1; + } + + CLEANUP_RESET; + + save = errno; + DO_LOCK (); + if ((SUB_REF () == 0 + && (sigaction (SIGINT, &intr, (struct sigaction *) NULL) + | sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)) != 0) + || sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL) != 0) + { + status = -1; + } + DO_UNLOCK (); + + return status; +} + + +int +__libc_system (const char *line) +{ + if (line == NULL) + /* Check that we have a command processor available. It might + not be available after a chroot(), for example. */ + return do_system ("exit 0") == 0; + + if (SINGLE_THREAD_P) + return do_system (line); + + int oldtype = LIBC_CANCEL_ASYNC (); + + int result = do_system (line); + + LIBC_CANCEL_RESET (oldtype); + + return result; +} + + +/* The cancellation handler. */ +static void +cancel_handler (void *arg) +{ + pid_t child = *(pid_t *) arg; + + INTERNAL_SYSCALL_DECL (err); + INTERNAL_SYSCALL (kill, err, 2, child, SIGKILL); + + TEMP_FAILURE_RETRY (waitpid (child, NULL, 0)); + + DO_LOCK (); + + if (SUB_REF () == 0) + { + (void) sigaction (SIGQUIT, &quit, (struct sigaction *) NULL); + (void) sigaction (SIGINT, &intr, (struct sigaction *) NULL); + } + + DO_UNLOCK (); +} +#endif +#ifdef IS_IN_libc weak_alias(__libc_system,system) +#endif diff --git a/libc/sysdeps/linux/common/__rt_sigtimedwait.c b/libc/sysdeps/linux/common/__rt_sigtimedwait.c index f9ec0eabf..554c6b9cb 100644 --- a/libc/sysdeps/linux/common/__rt_sigtimedwait.c +++ b/libc/sysdeps/linux/common/__rt_sigtimedwait.c @@ -2,44 +2,97 @@ /* * __rt_sigtimedwait() for uClibc * - * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org> + * Copyright (C) 2006 by Steven Hill <sjhill@realitydiluted.com> + * Copyright (C) 2000-2004 by Erik Andersen <andersen@codepoet.org> * - * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball. + * GNU Library General Public License (LGPL) version 2 or later. */ #include <sys/syscall.h> #include <signal.h> -#define __need_NULL -#include <stddef.h> +#include <string.h> +libc_hidden_proto(memcpy) #ifdef __NR_rt_sigtimedwait -#define __NR___rt_sigtimedwait __NR_rt_sigtimedwait -static _syscall4(int, __rt_sigtimedwait, const sigset_t *, set, siginfo_t *, info, - const struct timespec *, timeout, size_t, setsize) +#include <string.h> +libc_hidden_proto(memcpy) -int sigwaitinfo(const sigset_t * set, siginfo_t * info) +# ifdef __UCLIBC_HAS_THREADS_NATIVE__ +# include <sysdep-cancel.h> + +static int do_sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec *timeout) { - return __rt_sigtimedwait(set, info, NULL, _NSIG / 8); +# ifdef SIGCANCEL + sigset_t tmpset; + + if (set != NULL && (__builtin_expect (__sigismember (set, SIGCANCEL), 0) +# ifdef SIGSETXID + || __builtin_expect (__sigismember (set, SIGSETXID), 0) +# endif + )) + { + /* Create a temporary mask without the bit for SIGCANCEL set. */ + // We are not copying more than we have to. + memcpy (&tmpset, set, _NSIG / 8); + __sigdelset (&tmpset, SIGCANCEL); +# ifdef SIGSETXID + __sigdelset (&tmpset, SIGSETXID); +# endif + set = &tmpset; + } +# endif + + /* XXX The size argument hopefully will have to be changed to the + real size of the user-level sigset_t. */ + int result = INLINE_SYSCALL (rt_sigtimedwait, 4, set, info, + timeout, _NSIG / 8); + + /* The kernel generates a SI_TKILL code in si_code in case tkill is + used. tkill is transparently used in raise(). Since having + SI_TKILL as a code is useful in general we fold the results + here. */ + if (result != -1 && info != NULL && info->si_code == SI_TKILL) + info->si_code = SI_USER; + + return result; } -int sigtimedwait(const sigset_t * set, siginfo_t * info, - const struct timespec *timeout) +/* Return any pending signal or wait for one for the given time. */ +int __sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec *timeout) { - return __rt_sigtimedwait(set, info, timeout, _NSIG / 8); + if(SINGLE_THREAD_P) + return do_sigtimedwait(set, info, timeout); + + int oldtype = LIBC_CANCEL_ASYNC(); + + /* XXX The size argument hopefully will have to be changed to the + real size of the user-level sigset_t. */ + int result = do_sigtimedwait(set, info, timeout); + + LIBC_CANCEL_RESET(oldtype); + + return result; } -#else -int sigwaitinfo(const sigset_t * set, siginfo_t * info) +# else +# define __need_NULL +# include <stddef.h> +# define __NR___rt_sigtimedwait __NR_rt_sigtimedwait +static _syscall4(int, __rt_sigtimedwait, const sigset_t *, set, + siginfo_t *, info, const struct timespec *, timeout, + size_t, setsize); + |