(root)/
glibc-2.38/
posix/
tst-spawn6.c
       1  /* Check posix_spawn set controlling terminal extension.
       2     Copyright (C) 2022-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <array_length.h>
      20  #include <errno.h>
      21  #include <fcntl.h>
      22  #include <getopt.h>
      23  #include <intprops.h>
      24  #include <paths.h>
      25  #include <spawn.h>
      26  #include <stdbool.h>
      27  #include <stdio.h>
      28  #include <string.h>
      29  #include <support/check.h>
      30  #include <support/xunistd.h>
      31  #include <sys/wait.h>
      32  #include <sys/ioctl.h>
      33  #include <stdlib.h>
      34  #include <termios.h>
      35  
      36  #ifndef PATH_MAX
      37  # define PATH_MAX 1024
      38  #endif
      39  static char ptmxpath[PATH_MAX];
      40  
      41  static int
      42  handle_restart (const char *argv1, const char *argv2)
      43  {
      44    /* If process group is not changed (POSIX_SPAWN_SETPGROUP), then check
      45       the creating process one, otherwise check against the process group
      46       itself.  */
      47    pid_t pgrp;
      48    if (strcmp (argv1, "setgrpr") != 0)
      49      TEST_COMPARE (sscanf (argv1, "%d", &pgrp), 1);
      50    else
      51      {
      52        pgrp = getpgrp ();
      53        /* Check if a new process group was actually created.  */
      54        pid_t ppid = getppid ();
      55        pid_t pgid = getpgid (ppid);
      56        TEST_VERIFY (pgid != pgrp);
      57      }
      58  
      59    char *endptr;
      60    long int tcfd = strtol (argv2, &endptr, 10);
      61    if (*endptr != '\0' || tcfd > INT_MAX)
      62      FAIL_EXIT1 ("invalid file descriptor name: %s", argv2);
      63    if (tcfd != -1)
      64      {
      65        TEST_COMPARE (fcntl (tcfd, F_GETFD), -1);
      66        TEST_COMPARE (errno, EBADF);
      67      }
      68  
      69    int fd = xopen (_PATH_TTY, O_RDONLY, 0600);
      70    TEST_COMPARE (tcgetpgrp (fd), pgrp);
      71    xclose (fd);
      72  
      73    return 0;
      74  }
      75  
      76  static int restart;
      77  #define CMDLINE_OPTIONS \
      78    { "restart", no_argument, &restart, 1 },
      79  
      80  static void
      81  run_subprogram (int argc, char *argv[], const posix_spawnattr_t *attr,
      82  		const posix_spawn_file_actions_t *actions, int tcfd,
      83  		int exp_err)
      84  {
      85    short int flags;
      86    TEST_COMPARE (posix_spawnattr_getflags (attr, &flags), 0);
      87    bool setpgrp = flags & POSIX_SPAWN_SETPGROUP;
      88  
      89    char *spargv[9];
      90    TEST_VERIFY_EXIT (((argc - 1) + 4) < array_length (spargv));
      91    char pgrp[INT_STRLEN_BOUND (pid_t)];
      92    char tcfdstr[INT_STRLEN_BOUND (int)];
      93  
      94    int i = 0;
      95    for (; i < argc - 1; i++)
      96      spargv[i] = argv[i + 1];
      97    spargv[i++] = (char *) "--direct";
      98    spargv[i++] = (char *) "--restart";
      99    if (setpgrp)
     100      spargv[i++] = (char *) "setgrpr";
     101    else
     102      {
     103        snprintf (pgrp, sizeof pgrp, "%d", getpgrp ());
     104        spargv[i++] = pgrp;
     105      }
     106    snprintf (tcfdstr, sizeof tcfdstr, "%d", tcfd);
     107    spargv[i++] = tcfdstr;
     108    spargv[i] = NULL;
     109  
     110    pid_t pid;
     111    TEST_COMPARE (posix_spawn (&pid, argv[1], actions, attr, spargv, environ),
     112  		exp_err);
     113    if (exp_err != 0)
     114      return;
     115  
     116    int status;
     117    TEST_COMPARE (xwaitpid (pid, &status, WUNTRACED), pid);
     118    TEST_VERIFY (WIFEXITED (status));
     119    TEST_VERIFY (!WIFSTOPPED (status));
     120    TEST_VERIFY (!WIFSIGNALED (status));
     121    TEST_COMPARE (WEXITSTATUS (status), 0);
     122  }
     123  
     124  static int
     125  run_test (int argc, char *argv[])
     126  {
     127    /* We must have either:
     128       - four parameters left if called initially:
     129         + path to ld.so         optional
     130         + "--library-path"      optional
     131         + the library path      optional
     132         + the application name
     133       - six parameters left if called through re-execution:
     134         + --setgrpr             optional
     135     */
     136  
     137    int tcfd = xopen (ptmxpath, O_RDONLY, 0600);
     138  
     139    /* Check setting the controlling terminal without changing the group.  */
     140    {
     141      posix_spawnattr_t attr;
     142      TEST_COMPARE (posix_spawnattr_init (&attr), 0);
     143      posix_spawn_file_actions_t actions;
     144      TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0);
     145      TEST_COMPARE (posix_spawn_file_actions_addtcsetpgrp_np (&actions, tcfd),
     146  		  0);
     147  
     148      run_subprogram (argc, argv, &attr, &actions, -1, 0);
     149    }
     150  
     151    /* Check setting both the controlling terminal and the create a new process
     152       group.  */
     153    {
     154      posix_spawnattr_t attr;
     155      TEST_COMPARE (posix_spawnattr_init (&attr), 0);
     156      TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETPGROUP), 0);
     157      posix_spawn_file_actions_t actions;
     158      TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0);
     159      TEST_COMPARE (posix_spawn_file_actions_addtcsetpgrp_np (&actions, tcfd),
     160  		  0);
     161  
     162      run_subprogram (argc, argv, &attr, &actions, -1, 0);
     163    }
     164  
     165    /* Same as before, but check if the addclose file actions closes the terminal
     166       file descriptor.  */
     167    {
     168      posix_spawnattr_t attr;
     169      TEST_COMPARE (posix_spawnattr_init (&attr), 0);
     170      TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETPGROUP), 0);
     171      posix_spawn_file_actions_t actions;
     172      TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0);
     173      TEST_COMPARE (posix_spawn_file_actions_addtcsetpgrp_np (&actions, tcfd),
     174  		  0);
     175      TEST_COMPARE (posix_spawn_file_actions_addclose (&actions, tcfd), 0);
     176  
     177      run_subprogram (argc, argv, &attr, &actions, tcfd, 0);
     178    }
     179  
     180    /* Trying to set the controlling terminal after a setsid incurs in a ENOTTY
     181       from tcsetpgrp.  */
     182    {
     183      posix_spawnattr_t attr;
     184      TEST_COMPARE (posix_spawnattr_init (&attr), 0);
     185      TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETSID), 0);
     186      posix_spawn_file_actions_t actions;
     187      TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0);
     188      TEST_COMPARE (posix_spawn_file_actions_addtcsetpgrp_np (&actions, tcfd),
     189  		  0);
     190  
     191      run_subprogram (argc, argv, &attr, &actions, -1, ENOTTY);
     192    }
     193  
     194    xclose (tcfd);
     195  
     196    return 0;
     197  }
     198  
     199  static int
     200  do_test (int argc, char *argv[])
     201  {
     202    if (restart)
     203      return handle_restart (argv[1], argv[2]);
     204  
     205    pid_t pid = xfork ();
     206    if (pid == 0)
     207      {
     208        /* Create a pseudo-terminal to avoid interfering with the one using by
     209  	 test itself, creates a new session (so there is no controlling
     210  	 terminal), and set the pseudo-terminal as the controlling one.  */
     211        int ptmx = posix_openpt (0);
     212        if (ptmx == -1)
     213  	{
     214  	  if (errno == ENXIO)
     215  	    FAIL_UNSUPPORTED ("terminal not available, skipping test");
     216  	  FAIL_EXIT1 ("posix_openpt (0): %m");
     217  	}
     218        TEST_VERIFY_EXIT (grantpt (ptmx) == 0);
     219        TEST_VERIFY_EXIT (unlockpt (ptmx) == 0);
     220  
     221        TEST_VERIFY_EXIT (setsid () != -1);
     222        TEST_VERIFY_EXIT (ioctl (ptmx, TIOCSCTTY, NULL) == 0);
     223        while (dup2 (ptmx, STDIN_FILENO) == -1 && errno == EBUSY)
     224  	;
     225        while (dup2 (ptmx, STDOUT_FILENO) == -1 && errno == EBUSY)
     226  	;
     227        while (dup2 (ptmx, STDERR_FILENO) == -1 && errno == EBUSY)
     228  	;
     229        TEST_VERIFY_EXIT (ptsname_r (ptmx, ptmxpath, sizeof ptmxpath) == 0);
     230        xclose (ptmx);
     231  
     232        run_test (argc, argv);
     233        _exit (0);
     234      }
     235    int status;
     236    xwaitpid (pid, &status, 0);
     237    TEST_VERIFY (WIFEXITED (status));
     238    exit (0);
     239  }
     240  
     241  #define TEST_FUNCTION_ARGV do_test
     242  #include <support/test-driver.c>