]>
Commit | Line | Data |
---|---|---|
cb323159 A |
1 | /* |
2 | * prioritize process launch: Tests prioritized process launch across posix spawn and exec. | |
3 | */ | |
4 | ||
5 | #include <dispatch/dispatch.h> | |
6 | #include <pthread.h> | |
7 | #include <launch.h> | |
8 | #include <mach/mach.h> | |
9 | #include <mach/message.h> | |
10 | #include <mach/mach_voucher.h> | |
11 | #include <pthread/workqueue_private.h> | |
12 | #include <voucher/ipc_pthread_priority_types.h> | |
13 | #include <servers/bootstrap.h> | |
14 | #include <stdlib.h> | |
15 | #include <sys/event.h> | |
16 | #include <stdio.h> | |
17 | #include <unistd.h> | |
18 | #include <crt_externs.h> | |
19 | #include <signal.h> | |
20 | #include <sys/types.h> | |
21 | #include <sys/sysctl.h> | |
22 | #include <libkern/OSAtomic.h> | |
23 | #include <sys/wait.h> | |
24 | #include <spawn.h> | |
25 | #include <spawn_private.h> | |
26 | #include <string.h> | |
27 | ||
28 | ||
29 | mach_port_t | |
30 | receive( | |
31 | mach_port_t rcv_port, | |
32 | mach_port_t notify_port); | |
33 | ||
34 | static int | |
35 | get_pri(thread_t thread_port) | |
36 | { | |
37 | kern_return_t kr; | |
38 | ||
39 | thread_extended_info_data_t extended_info; | |
40 | mach_msg_type_number_t count = THREAD_EXTENDED_INFO_COUNT; | |
41 | kr = thread_info(thread_port, THREAD_EXTENDED_INFO, | |
42 | (thread_info_t)&extended_info, &count); | |
43 | ||
44 | if (kr != KERN_SUCCESS) { | |
45 | printf("thread info failed to get current priority of the thread\n"); | |
46 | } | |
47 | return extended_info.pth_curpri; | |
48 | } | |
49 | ||
50 | static void | |
51 | set_thread_name(const char *fn_name) | |
52 | { | |
53 | char name[50] = ""; | |
54 | ||
55 | thread_t thread_port = pthread_mach_thread_np(pthread_self()); | |
56 | ||
57 | int pri = get_pri(thread_port); | |
58 | ||
59 | snprintf(name, sizeof(name), "%s at pri %2d", fn_name, pri); | |
60 | pthread_setname_np(name); | |
61 | } | |
62 | ||
63 | static void | |
64 | send( | |
65 | mach_port_t send_port, | |
66 | mach_port_t reply_port, | |
67 | mach_port_t msg_port, | |
68 | mach_msg_option_t options, | |
69 | int send_disposition) | |
70 | { | |
71 | kern_return_t ret = 0; | |
72 | ||
73 | struct { | |
74 | mach_msg_header_t header; | |
75 | mach_msg_body_t body; | |
76 | mach_msg_port_descriptor_t port_descriptor; | |
77 | } send_msg = { | |
78 | .header = { | |
79 | .msgh_remote_port = send_port, | |
80 | .msgh_local_port = reply_port, | |
81 | .msgh_bits = MACH_MSGH_BITS_SET(send_disposition, | |
82 | reply_port ? MACH_MSG_TYPE_MAKE_SEND_ONCE : 0, | |
83 | MACH_MSG_TYPE_MOVE_SEND, | |
84 | MACH_MSGH_BITS_COMPLEX), | |
85 | .msgh_id = 0x100, | |
86 | .msgh_size = sizeof(send_msg), | |
87 | }, | |
88 | .body = { | |
89 | .msgh_descriptor_count = 1, | |
90 | }, | |
91 | .port_descriptor = { | |
92 | .name = msg_port, | |
93 | .disposition = MACH_MSG_TYPE_MOVE_RECEIVE, | |
94 | .type = MACH_MSG_PORT_DESCRIPTOR, | |
95 | }, | |
96 | }; | |
97 | ||
98 | if (msg_port == MACH_PORT_NULL) { | |
99 | send_msg.body.msgh_descriptor_count = 0; | |
100 | } | |
101 | ||
102 | ret = mach_msg(&(send_msg.header), | |
103 | MACH_SEND_MSG | | |
104 | MACH_SEND_TIMEOUT | | |
105 | MACH_SEND_OVERRIDE | | |
106 | ((reply_port ? MACH_SEND_SYNC_OVERRIDE : 0) | options), | |
107 | send_msg.header.msgh_size, | |
108 | 0, | |
109 | MACH_PORT_NULL, | |
110 | 10000, | |
111 | 0); | |
112 | ||
113 | if (ret != KERN_SUCCESS) { | |
114 | printf("mach_msg_send failed with error %d\n", ret); | |
115 | } | |
116 | } | |
117 | ||
118 | mach_port_t | |
119 | receive( | |
120 | mach_port_t rcv_port, | |
121 | mach_port_t notify_port) | |
122 | { | |
123 | kern_return_t ret = 0; | |
124 | mach_port_t service_port; | |
125 | ||
126 | struct { | |
127 | mach_msg_header_t header; | |
128 | mach_msg_body_t body; | |
129 | mach_msg_port_descriptor_t port_descriptor; | |
130 | mach_msg_trailer_t trailer; | |
131 | } rcv_msg = { | |
132 | .header = | |
133 | { | |
134 | .msgh_remote_port = MACH_PORT_NULL, | |
135 | .msgh_local_port = rcv_port, | |
136 | .msgh_size = sizeof(rcv_msg), | |
137 | }, | |
138 | }; | |
139 | ||
140 | printf("Client: Starting sync receive\n"); | |
141 | ||
142 | ret = mach_msg(&(rcv_msg.header), | |
143 | MACH_RCV_MSG | MACH_RCV_LARGE | | |
144 | (notify_port ? MACH_RCV_SYNC_WAIT : 0), | |
145 | 0, | |
146 | rcv_msg.header.msgh_size, | |
147 | rcv_port, | |
148 | 0, | |
149 | notify_port); | |
150 | ||
151 | printf("mach msg rcv returned %d\n", ret); | |
152 | ||
153 | ||
154 | if (rcv_msg.body.msgh_descriptor_count != 1) { | |
155 | if (notify_port) { | |
156 | printf("Did not receive a service port in mach msg %d\n", rcv_msg.body.msgh_descriptor_count); | |
157 | } | |
158 | return MACH_PORT_NULL; | |
159 | } | |
160 | ||
161 | service_port = rcv_msg.port_descriptor.name; | |
162 | return service_port; | |
163 | } | |
164 | ||
165 | int | |
166 | main(int argc __attribute__((unused)), char *argv[]) | |
167 | { | |
168 | int priority; | |
169 | set_thread_name(__FUNCTION__); | |
170 | ||
171 | /* Check for priority */ | |
172 | priority = get_pri(mach_thread_self()); | |
173 | printf("The priority of child is %d\n", priority); | |
174 | ||
175 | if (strcmp(argv[1], "EXIT") == 0) { | |
176 | printf("Helper process exiting\n"); | |
177 | exit(priority); | |
178 | } else if (strcmp(argv[1], "EXEC") == 0) { | |
179 | int ret; | |
180 | ||
181 | printf("Helper process execing\n"); | |
182 | /* exec the same binary with EXIT arg */ | |
183 | char *binary = "prioritize_process_launch_helper"; | |
184 | char *new_argv[] = {binary, "EXIT", NULL}; | |
185 | ret = execve(binary, new_argv, NULL); | |
186 | exit(ret); | |
187 | } else if (strcmp(argv[1], "SETEXEC") == 0) { | |
188 | int ret; | |
189 | int child_pid; | |
190 | posix_spawnattr_t attr; | |
191 | ||
192 | ret = posix_spawnattr_init(&attr); | |
193 | if (ret != 0) { | |
194 | printf("posix_spawnattr_init failed \n"); | |
195 | exit(ret); | |
196 | } | |
197 | ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC); | |
198 | if (ret != 0) { | |
199 | printf("posix_spawnattr_setflags failed \n"); | |
200 | exit(ret); | |
201 | } | |
202 | ||
203 | printf("Helper process doing posix_spawn set_exec\n"); | |
204 | /* set exec the same binary with EXIT arg */ | |
205 | char *binary = "prioritize_process_launch_helper"; | |
206 | char *new_argv[] = {binary, "EXIT", NULL}; | |
207 | ||
208 | ret = posix_spawn(&child_pid, binary, NULL, &attr, new_argv, NULL); | |
209 | exit(ret); | |
210 | } else if (strcmp(argv[1], "SETEXEC_PORTS") == 0) { | |
211 | int ret; | |
212 | int child_pid; | |
213 | posix_spawnattr_t attr; | |
214 | mach_port_t port; | |
215 | ||
216 | kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); | |
217 | if (kr != KERN_SUCCESS) { | |
218 | printf("mach_port_allocate failed with error %d\n", kr); | |
219 | exit(kr); | |
220 | } | |
221 | ||
222 | kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); | |
223 | if (kr != KERN_SUCCESS) { | |
224 | printf("mach_port_insert_right failed with error %d\n", kr); | |
225 | exit(kr); | |
226 | } | |
227 | ||
228 | ret = posix_spawnattr_init(&attr); | |
229 | if (ret != 0) { | |
230 | printf("posix_spawnattr_init failed \n"); | |
231 | exit(ret); | |
232 | } | |
233 | ||
234 | ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC); | |
235 | if (ret != 0) { | |
236 | printf("posix_spawnattr_setflags failed \n"); | |
237 | exit(ret); | |
238 | } | |
239 | ||
240 | ret = posix_spawnattr_set_importancewatch_port_np(&attr, 1, &port); | |
241 | if (ret != 0) { | |
242 | printf("posix_spawnattr_set_importance_port_np failed \n"); | |
243 | exit(ret); | |
244 | } | |
245 | ||
246 | printf("Helper process doing posix_spawn set_exec\n"); | |
247 | /* set exec the same binary with EXIT arg */ | |
248 | char *binary = "prioritize_process_launch_helper"; | |
249 | char *new_argv[] = {binary, "EXIT", NULL}; | |
250 | ||
251 | ret = posix_spawn(&child_pid, binary, NULL, &attr, new_argv, NULL); | |
252 | printf("spawned failed with error %d\n", ret); | |
253 | exit(ret); | |
254 | } else if (strcmp(argv[1], "WAIT") == 0) { | |
255 | do { | |
256 | sleep(1); | |
257 | priority = get_pri(mach_thread_self()); | |
258 | } while (priority == 47); | |
259 | exit(priority); | |
260 | } else if (strcmp(argv[1], "MULTIWAIT") == 0) { | |
261 | do { | |
262 | sleep(1); | |
263 | priority = get_pri(mach_thread_self()); | |
264 | } while (priority == 47); | |
265 | printf("The priority came down to %d\n", priority); | |
266 | do { | |
267 | sleep(1); | |
268 | priority = get_pri(mach_thread_self()); | |
269 | } while (priority == 37); | |
270 | printf("The priority came down to %d\n", priority); | |
271 | exit(priority); | |
272 | } else if (strcmp(argv[1], "MSGSYNC") == 0) { | |
273 | int ret_val = 31; | |
274 | mach_port_array_t port_array = NULL; | |
275 | unsigned int portCnt = 0; | |
276 | mach_port_t send_port; | |
277 | mach_port_t special_reply_port; | |
278 | mach_port_t service_port; | |
279 | kern_return_t kr; | |
280 | ||
281 | priority = get_pri(mach_thread_self()); | |
282 | printf("The priority of spawned binary is to %d\n", priority); | |
283 | if (priority != 47) { | |
284 | ret_val = 0; | |
285 | } | |
286 | ||
287 | /* Get the stashed send right using mach_ports_lookup */ | |
288 | kr = mach_ports_lookup(mach_task_self(), &port_array, &portCnt); | |
289 | if (kr != KERN_SUCCESS) { | |
290 | printf("mach_ports_lookup failed with return value %d and port count %d\n", kr, portCnt); | |
291 | exit(0); | |
292 | } | |
293 | ||
294 | send_port = port_array[0]; | |
295 | special_reply_port = thread_get_special_reply_port(); | |
296 | if (!MACH_PORT_VALID(special_reply_port)) { | |
297 | printf("Failed to special reply port for thread\n"); | |
298 | exit(0); | |
299 | } | |
300 | ||
301 | /* Perform a Sync bootstrap checkin */ | |
302 | send(send_port, special_reply_port, MACH_PORT_NULL, MACH_SEND_SYNC_BOOTSTRAP_CHECKIN, MACH_MSG_TYPE_COPY_SEND); | |
303 | sleep(2); | |
304 | ||
305 | /* Make sure we are still boosted */ | |
306 | priority = get_pri(mach_thread_self()); | |
307 | printf("The priority of spawned binary is to %d\n", priority); | |
308 | if (priority != 47) { | |
309 | ret_val = 0; | |
310 | } | |
311 | ||
312 | /* Receive the service port */ | |
313 | service_port = receive(special_reply_port, send_port); | |
314 | ||
315 | /* Make sure we are still boosted */ | |
316 | priority = get_pri(mach_thread_self()); | |
317 | printf("The priority of spawned binary is to %d\n", priority); | |
318 | if (priority != 47) { | |
319 | ret_val = 0; | |
320 | } | |
321 | ||
322 | /* Try to receive on service port */ | |
323 | receive(service_port, MACH_PORT_NULL); | |
324 | ||
325 | /* Make sure we are no longer boosted */ | |
326 | priority = get_pri(mach_thread_self()); | |
327 | printf("The priority of spawned binary is to %d\n", priority); | |
328 | if (priority != 31) { | |
329 | ret_val = 0; | |
330 | } | |
331 | exit(ret_val); | |
332 | } | |
333 | ||
334 | exit(0); | |
335 | } |