Exemplo n.º 1
0
/* Change the ownership and access permission of the slave pseudo
   terminal associated with the master pseudo terminal specified
   by FD.  */
int
grantpt (int fd)
{
  int retval = -1;
#ifdef PATH_MAX
  char _buf[PATH_MAX];
#else
  char _buf[512];
#endif
  char *buf = _buf;
  struct stat64 st;

  if (__glibc_unlikely (pts_name (fd, &buf, sizeof (_buf), &st)))
    {
      int save_errno = errno;

      /* Check, if the file descriptor is valid.  pts_name returns the
	 wrong errno number, so we cannot use that.  */
      if (__libc_fcntl (fd, F_GETFD) == -1 && errno == EBADF)
	return -1;

       /* If the filedescriptor is no TTY, grantpt has to set errno
	  to EINVAL.  */
       if (save_errno == ENOTTY)
	 __set_errno (EINVAL);
       else
	 __set_errno (save_errno);

       return -1;
    }

  /* Make sure that we own the device.  */
  uid_t uid = __getuid ();
  if (st.st_uid != uid)
    {
      if (__chown (buf, uid, st.st_gid) < 0)
	goto helper;
    }

  static int tty_gid = -1;
  if (__glibc_unlikely (tty_gid == -1))
    {
      char *grtmpbuf;
      struct group grbuf;
      size_t grbuflen = __sysconf (_SC_GETGR_R_SIZE_MAX);
      struct group *p;

      /* Get the group ID of the special `tty' group.  */
      if (grbuflen == (size_t) -1L)
	/* `sysconf' does not support _SC_GETGR_R_SIZE_MAX.
	   Try a moderate value.  */
	grbuflen = 1024;
      grtmpbuf = (char *) __alloca (grbuflen);
      __getgrnam_r (TTY_GROUP, &grbuf, grtmpbuf, grbuflen, &p);
      if (p != NULL)
	tty_gid = p->gr_gid;
    }
  gid_t gid = tty_gid == -1 ? __getgid () : tty_gid;

  /* Make sure the group of the device is that special group.  */
  if (st.st_gid != gid)
    {
      if (__chown (buf, uid, gid) < 0)
	goto helper;
    }

  /* Make sure the permission mode is set to readable and writable by
     the owner, and writable by the group.  */
  if ((st.st_mode & ACCESSPERMS) != (S_IRUSR|S_IWUSR|S_IWGRP))
    {
      if (__chmod (buf, S_IRUSR|S_IWUSR|S_IWGRP) < 0)
	goto helper;
    }

  retval = 0;
  goto cleanup;

  /* We have to use the helper program if it is available.  */
 helper:;

#ifdef HAVE_PT_CHOWN
  pid_t pid = __fork ();
  if (pid == -1)
    goto cleanup;
  else if (pid == 0)
    {
      /* Disable core dumps.  */
      struct rlimit rl = { 0, 0 };
      __setrlimit (RLIMIT_CORE, &rl);

      /* We pass the master pseudo terminal as file descriptor PTY_FILENO.  */
      if (fd != PTY_FILENO)
	if (__dup2 (fd, PTY_FILENO) < 0)
	  _exit (FAIL_EBADF);

# ifdef CLOSE_ALL_FDS
      CLOSE_ALL_FDS ();
# endif

      execle (_PATH_PT_CHOWN, basename (_PATH_PT_CHOWN), NULL, NULL);
      _exit (FAIL_EXEC);
    }
  else
    {
      int w;

      if (__waitpid (pid, &w, 0) == -1)
	goto cleanup;
      if (!WIFEXITED (w))
	__set_errno (ENOEXEC);
      else
	switch (WEXITSTATUS (w))
	  {
	  case 0:
	    retval = 0;
	    break;
	  case FAIL_EBADF:
	    __set_errno (EBADF);
	    break;
	  case FAIL_EINVAL:
	    __set_errno (EINVAL);
	    break;
	  case FAIL_EACCES:
	    __set_errno (EACCES);
	    break;
	  case FAIL_EXEC:
	    __set_errno (ENOEXEC);
	    break;
	  case FAIL_ENOMEM:
	    __set_errno (ENOMEM);
	    break;

	  default:
	    assert(! "getpt: internal error: invalid exit code from pt_chown");
	  }
    }
#endif

 cleanup:
  if (buf != _buf)
    free (buf);

  return retval;
}
Exemplo n.º 2
0
/* Execute LINE as a shell command, returning its status.  */
static int
do_system (const char *line)
{
  int status, save;
  pid_t pid;
  struct sigaction sa;
#ifndef _LIBC_REENTRANT
  struct sigaction intr, quit;
#endif
  sigset_t omask;

  sa.sa_handler = SIG_IGN;
  sa.sa_flags = 0;
  __sigemptyset (&sa.sa_mask);

  DO_LOCK ();
  if (ADD_REF () == 0)
    {
      if (__sigaction (SIGINT, &sa, &intr) < 0)
	{
	  SUB_REF ();
	  goto out;
	}
      if (__sigaction (SIGQUIT, &sa, &quit) < 0)
	{
	  save = errno;
	  SUB_REF ();
	  goto out_restore_sigint;
	}
    }
  DO_UNLOCK ();

  /* We reuse the bitmap in the 'sa' structure.  */
  __sigaddset (&sa.sa_mask, SIGCHLD);
  save = errno;
  if (__sigprocmask (SIG_BLOCK, &sa.sa_mask, &omask) < 0)
    {
#ifndef _LIBC
      if (errno == ENOSYS)
	__set_errno (save);
      else
#endif
	{
	  DO_LOCK ();
	  if (SUB_REF () == 0)
	    {
	      save = errno;
	      (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
	    out_restore_sigint:
	      (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
	      __set_errno (save);
	    }
	out:
	  DO_UNLOCK ();
	  return -1;
	}
    }

#ifdef CLEANUP_HANDLER
  CLEANUP_HANDLER;
#endif

#ifdef FORK
  pid = FORK ();
#else
  pid = __fork ();
#endif
  if (pid == (pid_t) 0)
    {
      /* Child side.  */
      const char *new_argv[4];
      new_argv[0] = SHELL_NAME;
      new_argv[1] = "-c";
      new_argv[2] = line;
      new_argv[3] = NULL;

      /* Restore the signals.  */
      (void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
      (void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
      (void) __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL);
      INIT_LOCK ();

      /* Exec the shell.  */
      SB_LOG(SB_LOGLEVEL_DEBUG, "system(%s)", line);
      (void) __execve (SHELL_PATH, (char *const *) new_argv, __environ);
      _exit (127);
    }
  else if (pid < (pid_t) 0)
    /* The fork failed.  */
    status = -1;
  else
    /* Parent side.  */
    {
      /* Note the system() is a cancellation point.  But since we call
	 waitpid() which itself is a cancellation point we do not
	 have to do anything here.  */
      if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid)
	status = -1;
      SB_LOG(SB_LOGLEVEL_DEBUG, "system: waitpid => status=%d", status);
    }

#ifdef CLEANUP_HANDLER
  CLEANUP_RESET;
#endif

  save = errno;
  DO_LOCK ();
  if ((SUB_REF () == 0
       && (__sigaction (SIGINT, &intr, (struct sigaction *) NULL)
	   | __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)) != 0)
      || __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL) != 0)
    {
#ifndef _LIBC
      /* glibc cannot be used on systems without waitpid.  */
      if (errno == ENOSYS)
	__set_errno (save);
      else
#endif
	status = -1;
    }
  DO_UNLOCK ();

  return status;
}
Exemplo n.º 3
0
/* Wait for a child to die.  When one does, put its status in *STAT_LOC
   and return its process ID.  For errors, return (pid_t) -1.  */
