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(process_t *process) 40 { 41 return ((process->notifiable.notifications & NOTIFY_TASK_SIGNAL) && 42 (process->notifiable.values.sig == 0)) || 43 (process->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 /* Initialise the given process structure. */ 85 86 void process_init(process_t *process) 87 { 88 process->ref = L4_INVALID_CAP; 89 90 /* Initialise the notifiable section of the structure. */ 91 92 process->notifiable.notifications = 0; 93 process->notifiable.pending_notifications = 0; 94 process->notifiable.base = (notifiable_base_t *) process; 95 process->notifiable.handler = NULL; 96 } 97 98 /* Start a process using the given arguments. 99 NOTE: This does not yet obtain input/output pipes. */ 100 101 long process_start(process_t *process, int argc, const char *argv[]) 102 { 103 l4_cap_idx_t server = l4re_env_get_cap(ENV_PROCESS_SERVER_NAME); 104 105 if (l4_is_invalid_cap(server)) 106 return -L4_ENOENT; 107 108 /* Obtain a context for process creation. */ 109 110 file_t context; 111 long err = file_context(&context, server); 112 113 if (err) 114 return err; 115 116 offset_t pos = 0, written; 117 118 for (int i = 0; i < argc; i++) 119 { 120 if (!file_string_set(&context, argv[i], pos, &written)) 121 return -L4_ENOMEM; 122 123 pos += written + 1; 124 } 125 126 /* Obtain a client for the process creator context. */ 127 128 client_ProcessCreatorContext creator(context.ref); 129 130 /* Start the process, obtaining a reference to it. */ 131 132 err = creator.start(argc, &process->ref); 133 134 /* Initialise the notifiable section of the structure. */ 135 136 process->notifiable.notifications = 0; 137 process->notifiable.pending_notifications = 0; 138 process->notifiable.base = (notifiable_base_t *) process; 139 140 /* Close the context, although a separate mechanism could permit contexts to 141 open several processes. */ 142 143 file_close(&context); 144 return err; 145 } 146 147 148 149 /* NOTE: Much of the code below could be unified with the file-related 150 notification code. */ 151 152 /* Opaque notifier type for process_notifier_t. */ 153 154 struct process_notifier 155 { 156 ObjectNotifier *obj; 157 }; 158 159 /* Conversion to the generic notification types. */ 160 161 notifiable_t *process_notifiable(process_t *process) 162 { 163 return &process->notifiable; 164 } 165 166 /* Return the notification flags for a process. */ 167 168 notify_flags_t process_notifications(process_t *process) 169 { 170 return process->notifiable.notifications; 171 } 172 173 /* Return the notification values for a process. */ 174 175 notify_values_t process_notification_values(process_t *process) 176 { 177 return process->notifiable.values; 178 } 179 180 /* Close a notifier object. */ 181 182 void process_notify_close(process_notifier_t *notifier) 183 { 184 delete notifier->obj; 185 delete notifier; 186 } 187 188 /* Obtain a local notifier object. */ 189 190 process_notifier_t *process_notify_local() 191 { 192 process_notifier_t *notifier = new process_notifier_t; 193 194 notifier->obj = notifier_get_local_notifier(); 195 return notifier; 196 } 197 198 /* Obtain the task-wide notifier object. */ 199 200 process_notifier_t *process_notify_task() 201 { 202 process_notifier_t *notifier = new process_notifier_t; 203 204 notifier->obj = notifier_get_task_notifier(); 205 return notifier; 206 } 207 208 /* Subscribe to notification events on a process. */ 209 210 long process_notify_subscribe(process_t *process, notify_flags_t flags, process_notifier_t *notifier) 211 { 212 return notifier->obj->subscribe(process_notifiable(process), flags); 213 } 214 215 /* Unsubscribe from notification events on a process. */ 216 217 long process_notify_unsubscribe(process_t *process, process_notifier_t *notifier) 218 { 219 return notifier->obj->unsubscribe(process_notifiable(process)); 220 } 221 222 /* Wait for a notification event on a process. */ 223 224 long process_notify_wait_process(process_t *process, process_notifier_t *notifier) 225 { 226 long err = notifier->obj->wait_object(process_notifiable(process)); 227 228 /* Unsubscribe if a termination notification has been received. */ 229 230 if (!err && _process_terminated(process)) 231 process_notify_unsubscribe(process, notifier); 232 233 return err; 234 } 235 236 /* Wait for notification events on processes. */ 237 238 long process_notify_wait_processes(process_t **process, process_notifier_t *notifier) 239 { 240 notifiable_t *notifiable; 241 long err = notifier->obj->wait(¬ifiable); 242 243 *process = (process_t *) notifiable->base; 244 245 /* Unsubscribe if a termination notification has been received. */ 246 247 if (!err && _process_terminated(*process)) 248 process_notify_unsubscribe(*process, notifier); 249 250 return err; 251 } 252 253 // vim: tabstop=2 expandtab shiftwidth=2