2 Copyright 2015, 2016, 2017 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 load, 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))
79 /* not here. load it? */
86 if (!j_read_string_at(appli, "unit-name", &uname)) {
87 ERROR("'unit-name' missing in appli description %s", json_object_get_string(appli));
91 /* is user parametric? */
92 arodot = strchr(uname, '@');
93 if (arodot && *++arodot == '.') {
97 ERROR("unexpected uid %d", uid);
100 rc = snprintf(userid, sizeof userid, "%d", uid);
101 assert(rc < (int)(sizeof userid));
103 /* create the dpaths of known users */
104 odp = json_object_new_object();
107 json_object_object_add(appli, key_unit_d_path, odp);
110 /* get dpath of userid */
111 nun = alloca((size_t)(arodot - uname) + strlen(userid) + strlen(arodot) + 1);
112 stpcpy(stpcpy(stpncpy(nun, uname, (size_t)(arodot - uname)), userid), arodot);
113 dp = systemd_unit_dpath_by_name(*isuser, nun, 1);
115 ERROR("Can't load unit of name %s for %s: %m", nun, uscope);
118 /* record the dpath */
119 if (!j_add_string(odp, userid, dp)) {
124 j_read_string_at(odp, userid, dpath);
127 dp = systemd_unit_dpath_by_name(*isuser, uname, 1);
129 ERROR("Can't load unit of name %s for %s: %m", uname, uscope);
132 /* record the dpath */
133 if (!j_add_string(appli, key_unit_d_path, dp)) {
138 j_read_string_at(appli, key_unit_d_path, dpath);
143 ERROR("out of memory");
153 static const char *wait_state_stable(int isuser, const char *dpath)
158 state = systemd_unit_state_of_dpath(isuser, dpath);
159 if (state == NULL || state == SysD_State_Active
160 || state == SysD_State_Failed)
167 * Creates a json object that describes the state for:
168 * - 'id', the id of the application
169 * - 'runid', its runid
171 * - 'state', its systemd state
173 * Returns the created object or NULL in case of error.
175 static json_object *mkstate(const char *id, int runid, int pid, const char *state)
177 struct json_object *result, *pids;
180 result = json_object_new_object();
185 if (!j_add_integer(result, "runid", runid))
190 pids = j_add_new_array(result, "pids");
193 if (!j_add_integer(pids, NULL, pid))
198 if (!j_add_string(result, "state", state == SysD_State_Active ? "running" : "terminated"))
201 /* the application id */
202 if (!j_add_string(result, "id", id))
209 json_object_put(result);
214 /**************** API handling ************************/
217 * Starts the application described by 'appli' for the 'mode'.
218 * In case of remote start, it returns in uri the uri to
221 * A reference to 'appli' is kept during the live of the
222 * runner. This is made using json_object_get. Thus be aware
223 * that further modifications to 'appli' might create errors.
225 * Returns the runid in case of success or -1 in case of error
227 int afm_urun_start(struct json_object *appli, int uid)
229 return afm_urun_once(appli, uid);
233 * Returns the runid of a previously started application 'appli'
234 * or if none is running, starts the application described by 'appli'
237 * A reference to 'appli' is kept during the live of the
238 * runner. This is made using json_object_get. Thus be aware
239 * that further modifications to 'appli' might create errors.
241 * Returns the runid in case of success or -1 in case of error
243 int afm_urun_once(struct json_object *appli, int uid)
245 const char *udpath, *state, *uscope, *uname;
249 rc = get_basis(appli, &isuser, &udpath, 1, uid);
254 rc = systemd_unit_start_dpath(isuser, udpath);
256 j_read_string_at(appli, "unit-scope", &uscope);
257 j_read_string_at(appli, "unit-name", &uname);
258 ERROR("can't start %s unit %s for uid %d", uscope, uname, uid);
262 state = wait_state_stable(isuser, udpath);
264 j_read_string_at(appli, "unit-scope", &uscope);
265 j_read_string_at(appli, "unit-name", &uname);
266 ERROR("can't wait %s unit %s for uid %d: %m", uscope, uname, uid);
269 if (state != SysD_State_Active) {
270 j_read_string_at(appli, "unit-scope", &uscope);
271 j_read_string_at(appli, "unit-name", &uname);
272 ERROR("start error %s unit %s for uid %d: %s", uscope, uname, uid, state);
276 rc = systemd_unit_pid_of_dpath(isuser, udpath);
278 j_read_string_at(appli, "unit-scope", &uscope);
279 j_read_string_at(appli, "unit-name", &uname);
280 ERROR("can't getpid of %s unit %s for uid %d: %m", uscope, uname, uid);
290 static int not_yet_implemented(const char *what)
292 ERROR("%s isn't yet implemented", what);
298 * Terminates the runner of 'runid'
300 * Returns 0 in case of success or -1 in case of error
302 int afm_urun_terminate(int runid, int uid)
304 int rc = systemd_unit_stop_pid(1 /* TODO: isuser? */, (unsigned)runid);
306 rc = systemd_unit_stop_pid(0 /* TODO: isuser? */, (unsigned)runid);
307 return rc < 0 ? rc : 0;
311 * Stops (aka pause) the runner of 'runid'
313 * Returns 0 in case of success or -1 in case of error
315 int afm_urun_pause(int runid, int uid)
317 return not_yet_implemented("pause");
321 * Continue (aka resume) the runner of 'runid'
323 * Returns 0 in case of success or -1 in case of error
325 int afm_urun_resume(int runid, int uid)
327 return not_yet_implemented("resume");
331 * Get the list of the runners.
333 * Returns the list or NULL in case of error.
335 struct json_object *afm_urun_list(struct afm_udb *db, int uid)
337 int i, n, isuser, pid;
341 struct json_object *desc;
342 struct json_object *appli;
343 struct json_object *apps;
344 struct json_object *result;
347 result = json_object_new_array();
351 apps = afm_udb_applications_private(db, uid);
352 n = json_object_array_length(apps);
353 for (i = 0 ; i < n ; i++) {
354 appli = json_object_array_get_idx(apps, i);
355 if (appli && get_basis(appli, &isuser, &udpath, 0, uid) >= 0) {
356 pid = systemd_unit_pid_of_dpath(isuser, udpath);
357 if (pid > 0 && j_read_string_at(appli, "id", &id)) {
358 state = systemd_unit_state_of_dpath(isuser, udpath);
359 if (state == SysD_State_Active) {
360 desc = mkstate(id, pid, pid, state);
361 if (desc && json_object_array_add(result, desc) == -1) {
362 ERROR("can't add desc %s to result", json_object_get_string(desc));
363 json_object_put(desc);
371 json_object_put(apps);
376 * Get the state of the runner of 'runid'.
378 * Returns the state or NULL in case of success
380 struct json_object *afm_urun_state(struct afm_udb *db, int runid, int uid)
382 int i, n, isuser, pid, wasuser;
387 struct json_object *appli;
388 struct json_object *apps;
389 struct json_object *result;
394 dpath = systemd_unit_dpath_by_pid(wasuser = 1, (unsigned)runid);
396 dpath = systemd_unit_dpath_by_pid(wasuser = 0, (unsigned)runid);
399 WARNING("searched runid %d not found", runid);
401 /* search in the base */
402 apps = afm_udb_applications_private(db, uid);
403 n = json_object_array_length(apps);
404 for (i = 0 ; i < n ; i++) {
405 appli = json_object_array_get_idx(apps, i);
407 && get_basis(appli, &isuser, &udpath, 0, uid) >= 0
408 && !strcmp(dpath, udpath)
409 && j_read_string_at(appli, "id", &id)) {
410 pid = systemd_unit_pid_of_dpath(isuser, udpath);
411 state = systemd_unit_state_of_dpath(isuser, dpath);
412 if (state == SysD_State_Active)
413 result = mkstate(id, runid, pid, state);
418 WARNING("searched runid %d of dpath %s isn't an applications", runid, dpath);
420 json_object_put(apps);
428 * Search the runid, if any, of the application of 'id' for the user 'uid'.
429 * Returns the pid (a positive not null number) or -1 in case of error.
431 int afm_urun_search_runid(struct afm_udb *db, const char *id, int uid)
435 struct json_object *appli;
437 appli = afm_udb_get_application_private(db, id, uid);
439 NOTICE("Unknown appid %s", id);
442 } else if (get_basis(appli, &isuser, &udpath, 0, uid) < 0) {
445 pid = systemd_unit_pid_of_dpath(isuser, udpath);