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