2 Copyright 2015, 2016 IoT.bzh
4 author: José Bollo <jose.bollo@iot.bzh>
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
25 #include <sys/types.h>
32 #include <linux/xattr.h>
34 #include "simulation/smack.h"
36 #include <sys/smack.h>
39 #include <json-c/json.h>
42 #include "utils-dir.h"
43 #include "utils-json.h"
44 #include "afm-launch-mode.h"
45 #include "afm-launch.h"
49 * State of a launched/running application
52 as_starting, /* start in progress */
53 as_running, /* started and running */
54 as_paused, /* paused */
55 as_terminating, /* termination in progress */
56 as_terminated /* terminated */
60 * Structure for recording a runner
63 struct apprun *next_by_runid; /* link for hashing by runid */
64 struct apprun *next_by_pgid; /* link for hashing by pgid */
65 int runid; /* runid */
66 pid_t pids[2]; /* pids (0: group leader, 1: slave) */
67 enum appstate state; /* current state of the application */
68 json_object *appli; /* json object describing the application */
72 * Count of item by hash table
74 #define ROOT_RUNNERS_COUNT 32
77 * Maximum count of simultaneous running application
79 #define MAX_RUNNER_COUNT 32767
82 * Hash tables of runners by runid and by pgid
84 static struct apprun *runners_by_runid[ROOT_RUNNERS_COUNT];
85 static struct apprun *runners_by_pgid[ROOT_RUNNERS_COUNT];
88 * List of terminated runners
90 static struct apprun *terminated_runners = NULL;
95 static int runnercount = 0;
100 static int runnerid = 0;
103 * Path name of the directory for applications in the
104 * home directory of the user.
106 static const char fwk_user_app_dir[] = FWK_USER_APP_DIR;
107 static const char fwk_user_app_label[] = FWK_USER_APP_DIR_LABEL;
110 * Path of the root directory for applications of the
113 static char *homeappdir;
115 /****************** manages pids **********************/
118 * Get a runner by its 'pid' (NULL if not found)
120 static struct apprun *runner_of_pid(pid_t pid)
123 struct apprun *result;
125 for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
126 result = runners_by_pgid[i];
127 while (result != NULL) {
128 if (result->pids[0] == pid || result->pids[1] == pid)
130 result = result->next_by_pgid;
136 /****************** manages pgids **********************/
139 * Get a runner by its 'pgid' (NULL if not found)
141 static struct apprun *runner_of_pgid(pid_t pgid)
143 struct apprun *result;
145 result = runners_by_pgid[pgid & (ROOT_RUNNERS_COUNT - 1)];
146 while (result && result->pids[0] != pgid)
147 result = result->next_by_pgid;
152 * Insert a 'runner' for its pgid
154 static void pgid_insert(struct apprun *runner)
156 struct apprun **prev;
158 prev = &runners_by_pgid[runner->pids[0] & (ROOT_RUNNERS_COUNT - 1)];
159 runner->next_by_pgid = *prev;
164 * Remove a 'runner' for its pgid
166 static void pgid_remove(struct apprun *runner)
168 struct apprun **prev;
170 prev = &runners_by_pgid[runner->pids[0] & (ROOT_RUNNERS_COUNT - 1)];
172 if (*prev == runner) {
173 *prev = runner->next_by_pgid;
176 prev = &(*prev)->next_by_pgid;
180 /****************** manages runners (by runid) **********************/
183 * Is a 'runner' alive?
185 static inline int is_alive(struct apprun *runner)
187 switch(runner->state) {
197 * Is a 'runner' dead?
199 static inline int is_dead(struct apprun *runner)
201 switch(runner->state) {
211 * Is a 'runner' running?
213 static inline int is_running(struct apprun *runner)
215 switch(runner->state) {
225 * Is a 'runner' paused?
227 static inline int is_paused(struct apprun *runner)
229 switch(runner->state) {
238 * Get a runner by its 'runid' (NULL if not found)
240 static struct apprun *getrunner(int runid)
242 struct apprun *result;
244 result = runners_by_runid[runid & (ROOT_RUNNERS_COUNT - 1)];
245 while (result && result->runid != runid)
246 result = result->next_by_runid;
251 * Free an existing 'runner'
253 static void freerunner(struct apprun *runner)
255 struct apprun **prev;
257 /* get previous pointer to runner */
258 prev = &runners_by_runid[runner->runid & (ROOT_RUNNERS_COUNT - 1)];
260 while(*prev != runner) {
261 prev = &(*prev)->next_by_runid;
266 *prev = runner->next_by_runid;
270 json_object_put(runner->appli);
275 * Cleans the list of runners from its terminated
277 static void cleanrunners()
279 struct apprun *runner;
280 while (terminated_runners) {
281 runner = terminated_runners;
282 terminated_runners = runner->next_by_pgid;
288 * Create a new runner for the 'appli'
290 * Returns the created runner or NULL
293 static struct apprun *createrunner(json_object *appli)
295 struct apprun *result;
296 struct apprun **prev;
302 if (runnercount >= MAX_RUNNER_COUNT) {
308 if (runnerid > MAX_RUNNER_COUNT)
310 } while(getrunner(runnerid));
312 /* create the structure */
313 result = calloc(1, sizeof * result);
317 /* initialize it linked to the list */
318 prev = &runners_by_runid[runnerid & (ROOT_RUNNERS_COUNT - 1)];
319 result->next_by_runid = *prev;
320 result->next_by_pgid = NULL;
321 result->runid = runnerid;
322 result->pids[0] = result->pids[1] = 0;
323 result->state = as_starting;
324 result->appli = json_object_get(appli);
331 /**************** signaling ************************/
333 static void started(int runid)
337 static void paused(int runid)
341 static void resumed(int runid)
345 static void terminated(int runid)
349 static void removed(int runid)
353 /**************** running ************************/
356 * Sends (with pgkill) the signal 'sig' to the process group
357 * for 'runid' and put the runner's state to 'tostate'
358 * in case of success.
360 * Only processes in the state 'as_running' or 'as_paused'
363 * Returns 0 in case of success or -1 in case of error.
365 static int killrunner(int runid, int sig, enum appstate tostate)
368 struct apprun *runner = getrunner(runid);
369 if (runner == NULL) {
373 else if (is_dead(runner)) {
377 else if (runner->state == tostate) {
381 rc = killpg(runner->pids[0], sig);
383 runner->state = tostate;
389 * Signal callback called on SIGCHLD. This is set using sigaction.
391 static void on_sigchld(int signum, siginfo_t *info, void *uctxt)
393 struct apprun *runner;
395 /* retrieves the runner */
396 runner = runner_of_pid(info->si_pid);
400 /* known runner, inspect cause of signal */
401 switch(info->si_code) {
406 /* update the state */
407 runner->state = as_terminated;
408 /* remove it from pgid list */
410 runner->next_by_pgid = terminated_runners;
411 terminated_runners = runner;
412 /* ensures that all the group terminates */
413 killpg(runner->pids[0], SIGKILL);
417 /* update the state */
418 runner->state = as_paused;
422 /* update the state */
423 runner->state = as_running;
428 /**************** handle afm_launch_desc *********************/
431 * Initialize the data of the launch description 'desc'
432 * for the application 'appli' and the 'mode'.
434 * Returns 0 in case of success or -1 in case of error.
436 static int fill_launch_desc(struct json_object *appli,
437 enum afm_launch_mode mode, struct afm_launch_desc *desc)
441 assert(is_valid_launch_mode(mode));
444 if(!j_read_object_at(appli, "public", &pub)
445 || !j_read_string_at(appli, "path", &desc->path)
446 || !j_read_string_at(appli, "id", &desc->appid)
447 || !j_read_string_at(appli, "content", &desc->content)
448 || !j_read_string_at(appli, "type", &desc->type)
449 || !j_read_string_at(pub, "name", &desc->name)
450 || !j_read_integer_at(pub, "width", &desc->width)
451 || !j_read_integer_at(pub, "height", &desc->height)) {
459 static const char *null = NULL;
460 desc->bindings = &null;
464 desc->home = homeappdir;
469 /**************** report state of runner *********************/
472 * Creates a json object that describes the state of 'runner'.
474 * Returns the created object or NULL in case of error.
476 static json_object *mkstate(struct apprun *runner)
479 struct json_object *result, *obj, *pids;
483 result = json_object_new_object();
488 if (!j_add_integer(result, "runid", runner->runid))
492 if (is_alive(runner)) {
493 pids = j_add_new_array(result, "pids");
496 if (!j_add_integer(pids, NULL, runner->pids[0]))
498 if (runner->pids[1] && !j_add_integer(pids, NULL, runner->pids[1]))
503 switch(runner->state) {
512 state = "terminated";
515 if (!j_add_string(result, "state", state))
518 /* the application id */
519 rc = json_object_object_get_ex(runner->appli, "public", &obj);
521 rc = json_object_object_get_ex(obj, "id", &obj);
523 if (!j_add(result, "id", obj))
525 json_object_get(obj);
531 json_object_put(result);
537 /**************** API handling ************************/
540 * Starts the application described by 'appli' for the 'mode'.
541 * In case of remote start, it returns in uri the uri to
544 * A reference to 'appli' is kept during the live of the
545 * runner. This is made using json_object_get. Thus be aware
546 * that further modifications to 'appli' might create errors.
548 * Returns the runid in case of success or -1 in case of error
550 int afm_run_start(struct json_object *appli, enum afm_launch_mode mode,
553 struct apprun *runner;
554 struct afm_launch_desc desc;
556 sigset_t saved, blocked;
558 assert(is_valid_launch_mode(mode));
559 assert(mode == mode_local || uri != NULL);
560 assert(uri == NULL || *uri == NULL);
562 /* prepare to launch */
563 rc = fill_launch_desc(appli, mode, &desc);
566 runner = createrunner(appli);
570 /* block children signals until launched */
571 sigemptyset(&blocked);
572 sigaddset(&blocked, SIGCHLD);
573 sigprocmask(SIG_BLOCK, &blocked, &saved);
576 rc = afm_launch(&desc, runner->pids, uri);
579 sigprocmask(SIG_SETMASK, &saved, NULL);
580 ERROR("can't start, afm_launch failed: %m");
586 runner->state = as_running;
590 /* unblock children signal now */
591 sigprocmask(SIG_SETMASK, &saved, NULL);
596 * Terminates the runner of 'runid'
598 * Returns 0 in case of success or -1 in case of error
600 int afm_run_terminate(int runid)
602 return killrunner(runid, SIGTERM, as_terminating);
606 * Stops (aka pause) the runner of 'runid'
608 * Returns 0 in case of success or -1 in case of error
610 int afm_run_pause(int runid)
612 return killrunner(runid, SIGSTOP, as_paused);
616 * Continue (aka resume) the runner of 'runid'
618 * Returns 0 in case of success or -1 in case of error
620 int afm_run_resume(int runid)
622 return killrunner(runid, SIGCONT, as_running);
626 * Get the list of the runners.
628 * Returns the list or NULL in case of error.
630 struct json_object *afm_run_list()
632 struct json_object *result, *obj;
633 struct apprun *runner;
636 /* creates the object */
637 result = json_object_new_array();
641 /* iterate over runners */
642 for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
643 runner = runners_by_runid[i];
645 if (is_alive(runner)) {
646 /* adds the living runner */
647 obj = mkstate(runner);
650 if (json_object_array_add(result, obj) == -1) {
651 json_object_put(obj);
655 runner = runner->next_by_runid;
661 json_object_put(result);
668 * Get the state of the runner of 'runid'.
670 * Returns the state or NULL in case of success
672 struct json_object *afm_run_state(int runid)
674 struct apprun *runner = getrunner(runid);
675 if (runner == NULL || is_dead(runner)) {
679 return mkstate(runner);
682 /**************** INITIALISATION **********************/
685 * Initialize the module
692 struct passwd passwd, *pw;
693 struct sigaction siga;
696 rc = afm_launch_initialize();
700 /* computes the 'homeappdir' */
702 rc = getpwuid_r(me, &passwd, buf, sizeof buf, &pw);
703 if (rc || pw == NULL) {
704 errno = rc ? errno : ENOENT;
705 ERROR("getpwuid_r failed for uid=%d: %m",(int)me);
708 rc = asprintf(&homeappdir, "%s/%s", passwd.pw_dir, fwk_user_app_dir);
711 ERROR("allocating homeappdir for uid=%d failed", (int)me);
714 rc = create_directory(homeappdir, 0755, 1);
715 if (rc && errno != EEXIST) {
716 ERROR("creation of directory %s failed: %m", homeappdir);
720 rc = smack_remove_label_for_path(homeappdir,
721 XATTR_NAME_SMACKTRANSMUTE, 0);
722 if (rc < 0 && errno != ENODATA) {
723 ERROR("can't remove smack transmutation of directory %s: %m",
728 rc = smack_set_label_for_path(homeappdir, XATTR_NAME_SMACK, 0,
731 ERROR("can't set smack label %s to directory %s: %m",
732 fwk_user_app_label, homeappdir);
736 /* install signal handlers */
737 siga.sa_flags = SA_SIGINFO | SA_NOCLDWAIT;
738 sigemptyset(&siga.sa_mask);
739 sigaddset(&siga.sa_mask, SIGCHLD);
740 siga.sa_sigaction = on_sigchld;
741 sigaction(SIGCHLD, &siga, NULL);