afm-run: adds helper functions
[src/app-framework-main.git] / src / afm-run.c
1 /*
2  Copyright 2015, 2016 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 #define _GNU_SOURCE
20
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <signal.h>
24 #include <pwd.h>
25 #include <sys/types.h>
26 #include <errno.h>
27 #include <assert.h>
28 #include <stdio.h>
29 #include <limits.h>
30 #include <string.h>
31
32 #include <linux/xattr.h>
33 #if SIMULATE_LIBSMACK
34 #include "simulation/smack.h"
35 #else
36 #include <sys/smack.h>
37 #endif
38
39 #include <json-c/json.h>
40
41 #include "verbose.h"
42 #include "utils-dir.h"
43 #include "utils-json.h"
44 #include "afm-launch-mode.h"
45 #include "afm-launch.h"
46 #include "afm-run.h"
47
48 /*
49  * State of a launched/running application
50  */
51 enum appstate {
52         as_starting,    /* start in progress */
53         as_running,     /* started and running */
54         as_paused,      /* paused */
55         as_terminating, /* termination in progress */
56         as_terminated   /* terminated */
57 };
58
59 /*
60  * Structure for recording a runner
61  */
62 struct apprun {
63         struct apprun *next_by_runid; /* link for hashing by runid */
64         struct apprun *next_by_pgid;  /* link for hashing by pgid */
65         int runid;           /* runid */
66         pid_t pids[2];       /* pids (0: group leader, 1: slave) */
67         enum appstate state; /* current state of the application */
68         json_object *appli;  /* json object describing the application */
69 };
70
71 /*
72  * Count of item by hash table
73  */
74 #define ROOT_RUNNERS_COUNT  32
75
76 /*
77  * Maximum count of simultaneous running application
78  */
79 #define MAX_RUNNER_COUNT    32767
80
81 /*
82  * Hash tables of runners by runid and by pgid
83  */
84 static struct apprun *runners_by_runid[ROOT_RUNNERS_COUNT];
85 static struct apprun *runners_by_pgid[ROOT_RUNNERS_COUNT];
86
87 /*
88  * List of terminated runners
89  */
90 static struct apprun *terminated_runners = NULL;
91
92 /*
93  * Count of runners
94  */
95 static int runnercount = 0;
96
97 /*
98  * Last given runid
99  */
100 static int runnerid = 0;
101
102 /*
103  * Path name of the directory for applications in the
104  * home directory of the user.
105  */
106 static const char fwk_user_app_dir[] = FWK_USER_APP_DIR;
107 static const char fwk_user_app_label[] = FWK_USER_APP_DIR_LABEL;
108
109 /*
110  * Path of the root directory for applications of the
111  * current user
112  */
113 static char *homeappdir;
114
115 /****************** manages pids **********************/
116
117 /*
118  * Get a runner by its 'pid' (NULL if not found)
119  */
120 static struct apprun *runner_of_pid(pid_t pid)
121 {
122         int i;
123         struct apprun *result;
124
125         for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
126                 result = runners_by_pgid[i];
127                 while (result != NULL) {
128                         if (result->pids[0] == pid || result->pids[1] == pid)
129                                 return result;
130                         result = result->next_by_pgid;
131                 }
132         }
133         return NULL;
134 }
135
136 /****************** manages pgids **********************/
137
138 /*
139  * Get a runner by its 'pgid' (NULL if not found)
140  */
141 static struct apprun *runner_of_pgid(pid_t pgid)
142 {
143         struct apprun *result;
144
145         result = runners_by_pgid[pgid & (ROOT_RUNNERS_COUNT - 1)];
146         while (result && result->pids[0] != pgid)
147                 result = result->next_by_pgid;
148         return result;
149 }
150
151 /*
152  * Insert a 'runner' for its pgid
153  */
154 static void pgid_insert(struct apprun *runner)
155 {
156         struct apprun **prev;
157
158         prev = &runners_by_pgid[runner->pids[0] & (ROOT_RUNNERS_COUNT - 1)];
159         runner->next_by_pgid = *prev;
160         *prev = runner;
161 }
162
163 /*
164  * Remove a 'runner' for its pgid
165  */
166 static void pgid_remove(struct apprun *runner)
167 {
168         struct apprun **prev;
169
170         prev = &runners_by_pgid[runner->pids[0] & (ROOT_RUNNERS_COUNT - 1)];
171         while (*prev) {
172                 if (*prev == runner) {
173                         *prev = runner->next_by_pgid;
174                         break;
175                 }
176                 prev = &(*prev)->next_by_pgid;
177         }
178 }
179
180 /****************** manages runners (by runid) **********************/
181
182 /*
183  * Is a 'runner' alive?
184  */
185 static inline int is_alive(struct apprun *runner)
186 {
187         switch(runner->state) {
188         case as_terminating:
189         case as_terminated:
190                 return 0;
191         default:
192                 return 1;
193         }
194 }
195
196 /*
197  * Is a 'runner' dead?
198  */
199 static inline int is_dead(struct apprun *runner)
200 {
201         switch(runner->state) {
202         case as_terminating:
203         case as_terminated:
204                 return 1;
205         default:
206                 return 0;
207         }
208 }
209
210 /*
211  * Is a 'runner' running?
212  */
213 static inline int is_running(struct apprun *runner)
214 {
215         switch(runner->state) {
216         case as_starting:
217         case as_running:
218                 return 1;
219         default:
220                 return 0;
221         }
222 }
223
224 /*
225  * Is a 'runner' paused?
226  */
227 static inline int is_paused(struct apprun *runner)
228 {
229         switch(runner->state) {
230         case as_paused:
231                 return 1;
232         default:
233                 return 0;
234         }
235 }
236
237 /*
238  * Get a runner by its 'runid'  (NULL if not found)
239  */
240 static struct apprun *getrunner(int runid)
241 {
242         struct apprun *result;
243
244         result = runners_by_runid[runid & (ROOT_RUNNERS_COUNT - 1)];
245         while (result && result->runid != runid)
246                 result = result->next_by_runid;
247         return result;
248 }
249
250 /*
251  * Free an existing 'runner'
252  */
253 static void freerunner(struct apprun *runner)
254 {
255         struct apprun **prev;
256
257         /* get previous pointer to runner */
258         prev = &runners_by_runid[runner->runid & (ROOT_RUNNERS_COUNT - 1)];
259         assert(*prev);
260         while(*prev != runner) {
261                 prev = &(*prev)->next_by_runid;
262                 assert(*prev);
263         }
264
265         /* unlink */
266         *prev = runner->next_by_runid;
267         runnercount--;
268
269         /* release/free */
270         json_object_put(runner->appli);
271         free(runner);
272 }
273
274 /*
275  * Cleans the list of runners from its terminated
276  */
277 static void cleanrunners()
278 {
279         struct apprun *runner;
280         while (terminated_runners) {
281                 runner = terminated_runners;
282                 terminated_runners = runner->next_by_pgid;
283                 freerunner(runner);
284         }
285 }
286
287 /*
288  * Create a new runner for the 'appli'
289  *
290  * Returns the created runner or NULL
291  * in case of error.
292  */
293 static struct apprun *createrunner(json_object *appli)
294 {
295         struct apprun *result;
296         struct apprun **prev;
297
298         /* cleanup */
299         cleanrunners();
300
301         /* get a runid */
302         if (runnercount >= MAX_RUNNER_COUNT) {
303                 errno = EAGAIN;
304                 return NULL;
305         }
306         do {
307                 runnerid++;
308                 if (runnerid > MAX_RUNNER_COUNT)
309                         runnerid = 1;
310         } while(getrunner(runnerid));
311
312         /* create the structure */
313         result = calloc(1, sizeof * result);
314         if (result == NULL)
315                 errno = ENOMEM;
316         else {
317                 /* initialize it linked to the list */
318                 prev = &runners_by_runid[runnerid & (ROOT_RUNNERS_COUNT - 1)];
319                 result->next_by_runid = *prev;
320                 result->next_by_pgid = NULL;
321                 result->runid = runnerid;
322                 result->pids[0] = result->pids[1] = 0;
323                 result->state = as_starting;
324                 result->appli = json_object_get(appli);
325                 *prev = result;
326                 runnercount++;
327         }
328         return result;
329 }
330
331 /**************** signaling ************************/
332 #if 0
333 static void started(int runid)
334 {
335 }
336
337 static void paused(int runid)
338 {
339 }
340
341 static void resumed(int runid)
342 {
343 }
344
345 static void terminated(int runid)
346 {
347 }
348
349 static void removed(int runid)
350 {
351 }
352 #endif
353 /**************** running ************************/
354
355 /*
356  * Sends (with pgkill) the signal 'sig' to the process group
357  * for 'runid' and put the runner's state to 'tostate'
358  * in case of success.
359  *
360  * Only processes in the state 'as_running' or 'as_paused'
361  * can be signalled.
362  *
363  * Returns 0 in case of success or -1 in case of error.
364  */
365 static int killrunner(int runid, int sig, enum appstate tostate)
366 {
367         int rc;
368         struct apprun *runner = getrunner(runid);
369         if (runner == NULL) {
370                 errno = ENOENT;
371                 rc = -1;
372         }
373         else if (is_dead(runner)) {
374                 errno = EINVAL;
375                 rc = -1;
376         }
377         else if (runner->state == tostate) {
378                 rc = 0;
379         }
380         else {
381                 rc = killpg(runner->pids[0], sig);
382                 if (!rc)
383                         runner->state = tostate;
384         }
385         return rc;
386 }
387
388 /*
389  * Signal callback called on SIGCHLD. This is set using sigaction.
390  */
391 static void on_sigchld(int signum, siginfo_t *info, void *uctxt)
392 {
393         struct apprun *runner;
394
395         /* retrieves the runner */
396         runner = runner_of_pid(info->si_pid);
397         if (!runner)
398                 return;
399
400         /* known runner, inspect cause of signal */
401         switch(info->si_code) {
402         case CLD_EXITED:
403         case CLD_KILLED:
404         case CLD_DUMPED:
405         case CLD_TRAPPED:
406                 /* update the state */
407                 runner->state = as_terminated;
408                 /* remove it from pgid list */
409                 pgid_remove(runner);
410                 runner->next_by_pgid = terminated_runners;
411                 terminated_runners = runner;
412                 /* ensures that all the group terminates */
413                 killpg(runner->pids[0], SIGKILL);
414                 break;
415
416         case CLD_STOPPED:
417                 /* update the state */
418                 runner->state = as_paused;
419                 break;
420
421         case CLD_CONTINUED:
422                 /* update the state */
423                 runner->state = as_running;
424                 break;
425         }
426 }
427
428 /**************** handle afm_launch_desc *********************/
429
430 /*
431  * Initialize the data of the launch description 'desc'
432  * for the application 'appli' and the 'mode'.
433  *
434  * Returns 0 in case of success or -1 in case of error.
435  */
436 static int fill_launch_desc(struct json_object *appli,
437                 enum afm_launch_mode mode, struct afm_launch_desc *desc)
438 {
439         json_object *pub;
440
441         assert(is_valid_launch_mode(mode));
442
443         /* main items */
444         if(!j_read_object_at(appli, "public", &pub)
445         || !j_read_string_at(appli, "path", &desc->path)
446         || !j_read_string_at(appli, "id", &desc->appid)
447         || !j_read_string_at(appli, "content", &desc->content)
448         || !j_read_string_at(appli, "type", &desc->type)
449         || !j_read_string_at(pub, "name", &desc->name)
450         || !j_read_integer_at(pub, "width", &desc->width)
451         || !j_read_integer_at(pub, "height", &desc->height)) {
452                 errno = EINVAL;
453                 return -1;
454         }
455
456         /* bindings */
457         {
458                 /* TODO */
459                 static const char *null = NULL;
460                 desc->bindings = &null;
461         }
462
463         /* finaly */
464         desc->home = homeappdir;
465         desc->mode = mode;
466         return 0;
467 }
468
469 /**************** report state of runner *********************/
470
471 /*
472  * Creates a json object that describes the state of 'runner'.
473  *
474  * Returns the created object or NULL in case of error.
475  */
476 static json_object *mkstate(struct apprun *runner)
477 {
478         const char *state;
479         struct json_object *result, *obj, *pids;
480         int rc;
481
482         /* the structure */
483         result = json_object_new_object();
484         if (result == NULL)
485                 goto error;
486
487         /* the runid */
488         if (!j_add_integer(result, "runid", runner->runid))
489                 goto error2;
490
491         /* the pids */
492         if (is_alive(runner)) {
493                 pids = j_add_new_array(result, "pids");
494                 if (!pids)
495                         goto error2;
496                 if (!j_add_integer(pids, NULL, runner->pids[0]))
497                         goto error2;
498                 if (runner->pids[1] && !j_add_integer(pids, NULL, runner->pids[1]))
499                         goto error2;
500         }
501
502         /* the state */
503         switch(runner->state) {
504         case as_starting:
505         case as_running:
506                 state = "running";
507                 break;
508         case as_paused:
509                 state = "paused";
510                 break;
511         default:
512                 state = "terminated";
513                 break;
514         }
515         if (!j_add_string(result, "state", state))
516                 goto error2;
517
518         /* the application id */
519         rc = json_object_object_get_ex(runner->appli, "public", &obj);
520         assert(rc);
521         rc = json_object_object_get_ex(obj, "id", &obj);
522         assert(rc);
523         if (!j_add(result, "id", obj))
524                 goto error2;
525         json_object_get(obj);
526
527         /* done */
528         return result;
529
530 error2:
531         json_object_put(result);
532 error:
533         errno = ENOMEM;
534         return NULL;
535 }
536
537 /**************** API handling ************************/
538
539 /*
540  * Starts the application described by 'appli' for the 'mode'.
541  * In case of remote start, it returns in uri the uri to
542  * connect to.
543  *
544  * A reference to 'appli' is kept during the live of the
545  * runner. This is made using json_object_get. Thus be aware
546  * that further modifications to 'appli' might create errors.
547  *
548  * Returns the runid in case of success or -1 in case of error
549  */
550 int afm_run_start(struct json_object *appli, enum afm_launch_mode mode,
551                                                         char **uri)
552 {
553         struct apprun *runner;
554         struct afm_launch_desc desc;
555         int rc;
556         sigset_t saved, blocked;
557
558         assert(is_valid_launch_mode(mode));
559         assert(mode == mode_local || uri != NULL);
560         assert(uri == NULL || *uri == NULL);
561
562         /* prepare to launch */
563         rc = fill_launch_desc(appli, mode, &desc);
564         if (rc)
565                 return rc;
566         runner = createrunner(appli);
567         if (!runner)
568                 return -1;
569
570         /* block children signals until launched */
571         sigemptyset(&blocked);
572         sigaddset(&blocked, SIGCHLD);
573         sigprocmask(SIG_BLOCK, &blocked, &saved);
574
575         /* launch now */
576         rc = afm_launch(&desc, runner->pids, uri);
577         if (rc < 0) {
578                 /* fork failed */
579                 sigprocmask(SIG_SETMASK, &saved, NULL);
580                 ERROR("can't start, afm_launch failed: %m");
581                 freerunner(runner);
582                 return -1;
583         }
584
585         /* insert the pid */
586         runner->state = as_running;
587         pgid_insert(runner);
588         rc = runner->runid;
589
590         /* unblock children signal now */
591         sigprocmask(SIG_SETMASK, &saved, NULL);
592         return rc;
593 }
594
595 /*
596  * Terminates the runner of 'runid'
597  *
598  * Returns 0 in case of success or -1 in case of error
599  */
600 int afm_run_terminate(int runid)
601 {
602         return killrunner(runid, SIGTERM, as_terminating);
603 }
604
605 /*
606  * Stops (aka pause) the runner of 'runid'
607  *
608  * Returns 0 in case of success or -1 in case of error
609  */
610 int afm_run_pause(int runid)
611 {
612         return killrunner(runid, SIGSTOP, as_paused);
613 }
614
615 /*
616  * Continue (aka resume) the runner of 'runid'
617  *
618  * Returns 0 in case of success or -1 in case of error
619  */
620 int afm_run_resume(int runid)
621 {
622         return killrunner(runid, SIGCONT, as_running);
623 }
624
625 /*
626  * Get the list of the runners.
627  *
628  * Returns the list or NULL in case of error.
629  */
630 struct json_object *afm_run_list()
631 {
632         struct json_object *result, *obj;
633         struct apprun *runner;
634         int i;
635
636         /* creates the object */
637         result = json_object_new_array();
638         if (result == NULL)
639                 goto error;
640
641         /* iterate over runners */
642         for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
643                 runner = runners_by_runid[i];
644                 while (runner) {
645                         if (is_alive(runner)) {
646                                 /* adds the living runner */
647                                 obj = mkstate(runner);
648                                 if (obj == NULL)
649                                         goto error2;
650                                 if (json_object_array_add(result, obj) == -1) {
651                                         json_object_put(obj);
652                                         goto error2;
653                                 }
654                         }
655                         runner = runner->next_by_runid;
656                 }
657         }
658         return result;
659
660 error2:
661         json_object_put(result);
662 error:
663         errno = ENOMEM;
664         return NULL;
665 }
666
667 /*
668  * Get the state of the runner of 'runid'.
669  *
670  * Returns the state or NULL in case of success
671  */
672 struct json_object *afm_run_state(int runid)
673 {
674         struct apprun *runner = getrunner(runid);
675         if (runner == NULL || is_dead(runner)) {
676                 errno = ENOENT;
677                 return NULL;
678         }
679         return mkstate(runner);
680 }
681
682 /**************** INITIALISATION **********************/
683
684 /*
685  * Initialize the module
686  */
687 int afm_run_init()
688 {
689         char buf[2048];
690         int rc;
691         uid_t me;
692         struct passwd passwd, *pw;
693         struct sigaction siga;
694
695         /* init launcher */
696         rc = afm_launch_initialize();
697         if (rc)
698                 return rc;
699
700         /* computes the 'homeappdir' */
701         me = geteuid();
702         rc = getpwuid_r(me, &passwd, buf, sizeof buf, &pw);
703         if (rc || pw == NULL) {
704                 errno = rc ? errno : ENOENT;
705                 ERROR("getpwuid_r failed for uid=%d: %m",(int)me);
706                 return -1;
707         }
708         rc = asprintf(&homeappdir, "%s/%s", passwd.pw_dir, fwk_user_app_dir);
709         if (rc < 0) {
710                 errno = ENOMEM;
711                 ERROR("allocating homeappdir for uid=%d failed", (int)me);
712                 return -1;
713         }
714         rc = create_directory(homeappdir, 0755, 1);
715         if (rc && errno != EEXIST) {
716                 ERROR("creation of directory %s failed: %m", homeappdir);
717                 free(homeappdir);
718                 return -1;
719         }
720         rc = smack_remove_label_for_path(homeappdir,
721                                                 XATTR_NAME_SMACKTRANSMUTE, 0);
722         if (rc < 0 && errno != ENODATA) {
723                 ERROR("can't remove smack transmutation of directory %s: %m",
724                                                                 homeappdir);
725                 free(homeappdir);
726                 return -1;
727         }
728         rc = smack_set_label_for_path(homeappdir, XATTR_NAME_SMACK, 0,
729                                                         fwk_user_app_label);
730         if (rc < 0) {
731                 ERROR("can't set smack label %s to directory %s: %m",
732                                         fwk_user_app_label, homeappdir);
733                 free(homeappdir);
734                 return -1;
735         }
736         /* install signal handlers */
737         siga.sa_flags = SA_SIGINFO | SA_NOCLDWAIT;
738         sigemptyset(&siga.sa_mask);
739         sigaddset(&siga.sa_mask, SIGCHLD);
740         siga.sa_sigaction = on_sigchld;
741         sigaction(SIGCHLD, &siga, NULL);
742         return 0;
743 }
744