afm-run: improve error diagnostic
[src/app-framework-main.git] / src / afm-run.c
1 /*
2  Copyright 2015, 2016, 2017 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  * Get first runner of 'appli' (NULL if not found)
252  */
253 static struct apprun *getrunner_appli(json_object *appli)
254 {
255         int i;
256         struct apprun *result;
257
258         for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
259                 result = runners_by_pgid[i];
260                 while (result != NULL) {
261                         if (result->appli == appli)
262                                 return result;
263                         result = result->next_by_pgid;
264                 }
265         }
266         return NULL;
267 }
268
269 /*
270  * Free an existing 'runner'
271  */
272 static void freerunner(struct apprun *runner)
273 {
274         struct apprun **prev;
275
276         /* get previous pointer to runner */
277         prev = &runners_by_runid[runner->runid & (ROOT_RUNNERS_COUNT - 1)];
278         assert(*prev);
279         while(*prev != runner) {
280                 prev = &(*prev)->next_by_runid;
281                 assert(*prev);
282         }
283
284         /* unlink */
285         *prev = runner->next_by_runid;
286         runnercount--;
287
288         /* release/free */
289         json_object_put(runner->appli);
290         free(runner);
291 }
292
293 /*
294  * Cleans the list of runners from its terminated
295  */
296 static void cleanrunners()
297 {
298         struct apprun *runner;
299         while (terminated_runners) {
300                 runner = terminated_runners;
301                 terminated_runners = runner->next_by_pgid;
302                 freerunner(runner);
303         }
304 }
305
306 /*
307  * Create a new runner for the 'appli'
308  *
309  * Returns the created runner or NULL
310  * in case of error.
311  */
312 static struct apprun *createrunner(json_object *appli)
313 {
314         struct apprun *result;
315         struct apprun **prev;
316
317         /* cleanup */
318         cleanrunners();
319
320         /* get a runid */
321         if (runnercount >= MAX_RUNNER_COUNT) {
322                 errno = EAGAIN;
323                 return NULL;
324         }
325         do {
326                 runnerid++;
327                 if (runnerid > MAX_RUNNER_COUNT)
328                         runnerid = 1;
329         } while(getrunner(runnerid));
330
331         /* create the structure */
332         result = calloc(1, sizeof * result);
333         if (result == NULL)
334                 errno = ENOMEM;
335         else {
336                 /* initialize it linked to the list */
337                 prev = &runners_by_runid[runnerid & (ROOT_RUNNERS_COUNT - 1)];
338                 result->next_by_runid = *prev;
339                 result->next_by_pgid = NULL;
340                 result->runid = runnerid;
341                 result->pids[0] = result->pids[1] = 0;
342                 result->state = as_starting;
343                 result->appli = json_object_get(appli);
344                 *prev = result;
345                 runnercount++;
346         }
347         return result;
348 }
349
350 /**************** signaling ************************/
351 #if 0
352 static void started(int runid)
353 {
354 }
355
356 static void paused(int runid)
357 {
358 }
359
360 static void resumed(int runid)
361 {
362 }
363
364 static void terminated(int runid)
365 {
366 }
367
368 static void removed(int runid)
369 {
370 }
371 #endif
372 /**************** running ************************/
373
374 /*
375  * Sends (with pgkill) the signal 'sig' to the process group
376  * for 'runid' and put the runner's state to 'tostate'
377  * in case of success.
378  *
379  * Only processes in the state 'as_running' or 'as_paused'
380  * can be signalled.
381  *
382  * Returns 0 in case of success or -1 in case of error.
383  */
384 static int killrunner(int runid, int sig, enum appstate tostate)
385 {
386         int rc;
387         struct apprun *runner = getrunner(runid);
388         if (runner == NULL) {
389                 errno = ENOENT;
390                 rc = -1;
391         }
392         else if (is_dead(runner)) {
393                 errno = EINVAL;
394                 rc = -1;
395         }
396         else if (runner->state == tostate) {
397                 rc = 0;
398         }
399         else {
400                 rc = killpg(runner->pids[0], sig);
401                 if (!rc)
402                         runner->state = tostate;
403         }
404         return rc;
405 }
406
407 /*
408  * Signal callback called on SIGCHLD. This is set using sigaction.
409  */
410 static void on_sigchld(int signum, siginfo_t *info, void *uctxt)
411 {
412         struct apprun *runner;
413
414         /* retrieves the runner */
415         runner = runner_of_pid(info->si_pid);
416         if (!runner)
417                 return;
418
419         /* known runner, inspect cause of signal */
420         switch(info->si_code) {
421         case CLD_EXITED:
422         case CLD_KILLED:
423         case CLD_DUMPED:
424         case CLD_TRAPPED:
425                 /* update the state */
426                 runner->state = as_terminated;
427                 /* remove it from pgid list */
428                 pgid_remove(runner);
429                 runner->next_by_pgid = terminated_runners;
430                 terminated_runners = runner;
431                 /* ensures that all the group terminates */
432                 killpg(runner->pids[0], SIGKILL);
433                 break;
434
435         case CLD_STOPPED:
436                 /* update the state */
437                 runner->state = as_paused;
438                 break;
439
440         case CLD_CONTINUED:
441                 /* update the state */
442                 runner->state = as_running;
443                 break;
444         }
445 }
446
447 /**************** handle afm_launch_desc *********************/
448
449 /*
450  * Initialize the data of the launch description 'desc'
451  * for the application 'appli' and the 'mode'.
452  *
453  * Returns 0 in case of success or -1 in case of error.
454  */
455 static int fill_launch_desc(struct json_object *appli,
456                 enum afm_launch_mode mode, struct afm_launch_desc *desc)
457 {
458         json_object *pub;
459
460         assert(is_valid_launch_mode(mode));
461
462         /* main items */
463         if(!j_read_object_at(appli, "public", &pub)
464         || !j_read_string_at(appli, "path", &desc->path)
465         || !j_read_string_at(appli, "id", &desc->appid)
466         || !j_read_string_at(appli, "content", &desc->content)
467         || !j_read_string_at(appli, "type", &desc->type)
468         || !j_read_string_at(pub, "name", &desc->name)
469         || !j_read_integer_at(pub, "width", &desc->width)
470         || !j_read_integer_at(pub, "height", &desc->height)) {
471                 ERROR("bad internal description of the application to launch: %s", json_object_get_string(appli));
472                 errno = EINVAL;
473                 return -1;
474         }
475
476         /* bindings */
477         {
478                 /* TODO */
479                 static const char *null = NULL;
480                 desc->bindings = &null;
481         }
482
483         /* finaly */
484         desc->home = homeappdir;
485         desc->mode = mode;
486         return 0;
487 }
488
489 /**************** report state of runner *********************/
490
491 /*
492  * Creates a json object that describes the state of 'runner'.
493  *
494  * Returns the created object or NULL in case of error.
495  */
496 static json_object *mkstate(struct apprun *runner)
497 {
498         const char *state;
499         struct json_object *result, *obj, *pids;
500         int rc;
501
502         /* the structure */
503         result = json_object_new_object();
504         if (result == NULL)
505                 goto error;
506
507         /* the runid */
508         if (!j_add_integer(result, "runid", runner->runid))
509                 goto error2;
510
511         /* the pids */
512         if (is_alive(runner)) {
513                 pids = j_add_new_array(result, "pids");
514                 if (!pids)
515                         goto error2;
516                 if (!j_add_integer(pids, NULL, runner->pids[0]))
517                         goto error2;
518                 if (runner->pids[1] && !j_add_integer(pids, NULL, runner->pids[1]))
519                         goto error2;
520         }
521
522         /* the state */
523         switch(runner->state) {
524         case as_starting:
525         case as_running:
526                 state = "running";
527                 break;
528         case as_paused:
529                 state = "paused";
530                 break;
531         default:
532                 state = "terminated";
533                 break;
534         }
535         if (!j_add_string(result, "state", state))
536                 goto error2;
537
538         /* the application id */
539         rc = json_object_object_get_ex(runner->appli, "public", &obj);
540         assert(rc);
541         rc = json_object_object_get_ex(obj, "id", &obj);
542         assert(rc);
543         if (!j_add(result, "id", obj))
544                 goto error2;
545         json_object_get(obj);
546
547         /* done */
548         return result;
549
550 error2:
551         json_object_put(result);
552 error:
553         errno = ENOMEM;
554         return NULL;
555 }
556
557 /**************** API handling ************************/
558
559 /*
560  * Starts the application described by 'appli' for the 'mode'.
561  * In case of remote start, it returns in uri the uri to
562  * connect to.
563  *
564  * A reference to 'appli' is kept during the live of the
565  * runner. This is made using json_object_get. Thus be aware
566  * that further modifications to 'appli' might create errors.
567  *
568  * Returns the runid in case of success or -1 in case of error
569  */
570 int afm_run_start(struct json_object *appli, enum afm_launch_mode mode,
571                                                         char **uri)
572 {
573         struct apprun *runner;
574         struct afm_launch_desc desc;
575         int rc;
576         sigset_t saved, blocked;
577
578         assert(is_valid_launch_mode(mode));
579         assert(mode == mode_local || uri != NULL);
580         assert(uri == NULL || *uri == NULL);
581
582         /* prepare to launch */
583         rc = fill_launch_desc(appli, mode, &desc);
584         if (rc)
585                 return rc;
586         runner = createrunner(appli);
587         if (!runner)
588                 return -1;
589
590         /* block children signals until launched */
591         sigemptyset(&blocked);
592         sigaddset(&blocked, SIGCHLD);
593         sigprocmask(SIG_BLOCK, &blocked, &saved);
594
595         /* launch now */
596         rc = afm_launch(&desc, runner->pids, uri);
597         if (rc < 0) {
598                 /* fork failed */
599                 sigprocmask(SIG_SETMASK, &saved, NULL);
600                 ERROR("can't start, afm_launch failed: %m");
601                 freerunner(runner);
602                 return -1;
603         }
604
605         /* insert the pid */
606         runner->state = as_running;
607         pgid_insert(runner);
608         rc = runner->runid;
609
610         /* unblock children signal now */
611         sigprocmask(SIG_SETMASK, &saved, NULL);
612         return rc;
613 }
614
615 /*
616  * Returns the runid of a previously started application 'appli'
617  * or if none is running, starts the application described by 'appli'
618  * in local mode.
619  *
620  * A reference to 'appli' is kept during the live of the
621  * runner. This is made using json_object_get. Thus be aware
622  * that further modifications to 'appli' might create errors.
623  *
624  * Returns the runid in case of success or -1 in case of error
625  */
626 int afm_run_once(struct json_object *appli)
627 {
628         struct apprun *runner = getrunner_appli(appli);
629         return runner && is_alive(runner) ? runner->runid : afm_run_start(appli, mode_local, NULL);
630 }
631
632 /*
633  * Terminates the runner of 'runid'
634  *
635  * Returns 0 in case of success or -1 in case of error
636  */
637 int afm_run_terminate(int runid)
638 {
639         return killrunner(runid, SIGTERM, as_terminating);
640 }
641
642 /*
643  * Stops (aka pause) the runner of 'runid'
644  *
645  * Returns 0 in case of success or -1 in case of error
646  */
647 int afm_run_pause(int runid)
648 {
649         return killrunner(runid, SIGSTOP, as_paused);
650 }
651
652 /*
653  * Continue (aka resume) the runner of 'runid'
654  *
655  * Returns 0 in case of success or -1 in case of error
656  */
657 int afm_run_resume(int runid)
658 {
659         return killrunner(runid, SIGCONT, as_running);
660 }
661
662 /*
663  * Get the list of the runners.
664  *
665  * Returns the list or NULL in case of error.
666  */
667 struct json_object *afm_run_list()
668 {
669         struct json_object *result, *obj;
670         struct apprun *runner;
671         int i;
672
673         /* creates the object */
674         result = json_object_new_array();
675         if (result == NULL)
676                 goto error;
677
678         /* iterate over runners */
679         for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
680                 runner = runners_by_runid[i];
681                 while (runner) {
682                         if (is_alive(runner)) {
683                                 /* adds the living runner */
684                                 obj = mkstate(runner);
685                                 if (obj == NULL)
686                                         goto error2;
687                                 if (json_object_array_add(result, obj) == -1) {
688                                         json_object_put(obj);
689                                         goto error2;
690                                 }
691                         }
692                         runner = runner->next_by_runid;
693                 }
694         }
695         return result;
696
697 error2:
698         json_object_put(result);
699 error:
700         errno = ENOMEM;
701         return NULL;
702 }
703
704 /*
705  * Get the state of the runner of 'runid'.
706  *
707  * Returns the state or NULL in case of success
708  */
709 struct json_object *afm_run_state(int runid)
710 {
711         struct apprun *runner = getrunner(runid);
712         if (runner == NULL || is_dead(runner)) {
713                 errno = ENOENT;
714                 return NULL;
715         }
716         return mkstate(runner);
717 }
718
719 /**************** INITIALISATION **********************/
720
721 /*
722  * Initialize the module
723  */
724 int afm_run_init()
725 {
726         char buf[2048];
727         int rc;
728         uid_t me;
729         struct passwd passwd, *pw;
730         struct sigaction siga;
731
732         /* init launcher */
733         rc = afm_launch_initialize();
734         if (rc)
735                 return rc;
736
737         /* computes the 'homeappdir' */
738         me = geteuid();
739         rc = getpwuid_r(me, &passwd, buf, sizeof buf, &pw);
740         if (rc || pw == NULL) {
741                 errno = rc ? errno : ENOENT;
742                 ERROR("getpwuid_r failed for uid=%d: %m",(int)me);
743                 return -1;
744         }
745         rc = asprintf(&homeappdir, "%s/%s", passwd.pw_dir, fwk_user_app_dir);
746         if (rc < 0) {
747                 errno = ENOMEM;
748                 ERROR("allocating homeappdir for uid=%d failed", (int)me);
749                 return -1;
750         }
751         rc = create_directory(homeappdir, 0755, 1);
752         if (rc && errno != EEXIST) {
753                 ERROR("creation of directory %s failed: %m", homeappdir);
754                 free(homeappdir);
755                 return -1;
756         }
757         rc = smack_remove_label_for_path(homeappdir,
758                                                 XATTR_NAME_SMACKTRANSMUTE, 0);
759         if (rc < 0 && errno != ENODATA) {
760                 ERROR("can't remove smack transmutation of directory %s: %m",
761                                                                 homeappdir);
762                 free(homeappdir);
763                 return -1;
764         }
765         rc = smack_set_label_for_path(homeappdir, XATTR_NAME_SMACK, 0,
766                                                         fwk_user_app_label);
767         if (rc < 0) {
768                 ERROR("can't set smack label %s to directory %s: %m",
769                                         fwk_user_app_label, homeappdir);
770                 free(homeappdir);
771                 return -1;
772         }
773         /* install signal handlers */
774         siga.sa_flags = SA_SIGINFO | SA_NOCLDWAIT;
775         sigemptyset(&siga.sa_mask);
776         sigaddset(&siga.sa_mask, SIGCHLD);
777         siga.sa_sigaction = on_sigchld;
778         sigaction(SIGCHLD, &siga, NULL);
779         return 0;
780 }
781