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
);