xref: /illumos-gate/usr/src/lib/libc/port/gen/pt.c (revision 88f8b78a88cbdc6d8c1af5c3e54bc49d25095c98)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*	Copyright (c) 1988 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 
33 #pragma weak ptsname = _ptsname
34 #pragma weak grantpt = _grantpt
35 #pragma weak unlockpt = _unlockpt
36 #pragma weak posix_openpt = _posix_openpt
37 
38 #include "synonyms.h"
39 #include <mtlib.h>
40 #include <sys/types.h>
41 #include <signal.h>
42 #include <sys/param.h>
43 #include <sys/mkdev.h>
44 #include <sys/fs/ufs_fsdir.h>
45 #include <sys/stream.h>
46 #include <sys/stropts.h>
47 #include <sys/wait.h>
48 #include <sys/signal.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <sys/stat.h>
52 #include <sys/ptms.h>
53 #include <string.h>
54 #include <stdlib.h>
55 #include <unistd.h>
56 #include <wait.h>
57 #include <synch.h>
58 #include <thread.h>
59 #include <spawn.h>
60 #include <libc.h>
61 #include "tsd.h"
62 
63 #define	PTSNAME "/dev/pts/"		/* slave name */
64 #define	PTLEN   32			/* slave name length */
65 #define	PTPATH  "/usr/lib/pt_chmod"    	/* setuid root program */
66 #define	PTPGM   "pt_chmod"		/* setuid root program */
67 
68 static void itoa(int, char *);
69 static int grantpt_u(int, int);
70 
71 /*
72  *  Check that fd argument is a file descriptor of an opened master.
73  *  Do this by sending an ISPTM ioctl message down stream. Ioctl()
74  *  will fail if:(1) fd is not a valid file descriptor.(2) the file
75  *  represented by fd does not understand ISPTM(not a master device).
76  *  If we have a valid master, get its minor number via fstat().
77  *  Concatenate it to PTSNAME and return it as the name of the slave
78  *  device.
79  */
80 static dev_t
81 ptsdev(int fd)
82 {
83 	struct stat64 status;
84 	struct strioctl istr;
85 
86 	istr.ic_cmd = ISPTM;
87 	istr.ic_len = 0;
88 	istr.ic_timout = 0;
89 	istr.ic_dp = NULL;
90 
91 	if (ioctl(fd, I_STR, &istr) < 0 || fstat64(fd, &status) < 0)
92 		return (NODEV);
93 
94 	return (minor(status.st_rdev));
95 }
96 
97 static int
98 ptscreate(void)
99 {
100 	static mutex_t clk = DEFAULTMUTEX;
101 	int ret;
102 
103 	lmutex_lock(&clk);
104 	ret = grantpt_u(-1, 1);
105 	lmutex_unlock(&clk);
106 	return (ret);
107 }
108 
109 char *
110 ptsname(int fd)
111 {
112 	dev_t dev;
113 	char *sname;
114 
115 	if ((dev = ptsdev(fd)) == NODEV)
116 		return (NULL);
117 
118 	sname = tsdalloc(_T_PTSNAME, PTLEN, NULL);
119 	if (sname == NULL)
120 		return (NULL);
121 	(void) strcpy(sname, PTSNAME);
122 	itoa(dev, sname + strlen(PTSNAME));
123 
124 	/*
125 	 * devfsadm synchronization: if the node does not exist,
126 	 * attempt to synchronize with slave device node creation.
127 	 */
128 	if (access(sname, F_OK) ==  0 ||
129 	    (ptscreate() == 0 && access(sname, F_OK) == 0))
130 		return (sname);
131 	return (NULL);
132 }
133 
134 /*
135  * Send an ioctl down to the master device requesting the
136  * master/slave pair be unlocked.
137  */
138 int
139 unlockpt(int fd)
140 {
141 	struct strioctl istr;
142 
143 	istr.ic_cmd = UNLKPT;
144 	istr.ic_len = 0;
145 	istr.ic_timout = 0;
146 	istr.ic_dp = NULL;
147 
148 	if (ioctl(fd, I_STR, &istr) < 0)
149 		return (-1);
150 
151 	return (0);
152 }
153 
154 
155 /*
156  * Execute a setuid root program to change the mode, ownership and
157  * group of the slave device. The parent forks a child process that
158  * executes the setuid program. It then waits for the child to return.
159  *
160  * When create is 1, execute the setuid root program without arguments,
161  * to create minor nodes and symlinks for all slave devices.
162  */
163 static int
164 grantpt_u(int fd, int create)
165 {
166 	extern char **environ;
167 	char *argvec[3];
168 	int	st_loc;
169 	pid_t	pid;
170 	int	w;
171 	char	fds[24];
172 	sigset_t oset, nset;
173 	int	error;
174 
175 	/* validate the file descriptor before proceeding */
176 	if (create != 1 && ptsdev(fd) == NODEV)
177 		return (-1);
178 
179 	if (sigemptyset(&nset) == -1)
180 		return (-1);
181 	if (sigaddset(&nset, SIGCHLD) == -1)
182 		return (-1);
183 	if (sigprocmask(SIG_BLOCK, &nset, &oset) == -1)
184 		return (-1);
185 
186 	itoa(fd, fds);
187 	argvec[0] = PTPGM;
188 	argvec[1] = create == 1 ? NULL : fds;
189 	argvec[2] = NULL;
190 	error = posix_spawn(&pid, PTPATH, NULL, NULL, argvec, environ);
191 	if (error) {
192 		(void) sigprocmask(SIG_SETMASK, &oset, NULL);
193 		errno = error;
194 		return (-1);
195 	}
196 
197 	/*
198 	 * waitpid() returns the process id for the child process
199 	 * on success or -1 on failure.
200 	 */
201 	while ((w = waitpid(pid, &st_loc, 0)) < 0 && errno == EINTR)
202 		continue;
203 
204 	/* Restore signal mask */
205 	(void) sigprocmask(SIG_SETMASK, &oset, NULL);
206 
207 	/*
208 	 * If SIGCHLD is currently ignored, waitpid() fails with
209 	 * ECHILD after the child terminates.
210 	 * This is not a failure; assume the child succeded.
211 	 */
212 	if (w == -1) {
213 		if (errno != ECHILD)
214 			return (-1);
215 		st_loc = 0;
216 	}
217 
218 	/*
219 	 * If child terminated due to exit() and the exit status is zero
220 	 *	return success
221 	 * else it was an exit(-1) or it was struck by a signal
222 	 *	return failure (EACCES)
223 	 */
224 	if (WIFEXITED(st_loc) && WEXITSTATUS(st_loc) == 0)
225 		return (0);
226 	errno = EACCES;
227 	return (-1);
228 }
229 
230 int
231 grantpt(int fd)
232 {
233 	static mutex_t glk = DEFAULTMUTEX;
234 	int ret;
235 
236 	lmutex_lock(&glk);
237 	ret = grantpt_u(fd, 0);
238 	lmutex_unlock(&glk);
239 	return (ret);
240 }
241 
242 /*
243  * Send an ioctl down to the master device requesting the master/slave pair
244  * be assigned to the given zone.
245  */
246 int
247 zonept(int fd, zoneid_t zoneid)
248 {
249 	struct strioctl istr;
250 
251 	istr.ic_cmd = ZONEPT;
252 	istr.ic_len = sizeof (zoneid);
253 	istr.ic_timout = 0;
254 	istr.ic_dp = (char *)&zoneid;
255 
256 	if (ioctl(fd, I_STR, &istr) != 0) {
257 		return (-1);
258 	}
259 	return (0);
260 }
261 
262 
263 static void
264 itoa(int i, char *ptr)
265 {
266 	int dig = 0;
267 	int tempi;
268 
269 	tempi = i;
270 	do {
271 		dig++;
272 		tempi /= 10;
273 	} while (tempi);
274 
275 	ptr += dig;
276 	*ptr = '\0';
277 	while (--dig >= 0) {
278 		*(--ptr) = i % 10 + '0';
279 		i /= 10;
280 	}
281 }
282 
283 
284 /*
285  * added for SUSv3 standard
286  *
287  * Open a pseudo-terminal device.  External interface.
288  */
289 
290 int
291 posix_openpt(int oflag)
292 {
293 	return (open("/dev/ptmx", oflag));
294 }
295