ce80ad49ffc5faccd9c1ef95451b2d73f1e96f58
[gnulib.git] / lib / grantpt.c
1 /* Acquire ownership of the slave side of a pseudo-terminal.
2    Copyright (C) 1998-2002, 2009-2012 Free Software Foundation, Inc.
3    Contributed by Zack Weinberg <zack@rabi.phys.columbia.edu>, 1998.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program 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
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19
20 #include <stdlib.h>
21
22 #include <assert.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <sys/wait.h>
27 #include <unistd.h>
28
29 #if HAVE_SETRLIMIT
30 # include <sys/types.h>
31 # include <sys/time.h>
32 # include <sys/resource.h>
33 #endif
34
35 #include "configmake.h"
36 #include "pty-private.h"
37
38 #ifndef _LIBC
39 # define __builtin_expect(expr,val) (expr)
40 # define __set_errno(e) errno = (e)
41 # define __dup2 dup2
42 # define __fork fork
43 # define __setrlimit setrlimit
44 # define __waitpid waitpid
45 #endif
46
47 int
48 grantpt (int fd)
49 {
50 #if defined __OpenBSD__
51   /* On OpenBSD, master and slave of a pseudo-terminal are allocated together,
52      through an ioctl on /dev/ptm.  There is no need for grantpt().  */
53   if (fcntl (fd, F_GETFD) < 0)
54     return -1;
55   return 0;
56 #else
57   /* This function is most often called from a process without 'root'
58      credentials.  Use the helper program.  */
59   int retval = -1;
60   pid_t pid = __fork ();
61   if (pid == -1)
62     goto cleanup;
63   else if (pid == 0)
64     {
65       /* This is executed in the child process.  */
66
67 # if HAVE_SETRLIMIT && defined RLIMIT_CORE
68       /* Disable core dumps.  */
69       struct rlimit rl = { 0, 0 };
70       __setrlimit (RLIMIT_CORE, &rl);
71 # endif
72
73       /* We pass the master pseudo terminal as file descriptor PTY_FILENO.  */
74       if (fd != PTY_FILENO)
75         if (__dup2 (fd, PTY_FILENO) < 0)
76           _exit (FAIL_EBADF);
77
78 # ifdef CLOSE_ALL_FDS
79       CLOSE_ALL_FDS ();
80 # endif
81
82       execle (_PATH_PT_CHOWN, strrchr (_PATH_PT_CHOWN, '/') + 1, NULL, NULL);
83       _exit (FAIL_EXEC);
84     }
85   else
86     {
87       int w;
88
89       if (__waitpid (pid, &w, 0) == -1)
90         goto cleanup;
91       if (!WIFEXITED (w))
92         __set_errno (ENOEXEC);
93       else
94         switch (WEXITSTATUS (w))
95           {
96           case 0:
97             retval = 0;
98             break;
99           case FAIL_EBADF:
100             __set_errno (EBADF);
101             break;
102           case FAIL_EINVAL:
103             __set_errno (EINVAL);
104             break;
105           case FAIL_EACCES:
106             __set_errno (EACCES);
107             break;
108           case FAIL_EXEC:
109             __set_errno (ENOEXEC);
110             break;
111           case FAIL_ENOMEM:
112             __set_errno (ENOMEM);
113             break;
114
115           default:
116             assert(! "getpt: internal error: invalid exit code from pt_chown");
117           }
118     }
119
120  cleanup:
121   return retval;
122 #endif
123 }