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
mystpcpy(char * s1,const char * s2)173 char *mystpcpy(char *s1, const char *s2)
174 {
175 while (*s1++ = *s2++)
176 ;
177 return (s1-1);
178 }
179
180 static
nv_open_fmt(Dt_t * dict,int flags,const char * namefmt,...)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
poll_strtoevents(const char * str)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
poll_eventstostr(char * s,int events)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
b_poll(int argc,char * argv[],void * extra)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