qlexer.l: rewrite not to need static var, at_eof.
[iwhd.git] / setup.c
blob9ad13f14810d9dd173ed26f97307d8fd507321c7
1 /* Copyright (C) 2010 Red Hat, Inc.
3 This program is free software: you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation, either version 3 of the License, or
6 (at your option) any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>. */
16 #include <config.h>
18 #include <errno.h>
19 #include <error.h>
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <poll.h>
23 #include <pthread.h>
24 #include <semaphore.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <strings.h>
30 #include <unistd.h>
31 #include <assert.h>
33 #include <jansson.h>
35 #include "iwh.h"
36 #include "setup.h"
37 #include "query.h"
38 #include "meta.h"
41 * A config consists of a JSON array of objects, where each object includes:
43 * name string
44 * type string "s3" or "cf" or "http" (case insensitive)
45 * host string
46 * port integer
47 * key string S3 key or (optional TBD) HTTP user
48 * secret string S3 secret or (optional TBD) HTTP password
50 * The above fields are all used to implement replication once we've decided to
51 * do it. There may be other fields as well, to help us make that decision.
52 * For example, there might be one or more fields to describe geographic
53 * location, an array of supported image-format names, etc. This information
54 * is deliberately left "schema-free" so that users may add whatever fields
55 * they like to both the config and to replication attributes on objects.
58 /* Bitfield for things to check in validate_server */
59 #define NEED_NONE 0
60 #define NEED_SERVER 0x00000001
61 #define NEED_CREDS 0x00000002
62 #define NEED_PATH 0x00000004
63 #define NEED_ALL ~0
65 extern backend_func_tbl bad_func_tbl;
66 extern backend_func_tbl s3_func_tbl;
67 extern backend_func_tbl curl_func_tbl;
68 extern backend_func_tbl cf_func_tbl;
69 extern backend_func_tbl fs_func_tbl;
71 static json_t *config = NULL;
72 static GHashTable *prov_hash = NULL;
74 provider_t *main_prov = NULL;
75 provider_t *master_prov = NULL;
77 static int
78 validate_server (unsigned int i)
80 json_t *server;
81 json_t *elem;
82 const char *name;
83 const char *type;
84 unsigned int needs = NEED_ALL;
86 server = json_array_get(config,i);
87 if (!json_is_object(server)) {
88 error(0,0,"config elem %u: missing object",i);
89 return 0;
92 elem = json_object_get(server,"name");
93 if (!json_is_string(elem)) {
94 error(0,0,"config elem %u: missing name",i);
95 return 0;
97 name = json_string_value(elem);
99 elem = json_object_get(server,"type");
100 if (!json_is_string(elem)) {
101 error(0,0,"config elem %u (%s): missing type",i,name);
102 return 0;
104 type = json_string_value(elem);
106 if (!strcasecmp(type,"s3") || !strcasecmp(type,"cf")) {
107 needs = NEED_SERVER | NEED_CREDS;
109 else if (!strcasecmp(type,"http")) {
110 needs = NEED_SERVER;
112 else if (!strcasecmp(type,"fs")) {
113 needs = NEED_PATH;
115 else {
116 error(0,0,"config elem %u (%s): bad type",i,name);
117 return 0;
120 if (needs & NEED_SERVER) {
121 elem = json_object_get(server,"host");
122 if (!json_is_string(elem)) {
123 error(0,0,"config elem %u (%s): missing host",
124 i,name);
125 return 0;
127 elem = json_object_get(server,"port");
128 if (!json_is_integer(elem)) {
129 error(0,0,"config elem %u (%s): missing port",
130 i,name);
131 return 0;
135 if (needs & NEED_CREDS) {
136 elem = json_object_get(server,"key");
137 if (!json_is_string(elem)) {
138 error(0,0,"config elem %u (%s): missing key",
139 i, name);
140 return 0;
142 elem = json_object_get(server,"secret");
143 if (!json_is_string(elem)) {
144 error(0,0, "config elem %u (%s): missing secret",
145 i, name);
146 return 0;
150 if (needs & NEED_PATH) {
151 elem = json_object_get(server,"path");
152 if (!json_is_string(elem)) {
153 error(0,0,"config elem %u (%s): missing path",
154 i, name);
155 return 0;
159 return 1;
162 static const char *
163 dup_json_string (json_t *obj, char *field)
165 const char *tmp;
167 tmp = json_string_value(json_object_get(obj,field));
168 if (tmp) {
169 tmp = strdup(tmp);
172 return tmp;
175 static int
176 is_reserved_attr (const char *name)
178 static const char const *rsvd[] = {
179 "name", "type", "host", "port", "key", "secret", "path",
180 NULL
182 const char *const *r;
184 for (r = rsvd; *r; ++r) {
185 if (!strcasecmp(*r,name)) {
186 return 1;
190 return 0;
193 static int
194 convert_provider (int i, provider_t *out)
196 json_t *server;
197 void *iter;
198 const char *key;
199 const char *value;
201 server = json_array_get(config,i);
202 if (!server) {
203 DPRINTF("no such entry %d\n",i);
204 return 0;
207 out->name = dup_json_string(server,"name");
208 out->type = dup_json_string(server,"type");
209 out->host = dup_json_string(server,"host");
210 out->port = json_integer_value(json_object_get(server,"port"));
211 /* TBD: change key/secret field names to username/password */
212 out->username = dup_json_string(server,"key");
213 out->password = dup_json_string(server,"secret");
214 out->path = dup_json_string(server,"path");
216 /* TBD: do this a cleaner way. */
217 if (!strcasecmp(out->type,"s3")) {
218 out->func_tbl = &s3_func_tbl;
220 else if (!strcasecmp(out->type,"http")) {
221 out->func_tbl = &curl_func_tbl;
223 else if (!strcasecmp(out->type,"cf")) {
224 out->func_tbl = &cf_func_tbl;
226 else if (!strcasecmp(out->type,"fs")) {
227 out->func_tbl = &fs_func_tbl;
229 else {
230 out->func_tbl = &bad_func_tbl;
233 out->attrs = g_hash_table_new_full(g_str_hash,g_str_equal,free,free);
234 iter = json_object_iter(server);
235 while (iter) {
236 key = json_object_iter_key(iter);
237 if (!is_reserved_attr(key)) {
238 value = json_string_value(json_object_iter_value(iter));
239 if (value) {
240 value = strdup(value);
242 if (value) {
243 DPRINTF("%p.%s = %s\n",out,key,value);
244 g_hash_table_insert(out->attrs,
245 strdup((char *)key), (char *)value);
247 else {
248 error(0,0,"could not extract %u.%s",i,key);
251 iter = json_object_iter_next(server,iter);
254 out->token = NULL;
256 return 1;
259 static const char *
260 parse_config_inner (void)
262 unsigned int nservers;
263 unsigned int i;
264 json_t *server;
265 const char *new_key;
266 provider_t *new_prov;
267 const char *primary = NULL;
269 if (json_typeof(config) != JSON_ARRAY) {
270 error(0,0,"config should be a JSON array");
271 goto err;
274 nservers = json_array_size(config);
275 if (!nservers) {
276 goto err;
279 for (i = 0; i < nservers; ++i) {
280 if (!validate_server(i)) {
281 goto err;
285 /* Everything looks OK. */
286 printf("%u replication servers defined\n",nservers-1);
287 prov_hash = g_hash_table_new_full(g_str_hash,g_str_equal,free,free);
288 if (!prov_hash) {
289 error(0,0,"could not allocate provider hash");
290 goto err;
292 for (i = 0; i < nservers; ++i) {
293 server = json_array_get(config,i);
294 if (!server) {
295 error(0,0,"could not get pointer to provider %u",i);
296 goto err_free_hash;
298 new_key = dup_json_string(server,"name");
299 if (!new_key) {
300 error(0,errno,"could not copy key %u",i);
301 goto err_free_hash;
303 new_prov = (provider_t *)malloc(sizeof(*new_prov));
304 if (!new_prov) {
305 error(0,errno,"could not allocate provider %u",i);
306 free((char *)new_key);
307 goto err_free_hash;
309 if (!convert_provider(i,new_prov)) {
310 error(0,0,"could not add provider %u",i);
311 free((char *)new_key);
312 free(new_prov);
314 g_hash_table_insert(prov_hash,(char *)new_key,new_prov);
315 if (!i) {
316 main_prov = new_prov;
317 primary = new_prov->name;
319 new_prov->func_tbl->init_func(new_prov);
321 return primary;
323 err_free_hash:
324 g_hash_table_destroy(prov_hash);
325 prov_hash = NULL;
326 err:
327 return 0;
330 const char *
331 parse_config (char *cfg_file)
333 json_error_t err;
334 const char *primary = NULL;
337 * The master provider is special. It's not in the provider hash
338 * (so replication code doesn't try to initiate replication to it)
339 * and it's always assumed to be our own protocol (which we access
340 * using CURL).
342 * TBD: initialize this in a separate module-init function, passing
343 * in master_host and master_port instead of using globals.
345 if (!master_prov) {
346 master_prov = malloc(sizeof(*master_prov));
347 if (master_prov) {
348 master_prov->func_tbl = &curl_func_tbl;
352 if (access(cfg_file,R_OK) < 0) {
353 error(0,errno,"failed to open %s for reading", cfg_file);
354 return NULL;
357 config = json_load_file(cfg_file,&err);
358 if (!config) {
359 error(0,0,"JSON error on line %d: %s",err.line,err.text);
360 return NULL;
363 primary = parse_config_inner();
365 json_decref(config);
366 config = NULL;
368 return primary;
371 const char *
372 auto_config(void)
374 static const char auto_json[] = {
375 "[ {\n"
376 " \"name\": \"fs_autostart\",\n"
377 " \"type\": \"fs\",\n"
378 " \"path\": \"" AUTO_DIR_FS "\"\n"
379 "} ]\n",
381 json_error_t err;
382 const char *primary = NULL;
384 if (auto_start(db_port) != 0) {
385 return NULL;
388 config = json_loads(auto_json,&err);
389 if (!config) {
390 fprintf(stderr,"JSON error on line %d: %s\n",err.line,err.text);
391 return NULL;
394 primary = parse_config_inner();
395 if (primary) {
396 printf("auto-start in effect\n");
398 else {
399 error(0, 0, "invalid autostart configuration (internal error)");
402 json_decref(config);
403 config = NULL;
405 return primary;
408 const provider_t *
409 get_provider (const char *name)
411 if (!prov_hash || !name || (*name == '\0')) {
412 return NULL;
414 return g_hash_table_lookup(prov_hash,name);
417 const char *
418 get_provider_value (const provider_t *prov, const char *fname)
420 if (!prov || !fname || (*fname == '\0')) {
421 return NULL;
423 return g_hash_table_lookup(prov->attrs,fname);
426 void
427 init_prov_iter (GHashTableIter *iter)
429 g_hash_table_iter_init(iter,prov_hash);
432 void
433 update_provider (const char *provname, const char *username,
434 const char *password)
436 provider_t *prov;
438 DPRINTF("updating %s username=%s password=%s\n",
439 provname, username, password);
441 prov = (provider_t *)get_provider(provname);
442 if (!prov) {
443 DPRINTF(" could not find provider %s\n",provname);
444 return; /* TBD: return actual HTTP status for user */
447 free((char *)prov->username);
448 prov->username = strdup(username);
449 free((char *)prov->password);
450 prov->password = strdup(password);