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@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@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