2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_APACHE_LICENSE_HEADER_START@
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * @APPLE_APACHE_LICENSE_HEADER_END@
21 static const char *const __rcs_file_version__
= "$Revision: 1.11 $";
23 #include <sys/types.h>
24 #include <sys/queue.h>
25 #include <sys/event.h>
27 #include <sys/ucred.h>
28 #include <sys/fcntl.h>
31 #include <sys/sysctl.h>
32 #include <sys/sockio.h>
34 #include <sys/resource.h>
35 #include <sys/ioctl.h>
49 #include "launch_priv.h"
51 #include "launchd_core_logic.h"
52 #include "launchd_unix_ipc.h"
54 extern char **environ
;
56 static SLIST_HEAD(, conncb
) connections
= { NULL
};
58 static launch_data_t
adjust_rlimits(launch_data_t in
);
60 static void ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
);
62 static void ipc_listen_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
);
64 static kq_callback kqipc_listen_callback
= ipc_listen_callback
;
66 static pid_t ipc_self
= 0;
68 char *sockpath
= NULL
;
69 static char *sockdir
= NULL
;
71 static bool ipc_inited
= false;
76 if (ipc_self
!= getpid())
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
);
86 ipc_server_init(int *fds
, size_t fd_cnt
)
88 struct sockaddr_un sun
;
100 memset(&sun
, 0, sizeof(sun
));
101 sun
.sun_family
= AF_UNIX
;
104 strcpy(ourdir
, LAUNCHD_SOCK_PREFIX
);
105 strncpy(sun
.sun_path
, LAUNCHD_SOCK_PREFIX
"/sock", sizeof(sun
.sun_path
));
108 if (mkdir(ourdir
, S_IRWXU
) == -1) {
109 if (errno
== EROFS
) {
111 } else if (errno
== EEXIST
) {
114 if (!S_ISDIR(sb
.st_mode
)) {
116 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
120 syslog(LOG_ERR
, "mkdir(\"%s\"): %m", ourdir
);
125 snprintf(ourdir
, sizeof(ourdir
), "/tmp/launchd-%u.XXXXXX", getpid());
126 if (!launchd_assumes(mkdtemp(ourdir
) != NULL
))
128 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/sock", ourdir
);
129 setenv(LAUNCHD_SOCKET_ENV
, sun
.sun_path
, 1);
132 if (unlink(sun
.sun_path
) == -1 && errno
!= ENOENT
) {
134 syslog(LOG_ERR
, "unlink(\"thesocket\"): %m");
138 if (!launchd_assumes((fd
= _fd(socket(AF_UNIX
, SOCK_STREAM
, 0))) != -1))
141 oldmask
= umask(S_IRWXG
|S_IRWXO
);
142 r
= bind(fd
, (struct sockaddr
*)&sun
, sizeof(sun
));
147 syslog(LOG_ERR
, "bind(\"thesocket\"): %m");
151 if (listen(fd
, SOMAXCONN
) == -1) {
152 syslog(LOG_ERR
, "listen(\"thesocket\"): %m");
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
]);
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");
172 sockdir
= strdup(ourdir
);
173 sockpath
= strdup(sun
.sun_path
);
175 atexit(ipc_clean_up
);
179 if (!ipc_inited
&& fd
!= -1)
180 launchd_assumes(close(fd
) == 0);
184 ipc_open(int fd
, struct jobcb
*j
)
186 struct conncb
*c
= calloc(1, sizeof(struct conncb
));
188 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
190 c
->kqconn_callback
= ipc_callback
;
191 c
->conn
= launchd_fdopen(fd
);
193 SLIST_INSERT_HEAD(&connections
, c
, sle
);
194 kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &c
->kqconn_callback
);
198 ipc_listen_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
200 struct sockaddr_un sun
;
201 socklen_t sl
= sizeof(sun
);
204 if ((cfd
= _fd(accept(kev
->ident
, (struct sockaddr
*)&sun
, &sl
))) == -1) {
212 ipc_callback(void *obj
, struct kevent
*kev
)
214 struct conncb
*c
= obj
;
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__
);
223 } else if (kev
->filter
== EVFILT_WRITE
) {
224 r
= launchd_msg_send(c
->conn
, NULL
);
226 if (errno
!= EAGAIN
) {
227 syslog(LOG_DEBUG
, "%s(): send: %m", __func__
);
231 kevent_mod(launchd_getfd(c
->conn
), EVFILT_WRITE
, EV_DELETE
, 0, 0, NULL
);
234 syslog(LOG_DEBUG
, "%s(): unknown filter type!", __func__
);
239 static void set_user_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
241 setenv(key
, launch_data_get_string(obj
), 1);
245 ipc_close_fds(launch_data_t o
)
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
);
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
));
258 if (launch_data_get_fd(o
) != -1)
259 launchd_assumes(close(launch_data_get_fd(o
)) == 0);
267 ipc_revoke_fds(launch_data_t o
)
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
);
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
));
280 launch_data_set_fd(o
, -1);
287 struct readmsg_context
{
293 ipc_readmsg(launch_data_t msg
, void *context
)
295 struct readmsg_context rmc
= { context
, NULL
};
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
);
302 rmc
.resp
= launch_data_new_errno(EINVAL
);
305 if (NULL
== rmc
.resp
)
306 rmc
.resp
= launch_data_new_errno(ENOSYS
);
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
);
314 syslog(LOG_DEBUG
, "launchd_msg_send() == -1: %m");
318 launch_data_free(rmc
.resp
);
323 ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
)
325 struct readmsg_context
*rmc
= context
;
326 launch_data_t resp
= NULL
;
333 if (!strcmp(cmd
, LAUNCH_KEY_CHECKIN
)) {
335 resp
= job_export(rmc
->c
->j
);
336 job_checkin(rmc
->c
->j
);
338 resp
= launch_data_new_errno(EACCES
);
340 } else if (!strcmp(cmd
, LAUNCH_KEY_RELOADTTYS
)) {
342 resp
= launch_data_new_errno(0);
343 } else if (!strcmp(cmd
, LAUNCH_KEY_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
++) {
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
);
365 } else if (!strcmp(cmd
, LAUNCH_KEY_GETLOGMASK
)) {
366 int oldmask
= setlogmask(LOG_UPTO(LOG_DEBUG
));
367 resp
= launch_data_new_integer(oldmask
);
369 } else if (!strcmp(cmd
, LAUNCH_KEY_GETUMASK
)) {
370 mode_t oldmask
= umask(0);
371 resp
= launch_data_new_integer(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);
385 } else if (!strcmp(cmd
, LAUNCH_KEY_STARTJOB
)) {
386 if ((j
= job_find(root_job
, launch_data_get_string(data
))) != NULL
) {
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
) {
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
) {
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
);
407 if (job_import(data
))
409 resp
= launch_data_new_errno(errno
);
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
);
423 resp
= job_export(j
);
424 ipc_revoke_fds(resp
);
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
);
430 resp
= job_export(j
);
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);
449 ipc_close(struct conncb
*c
)
451 batch_job_enable(true, c
);
453 SLIST_REMOVE(&connections
, c
, conncb
, sle
);
454 launchd_close(c
->conn
);
459 adjust_rlimits(launch_data_t in
)
461 struct rlimit l
[RLIM_NLIMITS
];
465 for (i
= 0; i
< RLIM_NLIMITS
; i
++) {
466 launchd_assumes(getrlimit(i
, l
+ i
) != -1);
470 ltmp
= launch_data_get_opaque(in
);
471 ltmpsz
= launch_data_get_opaque_size(in
);
473 if (ltmpsz
> sizeof(l
)) {
474 syslog(LOG_WARNING
, "Too much rlimit data sent!");
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
)
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
;
491 gmib
[1] = KERN_MAXFILES
;
492 pmib
[1] = KERN_MAXFILESPERPROC
;
493 gstr
= "kern.maxfiles";
494 pstr
= "kern.maxfilesperproc";
497 /* kernel will not clamp to this value, we must */
498 if (gval
> (2048 + 20))
506 launchd_assumes(sysctl(gmib
, 2, NULL
, NULL
, &gval
, sizeof(gval
)) != -1);
508 syslog(LOG_WARNING
, "sysctl(\"%s\"): can't be zero", gstr
);
511 launchd_assumes(sysctl(pmib
, 2, NULL
, NULL
, &pval
, sizeof(pval
)) != -1);
513 syslog(LOG_WARNING
, "sysctl(\"%s\"): can't be zero", pstr
);
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);
522 return launch_data_new_opaque(l
, sizeof(struct rlimit
) * RLIM_NLIMITS
);