__pid_t
__libc_wait (__WAIT_STATUS_DEFN stat_loc)
{
  return __waitpid (WAIT_ANY, (int *) stat_loc, 0);
}
static int
OUR_WAITID (idtype_t idtype, id_t id, siginfo_t *infop, int options)
{
  pid_t pid, child;
  int status;

  switch (idtype)
    {
    case P_PID:
      if(id <= 0)
	goto invalid;
      pid = (pid_t) id;
      break;
    case P_PGID:
      if (id < 0 || id == 1)
	goto invalid;
      pid = (pid_t) -id;
      break;
    case P_ALL:
      pid = -1;
      break;
    default:
    invalid:
      __set_errno (EINVAL);
      return -1;
    }

  /* Technically we're supposed to return EFAULT if infop is bogus,
     but that would involve mucking with signals, which is
     too much hassle.  User will have to deal with SIGSEGV/SIGBUS.
     We just check for a null pointer. */

  if (infop == NULL)
    {
      __set_errno (EFAULT);
      return -1;
    }

  /* This emulation using waitpid cannot support the waitid modes in which
     we do not reap the child, or match only stopped and not dead children.  */
  if (0
#ifdef WNOWAIT
      || (options & WNOWAIT)
#endif
#ifdef WEXITED
      || ((options & (WEXITED|WSTOPPED|WCONTINUED))
	  != (WEXITED | (options & WUNTRACED)))
#endif
      )
    {
      __set_errno (ENOTSUP);
      return -1;
    }

  /* Note the waitid() is a cancellation point.  But since we call
     waitpid() which itself is a cancellation point we do not have
     to do anything here.  */
  child = __waitpid (pid, &status,
		     options
#ifdef WEXITED
		     &~ WEXITED
#endif
		     );

  if (child == -1)
    /* `waitpid' set `errno' for us.  */
    return -1;

  if (child == 0)
    {
      /* The WHOHANG bit in OPTIONS is set and there are children available
	 but none has a status for us.  The XPG docs do not mention this
	 case so we clear the `siginfo_t' struct and return successfully.  */
      infop->si_signo = 0;
      infop->si_code = 0;
      return 0;
    }

  /* Decode the status field and set infop members... */
  infop->si_signo = SIGCHLD;
  infop->si_pid = child;
  infop->si_errno = 0;

  if (WIFEXITED (status))
    {
      infop->si_code = CLD_EXITED;
      infop->si_status = WEXITSTATUS (status);
    }
  else if (WIFSIGNALED (status))
    {
      infop->si_code = WCOREDUMP (status) ? CLD_DUMPED : CLD_KILLED;
      infop->si_status = WTERMSIG (status);
    }
  else if (WIFSTOPPED (status))
    {
      infop->si_code = CLD_STOPPED;
      infop->si_status = WSTOPSIG (status);
    }
#ifdef WIFCONTINUED
  else if (WIFCONTINUED (status))
    {
      infop->si_code = CLD_CONTINUED;
      infop->si_status = SIGCONT;
    }
#endif
  else
    /* Can't happen. */
    assert (! "What?");

  return 0;
}
Exemplo n.º 5
0
/* Spawn a new process executing PATH with the attributes describes in *ATTRP.
   Before running the process perform the actions described in FILE-ACTIONS. */
