コード例 #1
0
ファイル: fuse.c プロジェクト: msmhrt/libguestfs
int
guestfs__mount_local_run (guestfs_h *g)
{
  int r, mounted;

  gl_lock_lock (mount_local_lock);
  mounted = g->localmountpoint != NULL;
  gl_lock_unlock (mount_local_lock);

  if (!mounted) {
    error (g, _("you must call guestfs_mount_local first"));
    return -1;
  }

  debug (g, "%s: entering fuse_loop", __func__);

  /* Enter the main loop. */
  r = fuse_loop (g->fuse);
  if (r != 0)
    perrorf (g, _("fuse_loop: %s"), g->localmountpoint);

  debug (g, "%s: leaving fuse_loop", __func__);

  guestfs___free_fuse (g);
  gl_lock_lock (mount_local_lock);
  g->localmountpoint = NULL;
  gl_lock_unlock (mount_local_lock);

  /* By inspection, I found that fuse_loop only returns 0 or -1, but
   * don't rely on this in future.
   */
  return r == 0 ? 0 : -1;
}
コード例 #2
0
ファイル: test-cond.c プロジェクト: cooljeanius/wget
void
test_cond ()
{
  int remain = 2;
  gl_thread_t thread;

  cond_value = 0;

  thread = gl_thread_create (cond_routine, NULL);
  do
    {
      yield ();
      remain = sleep (remain);
    }
  while (remain);

  /* signal condition */
  gl_lock_lock (lockcond);
  cond_value = 1;
  gl_cond_signal (condtest);
  gl_lock_unlock (lockcond);

  gl_thread_join (thread, NULL);

  if (cond_value != 2)
    abort ();
}
コード例 #3
0
ファイル: lpj.c プロジェクト: ArikaChen/libguestfs
int
guestfs___get_lpj (guestfs_h *g)
{
  int r;

  gl_lock_lock (lpj_lock);
  if (lpj != 0)
    goto out;

  /* Try reading lpj from these sources:
   * - /proc/cpuinfo [in future]
   * - dmesg
   * - files:
   *   + /var/log/dmesg
   *   + /var/log/boot.msg
   */
  r = read_lpj_from_dmesg (g);
  if (r > 0) {
    lpj = r;
    goto out;
  }
  lpj = read_lpj_from_files (g);

 out:
  gl_lock_unlock (lpj_lock);
  return lpj;
}
コード例 #4
0
ファイル: fuse.c プロジェクト: noxdafox/libguestfs
int
guestfs_impl_mount_local_run (guestfs_h *g)
{
  int r, mounted;

  gl_lock_lock (mount_local_lock);
  mounted = g->localmountpoint != NULL;
  gl_lock_unlock (mount_local_lock);

  if (!mounted) {
    error (g, _("you must call guestfs_mount_local first"));
    return -1;
  }

  /* Test if root is mounted.  We do this by using a side-effect of
   * guestfs_exists (which is that it calls NEED_ROOT).
   */
  guestfs_push_error_handler (g, NULL, NULL);
  r = guestfs_exists (g, "/");
  guestfs_pop_error_handler (g);
  if (r == -1) {
    error (g, _("you must call 'guestfs_mount' first to mount a filesystem on '/'.\nNote: '%s' is still mounted.  Use 'guestunmount %s' to clean up."),
           g->localmountpoint, g->localmountpoint);
    return -1;
  }

  debug (g, "%s: entering fuse_loop", __func__);

  /* Enter the main loop. */
  r = fuse_loop (g->fuse);
  if (r != 0)
    perrorf (g, _("fuse_loop: %s"), g->localmountpoint);

  debug (g, "%s: leaving fuse_loop", __func__);

  guestfs_int_free_fuse (g);
  gl_lock_lock (mount_local_lock);
  g->localmountpoint = NULL;
  gl_lock_unlock (mount_local_lock);

  /* By inspection, I found that fuse_loop only returns 0 or -1, but
   * don't rely on this in future.
   */
  return r == 0 ? 0 : -1;
}
コード例 #5
0
ファイル: osinfo.c プロジェクト: rbuj/libguestfs
/* Given one or more fields from the header of a CD/DVD/ISO, look up
 * the media in the libosinfo database and return our best guess for
 * the operating system.
 *
 * This returns:
 *   -1 => a fatal error ('error' has been called, caller must not ignore it)
 *   0  => could not locate the OS
 *   1  => matching OS found, the osinfo_ret pointer has been filled in
 */
int
guestfs_int_osinfo_map (guestfs_h *g, const struct guestfs_isoinfo *isoinfo,
                      const struct osinfo **osinfo_ret)
{
  size_t i;

  /* We only need to lock the database when reading it for the first time. */
  gl_lock_lock (osinfo_db_lock);
  if (osinfo_db_size == 0) {
    if (read_osinfo_db (g) == -1) {
      gl_lock_unlock (osinfo_db_lock);
      return -1;
    }
  }
  gl_lock_unlock (osinfo_db_lock);

  if (osinfo_db_size <= 0)
    return 0;

  /* Look in the database to see if we can find a match. */
  for (i = 0; i < (size_t) osinfo_db_size; ++i) {
    if (osinfo_db[i].re_system_id) {
      if (!isoinfo->iso_system_id ||
          !match (g, isoinfo->iso_system_id, osinfo_db[i].re_system_id))
        continue;
    }

    if (osinfo_db[i].re_volume_id) {
      if (!isoinfo->iso_volume_id ||
          !match (g, isoinfo->iso_volume_id, osinfo_db[i].re_volume_id))
        continue;
    }

    if (osinfo_db[i].re_publisher_id) {
      if (!isoinfo->iso_publisher_id ||
          !match (g, isoinfo->iso_publisher_id, osinfo_db[i].re_publisher_id))
        continue;
    }

    if (osinfo_db[i].re_application_id) {
      if (!isoinfo->iso_application_id ||
          !match (g, isoinfo->iso_application_id, osinfo_db[i].re_application_id))
        continue;
    }

    debug (g, "osinfo: mapped disk to database entry %zu", i);

    if (osinfo_ret)
      *osinfo_ret = &osinfo_db[i];
    return 1;
  }

  debug (g, "osinfo: no mapping found");

  return 0;
}
コード例 #6
0
ファイル: requiem.c プロジェクト: requiem-forasiem/librequiem
void requiem_fork_prepare(void)
{
#ifdef HAVE_PTHREAD_ATFORK
        return;
#endif

        _requiem_async_fork_prepare();
        _requiem_timer_fork_prepare();

        _idmef_path_cache_lock();
        gl_lock_lock(_criteria_parse_mutex);
}
コード例 #7
0
/**
 * prelude_client_profile_get_prefix:
 * @cp: pointer on a #prelude_client_profile_t object.
 * @buf: buffer to write the returned filename to.
 * @size: size of @buf.
 *
 * Retrieve current prefix used with this profile.
 */
