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 /* Create a new process object. */ 38 39 process_t *process_new() 40 { 41 process_t *process = (process_t *) malloc(sizeof(process_t)); 42 43 if (process == NULL) 44 return NULL; 45 46 process_init(process); 47 return process; 48 } 49 50 /* Discard the given process. */ 51 52 void process_close(process_t *process) 53 { 54 if (process == NULL) 55 return; 56 57 if (l4_is_valid_cap(process->ref)) 58 ipc_cap_free_um(process->ref); 59 60 process_init(process); 61 } 62 63 /* Return any process initiation error. */ 64 65 long process_error(process_t *process) 66 { 67 if (process->notifiable.notifications & NOTIFY_TASK_ALL) 68 return process->notifiable.values.val; 69 else 70 return L4_EOK; 71 } 72 73 /* Discard and free the given process, similar to process_close but freeing the 74 structure instead of reinitialising it. */ 75 76 void process_free(process_t *process) 77 { 78 if (process == NULL) 79 return; 80 81 if (l4_is_valid_cap(process->ref)) 82 ipc_cap_free_um(process->ref); 83 84 free(process); 85 } 86 87 /* Initialise the given process structure. */ 88 89 void process_init(process_t *process) 90 { 91 process->ref = L4_INVALID_CAP; 92 93 /* Initialise the notifiable section of the structure. */ 94 95 process->notifiable.notifications = 0; 96 process->notifiable.pending_notifications = 0; 97 process->notifiable.base = (notifiable_base_t *) process; 98 process->notifiable.handler = NULL; 99 } 100 101 /* A convenience function for creating and starting a process. */ 102 103 long process_spawn(int argc, const char *argv[], file_t *reader, 104 file_t *writer, file_t *error, process_t **process) 105 { 106 *process = process_new(); 107 108 /* Start the process with the given arguments. */ 109 110 if (*process != NULL) 111 return process_start(*process, argc, argv, reader, writer, error); 112 else 113 return -L4_ENOMEM; 114 } 115 116 /* Start a process using the given arguments. */ 117 118 long process_start(process_t *process, int argc, const char *argv[], 119 file_t *reader, file_t *writer, file_t *error) 120 { 121 l4_cap_idx_t server = l4re_env_get_cap(ENV_PROCESS_SERVER_NAME); 122 123 if (l4_is_invalid_cap(server)) 124 return -L4_ENOENT; 125 126 /* Obtain a context for process creation. */ 127 128 file_t context; 129 long err = file_context(&context, server); 130 131 if (err) 132 return err; 133 134 offset_t pos = 0, written; 135 136 for (int i = 0; i < argc; i++) 137 { 138 if (!file_string_set(&context, argv[i], pos, &written)) 139 return -L4_ENOMEM; 140 141 pos += written + 1; 142 } 143 144 /* Obtain a client for the process creator context. */ 145 146 client_ProcessCreatorContext creator(context.ref); 147 148 /* Start the process, obtaining a reference to it. */ 149 150 err = creator.start(argc, 151 reader != NULL ? reader->ref : (l4_cap_idx_t) L4_INVALID_CAP, 152 writer != NULL ? writer->ref : (l4_cap_idx_t) L4_INVALID_CAP, 153 error != NULL ? error->ref : (l4_cap_idx_t) L4_INVALID_CAP, 154 &process->ref); 155 156 /* Initialise the notifiable section of the structure. */ 157 158 process->notifiable.notifications = 0; 159 process->notifiable.pending_notifications = 0; 160 process->notifiable.base = (notifiable_base_t *) process; 161 162 /* Close the context, although a separate mechanism could permit contexts to 163 open several processes. */ 164 165 file_close(&context); 166 return err; 167 } 168 169 /* A convenience function for waiting for a started process. */ 170 171 long process_wait(process_t *process, notify_flags_t *flags, notify_values_t *values) 172 { 173 /* Obtain the common notifier. */ 174 175 notifier_t *notifier = notify_get_task(); 176 177 /* Subscribe to the process for notifications. */ 178 179 long err = notify_subscribe(process_notifiable(process), NOTIFY_TASK_ALL, notifier); 180 181 if (err) 182 return err; 183 184 /* Wait for a signal from the process. */ 185 186 err = process_notify_wait_process(process, notifier); 187 188 if (err) 189 return err; 190 191 /* Obtain the notification flags and values. */ 192 193 *flags = process_notifications(process); 194 *values = process_notification_values(process); 195 196 /* Obtain any error, closing the process. */ 197 198 err = process_error(process); 199 process_free(process); 200 return err; 201 } 202 203 204 205 /* Conversion to the generic notification types. */ 206 207 notifiable_t *process_notifiable(process_t *process) 208 { 209 return notify_notifiable((notifiable_base_t *) process); 210 } 211 212 /* Return the notification flags for a process. */ 213 214 notify_flags_t process_notifications(process_t *process) 215 { 216 return notify_notifications((notifiable_base_t *) process); 217 } 218 219 /* Return the notification values for a process. */ 220 221 notify_values_t process_notification_values(process_t *process) 222 { 223 return notify_notification_values((notifiable_base_t *) process); 224 } 225 226 /* Return whether a notification has indicated process termination. */ 227 228 int process_terminated(notifiable_t *notifiable) 229 { 230 return ((notifiable->notifications & NOTIFY_TASK_SIGNAL) && 231 (notifiable->values.sig == 0)) || 232 (notifiable->notifications & NOTIFY_TASK_ERROR); 233 } 234 235 /* Wait for a notification event on a process. */ 236 237 long process_notify_wait_process(process_t *process, notifier_t *notifier) 238 { 239 long err = notify_wait(process_notifiable(process), notifier); 240 241 /* Unsubscribe if a termination notification has been received. */ 242 243 if (!err && process_terminated(process_notifiable(process))) 244 notify_unsubscribe(process_notifiable(process), notifier); 245 246 return err; 247 } 248 249 /* Wait for notification events on processes. */ 250 251 long process_notify_wait_processes(process_t **process, notifier_t *notifier) 252 { 253 notifiable_t *notifiable; 254 long err = notify_wait_many(¬ifiable, notifier); 255 256 /* Unsubscribe if a termination notification has been received. */ 257 258 if (!err && process_terminated(notifiable)) 259 { 260 *process = (process_t *) notifiable->base; 261 notify_unsubscribe(notifiable, notifier); 262 } 263 264 return err; 265 } 266 267 // vim: tabstop=2 expandtab shiftwidth=2