Blame src/sysutils.c

Packit fc043f
/* sysutils.c - Platform specific helper functions
Packit fc043f
 * Copyright (C) 2017 g10 Code GmbH
Packit fc043f
 *
Packit fc043f
 * This file is part of libgpg-error.
Packit fc043f
 *
Packit fc043f
 * libgpg-error is free software; you can redistribute it and/or
Packit fc043f
 * modify it under the terms of the GNU Lesser General Public License
Packit fc043f
 * as published by the Free Software Foundation; either version 2.1 of
Packit fc043f
 * the License, or (at your option) any later version.
Packit fc043f
 *
Packit fc043f
 * libgpg-error is distributed in the hope that it will be useful, but
Packit fc043f
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit fc043f
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit fc043f
 * Lesser General Public License for more details.
Packit fc043f
 *
Packit fc043f
 * You should have received a copy of the GNU Lesser General Public
Packit fc043f
 * License along with this program; if not, see <https://www.gnu.org/licenses/>.
Packit fc043f
 * SPDX-License-Identifier: LGPL-2.1+
Packit fc043f
 */
Packit fc043f
Packit fc043f
#include <config.h>
Packit fc043f
#include <stdlib.h>
Packit fc043f
#include <stdint.h>
Packit fc043f
#include <string.h>
Packit fc043f
#include <unistd.h>
Packit fc043f
#include <errno.h>
Packit fc043f
#ifdef HAVE_W32_SYSTEM
Packit fc043f
# include <windows.h>
Packit fc043f
#endif
Packit fc043f
#ifdef HAVE_STAT
Packit fc043f
# include <sys/stat.h>
Packit fc043f
#endif
Packit fc043f
#include <sys/types.h>
Packit fc043f
#include <fcntl.h>
Packit fc043f
Packit fc043f
#include "gpgrt-int.h"
Packit fc043f
Packit fc043f
Packit fc043f
Packit fc043f
/* Return true if FD is valid.  */
Packit fc043f
int
Packit fc043f
_gpgrt_fd_valid_p (int fd)
Packit fc043f
{
Packit fc043f
  int d = dup (fd);
Packit fc043f
  if (d < 0)
Packit fc043f
    return 0;
Packit fc043f
  close (d);
Packit fc043f
  return 1;
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* Our variant of getenv.  The returned string must be freed.  If the
Packit fc043f
 * environment variable does not exists NULL is returned and ERRNO set
Packit fc043f
 * to 0.  */
Packit fc043f
char *
Packit fc043f
_gpgrt_getenv (const char *name)
Packit fc043f
{
Packit fc043f
  if (!name || !*name || strchr (name, '='))
Packit fc043f
    {
Packit fc043f
      _gpg_err_set_errno (EINVAL);
Packit fc043f
      return NULL;
Packit fc043f
    }
Packit fc043f
Packit fc043f
#ifdef HAVE_W32_SYSTEM
Packit fc043f
  {
Packit fc043f
    int len, size;
Packit fc043f
    char *result;
Packit fc043f
Packit fc043f
    len = GetEnvironmentVariable (name, NULL, 0);
Packit fc043f
    if (!len && GetLastError () == ERROR_ENVVAR_NOT_FOUND)
Packit fc043f
      {
Packit fc043f
        _gpg_err_set_errno (0);
Packit fc043f
        return NULL;
Packit fc043f
      }
Packit fc043f
  again:
Packit fc043f
    size = len;
Packit fc043f
    result = _gpgrt_malloc (size);
Packit fc043f
    if (!result)
Packit fc043f
      return NULL;
Packit fc043f
    len = GetEnvironmentVariable (name, result, size);
Packit fc043f
    if (len >= size)
Packit fc043f
      {
Packit fc043f
        /* Changed in the meantime - retry.  */
Packit fc043f
        _gpgrt_free (result);
Packit fc043f
        goto again;
Packit fc043f
      }
Packit fc043f
    if (!len && GetLastError () == ERROR_ENVVAR_NOT_FOUND)
Packit fc043f
      {
Packit fc043f
        /* Deleted in the meantime.  */
Packit fc043f
        _gpgrt_free (result);
Packit fc043f
        _gpg_err_set_errno (0);
Packit fc043f
        return NULL;
Packit fc043f
      }
Packit fc043f
    if (!len)
Packit fc043f
      {
Packit fc043f
        /* Other error.  FIXME: We need mapping fucntion. */
Packit fc043f
        _gpgrt_free (result);
Packit fc043f
        _gpg_err_set_errno (EIO);
Packit fc043f
        return NULL;
Packit fc043f
      }
Packit fc043f
Packit fc043f
    return result;
Packit fc043f
  }
Packit fc043f
#else /*!HAVE_W32_SYSTEM*/
Packit fc043f
  {
Packit fc043f
    const char *s = getenv (name);
Packit fc043f
    if (!s)
Packit fc043f
      {
Packit fc043f
        _gpg_err_set_errno (0);
Packit fc043f
        return NULL;
Packit fc043f
      }
Packit fc043f
    return _gpgrt_strdup (s);
Packit fc043f
  }
Packit fc043f
#endif /*!HAVE_W32_SYSTEM*/
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* Wrapper around setenv so that we can have the same function in
Packit fc043f
 * Windows and Unix.  In contrast to the standard setenv passing a
Packit fc043f
 * VALUE as NULL and setting OVERWRITE will remove the envvar.  */
Packit fc043f
gpg_err_code_t
Packit fc043f
_gpgrt_setenv (const char *name, const char *value, int overwrite)
Packit fc043f
{
Packit fc043f
  if (!name || !*name || strchr (name, '='))
Packit fc043f
    return GPG_ERR_EINVAL;
Packit fc043f
Packit fc043f
#ifdef HAVE_W32_SYSTEM
Packit fc043f
  /* Windows maintains (at least) two sets of environment variables.
Packit fc043f
   * One set can be accessed by GetEnvironmentVariable and
Packit fc043f
   * SetEnvironmentVariable.  This set is inherited by the children.
Packit fc043f
   * The other set is maintained in the C runtime, and is accessed
Packit fc043f
   * using getenv and putenv.  We try to keep them in sync by
Packit fc043f
   * modifying both sets.  Note that gpgrt_getenv ignores the libc
Packit fc043f
   * values - however, too much existing code still uses getenv.  */
Packit fc043f
  {
Packit fc043f
    int exists;
Packit fc043f
    char tmpbuf[10];
Packit fc043f
    char *buf;
Packit fc043f
Packit fc043f
    if (!value && overwrite)
Packit fc043f
      {
Packit fc043f
        if (!SetEnvironmentVariable (name, NULL))
Packit fc043f
          return GPG_ERR_EINVAL;
Packit fc043f
        if (getenv (name))
Packit fc043f
          {
Packit fc043f
            /* Ugly: Leaking memory.  */
Packit fc043f
            buf = _gpgrt_strdup (name);
Packit fc043f
            if (!buf)
Packit fc043f
              return _gpg_err_code_from_syserror ();
Packit fc043f
            if (putenv (buf))
Packit fc043f
              return _gpg_err_code_from_syserror ();
Packit fc043f
          }
Packit fc043f
        return 0;
Packit fc043f
      }
Packit fc043f
Packit fc043f
    exists = GetEnvironmentVariable (name, tmpbuf, sizeof tmpbuf);
Packit fc043f
    if ((! exists || overwrite) && !SetEnvironmentVariable (name, value))
Packit fc043f
      return GPG_ERR_EINVAL; /* (Might also be ENOMEM.) */
Packit fc043f
    if (overwrite || !getenv (name))
Packit fc043f
      {
Packit fc043f
        /* Ugly: Leaking memory.  */
Packit fc043f
        buf = _gpgrt_strconcat (name, "=", value, NULL);
Packit fc043f
        if (!buf)
Packit fc043f
          return _gpg_err_code_from_syserror ();
Packit fc043f
        if (putenv (buf))
Packit fc043f
          return _gpg_err_code_from_syserror ();
Packit fc043f
      }
Packit fc043f
    return 0;
Packit fc043f
  }
Packit fc043f
Packit fc043f
#else /*!HAVE_W32_SYSTEM*/
Packit fc043f
Packit fc043f
# ifdef HAVE_SETENV
Packit fc043f
Packit fc043f
  {
Packit fc043f
    if (!value && overwrite)
Packit fc043f
      {
Packit fc043f
        if (unsetenv (name))
Packit fc043f
          return _gpg_err_code_from_syserror ();
Packit fc043f
      }
Packit fc043f
    else
Packit fc043f
      {
Packit fc043f
        if (setenv (name, value, overwrite))
Packit fc043f
          return _gpg_err_code_from_syserror ();
Packit fc043f
      }
Packit fc043f
Packit fc043f
    return 0;
Packit fc043f
  }
Packit fc043f
Packit fc043f
# else /*!HAVE_SETENV*/
Packit fc043f
Packit fc043f
# if __GNUC__
Packit fc043f
#   warning no setenv - using putenv but leaking memory.
Packit fc043f
# endif
Packit fc043f
  {
Packit fc043f
    char *buf;
Packit fc043f
Packit fc043f
    if (!value && overwrite)
Packit fc043f
      {
Packit fc043f
        if (getenv (name))
Packit fc043f
          {
Packit fc043f
            buf = _gpgrt_strdup (name);
Packit fc043f
            if (!buf)
Packit fc043f
              return _gpg_err_code_from_syserror ();
Packit fc043f
            if (putenv (buf))
Packit fc043f
              return -1;
Packit fc043f
          }
Packit fc043f
      }
Packit fc043f
    else if (overwrite || !getenv (name))
Packit fc043f
      {
Packit fc043f
        buf = _gpgrt_strconcat (name, "=", value, NULL);
Packit fc043f
        if (!buf)
Packit fc043f
          return _gpg_err_code_from_syserror ();
Packit fc043f
        if (putenv (buf))
Packit fc043f
          return _gpg_err_code_from_syserror ();
Packit fc043f
      }
Packit fc043f
Packit fc043f
    return 0;
Packit fc043f
  }
Packit fc043f
# endif /*!HAVE_SETENV*/
Packit fc043f
#endif /*!HAVE_W32_SYSTEM*/
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
#ifndef HAVE_W32_SYSTEM
Packit fc043f
static mode_t
Packit fc043f
modestr_to_mode (const char *modestr)
Packit fc043f
{
Packit fc043f
  mode_t mode = 0;
Packit fc043f
Packit fc043f
  if (modestr && *modestr)
Packit fc043f
    {
Packit fc043f
      modestr++;
Packit fc043f
      if (*modestr && *modestr++ == 'r')
Packit fc043f
        mode |= S_IRUSR;
Packit fc043f
      if (*modestr && *modestr++ == 'w')
Packit fc043f
        mode |= S_IWUSR;
Packit fc043f
      if (*modestr && *modestr++ == 'x')
Packit fc043f
        mode |= S_IXUSR;
Packit fc043f
      if (*modestr && *modestr++ == 'r')
Packit fc043f
        mode |= S_IRGRP;
Packit fc043f
      if (*modestr && *modestr++ == 'w')
Packit fc043f
        mode |= S_IWGRP;
Packit fc043f
      if (*modestr && *modestr++ == 'x')
Packit fc043f
        mode |= S_IXGRP;
Packit fc043f
      if (*modestr && *modestr++ == 'r')
Packit fc043f
        mode |= S_IROTH;
Packit fc043f
      if (*modestr && *modestr++ == 'w')
Packit fc043f
        mode |= S_IWOTH;
Packit fc043f
      if (*modestr && *modestr++ == 'x')
Packit fc043f
        mode |= S_IXOTH;
Packit fc043f
    }
Packit fc043f
Packit fc043f
  return mode;
Packit fc043f
}
Packit fc043f
#endif
Packit fc043f
Packit fc043f
Packit fc043f
/* A wrapper around mkdir which takes a string for the mode argument.
Packit fc043f
 * This makes it easier to handle the mode argument which is not
Packit fc043f
 * defined on all systems.  The format of the modestring is
Packit fc043f
 *
Packit fc043f
 *    "-rwxrwxrwx"
Packit fc043f
 *
Packit fc043f
 * '-' is a don't care or not set.  'r', 'w', 'x' are read allowed,
Packit fc043f
 * write allowed, execution allowed with the first group for the user,
Packit fc043f
 * the second for the group and the third for all others.  If the
Packit fc043f
 * string is shorter than above the missing mode characters are meant
Packit fc043f
 * to be not set.  */
Packit fc043f
gpg_err_code_t
Packit fc043f
_gpgrt_mkdir (const char *name, const char *modestr)
Packit fc043f
{
Packit fc043f
#ifdef HAVE_W32CE_SYSTEM
Packit fc043f
  wchar_t *wname;
Packit fc043f
  (void)modestr;
Packit fc043f
Packit fc043f
  wname = utf8_to_wchar (name);
Packit fc043f
  if (!wname)
Packit fc043f
    return _gpg_err_code_from_syserror ();
Packit fc043f
  if (!CreateDirectoryW (wname, NULL))
Packit fc043f
    {
Packit fc043f
      xfree (wname);
Packit fc043f
      return _gpg_err_code_from_syserror ();
Packit fc043f
    }
Packit fc043f
  xfree (wname);
Packit fc043f
  return 0;
Packit fc043f
#elif MKDIR_TAKES_ONE_ARG
Packit fc043f
  (void)modestr;
Packit fc043f
  /* Note: In the case of W32 we better use CreateDirectory and try to
Packit fc043f
     set appropriate permissions.  However using mkdir is easier
Packit fc043f
     because this sets ERRNO.  */
Packit fc043f
  if (mkdir (name))
Packit fc043f
    return _gpg_err_code_from_syserror ();
Packit fc043f
  return 0;
Packit fc043f
#else
Packit fc043f
  if (mkdir (name, modestr_to_mode (modestr)))
Packit fc043f
    return _gpg_err_code_from_syserror ();
Packit fc043f
  return 0;
Packit fc043f
#endif
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* A simple wrapper around chdir.  NAME is expected to be utf8
Packit fc043f
 * encoded.  */
Packit fc043f
gpg_err_code_t
Packit fc043f
_gpgrt_chdir (const char *name)
Packit fc043f
{
Packit fc043f
  if (chdir (name))
Packit fc043f
    return _gpg_err_code_from_syserror ();
Packit fc043f
  return 0;
Packit fc043f
}
Packit fc043f
Packit fc043f
Packit fc043f
/* Return the current working directory as a malloced string.  Return
Packit fc043f
 * NULL and sets ERRNO on error.  */
Packit fc043f
char *
Packit fc043f
_gpgrt_getcwd (void)
Packit fc043f
{
Packit fc043f
  char *buffer;
Packit fc043f
  size_t size = 100;
Packit fc043f
Packit fc043f
  for (;;)
Packit fc043f
    {
Packit fc043f
      buffer = xtrymalloc (size+1);
Packit fc043f
      if (!buffer)
Packit fc043f
        return NULL;
Packit fc043f
#ifdef HAVE_W32CE_SYSTEM
Packit fc043f
      strcpy (buffer, "/");  /* Always "/".  */
Packit fc043f
      return buffer;
Packit fc043f
#else
Packit fc043f
      if (getcwd (buffer, size) == buffer)
Packit fc043f
        return buffer;
Packit fc043f
      xfree (buffer);
Packit fc043f
      if (errno != ERANGE)
Packit fc043f
        return NULL;
Packit fc043f
      size *= 2;
Packit fc043f
#endif
Packit fc043f
    }
Packit fc043f
}