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

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

Overhalled the logging facility, packaging it so that it is
simmilar to bbftpd_private_auth. When compiling non-embedded bbftp
you can activate what is called "private-logging" with the
--enable-private-logging switch. As a result both the test_embedded.py
and test_bbftpd.py test scripts can be run without access to syslog.

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