void prelude_client_profile_get_prefix(const prelude_client_profile_t *cp, char *buf, size_t size)
{
        const char *prefix;

        prelude_return_if_fail(buf);

        gl_lock_lock(lock);

        prefix = init_once_and_get_prefix();
        snprintf(buf, size, "%s", prefix);

        gl_lock_unlock(lock);
}
コード例 #8
0
ファイル: test-cond.c プロジェクト: cooljeanius/wget
static void *
cond_routine (void *arg)
{
  gl_lock_lock (lockcond);
  while (!cond_value)
    {
      gl_cond_wait (condtest, lockcond);
    }
  gl_lock_unlock (lockcond);

  cond_value = 2;

  return NULL;
}
コード例 #9
0
ファイル: handle.c プロジェクト: myyyy/libguestfs
static void
init_libguestfs (void)
{
  gl_lock_lock (init_lock);

#ifdef HAVE_LIBVIRT
  virInitialize ();
#endif

  xmlInitParser ();
  LIBXML_TEST_VERSION;

  gl_lock_unlock (init_lock);
}
コード例 #10
0
/**
 * prelude_client_profile_get_analyzerid_filename:
 * @cp: pointer on a #prelude_client_profile_t object.
 * @buf: buffer to write the returned filename to.
 * @size: size of @buf.
 *
 * Writes the filename used to store @cp unique and permanent analyzer ident.
 */
void prelude_client_profile_get_default_config_dirname(const prelude_client_profile_t *cp, char *buf, size_t size)
{
        const char *prefix;

        prelude_return_if_fail(buf);

        gl_lock_lock(lock);

        prefix = init_once_and_get_prefix();
        if ( ! relative_config_default_dir )
                snprintf(buf, size, "%s", PRELUDE_CONFIG_DEFAULT_DIR);
        else
                snprintf(buf, size, "%s/%s", prefix, relative_config_default_dir);

        gl_lock_unlock(lock);
}
コード例 #11
0
ファイル: fuse.c プロジェクト: noxdafox/libguestfs
int
guestfs_impl_umount_local (guestfs_h *g,
			   const struct guestfs_umount_local_argv *optargs)
{
  const char *retry;
  int r;
  CLEANUP_FREE char *localmountpoint = NULL;
  CLEANUP_CMD_CLOSE struct command *cmd = NULL;

  /* How many times should we try the fusermount command? */
  if (optargs->bitmask & GUESTFS_UMOUNT_LOCAL_RETRY_BITMASK)
    retry = optargs->retry ? "--retry=5" : "--no-retry";
  else
    retry = "--no-retry";

  /* Make a local copy of g->localmountpoint.  It could be freed from
   * under us by another thread, except when we are holding the lock.
   */
  gl_lock_lock (mount_local_lock);
  if (g->localmountpoint)
    localmountpoint = safe_strdup (g, g->localmountpoint);
  else
    localmountpoint = NULL;
  gl_lock_unlock (mount_local_lock);

  if (!localmountpoint) {
    error (g, _("no filesystem is mounted"));
    return -1;
  }

  /* Run guestunmount --retry=... localmountpoint. */
  cmd = guestfs_int_new_command (g);
  guestfs_int_cmd_add_arg (cmd, "guestunmount");
  guestfs_int_cmd_add_arg (cmd, retry);
  guestfs_int_cmd_add_arg (cmd, localmountpoint);
  r = guestfs_int_cmd_run (cmd);
  if (r == -1)
    return -1;
  if (WIFEXITED (r) && WEXITSTATUS (r) == EXIT_SUCCESS)
    /* External fusermount succeeded.  Note that the original thread
     * is responsible for setting g->localmountpoint to NULL.
     */
    return 0;

  return -1;
}
コード例 #12
0
/**
 * prelude_client_profile_get_tls_client_keycert_filename:
 * @cp: pointer on a #prelude_client_profile_t object.
 * @buf: buffer to write the returned filename to.
 * @size: size of @buf.
 *
 * Writes the filename used to store public certificate for @cp private key.
 * This only apply to client connecting to a peer.
 */
void prelude_client_profile_get_tls_client_keycert_filename(const prelude_client_profile_t *cp, char *buf, size_t size)
{
        const char *prefix;

        prelude_return_if_fail(cp);
        prelude_return_if_fail(buf);

        gl_lock_lock(lock);

        prefix = init_once_and_get_prefix();
        if ( ! relative_profile_dir )
                snprintf(buf, size, "%s/%s/client.keycrt", PRELUDE_PROFILE_DIR, cp->name);
        else
                snprintf(buf, size, "%s/%s/%s/client.keycrt", prefix, relative_profile_dir, cp->name);

        gl_lock_unlock(lock);
}
コード例 #13
0
/**
 * prelude_client_profile_get_backup_dirname:
 * @cp: pointer on a #prelude_client_profile_t object.
 * @buf: buffer to write the returned filename to.
 * @size: size of @buf.
 *
 * Writes the directory name where message sent by @cp will be stored,
 * in case writing the message to the peer fail.
 */
void prelude_client_profile_get_backup_dirname(const prelude_client_profile_t *cp, char *buf, size_t size)
{
        const char *prefix;

        prelude_return_if_fail(cp);
        prelude_return_if_fail(buf);

        gl_lock_lock(lock);

        prefix = init_once_and_get_prefix();
        if ( ! relative_spool_dir )
                snprintf(buf, size, "%s/%s", PRELUDE_SPOOL_DIR, cp->name);
        else
                snprintf(buf, size, "%s/%s/%s", prefix, relative_spool_dir, cp->name);

        gl_lock_unlock(lock);
}
コード例 #14
0
ファイル: guestfs.c プロジェクト: msmhrt/libguestfs
static void
init_libguestfs (void)
{
#if defined(HAVE_LIBVIRT) || defined(HAVE_LIBXML2)
  gl_lock_lock (init_lock);
#endif
#ifdef HAVE_LIBVIRT
  virInitialize ();
#endif
#ifdef HAVE_LIBXML2
  xmlInitParser ();
  LIBXML_TEST_VERSION;
#endif
#if defined(HAVE_LIBVIRT) || defined(HAVE_LIBXML2)
  gl_lock_unlock (init_lock);
#endif
}
コード例 #15
0
/**
 * prelude_client_profile_set_prefix:
 * @cp: pointer on a #prelude_client_profile_t object.
 * @prefix: Prefix to use for various libprelude files.
 *
 * This function allow to dynamically change the prefix used to acess
 * libprelude related file. This is particularly usefull in case of
 * application running under certain condition (chroot).
 *
 * Returns: 0 on success, a negative value if an error occured.
 */
