2 Copyright (C) 2015-2018 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)
152 state = systemd_unit_state_of_dpath(isuser, dpath);
153 if (state == NULL || state == SysD_State_Active
154 || state == SysD_State_Failed)
161 * Creates a json object that describes the state for:
162 * - 'id', the id of the application
163 * - 'runid', its runid
165 * - 'state', its systemd state
167 * Returns the created object or NULL in case of error.
169 static json_object *mkstate(const char *id, int runid, int pid, const char *state)
171 struct json_object *result, *pids;
174 result = json_object_new_object();
179 if (!j_add_integer(result, "runid", runid))
184 pids = j_add_new_array(result, "pids");
187 if (!j_add_integer(pids, NULL, pid))
192 if (!j_add_string(result, "state", state == SysD_State_Active ? "running" : "terminated"))
195 /* the application id */
196 if (!j_add_string(result, "id", id))
203 json_object_put(result);
208 /**************** API handling ************************/
211 * Starts the application described by 'appli' for the 'mode'.
212 * In case of remote start, it returns in uri the uri to
215 * A reference to 'appli' is kept during the live of the
216 * runner. This is made using json_object_get. Thus be aware
217 * that further modifications to 'appli' might create errors.
219 * Returns the runid in case of success or -1 in case of error
221 int afm_urun_start(struct json_object *appli, int uid)
223 return afm_urun_once(appli, uid);
227 * Returns the runid of a previously started application 'appli'
228 * or if none is running, starts the application described by 'appli'
231 * A reference to 'appli' is kept during the live of the
232 * runner. This is made using json_object_get. Thus be aware
233 * that further modifications to 'appli' might create errors.
235 * Returns the runid in case of success or -1 in case of error
237 int afm_urun_once(struct json_object *appli, int uid)
239 const char *udpath, *state, *uscope, *uname;
243 rc = get_basis(appli, &isuser, &udpath, uid);
248 rc = systemd_unit_start_dpath(isuser, udpath);
250 j_read_string_at(appli, "unit-scope", &uscope);
251 j_read_string_at(appli, "unit-name", &uname);
252 ERROR("can't start %s unit %s for uid %d", uscope, uname, uid);
256 state = wait_state_stable(isuser, udpath);
258 j_read_string_at(appli, "unit-scope", &uscope);
259 j_read_string_at(appli, "unit-name", &uname);
260 ERROR("can't wait %s unit %s for uid %d: %m", uscope, uname, uid);
263 if (state != SysD_State_Active) {
264 j_read_string_at(appli, "unit-scope", &uscope);
265 j_read_string_at(appli, "unit-name", &uname);
266 ERROR("start error %s unit %s for uid %d: %s", uscope, uname, uid, state);
270 rc = systemd_unit_pid_of_dpath(isuser, udpath);
272 j_read_string_at(appli, "unit-scope", &uscope);
273 j_read_string_at(appli, "unit-name", &uname);
274 ERROR("can't getpid of %s unit %s for uid %d: %m", uscope, uname, uid);
284 static int not_yet_implemented(const char *what)
286 ERROR("%s isn't yet implemented", what);
292 * Terminates the runner of 'runid'
294 * Returns 0 in case of success or -1 in case of error
296 int afm_urun_terminate(int runid, int uid)
298 int rc = systemd_unit_stop_pid(1 /* TODO: isuser? */, (unsigned)runid);
300 rc = systemd_unit_stop_pid(0 /* TODO: isuser? */, (unsigned)runid);
301 return rc < 0 ? rc : 0;
305 * Stops (aka pause) the runner of 'runid'
307 * Returns 0 in case of success or -1 in case of error
309 int afm_urun_pause(int runid, int uid)
311 return not_yet_implemented("pause");
315 * Continue (aka resume) the runner of 'runid'
317 * Returns 0 in case of success or -1 in case of error
319 int afm_urun_resume(int runid, int uid)
321 return not_yet_implemented("resume");
325 * Get the list of the runners.
327 * Returns the list or NULL in case of error.
329 struct json_object *afm_urun_list(struct afm_udb *db, int uid)
331 int i, n, isuser, pid;
335 struct json_object *desc;
336 struct json_object *appli;
337 struct json_object *apps;
338 struct json_object *result;
341 result = json_object_new_array();
345 apps = afm_udb_applications_private(db, uid);
346 n = json_object_array_length(apps);
347 for (i = 0 ; i < n ; i++) {
348 appli = json_object_array_get_idx(apps, i);
349 if (appli && get_basis(appli, &isuser, &udpath, uid) >= 0) {
350 pid = systemd_unit_pid_of_dpath(isuser, udpath);
351 if (pid > 0 && j_read_string_at(appli, "id", &id)) {
352 state = systemd_unit_state_of_dpath(isuser, udpath);
353 if (state == SysD_State_Active) {
354 desc = mkstate(id, pid, pid, state);
355 if (desc && json_object_array_add(result, desc) == -1) {
356 ERROR("can't add desc %s to result", json_object_get_string(desc));
357 json_object_put(desc);
365 json_object_put(apps);
370 * Get the state of the runner of 'runid'.
372 * Returns the state or NULL in case of success
374 struct json_object *afm_urun_state(struct afm_udb *db, int runid, int uid)
376 int i, n, isuser, pid, wasuser;
381 struct json_object *appli;
382 struct json_object *apps;
383 struct json_object *result;
388 dpath = systemd_unit_dpath_by_pid(wasuser = 1, (unsigned)runid);
390 dpath = systemd_unit_dpath_by_pid(wasuser = 0, (unsigned)runid);
393 WARNING("searched runid %d not found", runid);
395 /* search in the base */
396 apps = afm_udb_applications_private(db, uid);
397 n = json_object_array_length(apps);
398 for (i = 0 ; i < n ; i++) {
399 appli = json_object_array_get_idx(apps, i);
401 && get_basis(appli, &isuser, &udpath, uid) >= 0
402 && !strcmp(dpath, udpath)
403 && j_read_string_at(appli, "id", &id)) {
404 pid = systemd_unit_pid_of_dpath(isuser, udpath);
405 state = systemd_unit_state_of_dpath(isuser, dpath);
406 if (pid > 0 && state == SysD_State_Active)
407 result = mkstate(id, runid, pid, state);
412 WARNING("searched runid %d of dpath %s isn't an applications", runid, dpath);
414 json_object_put(apps);
422 * Search the runid, if any, of the application of 'id' for the user 'uid'.
423 * Returns the pid (a positive not null number) or -1 in case of error.
425 int afm_urun_search_runid(struct afm_udb *db, const char *id, int uid)
429 struct json_object *appli;
431 appli = afm_udb_get_application_private(db, id, uid);
433 NOTICE("Unknown appid %s", id);
436 } else if (get_basis(appli, &isuser, &udpath, uid) < 0) {
439 pid = systemd_unit_pid_of_dpath(isuser, udpath);