xref: /netbsd-src/external/mit/libuv/dist/src/unix/os390-syscalls.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /* Copyright libuv project contributors. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 
22 
23 #include "os390-syscalls.h"
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <search.h>
27 #include <termios.h>
28 #include <sys/msg.h>
29 
30 #define CW_INTRPT 1
31 #define CW_CONDVAR 32
32 
33 #pragma linkage(BPX4CTW, OS)
34 #pragma linkage(BPX1CTW, OS)
35 
36 static int number_of_epolls;
37 static QUEUE global_epoll_queue;
38 static uv_mutex_t global_epoll_lock;
39 static uv_once_t once = UV_ONCE_INIT;
40 
41 int scandir(const char* maindir, struct dirent*** namelist,
42             int (*filter)(const struct dirent*),
43             int (*compar)(const struct dirent**,
44             const struct dirent **)) {
45   struct dirent** nl;
46   struct dirent** nl_copy;
47   struct dirent* dirent;
48   unsigned count;
49   size_t allocated;
50   DIR* mdir;
51 
52   nl = NULL;
53   count = 0;
54   allocated = 0;
55   mdir = opendir(maindir);
56   if (!mdir)
57     return -1;
58 
59   while (1) {
60     dirent = readdir(mdir);
61     if (!dirent)
62       break;
63     if (!filter || filter(dirent)) {
64       struct dirent* copy;
65       copy = uv__malloc(sizeof(*copy));
66       if (!copy)
67         goto error;
68       memcpy(copy, dirent, sizeof(*copy));
69 
70       nl_copy = uv__realloc(nl, sizeof(*copy) * (count + 1));
71       if (nl_copy == NULL) {
72         uv__free(copy);
73         goto error;
74       }
75 
76       nl = nl_copy;
77       nl[count++] = copy;
78     }
79   }
80 
81   qsort(nl, count, sizeof(struct dirent *),
82        (int (*)(const void *, const void *)) compar);
83 
84   closedir(mdir);
85 
86   *namelist = nl;
87   return count;
88 
89 error:
90   while (count > 0) {
91     dirent = nl[--count];
92     uv__free(dirent);
93   }
94   uv__free(nl);
95   closedir(mdir);
96   errno = ENOMEM;
97   return -1;
98 }
99 
100 
101 static unsigned int next_power_of_two(unsigned int val) {
102   val -= 1;
103   val |= val >> 1;
104   val |= val >> 2;
105   val |= val >> 4;
106   val |= val >> 8;
107   val |= val >> 16;
108   val += 1;
109   return val;
110 }
111 
112 
113 static void maybe_resize(uv__os390_epoll* lst, unsigned int len) {
114   unsigned int newsize;
115   unsigned int i;
116   struct pollfd* newlst;
117   struct pollfd event;
118 
119   if (len <= lst->size)
120     return;
121 
122   if (lst->size == 0)
123     event.fd = -1;
124   else {
125     /* Extract the message queue at the end. */
126     event = lst->items[lst->size - 1];
127     lst->items[lst->size - 1].fd = -1;
128   }
129 
130   newsize = next_power_of_two(len);
131   newlst = uv__reallocf(lst->items, newsize * sizeof(lst->items[0]));
132 
133   if (newlst == NULL)
134     abort();
135   for (i = lst->size; i < newsize; ++i)
136     newlst[i].fd = -1;
137 
138   /* Restore the message queue at the end */
139   newlst[newsize - 1] = event;
140 
141   lst->items = newlst;
142   lst->size = newsize;
143 }
144 
145 
146 static void init_message_queue(uv__os390_epoll* lst) {
147   struct {
148     long int header;
149     char body;
150   } msg;
151 
152   /* initialize message queue */
153   lst->msg_queue = msgget(IPC_PRIVATE, 0600 | IPC_CREAT);
154   if (lst->msg_queue == -1)
155     abort();
156 
157   /*
158      On z/OS, the message queue will be affiliated with the process only
159      when a send is performed on it. Once this is done, the system
160      can be queried for all message queues belonging to our process id.
161   */
162   msg.header = 1;
163   if (msgsnd(lst->msg_queue, &msg, sizeof(msg.body), 0) != 0)
164     abort();
165 
166   /* Clean up the dummy message sent above */
167   if (msgrcv(lst->msg_queue, &msg, sizeof(msg.body), 0, 0) != sizeof(msg.body))
168     abort();
169 }
170 
171 
172 static void before_fork(void) {
173   uv_mutex_lock(&global_epoll_lock);
174 }
175 
176 
177 static void after_fork(void) {
178   uv_mutex_unlock(&global_epoll_lock);
179 }
180 
181 
182 static void child_fork(void) {
183   QUEUE* q;
184   uv_once_t child_once = UV_ONCE_INIT;
185 
186   /* reset once */
187   memcpy(&once, &child_once, sizeof(child_once));
188 
189   /* reset epoll list */
190   while (!QUEUE_EMPTY(&global_epoll_queue)) {
191     uv__os390_epoll* lst;
192     q = QUEUE_HEAD(&global_epoll_queue);
193     QUEUE_REMOVE(q);
194     lst = QUEUE_DATA(q, uv__os390_epoll, member);
195     uv__free(lst->items);
196     lst->items = NULL;
197     lst->size = 0;
198   }
199 
200   uv_mutex_unlock(&global_epoll_lock);
201   uv_mutex_destroy(&global_epoll_lock);
202 }
203 
204 
205 static void epoll_init(void) {
206   QUEUE_INIT(&global_epoll_queue);
207   if (uv_mutex_init(&global_epoll_lock))
208     abort();
209 
210   if (pthread_atfork(&before_fork, &after_fork, &child_fork))
211     abort();
212 }
213 
214 
215 uv__os390_epoll* epoll_create1(int flags) {
216   uv__os390_epoll* lst;
217 
218   lst = uv__malloc(sizeof(*lst));
219   if (lst != NULL) {
220     /* initialize list */
221     lst->size = 0;
222     lst->items = NULL;
223     init_message_queue(lst);
224     maybe_resize(lst, 1);
225     lst->items[lst->size - 1].fd = lst->msg_queue;
226     lst->items[lst->size - 1].events = POLLIN;
227     lst->items[lst->size - 1].revents = 0;
228     uv_once(&once, epoll_init);
229     uv_mutex_lock(&global_epoll_lock);
230     QUEUE_INSERT_TAIL(&global_epoll_queue, &lst->member);
231     uv_mutex_unlock(&global_epoll_lock);
232   }
233 
234   return lst;
235 }
236 
237 
238 int epoll_ctl(uv__os390_epoll* lst,
239               int op,
240               int fd,
241               struct epoll_event *event) {
242   uv_mutex_lock(&global_epoll_lock);
243 
244   if (op == EPOLL_CTL_DEL) {
245     if (fd >= lst->size || lst->items[fd].fd == -1) {
246       uv_mutex_unlock(&global_epoll_lock);
247       errno = ENOENT;
248       return -1;
249     }
250     lst->items[fd].fd = -1;
251   } else if (op == EPOLL_CTL_ADD) {
252 
253     /* Resizing to 'fd + 1' would expand the list to contain at least
254      * 'fd'. But we need to guarantee that the last index on the list
255      * is reserved for the message queue. So specify 'fd + 2' instead.
256      */
257     maybe_resize(lst, fd + 2);
258     if (lst->items[fd].fd != -1) {
259       uv_mutex_unlock(&global_epoll_lock);
260       errno = EEXIST;
261       return -1;
262     }
263     lst->items[fd].fd = fd;
264     lst->items[fd].events = event->events;
265     lst->items[fd].revents = 0;
266   } else if (op == EPOLL_CTL_MOD) {
267     if (fd >= lst->size - 1 || lst->items[fd].fd == -1) {
268       uv_mutex_unlock(&global_epoll_lock);
269       errno = ENOENT;
270       return -1;
271     }
272     lst->items[fd].events = event->events;
273     lst->items[fd].revents = 0;
274   } else
275     abort();
276 
277   uv_mutex_unlock(&global_epoll_lock);
278   return 0;
279 }
280 
281 #define EP_MAX_PFDS (ULONG_MAX / sizeof(struct pollfd))
282 #define EP_MAX_EVENTS (INT_MAX / sizeof(struct epoll_event))
283 
284 int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events,
285                int maxevents, int timeout) {
286   nmsgsfds_t size;
287   struct pollfd* pfds;
288   int pollret;
289   int reventcount;
290   int nevents;
291   struct pollfd msg_fd;
292   int i;
293 
294   if (!lst || !lst->items || !events) {
295     errno = EFAULT;
296     return -1;
297   }
298 
299   if (lst->size > EP_MAX_PFDS) {
300     errno = EINVAL;
301     return -1;
302   }
303 
304   if (maxevents <= 0 || maxevents > EP_MAX_EVENTS) {
305     errno = EINVAL;
306     return -1;
307   }
308 
309   if (lst->size > 0)
310     _SET_FDS_MSGS(size, 1, lst->size - 1);
311   else
312     _SET_FDS_MSGS(size, 0, 0);
313   pfds = lst->items;
314   pollret = poll(pfds, size, timeout);
315   if (pollret <= 0)
316     return pollret;
317 
318   assert(lst->size > 0);
319 
320   pollret = _NFDS(pollret) + _NMSGS(pollret);
321 
322   reventcount = 0;
323   nevents = 0;
324   msg_fd = pfds[lst->size - 1];
325   for (i = 0;
326        i < lst->size && i < maxevents && reventcount < pollret; ++i) {
327     struct epoll_event ev;
328     struct pollfd* pfd;
329 
330     pfd = &pfds[i];
331     if (pfd->fd == -1 || pfd->revents == 0)
332       continue;
333 
334     ev.fd = pfd->fd;
335     ev.events = pfd->revents;
336     ev.is_msg = 0;
337     if (pfd->revents & POLLIN && pfd->revents & POLLOUT)
338       reventcount += 2;
339     else if (pfd->revents & (POLLIN | POLLOUT))
340       ++reventcount;
341 
342     pfd->revents = 0;
343     events[nevents++] = ev;
344   }
345 
346   if (msg_fd.revents != 0 && msg_fd.fd != -1)
347     if (i == lst->size)
348       events[nevents - 1].is_msg = 1;
349 
350   return nevents;
351 }
352 
353 
354 int epoll_file_close(int fd) {
355   QUEUE* q;
356 
357   uv_once(&once, epoll_init);
358   uv_mutex_lock(&global_epoll_lock);
359   QUEUE_FOREACH(q, &global_epoll_queue) {
360     uv__os390_epoll* lst;
361 
362     lst = QUEUE_DATA(q, uv__os390_epoll, member);
363     if (fd < lst->size && lst->items != NULL && lst->items[fd].fd != -1)
364       lst->items[fd].fd = -1;
365   }
366 
367   uv_mutex_unlock(&global_epoll_lock);
368   return 0;
369 }
370 
371 void epoll_queue_close(uv__os390_epoll* lst) {
372   /* Remove epoll instance from global queue */
373   uv_mutex_lock(&global_epoll_lock);
374   QUEUE_REMOVE(&lst->member);
375   uv_mutex_unlock(&global_epoll_lock);
376 
377   /* Free resources */
378   msgctl(lst->msg_queue, IPC_RMID, NULL);
379   lst->msg_queue = -1;
380   uv__free(lst->items);
381   lst->items = NULL;
382 }
383 
384 
385 int nanosleep(const struct timespec* req, struct timespec* rem) {
386   unsigned nano;
387   unsigned seconds;
388   unsigned events;
389   unsigned secrem;
390   unsigned nanorem;
391   int rv;
392   int err;
393   int rsn;
394 
395   nano = (int)req->tv_nsec;
396   seconds = req->tv_sec;
397   events = CW_CONDVAR | CW_INTRPT;
398   secrem = 0;
399   nanorem = 0;
400 
401 #if defined(_LP64)
402   BPX4CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &err, &rsn);
403 #else
404   BPX1CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &err, &rsn);
405 #endif
406 
407   /* Don't clobber errno unless BPX1CTW/BPX4CTW errored.
408    * Don't leak EAGAIN, that just means the timeout expired.
409    */
410   if (rv == -1)
411     if (err == EAGAIN)
412       rv = 0;
413     else
414       errno = err;
415 
416   if (rem != NULL && (rv == 0 || err == EINTR)) {
417     rem->tv_nsec = nanorem;
418     rem->tv_sec = secrem;
419   }
420 
421   return rv;
422 }
423 
424 
425 char* mkdtemp(char* path) {
426   static const char* tempchars =
427     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
428   static const size_t num_chars = 62;
429   static const size_t num_x = 6;
430   char *ep, *cp;
431   unsigned int tries, i;
432   size_t len;
433   uint64_t v;
434   int fd;
435   int retval;
436   int saved_errno;
437 
438   len = strlen(path);
439   ep = path + len;
440   if (len < num_x || strncmp(ep - num_x, "XXXXXX", num_x)) {
441     errno = EINVAL;
442     return NULL;
443   }
444 
445   fd = open("/dev/urandom", O_RDONLY);
446   if (fd == -1)
447     return NULL;
448 
449   tries = TMP_MAX;
450   retval = -1;
451   do {
452     if (read(fd, &v, sizeof(v)) != sizeof(v))
453       break;
454 
455     cp = ep - num_x;
456     for (i = 0; i < num_x; i++) {
457       *cp++ = tempchars[v % num_chars];
458       v /= num_chars;
459     }
460 
461     if (mkdir(path, S_IRWXU) == 0) {
462       retval = 0;
463       break;
464     }
465     else if (errno != EEXIST)
466       break;
467   } while (--tries);
468 
469   saved_errno = errno;
470   uv__close(fd);
471   if (tries == 0) {
472     errno = EEXIST;
473     return NULL;
474   }
475 
476   if (retval == -1) {
477     errno = saved_errno;
478     return NULL;
479   }
480 
481   return path;
482 }
483 
484 
485 ssize_t os390_readlink(const char* path, char* buf, size_t len) {
486   ssize_t rlen;
487   ssize_t vlen;
488   ssize_t plen;
489   char* delimiter;
490   char old_delim;
491   char* tmpbuf;
492   char realpathstr[PATH_MAX + 1];
493 
494   tmpbuf = uv__malloc(len + 1);
495   if (tmpbuf == NULL) {
496     errno = ENOMEM;
497     return -1;
498   }
499 
500   rlen = readlink(path, tmpbuf, len);
501   if (rlen < 0) {
502     uv__free(tmpbuf);
503     return rlen;
504   }
505 
506   if (rlen < 3 || strncmp("/$", tmpbuf, 2) != 0) {
507     /* Straightforward readlink. */
508     memcpy(buf, tmpbuf, rlen);
509     uv__free(tmpbuf);
510     return rlen;
511   }
512 
513   /*
514    * There is a parmlib variable at the beginning
515    * which needs interpretation.
516    */
517   tmpbuf[rlen] = '\0';
518   delimiter = strchr(tmpbuf + 2, '/');
519   if (delimiter == NULL)
520     /* No slash at the end */
521     delimiter = strchr(tmpbuf + 2, '\0');
522 
523   /* Read real path of the variable. */
524   old_delim = *delimiter;
525   *delimiter = '\0';
526   if (realpath(tmpbuf, realpathstr) == NULL) {
527     uv__free(tmpbuf);
528     return -1;
529   }
530 
531   /* realpathstr is not guaranteed to end with null byte.*/
532   realpathstr[PATH_MAX] = '\0';
533 
534   /* Reset the delimiter and fill up the buffer. */
535   *delimiter = old_delim;
536   plen = strlen(delimiter);
537   vlen = strlen(realpathstr);
538   rlen = plen + vlen;
539   if (rlen > len) {
540     uv__free(tmpbuf);
541     errno = ENAMETOOLONG;
542     return -1;
543   }
544   memcpy(buf, realpathstr, vlen);
545   memcpy(buf + vlen, delimiter, plen);
546 
547   /* Done using temporary buffer. */
548   uv__free(tmpbuf);
549 
550   return rlen;
551 }
552 
553 
554 size_t strnlen(const char* str, size_t maxlen) {
555   char* p = memchr(str, 0, maxlen);
556   if (p == NULL)
557     return maxlen;
558   else
559     return p - str;
560 }
561 
562 
563 int sem_init(UV_PLATFORM_SEM_T* semid, int pshared, unsigned int value) {
564   UNREACHABLE();
565 }
566 
567 
568 int sem_destroy(UV_PLATFORM_SEM_T* semid) {
569   UNREACHABLE();
570 }
571 
572 
573 int sem_post(UV_PLATFORM_SEM_T* semid) {
574   UNREACHABLE();
575 }
576 
577 
578 int sem_trywait(UV_PLATFORM_SEM_T* semid) {
579   UNREACHABLE();
580 }
581 
582 
583 int sem_wait(UV_PLATFORM_SEM_T* semid) {
584   UNREACHABLE();
585 }
586