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/>. */
24 #include <semaphore.h>
41 * A config consists of a JSON array of objects, where each object includes:
44 * type string "s3" or "cf" or "http" (case insensitive)
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 */
60 #define NEED_SERVER 0x00000001
61 #define NEED_CREDS 0x00000002
62 #define NEED_PATH 0x00000004
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
;
78 validate_server (unsigned int i
)
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
);
92 elem
= json_object_get(server
,"name");
93 if (!json_is_string(elem
)) {
94 error(0,0,"config elem %u: missing name",i
);
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
);
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")) {
112 else if (!strcasecmp(type
,"fs")) {
116 error(0,0,"config elem %u (%s): bad type",i
,name
);
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",
127 elem
= json_object_get(server
,"port");
128 if (!json_is_integer(elem
)) {
129 error(0,0,"config elem %u (%s): missing port",
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",
142 elem
= json_object_get(server
,"secret");
143 if (!json_is_string(elem
)) {
144 error(0,0, "config elem %u (%s): missing secret",
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",
163 dup_json_string (json_t
*obj
, char *field
)
167 tmp
= json_string_value(json_object_get(obj
,field
));
176 is_reserved_attr (const char *name
)
178 static const char const *rsvd
[] = {
179 "name", "type", "host", "port", "key", "secret", "path",
182 const char *const *r
;
184 for (r
= rsvd
; *r
; ++r
) {
185 if (!strcasecmp(*r
,name
)) {
194 convert_provider (int i
, provider_t
*out
)
201 server
= json_array_get(config
,i
);
203 DPRINTF("no such entry %d\n",i
);
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
;
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
);
236 key
= json_object_iter_key(iter
);
237 if (!is_reserved_attr(key
)) {
238 value
= json_string_value(json_object_iter_value(iter
));
240 value
= strdup(value
);
243 DPRINTF("%p.%s = %s\n",out
,key
,value
);
244 g_hash_table_insert(out
->attrs
,
245 strdup((char *)key
), (char *)value
);
248 error(0,0,"could not extract %u.%s",i
,key
);
251 iter
= json_object_iter_next(server
,iter
);
260 parse_config_inner (void)
262 unsigned int nservers
;
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");
274 nservers
= json_array_size(config
);
279 for (i
= 0; i
< nservers
; ++i
) {
280 if (!validate_server(i
)) {
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
);
289 error(0,0,"could not allocate provider hash");
292 for (i
= 0; i
< nservers
; ++i
) {
293 server
= json_array_get(config
,i
);
295 error(0,0,"could not get pointer to provider %u",i
);
298 new_key
= dup_json_string(server
,"name");
300 error(0,errno
,"could not copy key %u",i
);
303 new_prov
= (provider_t
*)malloc(sizeof(*new_prov
));
305 error(0,errno
,"could not allocate provider %u",i
);
306 free((char *)new_key
);
309 if (!convert_provider(i
,new_prov
)) {
310 error(0,0,"could not add provider %u",i
);
311 free((char *)new_key
);
314 g_hash_table_insert(prov_hash
,(char *)new_key
,new_prov
);
316 main_prov
= new_prov
;
317 primary
= new_prov
->name
;
319 new_prov
->func_tbl
->init_func(new_prov
);
324 g_hash_table_destroy(prov_hash
);
331 parse_config (char *cfg_file
)
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
342 * TBD: initialize this in a separate module-init function, passing
343 * in master_host and master_port instead of using globals.
346 master_prov
= malloc(sizeof(*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
);
357 config
= json_load_file(cfg_file
,&err
);
359 error(0,0,"JSON error on line %d: %s",err
.line
,err
.text
);
363 primary
= parse_config_inner();
374 static const char auto_json
[] = {
376 " \"name\": \"fs_autostart\",\n"
377 " \"type\": \"fs\",\n"
378 " \"path\": \"" AUTO_DIR_FS
"\"\n"
382 const char *primary
= NULL
;
384 if (auto_start(db_port
) != 0) {
388 config
= json_loads(auto_json
,&err
);
390 fprintf(stderr
,"JSON error on line %d: %s\n",err
.line
,err
.text
);
394 primary
= parse_config_inner();
396 printf("auto-start in effect\n");
399 error(0, 0, "invalid autostart configuration (internal error)");
409 get_provider (const char *name
)
411 if (!prov_hash
|| !name
|| (*name
== '\0')) {
414 return g_hash_table_lookup(prov_hash
,name
);
418 get_provider_value (const provider_t
*prov
, const char *fname
)
420 if (!prov
|| !fname
|| (*fname
== '\0')) {
423 return g_hash_table_lookup(prov
->attrs
,fname
);
427 init_prov_iter (GHashTableIter
*iter
)
429 g_hash_table_iter_init(iter
,prov_hash
);
433 update_provider (const char *provname
, const char *username
,
434 const char *password
)
438 DPRINTF("updating %s username=%s password=%s\n",
439 provname
, username
, password
);
441 prov
= (provider_t
*)get_provider(provname
);
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
);