source: trunk/libtransmission/fdlimit.c @ 11398

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

(trunk libT) add some new bugs to the code so that it will crash when vraa tries to use it

  • 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 11398 2010-11-11 15:31:11Z 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    time_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 UNUSED, off_t offset UNUSED, size_t count UNUSED )
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( );
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        time_t date = tr_time( ) + 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( );
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.