source: mainline/uspace/lib/c/generic/task.c@ 2443ad8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2443ad8 was 2443ad8, checked in by Jiri Svoboda <jiri@…>, 6 years ago

Let trace not use the loader API directly

This fixes traced binaries crashing due to missing working directory
in PCB. It should also prevent similar problems in the future.

  • 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 loader_t *ldr = loader_connect();
184 if (ldr == NULL)
185 return EREFUSED;
186
187 bool wait_initialized = false;
188
189 /* Get task ID. */
190 task_id_t task_id;
191 errno_t rc = loader_get_task_id(ldr, &task_id);
192 if (rc != EOK)
193 goto error;
194
195 /* Send spawner's current working directory. */
196 rc = loader_set_cwd(ldr);
197 if (rc != EOK)
198 goto error;
199
200 /* Send program binary. */
201 rc = loader_set_program_path(ldr, path);
202 if (rc != EOK)
203 goto error;
204
205 /* Send arguments. */
206 rc = loader_set_args(ldr, args);
207 if (rc != EOK)
208 goto error;
209
210 /* Send files */
211 int root = vfs_root();
212 if (root >= 0) {
213 rc = loader_add_inbox(ldr, "root", root);
214 vfs_put(root);
215 if (rc != EOK)
216 goto error;
217 }
218
219 if (fd_stdin >= 0) {
220 rc = loader_add_inbox(ldr, "stdin", fd_stdin);
221 if (rc != EOK)
222 goto error;
223 }
224
225 if (fd_stdout >= 0) {
226 rc = loader_add_inbox(ldr, "stdout", fd_stdout);
227 if (rc != EOK)
228 goto error;
229 }
230
231 if (fd_stderr >= 0) {
232 rc = loader_add_inbox(ldr, "stderr", fd_stderr);
233 if (rc != EOK)
234 goto error;
235 }
236
237 /* Load the program. */
238 rc = loader_load_program(ldr);
239 if (rc != EOK)
240 goto error;
241
242 /* Setup waiting for return value if needed */
243 if (wait) {
244 rc = task_setup_wait(task_id, wait);
245 if (rc != EOK)
246 goto error;
247 wait_initialized = true;
248 }
249
250 /* Start a debug session if requested */
251 if (rsess != NULL) {
252 ksess = async_connect_kbox(task_id);
253 if (ksess == NULL) {
254 /* Most likely debugging support is not compiled in */
255 rc = ENOTSUP;
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 async_sess_t *sess_ns = ns_session_get();
405 if (sess_ns == NULL)
406 return EIO;
407
408 async_exch_t *exch = async_exchange_begin(sess_ns);
409 wait->aid = async_send_2(exch, NS_TASK_WAIT, LOWER32(id), UPPER32(id),
410 &wait->result);
411 async_exchange_end(exch);
412
413 return EOK;
414}
415
416/** Cancel waiting for a task.
417 *
418 * This can be called *instead of* task_wait if the caller is not interested
419 * in waiting for the task anymore.
420 *
421 * This function cannot be called if the task_wait was already called.
422 *
423 * @param wait task_wait_t previously initialized by task_setup_wait.
424 */
425void task_cancel_wait(task_wait_t *wait)
426{
427 async_forget(wait->aid);
428}
429
430/** Wait for a task to finish.
431 *
432 * This function returns correct values even if the task finished in
433 * between task_setup_wait and this task_wait call.
434 *
435 * This function cannot be called more than once with the same task_wait_t
436 * (it can be reused, but must be reinitialized with task_setup_wait first)
437 *
438 * @param wait task_wait_t previously initialized by task_setup_wait.
439 * @param texit Store type of task exit here.
440 * @param retval Store return value of the task here.
441 *
442 * @return EOK on success, else error code.
443 */
444errno_t task_wait(task_wait_t *wait, task_exit_t *texit, int *retval)
445{
446 assert(texit);
447 assert(retval);
448
449 errno_t rc;
450 async_wait_for(wait->aid, &rc);
451
452 if (rc == EOK) {
453 *texit = ipc_get_arg1(&wait->result);
454 *retval = ipc_get_arg2(&wait->result);
455 }
456
457 return rc;
458}
459
460/** Wait for a task to finish by its id.
461 *
462 * Note that this will fail with ENOENT if the task id is not registered in ns
463 * (e.g. if the task finished). If you are spawning a task and need to wait
464 * for its completion, use wait parameter of the task_spawn* functions instead
465 * to prevent a race where the task exits before you may have a chance to wait
466 * wait for it.
467 *
468 * @param id ID of the task to wait for.
469 * @param texit Store type of task exit here.
470 * @param retval Store return value of the task here.
471 *
472 * @return EOK on success, else error code.
473 */
474errno_t task_wait_task_id(task_id_t id, task_exit_t *texit, int *retval)
475{
476 task_wait_t wait;
477 errno_t rc = task_setup_wait(id, &wait);
478 if (rc != EOK)
479 return rc;
480
481 return task_wait(&wait, texit, retval);
482}
483
484errno_t task_retval(int val)
485{
486 async_sess_t *sess_ns = ns_session_get();
487 if (sess_ns == NULL)
488 return EIO;
489
490 async_exch_t *exch = async_exchange_begin(sess_ns);
491 errno_t rc = (errno_t) async_req_1_0(exch, NS_RETVAL, val);
492 async_exchange_end(exch);
493
494 return rc;
495}
496
497/** @}
498 */
Note: See TracBrowser for help on using the repository browser.