source: mainline/uspace/lib/c/generic/task.c@ 28a5ebd

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 28a5ebd was 01900b6, checked in by Martin Decky <martin@…>, 5 years ago

Use an optional output argument instead of errno to propagate the error

The use of errno is troublesome in all other than top-level library
functions since the value in errno might get overwritten by subsequent
inner calls on the error path (e.g. cleanup, deallocation, etc.). The
optional output argument makes it possible to explicitly ignore the
error code if it is not needed, but still to pass it reliably back to
the original caller.

This change affecs async_connect_me_to(),
async_connect_me_to_blocking(), async_connect_kbox(), service_connect(),
service_connect_blocking() and loader_connect().

  • Property mode set to 100644
File size: 13.4 KB
Line 
1/*
2 * Copyright (c) 2006 Jakub Jermar
3 * Copyright (c) 2008 Jiri Svoboda
4 * Copyright (c) 2014 Martin Sucha
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/** @addtogroup libc
32 * @{
33 */
34/** @file
35 */
36
37#include <async.h>
38#include <task.h>
39#include <loader/loader.h>
40#include <stdarg.h>
41#include <str.h>
42#include <ipc/ns.h>
43#include <macros.h>
44#include <assert.h>
45#include <async.h>
46#include <errno.h>
47#include <ns.h>
48#include <stdlib.h>
49#include <udebug.h>
50#include <libc.h>
51#include "private/ns.h"
52#include <vfs/vfs.h>
53
54task_id_t task_get_id(void)
55{
56#ifdef __32_BITS__
57 task_id_t task_id;
58 (void) __SYSCALL1(SYS_TASK_GET_ID, (sysarg_t) &task_id);
59
60 return task_id;
61#endif /* __32_BITS__ */
62
63#ifdef __64_BITS__
64 return (task_id_t) __SYSCALL0(SYS_TASK_GET_ID);
65#endif /* __64_BITS__ */
66}
67
68/** Set the task name.
69 *
70 * @param name The new name, typically the command used to execute the
71 * program.
72 *
73 * @return Zero on success or an error code.
74 */
75errno_t task_set_name(const char *name)
76{
77 assert(name);
78
79 return (errno_t) __SYSCALL2(SYS_TASK_SET_NAME, (sysarg_t) name, str_size(name));
80}
81
82/** Kill a task.
83 *
84 * @param task_id ID of task to kill.
85 *
86 * @return Zero on success or an error code.
87 */
88
89errno_t task_kill(task_id_t task_id)
90{
91 return (errno_t) __SYSCALL1(SYS_TASK_KILL, (sysarg_t) &task_id);
92}
93
94/** Create a new task by running an executable from the filesystem.
95 *
96 * This is really just a convenience wrapper over the more complicated
97 * loader API. Arguments are passed as a null-terminated array of strings.
98 * A debug session is created optionally.
99 *
100 * @param id If not NULL, the ID of the task is stored here on success.
101 * @param wait If not NULL, setup waiting for task's return value and store
102 * the information necessary for waiting here on success.
103 * @param path Pathname of the binary to execute.
104 * @param argv Command-line arguments.
105 * @param rsess Place to store pointer to debug session or @c NULL
106 * not to start a debug session
107 *
108 * @return Zero on success or an error code.
109 *
110 */
111errno_t task_spawnv_debug(task_id_t *id, task_wait_t *wait, const char *path,
112 const char *const args[], async_sess_t **rsess)
113{
114 /* Send default files */
115
116 int fd_stdin = -1;
117 int fd_stdout = -1;
118 int fd_stderr = -1;
119
120 if (stdin != NULL) {
121 (void) vfs_fhandle(stdin, &fd_stdin);
122 }
123
124 if (stdout != NULL) {
125 (void) vfs_fhandle(stdout, &fd_stdout);
126 }
127
128 if (stderr != NULL) {
129 (void) vfs_fhandle(stderr, &fd_stderr);
130 }
131
132 return task_spawnvf_debug(id, wait, path, args, fd_stdin, fd_stdout,
133 fd_stderr, rsess);
134}
135
136/** Create a new task by running an executable from the filesystem.
137 *
138 * This is really just a convenience wrapper over the more complicated
139 * loader API. Arguments are passed as a null-terminated array of strings.
140 *
141 * @param id If not NULL, the ID of the task is stored here on success.
142 * @param wait If not NULL, setup waiting for task's return value and store
143 * the information necessary for waiting here on success.
144 * @param path Pathname of the binary to execute.
145 * @param argv Command-line arguments.
146 *
147 * @return Zero on success or an error code.
148 *
149 */
150errno_t task_spawnv(task_id_t *id, task_wait_t *wait, const char *path,
151 const char *const args[])
152{
153 return task_spawnv_debug(id, wait, path, args, NULL);
154}
155
156/** Create a new task by loading an executable from the filesystem.
157 *
158 * This is really just a convenience wrapper over the more complicated
159 * loader API. Arguments are passed as a null-terminated array of strings.
160 * Files are passed as null-terminated array of pointers to fdi_node_t.
161 * A debug session is created optionally.
162 *
163 * @param id If not NULL, the ID of the task is stored here on success.
164 * @param wait If not NULL, setup waiting for task's return value and store.
165 * @param path Pathname of the binary to execute.
166 * @param argv Command-line arguments
167 * @param std_in File to use as stdin
168 * @param std_out File to use as stdout
169 * @param std_err File to use as stderr
170 * @param rsess Place to store pointer to debug session or @c NULL
171 * not to start a debug session
172 *
173 * @return Zero on success or an error code
174 *
175 */
176errno_t task_spawnvf_debug(task_id_t *id, task_wait_t *wait,
177 const char *path, const char *const args[], int fd_stdin, int fd_stdout,
178 int fd_stderr, async_sess_t **rsess)
179{
180 async_sess_t *ksess = NULL;
181
182 /* Connect to a program loader. */
183 errno_t rc;
184 loader_t *ldr = loader_connect(&rc);
185 if (ldr == NULL)
186 return rc;
187
188 bool wait_initialized = false;
189
190 /* Get task ID. */
191 task_id_t task_id;
192 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 /* Start a debug session if requested */
252 if (rsess != NULL) {
253 ksess = async_connect_kbox(task_id, &rc);
254 if (ksess == NULL) {
255 /* Most likely debugging support is not compiled in */
256 goto error;
257 }
258
259 rc = udebug_begin(ksess);
260 if (rc != EOK)
261 goto error;
262
263 /*
264 * Run it, not waiting for response. It would never come
265 * as the loader is stopped.
266 */
267 loader_run_nowait(ldr);
268 } else {
269 /* Run it. */
270 rc = loader_run(ldr);
271 if (rc != EOK)
272 goto error;
273 }
274
275 /* Success */
276 if (id != NULL)
277 *id = task_id;
278 if (rsess != NULL)
279 *rsess = ksess;
280 return EOK;
281
282error:
283 if (ksess != NULL)
284 async_hangup(ksess);
285 if (wait_initialized)
286 task_cancel_wait(wait);
287
288 /* Error exit */
289 loader_abort(ldr);
290 return rc;
291}
292
293/** Create a new task by running an executable from the filesystem.
294 *
295 * Arguments are passed as a null-terminated array of strings.
296 * Files are passed as null-terminated array of pointers to fdi_node_t.
297 *
298 * @param id If not NULL, the ID of the task is stored here on success.
299 * @param wait If not NULL, setup waiting for task's return value and store.
300 * @param path Pathname of the binary to execute
301 * @param argv Command-line arguments
302 * @param std_in File to use as stdin
303 * @param std_out File to use as stdout
304 * @param std_err File to use as stderr
305 *
306 * @return Zero on success or an error code.
307 *
308 */
309errno_t task_spawnvf(task_id_t *id, task_wait_t *wait, const char *path,
310 const char *const args[], int fd_stdin, int fd_stdout, int fd_stderr)
311{
312 return task_spawnvf_debug(id, wait, path, args, fd_stdin, fd_stdout,
313 fd_stderr, NULL);
314}
315
316/** Create a new task by running an executable from the filesystem.
317 *
318 * This is really just a convenience wrapper over the more complicated
319 * loader API. Arguments are passed in a va_list.
320 *
321 * @param id If not NULL, the ID of the task is stored here on success.
322 * @param wait If not NULL, setup waiting for task's return value and store
323 * the information necessary for waiting here on success.
324 * @param path Pathname of the binary to execute.
325 * @param cnt Number of arguments.
326 * @param ap Command-line arguments.
327 *
328 * @return Zero on success or an error code.
329 *
330 */
331errno_t task_spawn(task_id_t *task_id, task_wait_t *wait, const char *path,
332 int cnt, va_list ap)
333{
334 /* Allocate argument list. */
335 const char **arglist = malloc(cnt * sizeof(const char *));
336 if (arglist == NULL)
337 return ENOMEM;
338
339 /* Fill in arguments. */
340 const char *arg;
341 cnt = 0;
342 do {
343 arg = va_arg(ap, const char *);
344 arglist[cnt++] = arg;
345 } while (arg != NULL);
346
347 /* Spawn task. */
348 errno_t rc = task_spawnv(task_id, wait, path, arglist);
349
350 /* Free argument list. */
351 free(arglist);
352 return rc;
353}
354
355/** Create a new task by running an executable from the filesystem.
356 *
357 * This is really just a convenience wrapper over the more complicated
358 * loader API. Arguments are passed as a null-terminated list of arguments.
359 *
360 * @param id If not NULL, the ID of the task is stored here on success.
361 * @param wait If not NULL, setup waiting for task's return value and store
362 * the information necessary for waiting here on success.
363 * @param path Pathname of the binary to execute.
364 * @param ... Command-line arguments.
365 *
366 * @return Zero on success or an error code.
367 *
368 */
369errno_t task_spawnl(task_id_t *task_id, task_wait_t *wait, const char *path, ...)
370{
371 /* Count the number of arguments. */
372
373 va_list ap;
374 const char *arg;
375 int cnt = 0;
376
377 va_start(ap, path);
378 do {
379 arg = va_arg(ap, const char *);
380 cnt++;
381 } while (arg != NULL);
382 va_end(ap);
383
384 va_start(ap, path);
385 errno_t rc = task_spawn(task_id, wait, path, cnt, ap);
386 va_end(ap);
387
388 return rc;
389}
390
391/** Setup waiting for a task.
392 *
393 * If the task finishes after this call succeeds, it is guaranteed that
394 * task_wait(wait, &texit, &retval) will return correct return value for
395 * the task.
396 *
397 * @param id ID of the task to setup waiting for.
398 * @param wait Information necessary for the later task_wait call is stored here.
399 *
400 * @return EOK on success, else error code.
401 */
402errno_t task_setup_wait(task_id_t id, task_wait_t *wait)
403{
404 errno_t rc;
405 async_sess_t *sess_ns = ns_session_get(&rc);
406 if (sess_ns == NULL)
407 return rc;
408
409 async_exch_t *exch = async_exchange_begin(sess_ns);
410 wait->aid = async_send_2(exch, NS_TASK_WAIT, LOWER32(id), UPPER32(id),
411 &wait->result);
412 async_exchange_end(exch);
413
414 return EOK;
415}
416
417/** Cancel waiting for a task.
418 *
419 * This can be called *instead of* task_wait if the caller is not interested
420 * in waiting for the task anymore.
421 *
422 * This function cannot be called if the task_wait was already called.
423 *
424 * @param wait task_wait_t previously initialized by task_setup_wait.
425 */
426void task_cancel_wait(task_wait_t *wait)
427{
428 async_forget(wait->aid);
429}
430
431/** Wait for a task to finish.
432 *
433 * This function returns correct values even if the task finished in
434 * between task_setup_wait and this task_wait call.
435 *
436 * This function cannot be called more than once with the same task_wait_t
437 * (it can be reused, but must be reinitialized with task_setup_wait first)
438 *
439 * @param wait task_wait_t previously initialized by task_setup_wait.
440 * @param texit Store type of task exit here.
441 * @param retval Store return value of the task here.
442 *
443 * @return EOK on success, else error code.
444 */
445errno_t task_wait(task_wait_t *wait, task_exit_t *texit, int *retval)
446{
447 assert(texit);
448 assert(retval);
449
450 errno_t rc;
451 async_wait_for(wait->aid, &rc);
452
453 if (rc == EOK) {
454 *texit = ipc_get_arg1(&wait->result);
455 *retval = ipc_get_arg2(&wait->result);
456 }
457
458 return rc;
459}
460
461/** Wait for a task to finish by its id.
462 *
463 * Note that this will fail with ENOENT if the task id is not registered in ns
464 * (e.g. if the task finished). If you are spawning a task and need to wait
465 * for its completion, use wait parameter of the task_spawn* functions instead
466 * to prevent a race where the task exits before you may have a chance to wait
467 * wait for it.
468 *
469 * @param id ID of the task to wait for.
470 * @param texit Store type of task exit here.
471 * @param retval Store return value of the task here.
472 *
473 * @return EOK on success, else error code.
474 */
475errno_t task_wait_task_id(task_id_t id, task_exit_t *texit, int *retval)
476{
477 task_wait_t wait;
478 errno_t rc = task_setup_wait(id, &wait);
479 if (rc != EOK)
480 return rc;
481
482 return task_wait(&wait, texit, retval);
483}
484
485errno_t task_retval(int val)
486{
487 errno_t rc;
488 async_sess_t *sess_ns = ns_session_get(&rc);
489 if (sess_ns == NULL)
490 return rc;
491
492 async_exch_t *exch = async_exchange_begin(sess_ns);
493 rc = (errno_t) async_req_1_0(exch, NS_RETVAL, val);
494 async_exchange_end(exch);
495
496 return rc;
497}
498
499/** @}
500 */
Note: See TracBrowser for help on using the repository browser.