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