1 /* Copyright (C) 2010 Free Software Foundation, 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>
33 #include <microhttpd.h>
34 #include <curl/curl.h>
44 * A config consists of a JSON array of objects, where each object includes:
47 * type string "s3" or "cf" or "http" (case insensitive)
50 * key string S3 key or (optional TBD) HTTP user
51 * secret string S3 secret or (optional TBD) HTTP password
53 * The above fields are all used to implement replication once we've decided to
54 * do it. There may be other fields as well, to help us make that decision.
55 * For example, there might be one or more fields to describe geographic
56 * location, an array of supported image-format names, etc. This information
57 * is deliberately left "schema-free" so that users may add whatever fields
58 * they like to both the config and to replication attributes on objects.
61 /* Sizes for internal string buffers. */
62 #define ADDR_SIZE 1024
63 #define SVC_ACC_SIZE 128
64 #define HEADER_SIZE 64
66 /* Bitfield for things to check in validate_server */
68 #define NEED_SERVER 0x00000001
69 #define NEED_CREDS 0x00000002
70 #define NEED_PATH 0x00000004
73 extern backend_func_tbl bad_func_tbl
;
74 extern backend_func_tbl s3_func_tbl
;
75 extern backend_func_tbl curl_func_tbl
;
76 extern backend_func_tbl fs_func_tbl
;
79 REPL_PUT
, /* store an object */
80 REPL_ODELETE
, /* delete an object */
81 REPL_BCREATE
, /* create a bucket */
82 /* TBD: bucket deletion, others? */
85 typedef struct _repl_item
{
86 struct _repl_item
*next
;
100 repl_item
*queue_head
= NULL
;
101 repl_item
*queue_tail
= NULL
;
102 pthread_mutex_t queue_lock
;
104 json_t
*config
= NULL
;
107 validate_server (unsigned int i
)
113 unsigned int needs
= NEED_ALL
;
115 server
= json_array_get(config
,i
);
116 if (!json_is_object(server
)) {
117 error(0,0,"config elem %u: missing object",i
);
121 elem
= json_object_get(server
,"name");
122 if (!json_is_string(elem
)) {
123 error(0,0,"config elem %u: missing name",i
);
126 name
= json_string_value(elem
);
128 elem
= json_object_get(server
,"type");
129 if (!json_is_string(elem
)) {
130 error(0,0,"config elem %u (%s): missing type",i
,name
);
133 type
= json_string_value(elem
);
135 if (!strcasecmp(type
,"s3") || !strcasecmp(type
,"cf")) {
136 needs
= NEED_SERVER
| NEED_CREDS
;
138 else if (!strcasecmp(type
,"http")) {
141 else if (!strcasecmp(type
,"fs")) {
145 error(0,0,"config elem %u (%s): bad type",i
,name
);
149 if (needs
& NEED_SERVER
) {
150 elem
= json_object_get(server
,"host");
151 if (!json_is_string(elem
)) {
152 error(0,0,"config elem %u (%s): missing host",
156 elem
= json_object_get(server
,"port");
157 if (!json_is_integer(elem
)) {
158 error(0,0,"config elem %u (%s): missing port",
164 if (needs
& NEED_CREDS
) {
165 elem
= json_object_get(server
,"key");
166 if (!json_is_string(elem
)) {
167 error(0,0,"config elem %u (%s): missing key",
171 elem
= json_object_get(server
,"secret");
172 if (!json_is_string(elem
)) {
173 error(0,0, "config elem %u (%s): missing secret",
179 if (needs
& NEED_PATH
) {
180 elem
= json_object_get(server
,"path");
181 if (!json_is_string(elem
)) {
182 error(0,0,"config elem %u (%s): missing path",
191 /* We've already validated, so minimal checking here. */
198 server
= json_array_get(config
,0);
199 type
= json_string_value(json_object_get(server
,"type"));
200 if (strcasecmp(type
,"fs")) {
201 proxy_host
= json_string_value(
202 json_object_get(server
,"host"));
203 proxy_port
= json_integer_value(
204 json_object_get(server
,"port"));
205 if (!strcasecmp(type
,"s3")) {
207 proxy_key
= json_string_value(
208 json_object_get(server
,"key"));
209 proxy_secret
= json_string_value(
210 json_object_get(server
,"secret"));
217 local_path
= json_string_value(
218 json_object_get(server
,"path"));
221 return (char *)json_string_value(json_object_get(server
,"name"));
228 unsigned int nservers
;
231 if (access(cfg_file
,R_OK
) < 0) {
232 error(0,errno
,"failed to open %s for reading", cfg_file
);
236 config
= json_load_file(cfg_file
,&err
);
238 error(0,0,"JSON error on line %d: %s",err
.line
,err
.text
);
242 if (json_typeof(config
) != JSON_ARRAY
) {
243 error(0,0,"config should be a JSON array");
247 nservers
= json_array_size(config
);
252 for (i
= 0; i
< nservers
; ++i
) {
253 if (!validate_server(i
)) {
258 /* Everything looks OK. */
259 printf("%u replication servers defined\n",nservers
-1);
269 junk_writer (void *ptr
, size_t size
, size_t nmemb
, void *stream
)
273 n
= fwrite(ptr
,size
,nmemb
,stream
);
275 DPRINTF("in %s(%zu,%zu) => %zu\n",__func__
,size
,nmemb
,n
);
281 proxy_repl_prod_fs (void *ctx
)
283 repl_item
*item
= ctx
;
291 DPRINTF("replicating from %s (FS)\n",item
->path
);
293 ifd
= open(item
->path
,O_RDONLY
);
295 error(0,errno
,"ifd open");
296 return THREAD_FAILED
;
298 ofd
= item
->pipes
[1];
301 ibytes
= read(ifd
,buf
,sizeof(buf
));
304 error(0,errno
,"%s: read failed", item
->path
);
307 DPRINTF("EOF on ifd\n");
313 obytes
= write(ofd
,buf
+offset
,ibytes
);
316 error(0,errno
,"ofd write");
319 DPRINTF("zero-length write on ofd\n");
325 } while (ibytes
> 0);
331 DPRINTF("%s returning\n",__func__
);
332 close(item
->pipes
[1]);
337 proxy_repl_prod (void *ctx
)
339 repl_item
*item
= ctx
;
340 FILE *fp
= fdopen(item
->pipes
[1],"w");
341 char addr
[ADDR_SIZE
];
343 char svc_acc
[SVC_ACC_SIZE
];
344 struct hstor_client
*hstor
;
352 error(0, errno
, "%s: fdopen failed", __func__
);
356 chars
= snprintf(addr
,ADDR_SIZE
,
357 "http://%s:%u/%s",proxy_host
,proxy_port
,item
->path
);
358 if (chars
>= ADDR_SIZE
) {
359 error(0,0,"path too long in %s",__func__
);
362 DPRINTF("replicating from %s\n",addr
);
365 chars
= snprintf(svc_acc
,SVC_ACC_SIZE
,"%s:%u",
366 proxy_host
,proxy_port
);
367 if (chars
>= SVC_ACC_SIZE
) {
368 error(0,0,"svc_acc too long in %s",__func__
);
371 hstor
= hstor_new(svc_acc
,proxy_host
,
372 proxy_key
,proxy_secret
);
373 /* Blech. Can't conflict with consumer, though. */
374 myurl
= strdup(item
->path
);
376 bucket
= strtok_r(myurl
,"/",&stctx
);
377 key
= strtok_r(NULL
,"/",&stctx
);
378 hstor_get(hstor
,bucket
,key
,
384 curl
= curl_easy_init();
385 curl_easy_setopt(curl
,CURLOPT_URL
,addr
);
386 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,junk_writer
);
387 curl_easy_setopt(curl
,CURLOPT_WRITEDATA
,fp
);
388 DPRINTF("%s calling curl_easy_perform\n",__func__
);
389 curl_easy_perform(curl
);
390 curl_easy_cleanup(curl
);
394 DPRINTF("%s returning\n",__func__
);
395 /* Closing should signal to the consumer that we're finished. */
401 junk_reader (void *ptr
, size_t size
, size_t nmemb
, void *stream
)
405 n
= fread(ptr
,size
,nmemb
,stream
);
406 printf("in %s(%zu,%zu) => %zu\n",__func__
,size
,nmemb
,n
);
411 cf_writer (void *ptr ATTRIBUTE_UNUSED
, size_t size
, size_t nmemb
,
412 void *stream ATTRIBUTE_UNUSED
)
418 cf_header (void *ptr
, size_t size
, size_t nmemb
, void *stream
)
422 json_t
*server
= (json_t
*)stream
;
424 next
= strtok_r(ptr
,":",&sctx
);
426 if (!strcasecmp(next
,"X-Storage-Url")) {
427 next
= strtok_r(NULL
," \n\r",&sctx
);
429 DPRINTF("got CF URL %s\n",next
);
430 /* NB: after this, original "host" is gone. */
431 json_object_set_new(server
,"host",
435 else if (!strcasecmp(next
,"X-Storage-Token")) {
436 next
= strtok_r(NULL
," \n\r",&sctx
);
438 DPRINTF("got CF token %s\n",next
);
439 json_object_set_new(server
,"token",
448 get_cloudfiles_token (json_t
*server
, const char *host
, unsigned int port
,
449 const char * user
, const char * key
)
452 char addr
[ADDR_SIZE
];
453 char auth_user
[HEADER_SIZE
];
454 char auth_key
[HEADER_SIZE
];
456 struct curl_slist
*slist
;
459 token_obj
= json_object_get(server
,"token");
461 return json_string_value(token_obj
);
464 chars
= snprintf(addr
,ADDR_SIZE
,"https://%s:%u/v1.0",host
,port
);
465 if (chars
>= ADDR_SIZE
) {
466 error(0,0,"API URL too long in %s",__func__
);
470 chars
= snprintf(auth_user
,HEADER_SIZE
,"X-Auth-User: %s",user
);
471 if (chars
>= HEADER_SIZE
) {
472 error(0,0,"auth_user too long in %s",__func__
);
476 chars
= snprintf(auth_key
,HEADER_SIZE
,"X-Auth-Key: %s",key
);
477 if (chars
>= HEADER_SIZE
) {
478 error(0,0,"auth_key too long in %s",__func__
);
482 curl
= curl_easy_init();
483 curl_easy_setopt(curl
,CURLOPT_URL
,addr
);
484 curl_easy_setopt(curl
,CURLOPT_WRITEFUNCTION
,cf_writer
);
485 curl_easy_setopt(curl
,CURLOPT_HEADERFUNCTION
,cf_header
);
486 curl_easy_setopt(curl
,CURLOPT_WRITEHEADER
,server
);
487 slist
= curl_slist_append(NULL
,auth_user
);
488 slist
= curl_slist_append(slist
,auth_key
);
489 curl_easy_setopt(curl
,CURLOPT_HTTPHEADER
,slist
);
490 curl_easy_perform(curl
);
491 curl_easy_cleanup(curl
);
492 curl_slist_free_all(slist
);
494 token_obj
= json_object_get(server
,"token");
495 return token_obj
? json_string_value(token_obj
) : NULL
;
499 proxy_repl_cons (void *ctx
)
501 repl_item
*item
= ctx
;
502 FILE *fp
= fdopen(item
->pipes
[0],"r");
503 char addr
[ADDR_SIZE
];
506 char svc_acc
[SVC_ACC_SIZE
];
507 char auth_hdr
[HEADER_SIZE
];
508 struct hstor_client
*hstor
;
515 const char *s_secret
;
518 struct curl_slist
*slist
;
523 error(0, errno
, "%s: fdopen failed", __func__
);
524 return THREAD_FAILED
;
527 server
= json_array_get(config
,item
->server
);
528 s_host
= json_string_value(json_object_get(server
,"host"));
529 s_port
= json_integer_value(json_object_get(server
,"port"));
530 s_key
= json_string_value(json_object_get(server
,"key"));
531 s_secret
= json_string_value(json_object_get(server
,"secret"));
532 s_type
= json_string_value(json_object_get(server
,"type"));
533 s_name
= json_string_value(json_object_get(server
,"name"));
535 myurl
= strdup(item
->path
);
537 bucket
= strtok_r(myurl
,"/",&stctx
);
538 key
= strtok_r(NULL
,"/",&stctx
);
540 if (!strcasecmp(s_type
,"s3")) {
541 DPRINTF("replicating %zu to %s/%s (S3)\n",item
->size
,s_host
,
543 chars
= snprintf(svc_acc
,SVC_ACC_SIZE
,"%s:%u",s_host
,s_port
);
544 if (chars
>= SVC_ACC_SIZE
) {
545 error(0,0,"svc_acc too long in %s",__func__
);
546 return THREAD_FAILED
;
548 hstor
= hstor_new(svc_acc
,s_host
,s_key
,s_secret
);
549 /* Blech. Can't conflict with producer, though. */
550 hstor_put(hstor
,bucket
,key
,
551 junk_reader
,item
->size
,fp
,NULL
);
555 const char *token_str
= NULL
;
556 if (!strcasecmp(s_type
,"cf")) {
557 token_str
= get_cloudfiles_token(server
,s_host
,s_port
,
560 DPRINTF("could not get CF token\n");
561 return THREAD_FAILED
;
563 /* Re-fetch as this might have changed. */
564 s_host
= json_string_value(json_object_get(server
,
566 chars
= snprintf(addr
,ADDR_SIZE
,"%s/%s",
568 if (chars
>= ADDR_SIZE
) {
569 error(0,0,"CF path too long in %s",__func__
);
570 return THREAD_FAILED
;
572 DPRINTF("replicating %zu to %s (CF)\n",item
->size
,
576 chars
= snprintf(addr
,ADDR_SIZE
,"http://%s:%u/%s",
577 s_host
,s_port
,item
->path
);
578 if (chars
>= ADDR_SIZE
) {
579 error(0,0,"HTTP path too long in %s",
581 return THREAD_FAILED
;
583 DPRINTF("replicating %zu to %s (repod)\n",item
->size
,
586 curl
= curl_easy_init();
587 curl_easy_setopt(curl
,CURLOPT_URL
,addr
);
588 curl_easy_setopt(curl
,CURLOPT_UPLOAD
,1);
589 curl_easy_setopt(curl
,CURLOPT_INFILESIZE_LARGE
,
590 (curl_off_t
)item
->size
);
591 curl_easy_setopt(curl
,CURLOPT_READFUNCTION
,junk_reader
);
592 if (!strcasecmp(s_type
,"cf")) {
593 chars
= snprintf(auth_hdr
,HEADER_SIZE
,
594 "X-Auth-Token: %s",token_str
);
595 if (chars
>= HEADER_SIZE
) {
596 error(0,0,"auth_token too long in %s",
598 return THREAD_FAILED
;
600 slist
= curl_slist_append(NULL
,auth_hdr
);
602 * Rackspace doesn't clearly document that you'll get
603 * 412 (Precondition Failed) if you omit this.
605 slist
= curl_slist_append(slist
,
606 "Content-Type: binary/octet-stream");
609 slist
= curl_slist_append(NULL
,"X-redhat-role: master");
611 curl_easy_setopt(curl
,CURLOPT_HTTPHEADER
,slist
);
612 curl_easy_setopt(curl
,CURLOPT_READDATA
,fp
);
613 DPRINTF("%s calling curl_easy_perform\n",__func__
);
614 curl_easy_perform(curl
);
615 curl_easy_cleanup(curl
);
616 curl_slist_free_all(slist
);
619 DPRINTF("%s returning\n",__func__
);
621 meta_got_copy(bucket
,key
,s_name
);
627 repl_worker_del (const repl_item
*item
)
633 const char *s_secret
;
635 char svc_acc
[SVC_ACC_SIZE
];
636 struct hstor_client
*hstor
;
637 char addr
[ADDR_SIZE
];
644 server
= json_array_get(config
,item
->server
);
645 s_host
= json_string_value(json_object_get(server
,"host"));
646 s_port
= json_integer_value(json_object_get(server
,"port"));
647 s_key
= json_string_value(json_object_get(server
,"key"));
648 s_secret
= json_string_value(json_object_get(server
,"secret"));
649 s_type
= json_string_value(json_object_get(server
,"type"));
651 if (!strcasecmp(s_type
,"s3")) {
652 DPRINTF("%s replicating delete of %s on %s:%u (S3)\n",__func__
,
653 item
->path
, s_host
, s_port
);
654 chars
= snprintf(svc_acc
,SVC_ACC_SIZE
,"%s:%u",s_host
,s_port
);
655 if (chars
>= SVC_ACC_SIZE
) {
656 error(0,0,"svc_acc too long in %s",__func__
);
659 /* TBD: check return */
660 hstor
= hstor_new(svc_acc
,s_host
,s_key
,s_secret
);
662 bucket
= strtok_r(item
->path
,"/",&stctx
);
663 key
= strtok_r(NULL
,"/",&stctx
);
664 (void)hstor_del(hstor
,bucket
,key
);
668 DPRINTF("%s replicating delete of %s on %s:%u (HTTP)\n",
669 __func__
, item
->path
, s_host
, s_port
);
670 chars
= snprintf(addr
,ADDR_SIZE
,"http://%s:%d%s",
671 s_host
,s_port
,item
->path
);
672 if (chars
>= ADDR_SIZE
) {
673 error(0,0,"path too long in %s",__func__
);
676 curl
= curl_easy_init();
677 curl_easy_setopt(curl
,CURLOPT_URL
,addr
);
678 curl_easy_setopt(curl
,CURLOPT_CUSTOMREQUEST
,"DELETE");
679 curl_easy_perform(curl
);
680 curl_easy_cleanup(curl
);
683 DPRINTF("%s returning\n",__func__
);
687 repl_worker_bcreate (repl_item
*item
)
693 const char *s_secret
;
695 char svc_acc
[SVC_ACC_SIZE
];
696 struct hstor_client
*hstor
;
697 char addr
[ADDR_SIZE
];
701 server
= json_array_get(config
,item
->server
);
702 s_host
= json_string_value(json_object_get(server
,"host"));
703 s_port
= json_integer_value(json_object_get(server
,"port"));
704 s_key
= json_string_value(json_object_get(server
,"key"));
705 s_secret
= json_string_value(json_object_get(server
,"secret"));
706 s_type
= json_string_value(json_object_get(server
,"type"));
708 if (!strcasecmp(s_type
,"s3")) {
709 DPRINTF("%s replicating create of bucket %s on %s:%u (S3)\n",
710 __func__
, item
->path
, s_host
, s_port
);
711 chars
= snprintf(svc_acc
,SVC_ACC_SIZE
,"%s:%u",s_host
,s_port
);
712 if (chars
>= SVC_ACC_SIZE
) {
713 error(0,0,"svc_acc too long in %s",__func__
);
716 /* TBD: check return */
717 hstor
= hstor_new(svc_acc
,s_host
,s_key
,s_secret
);
719 if (!hstor_add_bucket(hstor
,item
->path
)) {
720 error(0,0,"bucket create failed for %s",
726 DPRINTF("%s replicating create of bucket %s on %s:%u (HTTP)\n",
727 __func__
, item
->path
, s_host
, s_port
);
728 chars
= snprintf(addr
,ADDR_SIZE
,"http://%s:%d/%s",
729 s_host
,s_port
,item
->path
);
730 if (chars
>= ADDR_SIZE
) {
731 error(0,0,"path too long in %s",__func__
);
734 curl
= curl_easy_init();
735 curl_easy_setopt(curl
,CURLOPT_URL
,addr
);
736 curl_easy_setopt(curl
,CURLOPT_CUSTOMREQUEST
,"PUT");
737 curl_easy_perform(curl
);
738 curl_easy_cleanup(curl
);
741 DPRINTF("%s returning\n",__func__
);
745 repl_worker (void *notused ATTRIBUTE_UNUSED
)
752 sem_wait(&queue_sema
);
753 pthread_mutex_lock(&queue_lock
);
755 queue_head
= item
->next
;
759 pthread_mutex_unlock(&queue_lock
);
761 switch (item
->type
) {
763 if (pipe(item
->pipes
) >= 0) {
765 pthread_create(&prod
,NULL
,
766 proxy_repl_prod
,item
);
769 pthread_create(&prod
,NULL
,
770 proxy_repl_prod_fs
,item
);
772 pthread_create(&cons
,NULL
,proxy_repl_cons
,item
);
773 pthread_join(prod
,NULL
);
774 pthread_join(cons
,NULL
);
777 error(0,errno
,"pipe");
781 repl_worker_del(item
);
784 repl_worker_bcreate(item
);
787 error(0,0,"bad repl type %d (url=%s) skipped",
788 item
->type
, item
->path
);
800 sem_init(&queue_sema
,0,0);
801 pthread_mutex_init(&queue_lock
,NULL
);
802 pthread_create(&tid
,NULL
,repl_worker
,NULL
);
806 repl_oget (void *ctx
, const char *id
)
808 query_ctx_t
*qctx
= ctx
;
811 (void)meta_get_value(qctx
->cur_bucket
,qctx
->cur_key
,id
,&cur_value
);
817 repl_sget (void *ctx
, const char *id
)
819 query_ctx_t
*qctx
= ctx
;
820 json_t
*elem
= json_object_get(qctx
->cur_server
,id
);
822 return elem
? (char *)json_string_value(elem
) : NULL
;
826 replicate (const char *url
, size_t size
, const char *policy
)
840 error(0,0,"could not parse url %s",url
);
843 qctx
.cur_bucket
= strtok_r(url2
,"/",&stctx
);
844 qctx
.cur_key
= strtok_r(NULL
,"/",&stctx
);
847 size
= meta_get_size(qctx
.cur_bucket
,qctx
.cur_key
);
848 DPRINTF("fetched size %zu for %s\n",size
,url
);
852 DPRINTF("--- policy = %s\n",policy
);
853 expr
= parse(policy
);
859 oget
.func
= repl_oget
;
861 sget
.func
= repl_sget
;
864 for (i
= 1; i
< json_array_size(config
); ++i
) {
866 qctx
.cur_server
= json_array_get(config
,i
);
867 res
= eval(expr
,&oget
,&sget
);
873 DPRINTF("skipping %u for %s\n",i
,url
);
876 DPRINTF("REPLICATING %s to %u\n",url
,i
);
877 item
= malloc(sizeof(*item
));
879 error(0,errno
,"could not create repl_item for %s",
883 item
->type
= REPL_PUT
;
884 item
->path
= strdup(url
);
886 error(0,errno
,"could not create repl_item for %s",
892 pthread_mutex_lock(&queue_lock
);
894 item
->next
= queue_tail
->next
;
895 queue_tail
->next
= item
;
902 pthread_mutex_unlock(&queue_lock
);
903 sem_post(&queue_sema
);
913 replicate_namespace_action (const char *name
, repl_t action
)
918 for (i
= 1; i
< json_array_size(config
); ++i
) {
919 DPRINTF("replicating delete(%s) on %u\n",name
,i
);
920 item
= malloc(sizeof(*item
));
922 error(0,errno
,"could not create repl_item for %s",
927 item
->path
= strdup(name
);
933 pthread_mutex_lock(&queue_lock
);
935 item
->next
= queue_tail
->next
;
936 queue_tail
->next
= item
;
943 pthread_mutex_unlock(&queue_lock
);
944 sem_post(&queue_sema
);
949 replicate_delete (const char *name
)
951 replicate_namespace_action(name
,REPL_ODELETE
);
955 replicate_bcreate (const char *name
)
957 replicate_namespace_action(name
,REPL_BCREATE
);
961 get_provider (int i
, provider_t
*out
)
965 server
= json_array_get(config
,i
);
967 DPRINTF("no such entry %d\n",i
);
972 out
->name
= json_string_value(json_object_get(server
,"name"));
973 out
->type
= json_string_value(json_object_get(server
,"type"));
974 out
->host
= json_string_value(json_object_get(server
,"host"));
975 out
->port
= json_integer_value(json_object_get(server
,"port"));
976 /* TBD: change key/secret field names to username/password */
977 out
->username
= json_string_value(json_object_get(server
,"key"));
978 out
->password
= json_string_value(json_object_get(server
,"secret"));
979 out
->path
= json_string_value(json_object_get(server
,"path"));
981 /* Use empty strings instead of NULL. */
982 if (!out
->username
) out
->username
= "";
983 if (!out
->password
) out
->password
= "";
985 /* TBD: do this a cleaner way. */
986 if (!strcasecmp(out
->type
,"s3")) {
987 out
->func_tbl
= &s3_func_tbl
;
989 else if (!strcasecmp(out
->type
,"http")) {
990 out
->func_tbl
= &curl_func_tbl
;
992 else if (!strcasecmp(out
->type
,"fs")) {
993 out
->func_tbl
= &fs_func_tbl
;
996 out
->func_tbl
= &bad_func_tbl
;
1003 get_provider_value (int i
, const char *fname
)
1008 server
= json_array_get(config
,i
);
1010 DPRINTF("no such entry %d\n",i
);
1014 field
= json_object_get(server
,fname
);
1016 DPRINTF("no such field %s on server %d\n",fname
,i
);
1020 return (char *)json_string_value(field
);
1024 update_provider (const char *provider
, const char *username
, const char *password
)
1030 DPRINTF("updating %s username=%s password=%s\n",
1031 provider
, username
, password
);
1033 for (i
= 0; ; ++i
) {
1034 server
= json_array_get(config
,i
);
1036 DPRINTF(" could not find provider %s\n",provider
);
1039 s_name
= json_string_value(json_object_get(server
,"name"));
1040 DPRINTF(" checking %s\n",s_name
);
1041 if (s_name
&& !strcmp(s_name
,provider
)) {
1042 json_object_set_new(server
,"key",json_string(username
));
1043 json_object_set_new(server
,"secret",
1044 json_string(password
));
1050 /* Part of our API to the query module. */
1052 follow_link (char *object
, char *key
)
1057 return "no_such_object";