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_stopped, /* stopped */
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 stopped(int runid)
286 static void continued(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_stopped'
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_stopped) {
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 stops */
358 killpg(runner->pids[0], SIGKILL);
362 /* update the state */
363 runner->state = as_stopped;
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;
428 result = json_object_new_object();
433 if (!j_add_integer(result, "runid", runner->runid))
437 switch(runner->state) {
446 state = "terminated";
449 if (!j_add_string(result, "state", state))
452 /* the application id */
453 rc = json_object_object_get_ex(runner->appli, "public", &obj);
455 rc = json_object_object_get_ex(obj, "id", &obj);
457 if (!j_add(result, "id", obj))
459 json_object_get(obj);
465 json_object_put(result);
471 /**************** API handling ************************/
474 * Starts the application described by 'appli' for the 'mode'.
475 * In case of remote start, it returns in uri the uri to
478 * A reference to 'appli' is kept during the live of the
479 * runner. This is made using json_object_get. Thus be aware
480 * that further modifications to 'appli' might create errors.
482 * Returns 0 in case of success or -1 in case of error
484 int afm_run_start(struct json_object *appli, enum afm_launch_mode mode,
487 static struct apprun *runner;
488 struct afm_launch_desc desc;
490 sigset_t saved, blocked;
492 assert(is_valid_launch_mode(mode));
493 assert(mode == mode_local || uri != NULL);
494 assert(uri == NULL || *uri == NULL);
496 /* prepare to launch */
497 rc = fill_launch_desc(appli, mode, &desc);
500 runner = createrunner(appli);
504 /* block children signals until launched */
505 sigemptyset(&blocked);
506 sigaddset(&blocked, SIGCHLD);
507 sigprocmask(SIG_BLOCK, &blocked, &saved);
510 rc = afm_launch(&desc, runner->pids, uri);
513 sigprocmask(SIG_SETMASK, &saved, NULL);
514 ERROR("can't start, afm_launch failed: %m");
520 runner->state = as_running;
524 /* unblock children signal now */
525 sigprocmask(SIG_SETMASK, &saved, NULL);
530 * Terminates the runner of 'runid'
532 * Returns 0 in case of success or -1 in case of error
534 int afm_run_terminate(int runid)
536 return killrunner(runid, SIGTERM, as_terminating);
540 * Stops (aka pause) the runner of 'runid'
542 * Returns 0 in case of success or -1 in case of error
544 int afm_run_stop(int runid)
546 return killrunner(runid, SIGSTOP, as_stopped);
550 * Continue (aka resume) the runner of 'runid'
552 * Returns 0 in case of success or -1 in case of error
554 int afm_run_continue(int runid)
556 return killrunner(runid, SIGCONT, as_running);
560 * Get the list of the runners.
562 * Returns the list or NULL in case of error.
564 struct json_object *afm_run_list()
566 struct json_object *result, *obj;
567 struct apprun *runner;
570 /* creates the object */
571 result = json_object_new_array();
575 /* iterate over runners */
576 for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
577 runner = runners_by_runid[i];
579 if (runner->state != as_terminating
580 && runner->state != as_terminated) {
581 /* adds the living runner */
582 obj = mkstate(runner);
585 if (json_object_array_add(result, obj) == -1) {
586 json_object_put(obj);
590 runner = runner->next_by_runid;
596 json_object_put(result);
603 * Get the state of the runner of 'runid'.
605 * Returns the state or NULL in case of success
607 struct json_object *afm_run_state(int runid)
609 struct apprun *runner = getrunner(runid);
610 if (runner == NULL || runner->state == as_terminating
611 || runner->state == as_terminated) {
615 return mkstate(runner);
618 /**************** INITIALISATION **********************/
621 * Initialize the module
628 struct passwd passwd, *pw;
629 struct sigaction siga;
632 rc = afm_launch_initialize();
636 /* computes the 'homeappdir' */
638 rc = getpwuid_r(me, &passwd, buf, sizeof buf, &pw);
639 if (rc || pw == NULL) {
640 errno = rc ? errno : ENOENT;
641 ERROR("getpwuid_r failed for uid=%d: %m",(int)me);
644 rc = asprintf(&homeappdir, "%s/%s", passwd.pw_dir, fwk_user_app_dir);
647 ERROR("allocating homeappdir for uid=%d failed", (int)me);
650 rc = create_directory(homeappdir, 0755, 1);
651 if (rc && errno != EEXIST) {
652 ERROR("creation of directory %s failed: %m", homeappdir);
656 rc = smack_remove_label_for_path(homeappdir,
657 XATTR_NAME_SMACKTRANSMUTE, 0);
658 if (rc < 0 && errno != ENODATA) {
659 ERROR("can't remove smack transmutation of directory %s: %m",
664 rc = smack_set_label_for_path(homeappdir, XATTR_NAME_SMACK, 0,
667 ERROR("can't set smack label %s to directory %s: %m",
668 fwk_user_app_label, homeappdir);
672 /* install signal handlers */
673 siga.sa_flags = SA_SIGINFO | SA_NOCLDWAIT;
674 sigemptyset(&siga.sa_mask);
675 sigaddset(&siga.sa_mask, SIGCHLD);
676 siga.sa_sigaction = on_sigchld;
677 sigaction(SIGCHLD, &siga, NULL);