ghiper.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  ***************************************************************************/
00022 /* <DESC>
00023  * multi socket API usage together with with glib2
00024  * </DESC>
00025  */
00026 /* Example application source code using the multi socket interface to
00027  * download many files at once.
00028  *
00029  * Written by Jeff Pohlmeyer
00030 
00031  Requires glib-2.x and a (POSIX?) system that has mkfifo().
00032 
00033  This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c"
00034  sample programs, adapted to use glib's g_io_channel in place of libevent.
00035 
00036  When running, the program creates the named pipe "hiper.fifo"
00037 
00038  Whenever there is input into the fifo, the program reads the input as a list
00039  of URL's and creates some new easy handles to fetch each URL via the
00040  curl_multi "hiper" API.
00041 
00042 
00043  Thus, you can try a single URL:
00044  % echo http://www.yahoo.com > hiper.fifo
00045 
00046  Or a whole bunch of them:
00047  % cat my-url-list > hiper.fifo
00048 
00049  The fifo buffer is handled almost instantly, so you can even add more URL's
00050  while the previous requests are still being downloaded.
00051 
00052  This is purely a demo app, all retrieved data is simply discarded by the write
00053  callback.
00054 
00055 */
00056 
00057 #include <glib.h>
00058 #include <sys/stat.h>
00059 #include <unistd.h>
00060 #include <fcntl.h>
00061 #include <stdlib.h>
00062 #include <stdio.h>
00063 #include <errno.h>
00064 #include <curl/curl.h>
00065 
00066 #define MSG_OUT g_print   /* Change to "g_error" to write to stderr */
00067 #define SHOW_VERBOSE 0    /* Set to non-zero for libcurl messages */
00068 #define SHOW_PROGRESS 0   /* Set to non-zero to enable progress callback */
00069 
00070 /* Global information, common to all connections */
00071 typedef struct _GlobalInfo {
00072   CURLM *multi;
00073   guint timer_event;
00074   int still_running;
00075 } GlobalInfo;
00076 
00077 /* Information associated with a specific easy handle */
00078 typedef struct _ConnInfo {
00079   CURL *easy;
00080   char *url;
00081   GlobalInfo *global;
00082   char error[CURL_ERROR_SIZE];
00083 } ConnInfo;
00084 
00085 /* Information associated with a specific socket */
00086 typedef struct _SockInfo {
00087   curl_socket_t sockfd;
00088   CURL *easy;
00089   int action;
00090   long timeout;
00091   GIOChannel *ch;
00092   guint ev;
00093   GlobalInfo *global;
00094 } SockInfo;
00095 
00096 /* Die if we get a bad CURLMcode somewhere */
00097 static void mcode_or_die(const char *where, CURLMcode code)
00098 {
00099   if(CURLM_OK != code) {
00100     const char *s;
00101     switch(code) {
00102     case     CURLM_BAD_HANDLE:         s="CURLM_BAD_HANDLE";         break;
00103     case     CURLM_BAD_EASY_HANDLE:    s="CURLM_BAD_EASY_HANDLE";    break;
00104     case     CURLM_OUT_OF_MEMORY:      s="CURLM_OUT_OF_MEMORY";      break;
00105     case     CURLM_INTERNAL_ERROR:     s="CURLM_INTERNAL_ERROR";     break;
00106     case     CURLM_BAD_SOCKET:         s="CURLM_BAD_SOCKET";         break;
00107     case     CURLM_UNKNOWN_OPTION:     s="CURLM_UNKNOWN_OPTION";     break;
00108     case     CURLM_LAST:               s="CURLM_LAST";               break;
00109     default: s="CURLM_unknown";
00110     }
00111     MSG_OUT("ERROR: %s returns %s\n", where, s);
00112     exit(code);
00113   }
00114 }
00115 
00116 /* Check for completed transfers, and remove their easy handles */
00117 static void check_multi_info(GlobalInfo *g)
00118 {
00119   char *eff_url;
00120   CURLMsg *msg;
00121   int msgs_left;
00122   ConnInfo *conn;
00123   CURL *easy;
00124   CURLcode res;
00125 
00126   MSG_OUT("REMAINING: %d\n", g->still_running);
00127   while((msg = curl_multi_info_read(g->multi, &msgs_left))) {
00128     if(msg->msg == CURLMSG_DONE) {
00129       easy = msg->easy_handle;
00130       res = msg->data.result;
00131       curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
00132       curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
00133       MSG_OUT("DONE: %s => (%d) %s\n", eff_url, res, conn->error);
00134       curl_multi_remove_handle(g->multi, easy);
00135       free(conn->url);
00136       curl_easy_cleanup(easy);
00137       free(conn);
00138     }
00139   }
00140 }
00141 
00142 /* Called by glib when our timeout expires */
00143 static gboolean timer_cb(gpointer data)
00144 {
00145   GlobalInfo *g = (GlobalInfo *)data;
00146   CURLMcode rc;
00147 
00148   rc = curl_multi_socket_action(g->multi,
00149                                 CURL_SOCKET_TIMEOUT, 0, &g->still_running);
00150   mcode_or_die("timer_cb: curl_multi_socket_action", rc);
00151   check_multi_info(g);
00152   return FALSE;
00153 }
00154 
00155 /* Update the event timer after curl_multi library calls */
00156 static int update_timeout_cb(CURLM *multi, long timeout_ms, void *userp)
00157 {
00158   struct timeval timeout;
00159   GlobalInfo *g=(GlobalInfo *)userp;
00160   timeout.tv_sec = timeout_ms/1000;
00161   timeout.tv_usec = (timeout_ms%1000)*1000;
00162 
00163   MSG_OUT("*** update_timeout_cb %ld => %ld:%ld ***\n",
00164           timeout_ms, timeout.tv_sec, timeout.tv_usec);
00165 
00166   g->timer_event = g_timeout_add(timeout_ms, timer_cb, g);
00167   return 0;
00168 }
00169 
00170 /* Called by glib when we get action on a multi socket */
00171 static gboolean event_cb(GIOChannel *ch, GIOCondition condition, gpointer data)
00172 {
00173   GlobalInfo *g = (GlobalInfo*) data;
00174   CURLMcode rc;
00175   int fd=g_io_channel_unix_get_fd(ch);
00176 
00177   int action =
00178     (condition & G_IO_IN ? CURL_CSELECT_IN : 0) |
00179     (condition & G_IO_OUT ? CURL_CSELECT_OUT : 0);
00180 
00181   rc = curl_multi_socket_action(g->multi, fd, action, &g->still_running);
00182   mcode_or_die("event_cb: curl_multi_socket_action", rc);
00183 
00184   check_multi_info(g);
00185   if(g->still_running) {
00186     return TRUE;
00187   }
00188   else {
00189     MSG_OUT("last transfer done, kill timeout\n");
00190     if(g->timer_event) {
00191       g_source_remove(g->timer_event);
00192     }
00193     return FALSE;
00194   }
00195 }
00196 
00197 /* Clean up the SockInfo structure */
00198 static void remsock(SockInfo *f)
00199 {
00200   if(!f) {
00201     return;
00202   }
00203   if(f->ev) {
00204     g_source_remove(f->ev);
00205   }
00206   g_free(f);
00207 }
00208 
00209 /* Assign information to a SockInfo structure */
00210 static void setsock(SockInfo *f, curl_socket_t s, CURL *e, int act,
00211                     GlobalInfo *g)
00212 {
00213   GIOCondition kind =
00214     (act&CURL_POLL_IN?G_IO_IN:0)|(act&CURL_POLL_OUT?G_IO_OUT:0);
00215 
00216   f->sockfd = s;
00217   f->action = act;
00218   f->easy = e;
00219   if(f->ev) {
00220     g_source_remove(f->ev);
00221   }
00222   f->ev=g_io_add_watch(f->ch, kind, event_cb, g);
00223 }
00224 
00225 /* Initialize a new SockInfo structure */
00226 static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
00227 {
00228   SockInfo *fdp = g_malloc0(sizeof(SockInfo));
00229 
00230   fdp->global = g;
00231   fdp->ch=g_io_channel_unix_new(s);
00232   setsock(fdp, s, easy, action, g);
00233   curl_multi_assign(g->multi, s, fdp);
00234 }
00235 
00236 /* CURLMOPT_SOCKETFUNCTION */
00237 static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
00238 {
00239   GlobalInfo *g = (GlobalInfo*) cbp;
00240   SockInfo *fdp = (SockInfo*) sockp;
00241   static const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE" };
00242 
00243   MSG_OUT("socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]);
00244   if(what == CURL_POLL_REMOVE) {
00245     MSG_OUT("\n");
00246     remsock(fdp);
00247   }
00248   else {
00249     if(!fdp) {
00250       MSG_OUT("Adding data: %s%s\n",
00251               what&CURL_POLL_IN?"READ":"",
00252               what&CURL_POLL_OUT?"WRITE":"");
00253       addsock(s, e, what, g);
00254     }
00255     else {
00256       MSG_OUT(
00257         "Changing action from %d to %d\n", fdp->action, what);
00258       setsock(fdp, s, e, what, g);
00259     }
00260   }
00261   return 0;
00262 }
00263 
00264 /* CURLOPT_WRITEFUNCTION */
00265 static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
00266 {
00267   size_t realsize = size * nmemb;
00268   ConnInfo *conn = (ConnInfo*) data;
00269   (void)ptr;
00270   (void)conn;
00271   return realsize;
00272 }
00273 
00274 /* CURLOPT_PROGRESSFUNCTION */
00275 static int prog_cb(void *p, double dltotal, double dlnow, double ult,
00276                    double uln)
00277 {
00278   ConnInfo *conn = (ConnInfo *)p;
00279   MSG_OUT("Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
00280   return 0;
00281 }
00282 
00283 /* Create a new easy handle, and add it to the global curl_multi */
00284 static void new_conn(char *url, GlobalInfo *g)
00285 {
00286   ConnInfo *conn;
00287   CURLMcode rc;
00288 
00289   conn = g_malloc0(sizeof(ConnInfo));
00290   conn->error[0]='\0';
00291   conn->easy = curl_easy_init();
00292   if(!conn->easy) {
00293     MSG_OUT("curl_easy_init() failed, exiting!\n");
00294     exit(2);
00295   }
00296   conn->global = g;
00297   conn->url = g_strdup(url);
00298   curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url);
00299   curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb);
00300   curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, &conn);
00301   curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, (long)SHOW_VERBOSE);
00302   curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
00303   curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
00304   curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, SHOW_PROGRESS?0L:1L);
00305   curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
00306   curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn);
00307   curl_easy_setopt(conn->easy, CURLOPT_FOLLOWLOCATION, 1L);
00308   curl_easy_setopt(conn->easy, CURLOPT_CONNECTTIMEOUT, 30L);
00309   curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_LIMIT, 1L);
00310   curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_TIME, 30L);
00311 
00312   MSG_OUT("Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url);
00313   rc =curl_multi_add_handle(g->multi, conn->easy);
00314   mcode_or_die("new_conn: curl_multi_add_handle", rc);
00315 
00316   /* note that the add_handle() will set a time-out to trigger very soon so
00317      that the necessary socket_action() call will be called by this app */
00318 }
00319 
00320 /* This gets called by glib whenever data is received from the fifo */
00321 static gboolean fifo_cb(GIOChannel *ch, GIOCondition condition, gpointer data)
00322 {
00323 #define BUF_SIZE 1024
00324   gsize len, tp;
00325   gchar *buf, *tmp, *all=NULL;
00326   GIOStatus rv;
00327 
00328   do {
00329     GError *err=NULL;
00330     rv = g_io_channel_read_line(ch, &buf, &len, &tp, &err);
00331     if(buf) {
00332       if(tp) {
00333         buf[tp]='\0';
00334       }
00335       new_conn(buf, (GlobalInfo*)data);
00336       g_free(buf);
00337     }
00338     else {
00339       buf = g_malloc(BUF_SIZE+1);
00340       while(TRUE) {
00341         buf[BUF_SIZE]='\0';
00342         g_io_channel_read_chars(ch, buf, BUF_SIZE, &len, &err);
00343         if(len) {
00344           buf[len]='\0';
00345           if(all) {
00346             tmp=all;
00347             all=g_strdup_printf("%s%s", tmp, buf);
00348             g_free(tmp);
00349           }
00350           else {
00351             all = g_strdup(buf);
00352           }
00353         }
00354         else {
00355           break;
00356         }
00357       }
00358       if(all) {
00359         new_conn(all, (GlobalInfo*)data);
00360         g_free(all);
00361       }
00362       g_free(buf);
00363     }
00364     if(err) {
00365       g_error("fifo_cb: %s", err->message);
00366       g_free(err);
00367       break;
00368     }
00369   } while((len) && (rv == G_IO_STATUS_NORMAL));
00370   return TRUE;
00371 }
00372 
00373 int init_fifo(void)
00374 {
00375   struct stat st;
00376   const char *fifo = "hiper.fifo";
00377   int socket;
00378 
00379   if(lstat (fifo, &st) == 0) {
00380     if((st.st_mode & S_IFMT) == S_IFREG) {
00381       errno = EEXIST;
00382       perror("lstat");
00383       exit(1);
00384     }
00385   }
00386 
00387   unlink(fifo);
00388   if(mkfifo (fifo, 0600) == -1) {
00389     perror("mkfifo");
00390     exit(1);
00391   }
00392 
00393   socket = open(fifo, O_RDWR | O_NONBLOCK, 0);
00394 
00395   if(socket == -1) {
00396     perror("open");
00397     exit(1);
00398   }
00399   MSG_OUT("Now, pipe some URL's into > %s\n", fifo);
00400 
00401   return socket;
00402 }
00403 
00404 int main(int argc, char **argv)
00405 {
00406   GlobalInfo *g;
00407   CURLMcode rc;
00408   GMainLoop*gmain;
00409   int fd;
00410   GIOChannel* ch;
00411   g=g_malloc0(sizeof(GlobalInfo));
00412 
00413   fd=init_fifo();
00414   ch=g_io_channel_unix_new(fd);
00415   g_io_add_watch(ch, G_IO_IN, fifo_cb, g);
00416   gmain=g_main_loop_new(NULL, FALSE);
00417   g->multi = curl_multi_init();
00418   curl_multi_setopt(g->multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
00419   curl_multi_setopt(g->multi, CURLMOPT_SOCKETDATA, g);
00420   curl_multi_setopt(g->multi, CURLMOPT_TIMERFUNCTION, update_timeout_cb);
00421   curl_multi_setopt(g->multi, CURLMOPT_TIMERDATA, g);
00422 
00423   /* we don't call any curl_multi_socket*() function yet as we have no handles
00424      added! */
00425 
00426   g_main_loop_run(gmain);
00427   curl_multi_cleanup(g->multi);
00428   return 0;
00429 }


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:02