afm-run: improve error diagnostic
[src/app-framework-main.git] / src / afm-run.c
index 91e2a0b..089aa77 100644 (file)
@@ -1,5 +1,5 @@
 /*
- Copyright 2015 IoT.bzh
+ Copyright 2015, 2016, 2017 IoT.bzh
 
  author: José Bollo <jose.bollo@iot.bzh>
 
@@ -16,6 +16,8 @@
  limitations under the License.
 */
 
+#define _GNU_SOURCE
+
 #include <fcntl.h>
 #include <unistd.h>
 #include <signal.h>
 #include <limits.h>
 #include <string.h>
 
-#include <json.h>
+#include <linux/xattr.h>
+#if SIMULATE_LIBSMACK
+#include "simulation/smack.h"
+#else
+#include <sys/smack.h>
+#endif
+
+#include <json-c/json.h>
 
 #include "verbose.h"
 #include "utils-dir.h"
@@ -42,7 +51,7 @@
 enum appstate {
        as_starting,    /* start in progress */
        as_running,     /* started and running */
-       as_stopped,     /* stopped */
+       as_paused,      /* paused */
        as_terminating, /* termination in progress */
        as_terminated   /* terminated */
 };
@@ -95,6 +104,7 @@ static int runnerid = 0;
  * home directory of the user.
  */
 static const char fwk_user_app_dir[] = FWK_USER_APP_DIR;
+static const char fwk_user_app_label[] = FWK_USER_APP_DIR_LABEL;
 
 /*
  * Path of the root directory for applications of the
@@ -169,6 +179,61 @@ static void pgid_remove(struct apprun *runner)
 
 /****************** manages runners (by runid) **********************/
 
