source: trunk/libtransmission/fdlimit.c @ 9505

Last change on this file since 9505 was 9505, checked in by livings124, 12 years ago

remove unused variable

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