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