Almost working
[src/app-framework-binder.git] / src / config.c
1 /*
2  * Copyright (C) 2015 "IoT.bzh"
3  * Author "Fulup Ar Foll"
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18    References:
19      https://www.gnu.org/software/libmicrohttpd/manual/html_node/index.html#Top
20      http://www-01.ibm.com/support/knowledgecenter/SSB23S_1.1.0.9/com.ibm.ztpf-ztpfdf.doc_put.09/gtpc2/cpp_vsprintf.html?cp=SSB23S_1.1.0.9%2F0-3-8-1-0-16-8
21
22 */
23
24
25 #include "../include/local-def.h"
26 #include <stdarg.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29
30
31 #define AFB_CONFIG_JTYPE "AFB_config"
32
33 PUBLIC  char *ERROR_LABEL[]=ERROR_LABEL_DEF;
34
35 PUBLIC int verbose;
36 STATIC AFB_errorT   AFBerr [AFB_SUCCESS+1];
37 STATIC json_object *jTypeStatic;
38
39 /* ------------------------------------------------------------------------------
40  * Get localtime and return in a string
41  * ------------------------------------------------------------------------------ */
42
43 PUBLIC char * configTime (void) {
44   static char reqTime [26];
45   time_t tt;
46   struct tm *rt;
47
48   /* Get actual Date and Time */
49   time (&tt);
50   rt = localtime (&tt);
51
52   strftime (reqTime, sizeof (reqTime), "(%d-%b %H:%M)",rt);
53
54   // return pointer on static data
55   return (reqTime);
56 }
57
58 // load config from disk and merge with CLI option
59 PUBLIC AFB_error configLoadFile (AFB_session * session, AFB_config *cliconfig) {
60    static char cacheTimeout [10];
61    int fd;
62    json_object * AFBConfig, *value;
63    
64    // default HTTP port
65    if (cliconfig->httpdPort == 0) session->config->httpdPort=1234;
66    else session->config->httpdPort=cliconfig->httpdPort;
67
68    // cache timeout default one hour
69    if (cliconfig->cacheTimeout == 0) session->config->cacheTimeout=3600;
70    else session->config->cacheTimeout=cliconfig->cacheTimeout;
71
72    if (cliconfig->rootdir == NULL) {
73        session->config->rootdir = getenv("AFBDIR");
74        if (session->config->rootdir == NULL) {
75            session->config->rootdir = malloc (512);
76            strncpy  (session->config->rootdir, getenv("HOME"),512);
77            strncat (session->config->rootdir, "/.AFB",512);
78        }
79        // if directory does not exist createit
80        mkdir (session->config->rootdir,  O_RDWR | S_IRWXU | S_IRGRP);
81    } else {
82        session->config->rootdir =  cliconfig->rootdir;
83    }
84    
85    // if no Angular/HTML5 rootbase let's try '/' as default
86    if  (cliconfig->rootbase == NULL) {
87        session->config->rootbase = "/opa";
88    } else {
89        session->config->rootbase= cliconfig->rootbase;
90    }
91    
92    if  (cliconfig->rootapi == NULL) {
93        session->config->rootapi = "/api";
94    } else {
95        session->config->rootapi= cliconfig->rootapi;
96    }
97
98    if  (cliconfig->smack == NULL) {
99        session->config->smack = "demo";
100    } else {
101        session->config->smack= cliconfig->smack;
102    }
103
104    if  (cliconfig->smack == NULL) {
105        session->config->plugins = "all";
106    } else {
107        session->config->plugins= cliconfig->plugins;
108    }
109
110
111
112    // if no session dir create a default path from rootdir
113    if  (cliconfig->sessiondir == NULL) {
114        session->config->sessiondir = malloc (512);
115        strncpy (session->config->sessiondir, session->config->rootdir, 512);
116        strncat (session->config->sessiondir, "/sessions",512);
117    } else {
118        session->config->sessiondir = cliconfig->sessiondir;
119    }
120
121    // if no config dir create a default path from sessiondir
122    if  (cliconfig->configfile == NULL) {
123        session->config->configfile = malloc (512);
124        strncpy (session->config->configfile, session->config->sessiondir, 512);
125        strncat (session->config->configfile, "/AFB-config.json",512);
126    } else {
127        session->config->configfile = cliconfig->configfile;
128    }
129
130    // if no config dir create a default path from sessiondir
131    if  (cliconfig->pidfile == NULL) {
132        session->config->pidfile = malloc (512);
133        strncpy (session->config->pidfile, session->config->sessiondir, 512);
134        strncat (session->config->pidfile, "/AFB-process.pid",512);
135    } else {
136        session->config->pidfile= cliconfig->pidfile;
137    }
138
139    // if no config dir create a default path from sessiondir
140    if  (cliconfig->console == NULL) {
141        session->config->console = malloc (512);
142        strncpy (session->config->console, session->config->sessiondir, 512);
143        strncat (session->config->console, "/AFB-console.out",512);
144    } else {
145        session->config->console= cliconfig->console;
146    }
147    
148    // just upload json object and return without any further processing
149    if((fd = open(session->config->configfile, O_RDONLY)) < 0) {
150       if (verbose) fprintf (stderr, "AFB:notice: config at %s: %s\n", session->config->configfile, strerror(errno));
151       return AFB_EMPTY;
152    }
153
154    // openjson from FD is not public API we need to reopen it !!!
155    close(fd);
156    AFBConfig = json_object_from_file (session->config->configfile);
157
158    // check it is an AFB_config
159    if (json_object_object_get_ex (AFBConfig, "jtype", &value)) {
160       if (strcmp (AFB_CONFIG_JTYPE, json_object_get_string (value))) {
161          fprintf (stderr,"AFB: Error file [%s] is not a valid [%s] type\n ", session->config->configfile, AFB_CONFIG_JTYPE);
162          return AFB_FAIL;
163       }
164    }
165
166    if (!cliconfig->rootdir && json_object_object_get_ex (AFBConfig, "rootdir", &value)) {
167       session->config->rootdir =  strdup (json_object_get_string (value));
168    }
169    
170    if (!cliconfig->rootbase && json_object_object_get_ex (AFBConfig, "rootbase", &value)) {
171       session->config->rootbase =  strdup (json_object_get_string (value));
172    }
173    
174    if (!cliconfig->rootapi && json_object_object_get_ex (AFBConfig, "rootapi", &value)) {
175       session->config->rootapi =  strdup (json_object_get_string (value));
176    }
177    
178    if (!cliconfig->smack && json_object_object_get_ex (AFBConfig, "smack", &value)) {
179       session->config->smack =  strdup (json_object_get_string (value));
180    }
181    
182    if (!cliconfig->plugins && json_object_object_get_ex (AFBConfig, "plugins", &value)) {
183       session->config->plugins =  strdup (json_object_get_string (value));
184    }
185
186    if (!cliconfig->sessiondir && json_object_object_get_ex (AFBConfig, "sessiondir", &value)) {
187       session->config->sessiondir = strdup (json_object_get_string (value));
188    }
189    
190    if (!cliconfig->pidfile && json_object_object_get_ex (AFBConfig, "pidfile", &value)) {
191       session->config->pidfile = strdup (json_object_get_string (value));
192    }
193
194    if (!cliconfig->httpdPort && json_object_object_get_ex (AFBConfig, "httpdPort", &value)) {
195       session->config->httpdPort = json_object_get_int (value);
196    }
197    
198    if (!cliconfig->setuid && json_object_object_get_ex (AFBConfig, "setuid", &value)) {
199       session->config->setuid = json_object_get_int (value);
200    }
201
202    if (!cliconfig->localhostOnly && json_object_object_get_ex (AFBConfig, "localhostonly", &value)) {
203       session->config->localhostOnly = json_object_get_int (value);
204    }
205    
206    if (!cliconfig->cacheTimeout && json_object_object_get_ex (AFBConfig, "cachetimeout", &value)) {
207       session->config->cacheTimeout = json_object_get_int (value);
208    }
209    // cacheTimeout is an interger but HTTPd wants it as a string
210    snprintf (cacheTimeout, sizeof (cacheTimeout),"%d", session->config->cacheTimeout);
211    session->cacheTimeout = cacheTimeout; // httpd uses cacheTimeout string version
212    json_object_put   (AFBConfig);    // decrease reference count to free the json object
213
214  
215    
216    return AFB_SUCCESS;
217 }
218
219 // Save the config on disk
220 PUBLIC void configStoreFile (AFB_session * session) {
221    json_object * AFBConfig;
222    time_t rawtime;
223    struct tm * timeinfo;
224    int err;
225
226    AFBConfig =  json_object_new_object();
227
228    // add a timestamp and store session on disk
229    time ( &rawtime );  timeinfo = localtime ( &rawtime );
230    // A copy of the string is made and the memory is managed by the json_object
231    json_object_object_add (AFBConfig, "jtype"         , json_object_new_string (AFB_CONFIG_JTYPE));
232    json_object_object_add (AFBConfig, "timestamp"     , json_object_new_string (asctime (timeinfo)));
233    json_object_object_add (AFBConfig, "rootdir"       , json_object_new_string (session->config->rootdir));
234    json_object_object_add (AFBConfig, "rootapi"       , json_object_new_string (session->config->rootapi));
235    json_object_object_add (AFBConfig, "rootbase"      , json_object_new_string (session->config->rootbase));
236    json_object_object_add (AFBConfig, "smack"         , json_object_new_string (session->config->smack));
237    json_object_object_add (AFBConfig, "plugins"       , json_object_new_string (session->config->plugins));
238    json_object_object_add (AFBConfig, "sessiondir"    , json_object_new_string (session->config->sessiondir));
239    json_object_object_add (AFBConfig, "pidfile"       , json_object_new_string (session->config->pidfile));
240    json_object_object_add (AFBConfig, "httpdPort"     , json_object_new_int (session->config->httpdPort));
241    json_object_object_add (AFBConfig, "setuid"        , json_object_new_int (session->config->setuid));
242    json_object_object_add (AFBConfig, "localhostonly" , json_object_new_int (session->config->localhostOnly));
243    json_object_object_add (AFBConfig, "cachetimeout"  , json_object_new_int (session->config->cacheTimeout));
244
245    err = json_object_to_file (session->config->configfile, AFBConfig);
246    json_object_put   (AFBConfig);    // decrease reference count to free the json object
247    if (err < 0) {
248       fprintf(stderr, "AFB: Fail to save config on disk [%s]\n ", session->config->configfile);
249    }
250 }
251
252
253 PUBLIC AFB_session *configInit () {
254
255   AFB_session *session;
256   AFB_config  *config;
257   int idx, verbosesav;
258
259
260   session = malloc (sizeof (AFB_session));
261   memset (session,0, sizeof (AFB_session));
262
263   // create config handle
264   config = malloc (sizeof (AFB_config));
265   memset (config,0, sizeof (AFB_config));
266
267   // stack config handle into session
268   session->config = config;
269
270   jTypeStatic = json_object_new_string ("AFB_message");
271
272   // initialise JSON constant messages and increase reference count to make them permanent
273   verbosesav = verbose;
274   verbose = 0;  // run initialisation in silent mode
275
276
277   for (idx = 0; idx <= AFB_SUCCESS; idx++) {
278      AFBerr[idx].level = idx;
279      AFBerr[idx].label = ERROR_LABEL [idx];
280      AFBerr[idx].json  = jsonNewMessage (idx, NULL);
281   }
282   verbose = verbosesav;
283   
284   // Load Plugins
285   initPlugins (session);
286   
287   return (session);
288 }
289
290
291 // get JSON object from error level and increase its reference count
292 PUBLIC json_object *jsonNewStatus (AFB_error level) {
293
294   json_object *target =  AFBerr[level].json;
295   json_object_get (target);
296
297   return (target);
298 }
299
300 // get AFB object type with adequate usage count
301 PUBLIC json_object *jsonNewjtype (void) {
302   json_object_get (jTypeStatic); // increase reference count
303   return (jTypeStatic);
304 }
305
306 // build an ERROR message and return it as a valid json object
307 PUBLIC  json_object *jsonNewMessage (AFB_error level, char* format, ...) {
308    static int count = 0;
309    json_object * AFBResponse;
310    va_list args;
311    char message [512];
312
313    // format message
314    if (format != NULL) {
315        va_start(args, format);
316        vsnprintf (message, sizeof (message), format, args);
317        va_end(args);
318    }
319
320    AFBResponse = json_object_new_object();
321    json_object_object_add (AFBResponse, "jtype", jsonNewjtype ());
322    json_object_object_add (AFBResponse, "status" , json_object_new_string (ERROR_LABEL[level]));
323    if (format != NULL) {
324         json_object_object_add (AFBResponse, "info"   , json_object_new_string (message));
325    }
326    if (verbose) {
327         fprintf (stderr, "AFB:%-6s [%3d]: ", AFBerr [level].label, count++);
328         if (format != NULL) {
329             fprintf (stderr, "%s", message);
330         } else {
331             fprintf (stderr, "No Message");
332         }
333         fprintf (stderr, "\n");
334    }
335
336    return (AFBResponse);
337 }
338
339 // Dump a message on stderr
340 PUBLIC void jsonDumpObject (json_object * jObject) {
341
342    if (verbose) {
343         fprintf (stderr, "AFB:dump [%s]\n", json_object_to_json_string(jObject));
344    }
345 }
346