(root)/
glibc-2.38/
sysdeps/
posix/
system.c
       1  /* Copyright (C) 1991-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <errno.h>
      19  #include <signal.h>
      20  #include <stdlib.h>
      21  #include <unistd.h>
      22  #include <sigsetops.h>
      23  #include <spawn.h>
      24  #include <pthread.h>
      25  #include <sys/types.h>
      26  #include <sys/wait.h>
      27  #include <stdio.h>
      28  
      29  #include <libc-lock.h>
      30  #include <not-errno.h>
      31  #include <not-cancel.h>
      32  #include <internal-signals.h>
      33  
      34  #define	SHELL_PATH	"/bin/sh"	/* Path of the shell.  */
      35  #define	SHELL_NAME	"sh"		/* Name to give it.  */
      36  
      37  
      38  /* This system implementation aims to be thread-safe, which requires to
      39     restore the signal dispositions for SIGINT and SIGQUIT correctly and to
      40     deal with cancellation by terminating the child process.
      41  
      42     The signal disposition restoration on the single-thread case is
      43     straighfoward.  For multithreaded case, a reference-counter with a lock
      44     is used, so the first thread will set the SIGINT/SIGQUIT dispositions and
      45     last thread will restore them.
      46  
      47     Cancellation handling is done with thread cancellation clean-up handlers
      48     on waitpid call.  */
      49  
      50  #ifdef _LIBC_REENTRANT
      51  static struct sigaction intr, quit;
      52  static int sa_refcntr;
      53  __libc_lock_define_initialized (static, lock);
      54  
      55  # define DO_LOCK() __libc_lock_lock (lock)
      56  # define DO_UNLOCK() __libc_lock_unlock (lock)
      57  # define INIT_LOCK() ({ __libc_lock_init (lock); sa_refcntr = 0; })
      58  # define ADD_REF() sa_refcntr++
      59  # define SUB_REF() --sa_refcntr
      60  #else
      61  # define DO_LOCK()
      62  # define DO_UNLOCK()
      63  # define INIT_LOCK()
      64  # define ADD_REF() 0
      65  # define SUB_REF() 0
      66  #endif
      67  
      68  
      69  #if defined(_LIBC_REENTRANT) && defined(SIGCANCEL)
      70  struct cancel_handler_args
      71  {
      72    struct sigaction *quit;
      73    struct sigaction *intr;
      74    pid_t pid;
      75  };
      76  
      77  static void
      78  cancel_handler (void *arg)
      79  {
      80    struct cancel_handler_args *args = (struct cancel_handler_args *) (arg);
      81  
      82    __kill_noerrno (args->pid, SIGKILL);
      83  
      84    int state;
      85    __pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &state);
      86    TEMP_FAILURE_RETRY (__waitpid (args->pid, NULL, 0));
      87    __pthread_setcancelstate (state, NULL);
      88  
      89    DO_LOCK ();
      90    if (SUB_REF () == 0)
      91      {
      92        __sigaction (SIGQUIT, args->quit, NULL);
      93        __sigaction (SIGINT, args->intr, NULL);
      94      }
      95    DO_UNLOCK ();
      96  }
      97  #endif
      98  
      99  /* Execute LINE as a shell command, returning its status.  */
     100  static int
     101  do_system (const char *line)
     102  {
     103    int status = -1;
     104    int ret;
     105    pid_t pid;
     106    struct sigaction sa;
     107  #ifndef _LIBC_REENTRANT
     108    struct sigaction intr, quit;
     109  #endif
     110    sigset_t omask;
     111    sigset_t reset;
     112  
     113    sa.sa_handler = SIG_IGN;
     114    sa.sa_flags = 0;
     115    __sigemptyset (&sa.sa_mask);
     116  
     117    DO_LOCK ();
     118    if (ADD_REF () == 0)
     119      {
     120        /* sigaction can not fail with SIGINT/SIGQUIT used with SIG_IGN.  */
     121        __sigaction (SIGINT, &sa, &intr);
     122        __sigaction (SIGQUIT, &sa, &quit);
     123      }
     124    DO_UNLOCK ();
     125  
     126    __sigaddset (&sa.sa_mask, SIGCHLD);
     127    /* sigprocmask can not fail with SIG_BLOCK used with valid input
     128       arguments.  */
     129    __sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask);
     130  
     131    __sigemptyset (&reset);
     132    if (intr.sa_handler != SIG_IGN)
     133      __sigaddset(&reset, SIGINT);
     134    if (quit.sa_handler != SIG_IGN)
     135      __sigaddset(&reset, SIGQUIT);
     136  
     137    posix_spawnattr_t spawn_attr;
     138    /* None of the posix_spawnattr_* function returns an error, including
     139       posix_spawnattr_setflags for the follow specific usage (using valid
     140       flags).  */
     141    __posix_spawnattr_init (&spawn_attr);
     142    __posix_spawnattr_setsigmask (&spawn_attr, &omask);
     143    __posix_spawnattr_setsigdefault (&spawn_attr, &reset);
     144    __posix_spawnattr_setflags (&spawn_attr,
     145  			      POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK);
     146  
     147    ret = __posix_spawn (&pid, SHELL_PATH, 0, &spawn_attr,
     148  		       (char *const[]){ (char *) SHELL_NAME,
     149  					(char *) "-c",
     150  					(char *) "--",
     151  					(char *) line, NULL },
     152  		       __environ);
     153    __posix_spawnattr_destroy (&spawn_attr);
     154  
     155    if (ret == 0)
     156      {
     157        /* Cancellation results in cleanup handlers running as exceptions in
     158  	 the block where they were installed, so it is safe to reference
     159  	 stack variable allocate in the broader scope.  */
     160  #if defined(_LIBC_REENTRANT) && defined(SIGCANCEL)
     161        struct cancel_handler_args cancel_args =
     162        {
     163  	.quit = &quit,
     164  	.intr = &intr,
     165  	.pid = pid
     166        };
     167        __libc_cleanup_region_start (1, cancel_handler, &cancel_args);
     168  #endif
     169        /* Note the system() is a cancellation point.  But since we call
     170  	 waitpid() which itself is a cancellation point we do not
     171  	 have to do anything here.  */
     172        if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid)
     173  	status = -1;
     174  #if defined(_LIBC_REENTRANT) && defined(SIGCANCEL)
     175        __libc_cleanup_region_end (0);
     176  #endif
     177      }
     178    else
     179     /* POSIX states that failure to execute the shell should return
     180        as if the shell had terminated using _exit(127).  */
     181     status = W_EXITCODE (127, 0);
     182  
     183    /* sigaction can not fail with SIGINT/SIGQUIT used with old
     184       disposition.  Same applies for sigprocmask.  */
     185    DO_LOCK ();
     186    if (SUB_REF () == 0)
     187      {
     188        __sigaction (SIGINT, &intr, NULL);
     189        __sigaction (SIGQUIT, &quit, NULL);
     190      }
     191    DO_UNLOCK ();
     192    __sigprocmask (SIG_SETMASK, &omask, NULL);
     193  
     194    if (ret != 0)
     195      __set_errno (ret);
     196  
     197    return status;
     198  }
     199  
     200  int
     201  __libc_system (const char *line)
     202  {
     203    if (line == NULL)
     204      /* Check that we have a command processor available.  It might
     205         not be available after a chroot(), for example.  */
     206      return do_system ("exit 0") == 0;
     207  
     208    return do_system (line);
     209  }
     210  weak_alias (__libc_system, system)