source: trunk/libtransmission/fdlimit.c @ 10075

Last change on this file since 10075 was 10075, checked in by charles, 12 years ago

(trunk libT) #2849 "When possible, use fallocate64() for file preallocation" -- implemented for 1.90

  • Property svn:keywords set to Date Rev Author Id
File size: 21.3 KB
Line 
1/******************************************************************************
2 * $Id: fdlimit.c 10075 2010-02-02 03:01:25Z charles $
3 *
4 * Copyright (c) 2005-2010 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#ifndef WIN32
26 #define HAVE_GETRLIMIT
27#endif
28
29#ifdef HAVE_POSIX_FADVISE
30 #ifdef _XOPEN_SOURCE
31  #undef _XOPEN_SOURCE
32 #endif
33 #define _XOPEN_SOURCE 600
34#endif
35
36#include <assert.h>
37#include <errno.h>
38#include <inttypes.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#ifdef SYS_DARWIN
43 #include <fcntl.h>
44#endif
45
46#ifdef HAVE_FALLOCATE64
47  /* FIXME can't find the right #include voodoo to pick up the declaration.. */
48  extern int fallocate64( int fd, int mode, uint64_t offset, uint64_t len );
49#endif
50
51#ifdef HAVE_XFS_XFS_H
52 #include <xfs/xfs.h>
53#endif
54
55#include <sys/types.h>
56#include <sys/stat.h>
57#ifdef HAVE_GETRLIMIT
58 #include <sys/time.h> /* getrlimit */
59 #include <sys/resource.h> /* getrlimit */
60#endif
61#include <fcntl.h> /* O_LARGEFILE posix_fadvise */
62#include <unistd.h>
63
64#include <stdarg.h> /* some 1.4.x versions of evutil.h need this */
65#include <evutil.h>
66
67#include "transmission.h"
68#include "fdlimit.h"
69#include "net.h"
70#include "platform.h" /* MAX_PATH_LENGTH, TR_PATH_DELIMITER */
71#include "session.h"
72#include "torrent.h" /* tr_isTorrent() */
73#include "utils.h"
74
75#define dbgmsg( ... ) \
76    do { \
77        if( tr_deepLoggingIsActive( ) ) \
78            tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
79    } while( 0 )
80
81/**
82***
83**/
84
85struct tr_openfile
86{
87    tr_bool          isWritable;
88    int              torrentId;
89    tr_file_index_t  fileNum;
90    char             filename[MAX_PATH_LENGTH];
91    int              fd;
92    uint64_t         date;
93};
94
95struct tr_fdInfo
96{
97    int                   socketCount;
98    int                   socketLimit;
99    int                   publicSocketLimit;
100    int                   openFileLimit;
101    struct tr_openfile  * openFiles;
102};
103
104/***
105****
106****  Local Files
107****
108***/
109
110#ifndef O_LARGEFILE
111 #define O_LARGEFILE 0
112#endif
113
114static tr_bool
115preallocateFileSparse( int fd, uint64_t length )
116{
117    const char zero = '\0';
118
119    if( length == 0 )
120        return TRUE;
121
122    if( lseek( fd, length-1, SEEK_SET ) == -1 )
123        return FALSE;
124    if( write( fd, &zero, 1 ) == -1 )
125        return FALSE;
126    if( ftruncate( fd, length ) == -1 )
127        return FALSE;
128
129    return TRUE;
130}
131
132static tr_bool
133preallocateFileFull( const char * filename, uint64_t length )
134{
135    tr_bool success = 0;
136
137#ifdef WIN32
138
139    HANDLE hFile = CreateFile( filename, GENERIC_WRITE, 0, 0, CREATE_NEW, 0, 0 );
140    if( hFile != INVALID_HANDLE_VALUE )
141    {
142        LARGE_INTEGER li;
143        li.QuadPart = length;
144        success = SetFilePointerEx( hFile, li, NULL, FILE_BEGIN ) && SetEndOfFile( hFile );
145        CloseHandle( hFile );
146    }
147
148#else
149
150    int flags = O_RDWR | O_CREAT | O_LARGEFILE;
151    int fd = open( filename, flags, 0666 );
152    if( fd >= 0 )
153    {
154# ifdef HAVE_FALLOCATE64
155       if( !success )
156       {
157           success = !fallocate64( fd, 0, 0, length );
158       }
159# endif
160# ifdef HAVE_XFS_XFS_H
161        if( !success && platform_test_xfs_fd( fd ) )
162        {
163            xfs_flock64_t fl;
164            fl.l_whence = 0;
165            fl.l_start = 0;
166            fl.l_len = length;
167            success = !xfsctl( NULL, fd, XFS_IOC_RESVSP64, &fl );
168        }
169# endif
170# ifdef SYS_DARWIN
171        if( !success )
172        {
173            fstore_t fst;
174            fst.fst_flags = F_ALLOCATECONTIG;
175            fst.fst_posmode = F_PEOFPOSMODE;
176            fst.fst_offset = 0;
177            fst.fst_length = length;
178            fst.fst_bytesalloc = 0;
179            success = !fcntl( fd, F_PREALLOCATE, &fst );
180        }
181# endif
182# ifdef HAVE_POSIX_FALLOCATE
183        if( !success )
184        {
185            success = !posix_fallocate( fd, 0, length );
186        }
187# endif
188
189        if( !success ) /* if nothing else works, do it the old-fashioned way */
190        {
191            uint8_t buf[ 4096 ];
192            memset( buf, 0, sizeof( buf ) );
193            success = TRUE;
194            while ( success && ( length > 0 ) )
195            {
196                const int thisPass = MIN( length, sizeof( buf ) );
197                success = write( fd, buf, thisPass ) == thisPass;
198                length -= thisPass;
199            }
200        }
201
202        close( fd );
203    }
204
205#endif
206
207    return success;
208}
209
210tr_bool
211tr_preallocate_file( const char * filename, uint64_t length )
212{
213    return preallocateFileFull( filename, length );
214}
215
216/* Like pread and pwrite, except that the position is undefined afterwards.
217   And of course they are not thread-safe. */
218
219#ifdef SYS_DARWIN
220 #define HAVE_PREAD
221 #define HAVE_PWRITE
222#endif
223
224ssize_t
225tr_pread( int fd, void *buf, size_t count, off_t offset )
226{
227#ifdef HAVE_PREAD
228    return pread( fd, buf, count, offset );
229#else
230    const off_t lrc = lseek( fd, offset, SEEK_SET );
231    if( lrc < 0 )
232        return -1;
233    return read( fd, buf, count );
234#endif
235}
236
237ssize_t
238tr_pwrite( int fd, void *buf, size_t count, off_t offset )
239{
240#ifdef HAVE_PWRITE
241    return pwrite( fd, buf, count, offset );
242#else
243    const off_t lrc = lseek( fd, offset, SEEK_SET );
244    if( lrc < 0 )
245        return -1;
246    return write( fd, buf, count );
247#endif
248}
249
250int
251tr_prefetch( int fd, off_t offset, size_t count )
252{
253#ifdef HAVE_POSIX_FADVISE
254    return posix_fadvise( fd, offset, count, POSIX_FADV_WILLNEED );
255#elif defined(SYS_DARWIN)
256    struct radvisory radv;
257    radv.ra_offset = offset;
258    radv.ra_count = count;
259    return fcntl( fd, F_RDADVISE, &radv );
260#else
261    return 0;
262#endif
263}
264
265int
266tr_open_file_for_writing( const char * filename )
267{
268    int flags = O_WRONLY | O_CREAT;
269#ifdef O_BINARY
270    flags |= O_BINARY;
271#endif
272#ifdef O_LARGEFILE
273    flags |= O_LARGEFILE;
274#endif
275    return open( filename, flags, 0666 );
276}
277
278int
279tr_open_file_for_scanning( const char * filename )
280{
281    int fd;
282    int flags;
283
284    /* build the flags */
285    flags = O_RDONLY;
286#ifdef O_SEQUENTIAL
287    flags |= O_SEQUENTIAL;
288#endif
289#ifdef O_BINARY
290    flags |= O_BINARY;
291#endif
292#ifdef O_LARGEFILE
293    flags |= O_LARGEFILE;
294#endif
295
296    /* open the file */
297    fd = open( filename, flags, 0666 );
298    if( fd >= 0 )
299    {
300        /* Set hints about the lookahead buffer and caching. It's okay
301           for these to fail silently, so don't let them affect errno */
302        const int err = errno;
303#ifdef HAVE_POSIX_FADVISE
304        posix_fadvise( fd, 0, 0, POSIX_FADV_SEQUENTIAL );
305#endif
306#ifdef SYS_DARWIN
307        fcntl( fd, F_NOCACHE, 1 );
308        fcntl( fd, F_RDAHEAD, 1 );
309#endif
310        errno = err;
311    }
312
313    return fd;
314}
315
316void
317tr_close_file( int fd )
318{
319#if defined(HAVE_POSIX_FADVISE)
320    /* Set hint about not caching this file.
321       It's okay for this to fail silently, so don't let it affect errno */
322    const int err = errno;
323    posix_fadvise( fd, 0, 0, POSIX_FADV_DONTNEED );
324    errno = err;
325#endif
326    close( fd );
327}
328
329/**
330 * returns 0 on success, or an errno value on failure.
331 * errno values include ENOENT if the parent folder doesn't exist,
332 * plus the errno values set by tr_mkdirp() and open().
333 */
334static int
335TrOpenFile( tr_session             * session,
336            int                      i,
337            const char             * filename,
338            tr_bool                  doWrite,
339            tr_preallocation_mode    preallocationMode,
340            uint64_t                 desiredFileSize )
341{
342    int flags;
343    struct stat sb;
344    tr_bool alreadyExisted;
345    struct tr_openfile * file;
346
347    assert( tr_isSession( session ) );
348    assert( session->fdInfo != NULL );
349
350    file = &session->fdInfo->openFiles[i];
351
352    /* create subfolders, if any */
353    if( doWrite )
354    {
355        char * dir = tr_dirname( filename );
356        const int err = tr_mkdirp( dir, 0777 ) ? errno : 0;
357        if( err ) {
358            tr_err( _( "Couldn't create \"%1$s\": %2$s" ), dir, tr_strerror( err ) );
359            tr_free( dir );
360            return err;
361        }
362        tr_free( dir );
363    }
364
365    alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode );
366
367    if( doWrite && !alreadyExisted && ( preallocationMode == TR_PREALLOCATE_FULL ) )
368        if( preallocateFileFull( filename, desiredFileSize ) )
369            tr_inf( _( "Preallocated file \"%s\"" ), filename );
370
371    /* open the file */
372    flags = doWrite ? ( O_RDWR | O_CREAT ) : O_RDONLY;
373#ifdef O_SEQUENTIAL
374    flags |= O_SEQUENTIAL;
375#endif
376#ifdef O_LARGEFILE
377    flags |= O_LARGEFILE;
378#endif
379#ifdef WIN32
380    flags |= O_BINARY;
381#endif
382    file->fd = open( filename, flags, 0666 );
383    if( file->fd == -1 )
384    {
385        const int err = errno;
386        tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename, tr_strerror( err ) );
387        return err;
388    }
389
390    /* If the file already exists and it's too large, truncate it.
391     * This is a fringe case that happens if a torrent's been updated
392     * and one of the updated torrent's files is smaller.
393     * http://trac.transmissionbt.com/ticket/2228
394     * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
395     */
396    if( alreadyExisted && ( desiredFileSize < (uint64_t)sb.st_size ) )
397        ftruncate( file->fd, desiredFileSize );
398
399    if( doWrite && !alreadyExisted && ( preallocationMode == TR_PREALLOCATE_SPARSE ) )
400        preallocateFileSparse( file->fd, desiredFileSize );
401
402#ifdef HAVE_POSIX_FADVISE
403    /* this doubles the OS level readahead buffer, which in practice
404     * turns out to be a good thing, because many (most?) clients request
405     * chunks of blocks in order.
406     * It's okay for this to fail silently, so don't let it affect errno */
407    {
408        const int err = errno;
409        posix_fadvise( file->fd, 0, 0, POSIX_FADV_SEQUENTIAL );
410        errno = err;
411    }
412#endif
413
414    return 0;
415}
416
417static inline tr_bool
418fileIsOpen( const struct tr_openfile * o )
419{
420    return o->fd >= 0;
421}
422
423static void
424TrCloseFile( struct tr_openfile * o )
425{
426    assert( o != NULL );
427    assert( fileIsOpen( o ) );
428
429    tr_close_file( o->fd );
430    o->fd = -1;
431}
432
433int
434tr_fdFileGetCached( tr_session       * session,
435                    int                torrentId,
436                    tr_file_index_t    fileNum,
437                    tr_bool            doWrite )
438{
439    struct tr_openfile * match = NULL;
440    struct tr_fdInfo * gFd;
441
442    assert( tr_isSession( session ) );
443    assert( session->fdInfo != NULL );
444    assert( torrentId > 0 );
445    assert( tr_isBool( doWrite ) );
446
447    gFd = session->fdInfo;
448
449    /* is it already open? */
450    {
451        int i;
452        struct tr_openfile * o;
453        for( i=0; i<gFd->openFileLimit; ++i )
454        {
455            o = &gFd->openFiles[i];
456
457            if( torrentId != o->torrentId )
458                continue;
459            if( fileNum != o->fileNum )
460                continue;
461            if( !fileIsOpen( o ) )
462                continue;
463
464            match = o;
465            break;
466        }
467    }
468
469    if( ( match != NULL ) && ( !doWrite || match->isWritable ) )
470    {
471        match->date = tr_date( );
472        return match->fd;
473    }
474
475    return -1;
476}
477
478/* returns an fd on success, or a -1 on failure and sets errno */
479int
480tr_fdFileCheckout( tr_session             * session,
481                   int                      torrentId,
482                   tr_file_index_t          fileNum,
483                   const char             * filename,
484                   tr_bool                  doWrite,
485                   tr_preallocation_mode    preallocationMode,
486                   uint64_t                 desiredFileSize )
487{
488    int i, winner = -1;
489    struct tr_fdInfo * gFd;
490    struct tr_openfile * o;
491
492    assert( tr_isSession( session ) );
493    assert( session->fdInfo != NULL );
494    assert( torrentId > 0 );
495    assert( filename && *filename );
496    assert( tr_isBool( doWrite ) );
497
498    gFd = session->fdInfo;
499
500    dbgmsg( "looking for file '%s', writable %c", filename, doWrite ? 'y' : 'n' );
501
502    /* is it already open? */
503    for( i=0; i<gFd->openFileLimit; ++i )
504    {
505        o = &gFd->openFiles[i];
506
507        if( torrentId != o->torrentId )
508            continue;
509        if( fileNum != o->fileNum )
510            continue;
511        if( !fileIsOpen( o ) )
512            continue;
513
514        if( doWrite && !o->isWritable )
515        {
516            dbgmsg( "found it!  it's open and available, but isn't writable. closing..." );
517            TrCloseFile( o );
518            break;
519        }
520
521        dbgmsg( "found it!  it's ready for use!" );
522        winner = i;
523        break;
524    }
525
526    dbgmsg( "it's not already open.  looking for an open slot or an old file." );
527    while( winner < 0 )
528    {
529        uint64_t date = tr_date( ) + 1;
530
531        /* look for the file that's been open longest */
532        for( i=0; i<gFd->openFileLimit; ++i )
533        {
534            o = &gFd->openFiles[i];
535
536            if( !fileIsOpen( o ) )
537            {
538                winner = i;
539                dbgmsg( "found an empty slot in %d", winner );
540                break;
541            }
542
543            if( date > o->date )
544            {
545                date = o->date;
546                winner = i;
547            }
548        }
549
550        assert( winner >= 0 );
551
552        if( fileIsOpen( &gFd->openFiles[winner] ) )
553        {
554            dbgmsg( "closing file \"%s\"", gFd->openFiles[winner].filename );
555            TrCloseFile( &gFd->openFiles[winner] );
556        }
557    }
558
559    assert( winner >= 0 );
560    o = &gFd->openFiles[winner];
561    if( !fileIsOpen( o ) )
562    {
563        const int err = TrOpenFile( session, winner, filename, doWrite,
564                                    preallocationMode, desiredFileSize );
565        if( err ) {
566            errno = err;
567            return -1;
568        }
569
570        dbgmsg( "opened '%s' in slot %d, doWrite %c", filename, winner,
571                doWrite ? 'y' : 'n' );
572        tr_strlcpy( o->filename, filename, sizeof( o->filename ) );
573        o->isWritable = doWrite;
574    }
575
576    dbgmsg( "checking out '%s' in slot %d", filename, winner );
577    o->torrentId = torrentId;
578    o->fileNum = fileNum;
579    o->date = tr_date( );
580    return o->fd;
581}
582
583void
584tr_fdFileClose( tr_session        * session,
585                const tr_torrent  * tor,
586                tr_file_index_t     fileNum )
587{
588    struct tr_openfile * o;
589    struct tr_fdInfo * gFd;
590    const struct tr_openfile * end;
591    const int torrentId = tr_torrentId( tor );
592
593    assert( tr_isSession( session ) );
594    assert( session->fdInfo != NULL );
595    assert( tr_isTorrent( tor ) );
596    assert( fileNum < tor->info.fileCount );
597
598    gFd = session->fdInfo;
599
600    for( o=gFd->openFiles, end=o+gFd->openFileLimit; o!=end; ++o )
601    {
602        if( torrentId != o->torrentId )
603            continue;
604        if( fileNum != o->fileNum )
605            continue;
606        if( !fileIsOpen( o ) )
607            continue;
608
609        dbgmsg( "tr_fdFileClose closing \"%s\"", o->filename );
610        TrCloseFile( o );
611    }
612}
613
614void
615tr_fdTorrentClose( tr_session * session, int torrentId )
616{
617    assert( tr_isSession( session ) );
618
619    if( session->fdInfo != NULL )
620    {
621        struct tr_openfile * o;
622        const struct tr_openfile * end;
623        struct tr_fdInfo * gFd = session->fdInfo;
624
625        for( o=gFd->openFiles, end=o+gFd->openFileLimit; o!=end; ++o )
626            if( fileIsOpen( o ) && ( o->torrentId == torrentId ) )
627                TrCloseFile( o );
628    }
629}
630
631/***
632****
633****  Sockets
634****
635***/
636
637int
638tr_fdSocketCreate( tr_session * session, int domain, int type )
639{
640    int s = -1;
641    struct tr_fdInfo * gFd;
642
643    assert( tr_isSession( session ) );
644    assert( session->fdInfo != NULL );
645
646    gFd = session->fdInfo;
647
648    if( gFd->socketCount < gFd->socketLimit )
649        if( ( s = socket( domain, type, 0 ) ) < 0 )
650        {
651            if( sockerrno != EAFNOSUPPORT )
652                tr_err( _( "Couldn't create socket: %s" ),
653                        tr_strerror( sockerrno ) );
654        }
655
656    if( s > -1 )
657        ++gFd->socketCount;
658
659    assert( gFd->socketCount >= 0 );
660
661    if( s >= 0 )
662    {
663        static tr_bool buf_logged = FALSE;
664        if( !buf_logged )
665        {
666            int i;
667            socklen_t size = sizeof( int );
668            buf_logged = TRUE;
669            getsockopt( s, SOL_SOCKET, SO_SNDBUF, &i, &size );
670            tr_inf( "SO_SNDBUF size is %d", i );
671            getsockopt( s, SOL_SOCKET, SO_RCVBUF, &i, &size );
672            tr_inf( "SO_RCVBUF size is %d", i );
673        }
674    }
675
676    return s;
677}
678
679int
680tr_fdSocketAccept( tr_session  * session,
681                   int           b,
682                   tr_address  * addr,
683                   tr_port     * port )
684{
685    int s;
686    unsigned int len;
687    struct tr_fdInfo * gFd;
688    struct sockaddr_storage sock;
689
690    assert( tr_isSession( session ) );
691    assert( session->fdInfo != NULL );
692    assert( addr );
693    assert( port );
694
695    gFd = session->fdInfo;
696
697    len = sizeof( struct sockaddr_storage );
698    s = accept( b, (struct sockaddr *) &sock, &len );
699
700    if( ( s >= 0 ) && gFd->socketCount > gFd->socketLimit )
701    {
702        tr_netCloseSocket( s );
703        s = -1;
704    }
705
706    if( s >= 0 )
707    {
708        /* "The ss_family field of the sockaddr_storage structure will always
709         * align with the family field of any protocol-specific structure." */
710        if( sock.ss_family == AF_INET )
711        {
712            struct sockaddr_in *si;
713            union { struct sockaddr_storage dummy; struct sockaddr_in si; } s;
714            s.dummy = sock;
715            si = &s.si;
716            addr->type = TR_AF_INET;
717            addr->addr.addr4.s_addr = si->sin_addr.s_addr;
718            *port = si->sin_port;
719        }
720        else
721        {
722            struct sockaddr_in6 *si;
723            union { struct sockaddr_storage dummy; struct sockaddr_in6 si; } s;
724            s.dummy = sock;
725            si = &s.si;
726            addr->type = TR_AF_INET6;
727            addr->addr.addr6 = si->sin6_addr;
728            *port = si->sin6_port;
729        }
730        ++gFd->socketCount;
731    }
732
733    return s;
734}
735
736void
737tr_fdSocketClose( tr_session * session, int fd )
738{
739    assert( tr_isSession( session ) );
740
741    if( session->fdInfo != NULL )
742    {
743        struct tr_fdInfo * gFd = session->fdInfo;
744
745        if( fd >= 0 )
746        {
747            tr_netCloseSocket( fd );
748            --gFd->socketCount;
749        }
750
751        assert( gFd->socketCount >= 0 );
752    }
753}
754
755/***
756****
757****  Startup / Shutdown
758****
759***/
760
761static void
762ensureSessionFdInfoExists( tr_session * session )
763{
764    assert( tr_isSession( session ) );
765
766    if( session->fdInfo == NULL )
767        session->fdInfo = tr_new0( struct tr_fdInfo, 1 );
768}
769
770void
771tr_fdClose( tr_session * session )
772{
773    struct tr_fdInfo * gFd;
774    struct tr_openfile * o;
775    const struct tr_openfile * end;
776
777    assert( tr_isSession( session ) );
778    assert( session->fdInfo != NULL );
779
780    gFd = session->fdInfo;
781
782    for( o=gFd->openFiles, end=o+gFd->openFileLimit; o!=end; ++o )
783        if( fileIsOpen( o ) )
784            TrCloseFile( o );
785
786    tr_free( gFd->openFiles );
787    tr_free( gFd );
788    session->fdInfo = NULL;
789}
790
791/***
792****
793***/
794
795void
796tr_fdSetFileLimit( tr_session * session, int limit )
797{
798    struct tr_fdInfo * gFd;
799
800    ensureSessionFdInfoExists( session );
801
802    gFd = session->fdInfo;
803
804    if( gFd->openFileLimit != limit )
805    {
806        int i;
807        struct tr_openfile * o;
808        const struct tr_openfile * end;
809
810        /* close any files we've got open  */
811        for( o=gFd->openFiles, end=o+gFd->openFileLimit; o!=end; ++o )
812            if( fileIsOpen( o ) )
813                TrCloseFile( o );
814
815        /* rebuild the openFiles array */
816        tr_free( gFd->openFiles );
817        gFd->openFiles = tr_new0( struct tr_openfile, limit );
818        gFd->openFileLimit = limit;
819        for( i=0; i<gFd->openFileLimit; ++i )
820            gFd->openFiles[i].fd = -1;
821    }
822}
823
824int
825tr_fdGetFileLimit( const tr_session * session )
826{
827    return session && session->fdInfo ? session->fdInfo->openFileLimit : -1;
828}
829
830void
831tr_fdSetPeerLimit( tr_session * session, int socketLimit )
832{
833    struct tr_fdInfo * gFd;
834
835    ensureSessionFdInfoExists( session );
836
837    gFd = session->fdInfo;
838
839#ifdef HAVE_GETRLIMIT
840    {
841        struct rlimit rlim;
842        const int NOFILE_BUFFER = 512;
843        const int open_max = sysconf( _SC_OPEN_MAX );
844        getrlimit( RLIMIT_NOFILE, &rlim );
845        rlim.rlim_cur = MAX( 1024, open_max );
846        rlim.rlim_cur = MIN( rlim.rlim_cur, rlim.rlim_max );
847        setrlimit( RLIMIT_NOFILE, &rlim );
848        tr_dbg( "setrlimit( RLIMIT_NOFILE, %d )", (int)rlim.rlim_cur );
849        gFd->socketLimit = MIN( socketLimit, (int)rlim.rlim_cur - NOFILE_BUFFER );
850    }
851#else
852    gFd->socketLimit = socketLimit;
853#endif
854    gFd->publicSocketLimit = socketLimit;
855
856    tr_dbg( "socket limit is %d", (int)gFd->socketLimit );
857}
858
859int
860tr_fdGetPeerLimit( const tr_session * session )
861{
862    return session && session->fdInfo ? session->fdInfo->publicSocketLimit : -1;
863}
Note: See TracBrowser for help on using the repository browser.