113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci/* <DESC>
2513498266Sopenharmony_ci * multi socket API usage with epoll and timerfd
2613498266Sopenharmony_ci * </DESC>
2713498266Sopenharmony_ci */
2813498266Sopenharmony_ci/* Example application source code using the multi socket interface to
2913498266Sopenharmony_ci * download many files at once.
3013498266Sopenharmony_ci *
3113498266Sopenharmony_ci * This example features the same basic functionality as hiperfifo.c does,
3213498266Sopenharmony_ci * but this uses epoll and timerfd instead of libevent.
3313498266Sopenharmony_ci *
3413498266Sopenharmony_ci * Written by Jeff Pohlmeyer, converted to use epoll by Josh Bialkowski
3513498266Sopenharmony_ci
3613498266Sopenharmony_ciRequires a linux system with epoll
3713498266Sopenharmony_ci
3813498266Sopenharmony_ciWhen running, the program creates the named pipe "hiper.fifo"
3913498266Sopenharmony_ci
4013498266Sopenharmony_ciWhenever there is input into the fifo, the program reads the input as a list
4113498266Sopenharmony_ciof URL's and creates some new easy handles to fetch each URL via the
4213498266Sopenharmony_cicurl_multi "hiper" API.
4313498266Sopenharmony_ci
4413498266Sopenharmony_ci
4513498266Sopenharmony_ciThus, you can try a single URL:
4613498266Sopenharmony_ci  % echo http://www.yahoo.com > hiper.fifo
4713498266Sopenharmony_ci
4813498266Sopenharmony_ciOr a whole bunch of them:
4913498266Sopenharmony_ci  % cat my-url-list > hiper.fifo
5013498266Sopenharmony_ci
5113498266Sopenharmony_ciThe fifo buffer is handled almost instantly, so you can even add more URL's
5213498266Sopenharmony_ciwhile the previous requests are still being downloaded.
5313498266Sopenharmony_ci
5413498266Sopenharmony_ciNote:
5513498266Sopenharmony_ci  For the sake of simplicity, URL length is limited to 1023 char's !
5613498266Sopenharmony_ci
5713498266Sopenharmony_ciThis is purely a demo app, all retrieved data is simply discarded by the write
5813498266Sopenharmony_cicallback.
5913498266Sopenharmony_ci
6013498266Sopenharmony_ci*/
6113498266Sopenharmony_ci
6213498266Sopenharmony_ci#include <errno.h>
6313498266Sopenharmony_ci#include <fcntl.h>
6413498266Sopenharmony_ci#include <signal.h>
6513498266Sopenharmony_ci#include <stdio.h>
6613498266Sopenharmony_ci#include <stdlib.h>
6713498266Sopenharmony_ci#include <string.h>
6813498266Sopenharmony_ci#include <sys/epoll.h>
6913498266Sopenharmony_ci#include <sys/stat.h>
7013498266Sopenharmony_ci#include <sys/time.h>
7113498266Sopenharmony_ci#include <sys/timerfd.h>
7213498266Sopenharmony_ci#include <sys/types.h>
7313498266Sopenharmony_ci#include <time.h>
7413498266Sopenharmony_ci#include <unistd.h>
7513498266Sopenharmony_ci
7613498266Sopenharmony_ci#include <curl/curl.h>
7713498266Sopenharmony_ci
7813498266Sopenharmony_ci#define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */
7913498266Sopenharmony_ci
8013498266Sopenharmony_ci
8113498266Sopenharmony_ci/* Global information, common to all connections */
8213498266Sopenharmony_citypedef struct _GlobalInfo
8313498266Sopenharmony_ci{
8413498266Sopenharmony_ci  int epfd;    /* epoll filedescriptor */
8513498266Sopenharmony_ci  int tfd;     /* timer filedescriptor */
8613498266Sopenharmony_ci  int fifofd;  /* fifo filedescriptor */
8713498266Sopenharmony_ci  CURLM *multi;
8813498266Sopenharmony_ci  int still_running;
8913498266Sopenharmony_ci  FILE *input;
9013498266Sopenharmony_ci} GlobalInfo;
9113498266Sopenharmony_ci
9213498266Sopenharmony_ci
9313498266Sopenharmony_ci/* Information associated with a specific easy handle */
9413498266Sopenharmony_citypedef struct _ConnInfo
9513498266Sopenharmony_ci{
9613498266Sopenharmony_ci  CURL *easy;
9713498266Sopenharmony_ci  char *url;
9813498266Sopenharmony_ci  GlobalInfo *global;
9913498266Sopenharmony_ci  char error[CURL_ERROR_SIZE];
10013498266Sopenharmony_ci} ConnInfo;
10113498266Sopenharmony_ci
10213498266Sopenharmony_ci
10313498266Sopenharmony_ci/* Information associated with a specific socket */
10413498266Sopenharmony_citypedef struct _SockInfo
10513498266Sopenharmony_ci{
10613498266Sopenharmony_ci  curl_socket_t sockfd;
10713498266Sopenharmony_ci  CURL *easy;
10813498266Sopenharmony_ci  int action;
10913498266Sopenharmony_ci  long timeout;
11013498266Sopenharmony_ci  GlobalInfo *global;
11113498266Sopenharmony_ci} SockInfo;
11213498266Sopenharmony_ci
11313498266Sopenharmony_ci#define mycase(code) \
11413498266Sopenharmony_ci  case code: s = __STRING(code)
11513498266Sopenharmony_ci
11613498266Sopenharmony_ci/* Die if we get a bad CURLMcode somewhere */
11713498266Sopenharmony_cistatic void mcode_or_die(const char *where, CURLMcode code)
11813498266Sopenharmony_ci{
11913498266Sopenharmony_ci  if(CURLM_OK != code) {
12013498266Sopenharmony_ci    const char *s;
12113498266Sopenharmony_ci    switch(code) {
12213498266Sopenharmony_ci      mycase(CURLM_BAD_HANDLE); break;
12313498266Sopenharmony_ci      mycase(CURLM_BAD_EASY_HANDLE); break;
12413498266Sopenharmony_ci      mycase(CURLM_OUT_OF_MEMORY); break;
12513498266Sopenharmony_ci      mycase(CURLM_INTERNAL_ERROR); break;
12613498266Sopenharmony_ci      mycase(CURLM_UNKNOWN_OPTION); break;
12713498266Sopenharmony_ci      mycase(CURLM_LAST); break;
12813498266Sopenharmony_ci      default: s = "CURLM_unknown"; break;
12913498266Sopenharmony_ci      mycase(CURLM_BAD_SOCKET);
13013498266Sopenharmony_ci      fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
13113498266Sopenharmony_ci      /* ignore this error */
13213498266Sopenharmony_ci      return;
13313498266Sopenharmony_ci    }
13413498266Sopenharmony_ci    fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
13513498266Sopenharmony_ci    exit(code);
13613498266Sopenharmony_ci  }
13713498266Sopenharmony_ci}
13813498266Sopenharmony_ci
13913498266Sopenharmony_cistatic void timer_cb(GlobalInfo* g, int revents);
14013498266Sopenharmony_ci
14113498266Sopenharmony_ci/* Update the timer after curl_multi library does it's thing. Curl will
14213498266Sopenharmony_ci * inform us through this callback what it wants the new timeout to be,
14313498266Sopenharmony_ci * after it does some work. */
14413498266Sopenharmony_cistatic int multi_timer_cb(CURLM *multi, long timeout_ms, GlobalInfo *g)
14513498266Sopenharmony_ci{
14613498266Sopenharmony_ci  struct itimerspec its;
14713498266Sopenharmony_ci
14813498266Sopenharmony_ci  fprintf(MSG_OUT, "multi_timer_cb: Setting timeout to %ld ms\n", timeout_ms);
14913498266Sopenharmony_ci
15013498266Sopenharmony_ci  if(timeout_ms > 0) {
15113498266Sopenharmony_ci    its.it_interval.tv_sec = 0;
15213498266Sopenharmony_ci    its.it_interval.tv_nsec = 0;
15313498266Sopenharmony_ci    its.it_value.tv_sec = timeout_ms / 1000;
15413498266Sopenharmony_ci    its.it_value.tv_nsec = (timeout_ms % 1000) * 1000 * 1000;
15513498266Sopenharmony_ci  }
15613498266Sopenharmony_ci  else if(timeout_ms == 0) {
15713498266Sopenharmony_ci    /* libcurl wants us to timeout now, however setting both fields of
15813498266Sopenharmony_ci     * new_value.it_value to zero disarms the timer. The closest we can
15913498266Sopenharmony_ci     * do is to schedule the timer to fire in 1 ns. */
16013498266Sopenharmony_ci    its.it_interval.tv_sec = 0;
16113498266Sopenharmony_ci    its.it_interval.tv_nsec = 0;
16213498266Sopenharmony_ci    its.it_value.tv_sec = 0;
16313498266Sopenharmony_ci    its.it_value.tv_nsec = 1;
16413498266Sopenharmony_ci  }
16513498266Sopenharmony_ci  else {
16613498266Sopenharmony_ci    memset(&its, 0, sizeof(struct itimerspec));
16713498266Sopenharmony_ci  }
16813498266Sopenharmony_ci
16913498266Sopenharmony_ci  timerfd_settime(g->tfd, /* flags= */0, &its, NULL);
17013498266Sopenharmony_ci  return 0;
17113498266Sopenharmony_ci}
17213498266Sopenharmony_ci
17313498266Sopenharmony_ci
17413498266Sopenharmony_ci/* Check for completed transfers, and remove their easy handles */
17513498266Sopenharmony_cistatic void check_multi_info(GlobalInfo *g)
17613498266Sopenharmony_ci{
17713498266Sopenharmony_ci  char *eff_url;
17813498266Sopenharmony_ci  CURLMsg *msg;
17913498266Sopenharmony_ci  int msgs_left;
18013498266Sopenharmony_ci  ConnInfo *conn;
18113498266Sopenharmony_ci  CURL *easy;
18213498266Sopenharmony_ci  CURLcode res;
18313498266Sopenharmony_ci
18413498266Sopenharmony_ci  fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running);
18513498266Sopenharmony_ci  while((msg = curl_multi_info_read(g->multi, &msgs_left))) {
18613498266Sopenharmony_ci    if(msg->msg == CURLMSG_DONE) {
18713498266Sopenharmony_ci      easy = msg->easy_handle;
18813498266Sopenharmony_ci      res = msg->data.result;
18913498266Sopenharmony_ci      curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
19013498266Sopenharmony_ci      curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
19113498266Sopenharmony_ci      fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error);
19213498266Sopenharmony_ci      curl_multi_remove_handle(g->multi, easy);
19313498266Sopenharmony_ci      free(conn->url);
19413498266Sopenharmony_ci      curl_easy_cleanup(easy);
19513498266Sopenharmony_ci      free(conn);
19613498266Sopenharmony_ci    }
19713498266Sopenharmony_ci  }
19813498266Sopenharmony_ci}
19913498266Sopenharmony_ci
20013498266Sopenharmony_ci/* Called by libevent when we get action on a multi socket filedescriptor */
20113498266Sopenharmony_cistatic void event_cb(GlobalInfo *g, int fd, int revents)
20213498266Sopenharmony_ci{
20313498266Sopenharmony_ci  CURLMcode rc;
20413498266Sopenharmony_ci  struct itimerspec its;
20513498266Sopenharmony_ci
20613498266Sopenharmony_ci  int action = ((revents & EPOLLIN) ? CURL_CSELECT_IN : 0) |
20713498266Sopenharmony_ci               ((revents & EPOLLOUT) ? CURL_CSELECT_OUT : 0);
20813498266Sopenharmony_ci
20913498266Sopenharmony_ci  rc = curl_multi_socket_action(g->multi, fd, action, &g->still_running);
21013498266Sopenharmony_ci  mcode_or_die("event_cb: curl_multi_socket_action", rc);
21113498266Sopenharmony_ci
21213498266Sopenharmony_ci  check_multi_info(g);
21313498266Sopenharmony_ci  if(g->still_running <= 0) {
21413498266Sopenharmony_ci    fprintf(MSG_OUT, "last transfer done, kill timeout\n");
21513498266Sopenharmony_ci    memset(&its, 0, sizeof(struct itimerspec));
21613498266Sopenharmony_ci    timerfd_settime(g->tfd, 0, &its, NULL);
21713498266Sopenharmony_ci  }
21813498266Sopenharmony_ci}
21913498266Sopenharmony_ci
22013498266Sopenharmony_ci/* Called by main loop when our timeout expires */
22113498266Sopenharmony_cistatic void timer_cb(GlobalInfo* g, int revents)
22213498266Sopenharmony_ci{
22313498266Sopenharmony_ci  CURLMcode rc;
22413498266Sopenharmony_ci  uint64_t count = 0;
22513498266Sopenharmony_ci  ssize_t err = 0;
22613498266Sopenharmony_ci
22713498266Sopenharmony_ci  err = read(g->tfd, &count, sizeof(uint64_t));
22813498266Sopenharmony_ci  if(err == -1) {
22913498266Sopenharmony_ci    /* Note that we may call the timer callback even if the timerfd is not
23013498266Sopenharmony_ci     * readable. It's possible that there are multiple events stored in the
23113498266Sopenharmony_ci     * epoll buffer (i.e. the timer may have fired multiple times). The
23213498266Sopenharmony_ci     * event count is cleared after the first call so future events in the
23313498266Sopenharmony_ci     * epoll buffer will fail to read from the timer. */
23413498266Sopenharmony_ci    if(errno == EAGAIN) {
23513498266Sopenharmony_ci      fprintf(MSG_OUT, "EAGAIN on tfd %d\n", g->tfd);
23613498266Sopenharmony_ci      return;
23713498266Sopenharmony_ci    }
23813498266Sopenharmony_ci  }
23913498266Sopenharmony_ci  if(err != sizeof(uint64_t)) {
24013498266Sopenharmony_ci    fprintf(stderr, "read(tfd) == %ld", err);
24113498266Sopenharmony_ci    perror("read(tfd)");
24213498266Sopenharmony_ci  }
24313498266Sopenharmony_ci
24413498266Sopenharmony_ci  rc = curl_multi_socket_action(g->multi,
24513498266Sopenharmony_ci                                  CURL_SOCKET_TIMEOUT, 0, &g->still_running);
24613498266Sopenharmony_ci  mcode_or_die("timer_cb: curl_multi_socket_action", rc);
24713498266Sopenharmony_ci  check_multi_info(g);
24813498266Sopenharmony_ci}
24913498266Sopenharmony_ci
25013498266Sopenharmony_ci
25113498266Sopenharmony_ci
25213498266Sopenharmony_ci/* Clean up the SockInfo structure */
25313498266Sopenharmony_cistatic void remsock(SockInfo *f, GlobalInfo* g)
25413498266Sopenharmony_ci{
25513498266Sopenharmony_ci  if(f) {
25613498266Sopenharmony_ci    if(f->sockfd) {
25713498266Sopenharmony_ci      if(epoll_ctl(g->epfd, EPOLL_CTL_DEL, f->sockfd, NULL))
25813498266Sopenharmony_ci        fprintf(stderr, "EPOLL_CTL_DEL failed for fd: %d : %s\n",
25913498266Sopenharmony_ci                f->sockfd, strerror(errno));
26013498266Sopenharmony_ci    }
26113498266Sopenharmony_ci    free(f);
26213498266Sopenharmony_ci  }
26313498266Sopenharmony_ci}
26413498266Sopenharmony_ci
26513498266Sopenharmony_ci
26613498266Sopenharmony_ci
26713498266Sopenharmony_ci/* Assign information to a SockInfo structure */
26813498266Sopenharmony_cistatic void setsock(SockInfo *f, curl_socket_t s, CURL *e, int act,
26913498266Sopenharmony_ci                    GlobalInfo *g)
27013498266Sopenharmony_ci{
27113498266Sopenharmony_ci  struct epoll_event ev;
27213498266Sopenharmony_ci  int kind = ((act & CURL_POLL_IN) ? EPOLLIN : 0) |
27313498266Sopenharmony_ci             ((act & CURL_POLL_OUT) ? EPOLLOUT : 0);
27413498266Sopenharmony_ci
27513498266Sopenharmony_ci  if(f->sockfd) {
27613498266Sopenharmony_ci    if(epoll_ctl(g->epfd, EPOLL_CTL_DEL, f->sockfd, NULL))
27713498266Sopenharmony_ci      fprintf(stderr, "EPOLL_CTL_DEL failed for fd: %d : %s\n",
27813498266Sopenharmony_ci              f->sockfd, strerror(errno));
27913498266Sopenharmony_ci  }
28013498266Sopenharmony_ci
28113498266Sopenharmony_ci  f->sockfd = s;
28213498266Sopenharmony_ci  f->action = act;
28313498266Sopenharmony_ci  f->easy = e;
28413498266Sopenharmony_ci
28513498266Sopenharmony_ci  ev.events = kind;
28613498266Sopenharmony_ci  ev.data.fd = s;
28713498266Sopenharmony_ci  if(epoll_ctl(g->epfd, EPOLL_CTL_ADD, s, &ev))
28813498266Sopenharmony_ci    fprintf(stderr, "EPOLL_CTL_ADD failed for fd: %d : %s\n",
28913498266Sopenharmony_ci            s, strerror(errno));
29013498266Sopenharmony_ci}
29113498266Sopenharmony_ci
29213498266Sopenharmony_ci
29313498266Sopenharmony_ci
29413498266Sopenharmony_ci/* Initialize a new SockInfo structure */
29513498266Sopenharmony_cistatic void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
29613498266Sopenharmony_ci{
29713498266Sopenharmony_ci  SockInfo *fdp = (SockInfo*)calloc(1, sizeof(SockInfo));
29813498266Sopenharmony_ci
29913498266Sopenharmony_ci  fdp->global = g;
30013498266Sopenharmony_ci  setsock(fdp, s, easy, action, g);
30113498266Sopenharmony_ci  curl_multi_assign(g->multi, s, fdp);
30213498266Sopenharmony_ci}
30313498266Sopenharmony_ci
30413498266Sopenharmony_ci/* CURLMOPT_SOCKETFUNCTION */
30513498266Sopenharmony_cistatic int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
30613498266Sopenharmony_ci{
30713498266Sopenharmony_ci  GlobalInfo *g = (GlobalInfo*) cbp;
30813498266Sopenharmony_ci  SockInfo *fdp = (SockInfo*) sockp;
30913498266Sopenharmony_ci  const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE" };
31013498266Sopenharmony_ci
31113498266Sopenharmony_ci  fprintf(MSG_OUT,
31213498266Sopenharmony_ci          "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]);
31313498266Sopenharmony_ci  if(what == CURL_POLL_REMOVE) {
31413498266Sopenharmony_ci    fprintf(MSG_OUT, "\n");
31513498266Sopenharmony_ci    remsock(fdp, g);
31613498266Sopenharmony_ci  }
31713498266Sopenharmony_ci  else {
31813498266Sopenharmony_ci    if(!fdp) {
31913498266Sopenharmony_ci      fprintf(MSG_OUT, "Adding data: %s\n", whatstr[what]);
32013498266Sopenharmony_ci      addsock(s, e, what, g);
32113498266Sopenharmony_ci    }
32213498266Sopenharmony_ci    else {
32313498266Sopenharmony_ci      fprintf(MSG_OUT,
32413498266Sopenharmony_ci              "Changing action from %s to %s\n",
32513498266Sopenharmony_ci              whatstr[fdp->action], whatstr[what]);
32613498266Sopenharmony_ci      setsock(fdp, s, e, what, g);
32713498266Sopenharmony_ci    }
32813498266Sopenharmony_ci  }
32913498266Sopenharmony_ci  return 0;
33013498266Sopenharmony_ci}
33113498266Sopenharmony_ci
33213498266Sopenharmony_ci
33313498266Sopenharmony_ci
33413498266Sopenharmony_ci/* CURLOPT_WRITEFUNCTION */
33513498266Sopenharmony_cistatic size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
33613498266Sopenharmony_ci{
33713498266Sopenharmony_ci  (void)ptr;
33813498266Sopenharmony_ci  (void)data;
33913498266Sopenharmony_ci  return size * nmemb;
34013498266Sopenharmony_ci}
34113498266Sopenharmony_ci
34213498266Sopenharmony_ci
34313498266Sopenharmony_ci/* CURLOPT_PROGRESSFUNCTION */
34413498266Sopenharmony_cistatic int prog_cb(void *p, double dltotal, double dlnow, double ult,
34513498266Sopenharmony_ci                   double uln)
34613498266Sopenharmony_ci{
34713498266Sopenharmony_ci  ConnInfo *conn = (ConnInfo *)p;
34813498266Sopenharmony_ci  (void)ult;
34913498266Sopenharmony_ci  (void)uln;
35013498266Sopenharmony_ci
35113498266Sopenharmony_ci  fprintf(MSG_OUT, "Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
35213498266Sopenharmony_ci  return 0;
35313498266Sopenharmony_ci}
35413498266Sopenharmony_ci
35513498266Sopenharmony_ci
35613498266Sopenharmony_ci/* Create a new easy handle, and add it to the global curl_multi */
35713498266Sopenharmony_cistatic void new_conn(char *url, GlobalInfo *g)
35813498266Sopenharmony_ci{
35913498266Sopenharmony_ci  ConnInfo *conn;
36013498266Sopenharmony_ci  CURLMcode rc;
36113498266Sopenharmony_ci
36213498266Sopenharmony_ci  conn = (ConnInfo*)calloc(1, sizeof(ConnInfo));
36313498266Sopenharmony_ci  conn->error[0]='\0';
36413498266Sopenharmony_ci
36513498266Sopenharmony_ci  conn->easy = curl_easy_init();
36613498266Sopenharmony_ci  if(!conn->easy) {
36713498266Sopenharmony_ci    fprintf(MSG_OUT, "curl_easy_init() failed, exiting!\n");
36813498266Sopenharmony_ci    exit(2);
36913498266Sopenharmony_ci  }
37013498266Sopenharmony_ci  conn->global = g;
37113498266Sopenharmony_ci  conn->url = strdup(url);
37213498266Sopenharmony_ci  curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url);
37313498266Sopenharmony_ci  curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb);
37413498266Sopenharmony_ci  curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, conn);
37513498266Sopenharmony_ci  curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L);
37613498266Sopenharmony_ci  curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
37713498266Sopenharmony_ci  curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
37813498266Sopenharmony_ci  curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0L);
37913498266Sopenharmony_ci  curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
38013498266Sopenharmony_ci  curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn);
38113498266Sopenharmony_ci  curl_easy_setopt(conn->easy, CURLOPT_FOLLOWLOCATION, 1L);
38213498266Sopenharmony_ci  curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_TIME, 3L);
38313498266Sopenharmony_ci  curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_LIMIT, 10L);
38413498266Sopenharmony_ci  fprintf(MSG_OUT,
38513498266Sopenharmony_ci          "Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url);
38613498266Sopenharmony_ci  rc = curl_multi_add_handle(g->multi, conn->easy);
38713498266Sopenharmony_ci  mcode_or_die("new_conn: curl_multi_add_handle", rc);
38813498266Sopenharmony_ci
38913498266Sopenharmony_ci  /* note that the add_handle() will set a time-out to trigger soon so that
39013498266Sopenharmony_ci     the necessary socket_action() call will be called by this app */
39113498266Sopenharmony_ci}
39213498266Sopenharmony_ci
39313498266Sopenharmony_ci/* This gets called whenever data is received from the fifo */
39413498266Sopenharmony_cistatic void fifo_cb(GlobalInfo* g, int revents)
39513498266Sopenharmony_ci{
39613498266Sopenharmony_ci  char s[1024];
39713498266Sopenharmony_ci  long int rv = 0;
39813498266Sopenharmony_ci  int n = 0;
39913498266Sopenharmony_ci
40013498266Sopenharmony_ci  do {
40113498266Sopenharmony_ci    s[0]='\0';
40213498266Sopenharmony_ci    rv = fscanf(g->input, "%1023s%n", s, &n);
40313498266Sopenharmony_ci    s[n]='\0';
40413498266Sopenharmony_ci    if(n && s[0]) {
40513498266Sopenharmony_ci      new_conn(s, g); /* if we read a URL, go get it! */
40613498266Sopenharmony_ci    }
40713498266Sopenharmony_ci    else
40813498266Sopenharmony_ci      break;
40913498266Sopenharmony_ci  } while(rv != EOF);
41013498266Sopenharmony_ci}
41113498266Sopenharmony_ci
41213498266Sopenharmony_ci/* Create a named pipe and tell libevent to monitor it */
41313498266Sopenharmony_cistatic const char *fifo = "hiper.fifo";
41413498266Sopenharmony_cistatic int init_fifo(GlobalInfo *g)
41513498266Sopenharmony_ci{
41613498266Sopenharmony_ci  struct stat st;
41713498266Sopenharmony_ci  curl_socket_t sockfd;
41813498266Sopenharmony_ci  struct epoll_event epev;
41913498266Sopenharmony_ci
42013498266Sopenharmony_ci  fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo);
42113498266Sopenharmony_ci  if(lstat (fifo, &st) == 0) {
42213498266Sopenharmony_ci    if((st.st_mode & S_IFMT) == S_IFREG) {
42313498266Sopenharmony_ci      errno = EEXIST;
42413498266Sopenharmony_ci      perror("lstat");
42513498266Sopenharmony_ci      exit(1);
42613498266Sopenharmony_ci    }
42713498266Sopenharmony_ci  }
42813498266Sopenharmony_ci  unlink(fifo);
42913498266Sopenharmony_ci  if(mkfifo (fifo, 0600) == -1) {
43013498266Sopenharmony_ci    perror("mkfifo");
43113498266Sopenharmony_ci    exit(1);
43213498266Sopenharmony_ci  }
43313498266Sopenharmony_ci  sockfd = open(fifo, O_RDWR | O_NONBLOCK, 0);
43413498266Sopenharmony_ci  if(sockfd == -1) {
43513498266Sopenharmony_ci    perror("open");
43613498266Sopenharmony_ci    exit(1);
43713498266Sopenharmony_ci  }
43813498266Sopenharmony_ci
43913498266Sopenharmony_ci  g->fifofd = sockfd;
44013498266Sopenharmony_ci  g->input = fdopen(sockfd, "r");
44113498266Sopenharmony_ci
44213498266Sopenharmony_ci  epev.events = EPOLLIN;
44313498266Sopenharmony_ci  epev.data.fd = sockfd;
44413498266Sopenharmony_ci  epoll_ctl(g->epfd, EPOLL_CTL_ADD, sockfd, &epev);
44513498266Sopenharmony_ci
44613498266Sopenharmony_ci  fprintf(MSG_OUT, "Now, pipe some URL's into > %s\n", fifo);
44713498266Sopenharmony_ci  return 0;
44813498266Sopenharmony_ci}
44913498266Sopenharmony_ci
45013498266Sopenharmony_cistatic void clean_fifo(GlobalInfo *g)
45113498266Sopenharmony_ci{
45213498266Sopenharmony_ci    epoll_ctl(g->epfd, EPOLL_CTL_DEL, g->fifofd, NULL);
45313498266Sopenharmony_ci    fclose(g->input);
45413498266Sopenharmony_ci    unlink(fifo);
45513498266Sopenharmony_ci}
45613498266Sopenharmony_ci
45713498266Sopenharmony_ci
45813498266Sopenharmony_ciint g_should_exit_ = 0;
45913498266Sopenharmony_ci
46013498266Sopenharmony_civoid sigint_handler(int signo)
46113498266Sopenharmony_ci{
46213498266Sopenharmony_ci  g_should_exit_ = 1;
46313498266Sopenharmony_ci}
46413498266Sopenharmony_ci
46513498266Sopenharmony_ciint main(int argc, char **argv)
46613498266Sopenharmony_ci{
46713498266Sopenharmony_ci  GlobalInfo g;
46813498266Sopenharmony_ci  struct itimerspec its;
46913498266Sopenharmony_ci  struct epoll_event ev;
47013498266Sopenharmony_ci  struct epoll_event events[10];
47113498266Sopenharmony_ci  (void)argc;
47213498266Sopenharmony_ci  (void)argv;
47313498266Sopenharmony_ci
47413498266Sopenharmony_ci  g_should_exit_ = 0;
47513498266Sopenharmony_ci  signal(SIGINT, sigint_handler);
47613498266Sopenharmony_ci
47713498266Sopenharmony_ci  memset(&g, 0, sizeof(GlobalInfo));
47813498266Sopenharmony_ci  g.epfd = epoll_create1(EPOLL_CLOEXEC);
47913498266Sopenharmony_ci  if(g.epfd == -1) {
48013498266Sopenharmony_ci    perror("epoll_create1 failed");
48113498266Sopenharmony_ci    exit(1);
48213498266Sopenharmony_ci  }
48313498266Sopenharmony_ci
48413498266Sopenharmony_ci  g.tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
48513498266Sopenharmony_ci  if(g.tfd == -1) {
48613498266Sopenharmony_ci    perror("timerfd_create failed");
48713498266Sopenharmony_ci    exit(1);
48813498266Sopenharmony_ci  }
48913498266Sopenharmony_ci
49013498266Sopenharmony_ci  memset(&its, 0, sizeof(struct itimerspec));
49113498266Sopenharmony_ci  its.it_interval.tv_sec = 0;
49213498266Sopenharmony_ci  its.it_value.tv_sec = 1;
49313498266Sopenharmony_ci  timerfd_settime(g.tfd, 0, &its, NULL);
49413498266Sopenharmony_ci
49513498266Sopenharmony_ci  ev.events = EPOLLIN;
49613498266Sopenharmony_ci  ev.data.fd = g.tfd;
49713498266Sopenharmony_ci  epoll_ctl(g.epfd, EPOLL_CTL_ADD, g.tfd, &ev);
49813498266Sopenharmony_ci
49913498266Sopenharmony_ci  init_fifo(&g);
50013498266Sopenharmony_ci  g.multi = curl_multi_init();
50113498266Sopenharmony_ci
50213498266Sopenharmony_ci  /* setup the generic multi interface options we want */
50313498266Sopenharmony_ci  curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
50413498266Sopenharmony_ci  curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g);
50513498266Sopenharmony_ci  curl_multi_setopt(g.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
50613498266Sopenharmony_ci  curl_multi_setopt(g.multi, CURLMOPT_TIMERDATA, &g);
50713498266Sopenharmony_ci
50813498266Sopenharmony_ci  /* we do not call any curl_multi_socket*() function yet as we have no handles
50913498266Sopenharmony_ci     added! */
51013498266Sopenharmony_ci
51113498266Sopenharmony_ci  fprintf(MSG_OUT, "Entering wait loop\n");
51213498266Sopenharmony_ci  fflush(MSG_OUT);
51313498266Sopenharmony_ci  while(!g_should_exit_) {
51413498266Sopenharmony_ci    int idx;
51513498266Sopenharmony_ci    int err = epoll_wait(g.epfd, events,
51613498266Sopenharmony_ci                         sizeof(events)/sizeof(struct epoll_event), 10000);
51713498266Sopenharmony_ci    if(err == -1) {
51813498266Sopenharmony_ci      if(errno == EINTR) {
51913498266Sopenharmony_ci        fprintf(MSG_OUT, "note: wait interrupted\n");
52013498266Sopenharmony_ci        continue;
52113498266Sopenharmony_ci      }
52213498266Sopenharmony_ci      else {
52313498266Sopenharmony_ci        perror("epoll_wait");
52413498266Sopenharmony_ci        exit(1);
52513498266Sopenharmony_ci      }
52613498266Sopenharmony_ci    }
52713498266Sopenharmony_ci
52813498266Sopenharmony_ci    for(idx = 0; idx < err; ++idx) {
52913498266Sopenharmony_ci      if(events[idx].data.fd == g.fifofd) {
53013498266Sopenharmony_ci        fifo_cb(&g, events[idx].events);
53113498266Sopenharmony_ci      }
53213498266Sopenharmony_ci      else if(events[idx].data.fd == g.tfd) {
53313498266Sopenharmony_ci        timer_cb(&g, events[idx].events);
53413498266Sopenharmony_ci      }
53513498266Sopenharmony_ci      else {
53613498266Sopenharmony_ci        event_cb(&g, events[idx].data.fd, events[idx].events);
53713498266Sopenharmony_ci      }
53813498266Sopenharmony_ci    }
53913498266Sopenharmony_ci  }
54013498266Sopenharmony_ci
54113498266Sopenharmony_ci  fprintf(MSG_OUT, "Exiting normally.\n");
54213498266Sopenharmony_ci  fflush(MSG_OUT);
54313498266Sopenharmony_ci
54413498266Sopenharmony_ci  curl_multi_cleanup(g.multi);
54513498266Sopenharmony_ci  clean_fifo(&g);
54613498266Sopenharmony_ci  return 0;
54713498266Sopenharmony_ci}
548