]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - lib/libc/stdlib/grantpt.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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 PTYM_PREFIX     "pty"   /* pty(4) master naming convention */
60 #define PTYS_PREFIX     "tty"   /* pty(4) slave naming convention */
61 #define PTMXS_PREFIX    "pts/"  /* pts(4) slave naming convention */
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].
67  */
68 #define PTY_DEV1        "pqrsPQRSlmnoLMNO"
69 #define PTY_DEV2        "0123456789abcdefghijklmnopqrstuv"
70
71 /*
72  * grantpt(3) support utility.
73  */
74 #define _PATH_PTCHOWN   "/usr/libexec/pt_chown"
75
76 #if 0
77 int
78 __use_pts(void)
79 {
80         int use_pts;
81         size_t len;
82         int error;
83
84         len = sizeof(use_pts);
85         error = sysctlbyname("kern.pts.enable", &use_pts, &len, NULL, 0);
86         if (error) {
87                 struct stat sb;
88
89                 if (stat(_PATH_DEV PTMX, &sb) != 0)
90                         return (0);
91                 use_pts = 1;
92         }
93         return (use_pts);
94 }
95 #endif
96
97 /*
98  * grantpt():  grant ownership of a slave pseudo-terminal device to the
99  *             current user.
100  */
101
102 int
103 grantpt(int fildes)
104 {
105         int retval, serrno, status;
106         pid_t pid, spid;
107         gid_t gid;
108         char *slave;
109         sigset_t oblock, nblock;
110         struct group *grp;
111
112         retval = -1;
113         serrno = errno;
114
115         if ((slave = ptsname(fildes)) != NULL) {
116                 /*
117                  * Block SIGCHLD.
118                  */
119                 (void)sigemptyset(&nblock);
120                 (void)sigaddset(&nblock, SIGCHLD);
121                 (void)_sigprocmask(SIG_BLOCK, &nblock, &oblock);
122
123                 switch (pid = fork()) {
124                 case -1:
125                         break;
126                 case 0:         /* child */
127                         /*
128                          * pt_chown expects the master pseudo TTY to be its
129                          * standard input.
130                          */
131                         (void)_dup2(fildes, STDIN_FILENO);
132                         (void)_sigprocmask(SIG_SETMASK, &oblock, NULL);
133                         execl(_PATH_PTCHOWN, _PATH_PTCHOWN, (char *)NULL);
134                         _exit(EX_UNAVAILABLE);
135                         /* NOTREACHED */
136                 default:        /* parent */
137                         /*
138                          * Just wait for the process.  Error checking is
139                          * done below.
140                          */
141                         while ((spid = _waitpid(pid, &status, 0)) == -1 &&
142                                (errno == EINTR))
143                                 ;
144                         if (spid != -1 && WIFEXITED(status) &&
145                             WEXITSTATUS(status) == EX_OK)
146                                 retval = 0;
147                         else
148                                 errno = EACCES;
149                         break;
150                 }
151
152                 /*
153                  * Restore process's signal mask.
154                  */
155                 (void)_sigprocmask(SIG_SETMASK, &oblock, NULL);
156
157                 if (retval) {
158                         /*
159                          * pt_chown failed.  Try to manually change the
160                          * permissions for the slave.
161                          */
162                         gid = (grp = getgrnam("tty")) ? grp->gr_gid : -1;
163                         if (chown(slave, getuid(), gid) == -1 ||
164                             chmod(slave, S_IRUSR | S_IWUSR | S_IWGRP) == -1)
165                                 errno = EACCES;
166                         else
167                                 retval = 0;
168                 }
169         }
170
171         if (!retval)
172                 errno = serrno;
173
174         return (retval);
175 }
176
177 /*
178  * posix_openpt():  open the first available master pseudo-terminal device
179  *                  and return descriptor.
180  */
181 int
182 posix_openpt(int oflag)
183 {
184         char *mc1, *mc2, master[] = _PATH_DEV PTYM_PREFIX "XY";
185         const char *pc1, *pc2;
186         int fildes, bflag, serrno;
187
188         fildes = -1;
189         bflag = 0;
190         serrno = errno;
191
192         /*
193          * Check flag validity.  POSIX doesn't require it,
194          * but we still do so.
195          */
196         if (oflag & ~(O_RDWR | O_NOCTTY))
197                 errno = EINVAL;
198         else {
199 #if 0
200                 if (__use_pts()) {
201                         fildes = _open(_PATH_DEV PTMX, oflag);
202                         return (fildes);
203                 }
204 #endif
205                 mc1 = master + strlen(_PATH_DEV PTYM_PREFIX);
206                 mc2 = mc1 + 1;
207
208                 /* Cycle through all possible master PTY devices. */
209                 for (pc1 = PTY_DEV1; !bflag && (*mc1 = *pc1); ++pc1)
210                         for (pc2 = PTY_DEV2; (*mc2 = *pc2) != '\0'; ++pc2) {
211                                 /*
212                                  * Break out if we successfully open a PTY,
213                                  * or if open() fails due to limits.
214                                  */
215                                 if ((fildes = _open(master, oflag)) != -1 ||
216                                     (errno == EMFILE || errno == ENFILE)) {
217                                         ++bflag;
218                                         break;
219                                 }
220                         }
221
222                 if (fildes != -1)
223                         errno = serrno;
224                 else if (!bflag)
225                         errno = EAGAIN;
226         }
227
228         return (fildes);
229 }
230
231 /*
232  * ptsname():  return the pathname of the slave pseudo-terminal device
233  *             associated with the specified master.
234  */
235 char *
236 ptsname(int fildes)
237 {
238         static char pty_slave[] = _PATH_DEV PTYS_PREFIX "XY";
239 #if 0
240         static char ptmx_slave[] = _PATH_DEV PTMXS_PREFIX "4294967295";
241 #endif
242         const char *master;
243         struct stat sbuf;
244 #if 0
245         int ptn;
246
247         /* Handle pts(4) masters first. */
248         if (_ioctl(fildes, TIOCGPTN, &ptn) == 0) {
249                 (void)snprintf(ptmx_slave, sizeof(ptmx_slave),
250                     _PATH_DEV PTMXS_PREFIX "%d", ptn);
251                 return (ptmx_slave);
252         }
253 #endif
254
255         /* All master pty's must be char devices. */
256         if (_fstat(fildes, &sbuf) == -1)
257                 goto invalid;
258         if (!S_ISCHR(sbuf.st_mode))
259                 goto invalid;
260
261         /* Check to see if this device is a pty(4) master. */
262         master = devname(sbuf.st_rdev, S_IFCHR);
263         if (strlen(master) != strlen(PTYM_PREFIX "XY"))
264                 goto invalid;
265         if (strncmp(master, PTYM_PREFIX, strlen(PTYM_PREFIX)) != 0)
266                 goto invalid;
267
268         /* It is, so generate the corresponding pty(4) slave name. */
269         (void)snprintf(pty_slave, sizeof(pty_slave), _PATH_DEV PTYS_PREFIX "%s",
270             master + strlen(PTYM_PREFIX));
271         return (pty_slave);
272
273 invalid:
274         errno = EINVAL;
275         return (NULL);
276 }
277
278 /*
279  * unlockpt():  unlock a pseudo-terminal device pair.
280  */
281 int
282 unlockpt(int fildes)
283 {
284         const char *slave;
285
286         /*
287          * Even though unlocking a PTY has no meaning in a non-streams
288          * PTY environment, make this function call revoke() to ensure
289          * the PTY slave device is not being evesdropped.
290          */
291         if ((slave = ptsname(fildes)) == NULL)
292                 return (-1);
293
294         if (revoke(slave) == -1) {
295                 errno = EINVAL;
296                 return (-1);
297         }
298
299         return (0);
300 }