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: 23274 $";
24 #include "launchd_unix_ipc.h"
26 #include <sys/types.h>
27 #include <sys/queue.h>
28 #include <sys/event.h>
30 #include <sys/ucred.h>
31 #include <sys/fcntl.h>
34 #include <sys/sysctl.h>
35 #include <sys/sockio.h>
37 #include <sys/resource.h>
38 #include <sys/ioctl.h>
50 #include "liblaunch_public.h"
51 #include "liblaunch_private.h"
53 #include "launchd_runtime.h"
54 #include "launchd_core_logic.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
);
64 static void ipc_listen_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
);
66 static kq_callback kqipc_listen_callback
= ipc_listen_callback
;
68 static pid_t ipc_self
= 0;
70 char *sockpath
= NULL
;
71 static char *sockdir
= NULL
;
73 static bool ipc_inited
= false;
78 if (ipc_self
!= getpid()) {
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
);
92 struct sockaddr_un sun
;
101 memset(&sun
, 0, sizeof(sun
));
102 sun
.sun_family
= AF_UNIX
;
105 strcpy(ourdir
, LAUNCHD_SOCK_PREFIX
);
106 strncpy(sun
.sun_path
, LAUNCHD_SOCK_PREFIX
"/sock", sizeof(sun
.sun_path
));
109 if (mkdir(ourdir
, S_IRWXU
) == -1) {
110 if (errno
== EROFS
) {
112 } else if (errno
== EEXIST
) {
115 if (!S_ISDIR(sb
.st_mode
)) {
117 runtime_syslog(LOG_ERR
, "mkdir(\"%s\"): %m", LAUNCHD_SOCK_PREFIX
);
121 runtime_syslog(LOG_ERR
, "mkdir(\"%s\"): %m", ourdir
);
126 snprintf(ourdir
, sizeof(ourdir
), "/tmp/launchd-%u.XXXXXX", getpid());
127 if (!launchd_assumes(mkdtemp(ourdir
) != NULL
)) {
130 snprintf(sun
.sun_path
, sizeof(sun
.sun_path
), "%s/sock", ourdir
);
133 if (unlink(sun
.sun_path
) == -1 && errno
!= ENOENT
) {
134 if (errno
!= EROFS
) {
135 runtime_syslog(LOG_ERR
, "unlink(\"thesocket\"): %m");
140 if (!launchd_assumes((fd
= _fd(socket(AF_UNIX
, SOCK_STREAM
, 0))) != -1)) {
144 oldmask
= umask(S_IRWXG
|S_IRWXO
);
145 r
= bind(fd
, (struct sockaddr
*)&sun
, sizeof(sun
));
149 if (errno
!= EROFS
) {
150 runtime_syslog(LOG_ERR
, "bind(\"thesocket\"): %m");
155 if (listen(fd
, SOMAXCONN
) == -1) {
156 runtime_syslog(LOG_ERR
, "listen(\"thesocket\"): %m");
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");
167 sockdir
= strdup(ourdir
);
168 sockpath
= strdup(sun
.sun_path
);
170 atexit(ipc_clean_up
);
173 if (!ipc_inited
&& fd
!= -1) {
174 launchd_assumes(runtime_close(fd
) == 0);
179 ipc_open(int fd
, job_t j
)
181 struct conncb
*c
= calloc(1, sizeof(struct conncb
));
183 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
185 c
->kqconn_callback
= ipc_callback
;
186 c
->conn
= launchd_fdopen(fd
);
188 LIST_INSERT_HEAD(&connections
, c
, sle
);
189 kevent_mod(fd
, EVFILT_READ
, EV_ADD
, 0, 0, &c
->kqconn_callback
);
193 ipc_listen_callback(void *obj
__attribute__((unused
)), struct kevent
*kev
)
195 struct sockaddr_un sun
;
196 socklen_t sl
= sizeof(sun
);
199 if ((cfd
= _fd(accept(kev
->ident
, (struct sockaddr
*)&sun
, &sl
))) == -1) {
207 ipc_callback(void *obj
, struct kevent
*kev
)
209 struct conncb
*c
= obj
;
212 if (kev
->filter
== EVFILT_READ
) {
213 if (launchd_msg_recv(c
->conn
, ipc_readmsg
, c
) == -1 && errno
!= EAGAIN
) {
214 if (errno
!= ECONNRESET
) {
215 runtime_syslog(LOG_DEBUG
, "%s(): recv: %m", __func__
);
219 } else if (kev
->filter
== EVFILT_WRITE
) {
220 r
= launchd_msg_send(c
->conn
, NULL
);
222 if (errno
!= EAGAIN
) {
223 runtime_syslog(LOG_DEBUG
, "%s(): send: %m", __func__
);
227 kevent_mod(launchd_getfd(c
->conn
), EVFILT_WRITE
, EV_DELETE
, 0, 0, NULL
);
230 runtime_syslog(LOG_DEBUG
, "%s(): unknown filter type!", __func__
);
235 static void set_user_env(launch_data_t obj
, const char *key
, void *context
__attribute__((unused
)))
237 setenv(key
, launch_data_get_string(obj
), 1);
241 ipc_close_all_with_job(job_t j
)
243 struct conncb
*ci
, *cin
;
245 LIST_FOREACH_SAFE(ci
, &connections
, sle
, cin
) {
253 ipc_close_fds(launch_data_t o
)
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
);
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
));
266 if (launch_data_get_fd(o
) != -1) {
267 launchd_assumes(runtime_close(launch_data_get_fd(o
)) == 0);
276 ipc_revoke_fds(launch_data_t o
)
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
);
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
));
289 launch_data_set_fd(o
, -1);
296 struct readmsg_context
{
302 ipc_readmsg(launch_data_t msg
, void *context
)
304 struct readmsg_context rmc
= { context
, NULL
};
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
);
311 rmc
.resp
= launch_data_new_errno(EINVAL
);
314 if (NULL
== rmc
.resp
) {
315 rmc
.resp
= launch_data_new_errno(ENOSYS
);
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
);
324 runtime_syslog(LOG_DEBUG
, "launchd_msg_send() == -1: %m");
328 launch_data_free(rmc
.resp
);
333 ipc_readmsg2(launch_data_t data
, const char *cmd
, void *context
)
335 struct readmsg_context
*rmc
= context
;
336 launch_data_t resp
= NULL
;
343 //job_log(rmc->c->j, LOG_DEBUG, "Unix IPC request: %s", cmd);
346 if (!strcmp(cmd
, LAUNCH_KEY_CHECKIN
)) {
348 resp
= job_export(rmc
->c
->j
);
349 job_checkin(rmc
->c
->j
);
351 resp
= launch_data_new_errno(EACCES
);
353 } else if (!strcmp(cmd
, LAUNCH_KEY_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
);
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
));
373 } else if (!strcmp(cmd
, LAUNCH_KEY_STARTJOB
)) {
374 if ((j
= job_find(launch_data_get_string(data
))) != NULL
) {
375 job_dispatch(j
, true);
378 resp
= launch_data_new_errno(errno
);
379 } else if (!strcmp(cmd
, LAUNCH_KEY_STOPJOB
)) {
380 if ((j
= job_find(launch_data_get_string(data
))) != NULL
) {
384 resp
= launch_data_new_errno(errno
);
385 } else if (!strcmp(cmd
, LAUNCH_KEY_REMOVEJOB
)) {
386 if ((j
= job_find(launch_data_get_string(data
))) != NULL
) {
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
);
395 if (job_import(data
)) {
398 resp
= launch_data_new_errno(errno
);
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
)) {
409 if ((j
= job_find(launch_data_get_string(data
))) == NULL
) {
410 resp
= launch_data_new_errno(errno
);
412 resp
= job_export(j
);
413 ipc_revoke_fds(resp
);
421 ipc_close(struct conncb
*c
)
424 launchd_close(c
->conn
, runtime_close
);
429 adjust_rlimits(launch_data_t in
)
431 struct rlimit l
[RLIM_NLIMITS
];
435 for (i
= 0; i
< RLIM_NLIMITS
; i
++) {
436 launchd_assumes(getrlimit(i
, l
+ i
) != -1);
440 ltmp
= launch_data_get_opaque(in
);
441 ltmpsz
= launch_data_get_opaque_size(in
);
443 if (ltmpsz
> sizeof(l
)) {
444 runtime_syslog(LOG_WARNING
, "Too much rlimit data sent!");
448 for (i
= 0; i
< (ltmpsz
/ sizeof(struct rlimit
)); i
++) {
449 if (ltmp
[i
].rlim_cur
== l
[i
].rlim_cur
&& ltmp
[i
].rlim_max
== l
[i
].rlim_max
) {
453 if (/* XXX readcfg_pid && */ getpid() == 1 && (i
== RLIMIT_NOFILE
|| i
== RLIMIT_NPROC
)) {
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
;
462 gmib
[1] = KERN_MAXFILES
;
463 pmib
[1] = KERN_MAXFILESPERPROC
;
464 gstr
= "kern.maxfiles";
465 pstr
= "kern.maxfilesperproc";
468 /* kernel will not clamp to this value, we must */
469 if (gval
> (2048 + 20)) {
478 launchd_assumes(sysctl(gmib
, 2, NULL
, NULL
, &gval
, sizeof(gval
)) != -1);
480 runtime_syslog(LOG_WARNING
, "sysctl(\"%s\"): can't be zero", gstr
);
483 launchd_assumes(sysctl(pmib
, 2, NULL
, NULL
, &pval
, sizeof(pval
)) != -1);
485 runtime_syslog(LOG_WARNING
, "sysctl(\"%s\"): can't be zero", pstr
);
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);
494 return launch_data_new_opaque(l
, sizeof(struct rlimit
) * RLIM_NLIMITS
);