+/*
+ * Is a 'runner' alive?
+ */
+static inline int is_alive(struct apprun *runner)
+{
+       switch(runner->state) {
+       case as_terminating:
+       case as_terminated:
+               return 0;
+       default:
+               return 1;
+       }
+}
+
+/*
+ * Is a 'runner' dead?
+ */
+static inline int is_dead(struct apprun *runner)
+{
+       switch(runner->state) {
+       case as_terminating:
+       case as_terminated:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+/*
+ * Is a 'runner' running?
+ */
+static inline int is_running(struct apprun *runner)
+{
+       switch(runner->state) {
+       case as_starting:
+       case as_running:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+/*
+ * Is a 'runner' paused?
+ */
+static inline int is_paused(struct apprun *runner)
+{
+       switch(runner->state) {
+       case as_paused:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
 /*
  * Get a runner by its 'runid'  (NULL if not found)
  */
@@ -182,6 +247,25 @@ static struct apprun *getrunner(int runid)
        return result;
 }
 
+/*
+ * Get first runner of 'appli' (NULL if not found)
+ */
+static struct apprun *getrunner_appli(json_object *appli)
+{
+       int i;
+       struct apprun *result;
+
+       for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
+               result = runners_by_pgid[i];
+               while (result != NULL) {
+                       if (result->appli == appli)
+                               return result;
+                       result = result->next_by_pgid;
+               }
+       }
+       return NULL;
+}
+
 /*
  * Free an existing 'runner'
  */
@@ -269,11 +353,11 @@ static void started(int runid)
 {
 }
 
-static void stopped(int runid)
+static void paused(int runid)
 {
 }
 
-static void continued(int runid)
+static void resumed(int runid)
 {
 }
 
@@ -292,7 +376,7 @@ static void removed(int runid)
  * for 'runid' and put the runner's state to 'tostate'
  * in case of success.
  *
- * Only processes in the state 'as_running' or 'as_stopped'
+ * Only processes in the state 'as_running' or 'as_paused'
  * can be signalled.
  *
  * Returns 0 in case of success or -1 in case of error.
@@ -305,7 +389,7 @@ static int killrunner(int runid, int sig, enum appstate tostate)
                errno = ENOENT;
                rc = -1;
        }
-       else if (runner->state != as_running && runner->state != as_stopped) {
+       else if (is_dead(runner)) {
                errno = EINVAL;
                rc = -1;
        }
@@ -344,13 +428,13 @@ static void on_sigchld(int signum, siginfo_t *info, void *uctxt)
                pgid_remove(runner);
                runner->next_by_pgid = terminated_runners;
                terminated_runners = runner;
-               /* ensures that all the group stops */
+               /* ensures that all the group terminates */
                killpg(runner->pids[0], SIGKILL);
                break;
 
        case CLD_STOPPED:
                /* update the state */
-               runner->state = as_stopped;
+               runner->state = as_paused;
                break;
 
        case CLD_CONTINUED:
@@ -384,22 +468,23 @@ static int fill_launch_desc(struct json_object *appli,
        || !j_read_string_at(pub, "name", &desc->name)
        || !j_read_integer_at(pub, "width", &desc->width)
        || !j_read_integer_at(pub, "height", &desc->height)) {
+               ERROR("bad internal description of the application to launch: %s", json_object_get_string(appli));
                errno = EINVAL;
                return -1;
        }
 
-       /* plugins */
+       /* bindings */
        {
                /* TODO */
                static const char *null = NULL;
-               desc->plugins = &null;
+               desc->bindings = &null;
        }
 
        /* finaly */
        desc->home = homeappdir;
        desc->mode = mode;
        return 0;
-};
+}
 
 /**************** report state of runner *********************/
 
@@ -411,7 +496,7 @@ static int fill_launch_desc(struct json_object *appli,
 static json_object *mkstate(struct apprun *runner)
 {
        const char *state;
-       struct json_object *result, *obj;
+       struct json_object *result, *obj, *pids;
        int rc;
 
        /* the structure */
@@ -423,14 +508,25 @@ static json_object *mkstate(struct apprun *runner)
        if (!j_add_integer(result, "runid", runner->runid))
                goto error2;
 
+       /* the pids */
+       if (is_alive(runner)) {
+               pids = j_add_new_array(result, "pids");
+               if (!pids)
+                       goto error2;
+               if (!j_add_integer(pids, NULL, runner->pids[0]))
+                       goto error2;
+               if (runner->pids[1] && !j_add_integer(pids, NULL, runner->pids[1]))
+                       goto error2;
+       }
+
        /* the state */
        switch(runner->state) {
        case as_starting:
        case as_running:
                state = "running";
                break;
-       case as_stopped:
-               state = "stopped";
+       case as_paused:
+               state = "paused";
                break;
        default:
                state = "terminated";
@@ -469,12 +565,12 @@ error:
  * runner. This is made using json_object_get. Thus be aware
  * that further modifications to 'appli' might create errors.
  *
- * Returns 0 in case of success or -1 in case of error
+ * Returns the runid in case of success or -1 in case of error
  */
 int afm_run_start(struct json_object *appli, enum afm_launch_mode mode,
                                                        char **uri)
 {
-       static struct apprun *runner;
+       struct apprun *runner;
        struct afm_launch_desc desc;
        int rc;
        sigset_t saved, blocked;
@@ -516,6 +612,23 @@ int afm_run_start(struct json_object *appli, enum afm_launch_mode mode,
        return rc;
 }
 
+/*
+ * Returns the runid of a previously started application 'appli'
+ * or if none is running, starts the application described by 'appli'
+ * in local mode.
+ *
+ * A reference to 'appli' is kept during the live of the
+ * runner. This is made using json_object_get. Thus be aware
+ * that further modifications to 'appli' might create errors.
+ *
+ * Returns the runid in case of success or -1 in case of error
+ */
+int afm_run_once(struct json_object *appli)
+{
+       struct apprun *runner = getrunner_appli(appli);
+       return runner && is_alive(runner) ? runner->runid : afm_run_start(appli, mode_local, NULL);
+}
+
 /*
  * Terminates the runner of 'runid'
  *
@@ -531,9 +644,9 @@ int afm_run_terminate(int runid)
  *
  * Returns 0 in case of success or -1 in case of error
  */
-int afm_run_stop(int runid)
+int afm_run_pause(int runid)
 {
-       return killrunner(runid, SIGSTOP, as_stopped);
+       return killrunner(runid, SIGSTOP, as_paused);
 }
 
 /*
@@ -541,7 +654,7 @@ int afm_run_stop(int runid)
  *
  * Returns 0 in case of success or -1 in case of error
  */
-int afm_run_continue(int runid)
+int afm_run_resume(int runid)
 {
        return killrunner(runid, SIGCONT, as_running);
 }
@@ -566,8 +679,7 @@ struct json_object *afm_run_list()
        for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
                runner = runners_by_runid[i];
                while (runner) {
-                       if (runner->state != as_terminating
-                                       && runner->state != as_terminated) {
+                       if (is_alive(runner)) {
                                /* adds the living runner */
                                obj = mkstate(runner);
                                if (obj == NULL)
@@ -597,8 +709,7 @@ error:
 struct json_object *afm_run_state(int runid)
 {
        struct apprun *runner = getrunner(runid);
-       if (runner == NULL || runner->state == as_terminating
-                               || runner->state == as_terminated) {
+       if (runner == NULL || is_dead(runner)) {
                errno = ENOENT;
                return NULL;
        }
@@ -613,7 +724,6 @@ struct json_object *afm_run_state(int runid)
 int afm_run_init()
 {
        char buf[2048];
-       char dir[PATH_MAX];
        int rc;
        uid_t me;
        struct passwd passwd, *pw;
@@ -632,25 +742,34 @@ int afm_run_init()
                ERROR("getpwuid_r failed for uid=%d: %m",(int)me);
                return -1;
        }
-       rc = snprintf(dir, sizeof dir, "%s/%s", passwd.pw_dir,
-                                                       fwk_user_app_dir);
-       if (rc >= sizeof dir) {
-               ERROR("buffer overflow in user_app_dir for uid=%d",(int)me);
+       rc = asprintf(&homeappdir, "%s/%s", passwd.pw_dir, fwk_user_app_dir);
+       if (rc < 0) {
+               errno = ENOMEM;
+               ERROR("allocating homeappdir for uid=%d failed", (int)me);
                return -1;
        }
-       rc = create_directory(dir, 0755, 1);
+       rc = create_directory(homeappdir, 0755, 1);
        if (rc && errno != EEXIST) {
-               ERROR("creation of directory %s failed in user_app_dir: %m",
-                                                                       dir);
+               ERROR("creation of directory %s failed: %m", homeappdir);
+               free(homeappdir);
                return -1;
        }
-       homeappdir = strdup(dir);
-       if (homeappdir == NULL) {
-               errno = ENOMEM;
-               ERROR("out of memory in user_app_dir for %s : %m", dir);
+       rc = smack_remove_label_for_path(homeappdir,
+                                               XATTR_NAME_SMACKTRANSMUTE, 0);
+       if (rc < 0 && errno != ENODATA) {
+               ERROR("can't remove smack transmutation of directory %s: %m",
+                                                               homeappdir);
+               free(homeappdir);
+               return -1;
+       }
+       rc = smack_set_label_for_path(homeappdir, XATTR_NAME_SMACK, 0,
+                                                       fwk_user_app_label);
+       if (rc < 0) {
+               ERROR("can't set smack label %s to directory %s: %m",
+                                       fwk_user_app_label, homeappdir);
+               free(homeappdir);
                return -1;
        }
-
        /* install signal handlers */
        siga.sa_flags = SA_SIGINFO | SA_NOCLDWAIT;
        sigemptyset(&siga.sa_mask);