/[axel]/tags/1.0a/axel.c
ViewVC logotype

Contents of /tags/1.0a/axel.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (hide annotations) (download)
Fri Jan 11 12:01:11 2008 UTC (5 years, 5 months ago) by appaji-guest
File MIME type: text/plain
File size: 16357 byte(s)
Tagging 1.0a release
1 appaji-guest 2 /********************************************************************\
2     * Axel -- A lighter download accelerator for Linux and other Unices. *
3     * *
4     * Copyright 2001 Wilmer van der Gaast *
5     \********************************************************************/
6    
7     /* Main control */
8    
9     /*
10     This program is free software; you can redistribute it and/or modify
11     it under the terms of the GNU General Public License as published by
12     the Free Software Foundation; either version 2 of the License, or
13     (at your option) any later version.
14    
15     This program is distributed in the hope that it will be useful,
16     but WITHOUT ANY WARRANTY; without even the implied warranty of
17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18     GNU General Public License for more details.
19    
20     You should have received a copy of the GNU General Public License with
21     the Debian GNU/Linux distribution in file /usr/doc/copyright/GPL;
22     if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23     Suite 330, Boston, MA 02111-1307 USA
24     */
25    
26     #include "axel.h"
27    
28     /* Axel */
29     static void save_state( axel_t *axel );
30     static void *setup_thread( void * );
31     static void axel_message( axel_t *axel, char *format, ... );
32     static void axel_divide( axel_t *axel );
33    
34     static char *buffer = NULL;
35    
36     /* Create a new axel_t structure */
37     axel_t *axel_new( conf_t *conf, int count, void *url )
38     {
39     search_t *res;
40     axel_t *axel;
41     url_t *u;
42     char *s;
43     int i;
44    
45     axel = malloc( sizeof( axel_t ) );
46     memset( axel, 0, sizeof( axel_t ) );
47     *axel->conf = *conf;
48     axel->conn = malloc( sizeof( conn_t ) * axel->conf->num_connections );
49     memset( axel->conn, 0, sizeof( conn_t ) * axel->conf->num_connections );
50     if( axel->conf->max_speed > 0 )
51     {
52     if( (float) axel->conf->max_speed / axel->conf->buffer_size < 0.5 )
53     {
54     if( axel->conf->verbose >= 2 )
55     axel_message( axel, _("Buffer resized for this speed.") );
56     axel->conf->buffer_size = axel->conf->max_speed;
57     }
58     axel->delay_time = (int) ( (float) 1000000 / axel->conf->max_speed * axel->conf->buffer_size * axel->conf->num_connections );
59     }
60     if( buffer == NULL )
61     buffer = malloc( max( MAX_STRING, axel->conf->buffer_size ) );
62    
63     if( count == 0 )
64     {
65     axel->url = malloc( sizeof( url_t ) );
66     axel->url->next = axel->url;
67     strcpy( axel->url->text, (char *) url );
68     }
69     else
70     {
71     res = (search_t *) url;
72     u = axel->url = malloc( sizeof( url_t ) );
73     for( i = 0; i < count; i ++ )
74     {
75     strcpy( u->text, res[i].url );
76     if( i < count - 1 )
77     {
78     u->next = malloc( sizeof( url_t ) );
79     u = u->next;
80     }
81     else
82     {
83     u->next = axel->url;
84     }
85     }
86     }
87    
88     axel->conn[0].conf = axel->conf;
89     if( !conn_set( &axel->conn[0], axel->url->text ) )
90     {
91     axel_message( axel, _("Could not parse URL.\n") );
92     axel->ready = -1;
93     return( axel );
94     }
95    
96     axel->conn[0].local_if = axel->conf->interfaces->text;
97     axel->conf->interfaces = axel->conf->interfaces->next;
98    
99     strcpy( axel->filename, axel->conn[0].file );
100     http_decode( axel->filename );
101     if( *axel->filename == 0 ) /* Index page == no fn */
102     strcpy( axel->filename, axel->conf->default_filename );
103     if( ( s = strchr( axel->filename, '?' ) ) != NULL && axel->conf->strip_cgi_parameters )
104     *s = 0; /* Get rid of CGI parameters */
105    
106     if( !conn_init( &axel->conn[0] ) )
107     {
108     axel_message( axel, axel->conn[0].message );
109     axel->ready = -1;
110     return( axel );
111     }
112    
113     /* This does more than just checking the file size, it all depends
114     on the protocol used. */
115     if( !conn_info( &axel->conn[0] ) )
116     {
117     axel_message( axel, axel->conn[0].message );
118     axel->ready = -1;
119     return( axel );
120     }
121     s = conn_url( axel->conn );
122     strcpy( axel->url->text, s );
123     if( ( axel->size = axel->conn[0].size ) != INT_MAX )
124     {
125     if( axel->conf->verbose > 0 )
126     axel_message( axel, _("File size: %i bytes"), axel->size );
127     }
128    
129     /* Wildcards in URL --> Get complete filename */
130     if( strchr( axel->filename, '*' ) || strchr( axel->filename, '?' ) )
131     strcpy( axel->filename, axel->conn[0].file );
132    
133     return( axel );
134     }
135    
136     /* Open a local file to store the downloaded data */
137     int axel_open( axel_t *axel )
138     {
139     int i, fd;
140    
141     if( axel->conf->verbose > 0 )
142     axel_message( axel, _("Opening output file %s"), axel->filename );
143     snprintf( buffer, MAX_STRING, "%s.st", axel->filename );
144    
145     axel->outfd = -1;
146    
147     /* Check whether server knows about RESTart and switch back to
148     single connection download if necessary */
149     if( !axel->conn[0].supported )
150     {
151     axel_message( axel, _("Server unsupported, "
152     "starting from scratch with one connection.") );
153     axel->conf->num_connections = 1;
154     axel->conn = realloc( axel->conn, sizeof( conn_t ) );
155     axel_divide( axel );
156     }
157     else if( ( fd = open( buffer, O_RDONLY ) ) != -1 )
158     {
159     read( fd, &axel->conf->num_connections, sizeof( axel->conf->num_connections ) );
160    
161     axel->conn = realloc( axel->conn, sizeof( conn_t ) * axel->conf->num_connections );
162     memset( axel->conn + 1, 0, sizeof( conn_t ) * ( axel->conf->num_connections - 1 ) );
163    
164     axel_divide( axel );
165    
166     read( fd, &axel->bytes_done, sizeof( axel->bytes_done ) );
167     for( i = 0; i < axel->conf->num_connections; i ++ )
168     read( fd, &axel->conn[i].currentbyte, sizeof( axel->conn[i].currentbyte ) );
169    
170     axel_message( axel, _("State file found: %i bytes downloaded, %i to go."),
171     axel->bytes_done, axel->size - axel->bytes_done );
172    
173     close( fd );
174    
175     if( ( axel->outfd = open( axel->filename, O_WRONLY, 0666 ) ) == -1 )
176     {
177     axel_message( axel, _("Error opening local file") );
178     return( 0 );
179     }
180     }
181    
182     /* If outfd == -1 we have to start from scrath now */
183     if( axel->outfd == -1 )
184     {
185     axel_divide( axel );
186    
187     if( ( axel->outfd = open( axel->filename, O_CREAT | O_WRONLY, 0666 ) ) == -1 )
188     {
189     axel_message( axel, _("Error opening local file") );
190     return( 0 );
191     }
192    
193     /* And check whether the filesystem can handle seeks to
194     past-EOF areas.. Speeds things up. :) AFAIK this
195     should just not happen: */
196     if( lseek( axel->outfd, axel->size, SEEK_SET ) == -1 && axel->conf->num_connections > 1 )
197     {
198     /* But if the OS/fs does not allow to seek behind
199     EOF, we have to fill the file with zeroes before
200     starting. Slow.. */
201     axel_message( axel, _("Crappy filesystem/OS.. Working around. :-(") );
202     lseek( axel->outfd, 0, SEEK_SET );
203     memset( buffer, 0, axel->conf->buffer_size );
204     i = axel->size;
205     while( i > 0 )
206     {
207     write( axel->outfd, buffer, min( i, axel->conf->buffer_size ) );
208     i -= axel->conf->buffer_size;
209     }
210     }
211     }
212    
213     return( 1 );
214     }
215    
216     /* Start downloading */
217     void axel_start( axel_t *axel )
218     {
219     int i;
220    
221     /* HTTP might've redirected and FTP handles wildcards, so
222     re-scan the URL for every conn */
223     for( i = 0; i < axel->conf->num_connections; i ++ )
224     {
225     conn_set( &axel->conn[i], axel->url->text );
226     axel->url = axel->url->next;
227     axel->conn[i].local_if = axel->conf->interfaces->text;
228     axel->conf->interfaces = axel->conf->interfaces->next;
229     axel->conn[i].conf = axel->conf;
230     axel->conn[i].supported = 1;
231     }
232    
233     if( axel->conf->verbose > 0 )
234     axel_message( axel, _("Starting download") );
235    
236     for( i = 0; i < axel->conf->num_connections; i ++ )
237     if( axel->conn[i].currentbyte <= axel->conn[i].lastbyte )
238     {
239     if( axel->conf->verbose >= 2 )
240     axel_message( axel, _("Connection %i downloading from %s:%i using interface %s"),
241     i, axel->conn[i].host, axel->conn[i].port, axel->conn[i].local_if );
242     if( pthread_create( axel->conn[i].setup_thread, NULL, setup_thread, &axel->conn[i] ) != 0 )
243     {
244     axel_message( axel, _("pthread error!!!") );
245     axel->ready = -1;
246     }
247     else
248     {
249     axel->conn[i].last_transfer = gettime();
250     axel->conn[i].state = 1;
251     }
252     }
253    
254     /* The real downloading will start now, so let's start counting */
255     axel->start_time = gettime();
256     axel->ready = 0;
257     }
258    
259     /* Main 'loop' */
260     void axel_do( axel_t *axel )
261     {
262     fd_set fds[1];
263     int hifd, i, j, size;
264     struct timeval timeval[1];
265    
266     /* Create statefile if necessary */
267     if( gettime() > axel->next_state )
268     {
269     save_state( axel );
270     axel->next_state = gettime() + axel->conf->save_state_interval;
271     }
272    
273     /* Wait for data on (one of) the connections */
274     FD_ZERO( fds );
275     hifd = 0;
276     for( i = 0; i < axel->conf->num_connections; i ++ )
277     {
278     if( axel->conn[i].enabled )
279     FD_SET( axel->conn[i].fd, fds );
280     hifd = max( hifd, axel->conn[i].fd );
281     }
282     if( hifd == 0 )
283     {
284     /* No connections yet. Wait... */
285     usleep( 100000 );
286     goto conn_check;
287     }
288     else
289     {
290     timeval->tv_sec = 0;
291     timeval->tv_usec = 100000;
292     /* A select() error probably means it was interrupted
293     by a signal, or that something else's very wrong... */
294     if( select( hifd + 1, fds, NULL, NULL, timeval ) == -1 )
295     {
296     axel->ready = -1;
297     return;
298     }
299     }
300    
301     /* Handle connections which need attention */
302     for( i = 0; i < axel->conf->num_connections; i ++ )
303     if( axel->conn[i].enabled ) {
304     if( FD_ISSET( axel->conn[i].fd, fds ) )
305     {
306     axel->conn[i].last_transfer = gettime();
307     size = read( axel->conn[i].fd, buffer, axel->conf->buffer_size );
308     if( size == -1 )
309     {
310     if( axel->conf->verbose )
311     {
312     axel_message( axel, _("Error on connection %i! "
313     "Connection closed"), i );
314     }
315     axel->conn[i].enabled = 0;
316     conn_disconnect( &axel->conn[i] );
317     continue;
318     }
319     else if( size == 0 )
320     {
321     if( axel->conf->verbose )
322     {
323     /* Only abnormal behaviour if: */
324     if( axel->conn[i].currentbyte < axel->conn[i].lastbyte && axel->size != INT_MAX )
325     {
326     axel_message( axel, _("Connection %i unexpectedly closed"), i );
327     }
328     else
329     {
330     axel_message( axel, _("Connection %i finished"), i );
331     }
332     }
333     if( !axel->conn[0].supported )
334     {
335     axel->ready = 1;
336     }
337     axel->conn[i].enabled = 0;
338     conn_disconnect( &axel->conn[i] );
339     continue;
340     }
341     /* j == Bytes to go */
342     j = axel->conn[i].lastbyte - axel->conn[i].currentbyte + 1;
343     if( j < size )
344     {
345     if( axel->conf->verbose )
346     {
347     axel_message( axel, _("Connection %i finished"), i );
348     }
349     axel->conn[i].enabled = 0;
350     conn_disconnect( &axel->conn[i] );
351     size = j;
352     /* Don't terminate, still stuff to write! */
353     }
354     /* This should always succeed.. */
355     lseek( axel->outfd, axel->conn[i].currentbyte, SEEK_SET );
356     if( write( axel->outfd, buffer, size ) != size )
357     {
358    
359     axel_message( axel, _("Write error!") );
360     axel->ready = -1;
361     return;
362     }
363     axel->conn[i].currentbyte += size;
364     axel->bytes_done += size;
365     }
366     else
367     {
368     if( gettime() > axel->conn[i].last_transfer + axel->conf->connection_timeout )
369     {
370     if( axel->conf->verbose )
371     axel_message( axel, _("Connection %i timed out"), i );
372     conn_disconnect( &axel->conn[i] );
373     axel->conn[i].enabled = 0;
374     }
375     } }
376    
377     if( axel->ready )
378     return;
379    
380     conn_check:
381     /* Look for aborted connections and attempt to restart them. */
382     for( i = 0; i < axel->conf->num_connections; i ++ )
383     {
384     if( !axel->conn[i].enabled && axel->conn[i].currentbyte < axel->conn[i].lastbyte )
385     {
386     if( axel->conn[i].state == 0 )
387     {
388     conn_set( &axel->conn[i], axel->url->text );
389     axel->url = axel->url->next;
390     /* axel->conn[i].local_if = axel->conf->interfaces->text;
391     axel->conf->interfaces = axel->conf->interfaces->next; */
392     if( axel->conf->verbose >= 2 )
393     axel_message( axel, _("Connection %i downloading from %s:%i using interface %s"),
394     i, axel->conn[i].host, axel->conn[i].port, axel->conn[i].local_if );
395     if( pthread_create( axel->conn[i].setup_thread, NULL, setup_thread, &axel->conn[i] ) == 0 )
396     {
397     axel->conn[i].state = 1;
398     axel->conn[i].last_transfer = gettime();
399     }
400     else
401     {
402     axel_message( axel, _("pthread error!!!") );
403     axel->ready = -1;
404     }
405     }
406     else
407     {
408     if( gettime() > axel->conn[i].last_transfer + axel->conf->reconnect_delay )
409     {
410     pthread_cancel( *axel->conn[i].setup_thread );
411     axel->conn[i].state = 0;
412     }
413     }
414     }
415     }
416    
417     /* Calculate current average speed and finish_time */
418     axel->bytes_per_second = (int) ( (double) ( axel->bytes_done - axel->start_byte ) / ( gettime() - axel->start_time ) );
419     axel->finish_time = (int) ( axel->start_time + (double) ( axel->size - axel->start_byte ) / axel->bytes_per_second );
420    
421     /* Check speed. If too high, delay for some time to slow things
422     down a bit. I think a 5% deviation should be acceptable. */
423     if( axel->conf->max_speed > 0 )
424     {
425     if( (float) axel->bytes_per_second / axel->conf->max_speed > 1.05 )
426     axel->delay_time += 10000;
427     else if( ( (float) axel->bytes_per_second / axel->conf->max_speed < 0.95 ) && ( axel->delay_time >= 10000 ) )
428     axel->delay_time -= 10000;
429     else if( ( (float) axel->bytes_per_second / axel->conf->max_speed < 0.95 ) )
430     axel->delay_time = 0;
431     usleep( axel->delay_time );
432     }
433    
434     /* Ready? */
435     if( axel->bytes_done == axel->size )
436     axel->ready = 1;
437     }
438    
439     /* Close an axel connection */
440     void axel_close( axel_t *axel )
441     {
442     int i;
443     message_t *m;
444    
445     /* Terminate any thread still running */
446     for( i = 0; i < axel->conf->num_connections; i ++ )
447     pthread_cancel( *axel->conn[i].setup_thread );
448    
449     /* Delete state file if necessary */
450     if( axel->ready == 1 )
451     {
452     snprintf( buffer, MAX_STRING, "%s.st", axel->filename );
453     unlink( buffer );
454     }
455     /* Else: Create it.. */
456     else if( axel->bytes_done > 0 )
457     {
458     save_state( axel );
459     }
460    
461     /* Delete any message not processed yet */
462     while( axel->message )
463     {
464     m = axel->message;
465     axel->message = axel->message->next;
466     free( m );
467     }
468    
469     /* Close all connections and local file */
470     close( axel->outfd );
471     for( i = 0; i < axel->conf->num_connections; i ++ )
472     conn_disconnect( &axel->conn[i] );
473    
474     free( axel->conn );
475     free( axel );
476     }
477    
478     /* time() with more precision */
479     double gettime()
480     {
481     struct timeval time[1];
482    
483     gettimeofday( time, 0 );
484     return( (double) time->tv_sec + (double) time->tv_usec / 1000000 );
485     }
486    
487     /* Save the state of the current download */
488     void save_state( axel_t *axel )
489     {
490     int fd, i;
491     char fn[MAX_STRING+4];
492    
493     /* No use for such a file if the server doesn't support
494     resuming anyway.. */
495     if( !axel->conn[0].supported )
496     return;
497    
498     snprintf( fn, MAX_STRING, "%s.st", axel->filename );
499     if( ( fd = open( fn, O_CREAT | O_TRUNC | O_WRONLY, 0666 ) ) == -1 )
500     {
501     return; /* Not 100% fatal.. */
502     }
503     write( fd, &axel->conf->num_connections, sizeof( axel->conf->num_connections ) );
504     write( fd, &axel->bytes_done, sizeof( axel->bytes_done ) );
505     for( i = 0; i < axel->conf->num_connections; i ++ )
506     {
507     write( fd, &axel->conn[i].currentbyte, sizeof( axel->conn[i].currentbyte ) );
508     }
509     close( fd );
510     }
511    
512     /* Thread used to set up a connection */
513     void *setup_thread( void *c )
514     {
515     conn_t *conn = c;
516     int oldstate;
517    
518     /* Allow this thread to be killed at any time. */
519     pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate );
520     pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, &oldstate );
521    
522     if( conn_setup( conn ) )
523     {
524     conn->last_transfer = gettime();
525     if( conn_exec( conn ) )
526     {
527     conn->last_transfer = gettime();
528     conn->enabled = 1;
529     conn->state = 0;
530     return( NULL );
531     }
532     }
533    
534     conn_disconnect( conn );
535     conn->state = 0;
536     return( NULL );
537     }
538    
539     /* Add a message to the axel->message structure */
540     static void axel_message( axel_t *axel, char *format, ... )
541     {
542     message_t *m = malloc( sizeof( message_t ) ), *n = axel->message;
543     va_list params;
544    
545     memset( m, 0, sizeof( message_t ) );
546     va_start( params, format );
547     vsnprintf( m->text, MAX_STRING, format, params );
548     va_end( params );
549    
550     if( axel->message == NULL )
551     {
552     axel->message = m;
553     }
554     else
555     {
556     while( n->next != NULL )
557     n = n->next;
558     n->next = m;
559     }
560     }
561    
562     /* Divide the file and set the locations for each connection */
563     static void axel_divide( axel_t *axel )
564     {
565     int i;
566    
567     axel->conn[0].currentbyte = 0;
568     axel->conn[0].lastbyte = axel->size / axel->conf->num_connections - 1;
569     for( i = 1; i < axel->conf->num_connections; i ++ )
570     {
571     #ifdef DEBUG
572     printf( "Downloading %i-%i using conn. %i\n", axel->conn[i-1].currentbyte, axel->conn[i-1].lastbyte, i - 1 );
573     #endif
574     axel->conn[i].currentbyte = axel->conn[i-1].lastbyte + 1;
575     axel->conn[i].lastbyte = axel->conn[i].currentbyte + axel->size / axel->conf->num_connections;
576     }
577     axel->conn[axel->conf->num_connections-1].lastbyte = axel->size - 1;
578     #ifdef DEBUG
579     printf( "Downloading %i-%i using conn. %i\n", axel->conn[i-1].currentbyte, axel->conn[i-1].lastbyte, i - 1 );
580     #endif
581     }

  ViewVC Help
Powered by ViewVC 1.1.5