00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060 #include <stdio.h>
00061 #include <string.h>
00062 #include <stdlib.h>
00063 #include <sys/time.h>
00064 #include <time.h>
00065 #include <unistd.h>
00066 #include <sys/poll.h>
00067 #include <curl/curl.h>
00068 #include <event2/event.h>
00069 #include <fcntl.h>
00070 #include <sys/stat.h>
00071 #include <errno.h>
00072
00073
00074 #define MSG_OUT stdout
00075
00076
00077
00078 typedef struct _GlobalInfo
00079 {
00080 struct event_base *evbase;
00081 struct event *fifo_event;
00082 struct event *timer_event;
00083 CURLM *multi;
00084 int still_running;
00085 FILE *input;
00086 } GlobalInfo;
00087
00088
00089
00090 typedef struct _ConnInfo
00091 {
00092 CURL *easy;
00093 char *url;
00094 GlobalInfo *global;
00095 char error[CURL_ERROR_SIZE];
00096 } ConnInfo;
00097
00098
00099
00100 typedef struct _SockInfo
00101 {
00102 curl_socket_t sockfd;
00103 CURL *easy;
00104 int action;
00105 long timeout;
00106 struct event *ev;
00107 int evset;
00108 GlobalInfo *global;
00109 } SockInfo;
00110
00111
00112
00113
00114 static int multi_timer_cb(CURLM *multi, long timeout_ms, GlobalInfo *g)
00115 {
00116 struct timeval timeout;
00117 (void)multi;
00118
00119 timeout.tv_sec = timeout_ms/1000;
00120 timeout.tv_usec = (timeout_ms%1000)*1000;
00121 fprintf(MSG_OUT, "multi_timer_cb: Setting timeout to %ld ms\n", timeout_ms);
00122 evtimer_add(g->timer_event, &timeout);
00123 return 0;
00124 }
00125
00126
00127 static void mcode_or_die(const char *where, CURLMcode code)
00128 {
00129 if(CURLM_OK != code) {
00130 const char *s;
00131 switch(code) {
00132 case CURLM_BAD_HANDLE: s="CURLM_BAD_HANDLE"; break;
00133 case CURLM_BAD_EASY_HANDLE: s="CURLM_BAD_EASY_HANDLE"; break;
00134 case CURLM_OUT_OF_MEMORY: s="CURLM_OUT_OF_MEMORY"; break;
00135 case CURLM_INTERNAL_ERROR: s="CURLM_INTERNAL_ERROR"; break;
00136 case CURLM_UNKNOWN_OPTION: s="CURLM_UNKNOWN_OPTION"; break;
00137 case CURLM_LAST: s="CURLM_LAST"; break;
00138 default: s="CURLM_unknown";
00139 break;
00140 case CURLM_BAD_SOCKET: s="CURLM_BAD_SOCKET";
00141 fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
00142
00143 return;
00144 }
00145 fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
00146 exit(code);
00147 }
00148 }
00149
00150
00151
00152
00153 static void check_multi_info(GlobalInfo *g)
00154 {
00155 char *eff_url;
00156 CURLMsg *msg;
00157 int msgs_left;
00158 ConnInfo *conn;
00159 CURL *easy;
00160 CURLcode res;
00161
00162 fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running);
00163 while((msg = curl_multi_info_read(g->multi, &msgs_left))) {
00164 if(msg->msg == CURLMSG_DONE) {
00165 easy = msg->easy_handle;
00166 res = msg->data.result;
00167 curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
00168 curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
00169 fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error);
00170 curl_multi_remove_handle(g->multi, easy);
00171 free(conn->url);
00172 curl_easy_cleanup(easy);
00173 free(conn);
00174 }
00175 }
00176 }
00177
00178
00179
00180
00181 static void event_cb(int fd, short kind, void *userp)
00182 {
00183 GlobalInfo *g = (GlobalInfo*) userp;
00184 CURLMcode rc;
00185
00186 int action =
00187 (kind & EV_READ ? CURL_CSELECT_IN : 0) |
00188 (kind & EV_WRITE ? CURL_CSELECT_OUT : 0);
00189
00190 rc = curl_multi_socket_action(g->multi, fd, action, &g->still_running);
00191 mcode_or_die("event_cb: curl_multi_socket_action", rc);
00192
00193 check_multi_info(g);
00194 if(g->still_running <= 0) {
00195 fprintf(MSG_OUT, "last transfer done, kill timeout\n");
00196 if(evtimer_pending(g->timer_event, NULL)) {
00197 evtimer_del(g->timer_event);
00198 }
00199 }
00200 }
00201
00202
00203
00204
00205 static void timer_cb(int fd, short kind, void *userp)
00206 {
00207 GlobalInfo *g = (GlobalInfo *)userp;
00208 CURLMcode rc;
00209 (void)fd;
00210 (void)kind;
00211
00212 rc = curl_multi_socket_action(g->multi,
00213 CURL_SOCKET_TIMEOUT, 0, &g->still_running);
00214 mcode_or_die("timer_cb: curl_multi_socket_action", rc);
00215 check_multi_info(g);
00216 }
00217
00218
00219
00220
00221 static void remsock(SockInfo *f)
00222 {
00223 if(f) {
00224 if(f->evset)
00225 event_free(f->ev);
00226 free(f);
00227 }
00228 }
00229
00230
00231
00232
00233 static void setsock(SockInfo *f, curl_socket_t s, CURL *e, int act,
00234 GlobalInfo *g)
00235 {
00236 int kind =
00237 (act&CURL_POLL_IN?EV_READ:0)|(act&CURL_POLL_OUT?EV_WRITE:0)|EV_PERSIST;
00238
00239 f->sockfd = s;
00240 f->action = act;
00241 f->easy = e;
00242 if(f->evset)
00243 event_free(f->ev);
00244 f->ev = event_new(g->evbase, f->sockfd, kind, event_cb, g);
00245 f->evset = 1;
00246 event_add(f->ev, NULL);
00247 }
00248
00249
00250
00251
00252 static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
00253 {
00254 SockInfo *fdp = calloc(sizeof(SockInfo), 1);
00255
00256 fdp->global = g;
00257 setsock(fdp, s, easy, action, g);
00258 curl_multi_assign(g->multi, s, fdp);
00259 }
00260
00261
00262 static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
00263 {
00264 GlobalInfo *g = (GlobalInfo*) cbp;
00265 SockInfo *fdp = (SockInfo*) sockp;
00266 const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE" };
00267
00268 fprintf(MSG_OUT,
00269 "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]);
00270 if(what == CURL_POLL_REMOVE) {
00271 fprintf(MSG_OUT, "\n");
00272 remsock(fdp);
00273 }
00274 else {
00275 if(!fdp) {
00276 fprintf(MSG_OUT, "Adding data: %s\n", whatstr[what]);
00277 addsock(s, e, what, g);
00278 }
00279 else {
00280 fprintf(MSG_OUT,
00281 "Changing action from %s to %s\n",
00282 whatstr[fdp->action], whatstr[what]);
00283 setsock(fdp, s, e, what, g);
00284 }
00285 }
00286 return 0;
00287 }
00288
00289
00290
00291
00292 static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
00293 {
00294 size_t realsize = size * nmemb;
00295 ConnInfo *conn = (ConnInfo*) data;
00296 (void)ptr;
00297 (void)conn;
00298 return realsize;
00299 }
00300
00301
00302
00303 static int prog_cb(void *p, double dltotal, double dlnow, double ult,
00304 double uln)
00305 {
00306 ConnInfo *conn = (ConnInfo *)p;
00307 (void)ult;
00308 (void)uln;
00309
00310 fprintf(MSG_OUT, "Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
00311 return 0;
00312 }
00313
00314
00315
00316 static void new_conn(char *url, GlobalInfo *g)
00317 {
00318 ConnInfo *conn;
00319 CURLMcode rc;
00320
00321 conn = calloc(1, sizeof(ConnInfo));
00322 memset(conn, 0, sizeof(ConnInfo));
00323 conn->error[0]='\0';
00324
00325 conn->easy = curl_easy_init();
00326 if(!conn->easy) {
00327 fprintf(MSG_OUT, "curl_easy_init() failed, exiting!\n");
00328 exit(2);
00329 }
00330 conn->global = g;
00331 conn->url = strdup(url);
00332 curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url);
00333 curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb);
00334 curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, conn);
00335 curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L);
00336 curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
00337 curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
00338 curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0L);
00339 curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
00340 curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn);
00341 fprintf(MSG_OUT,
00342 "Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url);
00343 rc = curl_multi_add_handle(g->multi, conn->easy);
00344 mcode_or_die("new_conn: curl_multi_add_handle", rc);
00345
00346
00347
00348 }
00349
00350
00351 static void fifo_cb(int fd, short event, void *arg)
00352 {
00353 char s[1024];
00354 long int rv=0;
00355 int n=0;
00356 GlobalInfo *g = (GlobalInfo *)arg;
00357 (void)fd;
00358 (void)event;
00359
00360 do {
00361 s[0]='\0';
00362 rv=fscanf(g->input, "%1023s%n", s, &n);
00363 s[n]='\0';
00364 if(n && s[0]) {
00365 new_conn(s, arg);
00366 }
00367 else
00368 break;
00369 } while(rv != EOF);
00370 }
00371
00372
00373 static const char *fifo = "hiper.fifo";
00374 static int init_fifo(GlobalInfo *g)
00375 {
00376 struct stat st;
00377 curl_socket_t sockfd;
00378
00379 fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo);
00380 if(lstat (fifo, &st) == 0) {
00381 if((st.st_mode & S_IFMT) == S_IFREG) {
00382 errno = EEXIST;
00383 perror("lstat");
00384 exit(1);
00385 }
00386 }
00387 unlink(fifo);
00388 if(mkfifo (fifo, 0600) == -1) {
00389 perror("mkfifo");
00390 exit(1);
00391 }
00392 sockfd = open(fifo, O_RDWR | O_NONBLOCK, 0);
00393 if(sockfd == -1) {
00394 perror("open");
00395 exit(1);
00396 }
00397 g->input = fdopen(sockfd, "r");
00398
00399 fprintf(MSG_OUT, "Now, pipe some URL's into > %s\n", fifo);
00400 g->fifo_event = event_new(g->evbase, sockfd, EV_READ|EV_PERSIST, fifo_cb, g);
00401 event_add(g->fifo_event, NULL);
00402 return (0);
00403 }
00404
00405 static void clean_fifo(GlobalInfo *g)
00406 {
00407 event_free(g->fifo_event);
00408 fclose(g->input);
00409 unlink(fifo);
00410 }
00411
00412 int main(int argc, char **argv)
00413 {
00414 GlobalInfo g;
00415 (void)argc;
00416 (void)argv;
00417
00418 memset(&g, 0, sizeof(GlobalInfo));
00419 g.evbase = event_base_new();
00420 init_fifo(&g);
00421 g.multi = curl_multi_init();
00422 g.timer_event = evtimer_new(g.evbase, timer_cb, &g);
00423
00424
00425 curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
00426 curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g);
00427 curl_multi_setopt(g.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
00428 curl_multi_setopt(g.multi, CURLMOPT_TIMERDATA, &g);
00429
00430
00431
00432
00433 event_base_dispatch(g.evbase);
00434
00435
00436
00437 clean_fifo(&g);
00438 event_free(g.timer_event);
00439 event_base_free(g.evbase);
00440 curl_multi_cleanup(g.multi);
00441 return 0;
00442 }