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.
23 #include <sys/types.h>
33 #include "utils-dir.h"
34 #include "utils-json.h"
35 #include "afm-launch-mode.h"
36 #include "afm-launch.h"
40 * State of a launched/running application
43 as_starting, /* start in progress */
44 as_running, /* started and running */
45 as_stopped, /* stopped */
46 as_terminating, /* termination in progress */
47 as_terminated /* terminated */
51 * Structure for recording a runner
54 struct apprun *next_by_runid; /* link for hashing by runid */
55 struct apprun *next_by_pgid; /* link for hashing by pgid */
56 int runid; /* runid */
57 pid_t pids[2]; /* pids (0: group leader, 1: slave) */
58 enum appstate state; /* current state of the application */
59 json_object *appli; /* json object describing the application */
63 * Count of item by hash table
65 #define ROOT_RUNNERS_COUNT 32
68 * Maximum count of simultaneous running application
70 #define MAX_RUNNER_COUNT 32767
73 * Hash tables of runners by runid and by pgid
75 static struct apprun *runners_by_runid[ROOT_RUNNERS_COUNT];
76 static struct apprun *runners_by_pgid[ROOT_RUNNERS_COUNT];
79 * List of terminated runners
81 static struct apprun *terminated_runners = NULL;
86 static int runnercount = 0;
91 static int runnerid = 0;
94 * Path name of the directory for applications in the
95 * home directory of the user.
97 static const char fwk_user_app_dir[] = FWK_USER_APP_DIR;
100 * Path of the root directory for applications of the
103 static char *homeappdir;
105 /****************** manages pids **********************/
108 * Get a runner by its 'pid' (NULL if not found)
110 static struct apprun *runner_of_pid(pid_t pid)
113 struct apprun *result;
115 for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
116 result = runners_by_pgid[i];
117 while (result != NULL) {
118 if (result->pids[0] == pid || result->pids[1] == pid)
120 result = result->next_by_pgid;
126 /****************** manages pgids **********************/
129 * Get a runner by its 'pgid' (NULL if not found)
131 static struct apprun *runner_of_pgid(pid_t pgid)
133 struct apprun *result;
135 result = runners_by_pgid[pgid & (ROOT_RUNNERS_COUNT - 1)];
136 while (result && result->pids[0] != pgid)
137 result = result->next_by_pgid;
142 * Insert a 'runner' for its pgid
144 static void pgid_insert(struct apprun *runner)
146 struct apprun **prev;
148 prev = &runners_by_pgid[runner->pids[0] & (ROOT_RUNNERS_COUNT - 1)];
149 runner->next_by_pgid = *prev;
154 * Remove a 'runner' for its pgid
156 static void pgid_remove(struct apprun *runner)
158 struct apprun **prev;
160 prev = &runners_by_pgid[runner->pids[0] & (ROOT_RUNNERS_COUNT - 1)];
162 if (*prev == runner) {
163 *prev = runner->next_by_pgid;
166 prev = &(*prev)->next_by_pgid;
170 /****************** manages runners (by runid) **********************/
173 * Get a runner by its 'runid' (NULL if not found)
175 static struct apprun *getrunner(int runid)
177 struct apprun *result;
179 result = runners_by_runid[runid & (ROOT_RUNNERS_COUNT - 1)];
180 while (result && result->runid != runid)
181 result = result->next_by_runid;
186 * Free an existing 'runner'
188 static void freerunner(struct apprun *runner)
190 struct apprun **prev;
192 /* get previous pointer to runner */
193 prev = &runners_by_runid[runner->runid & (ROOT_RUNNERS_COUNT - 1)];
195 while(*prev != runner) {
196 prev = &(*prev)->next_by_runid;
201 *prev = runner->next_by_runid;
205 json_object_put(runner->appli);
210 * Cleans the list of runners from its terminated
212 static void cleanrunners()
214 struct apprun *runner;
215 while (terminated_runners) {
216 runner = terminated_runners;
217 terminated_runners = runner->next_by_pgid;
223 * Create a new runner for the 'appli'
225 * Returns the created runner or NULL
228 static struct apprun *createrunner(json_object *appli)
230 struct apprun *result;
231 struct apprun **prev;
237 if (runnercount >= MAX_RUNNER_COUNT) {
243 if (runnerid > MAX_RUNNER_COUNT)
245 } while(getrunner(runnerid));
247 /* create the structure */
248 result = calloc(1, sizeof * result);
252 /* initialize it linked to the list */
253 prev = &runners_by_runid[runnerid & (ROOT_RUNNERS_COUNT - 1)];
254 result->next_by_runid = *prev;
255 result->next_by_pgid = NULL;
256 result->runid = runnerid;
257 result->pids[0] = result->pids[1] = 0;
258 result->state = as_starting;
259 result->appli = json_object_get(appli);
266 /**************** signaling ************************/
268 static void started(int runid)
272 static void stopped(int runid)
276 static void continued(int runid)
280 static void terminated(int runid)
284 static void removed(int runid)
288 /**************** running ************************/
291 * Sends (with pgkill) the signal 'sig' to the process group
292 * for 'runid' and put the runner's state to 'tostate'
293 * in case of success.
295 * Only processes in the state 'as_running' or 'as_stopped'
298 * Returns 0 in case of success or -1 in case of error.
300 static int killrunner(int runid, int sig, enum appstate tostate)
303 struct apprun *runner = getrunner(runid);
304 if (runner == NULL) {
308 else if (runner->state != as_running && runner->state != as_stopped) {
312 else if (runner->state == tostate) {
316 rc = killpg(runner->pids[0], sig);
318 runner->state = tostate;
324 * Signal callback called on SIGCHLD. This is set using sigaction.
326 static void on_sigchld(int signum, siginfo_t *info, void *uctxt)
328 struct apprun *runner;
330 /* retrieves the runner */
331 runner = runner_of_pid(info->si_pid);
335 /* known runner, inspect cause of signal */
336 switch(info->si_code) {
341 /* update the state */
342 runner->state = as_terminated;
343 /* remove it from pgid list */
345 runner->next_by_pgid = terminated_runners;
346 terminated_runners = runner;
347 /* ensures that all the group stops */
348 killpg(runner->pids[0], SIGKILL);
352 /* update the state */
353 runner->state = as_stopped;
357 /* update the state */
358 runner->state = as_running;
363 /**************** handle afm_launch_desc *********************/
366 * Initialize the data of the launch description 'desc'
367 * for the application 'appli' and the 'mode'.
369 * Returns 0 in case of success or -1 in case of error.
371 static int fill_launch_desc(struct json_object *appli,
372 enum afm_launch_mode mode, struct afm_launch_desc *desc)
376 assert(is_valid_launch_mode(mode));
379 if(!j_read_object_at(appli, "public", &pub)
380 || !j_read_string_at(appli, "path", &desc->path)
381 || !j_read_string_at(appli, "id", &desc->appid)
382 || !j_read_string_at(appli, "content", &desc->content)
383 || !j_read_string_at(appli, "type", &desc->type)
384 || !j_read_string_at(pub, "name", &desc->name)
385 || !j_read_integer_at(pub, "width", &desc->width)
386 || !j_read_integer_at(pub, "height", &desc->height)) {
394 static const char *null = NULL;
395 desc->plugins = &null;
399 desc->home = homeappdir;
404 /**************** report state of runner *********************/
407 * Creates a json object that describes the state of 'runner'.
409 * Returns the created object or NULL in case of error.
411 static json_object *mkstate(struct apprun *runner)
414 struct json_object *result, *obj;
418 result = json_object_new_object();
423 if (!j_add_integer(result, "runid", runner->runid))
427 switch(runner->state) {
436 state = "terminated";
439 if (!j_add_string(result, "state", state))
442 /* the application id */
443 rc = json_object_object_get_ex(runner->appli, "public", &obj);
445 rc = json_object_object_get_ex(obj, "id", &obj);
447 if (!j_add(result, "id", obj))
449 json_object_get(obj);
455 json_object_put(result);
461 /**************** API handling ************************/
464 * Starts the application described by 'appli' for the 'mode'.
465 * In case of remote start, it returns in uri the uri to
468 * A reference to 'appli' is kept during the live of the
469 * runner. This is made using json_object_get. Thus be aware
470 * that further modifications to 'appli' might create errors.
472 * Returns 0 in case of success or -1 in case of error
474 int afm_run_start(struct json_object *appli, enum afm_launch_mode mode,
477 static struct apprun *runner;
478 struct afm_launch_desc desc;
480 sigset_t saved, blocked;
482 assert(is_valid_launch_mode(mode));
483 assert(mode == mode_local || uri != NULL);
484 assert(uri == NULL || *uri == NULL);
486 /* prepare to launch */
487 rc = fill_launch_desc(appli, mode, &desc);
490 runner = createrunner(appli);
494 /* block children signals until launched */
495 sigemptyset(&blocked);
496 sigaddset(&blocked, SIGCHLD);
497 sigprocmask(SIG_BLOCK, &blocked, &saved);
500 rc = afm_launch(&desc, runner->pids, uri);
503 sigprocmask(SIG_SETMASK, &saved, NULL);
504 ERROR("can't start, afm_launch failed: %m");
510 runner->state = as_running;
514 /* unblock children signal now */
515 sigprocmask(SIG_SETMASK, &saved, NULL);
520 * Terminates the runner of 'runid'
522 * Returns 0 in case of success or -1 in case of error
524 int afm_run_terminate(int runid)
526 return killrunner(runid, SIGTERM, as_terminating);
530 * Stops (aka pause) the runner of 'runid'
532 * Returns 0 in case of success or -1 in case of error
534 int afm_run_stop(int runid)
536 return killrunner(runid, SIGSTOP, as_stopped);
540 * Continue (aka resume) the runner of 'runid'
542 * Returns 0 in case of success or -1 in case of error
544 int afm_run_continue(int runid)
546 return killrunner(runid, SIGCONT, as_running);
550 * Get the list of the runners.
552 * Returns the list or NULL in case of error.
554 struct json_object *afm_run_list()
556 struct json_object *result, *obj;
557 struct apprun *runner;
560 /* creates the object */
561 result = json_object_new_array();
565 /* iterate over runners */
566 for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
567 runner = runners_by_runid[i];
569 if (runner->state != as_terminating
570 && runner->state != as_terminated) {
571 /* adds the living runner */
572 obj = mkstate(runner);
575 if (json_object_array_add(result, obj) == -1) {
576 json_object_put(obj);
580 runner = runner->next_by_runid;
586 json_object_put(result);
593 * Get the state of the runner of 'runid'.
595 * Returns the state or NULL in case of success
597 struct json_object *afm_run_state(int runid)
599 struct apprun *runner = getrunner(runid);
600 if (runner == NULL || runner->state == as_terminating
601 || runner->state == as_terminated) {
605 return mkstate(runner);
608 /**************** INITIALISATION **********************/
611 * Initialize the module
619 struct passwd passwd, *pw;
620 struct sigaction siga;
623 rc = afm_launch_initialize();
627 /* computes the 'homeappdir' */
629 rc = getpwuid_r(me, &passwd, buf, sizeof buf, &pw);
630 if (rc || pw == NULL) {
631 errno = rc ? errno : ENOENT;
632 ERROR("getpwuid_r failed for uid=%d: %m",(int)me);
635 rc = snprintf(dir, sizeof dir, "%s/%s", passwd.pw_dir,
637 if (rc >= (int)sizeof dir) {
638 ERROR("buffer overflow in user_app_dir for uid=%d",(int)me);
641 rc = create_directory(dir, 0755, 1);
642 if (rc && errno != EEXIST) {
643 ERROR("creation of directory %s failed in user_app_dir: %m",
647 homeappdir = strdup(dir);
648 if (homeappdir == NULL) {
650 ERROR("out of memory in user_app_dir for %s : %m", dir);
654 /* install signal handlers */
655 siga.sa_flags = SA_SIGINFO | SA_NOCLDWAIT;
656 sigemptyset(&siga.sa_mask);
657 sigaddset(&siga.sa_mask, SIGCHLD);
658 siga.sa_sigaction = on_sigchld;
659 sigaction(SIGCHLD, &siga, NULL);