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

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

Initial import of bbftp source

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