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 /**************** get appli basis *********************/
41 static int get_basis(struct json_object *appli, int *isuser, const char **dpath, int load)
44 const char *uname, *uscope;
47 if (!j_read_string_at(appli, "unit-scope", &uscope)) {
48 ERROR("'unit-scope' missing in appli description %s", json_object_get_string(appli));
51 *isuser = strcmp(uscope, "system") != 0;
54 if (!j_read_string_at(appli, "-unit-dpath-", dpath)) {
59 if (!j_read_string_at(appli, "unit-name", &uname)) {
60 ERROR("'unit-name' missing in appli description %s", json_object_get_string(appli));
63 dp = systemd_unit_dpath_by_name(*isuser, uname, 1);
65 ERROR("Can't load unit of name %s for %s: %m", uname, uscope);
68 if (!j_add_string(appli, "-unit-dpath-", dp)) {
73 j_read_string_at(appli, "-unit-dpath-", dpath);
79 ERROR("out of memory");
89 static const char *wait_state_stable(int isuser, const char *dpath)
94 state = systemd_unit_state_of_dpath(isuser, dpath);
95 if (state == NULL || state == SysD_State_Active
96 || state == SysD_State_Failed)
103 * Creates a json object that describes the state for:
104 * - 'id', the id of the application
105 * - 'runid', its runid
107 * - 'state', its systemd state
109 * Returns the created object or NULL in case of error.
111 static json_object *mkstate(const char *id, int runid, int pid, const char *state)
113 struct json_object *result, *pids;
116 result = json_object_new_object();
121 if (!j_add_integer(result, "runid", runid))
126 pids = j_add_new_array(result, "pids");
129 if (!j_add_integer(pids, NULL, pid))
134 if (!j_add_string(result, "state", state == SysD_State_Active ? "running" : "terminated"))
137 /* the application id */
138 if (!j_add_string(result, "id", id))
145 json_object_put(result);
150 /**************** API handling ************************/
153 * Starts the application described by 'appli' for the 'mode'.
154 * In case of remote start, it returns in uri the uri to
157 * A reference to 'appli' is kept during the live of the
158 * runner. This is made using json_object_get. Thus be aware
159 * that further modifications to 'appli' might create errors.
161 * Returns the runid in case of success or -1 in case of error
163 int afm_urun_start(struct json_object *appli)
165 return afm_urun_once(appli);
169 * Returns the runid of a previously started application 'appli'
170 * or if none is running, starts the application described by 'appli'
173 * A reference to 'appli' is kept during the live of the
174 * runner. This is made using json_object_get. Thus be aware
175 * that further modifications to 'appli' might create errors.
177 * Returns the runid in case of success or -1 in case of error
179 int afm_urun_once(struct json_object *appli)
181 const char *udpath, *state, *uscope, *uname;
185 rc = get_basis(appli, &isuser, &udpath, 1);
190 rc = systemd_unit_start_dpath(isuser, udpath);
192 j_read_string_at(appli, "unit-scope", &uscope);
193 j_read_string_at(appli, "unit-name", &uname);
194 ERROR("can't start %s unit %s", uscope, uname);
198 state = wait_state_stable(isuser, udpath);
200 j_read_string_at(appli, "unit-scope", &uscope);
201 j_read_string_at(appli, "unit-name", &uname);
202 ERROR("can't wait %s unit %s: %m", uscope, uname);
205 if (state != SysD_State_Active) {
206 j_read_string_at(appli, "unit-scope", &uscope);
207 j_read_string_at(appli, "unit-name", &uname);
208 ERROR("start error %s unit %s: %s", uscope, uname, state);
212 rc = systemd_unit_pid_of_dpath(isuser, udpath);
214 j_read_string_at(appli, "unit-scope", &uscope);
215 j_read_string_at(appli, "unit-name", &uname);
216 ERROR("can't getpid of %s unit %s: %m", uscope, uname);
226 static int not_yet_implemented(const char *what)
228 ERROR("%s isn't yet implemented", what);
234 * Terminates the runner of 'runid'
236 * Returns 0 in case of success or -1 in case of error
238 int afm_urun_terminate(int runid)
240 int rc = systemd_unit_stop_pid(1 /* TODO: isuser? */, (unsigned)runid);
241 return rc < 0 ? rc : 0;
245 * Stops (aka pause) the runner of 'runid'
247 * Returns 0 in case of success or -1 in case of error
249 int afm_urun_pause(int runid)
251 return not_yet_implemented("pause");
255 * Continue (aka resume) the runner of 'runid'
257 * Returns 0 in case of success or -1 in case of error
259 int afm_urun_resume(int runid)
261 return not_yet_implemented("resume");
265 * Get the list of the runners.
267 * Returns the list or NULL in case of error.
269 struct json_object *afm_urun_list(struct afm_udb *db)
271 int i, n, isuser, pid;
275 struct json_object *desc;
276 struct json_object *appli;
277 struct json_object *apps;
278 struct json_object *result;
281 result = json_object_new_array();
285 apps = afm_udb_applications_private(db);
286 n = json_object_array_length(apps);
287 for (i = 0 ; i < n ; i++) {
288 appli = json_object_array_get_idx(apps, i);
289 if (appli && get_basis(appli, &isuser, &udpath, 0) >= 0) {
290 pid = systemd_unit_pid_of_dpath(isuser, udpath);
291 if (pid > 0 && j_read_string_at(appli, "id", &id)) {
292 state = systemd_unit_state_of_dpath(isuser, udpath);
293 if (state == SysD_State_Active) {
294 desc = mkstate(id, pid, pid, state);
295 if (desc && json_object_array_add(result, desc) == -1) {
296 ERROR("can't add desc %s to result", json_object_get_string(desc));
297 json_object_put(desc);
305 json_object_put(apps);
310 * Get the state of the runner of 'runid'.
312 * Returns the state or NULL in case of success
314 struct json_object *afm_urun_state(struct afm_udb *db, int runid)
316 int i, n, isuser, pid;
321 struct json_object *appli;
322 struct json_object *apps;
323 struct json_object *result;
328 dpath = systemd_unit_dpath_by_pid(1 /* TODO: isuser? */, (unsigned)runid);
331 WARNING("searched runid %d not found", runid);
333 /* search in the base */
334 apps = afm_udb_applications_private(db);
335 n = json_object_array_length(apps);
336 for (i = 0 ; i < n ; i++) {
337 appli = json_object_array_get_idx(apps, i);
339 && get_basis(appli, &isuser, &udpath, 0) >= 0
340 && !strcmp(dpath, udpath)
341 && j_read_string_at(appli, "id", &id)) {
342 pid = systemd_unit_pid_of_dpath(isuser, udpath);
343 state = systemd_unit_state_of_dpath(isuser, dpath);
344 if (state == SysD_State_Active)
345 result = mkstate(id, runid, pid, state);
350 WARNING("searched runid %d of dpath %s isn't an applications", runid, dpath);
352 json_object_put(apps);