1 /* 2 * Process-related convenience functions. 3 * 4 * Copyright (C) 2023 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <ipc/cap_alloc.h> 23 #include <ipc/mem_ipc.h> 24 #include <notifier/notifier.h> 25 #include <systypes/env.h> 26 27 #include <stdio.h> 28 #include <stdlib.h> 29 30 #include "process_creator_context_client.h" 31 32 #include "file.h" 33 #include "process.h" 34 35 36 37 /* Utility functions. */ 38 39 static bool _process_terminated(notifiable_t *notifiable) 40 { 41 return ((notifiable->notifications & NOTIFY_TASK_SIGNAL) && 42 (notifiable->values.sig == 0)) || 43 (notifiable->notifications & NOTIFY_TASK_ERROR); 44 } 45 46 47 48 /* Create a new process object. */ 49 50 process_t *process_new() 51 { 52 process_t *process = (process_t *) malloc(sizeof(process_t)); 53 54 if (process == NULL) 55 return NULL; 56 57 process_init(process); 58 return process; 59 } 60 61 /* Discard the given process. */ 62 63 void process_close(process_t *process) 64 { 65 if (process == NULL) 66 return; 67 68 if (l4_is_valid_cap(process->ref)) 69 ipc_cap_free_um(process->ref); 70 71 process_init(process); 72 } 73 74 /* Return any process initiation error. */ 75 76 long process_error(process_t *process) 77 { 78 if (process->notifiable.notifications & NOTIFY_TASK_ALL) 79 return process->notifiable.values.val; 80 else 81 return L4_EOK; 82 } 83 84 /* Discard and free the given process, similar to process_close but freeing the 85 structure instead of reinitialising it. */ 86 87 void process_free(process_t *process) 88 { 89 if (process == NULL) 90 return; 91 92 if (l4_is_valid_cap(process->ref)) 93 ipc_cap_free_um(process->ref); 94 95 free(process); 96 } 97 98 /* Initialise the given process structure. */ 99 100 void process_init(process_t *process) 101 { 102 process->ref = L4_INVALID_CAP; 103 104 /* Initialise the notifiable section of the structure. */ 105 106 process->notifiable.notifications = 0; 107 process->notifiable.pending_notifications = 0; 108 process->notifiable.base = (notifiable_base_t *) process; 109 process->notifiable.handler = NULL; 110 } 111 112 /* A convenience function for creating and starting a process. */ 113 114 long process_spawn(int argc, const char *argv[], process_t **process) 115 { 116 *process = process_new(); 117 118 /* Start the process with the given arguments. */ 119 120 if (*process != NULL) 121 return process_start(*process, argc, argv); 122 else 123 return -L4_ENOMEM; 124 } 125 126 /* Start a process using the given arguments. 127 NOTE: This does not yet obtain input/output pipes. */ 128 129 long process_start(process_t *process, int argc, const char *argv[]) 130 { 131 l4_cap_idx_t server = l4re_env_get_cap(ENV_PROCESS_SERVER_NAME); 132 133 if (l4_is_invalid_cap(server)) 134 return -L4_ENOENT; 135 136 /* Obtain a context for process creation. */ 137 138 file_t context; 139 long err = file_context(&context, server); 140 141 if (err) 142 return err; 143 144 offset_t pos = 0, written; 145 146 for (int i = 0; i < argc; i++) 147 { 148 if (!file_string_set(&context, argv[i], pos, &written)) 149 return -L4_ENOMEM; 150 151 pos += written + 1; 152 } 153 154 /* Obtain a client for the process creator context. */ 155 156 client_ProcessCreatorContext creator(context.ref); 157 158 /* Start the process, obtaining a reference to it. */ 159 160 err = creator.start(argc, &process->ref); 161 162 /* Initialise the notifiable section of the structure. */ 163 164 process->notifiable.notifications = 0; 165 process->notifiable.pending_notifications = 0; 166 process->notifiable.base = (notifiable_base_t *) process; 167 168 /* Close the context, although a separate mechanism could permit contexts to 169 open several processes. */ 170 171 file_close(&context); 172 return err; 173 } 174 175 /* A convenience function for waiting for a started process. */ 176 177 long process_wait(process_t *process, notify_flags_t *flags, notify_values_t *values) 178 { 179 /* Obtain the common notifier. */ 180 181 notifier_t *notifier = notify_get_task(); 182 183 /* Subscribe to the process for notifications. */ 184 185 long err = notify_subscribe(process_notifiable(process), NOTIFY_TASK_ALL, notifier); 186 187 if (err) 188 return err; 189 190 /* Wait for a signal from the process. */ 191 192 err = process_notify_wait_process(process, notifier); 193 194 if (err) 195 return err; 196 197 /* Obtain the notification flags and values. */ 198 199 *flags = process_notifications(process); 200 *values = process_notification_values(process); 201 202 /* Obtain any error, closing the process. */ 203 204 err = process_error(process); 205 process_free(process); 206 return err; 207 } 208 209 210 211 /* Conversion to the generic notification types. */ 212 213 notifiable_t *process_notifiable(process_t *process) 214 { 215 return notify_notifiable((notifiable_base_t *) process); 216 } 217 218 /* Return the notification flags for a process. */ 219 220 notify_flags_t process_notifications(process_t *process) 221 { 222 return notify_notifications((notifiable_base_t *) process); 223 } 224 225 /* Return the notification values for a process. */ 226 227 notify_values_t process_notification_values(process_t *process) 228 { 229 return notify_notification_values((notifiable_base_t *) process); 230 } 231 232 /* Wait for a notification event on a process. */ 233 234 long process_notify_wait_process(process_t *process, notifier_t *notifier) 235 { 236 long err = notify_wait(process_notifiable(process), notifier); 237 238 /* Unsubscribe if a termination notification has been received. */ 239 240 if (!err && _process_terminated(process_notifiable(process))) 241 notify_unsubscribe(process_notifiable(process), notifier); 242 243 return err; 244 } 245 246 /* Wait for notification events on processes. */ 247 248 long process_notify_wait_processes(process_t **process, notifier_t *notifier) 249 { 250 notifiable_t *notifiable; 251 long err = notify_wait_many(¬ifiable, notifier); 252 253 *process = (process_t *) notifiable->base; 254 255 /* Unsubscribe if a termination notification has been received. */ 256 257 if (!err && _process_terminated(notifiable)) 258 notify_unsubscribe(notifiable, notifier); 259 260 return err; 261 } 262 263 // vim: tabstop=2 expandtab shiftwidth=2