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