static int
__spawnix (pid_t * pid, const char *file,
	   const posix_spawn_file_actions_t * file_actions,
	   const posix_spawnattr_t * attrp, char *const argv[],
	   char *const envp[], int xflags,
	   int (*exec) (const char *, char *const *, char *const *))
{
  pid_t new_pid;
  struct posix_spawn_args args;
  int ec;

  if (__pipe2 (args.pipe, O_CLOEXEC))
    return errno;

  /* To avoid imposing hard limits on posix_spawn{p} the total number of
     arguments is first calculated to allocate a mmap to hold all possible
     values.  */
  ptrdiff_t argc = 0;
  /* Linux allows at most max (0x7FFFFFFF, 1/4 stack size) arguments
     to be used in a execve call.  We limit to INT_MAX minus one due the
     compatiblity code that may execute a shell script (maybe_script_execute)
     where it will construct another argument list with an additional
     argument.  */
  ptrdiff_t limit = INT_MAX - 1;
  while (argv[argc++] != NULL)
    if (argc == limit)
      {
	errno = E2BIG;
	return errno;
      }

  int prot = (PROT_READ | PROT_WRITE
	     | ((GL (dl_stack_flags) & PF_X) ? PROT_EXEC : 0));

  /* Add a slack area for child's stack.  */
  size_t argv_size = (argc * sizeof (void *)) + 512;
  size_t stack_size = ALIGN_UP (argv_size, GLRO(dl_pagesize));
  void *stack = __mmap (NULL, stack_size, prot,
			MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
  if (__glibc_unlikely (stack == MAP_FAILED))
    {
      close_not_cancel (args.pipe[0]);
      close_not_cancel (args.pipe[1]);
      return errno;
    }

  /* Disable asynchronous cancellation.  */
  int cs = LIBC_CANCEL_ASYNC ();

  args.file = file;
  args.exec = exec;
  args.fa = file_actions;
  args.attr = attrp ? attrp : &(const posix_spawnattr_t) { 0 };
  args.argv = argv;
  args.argc = argc;
  args.envp = envp;
  args.xflags = xflags;

  __sigprocmask (SIG_BLOCK, &SIGALL_SET, &args.oldmask);

  /* The clone flags used will create a new child that will run in the same
     memory space (CLONE_VM) and the execution of calling thread will be
     suspend until the child calls execve or _exit.  These condition as
     signal below either by pipe write (_exit with SPAWN_ERROR) or
     a successful execve.
     Also since the calling thread execution will be suspend, there is not
     need for CLONE_SETTLS.  Although parent and child share the same TLS
     namespace, there will be no concurrent access for TLS variables (errno
     for instance).  */
  new_pid = CLONE (__spawni_child, STACK (stack, stack_size), stack_size,
		   CLONE_VM | CLONE_VFORK | SIGCHLD, &args);

  close_not_cancel (args.pipe[1]);

  if (new_pid > 0)
    {
      if (__read (args.pipe[0], &ec, sizeof ec) != sizeof ec)
	ec = 0;
      else
	__waitpid (new_pid, NULL, 0);
    }
  else
    ec = -new_pid;

  __munmap (stack, stack_size);

  close_not_cancel (args.pipe[0]);

  if (!ec && new_pid)
    *pid = new_pid;

  __sigprocmask (SIG_SETMASK, &args.oldmask, 0);

  LIBC_CANCEL_RESET (cs);

  return ec;
}

/* Spawn a new process executing PATH with the attributes describes in *ATTRP.
   Before running the process perform the actions described in FILE-ACTIONS. */
int
__spawni (pid_t * pid, const char *file,
	  const posix_spawn_file_actions_t * acts,
	  const posix_spawnattr_t * attrp, char *const argv[],
	  char *const envp[], int xflags)
{
  return __spawnix (pid, file, acts, attrp, argv, envp, xflags,
		    xflags & SPAWN_XFLAGS_USE_PATH ? __execvpe : __execve);
}
Exemplo n.º 6
0
/* Wait for a child to die.  When one does, put its status in *STAT_LOC
   and return its process ID.  For errors, return (pid_t) -1.  */
__pid_t
__libc_wait (int *stat_loc)
{
  return __waitpid (WAIT_ANY, (int *) stat_loc, 0);
}
Exemplo n.º 7
0
int
grantpt (int fd)
{
#if defined __OpenBSD__
  /* On OpenBSD, master and slave of a pseudo-terminal are allocated together,
     through an ioctl on /dev/ptm.  There is no need for grantpt().  */
  if (fcntl (fd, F_GETFD) < 0)
    return -1;
  return 0;
#else
  /* This function is most often called from a process without 'root'
     credentials.  Use the helper program.  */
  int retval = -1;
  pid_t pid = __fork ();
  if (pid == -1)
    goto cleanup;
  else if (pid == 0)
    {
      /* This is executed in the child process.  */

# if HAVE_SETRLIMIT && defined RLIMIT_CORE
      /* Disable core dumps.  */
      struct rlimit rl = { 0, 0 };
      __setrlimit (RLIMIT_CORE, &rl);
# endif

      /* We pass the master pseudo terminal as file descriptor PTY_FILENO.  */
      if (fd != PTY_FILENO)
        if (__dup2 (fd, PTY_FILENO) < 0)
          _exit (FAIL_EBADF);

# ifdef CLOSE_ALL_FDS
      CLOSE_ALL_FDS ();
# endif

      execle (_PATH_PT_CHOWN, strrchr (_PATH_PT_CHOWN, '/') + 1, NULL, NULL);
      _exit (FAIL_EXEC);
    }
  else
    {
      int w;

      if (__waitpid (pid, &w, 0) == -1)
        goto cleanup;
      if (!WIFEXITED (w))
        __set_errno (ENOEXEC);
      else
        switch (WEXITSTATUS (w))
          {
          case 0:
            retval = 0;
            break;
          case FAIL_EBADF:
            __set_errno (EBADF);
            break;
          case FAIL_EINVAL:
            __set_errno (EINVAL);
            break;
          case FAIL_EACCES:
            __set_errno (EACCES);
            break;
          case FAIL_EXEC:
            __set_errno (ENOEXEC);
            break;
          case FAIL_ENOMEM:
            __set_errno (ENOMEM);
            break;

          default:
            assert(! "getpt: internal error: invalid exit code from pt_chown");
          }
    }

 cleanup:
  return retval;
#endif
}