int prelude_client_profile_set_prefix(prelude_client_profile_t *cp, const char *prefix)
{
        char *n;

        n = strdup(prefix);

        gl_lock_lock(lock);

        if ( user_prefix )
                free(user_prefix);

        user_prefix = n;

        gl_lock_unlock(lock);

        return (n) ? 0 : prelude_error_from_errno(errno);
}
コード例 #16
0
ファイル: test-cond.c プロジェクト: cooljeanius/wget
static void *
timedcond_routine (void *arg)
{
  int ret;
  struct timespec ts;

  gl_lock_lock (lockcond);
  while (!cond_value)
    {
      get_ts (&ts);
      ret = glthread_cond_timedwait (&condtest, &lockcond, &ts);
      if (ret == ETIMEDOUT)
        cond_timeout = 1;
    }
  gl_lock_unlock (lockcond);

  return NULL;
}
コード例 #17
0
ファイル: handle.c プロジェクト: libguestfs/hivex
iconv_t *
_hivex_get_iconv (hive_h *h, recode_type t)
{
  gl_lock_lock (h->iconv_cache[t].mutex);
  if (h->iconv_cache[t].handle == NULL) {
    if (t == utf8_to_latin1)
      h->iconv_cache[t].handle = iconv_open ("LATIN1", "UTF-8");
    else if (t == latin1_to_utf8)
      h->iconv_cache[t].handle = iconv_open ("UTF-8", "LATIN1");
    else if (t == utf8_to_utf16le)
      h->iconv_cache[t].handle = iconv_open ("UTF-16LE", "UTF-8");
    else if (t == utf16le_to_utf8)
      h->iconv_cache[t].handle = iconv_open ("UTF-8", "UTF-16LE");
  } else {
    /* reinitialize iconv context */
    iconv (h->iconv_cache[t].handle, NULL, 0, NULL, 0);
  }
  return h->iconv_cache[t].handle;
}
コード例 #18
0
/**
 * prelude_client_profile_get_backup_dirname:
 * @cp: pointer on a #prelude_client_profile_t object.
 * @buf: buffer to write the returned filename to.
 * @size: size of @buf.
 *
 * Writes the directory name where the profile for @cp is stored. If
 * @cp is NULL or has no name, then this function will provide the main
 * profile directory.
 */
