1 /* Copyright (c) 2010-2017,2018 John E. Davis
2  * This file is part of the S-Lang library.
3  *
4  * You may distribute under the terms of the GNU General Public
5  * License.
6  */
7 #include <stdio.h>
8 #include <slang.h>
9 
10 #include <sys/time.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13 #include <string.h>
14 #include <errno.h>
15 
16 SLANG_MODULE(select);
17 
pop_fd_set(SLang_Array_Type ** ats,fd_set ** fd_set_p,fd_set * fd_set_buf,int * max_n)18 static int pop_fd_set (SLang_Array_Type **ats,
19 		       fd_set **fd_set_p, fd_set *fd_set_buf,
20 		       int *max_n)
21 {
22    SLuindex_Type num, i;
23    SLang_Array_Type *at;
24    SLFile_FD_Type **f;
25 
26    *ats = NULL;
27    *fd_set_p = NULL;
28 
29    if (SLang_peek_at_stack () == SLANG_NULL_TYPE)
30      return SLang_pop_null ();
31 
32    if (-1 == SLang_pop_array_of_type (&at, SLANG_FILE_FD_TYPE))
33      return -1;
34 
35    FD_ZERO(fd_set_buf);
36    *fd_set_p = fd_set_buf;
37 
38    *ats = at;
39    num = at->num_elements;
40    f = (SLFile_FD_Type **) at->data;
41 
42    for (i = 0; i < num; i++)
43      {
44 	int fd;
45 
46 	if (-1 == SLfile_get_fd (f[i], &fd))
47 	  continue;
48 
49 	if (fd > *max_n)
50 	  *max_n = fd;
51 
52 	FD_SET(fd, fd_set_buf);
53      }
54 
55    return 0;
56 }
57 
do_fdisset(int nready,SLang_Array_Type * fds,fd_set * fdset)58 static SLang_Array_Type *do_fdisset (int nready, SLang_Array_Type *fds, fd_set *fdset)
59 {
60    SLang_Array_Type *at;
61    int i, num;
62    SLFile_FD_Type **f;
63    SLindex_Type ind_nready;
64 
65    if (fds == NULL)
66      nready = 0;
67 
68    if (nready)
69      {
70 	nready = 0;
71 	num = fds->num_elements;
72 	f = (SLFile_FD_Type **) fds->data;
73 	for (i = 0; i < num; i++)
74 	  {
75 	     int fd;
76 
77 	     if (-1 == SLfile_get_fd (f[i], &fd))
78 	       continue;
79 
80 	     if (FD_ISSET(fd, fdset))
81 	       nready++;
82 	  }
83      }
84 
85    ind_nready = (SLindex_Type) nready;
86    at = SLang_create_array (SLANG_INT_TYPE, 0, NULL, &ind_nready, 1);
87    if (at == NULL)
88      return NULL;
89 
90    if (nready)
91      {
92 	int *indx = (int *) at->data;
93 	f = (SLFile_FD_Type **) fds->data;
94 	num = fds->num_elements;
95 	for (i = 0; i < num; i++)
96 	  {
97 	     int fd;
98 
99 	     if (-1 == SLfile_get_fd (f[i], &fd))
100 	       continue;
101 
102 	     if (FD_ISSET(fd, fdset))
103 	       *indx++ = (int) i;
104 	  }
105      }
106 
107    return at;
108 }
109 
push_select_struct(int num,SLang_Array_Type * at_read,SLang_Array_Type * at_write,SLang_Array_Type * at_except,fd_set * readfs,fd_set * writefds,fd_set * exceptfds)110 static int push_select_struct (int num,
111 			       SLang_Array_Type *at_read,
112 			       SLang_Array_Type *at_write,
113 			       SLang_Array_Type *at_except,
114 			       fd_set *readfs, fd_set *writefds, fd_set *exceptfds)
115 {
116    SLFUTURE_CONST char *field_names [4];
117    SLtype field_types[4];
118    VOID_STAR field_values [4];
119    SLang_Array_Type *iread, *iwrite, *iexcept;
120    int status;
121 
122    iread = iwrite = iexcept = NULL;
123 
124    field_names[0] = "nready";
125    field_names[1] = "iread";
126    field_names[2] = "iwrite";
127    field_names[3] = "iexcept";
128    field_types[0] = SLANG_INT_TYPE;
129    field_types[1] = SLANG_ARRAY_TYPE;
130    field_types[2] = SLANG_ARRAY_TYPE;
131    field_types[3] = SLANG_ARRAY_TYPE;
132    field_values[0] = &num;
133 
134    if ((NULL == (iread = do_fdisset (num, at_read, readfs)))
135        || (NULL == (iwrite = do_fdisset (num, at_write, writefds)))
136        || (NULL == (iexcept = do_fdisset (num, at_except, exceptfds))))
137      {
138 	SLang_free_array (iread);
139 	SLang_free_array (iwrite);
140 	return -1;
141      }
142 
143    field_values[1] = &iread;
144    field_values[2] = &iwrite;
145    field_values[3] = &iexcept;
146 
147    /* Note: This function call pushes the struct and frees it upon error. */
148    status = SLstruct_create_struct (4, field_names, field_types, field_values);
149    SLang_free_array (iexcept);
150    SLang_free_array (iwrite);
151    SLang_free_array (iread);
152    return status;
153 }
154 
155 /* Usage: Struct_Type select (R[],W[],E[],TIME) */
156 
select_intrin(double * secsp)157 static void select_intrin (double *secsp)
158 {
159    SLang_Array_Type *at_read, *at_write, *at_except;
160    fd_set readfs_buf, writefds_buf, exceptfds_buf;
161    fd_set readfs_save_buf, writefds_save_buf, exceptfds_save_buf;
162    fd_set *readfs, *writefds, *exceptfds;
163    struct timeval tv, *tv_ptr;
164    double secs;
165    int ret, n;
166 
167    secs = *secsp;
168    if (secs < 0.0) tv_ptr = NULL;
169    else
170      {
171 	tv.tv_sec = (unsigned long) secs;
172 	tv.tv_usec = (unsigned long) ((secs - tv.tv_sec) * 1e6);
173 	tv_ptr = &tv;
174      }
175 
176    n = 0;
177    if (-1 == pop_fd_set (&at_except, &exceptfds, &exceptfds_buf, &n))
178      return;
179    if (-1 == pop_fd_set (&at_write, &writefds, &writefds_buf, &n))
180      {
181 	SLang_free_array (at_except);
182 	return;
183      }
184    if (-1 == pop_fd_set (&at_read, &readfs, &readfs_buf, &n))
185      goto free_return;
186 
187    readfs_save_buf = readfs_buf;
188    writefds_save_buf = writefds_buf;
189    exceptfds_save_buf = exceptfds_buf;
190 
191    n += 1;
192    while (-1 == (ret = select (n, readfs, writefds, exceptfds, tv_ptr)))
193      {
194 #ifdef EINTR
195 	if (errno == EINTR)
196 	  {
197 	     readfs_buf = readfs_save_buf;
198 	     writefds_buf = writefds_save_buf;
199 	     exceptfds_buf = exceptfds_save_buf;
200 	     if (0 == SLang_handle_interrupt ())
201 	       continue;
202 	  }
203 #endif
204 	(void) SLerrno_set_errno (errno);
205 	break;
206      }
207 
208    if (ret == -1)
209      (void) SLang_push_null ();
210    else
211      (void) push_select_struct (ret, at_read, at_write, at_except,
212 				readfs, writefds, exceptfds);
213 
214    free_return:
215    SLang_free_array (at_read);
216    SLang_free_array (at_write);
217    SLang_free_array (at_except);
218 }
219 
220 static SLang_Intrin_Fun_Type Select_Intrinsics [] =
221 {
222    MAKE_INTRINSIC_1("select", select_intrin, SLANG_VOID_TYPE, SLANG_DOUBLE_TYPE),
223    SLANG_END_INTRIN_FUN_TABLE
224 };
225 
init_select_module_ns(char * ns_name)226 int init_select_module_ns (char *ns_name)
227 {
228    SLang_NameSpace_Type *ns;
229 
230    ns = SLns_create_namespace (ns_name);
231    if (ns == NULL)
232      return -1;
233 
234    if (-1 == SLns_add_intrin_fun_table (ns, Select_Intrinsics, "__SELECT__"))
235      return -1;
236 
237    return 0;
238 }
239 
240 /* This function is optional */
deinit_select_module(void)241 void deinit_select_module (void)
242 {
243 }
244