From b38d0542e193512796d5d9502ac8d688a1036157 Mon Sep 17 00:00:00 2001 From: Günther Deschner Date: Fri, 1 Oct 2010 10:08:15 +0200 Subject: samba: share select wrappers. Guenther --- lib/util/config.mk | 3 +- lib/util/select.c | 210 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/util/select.h | 29 +++++++ lib/util/wscript_build | 2 +- 4 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 lib/util/select.c create mode 100644 lib/util/select.h (limited to 'lib/util') diff --git a/lib/util/config.mk b/lib/util/config.mk index 0a5d62ff86..719f5f2493 100644 --- a/lib/util/config.mk +++ b/lib/util/config.mk @@ -35,7 +35,8 @@ LIBSAMBA-UTIL_OBJ_FILES = $(addprefix $(libutilsrcdir)/, \ smb_threads.o \ params.o \ parmlist.o \ - util_id.o) + util_id.o + select.o) PUBLIC_HEADERS += $(addprefix $(libutilsrcdir)/, util.h \ dlinklist.h \ diff --git a/lib/util/select.c b/lib/util/select.c new file mode 100644 index 0000000000..61df649526 --- /dev/null +++ b/lib/util/select.c @@ -0,0 +1,210 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + Samba select/poll implementation + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "system/select.h" +#include "lib/util/select.h" + +/* This is here because it allows us to avoid a nasty race in signal handling. + We need to guarantee that when we get a signal we get out of a select immediately + but doing that involves a race condition. We can avoid the race by getting the + signal handler to write to a pipe that is in the select/poll list + + This means all Samba signal handlers should call sys_select_signal(). +*/ + +static pid_t initialised; +static int select_pipe[2]; +static volatile unsigned pipe_written, pipe_read; + +/******************************************************************* + Call this from all Samba signal handlers if you want to avoid a + nasty signal race condition. +********************************************************************/ + +void sys_select_signal(char c) +{ + int saved_errno = errno; + + if (!initialised) return; + + if (pipe_written > pipe_read+256) return; + + if (write(select_pipe[1], &c, 1) == 1) pipe_written++; + + errno = saved_errno; +} + +/******************************************************************* + Like select() but avoids the signal race using a pipe + it also guuarantees that fds on return only ever contains bits set + for file descriptors that were readable. +********************************************************************/ + +int sys_select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *tval) +{ + int ret, saved_errno; + fd_set *readfds2, readfds_buf; + + if (initialised != sys_getpid()) { + if (pipe(select_pipe) == -1) + { + DEBUG(0, ("sys_select: pipe failed (%s)\n", + strerror(errno))); + if (readfds != NULL) + FD_ZERO(readfds); + if (writefds != NULL) + FD_ZERO(writefds); + if (errorfds != NULL) + FD_ZERO(errorfds); + return -1; + } + + /* + * These next two lines seem to fix a bug with the Linux + * 2.0.x kernel (and probably other UNIXes as well) where + * the one byte read below can block even though the + * select returned that there is data in the pipe and + * the pipe_written variable was incremented. Thanks to + * HP for finding this one. JRA. + */ + + if(set_blocking(select_pipe[0],0)==-1) + smb_panic("select_pipe[0]: O_NONBLOCK failed"); + if(set_blocking(select_pipe[1],0)==-1) + smb_panic("select_pipe[1]: O_NONBLOCK failed"); + + initialised = sys_getpid(); + } + + maxfd = MAX(select_pipe[0]+1, maxfd); + + /* If readfds is NULL we need to provide our own set. */ + if (readfds) { + readfds2 = readfds; + } else { + readfds2 = &readfds_buf; + FD_ZERO(readfds2); + } + FD_SET(select_pipe[0], readfds2); + + errno = 0; + ret = select(maxfd,readfds2,writefds,errorfds,tval); + + if (ret <= 0) { + FD_ZERO(readfds2); + if (writefds) + FD_ZERO(writefds); + if (errorfds) + FD_ZERO(errorfds); + } else if (FD_ISSET(select_pipe[0], readfds2)) { + char c; + saved_errno = errno; + if (read(select_pipe[0], &c, 1) == 1) { + pipe_read++; + /* Mark Weaver pointed out a critical + fix to ensure we don't lose signals. We must always + return -1 when the select pipe is set, otherwise if another + fd is also ready (so ret == 2) then we used to eat the + byte in the pipe and lose the signal. JRA. + */ + ret = -1; +#if 0 + /* JRA - we can use this to debug the signal messaging... */ + DEBUG(0,("select got %u signal\n", (unsigned int)c)); +#endif + errno = EINTR; + } else { + FD_CLR(select_pipe[0], readfds2); + ret--; + errno = saved_errno; + } + } + + return ret; +} + +/******************************************************************* + Similar to sys_select() but catch EINTR and continue. + This is what sys_select() used to do in Samba. +********************************************************************/ + +int sys_select_intr(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *tval) +{ + int ret; + fd_set *readfds2, readfds_buf, *writefds2, writefds_buf, *errorfds2, errorfds_buf; + struct timeval tval2, *ptval; + struct timespec end_time; + + readfds2 = (readfds ? &readfds_buf : NULL); + writefds2 = (writefds ? &writefds_buf : NULL); + errorfds2 = (errorfds ? &errorfds_buf : NULL); + if (tval) { + clock_gettime_mono(&end_time); + end_time.tv_sec += tval->tv_sec; + end_time.tv_nsec += tval->tv_usec *1000; + end_time.tv_sec += end_time.tv_nsec / 1000000000; + end_time.tv_nsec %= 1000000000; + errno = 0; + tval2 = *tval; + ptval = &tval2; + } else { + ptval = NULL; + } + + do { + if (readfds) + readfds_buf = *readfds; + if (writefds) + writefds_buf = *writefds; + if (errorfds) + errorfds_buf = *errorfds; + if (ptval && (errno == EINTR)) { + struct timespec now_time; + int64_t tdif; + + clock_gettime_mono(&now_time); + tdif = nsec_time_diff(&end_time,&now_time); + if (tdif <= 0) { + ret = 0; /* time expired. */ + break; + } + ptval->tv_sec = tdif / 1000000000; + ptval->tv_usec = (tdif % 1000000000) / 1000; + } + + /* We must use select and not sys_select here. If we use + sys_select we'd lose the fact a signal occurred when sys_select + read a byte from the pipe. Fix from Mark Weaver + + */ + ret = select(maxfd, readfds2, writefds2, errorfds2, ptval); + } while (ret == -1 && errno == EINTR); + + if (readfds) + *readfds = readfds_buf; + if (writefds) + *writefds = writefds_buf; + if (errorfds) + *errorfds = errorfds_buf; + + return ret; +} diff --git a/lib/util/select.h b/lib/util/select.h new file mode 100644 index 0000000000..13c85ce0bf --- /dev/null +++ b/lib/util/select.h @@ -0,0 +1,29 @@ +/* + Unix SMB/Netbios implementation. + Samba select/poll implementation + Copyright (C) Andrew Tridgell 1992-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _select_h_ +#define _select_h_ + +/* The following definitions come from lib/util/select.c */ + +void sys_select_signal(char c); +int sys_select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *tval); +int sys_select_intr(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *tval); + +#endif diff --git a/lib/util/wscript_build b/lib/util/wscript_build index 0da6ec5b26..457dee2e19 100644 --- a/lib/util/wscript_build +++ b/lib/util/wscript_build @@ -1,7 +1,7 @@ #!/usr/bin/env python bld.SAMBA_LIBRARY('LIBSAMBA-UTIL', - source='xfile.c debug.c fault.c signal.c system.c time.c genrand.c dprintf.c util_str.c rfc1738.c substitute.c util_strlist.c util_file.c data_blob.c util.c blocking.c util_net.c fsusage.c ms_fnmatch.c mutex.c idtree.c become_daemon.c rbtree.c talloc_stack.c smb_threads.c params.c parmlist.c util_id.c', + source='xfile.c debug.c fault.c signal.c system.c time.c genrand.c dprintf.c util_str.c rfc1738.c substitute.c util_strlist.c util_file.c data_blob.c util.c blocking.c util_net.c fsusage.c ms_fnmatch.c mutex.c idtree.c become_daemon.c rbtree.c talloc_stack.c smb_threads.c params.c parmlist.c util_id.c select.c', public_deps='talloc LIBCRYPTO CHARSET execinfo UID_WRAPPER tevent', public_headers='attr.h byteorder.h data_blob.h debug.h memory.h mutex.h safe_string.h time.h talloc_stack.h xfile.h dlinklist.h util.h tevent_ntstatus.h tevent_unix.h', header_path= [ ('dlinklist.h util.h', '.'), ('*', 'util') ], -- cgit