use first gnulib module: progname
[iwhd.git] / setup.c
blob2296b151cee5e2d2dc78934ad678ae912951b318
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 <poll.h>
22 #include <pthread.h>
23 #include <semaphore.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <strings.h>
29 #include <unistd.h>
31 #include <jansson.h>
33 #include "iwh.h"
34 #include "setup.h"
35 #include "query.h"
36 #include "meta.h"
39 * A config consists of a JSON array of objects, where each object includes:
41 * name string
42 * type string "s3" or "cf" or "http" (case insensitive)
43 * host string
44 * port integer
45 * key string S3 key or (optional TBD) HTTP user
46 * secret string S3 secret or (optional TBD) HTTP password
48 * The above fields are all used to implement replication once we've decided to
49 * do it. There may be other fields as well, to help us make that decision.
50 * For example, there might be one or more fields to describe geographic
51 * location, an array of supported image-format names, etc. This information
52 * is deliberately left "schema-free" so that users may add whatever fields
53 * they like to both the config and to replication attributes on objects.
56 /* Bitfield for things to check in validate_server */
57 #define NEED_NONE 0
58 #define NEED_SERVER 0x00000001
59 #define NEED_CREDS 0x00000002
60 #define NEED_PATH 0x00000004
61 #define NEED_ALL ~0
63 extern backend_func_tbl bad_func_tbl;
64 extern backend_func_tbl s3_func_tbl;
65 extern backend_func_tbl curl_func_tbl;
66 extern backend_func_tbl cf_func_tbl;
67 extern backend_func_tbl fs_func_tbl;
69 static json_t *config = NULL;
70 static GHashTable *prov_hash = NULL;
72 provider_t *main_prov = NULL;
73 provider_t *master_prov = NULL;
75 static int
76 validate_server (unsigned int i)
78 json_t *server;
79 json_t *elem;
80 const char *name;
81 const char *type;
82 unsigned int needs = NEED_ALL;
84 server = json_array_get(config,i);
85 if (!json_is_object(server)) {
86 error(0,0,"config elem %u: missing object",i);
87 return 0;
90 elem = json_object_get(server,"name");
91 if (!json_is_string(elem)) {
92 error(0,0,"config elem %u: missing name",i);
93 return 0;
95 name = json_string_value(elem);
97 elem = json_object_get(server,"type");
98 if (!json_is_string(elem)) {
99 error(0,0,"config elem %u (%s): missing type",i,name);
100 return 0;
102 type = json_string_value(elem);
104 if (!strcasecmp(type,"s3") || !strcasecmp(type,"cf")) {
105 needs = NEED_SERVER | NEED_CREDS;
107 else if (!strcasecmp(type,"http")) {
108 needs = NEED_SERVER;
110 else if (!strcasecmp(type,"fs")) {
111 needs = NEED_PATH;
113 else {
114 error(0,0,"config elem %u (%s): bad type",i,name);
115 return 0;
118 if (needs & NEED_SERVER) {
119 elem = json_object_get(server,"host");
120 if (!json_is_string(elem)) {
121 error(0,0,"config elem %u (%s): missing host",
122 i,name);
123 return 0;
125 elem = json_object_get(server,"port");
126 if (!json_is_integer(elem)) {
127 error(0,0,"config elem %u (%s): missing port",
128 i,name);
129 return 0;
133 if (needs & NEED_CREDS) {
134 elem = json_object_get(server,"key");
135 if (!json_is_string(elem)) {
136 error(0,0,"config elem %u (%s): missing key",
137 i, name);
138 return 0;
140 elem = json_object_get(server,"secret");
141 if (!json_is_string(elem)) {
142 error(0,0, "config elem %u (%s): missing secret",
143 i, name);
144 return 0;
148 if (needs & NEED_PATH) {
149 elem = json_object_get(server,"path");
150 if (!json_is_string(elem)) {
151 error(0,0,"config elem %u (%s): missing path",
152 i, name);
153 return 0;
157 return 1;
160 static const char *
161 dup_json_string (json_t *obj, char *field)
163 const char *tmp;
165 tmp = json_string_value(json_object_get(obj,field));
166 if (tmp) {
167 tmp = strdup(tmp);
170 return tmp;
173 static int
174 is_reserved_attr (const char *name)
176 static const char const *rsvd[] = {
177 "name", "type", "host", "port", "key", "secret", "path",
178 NULL
180 const char *const *r;
182 for (r = rsvd; *r; ++r) {
183 if (!strcasecmp(*r,name)) {
184 return 1;
188 return 0;
191 static int
192 convert_provider (int i, provider_t *out)
194 json_t *server;
195 void *iter;
196 const char *key;
197 const char *value;
199 server = json_array_get(config,i);
200 if (!server) {
201 DPRINTF("no such entry %d\n",i);
202 return 0;
205 out->name = dup_json_string(server,"name");
206 out->type = dup_json_string(server,"type");
207 out->host = dup_json_string(server,"host");
208 out->port = json_integer_value(json_object_get(server,"port"));
209 /* TBD: change key/secret field names to username/password */
210 out->username = dup_json_string(server,"key");
211 out->password = dup_json_string(server,"secret");
212 out->path = dup_json_string(server,"path");
214 /* TBD: do this a cleaner way. */
215 if (!strcasecmp(out->type,"s3")) {
216 out->func_tbl = &s3_func_tbl;
218 else if (!strcasecmp(out->type,"http")) {
219 out->func_tbl = &curl_func_tbl;
221 else if (!strcasecmp(out->type,"cf")) {
222 out->func_tbl = &cf_func_tbl;
224 else if (!strcasecmp(out->type,"fs")) {
225 out->func_tbl = &fs_func_tbl;
227 else {
228 out->func_tbl = &bad_func_tbl;
231 out->attrs = g_hash_table_new_full(g_str_hash,g_str_equal,free,free);
232 iter = json_object_iter(server);
233 while (iter) {
234 key = json_object_iter_key(iter);
235 if (!is_reserved_attr(key)) {
236 value = json_string_value(json_object_iter_value(iter));
237 if (value) {
238 value = strdup(value);
240 if (value) {
241 DPRINTF("%p.%s = %s\n",out,key,value);
242 g_hash_table_insert(out->attrs,
243 strdup((char *)key), (char *)value);
245 else {
246 error(0,0,"could not extract %u.%s",i,key);
249 iter = json_object_iter_next(server,iter);
252 out->token = NULL;
254 return 1;
257 static const char *
258 parse_config_inner (void)
260 unsigned int nservers;
261 unsigned int i;
262 json_t *server;
263 const char *new_key;
264 provider_t *new_prov;
265 const char *primary = NULL;
267 if (json_typeof(config) != JSON_ARRAY) {
268 error(0,0,"config should be a JSON array");
269 goto err;
272 nservers = json_array_size(config);
273 if (!nservers) {
274 goto err;
277 for (i = 0; i < nservers; ++i) {
278 if (!validate_server(i)) {
279 goto err;
283 /* Everything looks OK. */
284 printf("%u replication servers defined\n",nservers-1);
285 prov_hash = g_hash_table_new_full(g_str_hash,g_str_equal,free,free);
286 if (!prov_hash) {
287 error(0,0,"could not allocate provider hash");
288 goto err;
290 for (i = 0; i < nservers; ++i) {
291 server = json_array_get(config,i);
292 if (!server) {
293 error(0,0,"could not get pointer to provider %u",i);
294 goto err_free_hash;
296 new_key = dup_json_string(server,"name");
297 if (!new_key) {
298 error(0,errno,"could not copy key %u",i);
299 goto err_free_hash;
301 new_prov = (provider_t *)malloc(sizeof(*new_prov));
302 if (!new_prov) {
303 error(0,errno,"could not allocate provider %u",i);
304 free((char *)new_key);
305 goto err_free_hash;
307 if (!convert_provider(i,new_prov)) {
308 error(0,0,"could not add provider %u",i);
309 free((char *)new_key);
310 free(new_prov);
312 g_hash_table_insert(prov_hash,(char *)new_key,new_prov);
313 if (!i) {
314 main_prov = new_prov;
315 primary = new_prov->name;
317 new_prov->func_tbl->init_func(new_prov);
319 return primary;
321 err_free_hash:
322 g_hash_table_destroy(prov_hash);
323 prov_hash = NULL;
324 err:
325 return 0;
328 const char *
329 parse_config (char *cfg_file)
331 json_error_t err;
332 const char *primary = NULL;
335 * The master provider is special. It's not in the provider hash
336 * (so replication code doesn't try to initiate replication to it)
337 * and it's always assumed to be our own protocol (which we access
338 * using CURL).
340 * TBD: initialize this in a separate module-init function, passing
341 * in master_host and master_port instead of using globals.
343 if (!master_prov) {
344 master_prov = malloc(sizeof(*master_prov));
345 if (master_prov) {
346 master_prov->func_tbl = &curl_func_tbl;
350 if (access(cfg_file,R_OK) < 0) {
351 error(0,errno,"failed to open %s for reading", cfg_file);
352 return NULL;
355 config = json_load_file(cfg_file,&err);
356 if (!config) {
357 error(0,0,"JSON error on line %d: %s",err.line,err.text);
358 return NULL;
361 primary = parse_config_inner();
363 json_decref(config);
364 config = NULL;
366 return primary;
369 const char *
370 auto_config(void)
372 static const char auto_json[] = {
373 "[ {\n"
374 " \"name\": \"fs_autostart\",\n"
375 " \"type\": \"fs\",\n"
376 " \"path\": \"" AUTO_DIR_FS "\"\n"
377 "} ]\n",
379 json_error_t err;
380 const char *primary = NULL;
382 if (auto_start(db_port) != 0) {
383 return NULL;
386 config = json_loads(auto_json,&err);
387 if (!config) {
388 fprintf(stderr,"JSON error on line %d: %s\n",err.line,err.text);
389 return NULL;
392 primary = parse_config_inner();
393 if (primary) {
394 printf("auto-start in effect\n");
396 else {
397 error(0, 0, "invalid autostart configuration (internal error)");
400 json_decref(config);
401 config = NULL;
403 return primary;
406 const provider_t *
407 get_provider (const char *name)
409 if (!prov_hash || !name || (*name == '\0')) {
410 return NULL;
412 return g_hash_table_lookup(prov_hash,name);
415 const char *
416 get_provider_value (const provider_t *prov, const char *fname)
418 if (!prov || !fname || (*fname == '\0')) {
419 return NULL;
421 return g_hash_table_lookup(prov->attrs,fname);
424 void
425 init_prov_iter (GHashTableIter *iter)
427 g_hash_table_iter_init(iter,prov_hash);
430 void
431 update_provider (const char *provname, const char *username,
432 const char *password)
434 provider_t *prov;
436 DPRINTF("updating %s username=%s password=%s\n",
437 provname, username, password);
439 prov = (provider_t *)get_provider(provname);
440 if (!prov) {
441 DPRINTF(" could not find provider %s\n",provname);
442 return; /* TBD: return actual HTTP status for user */
445 free((char *)prov->username);
446 prov->username = strdup(username);
447 free((char *)prov->password);
448 prov->password = strdup(password);