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>
33 #include <microhttpd.h>
34 #include <hstor.h> /* only for ARRAY_SIZE at this point */
35 #include <curl/curl.h>
45 #include "state_defs.h"
47 /* Define-away for now. Eventually, define to gettext. */
48 #define _(msgid) (msgid)
51 #define MY_MHD_FLAGS MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG
52 //#define MY_MHD_FLAGS MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG
54 #define MY_MHD_FLAGS MHD_USE_THREAD_PER_CONNECTION
58 URL_ROOT
=0, URL_BUCKET
, URL_OBJECT
, URL_ATTR
, URL_INVAL
,
59 URL_QUERY
, URL_PROVLIST
65 MHD_AccessHandlerCallback handler
;
68 static unsigned short my_port
= MY_PORT
;
69 const char *program_name
;
70 char *cfg_file
= NULL
;
72 static char *(reserved_name
[]) = { "_default", "_query", "_new", NULL
};
73 static char *(reserved_attr
[]) = { "bucket", "key", "date", "etag", "loc", NULL
};
76 free_ms (my_state
*ms
)
78 if (!g_atomic_int_dec_and_test(&ms
->refcnt
)) {
82 if (ms
->cleanup
& CLEANUP_BUF_PTR
) {
83 free(ms
->pipe
.data_ptr
);
86 if (ms
->cleanup
& CLEANUP_POST
) {
87 MHD_destroy_post_processor(ms
->post
);
90 if (ms
->cleanup
& CLEANUP_DICT
) {
91 g_hash_table_destroy(ms
->dict
);
94 if (ms
->cleanup
& CLEANUP_QUERY
) {
95 meta_query_stop(ms
->query
);
98 if (ms
->cleanup
& CLEANUP_TMPL
) {
102 if (ms
->cleanup
& CLEANUP_URL
) {
106 if (ms
->cleanup
& CLEANUP_AQUERY
) {
107 meta_attr_stop(ms
->aquery
);
114 validate_put (struct MHD_Connection
*conn
)
119 /* We're not a slave, so we don't care. */
123 mhdr
= MHD_lookup_connection_value(conn
,MHD_HEADER_KIND
,
125 return (mhdr
&& !strcmp(mhdr
,"master"));
129 is_reserved (const char *cand
, char **resv_list
)
133 for (i
= 0; resv_list
[i
]; ++i
) {
134 if (!strcmp(cand
,resv_list
[i
])) {
143 validate_url (const char *url
)
145 char *slash
= rindex(url
,'/');
148 /* There should be at least one betwixt bucket and key. */
152 return !is_reserved(slash
+1,reserved_name
);
156 * The proxy has MHD on one side and CURL on the other. The CURL side is
157 * always run in a child thread. Yes, there are both context switches
158 * and copies between the threads. Get over it. The focus here is on
159 * supporting multi-way replication on PUT, with minimal complexity. These
160 * were the two easiest libraries to use, and they both want to allocate
161 * their own buffers so we're kind of stuck with the copies unless we want
162 * to buffer whole files in memory (which would have to be virtual since
163 * they're potentialy bigger than physical) or explicitly ping them through
164 * a local filesystem. We could potentially take over scheduling from one
165 * or both to avoid some of the context switching, but those interfaces are
166 * significantly more error-prone and (at least in CURL's case) buggy.
168 * For a GET, the CURL child acts as a producer while the MHD parent acts
169 * as consumer. For a PUT, the MHD parent is the producer and the CURL
170 * child is the consumer. For GET the MHD component is invoked via a
171 * callback set up in the access handler; for PUT it's invoked via repeated
172 * calls to the access handler itself. Either way, the producer's job is
173 * to post its pointer+length to the my_state structure and then wait for
174 * all consumers to check back in saying they've finished it. This might
175 * involve multiple passes through each consumer for one pass through the
176 * single producer. When the producer is done, it does a similar handshake
177 * with the consumers. Each consumer has its own pipe_private structure,
178 * containing a pointer to the shared my_state plus a per-consumer offset
179 * into the current chunk.
181 * Attribute functions don't use CURL, so they do much simpler in-memory
182 * buffering. Queries also don't use CURL, but the MHD POST interface
183 * introduces some of its own complexity so see below for that.
187 simple_closer (void *ctx
)
191 DPRINTF("%s: cleaning up\n",__func__
);
196 child_closer (void * ctx
)
198 pipe_private
*pp
= ctx
;
200 DPRINTF("in %s\n",__func__
);
205 /* Invoked from MHD. */
207 proxy_get_cons (void *ctx
, uint64_t pos
, char *buf
, int max
)
209 pipe_private
*pp
= ctx
;
210 pipe_shared
*ps
= pp
->shared
;
211 my_state
*ms
= ps
->owner
;
217 DPRINTF("consumer asked to read %d\n",max
);
219 if (pipe_cons_wait(pp
)) {
220 DPRINTF("consumer offset %zu into %zu\n",
221 pp
->offset
, ps
->data_len
);
222 done
= ps
->data_len
- pp
->offset
;
226 memcpy(buf
,ps
->data_ptr
+pp
->offset
,done
);
228 DPRINTF("consumer copied %d, new offset %zu\n",
230 if (pp
->offset
== ps
->data_len
) {
231 DPRINTF("consumer finished chunk\n");
232 pipe_cons_signal(pp
, 0);
241 pthread_join(ms
->backend_th
,&child_res
);
242 if (child_res
== THREAD_FAILED
) {
243 DPRINTF("GET producer failed\n");
244 /* Nothing we can do; already sent status. */
246 if (ms
->from_master
) {
247 pthread_join(ms
->cache_th
,NULL
);
248 /* TBD: do something about cache failure? */
257 proxy_get_data (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
258 const char *method
, const char *version
, const char *data
,
259 size_t *data_size
, void **rctx
)
261 struct MHD_Response
*resp
;
262 my_state
*ms
= *rctx
;
266 const char *user_etag
;
275 DPRINTF("PROXY GET DATA %s\n",url
);
277 ms
->url
= strdup(url
);
281 ms
->cleanup
|= CLEANUP_URL
;
283 my_etag
= meta_has_copy(ms
->bucket
,ms
->key
,me
);
285 DPRINTF("falling back to local for %s/%s\n",ms
->bucket
,ms
->key
);
289 user_etag
= MHD_lookup_connection_value(
290 conn
, MHD_HEADER_KIND
, "If-None-Match");
291 if (user_etag
&& !strcmp(user_etag
,my_etag
)) {
292 DPRINTF("ETag match!\n");
294 resp
= MHD_create_response_from_data(0,NULL
,
296 MHD_queue_response(conn
,MHD_HTTP_NOT_MODIFIED
,resp
);
297 MHD_destroy_response(resp
);
304 DPRINTF("%s/%s not found locally\n",ms
->bucket
,ms
->key
);
306 DPRINTF(" that means it doesn't exist\n");
307 resp
= MHD_create_response_from_data(0,NULL
,
309 MHD_queue_response(conn
,MHD_HTTP_NOT_FOUND
,resp
);
310 MHD_destroy_response(resp
);
314 DPRINTF(" will fetch from %s:%u\n", master_host
,master_port
);
318 pipe_init_shared(&ms
->pipe
,ms
,ms
->from_master
+1);
319 pp
= pipe_init_private(&ms
->pipe
);
323 ms
->thunk
.parent
= ms
;
324 ms
->thunk
.prov
= ms
->from_master
? master_prov
: main_prov
;
325 pthread_create(&ms
->backend_th
,NULL
,
326 ms
->thunk
.prov
->func_tbl
->get_child_func
,&ms
->thunk
);
327 /* TBD: check return value */
329 if (ms
->from_master
) {
330 pp2
= pipe_init_private(&ms
->pipe
);
334 pp2
->prov
= main_prov
;
335 pthread_create(&ms
->cache_th
,NULL
,
336 main_prov
->func_tbl
->cache_child_func
,pp2
);
337 /* TBD: check return value */
343 rc
= pipe_cons_wait_init(&ms
->pipe
);
344 ms
->rc
= (rc
== 0) ? MHD_HTTP_OK
: MHD_HTTP_INTERNAL_SERVER_ERROR
;
346 resp
= MHD_create_response_from_callback(
347 MHD_SIZE_UNKNOWN
, 65536, proxy_get_cons
, pp
, child_closer
);
349 fprintf(stderr
,"MHD_crfc failed\n");
351 /* TBD: terminate thread */
357 MHD_queue_response(conn
,ms
->rc
,resp
);
358 MHD_destroy_response(resp
);
364 recheck_replication (my_state
* ms
, char *policy
)
368 char fixed
[MAX_FIELD_LEN
];
370 if (is_reserved(ms
->key
,reserved_name
)) {
371 DPRINTF("declining to replicate reserved object %s\n",ms
->key
);
375 if (!policy
&& ms
->dict
) {
376 DPRINTF("using new policy for %s/%s\n",ms
->bucket
,ms
->key
);
377 policy
= g_hash_table_lookup(ms
->dict
,"_policy");
381 /* If we get a policy here or below, we have to free it. */
383 DPRINTF("fetching policy for %s/%s\n",ms
->bucket
,ms
->key
);
384 rc
= meta_get_value(ms
->bucket
,ms
->key
, "_policy", &policy
);
388 DPRINTF(" inheriting policy from %s\n",ms
->bucket
);
389 rc
= meta_get_value(ms
->bucket
,
390 "_default", "_policy", &policy
);
394 DPRINTF(" implementing policy %s\n",policy
);
396 * Can't use ms->url here because it might be a bucket POST
397 * and in that case ms->url points to the bucket.
399 snprintf(fixed
,sizeof(fixed
),"%s/%s",ms
->bucket
,ms
->key
);
400 replicate(fixed
,0,policy
,ms
);
406 DPRINTF(" could not find a policy anywhere!\n");
411 proxy_put_data (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
412 const char *method
, const char *version
, const char *data
,
413 size_t *data_size
, void **rctx
)
415 struct MHD_Response
*resp
;
416 my_state
*ms
= *rctx
;
426 DPRINTF("PROXY PUT DATA %s (%zu)\n",url
,*data_size
);
428 if (ms
->state
== MS_NEW
) {
429 if (!validate_put(conn
) || !validate_url(url
)) {
430 DPRINTF("rejecting %s\n",url
);
431 resp
= MHD_create_response_from_data(0,NULL
,
436 MHD_queue_response(conn
,MHD_HTTP_FORBIDDEN
,resp
);
437 MHD_destroy_response(resp
);
440 ms
->state
= MS_NORMAL
;
441 ms
->url
= strdup(url
);
445 ms
->cleanup
|= CLEANUP_URL
;
447 pipe_init_shared(&ms
->pipe
,ms
,1);
448 pp
= pipe_init_private(&ms
->pipe
);
452 pp
->prov
= main_prov
;
453 ms
->be_flags
= BACKEND_GET_SIZE
;
454 pthread_create(&ms
->backend_th
,NULL
,
455 main_prov
->func_tbl
->put_child_func
,pp
);
456 /* TBD: check return value */
459 * Do the initial handshake with children. If we return from
460 * this callback without an error response, Microhttpd posts
461 * the "100 Continue" header and the client starts sending
462 * the data. We must report errors here or forever keep
465 rc
= pipe_prod_wait_init(&ms
->pipe
);
467 DPRINTF("producer wait failed\n");
468 resp
= MHD_create_response_from_data(0,NULL
,
473 MHD_queue_response(conn
,MHD_HTTP_INTERNAL_SERVER_ERROR
,
475 MHD_destroy_response(resp
);
478 * Note that we fail here even if 1 of N replicas fail.
479 * Might want to fix this when we start looping over
480 * pipe_init_private() above.
482 DPRINTF("producer replicas failed (%u of %u)\n",
483 rc
, ms
->pipe
.cons_total
);
484 resp
= MHD_create_response_from_data(0,NULL
,
489 MHD_queue_response(conn
,MHD_HTTP_INTERNAL_SERVER_ERROR
,
491 MHD_destroy_response(resp
);
493 DPRINTF("producer proceeding\n");
496 else if (*data_size
) {
497 pipe_prod_signal(&ms
->pipe
,(void *)data
,*data_size
);
498 ms
->size
+= *data_size
;
499 DPRINTF("producer chunk finished\n");
503 pipe_prod_finish(&ms
->pipe
);
504 pthread_join(ms
->backend_th
,&child_res
);
505 if (child_res
== THREAD_FAILED
) {
506 DPRINTF("thread failed\n");
507 rc
= MHD_HTTP_INTERNAL_SERVER_ERROR
;
509 else if (ms
->pipe
.cons_error
== ms
->pipe
.cons_total
) {
510 DPRINTF("all %u consumers failed\n",
511 ms
->pipe
.cons_error
);
512 rc
= MHD_HTTP_INTERNAL_SERVER_ERROR
;
516 meta_got_copy(ms
->bucket
,ms
->key
,me
);
520 etag
= meta_did_put(ms
->bucket
,ms
->key
,me
,
523 DPRINTF("rereplicate (obj PUT)\n");
524 recheck_replication(ms
,NULL
);
528 resp
= MHD_create_response_from_data(0,NULL
,MHD_NO
,MHD_NO
);
534 MHD_add_response_header(resp
,"ETag",etag
);
537 MHD_queue_response(conn
,rc
,resp
);
538 MHD_destroy_response(resp
);
545 proxy_get_attr (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
546 const char *method
, const char *version
, const char *data
,
547 size_t *data_size
, void **rctx
)
549 struct MHD_Response
*resp
;
551 my_state
*ms
= *rctx
;
552 int rc
= MHD_HTTP_NOT_FOUND
;
560 DPRINTF("PROXY GET ATTR %s\n",url
);
562 if (meta_get_value(ms
->bucket
,ms
->key
,ms
->attr
,&fixed
) == 0) {
563 resp
= MHD_create_response_from_data(strlen(fixed
),fixed
,
568 resp
= MHD_create_response_from_data(0,NULL
,MHD_NO
,MHD_NO
);
573 MHD_queue_response(conn
,rc
,resp
);
574 MHD_destroy_response(resp
);
581 proxy_put_attr (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
582 const char *method
, const char *version
, const char *data
,
583 size_t *data_size
, void **rctx
)
585 struct MHD_Response
*resp
;
586 my_state
*ms
= *rctx
;
594 DPRINTF("PROXY PUT ATTR %s (%zu)\n",url
,*data_size
);
596 if (ms
->state
== MS_NEW
) {
597 ms
->state
= MS_NORMAL
;
598 ms
->url
= strdup(url
);
602 ms
->cleanup
|= CLEANUP_URL
;
603 attrval
= MHD_lookup_connection_value(conn
,MHD_HEADER_KIND
,
606 meta_set_value(ms
->bucket
,ms
->key
,ms
->attr
,
611 else if (*data_size
) {
612 if (ms
->pipe
.data_len
) {
613 ms
->pipe
.data_len
+= *data_size
;
614 char *p
= realloc(ms
->pipe
.data_ptr
,ms
->pipe
.data_len
);
618 ms
->pipe
.data_ptr
= p
;
621 ms
->pipe
.data_len
= *data_size
+ 1;
622 ms
->pipe
.data_ptr
= malloc(ms
->pipe
.data_len
);
623 if (!ms
->pipe
.data_ptr
) {
626 ((char *)ms
->pipe
.data_ptr
)[0] = '\0';
627 ms
->cleanup
|= CLEANUP_BUF_PTR
;
629 (void)strncat(ms
->pipe
.data_ptr
,data
,*data_size
);
630 /* TBD: check return value */
634 if (!ms
->pipe
.data_ptr
) {
637 if (is_reserved(ms
->attr
,reserved_attr
)) {
638 resp
= MHD_create_response_from_data(
639 0,NULL
,MHD_NO
,MHD_NO
);
643 MHD_queue_response(conn
,MHD_HTTP_BAD_REQUEST
,
645 MHD_destroy_response(resp
);
649 meta_set_value(ms
->bucket
,ms
->key
,ms
->attr
,ms
->pipe
.data_ptr
);
650 /* This might get stomped by replication. */
651 if (ms
->cleanup
& CLEANUP_BUF_PTR
) {
652 free(ms
->pipe
.data_ptr
);
653 ms
->cleanup
&= ~CLEANUP_BUF_PTR
;
656 * We should always re-replicate, because the replication
657 * policy might refer to this attr.
659 DPRINTF("rereplicate (attr PUT)\n");
660 recheck_replication(ms
,NULL
);
666 resp
= MHD_create_response_from_data(0,NULL
,MHD_NO
,MHD_NO
);
670 MHD_queue_response(conn
,MHD_HTTP_CREATED
,resp
);
671 MHD_destroy_response(resp
);
673 * TBD: check if the attribute was a replication policy, and
674 * start/stop replication activities as appropriate.
682 * For queries, we have to deal with MHD's post-iterator interface (not
683 * quite the same as the data-iteration even though we use it that way) on
684 * one side, and a query-iterator interface on the other. Data on both
685 * sides could be quite large, so we can't just stick them in header lines.
686 * We do still buffer the query in memory, though. Once that's done, we do
687 * very simple parsing - it will be more complicated later - and create the
688 * query iterator. That's also driven by MHD, this time though the
689 * content-callback interface, and repeatedly calls in to the metadata
690 * module to fetch one object name at a time.
694 query_iterator (void *ctx
, enum MHD_ValueKind kind
, const char *key
,
695 const char *filename
, const char *content_type
,
696 const char *transfer_encoding
, const char *data
,
697 uint64_t off
, size_t size
)
704 (void)transfer_encoding
;
709 /* We actually accumulate the data in proxy_query. */
713 /* MHD reader function during queries. Return -1 for EOF. */
715 proxy_query_func (void *ctx
, uint64_t pos
, char *buf
, int max
)
719 const char *accept_hdr
;
725 accept_hdr
= MHD_lookup_connection_value(ms
->conn
,MHD_HEADER_KIND
,
729 ms
->gen_ctx
= tmpl_get_ctx(accept_hdr
);
733 ms
->cleanup
|= CLEANUP_TMPL
;
734 len
= tmpl_list_header(ms
->gen_ctx
);
741 memcpy(buf
,ms
->gen_ctx
->buf
,len
);
745 if (ms
->gen_ctx
== TMPL_CTX_DONE
) {
750 if (!meta_query_next(ms
->query
,&bucket
,&key
)) {
753 if (is_reserved(key
,reserved_name
)) {
756 len
= tmpl_list_entry(ms
->gen_ctx
,bucket
,key
);
763 memcpy(buf
,ms
->gen_ctx
->buf
,len
);
767 len
= tmpl_list_footer(ms
->gen_ctx
);
774 memcpy(buf
,ms
->gen_ctx
->buf
,len
);
776 ms
->cleanup
&= ~CLEANUP_TMPL
;
777 ms
->gen_ctx
= TMPL_CTX_DONE
;
782 proxy_query (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
783 const char *method
, const char *version
, const char *data
,
784 size_t *data_size
, void **rctx
)
786 struct MHD_Response
*resp
;
787 my_state
*ms
= *rctx
;
793 DPRINTF("PROXY QUERY %s (%zu)\n",url
,*data_size
);
795 if (ms
->state
== MS_NEW
) {
796 ms
->state
= MS_NORMAL
;
797 ms
->post
= MHD_create_post_processor(conn
,4096,
799 ms
->cleanup
|= CLEANUP_POST
;
801 else if (*data_size
) {
802 MHD_post_process(ms
->post
,data
,*data_size
);
803 if (ms
->pipe
.data_len
) {
804 ms
->pipe
.data_len
+= *data_size
;
805 char *p
= realloc(ms
->pipe
.data_ptr
,ms
->pipe
.data_len
);
809 ms
->pipe
.data_ptr
= p
;
812 ms
->pipe
.data_len
= *data_size
+ 1;
813 ms
->pipe
.data_ptr
= malloc(ms
->pipe
.data_len
);
814 if (!ms
->pipe
.data_ptr
) {
817 ((char *)ms
->pipe
.data_ptr
)[0] = '\0';
818 ms
->cleanup
|= CLEANUP_BUF_PTR
;
820 (void)strncat(ms
->pipe
.data_ptr
,data
,*data_size
);
821 /* TBD: check return value */
825 if (!ms
->pipe
.data_ptr
) {
828 ms
->query
= meta_query_new(ms
->bucket
,NULL
,ms
->pipe
.data_ptr
);
829 ms
->cleanup
|= CLEANUP_QUERY
;
830 resp
= MHD_create_response_from_callback(MHD_SIZE_UNKNOWN
,
831 65536, proxy_query_func
, ms
, simple_closer
);
833 fprintf(stderr
,"MHD_crfc failed\n");
837 MHD_queue_response(conn
,MHD_HTTP_OK
,resp
);
838 MHD_destroy_response(resp
);
846 proxy_list_objs (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
847 const char *method
, const char *version
, const char *data
,
848 size_t *data_size
, void **rctx
)
850 my_state
*ms
= *rctx
;
851 struct MHD_Response
*resp
;
860 ms
->query
= meta_query_new((char *)ms
->bucket
,NULL
,NULL
);
861 ms
->cleanup
|= CLEANUP_QUERY
;
863 resp
= MHD_create_response_from_callback(MHD_SIZE_UNKNOWN
,
864 65536, proxy_query_func
, ms
, simple_closer
);
866 fprintf(stderr
,"MHD_crfc failed\n");
871 MHD_queue_response(conn
,MHD_HTTP_OK
,resp
);
872 MHD_destroy_response(resp
);
877 proxy_delete (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
878 const char *method
, const char *version
, const char *data
,
879 size_t *data_size
, void **rctx
)
881 my_state
*ms
= *rctx
;
882 struct MHD_Response
*resp
;
895 DPRINTF("PROXY DELETE %s\n",url
);
897 ms
->thunk
.parent
= ms
;
898 ms
->thunk
.prov
= main_prov
;
899 rc
= ms
->thunk
.prov
->func_tbl
->delete_func(main_prov
,
900 ms
->bucket
,ms
->key
,url
);
901 if (rc
== MHD_HTTP_OK
) {
902 copied_url
= strdup(url
);
904 bucket
= strtok_r(copied_url
,"/",&stctx
);
905 key
= strtok_r(NULL
,"/",&stctx
);
906 meta_delete(bucket
,key
);
908 replicate_delete(url
,ms
);
911 resp
= MHD_create_response_from_data(0,NULL
,MHD_NO
,MHD_NO
);
915 MHD_queue_response(conn
,rc
,resp
);
916 MHD_destroy_response(resp
);
922 /* TBD: get actual bucket list */
928 static const fake_bucket_t fake_bucket_list
[] = {
929 { "bucket_factory", "_new" },
930 { "provider_list", "_providers" },
934 root_blob_generator (void *ctx
, uint64_t pos
, char *buf
, int max
)
937 const fake_bucket_t
*fb
;
939 const char *accept_hdr
;
946 accept_hdr
= MHD_lookup_connection_value(ms
->conn
,MHD_HEADER_KIND
,
948 host
= MHD_lookup_connection_value(ms
->conn
,MHD_HEADER_KIND
,"Host");
951 ms
->gen_ctx
= tmpl_get_ctx(accept_hdr
);
955 ms
->cleanup
|= CLEANUP_TMPL
;
956 ms
->gen_ctx
->base
= host
;
957 len
= tmpl_root_header(ms
->gen_ctx
,"image_warehouse","1.0");
964 memcpy(buf
,ms
->gen_ctx
->buf
,len
);
968 if (ms
->gen_ctx
== TMPL_CTX_DONE
) {
972 if (ms
->gen_ctx
->index
< ARRAY_SIZE(fake_bucket_list
)) {
973 fb
= fake_bucket_list
+ ms
->gen_ctx
->index
;
974 len
= tmpl_root_entry(ms
->gen_ctx
,fb
->rel
,fb
->link
);
981 memcpy(buf
,ms
->gen_ctx
->buf
,len
);
985 if (meta_query_next(ms
->query
,&bucket
,&key
)) {
986 len
= tmpl_root_entry(ms
->gen_ctx
,"bucket",bucket
);
993 memcpy(buf
,ms
->gen_ctx
->buf
,len
);
997 len
= tmpl_root_footer(ms
->gen_ctx
);
1004 memcpy(buf
,ms
->gen_ctx
->buf
,len
);
1006 ms
->cleanup
&= ~CLEANUP_TMPL
;
1007 ms
->gen_ctx
= TMPL_CTX_DONE
;
1012 proxy_api_root (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
1013 const char *method
, const char *version
, const char *data
,
1014 size_t *data_size
, void **rctx
)
1016 struct MHD_Response
*resp
= NULL
;
1017 unsigned int rc
= MHD_HTTP_OK
;
1018 my_state
*ms
= *rctx
;
1025 DPRINTF("PROXY API ROOT (%s, %zu)\n",url
,*data_size
);
1027 ms
->query
= meta_query_new(NULL
,"_default",NULL
);
1032 ms
->cleanup
|= CLEANUP_QUERY
;
1034 resp
= MHD_create_response_from_callback(MHD_SIZE_UNKNOWN
,
1035 65536, root_blob_generator
, ms
, simple_closer
);
1039 MHD_queue_response(conn
,rc
,resp
);
1040 MHD_destroy_response(resp
);
1047 post_iterator (void *ctx
, enum MHD_ValueKind kind
, const char *key
,
1048 const char *filename
, const char *content_type
,
1049 const char *transfer_encoding
, const char *data
,
1050 uint64_t off
, size_t size
)
1059 (void)transfer_encoding
;
1062 printf("adding %s, size=%zu\n",key
,size
);
1064 // TBD: don't assume that values are null-terminated strings
1065 old_val
= g_hash_table_lookup(ctx
,key
);
1067 old_len
= strlen(old_val
);
1068 new_val
= malloc(old_len
+size
+1);
1072 memcpy(new_val
,old_val
,old_len
);
1073 memcpy(new_val
+old_len
,data
,size
);
1074 new_val
[old_len
+size
] = '\0';
1077 new_val
= malloc(size
+1);
1081 memcpy(new_val
,data
,size
);
1082 new_val
[size
] = '\0';
1085 g_hash_table_insert(ctx
,strdup(key
),new_val
);
1086 /* TBD: check return value for strdups (none avail for insert) */
1090 /* Returns TRUE if we found an *invalid* key. */
1092 post_find (gpointer key
, gpointer value
, gpointer ctx
)
1097 if (!is_reserved(key
,reserved_attr
)) {
1101 DPRINTF("bad attr %s\n", (char *)key
);
1106 post_foreach (gpointer key
, gpointer value
, gpointer ctx
)
1110 DPRINTF("setting %s = %s for %s/%s\n",(char *)key
, (char *)value
,
1111 ms
->bucket
,ms
->key
);
1112 meta_set_value(ms
->bucket
,ms
->key
,key
,value
);
1116 create_bucket (char *name
, my_state
*ms
)
1120 if (is_reserved(name
,reserved_name
)) {
1121 return MHD_HTTP_BAD_REQUEST
;
1124 rc
= main_prov
->func_tbl
->bcreate_func(main_prov
,name
);
1125 if (rc
== MHD_HTTP_OK
) {
1126 if (meta_set_value(name
,"_default", "_policy","0") != 0) {
1127 DPRINTF("default-policy " "create failed\n");
1130 DPRINTF("created bucket %s\n",name
);
1132 * There's not a whole lot to do about bucket-creation
1133 * failures on replicas, other than to report them, unless
1134 * we adopt an "all or nothing" approach and unwind the
1135 * create on the primary as well. Then what if that fails?
1136 * It's just one example of the general "fewer replicas
1137 * than desired" distributed-system problem, not worth a
1138 * point solution here/now. Revisit when we have a more
1139 * general replica-repair policy/system in place.
1141 replicate_bcreate(name
,ms
);
1148 control_api_root (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
1149 const char *method
, const char *version
, const char *data
,
1150 size_t *data_size
, void **rctx
)
1152 struct MHD_Response
*resp
;
1153 my_state
*ms
= *rctx
;
1163 DPRINTF("ROOT POST (%s, %zu)\n",url
,*data_size
);
1165 if (ms
->state
== MS_NEW
) {
1166 ms
->state
= MS_NORMAL
;
1167 ms
->url
= (char *)url
;
1168 ms
->dict
= g_hash_table_new_full(
1169 g_str_hash
,g_str_equal
,free
,free
);
1170 ms
->cleanup
|= CLEANUP_DICT
;
1171 ms
->post
= MHD_create_post_processor(conn
,4096,
1172 post_iterator
,ms
->dict
);
1173 ms
->cleanup
|= CLEANUP_POST
;
1178 MHD_post_process(ms
->post
,data
,*data_size
);
1183 rc
= MHD_HTTP_BAD_REQUEST
;
1185 op
= g_hash_table_lookup(ms
->dict
,"op");
1187 if (!strcmp(op
,"rep_status")) {
1188 len
= snprintf(buf
,sizeof(buf
),"%d requests\n",
1193 len
= snprintf(buf
,sizeof(buf
),"unknown op");
1197 len
= snprintf(buf
,sizeof(buf
),"missing op");
1200 if (len
>= (int)sizeof(buf
)) {
1202 rc
= MHD_HTTP_INTERNAL_SERVER_ERROR
;
1205 /* NB The last arg tells MHD to copy the arg and free it later. */
1206 resp
= MHD_create_response_from_data(len
,buf
,MHD_NO
,MHD_YES
);
1210 MHD_queue_response(conn
,rc
,resp
);
1211 MHD_destroy_response(resp
);
1218 proxy_bucket_post (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
1219 const char *method
, const char *version
, const char *data
,
1220 size_t *data_size
, void **rctx
)
1222 struct MHD_Response
*resp
;
1223 my_state
*ms
= *rctx
;
1231 DPRINTF("PROXY POST (%s, %zu)\n",url
,*data_size
);
1233 if (ms
->state
== MS_NEW
) {
1234 ms
->state
= MS_NORMAL
;
1235 ms
->url
= (char *)url
;
1236 ms
->dict
= g_hash_table_new_full(
1237 g_str_hash
,g_str_equal
,free
,free
);
1238 ms
->cleanup
|= CLEANUP_DICT
;
1239 ms
->post
= MHD_create_post_processor(conn
,4096,
1240 post_iterator
,ms
->dict
);
1241 ms
->cleanup
|= CLEANUP_POST
;
1243 else if (*data_size
) {
1244 MHD_post_process(ms
->post
,data
,*data_size
);
1248 rc
= MHD_HTTP_BAD_REQUEST
;
1249 key
= g_hash_table_lookup(ms
->dict
,"key");
1251 strncpy(ms
->key
,key
,MAX_FIELD_LEN
-1);
1252 g_hash_table_remove(ms
->dict
,"key");
1253 if (!g_hash_table_find(ms
->dict
,post_find
,ms
)) {
1254 g_hash_table_foreach(ms
->dict
,post_foreach
,ms
);
1255 DPRINTF("rereplicate (bucket POST)\n");
1256 recheck_replication(ms
,NULL
);
1260 else if (!strcmp(ms
->bucket
,"_new")) {
1261 key
= g_hash_table_lookup(ms
->dict
,"name");
1263 rc
= create_bucket(key
,ms
);
1267 DPRINTF("A parameter is MISSING (fail)\n");
1269 resp
= MHD_create_response_from_data(0,NULL
,MHD_NO
,MHD_NO
);
1271 fprintf(stderr
,"MHD_crfd failed\n");
1274 MHD_queue_response(conn
,rc
,resp
);
1275 MHD_destroy_response(resp
);
1283 check_location (my_state
*ms
)
1285 char *loc
= g_hash_table_lookup(ms
->dict
,"depot");
1288 DPRINTF("missing loc on check for %s/%s\n",ms
->bucket
,ms
->key
);
1289 return MHD_HTTP_BAD_REQUEST
;
1292 if (!meta_has_copy(ms
->bucket
,ms
->key
,loc
)) {
1293 DPRINTF("did not find %s/%s at %s\n",ms
->bucket
,ms
->key
,loc
);
1294 return MHD_HTTP_NOT_FOUND
;
1297 /* TBD: meta_has_copy returns an etag which we should check */
1298 DPRINTF("found %s/%s at %s\n",ms
->bucket
,ms
->key
,loc
);
1303 register_image (my_state
*ms
)
1306 const provider_t
*prov
;
1309 site
= g_hash_table_lookup(ms
->dict
,"site");
1311 printf("site MISSING\n");
1312 return MHD_HTTP_BAD_REQUEST
;
1315 next
= index(site
,':');
1320 prov
= get_provider(site
);
1322 DPRINTF("site %s not found\n",site
);
1323 return MHD_HTTP_BAD_REQUEST
;
1326 return prov
->func_tbl
->register_func(ms
,prov
,next
,ms
->dict
);
1331 parts_callback (void *ctx
, uint64_t pos
, char *buf
, int max
)
1335 const char *accept_hdr
;
1342 accept_hdr
= MHD_lookup_connection_value(ms
->conn
,MHD_HEADER_KIND
,
1344 host
= MHD_lookup_connection_value(ms
->conn
,MHD_HEADER_KIND
,"Host");
1347 ms
->gen_ctx
= tmpl_get_ctx(accept_hdr
);
1351 ms
->cleanup
|= CLEANUP_TMPL
;
1352 ms
->gen_ctx
->base
= host
;
1353 len
= tmpl_obj_header(ms
->gen_ctx
,ms
->bucket
,ms
->key
);
1360 memcpy(buf
,ms
->gen_ctx
->buf
,len
);
1364 if (ms
->gen_ctx
== TMPL_CTX_DONE
) {
1369 // Set up and use query for what attributes exist.
1371 if (!meta_attr_next(ms
->aquery
,&name
,&value
)) {
1374 if (is_reserved(name
,reserved_attr
)) {
1377 len
= tmpl_obj_entry(ms
->gen_ctx
,ms
->bucket
,ms
->key
,name
);
1384 memcpy(buf
,ms
->gen_ctx
->buf
,len
);
1388 len
= tmpl_obj_footer(ms
->gen_ctx
);
1395 memcpy(buf
,ms
->gen_ctx
->buf
,len
);
1397 ms
->cleanup
&= ~CLEANUP_TMPL
;
1398 ms
->gen_ctx
= TMPL_CTX_DONE
;
1403 show_parts (struct MHD_Connection
*conn
, my_state
*ms
)
1405 struct MHD_Response
*resp
;
1407 ms
->aquery
= meta_get_attrs(ms
->bucket
,ms
->key
);
1409 return MHD_HTTP_NOT_FOUND
;
1411 ms
->cleanup
|= CLEANUP_AQUERY
;
1413 resp
= MHD_create_response_from_callback(MHD_SIZE_UNKNOWN
,
1414 65536, parts_callback
, ms
, simple_closer
);
1416 fprintf(stderr
,"MHD_crfc failed\n");
1420 MHD_queue_response(conn
,MHD_HTTP_OK
,resp
);
1421 MHD_destroy_response(resp
);
1422 return MHD_HTTP_PROCESSING
;
1426 proxy_object_post (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
1427 const char *method
, const char *version
, const char *data
,
1428 size_t *data_size
, void **rctx
)
1430 struct MHD_Response
*resp
;
1431 my_state
*ms
= *rctx
;
1439 DPRINTF("PROXY POST (%s, %zu)\n",url
,*data_size
);
1441 if (ms
->state
== MS_NEW
) {
1442 ms
->state
= MS_NORMAL
;
1443 ms
->url
= (char *)url
;
1444 ms
->dict
= g_hash_table_new_full(
1445 g_str_hash
,g_str_equal
,free
,free
);
1446 ms
->cleanup
|= CLEANUP_DICT
;
1447 ms
->post
= MHD_create_post_processor(conn
,4096,
1448 post_iterator
,ms
->dict
);
1449 ms
->cleanup
|= CLEANUP_POST
;
1451 else if (*data_size
) {
1452 MHD_post_process(ms
->post
,data
,*data_size
);
1456 rc
= MHD_HTTP_BAD_REQUEST
;
1457 if (!g_hash_table_find(ms
->dict
,post_find
,ms
)) {
1458 op
= g_hash_table_lookup(ms
->dict
,"op");
1460 if (!strcmp(op
,"push")) {
1461 DPRINTF("rereplicate (obj POST)\n");
1462 recheck_replication(ms
,NULL
);
1465 else if (!strcmp(op
,"check")) {
1466 rc
= check_location(ms
);
1468 else if (!strcmp(op
,"register")) {
1469 rc
= register_image(ms
);
1471 else if (!strcmp(op
,"parts")) {
1472 rc
= show_parts(conn
,ms
);
1475 DPRINTF("unknown op %s for %s/%s\n",
1476 op
, ms
->bucket
, ms
->key
);
1480 DPRINTF("op is MISSING (fail)\n");
1483 if (rc
!= MHD_HTTP_PROCESSING
) {
1485 * MHD_HTTP_PROCESSING is a special response that
1486 * means a request-specific routine (e.g. show_parts)
1487 * created its own response. Therefore we shouldn't.
1489 resp
= MHD_create_response_from_data(0,NULL
,
1492 fprintf(stderr
,"MHD_crfd failed\n");
1495 MHD_queue_response(conn
,rc
,resp
);
1496 MHD_destroy_response(resp
);
1507 prov_list_generator (void *ctx
, uint64_t pos
, char *buf
, int max
)
1512 const provider_t
*prov
;
1513 const char *accept_hdr
;
1517 accept_hdr
= MHD_lookup_connection_value(ms
->conn
,MHD_HEADER_KIND
,
1521 ms
->gen_ctx
= tmpl_get_ctx(accept_hdr
);
1525 ms
->cleanup
|= CLEANUP_TMPL
;
1526 init_prov_iter(&ms
->prov_iter
);
1527 len
= tmpl_prov_header(ms
->gen_ctx
);
1534 memcpy(buf
,ms
->gen_ctx
->buf
,len
);
1538 if (ms
->gen_ctx
== TMPL_CTX_DONE
) {
1542 if (g_hash_table_iter_next(&ms
->prov_iter
,&key
,(gpointer
*)&prov
)) {
1543 len
= tmpl_prov_entry(ms
->gen_ctx
,prov
->name
,prov
->type
,
1544 prov
->host
, prov
->port
, prov
->username
, prov
->password
);
1551 memcpy(buf
,ms
->gen_ctx
->buf
,len
);
1555 len
= tmpl_prov_footer(ms
->gen_ctx
);
1562 memcpy(buf
,ms
->gen_ctx
->buf
,len
);
1564 ms
->cleanup
&= ~CLEANUP_TMPL
;
1565 ms
->gen_ctx
= TMPL_CTX_DONE
;
1570 proxy_list_provs (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
1571 const char *method
, const char *version
, const char *data
,
1572 size_t *data_size
, void **rctx
)
1574 struct MHD_Response
*resp
;
1575 my_state
*ms
= *rctx
;
1584 resp
= MHD_create_response_from_callback(MHD_SIZE_UNKNOWN
,
1585 65536, prov_list_generator
, ms
, simple_closer
);
1587 fprintf(stderr
,"MHD_crfd failed\n");
1591 MHD_queue_response(conn
,MHD_HTTP_OK
,resp
);
1592 MHD_destroy_response(resp
);
1598 prov_iterator (void *ctx
, enum MHD_ValueKind kind
, const char *key
,
1599 const char *filename
, const char *content_type
,
1600 const char *transfer_encoding
, const char *data
,
1601 uint64_t off
, size_t size
)
1606 (void)transfer_encoding
;
1609 g_hash_table_insert(ctx
,strdup(key
),strndup(data
,size
));
1610 /* TBD: check return value for strdups (none avail for insert) */
1616 proxy_update_prov (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
1617 const char *method
, const char *version
, const char *data
,
1618 size_t *data_size
, void **rctx
)
1620 struct MHD_Response
*resp
;
1621 my_state
*ms
= *rctx
;
1631 if (ms
->state
== MS_NEW
) {
1632 ms
->state
= MS_NORMAL
;
1633 ms
->url
= (char *)url
;
1634 ms
->dict
= g_hash_table_new_full(
1635 g_str_hash
,g_str_equal
,free
,free
);
1636 ms
->cleanup
|= CLEANUP_DICT
;
1637 ms
->post
= MHD_create_post_processor(conn
,4096,
1638 prov_iterator
,ms
->dict
);
1639 ms
->cleanup
|= CLEANUP_POST
;
1641 else if (*data_size
) {
1642 MHD_post_process(ms
->post
,data
,*data_size
);
1646 rc
= MHD_HTTP_BAD_REQUEST
;
1647 provider
= g_hash_table_lookup(ms
->dict
,"provider");
1648 username
= g_hash_table_lookup(ms
->dict
,"username");
1649 password
= g_hash_table_lookup(ms
->dict
,"password");
1650 if (provider
&& username
&& password
) {
1651 update_provider(provider
,username
,password
);
1655 DPRINTF("provider/username/password MISSING\n");
1657 resp
= MHD_create_response_from_data(0,NULL
,MHD_NO
,MHD_NO
);
1659 fprintf(stderr
,"MHD_crfd failed\n");
1662 MHD_queue_response(conn
,rc
,resp
);
1663 MHD_destroy_response(resp
);
1671 proxy_create_bucket (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
1672 const char *method
, const char *version
, const char *data
,
1673 size_t *data_size
, void **rctx
)
1675 struct MHD_Response
*resp
;
1676 my_state
*ms
= *rctx
;
1686 /* curl -T moo.empty http://localhost:9090/_new by accident */
1687 rc
= create_bucket(ms
->bucket
,ms
);
1689 resp
= MHD_create_response_from_data(0,NULL
,MHD_NO
,MHD_NO
);
1691 fprintf(stderr
,"MHD_crfd failed\n");
1694 MHD_queue_response(conn
,rc
,resp
);
1695 MHD_destroy_response(resp
);
1701 static const rule my_rules
[] = {
1702 { /* get bucket list */
1703 "GET", URL_ROOT
, proxy_api_root
},
1704 { /* perform a control operation on the API root */
1705 "POST", URL_ROOT
, control_api_root
},
1706 { /* get object list */
1707 "GET", URL_BUCKET
, proxy_list_objs
},
1708 { /* create bucket */
1709 "PUT", URL_BUCKET
, proxy_create_bucket
},
1710 { /* get object data */
1711 "GET", URL_OBJECT
, proxy_get_data
},
1712 { /* get attribute data */
1713 "GET", URL_ATTR
, proxy_get_attr
},
1714 { /* put object data */
1715 "PUT", URL_OBJECT
, proxy_put_data
},
1716 { /* put attribute data */
1717 "PUT", URL_ATTR
, proxy_put_attr
},
1718 { /* create object and/or modify attributes */
1719 "POST", URL_BUCKET
, proxy_bucket_post
},
1720 { /* perform control operations on an object */
1721 "POST", URL_OBJECT
, proxy_object_post
},
1723 "POST", URL_QUERY
, proxy_query
},
1724 { /* delete object */
1725 "DELETE", URL_OBJECT
, proxy_delete
},
1726 { /* delete attribute (TBD) */
1727 "DELETE", URL_ATTR
, NULL
},
1728 { /* get provider list */
1729 "GET", URL_PROVLIST
, proxy_list_provs
},
1730 { /* update a provider */
1731 "POST", URL_PROVLIST
, proxy_update_prov
},
1736 parse_url (const char *url
, my_state
*ms
)
1738 unsigned short esize
;
1739 unsigned short eindex
;
1740 char *parts
[URL_INVAL
];
1742 if (strstr(url
,"../")) {
1743 /* Naughty, naughty. Never a good reason to allow this. */
1744 DPRINTF("Rejecting ../ in path.\n");
1749 parts
[URL_BUCKET
] = ms
->bucket
;
1750 parts
[URL_OBJECT
] = ms
->key
;
1751 parts
[URL_ATTR
] = ms
->attr
;
1754 while (*url
== '/') {
1759 if (eindex
== URL_BUCKET
) {
1760 if (!strcmp(ms
->bucket
,"_providers")) {
1761 eindex
= URL_PROVLIST
;
1764 else if (eindex
== URL_OBJECT
) {
1765 if (!strcmp(ms
->key
,"_query")) {
1772 if (++eindex
>= URL_INVAL
) {
1777 while (*url
&& (*url
!= '/')) {
1778 parts
[eindex
][esize
++] = *(url
++);
1779 if (esize
>= MAX_FIELD_LEN
) {
1789 access_handler (void *cctx
, struct MHD_Connection
*conn
, const char *url
,
1790 const char *method
, const char *version
, const char *data
,
1791 size_t *data_size
, void **rctx
)
1795 struct MHD_Response
*resp
;
1796 my_state
*ms
= *rctx
;
1799 return ms
->handler(cctx
,conn
,url
,method
,version
,
1800 data
,data_size
,rctx
);
1803 ms
= calloc(sizeof(*ms
), 1);
1809 utype
= parse_url(url
,ms
);
1811 for (i
= 0; my_rules
[i
].method
; ++i
) {
1812 if (utype
!= my_rules
[i
].utype
) {
1815 if (strcmp(method
,my_rules
[i
].method
)) {
1818 if (!my_rules
[i
].handler
) {
1821 ms
->handler
= my_rules
[i
].handler
;
1827 return ms
->handler(cctx
,conn
,url
,method
,version
,
1828 data
,data_size
,rctx
);
1831 /* Don't need this after all. Free before the next check. */
1834 if (!strcmp(method
,"QUIT")) {
1835 (void)sem_post((sem_t
*)cctx
);
1839 fprintf(stderr
,"bad request m=%s u=%s\n",method
,url
);
1841 resp
= MHD_create_response_from_data(0,NULL
,MHD_NO
,MHD_NO
);
1845 MHD_queue_response(conn
,MHD_HTTP_NOT_FOUND
,resp
);
1846 MHD_destroy_response(resp
);
1850 /* These enum values cannot possibly conflict with the option values
1851 ordinarily used by commands, including CHAR_MAX + 1, etc. Avoid
1852 CHAR_MIN - 1, as it may equal -1, the getopt end-of-options value. */
1855 GETOPT_HELP_CHAR
= (CHAR_MIN
- 2),
1856 GETOPT_VERSION_CHAR
= (CHAR_MIN
- 3)
1859 static const struct option my_options
[] = {
1860 { "autostart", no_argument
, NULL
, 'a' },
1861 { "config", required_argument
, NULL
, 'c' },
1862 { "db", required_argument
, NULL
, 'd' },
1863 { "master", required_argument
, NULL
, 'm' },
1864 { "port", required_argument
, NULL
, 'p' },
1865 { "verbose", no_argument
, NULL
, 'v' },
1866 { "version", no_argument
, NULL
, GETOPT_VERSION_CHAR
},
1867 { "help", no_argument
, NULL
, GETOPT_HELP_CHAR
},
1868 { NULL
, 0, NULL
, '\0' }
1874 if (status
!= EXIT_SUCCESS
)
1875 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
1880 Usage: %s [OPTION]\n\
1884 Deltacloud image-warehouse daemon.\n\
1885 A configuration file must be specified.\n\
1887 -a, --autostart start necessary back-end services\n\
1888 -c, --config=FILE config file [required]\n\
1889 -d, --db=HOST_PORT database server as ip[:port]\n\
1890 -m, --master=HOST_PORT master (upstream) server as ip[:port]\n\
1891 -p, --port=PORT alternate listen port (default 9090)\n\
1892 -v, --verbose verbose/debug output\n\
1894 --help display this help and exit\n\
1895 --version output version information and exit\n\
1899 Report %s bugs to %s.\n\
1901 program_name
, PACKAGE_BUGREPORT
);
1908 main (int argc
, char **argv
)
1910 struct MHD_Daemon
*the_daemon
;
1916 program_name
= argv
[0];
1918 for (;;) switch (getopt_long(argc
,argv
,"ac:d:m:p:v",my_options
,NULL
)) {
1927 db_host
= strtok_r(optarg
,":",&stctx
);
1928 port_tmp
= strtok_r(NULL
,":",&stctx
);
1930 db_port
= (unsigned short)strtoul(port_tmp
,NULL
,10);
1935 master_host
= strtok_r(optarg
,":",&stctx
);
1936 port_tmp
= strtok_r(NULL
,":",&stctx
);
1938 master_port
= (unsigned short)strtoul(port_tmp
,NULL
,10);
1942 my_port
= (unsigned short)strtoul(optarg
,NULL
,10);
1947 case GETOPT_HELP_CHAR
:
1948 usage(EXIT_SUCCESS
);
1950 case GETOPT_VERSION_CHAR
:
1951 printf ("%s version %s\n", program_name
, PACKAGE_VERSION
);
1952 exit (EXIT_SUCCESS
);
1958 usage(EXIT_FAILURE
);
1964 db_port
= autostart
? AUTO_MONGOD_PORT
: 27017;
1967 if (autostart
&& cfg_file
) {
1968 error(0,0,"do not use -c and -a simultaneously");
1971 else if (autostart
&& !cfg_file
) {
1978 else if (!autostart
&& cfg_file
) {
1979 me
= parse_config(cfg_file
);
1981 error(0,0,"could not parse %s",cfg_file
);
1986 error(0,0,"specify at least -c or -a");
1987 usage (EXIT_FAILURE
);
1990 sem_init(&the_sem
,0,0);
1993 printf("primary store type is %s\n",main_prov
->type
);
1995 printf("operating as slave to %s:%u\n",
1996 master_host
, master_port
);
1998 printf("db is at %s:%u\n",db_host
,db_port
);
1999 printf("will listen on port %u\n",my_port
);
2000 printf("my location is \"%s\"\n",me
);
2001 if (fflush(stdout
) || ferror(stdout
))
2002 error(EXIT_FAILURE
, 0, "write failed");
2010 * Gotcha: if we don't set the connection memory limit explicitly,
2011 * the per-connection buffer for MHD will be smaller than that used
2012 * by CURL, so proxy_writefunc will never be able to do its job.
2014 the_daemon
= MHD_start_daemon(MY_MHD_FLAGS
,
2015 my_port
, NULL
, NULL
, &access_handler
, &the_sem
,
2016 MHD_OPTION_CONNECTION_MEMORY_LIMIT
, (size_t)1048576,
2019 fprintf(stderr
,"Could not create daemon.\n");