Home | History | Annotate | Download | only in bltins
      1 /***********************************************************************
      2 *                                                                      *
      3 *               This software is part of the ast package               *
      4 *          Copyright (c) 1982-2009 AT&T Intellectual Property          *
      5 *                      and is licensed under the                       *
      6 *                  Common Public License, Version 1.0                  *
      7 *                    by AT&T Intellectual Property                     *
      8 *                                                                      *
      9 *                A copy of the License is available at                 *
     10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
     11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
     12 *                                                                      *
     13 *              Information and Software Systems Research               *
     14 *                            AT&T Research                             *
     15 *                           Florham Park NJ                            *
     16 *                                                                      *
     17 *               Roland Mainz <roland.mainz (at) nrubsig.org>                *
     18 *                                                                      *
     19 ***********************************************************************/
     20 #pragma prototyped
     21 
     22 #include	<shell.h>
     23 #include	<stdio.h>
     24 #include	<stdbool.h>
     25 #include	<option.h>
     26 #include	<stk.h>
     27 #include	<tm.h>
     28 #include	"name.h"
     29 #undef nv_isnull
     30 #ifndef SH_DICT
     31 #   define SH_DICT     "libshell"
     32 #endif
     33 #include	<poll.h>
     34 #ifdef __GNUC__
     35 #include <alloca.h>
     36 #endif /* __GNUC__ */
     37 
     38 #define sh_contexttoshb(context)	((Shbltin_t*)(context))
     39 #define sh_contexttoshell(context)	((context)?(sh_contexttoshb(context)->shp):(NULL))
     40 
     41 static const char sh_optpoll[] =
     42 "[-?\n@(#)$Id: poll (AT&T Labs Research) 2009-05-14 $\n]"
     43 "[-author?Roland Mainz <roland.mainz (at) nrubsig.org]"
     44 "[-license?http://www.opensource.org/licenses/cpl1.0.txt]"
     45 "[+NAME? poll - input/output multiplexing]"
     46 "[+DESCRIPTION?The poll command provides applications with a mechanism "
     47 	"for multiplexing input/output over a set of file descriptors. "
     48 	"For each member of the array variable \bvar\b, "
     49 	"poll examines the given file descriptor in the subscript \b.fd\b "
     50 	"for the event(s) specified in the subscript \b.events\b."
     51 	"The poll command identifies those file descriptors on which an "
     52 	"application can read or write data, or on which certain events have "
     53 	"occurred.]"
     54 "[+?The \bvar\b argument specifies the file descriptors to be examined "
     55 	"and the events of interest for each file descriptor. "
     56 	"It is a array of structured variables with one member for each open "
     57 	"file descriptor of interest. The array's members contain the following "
     58 	"subscripts:]{"
     59 		"[+?\b.fd\b       # file descriptor]"
     60 		"[+?\b.events\b   # requested events]"
     61 		"[+?\b.revents\b  # returned event]"
     62 	"}"
     63 "[+?The \bfd\b variable specifies an open file descriptor and the "
     64 	"\bevents\b and \brevents\b members are strings constructed from "
     65 	"a concaternation of the following event flags, seperated by '|':]"
     66 	"{ "
     67 	"[+POLLIN?Data other than high priority data may be "
     68 		"read without blocking. For STREAMS, this "
     69 		"flag is set in revents even if the message "
     70 		"is of zero length.]"
     71 	"[+POLLRDNORM?Normal data (priority band equals 0) may be "
     72 		"read without blocking. For STREAMS, this "
     73 		"flag is set in revents even if the message "
     74 		"is of zero length.]"
     75 	"[+POLLRDBAND?Data from a non-zero priority band may be "
     76 		"read without blocking. For STREAMS, this "
     77 		"flag is set in revents even if the message "
     78 		"is of zero length.]"
     79 	"[+POLLPRI?High priority data may be received without "
     80 		"blocking. For STREAMS, this flag is set in "
     81 		"revents even if the message is of zero "
     82 		"length.]"
     83 	"[+POLLOUT?Normal data (priority band equals 0) may be "
     84 		"written without blocking.]"
     85 	"[+POLLWRNORM?The same as POLLOUT.]"
     86 	"[+POLLWRBAND?Priority data (priority band > 0) may be "
     87 		"written.  This event only examines bands "
     88 		"that have been written to at least once.]"
     89 	"[+POLLERR?An error has occurred on the device or "
     90 		"stream.  This flag is only valid in the "
     91 		"revents bitmask; it is not used in the "
     92 		"events member.]"
     93 	"[+POLLHUP?A hangup has occurred on the stream. This "
     94 		"event and POLLOUT are mutually exclusive; a "
     95 		"stream can never be writable if a hangup has "
     96 		"occurred. However, this event and POLLIN, "
     97 		", POLLRDBAND, or POLLPRI are not "
     98 		"mutually exclusive. This flag is only valid "
     99 		"in the revents bitmask; it is not used in "
    100 		"the events member.]"
    101 	"[+POLLNVAL?The specified fd value does not belong to an "
    102 		"open file. This flag is only valid in the "
    103 		"revents member; it is not used in the events "
    104 		"member.]"
    105    "}"
    106 "]"
    107 
    108 "[+?If the value fd is less than 0, events is ignored and "
    109 	"revents is set to 0 in that entry on return from poll.]"
    110 
    111 "[+?The results of the poll query are stored in the revents "
    112 	"member in the \bvar\b structure. POLL*-strings are set in the \brevents\b "
    113 	"variable to indicate which of the requested events are true. "
    114 	"If none are true, the \brevents\b will be an empty string when "
    115 	"the poll command returns. The event flags "
    116 	"POLLHUP, POLLERR, and POLLNVAL are always set in \brevents\b "
    117 	"if the conditions they indicate are true; this occurs even "
    118 	"though these flags were not present in events.]"
    119 
    120 "[+?If none of the defined events have occurred on any selected "
    121 	"file descriptor, poll waits at least timeout milliseconds "
    122 	"for an event to occur on any of the selected file descriptors. "
    123 	"On a computer where millisecond timing accuracy is not "
    124 	"available, timeout is rounded up to the nearest legal value "
    125 	"available on that system. If the value timeout is 0, poll "
    126 	"returns immediately. If the value of timeout is -1, poll "
    127 	"blocks until a requested event occurs or until the call is "
    128 	"interrupted.]"
    129 
    130 "[+?The poll function supports regular files, terminal and "
    131 	"pseudo-terminal devices, STREAMS-based files, FIFOs and "
    132 	"pipes. The behavior of poll on elements of fds that refer "
    133 	"to other types of file is unspecified.]"
    134 
    135 "[+?The poll function supports sockets.]"
    136 
    137 "[+?A file descriptor for a socket that is listening for connections "
    138 	"will indicate that it is ready for reading, once connections "
    139 	"are available. A file descriptor for a socket that "
    140 	"is connecting asynchronously will indicate that it is ready "
    141 	"for writing, once a connection has been established.]"
    142 
    143 "[+?Regular files always poll TRUE for reading and writing.]"
    144 
    145 "[e:eventarray]:[fdcount?Upon successful completion, an indexed array "
    146 	"of strings is returned which contains a list of array subscripts "
    147 	"in the poll array which received events.]"
    148 "[t:timeout]:[seconds?Timeout in seconds. If the value timeout is 0, "
    149 	"poll returns immediately. If the value of timeout is -1, poll "
    150 	"blocks until a requested event occurs or until the call is "
    151 	"interrupted.]"
    152 "[T:mtimeout]:[milliseconds?Timeout in milliseconds. If the value timeout is 0, "
    153 	"poll returns immediately. If the value of timeout is -1, poll "
    154 	"blocks until a requested event occurs or until the call is "
    155 	"interrupted.]"
    156 "\n"
    157 "\nvar\n"
    158 "\n"
    159 "[+EXIT STATUS?]{"
    160         "[+0?Success.]"
    161         "[+>0?An error occurred.]"
    162 "}"
    163 "[+SEE ALSO?\bopen\b(1),\btmpfile\b(1),\bdup\b(1),\bclose\b(1),\bpoll\b(2)]"
    164 ;
    165 
    166 /*
    167  * |mystpcpy| - like |strcpy()| but returns the end of the buffer
    168  *
    169  * Copy string s2 to s1.  s1 must be large enough.
    170  * return s1-1 (position of string terminator ('\0') in destnation buffer).
    171  */
    172 static
    173 char *mystpcpy(char *s1, const char *s2)
    174 {
    175         while (*s1++ = *s2++)
    176                 ;
    177         return (s1-1);
    178 }
    179 
    180 static
    181 Namval_t *nv_open_fmt(Dt_t *dict, int flags, const char *namefmt, ...)
    182 {
    183 	char 	varnamebuff[PATH_MAX];
    184 	va_list	ap;
    185 
    186 	va_start(ap, namefmt);
    187 	vsnprintf(varnamebuff, sizeof(varnamebuff), namefmt, ap);
    188 	va_end(ap);
    189 
    190 	return nv_open(varnamebuff, dict, flags);
    191 }
    192 
    193 static
    194 int poll_strtoevents(const char *str)
    195 {
    196 	int events = 0;
    197 
    198 	if (strstr(str, "POLLIN"))     events |= POLLIN;
    199 	if (strstr(str, "POLLRDNORM")) events |= POLLRDNORM;
    200 	if (strstr(str, "POLLRDBAND")) events |= POLLRDBAND;
    201 	if (strstr(str, "POLLPRI"))    events |= POLLPRI;
    202 	if (strstr(str, "POLLOUT"))    events |= POLLOUT;
    203 	if (strstr(str, "POLLWRNORM")) events |= POLLWRNORM;
    204 	if (strstr(str, "POLLWRBAND")) events |= POLLWRBAND;
    205 	if (strstr(str, "POLLERR"))    events |= POLLERR;
    206 	if (strstr(str, "POLLHUP"))    events |= POLLHUP;
    207 	if (strstr(str, "POLLNVAL"))   events |= POLLNVAL;
    208 
    209 	return events;
    210 }
    211 
    212 
    213 static
    214 void poll_eventstostr(char *s, int events)
    215 {
    216 	*s='\0';
    217 	if (!events)
    218 		return;
    219 
    220 	if (events & POLLIN)		s=mystpcpy(s, "POLLIN|");
    221 	if (events & POLLRDNORM)	s=mystpcpy(s, "POLLRDNORM|");
    222 	if (events & POLLRDBAND)	s=mystpcpy(s, "POLLRDBAND|");
    223 	if (events & POLLPRI)		s=mystpcpy(s, "POLLPRI|");
    224 	if (events & POLLOUT)		s=mystpcpy(s, "POLLOUT|");
    225 	if (events & POLLWRNORM)	s=mystpcpy(s, "POLLWRNORM|");
    226 	if (events & POLLWRBAND)	s=mystpcpy(s, "POLLWRBAND|");
    227 	if (events & POLLERR)		s=mystpcpy(s, "POLLERR|");
    228 	if (events & POLLHUP)		s=mystpcpy(s, "POLLHUP|");
    229 	if (events & POLLNVAL)		s=mystpcpy(s, "POLLNVAL|");
    230 
    231 	/* Remove trailling '|' */
    232 	s--;
    233 	if(*s=='|')
    234 		*s='\0';
    235 }
    236 
    237 #undef  getconf
    238 #define getconf(x)      strtol(astconf(x,NiL,NiL),NiL,0)
    239 
    240 extern int b_poll(int argc, char *argv[], void *extra)
    241 {
    242 	Namval_t *np;
    243 	Shell_t *shp = sh_contexttoshell(extra);
    244 	char *varname;
    245 	int n;
    246 	int fd;
    247 	nfds_t numpollfd = 0;
    248 	int i;
    249 	char *s;
    250 	double timeout = -1.;
    251 	char buff[PATH_MAX*2+1]; /* enogth to hold two variable names */
    252 	char *eventarrayname = NULL;
    253 
    254 	while (n = optget(argv, sh_optpoll)) switch (n)
    255 	{
    256 	    case 't':
    257 	    case 'T':
    258 		errno = 0;
    259 		timeout = strtod(opt_info.arg, (char **)NULL);
    260 		if (errno != 0)
    261 			errormsg(SH_DICT, ERROR_system(1), "%s: invalid timeout", opt_info.arg);
    262 
    263 		/* -t uses seconds, -T milliseconds */
    264 		if (n == 't')
    265 			timeout *= 1000.;
    266 		break;
    267 	    case 'e':
    268 	    	eventarrayname = opt_info.arg;
    269 		break;
    270 	    case ':':
    271 		errormsg(SH_DICT, 2, "%s", opt_info.arg);
    272 		break;
    273 	    case '?':
    274 		errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
    275 		break;
    276 	}
    277 	argc -= opt_info.index;
    278 	argv += opt_info.index;
    279 	if(argc!=1)
    280 		errormsg(SH_DICT, ERROR_usage(2), optusage((char*)0));
    281 
    282         varname = argv[0];
    283 
    284 	Namval_t *array_np, *array_np_sub;
    285 	const char *subname;
    286 
    287 	array_np = nv_open(varname, shp->var_tree, NV_NOFAIL|NV_NOADD);
    288 	if (!array_np)
    289 		errormsg(SH_DICT, ERROR_system(1), "cannot find array variable %s", varname);
    290 	if(!nv_isattr(array_np, NV_ARRAY))
    291 		errormsg(SH_DICT, ERROR_system(1), "variable %s is not an array", varname);
    292 
    293 	/* Count number of array elememts. We need to do it "manually" to
    294 	 * handle sparse indexed and associative arrays */
    295 	nv_putsub(array_np, NULL, ARRAY_SCAN);
    296 	array_np_sub = array_np;
    297 	do
    298 	{
    299 		if (!(subname=nv_getsub(array_np_sub)))
    300 			break;
    301 		numpollfd++;
    302 	} while( array_np_sub && nv_nextsub(array_np_sub) );
    303 
    304 #ifdef __GNUC__
    305         /*
    306          * Allocate stack space via |alloca()| for gcc builds since ctfconvert
    307          * is unable to handle VLAs from gcc. We need this until CR #6379193
    308 	 * is fixed.
    309          */
    310 	struct pollfd *pollfd = alloca(sizeof(struct pollfd)*(numpollfd+1));
    311 #else
    312 	/* We must allocate one more entry with VLA with zero elements do not work with all compilers */
    313 	struct pollfd pollfd[numpollfd+1];
    314 #endif /* __GNUC__ */
    315 
    316 	nv_putsub(array_np, NULL, ARRAY_SCAN);
    317 	array_np_sub = array_np;
    318 	i = 0;
    319 	do
    320 	{
    321 		if (!(subname=nv_getsub(array_np_sub)))
    322 			break;
    323 
    324 		np = nv_open_fmt(shp->var_tree, NV_NOFAIL|NV_NOADD, "%s[%s].fd", varname, subname);
    325 		if (!np)
    326 			errormsg(SH_DICT, ERROR_system(1), "missing pollfd %s[%s].fd", varname, subname);
    327 		fd = (int)nv_getnum(np);
    328 		if (fd < 0 || fd > OPEN_MAX)
    329 			errormsg(SH_DICT, ERROR_system(1), "invalid pollfd fd %d", fd);
    330 		nv_close(np);
    331 		pollfd[i].fd = fd;
    332 
    333 		np = nv_open_fmt(shp->var_tree, NV_NOFAIL|NV_NOADD, "%s[%s].events", varname, subname);
    334 		if (!np)
    335 			errormsg(SH_DICT, ERROR_system(1), "missing pollfd %s[%s].events", varname, subname);
    336 
    337 		s = nv_getval(np);
    338 		if (!s)
    339 			errormsg(SH_DICT, ERROR_system(1), "missing pollfd events value");
    340 		pollfd[i].events  = poll_strtoevents(s);
    341 		nv_close(np);
    342 
    343 		pollfd[i].revents = 0;
    344 
    345 		i++;
    346 	} while( array_np_sub && nv_nextsub(array_np_sub) );
    347 
    348 	n = poll(pollfd, numpollfd, timeout);
    349 	/* FixMe: EGAIN and EINTR may require extra handling */
    350 	if (n < 0)
    351 		errormsg(SH_DICT, ERROR_system(1), "failure");
    352 
    353 	if (eventarrayname)
    354 	{
    355 		np = nv_open_fmt(shp->var_tree, NV_VARNAME|NV_ARRAY|NV_NOFAIL, "%s", eventarrayname);
    356 		if (!np)
    357 			errormsg(SH_DICT, ERROR_system(1), "couldn't create poll count variable %s", eventarrayname);
    358 		nv_close(np);
    359 	}
    360 
    361 	nv_putsub(array_np, NULL, ARRAY_SCAN);
    362 	array_np_sub = array_np;
    363 	i = 0;
    364 	do
    365 	{
    366 		if (!(subname=nv_getsub(array_np_sub)))
    367 			break;
    368 
    369 		np = nv_open_fmt(shp->var_tree, NV_NOFAIL, "%s[%s].revents", varname, subname);
    370 		if (!np)
    371 			errormsg(SH_DICT, ERROR_system(1), "couldn't create pollfd %s[%s].revents", varname, subname);
    372 
    373 		poll_eventstostr(buff, pollfd[i].revents);
    374 
    375 		nv_putval(np, buff, 0);
    376 		nv_close(np);
    377 
    378 		if (eventarrayname && pollfd[i].revents)
    379 		{
    380 			sprintf(buff, "%s+=( '%s' )", eventarrayname, subname);
    381 			sh_trap(buff, 0);
    382 		}
    383 
    384 		i++;
    385 	} while( array_np_sub && nv_nextsub(array_np_sub) );
    386 
    387 	nv_close(array_np);
    388 
    389 	return(0);
    390 }
    391