]> git.saurik.com Git - apple/launchd.git/blame - src/ipc.c
launchd-442.26.2.tar.gz
[apple/launchd.git] / src / ipc.c
CommitLineData
ed34e3c3
A
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
5b0a4722 21#include "config.h"
eabd1701 22#include "ipc.h"
ed34e3c3 23
ddbbfbc1 24#include <sys/socket.h>
ed34e3c3
A
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/wait.h>
33#include <sys/sysctl.h>
34#include <sys/sockio.h>
35#include <sys/time.h>
36#include <sys/resource.h>
37#include <sys/ioctl.h>
38#include <unistd.h>
39#include <signal.h>
40#include <errno.h>
ed34e3c3
A
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>
eabd1701 48#include <assumes.h>
ed34e3c3 49
ef398931
A
50#include "launch.h"
51#include "launch_priv.h"
ed34e3c3 52#include "launchd.h"
eabd1701
A
53#include "runtime.h"
54#include "core.h"
ed34e3c3
A
55
56extern char **environ;
57
5b0a4722 58static LIST_HEAD(, conncb) connections;
ed34e3c3
A
59
60static launch_data_t adjust_rlimits(launch_data_t in);
61
62static void ipc_readmsg2(launch_data_t data, const char *cmd, void *context);
ddbbfbc1 63static void ipc_readmsg(launch_data_t msg, void *context);
ed34e3c3
A
64
65static void ipc_listen_callback(void *obj __attribute__((unused)), struct kevent *kev);
66
67static kq_callback kqipc_listen_callback = ipc_listen_callback;
68
69static pid_t ipc_self = 0;
70
71char *sockpath = NULL;
72static char *sockdir = NULL;
73
74static bool ipc_inited = false;
75
ddbbfbc1 76static void
ed34e3c3
A
77ipc_clean_up(void)
78{
5b0a4722 79 if (ipc_self != getpid()) {
ed34e3c3 80 return;
5b0a4722 81 }
ed34e3c3 82
5b0a4722 83 if (-1 == unlink(sockpath)) {
eabd1701 84 launchd_syslog(LOG_WARNING, "unlink(\"%s\"): %s", sockpath, strerror(errno));
5b0a4722 85 } else if (-1 == rmdir(sockdir)) {
eabd1701 86 launchd_syslog(LOG_WARNING, "rmdir(\"%s\"): %s", sockdir, strerror(errno));
5b0a4722 87 }
ed34e3c3
A
88}
89
90void
5b0a4722 91ipc_server_init(void)
ed34e3c3
A
92{
93 struct sockaddr_un sun;
94 mode_t oldmask;
95 int r, fd = -1;
96 char ourdir[1024];
ed34e3c3 97
5b0a4722 98 if (ipc_inited) {
ed34e3c3 99 return;
5b0a4722 100 }
ed34e3c3
A
101
102 memset(&sun, 0, sizeof(sun));
103 sun.sun_family = AF_UNIX;
104
ddbbfbc1 105 if (pid1_magic) {
ed34e3c3
A
106 strcpy(ourdir, LAUNCHD_SOCK_PREFIX);
107 strncpy(sun.sun_path, LAUNCHD_SOCK_PREFIX "/sock", sizeof(sun.sun_path));
108
109 unlink(ourdir);
110 if (mkdir(ourdir, S_IRWXU) == -1) {
111 if (errno == EROFS) {
112 goto out_bad;
113 } else if (errno == EEXIST) {
114 struct stat sb;
115 stat(ourdir, &sb);
116 if (!S_ISDIR(sb.st_mode)) {
117 errno = EEXIST;
eabd1701 118 launchd_syslog(LOG_ERR, "mkdir(\"%s\"): %s", LAUNCHD_SOCK_PREFIX, strerror(errno));
ed34e3c3
A
119 goto out_bad;
120 }
121 } else {
eabd1701 122 launchd_syslog(LOG_ERR, "mkdir(\"%s\"): %s", ourdir, strerror(errno));
ed34e3c3
A
123 goto out_bad;
124 }
125 }
126 } else {
ddbbfbc1
A
127 snprintf(ourdir, sizeof(ourdir), _PATH_TMP "launchd-%u.XXXXXX", getpid());
128 if (mkdtemp(ourdir) == NULL) {
eabd1701 129 launchd_syslog(LOG_ERR, "Could not create critical directory \"%s\": %s", ourdir, strerror(errno));
ed34e3c3 130 goto out_bad;
5b0a4722 131 }
ed34e3c3 132 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/sock", ourdir);
ed34e3c3
A
133 }
134
135 if (unlink(sun.sun_path) == -1 && errno != ENOENT) {
5b0a4722 136 if (errno != EROFS) {
eabd1701 137 launchd_syslog(LOG_ERR, "unlink(\"thesocket\"): %s", strerror(errno));
5b0a4722 138 }
ed34e3c3
A
139 goto out_bad;
140 }
141
eabd1701 142 if (posix_assumes_zero(fd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1) {
ed34e3c3 143 goto out_bad;
5b0a4722 144 }
ed34e3c3
A
145
146 oldmask = umask(S_IRWXG|S_IRWXO);
147 r = bind(fd, (struct sockaddr *)&sun, sizeof(sun));
148 umask(oldmask);
149
150 if (r == -1) {
5b0a4722 151 if (errno != EROFS) {
eabd1701 152 launchd_syslog(LOG_ERR, "bind(\"thesocket\"): %s", strerror(errno));
5b0a4722 153 }
ed34e3c3
A
154 goto out_bad;
155 }
156
157 if (listen(fd, SOMAXCONN) == -1) {
eabd1701 158 launchd_syslog(LOG_ERR, "listen(\"thesocket\"): %s", strerror(errno));
ed34e3c3
A
159 goto out_bad;
160 }
161
5b0a4722 162 if (kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &kqipc_listen_callback) == -1) {
eabd1701 163 launchd_syslog(LOG_ERR, "kevent_mod(\"thesocket\", EVFILT_READ): %s", strerror(errno));
ed34e3c3
A
164 goto out_bad;
165 }
166
167 ipc_inited = true;
168
5b0a4722
A
169 sockdir = strdup(ourdir);
170 sockpath = strdup(sun.sun_path);
171 ipc_self = getpid();
172 atexit(ipc_clean_up);
ed34e3c3
A
173
174out_bad:
5b0a4722 175 if (!ipc_inited && fd != -1) {
eabd1701 176 (void)runtime_close(fd);
5b0a4722 177 }
ed34e3c3
A
178}
179
180void
5b0a4722 181ipc_open(int fd, job_t j)
ed34e3c3
A
182{
183 struct conncb *c = calloc(1, sizeof(struct conncb));
184
185 fcntl(fd, F_SETFL, O_NONBLOCK);
186
187 c->kqconn_callback = ipc_callback;
dcace88f 188 if (j) {
ddbbfbc1
A
189 c->conn = launchd_fdopen(-1, fd);
190 } else {
191 c->conn = launchd_fdopen(fd, -1);
192 }
eabd1701 193
ed34e3c3 194 c->j = j;
5b0a4722 195 LIST_INSERT_HEAD(&connections, c, sle);
ed34e3c3
A
196 kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &c->kqconn_callback);
197}
198
199void
200ipc_listen_callback(void *obj __attribute__((unused)), struct kevent *kev)
201{
202 struct sockaddr_un sun;
203 socklen_t sl = sizeof(sun);
204 int cfd;
205
206 if ((cfd = _fd(accept(kev->ident, (struct sockaddr *)&sun, &sl))) == -1) {
207 return;
208 }
209
210 ipc_open(cfd, NULL);
211}
212
213void
214ipc_callback(void *obj, struct kevent *kev)
215{
216 struct conncb *c = obj;
217 int r;
eabd1701 218
ed34e3c3
A
219 if (kev->filter == EVFILT_READ) {
220 if (launchd_msg_recv(c->conn, ipc_readmsg, c) == -1 && errno != EAGAIN) {
5b0a4722 221 if (errno != ECONNRESET) {
eabd1701 222 launchd_syslog(LOG_DEBUG, "%s(): recv: %s", __func__, strerror(errno));
5b0a4722 223 }
ed34e3c3
A
224 ipc_close(c);
225 }
226 } else if (kev->filter == EVFILT_WRITE) {
227 r = launchd_msg_send(c->conn, NULL);
228 if (r == -1) {
229 if (errno != EAGAIN) {
eabd1701 230 launchd_syslog(LOG_DEBUG, "%s(): send: %s", __func__, strerror(errno));
ed34e3c3
A
231 ipc_close(c);
232 }
233 } else if (r == 0) {
234 kevent_mod(launchd_getfd(c->conn), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
235 }
236 } else {
eabd1701 237 launchd_syslog(LOG_DEBUG, "%s(): unknown filter type!", __func__);
ed34e3c3
A
238 ipc_close(c);
239 }
240}
241
ddbbfbc1
A
242static void
243set_user_env(launch_data_t obj, const char *key, void *context __attribute__((unused)))
ed34e3c3 244{
ddbbfbc1 245 const char *v = launch_data_get_string(obj);
dcace88f 246 if (v) {
ddbbfbc1
A
247 setenv(key, v, 1);
248 } else {
eabd1701 249 launchd_syslog(LOG_WARNING, "Attempt to set NULL environment variable: %s (type = %d)", key, launch_data_get_type(obj));
ddbbfbc1 250 }
ed34e3c3
A
251}
252
5b0a4722
A
253void
254ipc_close_all_with_job(job_t j)
255{
256 struct conncb *ci, *cin;
257
258 LIST_FOREACH_SAFE(ci, &connections, sle, cin) {
259 if (ci->j == j) {
260 ipc_close(ci);
261 }
262 }
263}
264
ed34e3c3
A
265void
266ipc_close_fds(launch_data_t o)
267{
268 size_t i;
269
270 switch (launch_data_get_type(o)) {
271 case LAUNCH_DATA_DICTIONARY:
272 launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))ipc_close_fds, NULL);
273 break;
274 case LAUNCH_DATA_ARRAY:
275 for (i = 0; i < launch_data_array_get_count(o); i++)
276 ipc_close_fds(launch_data_array_get_index(o, i));
277 break;
278 case LAUNCH_DATA_FD:
5b0a4722 279 if (launch_data_get_fd(o) != -1) {
eabd1701 280 (void)runtime_close(launch_data_get_fd(o));
5b0a4722 281 }
ed34e3c3
A
282 break;
283 default:
284 break;
285 }
286}
287
288void
289ipc_revoke_fds(launch_data_t o)
290{
291 size_t i;
292
293 switch (launch_data_get_type(o)) {
294 case LAUNCH_DATA_DICTIONARY:
295 launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))ipc_revoke_fds, NULL);
296 break;
297 case LAUNCH_DATA_ARRAY:
298 for (i = 0; i < launch_data_array_get_count(o); i++)
299 ipc_revoke_fds(launch_data_array_get_index(o, i));
300 break;
301 case LAUNCH_DATA_FD:
302 launch_data_set_fd(o, -1);
303 break;
304 default:
305 break;
306 }
307}
308
309struct readmsg_context {
310 struct conncb *c;
311 launch_data_t resp;
312};
313
314void
315ipc_readmsg(launch_data_t msg, void *context)
316{
317 struct readmsg_context rmc = { context, NULL };
318
319 if (LAUNCH_DATA_DICTIONARY == launch_data_get_type(msg)) {
320 launch_data_dict_iterate(msg, ipc_readmsg2, &rmc);
321 } else if (LAUNCH_DATA_STRING == launch_data_get_type(msg)) {
322 ipc_readmsg2(NULL, launch_data_get_string(msg), &rmc);
323 } else {
324 rmc.resp = launch_data_new_errno(EINVAL);
325 }
326
5b0a4722 327 if (NULL == rmc.resp) {
ed34e3c3 328 rmc.resp = launch_data_new_errno(ENOSYS);
5b0a4722 329 }
ed34e3c3
A
330
331 ipc_close_fds(msg);
332
333 if (launchd_msg_send(rmc.c->conn, rmc.resp) == -1) {
334 if (errno == EAGAIN) {
335 kevent_mod(launchd_getfd(rmc.c->conn), EVFILT_WRITE, EV_ADD, 0, 0, &rmc.c->kqconn_callback);
336 } else {
eabd1701 337 launchd_syslog(LOG_DEBUG, "launchd_msg_send() == -1: %s", strerror(errno));
ed34e3c3
A
338 ipc_close(rmc.c);
339 }
340 }
341 launch_data_free(rmc.resp);
342}
343
ed34e3c3
A
344void
345ipc_readmsg2(launch_data_t data, const char *cmd, void *context)
346{
347 struct readmsg_context *rmc = context;
348 launch_data_t resp = NULL;
5b0a4722 349 job_t j;
ed34e3c3 350
5b0a4722 351 if (rmc->resp) {
ed34e3c3 352 return;
5b0a4722
A
353 }
354
ddbbfbc1 355 /* Do not allow commands other than check-in to come over the trusted socket
eabd1701
A
356 * on the Desktop. On Embedded, allow all commands over the trusted socket
357 * if the job has the God Mode key set.
ddbbfbc1
A
358 */
359#if TARGET_OS_EMBEDDED
eabd1701 360 bool allow_privileged_ops = (!rmc->c->j || job_is_god(rmc->c->j));
ddbbfbc1
A
361#else
362 bool allow_privileged_ops = !rmc->c->j;
363#endif
eabd1701 364
dcace88f 365 if (rmc->c->j && strcmp(cmd, LAUNCH_KEY_CHECKIN) == 0) {
ddbbfbc1
A
366 resp = job_export(rmc->c->j);
367 job_checkin(rmc->c->j);
dcace88f 368 } else if (allow_privileged_ops) {
eabd1701
A
369#if TARGET_OS_EMBEDDED
370 launchd_embedded_handofgod = rmc->c->j && job_is_god(rmc->c->j);
371#endif
dcace88f 372 if (data == NULL) {
ddbbfbc1
A
373 if (!strcmp(cmd, LAUNCH_KEY_SHUTDOWN)) {
374 launchd_shutdown();
375 resp = launch_data_new_errno(0);
ddbbfbc1
A
376 } else if (!strcmp(cmd, LAUNCH_KEY_GETJOBS)) {
377 resp = job_export_all();
378 ipc_revoke_fds(resp);
379 } else if (!strcmp(cmd, LAUNCH_KEY_GETRESOURCELIMITS)) {
380 resp = adjust_rlimits(NULL);
381 } else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGESELF)) {
382 struct rusage rusage;
383 getrusage(RUSAGE_SELF, &rusage);
384 resp = launch_data_new_opaque(&rusage, sizeof(rusage));
385 } else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGECHILDREN)) {
386 struct rusage rusage;
387 getrusage(RUSAGE_CHILDREN, &rusage);
388 resp = launch_data_new_opaque(&rusage, sizeof(rusage));
ed34e3c3 389 }
ed34e3c3 390 } else {
ddbbfbc1 391 if (!strcmp(cmd, LAUNCH_KEY_STARTJOB)) {
dcace88f 392 if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
ddbbfbc1
A
393 errno = job_dispatch(j, true) ? 0 : errno;
394 }
395 resp = launch_data_new_errno(errno);
396 } else if (!strcmp(cmd, LAUNCH_KEY_STOPJOB)) {
dcace88f 397 if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
ddbbfbc1
A
398 errno = 0;
399 job_stop(j);
400 }
401 resp = launch_data_new_errno(errno);
402 } else if (!strcmp(cmd, LAUNCH_KEY_REMOVEJOB)) {
dcace88f 403 if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
ddbbfbc1
A
404 errno = 0;
405 job_remove(j);
406 }
407 resp = launch_data_new_errno(errno);
408 } else if (!strcmp(cmd, LAUNCH_KEY_SUBMITJOB)) {
409 if (launch_data_get_type(data) == LAUNCH_DATA_ARRAY) {
410 resp = job_import_bulk(data);
411 } else {
412 if (job_import(data)) {
413 errno = 0;
414 }
415 resp = launch_data_new_errno(errno);
416 }
417 } else if (!strcmp(cmd, LAUNCH_KEY_UNSETUSERENVIRONMENT)) {
418 unsetenv(launch_data_get_string(data));
419 resp = launch_data_new_errno(0);
420 } else if (!strcmp(cmd, LAUNCH_KEY_SETUSERENVIRONMENT)) {
421 launch_data_dict_iterate(data, set_user_env, NULL);
422 resp = launch_data_new_errno(0);
423 } else if (!strcmp(cmd, LAUNCH_KEY_SETRESOURCELIMITS)) {
424 resp = adjust_rlimits(data);
425 } else if (!strcmp(cmd, LAUNCH_KEY_GETJOB)) {
dcace88f 426 if ((j = job_find(NULL, launch_data_get_string(data))) == NULL) {
ddbbfbc1
A
427 resp = launch_data_new_errno(errno);
428 } else {
429 resp = job_export(j);
430 ipc_revoke_fds(resp);
431 }
dcace88f 432 } else if (!strcmp(cmd, LAUNCH_KEY_SETPRIORITYLIST)) {
eabd1701 433#if TARGET_OS_EMBEDDED
ddbbfbc1 434 resp = launch_data_new_errno(launchd_set_jetsam_priorities(data));
eabd1701
A
435#else
436 resp = launch_data_new_errno(ENOTSUP);
437#endif
5b0a4722 438 }
ed34e3c3 439 }
eabd1701
A
440#if TARGET_OS_EMBEDDED
441 launchd_embedded_handofgod = false;
442#endif
ddbbfbc1
A
443 } else {
444 resp = launch_data_new_errno(EACCES);
ed34e3c3
A
445 }
446
447 rmc->resp = resp;
448}
449
ddbbfbc1
A
450static int
451close_abi_fixup(int fd)
452{
453 return runtime_close(fd);
454}
455
ed34e3c3
A
456void
457ipc_close(struct conncb *c)
458{
5b0a4722 459 LIST_REMOVE(c, sle);
ddbbfbc1 460 launchd_close(c->conn, close_abi_fixup);
ed34e3c3
A
461 free(c);
462}
463
464launch_data_t
465adjust_rlimits(launch_data_t in)
466{
eabd1701
A
467 /* If I never have to deal with this rlimit nonsense again, I'll be a very
468 * happy man.
469 */
ed34e3c3
A
470 struct rlimit l[RLIM_NLIMITS];
471 struct rlimit *ltmp;
472 size_t i,ltmpsz;
473
474 for (i = 0; i < RLIM_NLIMITS; i++) {
eabd1701 475 (void)posix_assumes_zero(getrlimit(i, l + i));
ed34e3c3
A
476 }
477
478 if (in) {
479 ltmp = launch_data_get_opaque(in);
480 ltmpsz = launch_data_get_opaque_size(in);
481
482 if (ltmpsz > sizeof(l)) {
eabd1701 483 launchd_syslog(LOG_WARNING, "Too much rlimit data sent!");
ed34e3c3
A
484 ltmpsz = sizeof(l);
485 }
eabd1701 486
ed34e3c3 487 for (i = 0; i < (ltmpsz / sizeof(struct rlimit)); i++) {
5b0a4722 488 if (ltmp[i].rlim_cur == l[i].rlim_cur && ltmp[i].rlim_max == l[i].rlim_max) {
ed34e3c3 489 continue;
5b0a4722 490 }
ed34e3c3 491
ddbbfbc1 492 if (/* XXX readcfg_pid && */ pid1_magic && (i == RLIMIT_NOFILE || i == RLIMIT_NPROC)) {
ed34e3c3
A
493 int gmib[] = { CTL_KERN, KERN_MAXPROC };
494 int pmib[] = { CTL_KERN, KERN_MAXPROCPERUID };
495 const char *gstr = "kern.maxproc";
496 const char *pstr = "kern.maxprocperuid";
497 int gval = ltmp[i].rlim_max;
498 int pval = ltmp[i].rlim_cur;
499 switch (i) {
500 case RLIMIT_NOFILE:
501 gmib[1] = KERN_MAXFILES;
502 pmib[1] = KERN_MAXFILESPERPROC;
503 gstr = "kern.maxfiles";
504 pstr = "kern.maxfilesperproc";
505 break;
ed34e3c3
A
506 default:
507 break;
508 }
509
510 if (gval > 0) {
eabd1701 511 (void)posix_assumes_zero(sysctl(gmib, 2, NULL, NULL, &gval, sizeof(gval)));
ed34e3c3 512 } else {
eabd1701 513 launchd_syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", gstr);
ed34e3c3
A
514 }
515 if (pval > 0) {
eabd1701 516 (void)posix_assumes_zero(sysctl(pmib, 2, NULL, NULL, &pval, sizeof(pval)));
ed34e3c3 517 } else {
eabd1701 518 launchd_syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", pstr);
ed34e3c3
A
519 }
520 }
eabd1701 521 (void)posix_assumes_zero(setrlimit(i, ltmp + i));
ed34e3c3 522 /* the kernel may have clamped the values we gave it */
eabd1701 523 (void)posix_assumes_zero(getrlimit(i, l + i));
ed34e3c3
A
524 }
525 }
526
527 return launch_data_new_opaque(l, sizeof(struct rlimit) * RLIM_NLIMITS);
528}