From ce714369c64051d5307ef7ac9af2c53b6edfbb4a Mon Sep 17 00:00:00 2001 From: José Bollo Date: Tue, 22 Dec 2015 17:25:45 +0100 Subject: refactoring and moving forward Rename af-... files to afm-... Add system daemon draft Change-Id: I3f8b69ac9cc5af54be34b4ad11a512c57c0230b6 --- .gitignore | 1 + src/CMakeLists.txt | 16 +- src/af-db.c | 410 ----------------------------------------- src/af-db.h | 33 ---- src/af-launch.c | 358 ------------------------------------ src/af-launch.h | 33 ---- src/af-run.c | 478 ----------------------------------------------- src/af-run.h | 26 --- src/afm-db.c | 410 +++++++++++++++++++++++++++++++++++++++++ src/afm-db.h | 33 ++++ src/afm-launch.c | 358 ++++++++++++++++++++++++++++++++++++ src/afm-launch.h | 33 ++++ src/afm-run.c | 479 ++++++++++++++++++++++++++++++++++++++++++++++++ src/afm-run.h | 26 +++ src/afm-system-daemon.c | 213 +++++++++++++++++++++ src/afm-user-daemon.c | 75 +++++--- src/afm.h | 22 +++ src/utils-jbus.c | 18 +- src/wgtpkg-install.c | 8 +- src/wgtpkg.h | 2 +- 20 files changed, 1648 insertions(+), 1384 deletions(-) delete mode 100644 src/af-db.c delete mode 100644 src/af-db.h delete mode 100644 src/af-launch.c delete mode 100644 src/af-launch.h delete mode 100644 src/af-run.c delete mode 100644 src/af-run.h create mode 100644 src/afm-db.c create mode 100644 src/afm-db.h create mode 100644 src/afm-launch.c create mode 100644 src/afm-launch.h create mode 100644 src/afm-run.c create mode 100644 src/afm-run.h create mode 100644 src/afm-system-daemon.c create mode 100644 src/afm.h diff --git a/.gitignore b/.gitignore index 309aaf4..3fb1099 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ src/afm-user-daemon +src/afm-system-daemon wgtpkg-installer wgtpkg-pack wgtpkg-sign diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f2d41bd..499d38a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,6 +16,8 @@ # limitations under the License. ########################################################################### +cmake_minimum_required(VERSION 2.8) + include(FindPkgConfig) pkg_check_modules(EXTRAS REQUIRED @@ -78,30 +80,30 @@ add_library(secwrp ) add_library(afm - af-db.c - af-launch.c - af-run.c + afm-db.c + afm-launch.c + afm-run.c ) add_executable(wgtpkg-sign wgtpkg-sign.c) target_link_libraries(wgtpkg-sign wgtpkg utils) - add_executable(wgtpkg-pack wgtpkg-pack.c) target_link_libraries(wgtpkg-pack wgtpkg utils) - add_executable(wgtpkg-info wgtpkg-info.c) target_link_libraries(wgtpkg-info wgtpkg wgt utils) - add_executable(wgtpkg-installer wgtpkg-installer.c) target_link_libraries(wgtpkg-installer wgtpkg wgt secwrp utils) - add_executable(afm-user-daemon afm-user-daemon.c) target_link_libraries(afm-user-daemon afm secwrp wgt utils) +add_executable(afm-system-daemon afm-system-daemon.c) +target_link_libraries(afm-system-daemon wgtpkg afm secwrp wgt utils) + install(TARGETS wgtpkg-sign wgtpkg-pack wgtpkg-info wgtpkg-installer DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) install(TARGETS afm-user-daemon DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) +install(TARGETS afm-system-daemon DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) diff --git a/src/af-db.c b/src/af-db.c deleted file mode 100644 index 473b118..0000000 --- a/src/af-db.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - Copyright 2015 IoT.bzh - - author: José Bollo - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "wgt-info.h" -#include "af-db.h" - -struct afapps { - struct json_object *pubarr; - struct json_object *direct; - struct json_object *byapp; -}; - -enum dir_type { - type_root, - type_app -}; - -struct af_db_dir { - struct af_db_dir *next; - char *path; - enum dir_type type; -}; - -struct af_db { - int refcount; - struct af_db_dir *dirhead; - struct af_db_dir *dirtail; - struct afapps applications; -}; - -struct af_db *af_db_create() -{ - struct af_db *afdb = malloc(sizeof * afdb); - if (afdb == NULL) - errno = ENOMEM; - else { - afdb->refcount = 1; - afdb->dirhead = NULL; - afdb->dirtail = NULL; - afdb->applications.pubarr = NULL; - afdb->applications.direct = NULL; - afdb->applications.byapp = NULL; - } - return afdb; -} - -void af_db_addref(struct af_db *afdb) -{ - assert(afdb); - afdb->refcount++; -} - -void af_db_unref(struct af_db *afdb) -{ - struct af_db_dir *dir; - assert(afdb); - if (!--afdb->refcount) { - json_object_put(afdb->applications.pubarr); - json_object_put(afdb->applications.direct); - json_object_put(afdb->applications.byapp); - while (afdb->dirhead != NULL) { - dir = afdb->dirhead; - afdb->dirhead = dir->next; - free(dir->path); - free(dir); - } - free(afdb); - } -} - -int add_dir(struct af_db *afdb, const char *path, enum dir_type type) -{ - struct af_db_dir *dir; - char *r; - - assert(afdb); - - /* don't depend on the cwd and unique name */ - r = realpath(path, NULL); - if (!r) - return -1; - - /* avoiding duplications */ - dir = afdb->dirhead; - while(dir != NULL && (strcmp(dir->path, path) || dir->type != type)) - dir = dir ->next; - if (dir != NULL) { - free(r); - return 0; - } - - /* allocates the structure */ - dir = malloc(sizeof * dir); - if (dir == NULL) { - free(r); - errno = ENOMEM; - return -1; - } - - /* add */ - dir->next = NULL; - dir->path = r; - dir->type = type; - if (afdb->dirtail == NULL) - afdb->dirhead = dir; - else - afdb->dirtail->next = dir; - afdb->dirtail = dir; - return 0; -} - -int af_db_add_root(struct af_db *afdb, const char *path) -{ - return add_dir(afdb, path, type_root); -} - -int af_db_add_application(struct af_db *afdb, const char *path) -{ - return add_dir(afdb, path, type_app); -} - -static int json_add(struct json_object *obj, const char *key, struct json_object *val) -{ - json_object_object_add(obj, key, val); - return 0; -} - -static int json_add_str(struct json_object *obj, const char *key, const char *val) -{ - struct json_object *str = json_object_new_string (val ? val : ""); - return str ? json_add(obj, key, str) : -1; -} - -static int json_add_int(struct json_object *obj, const char *key, int val) -{ - struct json_object *v = json_object_new_int (val); - return v ? json_add(obj, key, v) : -1; -} - -static int addapp(struct afapps *apps, const char *path) -{ - struct wgt_info *info; - const struct wgt_desc *desc; - const struct wgt_desc_feature *feat; - struct json_object *priv = NULL, *pub, *bya, *plugs, *str; - char *appid, *end; - - /* connect to the widget */ - info = wgt_info_createat(AT_FDCWD, path, 0, 1, 0); - if (info == NULL) { - if (errno == ENOENT) - return 0; /* silently ignore bad directories */ - goto error; - } - desc = wgt_info_desc(info); - - /* create the application id */ - appid = alloca(2 + strlen(desc->id) + strlen(desc->version)); - end = stpcpy(appid, desc->id); - *end++ = '@'; - strcpy(end, desc->version); - - /* create the application structure */ - priv = json_object_new_object(); - if (!priv) - goto error2; - - pub = json_object_new_object(); - if (!priv) - goto error2; - - if (json_add(priv, "public", pub)) { - json_object_put(pub); - goto error2; - } - - plugs = json_object_new_array(); - if (!priv) - goto error2; - - if (json_add(priv, "plugins", plugs)) { - json_object_put(plugs); - goto error2; - } - - if(json_add_str(priv, "id", desc->id) - || json_add_str(priv, "path", path) - || json_add_str(priv, "content", desc->content_src) - || json_add_str(priv, "type", desc->content_type) - || json_add_str(pub, "id", appid) - || json_add_str(pub, "version", desc->version) - || json_add_int(pub, "width", desc->width) - || json_add_int(pub, "height", desc->height) - || json_add_str(pub, "name", desc->name) - || json_add_str(pub, "description", desc->description) - || json_add_str(pub, "shortname", desc->name_short) - || json_add_str(pub, "author", desc->author)) - goto error2; - - feat = desc->features; - while (feat) { - static const char prefix[] = FWK_PREFIX_PLUGIN; - if (!memcmp(feat->name, prefix, sizeof prefix - 1)) { - str = json_object_new_string (feat->name + sizeof prefix - 1); - if (str == NULL) - goto error2; - if (json_object_array_add(plugs, str)) { - json_object_put(str); - goto error2; - } - } - feat = feat->next; - } - - /* record the application structure */ - if (!json_object_object_get_ex(apps->byapp, desc->id, &bya)) { - bya = json_object_new_object(); - if (!bya) - goto error2; - if (json_add(apps->byapp, desc->id, bya)) { - json_object_put(bya); - goto error2; - } - } - - if (json_add(apps->direct, appid, priv)) - goto error2; - json_object_get(priv); - - if (json_add(bya, desc->version, priv)) { - json_object_put(priv); - goto error2; - } - - if (json_object_array_add(apps->pubarr, pub)) - goto error2; - - wgt_info_unref(info); - return 0; - -error2: - json_object_put(priv); - wgt_info_unref(info); -error: - return -1; -} - -struct enumdata { - char path[PATH_MAX]; - int length; - struct afapps apps; -}; - -static int enumentries(struct enumdata *data, int (*callto)(struct enumdata *)) -{ - DIR *dir; - int rc; - char *beg, *end; - struct dirent entry, *e; - - /* opens the directory */ - dir = opendir(data->path); - if (!dir) - return -1; - - /* prepare appending entry names */ - beg = data->path + data->length; - *beg++ = '/'; - - /* enumerate entries */ - rc = readdir_r(dir, &entry, &e); - while (!rc && e) { - if (entry.d_name[0] != '.' || (entry.d_name[1] && (entry.d_name[1] != '.' || entry.d_name[2]))) { - /* prepare callto */ - end = stpcpy(beg, entry.d_name); - data->length = end - data->path; - /* call the function */ - rc = callto(data); - if (rc) - break; - } - rc = readdir_r(dir, &entry, &e); - } - closedir(dir); - return rc; -} - -static int recordapp(struct enumdata *data) -{ - return addapp(&data->apps, data->path); -} - -/* enumerate the versions */ -static int enumvers(struct enumdata *data) -{ - int rc = enumentries(data, recordapp); - return !rc || errno != ENOTDIR ? 0 : rc; -} - -/* regenerate the list of applications */ -int af_db_update_applications(struct af_db *afdb) -{ - int rc; - struct enumdata edata; - struct afapps oldapps; - struct af_db_dir *dir; - - /* create the result */ - edata.apps.pubarr = json_object_new_array(); - edata.apps.direct = json_object_new_object(); - edata.apps.byapp = json_object_new_object(); - if (edata.apps.pubarr == NULL || edata.apps.direct == NULL || edata.apps.byapp == NULL) { - errno = ENOMEM; - goto error; - } - /* for each root */ - for (dir = afdb->dirhead ; dir != NULL ; dir = dir->next) { - if (dir->type == type_root) { - edata.length = stpcpy(edata.path, dir->path) - edata.path; - assert(edata.length < sizeof edata.path); - /* enumerate the applications */ - rc = enumentries(&edata, enumvers); - if (rc) - goto error; - } else { - rc = addapp(&edata.apps, dir->path); - } - } - /* commit the result */ - oldapps = afdb->applications; - afdb->applications = edata.apps; - json_object_put(oldapps.pubarr); - json_object_put(oldapps.direct); - json_object_put(oldapps.byapp); - return 0; - -error: - json_object_put(edata.apps.pubarr); - json_object_put(edata.apps.direct); - json_object_put(edata.apps.byapp); - return -1; -} - -int af_db_ensure_applications(struct af_db *afdb) -{ - return afdb->applications.pubarr ? 0 : af_db_update_applications(afdb); -} - -struct json_object *af_db_application_list(struct af_db *afdb) -{ - return af_db_ensure_applications(afdb) ? NULL : afdb->applications.pubarr; -} - -struct json_object *af_db_get_application(struct af_db *afdb, const char *id) -{ - struct json_object *result; - if (!af_db_ensure_applications(afdb) && json_object_object_get_ex(afdb->applications.direct, id, &result)) - return result; - return NULL; -} - -struct json_object *af_db_get_application_public(struct af_db *afdb, const char *id) -{ - struct json_object *result = af_db_get_application(afdb, id); - return result && json_object_object_get_ex(result, "public", &result) ? result : NULL; -} - - - - -#if defined(TESTAPPFWK) -#include -int main() -{ -struct af_db *afdb = af_db_create(); -af_db_add_root(afdb,FWK_APP_DIR); -af_db_update_applications(afdb); -printf("array = %s\n", json_object_to_json_string_ext(afdb->applications.pubarr, 3)); -printf("direct = %s\n", json_object_to_json_string_ext(afdb->applications.direct, 3)); -printf("byapp = %s\n", json_object_to_json_string_ext(afdb->applications.byapp, 3)); -return 0; -} -#endif - diff --git a/src/af-db.h b/src/af-db.h deleted file mode 100644 index ce4d04f..0000000 --- a/src/af-db.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright 2015 IoT.bzh - - author: José Bollo - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -struct af_db; - -extern struct af_db *af_db_create(); -extern void af_db_addref(struct af_db *afdb); -extern void af_db_unref(struct af_db *afdb); - -extern int af_db_add_root(struct af_db *afdb, const char *path); -extern int af_db_add_application(struct af_db *afdb, const char *path); -extern int af_db_update_applications(struct af_db *afdb); -extern int af_db_ensure_applications(struct af_db *afdb); - -extern struct json_object *af_db_application_list(struct af_db *afdb); -extern struct json_object *af_db_get_application(struct af_db *afdb, const char *id); -extern struct json_object *af_db_get_application_public(struct af_db *afdb, const char *id); - diff --git a/src/af-launch.c b/src/af-launch.c deleted file mode 100644 index ce42221..0000000 --- a/src/af-launch.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - Copyright 2015 IoT.bzh - - author: José Bollo - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern char **environ; - -#include "verbose.h" -#include "af-launch.h" -#include "secmgr-wrap.h" - -/* -%I icondir FWK_ICON_DIR -%P port params->port -%S secret params->secret -%D datadir params->datadir -%r rootdir desc->path -%h homedir desc->home -%t tag (smack label) desc->tag -%a appid desc->appid -%c content desc->content -%m mime-type desc->type -%n name desc->name -%p plugins desc->plugins -%W width desc->width -%H height desc->height -*/ - -static const char *launch_master_args[] = { - "/usr/bin/echo", - "--alias=/icons:%I", - "--port=%P", - "--rootdir=%D", - "--token=%S", - NULL -}; - -static const char *launch_html_args[] = { - "/usr/bin/chromium", - "--single-process", - "--user-data-dir=%D", - "--data-path=%r", - "file://%r/%c", -/* - "http://localhost:%P", -*/ - NULL -}; - -static const char *launch_bin_args[] = { - "/usr/bin/echo", - "BINARY", - NULL -}; - -static const char *launch_qml_args[] = { - "/usr/bin/echo", - "QML", - NULL -}; - -static struct { - const char *type; - const char **launch_args; -} -known_launchers[] = { - { "text/html", launch_html_args }, - { "application/x-executable", launch_bin_args }, - { "application/octet-stream", launch_bin_args }, - { "text/vnd.qt.qml", launch_qml_args } -}; - -struct launchparam { - int port; - const char *secret; - const char *datadir; -}; - -static char **instantiate_arguments(const char **args, struct af_launch_desc *desc, struct launchparam *params) -{ - const char **iter, *p, *v; - char *data, **result, port[20], width[20], height[20], mini[3], c; - int n, s, x; - - /* init */ - mini[0] = '%'; - mini[2] = 0; - - /* loop that either compute the size and build the result */ - n = s = x = 0; - for (;;) { - iter = args; - n = 0; - while (*iter) { - p = *iter++; - if (x) - result[n] = data; - n++; - while((c = *p++) != 0) { - if (c != '%') { - if (x) - *data++ = c; - else - s++; - } else { - c = *p++; - switch (c) { - case 'I': v = FWK_ICON_DIR; break; - case 'P': if(!x) sprintf(port, "%d", params->port); v = port; break; - case 'S': v = params->secret; break; - case 'D': v = params->datadir; break; - case 'r': v = desc->path; break; - case 'h': v = desc->home; break; - case 't': v = desc->tag; break; - case 'a': v = desc->appid; break; - case 'c': v = desc->content; break; - case 'm': v = desc->type; break; - case 'n': v = desc->name; break; - case 'p': v = "" /*desc->plugins*/; break; - case 'W': if(!x) sprintf(width, "%d", desc->width); v = width; break; - case 'H': if(!x) sprintf(height, "%d", desc->height); v = height; break; - case '%': c = 0; - default: mini[1] = c; v = mini; break; - } - if (x) - data = stpcpy(data, v); - else - s += strlen(v); - } - } - if (x) - *data++ = 0; - else - s++; - } - if (x) { - result[n] = NULL; - return result; - } - /* allocation */ - result = malloc((n+1)*sizeof(char*) + s); - if (result == NULL) { - errno = ENOMEM; - return NULL; - } - data = (char*)(&result[n + 1]); - x = 1; - } -} - -static void mksecret(char buffer[9]) -{ - snprintf(buffer, 9, "%08lX", (0xffffffff & random())); -} - -static int mkport() -{ - static int port_ring = 12345; - int port = port_ring; - if (port < 12345 || port > 15432) - port = 12345; - port_ring = port + 1; - return port; -} - -int af_launch(struct af_launch_desc *desc, pid_t children[2]) -{ - char datadir[PATH_MAX]; - int ikl, nkl, rc; - char secret[9]; - int port; - char message[10]; - int mpipe[2]; - int spipe[2]; - struct launchparam params; - char **args; - - /* what launcher ? */ - ikl = 0; - if (desc->type != NULL && *desc->type) { - nkl = sizeof known_launchers / sizeof * known_launchers; - while (ikl < nkl && strcmp(desc->type, known_launchers[ikl].type)) - ikl++; - if (ikl == nkl) { - ERROR("type %s not found!", desc->type); - errno = ENOENT; - return -1; - } - } - - /* prepare paths */ - rc = snprintf(datadir, sizeof datadir, "%s/%s", desc->home, desc->tag); - if (rc < 0 || rc >= sizeof datadir) { - ERROR("overflow for datadir"); - errno = EINVAL; - return -1; - } - - /* make the secret and port */ - mksecret(secret); - port = mkport(); - - params.port = port; - params.secret = secret; - params.datadir = datadir; - - /* prepare the pipes */ - rc = pipe2(mpipe, O_CLOEXEC); - if (rc < 0) { - ERROR("error while calling pipe2: %m"); - return -1; - } - rc = pipe2(spipe, O_CLOEXEC); - if (rc < 0) { - ERROR("error while calling pipe2: %m"); - close(spipe[0]); - close(spipe[1]); - return -1; - } - - /* fork the master child */ - children[0] = fork(); - if (children[0] < 0) { - ERROR("master fork failed: %m"); - close(mpipe[0]); - close(mpipe[1]); - close(spipe[0]); - close(spipe[1]); - return -1; - } - if (children[0]) { - /********* in the parent process ************/ - close(mpipe[1]); - close(spipe[0]); - /* wait the ready signal (that transmit the slave pid) */ - rc = read(mpipe[0], &children[1], sizeof children[1]); - if (rc < 0) { - ERROR("reading master pipe failed: %m"); - close(mpipe[0]); - close(spipe[1]); - return -1; - } - close(mpipe[0]); - assert(rc == sizeof children[1]); - /* start the child */ - rc = write(spipe[1], "start", 5); - if (rc < 0) { - ERROR("writing slave pipe failed: %m"); - close(spipe[1]); - return -1; - } - assert(rc == 5); - close(spipe[1]); - return 0; - } - - /********* in the master child ************/ - close(mpipe[0]); - close(spipe[1]); - - /* enter the process group */ - rc = setpgid(0, 0); - if (rc) { - ERROR("setpgid failed"); - _exit(1); - } - - /* enter security mode */ - rc = secmgr_prepare_exec(desc->tag); - if (rc < 0) { - ERROR("call to secmgr_prepare_exec failed: %m"); - _exit(1); - } - - /* enter the datadirectory */ - rc = mkdir(datadir, 0755); - if (rc && errno != EEXIST) { - ERROR("creation of datadir %s failed: %m", datadir); - _exit(1); - } - rc = chdir(datadir); - if (rc) { - ERROR("can't enter the datadir %s: %m", datadir); - _exit(1); - } - - /* fork the slave child */ - children[1] = fork(); - if (children[1] < 0) { - ERROR("slave fork failed: %m"); - _exit(1); - } - if (children[1] == 0) { - /********* in the slave child ************/ - close(mpipe[0]); - rc = read(spipe[0], message, sizeof message); - if (rc < 0) { - ERROR("reading slave pipe failed: %m"); - _exit(1); - } - - args = instantiate_arguments(known_launchers[ikl].launch_args, desc, ¶ms); - if (args == NULL) { - ERROR("out of memory in slave"); - } - else { - rc = execve(args[0], args, environ); - ERROR("failed to exec slave %s: %m", args[0]); - } - _exit(1); - } - - /********* still in the master child ************/ - close(spipe[1]); - args = instantiate_arguments(launch_master_args, desc, ¶ms); - if (args == NULL) { - ERROR("out of memory in master"); - } - else { - rc = write(mpipe[1], &children[1], sizeof children[1]); - if (rc < 0) { - ERROR("can't write master pipe: %m"); - } - else { - close(mpipe[1]); - rc = execve(args[0], args, environ); - ERROR("failed to exec master %s: %m", args[0]); - } - } - _exit(1); -} - diff --git a/src/af-launch.h b/src/af-launch.h deleted file mode 100644 index 301c98c..0000000 --- a/src/af-launch.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - Copyright 2015 IoT.bzh - - author: José Bollo - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -struct af_launch_desc { - const char *path; - const char *tag; - const char *appid; - const char *content; - const char *type; - const char *name; - const char *home; - const char **plugins; - int width; - int height; -}; - -int af_launch(struct af_launch_desc *desc, pid_t children[2]); - diff --git a/src/af-run.c b/src/af-run.c deleted file mode 100644 index 6512a11..0000000 --- a/src/af-run.c +++ /dev/null @@ -1,478 +0,0 @@ -/* - Copyright 2015 IoT.bzh - - author: José Bollo - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "verbose.h" -#include "utils-dir.h" -#include "af-launch.h" - -enum appstate { - as_starting, - as_running, - as_stopped, - as_terminating, - as_terminated -}; - -struct apprun { - struct apprun *next_by_runid; - struct apprun *next_by_pgid; - int runid; - pid_t pids[2]; /* 0: group leader, 1: slave (appli) */ - enum appstate state; - json_object *appli; -}; - -#define ROOT_RUNNERS_COUNT 32 -#define MAX_RUNNER_COUNT 32767 - -static struct apprun *runners_by_runid[ROOT_RUNNERS_COUNT]; -static struct apprun *runners_by_pgid[ROOT_RUNNERS_COUNT]; -static int runnercount = 0; -static int runnerid = 0; - -static const char fwk_user_app_dir[] = FWK_USER_APP_DIR; -static char *homeappdir; - -/****************** manages pgids **********************/ - -/* get a runner by its pgid */ -static struct apprun *runner_of_pgid(pid_t pgid) -{ - struct apprun *result = runners_by_pgid[(int)(pgid & (ROOT_RUNNERS_COUNT - 1))]; - while (result && result->pids[0] != pgid) - result = result->next_by_pgid; - return result; -} - -/* insert a runner for its pgid */ -static void pgid_insert(struct apprun *runner) -{ - struct apprun **prev = &runners_by_runid[(int)(runner->pids[0] & (ROOT_RUNNERS_COUNT - 1))]; - runner->next_by_pgid = *prev; - *prev = runner; -} - -/* remove a runner for its pgid */ -static void pgid_remove(struct apprun *runner) -{ - struct apprun **prev = &runners_by_runid[(int)(runner->pids[0] & (ROOT_RUNNERS_COUNT - 1))]; - runner->next_by_pgid = *prev; - *prev = runner; -} - -/****************** manages pids **********************/ - -/* get a runner by its pid */ -static struct apprun *runner_of_pid(pid_t pid) -{ - /* try avoiding system call */ - struct apprun *result = runner_of_pgid(pid); - if (result == NULL) { - result = runner_of_pgid(getpgid(pid)); - if (result && result->pids[1] != pid) - result = NULL; - } - return result; -} - -/****************** manages runners (by runid) **********************/ - -/* get a runner by its runid */ -static struct apprun *getrunner(int runid) -{ - struct apprun *result = runners_by_runid[runid & (ROOT_RUNNERS_COUNT - 1)]; - while (result && result->runid != runid) - result = result->next_by_runid; - return result; -} - -/* free an existing runner */ -static void freerunner(struct apprun *runner) -{ - struct apprun **prev = &runners_by_runid[runner->runid & (ROOT_RUNNERS_COUNT - 1)]; - assert(*prev); - while(*prev != runner) { - prev = &(*prev)->next_by_runid; - assert(*prev); - } - *prev = runner->next_by_runid; - json_object_put(runner->appli); - free(runner); - runnercount--; -} - -/* create a new runner */ -static struct apprun *createrunner(json_object *appli) -{ - struct apprun *result; - struct apprun **prev; - - if (runnercount >= MAX_RUNNER_COUNT) { - errno = EAGAIN; - return NULL; - } - do { - runnerid++; - if (runnerid > MAX_RUNNER_COUNT) - runnerid = 1; - } while(getrunner(runnerid)); - result = calloc(1, sizeof * result); - if (result == NULL) - errno = ENOMEM; - else { - prev = &runners_by_runid[runnerid & (ROOT_RUNNERS_COUNT - 1)]; - result->next_by_runid = *prev; - result->next_by_pgid = NULL; - result->runid = runnerid; - result->pids[0] = result->pids[1] = 0; - result->state = as_starting; - result->appli = json_object_get(appli); - *prev = result; - runnercount++; - } - return result; -} - -/**************** signaling ************************/ -#if 0 -static void started(int runid) -{ -} - -static void stopped(int runid) -{ -} - -static void continued(int runid) -{ -} - -static void terminated(int runid) -{ -} - -static void removed(int runid) -{ -} -#endif -/**************** running ************************/ - -static int killrunner(int runid, int sig, enum appstate tostate) -{ - int rc; - struct apprun *runner = getrunner(runid); - if (runner == NULL) { - errno = ENOENT; - rc = -1; - } - else if (runner->state != as_running && runner->state != as_stopped) { - errno = EPERM; - rc = -1; - } - else if (runner->state == tostate) { - rc = 0; - } - else { - rc = killpg(runner->pids[0], sig); - if (!rc) - runner->state = tostate; - } - return rc; -} - -static void on_sigchld(int signum, siginfo_t *info, void *uctxt) -{ - struct apprun *runner; - - runner = runner_of_pgid(info->si_pid); - if (!runner) - return; - - switch(info->si_code) { - case CLD_EXITED: - case CLD_KILLED: - case CLD_DUMPED: - case CLD_TRAPPED: - runner->state = as_terminated; - pgid_remove(runner); - break; - - case CLD_STOPPED: - runner->state = as_stopped; - break; - - case CLD_CONTINUED: - runner->state = as_running; - break; - } -} - -/**************** handle af_launch_desc *********************/ - -static int get_jstr(struct json_object *obj, const char *key, const char **value) -{ - json_object *data; - return json_object_object_get_ex(obj, key, &data) - && json_object_get_type(data) == json_type_string - && (*value = json_object_get_string(data)) != NULL; -} - -static int get_jint(struct json_object *obj, const char *key, int *value) -{ - json_object *data; - return json_object_object_get_ex(obj, key, &data) - && json_object_get_type(data) == json_type_int - && ((*value = (int)json_object_get_int(data)), 1); -} - -static int fill_launch_desc(struct json_object *appli, struct af_launch_desc *desc) -{ - json_object *pub; - - /* main items */ - if(!json_object_object_get_ex(appli, "public", &pub) - || !get_jstr(appli, "path", &desc->path) - || !get_jstr(appli, "id", &desc->tag) - || !get_jstr(appli, "content", &desc->content) - || !get_jstr(appli, "type", &desc->type) - || !get_jstr(pub, "name", &desc->name) - || !get_jint(pub, "width", &desc->width) - || !get_jint(pub, "height", &desc->height)) { - errno = EINVAL; - return -1; - } - - /* plugins */ - { - /* TODO */ - static const char *null = NULL; - desc->plugins = &null; - } - - /* finaly */ - desc->home = homeappdir; - return 0; -}; - -/**************** API handling ************************/ - -int af_run_start(struct json_object *appli) -{ - static struct apprun *runner; - struct af_launch_desc desc; - int rc; - sigset_t saved, blocked; - - /* prepare to launch */ - rc = fill_launch_desc(appli, &desc); - if (rc) - return rc; - runner = createrunner(appli); - if (!runner) - return -1; - - /* block children signals until launched */ - sigemptyset(&blocked); - sigaddset(&blocked, SIGCHLD); - sigprocmask(SIG_BLOCK, &blocked, &saved); - - /* launch now */ - rc = af_launch(&desc, runner->pids); - if (rc < 0) { - /* fork failed */ - sigprocmask(SIG_SETMASK, &saved, NULL); - ERROR("can't start, af_launch failed: %m"); - freerunner(runner); - return -1; - } - - /* insert the pid */ - runner->state = as_running; - pgid_insert(runner); - rc = runner->runid; - - /* unblock children signal now */ - sigprocmask(SIG_SETMASK, &saved, NULL); - return rc; -} - -int af_run_terminate(int runid) -{ - return killrunner(runid, SIGTERM, as_terminating); -} - -int af_run_stop(int runid) -{ - return killrunner(runid, SIGSTOP, as_stopped); -} - -int af_run_continue(int runid) -{ - return killrunner(runid, SIGCONT, as_running); -} - -static json_object *mkstate(struct apprun *runner) -{ - const char *state; - struct json_object *result, *obj; - int rc; - - /* the structure */ - result = json_object_new_object(); - if (result == NULL) - goto error; - - /* the runid */ - obj = json_object_new_int(runner->runid); - if (obj == NULL) - goto error2; - json_object_object_add(result, "runid", obj); /* TODO TEST STATUS */ - - /* the state */ - switch(runner->state) { - case as_starting: - case as_running: - state = "running"; - break; - case as_stopped: - state = "stopped"; - break; - default: - state = "terminated"; - break; - } - obj = json_object_new_string(state); - if (obj == NULL) - goto error2; - json_object_object_add(result, "state", obj); /* TODO TEST STATUS */ - - /* the application id */ - rc = json_object_object_get_ex(runner->appli, "public", &obj); - assert(rc); - rc = json_object_object_get_ex(obj, "id", &obj); - assert(rc); - json_object_object_add(result, "id", obj); /* TODO TEST STATUS */ - json_object_get(obj); - - /* done */ - return result; - -error2: - json_object_put(result); -error: - errno = ENOMEM; - return NULL; -} - -struct json_object *af_run_list() -{ - struct json_object *result, *obj; - struct apprun *runner; - int i; - - /* creates the object */ - result = json_object_new_array(); - if (result == NULL) { - errno = ENOMEM; - return NULL; - } - - for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) { - for (runner = runners_by_runid[i] ; runner ; runner = runner->next_by_runid) { - if (runner->state != as_terminating && runner->state != as_terminated) { - obj = mkstate(runner); - if (obj == NULL) { - json_object_put(result); - return NULL; - } - /* TODO status ? */ - json_object_array_add(result, obj); - } - } - } - return result; -} - -struct json_object *af_run_state(int runid) -{ - struct apprun *runner = getrunner(runid); - if (runner == NULL || runner->state == as_terminating || runner->state == as_terminated) { - errno = ENOENT; - return NULL; - } - return mkstate(runner); -} - -/**************** INITIALISATION **********************/ - -int af_run_init() -{ - char buf[2048]; - char dir[PATH_MAX]; - int rc; - uid_t me; - struct passwd passwd, *pw; - struct sigaction siga; - - /* computes the 'homeappdir' */ - me = geteuid(); - rc = getpwuid_r(me, &passwd, buf, sizeof buf, &pw); - if (rc || pw == NULL) { - errno = rc ? errno : ENOENT; - 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); - return -1; - } - rc = create_directory(dir, 0755, 1); - if (rc && errno != EEXIST) { - ERROR("creation of directory %s failed in user_app_dir: %m", dir); - return -1; - } - homeappdir = strdup(dir); - if (homeappdir == NULL) { - errno = ENOMEM; - ERROR("out of memory in user_app_dir for %s : %m", dir); - return -1; - } - - /* install signal handlers */ - siga.sa_flags = SA_SIGINFO | SA_NOCLDWAIT; - sigemptyset(&siga.sa_mask); - sigaddset(&siga.sa_mask, SIGCHLD); - siga.sa_sigaction = on_sigchld; - sigaction(SIGCHLD, &siga, NULL); - return 0; -} - diff --git a/src/af-run.h b/src/af-run.h deleted file mode 100644 index 1dab682..0000000 --- a/src/af-run.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - Copyright 2015 IoT.bzh - - author: José Bollo - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -extern int af_run_start(struct json_object *appli); -extern int af_run_terminate(int runid); -extern int af_run_stop(int runid); -extern int af_run_continue(int runid); -extern struct json_object *af_run_list(); -extern struct json_object *af_run_state(int runid); - -extern int af_run_init(); diff --git a/src/afm-db.c b/src/afm-db.c new file mode 100644 index 0000000..3f96821 --- /dev/null +++ b/src/afm-db.c @@ -0,0 +1,410 @@ +/* + Copyright 2015 IoT.bzh + + author: José Bollo + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "wgt-info.h" +#include "afm-db.h" + +struct afapps { + struct json_object *pubarr; + struct json_object *direct; + struct json_object *byapp; +}; + +enum dir_type { + type_root, + type_app +}; + +struct afm_db_dir { + struct afm_db_dir *next; + char *path; + enum dir_type type; +}; + +struct afm_db { + int refcount; + struct afm_db_dir *dirhead; + struct afm_db_dir *dirtail; + struct afapps applications; +}; + +struct afm_db *afm_db_create() +{ + struct afm_db *afdb = malloc(sizeof * afdb); + if (afdb == NULL) + errno = ENOMEM; + else { + afdb->refcount = 1; + afdb->dirhead = NULL; + afdb->dirtail = NULL; + afdb->applications.pubarr = NULL; + afdb->applications.direct = NULL; + afdb->applications.byapp = NULL; + } + return afdb; +} + +void afm_db_addref(struct afm_db *afdb) +{ + assert(afdb); + afdb->refcount++; +} + +void afm_db_unref(struct afm_db *afdb) +{ + struct afm_db_dir *dir; + assert(afdb); + if (!--afdb->refcount) { + json_object_put(afdb->applications.pubarr); + json_object_put(afdb->applications.direct); + json_object_put(afdb->applications.byapp); + while (afdb->dirhead != NULL) { + dir = afdb->dirhead; + afdb->dirhead = dir->next; + free(dir->path); + free(dir); + } + free(afdb); + } +} + +int add_dir(struct afm_db *afdb, const char *path, enum dir_type type) +{ + struct afm_db_dir *dir; + char *r; + + assert(afdb); + + /* don't depend on the cwd and unique name */ + r = realpath(path, NULL); + if (!r) + return -1; + + /* avoiding duplications */ + dir = afdb->dirhead; + while(dir != NULL && (strcmp(dir->path, path) || dir->type != type)) + dir = dir ->next; + if (dir != NULL) { + free(r); + return 0; + } + + /* allocates the structure */ + dir = malloc(sizeof * dir); + if (dir == NULL) { + free(r); + errno = ENOMEM; + return -1; + } + + /* add */ + dir->next = NULL; + dir->path = r; + dir->type = type; + if (afdb->dirtail == NULL) + afdb->dirhead = dir; + else + afdb->dirtail->next = dir; + afdb->dirtail = dir; + return 0; +} + +int afm_db_add_root(struct afm_db *afdb, const char *path) +{ + return add_dir(afdb, path, type_root); +} + +int afm_db_add_application(struct afm_db *afdb, const char *path) +{ + return add_dir(afdb, path, type_app); +} + +static int json_add(struct json_object *obj, const char *key, struct json_object *val) +{ + json_object_object_add(obj, key, val); + return 0; +} + +static int json_add_str(struct json_object *obj, const char *key, const char *val) +{ + struct json_object *str = json_object_new_string (val ? val : ""); + return str ? json_add(obj, key, str) : -1; +} + +static int json_add_int(struct json_object *obj, const char *key, int val) +{ + struct json_object *v = json_object_new_int (val); + return v ? json_add(obj, key, v) : -1; +} + +static int addapp(struct afapps *apps, const char *path) +{ + struct wgt_info *info; + const struct wgt_desc *desc; + const struct wgt_desc_feature *feat; + struct json_object *priv = NULL, *pub, *bya, *plugs, *str; + char *appid, *end; + + /* connect to the widget */ + info = wgt_info_createat(AT_FDCWD, path, 0, 1, 0); + if (info == NULL) { + if (errno == ENOENT) + return 0; /* silently ignore bad directories */ + goto error; + } + desc = wgt_info_desc(info); + + /* create the application id */ + appid = alloca(2 + strlen(desc->id) + strlen(desc->version)); + end = stpcpy(appid, desc->id); + *end++ = '@'; + strcpy(end, desc->version); + + /* create the application structure */ + priv = json_object_new_object(); + if (!priv) + goto error2; + + pub = json_object_new_object(); + if (!priv) + goto error2; + + if (json_add(priv, "public", pub)) { + json_object_put(pub); + goto error2; + } + + plugs = json_object_new_array(); + if (!priv) + goto error2; + + if (json_add(priv, "plugins", plugs)) { + json_object_put(plugs); + goto error2; + } + + if(json_add_str(priv, "id", desc->id) + || json_add_str(priv, "path", path) + || json_add_str(priv, "content", desc->content_src) + || json_add_str(priv, "type", desc->content_type) + || json_add_str(pub, "id", appid) + || json_add_str(pub, "version", desc->version) + || json_add_int(pub, "width", desc->width) + || json_add_int(pub, "height", desc->height) + || json_add_str(pub, "name", desc->name) + || json_add_str(pub, "description", desc->description) + || json_add_str(pub, "shortname", desc->name_short) + || json_add_str(pub, "author", desc->author)) + goto error2; + + feat = desc->features; + while (feat) { + static const char prefix[] = FWK_PREFIX_PLUGIN; + if (!memcmp(feat->name, prefix, sizeof prefix - 1)) { + str = json_object_new_string (feat->name + sizeof prefix - 1); + if (str == NULL) + goto error2; + if (json_object_array_add(plugs, str)) { + json_object_put(str); + goto error2; + } + } + feat = feat->next; + } + + /* record the application structure */ + if (!json_object_object_get_ex(apps->byapp, desc->id, &bya)) { + bya = json_object_new_object(); + if (!bya) + goto error2; + if (json_add(apps->byapp, desc->id, bya)) { + json_object_put(bya); + goto error2; + } + } + + if (json_add(apps->direct, appid, priv)) + goto error2; + json_object_get(priv); + + if (json_add(bya, desc->version, priv)) { + json_object_put(priv); + goto error2; + } + + if (json_object_array_add(apps->pubarr, pub)) + goto error2; + + wgt_info_unref(info); + return 0; + +error2: + json_object_put(priv); + wgt_info_unref(info); +error: + return -1; +} + +struct enumdata { + char path[PATH_MAX]; + int length; + struct afapps apps; +}; + +static int enumentries(struct enumdata *data, int (*callto)(struct enumdata *)) +{ + DIR *dir; + int rc; + char *beg, *end; + struct dirent entry, *e; + + /* opens the directory */ + dir = opendir(data->path); + if (!dir) + return -1; + + /* prepare appending entry names */ + beg = data->path + data->length; + *beg++ = '/'; + + /* enumerate entries */ + rc = readdir_r(dir, &entry, &e); + while (!rc && e) { + if (entry.d_name[0] != '.' || (entry.d_name[1] && (entry.d_name[1] != '.' || entry.d_name[2]))) { + /* prepare callto */ + end = stpcpy(beg, entry.d_name); + data->length = end - data->path; + /* call the function */ + rc = callto(data); + if (rc) + break; + } + rc = readdir_r(dir, &entry, &e); + } + closedir(dir); + return rc; +} + +static int recordapp(struct enumdata *data) +{ + return addapp(&data->apps, data->path); +} + +/* enumerate the versions */ +static int enumvers(struct enumdata *data) +{ + int rc = enumentries(data, recordapp); + return !rc || errno != ENOTDIR ? 0 : rc; +} + +/* regenerate the list of applications */ +int afm_db_update_applications(struct afm_db *afdb) +{ + int rc; + struct enumdata edata; + struct afapps oldapps; + struct afm_db_dir *dir; + + /* create the result */ + edata.apps.pubarr = json_object_new_array(); + edata.apps.direct = json_object_new_object(); + edata.apps.byapp = json_object_new_object(); + if (edata.apps.pubarr == NULL || edata.apps.direct == NULL || edata.apps.byapp == NULL) { + errno = ENOMEM; + goto error; + } + /* for each root */ + for (dir = afdb->dirhead ; dir != NULL ; dir = dir->next) { + if (dir->type == type_root) { + edata.length = stpcpy(edata.path, dir->path) - edata.path; + assert(edata.length < sizeof edata.path); + /* enumerate the applications */ + rc = enumentries(&edata, enumvers); + if (rc) + goto error; + } else { + rc = addapp(&edata.apps, dir->path); + } + } + /* commit the result */ + oldapps = afdb->applications; + afdb->applications = edata.apps; + json_object_put(oldapps.pubarr); + json_object_put(oldapps.direct); + json_object_put(oldapps.byapp); + return 0; + +error: + json_object_put(edata.apps.pubarr); + json_object_put(edata.apps.direct); + json_object_put(edata.apps.byapp); + return -1; +} + +int afm_db_ensure_applications(struct afm_db *afdb) +{ + return afdb->applications.pubarr ? 0 : afm_db_update_applications(afdb); +} + +struct json_object *afm_db_application_list(struct afm_db *afdb) +{ + return afm_db_ensure_applications(afdb) ? NULL : json_object_get(afdb->applications.pubarr); +} + +struct json_object *afm_db_get_application(struct afm_db *afdb, const char *id) +{ + struct json_object *result; + if (!afm_db_ensure_applications(afdb) && json_object_object_get_ex(afdb->applications.direct, id, &result)) + return json_object_get(result); + return NULL; +} + +struct json_object *afm_db_get_application_public(struct afm_db *afdb, const char *id) +{ + struct json_object *result = afm_db_get_application(afdb, id); + return result && json_object_object_get_ex(result, "public", &result) ? json_object_get(result) : NULL; +} + + + + +#if defined(TESTAPPFWK) +#include +int main() +{ +struct afm_db *afdb = afm_db_create(); +afm_db_add_root(afdb,FWK_APP_DIR); +afm_db_update_applications(afdb); +printf("array = %s\n", json_object_to_json_string_ext(afdb->applications.pubarr, 3)); +printf("direct = %s\n", json_object_to_json_string_ext(afdb->applications.direct, 3)); +printf("byapp = %s\n", json_object_to_json_string_ext(afdb->applications.byapp, 3)); +return 0; +} +#endif + diff --git a/src/afm-db.h b/src/afm-db.h new file mode 100644 index 0000000..fff5f4c --- /dev/null +++ b/src/afm-db.h @@ -0,0 +1,33 @@ +/* + Copyright 2015 IoT.bzh + + author: José Bollo + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +struct afm_db; + +extern struct afm_db *afm_db_create(); +extern void afm_db_addref(struct afm_db *afdb); +extern void afm_db_unref(struct afm_db *afdb); + +extern int afm_db_add_root(struct afm_db *afdb, const char *path); +extern int afm_db_add_application(struct afm_db *afdb, const char *path); +extern int afm_db_update_applications(struct afm_db *afdb); +extern int afm_db_ensure_applications(struct afm_db *afdb); + +extern struct json_object *afm_db_application_list(struct afm_db *afdb); +extern struct json_object *afm_db_get_application(struct afm_db *afdb, const char *id); +extern struct json_object *afm_db_get_application_public(struct afm_db *afdb, const char *id); + diff --git a/src/afm-launch.c b/src/afm-launch.c new file mode 100644 index 0000000..12a481e --- /dev/null +++ b/src/afm-launch.c @@ -0,0 +1,358 @@ +/* + Copyright 2015 IoT.bzh + + author: José Bollo + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char **environ; + +#include "verbose.h" +#include "afm-launch.h" +#include "secmgr-wrap.h" + +/* +%I icondir FWK_ICON_DIR +%P port params->port +%S secret params->secret +%D datadir params->datadir +%r rootdir desc->path +%h homedir desc->home +%t tag (smack label) desc->tag +%a appid desc->appid +%c content desc->content +%m mime-type desc->type +%n name desc->name +%p plugins desc->plugins +%W width desc->width +%H height desc->height +*/ + +static const char *launch_master_args[] = { + "/usr/bin/echo", + "--alias=/icons:%I", + "--port=%P", + "--rootdir=%D", + "--token=%S", + NULL +}; + +static const char *launch_html_args[] = { + "/usr/bin/chromium", + "--single-process", + "--user-data-dir=%D", + "--data-path=%r", + "file://%r/%c", +/* + "http://localhost:%P", +*/ + NULL +}; + +static const char *launch_bin_args[] = { + "/usr/bin/echo", + "BINARY", + NULL +}; + +static const char *launch_qml_args[] = { + "/usr/bin/echo", + "QML", + NULL +}; + +static struct { + const char *type; + const char **launch_args; +} +known_launchers[] = { + { "text/html", launch_html_args }, + { "application/x-executable", launch_bin_args }, + { "application/octet-stream", launch_bin_args }, + { "text/vnd.qt.qml", launch_qml_args } +}; + +struct launchparam { + int port; + const char *secret; + const char *datadir; +}; + +static char **instantiate_arguments(const char **args, struct afm_launch_desc *desc, struct launchparam *params) +{ + const char **iter, *p, *v; + char *data, **result, port[20], width[20], height[20], mini[3], c; + int n, s, x; + + /* init */ + mini[0] = '%'; + mini[2] = 0; + + /* loop that either compute the size and build the result */ + n = s = x = 0; + for (;;) { + iter = args; + n = 0; + while (*iter) { + p = *iter++; + if (x) + result[n] = data; + n++; + while((c = *p++) != 0) { + if (c != '%') { + if (x) + *data++ = c; + else + s++; + } else { + c = *p++; + switch (c) { + case 'I': v = FWK_ICON_DIR; break; + case 'P': if(!x) sprintf(port, "%d", params->port); v = port; break; + case 'S': v = params->secret; break; + case 'D': v = params->datadir; break; + case 'r': v = desc->path; break; + case 'h': v = desc->home; break; + case 't': v = desc->tag; break; + case 'a': v = desc->appid; break; + case 'c': v = desc->content; break; + case 'm': v = desc->type; break; + case 'n': v = desc->name; break; + case 'p': v = "" /*desc->plugins*/; break; + case 'W': if(!x) sprintf(width, "%d", desc->width); v = width; break; + case 'H': if(!x) sprintf(height, "%d", desc->height); v = height; break; + case '%': c = 0; + default: mini[1] = c; v = mini; break; + } + if (x) + data = stpcpy(data, v); + else + s += strlen(v); + } + } + if (x) + *data++ = 0; + else + s++; + } + if (x) { + result[n] = NULL; + return result; + } + /* allocation */ + result = malloc((n+1)*sizeof(char*) + s); + if (result == NULL) { + errno = ENOMEM; + return NULL; + } + data = (char*)(&result[n + 1]); + x = 1; + } +} + +static void mksecret(char buffer[9]) +{ + snprintf(buffer, 9, "%08lX", (0xffffffff & random())); +} + +static int mkport() +{ + static int port_ring = 12345; + int port = port_ring; + if (port < 12345 || port > 15432) + port = 12345; + port_ring = port + 1; + return port; +} + +int afm_launch(struct afm_launch_desc *desc, pid_t children[2]) +{ + char datadir[PATH_MAX]; + int ikl, nkl, rc; + char secret[9]; + int port; + char message[10]; + int mpipe[2]; + int spipe[2]; + struct launchparam params; + char **args; + + /* what launcher ? */ + ikl = 0; + if (desc->type != NULL && *desc->type) { + nkl = sizeof known_launchers / sizeof * known_launchers; + while (ikl < nkl && strcmp(desc->type, known_launchers[ikl].type)) + ikl++; + if (ikl == nkl) { + ERROR("type %s not found!", desc->type); + errno = ENOENT; + return -1; + } + } + + /* prepare paths */ + rc = snprintf(datadir, sizeof datadir, "%s/%s", desc->home, desc->tag); + if (rc < 0 || rc >= sizeof datadir) { + ERROR("overflow for datadir"); + errno = EINVAL; + return -1; + } + + /* make the secret and port */ + mksecret(secret); + port = mkport(); + + params.port = port; + params.secret = secret; + params.datadir = datadir; + + /* prepare the pipes */ + rc = pipe2(mpipe, O_CLOEXEC); + if (rc < 0) { + ERROR("error while calling pipe2: %m"); + return -1; + } + rc = pipe2(spipe, O_CLOEXEC); + if (rc < 0) { + ERROR("error while calling pipe2: %m"); + close(spipe[0]); + close(spipe[1]); + return -1; + } + + /* fork the master child */ + children[0] = fork(); + if (children[0] < 0) { + ERROR("master fork failed: %m"); + close(mpipe[0]); + close(mpipe[1]); + close(spipe[0]); + close(spipe[1]); + return -1; + } + if (children[0]) { + /********* in the parent process ************/ + close(mpipe[1]); + close(spipe[0]); + /* wait the ready signal (that transmit the slave pid) */ + rc = read(mpipe[0], &children[1], sizeof children[1]); + if (rc < 0) { + ERROR("reading master pipe failed: %m"); + close(mpipe[0]); + close(spipe[1]); + return -1; + } + close(mpipe[0]); + assert(rc == sizeof children[1]); + /* start the child */ + rc = write(spipe[1], "start", 5); + if (rc < 0) { + ERROR("writing slave pipe failed: %m"); + close(spipe[1]); + return -1; + } + assert(rc == 5); + close(spipe[1]); + return 0; + } + + /********* in the master child ************/ + close(mpipe[0]); + close(spipe[1]); + + /* enter the process group */ + rc = setpgid(0, 0); + if (rc) { + ERROR("setpgid failed"); + _exit(1); + } + + /* enter security mode */ + rc = secmgr_prepare_exec(desc->tag); + if (rc < 0) { + ERROR("call to secmgr_prepare_exec failed: %m"); + _exit(1); + } + + /* enter the datadirectory */ + rc = mkdir(datadir, 0755); + if (rc && errno != EEXIST) { + ERROR("creation of datadir %s failed: %m", datadir); + _exit(1); + } + rc = chdir(datadir); + if (rc) { + ERROR("can't enter the datadir %s: %m", datadir); + _exit(1); + } + + /* fork the slave child */ + children[1] = fork(); + if (children[1] < 0) { + ERROR("slave fork failed: %m"); + _exit(1); + } + if (children[1] == 0) { + /********* in the slave child ************/ + close(mpipe[0]); + rc = read(spipe[0], message, sizeof message); + if (rc < 0) { + ERROR("reading slave pipe failed: %m"); + _exit(1); + } + + args = instantiate_arguments(known_launchers[ikl].launch_args, desc, ¶ms); + if (args == NULL) { + ERROR("out of memory in slave"); + } + else { + rc = execve(args[0], args, environ); + ERROR("failed to exec slave %s: %m", args[0]); + } + _exit(1); + } + + /********* still in the master child ************/ + close(spipe[1]); + args = instantiate_arguments(launch_master_args, desc, ¶ms); + if (args == NULL) { + ERROR("out of memory in master"); + } + else { + rc = write(mpipe[1], &children[1], sizeof children[1]); + if (rc < 0) { + ERROR("can't write master pipe: %m"); + } + else { + close(mpipe[1]); + rc = execve(args[0], args, environ); + ERROR("failed to exec master %s: %m", args[0]); + } + } + _exit(1); +} + diff --git a/src/afm-launch.h b/src/afm-launch.h new file mode 100644 index 0000000..c6aaf49 --- /dev/null +++ b/src/afm-launch.h @@ -0,0 +1,33 @@ +/* + Copyright 2015 IoT.bzh + + author: José Bollo + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +struct afm_launch_desc { + const char *path; + const char *tag; + const char *appid; + const char *content; + const char *type; + const char *name; + const char *home; + const char **plugins; + int width; + int height; +}; + +int afm_launch(struct afm_launch_desc *desc, pid_t children[2]); + diff --git a/src/afm-run.c b/src/afm-run.c new file mode 100644 index 0000000..f28849a --- /dev/null +++ b/src/afm-run.c @@ -0,0 +1,479 @@ +/* + Copyright 2015 IoT.bzh + + author: José Bollo + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "verbose.h" +#include "utils-dir.h" +#include "afm-run.h" +#include "afm-launch.h" + +enum appstate { + as_starting, + as_running, + as_stopped, + as_terminating, + as_terminated +}; + +struct apprun { + struct apprun *next_by_runid; + struct apprun *next_by_pgid; + int runid; + pid_t pids[2]; /* 0: group leader, 1: slave (appli) */ + enum appstate state; + json_object *appli; +}; + +#define ROOT_RUNNERS_COUNT 32 +#define MAX_RUNNER_COUNT 32767 + +static struct apprun *runners_by_runid[ROOT_RUNNERS_COUNT]; +static struct apprun *runners_by_pgid[ROOT_RUNNERS_COUNT]; +static int runnercount = 0; +static int runnerid = 0; + +static const char fwk_user_app_dir[] = FWK_USER_APP_DIR; +static char *homeappdir; + +/****************** manages pgids **********************/ + +/* get a runner by its pgid */ +static struct apprun *runner_of_pgid(pid_t pgid) +{ + struct apprun *result = runners_by_pgid[(int)(pgid & (ROOT_RUNNERS_COUNT - 1))]; + while (result && result->pids[0] != pgid) + result = result->next_by_pgid; + return result; +} + +/* insert a runner for its pgid */ +static void pgid_insert(struct apprun *runner) +{ + struct apprun **prev = &runners_by_runid[(int)(runner->pids[0] & (ROOT_RUNNERS_COUNT - 1))]; + runner->next_by_pgid = *prev; + *prev = runner; +} + +/* remove a runner for its pgid */ +static void pgid_remove(struct apprun *runner) +{ + struct apprun **prev = &runners_by_runid[(int)(runner->pids[0] & (ROOT_RUNNERS_COUNT - 1))]; + runner->next_by_pgid = *prev; + *prev = runner; +} + +/****************** manages pids **********************/ + +/* get a runner by its pid */ +static struct apprun *runner_of_pid(pid_t pid) +{ + /* try avoiding system call */ + struct apprun *result = runner_of_pgid(pid); + if (result == NULL) { + result = runner_of_pgid(getpgid(pid)); + if (result && result->pids[1] != pid) + result = NULL; + } + return result; +} + +/****************** manages runners (by runid) **********************/ + +/* get a runner by its runid */ +static struct apprun *getrunner(int runid) +{ + struct apprun *result = runners_by_runid[runid & (ROOT_RUNNERS_COUNT - 1)]; + while (result && result->runid != runid) + result = result->next_by_runid; + return result; +} + +/* free an existing runner */ +static void freerunner(struct apprun *runner) +{ + struct apprun **prev = &runners_by_runid[runner->runid & (ROOT_RUNNERS_COUNT - 1)]; + assert(*prev); + while(*prev != runner) { + prev = &(*prev)->next_by_runid; + assert(*prev); + } + *prev = runner->next_by_runid; + json_object_put(runner->appli); + free(runner); + runnercount--; +} + +/* create a new runner */ +static struct apprun *createrunner(json_object *appli) +{ + struct apprun *result; + struct apprun **prev; + + if (runnercount >= MAX_RUNNER_COUNT) { + errno = EAGAIN; + return NULL; + } + do { + runnerid++; + if (runnerid > MAX_RUNNER_COUNT) + runnerid = 1; + } while(getrunner(runnerid)); + result = calloc(1, sizeof * result); + if (result == NULL) + errno = ENOMEM; + else { + prev = &runners_by_runid[runnerid & (ROOT_RUNNERS_COUNT - 1)]; + result->next_by_runid = *prev; + result->next_by_pgid = NULL; + result->runid = runnerid; + result->pids[0] = result->pids[1] = 0; + result->state = as_starting; + result->appli = json_object_get(appli); + *prev = result; + runnercount++; + } + return result; +} + +/**************** signaling ************************/ +#if 0 +static void started(int runid) +{ +} + +static void stopped(int runid) +{ +} + +static void continued(int runid) +{ +} + +static void terminated(int runid) +{ +} + +static void removed(int runid) +{ +} +#endif +/**************** running ************************/ + +static int killrunner(int runid, int sig, enum appstate tostate) +{ + int rc; + struct apprun *runner = getrunner(runid); + if (runner == NULL) { + errno = ENOENT; + rc = -1; + } + else if (runner->state != as_running && runner->state != as_stopped) { + errno = EPERM; + rc = -1; + } + else if (runner->state == tostate) { + rc = 0; + } + else { + rc = killpg(runner->pids[0], sig); + if (!rc) + runner->state = tostate; + } + return rc; +} + +static void on_sigchld(int signum, siginfo_t *info, void *uctxt) +{ + struct apprun *runner; + + runner = runner_of_pgid(info->si_pid); + if (!runner) + return; + + switch(info->si_code) { + case CLD_EXITED: + case CLD_KILLED: + case CLD_DUMPED: + case CLD_TRAPPED: + runner->state = as_terminated; + pgid_remove(runner); + break; + + case CLD_STOPPED: + runner->state = as_stopped; + break; + + case CLD_CONTINUED: + runner->state = as_running; + break; + } +} + +/**************** handle afm_launch_desc *********************/ + +static int get_jstr(struct json_object *obj, const char *key, const char **value) +{ + json_object *data; + return json_object_object_get_ex(obj, key, &data) + && json_object_get_type(data) == json_type_string + && (*value = json_object_get_string(data)) != NULL; +} + +static int get_jint(struct json_object *obj, const char *key, int *value) +{ + json_object *data; + return json_object_object_get_ex(obj, key, &data) + && json_object_get_type(data) == json_type_int + && ((*value = (int)json_object_get_int(data)), 1); +} + +static int fill_launch_desc(struct json_object *appli, struct afm_launch_desc *desc) +{ + json_object *pub; + + /* main items */ + if(!json_object_object_get_ex(appli, "public", &pub) + || !get_jstr(appli, "path", &desc->path) + || !get_jstr(appli, "id", &desc->tag) + || !get_jstr(appli, "content", &desc->content) + || !get_jstr(appli, "type", &desc->type) + || !get_jstr(pub, "name", &desc->name) + || !get_jint(pub, "width", &desc->width) + || !get_jint(pub, "height", &desc->height)) { + errno = EINVAL; + return -1; + } + + /* plugins */ + { + /* TODO */ + static const char *null = NULL; + desc->plugins = &null; + } + + /* finaly */ + desc->home = homeappdir; + return 0; +}; + +/**************** API handling ************************/ + +int afm_run_start(struct json_object *appli) +{ + static struct apprun *runner; + struct afm_launch_desc desc; + int rc; + sigset_t saved, blocked; + + /* prepare to launch */ + rc = fill_launch_desc(appli, &desc); + if (rc) + return rc; + runner = createrunner(appli); + if (!runner) + return -1; + + /* block children signals until launched */ + sigemptyset(&blocked); + sigaddset(&blocked, SIGCHLD); + sigprocmask(SIG_BLOCK, &blocked, &saved); + + /* launch now */ + rc = afm_launch(&desc, runner->pids); + if (rc < 0) { + /* fork failed */ + sigprocmask(SIG_SETMASK, &saved, NULL); + ERROR("can't start, afm_launch failed: %m"); + freerunner(runner); + return -1; + } + + /* insert the pid */ + runner->state = as_running; + pgid_insert(runner); + rc = runner->runid; + + /* unblock children signal now */ + sigprocmask(SIG_SETMASK, &saved, NULL); + return rc; +} + +int afm_run_terminate(int runid) +{ + return killrunner(runid, SIGTERM, as_terminating); +} + +int afm_run_stop(int runid) +{ + return killrunner(runid, SIGSTOP, as_stopped); +} + +int afm_run_continue(int runid) +{ + return killrunner(runid, SIGCONT, as_running); +} + +static json_object *mkstate(struct apprun *runner) +{ + const char *state; + struct json_object *result, *obj; + int rc; + + /* the structure */ + result = json_object_new_object(); + if (result == NULL) + goto error; + + /* the runid */ + obj = json_object_new_int(runner->runid); + if (obj == NULL) + goto error2; + json_object_object_add(result, "runid", obj); /* TODO TEST STATUS */ + + /* the state */ + switch(runner->state) { + case as_starting: + case as_running: + state = "running"; + break; + case as_stopped: + state = "stopped"; + break; + default: + state = "terminated"; + break; + } + obj = json_object_new_string(state); + if (obj == NULL) + goto error2; + json_object_object_add(result, "state", obj); /* TODO TEST STATUS */ + + /* the application id */ + rc = json_object_object_get_ex(runner->appli, "public", &obj); + assert(rc); + rc = json_object_object_get_ex(obj, "id", &obj); + assert(rc); + json_object_object_add(result, "id", obj); /* TODO TEST STATUS */ + json_object_get(obj); + + /* done */ + return result; + +error2: + json_object_put(result); +error: + errno = ENOMEM; + return NULL; +} + +struct json_object *afm_run_list() +{ + struct json_object *result, *obj; + struct apprun *runner; + int i; + + /* creates the object */ + result = json_object_new_array(); + if (result == NULL) { + errno = ENOMEM; + return NULL; + } + + for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) { + for (runner = runners_by_runid[i] ; runner ; runner = runner->next_by_runid) { + if (runner->state != as_terminating && runner->state != as_terminated) { + obj = mkstate(runner); + if (obj == NULL) { + json_object_put(result); + return NULL; + } + /* TODO status ? */ + json_object_array_add(result, obj); + } + } + } + return result; +} + +struct json_object *afm_run_state(int runid) +{ + struct apprun *runner = getrunner(runid); + if (runner == NULL || runner->state == as_terminating || runner->state == as_terminated) { + errno = ENOENT; + return NULL; + } + return mkstate(runner); +} + +/**************** INITIALISATION **********************/ + +int afm_run_init() +{ + char buf[2048]; + char dir[PATH_MAX]; + int rc; + uid_t me; + struct passwd passwd, *pw; + struct sigaction siga; + + /* computes the 'homeappdir' */ + me = geteuid(); + rc = getpwuid_r(me, &passwd, buf, sizeof buf, &pw); + if (rc || pw == NULL) { + errno = rc ? errno : ENOENT; + 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); + return -1; + } + rc = create_directory(dir, 0755, 1); + if (rc && errno != EEXIST) { + ERROR("creation of directory %s failed in user_app_dir: %m", dir); + return -1; + } + homeappdir = strdup(dir); + if (homeappdir == NULL) { + errno = ENOMEM; + ERROR("out of memory in user_app_dir for %s : %m", dir); + return -1; + } + + /* install signal handlers */ + siga.sa_flags = SA_SIGINFO | SA_NOCLDWAIT; + sigemptyset(&siga.sa_mask); + sigaddset(&siga.sa_mask, SIGCHLD); + siga.sa_sigaction = on_sigchld; + sigaction(SIGCHLD, &siga, NULL); + return 0; +} + diff --git a/src/afm-run.h b/src/afm-run.h new file mode 100644 index 0000000..4ad0de1 --- /dev/null +++ b/src/afm-run.h @@ -0,0 +1,26 @@ +/* + Copyright 2015 IoT.bzh + + author: José Bollo + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +extern int afm_run_start(struct json_object *appli); +extern int afm_run_terminate(int runid); +extern int afm_run_stop(int runid); +extern int afm_run_continue(int runid); +extern struct json_object *afm_run_list(); +extern struct json_object *afm_run_state(int runid); + +extern int afm_run_init(); diff --git a/src/afm-system-daemon.c b/src/afm-system-daemon.c new file mode 100644 index 0000000..f938372 --- /dev/null +++ b/src/afm-system-daemon.c @@ -0,0 +1,213 @@ +/* + Copyright 2015 IoT.bzh + + author: José Bollo + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include +#include +#include +#include + +#include + +#include "verbose.h" +#include "utils-jbus.h" +#include "afm.h" +#include "afm-db.h" + +static const char appname[] = "afm-system-daemon"; + +static void usage() +{ + printf( + "usage: %s [-q] [-v] [-r rootdir]... [-a appdir]...\n" + "\n" + " -a appdir adds an application directory\n" + " -r rootdir adds a root directory of applications\n" + " -d run as a daemon\n" + " -q quiet\n" + " -v verbose\n" + "\n", + appname + ); +} + +static struct option options[] = { + { "root", required_argument, NULL, 'r' }, + { "application", required_argument, NULL, 'a' }, + { "daemon", no_argument, NULL, 'd' }, + { "quiet", no_argument, NULL, 'q' }, + { "verbose", no_argument, NULL, 'v' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } +}; + +static struct jbus *jbus; +static struct afm_db *afdb; + +const char error_nothing[] = "[]"; +const char error_bad_request[] = "\"bad request\""; +const char error_not_found[] = "\"not found\""; +const char error_cant_start[] = "\"can't start\""; + +static const char *getappid(struct json_object *obj) +{ + return json_type_string == json_object_get_type(obj) ? json_object_get_string(obj) : NULL; +} + +static void reply(struct jreq *jreq, struct json_object *resp, const char *errstr) +{ + if (resp) + jbus_reply_j(jreq, resp); + else + jbus_reply_error_s(jreq, errstr); +} + +static void on_runnables(struct jreq *jreq, struct json_object *obj) +{ + struct json_object *resp = afm_db_application_list(afdb); + jbus_reply_j(jreq, resp); + json_object_put(resp); +} + +static void on_detail(struct jreq *jreq, struct json_object *obj) +{ + const char *appid = getappid(obj); + struct json_object *resp = afm_db_get_application_public(afdb, appid); + reply(jreq, resp, error_not_found); + json_object_put(resp); +} + +extern void install_widget(const char *wgtfile, const char *root, int force); +static void on_install(struct jreq *jreq, struct json_object *obj) +{ + jbus_reply_error_s(jreq, "\"not yet implemented\""); +} + +static void on_uninstall(struct jreq *jreq, struct json_object *obj) +{ + jbus_reply_error_s(jreq, "\"not yet implemented\""); +} + +static int daemonize() +{ + int rc = fork(); + if (rc < 0) + return rc; + if (rc) + _exit(0); + return 0; +} + +int main(int ac, char **av) +{ + int i, daemon = 0; + + LOGAUTH(appname); + + /* first interpretation of arguments */ + while ((i = getopt_long(ac, av, "hdqvr:a:", options, NULL)) >= 0) { + switch (i) { + case 'h': + usage(); + return 0; + case 'q': + if (verbosity) + verbosity--; + break; + case 'v': + verbosity++; + break; + case 'd': + daemon = 1; + break; + case 'r': + break; + case 'a': + break; + case ':': + ERROR("missing argument value"); + return 1; + default: + ERROR("unrecognized option"); + return 1; + } + } + + /* init framework */ + afdb = afm_db_create(); + if (!afdb) { + ERROR("afm_create failed"); + return 1; + } + if (afm_db_add_root(afdb, FWK_APP_DIR)) { + ERROR("can't add root %s", FWK_APP_DIR); + return 1; + } + + /* second interpretation of arguments */ + optind = 1; + while ((i = getopt_long(ac, av, "hdqvr:a:", options, NULL)) >= 0) { + switch (i) { + case 'r': + if (afm_db_add_root(afdb, optarg)) { + ERROR("can't add root %s", optarg); + return 1; + } + break; + case 'a': + if (afm_db_add_application(afdb, optarg)) { + ERROR("can't add application %s", optarg); + return 1; + } + break; + } + } + + /* update the database */ + if (afm_db_update_applications(afdb)) { + ERROR("afm_update_applications failed"); + return 1; + } + + if (daemon && daemonize()) { + ERROR("daemonization failed"); + return 1; + } + + /* init service */ + jbus = create_jbus(0, AFM_SYSTEM_DBUS_PATH); + if (!jbus) { + ERROR("create_jbus failed"); + return 1; + } + if(jbus_add_service_j(jbus, "runnables", on_runnables) + || jbus_add_service_j(jbus, "detail", on_detail) + || jbus_add_service_j(jbus, "install", on_install) + || jbus_add_service_j(jbus, "uninstall", on_uninstall)) { + ERROR("adding services failed"); + return 1; + } + + /* start and run */ + if (jbus_start_serving(jbus)) { + ERROR("cant start server"); + return 1; + } + while (!jbus_read_write_dispatch(jbus, -1)); + return 0; +} + diff --git a/src/afm-user-daemon.c b/src/afm-user-daemon.c index 346c58b..1459dde 100644 --- a/src/afm-user-daemon.c +++ b/src/afm-user-daemon.c @@ -25,8 +25,9 @@ #include "verbose.h" #include "utils-jbus.h" -#include "af-db.h" -#include "af-run.h" +#include "afm.h" +#include "afm-db.h" +#include "afm-run.h" static const char appname[] = "afm-user-daemon"; @@ -56,7 +57,8 @@ static struct option options[] = { }; static struct jbus *jbus; -static struct af_db *afdb; +static struct jbus *jbusys; +static struct afm_db *afdb; const char error_nothing[] = "[]"; const char error_bad_request[] = "\"bad request\""; @@ -91,17 +93,17 @@ static void reply_status(struct jreq *jreq, int status) static void on_runnables(struct jreq *jreq, struct json_object *obj) { - struct json_object *resp = af_db_application_list(afdb); + struct json_object *resp = afm_db_application_list(afdb); jbus_reply_j(jreq, resp); - json_object_put(obj); + json_object_put(resp); } static void on_detail(struct jreq *jreq, struct json_object *obj) { const char *appid = getappid(obj); - struct json_object *resp = af_db_get_application_public(afdb, appid); + struct json_object *resp = afm_db_get_application_public(afdb, appid); reply(jreq, resp, error_not_found); - json_object_put(obj); + json_object_put(resp); } static void on_start(struct jreq *jreq, struct json_object *obj) @@ -115,11 +117,11 @@ static void on_start(struct jreq *jreq, struct json_object *obj) if (appid == NULL) jbus_reply_error_s(jreq, error_bad_request); else { - appli = af_db_get_application(afdb, appid); + appli = afm_db_get_application(afdb, appid); if (appli == NULL) jbus_reply_error_s(jreq, error_not_found); else { - runid = af_run_start(appli); + runid = afm_run_start(appli); if (runid <= 0) jbus_reply_error_s(jreq, error_cant_start); else { @@ -129,48 +131,50 @@ static void on_start(struct jreq *jreq, struct json_object *obj) } } } - json_object_put(obj); } static void on_stop(struct jreq *jreq, struct json_object *obj) { int runid = getrunid(obj); - int status = af_run_stop(runid); + int status = afm_run_stop(runid); reply_status(jreq, status); - json_object_put(obj); } static void on_continue(struct jreq *jreq, struct json_object *obj) { int runid = getrunid(obj); - int status = af_run_continue(runid); + int status = afm_run_continue(runid); reply_status(jreq, status); - json_object_put(obj); } static void on_terminate(struct jreq *jreq, struct json_object *obj) { int runid = getrunid(obj); - int status = af_run_terminate(runid); + int status = afm_run_terminate(runid); reply_status(jreq, status); - json_object_put(obj); } static void on_runners(struct jreq *jreq, struct json_object *obj) { - struct json_object *resp = af_run_list(); + struct json_object *resp = afm_run_list(); jbus_reply_j(jreq, resp); json_object_put(resp); - json_object_put(obj); } static void on_state(struct jreq *jreq, struct json_object *obj) { int runid = getrunid(obj); - struct json_object *resp = af_run_state(runid); + struct json_object *resp = afm_run_state(runid); reply(jreq, resp, error_not_found); json_object_put(resp); - json_object_put(obj); +} + +static void on_signal_changed(struct json_object *obj) +{ + /* update the database */ + afm_db_update_applications(afdb); + /* propagate now */ + jbus_send_signal_j(jbus, "changed", obj); } static int daemonize() @@ -222,18 +226,18 @@ int main(int ac, char **av) srandom((unsigned int)time(NULL)); /* init runners */ - if (af_run_init()) { - ERROR("af_run_init failed"); + if (afm_run_init()) { + ERROR("afm_run_init failed"); return 1; } /* init framework */ - afdb = af_db_create(); + afdb = afm_db_create(); if (!afdb) { - ERROR("af_create failed"); + ERROR("afm_create failed"); return 1; } - if (af_db_add_root(afdb, FWK_APP_DIR)) { + if (afm_db_add_root(afdb, FWK_APP_DIR)) { ERROR("can't add root %s", FWK_APP_DIR); return 1; } @@ -243,13 +247,13 @@ int main(int ac, char **av) while ((i = getopt_long(ac, av, "hdqvr:a:", options, NULL)) >= 0) { switch (i) { case 'r': - if (af_db_add_root(afdb, optarg)) { + if (afm_db_add_root(afdb, optarg)) { ERROR("can't add root %s", optarg); return 1; } break; case 'a': - if (af_db_add_application(afdb, optarg)) { + if (afm_db_add_application(afdb, optarg)) { ERROR("can't add application %s", optarg); return 1; } @@ -258,8 +262,8 @@ int main(int ac, char **av) } /* update the database */ - if (af_db_update_applications(afdb)) { - ERROR("af_update_applications failed"); + if (afm_db_update_applications(afdb)) { + ERROR("afm_update_applications failed"); return 1; } @@ -268,8 +272,19 @@ int main(int ac, char **av) return 1; } + /* init observers */ + jbusys = create_jbus(0, AFM_SYSTEM_DBUS_PATH); + if (!jbusys) { + ERROR("create_jbus failed for system"); + return 1; + } + if(jbus_on_signal_j(jbusys, "changed", on_signal_changed)) { + ERROR("adding signal observer failed"); + return 1; + } + /* init service */ - jbus = create_jbus(1, "/org/AGL/afmMain"); + jbus = create_jbus(1, AFM_USER_DBUS_PATH); if (!jbus) { ERROR("create_jbus failed"); return 1; diff --git a/src/afm.h b/src/afm.h new file mode 100644 index 0000000..e7d1690 --- /dev/null +++ b/src/afm.h @@ -0,0 +1,22 @@ +/* + Copyright 2015 IoT.bzh + + author: José Bollo + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#define AFM_DBUS_PATH "/org/AGL/afm" +#define AFM_SYSTEM_DBUS_PATH AFM_DBUS_PATH"/system" +#define AFM_USER_DBUS_PATH AFM_DBUS_PATH"/user" + diff --git a/src/utils-jbus.c b/src/utils-jbus.c index 7e65723..d58def9 100644 --- a/src/utils-jbus.c +++ b/src/utils-jbus.c @@ -272,6 +272,7 @@ static DBusHandlerResult incoming_resp(DBusConnection *connection, DBusMessage * reply = json_tokener_parse(str); status = reply ? 0 : -1; jrw->onresp_j(iserror ? -1 : status, reply, jrw->data); + json_object_put(reply); } free(jrw); @@ -318,6 +319,7 @@ static DBusHandlerResult incoming_call(DBusConnection *connection, DBusMessage * if (query == NULL) return reply_invalid_request(jreq); srv->oncall_j(jreq, query); + json_object_put(query); } return DBUS_HANDLER_RESULT_HANDLED; } @@ -350,8 +352,10 @@ static DBusHandlerResult incoming_signal(DBusConnection *connection, DBusMessage else { /* handling json only */ obj = json_tokener_parse(str); - if (obj != NULL) + if (obj != NULL) { sig->onsignal_j(obj); + json_object_put(obj); + } } } return DBUS_HANDLER_RESULT_HANDLED; @@ -607,11 +611,17 @@ char *jbus_call_ss_sync(struct jbus *jbus, const char *method, const char *query struct json_object *jbus_call_sj_sync(struct jbus *jbus, const char *method, const char *query) { - const char *str = jbus_call_ss_sync(jbus, method, query); - return str ? json_tokener_parse(str) : NULL; + struct json_object *obj; + char *str = jbus_call_ss_sync(jbus, method, query); + if (str == NULL) + obj = NULL; + else { + obj = json_tokener_parse(str); + free(str); + } + return obj; } - char *jbus_call_js_sync(struct jbus *jbus, const char *method, struct json_object *query) { const char *str = json_object_to_json_string(query); diff --git a/src/wgtpkg-install.c b/src/wgtpkg-install.c index b3e4603..933e612 100644 --- a/src/wgtpkg-install.c +++ b/src/wgtpkg-install.c @@ -216,12 +216,12 @@ error: } /* install the widget of the file */ -void install_widget(const char *wgtfile, const char *root, int force) +int install_widget(const char *wgtfile, const char *root, int force) { struct wgt_info *ifo; const struct wgt_desc *desc; - NOTICE("-- INSTALLING widget %s --", wgtfile); + NOTICE("-- INSTALLING widget %s to %s --", wgtfile, root); /* workdir */ create_directory(root, 0755, 1); @@ -253,7 +253,7 @@ void install_widget(const char *wgtfile, const char *root, int force) if (install_security(desc)) goto error3; - return; + return 0; error3: wgt_info_unref(ifo); @@ -262,6 +262,6 @@ error2: remove_workdir(); error1: - return; + return -1; } diff --git a/src/wgtpkg.h b/src/wgtpkg.h index a355b4c..52a42e1 100644 --- a/src/wgtpkg.h +++ b/src/wgtpkg.h @@ -92,7 +92,7 @@ extern const char *file_get_prop(struct filedesc *file, const char *name); /**************************************************************/ /* from wgtpkg-install */ -extern void install_widget(const char *wgtfile, const char *root, int force); +extern int install_widget(const char *wgtfile, const char *root, int force); /**************************************************************/ /* from wgtpkg-permission */ -- cgit