hiperfifo.c
Go to the documentation of this file.
1 /***************************************************************************
2  * _ _ ____ _
3  * Project ___| | | | _ \| |
4  * / __| | | | |_) | |
5  * | (__| |_| | _ <| |___
6  * \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  ***************************************************************************/
22 /* <DESC>
23  * multi socket API usage with libevent 2
24  * </DESC>
25  */
26 /* Example application source code using the multi socket interface to
27  download many files at once.
28 
29 Written by Jeff Pohlmeyer
30 
31 Requires libevent version 2 and a (POSIX?) system that has mkfifo().
32 
33 This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c"
34 sample programs.
35 
36 When running, the program creates the named pipe "hiper.fifo"
37 
38 Whenever there is input into the fifo, the program reads the input as a list
39 of URL's and creates some new easy handles to fetch each URL via the
40 curl_multi "hiper" API.
41 
42 
43 Thus, you can try a single URL:
44  % echo http://www.yahoo.com > hiper.fifo
45 
46 Or a whole bunch of them:
47  % cat my-url-list > hiper.fifo
48 
49 The fifo buffer is handled almost instantly, so you can even add more URL's
50 while the previous requests are still being downloaded.
51 
52 Note:
53  For the sake of simplicity, URL length is limited to 1023 char's !
54 
55 This is purely a demo app, all retrieved data is simply discarded by the write
56 callback.
57 
58 */
59 
60 #include <stdio.h>
61 #include <string.h>
62 #include <stdlib.h>
63 #include <sys/time.h>
64 #include <time.h>
65 #include <unistd.h>
66 #include <sys/poll.h>
67 #include <curl/curl.h>
68 #include <event2/event.h>
69 #include <fcntl.h>
70 #include <sys/stat.h>
71 #include <errno.h>
72 
73 
74 #define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */
75 
76 
77 /* Global information, common to all connections */
78 typedef struct _GlobalInfo
79 {
80  struct event_base *evbase;
81  struct event *fifo_event;
82  struct event *timer_event;
83  CURLM *multi;
84  int still_running;
85  FILE *input;
86 } GlobalInfo;
87 
88 
89 /* Information associated with a specific easy handle */
90 typedef struct _ConnInfo
91 {
92  CURL *easy;
93  char *url;
94  GlobalInfo *global;
95  char error[CURL_ERROR_SIZE];
96 } ConnInfo;
97 
98 
99 /* Information associated with a specific socket */
100 typedef struct _SockInfo
101 {
102  curl_socket_t sockfd;
103  CURL *easy;
104  int action;
105  long timeout;
106  struct event *ev;
107  int evset;
108  GlobalInfo *global;
109 } SockInfo;
110 
111 /* Update the event timer after curl_multi library calls */
112 static int multi_timer_cb(CURLM *multi, long timeout_ms, GlobalInfo *g)
113 {
114  struct timeval timeout;
115  (void)multi; /* unused */
116 
117  timeout.tv_sec = timeout_ms/1000;
118  timeout.tv_usec = (timeout_ms%1000)*1000;
119  fprintf(MSG_OUT, "multi_timer_cb: Setting timeout to %ld ms\n", timeout_ms);
120 
121  /* TODO
122  *
123  * if timeout_ms is 0, call curl_multi_socket_action() at once!
124  *
125  * if timeout_ms is -1, just delete the timer
126  *
127  * for all other values of timeout_ms, this should set or *update*
128  * the timer to the new value
129  */
130  evtimer_add(g->timer_event, &timeout);
131  return 0;
132 }
133 
134 /* Die if we get a bad CURLMcode somewhere */
135 static void mcode_or_die(const char *where, CURLMcode code)
136 {
137  if(CURLM_OK != code) {
138  const char *s;
139  switch(code) {
140  case CURLM_BAD_HANDLE: s = "CURLM_BAD_HANDLE"; break;
141  case CURLM_BAD_EASY_HANDLE: s = "CURLM_BAD_EASY_HANDLE"; break;
142  case CURLM_OUT_OF_MEMORY: s = "CURLM_OUT_OF_MEMORY"; break;
143  case CURLM_INTERNAL_ERROR: s = "CURLM_INTERNAL_ERROR"; break;
144  case CURLM_UNKNOWN_OPTION: s = "CURLM_UNKNOWN_OPTION"; break;
145  case CURLM_LAST: s = "CURLM_LAST"; break;
146  default: s = "CURLM_unknown";
147  break;
148  case CURLM_BAD_SOCKET: s = "CURLM_BAD_SOCKET";
149  fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
150  /* ignore this error */
151  return;
152  }
153  fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
154  exit(code);
155  }
156 }
157 
158 
159 
160 /* Check for completed transfers, and remove their easy handles */
162 {
163  char *eff_url;
164  CURLMsg *msg;
165  int msgs_left;
166  ConnInfo *conn;
167  CURL *easy;
168  CURLcode res;
169 
170  fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running);
171  while((msg = curl_multi_info_read(g->multi, &msgs_left))) {
172  if(msg->msg == CURLMSG_DONE) {
173  easy = msg->easy_handle;
174  res = msg->data.result;
175  curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
176  curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
177  fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error);
179  free(conn->url);
180  curl_easy_cleanup(easy);
181  free(conn);
182  }
183  }
184 }
185 
186 
187 
188 /* Called by libevent when we get action on a multi socket */
189 static void event_cb(int fd, short kind, void *userp)
190 {
191  GlobalInfo *g = (GlobalInfo*) userp;
192  CURLMcode rc;
193 
194  int action =
195  (kind & EV_READ ? CURL_CSELECT_IN : 0) |
196  (kind & EV_WRITE ? CURL_CSELECT_OUT : 0);
197 
198  rc = curl_multi_socket_action(g->multi, fd, action, &g->still_running);
199  mcode_or_die("event_cb: curl_multi_socket_action", rc);
200 
201  check_multi_info(g);
202  if(g->still_running <= 0) {
203  fprintf(MSG_OUT, "last transfer done, kill timeout\n");
204  if(evtimer_pending(g->timer_event, NULL)) {
205  evtimer_del(g->timer_event);
206  }
207  }
208 }
209 
210 
211 
212 /* Called by libevent when our timeout expires */
213 static void timer_cb(int fd, short kind, void *userp)
214 {
215  GlobalInfo *g = (GlobalInfo *)userp;
216  CURLMcode rc;
217  (void)fd;
218  (void)kind;
219 
222  mcode_or_die("timer_cb: curl_multi_socket_action", rc);
223  check_multi_info(g);
224 }
225 
226 
227 
228 /* Clean up the SockInfo structure */
229 static void remsock(SockInfo *f)
230 {
231  if(f) {
232  if(f->evset)
233  event_free(f->ev);
234  free(f);
235  }
236 }
237 
238 
239 
240 /* Assign information to a SockInfo structure */
241 static void setsock(SockInfo *f, curl_socket_t s, CURL *e, int act,
242  GlobalInfo *g)
243 {
244  int kind =
245  (act&CURL_POLL_IN?EV_READ:0)|(act&CURL_POLL_OUT?EV_WRITE:0)|EV_PERSIST;
246 
247  f->sockfd = s;
248  f->action = act;
249  f->easy = e;
250  if(f->evset)
251  event_free(f->ev);
252  f->ev = event_new(g->evbase, f->sockfd, kind, event_cb, g);
253  f->evset = 1;
254  event_add(f->ev, NULL);
255 }
256 
257 
258 
259 /* Initialize a new SockInfo structure */
260 static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
261 {
262  SockInfo *fdp = calloc(sizeof(SockInfo), 1);
263 
264  fdp->global = g;
265  setsock(fdp, s, easy, action, g);
266  curl_multi_assign(g->multi, s, fdp);
267 }
268 
269 /* CURLMOPT_SOCKETFUNCTION */
270 static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
271 {
272  GlobalInfo *g = (GlobalInfo*) cbp;
273  SockInfo *fdp = (SockInfo*) sockp;
274  const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE" };
275 
277  "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]);
278  if(what == CURL_POLL_REMOVE) {
279  fprintf(MSG_OUT, "\n");
280  remsock(fdp);
281  }
282  else {
283  if(!fdp) {
284  fprintf(MSG_OUT, "Adding data: %s\n", whatstr[what]);
285  addsock(s, e, what, g);
286  }
287  else {
289  "Changing action from %s to %s\n",
290  whatstr[fdp->action], whatstr[what]);
291  setsock(fdp, s, e, what, g);
292  }
293  }
294  return 0;
295 }
296 
297 
298 
299 /* CURLOPT_WRITEFUNCTION */
300 static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
301 {
302  size_t realsize = size * nmemb;
303  ConnInfo *conn = (ConnInfo*) data;
304  (void)ptr;
305  (void)conn;
306  return realsize;
307 }
308 
309 
310 /* CURLOPT_PROGRESSFUNCTION */
311 static int prog_cb(void *p, double dltotal, double dlnow, double ult,
312  double uln)
313 {
314  ConnInfo *conn = (ConnInfo *)p;
315  (void)ult;
316  (void)uln;
317 
318  fprintf(MSG_OUT, "Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
319  return 0;
320 }
321 
322 
323 /* Create a new easy handle, and add it to the global curl_multi */
324 static void new_conn(char *url, GlobalInfo *g)
325 {
326  ConnInfo *conn;
327  CURLMcode rc;
328 
329  conn = calloc(1, sizeof(ConnInfo));
330  memset(conn, 0, sizeof(ConnInfo));
331  conn->error[0]='\0';
332 
333  conn->easy = curl_easy_init();
334  if(!conn->easy) {
335  fprintf(MSG_OUT, "curl_easy_init() failed, exiting!\n");
336  exit(2);
337  }
338  conn->global = g;
339  conn->url = strdup(url);
340  curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url);
341  curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb);
342  curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, conn);
343  curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L);
344  curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
345  curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
346  curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0L);
347  curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
348  curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn);
350  "Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url);
351  rc = curl_multi_add_handle(g->multi, conn->easy);
352  mcode_or_die("new_conn: curl_multi_add_handle", rc);
353 
354  /* note that the add_handle() will set a time-out to trigger very soon so
355  that the necessary socket_action() call will be called by this app */
356 }
357 
358 /* This gets called whenever data is received from the fifo */
359 static void fifo_cb(int fd, short event, void *arg)
360 {
361  char s[1024];
362  long int rv = 0;
363  int n = 0;
364  GlobalInfo *g = (GlobalInfo *)arg;
365  (void)fd; /* unused */
366  (void)event; /* unused */
367 
368  do {
369  s[0]='\0';
370  rv = fscanf(g->input, "%1023s%n", s, &n);
371  s[n]='\0';
372  if(n && s[0]) {
373  new_conn(s, arg); /* if we read a URL, go get it! */
374  }
375  else
376  break;
377  } while(rv != EOF);
378 }
379 
380 /* Create a named pipe and tell libevent to monitor it */
381 static const char *fifo = "hiper.fifo";
382 static int init_fifo(GlobalInfo *g)
383 {
384  struct stat st;
385  curl_socket_t sockfd;
386 
387  fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo);
388  if(lstat (fifo, &st) == 0) {
389  if((st.st_mode & S_IFMT) == S_IFREG) {
390  errno = EEXIST;
391  perror("lstat");
392  exit(1);
393  }
394  }
395  unlink(fifo);
396  if(mkfifo (fifo, 0600) == -1) {
397  perror("mkfifo");
398  exit(1);
399  }
400  sockfd = open(fifo, O_RDWR | O_NONBLOCK, 0);
401  if(sockfd == -1) {
402  perror("open");
403  exit(1);
404  }
405  g->input = fdopen(sockfd, "r");
406 
407  fprintf(MSG_OUT, "Now, pipe some URL's into > %s\n", fifo);
408  g->fifo_event = event_new(g->evbase, sockfd, EV_READ|EV_PERSIST, fifo_cb, g);
409  event_add(g->fifo_event, NULL);
410  return (0);
411 }
412 
413 static void clean_fifo(GlobalInfo *g)
414 {
415  event_free(g->fifo_event);
416  fclose(g->input);
417  unlink(fifo);
418 }
419 
420 int main(int argc, char **argv)
421 {
422  GlobalInfo g;
423  (void)argc;
424  (void)argv;
425 
426  memset(&g, 0, sizeof(GlobalInfo));
427  g.evbase = event_base_new();
428  init_fifo(&g);
429  g.multi = curl_multi_init();
430  g.timer_event = evtimer_new(g.evbase, timer_cb, &g);
431 
432  /* setup the generic multi interface options we want */
433  curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
434  curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g);
435  curl_multi_setopt(g.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
436  curl_multi_setopt(g.multi, CURLMOPT_TIMERDATA, &g);
437 
438  /* we don't call any curl_multi_socket*() function yet as we have no handles
439  added! */
440 
441  event_base_dispatch(g.evbase);
442 
443  /* this, of course, won't get called since only way to stop this program is
444  via ctrl-C, but it is here to show how cleanup /would/ be done. */
445  clean_fifo(&g);
446  event_free(g.timer_event);
447  event_base_free(g.evbase);
449  return 0;
450 }
#define free(ptr)
Definition: curl_memory.h:130
curl_socket_t sockfd
Definition: evhiperfifo.c:106
CURLM * multi
Definition: asiohiper.cpp:65
char * url
Definition: asiohiper.cpp:73
static int init_fifo(GlobalInfo *g)
Definition: hiperfifo.c:382
struct event * timer_event
Definition: hiperfifo.c:82
FILE * input
Definition: evhiperfifo.c:89
struct ev_timer timer_event
Definition: evhiperfifo.c:86
CURL * easy
Definition: evhiperfifo.c:107
GlobalInfo * global
Definition: evhiperfifo.c:112
CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle, CURL *curl_handle)
static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
Definition: hiperfifo.c:260
#define CURL_CSELECT_OUT
Definition: multi.h:265
int stat(const char *path, struct stat *buffer)
static int multi_timer_cb(CURLM *multi, long timeout_ms, GlobalInfo *g)
Definition: hiperfifo.c:112
static void mcode_or_die(const char *where, CURLMcode code)
Definition: hiperfifo.c:135
CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, curl_socket_t sockfd, void *sockp)
int main(int argc, char **argv)
Definition: hiperfifo.c:420
static void clean_fifo(GlobalInfo *g)
Definition: hiperfifo.c:413
#define strdup(ptr)
Definition: curl_memory.h:122
CURL * easy_handle
Definition: multi.h:95
XmlRpcServer s
uv_timer_t timeout
Definition: multi-uv.c:42
UNITTEST_START char * ptr
Definition: unit1330.c:38
CURLcode
Definition: curl.h:454
CURL * easy
Definition: asiohiper.cpp:72
struct _ConnInfo ConnInfo
Definition: multi.h:93
struct event_base * evbase
Definition: hiperfifo.c:80
struct ev_io ev
Definition: evhiperfifo.c:110
static int res
static void setsock(SockInfo *f, curl_socket_t s, CURL *e, int act, GlobalInfo *g)
Definition: hiperfifo.c:241
#define CURL_POLL_OUT
Definition: multi.h:258
struct event * fifo_event
Definition: hiperfifo.c:81
static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
Definition: hiperfifo.c:270
struct _GlobalInfo GlobalInfo
#define curl_easy_setopt(handle, option, value)
Definition: typecheck-gcc.h:41
const char ** p
Definition: unit1394.c:76
CURL_EXTERN CURLM * curl_multi_init(void)
Definition: multi.c:355
static void event_cb(int fd, short kind, void *userp)
Definition: hiperfifo.c:189
static int prog_cb(void *p, double dltotal, double dlnow, double ult, double uln)
Definition: hiperfifo.c:311
#define curl_multi_setopt(handle, opt, param)
#define CURL_CSELECT_IN
Definition: multi.h:264
#define curl_easy_getinfo(handle, info, arg)
UNITTEST_START int rc
Definition: unit1301.c:31
#define CURL_POLL_REMOVE
Definition: multi.h:260
struct _SockInfo SockInfo
#define CURL_POLL_IN
Definition: multi.h:257
CURL_EXTERN CURL * curl_easy_init(void)
Definition: easy.c:343
CURLMSG msg
Definition: multi.h:94
CURL_EXTERN void curl_easy_cleanup(CURL *curl)
static void check_multi_info(GlobalInfo *g)
Definition: hiperfifo.c:161
struct event * ev
Definition: hiperfifo.c:106
static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
Definition: hiperfifo.c:300
static void remsock(SockInfo *f)
Definition: hiperfifo.c:229
action
CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle, CURL *curl_handle)
CURLMcode
Definition: multi.h:61
#define CURL_SOCKET_TIMEOUT
Definition: multi.h:262
int still_running
Definition: asiohiper.cpp:66
char error[CURL_ERROR_SIZE]
Definition: asiohiper.cpp:75
void CURLM
Definition: multi.h:58
struct ev_io fifo_event
Definition: evhiperfifo.c:85
GlobalInfo * global
Definition: asiohiper.cpp:74
void CURL
Definition: curl.h:102
static const char * fifo
Definition: hiperfifo.c:381
CURL_EXTERN CURLMsg * curl_multi_info_read(CURLM *multi_handle, int *msgs_in_queue)
static void new_conn(char *url, GlobalInfo *g)
Definition: hiperfifo.c:324
#define MSG_OUT
Definition: hiperfifo.c:74
Definition: multi.h:64
union CURLMsg::@6 data
#define CURL_ERROR_SIZE
Definition: curl.h:724
static void timer_cb(int fd, short kind, void *userp)
Definition: hiperfifo.c:213
size_t size
Definition: unit1302.c:52
#define fprintf
Definition: curl_printf.h:41
static CURL * easy[MAX_EASY_HANDLES]
static void fifo_cb(int fd, short event, void *arg)
Definition: hiperfifo.c:359
CURLcode result
Definition: multi.h:98
int curl_socket_t
Definition: curl.h:130
CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle, curl_socket_t s, int ev_bitmask, int *running_handles)
Definition: debug.c:29
CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle)
#define calloc(nbelem, size)
Definition: curl_memory.h:126


rc_tagdetect_client
Author(s): Monika Florek-Jasinska , Raphael Schaller
autogenerated on Sat Feb 13 2021 03:42:15