]>
Commit | Line | Data |
---|---|---|
2d21ac55 | 1 | /* |
fe8ab488 | 2 | * Copyright (c) 1997-2013 Apple Inc. All rights reserved. |
2d21ac55 A |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | /* | |
29 | * Copyright (c) 1982, 1986, 1989, 1993 | |
30 | * The Regents of the University of California. All rights reserved. | |
31 | * | |
32 | * Redistribution and use in source and binary forms, with or without | |
33 | * modification, are permitted provided that the following conditions | |
34 | * are met: | |
35 | * 1. Redistributions of source code must retain the above copyright | |
36 | * notice, this list of conditions and the following disclaimer. | |
37 | * 2. Redistributions in binary form must reproduce the above copyright | |
38 | * notice, this list of conditions and the following disclaimer in the | |
39 | * documentation and/or other materials provided with the distribution. | |
40 | * 3. All advertising materials mentioning features or use of this software | |
41 | * must display the following acknowledgement: | |
42 | * This product includes software developed by the University of | |
43 | * California, Berkeley and its contributors. | |
44 | * 4. Neither the name of the University nor the names of its contributors | |
45 | * may be used to endorse or promote products derived from this software | |
46 | * without specific prior written permission. | |
47 | * | |
48 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
49 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
50 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
51 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
52 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
53 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
54 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
55 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
56 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
57 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
58 | * SUCH DAMAGE. | |
59 | * | |
60 | * @(#)tty_pty.c 8.4 (Berkeley) 2/20/95 | |
61 | */ | |
62 | ||
63 | /* | |
64 | * Pseudo-teletype Driver | |
65 | * (Actually two drivers, requiring two entries in 'cdevsw') | |
66 | */ | |
67 | #include "pty.h" /* XXX */ | |
68 | ||
69 | #include <sys/param.h> | |
70 | #include <sys/systm.h> | |
71 | #include <sys/ioctl.h> | |
72 | #include <sys/proc_internal.h> | |
73 | #include <sys/kauth.h> | |
74 | #include <sys/tty.h> | |
75 | #include <sys/conf.h> | |
76 | #include <sys/file_internal.h> | |
b0d623f7 | 77 | #include <sys/uio_internal.h> |
2d21ac55 A |
78 | #include <sys/kernel.h> |
79 | #include <sys/vnode.h> | |
2d21ac55 A |
80 | #include <sys/user.h> |
81 | #include <sys/signalvar.h> | |
82 | #include <sys/sysctl.h> | |
83 | #include <miscfs/devfs/devfs.h> | |
84 | #include <miscfs/devfs/devfsdefs.h> /* DEVFS_LOCK()/DEVFS_UNLOCK() */ | |
85 | ||
39236c6e A |
86 | #if CONFIG_MACF |
87 | #include <security/mac_framework.h> | |
88 | #endif | |
89 | ||
fe8ab488 | 90 | #include "tty_dev.h" |
2d21ac55 A |
91 | |
92 | /* | |
93 | * Forward declarations | |
94 | */ | |
95 | int ptmx_init(int n_ptys); | |
fe8ab488 A |
96 | static struct ptmx_ioctl *ptmx_get_ioctl(int minor, int open_flag); |
97 | static int ptmx_free_ioctl(int minor, int open_flag); | |
98 | static int ptmx_get_name(int minor, char *buffer, size_t size); | |
99 | static void ptsd_revoke_knotes(int minor, struct tty *tp); | |
100 | ||
101 | extern d_open_t ptsopen; | |
102 | extern d_close_t ptsclose; | |
103 | extern d_read_t ptsread; | |
104 | extern d_write_t ptswrite; | |
105 | extern d_ioctl_t ptyioctl; | |
106 | extern d_stop_t ptsstop; | |
107 | extern d_reset_t ptsreset; | |
108 | extern d_select_t ptsselect; | |
109 | ||
110 | extern d_open_t ptcopen; | |
111 | extern d_close_t ptcclose; | |
112 | extern d_read_t ptcread; | |
113 | extern d_write_t ptcwrite; | |
114 | extern d_stop_t ptcstop; | |
115 | extern d_reset_t ptcreset; | |
116 | extern d_select_t ptcselect; | |
2d21ac55 A |
117 | |
118 | static int ptmx_major; /* dynamically assigned major number */ | |
119 | static struct cdevsw ptmx_cdev = { | |
fe8ab488 A |
120 | ptcopen, ptcclose, ptcread, ptcwrite, |
121 | ptyioctl, ptcstop, ptcreset, 0, | |
122 | ptcselect, eno_mmap, eno_strat, eno_getc, | |
2d21ac55 A |
123 | eno_putc, D_TTY |
124 | }; | |
125 | ||
126 | static int ptsd_major; /* dynamically assigned major number */ | |
127 | static struct cdevsw ptsd_cdev = { | |
fe8ab488 A |
128 | ptsopen, ptsclose, ptsread, ptswrite, |
129 | ptyioctl, ptsstop, ptsreset, 0, | |
130 | ptsselect, eno_mmap, eno_strat, eno_getc, | |
2d21ac55 A |
131 | eno_putc, D_TTY |
132 | }; | |
133 | ||
2d21ac55 A |
134 | /* |
135 | * ptmx == /dev/ptmx | |
136 | * ptsd == /dev/pts[0123456789]{3} | |
137 | */ | |
138 | #define PTMX_TEMPLATE "ptmx" | |
139 | #define PTSD_TEMPLATE "ttys%03d" | |
140 | ||
141 | /* | |
142 | * System-wide limit on the max number of cloned ptys | |
143 | */ | |
144 | #define PTMX_MAX_DEFAULT 127 /* 128 entries */ | |
145 | #define PTMX_MAX_HARD 999 /* 1000 entries, due to PTSD_TEMPLATE */ | |
146 | ||
147 | static int ptmx_max = PTMX_MAX_DEFAULT; /* default # of clones we allow */ | |
148 | ||
149 | /* Range enforcement for the sysctl */ | |
150 | static int | |
151 | sysctl_ptmx_max(__unused struct sysctl_oid *oidp, __unused void *arg1, | |
152 | __unused int arg2, struct sysctl_req *req) | |
153 | { | |
154 | int new_value, changed; | |
155 | int error = sysctl_io_number(req, ptmx_max, sizeof(int), &new_value, &changed); | |
156 | if (changed) { | |
157 | if (new_value > 0 && new_value <= PTMX_MAX_HARD) | |
158 | ptmx_max = new_value; | |
159 | else | |
160 | error = EINVAL; | |
161 | } | |
162 | return(error); | |
163 | } | |
164 | ||
165 | SYSCTL_NODE(_kern, KERN_TTY, tty, CTLFLAG_RW|CTLFLAG_LOCKED, 0, "TTY"); | |
166 | SYSCTL_PROC(_kern_tty, OID_AUTO, ptmx_max, | |
6d2010ae | 167 | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, |
2d21ac55 A |
168 | &ptmx_max, 0, &sysctl_ptmx_max, "I", "ptmx_max"); |
169 | ||
2d21ac55 A |
170 | static int ptmx_clone(dev_t dev, int minor); |
171 | ||
6d2010ae A |
172 | /* |
173 | * Set of locks to keep the interaction between kevents and revoke | |
174 | * from causing havoc. | |
175 | */ | |
176 | ||
177 | #define LOG2_PTSD_KE_NLCK 2 | |
178 | #define PTSD_KE_NLCK (1l << LOG2_PTSD_KE_NLCK) | |
179 | #define PTSD_KE_LOCK_INDEX(x) ((x) & (PTSD_KE_NLCK - 1)) | |
180 | ||
181 | static lck_mtx_t ptsd_kevent_lock[PTSD_KE_NLCK]; | |
182 | ||
183 | static void | |
184 | ptsd_kevent_lock_init(void) | |
185 | { | |
186 | int i; | |
187 | lck_grp_t *lgrp = lck_grp_alloc_init("ptsd kevent", LCK_GRP_ATTR_NULL); | |
188 | ||
189 | for (i = 0; i < PTSD_KE_NLCK; i++) | |
190 | lck_mtx_init(&ptsd_kevent_lock[i], lgrp, LCK_ATTR_NULL); | |
191 | } | |
192 | ||
193 | static void | |
194 | ptsd_kevent_mtx_lock(int minor) | |
195 | { | |
196 | lck_mtx_lock(&ptsd_kevent_lock[PTSD_KE_LOCK_INDEX(minor)]); | |
197 | } | |
198 | ||
199 | static void | |
200 | ptsd_kevent_mtx_unlock(int minor) | |
201 | { | |
202 | lck_mtx_unlock(&ptsd_kevent_lock[PTSD_KE_LOCK_INDEX(minor)]); | |
203 | } | |
204 | ||
fe8ab488 A |
205 | static struct tty_dev_t _ptmx_driver; |
206 | ||
2d21ac55 A |
207 | int |
208 | ptmx_init( __unused int config_count) | |
209 | { | |
210 | /* | |
211 | * We start looking at slot 10, since there are inits that will | |
212 | * stomp explicit slots (e.g. vndevice stomps 1) below that. | |
213 | */ | |
214 | ||
215 | /* Get a major number for /dev/ptmx */ | |
216 | if((ptmx_major = cdevsw_add(-15, &ptmx_cdev)) == -1) { | |
217 | printf("ptmx_init: failed to obtain /dev/ptmx major number\n"); | |
218 | return (ENOENT); | |
219 | } | |
220 | ||
6d2010ae A |
221 | if (cdevsw_setkqueueok(ptmx_major, &ptmx_cdev, 0) == -1) { |
222 | panic("Failed to set flags on ptmx cdevsw entry."); | |
223 | } | |
224 | ||
2d21ac55 A |
225 | /* Get a major number for /dev/pts/nnn */ |
226 | if ((ptsd_major = cdevsw_add(-15, &ptsd_cdev)) == -1) { | |
227 | (void)cdevsw_remove(ptmx_major, &ptmx_cdev); | |
228 | printf("ptmx_init: failed to obtain /dev/ptmx major number\n"); | |
229 | return (ENOENT); | |
230 | } | |
6d2010ae A |
231 | |
232 | if (cdevsw_setkqueueok(ptsd_major, &ptsd_cdev, 0) == -1) { | |
233 | panic("Failed to set flags on ptmx cdevsw entry."); | |
234 | } | |
235 | ||
236 | /* | |
237 | * Locks to guard against races between revoke and kevents | |
238 | */ | |
239 | ptsd_kevent_lock_init(); | |
2d21ac55 A |
240 | |
241 | /* Create the /dev/ptmx device {<major>,0} */ | |
242 | (void)devfs_make_node_clone(makedev(ptmx_major, 0), | |
243 | DEVFS_CHAR, UID_ROOT, GID_TTY, 0666, | |
244 | ptmx_clone, PTMX_TEMPLATE); | |
fe8ab488 A |
245 | |
246 | _ptmx_driver.master = ptmx_major; | |
247 | _ptmx_driver.slave = ptsd_major; | |
248 | _ptmx_driver.fix_7828447 = 1; | |
249 | _ptmx_driver.fix_7070978 = 1; | |
250 | #if CONFIG_MACF | |
251 | _ptmx_driver.mac_notify = 1; | |
252 | #endif | |
253 | _ptmx_driver.open = &ptmx_get_ioctl; | |
254 | _ptmx_driver.free = &ptmx_free_ioctl; | |
255 | _ptmx_driver.name = &ptmx_get_name; | |
256 | _ptmx_driver.revoke = &ptsd_revoke_knotes; | |
257 | tty_dev_register(&_ptmx_driver); | |
258 | ||
259 | return (0); | |
2d21ac55 A |
260 | } |
261 | ||
262 | ||
263 | static struct _ptmx_ioctl_state { | |
264 | struct ptmx_ioctl **pis_ioctl_list; /* pointer vector */ | |
265 | int pis_total; /* total slots */ | |
266 | int pis_free; /* free slots */ | |
267 | } _state; | |
268 | #define PTMX_GROW_VECTOR 16 /* Grow by this many slots at a time */ | |
269 | ||
270 | /* | |
271 | * Given a minor number, return the corresponding structure for that minor | |
272 | * number. If there isn't one, and the create flag is specified, we create | |
273 | * one if possible. | |
274 | * | |
275 | * Parameters: minor Minor number of ptmx device | |
276 | * open_flag PF_OPEN_M First open of master | |
277 | * PF_OPEN_S First open of slave | |
278 | * 0 Just want ioctl struct | |
279 | * | |
280 | * Returns: NULL Did not exist/could not create | |
281 | * !NULL structure corresponding minor number | |
b0d623f7 A |
282 | * |
283 | * Locks: tty_lock() on ptmx_ioctl->pt_tty NOT held on entry or exit. | |
2d21ac55 A |
284 | */ |
285 | static struct ptmx_ioctl * | |
286 | ptmx_get_ioctl(int minor, int open_flag) | |
287 | { | |
288 | struct ptmx_ioctl *new_ptmx_ioctl; | |
289 | ||
290 | if (open_flag & PF_OPEN_M) { | |
291 | ||
292 | /* | |
293 | * If we are about to allocate more memory, but we have | |
294 | * already hit the administrative limit, then fail the | |
295 | * operation. | |
296 | * | |
297 | * Note: Subtract free from total when making this | |
298 | * check to allow unit increments, rather than | |
299 | * snapping to the nearest PTMX_GROW_VECTOR... | |
300 | */ | |
301 | if ((_state.pis_total - _state.pis_free) >= ptmx_max) { | |
302 | return (NULL); | |
303 | } | |
304 | ||
305 | MALLOC(new_ptmx_ioctl, struct ptmx_ioctl *, sizeof(struct ptmx_ioctl), M_TTYS, M_WAITOK|M_ZERO); | |
306 | if (new_ptmx_ioctl == NULL) { | |
307 | return (NULL); | |
308 | } | |
309 | ||
310 | if ((new_ptmx_ioctl->pt_tty = ttymalloc()) == NULL) { | |
311 | FREE(new_ptmx_ioctl, M_TTYS); | |
312 | return (NULL); | |
313 | } | |
314 | ||
315 | /* | |
316 | * Hold the DEVFS_LOCK() over this whole operation; devfs | |
317 | * itself does this over malloc/free as well, so this should | |
318 | * be safe to do. We hold it longer than we want to, but | |
319 | * doing so avoids a reallocation race on the minor number. | |
320 | */ | |
321 | DEVFS_LOCK(); | |
322 | /* Need to allocate a larger vector? */ | |
323 | if (_state.pis_free == 0) { | |
324 | struct ptmx_ioctl **new_pis_ioctl_list; | |
325 | struct ptmx_ioctl **old_pis_ioctl_list = NULL; | |
326 | ||
327 | /* Yes. */ | |
328 | MALLOC(new_pis_ioctl_list, struct ptmx_ioctl **, sizeof(struct ptmx_ioctl *) * (_state.pis_total + PTMX_GROW_VECTOR), M_TTYS, M_WAITOK|M_ZERO); | |
329 | if (new_pis_ioctl_list == NULL) { | |
330 | ttyfree(new_ptmx_ioctl->pt_tty); | |
331 | DEVFS_UNLOCK(); | |
332 | FREE(new_ptmx_ioctl, M_TTYS); | |
333 | return (NULL); | |
334 | } | |
335 | ||
336 | /* If this is not the first time, copy the old over */ | |
337 | bcopy(_state.pis_ioctl_list, new_pis_ioctl_list, sizeof(struct ptmx_ioctl *) * _state.pis_total); | |
338 | old_pis_ioctl_list = _state.pis_ioctl_list; | |
339 | _state.pis_ioctl_list = new_pis_ioctl_list; | |
340 | _state.pis_free += PTMX_GROW_VECTOR; | |
341 | _state.pis_total += PTMX_GROW_VECTOR; | |
342 | if (old_pis_ioctl_list) | |
343 | FREE(old_pis_ioctl_list, M_TTYS); | |
935ed37a A |
344 | } |
345 | ||
fe8ab488 A |
346 | /* is minor in range now? */ |
347 | if (minor < 0 || minor >= _state.pis_total) { | |
348 | ttyfree(new_ptmx_ioctl->pt_tty); | |
349 | DEVFS_UNLOCK(); | |
350 | FREE(new_ptmx_ioctl, M_TTYS); | |
351 | return (NULL); | |
352 | } | |
353 | ||
935ed37a A |
354 | if (_state.pis_ioctl_list[minor] != NULL) { |
355 | ttyfree(new_ptmx_ioctl->pt_tty); | |
356 | DEVFS_UNLOCK(); | |
357 | FREE(new_ptmx_ioctl, M_TTYS); | |
358 | ||
359 | /* Special error value so we know to redrive the open, we've been raced */ | |
360 | return (struct ptmx_ioctl*)-1; | |
361 | ||
2d21ac55 A |
362 | } |
363 | ||
364 | /* Vector is large enough; grab a new ptmx_ioctl */ | |
365 | ||
366 | /* Now grab a free slot... */ | |
367 | _state.pis_ioctl_list[minor] = new_ptmx_ioctl; | |
368 | ||
369 | /* reduce free count */ | |
370 | _state.pis_free--; | |
371 | ||
372 | _state.pis_ioctl_list[minor]->pt_flags |= PF_OPEN_M; | |
373 | DEVFS_UNLOCK(); | |
374 | ||
375 | /* Create the /dev/ttysXXX device {<major>,XXX} */ | |
376 | _state.pis_ioctl_list[minor]->pt_devhandle = devfs_make_node( | |
377 | makedev(ptsd_major, minor), | |
378 | DEVFS_CHAR, UID_ROOT, GID_TTY, 0620, | |
379 | PTSD_TEMPLATE, minor); | |
b0d623f7 A |
380 | if (_state.pis_ioctl_list[minor]->pt_devhandle == NULL) { |
381 | printf("devfs_make_node() call failed for ptmx_get_ioctl()!!!!\n"); | |
382 | } | |
2d21ac55 | 383 | } |
fe8ab488 A |
384 | |
385 | if (minor < 0 || minor >= _state.pis_total) { | |
386 | return (NULL); | |
387 | } | |
388 | ||
2d21ac55 A |
389 | return (_state.pis_ioctl_list[minor]); |
390 | } | |
391 | ||
b0d623f7 A |
392 | /* |
393 | * Locks: tty_lock() of old_ptmx_ioctl->pt_tty NOT held for this call. | |
394 | */ | |
2d21ac55 A |
395 | static int |
396 | ptmx_free_ioctl(int minor, int open_flag) | |
397 | { | |
398 | struct ptmx_ioctl *old_ptmx_ioctl = NULL; | |
399 | ||
400 | DEVFS_LOCK(); | |
fe8ab488 A |
401 | |
402 | if (minor < 0 || minor >= _state.pis_total) { | |
403 | DEVFS_UNLOCK(); | |
404 | return (-1); | |
405 | } | |
406 | ||
2d21ac55 A |
407 | _state.pis_ioctl_list[minor]->pt_flags &= ~(open_flag); |
408 | ||
409 | /* | |
410 | * Was this the last close? We will recognize it because we only get | |
411 | * a notification on the last close of a device, and we will have | |
412 | * cleared both the master and the slave open bits in the flags. | |
413 | */ | |
414 | if (!(_state.pis_ioctl_list[minor]->pt_flags & (PF_OPEN_M|PF_OPEN_S))) { | |
415 | /* Mark as free so it can be reallocated later */ | |
416 | old_ptmx_ioctl = _state.pis_ioctl_list[ minor]; | |
2d21ac55 A |
417 | } |
418 | DEVFS_UNLOCK(); | |
419 | ||
420 | /* Free old after dropping lock */ | |
421 | if (old_ptmx_ioctl != NULL) { | |
422 | /* | |
423 | * XXX See <rdar://5348651> and <rdar://4854638> | |
424 | * | |
425 | * XXX Conditional to be removed when/if tty/pty reference | |
426 | * XXX counting and mutex implemented. | |
427 | */ | |
428 | if (old_ptmx_ioctl->pt_devhandle != NULL) | |
429 | devfs_remove(old_ptmx_ioctl->pt_devhandle); | |
430 | ttyfree(old_ptmx_ioctl->pt_tty); | |
431 | FREE(old_ptmx_ioctl, M_TTYS); | |
935ed37a A |
432 | |
433 | /* Don't remove the entry until the devfs slot is free */ | |
434 | DEVFS_LOCK(); | |
fe8ab488 | 435 | _state.pis_ioctl_list[minor] = NULL; |
935ed37a A |
436 | _state.pis_free++; |
437 | DEVFS_UNLOCK(); | |
2d21ac55 A |
438 | } |
439 | ||
440 | return (0); /* Success */ | |
441 | } | |
442 | ||
fe8ab488 A |
443 | static int |
444 | ptmx_get_name(int minor, char *buffer, size_t size) | |
445 | { | |
446 | return snprintf(buffer, size, "/dev/" PTSD_TEMPLATE, minor); | |
447 | } | |
2d21ac55 A |
448 | |
449 | ||
450 | ||
451 | /* | |
452 | * Given the dev entry that's being opened, we clone the device. This driver | |
453 | * doesn't actually use the dev entry, since we alreaqdy know who we are by | |
454 | * being called from this code. This routine is a callback registered from | |
455 | * devfs_make_node_clone() in ptmx_init(); it's purpose is to provide a new | |
456 | * minor number, or to return -1, if one can't be provided. | |
457 | * | |
458 | * Parameters: dev The device we are cloning from | |
459 | * | |
460 | * Returns: >= 0 A new minor device number | |
461 | * -1 Error: ENOMEM ("Can't alloc device") | |
462 | * | |
463 | * NOTE: Called with DEVFS_LOCK() held | |
464 | */ | |
465 | static int | |
466 | ptmx_clone(__unused dev_t dev, int action) | |
467 | { | |
468 | int i; | |
469 | ||
470 | if (action == DEVFS_CLONE_ALLOC) { | |
471 | /* First one */ | |
472 | if (_state.pis_total == 0) | |
473 | return (0); | |
474 | ||
475 | /* | |
476 | * Note: We can add hinting on free slots, if this linear search | |
477 | * ends up being a performance bottleneck... | |
478 | */ | |
479 | for(i = 0; i < _state.pis_total; i++) { | |
480 | if (_state.pis_ioctl_list[ i] == NULL) | |
481 | break; | |
482 | } | |
483 | ||
484 | /* | |
485 | * XXX We fall off the end here; if we did this twice at the | |
486 | * XXX same time, we could return the same minor to two | |
487 | * XXX callers; we should probably exand the pointer vector | |
488 | * XXX here, but I need more information on the MALLOC/FREE | |
489 | * XXX locking to ensure against a deadlock. Maybe we can | |
490 | * XXX just high watermark it at 1/2 of PTMX_GROW_VECTOR? | |
491 | * XXX That would require returning &minor as implict return | |
492 | * XXX and an error code ("EAGAIN/ERESTART") or 0 as our | |
493 | * XXX explicit return. | |
494 | */ | |
495 | ||
496 | return (i); /* empty slot or next slot */ | |
497 | } | |
498 | return(-1); | |
499 | } | |
500 | ||
b0d623f7 A |
501 | |
502 | /* | |
503 | * kqueue support. | |
504 | */ | |
505 | int ptsd_kqfilter(dev_t, struct knote *); | |
6d2010ae A |
506 | static void ptsd_kqops_detach(struct knote *); |
507 | static int ptsd_kqops_event(struct knote *, long); | |
b0d623f7 | 508 | |
6d2010ae | 509 | static struct filterops ptsd_kqops = { |
b0d623f7 | 510 | .f_isfd = 1, |
6d2010ae A |
511 | .f_detach = ptsd_kqops_detach, |
512 | .f_event = ptsd_kqops_event, | |
b0d623f7 | 513 | }; |
b0d623f7 | 514 | |
6d2010ae A |
515 | #define PTSD_KNOTE_VALID NULL |
516 | #define PTSD_KNOTE_REVOKED ((void *)-911l) | |
b0d623f7 | 517 | |
6d2010ae A |
518 | /* |
519 | * In the normal case, by the time the driver_close() routine is called | |
520 | * on the slave, all knotes have been detached. However in the revoke(2) | |
521 | * case, the driver's close routine is called while there are knotes active | |
522 | * that reference the handlers below. And we have no obvious means to | |
523 | * reach from the driver out to the kqueue's that reference them to get | |
524 | * them to stop. | |
525 | */ | |
b0d623f7 | 526 | |
6d2010ae A |
527 | static void |
528 | ptsd_kqops_detach(struct knote *kn) | |
b0d623f7 A |
529 | { |
530 | struct ptmx_ioctl *pti; | |
531 | struct tty *tp; | |
6d2010ae | 532 | dev_t dev, lockdev = (dev_t)kn->kn_hookid; |
b0d623f7 | 533 | |
6d2010ae | 534 | ptsd_kevent_mtx_lock(minor(lockdev)); |
b0d623f7 | 535 | |
6d2010ae A |
536 | if ((dev = (dev_t)kn->kn_hookid) != 0) { |
537 | pti = ptmx_get_ioctl(minor(dev), 0); | |
538 | if (pti != NULL && (tp = pti->pt_tty) != NULL) { | |
539 | tty_lock(tp); | |
540 | if (kn->kn_filter == EVFILT_READ) | |
541 | KNOTE_DETACH(&tp->t_rsel.si_note, kn); | |
542 | else | |
543 | KNOTE_DETACH(&tp->t_wsel.si_note, kn); | |
544 | tty_unlock(tp); | |
545 | kn->kn_hookid = 0; | |
546 | } | |
b0d623f7 A |
547 | } |
548 | ||
6d2010ae | 549 | ptsd_kevent_mtx_unlock(minor(lockdev)); |
b0d623f7 A |
550 | } |
551 | ||
552 | static int | |
6d2010ae | 553 | ptsd_kqops_event(struct knote *kn, long hint) |
b0d623f7 A |
554 | { |
555 | struct ptmx_ioctl *pti; | |
556 | struct tty *tp; | |
6d2010ae | 557 | dev_t dev = (dev_t)kn->kn_hookid; |
b0d623f7 A |
558 | int retval = 0; |
559 | ||
6d2010ae | 560 | ptsd_kevent_mtx_lock(minor(dev)); |
b0d623f7 | 561 | |
6d2010ae A |
562 | do { |
563 | if (kn->kn_hook != PTSD_KNOTE_VALID ) { | |
564 | /* We were revoked */ | |
565 | kn->kn_data = 0; | |
566 | kn->kn_flags |= EV_EOF; | |
567 | retval = 1; | |
568 | break; | |
569 | } | |
b0d623f7 | 570 | |
6d2010ae A |
571 | pti = ptmx_get_ioctl(minor(dev), 0); |
572 | if (pti == NULL || (tp = pti->pt_tty) == NULL) { | |
573 | kn->kn_data = ENXIO; | |
574 | kn->kn_flags |= EV_ERROR; | |
575 | retval = 1; | |
576 | break; | |
577 | } | |
b0d623f7 | 578 | |
6d2010ae A |
579 | if (hint == 0) |
580 | tty_lock(tp); | |
b0d623f7 | 581 | |
6d2010ae A |
582 | if (kn->kn_filter == EVFILT_READ) { |
583 | kn->kn_data = ttnread(tp); | |
584 | if (kn->kn_data > 0) | |
585 | retval = 1; | |
586 | if (ISSET(tp->t_state, TS_ZOMBIE)) { | |
587 | kn->kn_flags |= EV_EOF; | |
588 | retval = 1; | |
589 | } | |
590 | } else { /* EVFILT_WRITE */ | |
591 | if ((tp->t_outq.c_cc <= tp->t_lowat) && | |
592 | ISSET(tp->t_state, TS_CONNECTED)) { | |
593 | kn->kn_data = tp->t_outq.c_cn - tp->t_outq.c_cc; | |
594 | retval = 1; | |
595 | } | |
596 | if (ISSET(tp->t_state, TS_ZOMBIE)) { | |
597 | kn->kn_flags |= EV_EOF; | |
598 | retval = 1; | |
599 | } | |
600 | } | |
b0d623f7 | 601 | |
6d2010ae A |
602 | if (hint == 0) |
603 | tty_unlock(tp); | |
604 | } while (0); | |
b0d623f7 | 605 | |
6d2010ae | 606 | ptsd_kevent_mtx_unlock(minor(dev)); |
b0d623f7 | 607 | |
6d2010ae A |
608 | return (retval); |
609 | } | |
b0d623f7 A |
610 | int |
611 | ptsd_kqfilter(dev_t dev, struct knote *kn) | |
612 | { | |
613 | struct tty *tp = NULL; | |
614 | struct ptmx_ioctl *pti = NULL; | |
615 | int retval = 0; | |
616 | ||
617 | /* make sure we're talking about the right device type */ | |
fe8ab488 | 618 | if (cdevsw[major(dev)].d_open != ptsopen) { |
b0d623f7 A |
619 | return (EINVAL); |
620 | } | |
621 | ||
622 | if ((pti = ptmx_get_ioctl(minor(dev), 0)) == NULL) { | |
623 | return (ENXIO); | |
624 | } | |
625 | ||
626 | tp = pti->pt_tty; | |
627 | tty_lock(tp); | |
628 | ||
629 | kn->kn_hookid = dev; | |
6d2010ae A |
630 | kn->kn_hook = PTSD_KNOTE_VALID; |
631 | kn->kn_fop = &ptsd_kqops; | |
b0d623f7 A |
632 | |
633 | switch (kn->kn_filter) { | |
634 | case EVFILT_READ: | |
b0d623f7 A |
635 | KNOTE_ATTACH(&tp->t_rsel.si_note, kn); |
636 | break; | |
637 | case EVFILT_WRITE: | |
b0d623f7 A |
638 | KNOTE_ATTACH(&tp->t_wsel.si_note, kn); |
639 | break; | |
640 | default: | |
641 | retval = EINVAL; | |
642 | break; | |
643 | } | |
644 | ||
645 | tty_unlock(tp); | |
646 | return (retval); | |
647 | } | |
648 | ||
6d2010ae A |
649 | /* |
650 | * Support for revoke(2). | |
651 | * | |
652 | * Mark all the kn_hook fields so that future invocations of the | |
653 | * f_event op will just say "EOF" *without* looking at the | |
654 | * ptmx_ioctl structure (which may disappear or be recycled at | |
655 | * the end of ptsd_close). Issue wakeups to post that EOF to | |
656 | * anyone listening. And finally remove the knotes from the | |
657 | * tty's klists to keep ttyclose() happy, and set the hookid to | |
658 | * zero to make the final detach passively successful. | |
659 | */ | |
660 | static void | |
fe8ab488 | 661 | ptsd_revoke_knotes(int minor, struct tty *tp) |
6d2010ae A |
662 | { |
663 | struct klist *list; | |
664 | struct knote *kn, *tkn; | |
665 | ||
666 | /* (Hold and drop the right locks in the right order.) */ | |
667 | ||
fe8ab488 | 668 | ptsd_kevent_mtx_lock(minor); |
6d2010ae A |
669 | tty_lock(tp); |
670 | ||
671 | list = &tp->t_rsel.si_note; | |
672 | SLIST_FOREACH(kn, list, kn_selnext) | |
673 | kn->kn_hook = PTSD_KNOTE_REVOKED; | |
674 | ||
675 | list = &tp->t_wsel.si_note; | |
676 | SLIST_FOREACH(kn, list, kn_selnext) | |
677 | kn->kn_hook = PTSD_KNOTE_REVOKED; | |
678 | ||
679 | tty_unlock(tp); | |
fe8ab488 | 680 | ptsd_kevent_mtx_unlock(minor); |
6d2010ae A |
681 | |
682 | tty_lock(tp); | |
683 | ttwakeup(tp); | |
684 | ttwwakeup(tp); | |
685 | tty_unlock(tp); | |
686 | ||
fe8ab488 | 687 | ptsd_kevent_mtx_lock(minor); |
6d2010ae A |
688 | tty_lock(tp); |
689 | ||
690 | list = &tp->t_rsel.si_note; | |
691 | SLIST_FOREACH_SAFE(kn, list, kn_selnext, tkn) { | |
692 | (void) KNOTE_DETACH(list, kn); | |
693 | kn->kn_hookid = 0; | |
694 | } | |
695 | ||
696 | list = &tp->t_wsel.si_note; | |
697 | SLIST_FOREACH_SAFE(kn, list, kn_selnext, tkn) { | |
698 | (void) KNOTE_DETACH(list, kn); | |
699 | kn->kn_hookid = 0; | |
700 | } | |
701 | ||
702 | tty_unlock(tp); | |
fe8ab488 | 703 | ptsd_kevent_mtx_unlock(minor); |
6d2010ae | 704 | } |