/*
- Copyright 2015 IoT.bzh
+ Copyright 2015, 2016 IoT.bzh
author: José Bollo <jose.bollo@iot.bzh>
limitations under the License.
*/
+#define _GNU_SOURCE
+
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <limits.h>
#include <string.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"
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 */
};
* 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
/****************** 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)
*/
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'
*/
{
}
-static void stopped(int runid)
+static void paused(int runid)
{
}
-static void continued(int runid)
+static void resumed(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.
errno = ENOENT;
rc = -1;
}
- else if (runner->state != as_running && runner->state != as_stopped) {
+ else if (is_dead(runner)) {
errno = EINVAL;
rc = -1;
}
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:
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 */
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";
* 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;
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'
*
*
* 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);
}
/*
*
* 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);
}
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)
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;
}
int afm_run_init()
{
char buf[2048];
- char dir[PATH_MAX];
int rc;
uid_t me;
struct passwd passwd, *pw;
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 >= (int)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);