]> git.saurik.com Git - apple/launchd.git/blame_incremental - src/ipc.c
launchd-442.26.2.tar.gz
[apple/launchd.git] / src / ipc.c
... / ...
CommitLineData
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/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>
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 <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
56extern char **environ;
57
58static LIST_HEAD(, conncb) connections;
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);
63static void ipc_readmsg(launch_data_t msg, void *context);
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
76static void
77ipc_clean_up(void)
78{
79 if (ipc_self != getpid()) {
80 return;
81 }
82
83 if (-1 == unlink(sockpath)) {
84 launchd_syslog(LOG_WARNING, "unlink(\"%s\"): %s", sockpath, strerror(errno));
85 } else if (-1 == rmdir(sockdir)) {
86 launchd_syslog(LOG_WARNING, "rmdir(\"%s\"): %s", sockdir, strerror(errno));
87 }
88}
89
90void
91ipc_server_init(void)
92{
93 struct sockaddr_un sun;
94 mode_t oldmask;
95 int r, fd = -1;
96 char ourdir[1024];
97
98 if (ipc_inited) {
99 return;
100 }
101
102 memset(&sun, 0, sizeof(sun));
103 sun.sun_family = AF_UNIX;
104
105 if (pid1_magic) {
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;
118 launchd_syslog(LOG_ERR, "mkdir(\"%s\"): %s", LAUNCHD_SOCK_PREFIX, strerror(errno));
119 goto out_bad;
120 }
121 } else {
122 launchd_syslog(LOG_ERR, "mkdir(\"%s\"): %s", ourdir, strerror(errno));
123 goto out_bad;
124 }
125 }
126 } else {
127 snprintf(ourdir, sizeof(ourdir), _PATH_TMP "launchd-%u.XXXXXX", getpid());
128 if (mkdtemp(ourdir) == NULL) {
129 launchd_syslog(LOG_ERR, "Could not create critical directory \"%s\": %s", ourdir, strerror(errno));
130 goto out_bad;
131 }
132 snprintf(sun.sun_path, sizeof(sun.sun_path), "%s/sock", ourdir);
133 }
134
135 if (unlink(sun.sun_path) == -1 && errno != ENOENT) {
136 if (errno != EROFS) {
137 launchd_syslog(LOG_ERR, "unlink(\"thesocket\"): %s", strerror(errno));
138 }
139 goto out_bad;
140 }
141
142 if (posix_assumes_zero(fd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1) {
143 goto out_bad;
144 }
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) {
151 if (errno != EROFS) {
152 launchd_syslog(LOG_ERR, "bind(\"thesocket\"): %s", strerror(errno));
153 }
154 goto out_bad;
155 }
156
157 if (listen(fd, SOMAXCONN) == -1) {
158 launchd_syslog(LOG_ERR, "listen(\"thesocket\"): %s", strerror(errno));
159 goto out_bad;
160 }
161
162 if (kevent_mod(fd, EVFILT_READ, EV_ADD, 0, 0, &kqipc_listen_callback) == -1) {
163 launchd_syslog(LOG_ERR, "kevent_mod(\"thesocket\", EVFILT_READ): %s", strerror(errno));
164 goto out_bad;
165 }
166
167 ipc_inited = true;
168
169 sockdir = strdup(ourdir);
170 sockpath = strdup(sun.sun_path);
171 ipc_self = getpid();
172 atexit(ipc_clean_up);
173
174out_bad:
175 if (!ipc_inited && fd != -1) {
176 (void)runtime_close(fd);
177 }
178}
179
180void
181ipc_open(int fd, job_t j)
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;
188 if (j) {
189 c->conn = launchd_fdopen(-1, fd);
190 } else {
191 c->conn = launchd_fdopen(fd, -1);
192 }
193
194 c->j = j;
195 LIST_INSERT_HEAD(&connections, c, sle);
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;
218
219 if (kev->filter == EVFILT_READ) {
220 if (launchd_msg_recv(c->conn, ipc_readmsg, c) == -1 && errno != EAGAIN) {
221 if (errno != ECONNRESET) {
222 launchd_syslog(LOG_DEBUG, "%s(): recv: %s", __func__, strerror(errno));
223 }
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) {
230 launchd_syslog(LOG_DEBUG, "%s(): send: %s", __func__, strerror(errno));
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 {
237 launchd_syslog(LOG_DEBUG, "%s(): unknown filter type!", __func__);
238 ipc_close(c);
239 }
240}
241
242static void
243set_user_env(launch_data_t obj, const char *key, void *context __attribute__((unused)))
244{
245 const char *v = launch_data_get_string(obj);
246 if (v) {
247 setenv(key, v, 1);
248 } else {
249 launchd_syslog(LOG_WARNING, "Attempt to set NULL environment variable: %s (type = %d)", key, launch_data_get_type(obj));
250 }
251}
252
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
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:
279 if (launch_data_get_fd(o) != -1) {
280 (void)runtime_close(launch_data_get_fd(o));
281 }
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
327 if (NULL == rmc.resp) {
328 rmc.resp = launch_data_new_errno(ENOSYS);
329 }
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 {
337 launchd_syslog(LOG_DEBUG, "launchd_msg_send() == -1: %s", strerror(errno));
338 ipc_close(rmc.c);
339 }
340 }
341 launch_data_free(rmc.resp);
342}
343
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;
349 job_t j;
350
351 if (rmc->resp) {
352 return;
353 }
354
355 /* Do not allow commands other than check-in to come over the trusted socket
356 * on the Desktop. On Embedded, allow all commands over the trusted socket
357 * if the job has the God Mode key set.
358 */
359#if TARGET_OS_EMBEDDED
360 bool allow_privileged_ops = (!rmc->c->j || job_is_god(rmc->c->j));
361#else
362 bool allow_privileged_ops = !rmc->c->j;
363#endif
364
365 if (rmc->c->j && strcmp(cmd, LAUNCH_KEY_CHECKIN) == 0) {
366 resp = job_export(rmc->c->j);
367 job_checkin(rmc->c->j);
368 } else if (allow_privileged_ops) {
369#if TARGET_OS_EMBEDDED
370 launchd_embedded_handofgod = rmc->c->j && job_is_god(rmc->c->j);
371#endif
372 if (data == NULL) {
373 if (!strcmp(cmd, LAUNCH_KEY_SHUTDOWN)) {
374 launchd_shutdown();
375 resp = launch_data_new_errno(0);
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));
389 }
390 } else {
391 if (!strcmp(cmd, LAUNCH_KEY_STARTJOB)) {
392 if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
393 errno = job_dispatch(j, true) ? 0 : errno;
394 }
395 resp = launch_data_new_errno(errno);
396 } else if (!strcmp(cmd, LAUNCH_KEY_STOPJOB)) {
397 if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
398 errno = 0;
399 job_stop(j);
400 }
401 resp = launch_data_new_errno(errno);
402 } else if (!strcmp(cmd, LAUNCH_KEY_REMOVEJOB)) {
403 if ((j = job_find(NULL, launch_data_get_string(data))) != NULL) {
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)) {
426 if ((j = job_find(NULL, launch_data_get_string(data))) == NULL) {
427 resp = launch_data_new_errno(errno);
428 } else {
429 resp = job_export(j);
430 ipc_revoke_fds(resp);
431 }
432 } else if (!strcmp(cmd, LAUNCH_KEY_SETPRIORITYLIST)) {
433#if TARGET_OS_EMBEDDED
434 resp = launch_data_new_errno(launchd_set_jetsam_priorities(data));
435#else
436 resp = launch_data_new_errno(ENOTSUP);
437#endif
438 }
439 }
440#if TARGET_OS_EMBEDDED
441 launchd_embedded_handofgod = false;
442#endif
443 } else {
444 resp = launch_data_new_errno(EACCES);
445 }
446
447 rmc->resp = resp;
448}
449
450static int
451close_abi_fixup(int fd)
452{
453 return runtime_close(fd);
454}
455
456void
457ipc_close(struct conncb *c)
458{
459 LIST_REMOVE(c, sle);
460 launchd_close(c->conn, close_abi_fixup);
461 free(c);
462}
463
464launch_data_t
465adjust_rlimits(launch_data_t in)
466{
467 /* If I never have to deal with this rlimit nonsense again, I'll be a very
468 * happy man.
469 */
470 struct rlimit l[RLIM_NLIMITS];
471 struct rlimit *ltmp;
472 size_t i,ltmpsz;
473
474 for (i = 0; i < RLIM_NLIMITS; i++) {
475 (void)posix_assumes_zero(getrlimit(i, l + i));
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)) {
483 launchd_syslog(LOG_WARNING, "Too much rlimit data sent!");
484 ltmpsz = sizeof(l);
485 }
486
487 for (i = 0; i < (ltmpsz / sizeof(struct rlimit)); i++) {
488 if (ltmp[i].rlim_cur == l[i].rlim_cur && ltmp[i].rlim_max == l[i].rlim_max) {
489 continue;
490 }
491
492 if (/* XXX readcfg_pid && */ pid1_magic && (i == RLIMIT_NOFILE || i == RLIMIT_NPROC)) {
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;
506 default:
507 break;
508 }
509
510 if (gval > 0) {
511 (void)posix_assumes_zero(sysctl(gmib, 2, NULL, NULL, &gval, sizeof(gval)));
512 } else {
513 launchd_syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", gstr);
514 }
515 if (pval > 0) {
516 (void)posix_assumes_zero(sysctl(pmib, 2, NULL, NULL, &pval, sizeof(pval)));
517 } else {
518 launchd_syslog(LOG_WARNING, "sysctl(\"%s\"): can't be zero", pstr);
519 }
520 }
521 (void)posix_assumes_zero(setrlimit(i, ltmp + i));
522 /* the kernel may have clamped the values we gave it */
523 (void)posix_assumes_zero(getrlimit(i, l + i));
524 }
525 }
526
527 return launch_data_new_opaque(l, sizeof(struct rlimit) * RLIM_NLIMITS);
528}