source: TI05-delivery/trunk/src/bbftp-server-3.2.0/bbftpd/sendafilerfio.c @ 1431

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/trunk/src/bbftp-server-3.2.0/bbftpd/sendafilerfio.c@1431
Revision 1431, 21.1 KB checked in by spascoe, 14 years ago (diff)

bbftpd.h should be the first #include so that glibc headers pick up
any configuration flags.

Line 
1/*
2 * bbftpd/sendafilerfio.c
3 * Copyright (C) 1999, 2000, 2001, 2002 IN2P3, CNRS
4 * bbftp@in2p3.fr
5 * http://doc.in2p3.fr/bbftp
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */ 
21
22/****************************************************************************
23
24 
25 RETURN:
26         0  Keep the connection open (does not mean that the file has been
27           successfully transfered)
28        -1 Tell the calling program to close the connection
29
30 Child Exit code:
31        = 0    Success
32        > 0    MES_BAD has to be sent to the client
33       
34        In fact only the first child ending in error will be taken in account.
35        The absolute value of the exit code will be an errno code and the
36        strerror(errno) will be sent as an explanation message.
37        There is no 255 exit code because no error from the child may be
38        considered as fatal
39 
40 sendafilerfio.c  v 1.7.0  2000/03/31   - Creation of the routine. This
41                                          routine is the copy of sendfile.c but
42                                          as rfio is not 64 bits compliant
43                                          we use a different routine
44                  v 1.8.0  2000/04/14   - Change location of headers file for zlib
45                  v 1.8.3  2000/04/18   - Implement better return code to client
46                  v 1.8.7  2000/05/24   - Modify headers
47                  v 1.8.8  2000/05/31   - Check if file to read is not a directory
48                                        - Take care of a zero length file
49                  v 1.8.9  2000/07/28   - Change stat call for rfio_stat
50                  v 1.8.10 2000/08/11   - Portage to Linux
51                                        - Better start child (wait only if HUP was
52                                          not sent)
53                  v 1.9.0  2000/08/18   - Use configure to help portage
54                  v 1.9.4  2000/10/16   - Close correctly data socket in child
55                                          in order not to keep socket in TIME_WAIT
56                                          on the server
57                                        - Supress %m
58                  v 2.0.0  2000/10/18   - Set state before starting children
59                  v 2.0.1  2001/04/23   - Correct indentation
60                  v 2.0.2  2001/05/04   - Correct include for RFIO
61                  v 2.1.0  2001/05/30   - Correct bbftpd_log level
62                                     
63 *****************************************************************************/
64#include <bbftpd.h>
65
66#include <errno.h>
67#include <fcntl.h>
68#include <netinet/in.h>
69#include <stdio.h>
70#include <signal.h>
71#include <bbftpd_private_log.h>
72#include <sys/stat.h>
73#include <sys/time.h>
74#include <sys/types.h>
75#include <unistd.h>
76
77#include <common.h>
78#include <daemon.h>
79#include <status.h>
80#include <structures.h>
81
82#ifdef WITH_GZIP
83#include <zlib.h>
84#endif
85
86#include <shift.h>
87
88extern int flagsighup ;
89extern int msgsock ;
90extern char currentfilename[MAXLENFILE];
91extern int childendinerror ;
92extern int pid_child[MAXPORT] ;
93extern int state ;
94extern  int     recvcontrolto ;
95extern  int     datato ;
96extern  int     ackto ;
97
98#ifndef WORDS_BIGENDIAN
99#ifndef HAVE_NTOHLL
100my64_t    ntohll(my64_t v) ;
101#endif
102#endif
103
104int sendafilerfio(int code) {
105
106    int        i ;
107    int        retcode ;
108    char    receive_buffer[MAXMESSLEN] ;
109   
110    char    receive_buffer_sup[MAXMESSLEN] ;
111    char    send_buffer[MAXMESSLEN] ;
112    int        savederrno ;
113
114    char    readbuffer[READBUFLEN] ;
115    char    buffercomp[READBUFLEN] ;
116    struct    mess_compress *msg_compress ;
117#ifdef WITH_GZIP
118    uLong    buflen ;
119    uLong    bufcomplen ;
120#endif
121    my64_t    filelen64 ;
122    my64_t    toprint64 ;
123   
124    off_t         nbperchild ;
125    off_t         nbtosend ;
126    off_t         nbread ;
127    off_t         nbsent ;
128    off_t         numberread ;
129    off_t         startpoint ;
130    off_t        realnbtosend ;
131    off_t        filelen ;
132    int            lentosend ;
133    struct stat statbuf ;
134
135    char    readfilename[MAXLENFILE] ;
136   
137    int        fd ;
138    int        sendsock ;
139    char    logmessage[256] ;
140    int        compressiontype ;
141   
142    struct message *msg ;
143    struct mess_store *msg_store ;
144    struct mess_retr_ok *msg_retr_ok ;
145   
146    int        nfds ;
147    fd_set    selectmask ; /* Select mask */
148    struct timeval    wait_timer;
149   
150    /*
151    ** Initilize the pid array
152    */
153     for ( i=0 ; i< MAXPORT ; i++) {
154        pid_child[i] = 0 ;
155    }
156    childendinerror = 0 ; /* No child so no error */
157
158    strcpy(currentfilename,"") ;
159    /*
160    ** We read the message even if we don not support the command
161    ** in order not to bore the clien
162    */ 
163    if ( (retcode = readmessage(msgsock,receive_buffer,STORMESSLEN,recvcontrolto) ) < 0 ) {
164        bbftpd_log(BBFTPD_ERR,"Error receiving file char") ;
165        return retcode ;
166    }
167    msg_store = (struct mess_store *) receive_buffer ;
168    strcpy(readfilename,msg_store->filename) ;
169#ifndef WORDS_BIGENDIAN
170    msg_store->nbport = ntohl(msg_store->nbport) ;
171    for (i = 0 ; i< MAXPORT ; i++ ) {
172        msg_store->port[i] = ntohl(msg_store->port[i]) ;
173    }
174#endif
175    if ( code == MSG_RETR_RFIO ) {
176        compressiontype = NOCOMPRESSION ;
177        bbftpd_log(BBFTPD_DEBUG,"Retreiving rfio file %s with %d children",readfilename,msg_store->nbport) ;
178    } else {
179        compressiontype = COMPRESSION ;
180        bbftpd_log(BBFTPD_DEBUG,"Retreiving rfio file %s with %d children in compressed mode",readfilename,msg_store->nbport) ;
181    }
182    /*
183    ** WARNING ----------
184    **
185    ** Do not use the receive buffer in father before having forked
186    ** because all data related to the ports are in it
187    **
188    ** WARNING ----------
189    */
190    /*
191    ** First stat the file in order to know if it is a directory
192    */
193    if ( rfio_stat(readfilename,&statbuf) < 0 ) {
194        /*
195        ** We tell the client not to retry in the following case (even in waiting
196        ** WAITRETRYTIME the problem will not be solved) :
197        **        EACCES        : Search permission denied
198        **        ELOOP        : To many symbolic links on path
199        **        ENAMETOOLONG: Path argument too long
200        **        ENOENT        : The file does not exists
201        **        ENOTDIR        : A component in path is not a directory
202        */
203        savederrno = errno ;
204        sprintf(logmessage,"Error stating file %s : %s ",readfilename,strerror(savederrno)) ;
205        bbftpd_log(BBFTPD_ERR,"Error stating file %s : %s ",readfilename,strerror(savederrno)) ;
206        if ( savederrno == EACCES ||
207            savederrno == ELOOP ||
208            savederrno == ENAMETOOLONG ||
209            savederrno == ENOENT ||
210            savederrno == ENOTDIR ) {
211            reply(MSG_BAD_NO_RETRY,logmessage) ;
212            return 0 ;
213        } else {
214            reply(MSG_BAD,logmessage) ;
215            return 0 ;
216        }
217    } else {
218        /*
219        ** The file exists so check if it is a directory
220        */
221        if ( (statbuf.st_mode & S_IFDIR) == S_IFDIR) {
222            bbftpd_log(BBFTPD_ERR,"file %s is a directory",readfilename) ;
223            sprintf(logmessage,"File %s is a directory",readfilename) ;
224            reply(MSG_BAD_NO_RETRY,logmessage) ;
225            return 0 ;
226        }
227    }               
228    /*
229    ** Getting filesize in order to send it to the client
230    */
231    if ( (fd = open(readfilename,O_RDONLY)) < 0 ) {
232        /*
233        ** An error on openning the local file is considered
234        ** as fatal. Maybe this need to be improved depending
235        ** on errno
236        */
237        savederrno = errno ;
238        bbftpd_log(BBFTPD_ERR,"Error opening local file %s : %s",readfilename,strerror(errno)) ;
239        sprintf(logmessage,"Error opening local file %s : %s ",readfilename,strerror(errno)) ;
240        /*
241        ** We tell the client not to retry in the following case (even in waiting
242        ** WAITRETRYTIME the problem will not be solved) :
243        **        EACCES        : Search permission denied
244        **        ELOOP        : To many symbolic links on path
245        **        ENOENT        : No such file or directory
246        **        ENAMETOOLONG: Path argument too long
247        **        ENOTDIR        : A component in path is not a directory
248        */
249        if ( savederrno == EACCES ||
250                savederrno == ELOOP ||
251                savederrno == ENOENT ||
252                savederrno == ENAMETOOLONG ||
253                savederrno == ENOTDIR ) {
254            reply(MSG_BAD_NO_RETRY,logmessage) ;
255        } else {
256            reply(MSG_BAD,logmessage) ;
257        }
258        return 0 ;
259    }
260    if ( (filelen = lseek(fd,0,SEEK_END) ) < 0 ) {
261        /*
262        ** An error on seekin the local file is considered
263        ** as fatal. lseek error  in this case is completly
264        ** abnormal
265        */
266        close(fd) ;
267        bbftpd_log(BBFTPD_ERR,"Error seeking local file %s : %s",readfilename,strerror(errno)) ;
268        sprintf(logmessage,"Error seeking local file %s : %s ",readfilename,strerror(errno)) ;
269        reply(MSG_BAD,logmessage) ;
270        return 0 ;
271    }
272    filelen64 = filelen ;
273    /*
274    ** Close the file as the only interresting thing was the length
275    */
276    close(fd) ;
277    /*
278    ** We are going to send the file length
279    */
280    msg = (struct message *) send_buffer ;
281    msg->code = MSG_RETR_OK ;
282#ifndef WORDS_BIGENDIAN
283    msg->msglen = ntohl(RETROKMESSLEN) ;
284#else
285    msg->msglen = RETROKMESSLEN ;
286#endif
287    if ( writemessage(msgsock,send_buffer,MINMESSLEN,recvcontrolto) < 0 ) {
288        /*
289        ** Something wrong in sending message
290        ** tell calling to close the connection
291        */
292        bbftpd_log(BBFTPD_ERR,"Error sending RETROK part 1") ;
293        return -1 ;
294    }
295    msg_retr_ok = (struct mess_retr_ok *) send_buffer ;
296   
297#ifndef WORDS_BIGENDIAN
298    msg_retr_ok->filesize = ntohll(filelen64) ;
299#else
300    msg_retr_ok->filesize = filelen64 ;
301#endif
302    if ( writemessage(msgsock,send_buffer,RETROKMESSLEN,recvcontrolto) < 0 ) {
303        /*
304        ** Something wrong in sending message
305        ** tell calling to close the connection
306        */
307        bbftpd_log(BBFTPD_ERR,"Error sending RETROK part 2") ;
308        return -1 ;
309    }
310    /*
311    ** Wait for the START message
312    */
313    if ( readmessage(msgsock,receive_buffer_sup,MINMESSLEN,RETRSTARTTO) < 0 ) {
314        /*
315        ** Something wrong in receiving message
316        ** tell calling to close the connection
317        */
318        bbftpd_log(BBFTPD_ERR,"Error receiving RETRSTART") ;
319        return -1 ;
320    }
321    msg = (struct message *) receive_buffer_sup ;
322    if ( msg->code == MSG_ABR) {
323        /*
324        ** In this case the client will not close the connection
325        ** so do the same
326        */
327        bbftpd_log(BBFTPD_ERR,"Receive ABORT message") ;
328        return 0 ;
329    } else if ( msg->code == MSG_CREATE_ZERO ) {
330        bbftpd_log(BBFTPD_INFO,"Send zero length file") ;
331        return 0 ;
332    } else if ( msg->code != MSG_RETR_START ) {
333        bbftpd_log(BBFTPD_ERR,"Receive Unknown message code while waiting RETRSTART %d",msg->code) ;
334        return -1 ;
335    }
336    /*
337    ** So we receive the start message ...
338    */
339    /*
340    ** Now start all our children
341    */
342    nbperchild = filelen/msg_store->nbport ;
343    for (i = 1 ; i <= msg_store->nbport ; i++) {
344        if ( i == msg_store->nbport) {
345            startpoint = (i-1)*nbperchild;
346            nbtosend = filelen-(nbperchild*(msg_store->nbport-1)) ;
347        } else {
348            startpoint = (i-1)*nbperchild;
349            nbtosend = nbperchild ;
350        }
351        /*
352        ** Now create the socket to send
353        */
354        sendsock = 0 ;
355        while (sendsock == 0 ) {
356            sendsock = createreceivesock(msg_store->port[i-1],i,logmessage) ;
357        }
358        if ( sendsock < 0 ) {
359            /*
360            ** We set childendinerror to 1 in order to prevent the father
361            ** to send a BAD message which can desynchronize the client and the
362            ** server (We need only one error message)
363            ** Bug discovered by amlutz on 2000/03/11
364            */
365            if ( childendinerror == 0 ) {
366                childendinerror = 1 ;
367                reply(MSG_BAD,logmessage) ;
368            }
369            clean_child() ;
370            return 0 ;
371        }
372        /*
373        ** Set flagsighup to zero in order to be able in child
374        ** not to wait STARTCHILDTO if signal was sent before
375        ** entering select. (Seen on Linux with one child)
376        */
377        flagsighup = 0 ;
378        /*
379        ** At this stage we are ready to receive packets
380        ** So we are going to fork
381        */
382        if ( (retcode = fork()) == 0 ) {
383            /*
384            ** We are in child
385            */
386            /*
387            ** Pause until father send a SIGHUP in order to prevent
388            ** child to die before father has started all children
389            */
390            if ( flagsighup == 0) {
391                nfds = sysconf(_SC_OPEN_MAX) ;
392                wait_timer.tv_sec  = STARTCHILDTO ;
393                wait_timer.tv_usec = 0 ;
394                select(nfds,0,0,0,&wait_timer) ;
395            }
396            bbftpd_log(BBFTPD_DEBUG,"Child Starting") ;
397            /*
398            ** Close all unnecessary stuff
399            */
400            close(msgsock) ;
401            /*
402            ** And open the file
403            */
404            if ( (fd = open(readfilename,O_RDONLY)) < 0 ) {
405                /*
406                ** An error on openning the local file is considered
407                ** as fatal. Maybe this need to be improved depending
408                ** on errno
409                */
410                i = errno ;
411                bbftpd_log(BBFTPD_ERR,"Error opening local file %s : %s",readfilename,strerror(errno)) ;
412                close(sendsock) ;
413                exit(i) ;
414            }
415            if ( lseek(fd,startpoint,SEEK_SET) < 0 ) {
416                i = errno ;
417                close(fd) ;
418                bbftpd_log(BBFTPD_ERR,"Error seeking file : %s",strerror(errno)) ;
419                close(sendsock) ;
420                exit(i)  ;
421            }
422            /*
423            ** Start the sending loop
424            */
425            nbread = 0 ;
426            while ( nbread < nbtosend ) {
427                if ( (numberread = read ( fd, readbuffer, (sizeof(readbuffer) <= nbtosend - nbread ) ? sizeof(readbuffer) : nbtosend - nbread) ) > 0 ) {
428                    nbread = nbread+numberread ;
429#ifdef WITH_GZIP
430                    if ( compressiontype == COMPRESSION ) {
431                        /*
432                        ** In case of compression we are going to use
433                        ** a temporary buffer
434                        */
435                        bufcomplen = READBUFLEN ;
436                        buflen = numberread ;
437                        msg_compress = ( struct mess_compress *) send_buffer;
438                        retcode = compress((Bytef *)buffercomp,&bufcomplen,(Bytef *)readbuffer,buflen) ;
439                        if ( retcode != 0 ) {
440                            /*
441                            ** Compress error, in this cas we are sending the
442                            ** date uncompressed
443                            */
444                            msg_compress->code = DATA_NOCOMPRESS ;
445                            lentosend = numberread ;
446#ifndef WORDS_BIGENDIAN
447                            msg_compress->datalen = ntohl(lentosend) ;
448#else
449                            msg_compress->datalen = lentosend ;
450#endif
451                            realnbtosend = numberread ;
452                        } else {
453                            msg_compress->code = DATA_COMPRESS ;
454                            lentosend = bufcomplen ;
455#ifndef WORDS_BIGENDIAN
456                            msg_compress->datalen = ntohl(lentosend) ;
457#else
458                            msg_compress->datalen = lentosend ;
459#endif
460                            realnbtosend =  bufcomplen ;
461                            memcpy(readbuffer,buffercomp,READBUFLEN) ;
462                        }
463                        /*
464                        ** Send the header
465                        */
466                        if ( writemessage(sendsock,send_buffer,COMPMESSLEN,datato) < 0 ) {
467                            i = ETIMEDOUT ;
468                            bbftpd_log(BBFTPD_ERR,"Error sending header data") ;
469                            close(sendsock) ;
470                            exit(i) ;
471                        }
472                    } else {
473                        realnbtosend = numberread ;
474                    }
475#else
476                    realnbtosend = numberread ;
477#endif
478                    /*
479                    ** Send the data
480                    */
481                    nbsent = 0 ;
482                    while ( nbsent < realnbtosend ) {
483                        lentosend = realnbtosend-nbsent ;
484                        nfds = sysconf(_SC_OPEN_MAX) ;
485                        FD_ZERO(&selectmask) ;
486                        FD_SET(sendsock,&selectmask) ;
487                        wait_timer.tv_sec  = datato ;
488                        wait_timer.tv_usec = 0 ;
489                        if ( (retcode = select(nfds,0,&selectmask,0,&wait_timer) ) == -1 ) {
490                            /*
491                            ** Select error
492                            */
493                            i = errno ;
494                            bbftpd_log(BBFTPD_ERR,"Error select while sending : %s",strerror(errno)) ;
495                            close(fd) ;
496                            close(sendsock) ;
497                            exit(i) ;
498                        } else if ( retcode == 0 ) {
499                            bbftpd_log(BBFTPD_ERR,"Time out while sending") ;
500                            close(fd) ;
501                            i=ETIMEDOUT ;
502                            close(sendsock) ;
503                            exit(i) ;
504                        } else {
505                            retcode = send(sendsock,&readbuffer[nbsent],lentosend,0) ;
506                            if ( retcode < 0 ) {
507                                i = errno ;
508                                bbftpd_log(BBFTPD_ERR,"Error while sending %s",strerror(i)) ;
509                                close(sendsock) ;
510                                exit(i) ;
511                            } else if ( retcode == 0 ) {
512                                i = ECONNRESET ;
513                                bbftpd_log(BBFTPD_ERR,"Connexion breaks") ;
514                                close(fd) ;
515                                close(sendsock) ;
516                                exit(i) ;
517                            } else {
518                                nbsent = nbsent+retcode ;
519                            }
520                        }
521                    }
522                } else {
523                    i = errno ;
524                    bbftpd_log(BBFTPD_ERR,"Child Error reading : %s",strerror(errno)) ;
525                    close(sendsock) ;
526                    exit(i) ;
527                }
528            }
529            /*
530            ** All data has been sent so wait for the acknoledge
531            */
532            if ( readmessage(sendsock,receive_buffer,MINMESSLEN,ackto) < 0 ) {
533                bbftpd_log(BBFTPD_ERR,"Error waiting ACK") ;
534                close(sendsock) ;
535                exit(ETIMEDOUT) ;
536            }
537            msg = (struct message *) receive_buffer ;
538            if ( msg->code != MSG_ACK) {
539                bbftpd_log(BBFTPD_ERR,"Error unknown messge while waiting ACK %d",msg->code) ;
540                close(sendsock) ;
541                exit(1) ;
542            }
543            toprint64 = nbtosend ;
544            bbftpd_log(BBFTPD_DEBUG,"Child send %" LONG_LONG_FORMAT" bytes ; end correct ",toprint64) ;
545            close(sendsock) ;
546            exit(0) ;
547        } else {
548            /*
549            ** We are in father
550            */
551            if ( retcode == -1 ) {
552                /*
553                ** Fork failed ...
554                */
555                bbftpd_log(BBFTPD_ERR,"fork failed : %s",strerror(errno)) ;
556                sprintf(logmessage,"fork failed : %s ",strerror(errno)) ;
557                if ( childendinerror == 0 ) {
558                    childendinerror = 1 ;
559                    reply(MSG_BAD,logmessage) ;
560                }
561                clean_child() ;
562                return 0 ;
563            } else {
564                bbftpd_log(BBFTPD_DEBUG,"Started child pid %d",retcode) ;
565                pid_child[i-1] = retcode ;
566                close(sendsock) ;
567            }
568        }
569    }
570    /*
571    ** Set the state before starting children because if the file was
572    ** small the child has ended before state was setup to correct value
573    */
574    state = S_SENDING ;
575    /*
576    ** Start all children
577    */
578    for (i = 0 ; i<MAXPORT ; i++) {
579        if (pid_child[i] != 0) {
580            kill(pid_child[i],SIGHUP) ;
581        }
582    }
583    return 0 ;
584}
585
Note: See TracBrowser for help on using the repository browser.