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