build: make the "rpm" rule work once again
[iwhd.git] / setup.c
blob2a03f40e2588bdf1c25287f9f688e25c4dd96746
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_SERVER 0x00000001
58 #define NEED_CREDS 0x00000002
59 #define NEED_PATH 0x00000004
60 #define NEED_ALL ~0
62 extern backend_func_tbl bad_func_tbl;
63 extern backend_func_tbl s3_func_tbl;
64 extern backend_func_tbl curl_func_tbl;
65 extern backend_func_tbl cf_func_tbl;
66 extern backend_func_tbl fs_func_tbl;
68 static json_t *config = NULL;
69 static GHashTable *prov_hash = NULL;
71 provider_t *main_prov = NULL;
72 provider_t *master_prov = NULL;
74 static int
75 validate_server (unsigned int i)
77 json_t *server;
78 json_t *elem;
79 const char *name;
80 const char *type;
81 unsigned int needs = NEED_ALL;
83 server = json_array_get(config,i);
84 if (!json_is_object(server)) {
85 error(0,0,"config elem %u: missing object",i);
86 return 0;
89 elem = json_object_get(server,"name");
90 if (!json_is_string(elem)) {
91 error(0,0,"config elem %u: missing name",i);
92 return 0;
94 name = json_string_value(elem);
96 elem = json_object_get(server,"type");
97 if (!json_is_string(elem)) {
98 error(0,0,"config elem %u (%s): missing type",i,name);
99 return 0;
101 type = json_string_value(elem);
103 if (!strcasecmp(type,"s3") || !strcasecmp(type,"cf")) {
104 needs = NEED_SERVER | NEED_CREDS;
106 else if (!strcasecmp(type,"http")) {
107 needs = NEED_SERVER;
109 else if (!strcasecmp(type,"fs")) {
110 needs = NEED_PATH;
112 else {
113 error(0,0,"config elem %u (%s): bad type",i,name);
114 return 0;
117 if (needs & NEED_SERVER) {
118 elem = json_object_get(server,"host");
119 if (!json_is_string(elem)) {
120 error(0,0,"config elem %u (%s): missing host",
121 i,name);
122 return 0;
124 elem = json_object_get(server,"port");
125 if (!json_is_integer(elem)) {
126 error(0,0,"config elem %u (%s): missing port",
127 i,name);
128 return 0;
132 if (needs & NEED_CREDS) {
133 elem = json_object_get(server,"key");
134 if (!json_is_string(elem)) {
135 error(0,0,"config elem %u (%s): missing key",
136 i, name);
137 return 0;
139 elem = json_object_get(server,"secret");
140 if (!json_is_string(elem)) {
141 error(0,0, "config elem %u (%s): missing secret",
142 i, name);
143 return 0;
147 if (needs & NEED_PATH) {
148 elem = json_object_get(server,"path");
149 if (!json_is_string(elem)) {
150 error(0,0,"config elem %u (%s): missing path",
151 i, name);
152 return 0;
156 return 1;
159 static const char *
160 dup_json_string (const json_t *obj, const char *field)
162 const char *tmp;
164 tmp = json_string_value(json_object_get(obj,field));
165 if (tmp) {
166 tmp = strdup(tmp);
169 return tmp;
172 static int
173 is_reserved_attr (const char *name)
175 static char const *const rsvd[] = {
176 "name", "type", "host", "port", "key", "secret", "path",
177 NULL
179 const char *const *r;
181 for (r = rsvd; *r; ++r) {
182 if (!strcasecmp(*r,name)) {
183 return 1;
187 return 0;
190 static int
191 convert_provider (int i, provider_t *out)
193 json_t *server;
194 void *iter;
195 const char *key;
196 const char *value;
198 server = json_array_get(config,i);
199 if (!server) {
200 DPRINTF("no such entry %d\n",i);
201 return 0;
204 out->name = dup_json_string(server,"name");
205 out->type = dup_json_string(server,"type");
206 out->host = dup_json_string(server,"host");
207 out->port = json_integer_value(json_object_get(server,"port"));
208 /* TBD: change key/secret field names to username/password */
209 out->username = dup_json_string(server,"key");
210 out->password = dup_json_string(server,"secret");
211 out->path = dup_json_string(server,"path");
213 /* TBD: do this a cleaner way. */
214 if (!strcasecmp(out->type,"s3")) {
215 out->func_tbl = &s3_func_tbl;
217 else if (!strcasecmp(out->type,"http")) {
218 out->func_tbl = &curl_func_tbl;
220 else if (!strcasecmp(out->type,"cf")) {
221 out->func_tbl = &cf_func_tbl;
223 else if (!strcasecmp(out->type,"fs")) {
224 out->func_tbl = &fs_func_tbl;
226 else {
227 out->func_tbl = &bad_func_tbl;
230 out->attrs = g_hash_table_new_full(g_str_hash,g_str_equal,free,free);
231 iter = json_object_iter(server);
232 while (iter) {
233 key = json_object_iter_key(iter);
234 if (!is_reserved_attr(key)) {
235 value = json_string_value(json_object_iter_value(iter));
236 if (value) {
237 value = strdup(value);
239 if (value) {
240 DPRINTF("%p.%s = %s\n",out,key,value);
241 g_hash_table_insert(out->attrs,
242 strdup((char *)key), (char *)value);
244 else {
245 error(0,0,"could not extract %u.%s",i,key);
248 iter = json_object_iter_next(server,iter);
251 out->token = NULL;
253 return 1;
256 static const char *
257 parse_config_inner (void)
259 unsigned int nservers;
260 unsigned int i;
261 json_t *server;
262 const char *new_key;
263 provider_t *new_prov;
264 const char *primary = NULL;
266 if (json_typeof(config) != JSON_ARRAY) {
267 error(0,0,"config should be a JSON array");
268 goto err;
271 nservers = json_array_size(config);
272 if (!nservers) {
273 goto err;
276 for (i = 0; i < nservers; ++i) {
277 if (!validate_server(i)) {
278 goto err;
282 /* Everything looks OK. */
283 printf("%u replication servers defined\n",nservers-1);
284 prov_hash = g_hash_table_new_full(g_str_hash,g_str_equal,free,free);
285 if (!prov_hash) {
286 error(0,0,"could not allocate provider hash");
287 goto err;
289 for (i = 0; i < nservers; ++i) {
290 server = json_array_get(config,i);
291 if (!server) {
292 error(0,0,"could not get pointer to provider %u",i);
293 goto err_free_hash;
295 new_key = dup_json_string(server,"name");
296 if (!new_key) {
297 error(0,errno,"could not copy key %u",i);
298 goto err_free_hash;
300 new_prov = (provider_t *)malloc(sizeof(*new_prov));
301 if (!new_prov) {
302 error(0,errno,"could not allocate provider %u",i);
303 free((char *)new_key);
304 goto err_free_hash;
306 if (!convert_provider(i,new_prov)) {
307 error(0,0,"could not add provider %u",i);
308 free((char *)new_key);
309 free(new_prov);
311 g_hash_table_insert(prov_hash,(char *)new_key,new_prov);
312 if (!i) {
313 main_prov = new_prov;
314 primary = new_prov->name;
316 new_prov->func_tbl->init_func(new_prov);
318 return primary;
320 err_free_hash:
321 g_hash_table_destroy(prov_hash);
322 prov_hash = NULL;
323 err:
324 return 0;
327 const char *
328 parse_config (char *cfg_file)
330 json_error_t err;
331 const char *primary = NULL;
334 * The master provider is special. It's not in the provider hash
335 * (so replication code doesn't try to initiate replication to it)
336 * and it's always assumed to be our own protocol (which we access
337 * using CURL).
339 * TBD: initialize this in a separate module-init function, passing
340 * in master_host and master_port instead of using globals.
342 if (!master_prov) {
343 master_prov = malloc(sizeof(*master_prov));
344 if (master_prov) {
345 master_prov->func_tbl = &curl_func_tbl;
349 if (access(cfg_file,R_OK) < 0) {
350 error(0,errno,"failed to open %s for reading", cfg_file);
351 return NULL;
354 config = json_load_file(cfg_file,&err);
355 if (!config) {
356 error(0,0,"JSON error on line %d: %s",err.line,err.text);
357 return NULL;
360 primary = parse_config_inner();
362 json_decref(config);
363 config = NULL;
365 return primary;
368 const char *
369 auto_config(void)
371 static const char auto_json[] = {
372 "[ {\n"
373 " \"name\": \"fs_autostart\",\n"
374 " \"type\": \"fs\",\n"
375 " \"path\": \"" AUTO_DIR_FS "\"\n"
376 "} ]\n",
378 json_error_t err;
379 const char *primary = NULL;
381 if (auto_start(db_port) != 0) {
382 return NULL;
385 config = json_loads(auto_json,&err);
386 if (!config) {
387 fprintf(stderr,"JSON error on line %d: %s\n",err.line,err.text);
388 return NULL;
391 primary = parse_config_inner();
392 if (primary) {
393 printf("auto-start in effect\n");
395 else {
396 error(0, 0, "invalid autostart configuration (internal error)");
399 json_decref(config);
400 config = NULL;
402 return primary;
405 const provider_t *
406 get_provider (const char *name)
408 if (!prov_hash || !name || (*name == '\0')) {
409 return NULL;
411 return g_hash_table_lookup(prov_hash,name);
414 const char *
415 get_provider_value (const provider_t *prov, const char *fname)
417 if (!prov || !fname || (*fname == '\0')) {
418 return NULL;
420 return g_hash_table_lookup(prov->attrs,fname);
423 void
424 init_prov_iter (GHashTableIter *iter)
426 g_hash_table_iter_init(iter,prov_hash);
429 void
430 update_provider (const char *provname, const char *username,
431 const char *password)
433 provider_t *prov;
435 DPRINTF("updating %s username=%s password=%s\n",
436 provname, username, password);
438 prov = (provider_t *)get_provider(provname);
439 if (!prov) {
440 DPRINTF(" could not find provider %s\n",provname);
441 return; /* TBD: return actual HTTP status for user */
444 free((char *)prov->username);
445 prov->username = strdup(username);
446 free((char *)prov->password);
447 prov->password = strdup(password);