(root)/
glib-2.79.0/
glib/
tests/
spawn-path-search.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 <glib.h>
      22  
      23  #ifdef G_OS_UNIX
      24  #include <sys/types.h>
      25  #include <sys/wait.h>
      26  #endif
      27  
      28  static gboolean
      29  skip_win32 (void)
      30  {
      31  #ifdef G_OS_WIN32
      32    g_test_skip ("The test manipulate PATH, and breaks DLL lookups.");
      33    return TRUE;
      34  #else
      35    return FALSE;
      36  #endif
      37  }
      38  
      39  static void
      40  test_do_not_search (void)
      41  {
      42    GPtrArray *argv = g_ptr_array_new_with_free_func (g_free);
      43    gchar *here = g_test_build_filename (G_TEST_BUILT, ".", NULL);
      44    gchar *subdir = g_test_build_filename (G_TEST_BUILT, "path-test-subdir", NULL);
      45    gchar **envp = g_get_environ ();
      46    gchar *out = NULL;
      47    gchar *err = NULL;
      48    GError *error = NULL;
      49    int wait_status = -1;
      50  
      51    g_test_summary ("Without G_SPAWN_SEARCH_PATH, spawn-test-helper "
      52                    "means ./spawn-test-helper.");
      53  
      54    if (skip_win32 ())
      55      return;
      56  
      57    envp = g_environ_setenv (envp, "PATH", subdir, TRUE);
      58  
      59    g_ptr_array_add (argv,
      60                     g_test_build_filename (G_TEST_BUILT, "spawn-path-search-helper", NULL));
      61    g_ptr_array_add (argv, g_strdup ("--"));
      62    g_ptr_array_add (argv, g_strdup ("spawn-test-helper"));
      63    g_ptr_array_add (argv, NULL);
      64  
      65    g_spawn_sync (here,
      66                  (char **) argv->pdata,
      67                  envp,
      68                  G_SPAWN_DEFAULT,
      69                  NULL,  /* child setup */
      70                  NULL,  /* user data */
      71                  &out,
      72                  &err,
      73                  &wait_status,
      74                  &error);
      75    g_assert_no_error (error);
      76  
      77    g_test_message ("%s", out);
      78    g_test_message ("%s", err);
      79    g_assert_nonnull (strstr (err, "this is spawn-test-helper from glib/tests"));
      80  
      81  #ifdef G_OS_UNIX
      82    g_assert_true (WIFEXITED (wait_status));
      83    g_assert_cmpint (WEXITSTATUS (wait_status), ==, 0);
      84  #endif
      85  
      86    g_strfreev (envp);
      87    g_free (here);
      88    g_free (subdir);
      89    g_free (out);
      90    g_free (err);
      91    g_ptr_array_unref (argv);
      92  }
      93  
      94  static void
      95  test_search_path (void)
      96  {
      97    GPtrArray *argv = g_ptr_array_new_with_free_func (g_free);
      98    gchar *here = g_test_build_filename (G_TEST_BUILT, ".", NULL);
      99    gchar *subdir = g_test_build_filename (G_TEST_BUILT, "path-test-subdir", NULL);
     100    gchar **envp = g_get_environ ();
     101    gchar *out = NULL;
     102    gchar *err = NULL;
     103    GError *error = NULL;
     104    int wait_status = -1;
     105  
     106    g_test_summary ("With G_SPAWN_SEARCH_PATH, spawn-test-helper "
     107                    "means $PATH/spawn-test-helper.");
     108  
     109    if (skip_win32 ())
     110      return;
     111  
     112    envp = g_environ_setenv (envp, "PATH", subdir, TRUE);
     113  
     114    g_ptr_array_add (argv,
     115                     g_test_build_filename (G_TEST_BUILT, "spawn-path-search-helper", NULL));
     116    g_ptr_array_add (argv, g_strdup ("--search-path"));
     117    g_ptr_array_add (argv, g_strdup ("--"));
     118    g_ptr_array_add (argv, g_strdup ("spawn-test-helper"));
     119    g_ptr_array_add (argv, NULL);
     120  
     121    g_spawn_sync (here,
     122                  (char **) argv->pdata,
     123                  envp,
     124                  G_SPAWN_DEFAULT,
     125                  NULL,  /* child setup */
     126                  NULL,  /* user data */
     127                  &out,
     128                  &err,
     129                  &wait_status,
     130                  &error);
     131    g_assert_no_error (error);
     132  
     133    g_test_message ("%s", out);
     134    g_test_message ("%s", err);
     135    g_assert_nonnull (strstr (err, "this is spawn-test-helper from path-test-subdir"));
     136  
     137  #ifdef G_OS_UNIX
     138    g_assert_true (WIFEXITED (wait_status));
     139    g_assert_cmpint (WEXITSTATUS (wait_status), ==, 5);
     140  #endif
     141  
     142    g_strfreev (envp);
     143    g_free (here);
     144    g_free (subdir);
     145    g_free (out);
     146    g_free (err);
     147    g_ptr_array_unref (argv);
     148  }
     149  
     150  static void
     151  test_search_path_from_envp (void)
     152  {
     153    GPtrArray *argv = g_ptr_array_new_with_free_func (g_free);
     154    gchar *here = g_test_build_filename (G_TEST_BUILT, ".", NULL);
     155    gchar *subdir = g_test_build_filename (G_TEST_BUILT, "path-test-subdir", NULL);
     156    gchar **envp = g_get_environ ();
     157    gchar *out = NULL;
     158    gchar *err = NULL;
     159    GError *error = NULL;
     160    int wait_status = -1;
     161  
     162    g_test_summary ("With G_SPAWN_SEARCH_PATH_FROM_ENVP, spawn-test-helper "
     163                    "means $PATH/spawn-test-helper with $PATH from envp.");
     164  
     165    if (skip_win32 ())
     166      return;
     167  
     168    envp = g_environ_setenv (envp, "PATH", here, TRUE);
     169  
     170    g_ptr_array_add (argv,
     171                     g_test_build_filename (G_TEST_BUILT, "spawn-path-search-helper", NULL));
     172    g_ptr_array_add (argv, g_strdup ("--search-path-from-envp"));
     173    g_ptr_array_add (argv, g_strdup ("--set-path-in-envp"));
     174    g_ptr_array_add (argv, g_strdup (subdir));
     175    g_ptr_array_add (argv, g_strdup ("--"));
     176    g_ptr_array_add (argv, g_strdup ("spawn-test-helper"));
     177    g_ptr_array_add (argv, NULL);
     178  
     179    g_spawn_sync (here,
     180                  (char **) argv->pdata,
     181                  envp,
     182                  G_SPAWN_DEFAULT,
     183                  NULL,  /* child setup */
     184                  NULL,  /* user data */
     185                  &out,
     186                  &err,
     187                  &wait_status,
     188                  &error);
     189    g_assert_no_error (error);
     190  
     191    g_test_message ("%s", out);
     192    g_test_message ("%s", err);
     193    g_assert_nonnull (strstr (err, "this is spawn-test-helper from path-test-subdir"));
     194  
     195  #ifdef G_OS_UNIX
     196    g_assert_true (WIFEXITED (wait_status));
     197    g_assert_cmpint (WEXITSTATUS (wait_status), ==, 5);
     198  #endif
     199  
     200    g_strfreev (envp);
     201    g_free (here);
     202    g_free (subdir);
     203    g_free (out);
     204    g_free (err);
     205    g_ptr_array_unref (argv);
     206  }
     207  
     208  static void
     209  test_search_path_ambiguous (void)
     210  {
     211    GPtrArray *argv = g_ptr_array_new_with_free_func (g_free);
     212    gchar *here = g_test_build_filename (G_TEST_BUILT, ".", NULL);
     213    gchar *subdir = g_test_build_filename (G_TEST_BUILT, "path-test-subdir", NULL);
     214    gchar **envp = g_get_environ ();
     215    gchar *out = NULL;
     216    gchar *err = NULL;
     217    GError *error = NULL;
     218    int wait_status = -1;
     219  
     220    g_test_summary ("With G_SPAWN_SEARCH_PATH and G_SPAWN_SEARCH_PATH_FROM_ENVP, "
     221                    "the latter wins.");
     222  
     223    if (skip_win32 ())
     224      return;
     225  
     226    envp = g_environ_setenv (envp, "PATH", here, TRUE);
     227  
     228    g_ptr_array_add (argv,
     229                     g_test_build_filename (G_TEST_BUILT, "spawn-path-search-helper", NULL));
     230    g_ptr_array_add (argv, g_strdup ("--search-path"));
     231    g_ptr_array_add (argv, g_strdup ("--search-path-from-envp"));
     232    g_ptr_array_add (argv, g_strdup ("--set-path-in-envp"));
     233    g_ptr_array_add (argv, g_strdup (subdir));
     234    g_ptr_array_add (argv, g_strdup ("--"));
     235    g_ptr_array_add (argv, g_strdup ("spawn-test-helper"));
     236    g_ptr_array_add (argv, NULL);
     237  
     238    g_spawn_sync (here,
     239                  (char **) argv->pdata,
     240                  envp,
     241                  G_SPAWN_DEFAULT,
     242                  NULL,  /* child setup */
     243                  NULL,  /* user data */
     244                  &out,
     245                  &err,
     246                  &wait_status,
     247                  &error);
     248    g_assert_no_error (error);
     249  
     250    g_test_message ("%s", out);
     251    g_test_message ("%s", err);
     252    g_assert_nonnull (strstr (err, "this is spawn-test-helper from path-test-subdir"));
     253  
     254  #ifdef G_OS_UNIX
     255    g_assert_true (WIFEXITED (wait_status));
     256    g_assert_cmpint (WEXITSTATUS (wait_status), ==, 5);
     257  #endif
     258  
     259    g_strfreev (envp);
     260    g_free (here);
     261    g_free (subdir);
     262    g_free (out);
     263    g_free (err);
     264    g_ptr_array_unref (argv);
     265  }
     266  
     267  static void
     268  test_search_path_fallback_in_environ (void)
     269  {
     270    GPtrArray *argv = g_ptr_array_new_with_free_func (g_free);
     271    gchar *here = g_test_build_filename (G_TEST_BUILT, ".", NULL);
     272    gchar *subdir = g_test_build_filename (G_TEST_BUILT, "path-test-subdir", NULL);
     273    gchar **envp = g_get_environ ();
     274    gchar *out = NULL;
     275    gchar *err = NULL;
     276    GError *error = NULL;
     277    int wait_status = -1;
     278  
     279    g_test_summary ("With G_SPAWN_SEARCH_PATH but no PATH, a fallback is used.");
     280  
     281    if (skip_win32 ())
     282      return;
     283  
     284    /* We can't make a meaningful assertion about what the fallback *is*,
     285     * but we can assert that it *includes* the current working directory. */
     286  
     287    if (g_file_test ("/usr/bin/spawn-test-helper", G_FILE_TEST_IS_EXECUTABLE) ||
     288        g_file_test ("/bin/spawn-test-helper", G_FILE_TEST_IS_EXECUTABLE))
     289      {
     290        g_test_skip ("Not testing fallback with unknown spawn-test-helper "
     291                     "executable in /usr/bin:/bin");
     292        return;
     293      }
     294  
     295    envp = g_environ_unsetenv (envp, "PATH");
     296  
     297    g_ptr_array_add (argv,
     298                     g_test_build_filename (G_TEST_BUILT, "spawn-path-search-helper", NULL));
     299    g_ptr_array_add (argv, g_strdup ("--search-path"));
     300    g_ptr_array_add (argv, g_strdup ("--set-path-in-envp"));
     301    g_ptr_array_add (argv, g_strdup (subdir));
     302    g_ptr_array_add (argv, g_strdup ("--"));
     303    g_ptr_array_add (argv, g_strdup ("spawn-test-helper"));
     304    g_ptr_array_add (argv, NULL);
     305  
     306    g_spawn_sync (here,
     307                  (char **) argv->pdata,
     308                  envp,
     309                  G_SPAWN_DEFAULT,
     310                  NULL,  /* child setup */
     311                  NULL,  /* user data */
     312                  &out,
     313                  &err,
     314                  &wait_status,
     315                  &error);
     316    g_assert_no_error (error);
     317  
     318    g_test_message ("%s", out);
     319    g_test_message ("%s", err);
     320    g_assert_nonnull (strstr (err, "this is spawn-test-helper from glib/tests"));
     321  
     322  #ifdef G_OS_UNIX
     323    g_assert_true (WIFEXITED (wait_status));
     324    g_assert_cmpint (WEXITSTATUS (wait_status), ==, 0);
     325  #endif
     326  
     327    g_strfreev (envp);
     328    g_free (here);
     329    g_free (subdir);
     330    g_free (out);
     331    g_free (err);
     332    g_ptr_array_unref (argv);
     333  }
     334  
     335  static void
     336  test_search_path_fallback_in_envp (void)
     337  {
     338    GPtrArray *argv = g_ptr_array_new_with_free_func (g_free);
     339    gchar *here = g_test_build_filename (G_TEST_BUILT, ".", NULL);
     340    gchar *subdir = g_test_build_filename (G_TEST_BUILT, "path-test-subdir", NULL);
     341    gchar **envp = g_get_environ ();
     342    gchar *out = NULL;
     343    gchar *err = NULL;
     344    GError *error = NULL;
     345    int wait_status = -1;
     346  
     347    g_test_summary ("With G_SPAWN_SEARCH_PATH_FROM_ENVP but no PATH, a fallback is used.");
     348    /* We can't make a meaningful assertion about what the fallback *is*,
     349     * but we can assert that it *includes* the current working directory. */
     350  
     351    if (skip_win32 ())
     352      return;
     353  
     354    if (g_file_test ("/usr/bin/spawn-test-helper", G_FILE_TEST_IS_EXECUTABLE) ||
     355        g_file_test ("/bin/spawn-test-helper", G_FILE_TEST_IS_EXECUTABLE))
     356      {
     357        g_test_skip ("Not testing fallback with unknown spawn-test-helper "
     358                     "executable in /usr/bin:/bin");
     359        return;
     360      }
     361  
     362    envp = g_environ_setenv (envp, "PATH", subdir, TRUE);
     363  
     364    g_ptr_array_add (argv,
     365                     g_test_build_filename (G_TEST_BUILT, "spawn-path-search-helper", NULL));
     366    g_ptr_array_add (argv, g_strdup ("--search-path-from-envp"));
     367    g_ptr_array_add (argv, g_strdup ("--unset-path-in-envp"));
     368    g_ptr_array_add (argv, g_strdup ("--"));
     369    g_ptr_array_add (argv, g_strdup ("spawn-test-helper"));
     370    g_ptr_array_add (argv, NULL);
     371  
     372    g_spawn_sync (here,
     373                  (char **) argv->pdata,
     374                  envp,
     375                  G_SPAWN_DEFAULT,
     376                  NULL,  /* child setup */
     377                  NULL,  /* user data */
     378                  &out,
     379                  &err,
     380                  &wait_status,
     381                  &error);
     382    g_assert_no_error (error);
     383  
     384    g_test_message ("%s", out);
     385    g_test_message ("%s", err);
     386    g_assert_nonnull (strstr (err, "this is spawn-test-helper from glib/tests"));
     387  
     388  #ifdef G_OS_UNIX
     389    g_assert_true (WIFEXITED (wait_status));
     390    g_assert_cmpint (WEXITSTATUS (wait_status), ==, 0);
     391  #endif
     392  
     393    g_strfreev (envp);
     394    g_free (here);
     395    g_free (subdir);
     396    g_free (out);
     397    g_free (err);
     398    g_ptr_array_unref (argv);
     399  }
     400  
     401  static void
     402  test_search_path_heap_allocation (void)
     403  {
     404    GPtrArray *argv = g_ptr_array_new_with_free_func (g_free);
     405    /* Must be longer than the arbitrary 4000 byte limit for stack allocation
     406     * in gspawn.c */
     407    char placeholder[4096];
     408    gchar *here = g_test_build_filename (G_TEST_BUILT, ".", NULL);
     409    gchar *subdir = g_test_build_filename (G_TEST_BUILT, "path-test-subdir", NULL);
     410    gchar *long_dir = NULL;
     411    gchar *long_path = NULL;
     412    gchar **envp = g_get_environ ();
     413    gchar *out = NULL;
     414    gchar *err = NULL;
     415    GError *error = NULL;
     416    int wait_status = -1;
     417    gsize i;
     418  
     419    if (skip_win32 ())
     420      return;
     421  
     422    memset (placeholder, '_', sizeof (placeholder) - 1);
     423    placeholder[sizeof (placeholder) - 1] = '\0';
     424    /* Force search_path_buffer to be heap-allocated */
     425    long_dir = g_test_build_filename (G_TEST_BUILT, "path-test-subdir", placeholder, NULL);
     426    long_path = g_strjoin (G_SEARCHPATH_SEPARATOR_S, subdir, long_dir, NULL);
     427    envp = g_environ_setenv (envp, "PATH", long_path, TRUE);
     428    g_free (long_path);
     429    g_free (long_dir);
     430  
     431    g_ptr_array_add (argv,
     432                     g_test_build_filename (G_TEST_BUILT, "spawn-path-search-helper", NULL));
     433    g_ptr_array_add (argv, g_strdup ("--search-path"));
     434    g_ptr_array_add (argv, g_strdup ("--"));
     435    g_ptr_array_add (argv, g_strdup ("spawn-test-helper"));
     436  
     437    /* Add enough arguments to make argv longer than the arbitrary 4000 byte
     438     * limit for stack allocation in gspawn.c.
     439     * This assumes sizeof (char *) >= 4. */
     440    for (i = 0; i < 1001; i++)
     441      g_ptr_array_add (argv, g_strdup ("_"));
     442  
     443    g_ptr_array_add (argv, NULL);
     444  
     445    g_spawn_sync (here,
     446                  (char **) argv->pdata,
     447                  envp,
     448                  G_SPAWN_DEFAULT,
     449                  NULL,  /* child setup */
     450                  NULL,  /* user data */
     451                  &out,
     452                  &err,
     453                  &wait_status,
     454                  &error);
     455    g_assert_no_error (error);
     456  
     457    g_test_message ("%s", out);
     458    g_test_message ("%s", err);
     459    g_assert_nonnull (strstr (err, "this is spawn-test-helper from path-test-subdir"));
     460  
     461  #ifdef G_OS_UNIX
     462    g_assert_true (WIFEXITED (wait_status));
     463    g_assert_cmpint (WEXITSTATUS (wait_status), ==, 5);
     464  #endif
     465  
     466    g_strfreev (envp);
     467    g_free (here);
     468    g_free (subdir);
     469    g_free (out);
     470    g_free (err);
     471    g_ptr_array_unref (argv);
     472  }
     473  
     474  int
     475  main (int    argc,
     476        char **argv)
     477  {
     478    g_test_init (&argc, &argv, NULL);
     479  
     480    g_test_add_func ("/spawn/do-not-search", test_do_not_search);
     481    g_test_add_func ("/spawn/search-path", test_search_path);
     482    g_test_add_func ("/spawn/search-path-from-envp", test_search_path_from_envp);
     483    g_test_add_func ("/spawn/search-path-ambiguous", test_search_path_ambiguous);
     484    g_test_add_func ("/spawn/search-path-heap-allocation",
     485                     test_search_path_heap_allocation);
     486    g_test_add_func ("/spawn/search-path-fallback-in-environ",
     487                     test_search_path_fallback_in_environ);
     488    g_test_add_func ("/spawn/search-path-fallback-in-envp",
     489                     test_search_path_fallback_in_envp);
     490  
     491    return g_test_run ();
     492  }