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

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

taskman: IPC builerplate for task event API

  • Actual implementation tomorrow…

Conflicts:

uspace/lib/c/include/task.h

  • Property mode set to 100644
File size: 13.9 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
38#include <assert.h>
39#include <async.h>
40#include <errno.h>
41#include <ns.h>
42#include <stdlib.h>
43#include <ipc/taskman.h>
44#include <libc.h>
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>
51#include <vfs/vfs.h>
52#include "private/ns.h"
53#include "private/task.h"
54
55static async_sess_t *session_taskman = NULL;
56static task_event_handler_t task_event_handler = NULL;
57
58task_id_t task_get_id(void)
59{
60#ifdef __32_BITS__
61 task_id_t task_id;
62 (void) __SYSCALL1(SYS_TASK_GET_ID, (sysarg_t) &task_id);
63
64 return task_id;
65#endif /* __32_BITS__ */
66
67#ifdef __64_BITS__
68 return (task_id_t) __SYSCALL0(SYS_TASK_GET_ID);
69#endif /* __64_BITS__ */
70}
71
72static async_exch_t *taskman_exchange_begin(void)
73{
74 /* Lazy connection */
75 if (session_taskman == NULL) {
76 // TODO unify exchange mgmt with taskman_handshake/__init
77 session_taskman = service_connect_blocking(EXCHANGE_SERIALIZE,
78 SERVICE_TASKMAN,
79 TASKMAN_CONTROL,
80 0);
81 }
82
83 if (session_taskman == NULL) {
84 return NULL;
85 }
86
87 async_exch_t *exch = async_exchange_begin(session_taskman);
88 return exch;
89}
90
91static void taskman_exchange_end(async_exch_t *exch)
92{
93 async_exchange_end(exch);
94}
95
96/** Set the task name.
97 *
98 * @param name The new name, typically the command used to execute the
99 * program.
100 *
101 * @return Zero on success or an error code.
102 */
103errno_t task_set_name(const char *name)
104{
105 assert(name);
106
107 return (errno_t) __SYSCALL2(SYS_TASK_SET_NAME, (sysarg_t) name, str_size(name));
108}
109
110/** Kill a task.
111 *
112 * @param task_id ID of task to kill.
113 *
114 * @return Zero on success or an error code.
115 */
116
117errno_t task_kill(task_id_t task_id)
118{
119 return (errno_t) __SYSCALL1(SYS_TASK_KILL, (sysarg_t) &task_id);
120}
121
122/** Setup waiting for a task.
123 *
124 * If the task finishes after this call succeeds, it is guaranteed that
125 * task_wait(wait, &texit, &retval) will return correct return value for
126 * the task.
127 *
128 * @param id ID of the task to setup waiting for.
129 * @param wait Information necessary for the later task_wait call is stored here.
130 *
131 * @return EOK on success, else error code.
132 * @return TODO check this doesn't return EINVAL -- clash with task_wait
133 */
134static errno_t task_setup_wait(task_id_t id, task_wait_t *wait)
135{
136 assert(wait->flags);
137 if (wait->flags & TASK_WAIT_BOTH) {
138 wait->flags |= (TASK_WAIT_RETVAL | TASK_WAIT_EXIT);
139 }
140 wait->tid = id;
141 async_exch_t *exch = taskman_exchange_begin();
142 if (exch == NULL)
143 return EIO;
144
145 wait->aid = async_send_3(exch, TASKMAN_WAIT, LOWER32(id), UPPER32(id),
146 wait->flags, &wait->result);
147 taskman_exchange_end(exch);
148
149 return EOK;
150}
151
152/** Create a new task by running an executable from the filesystem.
153 *
154 * This is really just a convenience wrapper over the more complicated
155 * loader API. Arguments are passed as a null-terminated array of strings.
156 *
157 * @param id If not NULL, the ID of the task is stored here on success.
158 * @param wait If not NULL, setup waiting for task's return value and store
159 * the information necessary for waiting here on success.
160 * @param path Pathname of the binary to execute.
161 * @param argv Command-line arguments.
162 *
163 * @return Zero on success or an error code.
164 *
165 */
166errno_t task_spawnv(task_id_t *id, task_wait_t *wait, const char *path,
167 const char *const args[])
168{
169 /* Send default files */
170
171 int fd_stdin = -1;
172 int fd_stdout = -1;
173 int fd_stderr = -1;
174
175 if (stdin != NULL) {
176 (void) vfs_fhandle(stdin, &fd_stdin);
177 }
178
179 if (stdout != NULL) {
180 (void) vfs_fhandle(stdout, &fd_stdout);
181 }
182
183 if (stderr != NULL) {
184 (void) vfs_fhandle(stderr, &fd_stderr);
185 }
186
187 return task_spawnvf(id, wait, path, args, fd_stdin, fd_stdout,
188 fd_stderr);
189}
190
191/** Create a new task by running an executable from the filesystem.
192 *
193 * This is really just a convenience wrapper over the more complicated
194 * loader API. Arguments are passed as a null-terminated array of strings.
195 * Files are passed as null-terminated array of pointers to fdi_node_t.
196 *
197 * @param id If not NULL, the ID of the task is stored here on success.
198 * @param wait If not NULL, setup waiting for task's return value and store
199 * @param path Pathname of the binary to execute.
200 * @param argv Command-line arguments.
201 * @param std_in File to use as stdin.
202 * @param std_out File to use as stdout.
203 * @param std_err File to use as stderr.
204 *
205 * @return Zero on success or an error code.
206 *
207 */
208errno_t task_spawnvf(task_id_t *id, task_wait_t *wait, const char *path,
209 const char *const args[], int fd_stdin, int fd_stdout, int fd_stderr)
210{
211 /* Connect to a program loader. */
212 loader_t *ldr = loader_connect();
213 if (ldr == NULL)
214 return EREFUSED;
215
216 bool wait_initialized = false;
217
218 /* Get task ID. */
219 task_id_t task_id;
220 errno_t rc = loader_get_task_id(ldr, &task_id);
221 if (rc != EOK)
222 goto error;
223
224 /* Send spawner's current working directory. */
225 rc = loader_set_cwd(ldr);
226 if (rc != EOK)
227 goto error;
228
229 /* Send program binary. */
230 rc = loader_set_program_path(ldr, path);
231 if (rc != EOK)
232 goto error;
233
234 /* Send arguments. */
235 rc = loader_set_args(ldr, args);
236 if (rc != EOK)
237 goto error;
238
239 /* Send files */
240 int root = vfs_root();
241 if (root >= 0) {
242 rc = loader_add_inbox(ldr, "root", root);
243 vfs_put(root);
244 if (rc != EOK)
245 goto error;
246 }
247
248 if (fd_stdin >= 0) {
249 rc = loader_add_inbox(ldr, "stdin", fd_stdin);
250 if (rc != EOK)
251 goto error;
252 }
253
254 if (fd_stdout >= 0) {
255 rc = loader_add_inbox(ldr, "stdout", fd_stdout);
256 if (rc != EOK)
257 goto error;
258 }
259
260 if (fd_stderr >= 0) {
261 rc = loader_add_inbox(ldr, "stderr", fd_stderr);
262 if (rc != EOK)
263 goto error;
264 }
265
266 /* Load the program. */
267 rc = loader_load_program(ldr);
268 if (rc != EOK)
269 goto error;
270
271 /* Setup waiting for return value if needed */
272 if (wait) {
273 rc = task_setup_wait(task_id, wait);
274 if (rc != EOK)
275 goto error;
276 wait_initialized = true;
277 }
278
279 /* Run it. */
280 rc = loader_run(ldr);
281 if (rc != EOK)
282 goto error;
283
284 /* Success */
285 if (id != NULL)
286 *id = task_id;
287
288 return EOK;
289
290error:
291 if (wait_initialized)
292 task_cancel_wait(wait);
293
294 /* Error exit */
295 loader_abort(ldr);
296 return rc;
297}
298
299/** Create a new task by running an executable from the filesystem.
300 *
301 * This is really just a convenience wrapper over the more complicated
302 * loader API. Arguments are passed in a va_list.
303 *
304 * @param id If not NULL, the ID of the task is stored here on success.
305 * @param wait If not NULL, setup waiting for task's return value and store
306 * the information necessary for waiting here on success.
307 * @param path Pathname of the binary to execute.
308 * @param cnt Number of arguments.
309 * @param ap Command-line arguments.
310 *
311 * @return Zero on success or an error code.
312 *
313 */
314errno_t task_spawn(task_id_t *task_id, task_wait_t *wait, const char *path,
315 int cnt, va_list ap)
316{
317 /* Allocate argument list. */
318 const char **arglist = malloc(cnt * sizeof(const char *));
319 if (arglist == NULL)
320 return ENOMEM;
321
322 /* Fill in arguments. */
323 const char *arg;
324 cnt = 0;
325 do {
326 arg = va_arg(ap, const char *);
327 arglist[cnt++] = arg;
328 } while (arg != NULL);
329
330 /* Spawn task. */
331 errno_t rc = task_spawnv(task_id, wait, path, arglist);
332
333 /* Free argument list. */
334 free(arglist);
335 return rc;
336}
337
338/** Create a new task by running an executable from the filesystem.
339 *
340 * This is really just a convenience wrapper over the more complicated
341 * loader API. Arguments are passed as a null-terminated list of arguments.
342 *
343 * @param id If not NULL, the ID of the task is stored here on success.
344 * @param wait If not NULL, setup waiting for task's return value and store
345 * the information necessary for waiting here on success.
346 * @param path Pathname of the binary to execute.
347 * @param ... Command-line arguments.
348 *
349 * @return Zero on success or an error code.
350 *
351 */
352errno_t task_spawnl(task_id_t *task_id, task_wait_t *wait, const char *path, ...)
353{
354 /* Count the number of arguments. */
355
356 va_list ap;
357 const char *arg;
358 int cnt = 0;
359
360 va_start(ap, path);
361 do {
362 arg = va_arg(ap, const char *);
363 cnt++;
364 } while (arg != NULL);
365 va_end(ap);
366
367 va_start(ap, path);
368 errno_t rc = task_spawn(task_id, wait, path, cnt, ap);
369 va_end(ap);
370
371 return rc;
372}
373
374/** Cancel waiting for a task.
375 *
376 * This can be called *instead of* task_wait if the caller is not interested
377 * in waiting for the task anymore.
378 *
379 * This function cannot be called if the task_wait was already called.
380 *
381 * @param wait task_wait_t previously initialized by task_setup_wait.
382 */
383void task_cancel_wait(task_wait_t *wait)
384{
385 async_forget(wait->aid);
386}
387
388/** Wait for a task to finish.
389 *
390 * This function returns correct values even if the task finished in
391 * between task_setup_wait and this task_wait call.
392 *
393 * This function cannot be called more than once with the same task_wait_t
394 * (it can be reused, but must be reinitialized with task_setup_wait first)
395 *
396 * @param wait task_wait_t previously initialized by task_setup_wait.
397 * @param texit Store type of task exit here.
398 * @param retval Store return value of the task here.
399 *
400 * @return EOK on success
401 * @return EINVAL on lost wait TODO other error codes
402 */
403errno_t task_wait(task_wait_t *wait, task_exit_t *texit, int *retval)
404{
405 errno_t rc;
406 async_wait_for(wait->aid, &rc);
407
408 if (rc == EOK || rc == EINVAL) {
409 if (wait->flags & TASK_WAIT_EXIT && texit)
410 *texit = ipc_get_arg1(wait->result);
411 if (wait->flags & TASK_WAIT_RETVAL && retval)
412 *retval = ipc_get_arg2(wait->result);
413
414 }
415
416 if (rc == EOK) {
417 /* Is there another wait to be done? Wait for it! */
418 int old_flags = wait->flags;
419 wait->flags = ipc_get_arg3(wait->result);
420 if (wait->flags != 0 && (old_flags & TASK_WAIT_BOTH)) {
421 rc = task_setup_wait(wait->tid, wait);
422 }
423 } else {
424 wait->flags = 0;
425 }
426
427 return rc;
428}
429
430/** Wait for a task to finish by its id.
431 *
432 * Note that this will fail with ENOENT if the task id is not registered in ns
433 * (e.g. if the task finished). If you are spawning a task and need to wait
434 * for its completion, use wait parameter of the task_spawn* functions instead
435 * to prevent a race where the task exits before you may have a chance to wait
436 * wait for it.
437 *
438 * @param id ID of the task to wait for.
439 * @param flags Specify for which task output we 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_id(task_id_t id, int flags, task_exit_t *texit, int *retval)
446{
447 task_wait_t wait;
448 wait.flags = flags;
449 errno_t rc = task_setup_wait(id, &wait);
450
451 if (rc != EOK)
452 return rc;
453
454 return task_wait(&wait, texit, retval);
455}
456
457errno_t task_retval_internal(int val, bool wait_for_exit)
458{
459 async_exch_t *exch = taskman_exchange_begin();
460 if (exch == NULL)
461 return EIO;
462
463 errno_t rc = (int) async_req_2_0(exch, TASKMAN_RETVAL, val, wait_for_exit);
464 taskman_exchange_end(exch);
465
466 return rc;
467}
468
469errno_t task_retval(int val)
470{
471 return task_retval_internal(val, false);
472}
473
474// TODO extract to separate module
475static void taskman_task_event(ipc_callid_t iid, ipc_call_t *icall)
476{
477 task_id_t tid = (task_id_t)
478 MERGE_LOUP32(IPC_GET_ARG1(*icall), IPC_GET_ARG2(*icall));
479 int flags = IPC_GET_ARG3(*icall);
480 task_exit_t texit = IPC_GET_ARG4(*icall);
481 int retval = IPC_GET_ARG5(*icall);
482
483 task_event_handler(tid, flags, texit, retval);
484
485 async_answer_0(iid, EOK);
486}
487
488static void taskman_event_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
489{
490 /* Accept connection */
491 async_answer_0(iid, EOK);
492
493 while (true) {
494 ipc_call_t call;
495 ipc_callid_t callid = async_get_call(&call);
496
497 if (!IPC_GET_IMETHOD(call)) {
498 /* Hangup, TODO explain or handle differntly */
499 break;
500 }
501
502 switch (IPC_GET_IMETHOD(call)) {
503 case TASKMAN_EV_TASK:
504 taskman_task_event(callid, &call);
505 break;
506 default:
507 async_answer_0(callid, ENOTSUP);
508 break;
509 }
510 }
511}
512
513/**
514 * Blocks, calls handler in another fibril
515 */
516int task_register_event_handler(task_event_handler_t handler)
517{
518 /*
519 * so far support assign once, modification cannot be naïve due to
520 * races
521 */
522 assert(task_event_handler == NULL);
523 assert(handler != NULL); /* no support for "unregistration" */
524
525 task_event_handler = handler;
526
527 async_exch_t *exch = taskman_exchange_begin();
528 aid_t req = async_send_0(exch, TASKMAN_EVENT_CALLBACK, NULL);
529
530 int rc = async_connect_to_me(exch, 0, 0, 0, taskman_event_conn, NULL);
531 taskman_exchange_end(exch);
532
533 if (rc != EOK) {
534 return rc;
535 }
536
537 sysarg_t retval;
538 async_wait_for(req, &retval);
539 return retval;
540}
541
542void __task_init(async_sess_t *sess)
543{
544 assert(session_taskman == NULL);
545 session_taskman = sess;
546}
547
548/** @}
549 */
Note: See TracBrowser for help on using the repository browser.