(root)/
glib-2.79.0/
glib/
tests/
spawn-path-search-helper.c
       1  /*
       2   * Copyright 2021 Collabora Ltd.
       3   *
       4   * SPDX-License-Identifier: LGPL-2.1-or-later
       5   *
       6   * This library is free software; you can redistribute it and/or
       7   * modify it under the terms of the GNU Lesser General Public
       8   * License as published by the Free Software Foundation; either
       9   * version 2.1 of the License, or (at your option) any later version.
      10   *
      11   * This library is distributed in the hope that it will be useful,
      12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14   * Lesser General Public License for more details.
      15   *
      16   * You should have received a copy of the GNU Lesser General Public
      17   * License along with this library; if not, see
      18   * <http://www.gnu.org/licenses/>.
      19   */
      20  
      21  #include <errno.h>
      22  #include <stdio.h>
      23  
      24  #include <glib.h>
      25  
      26  #ifdef G_OS_UNIX
      27  #include <sys/types.h>
      28  #include <sys/wait.h>
      29  #endif
      30  
      31  static void
      32  child_setup (gpointer user_data)
      33  {
      34  }
      35  
      36  typedef struct
      37  {
      38    int wait_status;
      39    gboolean done;
      40  } ChildStatus;
      41  
      42  static ChildStatus child_status = { -1, FALSE };
      43  
      44  static void
      45  child_watch_cb (GPid pid,
      46                  gint status,
      47                  gpointer user_data)
      48  {
      49    child_status.wait_status = status;
      50    child_status.done = TRUE;
      51  }
      52  
      53  int
      54  main (int    argc,
      55        char **argv)
      56  {
      57    gboolean search_path = FALSE;
      58    gboolean search_path_from_envp = FALSE;
      59    gboolean slow_path = FALSE;
      60    gboolean unset_path_in_envp = FALSE;
      61    gchar *chdir_child = NULL;
      62    gchar *set_path_in_envp = NULL;
      63    gchar **envp = NULL;
      64    GOptionEntry entries[] =
      65    {
      66      { "chdir-child", '\0',
      67        G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &chdir_child,
      68        "Run PROGRAM in this working directory", NULL },
      69      { "search-path", '\0',
      70        G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &search_path,
      71        "Search PATH for PROGRAM", NULL },
      72      { "search-path-from-envp", '\0',
      73        G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &search_path_from_envp,
      74        "Search PATH from specified environment", NULL },
      75      { "set-path-in-envp", '\0',
      76        G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &set_path_in_envp,
      77        "Set PATH in specified environment to this value", "PATH", },
      78      { "unset-path-in-envp", '\0',
      79        G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &unset_path_in_envp,
      80        "Unset PATH in specified environment", NULL },
      81      { "slow-path", '\0',
      82        G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &slow_path,
      83        "Use a child-setup function to avoid the posix_spawn fast path", NULL },
      84      G_OPTION_ENTRY_NULL
      85    };
      86    GError *error = NULL;
      87    int ret = 1;
      88    GSpawnFlags spawn_flags = G_SPAWN_DO_NOT_REAP_CHILD;
      89    GPid pid;
      90    GOptionContext *context = NULL;
      91  
      92    context = g_option_context_new ("PROGRAM [ARGS...]");
      93    g_option_context_add_main_entries (context, entries, NULL);
      94  
      95    if (!g_option_context_parse (context, &argc, &argv, &error))
      96      {
      97        ret = 2;
      98        goto out;
      99      }
     100  
     101    if (argc < 2)
     102      {
     103        g_set_error (&error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
     104                     "Usage: %s [OPTIONS] PROGRAM [ARGS...]", argv[0]);
     105        ret = 2;
     106        goto out;
     107      }
     108  
     109    envp = g_get_environ ();
     110  
     111    if (set_path_in_envp != NULL && unset_path_in_envp)
     112      {
     113        g_set_error (&error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
     114                     "Cannot both set PATH and unset it");
     115        ret = 2;
     116        goto out;
     117      }
     118  
     119    if (set_path_in_envp != NULL)
     120      envp = g_environ_setenv (envp, "PATH", set_path_in_envp, TRUE);
     121  
     122    if (unset_path_in_envp)
     123      envp = g_environ_unsetenv (envp, "PATH");
     124  
     125    if (search_path)
     126      spawn_flags |= G_SPAWN_SEARCH_PATH;
     127  
     128    if (search_path_from_envp)
     129      spawn_flags |= G_SPAWN_SEARCH_PATH_FROM_ENVP;
     130  
     131    if (!g_spawn_async_with_pipes (chdir_child,
     132                                   &argv[1],
     133                                   envp,
     134                                   spawn_flags,
     135                                   slow_path ? child_setup : NULL,
     136                                   NULL,  /* user_data */
     137                                   &pid,
     138                                   NULL,  /* stdin */
     139                                   NULL,  /* stdout */
     140                                   NULL,  /* stderr */
     141                                   &error))
     142      {
     143        ret = 1;
     144        goto out;
     145      }
     146  
     147    g_child_watch_add (pid, child_watch_cb, NULL);
     148  
     149    while (!child_status.done)
     150      g_main_context_iteration (NULL, TRUE);
     151  
     152    g_spawn_close_pid (pid);
     153  
     154  #ifdef G_OS_UNIX
     155    if (WIFEXITED (child_status.wait_status))
     156      ret = WEXITSTATUS (child_status.wait_status);
     157    else
     158      ret = 1;
     159  #else
     160    ret = child_status.wait_status;
     161  #endif
     162  
     163  out:
     164    if (error != NULL)
     165      fprintf (stderr, "%s\n", error->message);
     166  
     167    g_free (set_path_in_envp);
     168    g_free (chdir_child);
     169    g_clear_error (&error);
     170    g_strfreev (envp);
     171    g_option_context_free (context);
     172    return ret;
     173  }