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

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

Eliminated all "implicit declaration" warnings from "python setup.py
build"

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