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 * Get a runner by its 'runid' (NULL if not found)
185 static struct apprun *getrunner(int runid)
187 struct apprun *result;
189 result = runners_by_runid[runid & (ROOT_RUNNERS_COUNT - 1)];
190 while (result && result->runid != runid)
191 result = result->next_by_runid;
196 * Free an existing 'runner'
198 static void freerunner(struct apprun *runner)
200 struct apprun **prev;
202 /* get previous pointer to runner */
203 prev = &runners_by_runid[runner->runid & (ROOT_RUNNERS_COUNT - 1)];
205 while(*prev != runner) {
206 prev = &(*prev)->next_by_runid;
211 *prev = runner->next_by_runid;
215 json_object_put(runner->appli);
220 * Cleans the list of runners from its terminated
222 static void cleanrunners()
224 struct apprun *runner;
225 while (terminated_runners) {
226 runner = terminated_runners;
227 terminated_runners = runner->next_by_pgid;
233 * Create a new runner for the 'appli'
235 * Returns the created runner or NULL
238 static struct apprun *createrunner(json_object *appli)
240 struct apprun *result;
241 struct apprun **prev;
247 if (runnercount >= MAX_RUNNER_COUNT) {
253 if (runnerid > MAX_RUNNER_COUNT)
255 } while(getrunner(runnerid));
257 /* create the structure */
258 result = calloc(1, sizeof * result);
262 /* initialize it linked to the list */
263 prev = &runners_by_runid[runnerid & (ROOT_RUNNERS_COUNT - 1)];
264 result->next_by_runid = *prev;
265 result->next_by_pgid = NULL;
266 result->runid = runnerid;
267 result->pids[0] = result->pids[1] = 0;
268 result->state = as_starting;
269 result->appli = json_object_get(appli);
276 /**************** signaling ************************/
278 static void started(int runid)
282 static void paused(int runid)
286 static void resumed(int runid)
290 static void terminated(int runid)
294 static void removed(int runid)
298 /**************** running ************************/
301 * Sends (with pgkill) the signal 'sig' to the process group
302 * for 'runid' and put the runner's state to 'tostate'
303 * in case of success.
305 * Only processes in the state 'as_running' or 'as_paused'
308 * Returns 0 in case of success or -1 in case of error.
310 static int killrunner(int runid, int sig, enum appstate tostate)
313 struct apprun *runner = getrunner(runid);
314 if (runner == NULL) {
318 else if (runner->state != as_running && runner->state != as_paused) {
322 else if (runner->state == tostate) {
326 rc = killpg(runner->pids[0], sig);
328 runner->state = tostate;
334 * Signal callback called on SIGCHLD. This is set using sigaction.
336 static void on_sigchld(int signum, siginfo_t *info, void *uctxt)
338 struct apprun *runner;
340 /* retrieves the runner */
341 runner = runner_of_pid(info->si_pid);
345 /* known runner, inspect cause of signal */
346 switch(info->si_code) {
351 /* update the state */
352 runner->state = as_terminated;
353 /* remove it from pgid list */
355 runner->next_by_pgid = terminated_runners;
356 terminated_runners = runner;
357 /* ensures that all the group terminates */
358 killpg(runner->pids[0], SIGKILL);
362 /* update the state */
363 runner->state = as_paused;
367 /* update the state */
368 runner->state = as_running;
373 /**************** handle afm_launch_desc *********************/
376 * Initialize the data of the launch description 'desc'
377 * for the application 'appli' and the 'mode'.
379 * Returns 0 in case of success or -1 in case of error.
381 static int fill_launch_desc(struct json_object *appli,
382 enum afm_launch_mode mode, struct afm_launch_desc *desc)
386 assert(is_valid_launch_mode(mode));
389 if(!j_read_object_at(appli, "public", &pub)
390 || !j_read_string_at(appli, "path", &desc->path)
391 || !j_read_string_at(appli, "id", &desc->appid)
392 || !j_read_string_at(appli, "content", &desc->content)
393 || !j_read_string_at(appli, "type", &desc->type)
394 || !j_read_string_at(pub, "name", &desc->name)
395 || !j_read_integer_at(pub, "width", &desc->width)
396 || !j_read_integer_at(pub, "height", &desc->height)) {
404 static const char *null = NULL;
405 desc->bindings = &null;
409 desc->home = homeappdir;
414 /**************** report state of runner *********************/
417 * Creates a json object that describes the state of 'runner'.
419 * Returns the created object or NULL in case of error.
421 static json_object *mkstate(struct apprun *runner)
424 struct json_object *result, *obj, *pids;
428 result = json_object_new_object();
433 if (!j_add_integer(result, "runid", runner->runid))
437 switch(runner->state) {
441 pids = j_add_new_array(result, "pids");
444 if (!j_add_integer(pids, NULL, runner->pids[0]))
446 if (runner->pids[1] && !j_add_integer(pids, NULL, runner->pids[1]))
454 switch(runner->state) {
463 state = "terminated";
466 if (!j_add_string(result, "state", state))
469 /* the application id */
470 rc = json_object_object_get_ex(runner->appli, "public", &obj);
472 rc = json_object_object_get_ex(obj, "id", &obj);
474 if (!j_add(result, "id", obj))
476 json_object_get(obj);
482 json_object_put(result);
488 /**************** API handling ************************/
491 * Starts the application described by 'appli' for the 'mode'.
492 * In case of remote start, it returns in uri the uri to
495 * A reference to 'appli' is kept during the live of the
496 * runner. This is made using json_object_get. Thus be aware
497 * that further modifications to 'appli' might create errors.
499 * Returns the runid in case of success or -1 in case of error
501 int afm_run_start(struct json_object *appli, enum afm_launch_mode mode,
504 struct apprun *runner;
505 struct afm_launch_desc desc;
507 sigset_t saved, blocked;
509 assert(is_valid_launch_mode(mode));
510 assert(mode == mode_local || uri != NULL);
511 assert(uri == NULL || *uri == NULL);
513 /* prepare to launch */
514 rc = fill_launch_desc(appli, mode, &desc);
517 runner = createrunner(appli);
521 /* block children signals until launched */
522 sigemptyset(&blocked);
523 sigaddset(&blocked, SIGCHLD);
524 sigprocmask(SIG_BLOCK, &blocked, &saved);
527 rc = afm_launch(&desc, runner->pids, uri);
530 sigprocmask(SIG_SETMASK, &saved, NULL);
531 ERROR("can't start, afm_launch failed: %m");
537 runner->state = as_running;
541 /* unblock children signal now */
542 sigprocmask(SIG_SETMASK, &saved, NULL);
547 * Terminates the runner of 'runid'
549 * Returns 0 in case of success or -1 in case of error
551 int afm_run_terminate(int runid)
553 return killrunner(runid, SIGTERM, as_terminating);
557 * Stops (aka pause) the runner of 'runid'
559 * Returns 0 in case of success or -1 in case of error
561 int afm_run_pause(int runid)
563 return killrunner(runid, SIGSTOP, as_paused);
567 * Continue (aka resume) the runner of 'runid'
569 * Returns 0 in case of success or -1 in case of error
571 int afm_run_resume(int runid)
573 return killrunner(runid, SIGCONT, as_running);
577 * Get the list of the runners.
579 * Returns the list or NULL in case of error.
581 struct json_object *afm_run_list()
583 struct json_object *result, *obj;
584 struct apprun *runner;
587 /* creates the object */
588 result = json_object_new_array();
592 /* iterate over runners */
593 for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
594 runner = runners_by_runid[i];
596 if (runner->state != as_terminating
597 && runner->state != as_terminated) {
598 /* adds the living runner */
599 obj = mkstate(runner);
602 if (json_object_array_add(result, obj) == -1) {
603 json_object_put(obj);
607 runner = runner->next_by_runid;
613 json_object_put(result);
620 * Get the state of the runner of 'runid'.
622 * Returns the state or NULL in case of success
624 struct json_object *afm_run_state(int runid)
626 struct apprun *runner = getrunner(runid);
627 if (runner == NULL || runner->state == as_terminating
628 || runner->state == as_terminated) {
632 return mkstate(runner);
635 /**************** INITIALISATION **********************/
638 * Initialize the module
645 struct passwd passwd, *pw;
646 struct sigaction siga;
649 rc = afm_launch_initialize();
653 /* computes the 'homeappdir' */
655 rc = getpwuid_r(me, &passwd, buf, sizeof buf, &pw);
656 if (rc || pw == NULL) {
657 errno = rc ? errno : ENOENT;
658 ERROR("getpwuid_r failed for uid=%d: %m",(int)me);
661 rc = asprintf(&homeappdir, "%s/%s", passwd.pw_dir, fwk_user_app_dir);
664 ERROR("allocating homeappdir for uid=%d failed", (int)me);
667 rc = create_directory(homeappdir, 0755, 1);
668 if (rc && errno != EEXIST) {
669 ERROR("creation of directory %s failed: %m", homeappdir);
673 rc = smack_remove_label_for_path(homeappdir,
674 XATTR_NAME_SMACKTRANSMUTE, 0);
675 if (rc < 0 && errno != ENODATA) {
676 ERROR("can't remove smack transmutation of directory %s: %m",
681 rc = smack_set_label_for_path(homeappdir, XATTR_NAME_SMACK, 0,
684 ERROR("can't set smack label %s to directory %s: %m",
685 fwk_user_app_label, homeappdir);
689 /* install signal handlers */
690 siga.sa_flags = SA_SIGINFO | SA_NOCLDWAIT;
691 sigemptyset(&siga.sa_mask);
692 sigaddset(&siga.sa_mask, SIGCHLD);
693 siga.sa_sigaction = on_sigchld;
694 sigaction(SIGCHLD, &siga, NULL);