source: mainline/uspace/lib/c/generic/task.c@ aa0faeca

Last change on this file since aa0faeca was 3529f148, checked in by Matthieu Riolo <matthieu.riolo@…>, 5 years ago

Adding types task_wait_flag_t and ipc_start_flag_t which replaces makros

  • Property mode set to 100644
File size: 11.8 KB
Line 
1/*
2 * Copyright (c) 2006 Jakub Jermar
3 * Copyright (c) 2008 Jiri Svoboda
4 * Copyright (c) 2014 Martin Sucha
5 * Copyright (c) 2015 Michal Koutny
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * - Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * - The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/** @addtogroup libc
33 * @{
34 */
35/** @file
36 */
37#include <task.h>
38#include <loader/loader.h>
39#include <stdarg.h>
40#include <str.h>
41#include <macros.h>
42#include <assert.h>
43#include <async.h>
44#include <errno.h>
45#include <ns.h>
46#include <stdlib.h>
47#include <ipc/taskman.h>
48#include <libc.h>
49#include <taskman.h>
50#include <vfs/vfs.h>
51
52#include "private/task.h"
53#include "private/taskman.h"
54
55task_id_t task_get_id(void)
56{
57#ifdef __32_BITS__
58 task_id_t task_id;
59 (void) __SYSCALL1(SYS_TASK_GET_ID, (sysarg_t) &task_id);
60
61 return task_id;
62#endif /* __32_BITS__ */
63
64#ifdef __64_BITS__
65 return (task_id_t) __SYSCALL0(SYS_TASK_GET_ID);
66#endif /* __64_BITS__ */
67}
68
69/** Set the task name.
70 *
71 * @param name The new name, typically the command used to execute the
72 * program.
73 *
74 * @return Zero on success or an error code.
75 */
76errno_t task_set_name(const char *name)
77{
78 assert(name);
79
80 return (errno_t) __SYSCALL2(SYS_TASK_SET_NAME, (sysarg_t) name, str_size(name));
81}
82
83/** Kill a task.
84 *
85 * @param task_id ID of task to kill.
86 *
87 * @return Zero on success or an error code.
88 */
89
90errno_t task_kill(task_id_t task_id)
91{
92 return (errno_t) __SYSCALL1(SYS_TASK_KILL, (sysarg_t) &task_id);
93}
94
95/** Setup waiting for a task.
96 *
97 * If the task finishes after this call succeeds, it is guaranteed that
98 * task_wait(wait, &texit, &retval) will return correct return value for
99 * the task.
100 *
101 * @param id ID of the task to setup waiting for.
102 * @param wait Information necessary for the later task_wait call is stored here.
103 *
104 * @return EOK on success, else error code.
105 */
106static errno_t task_setup_wait(task_id_t id, task_wait_t *wait)
107{
108 assert(wait->flags);
109 if (wait->flags & TASK_WAIT_BOTH) {
110 wait->flags |= (TASK_WAIT_RETVAL | TASK_WAIT_EXIT);
111 }
112 wait->tid = id;
113 async_exch_t *exch = taskman_exchange_begin();
114 if (exch == NULL)
115 return EIO;
116
117 wait->aid = async_send_3(exch, TASKMAN_WAIT, LOWER32(id), UPPER32(id),
118 wait->flags, &wait->result);
119 taskman_exchange_end(exch);
120
121 return EOK;
122}
123
124/** Create a new task by running an executable from the filesystem.
125 *
126 * This is really just a convenience wrapper over the more complicated
127 * loader API. Arguments are passed as a null-terminated array of strings.
128 *
129 * @param id If not NULL, the ID of the task is stored here on success.
130 * @param wait If not NULL, setup waiting for task's return value and store
131 * the information necessary for waiting here on success.
132 * @param path Pathname of the binary to execute.
133 * @param argv Command-line arguments.
134 *
135 * @return Zero on success or an error code.
136 *
137 */
138errno_t task_spawnv(task_id_t *id, task_wait_t *wait, const char *path,
139 const char *const args[])
140{
141 /* Send default files */
142
143 int fd_stdin = -1;
144 int fd_stdout = -1;
145 int fd_stderr = -1;
146
147 if (stdin != NULL) {
148 (void) vfs_fhandle(stdin, &fd_stdin);
149 }
150
151 if (stdout != NULL) {
152 (void) vfs_fhandle(stdout, &fd_stdout);
153 }
154
155 if (stderr != NULL) {
156 (void) vfs_fhandle(stderr, &fd_stderr);
157 }
158
159 return task_spawnvf(id, wait, path, args, fd_stdin, fd_stdout,
160 fd_stderr);
161}
162
163/** Create a new task by running an executable from the filesystem.
164 *
165 * This is really just a convenience wrapper over the more complicated
166 * loader API. Arguments are passed as a null-terminated array of strings.
167 * Files are passed as null-terminated array of pointers to fdi_node_t.
168 *
169 * @param id If not NULL, the ID of the task is stored here on success.
170 * @param wait If not NULL, setup waiting for task's return value and store
171 * @param path Pathname of the binary to execute.
172 * @param argv Command-line arguments.
173 * @param std_in File to use as stdin.
174 * @param std_out File to use as stdout.
175 * @param std_err File to use as stderr.
176 *
177 * @return Zero on success or an error code.
178 *
179 */
180errno_t task_spawnvf(task_id_t *id, task_wait_t *wait, const char *path,
181 const char *const args[], int fd_stdin, int fd_stdout, int fd_stderr)
182{
183 /* Connect to a program loader. */
184 loader_t *ldr = loader_connect();
185 if (ldr == NULL)
186 return EREFUSED;
187
188 bool wait_initialized = false;
189
190 /* Get task ID. */
191 task_id_t task_id;
192 errno_t rc = loader_get_task_id(ldr, &task_id);
193 if (rc != EOK)
194 goto error;
195
196 /* Send spawner's current working directory. */
197 rc = loader_set_cwd(ldr);
198 if (rc != EOK)
199 goto error;
200
201 /* Send program binary. */
202 rc = loader_set_program_path(ldr, path);
203 if (rc != EOK)
204 goto error;
205
206 /* Send arguments. */
207 rc = loader_set_args(ldr, args);
208 if (rc != EOK)
209 goto error;
210
211 /* Send files */
212 int root = vfs_root();
213 if (root >= 0) {
214 rc = loader_add_inbox(ldr, "root", root);
215 vfs_put(root);
216 if (rc != EOK)
217 goto error;
218 }
219
220 if (fd_stdin >= 0) {
221 rc = loader_add_inbox(ldr, "stdin", fd_stdin);
222 if (rc != EOK)
223 goto error;
224 }
225
226 if (fd_stdout >= 0) {
227 rc = loader_add_inbox(ldr, "stdout", fd_stdout);
228 if (rc != EOK)
229 goto error;
230 }
231
232 if (fd_stderr >= 0) {
233 rc = loader_add_inbox(ldr, "stderr", fd_stderr);
234 if (rc != EOK)
235 goto error;
236 }
237
238 /* Load the program. */
239 rc = loader_load_program(ldr);
240 if (rc != EOK)
241 goto error;
242
243 /* Setup waiting for return value if needed */
244 if (wait) {
245 rc = task_setup_wait(task_id, wait);
246 if (rc != EOK)
247 goto error;
248 wait_initialized = true;
249 }
250
251 /* Run it. */
252 rc = loader_run(ldr);
253 if (rc != EOK)
254 goto error;
255
256 /* Success */
257 if (id != NULL)
258 *id = task_id;
259
260 return EOK;
261
262error:
263 if (wait_initialized)
264 task_cancel_wait(wait);
265
266 /* Error exit */
267 loader_abort(ldr);
268 return rc;
269}
270
271/** Create a new task by running an executable from the filesystem.
272 *
273 * This is really just a convenience wrapper over the more complicated
274 * loader API. Arguments are passed in a va_list.
275 *
276 * @param id If not NULL, the ID of the task is stored here on success.
277 * @param wait If not NULL, setup waiting for task's return value and store
278 * the information necessary for waiting here on success.
279 * @param path Pathname of the binary to execute.
280 * @param cnt Number of arguments.
281 * @param ap Command-line arguments.
282 *
283 * @return Zero on success or an error code.
284 *
285 */
286errno_t task_spawn(task_id_t *task_id, task_wait_t *wait, const char *path,
287 int cnt, va_list ap)
288{
289 /* Allocate argument list. */
290 const char **arglist = malloc(cnt * sizeof(const char *));
291 if (arglist == NULL)
292 return ENOMEM;
293
294 /* Fill in arguments. */
295 const char *arg;
296 cnt = 0;
297 do {
298 arg = va_arg(ap, const char *);
299 arglist[cnt++] = arg;
300 } while (arg != NULL);
301
302 /* Spawn task. */
303 errno_t rc = task_spawnv(task_id, wait, path, arglist);
304
305 /* Free argument list. */
306 free(arglist);
307 return rc;
308}
309
310/** Create a new task by running an executable from the filesystem.
311 *
312 * This is really just a convenience wrapper over the more complicated
313 * loader API. Arguments are passed as a null-terminated list of arguments.
314 *
315 * @param id If not NULL, the ID of the task is stored here on success.
316 * @param wait If not NULL, setup waiting for task's return value and store
317 * the information necessary for waiting here on success.
318 * @param path Pathname of the binary to execute.
319 * @param ... Command-line arguments.
320 *
321 * @return Zero on success or an error code.
322 *
323 */
324errno_t task_spawnl(task_id_t *task_id, task_wait_t *wait, const char *path, ...)
325{
326 /* Count the number of arguments. */
327
328 va_list ap;
329 const char *arg;
330 int cnt = 0;
331
332 va_start(ap, path);
333 do {
334 arg = va_arg(ap, const char *);
335 cnt++;
336 } while (arg != NULL);
337 va_end(ap);
338
339 va_start(ap, path);
340 errno_t rc = task_spawn(task_id, wait, path, cnt, ap);
341 va_end(ap);
342
343 return rc;
344}
345
346/** Cancel waiting for a task.
347 *
348 * This can be called *instead of* task_wait if the caller is not interested
349 * in waiting for the task anymore.
350 *
351 * This function cannot be called if the task_wait was already called.
352 *
353 * @param wait task_wait_t previously initialized by task_setup_wait.
354 */
355void task_cancel_wait(task_wait_t *wait)
356{
357 async_forget(wait->aid);
358}
359
360/** Wait for a task to finish.
361 *
362 * This function returns correct values even if the task finished in
363 * between task_setup_wait and this task_wait call.
364 *
365 * This function cannot be called more than once with the same task_wait_t
366 * (it can be reused, but must be reinitialized with task_setup_wait first)
367 *
368 * @param[in/out] wait task_wait_t previously initialized by task_setup_wait
369 * or returned by task_wait with non-zero flags. the
370 * flags are updated so that they represent what can be
371 * still waited for.
372 * @param[out] texit Store type of task exit here.
373 * @param[out] retval Store return value of the task here.
374 *
375 * @return EOK on success
376 * @return EINTR on lost wait
377 */
378errno_t task_wait(task_wait_t *wait, task_exit_t *texit, int *retval)
379{
380 errno_t rc;
381 async_wait_for(wait->aid, &rc);
382
383 if (rc == EOK || rc == EINTR) {
384 if (wait->flags & TASK_WAIT_EXIT && texit)
385 *texit = ipc_get_arg1(&wait->result);
386 if (wait->flags & TASK_WAIT_RETVAL && retval)
387 *retval = ipc_get_arg2(&wait->result);
388 }
389
390 if (rc == EOK) {
391 /* Is there another wait to be done? Wait for it! */
392 int old_flags = wait->flags;
393 wait->flags = ipc_get_arg3(&wait->result);
394 if (wait->flags != 0 && (old_flags & TASK_WAIT_BOTH)) {
395 rc = task_setup_wait(wait->tid, wait);
396 }
397 } else {
398 wait->flags = 0;
399 }
400
401 return rc;
402}
403
404/** Wait for a task to finish by its id.
405 *
406 * Note that this will fail with ENOENT if the task id is not registered in ns
407 * (e.g. if the task finished). If you are spawning a task and need to wait
408 * for its completion, use wait parameter of the task_spawn* functions instead
409 * to prevent a race where the task exits before you may have a chance to wait
410 * wait for it.
411 *
412 * @param id ID of the task to wait for.
413 * @param flags Specify for which task output we wait
414 * @param texit Store type of task exit here.
415 * @param retval Store return value of the task here.
416 *
417 * @return EOK on success, else error code.
418 */
419errno_t task_wait_task_id(task_id_t id, task_wait_flag_t flags, task_exit_t *texit, int *retval)
420{
421 task_wait_t wait;
422 wait.flags = flags;
423 errno_t rc = task_setup_wait(id, &wait);
424 if (rc != EOK)
425 return rc;
426
427 return task_wait(&wait, texit, retval);
428}
429
430errno_t task_retval_internal(int val, bool wait_for_exit)
431{
432 async_exch_t *exch = taskman_exchange_begin();
433 if (exch == NULL)
434 return EIO;
435
436 errno_t rc = async_req_2_0(exch, TASKMAN_RETVAL, val, wait_for_exit);
437 taskman_exchange_end(exch);
438
439 return rc;
440}
441
442errno_t task_retval(int val)
443{
444 return task_retval_internal(val, false);
445}
446
447/** @}
448 */
Note: See TracBrowser for help on using the repository browser.