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@
24 #include <sys/socket.h>
25 #include <sys/types.h>
26 #include <sys/queue.h>
27 #include <sys/event.h>
29 #include <sys/ucred.h>
30 #include <sys/fcntl.h>
33 #include <sys/sysctl.h>
34 #include <sys/sockio.h>
36 #include <sys/resource.h>
37 #include <sys/ioctl.h>
51 #include "launch_priv.h"
56 extern char **environ
;
58 static LIST_HEAD(, conncb
) connections
;
60 static launch_data_t
adjust_rlimits(launch_data_t in
);
62 static void ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
);
63 static void ipc_readmsg(launch_data_t msg
, void *context
);
65 static void ipc_listen_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
);
67 static kq_callback kqipc_listen_callback
= ipc_listen_callback
;
69 static pid_t ipc_self
= 0;
71 char *sockpath
= NULL
;
72 static char *sockdir
= NULL
;
74 static bool ipc_inited
= false;
79 if (ipc_self
!= getpid()) {
83 if (-1 == unlink(sockpath
)) {
84 launchd_syslog(LOG_WARNING
, "unlink(\"%s\"): %s", sockpath
, strerror(errno
));
85 } else if (-1 == rmdir(sockdir
)) {
86 launchd_syslog(LOG_WARNING
, "rmdir(\"%s\"): %s", sockdir
, strerror(errno
));
93 struct sockaddr_un sun
;
102 memset(&sun
, 0, sizeof(sun
));
103 sun
.sun_family
= AF_UNIX
;
106 strcpy(ourdir
, LAUNCHD_SOCK_PREFIX
);
107 strncpy(sun
.sun_path
, LAUNCHD_SOCK_PREFIX
"/sock", sizeof(sun
.sun_path
));
110 if (mkdir(ourdir
, S_IRWXU
) == -1) {
111 if (errno
== EROFS
) {
113 } else if (errno
== EEXIST
) {
116 if (!S_ISDIR(sb
.st_mode
)) {
118 launchd_syslog(LOG_ERR
, "mkdir(\"%s\"): %s", LAUNCHD_SOCK_PREFIX
, strerror(errno
));
122 launchd_syslog(LOG_ERR
, "mkdir(\"%s\"): %s", ourdir
, strerror(errno
));
127 snprintf(ourdir
, sizeof(ourdir
), _PATH_TMP
"launchd-%u.XXXXXX", getpid());
128 if (mkdtemp(ourdir
) == NULL
) {
129 launchd_syslog(LOG_ERR
, "Could not create critical directory \"%s\": %s", ourdir
, strerror(errno
));
132 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/sock", ourdir
);
135 if (unlink(sun
.sun_path
) == -1 && errno
!= ENOENT
) {
136 if (errno
!= EROFS
) {
137 launchd_syslog(LOG_ERR
, "unlink(\"thesocket\"): %s", strerror(errno
));
142 if (posix_assumes_zero(fd
= _fd(socket(AF_UNIX
, SOCK_STREAM
, 0))) == -1) {
146 oldmask
= umask(S_IRWXG
|S_IRWXO
);
147 r
= bind(fd
, (struct sockaddr
*)&sun
, sizeof(sun
));
151 if (errno
!= EROFS
) {
152 launchd_syslog(LOG_ERR
, "bind(\"thesocket\"): %s", strerror(errno
));
157 if (listen(fd
, SOMAXCONN
) == -1) {
158 launchd_syslog(LOG_ERR
, "listen(\"thesocket\"): %s", strerror(errno
));
162 if (kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &kqipc_listen_callback
) == -1) {
163 launchd_syslog(LOG_ERR
, "kevent_mod(\"thesocket\", EVFILT_READ): %s", strerror(errno
));
169 sockdir
= strdup(ourdir
);
170 sockpath
= strdup(sun
.sun_path
);
172 atexit(ipc_clean_up
);
175 if (!ipc_inited
&& fd
!= -1) {
176 (void)runtime_close(fd
);
181 ipc_open(int fd
, job_t j
)
183 struct conncb
*c
= calloc(1, sizeof(struct conncb
));
185 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
187 c
->kqconn_callback
= ipc_callback
;
189 c
->conn
= launchd_fdopen(-1, fd
);
191 c
->conn
= launchd_fdopen(fd
, -1);
195 LIST_INSERT_HEAD(&connections
, c
, sle
);
196 kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &c
->kqconn_callback
);
200 ipc_listen_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
202 struct sockaddr_un sun
;
203 socklen_t sl
= sizeof(sun
);
206 if ((cfd
= _fd(accept(kev
->ident
, (struct sockaddr
*)&sun
, &sl
))) == -1) {
214 ipc_callback(void *obj
, struct kevent
*kev
)
216 struct conncb
*c
= obj
;
219 if (kev
->filter
== EVFILT_READ
) {
220 if (launchd_msg_recv(c
->conn
, ipc_readmsg
, c
) == -1 && errno
!= EAGAIN
) {
221 if (errno
!= ECONNRESET
) {
222 launchd_syslog(LOG_DEBUG
, "%s(): recv: %s", __func__
, strerror(errno
));
226 } else if (kev
->filter
== EVFILT_WRITE
) {
227 r
= launchd_msg_send(c
->conn
, NULL
);
229 if (errno
!= EAGAIN
) {
230 launchd_syslog(LOG_DEBUG
, "%s(): send: %s", __func__
, strerror(errno
));
234 kevent_mod(launchd_getfd(c
->conn
), EVFILT_WRITE
, EV_DELETE
, 0, 0, NULL
);
237 launchd_syslog(LOG_DEBUG
, "%s(): unknown filter type!", __func__
);
243 set_user_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
245 const char *v
= launch_data_get_string(obj
);
249 launchd_syslog(LOG_WARNING
, "Attempt to set NULL environment variable: %s (type = %d)", key
, launch_data_get_type(obj
));
254 ipc_close_all_with_job(job_t j
)
256 struct conncb
*ci
, *cin
;
258 LIST_FOREACH_SAFE(ci
, &connections
, sle
, cin
) {
266 ipc_close_fds(launch_data_t o
)
270 switch (launch_data_get_type(o
)) {
271 case LAUNCH_DATA_DICTIONARY
:
272 launch_data_dict_iterate(o
, (void (*)(launch_data_t
, const char *, void *))ipc_close_fds
, NULL
);
274 case LAUNCH_DATA_ARRAY
:
275 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
276 ipc_close_fds(launch_data_array_get_index(o
, i
));
279 if (launch_data_get_fd(o
) != -1) {
280 (void)runtime_close(launch_data_get_fd(o
));
289 ipc_revoke_fds(launch_data_t o
)
293 switch (launch_data_get_type(o
)) {
294 case LAUNCH_DATA_DICTIONARY
:
295 launch_data_dict_iterate(o
, (void (*)(launch_data_t
, const char *, void *))ipc_revoke_fds
, NULL
);
297 case LAUNCH_DATA_ARRAY
:
298 for (i
= 0; i
< launch_data_array_get_count(o
); i
++)
299 ipc_revoke_fds(launch_data_array_get_index(o
, i
));
302 launch_data_set_fd(o
, -1);
309 struct readmsg_context
{
315 ipc_readmsg(launch_data_t msg
, void *context
)
317 struct readmsg_context rmc
= { context
, NULL
};
319 if (LAUNCH_DATA_DICTIONARY
== launch_data_get_type(msg
)) {
320 launch_data_dict_iterate(msg
, ipc_readmsg2
, &rmc
);
321 } else if (LAUNCH_DATA_STRING
== launch_data_get_type(msg
)) {
322 ipc_readmsg2(NULL
, launch_data_get_string(msg
), &rmc
);
324 rmc
.resp
= launch_data_new_errno(EINVAL
);
327 if (NULL
== rmc
.resp
) {
328 rmc
.resp
= launch_data_new_errno(ENOSYS
);
333 if (launchd_msg_send(rmc
.c
->conn
, rmc
.resp
) == -1) {
334 if (errno
== EAGAIN
) {
335 kevent_mod(launchd_getfd(rmc
.c
->conn
), EVFILT_WRITE
, EV_ADD
, 0, 0, &rmc
.c
->kqconn_callback
);
337 launchd_syslog(LOG_DEBUG
, "launchd_msg_send() == -1: %s", strerror(errno
));
341 launch_data_free(rmc
.resp
);
345 ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
)
347 struct readmsg_context
*rmc
= context
;
348 launch_data_t resp
= NULL
;
355 /* Do not allow commands other than check-in to come over the trusted socket
356 * on the Desktop. On Embedded, allow all commands over the trusted socket
357 * if the job has the God Mode key set.
359 #if TARGET_OS_EMBEDDED
360 bool allow_privileged_ops
= (!rmc
->c
->j
|| job_is_god(rmc
->c
->j
));
362 bool allow_privileged_ops
= !rmc
->c
->j
;
365 if (rmc
->c
->j
&& strcmp(cmd
, LAUNCH_KEY_CHECKIN
) == 0) {
366 resp
= job_export(rmc
->c
->j
);
367 job_checkin(rmc
->c
->j
);
368 } else if (allow_privileged_ops
) {
369 #if TARGET_OS_EMBEDDED
370 launchd_embedded_handofgod
= rmc
->c
->j
&& job_is_god(rmc
->c
->j
);
373 if (!strcmp(cmd
, LAUNCH_KEY_SHUTDOWN
)) {
375 resp
= launch_data_new_errno(0);
376 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOBS
)) {
377 resp
= job_export_all();
378 ipc_revoke_fds(resp
);
379 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRESOURCELIMITS
)) {
380 resp
= adjust_rlimits(NULL
);
381 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRUSAGESELF
)) {
382 struct rusage rusage
;
383 getrusage(RUSAGE_SELF
, &rusage
);
384 resp
= launch_data_new_opaque(&rusage
, sizeof(rusage
));
385 } else if (!strcmp(cmd
, LAUNCH_KEY_GETRUSAGECHILDREN
)) {
386 struct rusage rusage
;
387 getrusage(RUSAGE_CHILDREN
, &rusage
);
388 resp
= launch_data_new_opaque(&rusage
, sizeof(rusage
));
391 if (!strcmp(cmd
, LAUNCH_KEY_STARTJOB
)) {
392 if ((j
= job_find(NULL
, launch_data_get_string(data
))) != NULL
) {
393 errno
= job_dispatch(j
, true) ? 0 : errno
;
395 resp
= launch_data_new_errno(errno
);
396 } else if (!strcmp(cmd
, LAUNCH_KEY_STOPJOB
)) {
397 if ((j
= job_find(NULL
, launch_data_get_string(data
))) != NULL
) {
401 resp
= launch_data_new_errno(errno
);
402 } else if (!strcmp(cmd
, LAUNCH_KEY_REMOVEJOB
)) {
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_SUBMITJOB
)) {
409 if (launch_data_get_type(data
) == LAUNCH_DATA_ARRAY
) {
410 resp
= job_import_bulk(data
);
412 if (job_import(data
)) {
415 resp
= launch_data_new_errno(errno
);
417 } else if (!strcmp(cmd
, LAUNCH_KEY_UNSETUSERENVIRONMENT
)) {
418 unsetenv(launch_data_get_string(data
));
419 resp
= launch_data_new_errno(0);
420 } else if (!strcmp(cmd
, LAUNCH_KEY_SETUSERENVIRONMENT
)) {
421 launch_data_dict_iterate(data
, set_user_env
, NULL
);
422 resp
= launch_data_new_errno(0);
423 } else if (!strcmp(cmd
, LAUNCH_KEY_SETRESOURCELIMITS
)) {
424 resp
= adjust_rlimits(data
);
425 } else if (!strcmp(cmd
, LAUNCH_KEY_GETJOB
)) {
426 if ((j
= job_find(NULL
, launch_data_get_string(data
))) == NULL
) {
427 resp
= launch_data_new_errno(errno
);
429 resp
= job_export(j
);
430 ipc_revoke_fds(resp
);
432 } else if (!strcmp(cmd
, LAUNCH_KEY_SETPRIORITYLIST
)) {
433 #if TARGET_OS_EMBEDDED
434 resp
= launch_data_new_errno(launchd_set_jetsam_priorities(data
));
436 resp
= launch_data_new_errno(ENOTSUP
);
440 #if TARGET_OS_EMBEDDED
441 launchd_embedded_handofgod
= false;
444 resp
= launch_data_new_errno(EACCES
);
451 close_abi_fixup(int fd
)
453 return runtime_close(fd
);
457 ipc_close(struct conncb
*c
)
460 launchd_close(c
->conn
, close_abi_fixup
);
465 adjust_rlimits(launch_data_t in
)
467 /* If I never have to deal with this rlimit nonsense again, I'll be a very
470 struct rlimit l
[RLIM_NLIMITS
];
474 for (i
= 0; i
< RLIM_NLIMITS
; i
++) {
475 (void)posix_assumes_zero(getrlimit(i
, l
+ i
));
479 ltmp
= launch_data_get_opaque(in
);
480 ltmpsz
= launch_data_get_opaque_size(in
);
482 if (ltmpsz
> sizeof(l
)) {
483 launchd_syslog(LOG_WARNING
, "Too much rlimit data sent!");
487 for (i
= 0; i
< (ltmpsz
/ sizeof(struct rlimit
)); i
++) {
488 if (ltmp
[i
].rlim_cur
== l
[i
].rlim_cur
&& ltmp
[i
].rlim_max
== l
[i
].rlim_max
) {
492 if (/* XXX readcfg_pid && */ pid1_magic
&& (i
== RLIMIT_NOFILE
|| i
== RLIMIT_NPROC
)) {
493 int gmib
[] = { CTL_KERN
, KERN_MAXPROC
};
494 int pmib
[] = { CTL_KERN
, KERN_MAXPROCPERUID
};
495 const char *gstr
= "kern.maxproc";
496 const char *pstr
= "kern.maxprocperuid";
497 int gval
= ltmp
[i
].rlim_max
;
498 int pval
= ltmp
[i
].rlim_cur
;
501 gmib
[1] = KERN_MAXFILES
;
502 pmib
[1] = KERN_MAXFILESPERPROC
;
503 gstr
= "kern.maxfiles";
504 pstr
= "kern.maxfilesperproc";
511 (void)posix_assumes_zero(sysctl(gmib
, 2, NULL
, NULL
, &gval
, sizeof(gval
)));
513 launchd_syslog(LOG_WARNING
, "sysctl(\"%s\"): can't be zero", gstr
);
516 (void)posix_assumes_zero(sysctl(pmib
, 2, NULL
, NULL
, &pval
, sizeof(pval
)));
518 launchd_syslog(LOG_WARNING
, "sysctl(\"%s\"): can't be zero", pstr
);
521 (void)posix_assumes_zero(setrlimit(i
, ltmp
+ i
));
522 /* the kernel may have clamped the values we gave it */
523 (void)posix_assumes_zero(getrlimit(i
, l
+ i
));
527 return launch_data_new_opaque(l
, sizeof(struct rlimit
) * RLIM_NLIMITS
);