afm-run: adds control
[src/app-framework-main.git] / src / afm-run.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
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
9
10      http://www.apache.org/licenses/LICENSE-2.0
11
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.
17 */
18
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <signal.h>
22 #include <pwd.h>
23 #include <sys/types.h>
24 #include <errno.h>
25 #include <assert.h>
26 #include <stdio.h>
27 #include <limits.h>
28 #include <string.h>
29
30 #include <json.h>
31
32 #include "verbose.h"
33 #include "utils-dir.h"
34 #include "utils-json.h"
35 #include "afm-launch-mode.h"
36 #include "afm-launch.h"
37 #include "afm-run.h"
38
39 enum appstate {
40         as_starting,
41         as_running,
42         as_stopped,
43         as_terminating,
44         as_terminated
45 };
46
47 struct apprun {
48         struct apprun *next_by_runid;
49         struct apprun *next_by_pgid;
50         int runid;
51         pid_t pids[2]; /* 0: group leader, 1: slave (appli) */
52         enum appstate state;
53         json_object *appli;
54 };
55
56 #define ROOT_RUNNERS_COUNT  32
57 #define MAX_RUNNER_COUNT    32767
58
59 static struct apprun *runners_by_runid[ROOT_RUNNERS_COUNT];
60 static struct apprun *runners_by_pgid[ROOT_RUNNERS_COUNT];
61 static int runnercount = 0;
62 static int runnerid = 0;
63
64 static const char fwk_user_app_dir[] = FWK_USER_APP_DIR;
65 static char *homeappdir;
66
67 /****************** manages pgids **********************/
68
69 /* get a runner by its pgid */
70 static struct apprun *runner_of_pgid(pid_t pgid)
71 {
72         struct apprun *result = runners_by_pgid[(int)(pgid & (ROOT_RUNNERS_COUNT - 1))];
73         while (result && result->pids[0] != pgid)
74                 result = result->next_by_pgid;
75         return result;
76 }
77
78 /* insert a runner for its pgid */
79 static void pgid_insert(struct apprun *runner)
80 {
81         struct apprun **prev = &runners_by_runid[(int)(runner->pids[0] & (ROOT_RUNNERS_COUNT - 1))];
82         runner->next_by_pgid = *prev;
83         *prev = runner;
84 }
85
86 /* remove a runner for its pgid */
87 static void pgid_remove(struct apprun *runner)
88 {
89         struct apprun **prev = &runners_by_runid[(int)(runner->pids[0] & (ROOT_RUNNERS_COUNT - 1))];
90         runner->next_by_pgid = *prev;
91         *prev = runner;
92 }
93
94 /****************** manages pids **********************/
95
96 /* get a runner by its pid */
97 static struct apprun *runner_of_pid(pid_t pid)
98 {
99         /* try avoiding system call */
100         struct apprun *result = runner_of_pgid(pid);
101         if (result == NULL) {
102                 result = runner_of_pgid(getpgid(pid));
103                 if (result && result->pids[1] != pid)
104                         result = NULL;
105         }
106         return result;
107 }
108
109 /****************** manages runners (by runid) **********************/
110
111 /* get a runner by its runid */
112 static struct apprun *getrunner(int runid)
113 {
114         struct apprun *result = runners_by_runid[runid & (ROOT_RUNNERS_COUNT - 1)];
115         while (result && result->runid != runid)
116                 result = result->next_by_runid;
117         return result;
118 }
119
120 /* free an existing runner */
121 static void freerunner(struct apprun *runner)
122 {
123         struct apprun **prev = &runners_by_runid[runner->runid & (ROOT_RUNNERS_COUNT - 1)];
124         assert(*prev);
125         while(*prev != runner) {
126                 prev = &(*prev)->next_by_runid;
127                 assert(*prev);
128         }
129         *prev = runner->next_by_runid;
130         json_object_put(runner->appli);
131         free(runner);
132         runnercount--;
133 }
134
135 /* create a new runner */
136 static struct apprun *createrunner(json_object *appli)
137 {
138         struct apprun *result;
139         struct apprun **prev;
140
141         if (runnercount >= MAX_RUNNER_COUNT) {
142                 errno = EAGAIN;
143                 return NULL;
144         }
145         do {
146                 runnerid++;
147                 if (runnerid > MAX_RUNNER_COUNT)
148                         runnerid = 1;
149         } while(getrunner(runnerid));
150         result = calloc(1, sizeof * result);
151         if (result == NULL)
152                 errno = ENOMEM;
153         else {
154                 prev = &runners_by_runid[runnerid & (ROOT_RUNNERS_COUNT - 1)];
155                 result->next_by_runid = *prev;
156                 result->next_by_pgid = NULL;
157                 result->runid = runnerid;
158                 result->pids[0] = result->pids[1] = 0;
159                 result->state = as_starting;
160                 result->appli = json_object_get(appli);
161                 *prev = result;
162                 runnercount++;
163         }
164         return result;
165 }
166
167 /**************** signaling ************************/
168 #if 0
169 static void started(int runid)
170 {
171 }
172
173 static void stopped(int runid)
174 {
175 }
176
177 static void continued(int runid)
178 {
179 }
180
181 static void terminated(int runid)
182 {
183 }
184
185 static void removed(int runid)
186 {
187 }
188 #endif
189 /**************** running ************************/
190
191 static int killrunner(int runid, int sig, enum appstate tostate)
192 {
193         int rc;
194         struct apprun *runner = getrunner(runid);
195         if (runner == NULL) {
196                 errno = ENOENT;
197                 rc = -1;
198         }
199         else if (runner->state != as_running && runner->state != as_stopped) {
200                 errno = EPERM;
201                 rc = -1;
202         }
203         else if (runner->state == tostate) {
204                 rc = 0;
205         }
206         else {
207                 rc = killpg(runner->pids[0], sig);
208                 if (!rc)
209                         runner->state = tostate;
210         }
211         return rc;
212 }
213
214 static void on_sigchld(int signum, siginfo_t *info, void *uctxt)
215 {
216         struct apprun *runner;
217
218         runner = runner_of_pgid(info->si_pid);
219         if (!runner)
220                 return;
221
222         switch(info->si_code) {
223         case CLD_EXITED:
224         case CLD_KILLED:
225         case CLD_DUMPED:
226         case CLD_TRAPPED:
227                 runner->state = as_terminated;
228                 pgid_remove(runner);
229                 break;
230
231         case CLD_STOPPED:
232                 runner->state = as_stopped;
233                 break;
234
235         case CLD_CONTINUED:
236                 runner->state = as_running;
237                 break;
238         }
239 }
240
241 /**************** handle afm_launch_desc *********************/
242
243 static int fill_launch_desc(struct json_object *appli, enum afm_launch_mode mode, struct afm_launch_desc *desc)
244 {
245         json_object *pub;
246
247         assert(launch_mode_is_valid(mode));
248
249         /* main items */
250         if(!j_read_object_at(appli, "public", &pub)
251         || !j_read_string_at(appli, "path", &desc->path)
252         || !j_read_string_at(appli, "id", &desc->tag)
253         || !j_read_string_at(appli, "content", &desc->content)
254         || !j_read_string_at(appli, "type", &desc->type)
255         || !j_read_string_at(pub, "name", &desc->name)
256         || !j_read_integer_at(pub, "width", &desc->width)
257         || !j_read_integer_at(pub, "height", &desc->height)) {
258                 errno = EINVAL;
259                 return -1;
260         }
261
262         /* plugins */
263         {
264                 /* TODO */
265                 static const char *null = NULL;
266                 desc->plugins = &null;
267         }
268
269         /* finaly */
270         desc->home = homeappdir;
271         desc->mode = mode;
272         return 0;
273 };
274
275 /**************** API handling ************************/
276
277 int afm_run_start(struct json_object *appli, enum afm_launch_mode mode, char **uri)
278 {
279         static struct apprun *runner;
280         struct afm_launch_desc desc;
281         int rc;
282         sigset_t saved, blocked;
283
284         assert(launch_mode_is_valid(mode));
285         assert(mode == mode_local || uri != NULL);
286         assert(uri == NULL || *uri == NULL);
287
288         /* prepare to launch */
289         rc = fill_launch_desc(appli, mode, &desc);
290         if (rc)
291                 return rc;
292         runner = createrunner(appli);
293         if (!runner)
294                 return -1;
295
296         /* block children signals until launched */
297         sigemptyset(&blocked);
298         sigaddset(&blocked, SIGCHLD);
299         sigprocmask(SIG_BLOCK, &blocked, &saved);
300
301         /* launch now */
302         rc = afm_launch(&desc, runner->pids, uri);
303         if (rc < 0) {
304                 /* fork failed */
305                 sigprocmask(SIG_SETMASK, &saved, NULL);
306                 ERROR("can't start, afm_launch failed: %m");
307                 freerunner(runner);
308                 return -1;
309         }
310
311         /* insert the pid */
312         runner->state = as_running;
313         pgid_insert(runner);
314         rc = runner->runid;
315
316         /* unblock children signal now */
317         sigprocmask(SIG_SETMASK, &saved, NULL);
318         return rc;
319 }
320
321 int afm_run_terminate(int runid)
322 {
323         return killrunner(runid, SIGTERM, as_terminating);
324 }
325
326 int afm_run_stop(int runid)
327 {
328         return killrunner(runid, SIGSTOP, as_stopped);
329 }
330
331 int afm_run_continue(int runid)
332 {
333         return killrunner(runid, SIGCONT, as_running);
334 }
335
336 static json_object *mkstate(struct apprun *runner)
337 {
338         const char *state;
339         struct json_object *result, *obj;
340         int rc;
341
342         /* the structure */
343         result = json_object_new_object();
344         if (result == NULL)
345                 goto error;
346
347         /* the runid */
348         if (!j_add_integer(result, "runid", runner->runid))
349                 goto error2;
350
351         /* the state */
352         switch(runner->state) {
353         case as_starting:
354         case as_running:
355                 state = "running";
356                 break;
357         case as_stopped:
358                 state = "stopped";
359                 break;
360         default:
361                 state = "terminated";
362                 break;
363         }
364         if (!j_add_string(result, "state", state))
365                 goto error2;
366
367         /* the application id */
368         rc = json_object_object_get_ex(runner->appli, "public", &obj);
369         assert(rc);
370         rc = json_object_object_get_ex(obj, "id", &obj);
371         assert(rc);
372         if (!j_add(result, "id", obj))
373                 goto error2;
374         json_object_get(obj);
375
376         /* done */
377         return result;
378
379 error2:
380         json_object_put(result);
381 error:
382         errno = ENOMEM;
383         return NULL;
384 }
385
386 struct json_object *afm_run_list()
387 {
388         struct json_object *result, *obj;
389         struct apprun *runner;
390         int i;
391
392         /* creates the object */
393         result = json_object_new_array();
394         if (result == NULL)
395                 goto error;
396
397         for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
398                 for (runner = runners_by_runid[i] ; runner ; runner = runner->next_by_runid) {
399                         if (runner->state != as_terminating && runner->state != as_terminated) {
400                                 obj = mkstate(runner);
401                                 if (obj == NULL)
402                                         goto error2;
403                                 if (json_object_array_add(result, obj) == -1) {
404                                         json_object_put(obj);
405                                         goto error2;
406                                 }
407                         }
408                 }
409         }
410         return result;
411
412 error2:
413         json_object_put(result);
414 error:
415         errno = ENOMEM;
416         return NULL;
417 }
418
419 struct json_object *afm_run_state(int runid)
420 {
421         struct apprun *runner = getrunner(runid);
422         if (runner == NULL || runner->state == as_terminating || runner->state == as_terminated) {
423                 errno = ENOENT;
424                 return NULL;
425         }
426         return mkstate(runner);
427 }
428
429 /**************** INITIALISATION **********************/
430
431 int afm_run_init()
432 {
433         char buf[2048];
434         char dir[PATH_MAX];
435         int rc;
436         uid_t me;
437         struct passwd passwd, *pw;
438         struct sigaction siga;
439
440         /* init launcher */
441         rc = afm_launch_initialize();
442         if (rc)
443                 return rc;
444
445         /* computes the 'homeappdir' */
446         me = geteuid();
447         rc = getpwuid_r(me, &passwd, buf, sizeof buf, &pw);
448         if (rc || pw == NULL) {
449                 errno = rc ? errno : ENOENT;
450                 ERROR("getpwuid_r failed for uid=%d: %m",(int)me);
451                 return -1;
452         }
453         rc = snprintf(dir, sizeof dir, "%s/%s", passwd.pw_dir, fwk_user_app_dir);
454         if (rc >= sizeof dir) {
455                 ERROR("buffer overflow in user_app_dir for uid=%d",(int)me);
456                 return -1;
457         }
458         rc = create_directory(dir, 0755, 1);
459         if (rc && errno != EEXIST) {
460                 ERROR("creation of directory %s failed in user_app_dir: %m", dir);
461                 return -1;
462         }
463         homeappdir = strdup(dir);
464         if (homeappdir == NULL) {
465                 errno = ENOMEM;
466                 ERROR("out of memory in user_app_dir for %s : %m", dir);
467                 return -1;
468         }
469
470         /* install signal handlers */
471         siga.sa_flags = SA_SIGINFO | SA_NOCLDWAIT;
472         sigemptyset(&siga.sa_mask);
473         sigaddset(&siga.sa_mask, SIGCHLD);
474         siga.sa_sigaction = on_sigchld;
475         sigaction(SIGCHLD, &siga, NULL);
476         return 0;
477 }
478