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 */
58 #define NEED_SERVER 0x00000001
59 #define NEED_CREDS 0x00000002
60 #define NEED_PATH 0x00000004
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
;
76 validate_server (unsigned int i
)
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
);
90 elem
= json_object_get(server
,"name");
91 if (!json_is_string(elem
)) {
92 error(0,0,"config elem %u: missing name",i
);
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
);
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")) {
110 else if (!strcasecmp(type
,"fs")) {
114 error(0,0,"config elem %u (%s): bad type",i
,name
);
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",
125 elem
= json_object_get(server
,"port");
126 if (!json_is_integer(elem
)) {
127 error(0,0,"config elem %u (%s): missing port",
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",
140 elem
= json_object_get(server
,"secret");
141 if (!json_is_string(elem
)) {
142 error(0,0, "config elem %u (%s): missing secret",
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",
161 dup_json_string (json_t
*obj
, char *field
)
165 tmp
= json_string_value(json_object_get(obj
,field
));
174 is_reserved_attr (const char *name
)
176 static const char const *rsvd
[] = {
177 "name", "type", "host", "port", "key", "secret", "path",
180 const char *const *r
;
182 for (r
= rsvd
; *r
; ++r
) {
183 if (!strcasecmp(*r
,name
)) {
192 convert_provider (int i
, provider_t
*out
)
199 server
= json_array_get(config
,i
);
201 DPRINTF("no such entry %d\n",i
);
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
;
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
);
234 key
= json_object_iter_key(iter
);
235 if (!is_reserved_attr(key
)) {
236 value
= json_string_value(json_object_iter_value(iter
));
238 value
= strdup(value
);
241 DPRINTF("%p.%s = %s\n",out
,key
,value
);
242 g_hash_table_insert(out
->attrs
,
243 strdup((char *)key
), (char *)value
);
246 error(0,0,"could not extract %u.%s",i
,key
);
249 iter
= json_object_iter_next(server
,iter
);
258 parse_config_inner (void)
260 unsigned int nservers
;
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");
272 nservers
= json_array_size(config
);
277 for (i
= 0; i
< nservers
; ++i
) {
278 if (!validate_server(i
)) {
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
);
287 error(0,0,"could not allocate provider hash");
290 for (i
= 0; i
< nservers
; ++i
) {
291 server
= json_array_get(config
,i
);
293 error(0,0,"could not get pointer to provider %u",i
);
296 new_key
= dup_json_string(server
,"name");
298 error(0,errno
,"could not copy key %u",i
);
301 new_prov
= (provider_t
*)malloc(sizeof(*new_prov
));
303 error(0,errno
,"could not allocate provider %u",i
);
304 free((char *)new_key
);
307 if (!convert_provider(i
,new_prov
)) {
308 error(0,0,"could not add provider %u",i
);
309 free((char *)new_key
);
312 g_hash_table_insert(prov_hash
,(char *)new_key
,new_prov
);
314 main_prov
= new_prov
;
315 primary
= new_prov
->name
;
317 new_prov
->func_tbl
->init_func(new_prov
);
322 g_hash_table_destroy(prov_hash
);
329 parse_config (char *cfg_file
)
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
340 * TBD: initialize this in a separate module-init function, passing
341 * in master_host and master_port instead of using globals.
344 master_prov
= malloc(sizeof(*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
);
355 config
= json_load_file(cfg_file
,&err
);
357 error(0,0,"JSON error on line %d: %s",err
.line
,err
.text
);
361 primary
= parse_config_inner();
372 static const char auto_json
[] = {
374 " \"name\": \"fs_autostart\",\n"
375 " \"type\": \"fs\",\n"
376 " \"path\": \"" AUTO_DIR_FS
"\"\n"
380 const char *primary
= NULL
;
382 if (auto_start(db_port
) != 0) {
386 config
= json_loads(auto_json
,&err
);
388 fprintf(stderr
,"JSON error on line %d: %s\n",err
.line
,err
.text
);
392 primary
= parse_config_inner();
394 printf("auto-start in effect\n");
397 error(0, 0, "invalid autostart configuration (internal error)");
407 get_provider (const char *name
)
409 if (!prov_hash
|| !name
|| (*name
== '\0')) {
412 return g_hash_table_lookup(prov_hash
,name
);
416 get_provider_value (const provider_t
*prov
, const char *fname
)
418 if (!prov
|| !fname
|| (*fname
== '\0')) {
421 return g_hash_table_lookup(prov
->attrs
,fname
);
425 init_prov_iter (GHashTableIter
*iter
)
427 g_hash_table_iter_init(iter
,prov_hash
);
431 update_provider (const char *provname
, const char *username
,
432 const char *password
)
436 DPRINTF("updating %s username=%s password=%s\n",
437 provname
, username
, password
);
439 prov
= (provider_t
*)get_provider(provname
);
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
);