1 /* Copyright (C) 1991-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
17
18 #include <hurd.h>
19 #include <hurd/term.h>
20 #include <hurd/fd.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <fcntl.h>
24 #include <limits.h>
25 #include <lock-intern.h> /* For `struct mutex'. */
26 #include "set-hooks.h"
27 #include "hurdmalloc.h" /* XXX */
28
29
30 struct mutex _hurd_dtable_lock = MUTEX_INITIALIZER; /* XXX ld bug; must init */
31 struct hurd_fd **_hurd_dtable;
32 int _hurd_dtablesize;
33
34
35 DEFINE_HOOK (_hurd_fd_subinit, (void));
36
37 /* Initialize the file descriptor table at startup. */
38
39 static void attribute_used_retain
init_dtable(void)40 init_dtable (void)
41 {
42 int i;
43
44 __mutex_init (&_hurd_dtable_lock);
45
46 /* The initial size of the descriptor table is that of the passed-in
47 table. It will be expanded as necessary up to _hurd_dtable_rlimit. */
48 _hurd_dtablesize = _hurd_init_dtablesize;
49
50 /* Allocate the vector of pointers. */
51 _hurd_dtable = malloc (_hurd_dtablesize * sizeof (*_hurd_dtable));
52 if (_hurd_dtablesize != 0 && _hurd_dtable == NULL)
53 __libc_fatal ("hurd: Can't allocate file descriptor table\n");
54
55 /* Initialize the descriptor table. */
56 for (i = 0; (unsigned int) i < _hurd_init_dtablesize; ++i)
57 {
58 if (_hurd_init_dtable[i] == MACH_PORT_NULL)
59 /* An unused descriptor is marked by a null pointer. */
60 _hurd_dtable[i] = NULL;
61 else
62 {
63 /* Allocate a new file descriptor structure. */
64 struct hurd_fd *new = malloc (sizeof (struct hurd_fd));
65 if (new == NULL)
66 __libc_fatal ("hurd: Can't allocate initial file descriptors\n");
67
68 /* Initialize the port cells. */
69 _hurd_port_init (&new->port, MACH_PORT_NULL);
70 _hurd_port_init (&new->ctty, MACH_PORT_NULL);
71
72 /* Install the port in the descriptor.
73 This sets up all the ctty magic. */
74 _hurd_port2fd (new, _hurd_init_dtable[i], 0);
75
76 _hurd_dtable[i] = new;
77 }
78 }
79
80 /* Clear out the initial descriptor table.
81 Everything must use _hurd_dtable now. */
82 __vm_deallocate (__mach_task_self (),
83 (vm_address_t) _hurd_init_dtable,
84 _hurd_init_dtablesize * sizeof (_hurd_init_dtable[0]));
85 _hurd_init_dtable = NULL;
86 _hurd_init_dtablesize = 0;
87
88 /* Initialize the remaining empty slots in the table. */
89 for (; i < _hurd_dtablesize; ++i)
90 _hurd_dtable[i] = NULL;
91
92 /* Run things that want to run after the file descriptor table
93 is initialized. */
94 RUN_RELHOOK (_hurd_fd_subinit, ());
95 }
96
97 SET_RELHOOK (_hurd_subinit, init_dtable);
98
99 /* XXX when the linker supports it, the following functions should all be
100 elsewhere and just have text_set_elements here. */
101
102 /* Called by `getdport' to do its work. */
103
104 static file_t
get_dtable_port(int fd)105 get_dtable_port (int fd)
106 {
107 struct hurd_fd *d = _hurd_fd_get (fd);
108 file_t dport;
109
110 if (!d)
111 return __hurd_fail (EBADF), MACH_PORT_NULL;
112
113 HURD_CRITICAL_BEGIN;
114
115 dport = HURD_PORT_USE (&d->port,
116 ({
117 error_t err;
118 mach_port_t outport;
119 err = __mach_port_mod_refs (__mach_task_self (),
120 port,
121 MACH_PORT_RIGHT_SEND,
122 1);
123 if (err)
124 {
125 errno = err;
126 outport = MACH_PORT_NULL;
127 }
128 else
129 outport = port;
130 outport;
131 }));
132
133 HURD_CRITICAL_END;
134
135 return dport;
136 }
137
138 file_t (*_hurd_getdport_fn) (int fd) = get_dtable_port;
139
140 #include <hurd/signal.h>
141
142 /* We are in the child fork; the dtable lock is still held.
143 The parent has inserted send rights for all the normal io ports,
144 but we must recover ctty-special ports for ourselves. */
145 static error_t
fork_child_dtable(void)146 fork_child_dtable (void)
147 {
148 error_t err;
149 int i;
150
151 err = 0;
152
153 for (i = 0; !err && i < _hurd_dtablesize; ++i)
154 {
155 struct hurd_fd *d = _hurd_dtable[i];
156 if (d == NULL)
157 continue;
158
159 /* No other thread is using the send rights in the child task. */
160 d->port.users = d->ctty.users = NULL;
161
162 if (d->ctty.port != MACH_PORT_NULL)
163 {
164 /* There was a ctty-special port in the parent.
165 We need to get one for ourselves too. */
166 __mach_port_deallocate (__mach_task_self (), d->ctty.port);
167 err = __term_open_ctty (d->port.port, _hurd_pid, _hurd_pgrp,
168 &d->ctty.port);
169 if (err)
170 d->ctty.port = MACH_PORT_NULL;
171 }
172
173 /* XXX for each fd with a cntlmap, reauth and re-map_cntl. */
174 }
175 return err;
176
177 (void) &fork_child_dtable; /* Avoid "defined but not used" warning. */
178 }
179
180 data_set_element (_hurd_fork_locks, _hurd_dtable_lock); /* XXX ld bug: bss */
181 text_set_element (_hurd_fork_child_hook, fork_child_dtable);
182
183 /* Called when our process group has changed. */
184
185 static void
ctty_new_pgrp(void)186 ctty_new_pgrp (void)
187 {
188 int i;
189
190 retry:
191 HURD_CRITICAL_BEGIN;
192 __mutex_lock (&_hurd_dtable_lock);
193
194 if (__USEPORT (CTTYID, port == MACH_PORT_NULL))
195 {
196 /* We have no controlling terminal. If we haven't had one recently,
197 but our pgrp is being pointlessly diddled anyway, then we will
198 have nothing to do in the loop below because no fd will have a
199 ctty port at all.
200
201 More likely, a setsid call is responsible both for the change
202 in pgrp and for clearing the cttyid port. In that case, setsid
203 held the dtable lock while updating the dtable to clear all the
204 ctty ports, and ergo must have finished doing so before we run here.
205 So we can be sure, again, that the loop below has no work to do. */
206 }
207 else
208 for (i = 0; i < _hurd_dtablesize; ++i)
209 {
210 struct hurd_fd *const d = _hurd_dtable[i];
211 struct hurd_userlink ulink, ctty_ulink;
212 io_t port, ctty;
213
214 if (d == NULL)
215 /* Nothing to do for an unused descriptor cell. */
216 continue;
217
218 port = _hurd_port_get (&d->port, &ulink);
219 ctty = _hurd_port_get (&d->ctty, &ctty_ulink);
220
221 if (ctty != MACH_PORT_NULL)
222 {
223 /* This fd has a ctty-special port. We need a new one, to tell
224 the io server of our different process group. */
225 io_t new;
226 error_t err;
227 if ((err = __term_open_ctty (port, _hurd_pid, _hurd_pgrp, &new)))
228 {
229 if (err == EINTR)
230 {
231 /* Got a signal while inside an RPC of the critical section, retry again */
232 __mutex_unlock (&_hurd_dtable_lock);
233 HURD_CRITICAL_UNLOCK;
234 goto retry;
235 }
236 new = MACH_PORT_NULL;
237 }
238 _hurd_port_set (&d->ctty, new);
239 }
240
241 _hurd_port_free (&d->port, &ulink, port);
242 _hurd_port_free (&d->ctty, &ctty_ulink, ctty);
243 }
244
245 __mutex_unlock (&_hurd_dtable_lock);
246 HURD_CRITICAL_END;
247
248 (void) &ctty_new_pgrp; /* Avoid "defined but not used" warning. */
249 }
250
251 text_set_element (_hurd_pgrp_changed_hook, ctty_new_pgrp);
252
253 /* Called to reauthenticate the dtable when the auth port changes. */
254
255 static void
reauth_dtable(void)256 reauth_dtable (void)
257 {
258 int i;
259
260 HURD_CRITICAL_BEGIN;
261 __mutex_lock (&_hurd_dtable_lock);
262
263 for (i = 0; i < _hurd_dtablesize; ++i)
264 {
265 struct hurd_fd *const d = _hurd_dtable[i];
266 mach_port_t new, newctty, ref;
267
268 if (d == NULL)
269 /* Nothing to do for an unused descriptor cell. */
270 continue;
271
272 ref = __mach_reply_port ();
273
274 /* Take the descriptor cell's lock. */
275 __spin_lock (&d->port.lock);
276
277 /* Reauthenticate the descriptor's port. */
278 if (d->port.port != MACH_PORT_NULL
279 && ! __io_reauthenticate (d->port.port,
280 ref, MACH_MSG_TYPE_MAKE_SEND)
281 && ! __USEPORT (AUTH, __auth_user_authenticate
282 (port,
283 ref, MACH_MSG_TYPE_MAKE_SEND,
284 &new)))
285 {
286 /* Replace the port in the descriptor cell
287 with the newly reauthenticated port. */
288
289 if (d->ctty.port != MACH_PORT_NULL
290 && ! __io_reauthenticate (d->ctty.port,
291 ref, MACH_MSG_TYPE_MAKE_SEND)
292 && ! __USEPORT (AUTH, __auth_user_authenticate
293 (port,
294 ref, MACH_MSG_TYPE_MAKE_SEND,
295 &newctty)))
296 _hurd_port_set (&d->ctty, newctty);
297
298 _hurd_port_locked_set (&d->port, new);
299 }
300 else
301 /* Lost. Leave this descriptor cell alone. */
302 __spin_unlock (&d->port.lock);
303
304 __mach_port_destroy (__mach_task_self (), ref);
305 }
306
307 __mutex_unlock (&_hurd_dtable_lock);
308 HURD_CRITICAL_END;
309
310 (void) &reauth_dtable; /* Avoid "defined but not used" warning. */
311 }
312
313 text_set_element (_hurd_reauth_hook, reauth_dtable);
314