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

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

Implemented two hooks in the bbftpd source that allow
python code to be executed before and afer a fork(). This is needed
to reset python logging and will probably be usefull for other things
in the future.

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