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

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