123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645 |
- /*
- * $Id$
- *
- * Copyright (C) 2005 iptelorg GmbH
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- /*
- * tcp io wait common stuff used by tcp_main.c & tcp_read.c
- * (see io_wait.h)
- */
- /*
- * History:
- * --------
- * 2005-06-15 created by andrei
- * 2005-06-26 added kqueue (andrei)
- * 2005-07-04 added /dev/poll (andrei)
- */
- /*!
- * \file
- * \brief SIP-router core ::
- * \ingroup core
- * Module: \ref core
- */
- #ifndef NO_IO_WAIT
- #ifdef HAVE_EPOLL
- #include <unistd.h> /* close() */
- #endif
- #ifdef HAVE_DEVPOLL
- #include <sys/types.h> /* open */
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h> /* close, ioctl */
- #endif
- #include <stdlib.h> /* strtol() */
- #include "io_wait.h"
- #include "ut.h" /* get_sys_ver() */
- #include "mem/mem.h"
- #ifndef local_malloc
- #define local_malloc pkg_malloc
- #endif
- #ifndef local_free
- #define local_free pkg_free
- #endif
- char* poll_support="poll"
- #ifdef HAVE_EPOLL
- ", epoll_lt, epoll_et"
- #endif
- #ifdef HAVE_SIGIO_RT
- ", sigio_rt"
- #endif
- #ifdef HAVE_SELECT
- ", select"
- #endif
- #ifdef HAVE_KQUEUE
- ", kqueue"
- #endif
- #ifdef HAVE_DEVPOLL
- ", /dev/poll"
- #endif
- ;
- char* poll_method_str[POLL_END]={ "none", "poll", "epoll_lt", "epoll_et",
- "sigio_rt", "select", "kqueue", "/dev/poll"
- };
- int _os_ver=0; /* os version number */
- #ifdef HAVE_SIGIO_RT
- static int _sigio_init=0;
- static int _sigio_crt_rtsig;
- static sigset_t _sigio_rtsig_used;
- #endif
- #ifdef HAVE_SIGIO_RT
- /* sigio specific init
- * returns -1 on error, 0 on success */
- static int init_sigio(io_wait_h* h, int rsig)
- {
- int r;
- int n;
- int signo;
- int start_sig;
- sigset_t oldset;
-
- if (!_sigio_init){
- _sigio_init=1;
- _sigio_crt_rtsig=SIGRTMIN;
- sigemptyset(&_sigio_rtsig_used);
- }
- h->signo=0;
-
- if (rsig==0){
- start_sig=_sigio_crt_rtsig;
- n=SIGRTMAX-SIGRTMIN;
- }else{
- if ((rsig < SIGRTMIN) || (rsig >SIGRTMAX)){
- LM_CRIT("real time signal %d out of range [%d, %d]\n",
- rsig, SIGRTMIN, SIGRTMAX);
- goto error;
- }
- start_sig=rsig;
- n=0;
- }
-
- sigemptyset(&h->sset);
- sigemptyset(&oldset);
- retry1:
- /* get current block mask */
- if (sigprocmask(SIG_BLOCK, &h->sset, &oldset )==-1){
- if (errno==EINTR) goto retry1;
- LM_ERR("1st sigprocmask failed: %s [%d]\n", strerror(errno), errno);
- /* try to continue */
- }
-
- for (r=start_sig; r<=(n+start_sig); r++){
- signo=(r>SIGRTMAX)?r-SIGRTMAX+SIGRTMIN:r;
- if (! sigismember(&_sigio_rtsig_used, signo) &&
- ! sigismember(&oldset, signo)){
- sigaddset(&_sigio_rtsig_used, signo);
- h->signo=signo;
- _sigio_crt_rtsig=(signo<SIGRTMAX)?signo+1:SIGRTMIN;
- break;
- }
- }
-
- if (h->signo==0){
- LM_CRIT("%s\n", rsig?"could not assign requested real-time signal":
- "out of real-time signals");
- goto error;
- }
- DBG("init_sigio: trying signal %d... \n", h->signo);
-
- if (sigaddset(&h->sset, h->signo)==-1){
- LM_ERR("sigaddset failed for %d: %s [%d]\n",
- h->signo, strerror(errno), errno);
- goto error;
- }
- if (sigaddset(&h->sset, SIGIO)==-1){
- LM_ERR("sigaddset failed for %d: %s [%d]\n",
- SIGIO, strerror(errno), errno);
- goto error;
- }
- retry:
- if (sigprocmask(SIG_BLOCK, &h->sset, 0)==-1){
- if (errno==EINTR) goto retry;
- LM_ERR("sigprocmask failed: %s [%d]\n",
- strerror(errno), errno);
- goto error;
- }
- return 0;
- error:
- h->signo=0;
- sigemptyset(&h->sset);
- return -1;
- }
- /* sigio specific destroy */
- static void destroy_sigio(io_wait_h* h)
- {
- if (h->signo){
- sigprocmask(SIG_UNBLOCK, &h->sset, 0);
- sigemptyset(&h->sset);
- sigdelset(&_sigio_rtsig_used, h->signo);
- h->signo=0;
- }
- }
- #endif
- #ifdef HAVE_EPOLL
- /* epoll specific init
- * returns -1 on error, 0 on success */
- static int init_epoll(io_wait_h* h)
- {
- again:
- h->epfd=epoll_create(h->max_fd_no);
- if (h->epfd==-1){
- if (errno==EINTR) goto again;
- LM_ERR("epoll_create: %s [%d]\n", strerror(errno), errno);
- return -1;
- }
- return 0;
- }
- static void destroy_epoll(io_wait_h* h)
- {
- if (h->epfd!=-1){
- close(h->epfd);
- h->epfd=-1;
- }
- }
- #endif
- #ifdef HAVE_KQUEUE
- /* kqueue specific init
- * returns -1 on error, 0 on success */
- static int init_kqueue(io_wait_h* h)
- {
- again:
- h->kq_fd=kqueue();
- if (h->kq_fd==-1){
- if (errno==EINTR) goto again;
- LM_ERR("kqueue: %s [%d]\n", strerror(errno), errno);
- return -1;
- }
- return 0;
- }
- static void destroy_kqueue(io_wait_h* h)
- {
- if (h->kq_fd!=-1){
- close(h->kq_fd);
- h->kq_fd=-1;
- }
- }
- #endif
- #ifdef HAVE_DEVPOLL
- /* /dev/poll specific init
- * returns -1 on error, 0 on success */
- static int init_devpoll(io_wait_h* h)
- {
- again:
- h->dpoll_fd=open("/dev/poll", O_RDWR);
- if (h->dpoll_fd==-1){
- if (errno==EINTR) goto again;
- LM_ERR("open: %s [%d]\n", strerror(errno), errno);
- return -1;
- }
- return 0;
- }
- static void destroy_devpoll(io_wait_h* h)
- {
- if (h->dpoll_fd!=-1){
- close(h->dpoll_fd);
- h->dpoll_fd=-1;
- }
- }
- #endif
- #ifdef HAVE_SELECT
- static int init_select(io_wait_h* h)
- {
- FD_ZERO(&h->master_rset);
- FD_ZERO(&h->master_wset);
- return 0;
- }
- #endif
- /*
- * returns 0 on success, and an error message on error
- */
- char* check_poll_method(enum poll_types poll_method)
- {
- char* ret;
- ret=0;
- if (_os_ver==0)
- _os_ver=get_sys_version(0,0,0);
- switch(poll_method){
- case POLL_NONE:
- break;
- case POLL_POLL:
- /* always supported */
- break;
- case POLL_SELECT:
- /* should be always supported */
- #ifndef HAVE_SELECT
- ret="select not supported, try re-compiling with -DHAVE_SELECT";
- #endif
- break;
- case POLL_EPOLL_LT:
- case POLL_EPOLL_ET:
- #ifndef HAVE_EPOLL
- ret="epoll not supported, try re-compiling with -DHAVE_EPOLL";
- #else
- /* only on 2.6 + */
- if (_os_ver<0x020542) /* if ver < 2.5.66 */
- ret="epoll not supported on kernels < 2.6";
- #endif
- break;
- case POLL_SIGIO_RT:
- #ifndef HAVE_SIGIO_RT
- ret="sigio_rt not supported, try re-compiling with"
- " -DHAVE_SIGIO_RT";
- #else
- /* only on 2.2 + ?? */
- if (_os_ver<0x020200) /* if ver < 2.2.0 */
- ret="epoll not supported on kernels < 2.2 (?)";
- #endif
- break;
- case POLL_KQUEUE:
- #ifndef HAVE_KQUEUE
- ret="kqueue not supported, try re-compiling with -DHAVE_KQUEUE";
- #else
- /* only in FreeBSD 4.1, NETBSD 2.0, OpenBSD 2.9, Darwin, DragonFly */
- #ifdef __OS_freebsd
- /* all DragonFly versions have kqueque */
- #ifndef __OS_dragonfly
- if (_os_ver<0x0401) /* if ver < 4.1 */
- ret="kqueue not supported on FreeBSD < 4.1";
- #endif /* __OS_dragonfly */
- #elif defined (__OS_netbsd)
- if (_os_ver<0x020000) /* if ver < 2.0 */
- ret="kqueue not supported on NetBSD < 2.0";
- #elif defined (__OS_openbsd)
- if (_os_ver<0x0209) /* if ver < 2.9 ? */
- ret="kqueue not supported on OpenBSD < 2.9 (?)";
- #endif /* assume that the rest support kqueue ifdef HAVE_KQUEUE */
- #endif
- break;
- case POLL_DEVPOLL:
- #ifndef HAVE_DEVPOLL
- ret="/dev/poll not supported, try re-compiling with"
- " -DHAVE_DEVPOLL";
- #else
- /* only in Solaris >= 7.0 (?) */
- #ifdef __OS_solaris
- if (_os_ver<0x0507) /* ver < 5.7 */
- ret="/dev/poll not supported on Solaris < 7.0 (SunOS 5.7)";
- #endif
- #endif
- break;
- default:
- ret="unknown not supported method";
- }
- return ret;
- }
- enum poll_types choose_poll_method()
- {
- enum poll_types poll_method;
- if (_os_ver==0)
- _os_ver=get_sys_version(0,0,0);
- poll_method=0;
- #ifdef HAVE_EPOLL
- if (_os_ver>=0x020542) /* if ver >= 2.5.66 */
- poll_method=POLL_EPOLL_LT; /* or POLL_EPOLL_ET */
-
- #endif
- #ifdef HAVE_KQUEUE
- if (poll_method==0)
- /* only in FreeBSD 4.1, NETBSD 2.0, OpenBSD 2.9, Darwin, DragonFly */
- #ifdef __OS_freebsd
- /* all DragonFly versions have kqueque */
- #ifndef __OS_dragonfly
- if (_os_ver>=0x0401) /* if ver >= 4.1 */
- #endif /**__OS_dragonfly */
- #elif defined (__OS_netbsd)
- if (_os_ver>=0x020000) /* if ver >= 2.0 */
- #elif defined (__OS_openbsd)
- if (_os_ver>=0x0209) /* if ver >= 2.9 (?) */
- #endif /* assume that the rest support kqueue ifdef HAVE_KQUEUE */
- poll_method=POLL_KQUEUE;
- #endif
- #ifdef HAVE_DEVPOLL
- #ifdef __OS_solaris
- if (poll_method==0)
- /* only in Solaris >= 7.0 (?) */
- if (_os_ver>=0x0507) /* if ver >=SunOS 5.7 */
- poll_method=POLL_DEVPOLL;
- #endif
- #endif
- #ifdef HAVE_SIGIO_RT
- if (poll_method==0)
- if (_os_ver>=0x020200) /* if ver >= 2.2.0 */
- poll_method=POLL_SIGIO_RT;
- #endif
- if (poll_method==0) poll_method=POLL_POLL;
- return poll_method;
- }
- char* poll_method_name(enum poll_types poll_method)
- {
- if ((poll_method>=POLL_NONE) && (poll_method<POLL_END))
- return poll_method_str[poll_method];
- else
- return "invalid poll method";
- }
- /* converts a string into a poll_method
- * returns POLL_NONE (0) on error, else the corresponding poll type */
- enum poll_types get_poll_type(char* s)
- {
- int r;
- int l;
-
- l=strlen(s);
- for (r=POLL_END-1; r>POLL_NONE; r--)
- if ((strlen(poll_method_str[r])==l) &&
- (strncasecmp(poll_method_str[r], s, l)==0))
- break;
- return r;
- }
- /* initializes the static vars/arrays
- * params: h - pointer to the io_wait_h that will be initialized
- * max_fd - maximum allowed fd number
- * poll_m - poll method (0 for automatic best fit)
- */
- int init_io_wait(io_wait_h* h, int max_fd, enum poll_types poll_method)
- {
- char * poll_err;
-
- if (_os_ver==0) _os_ver=get_sys_version(0,0,0);
- memset(h, 0, sizeof(*h));
- h->max_fd_no=max_fd;
- #ifdef HAVE_EPOLL
- h->epfd=-1;
- #endif
- #ifdef HAVE_KQUEUE
- h->kq_fd=-1;
- #endif
- #ifdef HAVE_DEVPOLL
- h->dpoll_fd=-1;
- #endif
- poll_err=check_poll_method(poll_method);
-
- /* set an appropiate poll method */
- if (poll_err || (poll_method==0)){
- poll_method=choose_poll_method();
- if (poll_err){
- LM_ERR("%s, using %s instead\n",
- poll_err, poll_method_str[poll_method]);
- }else{
- LM_INFO("using %s as the io watch method (auto detected)\n",
- poll_method_str[poll_method]);
- }
- }
-
- h->poll_method=poll_method;
-
- /* common stuff, everybody has fd_hash */
- h->fd_hash=local_malloc(sizeof(*(h->fd_hash))*h->max_fd_no);
- if (h->fd_hash==0){
- LM_CRIT("could not alloc fd hashtable (%ld bytes)\n",
- (long)sizeof(*(h->fd_hash))*h->max_fd_no );
- goto error;
- }
- memset((void*)h->fd_hash, 0, sizeof(*(h->fd_hash))*h->max_fd_no);
-
- switch(poll_method){
- case POLL_POLL:
- #ifdef HAVE_SELECT
- case POLL_SELECT:
- #endif
- #ifdef HAVE_SIGIO_RT
- case POLL_SIGIO_RT:
- #endif
- #ifdef HAVE_DEVPOLL
- case POLL_DEVPOLL:
- #endif
- h->fd_array=local_malloc(sizeof(*(h->fd_array))*h->max_fd_no);
- if (h->fd_array==0){
- LM_CRIT("could not alloc fd array (%ld bytes)\n",
- (long)sizeof(*(h->fd_hash))*h->max_fd_no);
- goto error;
- }
- memset((void*)h->fd_array, 0, sizeof(*(h->fd_array))*h->max_fd_no);
- #ifdef HAVE_SIGIO_RT
- if ((poll_method==POLL_SIGIO_RT) && (init_sigio(h, 0)<0)){
- LM_CRIT("sigio init failed\n");
- goto error;
- }
- #endif
- #ifdef HAVE_DEVPOLL
- if ((poll_method==POLL_DEVPOLL) && (init_devpoll(h)<0)){
- LM_CRIT("/dev/poll init failed\n");
- goto error;
- }
- #endif
- #ifdef HAVE_SELECT
- if ((poll_method==POLL_SELECT) && (init_select(h)<0)){
- LM_CRIT("select init failed\n");
- goto error;
- }
- #endif
-
- break;
- #ifdef HAVE_EPOLL
- case POLL_EPOLL_LT:
- case POLL_EPOLL_ET:
- h->ep_array=local_malloc(sizeof(*(h->ep_array))*h->max_fd_no);
- if (h->ep_array==0){
- LM_CRIT("could not alloc epoll array\n");
- goto error;
- }
- memset((void*)h->ep_array, 0, sizeof(*(h->ep_array))*h->max_fd_no);
- if (init_epoll(h)<0){
- LM_CRIT("epoll init failed\n");
- goto error;
- }
- break;
- #endif
- #ifdef HAVE_KQUEUE
- case POLL_KQUEUE:
- h->kq_changes_size=KQ_CHANGES_ARRAY_SIZE;
- /* kevent returns different events for read & write
- => to get all the possible events in one call we
- need twice the number of added fds + space
- for possible changelist errors.
- OTOH if memory is to be saved at all costs, one can
- decrease the array size.
- */
- h->kq_array_size=2 * h->max_fd_no + h->kq_changes_size;
- h->kq_array=local_malloc(sizeof(*(h->kq_array))*h->kq_array_size);
- if (h->kq_array==0){
- LM_CRIT("could not alloc kqueue event array\n");
- goto error;
- }
- h->kq_changes=local_malloc(sizeof(*(h->kq_changes))*
- h->kq_changes_size);
- if (h->kq_changes==0){
- LM_CRIT("could not alloc kqueue changes array\n");
- goto error;
- }
- h->kq_nchanges=0;
- memset((void*)h->kq_array, 0,
- sizeof(*(h->kq_array))*h->kq_array_size);
- memset((void*)h->kq_changes, 0,
- sizeof(*(h->kq_changes))* h->kq_changes_size);
- if (init_kqueue(h)<0){
- LM_CRIT("kqueue init failed\n");
- goto error;
- }
- break;
- #endif
- default:
- LM_CRIT("unknown/unsupported poll method %s (%d)\n",
- poll_method_str[poll_method], poll_method);
- goto error;
- }
- return 0;
- error:
- return -1;
- }
- /* destroys everything init_io_wait allocated */
- void destroy_io_wait(io_wait_h* h)
- {
- switch(h->poll_method){
- #ifdef HAVE_EPOLL
- case POLL_EPOLL_LT:
- case POLL_EPOLL_ET:
- destroy_epoll(h);
- if (h->ep_array){
- local_free(h->ep_array);
- h->ep_array=0;
- }
- break;
- #endif
- #ifdef HAVE_KQUEUE
- case POLL_KQUEUE:
- destroy_kqueue(h);
- if (h->kq_array){
- local_free(h->kq_array);
- h->kq_array=0;
- }
- if (h->kq_changes){
- local_free(h->kq_changes);
- h->kq_changes=0;
- }
- break;
- #endif
- #ifdef HAVE_SIGIO_RT
- case POLL_SIGIO_RT:
- destroy_sigio(h);
- break;
- #endif
- #ifdef HAVE_DEVPOLL
- case POLL_DEVPOLL:
- destroy_devpoll(h);
- break;
- #endif
- default: /*do nothing*/
- ;
- }
- if (h->fd_array){
- local_free(h->fd_array);
- h->fd_array=0;
- }
- if (h->fd_hash){
- local_free(h->fd_hash);
- h->fd_hash=0;
- }
- }
- #endif /*ifndef NO_IO_WAIT */
|