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: 24557 $";
24 #include "launchd_unix_ipc.h"
26 #include <sys/socket.h>
27 #include <sys/types.h>
28 #include <sys/queue.h>
29 #include <sys/event.h>
31 #include <sys/ucred.h>
32 #include <sys/fcntl.h>
35 #include <sys/sysctl.h>
36 #include <sys/sockio.h>
38 #include <sys/resource.h>
39 #include <sys/ioctl.h>
52 #include "launch_priv.h"
54 #include "launchd_runtime.h"
55 #include "launchd_core_logic.h"
57 extern char **environ
;
59 static LIST_HEAD(, conncb
) connections
;
61 static launch_data_t
adjust_rlimits(launch_data_t in
);
63 static void ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
);
64 static void ipc_readmsg(launch_data_t msg
, void *context
);
66 static void ipc_listen_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
);
68 static kq_callback kqipc_listen_callback
= ipc_listen_callback
;
70 static pid_t ipc_self
= 0;
72 char *sockpath
= NULL
;
73 static char *sockdir
= NULL
;
75 static bool ipc_inited
= false;
80 if (ipc_self
!= getpid()) {
84 if (-1 == unlink(sockpath
)) {
85 runtime_syslog(LOG_WARNING
, "unlink(\"%s\"): %s", sockpath
, strerror(errno
));
86 } else if (-1 == rmdir(sockdir
)) {
87 runtime_syslog(LOG_WARNING
, "rmdir(\"%s\"): %s", sockdir
, strerror(errno
));
94 struct sockaddr_un sun
;
103 memset(&sun
, 0, sizeof(sun
));
104 sun
.sun_family
= AF_UNIX
;
107 strcpy(ourdir
, LAUNCHD_SOCK_PREFIX
);
108 strncpy(sun
.sun_path
, LAUNCHD_SOCK_PREFIX
"/sock", sizeof(sun
.sun_path
));
111 if (mkdir(ourdir
, S_IRWXU
) == -1) {
112 if (errno
== EROFS
) {
114 } else if (errno
== EEXIST
) {
117 if (!S_ISDIR(sb
.st_mode
)) {
119 runtime_syslog(LOG_ERR
, "mkdir(\"%s\"): %s", LAUNCHD_SOCK_PREFIX
, strerror(errno
));
123 runtime_syslog(LOG_ERR
, "mkdir(\"%s\"): %s", ourdir
, strerror(errno
));
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
));
133 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/sock", ourdir
);
136 if (unlink(sun
.sun_path
) == -1 && errno
!= ENOENT
) {
137 if (errno
!= EROFS
) {
138 runtime_syslog(LOG_ERR
, "unlink(\"thesocket\"): %s", strerror(errno
));
143 if (!launchd_assumes((fd
= _fd(socket(AF_UNIX
, SOCK_STREAM
, 0))) != -1)) {
147 oldmask
= umask(S_IRWXG
|S_IRWXO
);
148 r
= bind(fd
, (struct sockaddr
*)&sun
, sizeof(sun
));
152 if (errno
!= EROFS
) {
153 runtime_syslog(LOG_ERR
, "bind(\"thesocket\"): %s", strerror(errno
));
158 if (listen(fd
, SOMAXCONN
) == -1) {
159 runtime_syslog(LOG_ERR
, "listen(\"thesocket\"): %s", strerror(errno
));
163 if (kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &kqipc_listen_callback
) == -1) {
164 runtime_syslog(LOG_ERR
, "kevent_mod(\"thesocket\", EVFILT_READ): %s", strerror(errno
));
170 sockdir
= strdup(ourdir
);
171 sockpath
= strdup(sun
.sun_path
);
173 atexit(ipc_clean_up
);
176 if (!ipc_inited
&& fd
!= -1) {
177 (void)launchd_assumes(runtime_close(fd
) == 0);
182 ipc_open(int fd
, job_t j
)
184 struct conncb
*c
= calloc(1, sizeof(struct conncb
));
186 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
188 c
->kqconn_callback
= ipc_callback
;
190 c
->conn
= launchd_fdopen(-1, fd
);
192 c
->conn
= launchd_fdopen(fd
, -1);
196 LIST_INSERT_HEAD(&connections
, c
, sle
);
197 kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &c
->kqconn_callback
);
201 ipc_listen_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
203 struct sockaddr_un sun
;
204 socklen_t sl
= sizeof(sun
);
207 if ((cfd
= _fd(accept(kev
->ident
, (struct sockaddr
*)&sun
, &sl
))) == -1) {
215 ipc_callback(void *obj
, struct kevent
*kev
)
217 struct conncb
*c
= obj
;
220 if (kev
->filter
== EVFILT_READ
) {
221 if (launchd_msg_recv(c
->conn
, ipc_readmsg
, c
) == -1 && errno
!= EAGAIN
) {
222 if (errno
!= ECONNRESET
) {
223 runtime_syslog(LOG_DEBUG
, "%s(): recv: %s", __func__
, strerror(errno
));
227 } else if (kev
->filter
== EVFILT_WRITE
) {
228 r
= launchd_msg_send(c
->conn
, NULL
);
230 if (errno
!= EAGAIN
) {
231 runtime_syslog(LOG_DEBUG
, "%s(): send: %s", __func__
, strerror(errno
));
235 kevent_mod(launchd_getfd(c
->conn
), EVFILT_WRITE
, EV_DELETE
, 0, 0, NULL
);
238 runtime_syslog(LOG_DEBUG
, "%s(): unknown filter type!", __func__
);
244 set_user_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
246 const char *v
= launch_data_get_string(obj
);
250 runtime_syslog(LOG_WARNING
, "Attempt to set NULL environment variable: %s (type = %d)", key
, launch_data_get_type(obj
));
255 ipc_close_all_with_job(job_t j
)
257 struct conncb
*ci
, *cin
;
259 LIST_FOREACH_SAFE(ci
, &connections
, sle
, cin
) {
267 ipc_close_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_close_fds
, NULL
);
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
));
280 if (launch_data_get_fd(o
) != -1) {
281 (void)launchd_assumes(runtime_close(launch_data_get_fd(o
)) == 0);
290 ipc_revoke_fds(launch_data_t o
)
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
);
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
));
303 launch_data_set_fd(o
, -1);
310 struct readmsg_context
{
316 ipc_readmsg(launch_data_t msg
, void *context
)
318 struct readmsg_context rmc
= { context
, NULL
};
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
);
325 rmc
.resp
= launch_data_new_errno(EINVAL
);
328 if (NULL
== rmc
.resp
) {
329 rmc
.resp
= launch_data_new_errno(ENOSYS
);
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
);
338 runtime_syslog(LOG_DEBUG
, "launchd_msg_send() == -1: %s", strerror(errno
));
342 launch_data_free(rmc
.resp
);
346 ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
)
348 struct readmsg_context
*rmc
= context
;
349 launch_data_t resp
= NULL
;
356 // job_log(rmc->c->j, LOG_NOTICE, "Socket IPC request: %s.", cmd);
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.
362 #if TARGET_OS_EMBEDDED
363 bool allow_privileged_ops
= ( !rmc
->c
->j
|| job_is_god(rmc
->c
->j
) );
365 bool allow_privileged_ops
= !rmc
->c
->j
;
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
);
376 if (!strcmp(cmd
, LAUNCH_KEY_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
));
397 if (!strcmp(cmd
, LAUNCH_KEY_STARTJOB
)) {
398 if ((j
= job_find(NULL
, launch_data_get_string(data
))) != NULL
) {
399 errno
= job_dispatch(j
, true) ? 0 : errno
;
401 resp
= launch_data_new_errno(errno
);
402 } else if (!strcmp(cmd
, LAUNCH_KEY_STOPJOB
)) {
403 if ((j
= job_find(NULL
, launch_data_get_string(data
))) != NULL
) {
407 resp
= launch_data_new_errno(errno
);
408 } else if (!strcmp(cmd
, LAUNCH_KEY_REMOVEJOB
)) {
409 if ((j
= job_find(NULL
, launch_data_get_string(data
))) != NULL
) {
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
);
418 if (job_import(data
)) {
421 resp
= launch_data_new_errno(errno
);
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(NULL
, launch_data_get_string(data
))) == NULL
) {
433 resp
= launch_data_new_errno(errno
);
435 resp
= job_export(j
);
436 ipc_revoke_fds(resp
);
438 } else if (!strcmp(cmd
, LAUNCH_KEY_SETPRIORITYLIST
)) {
439 resp
= launch_data_new_errno(launchd_set_jetsam_priorities(data
));
442 #if TARGET_OS_EMBEDDED
443 g_embedded_privileged_action
= false;
446 resp
= launch_data_new_errno(EACCES
);
453 close_abi_fixup(int fd
)
455 return runtime_close(fd
);
459 ipc_close(struct conncb
*c
)
462 launchd_close(c
->conn
, close_abi_fixup
);
467 adjust_rlimits(launch_data_t in
)
469 struct rlimit l
[RLIM_NLIMITS
];
473 for (i
= 0; i
< RLIM_NLIMITS
; i
++) {
474 (void)launchd_assumes(getrlimit(i
, l
+ i
) != -1);
478 ltmp
= launch_data_get_opaque(in
);
479 ltmpsz
= launch_data_get_opaque_size(in
);
481 if (ltmpsz
> sizeof(l
)) {
482 runtime_syslog(LOG_WARNING
, "Too much rlimit data sent!");
486 for (i
= 0; i
< (ltmpsz
/ sizeof(struct rlimit
)); i
++) {
487 if (ltmp
[i
].rlim_cur
== l
[i
].rlim_cur
&& ltmp
[i
].rlim_max
== l
[i
].rlim_max
) {
491 if (/* XXX readcfg_pid && */ pid1_magic
&& (i
== RLIMIT_NOFILE
|| i
== RLIMIT_NPROC
)) {
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
;
500 gmib
[1] = KERN_MAXFILES
;
501 pmib
[1] = KERN_MAXFILESPERPROC
;
502 gstr
= "kern.maxfiles";
503 pstr
= "kern.maxfilesperproc";
510 (void)launchd_assumes(sysctl(gmib
, 2, NULL
, NULL
, &gval
, sizeof(gval
)) != -1);
512 runtime_syslog(LOG_WARNING
, "sysctl(\"%s\"): can't be zero", gstr
);
515 (void)launchd_assumes(sysctl(pmib
, 2, NULL
, NULL
, &pval
, sizeof(pval
)) != -1);
517 runtime_syslog(LOG_WARNING
, "sysctl(\"%s\"): can't be zero", pstr
);
520 (void)launchd_assumes(setrlimit(i
, ltmp
+ i
) != -1);
521 /* the kernel may have clamped the values we gave it */
522 (void)launchd_assumes(getrlimit(i
, l
+ i
) != -1);
526 return launch_data_new_opaque(l
, sizeof(struct rlimit
) * RLIM_NLIMITS
);