2 Copyright (C) 2015-2020 IoT.bzh
4 author: José Bollo <jose.bollo@iot.bzh>
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
23 #include <sys/types.h>
30 #include <json-c/json.h>
33 #include "utils-dir.h"
34 #include "utils-json.h"
35 #include "utils-systemd.h"
39 static const char key_unit_d_path[] = "-unit-dpath-";
41 /**************** get appli basis *********************/
43 static int get_basis(struct json_object *appli, int *isuser, const char **dpath, int uid)
46 char *dp, *arodot, *nun;
47 const char *uname, *uscope;
48 struct json_object *odp;
52 if (!j_read_string_at(appli, "unit-scope", &uscope)) {
53 ERROR("'unit-scope' missing in appli description %s", json_object_get_string(appli));
56 *isuser = strcmp(uscope, "system") != 0;
58 /* get dpaths of known users */
60 if (json_object_object_get_ex(appli, key_unit_d_path, &odp)) {
61 /* try not parametric dpath */
62 if (json_object_get_type(odp) == json_type_string) {
63 *dpath = json_object_get_string(odp);
66 assert(json_object_get_type(odp) == json_type_object);
69 ERROR("unexpected uid %d", uid);
72 rc = snprintf(userid, sizeof userid, "%d", uid);
73 assert(rc < (int)(sizeof userid));
74 /* try dpath for the user */
75 if (j_read_string_at(odp, userid, dpath))
80 if (!j_read_string_at(appli, "unit-name", &uname)) {
81 ERROR("'unit-name' missing in appli description %s", json_object_get_string(appli));
85 /* is user parametric? */
86 arodot = strchr(uname, '@');
87 if (arodot && *++arodot == '.') {
91 ERROR("unexpected uid %d", uid);
94 rc = snprintf(userid, sizeof userid, "%d", uid);
95 assert(rc < (int)(sizeof userid));
97 /* create the dpaths of known users */
98 odp = json_object_new_object();
101 json_object_object_add(appli, key_unit_d_path, odp);
104 /* get dpath of userid */
105 nun = alloca((size_t)(arodot - uname) + strlen(userid) + strlen(arodot) + 1);
106 stpcpy(stpcpy(stpncpy(nun, uname, (size_t)(arodot - uname)), userid), arodot);
107 dp = systemd_unit_dpath_by_name(*isuser, nun, 1);
109 ERROR("Can't load unit of name %s for %s: %m", nun, uscope);
112 /* record the dpath */
113 if (!j_add_string(odp, userid, dp)) {
118 j_read_string_at(odp, userid, dpath);
121 dp = systemd_unit_dpath_by_name(*isuser, uname, 1);
123 ERROR("Can't load unit of name %s for %s: %m", uname, uscope);
126 /* record the dpath */
127 if (!j_add_string(appli, key_unit_d_path, dp)) {
132 j_read_string_at(appli, key_unit_d_path, dpath);
137 ERROR("out of memory");
147 static const char *wait_state_stable(int isuser, const char *dpath)
150 const char *state = NULL;
153 for (trial = 1 ; trial <= count ; trial++) {
154 state = systemd_unit_state_of_dpath(isuser, dpath);
155 if (state == NULL || state == SysD_State_Active
156 || state == SysD_State_Failed)
164 * Creates a json object that describes the state for:
165 * - 'id', the id of the application
166 * - 'runid', its runid
168 * - 'state', its systemd state
170 * Returns the created object or NULL in case of error.
172 static json_object *mkstate(const char *id, int runid, int pid, const char *state)
174 struct json_object *result, *pids;
177 result = json_object_new_object();
182 if (!j_add_integer(result, "runid", runid))
187 pids = j_add_new_array(result, "pids");
190 if (!j_add_integer(pids, NULL, pid))
195 if (!j_add_string(result, "state", state == SysD_State_Active ? "running" : "terminated"))
198 /* the application id */
199 if (!j_add_string(result, "id", id))
206 json_object_put(result);
211 /**************** API handling ************************/
214 * Starts the application described by 'appli' for the 'mode'.
215 * In case of remote start, it returns in uri the uri to
218 * A reference to 'appli' is kept during the live of the
219 * runner. This is made using json_object_get. Thus be aware
220 * that further modifications to 'appli' might create errors.
222 * Returns the runid in case of success or -1 in case of error
224 int afm_urun_start(struct json_object *appli, int uid)
226 return afm_urun_once(appli, uid);
230 * Returns the runid of a previously started application 'appli'
231 * or if none is running, starts the application described by 'appli'
234 * A reference to 'appli' is kept during the live of the
235 * runner. This is made using json_object_get. Thus be aware
236 * that further modifications to 'appli' might create errors.
238 * Returns the runid in case of success or -1 in case of error
240 int afm_urun_once(struct json_object *appli, int uid)
242 const char *udpath, *state, *uscope, *uname;
246 rc = get_basis(appli, &isuser, &udpath, uid);
251 rc = systemd_unit_start_dpath(isuser, udpath);
253 j_read_string_at(appli, "unit-scope", &uscope);
254 j_read_string_at(appli, "unit-name", &uname);
255 ERROR("can't start %s unit %s for uid %d", uscope, uname, uid);
259 state = wait_state_stable(isuser, udpath);
261 j_read_string_at(appli, "unit-scope", &uscope);
262 j_read_string_at(appli, "unit-name", &uname);
263 ERROR("can't wait %s unit %s for uid %d: %m", uscope, uname, uid);
266 if (state != SysD_State_Active) {
267 j_read_string_at(appli, "unit-scope", &uscope);
268 j_read_string_at(appli, "unit-name", &uname);
269 ERROR("start error %s unit %s for uid %d: %s", uscope, uname, uid, state);
273 rc = systemd_unit_pid_of_dpath(isuser, udpath);
275 j_read_string_at(appli, "unit-scope", &uscope);
276 j_read_string_at(appli, "unit-name", &uname);
277 ERROR("can't getpid of %s unit %s for uid %d: %m", uscope, uname, uid);
287 static int not_yet_implemented(const char *what)
289 ERROR("%s isn't yet implemented", what);
295 * Terminates the runner of 'runid'
297 * Returns 0 in case of success or -1 in case of error
299 int afm_urun_terminate(int runid, int uid)
301 int rc = systemd_unit_stop_pid(1 /* TODO: isuser? */, (unsigned)runid);
303 rc = systemd_unit_stop_pid(0 /* TODO: isuser? */, (unsigned)runid);
304 return rc < 0 ? rc : 0;
308 * Stops (aka pause) the runner of 'runid'
310 * Returns 0 in case of success or -1 in case of error
312 int afm_urun_pause(int runid, int uid)
314 return not_yet_implemented("pause");
318 * Continue (aka resume) the runner of 'runid'
320 * Returns 0 in case of success or -1 in case of error
322 int afm_urun_resume(int runid, int uid)
324 return not_yet_implemented("resume");
328 * Get the list of the runners.
330 * Returns the list or NULL in case of error.
332 struct json_object *afm_urun_list(struct afm_udb *db, int all, int uid)
334 int i, n, isuser, pid;
338 struct json_object *desc;
339 struct json_object *appli;
340 struct json_object *apps;
341 struct json_object *result;
344 result = json_object_new_array();
348 apps = afm_udb_applications_private(db, all, uid);
349 n = json_object_array_length(apps);
350 for (i = 0 ; i < n ; i++) {
351 appli = json_object_array_get_idx(apps, i);
352 if (appli && get_basis(appli, &isuser, &udpath, uid) >= 0) {
353 pid = systemd_unit_pid_of_dpath(isuser, udpath);
354 if (pid > 0 && j_read_string_at(appli, "id", &id)) {
355 state = systemd_unit_state_of_dpath(isuser, udpath);
356 if (state == SysD_State_Active) {
357 desc = mkstate(id, pid, pid, state);
358 if (desc && json_object_array_add(result, desc) == -1) {
359 ERROR("can't add desc %s to result", json_object_get_string(desc));
360 json_object_put(desc);
368 json_object_put(apps);
373 * Get the state of the runner of 'runid'.
375 * Returns the state or NULL in case of success
377 struct json_object *afm_urun_state(struct afm_udb *db, int runid, int uid)
379 int i, n, isuser, pid, wasuser;
384 struct json_object *appli;
385 struct json_object *apps;
386 struct json_object *result;
391 dpath = systemd_unit_dpath_by_pid(wasuser = 1, (unsigned)runid);
393 dpath = systemd_unit_dpath_by_pid(wasuser = 0, (unsigned)runid);
396 WARNING("searched runid %d not found", runid);
398 /* search in the base */
399 apps = afm_udb_applications_private(db, 1, uid);
400 n = json_object_array_length(apps);
401 for (i = 0 ; i < n ; i++) {
402 appli = json_object_array_get_idx(apps, i);
404 && get_basis(appli, &isuser, &udpath, uid) >= 0
405 && !strcmp(dpath, udpath)
406 && j_read_string_at(appli, "id", &id)) {
407 pid = systemd_unit_pid_of_dpath(isuser, udpath);
408 state = systemd_unit_state_of_dpath(isuser, dpath);
409 if (pid > 0 && state == SysD_State_Active)
410 result = mkstate(id, runid, pid, state);
415 WARNING("searched runid %d of dpath %s isn't an applications", runid, dpath);
417 json_object_put(apps);
425 * Search the runid, if any, of the application of 'id' for the user 'uid'.
426 * Returns the pid (a positive not null number) or -1 in case of error.
428 int afm_urun_search_runid(struct afm_udb *db, const char *id, int uid)
432 struct json_object *appli;
434 appli = afm_udb_get_application_private(db, id, uid);
436 NOTICE("Unknown appid %s", id);
439 } else if (get_basis(appli, &isuser, &udpath, uid) < 0) {
442 pid = systemd_unit_pid_of_dpath(isuser, udpath);