void prelude_client_profile_get_profile_dirname(const prelude_client_profile_t *cp, char *buf, size_t size)
{
        const char *prefix, *name_sep = "", *name = "";

        prelude_return_if_fail(buf);

        if ( cp && cp->name ) {
                name_sep = "/";
                name = cp->name;
        }

        gl_lock_lock(lock);

        prefix = init_once_and_get_prefix();
        if ( ! relative_profile_dir )
                snprintf(buf, size, "%s/%s%s", PRELUDE_PROFILE_DIR, name_sep, name);
        else
                snprintf(buf, size, "%s/%s%s%s", prefix, relative_profile_dir, name_sep, name);

        gl_lock_unlock(lock);
}
コード例 #19
0
ファイル: handle.c プロジェクト: myyyy/libguestfs
guestfs_h *
guestfs_create_flags (unsigned flags, ...)
{
  guestfs_h *g;

  g = calloc (1, sizeof (*g));
  if (!g) return NULL;

  g->state = CONFIG;

  g->conn = NULL;

  guestfs_int_init_error_handler (g);
  g->abort_cb = abort;

  g->recovery_proc = 1;
  g->autosync = 1;

  g->memsize = DEFAULT_MEMSIZE;

  /* Start with large serial numbers so they are easy to spot
   * inside the protocol.
   */
  g->msg_next_serial = 0x00123400;

  /* Default is uniprocessor appliance. */
  g->smp = 1;

  g->path = strdup (GUESTFS_DEFAULT_PATH);
  if (!g->path) goto error;

#ifdef QEMU
  g->hv = strdup (QEMU);
#else
  /* configure --without-qemu, so set QEMU to something which will
   * definitely fail.  The user is expected to override the hypervisor
   * by setting an environment variable or calling set_hv.
   */
  g->hv = strdup ("false");
#endif
  if (!g->hv) goto error;

  /* Get program name. */
#if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME == 1
  if (STRPREFIX (program_invocation_short_name, "lt-"))
    /* Remove libtool (lt-*) prefix from short name. */
    g->program = strdup (program_invocation_short_name + 3);
  else
    g->program = strdup (program_invocation_short_name);
#else
  g->program = strdup ("");
#endif
  if (!g->program) goto error;

  g->identifier = strdup ("");
  if (!g->identifier) goto error;

  if (guestfs_int_set_backend (g, DEFAULT_BACKEND) == -1) {
    warning (g, _("libguestfs was built with an invalid default backend, using 'direct' instead"));
    if (guestfs_int_set_backend (g, "direct") == -1) {
      warning (g, _("'direct' backend does not work"));
      goto error;
    }
  }

  if (!(flags & GUESTFS_CREATE_NO_ENVIRONMENT))
    ignore_value (guestfs_parse_environment (g));

  if (!(flags & GUESTFS_CREATE_NO_CLOSE_ON_EXIT)) {
    g->close_on_exit = true;

    /* Link the handles onto a global list. */
    gl_lock_lock (handles_lock);
    g->next = handles;
    handles = g;
    if (!atexit_handler_set) {
      atexit (close_handles);
      atexit_handler_set = 1;
    }
    gl_lock_unlock (handles_lock);
  }

  debug (g, "create: flags = %u, handle = %p, program = %s",
         flags, g, g->program);

  return g;

 error:
  guestfs_int_free_string_list (g->backend_settings);
  free (g->backend);
  free (g->identifier);
  free (g->program);
  free (g->path);
  free (g->hv);
  free (g->append);
  free (g);
  return NULL;
}
コード例 #20
0
ファイル: fuse.c プロジェクト: noxdafox/libguestfs
int
guestfs_impl_mount_local (guestfs_h *g, const char *localmountpoint,
			  const struct guestfs_mount_local_argv *optargs)
{
  const char *t;
  struct fuse_args args = FUSE_ARGS_INIT (0, NULL);
  struct fuse_chan *ch;
  int fd;

  /* You can only mount each handle in one place in one thread. */
  gl_lock_lock (mount_local_lock);
  t = g->localmountpoint;
  gl_lock_unlock (mount_local_lock);
  if (t) {
    error (g, _("filesystem is already mounted in another thread"));
    return -1;
  }

  if (optargs->bitmask & GUESTFS_MOUNT_LOCAL_READONLY_BITMASK)
    g->ml_read_only = optargs->readonly;
  else
    g->ml_read_only = 0;
  if (optargs->bitmask & GUESTFS_MOUNT_LOCAL_CACHETIMEOUT_BITMASK)
    g->ml_dir_cache_timeout = optargs->cachetimeout;
  else
    g->ml_dir_cache_timeout = 60;
  if (optargs->bitmask & GUESTFS_MOUNT_LOCAL_DEBUGCALLS_BITMASK)
    g->ml_debug_calls = optargs->debugcalls;
  else
    g->ml_debug_calls = 0;

  /* Initialize the directory caches in the handle. */
  if (init_dir_caches (g) == -1)
    return -1;

  /* Create the FUSE 'args'. */
  /* XXX we don't have a program name */
  if (fuse_opt_add_arg (&args, "guestfs_mount_local") == -1) {
  arg_error:
    perrorf (g, _("fuse_opt_add_arg: %s"), localmountpoint);
    fuse_opt_free_args (&args);
    guestfs_int_free_fuse (g);
    return -1;
  }

  if (optargs->bitmask & GUESTFS_MOUNT_LOCAL_OPTIONS_BITMASK) {
    if (fuse_opt_add_arg (&args, "-o") == -1 ||
        fuse_opt_add_arg (&args, optargs->options) == -1)
      goto arg_error;
  }

  debug (g, "%s: fuse_mount %s", __func__, localmountpoint);

  /* Create the FUSE mountpoint. */
  ch = fuse_mount (localmountpoint, &args);
  if (ch == NULL) {
    perrorf (g, _("fuse_mount: %s"), localmountpoint);
    fuse_opt_free_args (&args);
    guestfs_int_free_fuse (g);
    return -1;
  }

  /* Set F_CLOEXEC on the channel.  XXX libfuse should do this. */
  fd = fuse_chan_fd (ch);
  if (fd >= 0)
    set_cloexec_flag (fd, 1);

  debug (g, "%s: fuse_new", __func__);

  /* Create the FUSE handle. */
  g->fuse = fuse_new (ch, &args,
                      &mount_local_operations, sizeof mount_local_operations,
                      g);
  if (!g->fuse) {
    perrorf (g, _("fuse_new: %s"), localmountpoint);
    fuse_unmount (localmountpoint, ch);
    fuse_opt_free_args (&args);
    guestfs_int_free_fuse (g);
    return -1;
  }

  fuse_opt_free_args (&args);

  debug (g, "%s: leaving fuse_mount_local", __func__);

  /* Set g->localmountpoint in the handle. */
  gl_lock_lock (mount_local_lock);
  g->localmountpoint = localmountpoint;
  gl_lock_unlock (mount_local_lock);

  return 0;
}
コード例 #21
0
ファイル: handle.c プロジェクト: myyyy/libguestfs
void
guestfs_close (guestfs_h *g)
{
  struct hv_param *hp, *hp_next;
  guestfs_h **gg;

  if (g->state == NO_HANDLE) {
    /* Not safe to call ANY callbacks here, so ... */
    fprintf (stderr, _("guestfs_close: called twice on the same handle\n"));
    return;
  }

  /* Remove the handle from the handles list. */
  if (g->close_on_exit) {
    gl_lock_lock (handles_lock);
    for (gg = &handles; *gg != g; gg = &(*gg)->next)
      ;
    *gg = g->next;
    gl_lock_unlock (handles_lock);
  }

  if (g->trace) {
    const char trace_msg[] = "close";

    guestfs_int_call_callbacks_message (g, GUESTFS_EVENT_TRACE,
					trace_msg, strlen (trace_msg));
  }

  debug (g, "closing guestfs handle %p (state %d)", g, (int) g->state);

  if (g->state != CONFIG)
    shutdown_backend (g, 0);

  /* Run user close callbacks. */
  guestfs_int_call_callbacks_void (g, GUESTFS_EVENT_CLOSE);

  /* Test output file used by bindtests. */
  if (g->test_fp != NULL)
    fclose (g->test_fp);

  /* Remove temporary directory. */
  guestfs_int_remove_tmpdir (g);

  /* Mark the handle as dead and then free up all memory. */
  g->state = NO_HANDLE;

  free (g->events);
  g->nr_events = 0;
  g->events = NULL;

#if HAVE_FUSE
  guestfs_int_free_fuse (g);
#endif

  guestfs_int_free_inspect_info (g);
  guestfs_int_free_drives (g);

  for (hp = g->hv_params; hp; hp = hp_next) {
    free (hp->hv_param);
    free (hp->hv_value);
    hp_next = hp->next;
    free (hp);
  }

  while (g->error_cb_stack)
    guestfs_pop_error_handler (g);

  if (g->pda)
    hash_free (g->pda);
  free (g->tmpdir);
  free (g->env_tmpdir);
  free (g->int_tmpdir);
  free (g->int_cachedir);
  free (g->last_error);
  free (g->identifier);
  free (g->program);
  free (g->path);
  free (g->hv);
  free (g->backend);
  free (g->backend_data);
  guestfs_int_free_string_list (g->backend_settings);
  free (g->append);
  free (g);
}
コード例 #22
0
ファイル: guestfs.c プロジェクト: msmhrt/libguestfs
void
guestfs_close (guestfs_h *g)
{
  struct qemu_param *qp, *qp_next;

  if (g->state == NO_HANDLE) {
    /* Not safe to call ANY callbacks here, so ... */
    fprintf (stderr, _("guestfs_close: called twice on the same handle\n"));
    return;
  }

  /* Remove the handle from the handles list. */
  gl_lock_lock (handles_lock);
  if (handles == g)
    handles = g->next;
  else {
    guestfs_h *gg;

    for (gg = handles; gg->next != g; gg = gg->next)
      ;
    gg->next = g->next;
  }
  gl_lock_unlock (handles_lock);

  if (g->trace) {
    const char trace_msg[] = "close";

    guestfs___call_callbacks_message (g, GUESTFS_EVENT_TRACE,
                                      trace_msg, strlen (trace_msg));
  }

  debug (g, "closing guestfs handle %p (state %d)", g, g->state);

  /* If we are valgrinding the daemon, then we *don't* want to kill
   * the subprocess because we want the final valgrind messages sent
   * when we close sockets below.  However for normal production use,
   * killing the subprocess is the right thing to do (in case the
   * daemon or qemu is not responding).
   */
#ifndef VALGRIND_DAEMON
  if (g->state != CONFIG)
    ignore_value (guestfs_shutdown (g));
#endif

  /* Run user close callbacks. */
  guestfs___call_callbacks_void (g, GUESTFS_EVENT_CLOSE);

  /* Remove whole temporary directory. */
  guestfs___remove_tmpdir (g->tmpdir);

  /* Mark the handle as dead and then free up all memory. */
  g->state = NO_HANDLE;

  free (g->events);
  g->nr_events = 0;
  g->events = NULL;

#if HAVE_FUSE
  guestfs___free_fuse (g);
#endif

  guestfs___free_inspect_info (g);
  guestfs___free_drives (&g->drives);

  for (qp = g->qemu_params; qp; qp = qp_next) {
    free (qp->qemu_param);
    free (qp->qemu_value);
    qp_next = qp->next;
    free (qp);
  }

  if (g->pda)
    hash_free (g->pda);
  free (g->tmpdir);
  free (g->last_error);
  free (g->path);
  free (g->qemu);
  free (g->append);
  free (g);
}
コード例 #23
0
ファイル: guestfs.c プロジェクト: mdbooth/libguestfs
guestfs_h *
guestfs_create (void)
{
  guestfs_h *g;
  const char *str;

  g = malloc (sizeof (*g));
  if (!g) return NULL;

  memset (g, 0, sizeof (*g));

  g->state = CONFIG;

  g->fd[0] = -1;
  g->fd[1] = -1;
  g->sock = -1;

  g->abort_cb = abort;
  g->error_cb = default_error_cb;
  g->error_cb_data = NULL;

  g->recovery_proc = 1;
  g->autosync = 1;

  str = getenv ("LIBGUESTFS_DEBUG");
  g->verbose = str != NULL && STREQ (str, "1");

  str = getenv ("LIBGUESTFS_TRACE");
  g->trace = str != NULL && STREQ (str, "1");

  str = getenv ("LIBGUESTFS_PATH");
  g->path = str != NULL ? strdup (str) : strdup (GUESTFS_DEFAULT_PATH);
  if (!g->path) goto error;

  str = getenv ("LIBGUESTFS_QEMU");
  g->qemu = str != NULL ? strdup (str) : strdup (QEMU);
  if (!g->qemu) goto error;

  str = getenv ("LIBGUESTFS_APPEND");
  if (str) {
    g->append = strdup (str);
    if (!g->append) goto error;
  }

  /* Choose a suitable memory size.  Previously we tried to choose
   * a minimal memory size, but this isn't really necessary since
   * recent QEMU and KVM don't do anything nasty like locking
   * memory into core any more.  Thus we can safely choose a
   * large, generous amount of memory, and it'll just get swapped
   * on smaller systems.
   */
  str = getenv ("LIBGUESTFS_MEMSIZE");
  if (str) {
    if (sscanf (str, "%d", &g->memsize) != 1 || g->memsize <= 256) {
      warning (g, "non-numeric or too small value for LIBGUESTFS_MEMSIZE");
      goto error;
    }
  } else
    g->memsize = 500;

  /* Start with large serial numbers so they are easy to spot
   * inside the protocol.
   */
  g->msg_next_serial = 0x00123400;

  /* Default is uniprocessor appliance. */
  g->smp = 1;

  /* Link the handles onto a global list. */
  gl_lock_lock (handles_lock);
  g->next = handles;
  handles = g;
  if (!atexit_handler_set) {
    atexit (close_handles);
    atexit_handler_set = 1;
  }
  gl_lock_unlock (handles_lock);

  debug (g, "new guestfs handle %p", g);

  return g;

 error:
  free (g->path);
  free (g->qemu);
  free (g->append);
  free (g);
  return NULL;
}
コード例 #24
0
ファイル: guestfs.c プロジェクト: mdbooth/libguestfs
void
guestfs_close (guestfs_h *g)
{
  if (g->state == NO_HANDLE) {
    /* Not safe to call ANY callbacks here, so ... */
    fprintf (stderr, _("guestfs_close: called twice on the same handle\n"));
    return;
  }

  if (g->trace) {
    const char trace_msg[] = "close";

    guestfs___call_callbacks_message (g, GUESTFS_EVENT_TRACE,
                                      trace_msg, strlen (trace_msg));
  }

  debug (g, "closing guestfs handle %p (state %d)", g, g->state);

  /* Try to sync if autosync flag is set. */
  if (g->autosync && g->state == READY)
    guestfs_internal_autosync (g);

  /* If we are valgrinding the daemon, then we *don't* want to kill
   * the subprocess because we want the final valgrind messages sent
   * when we close sockets below.  However for normal production use,
   * killing the subprocess is the right thing to do (in case the
   * daemon or qemu is not responding).
   */
#ifndef VALGRIND_DAEMON
  /* Kill the qemu subprocess. */
  if (g->state != CONFIG)
    guestfs_kill_subprocess (g);
#endif

  /* Run user close callbacks. */
  guestfs___call_callbacks_void (g, GUESTFS_EVENT_CLOSE);

  /* Remove all other registered callbacks.  Since we've already
   * called the close callbacks, we shouldn't call any others.
   */
  free (g->events);
  g->nr_events = 0;
  g->events = NULL;

  guestfs___free_inspect_info (g);
  guestfs___free_drives (&g->drives);

  /* Close sockets. */
  if (g->fd[0] >= 0)
    close (g->fd[0]);
  if (g->fd[1] >= 0)
    close (g->fd[1]);
  if (g->sock >= 0)
    close (g->sock);
  g->fd[0] = -1;
  g->fd[1] = -1;
  g->sock = -1;

  /* Wait for subprocess(es) to exit. */
  if (g->pid > 0) waitpid (g->pid, NULL, 0);
  if (g->recoverypid > 0) waitpid (g->recoverypid, NULL, 0);

  /* Remove whole temporary directory. */
  guestfs___remove_tmpdir (g->tmpdir);
  free (g->tmpdir);

  if (g->cmdline) {
    size_t i;

    for (i = 0; i < g->cmdline_size; ++i)
      free (g->cmdline[i]);
    free (g->cmdline);
  }

  /* Mark the handle as dead before freeing it. */
  g->state = NO_HANDLE;

  gl_lock_lock (handles_lock);
  if (handles == g)
    handles = g->next;
  else {
    guestfs_h *gg;

    for (gg = handles; gg->next != g; gg = gg->next)
      ;
    gg->next = g->next;
  }
  gl_lock_unlock (handles_lock);

  if (g->pda)
    hash_free (g->pda);
  free (g->last_error);
  free (g->path);
  free (g->qemu);
  free (g->append);
  free (g->qemu_help);
  free (g->qemu_version);
  free (g);
}
コード例 #25
0
int
strerror_r (int errnum, char *buf, size_t buflen)
#undef strerror_r
{
  /* Filter this out now, so that rest of this replacement knows that
     there is room for a non-empty message and trailing NUL.  */
  if (buflen <= 1)
    {
      if (buflen)
        *buf = '\0';
      return ERANGE;
    }
  *buf = '\0';

  /* Check for gnulib overrides.  */
  {
    char const *msg = strerror_override (errnum);

    if (msg)
      return safe_copy (buf, buflen, msg);
  }

  {
    int ret;
    int saved_errno = errno;

#if USE_XPG_STRERROR_R

    {
      ret = __xpg_strerror_r (errnum, buf, buflen);
      if (ret < 0)
        ret = errno;
      if (!*buf)
        {
          /* glibc 2.13 would not touch buf on err, so we have to fall
             back to GNU strerror_r which always returns a thread-safe
             untruncated string to (partially) copy into our buf.  */
          safe_copy (buf, buflen, strerror_r (errnum, buf, buflen));
        }
    }

#elif USE_SYSTEM_STRERROR_R

    if (buflen > INT_MAX)
      buflen = INT_MAX;

# ifdef __hpux
    /* On HP-UX 11.31, strerror_r always fails when buflen < 80; it
       also fails to change buf on EINVAL.  */
    {
      char stackbuf[80];

      if (buflen < sizeof stackbuf)
        {
          ret = strerror_r (errnum, stackbuf, sizeof stackbuf);
          if (ret == 0)
            ret = safe_copy (buf, buflen, stackbuf);
        }
      else
        ret = strerror_r (errnum, buf, buflen);
    }
# else
    ret = strerror_r (errnum, buf, buflen);

    /* Some old implementations may return (-1, EINVAL) instead of EINVAL.
       But on Haiku, valid error numbers are negative.  */
#  if !defined __HAIKU__
    if (ret < 0)
      ret = errno;
#  endif
# endif

# if defined _AIX || defined __HAIKU__
    /* AIX and Haiku return 0 rather than ERANGE when truncating strings; try
       again until we are sure we got the entire string.  */
    if (!ret && strlen (buf) == buflen - 1)
      {
        char stackbuf[STACKBUF_LEN];
        size_t len;
        strerror_r (errnum, stackbuf, sizeof stackbuf);
        len = strlen (stackbuf);
        /* STACKBUF_LEN should have been large enough.  */
        if (len + 1 == sizeof stackbuf)
          abort ();
        if (buflen <= len)
          ret = ERANGE;
      }
# else
    /* Solaris 10 does not populate buf on ERANGE.  OpenBSD 4.7
       truncates early on ERANGE rather than return a partial integer.
       We prefer the maximal string.  We set buf[0] earlier, and we
       know of no implementation that modifies buf to be an
       unterminated string, so this strlen should be portable in
       practice (rather than pulling in a safer strnlen).  */
    if (ret == ERANGE && strlen (buf) < buflen - 1)
      {
        char stackbuf[STACKBUF_LEN];

        /* STACKBUF_LEN should have been large enough.  */
        if (strerror_r (errnum, stackbuf, sizeof stackbuf) == ERANGE)
          abort ();
        safe_copy (buf, buflen, stackbuf);
      }
# endif

#else /* USE_SYSTEM_STRERROR */

    /* Try to do what strerror (errnum) does, but without clobbering the
       buffer used by strerror().  */

# if defined __NetBSD__ || defined __hpux || (defined _WIN32 && !defined __CYGWIN__) || defined __CYGWIN__ /* NetBSD, HP-UX, native Windows, Cygwin */

    /* NetBSD:         sys_nerr, sys_errlist are declared through _NETBSD_SOURCE
                       and <errno.h> above.
       HP-UX:          sys_nerr, sys_errlist are declared explicitly above.
       native Windows: sys_nerr, sys_errlist are declared in <stdlib.h>.
       Cygwin:         sys_nerr, sys_errlist are declared in <errno.h>.  */
    if (errnum >= 0 && errnum < sys_nerr)
      {
#  if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
#   if defined __NetBSD__
        nl_catd catd = catopen ("libc", NL_CAT_LOCALE);
        const char *errmsg =
          (catd != (nl_catd)-1
           ? catgets (catd, 1, errnum, sys_errlist[errnum])
           : sys_errlist[errnum]);
#   endif
#   if defined __hpux
        nl_catd catd = catopen ("perror", NL_CAT_LOCALE);
        const char *errmsg =
          (catd != (nl_catd)-1
           ? catgets (catd, 1, 1 + errnum, sys_errlist[errnum])
           : sys_errlist[errnum]);
#   endif
#  else
        const char *errmsg = sys_errlist[errnum];
#  endif
        if (errmsg == NULL || *errmsg == '\0')
          ret = EINVAL;
        else
          ret = safe_copy (buf, buflen, errmsg);
#  if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
        if (catd != (nl_catd)-1)
          catclose (catd);
#  endif
      }
    else
      ret = EINVAL;

# elif defined __sgi || (defined __sun && !defined _LP64) /* IRIX, Solaris <= 9 32-bit */

    /* For a valid error number, the system's strerror() function returns
       a pointer to a not copied string, not to a buffer.  */
    if (errnum >= 0 && errnum < sys_nerr)
      {
        char *errmsg = strerror (errnum);

        if (errmsg == NULL || *errmsg == '\0')
          ret = EINVAL;
        else
          ret = safe_copy (buf, buflen, errmsg);
      }
    else
      ret = EINVAL;

# else

    gl_lock_lock (strerror_lock);

    {
      char *errmsg = strerror (errnum);

      /* For invalid error numbers, strerror() on
           - IRIX 6.5 returns NULL,
           - HP-UX 11 returns an empty string.  */
      if (errmsg == NULL || *errmsg == '\0')
        ret = EINVAL;
      else
        ret = safe_copy (buf, buflen, errmsg);
    }

    gl_lock_unlock (strerror_lock);

# endif

#endif

#if defined _WIN32 && !defined __CYGWIN__
    /* MSVC 14 defines names for many error codes in the range 100..140,
       but _sys_errlist contains strings only for the error codes
       < _sys_nerr = 43.  */
    if (ret == EINVAL)
      {
        const char *errmsg;

        switch (errnum)
          {
          case 100 /* EADDRINUSE */:
            errmsg = "Address already in use";
            break;
          case 101 /* EADDRNOTAVAIL */:
            errmsg = "Cannot assign requested address";
            break;
          case 102 /* EAFNOSUPPORT */:
            errmsg = "Address family not supported by protocol";
            break;
          case 103 /* EALREADY */:
            errmsg = "Operation already in progress";
            break;
          case 105 /* ECANCELED */:
            errmsg = "Operation canceled";
            break;
          case 106 /* ECONNABORTED */:
            errmsg = "Software caused connection abort";
            break;
          case 107 /* ECONNREFUSED */:
            errmsg = "Connection refused";
            break;
          case 108 /* ECONNRESET */:
            errmsg = "Connection reset by peer";
            break;
          case 109 /* EDESTADDRREQ */:
            errmsg = "Destination address required";
            break;
          case 110 /* EHOSTUNREACH */:
            errmsg = "No route to host";
            break;
          case 112 /* EINPROGRESS */:
            errmsg = "Operation now in progress";
            break;
          case 113 /* EISCONN */:
            errmsg = "Transport endpoint is already connected";
            break;
          case 114 /* ELOOP */:
            errmsg = "Too many levels of symbolic links";
            break;
          case 115 /* EMSGSIZE */:
            errmsg = "Message too long";
            break;
          case 116 /* ENETDOWN */:
            errmsg = "Network is down";
            break;
          case 117 /* ENETRESET */:
            errmsg = "Network dropped connection on reset";
            break;
          case 118 /* ENETUNREACH */:
            errmsg = "Network is unreachable";
            break;
          case 119 /* ENOBUFS */:
            errmsg = "No buffer space available";
            break;
          case 123 /* ENOPROTOOPT */:
            errmsg = "Protocol not available";
            break;
          case 126 /* ENOTCONN */:
            errmsg = "Transport endpoint is not connected";
            break;
          case 128 /* ENOTSOCK */:
            errmsg = "Socket operation on non-socket";
            break;
          case 129 /* ENOTSUP */:
            errmsg = "Not supported";
            break;
          case 130 /* EOPNOTSUPP */:
            errmsg = "Operation not supported";
            break;
          case 132 /* EOVERFLOW */:
            errmsg = "Value too large for defined data type";
            break;
          case 133 /* EOWNERDEAD */:
            errmsg = "Owner died";
            break;
          case 134 /* EPROTO */:
            errmsg = "Protocol error";
            break;
          case 135 /* EPROTONOSUPPORT */:
            errmsg = "Protocol not supported";
            break;
          case 136 /* EPROTOTYPE */:
            errmsg = "Protocol wrong type for socket";
            break;
          case 138 /* ETIMEDOUT */:
            errmsg = "Connection timed out";
            break;
          case 140 /* EWOULDBLOCK */:
            errmsg = "Operation would block";
            break;
          default:
            errmsg = NULL;
            break;
          }
        if (errmsg != NULL)
          ret = safe_copy (buf, buflen, errmsg);
      }
#endif

    if (ret == EINVAL && !*buf)
      {
#if defined __HAIKU__
        /* For consistency with perror().  */
        snprintf (buf, buflen, "Unknown Application Error (%d)", errnum);
#else
        snprintf (buf, buflen, "Unknown error %d", errnum);
#endif
      }

    errno = saved_errno;
    return ret;
  }
}
コード例 #26
0
ファイル: fuse.c プロジェクト: msmhrt/libguestfs
int
guestfs__umount_local (guestfs_h *g,
                       const struct guestfs_umount_local_argv *optargs)
{
  size_t i, tries;
  char *localmountpoint;
  char *fusermount_log = NULL;
  int error_fd = -1;
  int ret = -1;

  /* How many times should we try the fusermount command? */
  if (optargs->bitmask & GUESTFS_UMOUNT_LOCAL_RETRY_BITMASK)
    tries = optargs->retry ? 5 : 1;
  else
    tries = 1;

  /* Make a local copy of g->localmountpoint.  It could be freed from
   * under us by another thread, except when we are holding the lock.
   */
  gl_lock_lock (mount_local_lock);
  if (g->localmountpoint)
    localmountpoint = safe_strdup (g, g->localmountpoint);
  else
    localmountpoint = NULL;
  gl_lock_unlock (mount_local_lock);

  if (!localmountpoint) {
    error (g, _("no filesystem is mounted"));
    goto out;
  }

  /* Send all errors from fusermount to a temporary file.  Only after
   * all 'tries' have failed do we print the contents of this file.  A
   * temporary failure when retry == true will not cause any error.
   */
  fusermount_log = safe_asprintf (g, "%s/fusermount.log", g->tmpdir);
  error_fd = open (fusermount_log,
                   O_RDWR|O_CREAT|O_TRUNC|O_NOCTTY /* not O_CLOEXEC */,
                   0600);
  if (error_fd == -1) {
    perrorf (g, _("open: %s"), fusermount_log);
    goto out;
  }

  for (i = 0; i < tries; ++i) {
    int r;

    r = do_fusermount (g, localmountpoint, error_fd);
    if (r == -1)
      goto out;
    if (r) {
      /* External fusermount succeeded.  Note that the original thread
       * is responsible for setting g->localmountpoint to NULL.
       */
      ret = 0;
      break;
    }

    sleep (1);
  }

  if (ret == -1) {              /* fusermount failed */
    char error_message[4096];
    ssize_t n;

    /* Get the error message from the log file. */
    if (lseek (error_fd, 0, SEEK_SET) >= 0 &&
        (n = read (error_fd, error_message, sizeof error_message - 1)) > 0) {
      while (n > 0 && error_message[n-1] == '\n')
        n--;
      error_message[n] = '\0';
    } else {
      snprintf (error_message, sizeof error_message,
                "(fusermount error could not be preserved)");
    }

    error (g, _("fusermount failed: %s: %s"), localmountpoint, error_message);
    goto out;
  }

 out:
  if (error_fd >= 0) close (error_fd);
  if (fusermount_log) {
    unlink (fusermount_log);
    free (fusermount_log);
  }
  free (localmountpoint);
  return ret;
}
コード例 #27
0
ファイル: strerror_r.c プロジェクト: AblePear/wget_pkg
int
strerror_r (int errnum, char *buf, size_t buflen)
#undef strerror_r
{
  /* Filter this out now, so that rest of this replacement knows that
     there is room for a non-empty message and trailing NUL.  */
  if (buflen <= 1)
    {
      if (buflen)
        *buf = '\0';
      return ERANGE;
    }
  *buf = '\0';

  /* Check for gnulib overrides.  */
  {
    char const *msg = strerror_override (errnum);

    if (msg)
      return safe_copy (buf, buflen, msg);
  }

  {
    int ret;
    int saved_errno = errno;

#if USE_XPG_STRERROR_R

    {
      ret = __xpg_strerror_r (errnum, buf, buflen);
      if (ret < 0)
        ret = errno;
      if (!*buf)
        {
          /* glibc 2.13 would not touch buf on err, so we have to fall
             back to GNU strerror_r which always returns a thread-safe
             untruncated string to (partially) copy into our buf.  */
          safe_copy (buf, buflen, strerror_r (errnum, buf, buflen));
        }
    }

#elif USE_SYSTEM_STRERROR_R

    if (buflen > INT_MAX)
      buflen = INT_MAX;

# ifdef __hpux
    /* On HP-UX 11.31, strerror_r always fails when buflen < 80; it
       also fails to change buf on EINVAL.  */
    {
      char stackbuf[80];

      if (buflen < sizeof stackbuf)
        {
          ret = strerror_r (errnum, stackbuf, sizeof stackbuf);
          if (ret == 0)
            ret = safe_copy (buf, buflen, stackbuf);
        }
      else
        ret = strerror_r (errnum, buf, buflen);
    }
# else
    ret = strerror_r (errnum, buf, buflen);

    /* Some old implementations may return (-1, EINVAL) instead of EINVAL.  */
    if (ret < 0)
      ret = errno;
# endif

# ifdef _AIX
    /* AIX returns 0 rather than ERANGE when truncating strings; try
       again until we are sure we got the entire string.  */
    if (!ret && strlen (buf) == buflen - 1)
      {
        char stackbuf[STACKBUF_LEN];
        size_t len;
        strerror_r (errnum, stackbuf, sizeof stackbuf);
        len = strlen (stackbuf);
        /* STACKBUF_LEN should have been large enough.  */
        if (len + 1 == sizeof stackbuf)
          abort ();
        if (buflen <= len)
          ret = ERANGE;
      }
# else
    /* Solaris 10 does not populate buf on ERANGE.  OpenBSD 4.7
       truncates early on ERANGE rather than return a partial integer.
       We prefer the maximal string.  We set buf[0] earlier, and we
       know of no implementation that modifies buf to be an
       unterminated string, so this strlen should be portable in
       practice (rather than pulling in a safer strnlen).  */
    if (ret == ERANGE && strlen (buf) < buflen - 1)
      {
        char stackbuf[STACKBUF_LEN];

        /* STACKBUF_LEN should have been large enough.  */
        if (strerror_r (errnum, stackbuf, sizeof stackbuf) == ERANGE)
          abort ();
        safe_copy (buf, buflen, stackbuf);
      }
# endif

#else /* USE_SYSTEM_STRERROR */

    /* Try to do what strerror (errnum) does, but without clobbering the
       buffer used by strerror().  */

# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __CYGWIN__ /* NetBSD, HP-UX, native Windows, Cygwin */

    /* NetBSD:         sys_nerr, sys_errlist are declared through _NETBSD_SOURCE
                       and <errno.h> above.
       HP-UX:          sys_nerr, sys_errlist are declared explicitly above.
       native Windows: sys_nerr, sys_errlist are declared in <stdlib.h>.
       Cygwin:         sys_nerr, sys_errlist are declared in <errno.h>.  */
    if (errnum >= 0 && errnum < sys_nerr)
      {
#  if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
#   if defined __NetBSD__
        nl_catd catd = catopen ("libc", NL_CAT_LOCALE);
        const char *errmsg =
          (catd != (nl_catd)-1
           ? catgets (catd, 1, errnum, sys_errlist[errnum])
           : sys_errlist[errnum]);
#   endif
#   if defined __hpux
        nl_catd catd = catopen ("perror", NL_CAT_LOCALE);
        const char *errmsg =
          (catd != (nl_catd)-1
           ? catgets (catd, 1, 1 + errnum, sys_errlist[errnum])
           : sys_errlist[errnum]);
#   endif
#  else
        const char *errmsg = sys_errlist[errnum];
#  endif
        if (errmsg == NULL || *errmsg == '\0')
          ret = EINVAL;
        else
          ret = safe_copy (buf, buflen, errmsg);
#  if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
        if (catd != (nl_catd)-1)
          catclose (catd);
#  endif
      }
    else
      ret = EINVAL;

# elif defined __sgi || (defined __sun && !defined _LP64) /* IRIX, Solaris <= 9 32-bit */

    /* For a valid error number, the system's strerror() function returns
       a pointer to a not copied string, not to a buffer.  */
    if (errnum >= 0 && errnum < sys_nerr)
      {
        char *errmsg = strerror (errnum);

        if (errmsg == NULL || *errmsg == '\0')
          ret = EINVAL;
        else
          ret = safe_copy (buf, buflen, errmsg);
      }
    else
      ret = EINVAL;

# else

    gl_lock_lock (strerror_lock);

    {
      char *errmsg = strerror (errnum);

      /* For invalid error numbers, strerror() on
           - IRIX 6.5 returns NULL,
           - HP-UX 11 returns an empty string.  */
      if (errmsg == NULL || *errmsg == '\0')
        ret = EINVAL;
      else
        ret = safe_copy (buf, buflen, errmsg);
    }

    gl_lock_unlock (strerror_lock);

# endif

#endif

    if (ret == EINVAL && !*buf)
      snprintf (buf, buflen, "Unknown error %d", errnum);

    errno = saved_errno;
    return ret;
  }
}