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