1 | /* Copyright (C) 1992-2024 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/msg_server.h> |
20 | #include <hurd/fd.h> |
21 | #include <unistd.h> |
22 | #include <limits.h> |
23 | #include <string.h> |
24 | #include <argz.h> |
25 | |
26 | |
27 | #define AUTHCHECK \ |
28 | if (auth != mach_task_self () && ! __USEPORT (AUTH, port == auth)) \ |
29 | return EPERM |
30 | |
31 | |
32 | /* Snarfing and frobbing the init ports. */ |
33 | |
34 | kern_return_t |
35 | _S_msg_get_init_port (mach_port_t msgport, mach_port_t auth, int which, |
36 | mach_port_t *result, mach_msg_type_name_t *result_type) |
37 | { |
38 | error_t err; |
39 | |
40 | AUTHCHECK; |
41 | |
42 | *result_type = MACH_MSG_TYPE_MOVE_SEND; |
43 | /* This function adds a new user reference for the *RESULT it gives back. |
44 | Our reply message uses a move-send right that consumes this reference. */ |
45 | err = _hurd_ports_get (which, result); |
46 | if (!err && MACH_PORT_VALID (auth)) |
47 | __mach_port_deallocate (__mach_task_self (), auth); |
48 | return err; |
49 | } |
50 | |
51 | kern_return_t |
52 | _S_msg_set_init_port (mach_port_t msgport, mach_port_t auth, |
53 | int which, mach_port_t port) |
54 | { |
55 | error_t err; |
56 | |
57 | AUTHCHECK; |
58 | |
59 | err = _hurd_ports_set (which, port); |
60 | |
61 | if (!err && MACH_PORT_VALID (port)) |
62 | __mach_port_deallocate (__mach_task_self (), port); |
63 | if (!err && MACH_PORT_VALID (auth)) |
64 | __mach_port_deallocate (__mach_task_self (), auth); |
65 | |
66 | return err; |
67 | } |
68 | |
69 | kern_return_t |
70 | _S_msg_get_init_ports (mach_port_t msgport, mach_port_t auth, |
71 | mach_port_t **ports, |
72 | mach_msg_type_name_t *ports_type, |
73 | mach_msg_type_number_t *nports) |
74 | { |
75 | mach_msg_type_number_t i; |
76 | error_t err; |
77 | |
78 | AUTHCHECK; |
79 | |
80 | if (err = __vm_allocate (__mach_task_self (), (vm_address_t *) ports, |
81 | _hurd_nports * sizeof (mach_port_t), 1)) |
82 | return err; |
83 | *nports = _hurd_nports; |
84 | |
85 | for (i = 0; i < _hurd_nports; ++i) |
86 | /* This function adds a new user ref for the *RESULT it gives back. |
87 | Our reply message uses move-send rights that consumes this ref. */ |
88 | if (err = _hurd_ports_get (i, &(*ports)[i])) |
89 | { |
90 | /* Died part way through. Deallocate the ports already fetched. */ |
91 | while (i-- > 0) |
92 | __mach_port_deallocate (__mach_task_self (), (*ports)[i]); |
93 | __vm_deallocate (__mach_task_self (), |
94 | (vm_address_t) *ports, |
95 | *nports * sizeof (mach_port_t)); |
96 | return err; |
97 | } |
98 | |
99 | *ports_type = MACH_MSG_TYPE_MOVE_SEND; |
100 | if (MACH_PORT_VALID (auth)) |
101 | __mach_port_deallocate (__mach_task_self (), auth); |
102 | return 0; |
103 | } |
104 | |
105 | kern_return_t |
106 | _S_msg_set_init_ports (mach_port_t msgport, mach_port_t auth, |
107 | const mach_port_t *ports, mach_msg_type_number_t nports) |
108 | { |
109 | mach_msg_type_number_t i; |
110 | error_t err; |
111 | |
112 | AUTHCHECK; |
113 | |
114 | for (i = 0; i < _hurd_nports; ++i) |
115 | { |
116 | if (err = _hurd_ports_set (i, ports[i])) |
117 | return err; |
118 | else |
119 | __mach_port_deallocate (__mach_task_self (), ports[i]); |
120 | } |
121 | |
122 | if (MACH_PORT_VALID (auth)) |
123 | __mach_port_deallocate (__mach_task_self (), auth); |
124 | return 0; |
125 | } |
126 | |
127 | /* Snarfing and frobbing the init ints. */ |
128 | |
129 | static kern_return_t |
130 | get_int (int which, int *value) |
131 | { |
132 | switch (which) |
133 | { |
134 | case INIT_UMASK: |
135 | *value = _hurd_umask; |
136 | return 0; |
137 | case INIT_SIGPENDING: |
138 | { |
139 | struct hurd_sigstate *ss = _hurd_global_sigstate; |
140 | __spin_lock (&ss->lock); |
141 | *value = ss->pending; |
142 | __spin_unlock (&ss->lock); |
143 | return 0; |
144 | } |
145 | case INIT_SIGIGN: |
146 | { |
147 | struct hurd_sigstate *ss = _hurd_global_sigstate; |
148 | sigset_t ign; |
149 | int sig; |
150 | __spin_lock (&ss->lock); |
151 | __sigemptyset (&ign); |
152 | for (sig = 1; sig < NSIG; ++sig) |
153 | if (ss->actions[sig].sa_handler == SIG_IGN) |
154 | __sigaddset (&ign, sig); |
155 | __spin_unlock (&ss->lock); |
156 | *value = ign; |
157 | return 0; |
158 | } |
159 | default: |
160 | return EINVAL; |
161 | } |
162 | } |
163 | |
164 | kern_return_t |
165 | _S_msg_get_init_int (mach_port_t msgport, mach_port_t auth, |
166 | int which, int *value) |
167 | { |
168 | error_t err; |
169 | |
170 | AUTHCHECK; |
171 | |
172 | err = get_int (which, value); |
173 | if (err) |
174 | return err; |
175 | if (MACH_PORT_VALID (auth)) |
176 | __mach_port_deallocate (__mach_task_self (), auth); |
177 | return 0; |
178 | } |
179 | |
180 | kern_return_t |
181 | _S_msg_get_init_ints (mach_port_t msgport, mach_port_t auth, |
182 | int **values, mach_msg_type_number_t *nvalues) |
183 | { |
184 | error_t err; |
185 | mach_msg_type_number_t i; |
186 | |
187 | AUTHCHECK; |
188 | |
189 | if (err = __vm_allocate (__mach_task_self (), (vm_address_t *) values, |
190 | INIT_INT_MAX * sizeof (int), 1)) |
191 | return err; |
192 | *nvalues = INIT_INT_MAX; |
193 | |
194 | for (i = 0; i < INIT_INT_MAX; ++i) |
195 | switch (err = get_int (i, &(*values)[i])) |
196 | { |
197 | case 0: /* Success. */ |
198 | break; |
199 | case EINVAL: /* Unknown index. */ |
200 | (*values)[i] = 0; |
201 | break; |
202 | default: /* Lossage. */ |
203 | __vm_deallocate (__mach_task_self (), |
204 | (vm_address_t) *values, INIT_INT_MAX * sizeof (int)); |
205 | return err; |
206 | } |
207 | |
208 | if (MACH_PORT_VALID (auth)) |
209 | __mach_port_deallocate (__mach_task_self (), auth); |
210 | return 0; |
211 | } |
212 | |
213 | |
214 | static kern_return_t |
215 | set_int (int which, int value) |
216 | { |
217 | switch (which) |
218 | { |
219 | case INIT_UMASK: |
220 | _hurd_umask = value; |
221 | return 0; |
222 | |
223 | /* These are pretty odd things to do. But you asked for it. */ |
224 | case INIT_SIGPENDING: |
225 | { |
226 | struct hurd_sigstate *ss = _hurd_global_sigstate; |
227 | __spin_lock (&ss->lock); |
228 | ss->pending = value; |
229 | __spin_unlock (&ss->lock); |
230 | return 0; |
231 | } |
232 | case INIT_SIGIGN: |
233 | { |
234 | struct hurd_sigstate *ss = _hurd_global_sigstate; |
235 | int sig; |
236 | const sigset_t ign = value; |
237 | __spin_lock (&ss->lock); |
238 | for (sig = 1; sig < NSIG; ++sig) |
239 | { |
240 | if (__sigismember (&ign, sig)) |
241 | ss->actions[sig].sa_handler = SIG_IGN; |
242 | else if (ss->actions[sig].sa_handler == SIG_IGN) |
243 | ss->actions[sig].sa_handler = SIG_DFL; |
244 | } |
245 | __spin_unlock (&ss->lock); |
246 | return 0; |
247 | |
248 | case INIT_TRACEMASK: |
249 | _hurdsig_traced = value; |
250 | return 0; |
251 | } |
252 | default: |
253 | return EINVAL; |
254 | } |
255 | } |
256 | |
257 | kern_return_t |
258 | _S_msg_set_init_int (mach_port_t msgport, mach_port_t auth, |
259 | int which, int value) |
260 | { |
261 | error_t err; |
262 | |
263 | AUTHCHECK; |
264 | |
265 | err = set_int (which, value); |
266 | if (err) |
267 | return err; |
268 | if (MACH_PORT_VALID (auth)) |
269 | __mach_port_deallocate (__mach_task_self (), auth); |
270 | return 0; |
271 | } |
272 | |
273 | kern_return_t |
274 | _S_msg_set_init_ints (mach_port_t msgport, mach_port_t auth, |
275 | const int *values, mach_msg_type_number_t nvalues) |
276 | { |
277 | error_t err; |
278 | mach_msg_type_number_t i; |
279 | |
280 | AUTHCHECK; |
281 | |
282 | for (i = 0; i < INIT_INT_MAX; ++i) |
283 | switch (err = set_int (i, values[i])) |
284 | { |
285 | case 0: /* Success. */ |
286 | break; |
287 | case EINVAL: /* Unknown index. */ |
288 | break; |
289 | default: /* Lossage. */ |
290 | return err; |
291 | } |
292 | |
293 | if (MACH_PORT_VALID (auth)) |
294 | __mach_port_deallocate (__mach_task_self (), auth); |
295 | return 0; |
296 | } |
297 | |
298 | |
299 | kern_return_t |
300 | _S_msg_get_fd (mach_port_t msgport, mach_port_t auth, int which, |
301 | mach_port_t *result, mach_msg_type_name_t *result_type) |
302 | { |
303 | AUTHCHECK; |
304 | |
305 | /* This creates a new user reference for the send right. |
306 | Our reply message will move that reference to the caller. */ |
307 | *result = __getdport (which); |
308 | if (*result == MACH_PORT_NULL) |
309 | return errno; |
310 | *result_type = MACH_MSG_TYPE_MOVE_SEND; |
311 | |
312 | if (MACH_PORT_VALID (auth)) |
313 | __mach_port_deallocate (__mach_task_self (), auth); |
314 | return 0; |
315 | } |
316 | |
317 | kern_return_t |
318 | _S_msg_set_fd (mach_port_t msgport, mach_port_t auth, |
319 | int which, mach_port_t port) |
320 | { |
321 | error_t err; |
322 | |
323 | AUTHCHECK; |
324 | |
325 | /* We consume the reference if successful. */ |
326 | err = HURD_FD_USE (which, |
327 | ({ |
328 | int flags = (descriptor->flags & FD_CLOEXEC) |
329 | ? O_CLOEXEC : 0; |
330 | _hurd_port2fd (descriptor, port, flags); |
331 | 0; |
332 | })); |
333 | if (err) |
334 | return err; |
335 | |
336 | if (MACH_PORT_VALID (auth)) |
337 | __mach_port_deallocate (__mach_task_self (), auth); |
338 | return 0; |
339 | } |
340 | |
341 | /* Snarfing and frobbing environment variables. */ |
342 | |
343 | kern_return_t |
344 | _S_msg_get_env_variable (mach_port_t msgport, |
345 | const_string_t variable, |
346 | char **data, mach_msg_type_number_t *datalen) |
347 | { |
348 | error_t err; |
349 | mach_msg_type_number_t valuelen; |
350 | const char *value = getenv (variable); |
351 | |
352 | if (value == NULL) |
353 | return ENOENT; |
354 | |
355 | valuelen = strlen (value); |
356 | if (valuelen > *datalen) |
357 | { |
358 | if (err = __vm_allocate (__mach_task_self (), |
359 | (vm_address_t *) data, valuelen, 1)) |
360 | return err; |
361 | } |
362 | |
363 | memcpy (*data, value, valuelen); |
364 | *datalen = valuelen; |
365 | |
366 | return 0; |
367 | } |
368 | |
369 | |
370 | kern_return_t |
371 | _S_msg_set_env_variable (mach_port_t msgport, mach_port_t auth, |
372 | const_string_t variable, |
373 | const_string_t value, |
374 | int replace) |
375 | { |
376 | AUTHCHECK; |
377 | |
378 | if (__setenv (variable, value, replace)) /* XXX name space */ |
379 | return errno; |
380 | |
381 | if (MACH_PORT_VALID (auth)) |
382 | __mach_port_deallocate (__mach_task_self (), auth); |
383 | return 0; |
384 | } |
385 | |
386 | kern_return_t |
387 | _S_msg_get_environment (mach_port_t msgport, |
388 | char **data, mach_msg_type_number_t *datalen) |
389 | { |
390 | /* Pack the environment into an array with nulls separating elements. */ |
391 | if (__environ != NULL) |
392 | { |
393 | char *ap, **p; |
394 | size_t envlen = 0; |
395 | |
396 | for (p = __environ; *p != NULL; ++p) |
397 | envlen += strlen (s: *p) + 1; |
398 | |
399 | if (envlen > *datalen) |
400 | { |
401 | if (__vm_allocate (__mach_task_self (), |
402 | (vm_address_t *) data, envlen, 1)) |
403 | return ENOMEM; |
404 | } |
405 | |
406 | ap = *data; |
407 | for (p = __environ; *p != NULL; ++p) |
408 | ap = __memccpy (dest: ap, src: *p, c: '\0', ULONG_MAX); |
409 | |
410 | *datalen = envlen; |
411 | } |
412 | else |
413 | *datalen = 0; |
414 | |
415 | return 0; |
416 | } |
417 | |
418 | kern_return_t |
419 | _S_msg_set_environment (mach_port_t msgport, mach_port_t auth, |
420 | const char *data, mach_msg_type_number_t datalen) |
421 | { |
422 | int _hurd_split_args (char *, mach_msg_type_number_t, char **); |
423 | int envc; |
424 | char **envp; |
425 | |
426 | AUTHCHECK; |
427 | |
428 | envc = __argz_count (argz: data, len: datalen); |
429 | envp = malloc ((envc + 1) * sizeof (char *)); |
430 | if (envp == NULL) |
431 | return errno; |
432 | __argz_extract (argz: data, len: datalen, argv: envp); |
433 | __environ = envp; /* XXX cooperate with loadenv et al */ |
434 | |
435 | if (MACH_PORT_VALID (auth)) |
436 | __mach_port_deallocate (__mach_task_self (), auth); |
437 | return 0; |
438 | } |
439 | |
440 | |
441 | kern_return_t |
442 | _S_msg_get_dtable (mach_port_t process, |
443 | mach_port_t auth, |
444 | portarray_t *dtable, |
445 | mach_msg_type_name_t *dtablePoly, |
446 | mach_msg_type_number_t *dtableCnt) |
447 | { |
448 | mach_port_t *ports; |
449 | mach_msg_type_number_t i; |
450 | error_t err; |
451 | |
452 | AUTHCHECK; |
453 | |
454 | HURD_CRITICAL_BEGIN; |
455 | __mutex_lock (&_hurd_dtable_lock); |
456 | |
457 | if (err = __vm_allocate (__mach_task_self (), (vm_address_t *) &ports, |
458 | _hurd_dtablesize * sizeof(mach_port_t), 1)) |
459 | goto out; |
460 | |
461 | for (i = 0; i < _hurd_dtablesize; i++) |
462 | { |
463 | struct hurd_fd *cell = _hurd_dtable[i]; |
464 | if (cell == NULL) |
465 | ports[i] = MACH_PORT_NULL; |
466 | else |
467 | { |
468 | __spin_lock (&cell->port.lock); |
469 | if (cell->port.port == MACH_PORT_NULL) |
470 | ports[i] = MACH_PORT_NULL; |
471 | else |
472 | { |
473 | ports[i] = cell->port.port; |
474 | /* We will move this send right. */ |
475 | __mach_port_mod_refs (__mach_task_self (), ports[i], |
476 | MACH_PORT_RIGHT_SEND, +1); |
477 | } |
478 | __spin_unlock (&cell->port.lock); |
479 | } |
480 | } |
481 | |
482 | *dtable = ports; |
483 | *dtablePoly = MACH_MSG_TYPE_MOVE_SEND; |
484 | *dtableCnt = _hurd_dtablesize; |
485 | |
486 | out: |
487 | __mutex_unlock (&_hurd_dtable_lock); |
488 | HURD_CRITICAL_END; |
489 | if (!err && MACH_PORT_VALID (auth)) |
490 | __mach_port_deallocate (__mach_task_self (), auth); |
491 | return err; |
492 | } |
493 | |
494 | /* XXX */ |
495 | |
496 | kern_return_t |
497 | _S_msg_set_dtable (mach_port_t process, |
498 | mach_port_t refport, |
499 | const_portarray_t dtable, |
500 | mach_msg_type_number_t dtableCnt) |
501 | { return EOPNOTSUPP; } |
502 | |