source: trunk/libtransmission/fdlimit.c @ 10998

Last change on this file since 10998 was 10998, checked in by charles, 11 years ago

(trunk libt) rename tr_date() as tr_time_msec() for clarity

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