X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fwgtpkg-unit.c;h=f141766af4d891b2d735fe4b0c410edf9322a104;hb=2a8b46d16ea5d0c99831e95b47cab037f220f7af;hp=8569c5a4ac010e929874ede7403547f494d1bd14;hpb=1d4de11a907e41c06063a2cd5028dc4101690f50;p=src%2Fapp-framework-main.git diff --git a/src/wgtpkg-unit.c b/src/wgtpkg-unit.c index 8569c5a..f141766 100644 --- a/src/wgtpkg-unit.c +++ b/src/wgtpkg-unit.c @@ -25,15 +25,76 @@ #include #include #include +#include +#include + #include "verbose.h" #include "utils-file.h" #include "wgtpkg-mustach.h" +#include "utils-json.h" +#include "wgt-json.h" +#include "utils-systemd.h" + #include "wgtpkg-unit.h" +#if 0 +#include +#else +#define isblank(c) ((c)==' '||(c)=='\t') +#endif + +/* the template for all units */ static char *template; +/* + * Search for the 'pattern' in 'text'. + * Returns 1 if 'text' matches the 'pattern' or else returns 0. + * When returning 1 and 'after' isn't NULL, the pointer to the + * first character after the pettern in 'text' is stored in 'after'. + * The characters '\n' and ' ' have a special mening in the search: + * * '\n': matches any space or tabs (including none) followed + * either by '\n' or '\0' (end of the string) + * * ' ': matches any space or tabs but at least one. + */ +static int matches(const char *pattern, char *text, char **after) +{ + char p, t; + + t = *text; + p = *pattern; + while(p) { + switch(p) { + case '\n': + while (isblank(t)) + t = *++text; + if (t) { + if (t != p) + return 0; + t = *++text; + } + break; + case ' ': + if (!isblank(t)) + return 0; + do { + t = *++text; + } while(isblank(t)); + break; + default: + if (t != p) + return 0; + t = *++text; + break; + } + p = *++pattern; + } + if (after) + *after = text; + return 1; +} + /* * Pack a null terminated 'text' by removing empty lines, * lines made of blanks and terminated with \, lines @@ -42,20 +103,30 @@ static char *template; * "nl" exactly (without quotes ") are replaced with * an empty line. * - * Returns the pointer to the ending null. + * Returns the size after packing (offset of the ending null). */ -static char *pack(char *text, char purge) +static size_t pack(char *text, char purge) { - char *read, *write, *begin, *start, c, emit, cont, nextcont; + char *read; /* read iterator */ + char *write; /* write iterator */ + char *begin; /* begin the copied text of the line */ + char *start; /* first character of the line that isn't blanck */ + char c; /* currently scanned character (pointed by read) */ + char emit; /* flag telling whether line is to be copied */ + char cont; /* flag telling whether the line continues the previous one */ + char nextcont; /* flag telling whether the line will continues the next one */ cont = 0; - c = *(begin = write = read = text); + c = *(write = read = text); + + /* iteration over lines */ while (c) { + /* computes emit, nextcont, emit and start for the current line */ emit = nextcont = 0; start = NULL; begin = read; while (c && c != '\n') { - if (c != ' ' && c != '\t') { + if (!isblank(c)) { if (c == '\\' && read[1] == '\n') nextcont = 1; else { @@ -68,13 +139,18 @@ static char *pack(char *text, char purge) } if (c) c = *++read; + /* emit the line if not empty */ if (emit) { + /* removes the blanks on the left of not continuing lines */ if (!cont && start) begin = start; + /* check if purge applies */ if (purge && *begin == purge) { + /* yes, insert new line if requested */ if (!strncmp(begin+1, "nl\n",3)) *write++ = '\n'; } else { + /* copies the line */ while (begin != read) *write++ = *begin++; } @@ -82,7 +158,7 @@ static char *pack(char *text, char purge) cont = nextcont; } *write = 0; - return write; + return (size_t)(write - text); } /* @@ -104,123 +180,157 @@ static inline char *nextline(char *text) */ static char *offset(char *text, const char *pattern, char **args) { - size_t len; - - if (text) { - len = strlen(pattern); - do { - if (strncmp(text, pattern, len)) - text = nextline(text); - else { - if (args) - *args = &text[len]; - break; - } - } while (text); - } + while (text && !matches(pattern, text, args)) + text = nextline(text); return text; } /* * process one unit */ - static int process_one_unit(char *spec, struct unitdesc *desc) { - char *nsoc, *nsrv; - int isuser, issystem, issock, isserv; + char *nsoc, *nsrv, *name, *wanted; + int isuser, issystem, issock, isserv, iswanted; + size_t len; - /* found the configuration directive of the unit */ + /* finds the configuration directive of the unit */ isuser = !!offset(spec, "%systemd-unit user\n", NULL); issystem = !!offset(spec, "%systemd-unit system\n", NULL); issock = !!offset(spec, "%systemd-unit socket ", &nsoc); isserv = !!offset(spec, "%systemd-unit service ", &nsrv); + iswanted = !!offset(spec, "%systemd-unit wanted-by ", &wanted); - if (isuser ^ issystem) { + /* check the unit scope */ + if ((isuser + issystem) == 1) { desc->scope = isuser ? unitscope_user : unitscope_system; } else { desc->scope = unitscope_unknown; } - if (issock ^ isserv) { + /* check the unit type */ + if ((issock + isserv) == 1) { if (issock) { desc->type = unittype_socket; - desc->name = nsoc; + name = nsoc; } else { desc->type = unittype_service; - desc->name = nsrv; + name = nsrv; } - desc->name_length = (size_t)(strchrnul(desc->name, '\n') - desc->name); - desc->name = strndup(desc->name, desc->name_length); + len = (size_t)(strchrnul(name, '\n') - name); + desc->name = strndup(name, len); + desc->name_length = desc->name ? len : 0; } else { desc->type = unittype_unknown; desc->name = NULL; desc->name_length = 0; } + if (iswanted) { + len = (size_t)(strchrnul(wanted, '\n') - wanted); + desc->wanted_by = strndup(wanted, len); + desc->wanted_by_length = len; + } else { + desc->wanted_by = NULL; + desc->wanted_by_length = 0; + } + desc->content = spec; - desc->content_length = (size_t)(pack(spec, '%') - spec); + desc->content_length = pack(spec, '%'); return 0; } /* + * Processes all the units of the 'corpus'. + * Each unit of the corpus is separated and packed and its + * charactistics are stored in a descriptor. + * At the end if no error was found, calls the function 'process' + * with its given 'closure' and the array descripbing the units. + * Return 0 in case of success or a negative value in case of error. */ -static int process_all_units(char *spec, int (*process)(void *closure, const struct unitdesc descs[], unsigned count), void *closure) +static int process_all_units(char *corpus, int (*process)(void *closure, const struct unitdesc descs[], unsigned count), void *closure) { int rc, rc2; unsigned n; - char *beg, *end, *after; + char *beg, *end; struct unitdesc *descs, *d; descs = NULL; n = 0; - rc = 0; - beg = offset(spec, "%begin systemd-unit\n", NULL); - while(beg) { - beg = nextline(beg); - end = offset(beg, "%end systemd-unit\n", &after); + rc = rc2 = 0; + + /* while there is a unit in the corpus */ + while(offset(corpus, "%begin systemd-unit\n", &beg)) { + + /* get the end of the unit */ + end = offset(beg, "%end systemd-unit\n", &corpus); if (!end) { /* unterminated unit !! */ ERROR("unterminated unit description!! %s", beg); - break; - } - *end = 0; + corpus = beg; + rc2 = -EINVAL; + } else { + /* separate the unit from the corpus */ + *end = 0; - d = realloc(descs, (n + 1) * sizeof *descs); - if (d == NULL) - rc2 = -ENOMEM; - else { - memset(&d[n], 0, sizeof *d); - descs = d; - rc2 = process_one_unit(beg, &descs[n]); + /* allocates a descriptor for the unit */ + d = realloc(descs, (n + 1) * sizeof *descs); + if (d == NULL) + rc2 = -ENOMEM; + else { + /* creates the unit description */ + memset(&d[n], 0, sizeof *d); + descs = d; + rc2 = process_one_unit(beg, &descs[n]); + if (rc2 >= 0) + n++; + } + } + /* records the error if there is an error */ + if (rc2 < 0) { + rc = rc ? : rc2; + rc2 = 0; } - - if (rc2 >= 0) - n++; - else if (rc == 0) - rc = rc2; - beg = offset(after, "%begin systemd-unit\n", NULL); } + /* call the function that processes the units */ if (rc == 0 && process) rc = process(closure, descs, n); - while(n) + + /* cleanup and frees */ + while(n) { free((char *)(descs[--n].name)); + free((char *)(descs[n].wanted_by)); + } free(descs); return rc; } +/* + * Clear the unit generator + */ +void unit_generator_off() +{ + free(template); + template = NULL; +} + +/* + * Initialises the unit generator with the content of the file of path 'filename'. + * Returns 0 in case of success or a negative number in case of error. + */ int unit_generator_on(const char *filename) { size_t size; char *tmp; int rc; + unit_generator_off(); rc = getfile(filename ? : FWK_UNIT_CONF, &template, NULL); if (!rc) { - size = (size_t)(pack(template, ';') - template); + size = pack(template, ';'); tmp = realloc(template, 1 + size); if (tmp) template = tmp; @@ -228,12 +338,15 @@ int unit_generator_on(const char *filename) return rc; } -void unit_generator_off() -{ - free(template); - template = NULL; -} - +/* + * Applies the object 'jdesc' to the current unit generator. + * The current unit generator will be set to the default one if not unit + * was previously set using the function 'unit_generator_on'. + * The callback function 'process' is then called with the + * unit descriptors array and the expected closure. + * Return what returned process in case of success or a negative + * error code. + */ int unit_generator_process(struct json_object *jdesc, int (*process)(void *closure, const struct unitdesc descs[], unsigned count), void *closure) { int rc; @@ -244,11 +357,174 @@ int unit_generator_process(struct json_object *jdesc, int (*process)(void *closu if (!rc) { instance = NULL; rc = apply_mustach(template, jdesc, &instance, &size); - if (!rc) { + if (!rc) rc = process_all_units(instance, process, closure); - } free(instance); } return rc; } +/**************** SPECIALIZED PART *****************************/ + +static int check_unit_desc(const struct unitdesc *desc, int tells) +{ + if (desc->scope != unitscope_unknown && desc->type != unittype_unknown && desc->name != NULL) + return 0; + + if (tells) { + if (desc->scope == unitscope_unknown) + ERROR("unit of unknown scope"); + if (desc->type == unittype_unknown) + ERROR("unit of unknown type"); + if (desc->name == NULL) + ERROR("unit of unknown name"); + } + errno = EINVAL; + return -1; +} + +static int get_unit_path(char *path, size_t pathlen, const struct unitdesc *desc) +{ + int rc = systemd_get_unit_path( + path, pathlen, desc->scope == unitscope_user, + desc->name, desc->type == unittype_socket ? "socket" : "service"); + + if (rc < 0) + ERROR("can't get the unit path for %s", desc->name); + + return rc; +} + +static int get_wants_path(char *path, size_t pathlen, const struct unitdesc *desc) +{ + int rc = systemd_get_wants_path( + path, pathlen, desc->scope == unitscope_user, desc->wanted_by, + desc->name, desc->type == unittype_socket ? "socket" : "service"); + + if (rc < 0) + ERROR("can't get the wants path for %s and %s", desc->name, desc->wanted_by); + + return rc; +} + +static int get_wants_target(char *path, size_t pathlen, const struct unitdesc *desc) +{ + int rc = systemd_get_wants_target( + path, pathlen, + desc->name, desc->type == unittype_socket ? "socket" : "service"); + + if (rc < 0) + ERROR("can't get the wants target for %s", desc->name); + + return rc; +} + +static int do_uninstall_units(void *closure, const struct unitdesc descs[], unsigned count) +{ + int rc, rc2; + unsigned i; + char path[PATH_MAX]; + + for (i = 0 ; i < count ; i++) { + rc = check_unit_desc(&descs[i], 0); + if (rc == 0) { + rc = get_unit_path(path, sizeof path, &descs[i]); + if (rc >= 0) { + rc = unlink(path); + } + if (descs[i].wanted_by != NULL) { + rc2 = get_wants_path(path, sizeof path, &descs[i]); + if (rc2 >= 0) + rc2 = unlink(path); + rc = rc < 0 ? rc : rc2; + } + } + } + return 0; +} + +static int do_install_units(void *closure, const struct unitdesc descs[], unsigned count) +{ + int rc; + unsigned i; + char path[PATH_MAX + 1], target[PATH_MAX + 1]; + + i = 0; + while (i < count) { + rc = check_unit_desc(&descs[i], 1); + if (!rc) { + rc = get_unit_path(path, sizeof path, &descs[i]); + if (rc >= 0) { + rc = putfile(path, descs[i].content, descs[i].content_length); + if (descs[i].wanted_by != NULL) { + rc = get_wants_path(path, sizeof path, &descs[i]); + if (rc >= 0) { + rc = get_wants_target(target, sizeof target, &descs[i]); + if (rc >= 0) { + unlink(path); /* TODO? check? */ + rc = symlink(target, path); + } + } + } + i++; + } + } + if (rc < 0) { + do_uninstall_units(closure, descs, i); + return rc; + } + } + return 0; +} + +static int add_metadata(struct json_object *jdesc, const char *installdir, const char *icondir, int port) +{ + char portstr[30]; + + sprintf(portstr, "%d", port); + return j_add_many_strings_m(jdesc, + "#metadata.install-dir", installdir, + "#metadata.app-data-dir", "%h/app-data", + "#metadata.icons-dir", icondir, + "#metadata.http-port", portstr, + NULL) ? 0 : -1; +} + +static int do_install_uninstall( + struct wgt_info *ifo, + const char *installdir, + const char *icondir, + int port, + int (*doer)(void *, const struct unitdesc[], unsigned) +) +{ + int rc; + struct json_object *jdesc; + + jdesc = wgt_info_to_json(ifo); + if (!jdesc) + rc = -1; + else { + rc = add_metadata(jdesc, installdir, icondir, port); + if (rc) + ERROR("can't set the metadata. %m"); + else { + rc = unit_generator_process(jdesc, doer, NULL); + if (rc) + ERROR("can't install units, error %d", rc); + } + json_object_put(jdesc); + } + return rc; +} + +int unit_install(struct wgt_info *ifo, const char *installdir, const char *icondir, int port) +{ + return do_install_uninstall(ifo, installdir, icondir, port, do_install_units); +} + +int unit_uninstall(struct wgt_info *ifo) +{ + return do_install_uninstall(ifo, "", "", 0, do_uninstall_units); +} +