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