xref: /NextBSD/sbin/launchd/ipc.c (revision e5f19987e8e1022aab8568fe11f27c10d22a8e2f)
1 /*
2  * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3  *
4  * @APPLE_APACHE_LICENSE_HEADER_START@
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * @APPLE_APACHE_LICENSE_HEADER_END@
19  */
20 
21 #include "config.h"
22 #include "ipc.h"
23 
24 #include <sys/socket.h>
25 #include <sys/types.h>
26 #include <sys/queue.h>
27 #include <sys/event.h>
28 #include <sys/stat.h>
29 #include <sys/ucred.h>
30 #include <sys/fcntl.h>
31 #include <sys/un.h>
32 #include <sys/resource.h>
33 #include <sys/wait.h>
34 #include <sys/sysctl.h>
35 #include <sys/sockio.h>
36 #include <sys/time.h>
37 #include <sys/ioctl.h>
38 #include <unistd.h>
39 #include <signal.h>
40 #include <errno.h>
41 #include <libgen.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <stdarg.h>
45 #include <stdbool.h>
46 #include <paths.h>
47 #include <string.h>
48 #include <os/assumes.h>
49 
50 #include "launch.h"
51 #include "launch_priv.h"
52 #include "launchd.h"
53 #include "runtime.h"
54 #include "core.h"
55 
56 extern char **environ;
57 
58 static LIST_HEAD(, conncb) connections;
59 
60 static launch_data_t adjust_rlimits(launch_data_t in);
61 
62 static void ipc_readmsg2(launch_data_t data, const char *cmd, void *context);
63 static void ipc_readmsg(launch_data_t msg, void *context);
64 static void ipc_process_command(launch_data_t data, const char *cmd, void *context);
65 
66 static void ipc_listen_callback(void *obj __attribute__((unused)), struct kevent *kev);
67 
68 static kq_callback kqipc_listen_callback = ipc_listen_callback;
69 
70 static pid_t ipc_self = 0;
71 
72 char *sockpath = NULL;
73 static char *sockdir = NULL;
74 
75 static bool ipc_inited = false;
76 
77 static void
ipc_clean_up(void)78 ipc_clean_up(void)
79 {
80 	if (ipc_self != getpid()) {
81 		return;
82 	}
83 
84 	if (-1 == unlink(sockpath)) {
85 		launchd_syslog(LOG_WARNING, "unlink(\"%s\"): %s", sockpath, strerror(errno));
86 	} else if (-1 == rmdir(sockdir)) {
87 		launchd_syslog(LOG_WARNING, "rmdir(\"%s\"): %s", sockdir, strerror(errno));
88 	}
89 }
90 
91 void
ipc_server_init(void)92 ipc_server_init(void)
93 {
94 	struct sockaddr_un sun;
95 	mode_t oldmask;
96 	int r, fd = -1;
97 	char ourdir[1024];
98 
99 	if (ipc_inited) {
100 		return;
101 	}
102 
103 	memset(&sun, 0, sizeof(sun));
104 	sun.sun_family = AF_UNIX;
105 
106 	if (pid1_magic) {
107 		strcpy(ourdir, LAUNCHD_SOCK_PREFIX);
108 		strncpy(sun.sun_path, LAUNCHD_SOCK_PREFIX "/sock", sizeof(sun.sun_path));
109 
110 		unlink(ourdir);
111 		if (mkdir(ourdir, S_IRWXU) == -1) {
112 			if (errno == EROFS) {
113 				goto out_bad;
114 			} else if (errno == EEXIST) {
115 				struct stat sb;
116 				stat(ourdir, &sb);
117 				if (!S_ISDIR(sb.st_mode)) {
118 					errno = EEXIST;
119 					launchd_syslog(LOG_ERR, "mkdir(\"%s\"): %s", LAUNCHD_SOCK_PREFIX, strerror(errno));
120 					goto out_bad;
121 				}
122 			} else {
123 				launchd_syslog(LOG_ERR, "mkdir(\"%s\"): %s", ourdir, strerror(errno));
124 				goto out_bad;
125 			}
126 		}
127 	} else {
128 		snprintf(ourdir, sizeof(ourdir), _PATH_TMP "launchd-%u.XXXXXX", getpid());
129 		if (mkdtemp(ourdir) == NULL) {
130 			launchd_syslog(LOG_ERR, "Could not create critical directory \"%s\": %s", ourdir, strerror(errno));
131 			goto out_bad;
132 		}
133 		snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/sock", ourdir);
134 	}
135 
136 	if (unlink(sun.sun_path) == -1 && errno != ENOENT) {
137 		if (errno != EROFS) {
138 			launchd_syslog(LOG_ERR, "unlink(\"thesocket\"): %s", strerror(errno));
139 		}
140 		goto out_bad;
141 	}
142 
143 	if (posix_assumes_zero(fd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1) {
144 		goto out_bad;
145 	}
146 
147 	oldmask = umask(S_IRWXG|S_IRWXO);
148 	r = bind(fd, (struct sockaddr *)&sun, sizeof(sun));
149 	umask(oldmask);
150 
151 	if (r == -1) {
152 		if (errno != EROFS) {
153 			launchd_syslog(LOG_ERR, "bind(\"thesocket\"): %s", strerror(errno));
154 		}
155 		goto out_bad;
156 	}
157 
158 	if (listen(fd, SOMAXCONN) == -1) {
159 		launchd_syslog(LOG_ERR, "listen(\"thesocket\"): %s", strerror(errno));
160 		goto out_bad;
161 	}
162 
163 	if (kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &kqipc_listen_callback) == -1) {
164 		launchd_syslog(LOG_ERR, "kevent_mod(\"thesocket\", EVFILT_READ): %s", strerror(errno));
165 		goto out_bad;
166 	}
167 
168 	ipc_inited = true;
169 
170 	sockdir = strdup(ourdir);
171 	sockpath = strdup(sun.sun_path);
172 	ipc_self = getpid();
173 	atexit(ipc_clean_up);
174 
175 out_bad:
176 	if (!ipc_inited && fd != -1) {
177 		(void)runtime_close(fd);
178 	}
179 }
180 
181 void
ipc_open(int fd,job_t j)182 ipc_open(int fd, job_t j)
183 {
184 	struct conncb *c = calloc(1, sizeof(struct conncb));
185 
186 	fcntl(fd, F_SETFL, O_NONBLOCK);
187 
188 	c->kqconn_callback = ipc_callback;
189 	if (j) {
190 		c->conn = launchd_fdopen(-1, fd);
191 	} else {
192 		c->conn = launchd_fdopen(fd, -1);
193 	}
194 
195 	c->j = j;
196 	LIST_INSERT_HEAD(&connections, c, sle);
197 	kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &c->kqconn_callback);
198 }
199 
200 void
ipc_listen_callback(void * obj,struct kevent * kev)201 ipc_listen_callback(void *obj __attribute__((unused)), struct kevent *kev)
202 {
203 	struct sockaddr_un sun;
204 	socklen_t sl = sizeof(sun);
205 	int cfd;
206 
207 	if ((cfd = _fd(accept(kev->ident, (struct sockaddr *)&sun, &sl))) == -1) {
208 		return;
209 	}
210 
211 	if (geteuid() == 0) {
212 		uid_t euid, guid;
213 		if (getpeereid(cfd, &euid, &guid) == -1) {
214 			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[%d] failed to getpeereid on incoming caller (%d)", getpid(), errno);
215 			(void)runtime_close(cfd);
216 			return;
217 		}
218 
219 		if (euid != geteuid()) {
220 			launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[%d] failed to euid check on incoming caller (%d != %d)", getpid(), euid, geteuid());
221 			(void)runtime_close(cfd);
222 			return;
223 		}
224 	}
225 
226 	ipc_open(cfd, NULL);
227 }
228 
229 void
ipc_callback(void * obj,struct kevent * kev)230 ipc_callback(void *obj, struct kevent *kev)
231 {
232 	struct conncb *c = obj;
233 	int r;
234 
235 	if (kev->filter == EVFILT_READ) {
236 		if (launchd_msg_recv(c->conn, ipc_readmsg, c) == -1 && errno != EAGAIN) {
237 			if (errno != ECONNRESET) {
238 				launchd_syslog(LOG_DEBUG, "%s(): recv: %s", __func__, strerror(errno));
239 			}
240 			ipc_close(c);
241 		}
242 	} else if (kev->filter == EVFILT_WRITE) {
243 		r = launchd_msg_send(c->conn, NULL);
244 		if (r == -1) {
245 			if (errno != EAGAIN) {
246 				launchd_syslog(LOG_DEBUG, "%s(): send: %s", __func__, strerror(errno));
247 				ipc_close(c);
248 			}
249 		} else if (r == 0) {
250 			kevent_mod(launchd_getfd(c->conn), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
251 		}
252 	} else {
253 		launchd_syslog(LOG_DEBUG, "%s(): unknown filter type!", __func__);
254 		ipc_close(c);
255 	}
256 }
257 
258 static void
set_user_env(launch_data_t obj,const char * key,void * context)259 set_user_env(launch_data_t obj, const char *key, void *context __attribute__((unused)))
260 {
261 	const char *v = launch_data_get_string(obj);
262 	if (v) {
263 		setenv(key, v, 1);
264 	} else {
265 		launchd_syslog(LOG_WARNING, "Attempt to set NULL environment variable: %s (type = %d)", key, launch_data_get_type(obj));
266 	}
267 }
268 
269 void
ipc_close_all_with_job(job_t j)270 ipc_close_all_with_job(job_t j)
271 {
272 	struct conncb *ci, *cin;
273 
274 	LIST_FOREACH_SAFE(ci, &connections, sle, cin) {
275 		if (ci->j == j) {
276 			ipc_close(ci);
277 		}
278 	}
279 }
280 
281 void
ipc_close_fds(launch_data_t o)282 ipc_close_fds(launch_data_t o)
283 {
284 	size_t i;
285 
286 	switch (launch_data_get_type(o)) {
287 	case LAUNCH_DATA_DICTIONARY:
288 		launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))ipc_close_fds, NULL);
289 		break;
290 	case LAUNCH_DATA_ARRAY:
291 		for (i = 0; i < launch_data_array_get_count(o); i++)
292 			ipc_close_fds(launch_data_array_get_index(o, i));
293 		break;
294 	case LAUNCH_DATA_FD:
295 		if (launch_data_get_fd(o) != -1) {
296 			(void)runtime_close(launch_data_get_fd(o));
297 		}
298 		break;
299 	default:
300 		break;
301 	}
302 }
303 
304 void
ipc_revoke_fds(launch_data_t o)305 ipc_revoke_fds(launch_data_t o)
306 {
307 	size_t i;
308 
309 	switch (launch_data_get_type(o)) {
310 	case LAUNCH_DATA_DICTIONARY:
311 		launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))ipc_revoke_fds, NULL);
312 		break;
313 	case LAUNCH_DATA_ARRAY:
314 		for (i = 0; i < launch_data_array_get_count(o); i++)
315 			ipc_revoke_fds(launch_data_array_get_index(o, i));
316 		break;
317 	case LAUNCH_DATA_FD:
318 		launch_data_set_fd(o, -1);
319 		break;
320 	default:
321 		break;
322 	}
323 }
324 
325 struct readmsg_context {
326 	struct conncb *c;
327 	launch_data_t resp;
328 };
329 
330 void
ipc_readmsg(launch_data_t msg,void * context)331 ipc_readmsg(launch_data_t msg, void *context)
332 {
333 	struct readmsg_context rmc = { context, NULL };
334 
335 	if (LAUNCH_DATA_DICTIONARY == launch_data_get_type(msg)) {
336 		launch_data_dict_iterate(msg, ipc_readmsg2, &rmc);
337 	} else if (LAUNCH_DATA_STRING == launch_data_get_type(msg)) {
338 		ipc_readmsg2(NULL, launch_data_get_string(msg), &rmc);
339 	} else {
340 		rmc.resp = launch_data_new_errno(EINVAL);
341 	}
342 
343 	if (NULL == rmc.resp) {
344 		rmc.resp = launch_data_new_errno(ENOSYS);
345 	}
346 
347 	ipc_close_fds(msg);
348 
349 	if (launchd_msg_send(rmc.c->conn, rmc.resp) == -1) {
350 		if (errno == EAGAIN) {
351 			kevent_mod(launchd_getfd(rmc.c->conn), EVFILT_WRITE, EV_ADD, 0, 0, &rmc.c->kqconn_callback);
352 		} else {
353 			launchd_syslog(LOG_DEBUG, "launchd_msg_send() == -1: %s", strerror(errno));
354 			ipc_close(rmc.c);
355 		}
356 	}
357 	launch_data_free(rmc.resp);
358 }
359 
360 void
ipc_readmsg2(launch_data_t data,const char * cmd,void * context)361 ipc_readmsg2(launch_data_t data, const char *cmd, void *context)
362 {
363 	struct readmsg_context *rmc = context;
364 	launch_data_t resp = NULL;
365 	job_t j;
366 
367 	if (rmc->resp) {
368 		return;
369 	}
370 
371 	/* Do not allow commands other than check-in to come over the trusted socket
372 	 * on the Desktop. On Embedded, allow all commands over the trusted socket
373 	 * if the job has the God Mode key set.
374 	 */
375 #if TARGET_OS_EMBEDDED
376 	bool allow_privileged_ops = (!rmc->c->j || job_is_god(rmc->c->j));
377 #else
378 	bool allow_privileged_ops = !rmc->c->j;
379 #endif
380 
381 	if (rmc->c->j && strcmp(cmd, LAUNCH_KEY_CHECKIN) == 0) {
382 		resp = job_export(rmc->c->j);
383 		job_checkin(rmc->c->j);
384 	} else if (allow_privileged_ops) {
385 #if TARGET_OS_EMBEDDED
386 		launchd_embedded_handofgod = rmc->c->j && job_is_god(rmc->c->j);
387 #endif
388 		if (data == NULL) {
389 			if (!strcmp(cmd, LAUNCH_KEY_SHUTDOWN)) {
390 				launchd_shutdown();
391 				resp = launch_data_new_errno(0);
392 			} else if (!strcmp(cmd, LAUNCH_KEY_GETJOBS)) {
393 				resp = job_export_all();
394 				ipc_revoke_fds(resp);
395 			} else if (!strcmp(cmd, LAUNCH_KEY_GETRESOURCELIMITS)) {
396 				resp = adjust_rlimits(NULL);
397 			} else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGESELF)) {
398 				struct rusage rusage;
399 				getrusage(RUSAGE_SELF, &rusage);
400 				resp = launch_data_new_opaque(&rusage, sizeof(rusage));
401 			} else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGECHILDREN)) {
402 				struct rusage rusage;
403 				getrusage(RUSAGE_CHILDREN, &rusage);
404 				resp = launch_data_new_opaque(&rusage, sizeof(rusage));
405 			}
406 		} else {
407 			if (!strcmp(cmd, LAUNCH_KEY_STARTJOB)) {
408 				if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
409 					errno = job_dispatch(j, true) ? 0 : errno;
410 				}
411 				resp = launch_data_new_errno(errno);
412 			} else if (!strcmp(cmd, LAUNCH_KEY_STOPJOB)) {
413 				if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
414 					errno = 0;
415 					job_stop(j);
416 				}
417 				resp = launch_data_new_errno(errno);
418 			} else if (!strcmp(cmd, LAUNCH_KEY_REMOVEJOB)) {
419 				if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
420 					errno = 0;
421 					job_remove(j);
422 				}
423 				resp = launch_data_new_errno(errno);
424 			} else if (!strcmp(cmd, LAUNCH_KEY_SUBMITJOB)) {
425 				if (launch_data_get_type(data) == LAUNCH_DATA_ARRAY) {
426 					resp = job_import_bulk(data);
427 				} else {
428 					if (job_import(data)) {
429 						errno = 0;
430 					}
431 					resp = launch_data_new_errno(errno);
432 				}
433 			} else if (!strcmp(cmd, LAUNCH_KEY_UNSETUSERENVIRONMENT)) {
434 				unsetenv(launch_data_get_string(data));
435 				resp = launch_data_new_errno(0);
436 			} else if (!strcmp(cmd, LAUNCH_KEY_SETUSERENVIRONMENT)) {
437 				launch_data_dict_iterate(data, set_user_env, NULL);
438 				resp = launch_data_new_errno(0);
439 			} else if (!strcmp(cmd, LAUNCH_KEY_SETRESOURCELIMITS)) {
440 				resp = adjust_rlimits(data);
441 			} else if (!strcmp(cmd, LAUNCH_KEY_GETJOB)) {
442 				if ((j = job_find(NULL, launch_data_get_string(data))) == NULL) {
443 					resp = launch_data_new_errno(errno);
444 				} else {
445 					resp = job_export(j);
446 					ipc_revoke_fds(resp);
447 				}
448 			}
449 		}
450 #if TARGET_OS_EMBEDDED
451 		launchd_embedded_handofgod = false;
452 #endif
453 	} else {
454 		resp = launch_data_new_errno(EACCES);
455 	}
456 
457 	rmc->resp = resp;
458 }
459 
460 struct ipc_process_msg_context
461 {
462 	job_t j;
463 	launch_data_t resp;
464 };
465 
466 launch_data_t
ipc_process_msg(job_t j,launch_data_t msg)467 ipc_process_msg(job_t j, launch_data_t msg)
468 {
469 	struct ipc_process_msg_context ctx = {j, NULL};
470 
471 	if (LAUNCH_DATA_DICTIONARY == launch_data_get_type(msg)) {
472 		launch_data_dict_iterate(msg, ipc_process_command, &ctx);
473 	} else if (LAUNCH_DATA_STRING == launch_data_get_type(msg)) {
474 		ipc_process_command(NULL, launch_data_get_string(msg), &ctx);
475 	} else {
476 		return (launch_data_new_errno(EINVAL));
477 	}
478 
479 	if (ctx.resp == NULL) {
480 		return (launch_data_new_errno(ENOSYS));
481 	}
482 
483 	return (ctx.resp);
484 }
485 
486 static void
ipc_process_command(launch_data_t data,const char * cmd,void * context)487 ipc_process_command(launch_data_t data, const char *cmd, void *context)
488 {
489 	struct ipc_process_msg_context *ctx = context;
490 	launch_data_t resp = NULL;
491 	job_t j;
492 
493 	/* Do not allow commands other than check-in to come over the trusted socket
494 	 * on the Desktop. On Embedded, allow all commands over the trusted socket
495 	 * if the job has the God Mode key set.
496 	 */
497 #if TARGET_OS_EMBEDDED
498 	bool allow_privileged_ops = (!ctx->j || job_is_god(ctx->j));
499 #else
500 	bool allow_privileged_ops = true;
501 #endif
502 
503 	if (ctx->resp)
504 		return;
505 
506 	if (ctx->j && strcmp(cmd, LAUNCH_KEY_CHECKIN) == 0) {
507 		resp = job_export(ctx->j);
508 		job_checkin(ctx->j);
509 	} else if (allow_privileged_ops) {
510 #if TARGET_OS_EMBEDDED
511 		launchd_embedded_handofgod = ctx->j && job_is_god(ctx->j);
512 #endif
513 		if (data == NULL) {
514 			if (!strcmp(cmd, LAUNCH_KEY_SHUTDOWN)) {
515 				launchd_shutdown();
516 				resp = launch_data_new_errno(0);
517 			} else if (!strcmp(cmd, LAUNCH_KEY_GETJOBS)) {
518 				resp = job_export_all();
519 				ipc_revoke_fds(resp);
520 			} else if (!strcmp(cmd, LAUNCH_KEY_GETRESOURCELIMITS)) {
521 				resp = adjust_rlimits(NULL);
522 			} else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGESELF)) {
523 				struct rusage rusage;
524 				getrusage(RUSAGE_SELF, &rusage);
525 				resp = launch_data_new_opaque(&rusage, sizeof(rusage));
526 			} else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGECHILDREN)) {
527 				struct rusage rusage;
528 				getrusage(RUSAGE_CHILDREN, &rusage);
529 				resp = launch_data_new_opaque(&rusage, sizeof(rusage));
530 			}
531 		} else {
532 			if (!strcmp(cmd, LAUNCH_KEY_STARTJOB)) {
533 				if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
534 					errno = job_dispatch(j, true) ? 0 : errno;
535 				}
536 				resp = launch_data_new_errno(errno);
537 			} else if (!strcmp(cmd, LAUNCH_KEY_STOPJOB)) {
538 				if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
539 					errno = 0;
540 					job_stop(j);
541 				}
542 				resp = launch_data_new_errno(errno);
543 			} else if (!strcmp(cmd, LAUNCH_KEY_REMOVEJOB)) {
544 				if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
545 					errno = 0;
546 					job_remove(j);
547 				}
548 				resp = launch_data_new_errno(errno);
549 			} else if (!strcmp(cmd, LAUNCH_KEY_SUBMITJOB)) {
550 				if (launch_data_get_type(data) == LAUNCH_DATA_ARRAY) {
551 					resp = job_import_bulk(data);
552 				} else {
553 					if (job_import(data)) {
554 						errno = 0;
555 					}
556 					resp = launch_data_new_errno(errno);
557 				}
558 			} else if (!strcmp(cmd, LAUNCH_KEY_UNSETUSERENVIRONMENT)) {
559 				unsetenv(launch_data_get_string(data));
560 				resp = launch_data_new_errno(0);
561 			} else if (!strcmp(cmd, LAUNCH_KEY_SETUSERENVIRONMENT)) {
562 				launch_data_dict_iterate(data, set_user_env, NULL);
563 				resp = launch_data_new_errno(0);
564 			} else if (!strcmp(cmd, LAUNCH_KEY_SETRESOURCELIMITS)) {
565 				resp = adjust_rlimits(data);
566 			} else if (!strcmp(cmd, LAUNCH_KEY_GETJOB)) {
567 				if ((j = job_find(NULL, launch_data_get_string(data))) == NULL) {
568 					resp = launch_data_new_errno(errno);
569 				} else {
570 					resp = job_export(ctx->j);
571 					ipc_revoke_fds(resp);
572 				}
573 			}
574 		}
575 #if TARGET_OS_EMBEDDED
576 		launchd_embedded_handofgod = false;
577 #endif
578 	} else {
579 		resp = launch_data_new_errno(EACCES);
580 	}
581 
582 	ctx->resp = resp;
583 }
584 
585 static int
close_abi_fixup(int fd)586 close_abi_fixup(int fd)
587 {
588 	return runtime_close(fd);
589 }
590 
591 void
ipc_close(struct conncb * c)592 ipc_close(struct conncb *c)
593 {
594 	LIST_REMOVE(c, sle);
595 	launchd_close(c->conn, close_abi_fixup);
596 	free(c);
597 }
598 
599 launch_data_t
adjust_rlimits(launch_data_t in)600 adjust_rlimits(launch_data_t in)
601 {
602 	/* If I never have to deal with this rlimit nonsense again, I'll be a very
603 	 * happy man.
604 	 */
605 	struct rlimit l[RLIM_NLIMITS];
606 	struct rlimit *ltmp;
607 	size_t i,ltmpsz;
608 
609 	for (i = 0; i < RLIM_NLIMITS; i++) {
610 		(void)posix_assumes_zero(getrlimit(i, l + i));
611 	}
612 
613 	if (in) {
614 		ltmp = launch_data_get_opaque(in);
615 		ltmpsz = launch_data_get_opaque_size(in);
616 
617 		if (ltmpsz > sizeof(l)) {
618 			launchd_syslog(LOG_WARNING, "Too much rlimit data sent!");
619 			ltmpsz = sizeof(l);
620 		}
621 
622 		for (i = 0; i < (ltmpsz / sizeof(struct rlimit)); i++) {
623 			if (ltmp[i].rlim_cur == l[i].rlim_cur && ltmp[i].rlim_max == l[i].rlim_max) {
624 				continue;
625 			}
626 
627 			if (/* XXX readcfg_pid && */ pid1_magic && (i == RLIMIT_NOFILE || i == RLIMIT_NPROC)) {
628 				int gmib[] = { CTL_KERN, KERN_MAXPROC };
629 				int pmib[] = { CTL_KERN, KERN_MAXPROCPERUID };
630 				const char *gstr = "kern.maxproc";
631 				const char *pstr = "kern.maxprocperuid";
632 				int gval = ltmp[i].rlim_max;
633 				int pval = ltmp[i].rlim_cur;
634 				switch (i) {
635 				case RLIMIT_NOFILE:
636 					gmib[1] = KERN_MAXFILES;
637 					pmib[1] = KERN_MAXFILESPERPROC;
638 					gstr = "kern.maxfiles";
639 					pstr = "kern.maxfilesperproc";
640 					break;
641 				default:
642 					break;
643 				}
644 
645 				if (gval > 0) {
646 					(void)posix_assumes_zero(sysctl(gmib, 2, NULL, NULL, &gval, sizeof(gval)));
647 				} else {
648 					launchd_syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", gstr);
649 				}
650 				if (pval > 0) {
651 					(void)posix_assumes_zero(sysctl(pmib, 2, NULL, NULL, &pval, sizeof(pval)));
652 				} else {
653 					launchd_syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", pstr);
654 				}
655 			}
656 			(void)posix_assumes_zero(setrlimit(i, ltmp + i));
657 			/* the kernel may have clamped the values we gave it */
658 			(void)posix_assumes_zero(getrlimit(i, l + i));
659 		}
660 	}
661 
662 	return launch_data_new_opaque(l, sizeof(struct rlimit) * RLIM_NLIMITS);
663 }
664