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
30static QUEUE global_epoll_queue;
31static uv_mutex_t global_epoll_lock;
32static uv_once_t once = UV_ONCE_INIT;
33
34int scandir(const char* maindir, struct dirent*** namelist,
35            int (*filter)(const struct dirent*),
36            int (*compar)(const struct dirent**,
37            const struct dirent **)) {
38  struct dirent** nl;
39  struct dirent** nl_copy;
40  struct dirent* dirent;
41  unsigned count;
42  size_t allocated;
43  DIR* mdir;
44
45  nl = NULL;
46  count = 0;
47  allocated = 0;
48  mdir = opendir(maindir);
49  if (!mdir)
50    return -1;
51
52  for (;;) {
53    dirent = readdir(mdir);
54    if (!dirent)
55      break;
56    if (!filter || filter(dirent)) {
57      struct dirent* copy;
58      copy = uv__malloc(sizeof(*copy));
59      if (!copy)
60        goto error;
61      memcpy(copy, dirent, sizeof(*copy));
62
63      nl_copy = uv__realloc(nl, sizeof(*copy) * (count + 1));
64      if (nl_copy == NULL) {
65        uv__free(copy);
66        goto error;
67      }
68
69      nl = nl_copy;
70      nl[count++] = copy;
71    }
72  }
73
74  qsort(nl, count, sizeof(struct dirent *),
75       (int (*)(const void *, const void *)) compar);
76
77  closedir(mdir);
78
79  *namelist = nl;
80  return count;
81
82error:
83  while (count > 0) {
84    dirent = nl[--count];
85    uv__free(dirent);
86  }
87  uv__free(nl);
88  closedir(mdir);
89  errno = ENOMEM;
90  return -1;
91}
92
93
94static unsigned int next_power_of_two(unsigned int val) {
95  val -= 1;
96  val |= val >> 1;
97  val |= val >> 2;
98  val |= val >> 4;
99  val |= val >> 8;
100  val |= val >> 16;
101  val += 1;
102  return val;
103}
104
105
106static void maybe_resize(uv__os390_epoll* lst, unsigned int len) {
107  unsigned int newsize;
108  unsigned int i;
109  struct pollfd* newlst;
110  struct pollfd event;
111
112  if (len <= lst->size)
113    return;
114
115  if (lst->size == 0)
116    event.fd = -1;
117  else {
118    /* Extract the message queue at the end. */
119    event = lst->items[lst->size - 1];
120    lst->items[lst->size - 1].fd = -1;
121  }
122
123  newsize = next_power_of_two(len);
124  newlst = uv__reallocf(lst->items, newsize * sizeof(lst->items[0]));
125
126  if (newlst == NULL)
127    abort();
128  for (i = lst->size; i < newsize; ++i)
129    newlst[i].fd = -1;
130
131  /* Restore the message queue at the end */
132  newlst[newsize - 1] = event;
133
134  lst->items = newlst;
135  lst->size = newsize;
136}
137
138
139void uv__os390_cleanup(void) {
140  msgctl(uv_backend_fd(uv_default_loop()), IPC_RMID, NULL);
141}
142
143
144static void init_message_queue(uv__os390_epoll* lst) {
145  struct {
146    long int header;
147    char body;
148  } msg;
149
150  /* initialize message queue */
151  lst->msg_queue = msgget(IPC_PRIVATE, 0600 | IPC_CREAT);
152  if (lst->msg_queue == -1)
153    abort();
154
155  /*
156     On z/OS, the message queue will be affiliated with the process only
157     when a send is performed on it. Once this is done, the system
158     can be queried for all message queues belonging to our process id.
159  */
160  msg.header = 1;
161  if (msgsnd(lst->msg_queue, &msg, sizeof(msg.body), 0) != 0)
162    abort();
163
164  /* Clean up the dummy message sent above */
165  if (msgrcv(lst->msg_queue, &msg, sizeof(msg.body), 0, 0) != sizeof(msg.body))
166    abort();
167}
168
169
170static void before_fork(void) {
171  uv_mutex_lock(&global_epoll_lock);
172}
173
174
175static void after_fork(void) {
176  uv_mutex_unlock(&global_epoll_lock);
177}
178
179
180static void child_fork(void) {
181  QUEUE* q;
182  uv_once_t child_once = UV_ONCE_INIT;
183
184  /* reset once */
185  memcpy(&once, &child_once, sizeof(child_once));
186
187  /* reset epoll list */
188  while (!QUEUE_EMPTY(&global_epoll_queue)) {
189    uv__os390_epoll* lst;
190    q = QUEUE_HEAD(&global_epoll_queue);
191    QUEUE_REMOVE(q);
192    lst = QUEUE_DATA(q, uv__os390_epoll, member);
193    uv__free(lst->items);
194    lst->items = NULL;
195    lst->size = 0;
196  }
197
198  uv_mutex_unlock(&global_epoll_lock);
199  uv_mutex_destroy(&global_epoll_lock);
200}
201
202
203static void epoll_init(void) {
204  QUEUE_INIT(&global_epoll_queue);
205  if (uv_mutex_init(&global_epoll_lock))
206    abort();
207
208  if (pthread_atfork(&before_fork, &after_fork, &child_fork))
209    abort();
210}
211
212
213uv__os390_epoll* epoll_create1(int flags) {
214  uv__os390_epoll* lst;
215
216  lst = uv__malloc(sizeof(*lst));
217  if (lst != NULL) {
218    /* initialize list */
219    lst->size = 0;
220    lst->items = NULL;
221    init_message_queue(lst);
222    maybe_resize(lst, 1);
223    lst->items[lst->size - 1].fd = lst->msg_queue;
224    lst->items[lst->size - 1].events = POLLIN;
225    lst->items[lst->size - 1].revents = 0;
226    uv_once(&once, epoll_init);
227    uv_mutex_lock(&global_epoll_lock);
228    QUEUE_INSERT_TAIL(&global_epoll_queue, &lst->member);
229    uv_mutex_unlock(&global_epoll_lock);
230  }
231
232  return lst;
233}
234
235
236int epoll_ctl(uv__os390_epoll* lst,
237              int op,
238              int fd,
239              struct epoll_event *event) {
240  uv_mutex_lock(&global_epoll_lock);
241
242  if (op == EPOLL_CTL_DEL) {
243    if (fd >= lst->size || lst->items[fd].fd == -1) {
244      uv_mutex_unlock(&global_epoll_lock);
245      errno = ENOENT;
246      return -1;
247    }
248    lst->items[fd].fd = -1;
249  } else if (op == EPOLL_CTL_ADD) {
250
251    /* Resizing to 'fd + 1' would expand the list to contain at least
252     * 'fd'. But we need to guarantee that the last index on the list
253     * is reserved for the message queue. So specify 'fd + 2' instead.
254     */
255    maybe_resize(lst, fd + 2);
256    if (lst->items[fd].fd != -1) {
257      uv_mutex_unlock(&global_epoll_lock);
258      errno = EEXIST;
259      return -1;
260    }
261    lst->items[fd].fd = fd;
262    lst->items[fd].events = event->events;
263    lst->items[fd].revents = 0;
264  } else if (op == EPOLL_CTL_MOD) {
265    if (fd >= lst->size - 1 || lst->items[fd].fd == -1) {
266      uv_mutex_unlock(&global_epoll_lock);
267      errno = ENOENT;
268      return -1;
269    }
270    lst->items[fd].events = event->events;
271    lst->items[fd].revents = 0;
272  } else
273    abort();
274
275  uv_mutex_unlock(&global_epoll_lock);
276  return 0;
277}
278
279#define EP_MAX_PFDS (ULONG_MAX / sizeof(struct pollfd))
280#define EP_MAX_EVENTS (INT_MAX / sizeof(struct epoll_event))
281
282int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events,
283               int maxevents, int timeout) {
284  nmsgsfds_t size;
285  struct pollfd* pfds;
286  int pollret;
287  int pollfdret;
288  int pollmsgret;
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  assert(lst->size > 0);
310  _SET_FDS_MSGS(size, 1, lst->size - 1);
311  pfds = lst->items;
312  pollret = poll(pfds, size, timeout);
313  if (pollret <= 0)
314    return pollret;
315
316  pollfdret = _NFDS(pollret);
317  pollmsgret = _NMSGS(pollret);
318
319  reventcount = 0;
320  nevents = 0;
321  msg_fd = pfds[lst->size - 1]; /* message queue is always last entry */
322  maxevents = maxevents - pollmsgret; /* allow spot for message queue */
323  for (i = 0;
324       i < lst->size - 1 &&
325       nevents < maxevents &&
326       reventcount < pollfdret; ++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
338    reventcount++;
339    events[nevents++] = ev;
340  }
341
342  if (pollmsgret > 0 && msg_fd.revents != 0 && msg_fd.fd != -1) {
343    struct epoll_event ev;
344    ev.fd = msg_fd.fd;
345    ev.events = msg_fd.revents;
346    ev.is_msg = 1;
347    events[nevents++] = ev;
348  }
349
350  return nevents;
351}
352
353
354int 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
371void 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
385char* mkdtemp(char* path) {
386  static const char* tempchars =
387    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
388  static const size_t num_chars = 62;
389  static const size_t num_x = 6;
390  char *ep, *cp;
391  unsigned int tries, i;
392  size_t len;
393  uint64_t v;
394  int fd;
395  int retval;
396  int saved_errno;
397
398  len = strlen(path);
399  ep = path + len;
400  if (len < num_x || strncmp(ep - num_x, "XXXXXX", num_x)) {
401    errno = EINVAL;
402    return NULL;
403  }
404
405  fd = open("/dev/urandom", O_RDONLY);
406  if (fd == -1)
407    return NULL;
408
409  tries = TMP_MAX;
410  retval = -1;
411  do {
412    if (read(fd, &v, sizeof(v)) != sizeof(v))
413      break;
414
415    cp = ep - num_x;
416    for (i = 0; i < num_x; i++) {
417      *cp++ = tempchars[v % num_chars];
418      v /= num_chars;
419    }
420
421    if (mkdir(path, S_IRWXU) == 0) {
422      retval = 0;
423      break;
424    }
425    else if (errno != EEXIST)
426      break;
427  } while (--tries);
428
429  saved_errno = errno;
430  uv__close(fd);
431  if (tries == 0) {
432    errno = EEXIST;
433    return NULL;
434  }
435
436  if (retval == -1) {
437    errno = saved_errno;
438    return NULL;
439  }
440
441  return path;
442}
443
444
445ssize_t os390_readlink(const char* path, char* buf, size_t len) {
446  ssize_t rlen;
447  ssize_t vlen;
448  ssize_t plen;
449  char* delimiter;
450  char old_delim;
451  char* tmpbuf;
452  char realpathstr[PATH_MAX + 1];
453
454  tmpbuf = uv__malloc(len + 1);
455  if (tmpbuf == NULL) {
456    errno = ENOMEM;
457    return -1;
458  }
459
460  rlen = readlink(path, tmpbuf, len);
461  if (rlen < 0) {
462    uv__free(tmpbuf);
463    return rlen;
464  }
465
466  if (rlen < 3 || strncmp("/$", tmpbuf, 2) != 0) {
467    /* Straightforward readlink. */
468    memcpy(buf, tmpbuf, rlen);
469    uv__free(tmpbuf);
470    return rlen;
471  }
472
473  /*
474   * There is a parmlib variable at the beginning
475   * which needs interpretation.
476   */
477  tmpbuf[rlen] = '\0';
478  delimiter = strchr(tmpbuf + 2, '/');
479  if (delimiter == NULL)
480    /* No slash at the end */
481    delimiter = strchr(tmpbuf + 2, '\0');
482
483  /* Read real path of the variable. */
484  old_delim = *delimiter;
485  *delimiter = '\0';
486  if (realpath(tmpbuf, realpathstr) == NULL) {
487    uv__free(tmpbuf);
488    return -1;
489  }
490
491  /* realpathstr is not guaranteed to end with null byte.*/
492  realpathstr[PATH_MAX] = '\0';
493
494  /* Reset the delimiter and fill up the buffer. */
495  *delimiter = old_delim;
496  plen = strlen(delimiter);
497  vlen = strlen(realpathstr);
498  rlen = plen + vlen;
499  if (rlen > len) {
500    uv__free(tmpbuf);
501    errno = ENAMETOOLONG;
502    return -1;
503  }
504  memcpy(buf, realpathstr, vlen);
505  memcpy(buf + vlen, delimiter, plen);
506
507  /* Done using temporary buffer. */
508  uv__free(tmpbuf);
509
510  return rlen;
511}
512
513
514int sem_init(UV_PLATFORM_SEM_T* semid, int pshared, unsigned int value) {
515  UNREACHABLE();
516}
517
518
519int sem_destroy(UV_PLATFORM_SEM_T* semid) {
520  UNREACHABLE();
521}
522
523
524int sem_post(UV_PLATFORM_SEM_T* semid) {
525  UNREACHABLE();
526}
527
528
529int sem_trywait(UV_PLATFORM_SEM_T* semid) {
530  UNREACHABLE();
531}
532
533
534int sem_wait(UV_PLATFORM_SEM_T* semid) {
535  UNREACHABLE();
536}
537