]>
Commit | Line | Data |
---|---|---|
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: src/lib/libc/stdlib/grantpt.c,v 1.2 2003/01/04 08:10:55 tjr Exp $"); | |
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 | ||
45 | #include <errno.h> | |
46 | #include <fcntl.h> | |
47 | #include <grp.h> | |
48 | #include <paths.h> | |
49 | #include <signal.h> | |
50 | #include <stdio.h> | |
51 | #include <stdlib.h> | |
52 | #include <string.h> | |
53 | #include <sysexits.h> | |
54 | #include <unistd.h> | |
55 | #include "un-namespace.h" | |
56 | ||
57 | #define PTM_MAJOR 6 /* pseudo tty master major */ | |
58 | #define PTS_MAJOR 5 /* pseudo tty slave major */ | |
59 | #define PTM_PREFIX "pty" /* pseudo tty master naming convention */ | |
60 | #define PTS_PREFIX "tty" /* pseudo tty slave naming convention */ | |
61 | ||
62 | /* | |
63 | * The following are range values for pseudo TTY devices. Pseudo TTYs have a | |
64 | * name of /dev/[pt]ty[p-sP-S][0-9a-v], yielding 256 combinations per major. | |
65 | */ | |
66 | #define PT_MAX 256 | |
67 | #define PT_DEV1 "pqrsPQRS" | |
68 | #define PT_DEV2 "0123456789abcdefghijklmnopqrstuv" | |
69 | ||
70 | /* | |
71 | * grantpt(3) support utility. | |
72 | */ | |
73 | #define _PATH_PTCHOWN "/usr/libexec/pt_chown" | |
74 | ||
75 | /* | |
76 | * ISPTM(x) returns 0 for struct stat x if x is not a pty master. | |
77 | * The bounds checking may be unnecessary but it does eliminate doubt. | |
78 | */ | |
79 | #define ISPTM(x) (S_ISCHR((x).st_mode) && \ | |
80 | major((x).st_rdev) == PTM_MAJOR && \ | |
81 | minor((x).st_rdev) >= 0 && \ | |
82 | minor((x).st_rdev) < PT_MAX) | |
83 | ||
84 | /* | |
85 | * grantpt(): grant ownership of a slave pseudo-terminal device to the | |
86 | * current user. | |
87 | */ | |
88 | ||
89 | int | |
90 | grantpt(int fildes) | |
91 | { | |
92 | int retval, serrno, status; | |
93 | pid_t pid, spid; | |
94 | gid_t gid; | |
95 | char *slave; | |
96 | sigset_t oblock, nblock; | |
97 | struct group *grp; | |
98 | ||
99 | retval = -1; | |
100 | serrno = errno; | |
101 | ||
102 | if ((slave = ptsname(fildes)) != NULL) { | |
103 | /* | |
104 | * Block SIGCHLD. | |
105 | */ | |
106 | (void)sigemptyset(&nblock); | |
107 | (void)sigaddset(&nblock, SIGCHLD); | |
108 | (void)_sigprocmask(SIG_BLOCK, &nblock, &oblock); | |
109 | ||
110 | switch (pid = fork()) { | |
111 | case -1: | |
112 | break; | |
113 | case 0: /* child */ | |
114 | /* | |
115 | * pt_chown expects the master pseudo TTY to be its | |
116 | * standard input. | |
117 | */ | |
118 | (void)_dup2(fildes, STDIN_FILENO); | |
119 | (void)_sigprocmask(SIG_SETMASK, &oblock, NULL); | |
120 | execl(_PATH_PTCHOWN, _PATH_PTCHOWN, (char *)NULL); | |
121 | _exit(EX_UNAVAILABLE); | |
122 | /* NOTREACHED */ | |
123 | default: /* parent */ | |
124 | /* | |
125 | * Just wait for the process. Error checking is | |
126 | * done below. | |
127 | */ | |
128 | while ((spid = _waitpid(pid, &status, 0)) == -1 && | |
129 | (errno == EINTR)) | |
130 | ; | |
131 | if (spid != -1 && WIFEXITED(status) && | |
132 | WEXITSTATUS(status) == EX_OK) | |
133 | retval = 0; | |
134 | else | |
135 | errno = EACCES; | |
136 | break; | |
137 | } | |
138 | ||
139 | /* | |
140 | * Restore process's signal mask. | |
141 | */ | |
142 | (void)_sigprocmask(SIG_SETMASK, &oblock, NULL); | |
143 | ||
144 | if (retval) { | |
145 | /* | |
146 | * pt_chown failed. Try to manually change the | |
147 | * permissions for the slave. | |
148 | */ | |
149 | gid = (grp = getgrnam("tty")) ? grp->gr_gid : -1; | |
150 | if (chown(slave, getuid(), gid) == -1 || | |
151 | chmod(slave, S_IRUSR | S_IWUSR | S_IWGRP) == -1) | |
152 | errno = EACCES; | |
153 | else | |
154 | retval = 0; | |
155 | } | |
156 | } | |
157 | ||
158 | if (!retval) | |
159 | errno = serrno; | |
160 | ||
161 | return (retval); | |
162 | } | |
163 | ||
164 | /* | |
165 | * posix_openpt(): open the first available master pseudo-terminal device | |
166 | * and return descriptor. | |
167 | */ | |
168 | int | |
169 | posix_openpt(int oflag) | |
170 | { | |
171 | char *mc1, *mc2, master[] = _PATH_DEV PTM_PREFIX "XY"; | |
172 | const char *pc1, *pc2; | |
173 | int fildes, bflag, serrno; | |
174 | ||
175 | fildes = -1; | |
176 | bflag = 0; | |
177 | serrno = errno; | |
178 | ||
179 | /* | |
180 | * Check flag validity. POSIX doesn't require it, | |
181 | * but we still do so. | |
182 | */ | |
183 | if (oflag & ~(O_RDWR | O_NOCTTY)) | |
184 | errno = EINVAL; | |
185 | else { | |
186 | mc1 = master + strlen(_PATH_DEV PTM_PREFIX); | |
187 | mc2 = mc1 + 1; | |
188 | ||
189 | /* Cycle through all possible master PTY devices. */ | |
190 | for (pc1 = PT_DEV1; !bflag && (*mc1 = *pc1); ++pc1) | |
191 | for (pc2 = PT_DEV2; (*mc2 = *pc2) != '\0'; ++pc2) { | |
192 | /* | |
193 | * Break out if we successfully open a PTY, | |
194 | * or if open() fails due to limits. | |
195 | */ | |
196 | if ((fildes = _open(master, oflag)) != -1 || | |
197 | (errno == EMFILE || errno == ENFILE)) { | |
198 | ++bflag; | |
199 | break; | |
200 | } | |
201 | } | |
202 | ||
203 | if (fildes != -1) | |
204 | errno = serrno; | |
205 | else if (!bflag) | |
206 | errno = EAGAIN; | |
207 | } | |
208 | ||
209 | return (fildes); | |
210 | } | |
211 | ||
212 | /* | |
213 | * ptsname(): return the pathname of the slave pseudo-terminal device | |
214 | * associated with the specified master. | |
215 | */ | |
216 | char * | |
217 | ptsname(int fildes) | |
218 | { | |
219 | static char slave[] = _PATH_DEV PTS_PREFIX "XY"; | |
220 | char *retval; | |
221 | struct stat sbuf; | |
222 | ||
223 | retval = NULL; | |
224 | ||
225 | if (_fstat(fildes, &sbuf) == 0) { | |
226 | if (!ISPTM(sbuf)) | |
227 | errno = EINVAL; | |
228 | else { | |
229 | (void)sprintf(slave, _PATH_DEV PTS_PREFIX "%c%c", | |
230 | PT_DEV1[minor(sbuf.st_rdev) / 32], | |
231 | PT_DEV2[minor(sbuf.st_rdev) % 32]); | |
232 | retval = slave; | |
233 | } | |
234 | } | |
235 | ||
236 | return (retval); | |
237 | } | |
238 | ||
239 | /* | |
240 | * unlockpt(): unlock a pseudo-terminal device pair. | |
241 | */ | |
242 | int | |
243 | unlockpt(int fildes) | |
244 | { | |
245 | int retval; | |
246 | struct stat sbuf; | |
247 | ||
248 | /* | |
249 | * Unlocking a master/slave pseudo-terminal pair has no meaning in a | |
250 | * non-streams PTY environment. However, we do ensure fildes is a | |
251 | * valid master pseudo-terminal device. | |
252 | */ | |
253 | if ((retval = _fstat(fildes, &sbuf)) == 0 && !ISPTM(sbuf)) { | |
254 | errno = EINVAL; | |
255 | retval = -1; | |
256 | } | |
257 | ||
258 | return (retval); | |
259 | } |