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