]> git.saurik.com Git - apple/launchd.git/blame - src/ipc.c
launchd-842.92.1.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>
95379394 48#include <os/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
95379394
A
210 if (geteuid() == 0) {
211 uid_t euid, guid;
212 if (getpeereid(cfd, &euid, &guid) == -1) {
213 launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[%d] failed to getpeereid on incoming caller (%d)", getpid(), errno);
214 (void)runtime_close(cfd);
215 return;
216 }
217
218 if (euid != geteuid()) {
219 launchd_syslog(LOG_NOTICE | LOG_CONSOLE, "*** launchd[%d] failed to euid check on incoming caller (%d != %d)", getpid(), euid, geteuid());
220 (void)runtime_close(cfd);
221 return;
222 }
223 }
224
ed34e3c3
A
225 ipc_open(cfd, NULL);
226}
227
228void
229ipc_callback(void *obj, struct kevent *kev)
230{
231 struct conncb *c = obj;
232 int r;
eabd1701 233
ed34e3c3
A
234 if (kev->filter == EVFILT_READ) {
235 if (launchd_msg_recv(c->conn, ipc_readmsg, c) == -1 && errno != EAGAIN) {
5b0a4722 236 if (errno != ECONNRESET) {
eabd1701 237 launchd_syslog(LOG_DEBUG, "%s(): recv: %s", __func__, strerror(errno));
5b0a4722 238 }
ed34e3c3
A
239 ipc_close(c);
240 }
241 } else if (kev->filter == EVFILT_WRITE) {
242 r = launchd_msg_send(c->conn, NULL);
243 if (r == -1) {
244 if (errno != EAGAIN) {
eabd1701 245 launchd_syslog(LOG_DEBUG, "%s(): send: %s", __func__, strerror(errno));
ed34e3c3
A
246 ipc_close(c);
247 }
248 } else if (r == 0) {
249 kevent_mod(launchd_getfd(c->conn), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
250 }
251 } else {
eabd1701 252 launchd_syslog(LOG_DEBUG, "%s(): unknown filter type!", __func__);
ed34e3c3
A
253 ipc_close(c);
254 }
255}
256
ddbbfbc1
A
257static void
258set_user_env(launch_data_t obj, const char *key, void *context __attribute__((unused)))
ed34e3c3 259{
ddbbfbc1 260 const char *v = launch_data_get_string(obj);
dcace88f 261 if (v) {
ddbbfbc1
A
262 setenv(key, v, 1);
263 } else {
eabd1701 264 launchd_syslog(LOG_WARNING, "Attempt to set NULL environment variable: %s (type = %d)", key, launch_data_get_type(obj));
ddbbfbc1 265 }
ed34e3c3
A
266}
267
5b0a4722
A
268void
269ipc_close_all_with_job(job_t j)
270{
271 struct conncb *ci, *cin;
272
273 LIST_FOREACH_SAFE(ci, &connections, sle, cin) {
274 if (ci->j == j) {
275 ipc_close(ci);
276 }
277 }
278}
279
ed34e3c3
A
280void
281ipc_close_fds(launch_data_t o)
282{
283 size_t i;
284
285 switch (launch_data_get_type(o)) {
286 case LAUNCH_DATA_DICTIONARY:
287 launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))ipc_close_fds, NULL);
288 break;
289 case LAUNCH_DATA_ARRAY:
290 for (i = 0; i < launch_data_array_get_count(o); i++)
291 ipc_close_fds(launch_data_array_get_index(o, i));
292 break;
293 case LAUNCH_DATA_FD:
5b0a4722 294 if (launch_data_get_fd(o) != -1) {
eabd1701 295 (void)runtime_close(launch_data_get_fd(o));
5b0a4722 296 }
ed34e3c3
A
297 break;
298 default:
299 break;
300 }
301}
302
303void
304ipc_revoke_fds(launch_data_t o)
305{
306 size_t i;
307
308 switch (launch_data_get_type(o)) {
309 case LAUNCH_DATA_DICTIONARY:
310 launch_data_dict_iterate(o, (void (*)(launch_data_t, const char *, void *))ipc_revoke_fds, NULL);
311 break;
312 case LAUNCH_DATA_ARRAY:
313 for (i = 0; i < launch_data_array_get_count(o); i++)
314 ipc_revoke_fds(launch_data_array_get_index(o, i));
315 break;
316 case LAUNCH_DATA_FD:
317 launch_data_set_fd(o, -1);
318 break;
319 default:
320 break;
321 }
322}
323
324struct readmsg_context {
325 struct conncb *c;
326 launch_data_t resp;
327};
328
329void
330ipc_readmsg(launch_data_t msg, void *context)
331{
332 struct readmsg_context rmc = { context, NULL };
333
334 if (LAUNCH_DATA_DICTIONARY == launch_data_get_type(msg)) {
335 launch_data_dict_iterate(msg, ipc_readmsg2, &rmc);
336 } else if (LAUNCH_DATA_STRING == launch_data_get_type(msg)) {
337 ipc_readmsg2(NULL, launch_data_get_string(msg), &rmc);
338 } else {
339 rmc.resp = launch_data_new_errno(EINVAL);
340 }
341
5b0a4722 342 if (NULL == rmc.resp) {
ed34e3c3 343 rmc.resp = launch_data_new_errno(ENOSYS);
5b0a4722 344 }
ed34e3c3
A
345
346 ipc_close_fds(msg);
347
348 if (launchd_msg_send(rmc.c->conn, rmc.resp) == -1) {
349 if (errno == EAGAIN) {
350 kevent_mod(launchd_getfd(rmc.c->conn), EVFILT_WRITE, EV_ADD, 0, 0, &rmc.c->kqconn_callback);
351 } else {
eabd1701 352 launchd_syslog(LOG_DEBUG, "launchd_msg_send() == -1: %s", strerror(errno));
ed34e3c3
A
353 ipc_close(rmc.c);
354 }
355 }
356 launch_data_free(rmc.resp);
357}
358
ed34e3c3
A
359void
360ipc_readmsg2(launch_data_t data, const char *cmd, void *context)
361{
362 struct readmsg_context *rmc = context;
363 launch_data_t resp = NULL;
5b0a4722 364 job_t j;
ed34e3c3 365
5b0a4722 366 if (rmc->resp) {
ed34e3c3 367 return;
5b0a4722
A
368 }
369
ddbbfbc1 370 /* Do not allow commands other than check-in to come over the trusted socket
eabd1701
A
371 * on the Desktop. On Embedded, allow all commands over the trusted socket
372 * if the job has the God Mode key set.
ddbbfbc1
A
373 */
374#if TARGET_OS_EMBEDDED
eabd1701 375 bool allow_privileged_ops = (!rmc->c->j || job_is_god(rmc->c->j));
ddbbfbc1
A
376#else
377 bool allow_privileged_ops = !rmc->c->j;
378#endif
eabd1701 379
dcace88f 380 if (rmc->c->j && strcmp(cmd, LAUNCH_KEY_CHECKIN) == 0) {
ddbbfbc1
A
381 resp = job_export(rmc->c->j);
382 job_checkin(rmc->c->j);
dcace88f 383 } else if (allow_privileged_ops) {
eabd1701
A
384#if TARGET_OS_EMBEDDED
385 launchd_embedded_handofgod = rmc->c->j && job_is_god(rmc->c->j);
386#endif
dcace88f 387 if (data == NULL) {
ddbbfbc1
A
388 if (!strcmp(cmd, LAUNCH_KEY_SHUTDOWN)) {
389 launchd_shutdown();
390 resp = launch_data_new_errno(0);
ddbbfbc1
A
391 } else if (!strcmp(cmd, LAUNCH_KEY_GETJOBS)) {
392 resp = job_export_all();
393 ipc_revoke_fds(resp);
394 } else if (!strcmp(cmd, LAUNCH_KEY_GETRESOURCELIMITS)) {
395 resp = adjust_rlimits(NULL);
396 } else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGESELF)) {
397 struct rusage rusage;
398 getrusage(RUSAGE_SELF, &rusage);
399 resp = launch_data_new_opaque(&rusage, sizeof(rusage));
400 } else if (!strcmp(cmd, LAUNCH_KEY_GETRUSAGECHILDREN)) {
401 struct rusage rusage;
402 getrusage(RUSAGE_CHILDREN, &rusage);
403 resp = launch_data_new_opaque(&rusage, sizeof(rusage));
ed34e3c3 404 }
ed34e3c3 405 } else {
ddbbfbc1 406 if (!strcmp(cmd, LAUNCH_KEY_STARTJOB)) {
dcace88f 407 if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
ddbbfbc1
A
408 errno = job_dispatch(j, true) ? 0 : errno;
409 }
410 resp = launch_data_new_errno(errno);
411 } else if (!strcmp(cmd, LAUNCH_KEY_STOPJOB)) {
dcace88f 412 if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
ddbbfbc1
A
413 errno = 0;
414 job_stop(j);
415 }
416 resp = launch_data_new_errno(errno);
417 } else if (!strcmp(cmd, LAUNCH_KEY_REMOVEJOB)) {
dcace88f 418 if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
ddbbfbc1
A
419 errno = 0;
420 job_remove(j);
421 }
422 resp = launch_data_new_errno(errno);
423 } else if (!strcmp(cmd, LAUNCH_KEY_SUBMITJOB)) {
424 if (launch_data_get_type(data) == LAUNCH_DATA_ARRAY) {
425 resp = job_import_bulk(data);
426 } else {
427 if (job_import(data)) {
428 errno = 0;
429 }
430 resp = launch_data_new_errno(errno);
431 }
432 } else if (!strcmp(cmd, LAUNCH_KEY_UNSETUSERENVIRONMENT)) {
433 unsetenv(launch_data_get_string(data));
434 resp = launch_data_new_errno(0);
435 } else if (!strcmp(cmd, LAUNCH_KEY_SETUSERENVIRONMENT)) {
436 launch_data_dict_iterate(data, set_user_env, NULL);
437 resp = launch_data_new_errno(0);
438 } else if (!strcmp(cmd, LAUNCH_KEY_SETRESOURCELIMITS)) {
439 resp = adjust_rlimits(data);
440 } else if (!strcmp(cmd, LAUNCH_KEY_GETJOB)) {
dcace88f 441 if ((j = job_find(NULL, launch_data_get_string(data))) == NULL) {
ddbbfbc1
A
442 resp = launch_data_new_errno(errno);
443 } else {
444 resp = job_export(j);
445 ipc_revoke_fds(resp);
446 }
5b0a4722 447 }
ed34e3c3 448 }
eabd1701
A
449#if TARGET_OS_EMBEDDED
450 launchd_embedded_handofgod = false;
451#endif
ddbbfbc1
A
452 } else {
453 resp = launch_data_new_errno(EACCES);
ed34e3c3
A
454 }
455
456 rmc->resp = resp;
457}
458
ddbbfbc1
A
459static int
460close_abi_fixup(int fd)
461{
462 return runtime_close(fd);
463}
464
ed34e3c3
A
465void
466ipc_close(struct conncb *c)
467{
5b0a4722 468 LIST_REMOVE(c, sle);
ddbbfbc1 469 launchd_close(c->conn, close_abi_fixup);
ed34e3c3
A
470 free(c);
471}
472
473launch_data_t
474adjust_rlimits(launch_data_t in)
475{
eabd1701
A
476 /* If I never have to deal with this rlimit nonsense again, I'll be a very
477 * happy man.
478 */
ed34e3c3
A
479 struct rlimit l[RLIM_NLIMITS];
480 struct rlimit *ltmp;
481 size_t i,ltmpsz;
482
483 for (i = 0; i < RLIM_NLIMITS; i++) {
eabd1701 484 (void)posix_assumes_zero(getrlimit(i, l + i));
ed34e3c3
A
485 }
486
487 if (in) {
488 ltmp = launch_data_get_opaque(in);
489 ltmpsz = launch_data_get_opaque_size(in);
490
491 if (ltmpsz > sizeof(l)) {
eabd1701 492 launchd_syslog(LOG_WARNING, "Too much rlimit data sent!");
ed34e3c3
A
493 ltmpsz = sizeof(l);
494 }
eabd1701 495
ed34e3c3 496 for (i = 0; i < (ltmpsz / sizeof(struct rlimit)); i++) {
5b0a4722 497 if (ltmp[i].rlim_cur == l[i].rlim_cur && ltmp[i].rlim_max == l[i].rlim_max) {
ed34e3c3 498 continue;
5b0a4722 499 }
ed34e3c3 500
ddbbfbc1 501 if (/* XXX readcfg_pid && */ pid1_magic && (i == RLIMIT_NOFILE || i == RLIMIT_NPROC)) {
ed34e3c3
A
502 int gmib[] = { CTL_KERN, KERN_MAXPROC };
503 int pmib[] = { CTL_KERN, KERN_MAXPROCPERUID };
504 const char *gstr = "kern.maxproc";
505 const char *pstr = "kern.maxprocperuid";
506 int gval = ltmp[i].rlim_max;
507 int pval = ltmp[i].rlim_cur;
508 switch (i) {
509 case RLIMIT_NOFILE:
510 gmib[1] = KERN_MAXFILES;
511 pmib[1] = KERN_MAXFILESPERPROC;
512 gstr = "kern.maxfiles";
513 pstr = "kern.maxfilesperproc";
514 break;
ed34e3c3
A
515 default:
516 break;
517 }
518
519 if (gval > 0) {
eabd1701 520 (void)posix_assumes_zero(sysctl(gmib, 2, NULL, NULL, &gval, sizeof(gval)));
ed34e3c3 521 } else {
eabd1701 522 launchd_syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", gstr);
ed34e3c3
A
523 }
524 if (pval > 0) {
eabd1701 525 (void)posix_assumes_zero(sysctl(pmib, 2, NULL, NULL, &pval, sizeof(pval)));
ed34e3c3 526 } else {
eabd1701 527 launchd_syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", pstr);
ed34e3c3
A
528 }
529 }
eabd1701 530 (void)posix_assumes_zero(setrlimit(i, ltmp + i));
ed34e3c3 531 /* the kernel may have clamped the values we gave it */
eabd1701 532 (void)posix_assumes_zero(getrlimit(i, l + i));
ed34e3c3
A
533 }
534 }
535
536 return launch_data_new_opaque(l, sizeof(struct rlimit) * RLIM_NLIMITS);
537}