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/>. */
23 #include <semaphore.h>
39 * A config consists of a JSON array of objects, where each object includes:
42 * type string "s3" or "cf" or "http" (case insensitive)
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
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
;
75 validate_server (unsigned int i
)
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
);
89 elem
= json_object_get(server
,"name");
90 if (!json_is_string(elem
)) {
91 error(0,0,"config elem %u: missing name",i
);
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
);
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")) {
109 else if (!strcasecmp(type
,"fs")) {
113 error(0,0,"config elem %u (%s): bad type",i
,name
);
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",
124 elem
= json_object_get(server
,"port");
125 if (!json_is_integer(elem
)) {
126 error(0,0,"config elem %u (%s): missing port",
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",
139 elem
= json_object_get(server
,"secret");
140 if (!json_is_string(elem
)) {
141 error(0,0, "config elem %u (%s): missing secret",
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",
160 dup_json_string (const json_t
*obj
, const char *field
)
164 tmp
= json_string_value(json_object_get(obj
,field
));
173 is_reserved_attr (const char *name
)
175 static char const *const rsvd
[] = {
176 "name", "type", "host", "port", "key", "secret", "path",
179 const char *const *r
;
181 for (r
= rsvd
; *r
; ++r
) {
182 if (!strcasecmp(*r
,name
)) {
191 convert_provider (int i
, provider_t
*out
)
198 server
= json_array_get(config
,i
);
200 DPRINTF("no such entry %d\n",i
);
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
;
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
);
233 key
= json_object_iter_key(iter
);
234 if (!is_reserved_attr(key
)) {
235 value
= json_string_value(json_object_iter_value(iter
));
237 value
= strdup(value
);
240 DPRINTF("%p.%s = %s\n",out
,key
,value
);
241 g_hash_table_insert(out
->attrs
,
242 strdup((char *)key
), (char *)value
);
245 error(0,0,"could not extract %u.%s",i
,key
);
248 iter
= json_object_iter_next(server
,iter
);
257 parse_config_inner (void)
259 unsigned int nservers
;
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");
271 nservers
= json_array_size(config
);
276 for (i
= 0; i
< nservers
; ++i
) {
277 if (!validate_server(i
)) {
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
);
286 error(0,0,"could not allocate provider hash");
289 for (i
= 0; i
< nservers
; ++i
) {
290 server
= json_array_get(config
,i
);
292 error(0,0,"could not get pointer to provider %u",i
);
295 new_key
= dup_json_string(server
,"name");
297 error(0,errno
,"could not copy key %u",i
);
300 new_prov
= (provider_t
*)malloc(sizeof(*new_prov
));
302 error(0,errno
,"could not allocate provider %u",i
);
303 free((char *)new_key
);
306 if (!convert_provider(i
,new_prov
)) {
307 error(0,0,"could not add provider %u",i
);
308 free((char *)new_key
);
311 g_hash_table_insert(prov_hash
,(char *)new_key
,new_prov
);
313 main_prov
= new_prov
;
314 primary
= new_prov
->name
;
316 new_prov
->func_tbl
->init_func(new_prov
);
321 g_hash_table_destroy(prov_hash
);
328 parse_config (char *cfg_file
)
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
339 * TBD: initialize this in a separate module-init function, passing
340 * in master_host and master_port instead of using globals.
343 master_prov
= malloc(sizeof(*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
);
354 config
= json_load_file(cfg_file
,&err
);
356 error(0,0,"JSON error on line %d: %s",err
.line
,err
.text
);
360 primary
= parse_config_inner();
371 static const char auto_json
[] = {
373 " \"name\": \"fs_autostart\",\n"
374 " \"type\": \"fs\",\n"
375 " \"path\": \"" AUTO_DIR_FS
"\"\n"
379 const char *primary
= NULL
;
381 if (auto_start(db_port
) != 0) {
385 config
= json_loads(auto_json
,&err
);
387 fprintf(stderr
,"JSON error on line %d: %s\n",err
.line
,err
.text
);
391 primary
= parse_config_inner();
393 printf("auto-start in effect\n");
396 error(0, 0, "invalid autostart configuration (internal error)");
406 get_provider (const char *name
)
408 if (!prov_hash
|| !name
|| (*name
== '\0')) {
411 return g_hash_table_lookup(prov_hash
,name
);
415 get_provider_value (const provider_t
*prov
, const char *fname
)
417 if (!prov
|| !fname
|| (*fname
== '\0')) {
420 return g_hash_table_lookup(prov
->attrs
,fname
);
424 init_prov_iter (GHashTableIter
*iter
)
426 g_hash_table_iter_init(iter
,prov_hash
);
430 update_provider (const char *provname
, const char *username
,
431 const char *password
)
435 DPRINTF("updating %s username=%s password=%s\n",
436 provname
, username
, password
);
438 prov
= (provider_t
*)get_provider(provname
);
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
);