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