Remove option readyfd
[src/app-framework-binder.git] / src / afb-config.c
1 /*
2  * Copyright (C) 2015, 2016, 2017 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE
19 #define NO_BINDING_VERBOSE_MACRO
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <getopt.h>
25 #include <limits.h>
26 #include <unistd.h>
27
28 #include <uuid/uuid.h>
29
30 #include "verbose.h"
31 #include "afb-config.h"
32 #include "afb-hook.h"
33
34 #include <afb/afb-binding.h>
35
36 #if !defined(BINDING_INSTALL_DIR)
37 #error "you should define BINDING_INSTALL_DIR"
38 #endif
39
40 #define AFB_VERSION    "0.6"
41
42 // default
43 #define DEFLT_CNTX_TIMEOUT  3600        // default Client Connection
44                                         // Timeout
45 #define DEFLT_API_TIMEOUT   20          // default Plugin API Timeout [0=NoLimit
46                                         // for Debug Only]
47 #define DEFLT_CACHE_TIMEOUT 100000      // default Static File Chache
48                                         // [Client Side Cache
49                                         // 100000~=1day]
50 #define DEFLT_AUTH_TOKEN    NULL        // expect for debug should == NULL
51 #define CTX_NBCLIENTS       10          // allow a default of 10 authenticated
52                                         // clients
53
54
55 // Define command line option
56 #define SET_BACKGROUND     2
57 #define SET_FORGROUND      3
58
59 #define SET_TCP_PORT       5
60 #define SET_ROOT_DIR       6
61 #define SET_ROOT_BASE      7
62 #define SET_ROOT_API       8
63 #define SET_ALIAS          9
64
65 #define SET_CACHE_TIMEOUT  10
66 #define SET_SESSION_DIR    11
67
68 #define SET_LDPATH         13
69 #define SET_APITIMEOUT     14
70 #define SET_CNTXTIMEOUT    15
71
72
73 #define SET_MODE           18
74
75 #define DBUS_CLIENT        20
76 #define DBUS_SERVICE       21
77 #define SO_BINDING         22
78
79 #define SET_SESSIONMAX     23
80
81 #define WS_CLIENT          24
82 #define WS_SERVICE         25
83
84 #define SET_ROOT_HTTP      26
85
86 #define SET_NO_HTTPD       28
87
88 #define SET_EXEC           'e'
89 #define DISPLAY_HELP       'h'
90 #define SET_QUIET          'q'
91 #define SET_RNDTOKEN       'r'
92 #define SET_TRACEREQ       'T'
93 #define SET_AUTH_TOKEN     't'
94 #define SET_UPLOAD_DIR     'u'
95 #define DISPLAY_VERSION    'V'
96 #define SET_VERBOSE        'v'
97 #define SET_WORK_DIR       'w'
98
99 #define SHORTOPTS       "ehqrT:t:u:Vvw:"
100
101 // Command line structure hold cli --command + help text
102 typedef struct {
103         int val;                // command number within application
104         int has_arg;            // command number within application
105         char *name;             // command as used in --xxxx cli
106         char *help;             // help text
107 } AFB_options;
108
109 // Supported option
110 static AFB_options cliOptions[] = {
111 /* *INDENT-OFF* */
112         {SET_VERBOSE,       0, "verbose",     "Verbose Mode, repeat to increase verbosity"},
113         {SET_QUIET,         0, "quiet",       "Quiet Mode, repeat to decrease verbosity"},
114
115         {SET_FORGROUND,     0, "foreground",  "Get all in foreground mode"},
116         {SET_BACKGROUND,    0, "daemon",      "Get all in background mode"},
117
118         {SET_TCP_PORT,      1, "port",        "HTTP listening TCP port  [default 1234]"},
119         {SET_ROOT_HTTP,     1, "roothttp",    "HTTP Root Directory [default no root http (files not served but apis still available)]"},
120         {SET_ROOT_BASE,     1, "rootbase",    "Angular Base Root URL [default /opa]"},
121         {SET_ROOT_API,      1, "rootapi",     "HTML Root API URL [default /api]"},
122         {SET_ALIAS,         1, "alias",       "Multiple url map outside of rootdir [eg: --alias=/icons:/usr/share/icons]"},
123
124         {SET_APITIMEOUT,    1, "apitimeout",  "Binding API timeout in seconds [default 10]"},
125         {SET_CNTXTIMEOUT,   1, "cntxtimeout", "Client Session Context Timeout [default 900]"},
126         {SET_CACHE_TIMEOUT, 1, "cache-eol",   "Client cache end of live [default 3600]"},
127
128         {SET_WORK_DIR,      1, "workdir",     "Set the working directory [default: $PWD or current working directory]"},
129         {SET_UPLOAD_DIR,    1, "uploaddir",   "Directory for uploading files [default: workdir]"},
130         {SET_ROOT_DIR,      1, "rootdir",     "Root Directory of the application [default: workdir]"},
131         {SET_SESSION_DIR,   1, "sessiondir",  "OBSOLETE (was: Sessions file path)"},
132
133         {SET_LDPATH,        1, "ldpaths",     "Load bindings from dir1:dir2:... [default = " BINDING_INSTALL_DIR "]"},
134         {SO_BINDING,        1, "binding",     "Load the binding of path"},
135
136         {SET_AUTH_TOKEN,    1, "token",       "Initial Secret [default=no-session, --token= for session without authentication]"},
137         {SET_RNDTOKEN,      0, "random-token","Creates a random token"},
138
139         {DISPLAY_VERSION,   0, "version",     "Display version and copyright"},
140         {DISPLAY_HELP,      0, "help",        "Display this help"},
141
142         {SET_MODE,          1, "mode",        "Set the mode: either local, remote or global"},
143
144         {DBUS_CLIENT,       1, "dbus-client", "Bind to an afb service through dbus"},
145         {DBUS_SERVICE,      1, "dbus-server", "Provides an afb service through dbus"},
146         {WS_CLIENT,         1, "ws-client",   "Bind to an afb service through websocket"},
147         {WS_SERVICE,        1, "ws-server",   "Provides an afb service through websockets"},
148
149         {SET_SESSIONMAX,    1, "session-max", "Max count of session simultaneously [default 10]"},
150
151         {SET_TRACEREQ,      1, "tracereq",    "Log the requests: no, common, extra, all"},
152
153         {SET_NO_HTTPD,      0, "no-httpd",    "Forbids HTTP service"},
154         {SET_EXEC,          0, "exec",        "Execute the remaining arguments"},
155
156         {0, 0, NULL, NULL}
157 /* *INDENT-ON* */
158 };
159
160
161 struct enumdesc
162 {
163         const char *name;
164         int value;
165 };
166
167 static struct enumdesc tracereq_desc[] = {
168         { "no",     0 },
169         { "common", afb_hook_flags_req_common },
170         { "extra",  afb_hook_flags_req_extra },
171         { "all",    afb_hook_flags_req_all },
172         { NULL, 0 }
173 };
174
175 static struct enumdesc mode_desc[] = {
176         { "local",  AFB_MODE_LOCAL },
177         { "remote", AFB_MODE_REMOTE },
178         { "global", AFB_MODE_GLOBAL },
179         { NULL, 0 }
180 };
181
182 /*----------------------------------------------------------
183  | printversion
184  |   print version and copyright
185  +--------------------------------------------------------- */
186 static void printVersion(FILE * file)
187 {
188         fprintf(file, "\n----------------------------------------- \n");
189         fprintf(file, "  AFB [Application Framework Binder] version=%s |\n",
190                 AFB_VERSION);
191         fprintf(file, " \n");
192         fprintf(file,
193                 "  Copyright (C) 2015, 2016, 2017 \"IoT.bzh\" [fulup -at- iot.bzh]\n");
194         fprintf(file, "  AFB comes with ABSOLUTELY NO WARRANTY.\n");
195         fprintf(file, "  Licence Apache 2\n\n");
196 }
197
198 /*----------------------------------------------------------
199  | printHelp
200  |   print information from long option array
201  +--------------------------------------------------------- */
202
203 static void printHelp(FILE * file, const char *name)
204 {
205         int ind;
206         char command[50];
207
208         fprintf(file, "%s:\nallowed options\n", name);
209         for (ind = 0; cliOptions[ind].name != NULL; ind++) {
210                 strcpy(command, cliOptions[ind].name);
211                 if (cliOptions[ind].has_arg)
212                         strcat(command, "=xxxx");
213                 fprintf(file, "  --%-15s %s\n", command, cliOptions[ind].help);
214         }
215         fprintf(file,
216                 "Example:\n  %s  --verbose --port=1234 --token='azerty' --ldpaths=build/bindings:/usr/lib64/agl/bindings\n",
217                 name);
218 }
219
220
221 /*----------------------------------------------------------
222  |   adds a string to the list
223  +--------------------------------------------------------- */
224 static char *random_token()
225 {
226         static char uuidstr[37];
227         uuid_t uuid;
228         uuid_generate_random(uuid);
229         uuid_unparse(uuid, uuidstr);
230         return uuidstr;
231 }
232
233 /*----------------------------------------------------------
234  |   adds a string to the list
235  +--------------------------------------------------------- */
236 static void list_add(struct afb_config_list **head, char *value)
237 {
238         struct afb_config_list *item;
239
240         /*
241          * search tail
242          */
243         item = *head;
244         while (item != NULL) {
245                 head = &item->next;
246                 item = item->next;
247         }
248
249         /*
250          * alloc the item
251          */
252         item = malloc(sizeof *item);
253         if (item == NULL) {
254                 ERROR("out of memory");
255                 exit(1);
256         }
257
258         /*
259          * init the item
260          */
261         *head = item;
262         item->value = value;
263         item->next = NULL;
264 }
265
266 /*---------------------------------------------------------
267  |   helpers for argument scanning
268  +--------------------------------------------------------- */
269
270 static const char *optname(int optc)
271 {
272         AFB_options *o = cliOptions;
273         while (o->name && o->val != optc)
274                 o++;
275         return o->name ? : "<unknown-option-name>";
276 }
277
278 static char *argvalstr(int optc)
279 {
280         if (optarg == 0) {
281                 ERROR("option [--%s] needs a value i.e. --%s=xxx",
282                       optname(optc), optname(optc));
283                 exit(1);
284         }
285         return optarg;
286 }
287
288 static int argvalenum(int optc, struct enumdesc *desc)
289 {
290         int i;
291         size_t len;
292         char *list, *name = argvalstr(optc);
293
294         i = 0;
295         while(desc[i].name && strcmp(desc[i].name, name))
296                 i++;
297         if (!desc[i].name) {
298                 len = 0;
299                 i = 0;
300                 while(desc[i].name)
301                         len += strlen(desc[i++].name);
302                 list = malloc(len + i + i);
303                 if (!i || !list)
304                         ERROR("option [--%s] bad value (found %s)",
305                                 optname(optc), name);
306                 else {
307                         i = 0;
308                         strcpy(list, desc[i].name ? : "");
309                         while(desc[++i].name)
310                                 strcat(strcat(list, ", "), desc[i].name);
311                         ERROR("option [--%s] bad value, only accepts values %s (found %s)",
312                                 optname(optc), list, name);
313                 }
314                 free(list);
315                 exit(1);
316         }
317         return desc[i].value;
318 }
319
320 static int argvalint(int optc, int mini, int maxi, int base)
321 {
322         char *beg, *end;
323         long int val;
324         beg = argvalstr(optc);
325         val = strtol(beg, &end, base);
326         if (*end || end == beg) {
327                 ERROR("option [--%s] requires a valid integer (found %s)",
328                         optname(optc), beg);
329                 exit(1);
330         }
331         if (val < (long int)mini || val > (long int)maxi) {
332                 ERROR("option [--%s] value out of bounds (not %d<=%ld<=%d)",
333                         optname(optc), mini, val, maxi);
334                 exit(1);
335         }
336         return (int)val;
337 }
338
339 static int argvalintdec(int optc, int mini, int maxi)
340 {
341         return argvalint(optc, mini, maxi, 10);
342 }
343
344 static void noarg(int optc)
345 {
346         if (optarg != 0) {
347                 ERROR("option [--%s] need no value (found %s)", optname(optc), optarg);
348                 exit(1);
349         }
350 }
351
352 /*---------------------------------------------------------
353  |   Parse option and launch action
354  +--------------------------------------------------------- */
355
356 static void parse_arguments(int argc, char **argv, struct afb_config *config)
357 {
358         char *programName = argv[0];
359         int optc, ind;
360         int nbcmd;
361         struct option *gnuOptions;
362
363         // ------------------ Process Command Line -----------------------
364
365         // build GNU getopt info from cliOptions
366         nbcmd = sizeof(cliOptions) / sizeof(AFB_options);
367         gnuOptions = malloc(sizeof(*gnuOptions) * (unsigned)nbcmd);
368         for (ind = 0; ind < nbcmd; ind++) {
369                 gnuOptions[ind].name = cliOptions[ind].name;
370                 gnuOptions[ind].has_arg = cliOptions[ind].has_arg;
371                 gnuOptions[ind].flag = 0;
372                 gnuOptions[ind].val = cliOptions[ind].val;
373         }
374
375         // get all options from command line
376         while ((optc = getopt_long(argc, argv, SHORTOPTS, gnuOptions, NULL)) != EOF) {
377                 switch (optc) {
378                 case SET_VERBOSE:
379                         verbosity++;
380                         break;
381
382                 case SET_QUIET:
383                         verbosity--;
384                         break;
385
386                 case SET_TCP_PORT:
387                         config->httpdPort = argvalintdec(optc, 1024, 32767);
388                         break;
389
390                 case SET_APITIMEOUT:
391                         config->apiTimeout = argvalintdec(optc, 0, INT_MAX);
392                         break;
393
394                 case SET_CNTXTIMEOUT:
395                         config->cntxTimeout = argvalintdec(optc, 0, INT_MAX);
396                         break;
397
398                 case SET_ROOT_DIR:
399                         config->rootdir = argvalstr(optc);
400                         INFO("Forcing Rootdir=%s", config->rootdir);
401                         break;
402
403                 case SET_ROOT_HTTP:
404                         config->roothttp = argvalstr(optc);
405                         INFO("Forcing Root HTTP=%s", config->roothttp);
406                         break;
407
408                 case SET_ROOT_BASE:
409                         config->rootbase = argvalstr(optc);
410                         INFO("Forcing Rootbase=%s", config->rootbase);
411                         break;
412
413                 case SET_ROOT_API:
414                         config->rootapi = argvalstr(optc);
415                         INFO("Forcing Rootapi=%s", config->rootapi);
416                         break;
417
418                 case SET_ALIAS:
419                         list_add(&config->aliases, argvalstr(optc));
420                         break;
421
422                 case SET_AUTH_TOKEN:
423                         config->token = argvalstr(optc);
424                         break;
425
426                 case SET_LDPATH:
427                         list_add(&config->ldpaths, argvalstr(optc));
428                         break;
429
430                 case SET_SESSION_DIR:
431                         /* config->sessiondir = argvalstr(optc); */
432                         WARNING("Obsolete otpion %s ignored", optname(optc));
433                         break;
434
435                 case SET_UPLOAD_DIR:
436                         config->uploaddir = argvalstr(optc);
437                         break;
438
439                 case SET_WORK_DIR:
440                         config->workdir = argvalstr(optc);
441                         break;
442
443                 case SET_CACHE_TIMEOUT:
444                         config->cacheTimeout = argvalintdec(optc, 0, INT_MAX);
445                         break;
446
447                 case SET_SESSIONMAX:
448                         config->nbSessionMax = argvalintdec(optc, 1, INT_MAX);
449                         break;
450
451                 case SET_FORGROUND:
452                         noarg(optc);
453                         config->background = 0;
454                         break;
455
456                 case SET_BACKGROUND:
457                         noarg(optc);
458                         config->background = 1;
459                         break;
460
461                 case SET_MODE:
462                         config->mode = argvalenum(optc, mode_desc);
463                         break;
464
465                 case DBUS_CLIENT:
466                         list_add(&config->dbus_clients, argvalstr(optc));
467                         break;
468
469                 case DBUS_SERVICE:
470                         list_add(&config->dbus_servers, argvalstr(optc));
471                         break;
472
473                 case WS_CLIENT:
474                         list_add(&config->ws_clients, argvalstr(optc));
475                         break;
476
477                 case WS_SERVICE:
478                         list_add(&config->ws_servers, argvalstr(optc));
479                         break;
480
481                 case SO_BINDING:
482                         list_add(&config->so_bindings, argvalstr(optc));
483                         break;
484
485                 case SET_TRACEREQ:
486                         config->tracereq = argvalenum(optc, tracereq_desc);
487                         break;
488
489                 case SET_NO_HTTPD:
490                         noarg(optc);
491                         config->noHttpd = 1;
492                         break;
493
494                 case SET_EXEC:
495                         config->exec = &argv[optind];
496                         optind = argc;
497                         break;
498
499                 case SET_RNDTOKEN:
500                         config->token = random_token();
501                         break;
502
503                 case DISPLAY_VERSION:
504                         noarg(optc);
505                         printVersion(stdout);
506                         exit(0);
507
508                 case DISPLAY_HELP:
509                         printHelp(stdout, programName);
510                         exit(0);
511
512                 default:
513                         exit(1);
514                 }
515         }
516         free(gnuOptions);
517 }
518
519 // load config from disk and merge with CLI option
520 static void config_set_default(struct afb_config *config)
521 {
522         // default HTTP port
523         if (config->httpdPort == 0)
524                 config->httpdPort = 1234;
525
526         // default binding API timeout
527         if (config->apiTimeout == 0)
528                 config->apiTimeout = DEFLT_API_TIMEOUT;
529
530         // default AUTH_TOKEN
531         if (config->token == NULL)
532                 config->token = DEFLT_AUTH_TOKEN;
533
534         // cache timeout default one hour
535         if (config->cacheTimeout == 0)
536                 config->cacheTimeout = DEFLT_CACHE_TIMEOUT;
537
538         // cache timeout default one hour
539         if (config->cntxTimeout == 0)
540                 config->cntxTimeout = DEFLT_CNTX_TIMEOUT;
541
542         // max count of sessions
543         if (config->nbSessionMax == 0)
544                 config->nbSessionMax = CTX_NBCLIENTS;
545
546         /* set directories */
547         if (config->workdir == NULL)
548                 config->workdir = get_current_dir_name();
549
550         if (config->rootdir == NULL)
551                 config->rootdir = config->workdir;
552
553         if (config->uploaddir == NULL)
554                 config->uploaddir = config->workdir;
555
556         // if no Angular/HTML5 rootbase let's try '/' as default
557         if (config->rootbase == NULL)
558                 config->rootbase = "/opa";
559
560         if (config->rootapi == NULL)
561                 config->rootapi = "/api";
562
563         if (config->ldpaths == NULL)
564                 list_add(&config->ldpaths, BINDING_INSTALL_DIR);
565
566         // if no config dir create a default path from uploaddir
567         if (config->console == NULL) {
568                 config->console = malloc(512);
569                 strncpy(config->console, config->uploaddir, 512);
570                 strncat(config->console, "/AFB-console.out", 512);
571         }
572 }
573
574 void afb_config_dump(struct afb_config *config)
575 {
576         struct afb_config_list *l;
577         struct enumdesc *e;
578         char **v;
579
580 #define NN(x)   (x)?:""
581 #define P(...)  fprintf(stderr, __VA_ARGS__)
582 #define PF(x)   P("-- %15s: ", #x)
583 #define PE      P("\n")
584 #define S(x)    PF(x);P("%s",NN(config->x));PE;
585 #define D(x)    PF(x);P("%d",config->x);PE;
586 #define H(x)    PF(x);P("%x",config->x);PE;
587 #define B(x)    PF(x);P("%s",config->x?"yes":"no");PE;
588 #define L(x)    PF(x);l=config->x;if(l){P("%s\n",NN(l->value));for(l=l->next;l;l=l->next)P("-- %15s  %s\n","",NN(l->value));}else PE;
589 #define E(x,d)  for(e=d;e->name&&e->value!=config->x;e++);if(e->name){PF(x);P("%s",e->name);PE;}else{D(x);}
590 #define V(x)    P("-- %15s:", #x);for(v=config->x;v&&*v;v++)P(" %s",*v); PE;
591
592         P("---BEGIN-OF-CONFIG---\n");
593         S(console)
594         S(rootdir)
595         S(roothttp)
596         S(rootbase)
597         S(rootapi)
598         S(workdir)
599         S(uploaddir)
600         S(token)
601
602         L(aliases)
603         L(dbus_clients)
604         L(dbus_servers)
605         L(ws_clients)
606         L(ws_servers)
607         L(so_bindings)
608         L(ldpaths)
609
610         V(exec)
611
612         D(httpdPort)
613         B(background)
614         D(cacheTimeout)
615         D(apiTimeout)
616         D(cntxTimeout)
617         D(nbSessionMax)
618         E(mode,mode_desc)
619         E(tracereq,tracereq_desc)
620         B(noHttpd)
621         P("---END-OF-CONFIG---\n");
622
623 #undef V
624 #undef E
625 #undef L
626 #undef B
627 #undef H
628 #undef D
629 #undef S
630 #undef PE
631 #undef PF
632 #undef P
633 #undef NN
634 }
635
636 struct afb_config *afb_config_parse_arguments(int argc, char **argv)
637 {
638         struct afb_config *result;
639
640         result = calloc(1, sizeof *result);
641
642         parse_arguments(argc, argv, result);
643         config_set_default(result);
644         if (verbosity >= 3)
645                 afb_config_dump(result);
646         return result;
647 }
648