1/* 2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_APACHE_LICENSE_HEADER_START@ 5 * 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 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 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. 17 * 18 * @APPLE_APACHE_LICENSE_HEADER_END@ 19 */ 20#include "config.h" 21#include <sys/types.h> 22#include <sys/select.h> 23#include <sys/event.h> 24#include <sys/socket.h> 25#include <sys/time.h> 26#include <stdio.h> 27#include <stdlib.h> 28#include <stdbool.h> 29#include <unistd.h> 30#include <string.h> 31#include <errno.h> 32#include <fcntl.h> 33#include <syslog.h> 34#include <libgen.h> 35#include <getopt.h> 36#include <signal.h> 37#include <netdb.h> 38 39#if !TARGET_OS_EMBEDDED 40#include <bsm/audit.h> 41#include <bsm/audit_session.h> 42#endif // !TARGET_OS_EMBEDDED 43 44#include "launch.h" 45 46static int kq = 0; 47 48static void find_fds(launch_data_t o, const char *key __attribute__((unused)), void *context __attribute__((unused))) 49{ 50 struct kevent kev; 51 size_t i; 52 int fd; 53 54 switch (launch_data_get_type(o)) { 55 case LAUNCH_DATA_FD: 56 fd = launch_data_get_fd(o); 57 if (-1 == fd) 58 break; 59 fcntl(fd, F_SETFD, 1); 60 EV_SET(&kev, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); 61 if (kevent(kq, &kev, 1, NULL, 0, NULL) == -1) 62 syslog(LOG_DEBUG, "kevent(%d): %m", fd); 63 break; 64 case LAUNCH_DATA_ARRAY: 65 for (i = 0; i < launch_data_array_get_count(o); i++) 66 find_fds(launch_data_array_get_index(o, i), NULL, NULL); 67 break; 68 case LAUNCH_DATA_DICTIONARY: 69 launch_data_dict_iterate(o, find_fds, NULL); 70 break; 71 default: 72 break; 73 } 74} 75 76int main(int argc __attribute__((unused)), char *argv[]) 77{ 78 struct timespec timeout = { 10, 0 }; 79 struct sockaddr_storage ss; 80 socklen_t slen = (socklen_t)sizeof ss; 81 struct kevent kev; 82 int r, ec = EXIT_FAILURE; 83 launch_data_t tmp, resp, msg = launch_data_alloc(LAUNCH_DATA_STRING); 84 const char *prog = argv[1]; 85 bool w = false, dupstdout = true, dupstderr = true; 86 87 launch_data_set_string(msg, LAUNCH_KEY_CHECKIN); 88 89 openlog(getprogname(), LOG_PERROR|LOG_PID|LOG_CONS, LOG_LAUNCHD); 90 91 kq = kqueue(); 92 93 if ((resp = launch_msg(msg)) == NULL) { 94 syslog(LOG_ERR, "launch_msg(%s): %m", LAUNCH_KEY_CHECKIN); 95 goto out; 96 } 97 98 launch_data_free(msg); 99 100 tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS); 101 if (tmp) { 102 find_fds(tmp, NULL, NULL); 103 } else { 104 syslog(LOG_ERR, "No FDs found to answer requests on!"); 105 goto out; 106 } 107 108 tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_TIMEOUT); 109 if (tmp) 110 timeout.tv_sec = (int)launch_data_get_integer(tmp); 111 112 tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_PROGRAM); 113 if (tmp) 114 prog = launch_data_get_string(tmp); 115 116 tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_INETDCOMPATIBILITY); 117 if (tmp) { 118 tmp = launch_data_dict_lookup(tmp, LAUNCH_JOBINETDCOMPATIBILITY_WAIT); 119 if (tmp) 120 w = launch_data_get_bool(tmp); 121 } 122 123 if (launch_data_dict_lookup(resp, LAUNCH_JOBKEY_STANDARDOUTPATH)) 124 dupstdout = false; 125 126 if (launch_data_dict_lookup(resp, LAUNCH_JOBKEY_STANDARDERRORPATH)) 127 dupstderr = false; 128 129 if (!w) 130 signal(SIGCHLD, SIG_IGN); 131 132 for (;;) { 133 if ((r = kevent(kq, NULL, 0, &kev, 1, &timeout)) == -1) { 134 syslog(LOG_DEBUG, "kevent(): %m"); 135 goto out; 136 } else if (r == 0) { 137 ec = EXIT_SUCCESS; 138 goto out; 139 } 140 141 if (w) { 142 dup2((int)kev.ident, STDIN_FILENO); 143 if (dupstdout) 144 dup2((int)kev.ident, STDOUT_FILENO); 145 if (dupstderr) 146 dup2((int)kev.ident, STDERR_FILENO); 147 execv(prog, argv + 1); 148 syslog(LOG_ERR, "execv(): %m"); 149 exit(EXIT_FAILURE); 150 } 151 152 if ((r = accept((int)kev.ident, (struct sockaddr *)&ss, &slen)) == -1) { 153 if (errno == EWOULDBLOCK) 154 continue; 155 syslog(LOG_WARNING, "accept(): %m"); 156 goto out; 157 } else { 158 if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) { 159 char fromhost[NI_MAXHOST]; 160 char fromport[NI_MAXSERV]; 161 int gni_r; 162 163 gni_r = getnameinfo((struct sockaddr *)&ss, slen, 164 fromhost, (socklen_t) sizeof fromhost, 165 fromport, (socklen_t) sizeof fromport, 166 NI_NUMERICHOST | NI_NUMERICSERV); 167 168 if (gni_r) { 169 syslog(LOG_WARNING, "%s: getnameinfo(): %s", prog, gai_strerror(gni_r)); 170 } else { 171 syslog(LOG_INFO, "%s: Connection from: %s on port: %s", prog, fromhost, fromport); 172 } 173 } else { 174 syslog(LOG_WARNING, "%s: getnameinfo() only supports IPv4/IPv6. Connection from address family: %u", prog, ss.ss_family); 175 } 176 177 switch (fork()) { 178 case -1: 179 syslog(LOG_WARNING, "fork(): %m"); 180 if (errno != ENOMEM) { 181 continue; 182 } 183 goto out; 184 case 0: 185 break; 186 default: 187 close(r); 188 continue; 189 } 190 191 setpgid(0, 0); 192 193#if !TARGET_OS_EMBEDDED 194 if ((tmp = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SESSIONCREATE)) && launch_data_get_bool(tmp)) { 195 auditinfo_addr_t auinfo = { 196 .ai_termid = { .at_type = AU_IPv4 }, 197 .ai_asid = AU_ASSIGN_ASID, 198 .ai_auid = getuid(), 199 .ai_flags = 0, 200 }; 201 if (setaudit_addr(&auinfo, sizeof(auinfo)) == 0) { 202 char session[16]; 203 snprintf(session, sizeof(session), "%x", auinfo.ai_asid); 204 setenv("SECURITYSESSIONID", session, 1); 205 } else { 206 syslog(LOG_NOTICE, "%s: Setting Audit Session ID failed: %d", prog, errno); 207 } 208 } 209#endif // !TARGET_OS_EMBEDDED 210 fcntl(r, F_SETFL, 0); 211 fcntl(r, F_SETFD, 1); 212 dup2(r, STDIN_FILENO); 213 if (dupstdout) 214 dup2(r, STDOUT_FILENO); 215 if (dupstderr) 216 dup2(r, STDERR_FILENO); 217 signal(SIGCHLD, SIG_DFL); 218 execv(prog, argv + 1); 219 syslog(LOG_ERR, "execv(): %m"); 220 exit(EXIT_FAILURE); 221 } 222 } 223 224out: 225 exit(ec); 226} 227