/* * prioritize process launch: Tests prioritized process launch across posix spawn and exec. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include mach_port_t receive( mach_port_t rcv_port, mach_port_t notify_port); static int get_pri(thread_t thread_port) { kern_return_t kr; thread_extended_info_data_t extended_info; mach_msg_type_number_t count = THREAD_EXTENDED_INFO_COUNT; kr = thread_info(thread_port, THREAD_EXTENDED_INFO, (thread_info_t)&extended_info, &count); if (kr != KERN_SUCCESS) { printf("thread info failed to get current priority of the thread\n"); } return extended_info.pth_priority; } static void set_thread_name(const char *fn_name) { char name[50] = ""; thread_t thread_port = pthread_mach_thread_np(pthread_self()); int pri = get_pri(thread_port); snprintf(name, sizeof(name), "%s at pri %2d", fn_name, pri); pthread_setname_np(name); } static void send( mach_port_t send_port, mach_port_t reply_port, mach_port_t msg_port, mach_msg_option_t options, int send_disposition) { kern_return_t ret = 0; struct { mach_msg_header_t header; mach_msg_body_t body; mach_msg_port_descriptor_t port_descriptor; } send_msg = { .header = { .msgh_remote_port = send_port, .msgh_local_port = reply_port, .msgh_bits = MACH_MSGH_BITS_SET(send_disposition, reply_port ? MACH_MSG_TYPE_MAKE_SEND_ONCE : 0, MACH_MSG_TYPE_MOVE_SEND, MACH_MSGH_BITS_COMPLEX), .msgh_id = 0x100, .msgh_size = sizeof(send_msg), }, .body = { .msgh_descriptor_count = 1, }, .port_descriptor = { .name = msg_port, .disposition = MACH_MSG_TYPE_MOVE_RECEIVE, .type = MACH_MSG_PORT_DESCRIPTOR, }, }; if (msg_port == MACH_PORT_NULL) { send_msg.body.msgh_descriptor_count = 0; } ret = mach_msg(&(send_msg.header), MACH_SEND_MSG | MACH_SEND_TIMEOUT | MACH_SEND_OVERRIDE | options, send_msg.header.msgh_size, 0, MACH_PORT_NULL, 10000, 0); if (ret != KERN_SUCCESS) { printf("mach_msg_send failed with error %d\n", ret); } } mach_port_t receive( mach_port_t rcv_port, mach_port_t notify_port) { kern_return_t ret = 0; mach_port_t service_port; struct { mach_msg_header_t header; mach_msg_body_t body; mach_msg_port_descriptor_t port_descriptor; mach_msg_trailer_t trailer; } rcv_msg = { .header = { .msgh_remote_port = MACH_PORT_NULL, .msgh_local_port = rcv_port, .msgh_size = sizeof(rcv_msg), }, }; printf("Client: Starting sync receive\n"); ret = mach_msg(&(rcv_msg.header), MACH_RCV_MSG | MACH_RCV_LARGE | (notify_port ? MACH_RCV_SYNC_WAIT : 0), 0, rcv_msg.header.msgh_size, rcv_port, 0, notify_port); printf("mach msg rcv returned %d\n", ret); if (rcv_msg.body.msgh_descriptor_count != 1) { if (notify_port) { printf("Did not receive a service port in mach msg %d\n", rcv_msg.body.msgh_descriptor_count); } return MACH_PORT_NULL; } service_port = rcv_msg.port_descriptor.name; return service_port; } int main(int argc __attribute__((unused)), char *argv[]) { int priority; set_thread_name(__FUNCTION__); /* Check for priority */ priority = get_pri(mach_thread_self()); printf("The priority of child is %d\n", priority); if (strcmp(argv[1], "EXIT") == 0) { printf("Helper process exiting\n"); exit(priority); } else if (strcmp(argv[1], "EXEC") == 0) { int ret; printf("Helper process execing\n"); /* exec the same binary with EXIT arg */ char *binary = "prioritize_process_launch_helper"; char *new_argv[] = {binary, "EXIT", NULL}; ret = execve(binary, new_argv, NULL); exit(ret); } else if (strcmp(argv[1], "SETEXEC") == 0) { int ret; int child_pid; posix_spawnattr_t attr; ret = posix_spawnattr_init(&attr); if (ret != 0) { printf("posix_spawnattr_init failed \n"); exit(ret); } ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC); if (ret != 0) { printf("posix_spawnattr_setflags failed \n"); exit(ret); } printf("Helper process doing posix_spawn set_exec\n"); /* set exec the same binary with EXIT arg */ char *binary = "prioritize_process_launch_helper"; char *new_argv[] = {binary, "EXIT", NULL}; ret = posix_spawn(&child_pid, binary, NULL, &attr, new_argv, NULL); exit(ret); } else if (strcmp(argv[1], "SETEXEC_PORTS") == 0) { int ret; int child_pid; posix_spawnattr_t attr; mach_port_t port; kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); if (kr != KERN_SUCCESS) { printf("mach_port_allocate failed with error %d\n", kr); exit(kr); } kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); if (kr != KERN_SUCCESS) { printf("mach_port_insert_right failed with error %d\n", kr); exit(kr); } ret = posix_spawnattr_init(&attr); if (ret != 0) { printf("posix_spawnattr_init failed \n"); exit(ret); } ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC); if (ret != 0) { printf("posix_spawnattr_setflags failed \n"); exit(ret); } ret = posix_spawnattr_set_importancewatch_port_np(&attr, 1, &port); if (ret != 0) { printf("posix_spawnattr_set_importance_port_np failed \n"); exit(ret); } printf("Helper process doing posix_spawn set_exec\n"); /* set exec the same binary with EXIT arg */ char *binary = "prioritize_process_launch_helper"; char *new_argv[] = {binary, "EXIT", NULL}; ret = posix_spawn(&child_pid, binary, NULL, &attr, new_argv, NULL); printf("spawned failed with error %d\n", ret); exit(ret); } else if (strcmp(argv[1], "WAIT") == 0) { do { sleep(1); priority = get_pri(mach_thread_self()); } while (priority == 47); exit(priority); } else if (strcmp(argv[1], "MULTIWAIT") == 0) { do { sleep(1); priority = get_pri(mach_thread_self()); } while (priority == 47); printf("The priority came down to %d\n", priority); do { sleep(1); priority = get_pri(mach_thread_self()); } while (priority == 37); printf("The priority came down to %d\n", priority); exit(priority); } else if (strcmp(argv[1], "MSGSYNC") == 0) { int ret_val = 31; mach_port_array_t port_array = NULL; unsigned int portCnt = 0; mach_port_t send_port; mach_port_t special_reply_port; mach_port_t service_port; kern_return_t kr; priority = get_pri(mach_thread_self()); printf("The priority of spawned binary is to %d\n", priority); if (priority != 47) { ret_val = 0; } /* Get the stashed send right using mach_ports_lookup */ kr = mach_ports_lookup(mach_task_self(), &port_array, &portCnt); if (kr != KERN_SUCCESS) { printf("mach_ports_lookup failed with return value %d and port count %d\n", kr, portCnt); exit(0); } send_port = port_array[0]; special_reply_port = thread_get_special_reply_port(); if (!MACH_PORT_VALID(special_reply_port)) { printf("Failed to special reply port for thread\n"); exit(0); } /* Perform a Sync bootstrap checkin */ send(send_port, special_reply_port, MACH_PORT_NULL, MACH_SEND_SYNC_BOOTSTRAP_CHECKIN, MACH_MSG_TYPE_COPY_SEND); sleep(2); /* Make sure we are still boosted */ priority = get_pri(mach_thread_self()); printf("The priority of spawned binary is to %d\n", priority); if (priority != 47) { ret_val = 0; } /* Receive the service port */ service_port = receive(special_reply_port, send_port); /* Make sure we are still boosted */ priority = get_pri(mach_thread_self()); printf("The priority of spawned binary is to %d\n", priority); if (priority != 47) { ret_val = 0; } /* Try to receive on service port */ receive(service_port, MACH_PORT_NULL); /* Make sure we are no longer boosted */ priority = get_pri(mach_thread_self()); printf("The priority of spawned binary is to %d\n", priority); if (priority != 31) { ret_val = 0; } exit(ret_val); } exit(0); }