source: mainline/uspace/lib/c/generic/task.c@ 035d7d8

Last change on this file since 035d7d8 was 456f7ae, checked in by Matthieu Riolo <matthieu.riolo@…>, 6 years ago

libc: Separated task event functions

Conflicts:

uspace/lib/c/Makefile
uspace/lib/c/include/task.h

  • Property mode set to 100644
File size: 12.3 KB
RevLine 
[f30e6a0b]1/*
[df4ed85]2 * Copyright (c) 2006 Jakub Jermar
[c98e6ee]3 * Copyright (c) 2008 Jiri Svoboda
[1c635d6]4 * Copyright (c) 2014 Martin Sucha
[b22b0a94]5 * Copyright (c) 2015 Michal Koutny
[f30e6a0b]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.
[b2951e2]30 */
31
[fadd381]32/** @addtogroup libc
[b2951e2]33 * @{
34 */
35/** @file
[ee369f3]36 */
[f30e6a0b]37
[79ae36dd]38#include <assert.h>
[ee369f3]39#include <async.h>
[79ae36dd]40#include <errno.h>
[7b616e2]41#include <ns.h>
[38d150e]42#include <stdlib.h>
[1be7bee]43#include <ipc/taskman.h>
[79ae36dd]44#include <libc.h>
[1be7bee]45#include <loader/loader.h>
46#include <macros.h>
47#include <malloc.h>
48#include <stdarg.h>
49#include <str.h>
50#include <task.h>
[df02460]51#include <vfs/vfs.h>
[1be7bee]52#include "private/ns.h"
53#include "private/task.h"
54
55static async_sess_t *session_taskman = NULL;
[f30e6a0b]56
[d3b8c1f]57task_id_t task_get_id(void)
[f30e6a0b]58{
[dd8d5a7]59#ifdef __32_BITS__
[f30e6a0b]60 task_id_t task_id;
[d3b8c1f]61 (void) __SYSCALL1(SYS_TASK_GET_ID, (sysarg_t) &task_id);
[a35b458]62
[f30e6a0b]63 return task_id;
[dd8d5a7]64#endif /* __32_BITS__ */
[a35b458]65
[dd8d5a7]66#ifdef __64_BITS__
67 return (task_id_t) __SYSCALL0(SYS_TASK_GET_ID);
68#endif /* __64_BITS__ */
[f30e6a0b]69}
[b2951e2]70
[456f7ae]71async_exch_t *taskman_exchange_begin(void)
[1be7bee]72{
73 /* Lazy connection */
74 if (session_taskman == NULL) {
75 // TODO unify exchange mgmt with taskman_handshake/__init
76 session_taskman = service_connect_blocking(EXCHANGE_SERIALIZE,
77 SERVICE_TASKMAN,
78 TASKMAN_CONTROL,
79 0);
80 }
81
82 if (session_taskman == NULL) {
83 return NULL;
84 }
85
86 async_exch_t *exch = async_exchange_begin(session_taskman);
87 return exch;
88}
89
[456f7ae]90void taskman_exchange_end(async_exch_t *exch)
[1be7bee]91{
92 async_exchange_end(exch);
93}
94
[bc18d63]95/** Set the task name.
96 *
[ee369f3]97 * @param name The new name, typically the command used to execute the
98 * program.
99 *
[cde999a]100 * @return Zero on success or an error code.
[bc18d63]101 */
[b7fd2a0]102errno_t task_set_name(const char *name)
[bc18d63]103{
[79ae36dd]104 assert(name);
[a35b458]105
[b7fd2a0]106 return (errno_t) __SYSCALL2(SYS_TASK_SET_NAME, (sysarg_t) name, str_size(name));
[bc18d63]107}
108
[1e9f8ab]109/** Kill a task.
110 *
111 * @param task_id ID of task to kill.
112 *
[cde999a]113 * @return Zero on success or an error code.
[1e9f8ab]114 */
115
[b7fd2a0]116errno_t task_kill(task_id_t task_id)
[1e9f8ab]117{
[b7fd2a0]118 return (errno_t) __SYSCALL1(SYS_TASK_KILL, (sysarg_t) &task_id);
[1e9f8ab]119}
120
[1be7bee]121/** Setup waiting for a task.
122 *
123 * If the task finishes after this call succeeds, it is guaranteed that
124 * task_wait(wait, &texit, &retval) will return correct return value for
125 * the task.
126 *
127 * @param id ID of the task to setup waiting for.
128 * @param wait Information necessary for the later task_wait call is stored here.
129 *
130 * @return EOK on success, else error code.
[d4ec49e]131 * @return TODO check this doesn't return EINVAL -- clash with task_wait
[1be7bee]132 */
133static errno_t task_setup_wait(task_id_t id, task_wait_t *wait)
134{
[2f44fafd]135 assert(wait->flags);
[d4ec49e]136 if (wait->flags & TASK_WAIT_BOTH) {
137 wait->flags |= (TASK_WAIT_RETVAL | TASK_WAIT_EXIT);
138 }
139 wait->tid = id;
[1be7bee]140 async_exch_t *exch = taskman_exchange_begin();
141 if (exch == NULL)
142 return EIO;
143
144 wait->aid = async_send_3(exch, TASKMAN_WAIT, LOWER32(id), UPPER32(id),
145 wait->flags, &wait->result);
146 taskman_exchange_end(exch);
147
148 return EOK;
149}
150
[45454e9b]151/** Create a new task by running an executable from the filesystem.
152 *
153 * This is really just a convenience wrapper over the more complicated
[0485135]154 * loader API. Arguments are passed as a null-terminated array of strings.
[c98e6ee]155 *
[79ae36dd]156 * @param id If not NULL, the ID of the task is stored here on success.
[1c635d6]157 * @param wait If not NULL, setup waiting for task's return value and store
158 * the information necessary for waiting here on success.
[79ae36dd]159 * @param path Pathname of the binary to execute.
160 * @param argv Command-line arguments.
161 *
[cde999a]162 * @return Zero on success or an error code.
[ee369f3]163 *
[c98e6ee]164 */
[b7fd2a0]165errno_t task_spawnv(task_id_t *id, task_wait_t *wait, const char *path,
[1c635d6]166 const char *const args[])
[ae45201]167{
168 /* Send default files */
[a35b458]169
[bb9ec2d]170 int fd_stdin = -1;
171 int fd_stdout = -1;
172 int fd_stderr = -1;
[a35b458]173
[bb9ec2d]174 if (stdin != NULL) {
175 (void) vfs_fhandle(stdin, &fd_stdin);
176 }
[a35b458]177
[bb9ec2d]178 if (stdout != NULL) {
179 (void) vfs_fhandle(stdout, &fd_stdout);
180 }
181
182 if (stderr != NULL) {
183 (void) vfs_fhandle(stderr, &fd_stderr);
184 }
[a35b458]185
[bb9ec2d]186 return task_spawnvf(id, wait, path, args, fd_stdin, fd_stdout,
187 fd_stderr);
[ae45201]188}
189
190/** Create a new task by running an executable from the filesystem.
191 *
192 * This is really just a convenience wrapper over the more complicated
193 * loader API. Arguments are passed as a null-terminated array of strings.
194 * Files are passed as null-terminated array of pointers to fdi_node_t.
195 *
[bb9ec2d]196 * @param id If not NULL, the ID of the task is stored here on success.
197 * @param wait If not NULL, setup waiting for task's return value and store
198 * @param path Pathname of the binary to execute.
199 * @param argv Command-line arguments.
200 * @param std_in File to use as stdin.
201 * @param std_out File to use as stdout.
202 * @param std_err File to use as stderr.
[ae45201]203 *
[cde999a]204 * @return Zero on success or an error code.
[ae45201]205 *
206 */
[b7fd2a0]207errno_t task_spawnvf(task_id_t *id, task_wait_t *wait, const char *path,
[bb9ec2d]208 const char *const args[], int fd_stdin, int fd_stdout, int fd_stderr)
[c98e6ee]209{
[bfd1546]210 /* Connect to a program loader. */
[79ae36dd]211 loader_t *ldr = loader_connect();
[0485135]212 if (ldr == NULL)
213 return EREFUSED;
[a35b458]214
[1c635d6]215 bool wait_initialized = false;
[a35b458]216
[47e0a05b]217 /* Get task ID. */
[79ae36dd]218 task_id_t task_id;
[b7fd2a0]219 errno_t rc = loader_get_task_id(ldr, &task_id);
[45454e9b]220 if (rc != EOK)
[47e0a05b]221 goto error;
[a35b458]222
[622cdbe]223 /* Send spawner's current working directory. */
224 rc = loader_set_cwd(ldr);
225 if (rc != EOK)
226 goto error;
[a35b458]227
[bb9ec2d]228 /* Send program binary. */
229 rc = loader_set_program_path(ldr, path);
[45454e9b]230 if (rc != EOK)
[17b2aac]231 goto error;
[a35b458]232
[4470e26]233 /* Send arguments. */
[ee369f3]234 rc = loader_set_args(ldr, args);
[17b2aac]235 if (rc != EOK)
236 goto error;
[a35b458]237
[ae45201]238 /* Send files */
[5126f80]239 int root = vfs_root();
240 if (root >= 0) {
241 rc = loader_add_inbox(ldr, "root", root);
[9c4cf0d]242 vfs_put(root);
[5126f80]243 if (rc != EOK)
244 goto error;
245 }
[a35b458]246
[bb9ec2d]247 if (fd_stdin >= 0) {
248 rc = loader_add_inbox(ldr, "stdin", fd_stdin);
249 if (rc != EOK)
250 goto error;
251 }
[a35b458]252
[bb9ec2d]253 if (fd_stdout >= 0) {
254 rc = loader_add_inbox(ldr, "stdout", fd_stdout);
255 if (rc != EOK)
256 goto error;
257 }
[a35b458]258
[bb9ec2d]259 if (fd_stderr >= 0) {
260 rc = loader_add_inbox(ldr, "stderr", fd_stderr);
261 if (rc != EOK)
262 goto error;
[1b20da0]263 }
[a35b458]264
[4470e26]265 /* Load the program. */
266 rc = loader_load_program(ldr);
267 if (rc != EOK)
268 goto error;
[a35b458]269
[1c635d6]270 /* Setup waiting for return value if needed */
271 if (wait) {
272 rc = task_setup_wait(task_id, wait);
273 if (rc != EOK)
274 goto error;
275 wait_initialized = true;
276 }
[a35b458]277
[4470e26]278 /* Run it. */
279 rc = loader_run(ldr);
[17b2aac]280 if (rc != EOK)
281 goto error;
[a35b458]282
[c98e6ee]283 /* Success */
[0485135]284 if (id != NULL)
285 *id = task_id;
[a35b458]286
[0485135]287 return EOK;
[a35b458]288
[c98e6ee]289error:
[1c635d6]290 if (wait_initialized)
291 task_cancel_wait(wait);
[a35b458]292
[ee369f3]293 /* Error exit */
[45454e9b]294 loader_abort(ldr);
[0485135]295 return rc;
296}
297
[9f5cf68]298/** Create a new task by running an executable from the filesystem.
299 *
300 * This is really just a convenience wrapper over the more complicated
301 * loader API. Arguments are passed in a va_list.
302 *
303 * @param id If not NULL, the ID of the task is stored here on success.
[1c635d6]304 * @param wait If not NULL, setup waiting for task's return value and store
305 * the information necessary for waiting here on success.
[9f5cf68]306 * @param path Pathname of the binary to execute.
307 * @param cnt Number of arguments.
308 * @param ap Command-line arguments.
309 *
[cde999a]310 * @return Zero on success or an error code.
[9f5cf68]311 *
312 */
[b7fd2a0]313errno_t task_spawn(task_id_t *task_id, task_wait_t *wait, const char *path,
[1c635d6]314 int cnt, va_list ap)
[9f5cf68]315{
316 /* Allocate argument list. */
317 const char **arglist = malloc(cnt * sizeof(const char *));
318 if (arglist == NULL)
319 return ENOMEM;
[a35b458]320
[9f5cf68]321 /* Fill in arguments. */
322 const char *arg;
323 cnt = 0;
324 do {
325 arg = va_arg(ap, const char *);
326 arglist[cnt++] = arg;
327 } while (arg != NULL);
[a35b458]328
[9f5cf68]329 /* Spawn task. */
[b7fd2a0]330 errno_t rc = task_spawnv(task_id, wait, path, arglist);
[a35b458]331
[9f5cf68]332 /* Free argument list. */
333 free(arglist);
334 return rc;
335}
336
[0485135]337/** Create a new task by running an executable from the filesystem.
338 *
339 * This is really just a convenience wrapper over the more complicated
340 * loader API. Arguments are passed as a null-terminated list of arguments.
341 *
[79ae36dd]342 * @param id If not NULL, the ID of the task is stored here on success.
[1c635d6]343 * @param wait If not NULL, setup waiting for task's return value and store
344 * the information necessary for waiting here on success.
[79ae36dd]345 * @param path Pathname of the binary to execute.
346 * @param ... Command-line arguments.
347 *
[cde999a]348 * @return Zero on success or an error code.
[0485135]349 *
350 */
[b7fd2a0]351errno_t task_spawnl(task_id_t *task_id, task_wait_t *wait, const char *path, ...)
[0485135]352{
[79ae36dd]353 /* Count the number of arguments. */
[a35b458]354
[0485135]355 va_list ap;
356 const char *arg;
[79ae36dd]357 int cnt = 0;
[a35b458]358
[0485135]359 va_start(ap, path);
360 do {
361 arg = va_arg(ap, const char *);
362 cnt++;
363 } while (arg != NULL);
364 va_end(ap);
[a35b458]365
[0485135]366 va_start(ap, path);
[b7fd2a0]367 errno_t rc = task_spawn(task_id, wait, path, cnt, ap);
[0485135]368 va_end(ap);
[a35b458]369
[0485135]370 return rc;
[adb4d6c2]371}
372
[1c635d6]373/** Cancel waiting for a task.
374 *
375 * This can be called *instead of* task_wait if the caller is not interested
376 * in waiting for the task anymore.
377 *
378 * This function cannot be called if the task_wait was already called.
379 *
380 * @param wait task_wait_t previously initialized by task_setup_wait.
381 */
[1433ecda]382void task_cancel_wait(task_wait_t *wait)
383{
[1c635d6]384 async_forget(wait->aid);
385}
386
387/** Wait for a task to finish.
388 *
389 * This function returns correct values even if the task finished in
390 * between task_setup_wait and this task_wait call.
391 *
392 * This function cannot be called more than once with the same task_wait_t
393 * (it can be reused, but must be reinitialized with task_setup_wait first)
394 *
395 * @param wait task_wait_t previously initialized by task_setup_wait.
396 * @param texit Store type of task exit here.
397 * @param retval Store return value of the task here.
[1b20da0]398 *
[d4ec49e]399 * @return EOK on success
400 * @return EINVAL on lost wait TODO other error codes
[1c635d6]401 */
[b7fd2a0]402errno_t task_wait(task_wait_t *wait, task_exit_t *texit, int *retval)
[1c635d6]403{
[b7fd2a0]404 errno_t rc;
[1c635d6]405 async_wait_for(wait->aid, &rc);
[d4ec49e]406
407 if (rc == EOK || rc == EINVAL) {
[5044114]408 if (wait->flags & TASK_WAIT_EXIT && texit)
409 *texit = ipc_get_arg1(wait->result);
410 if (wait->flags & TASK_WAIT_RETVAL && retval)
411 *retval = ipc_get_arg2(wait->result);
[d4ec49e]412
413 }
414
415 if (rc == EOK) {
416 /* Is there another wait to be done? Wait for it! */
417 int old_flags = wait->flags;
418 wait->flags = ipc_get_arg3(wait->result);
419 if (wait->flags != 0 && (old_flags & TASK_WAIT_BOTH)) {
420 rc = task_setup_wait(wait->tid, wait);
421 }
422 } else {
423 wait->flags = 0;
[1c635d6]424 }
425
[7114d83]426 return rc;
427}
428
[1c635d6]429/** Wait for a task to finish by its id.
430 *
431 * Note that this will fail with ENOENT if the task id is not registered in ns
432 * (e.g. if the task finished). If you are spawning a task and need to wait
433 * for its completion, use wait parameter of the task_spawn* functions instead
434 * to prevent a race where the task exits before you may have a chance to wait
435 * wait for it.
436 *
437 * @param id ID of the task to wait for.
[1be7bee]438 * @param flags Specify for which task output we wait
[1c635d6]439 * @param texit Store type of task exit here.
440 * @param retval Store return value of the task here.
[1b20da0]441 *
[1c635d6]442 * @return EOK on success, else error code.
443 */
[1be7bee]444errno_t task_wait_task_id(task_id_t id, int flags, task_exit_t *texit, int *retval)
[1c635d6]445{
446 task_wait_t wait;
[1be7bee]447 wait.flags = flags;
[b7fd2a0]448 errno_t rc = task_setup_wait(id, &wait);
[1be7bee]449
[1c635d6]450 if (rc != EOK)
451 return rc;
[a35b458]452
[1c635d6]453 return task_wait(&wait, texit, retval);
454}
455
[2f44fafd]456errno_t task_retval_internal(int val, bool wait_for_exit)
[7114d83]457{
[1be7bee]458 async_exch_t *exch = taskman_exchange_begin();
459 if (exch == NULL)
[7b616e2]460 return EIO;
461
[2f44fafd]462 errno_t rc = (int) async_req_2_0(exch, TASKMAN_RETVAL, val, wait_for_exit);
[1be7bee]463 taskman_exchange_end(exch);
464
[79ae36dd]465 return rc;
[ee369f3]466}
467
[2f44fafd]468errno_t task_retval(int val)
469{
470 return task_retval_internal(val, false);
471}
472
[1be7bee]473void __task_init(async_sess_t *sess)
474{
475 assert(session_taskman == NULL);
476 session_taskman = sess;
477}
478
[fadd381]479/** @}
[b2951e2]480 */
Note: See TracBrowser for help on using the repository browser.