]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/stdlib/grantpt.c
Refactor features a bit in order to make it possible to disable lazy
[FreeBSD/FreeBSD.git] / lib / libc / stdlib / grantpt.c
1 /*
2  * Copyright (c) 2002 The FreeBSD Project, Inc.
3  * All rights reserved.
4  *
5  * This software includes code contributed to the FreeBSD Project
6  * by Ryan Younce of North Carolina State University.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the FreeBSD Project nor the names of its
17  *    contributors may be used to endorse or promote products derived from
18  *    this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT AND CONTRIBUTORS ``AS IS''
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE FREEBSD PROJECT OR ITS CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <sys/cdefs.h>
34 #ifndef lint
35 __FBSDID("$FreeBSD$");
36 #endif /* not lint */
37
38 #include "namespace.h"
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/wait.h>
42 #include <sys/time.h>
43 #include <sys/resource.h>
44 #include <sys/sysctl.h>
45 #include <sys/ioctl.h>
46
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <grp.h>
50 #include <paths.h>
51 #include <signal.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <sysexits.h>
56 #include <unistd.h>
57 #include "un-namespace.h"
58
59 #define PTM_PREFIX      "pty"   /* pseudo tty master naming convention */
60 #define PTS_PREFIX      "tty"   /* pseudo tty slave naming convention */
61 #define NEWPTS_PREFIX   "pts"
62 #define PTMX            "ptmx"
63
64 /*
65  * The following are range values for pseudo TTY devices.  Pseudo TTYs have a
66  * name of /dev/[pt]ty[l-sL-S][0-9a-v], yielding 256 combinations per major.
67  */
68 #define PT_MAX          256
69 #define PT_DEV1         "pqrsPQRSlmnoLMNO"
70 #define PT_DEV2         "0123456789abcdefghijklmnopqrstuv"
71
72 /*
73  * grantpt(3) support utility.
74  */
75 #define _PATH_PTCHOWN   "/usr/libexec/pt_chown"
76
77 /*
78  * ISPTM(x) returns 0 for struct stat x if x is not a pty master.
79  * The bounds checking may be unnecessary but it does eliminate doubt.
80  */
81 #define ISPTM(x)        (S_ISCHR((x).st_mode) &&                        \
82                          minor((x).st_rdev) >= 0 &&                     \
83                          minor((x).st_rdev) < PT_MAX)
84
85
86 static int
87 is_pts(int fd)
88 {
89         int nb;
90
91         return (_ioctl(fd, TIOCGPTN, &nb) == 0);
92 }
93
94 int
95 __use_pts(void)
96 {
97         int use_pts;
98         size_t len;
99         int error;
100
101         len = sizeof(use_pts);
102         error = sysctlbyname("kern.pts.enable", &use_pts, &len, NULL, 0);
103         if (error) {
104                 struct stat sb;
105
106                 if (stat("/dev/ptmx", &sb) != 0)
107                         return (0);
108                 use_pts = 1;
109         }
110         return (use_pts);
111 }
112
113 /*
114  * grantpt():  grant ownership of a slave pseudo-terminal device to the
115  *             current user.
116  */
117
118 int
119 grantpt(int fildes)
120 {
121         int retval, serrno, status;
122         pid_t pid, spid;
123         gid_t gid;
124         char *slave;
125         sigset_t oblock, nblock;
126         struct group *grp;
127
128         retval = -1;
129         serrno = errno;
130
131         if ((slave = ptsname(fildes)) != NULL) {
132                 /*
133                  * Block SIGCHLD.
134                  */
135                 (void)sigemptyset(&nblock);
136                 (void)sigaddset(&nblock, SIGCHLD);
137                 (void)_sigprocmask(SIG_BLOCK, &nblock, &oblock);
138
139                 switch (pid = fork()) {
140                 case -1:
141                         break;
142                 case 0:         /* child */
143                         /*
144                          * pt_chown expects the master pseudo TTY to be its
145                          * standard input.
146                          */
147                         (void)_dup2(fildes, STDIN_FILENO);
148                         (void)_sigprocmask(SIG_SETMASK, &oblock, NULL);
149                         execl(_PATH_PTCHOWN, _PATH_PTCHOWN, (char *)NULL);
150                         _exit(EX_UNAVAILABLE);
151                         /* NOTREACHED */
152                 default:        /* parent */
153                         /*
154                          * Just wait for the process.  Error checking is
155                          * done below.
156                          */
157                         while ((spid = _waitpid(pid, &status, 0)) == -1 &&
158                                (errno == EINTR))
159                                 ;
160                         if (spid != -1 && WIFEXITED(status) &&
161                             WEXITSTATUS(status) == EX_OK)
162                                 retval = 0;
163                         else
164                                 errno = EACCES;
165                         break;
166                 }
167
168                 /*
169                  * Restore process's signal mask.
170                  */
171                 (void)_sigprocmask(SIG_SETMASK, &oblock, NULL);
172
173                 if (retval) {
174                         /*
175                          * pt_chown failed.  Try to manually change the
176                          * permissions for the slave.
177                          */
178                         gid = (grp = getgrnam("tty")) ? grp->gr_gid : -1;
179                         if (chown(slave, getuid(), gid) == -1 ||
180                             chmod(slave, S_IRUSR | S_IWUSR | S_IWGRP) == -1)
181                                 errno = EACCES;
182                         else
183                                 retval = 0;
184                 }
185         }
186
187         if (!retval)
188                 errno = serrno;
189
190         return (retval);
191 }
192
193 /*
194  * posix_openpt():  open the first available master pseudo-terminal device
195  *                  and return descriptor.
196  */
197 int
198 posix_openpt(int oflag)
199 {
200         char *mc1, *mc2, master[] = _PATH_DEV PTM_PREFIX "XY";
201         const char *pc1, *pc2;
202         int fildes, bflag, serrno;
203
204         fildes = -1;
205         bflag = 0;
206         serrno = errno;
207
208         /*
209          * Check flag validity.  POSIX doesn't require it,
210          * but we still do so.
211          */
212         if (oflag & ~(O_RDWR | O_NOCTTY))
213                 errno = EINVAL;
214         else {
215                 if (__use_pts()) {
216                         fildes = _open(_PATH_DEV PTMX, oflag);
217                         return (fildes);
218                 }
219                 mc1 = master + strlen(_PATH_DEV PTM_PREFIX);
220                 mc2 = mc1 + 1;
221
222                 /* Cycle through all possible master PTY devices. */
223                 for (pc1 = PT_DEV1; !bflag && (*mc1 = *pc1); ++pc1)
224                         for (pc2 = PT_DEV2; (*mc2 = *pc2) != '\0'; ++pc2) {
225                                 /*
226                                  * Break out if we successfully open a PTY,
227                                  * or if open() fails due to limits.
228                                  */
229                                 if ((fildes = _open(master, oflag)) != -1 ||
230                                     (errno == EMFILE || errno == ENFILE)) {
231                                         ++bflag;
232                                         break;
233                                 }
234                         }
235
236                 if (fildes != -1)
237                         errno = serrno;
238                 else if (!bflag)
239                         errno = EAGAIN;
240         }
241
242         return (fildes);
243 }
244
245 /*
246  * ptsname():  return the pathname of the slave pseudo-terminal device
247  *             associated with the specified master.
248  */
249 char *
250 ptsname(int fildes)
251 {
252         static char slave[] = _PATH_DEV PTS_PREFIX "XY";
253         static char new_slave[] = _PATH_DEV NEWPTS_PREFIX "4294967295";
254         char *retval;
255         struct stat sbuf;
256
257         retval = NULL;
258
259         if (_fstat(fildes, &sbuf) == 0) {
260                 if (!ISPTM(sbuf))
261                         errno = EINVAL;
262                 else {
263                         if (!is_pts(fildes)) {
264                                 (void)snprintf(slave, sizeof(slave),
265                                                _PATH_DEV PTS_PREFIX "%s",
266                                                devname(sbuf.st_rdev, S_IFCHR) +
267                                                strlen(PTM_PREFIX));
268                                 retval = slave;
269                         } else {
270                                 (void)snprintf(new_slave, sizeof(new_slave),
271                                                _PATH_DEV NEWPTS_PREFIX "%s",
272                                                devname(sbuf.st_rdev, S_IFCHR) +
273                                                strlen(PTM_PREFIX));
274                                 retval = new_slave;
275                         }
276                 }
277         }
278
279         return (retval);
280 }
281
282 /*
283  * unlockpt():  unlock a pseudo-terminal device pair.
284  */
285 int
286 unlockpt(int fildes)
287 {
288         int retval;
289         struct stat sbuf;
290
291         /*
292          * Unlocking a master/slave pseudo-terminal pair has no meaning in a
293          * non-streams PTY environment.  However, we do ensure fildes is a
294          * valid master pseudo-terminal device.
295          */
296         if ((retval = _fstat(fildes, &sbuf)) == 0 && !ISPTM(sbuf)) {
297                 errno = EINVAL;
298                 retval = -1;
299         }
300
301         return (retval);
302 }