diff --git a/graphics/grMain.c b/graphics/grMain.c index 9d565e3e3..2e4652975 100644 --- a/graphics/grMain.c +++ b/graphics/grMain.c @@ -457,8 +457,15 @@ grFgets(str, n, stream, name) twentySecs.tv_sec = 20; twentySecs.tv_usec = 0; + const int fd = fileno(stream); + ASSERT(fd >= 0 && fd < FD_SETSIZE, "fd>=0&&fd= FD_SETSIZE) + { + TxError("WARNING: grFgets(fd=%d) called with fd out of range 0..%d\n", fd, FD_SETSIZE-1); + return NULL; /* allowing things to continue is UB */ + } FD_ZERO(&fn); - FD_SET(fileno(stream), &fn); + FD_SET(fd, &fn); newstr = str; n--; if (n < 0) return (char *) NULL; @@ -470,13 +477,13 @@ grFgets(str, n, stream, name) int sel; f = fn; - sel = select(TX_MAX_OPEN_FILES, &f, (fd_set *) NULL, (fd_set *) NULL, &threeSec); + sel = select(fd + 1, &f, (fd_set *) NULL, (fd_set *) NULL, &threeSec); if (sel == 0) { TxError("The %s is responding slowly, or not at all.\n", name); TxError("I'll wait for 20 seconds and then give up.\n"); f = fn; - sel = select(TX_MAX_OPEN_FILES, &f, (fd_set *) NULL, + sel = select(fd + 1, &f, (fd_set *) NULL, (fd_set *) NULL, &twentySecs); if (sel == 0) { diff --git a/scripts/configure b/scripts/configure index 53e76d25d..51ca793d9 100755 --- a/scripts/configure +++ b/scripts/configure @@ -4893,6 +4893,30 @@ fi done +for ac_func in getrlimit +do : + ac_fn_c_check_func "$LINENO" "getrlimit" "ac_cv_func_getrlimit" +if test "x$ac_cv_func_getrlimit" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_GETRLIMIT 1 +_ACEOF + +fi +done + + +for ac_func in setrlimit +do : + ac_fn_c_check_func "$LINENO" "setrlimit" "ac_cv_func_setrlimit" +if test "x$ac_cv_func_setrlimit" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SETRLIMIT 1 +_ACEOF + +fi +done + + ac_fn_c_check_func "$LINENO" "vfork" "ac_cv_func_vfork" if test "x$ac_cv_func_vfork" = xyes; then : @@ -4964,6 +4988,19 @@ fi done +for ac_header in sys/resource.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "sys/resource.h" "ac_cv_header_sys_resource_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_resource_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_SYS_RESOURCE_H 1 +_ACEOF + +fi + +done + + for ac_header in sys/time.h do : ac_fn_c_check_header_mongrel "$LINENO" "sys/time.h" "ac_cv_header_sys_time_h" "$ac_includes_default" diff --git a/scripts/configure.in b/scripts/configure.in index 4f76cb730..c0be24f99 100644 --- a/scripts/configure.in +++ b/scripts/configure.in @@ -132,6 +132,12 @@ AC_HEADER_STDC dnl Need either setenv or putenv AC_CHECK_FUNCS(setenv putenv) +dnl Check for getrlimit +AC_CHECK_FUNCS(getrlimit) + +dnl Check for setrlimit +AC_CHECK_FUNCS(setrlimit) + dnl Check for vfork AC_CHECK_FUNC(vfork) @@ -150,6 +156,9 @@ AC_CHECK_HEADERS(param.h) dnl Check for AC_CHECK_HEADERS(paths.h) +dnl Check for +AC_CHECK_HEADERS(sys/resource.h) + dnl Check for AC_CHECK_HEADERS(sys/time.h) diff --git a/sim/SimRsim.c b/sim/SimRsim.c index 5088e8c33..9acbe75d7 100644 --- a/sim/SimRsim.c +++ b/sim/SimRsim.c @@ -650,6 +650,13 @@ SimFillBuffer(buffHead, pLastChar, charCount) FD_ZERO(&exceptfds); #endif /* SYSV */ + ASSERT(pipeIn >= 0 && pipeIn < FD_SETSIZE, "pipeIn>=0&&pipeIn= FD_SETSIZE) + { + TxError("WARNING: SimFillBuffer(fd=%d) called with fd out of range 0..%d\n", pipeIn, FD_SETSIZE-1); + return -1; /* allowing things to continue is UB */ + } + nfd = pipeIn + 1; try_again: diff --git a/tcltk/tclmagic.c b/tcltk/tclmagic.c index a477bd4ae..a616a4005 100644 --- a/tcltk/tclmagic.c +++ b/tcltk/tclmagic.c @@ -25,6 +25,9 @@ #include #include #include +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif #include "tcltk/tclmagic.h" #include "utils/main.h" @@ -530,6 +533,61 @@ MakeWindowCommand(char *wname, MagWindow *mw) freeMagic(tclcmdstr); } +#ifdef HAVE_SETRLIMIT +static int +process_rlimit_nofile_ensure(rlim_t nofile) +{ + struct rlimit rlim; + int err = getrlimit(RLIMIT_NOFILE, &rlim); + if (err < 0) + return err; + rlim_t rlim_cur = rlim.rlim_cur; + /* nofile != RLIM_INFINITY && rlim.rlim_max != RLIM_INFINITY */ + if (nofile > rlim.rlim_max && nofile != rlim.rlim_max) + return -1; + if (rlim.rlim_cur < nofile || nofile == RLIM_INFINITY) + { + rlim.rlim_cur = nofile; + err = setrlimit(RLIMIT_NOFILE, &rlim); + } + if (err != 0) + TxPrintf("WARNING: process_rlimit_nofile_ensure(%lu) = %d (%d) [rlim_cur=%lu rlim_max=%lu]\n", nofile, err, errno, rlim_cur, rlim.rlim_max); + return err; +} +#endif /* HAVE_SETRLIMIT */ + +/* this function encapsulates the default policy on startup */ +static int +process_rlimit_startup_check(void) +{ +#ifdef HAVE_GETRLIMIT + #if TCL_MAJOR_VERSION < 9 + /* TCL8 has select() support and no support for poll/epoll for the main event loop */ + struct rlimit rlim; + int err = getrlimit(RLIMIT_NOFILE, &rlim); + if (err < 0) + return err; + if (rlim.rlim_cur > FD_SETSIZE) + { + TxPrintf("WARNING: RLIMIT_NOFILE is above %d and Tcl_Version<9 this may cause runtime issues [rlim_cur=%lu]\n", FD_SETSIZE, rlim.rlim_cur); + return -1; + } + return 0; + #else + #ifdef HAVE_SETRLIMIT + /* TCL9 has poll/epoll support for the main event loop, + * ifdef due to rlim_t type availbility + */ + return process_rlimit_nofile_ensure(4096); + #else + return -1; + #endif /* HAVE_SETRLIMIT */ + #endif /* TCL_MAJOR_VERSION < 9 */ +#else + return -1; +#endif /* HAVE_GETRLIMIT */ +} + /*------------------------------------------------------*/ /* Main startup procedure */ /*------------------------------------------------------*/ @@ -588,6 +646,8 @@ _magic_initialize(ClientData clientData, TxPrintf("Using the terminal as the console.\n"); TxFlushOut(); + process_rlimit_startup_check(); + if (mainInitAfterArgs() != 0) goto magicfatal; /* Registration of commands is performed after calling the */ diff --git a/textio/textio.h b/textio/textio.h index c749ea7e7..2dd500466 100644 --- a/textio/textio.h +++ b/textio/textio.h @@ -110,6 +110,4 @@ extern void TxInit(void); extern void TxInitReadline(void); #endif -#define TX_MAX_OPEN_FILES 20 - #endif /* _TEXTIO_H */ diff --git a/textio/txCommands.c b/textio/txCommands.c index b002f04e2..5e05a5f33 100644 --- a/textio/txCommands.c +++ b/textio/txCommands.c @@ -64,11 +64,17 @@ bool TxDebug = FALSE; /* A mask of the file descriptors for all input devices. */ static fd_set txInputDescriptors; +/* Used for the 'nfds' field for select() syscall, the highest + * fd number that is set in the txInputDescriptors bitmask. + * Don't forget to plus 1 for select(). + */ +static int txInputDescriptors_nfds; +#define TX_MAX_INPUT_DEVICES 20 /* A table of all input devices. */ -static txInputDevRec txInputDevice[TX_MAX_OPEN_FILES]; -static int txLastInputEntry = -1; +static txInputDevRec txInputDevice[TX_MAX_INPUT_DEVICES]; +static int txLastInputEntry; /* The current point -- reset by the 'setpoint' command and for each * interactive command. Calls to TxClearPoint clear previous setpoints, @@ -130,6 +136,9 @@ static TxCommand *lisp_cur_cmd = NULL; * * FD_IsZero -- * FD_OrSet -- + * FD_MaxFd -- + * Returns the highest 'fd' in the mask for select(2) nfds argument, or + * -1 when 'fdmask' is empty. * * Routines for manipulating set of file descriptors. * @@ -140,9 +149,9 @@ bool FD_IsZero( const fd_set *fdmask) { - int i; - for (i = 0; i <= TX_MAX_OPEN_FILES; i++) - if (FD_ISSET(i, fdmask)) return FALSE; + int fd; + for (fd = 0; fd < FD_SETSIZE; fd++) + if (FD_ISSET(fd, fdmask)) return FALSE; return TRUE; } @@ -151,9 +160,30 @@ FD_OrSet( const fd_set *fdmask, fd_set *dst) { - int i; - for (i = 0; i <= TX_MAX_OPEN_FILES; i++) - if (FD_ISSET(i, fdmask)) FD_SET(i, dst); + int fd; + for (fd = 0; fd < FD_SETSIZE; fd++) + if (FD_ISSET(fd, fdmask)) FD_SET(fd, dst); +} + +/* A bitmask find max bit set operation */ +int +FD_MaxFd(fdmask) + const fd_set *fdmask; +{ + int fd; + for (fd = FD_SETSIZE-1; fd >= 0; fd--) /* backwards */ + if (FD_ISSET(fd, fdmask)) return fd; + return -1; +} + +/* update txInputDescriptors_nfds with the correct value + * call this everytime txInputDescriptors is modified + */ +static void +TxInputDescriptorsRecalc(void) +{ + int nfds = FD_MaxFd(&txInputDescriptors); + txInputDescriptors_nfds = (nfds >= 0) ? nfds : 0; } /* @@ -468,18 +498,20 @@ TxAddInputDevice( * it is called. */ { - int i; TxDeleteInputDevice(fdmask); - if (txLastInputEntry + 1 == TX_MAX_OPEN_FILES) + if (txLastInputEntry == TX_MAX_INPUT_DEVICES) { TxError("Too many input devices.\n"); return; } - txLastInputEntry++; + txInputDevice[txLastInputEntry].tx_fdmask = *fdmask; txInputDevice[txLastInputEntry].tx_inputProc = inputProc; txInputDevice[txLastInputEntry].tx_cdata = cdata; + txLastInputEntry++; + FD_OrSet(fdmask, &txInputDescriptors); + TxInputDescriptorsRecalc(); } void @@ -489,6 +521,12 @@ TxAdd1InputDevice( ClientData cdata) { fd_set fs; + ASSERT(fd >= 0 && fd < FD_SETSIZE, "fd>=0&&fd= FD_SETSIZE) + { + TxError("WARNING: TxAdd1InputDevice(fd=%d) called with fd out of range 0..%d\n", fd, FD_SETSIZE-1); + return; /* allowing things to continue is UB */ + } FD_ZERO(&fs); FD_SET(fd, &fs); TxAddInputDevice(&fs, inputProc, cdata); @@ -514,29 +552,46 @@ TxDeleteInputDevice( * no longer active. */ { - int i; - - for (i = 0; i <= TX_MAX_OPEN_FILES; i++) - if (FD_ISSET(i, fdmask)) TxDelete1InputDevice(i); + int fd; + for (fd = 0; fd < FD_SETSIZE; fd++) + if (FD_ISSET(fd, fdmask)) TxDelete1InputDevice(fd); } void TxDelete1InputDevice( int fd) { - int i, j; + ASSERT(fd >= 0 && fd < FD_SETSIZE, "fd>=0&&fd= FD_SETSIZE) + { + TxError("WARNING: TxDelete1InputDevice(fd=%d) called with fd out of range 0..%d\n", fd, FD_SETSIZE-1); + return; /* allowing things to continue is UB */ + } - for (i = 0; i <= txLastInputEntry; i++) +restart: { - FD_CLR(fd, &(txInputDevice[i].tx_fdmask)); - if (FD_IsZero(&txInputDevice[i].tx_fdmask)) + int i; + for (i = 0; i < txLastInputEntry; i++) { - for (j = i+1; j <= txLastInputEntry; j++) - txInputDevice[j-1] = txInputDevice[j]; - txLastInputEntry--; + FD_CLR(fd, &txInputDevice[i].tx_fdmask); + if (FD_IsZero(&txInputDevice[i].tx_fdmask)) + { + int j; + for (j = i+1; j < txLastInputEntry; j++) + txInputDevice[j-1] = txInputDevice[j]; + txLastInputEntry--; + /* we shuffled entries down one, so txInputDevice[i] now + * looks at potential next one to inspect, but we're about + * to i++ (if we continue) and that will incorrectly skip + * inspection of it. + * lets just start over + */ + goto restart; + } } } FD_CLR(fd, &txInputDescriptors); + TxInputDescriptorsRecalc(); } @@ -956,7 +1011,7 @@ TxGetInputEvent( { if (returnOnSigWinch && SigGotSigWinch) return gotSome; inputs = txInputDescriptors; - numReady = select(TX_MAX_OPEN_FILES, &inputs, (fd_set *)NULL, + numReady = select(txInputDescriptors_nfds + 1, &inputs, (fd_set *)NULL, (fd_set *)NULL, waitTime); if (numReady <= 0) FD_ZERO(&inputs); /* no fd is ready */ } while ((numReady <= 0) && (errno == EINTR)); @@ -966,21 +1021,33 @@ TxGetInputEvent( perror("magic"); } - for (i = 0; i <= txLastInputEntry; i++) + /* 0..1023 using numReady==0 to terminate early */ + for (fd = 0; numReady && fd <= txInputDescriptors_nfds; fd++) { - /* This device has data on its file descriptor, call - * it so that it can add events to the input queue. - */ - for (fd = 0; fd < TX_MAX_OPEN_FILES; fd++) { - if (FD_ISSET(fd, &inputs) && - FD_ISSET(fd, &(txInputDevice[i].tx_fdmask))) { - lastNum = txNumInputEvents; - (*(txInputDevice[i].tx_inputProc)) - (fd, txInputDevice[i].tx_cdata); /** @invoke cb_textio_input_t */ - FD_CLR(fd, &inputs); - /* Did this driver choose to add an event? */ - if (txNumInputEvents != lastNum) gotSome = TRUE; - } + if (!FD_ISSET(fd, &inputs)) + continue; /* this fd is was not monitored or is not ready */ + + /* find the input device receiver entry */ + for (i = 0; i < txLastInputEntry; i++) + { + if (!FD_ISSET(fd, &txInputDevice[i].tx_fdmask)) + continue; + + /* This device has data on its file descriptor, call + * it so that it can add events to the input queue. + */ + lastNum = txNumInputEvents; + + (*txInputDevice[i].tx_inputProc)(fd, txInputDevice[i].tx_cdata); + + /* Did this driver choose to add an event? */ + if (txNumInputEvents != lastNum) gotSome = TRUE; + numReady--; + + /* original code would FD_CLR() which would effectively break here + * so this only allows the first found receiver to receive the data + */ + break; } } /* @@ -1699,6 +1766,7 @@ txCommandsInit(void) txZeroTime.tv_sec = 0; txZeroTime.tv_usec = 0; FD_ZERO(&txInputDescriptors); + txInputDescriptors_nfds = 0; DQInit(&txInputEvents, 4); DQInit(&txFreeEvents, 4); DQInit(&txFreeCommands, 4);