text: /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to
   * deal in the Software without restriction, including without limitation the
   * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   * sell copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   * IN THE SOFTWARE.
   */
  
  #include "uv.h"
  #include "internal.h"
  
  #include <stdio.h>
  #include <stdint.h>
  #include <stdlib.h>
  #include <string.h>
  #include <assert.h>
  #include <errno.h>
  
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <sys/ioctl.h>
  #include <net/if.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  
  #include <sys/time.h>
  #include <unistd.h>
  #include <fcntl.h>
  #include <utmp.h>
  #include <libgen.h>
  
  #include <sys/protosw.h>
  #include <libperfstat.h>
  #include <procinfo.h>
  #include <sys/proc.h>
  #include <sys/procfs.h>
  
  #include <sys/poll.h>
  
  #include <sys/pollset.h>
  #include <ctype.h>
  #ifdef HAVE_SYS_AHAFS_EVPRODS_H
  #include <sys/ahafs_evProds.h>
  #endif
  
  #include <sys/mntctl.h>
  #include <sys/vmount.h>
  #include <limits.h>
  #include <strings.h>
  #include <sys/vnode.h>
  
  #define RDWR_BUF_SIZE   4096
  #define EQ(a,b)         (strcmp(a,b) == 0)
  
  static uv_mutex_t process_title_mutex;
  static uv_once_t process_title_mutex_once = UV_ONCE_INIT;
  static void* args_mem = NULL;
  static char** process_argv = NULL;
  static int process_argc = 0;
  static char* process_title_ptr = NULL;
  
  static void init_process_title_mutex_once(void) {
    uv_mutex_init(&process_title_mutex);
  }
  
  
  int uv__platform_loop_init(uv_loop_t* loop) {
    loop->fs_fd = -1;
  
    /* Passing maxfd of -1 should mean the limit is determined
     * by the user's ulimit or the global limit as per the doc */
    loop->backend_fd = pollset_create(-1);
  
    if (loop->backend_fd == -1)
      return -1;
  
    return 0;
  }
  
  
  void uv__platform_loop_delete(uv_loop_t* loop) {
    if (loop->fs_fd != -1) {
      uv__close(loop->fs_fd);
      loop->fs_fd = -1;
    }
  
    if (loop->backend_fd != -1) {
      pollset_destroy(loop->backend_fd);
      loop->backend_fd = -1;
    }
  }
  
  
  int uv__io_fork(uv_loop_t* loop) {
    uv__platform_loop_delete(loop);
  
    return uv__platform_loop_init(loop);
  }
  
  
  int uv__io_check_fd(uv_loop_t* loop, int fd) {
    struct poll_ctl pc;
  
    pc.events = POLLIN;
    pc.cmd = PS_MOD;  /* Equivalent to PS_ADD if the fd is not in the pollset. */
    pc.fd = fd;
  
    if (pollset_ctl(loop->backend_fd, &pc, 1))
      return UV__ERR(errno);
  
    pc.cmd = PS_DELETE;
    if (pollset_ctl(loop->backend_fd, &pc, 1))
      abort();
  
    return 0;
  }
  
  
  void uv__io_poll(uv_loop_t* loop, int timeout) {
    struct pollfd events[1024];
    struct pollfd pqry;
    struct pollfd* pe;
    struct poll_ctl pc;
    QUEUE* q;
    uv__io_t* w;
    uint64_t base;
    uint64_t diff;
    int have_signals;
    int nevents;
    int count;
    int nfds;
    int i;
    int rc;
    int add_failed;
  
    if (loop->nfds == 0) {
      assert(QUEUE_EMPTY(&loop->watcher_queue));
      return;
    }
  
    while (!QUEUE_EMPTY(&loop->watcher_queue)) {
      q = QUEUE_HEAD(&loop->watcher_queue);
      QUEUE_REMOVE(q);
      QUEUE_INIT(q);
  
      w = QUEUE_DATA(q, uv__io_t, watcher_queue);
      assert(w->pevents != 0);
      assert(w->fd >= 0);
      assert(w->fd < (int) loop->nwatchers);
  
      pc.events = w->pevents;
      pc.fd = w->fd;
  
      add_failed = 0;
      if (w->events == 0) {
        pc.cmd = PS_ADD;
        if (pollset_ctl(loop->backend_fd, &pc, 1)) {
          if (errno != EINVAL) {
            assert(0 && "Failed to add file descriptor (pc.fd) to pollset");
            abort();
          }
          /* Check if the fd is already in the pollset */
          pqry.fd = pc.fd;
          rc = pollset_query(loop->backend_fd, &pqry);
          switch (rc) {
          case -1:
            assert(0 && "Failed to query pollset for file descriptor");
            abort();
          case 0:
            assert(0 && "Pollset does not contain file descriptor");
            abort();
          }
          /* If we got here then the pollset already contained the file descriptor even though
           * we didn't think it should. This probably shouldn't happen, but we can continue. */
          add_failed = 1;
        }
      }
      if (w->events != 0 || add_failed) {
        /* Modify, potentially removing events -- need to delete then add.
         * Could maybe mod if we knew for sure no events are removed, but
         * content of w->events is handled above as not reliable (falls back)
         * so may require a pollset_query() which would have to be pretty cheap
         * compared to a PS_DELETE to be worth optimizing. Alternatively, could
         * lazily remove events, squelching them in the mean time. */
        pc.cmd = PS_DELETE;
        if (pollset_ctl(loop->backend_fd, &pc, 1)) {
          assert(0 && "Failed to delete file descriptor (pc.fd) from pollset");
          abort();
        }
        pc.cmd = PS_ADD;
        if (pollset_ctl(loop->backend_fd, &pc, 1)) {
          assert(0 && "Failed to add file descriptor (pc.fd) to pollset");
          abort();
        }
      }
  
      w->events = w->pevents;
    }
  
    assert(timeout >= -1);
    base = loop->time;
    count = 48; /* Benchmarks suggest this gives the best throughput. */
  
    for (;;) {
      nfds = pollset_poll(loop->backend_fd,
                          events,
                          ARRAY_SIZE(events),
                          timeout);
  
      /* Update loop->time unconditionally. It's tempting to skip the update when
       * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
       * operating system didn't reschedule our process while in the syscall.
       */
      SAVE_ERRNO(uv__update_time(loop));
  
      if (nfds == 0) {
        assert(timeout != -1);
        return;
      }
  
      if (nfds == -1) {
        if (errno != EINTR) {
          abort();
        }
  
        if (timeout == -1)
          continue;
  
        if (timeout == 0)
          return;
  
        /* Interrupted by a signal. Update timeout and poll again. */
        goto update_timeout;
      }
  
      have_signals = 0;
      nevents = 0;
  
      assert(loop->watchers != NULL);
      loop->watchers[loop->nwatchers] = (void*) events;
      loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
  
      for (i = 0; i < nfds; i++) {
        pe = events + i;
        pc.cmd = PS_DELETE;
        pc.fd = pe->fd;
  
        /* Skip invalidated events, see uv__platform_invalidate_fd */
        if (pc.fd == -1)
          continue;
  
        assert(pc.fd >= 0);
        assert((unsigned) pc.fd < loop->nwatchers);
  
        w = loop->watchers[pc.fd];
  
        if (w == NULL) {
          /* File descriptor that we've stopped watching, disarm it.
           *
           * Ignore all errors because we may be racing with another thread
           * when the file descriptor is closed.
           */
          pollset_ctl(loop->backend_fd, &pc, 1);
          continue;
        }
  
        /* Run signal watchers last.  This also affects child process watchers
         * because those are implemented in terms of signal watchers.
         */
        if (w == &loop->signal_io_watcher)
          have_signals = 1;
        else
          w->cb(loop, w, pe->revents);
  
        nevents++;
      }
  
      if (have_signals != 0)
        loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
  
      loop->watchers[loop->nwatchers] = NULL;
      loop->watchers[loop->nwatchers + 1] = NULL;
  
      if (have_signals != 0)
        return;  /* Event loop should cycle now so don't poll again. */
  
      if (nevents != 0) {
        if (nfds == ARRAY_SIZE(events) && --count != 0) {
          /* Poll for more events but don't block this time. */
          timeout = 0;
          continue;
        }
        return;
      }
  
      if (timeout == 0)
        return;
  
      if (timeout == -1)
        continue;
  
  update_timeout:
      assert(timeout > 0);
  
      diff = loop->time - base;
      if (diff >= (uint64_t) timeout)
        return;
  
      timeout -= diff;
    }
  }
  
  
  uint64_t uv_get_free_memory(void) {
    perfstat_memory_total_t mem_total;
    int result = perfstat_memory_total(NULL, &mem_total, sizeof(mem_total), 1);
    if (result == -1) {
      return 0;
    }
    return mem_total.real_free * 4096;
  }
  
  
  uint64_t uv_get_total_memory(void) {
    perfstat_memory_total_t mem_total;
    int result = perfstat_memory_total(NULL, &mem_total, sizeof(mem_total), 1);
    if (result == -1) {
      return 0;
    }
    return mem_total.real_total * 4096;
  }
  
  
  void uv_loadavg(double avg[3]) {
    perfstat_cpu_total_t ps_total;
    int result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1);
    if (result == -1) {
      avg[0] = 0.; avg[1] = 0.; avg[2] = 0.;
      return;
    }
    avg[0] = ps_total.loadavg[0] / (double)(1 << SBITS);
    avg[1] = ps_total.loadavg[1] / (double)(1 << SBITS);
    avg[2] = ps_total.loadavg[2] / (double)(1 << SBITS);
  }
  
  
  #ifdef HAVE_SYS_AHAFS_EVPRODS_H
  static char* uv__rawname(const char* cp, char (*dst)[FILENAME_MAX+1]) {
    char* dp;
  
    dp = rindex(cp, '/');
    if (dp == 0)
      return 0;
  
    snprintf(*dst, sizeof(*dst), "%.*s/r%s", (int) (dp - cp), cp, dp + 1);
    return *dst;
  }
  
  
  /*
   * Determine whether given pathname is a directory
   * Returns 0 if the path is a directory, -1 if not
   *
   * Note: Opportunity here for more detailed error information but
   *       that requires changing callers of this function as well
   */
  static int uv__path_is_a_directory(char* filename) {
    struct stat statbuf;
  
    if (stat(filename, &statbuf) < 0)
      return -1;  /* failed: not a directory, assume it is a file */
  
    if (statbuf.st_type == VDIR)
      return 0;
  
    return -1;
  }
  
  
  /*
   * Check whether AHAFS is mounted.
   * Returns 0 if AHAFS is mounted, or an error code < 0 on failure
   */
  static int uv__is_ahafs_mounted(void){
    char rawbuf[FILENAME_MAX+1];
    int rv, i = 2;
    struct vmount *p;
    int size_multiplier = 10;
    size_t siz = sizeof(struct vmount)*size_multiplier;
    struct vmount *vmt;
    const char *dev = "/aha";
    char *obj, *stub;
  
    p = uv__malloc(siz);
    if (p == NULL)
      return UV__ERR(errno);
  
    /* Retrieve all mounted filesystems */
    rv = mntctl(MCTL_QUERY, siz, (char*)p);
    if (rv < 0)
      return UV__ERR(errno);
    if (rv == 0) {
      /* buffer was not large enough, reallocate to correct size */
      siz = *(int*)p;
      uv__free(p);
      p = uv__malloc(siz);
      if (p == NULL)
        return UV__ERR(errno);
      rv = mntctl(MCTL_QUERY, siz, (char*)p);
      if (rv < 0)
        return UV__ERR(errno);
    }
  
    /* Look for dev in filesystems mount info */
    for(vmt = p, i = 0; i < rv; i++) {
      obj = vmt2dataptr(vmt, VMT_OBJECT);     /* device */
      stub = vmt2dataptr(vmt, VMT_STUB);      /* mount point */
  
      if (EQ(obj, dev) || EQ(uv__rawname(obj, &rawbuf), dev) || EQ(stub, dev)) {
        uv__free(p);  /* Found a match */
        return 0;
      }
      vmt = (struct vmount *) ((char *) vmt + vmt->vmt_length);
    }
  
    /* /aha is required for monitoring filesystem changes */
    return -1;
  }
  
  /*
   * Recursive call to mkdir() to create intermediate folders, if any
   * Returns code from mkdir call
   */
  static int uv__makedir_p(const char *dir) {
    char tmp[256];
    char *p = NULL;
    size_t len;
    int err;
  
    /* TODO(bnoordhuis) Check uv__strscpy() return value. */
    uv__strscpy(tmp, dir, sizeof(tmp));
    len = strlen(tmp);
    if (tmp[len - 1] == '/')
      tmp[len - 1] = 0;
    for (p = tmp + 1; *p; p++) {
      if (*p == '/') {
        *p = 0;
        err = mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
        if (err != 0 && errno != EEXIST)
          return err;
        *p = '/';
      }
    }
    return mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
  }
  
  /*
   * Creates necessary subdirectories in the AIX Event Infrastructure
   * file system for monitoring the object specified.
   * Returns code from mkdir call
   */
  static int uv__make_subdirs_p(const char *filename) {
    char cmd[2048];
    char *p;
    int rc = 0;
  
    /* Strip off the monitor file name */
    p = strrchr(filename, '/');
  
    if (p == NULL)
      return 0;
  
    if (uv__path_is_a_directory((char*)filename) == 0) {
      sprintf(cmd, "/aha/fs/modDir.monFactory");
    } else {
      sprintf(cmd, "/aha/fs/modFile.monFactory");
    }
  
    strncat(cmd, filename, (p - filename));
    rc = uv__makedir_p(cmd);
  
    if (rc == -1 && errno != EEXIST){
      return UV__ERR(errno);
    }
  
    return rc;
  }
  
  
  /*
   * Checks if /aha is mounted, then proceeds to set up the monitoring
   * objects for the specified file.
   * Returns 0 on success, or an error code < 0 on failure
   */
  static int uv__setup_ahafs(const char* filename, int *fd) {
    int rc = 0;
    char mon_file_write_string[RDWR_BUF_SIZE];
    char mon_file[PATH_MAX];
    int file_is_directory = 0; /* -1 == NO, 0 == YES  */
  
    /* Create monitor file name for object */
    file_is_directory = uv__path_is_a_directory((char*)filename);
  
    if (file_is_directory == 0)
      sprintf(mon_file, "/aha/fs/modDir.monFactory");
    else
      sprintf(mon_file, "/aha/fs/modFile.monFactory");
  
    if ((strlen(mon_file) + strlen(filename) + 5) > PATH_MAX)
      return UV_ENAMETOOLONG;
  
    /* Make the necessary subdirectories for the monitor file */
    rc = uv__make_subdirs_p(filename);
    if (rc == -1 && errno != EEXIST)
      return rc;
  
    strcat(mon_file, filename);
    strcat(mon_file, ".mon");
  
    *fd = 0; errno = 0;
  
    /* Open the monitor file, creating it if necessary */
    *fd = open(mon_file, O_CREAT|O_RDWR);
    if (*fd < 0)
      return UV__ERR(errno);
  
    /* Write out the monitoring specifications.
     * In this case, we are monitoring for a state change event type
     *    CHANGED=YES
     * We will be waiting in select call, rather than a read:
     *    WAIT_TYPE=WAIT_IN_SELECT
     * We only want minimal information for files:
     *      INFO_LVL=1
     * For directories, we want more information to track what file
     * caused the change
     *      INFO_LVL=2
     */
  
    if (file_is_directory == 0)
      sprintf(mon_file_write_string, "CHANGED=YES;WAIT_TYPE=WAIT_IN_SELECT;INFO_LVL=2");
    else
      sprintf(mon_file_write_string, "CHANGED=YES;WAIT_TYPE=WAIT_IN_SELECT;INFO_LVL=1");
  
    rc = write(*fd, mon_file_write_string, strlen(mon_file_write_string)+1);
    if (rc < 0)
      return UV__ERR(errno);
  
    return 0;
  }
  
  /*
   * Skips a specified number of lines in the buffer passed in.
   * Walks the buffer pointed to by p and attempts to skip n lines.
   * Returns the total number of lines skipped
   */
  static int uv__skip_lines(char **p, int n) {
    int lines = 0;
  
    while(n > 0) {
      *p = strchr(*p, '\n');
      if (!p)
        return lines;
  
      (*p)++;
      n--;
      lines++;
    }
    return lines;
  }
  
  
  /*
   * Parse the event occurrence data to figure out what event just occurred
   * and take proper action.
   *
   * The buf is a pointer to the buffer containing the event occurrence data
   * Returns 0 on success, -1 if unrecoverable error in parsing
   *
   */
  static int uv__parse_data(char *buf, int *events, uv_fs_event_t* handle) {
    int    evp_rc, i;
    char   *p;
    char   filename[PATH_MAX]; /* To be used when handling directories */
  
    p = buf;
    *events = 0;
  
    /* Clean the filename buffer*/
    for(i = 0; i < PATH_MAX; i++) {
      filename[i] = 0;
    }
    i = 0;
  
    /* Check for BUF_WRAP */
    if (strncmp(buf, "BUF_WRAP", strlen("BUF_WRAP")) == 0) {
      assert(0 && "Buffer wrap detected, Some event occurrences lost!");
      return 0;
    }
  
    /* Since we are using the default buffer size (4K), and have specified
     * INFO_LVL=1, we won't see any EVENT_OVERFLOW conditions.  Applications
     * should check for this keyword if they are using an INFO_LVL of 2 or
     * higher, and have a buffer size of <= 4K
     */
  
    /* Skip to RC_FROM_EVPROD */
    if (uv__skip_lines(&p, 9) != 9)
      return -1;
  
    if (sscanf(p, "RC_FROM_EVPROD=%d\nEND_EVENT_DATA", &evp_rc) == 1) {
      if (uv__path_is_a_directory(handle->path) == 0) { /* Directory */
        if (evp_rc == AHAFS_MODDIR_UNMOUNT || evp_rc == AHAFS_MODDIR_REMOVE_SELF) {
          /* The directory is no longer available for monitoring */
          *events = UV_RENAME;
          handle->dir_filename = NULL;
        } else {
          /* A file was added/removed inside the directory */
          *events = UV_CHANGE;
  
          /* Get the EVPROD_INFO */
          if (uv__skip_lines(&p, 1) != 1)
            return -1;
  
          /* Scan out the name of the file that triggered the event*/
          if (sscanf(p, "BEGIN_EVPROD_INFO\n%sEND_EVPROD_INFO", filename) == 1) {
            handle->dir_filename = uv__strdup((const char*)&filename);
          } else
            return -1;
          }
      } else { /* Regular File */
        if (evp_rc == AHAFS_MODFILE_RENAME)
          *events = UV_RENAME;
        else
          *events = UV_CHANGE;
      }
    }
    else
      return -1;
  
    return 0;
  }
  
  
  /* This is the internal callback */
  static void uv__ahafs_event(uv_loop_t* loop, uv__io_t* event_watch, unsigned int fflags) {
    char   result_data[RDWR_BUF_SIZE];
    int bytes, rc = 0;
    uv_fs_event_t* handle;
    int events = 0;
    char fname[PATH_MAX];
    char *p;
  
    handle = container_of(event_watch, uv_fs_event_t, event_watcher);
  
    /* At this point, we assume that polling has been done on the
     * file descriptor, so we can just read the AHAFS event occurrence
     * data and parse its results without having to block anything
     */
    bytes = pread(event_watch->fd, result_data, RDWR_BUF_SIZE, 0);
  
    assert((bytes >= 0) && "uv__ahafs_event - Error reading monitor file");
  
    /* In file / directory move cases, AIX Event infrastructure
     * produces a second event with no data.
     * Ignore it and return gracefully.
     */
    if(bytes == 0)
      return;
  
    /* Parse the data */
    if(bytes > 0)
      rc = uv__parse_data(result_data, &events, handle);
  
    /* Unrecoverable error */
    if (rc == -1)
      return;
  
    /* For directory changes, the name of the files that triggered the change
     * are never absolute pathnames
     */
    if (uv__path_is_a_directory(handle->path) == 0) {
      p = handle->dir_filename;
    } else {
      p = strrchr(handle->path, '/');
      if (p == NULL)
        p = handle->path;
      else
        p++;
    }
  
    /* TODO(bnoordhuis) Check uv__strscpy() return value. */
    uv__strscpy(fname, p, sizeof(fname));
  
    handle->cb(handle, fname, events, 0);
  }
  #endif
  
  
  int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
  #ifdef HAVE_SYS_AHAFS_EVPRODS_H
    uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
    return 0;
  #else
    return UV_ENOSYS;
  #endif
  }
  
  
  int uv_fs_event_start(uv_fs_event_t* handle,
                        uv_fs_event_cb cb,
                        const char* filename,
                        unsigned int flags) {
  #ifdef HAVE_SYS_AHAFS_EVPRODS_H
    int  fd, rc, str_offset = 0;
    char cwd[PATH_MAX];
    char absolute_path[PATH_MAX];
    char readlink_cwd[PATH_MAX];
  
  
    /* Figure out whether filename is absolute or not */
    if (filename[0] == '/') {
      /* We have absolute pathname */
      /* TODO(bnoordhuis) Check uv__strscpy() return value. */
      uv__strscpy(absolute_path, filename, sizeof(absolute_path));
    } else {
      /* We have a relative pathname, compose the absolute pathname */
      snprintf(cwd, sizeof(cwd), "/proc/%lu/cwd", (unsigned long) getpid());
      rc = readlink(cwd, readlink_cwd, sizeof(readlink_cwd) - 1);
      if (rc < 0)
        return rc;
      /* readlink does not null terminate our string */
      readlink_cwd[rc] = '\0';
  
      if (filename[0] == '.' && filename[1] == '/')
        str_offset = 2;
  
      snprintf(absolute_path, sizeof(absolute_path), "%s%s", readlink_cwd,
               filename + str_offset);
    }
  
    if (uv__is_ahafs_mounted() < 0)  /* /aha checks failed */
      return UV_ENOSYS;
  
    /* Setup ahafs */
    rc = uv__setup_ahafs((const char *)absolute_path, &fd);
    if (rc != 0)
      return rc;
  
    /* Setup/Initialize all the libuv routines */
    uv__handle_start(handle);
    uv__io_init(&handle->event_watcher, uv__ahafs_event, fd);
    handle->path = uv__strdup(filename);
    handle->cb = cb;
    handle->dir_filename = NULL;
  
    uv__io_start(handle->loop, &handle->event_watcher, POLLIN);
  
    return 0;
  #else
    return UV_ENOSYS;
  #endif
  }
  
  
  int uv_fs_event_stop(uv_fs_event_t* handle) {
  #ifdef HAVE_SYS_AHAFS_EVPRODS_H
    if (!uv__is_active(handle))
      return 0;
  
    uv__io_close(handle->loop, &handle->event_watcher);
    uv__handle_stop(handle);
  
    if (uv__path_is_a_directory(handle->path) == 0) {
      uv__free(handle->dir_filename);
      handle->dir_filename = NULL;
    }
  
    uv__free(handle->path);
    handle->path = NULL;
    uv__close(handle->event_watcher.fd);
    handle->event_watcher.fd = -1;
  
    return 0;
  #else
    return UV_ENOSYS;
  #endif
  }
  
  
  void uv__fs_event_close(uv_fs_event_t* handle) {
  #ifdef HAVE_SYS_AHAFS_EVPRODS_H
    uv_fs_event_stop(handle);
  #else
    UNREACHABLE();
  #endif
  }
  
  
  char** uv_setup_args(int argc, char** argv) {
    char** new_argv;
    size_t size;
    char* s;
    int i;
  
    if (argc <= 0)
      return argv;
  
    /* Save the original pointer to argv.
     * AIX uses argv to read the process name.
     * (Not the memory pointed to by argv[0..n] as on Linux.)
     */
    process_argv = argv;
    process_argc = argc;
  
    /* Calculate how much memory we need for the argv strings. */
    size = 0;
    for (i = 0; i < argc; i++)
      size += strlen(argv[i]) + 1;
  
    /* Add space for the argv pointers. */
    size += (argc + 1) * sizeof(char*);
  
    new_argv = uv__malloc(size);
    if (new_argv == NULL)
      return argv;
    args_mem = new_argv;
  
    /* Copy over the strings and set up the pointer table. */
    s = (char*) &new_argv[argc + 1];
    for (i = 0; i < argc; i++) {
      size = strlen(argv[i]) + 1;
      memcpy(s, argv[i], size);
      new_argv[i] = s;
      s += size;
    }
    new_argv[i] = NULL;
  
    return new_argv;
  }
  
  
  int uv_set_process_title(const char* title) {
    char* new_title;
  
    /* We cannot free this pointer when libuv shuts down,
     * the process may still be using it.
     */
    new_title = uv__strdup(title);
    if (new_title == NULL)
      return UV_ENOMEM;
  
    uv_once(&process_title_mutex_once, init_process_title_mutex_once);
    uv_mutex_lock(&process_title_mutex);
  
    /* If this is the first time this is set,
     * don't free and set argv[1] to NULL.
     */
    if (process_title_ptr != NULL)
      uv__free(process_title_ptr);
  
    process_title_ptr = new_title;
  
    process_argv[0] = process_title_ptr;
    if (process_argc > 1)
       process_argv[1] = NULL;
  
    uv_mutex_unlock(&process_title_mutex);
  
    return 0;
  }
  
  
  int uv_get_process_title(char* buffer, size_t size) {
    size_t len;
    if (buffer == NULL || size == 0)
      return UV_EINVAL;
  
    uv_once(&process_title_mutex_once, init_process_title_mutex_once);
    uv_mutex_lock(&process_title_mutex);
  
    len = strlen(process_argv[0]);
    if (size <= len) {
      uv_mutex_unlock(&process_title_mutex);
      return UV_ENOBUFS;
    }
  
    memcpy(buffer, process_argv[0], len);
    buffer[len] = '\0';
  
    uv_mutex_unlock(&process_title_mutex);
  
    return 0;
  }
  
  
  UV_DESTRUCTOR(static void free_args_mem(void)) {
    uv__free(args_mem);  /* Keep valgrind happy. */
    args_mem = NULL;
  }
  
  
  int uv_resident_set_memory(size_t* rss) {
    char pp[64];
    psinfo_t psinfo;
    int err;
    int fd;
  
    snprintf(pp, sizeof(pp), "/proc/%lu/psinfo", (unsigned long) getpid());
  
    fd = open(pp, O_RDONLY);
    if (fd == -1)
      return UV__ERR(errno);
  
    /* FIXME(bnoordhuis) Handle EINTR. */
    err = UV_EINVAL;
    if (read(fd, &psinfo, sizeof(psinfo)) == sizeof(psinfo)) {
      *rss = (size_t)psinfo.pr_rssize * 1024;
      err = 0;
    }
    uv__close(fd);
  
    return err;
  }
  
  
  int uv_uptime(double* uptime) {
    struct utmp *utmp_buf;
    size_t entries = 0;
    time_t boot_time;
  
    boot_time = 0;
    utmpname(UTMP_FILE);
  
    setutent();
  
    while ((utmp_buf = getutent()) != NULL) {
      if (utmp_buf->ut_user[0] && utmp_buf->ut_type == USER_PROCESS)
        ++entries;
      if (utmp_buf->ut_type == BOOT_TIME)
        boot_time = utmp_buf->ut_time;
    }
  
    endutent();
  
    if (boot_time == 0)
      return UV_ENOSYS;
  
    *uptime = time(NULL) - boot_time;
    return 0;
  }
  
  
  int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
    uv_cpu_info_t* cpu_info;
    perfstat_cpu_total_t ps_total;
    perfstat_cpu_t* ps_cpus;
    perfstat_id_t cpu_id;
    int result, ncpus, idx = 0;
  
    result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1);
    if (result == -1) {
      return UV_ENOSYS;
    }
  
    ncpus = result = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0);
    if (result == -1) {
      return UV_ENOSYS;
    }
  
    ps_cpus = (perfstat_cpu_t*) uv__malloc(ncpus * sizeof(perfstat_cpu_t));
    if (!ps_cpus) {
      return UV_ENOMEM;
    }
  
    /* TODO(bnoordhuis) Check uv__strscpy() return value. */
    uv__strscpy(cpu_id.name, FIRST_CPU, sizeof(cpu_id.name));
    result = perfstat_cpu(&cpu_id, ps_cpus, sizeof(perfstat_cpu_t), ncpus);
    if (result == -1) {
      uv__free(ps_cpus);
      return UV_ENOSYS;
    }
  
    *cpu_infos = (uv_cpu_info_t*) uv__malloc(ncpus * sizeof(uv_cpu_info_t));
    if (!*cpu_infos) {
      uv__free(ps_cpus);
      return UV_ENOMEM;
    }
  
    *count = ncpus;
  
    cpu_info = *cpu_infos;
    while (idx < ncpus) {
      cpu_info->speed = (int)(ps_total.processorHZ / 1000000);
      cpu_info->model = uv__strdup(ps_total.description);
      cpu_info->cpu_times.user = ps_cpus[idx].user;
      cpu_info->cpu_times.sys = ps_cpus[idx].sys;
      cpu_info->cpu_times.idle = ps_cpus[idx].idle;
      cpu_info->cpu_times.irq = ps_cpus[idx].wait;
      cpu_info->cpu_times.nice = 0;
      cpu_info++;
      idx++;
    }
  
    uv__free(ps_cpus);
    return 0;
  }
  
  
  void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
    struct pollfd* events;
    uintptr_t i;
    uintptr_t nfds;
    struct poll_ctl pc;
  
    assert(loop->watchers != NULL);
  
    events = (struct pollfd*) loop->watchers[loop->nwatchers];
    nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
  
    if (events != NULL)
      /* Invalidate events with same file descriptor */
      for (i = 0; i < nfds; i++)
        if ((int) events[i].fd == fd)
          events[i].fd = -1;
  
    /* Remove the file descriptor from the poll set */
    pc.events = 0;
    pc.cmd = PS_DELETE;
    pc.fd = fd;
    if(loop->backend_fd >= 0)
      pollset_ctl(loop->backend_fd, &pc, 1);
  }
  