source: trunk/libtransmission/fdlimit.c @ 11544

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

(trunk libT) #3531 "Pausing 500 torrents uses 650 MB of memory" -- on OS X, when saving a benc/json file, send a hint to the OS to not cache the file.

  • Property svn:keywords set to Date Rev Author Id
File size: 21.7 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 11544 2010-12-17 15:36:31Z 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/* https://trac.transmissionbt.com/ticket/3826 */
203#ifdef __UCLIBC__
204#define TR_UCLIBC_CHECK_VERSION(major,minor,micro) \
205    (__UCLIBC_MAJOR__ > (major) || \
206     (__UCLIBC_MAJOR__ == (major) && __UCLIBC_MINOR__ > (minor)) || \
207     (__UCLIBC_MAJOR__ == (major) && __UCLIBC_MINOR__ == (minor) && \
208      __UCLIBC_SUBLEVEL__ >= (micro)))
209#if !TR_UCLIBC_CHECK_VERSION(0,9,28)
210 #undef HAVE_PREAD
211 #undef HAVE_PWRITE
212#endif
213#endif
214
215#ifdef SYS_DARWIN
216 #define HAVE_PREAD
217 #define HAVE_PWRITE
218#endif
219
220ssize_t
221tr_pread( int fd, void *buf, size_t count, off_t offset )
222{
223#ifdef HAVE_PREAD
224    return pread( 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 read( fd, buf, count );
230#endif
231}
232
233ssize_t
234tr_pwrite( int fd, const void *buf, size_t count, off_t offset )
235{
236#ifdef HAVE_PWRITE
237    return pwrite( fd, buf, count, offset );
238#else
239    const off_t lrc = lseek( fd, offset, SEEK_SET );
240    if( lrc < 0 )
241        return -1;
242    return write( fd, buf, count );
243#endif
244}
245
246int
247tr_prefetch( int fd UNUSED, off_t offset UNUSED, size_t count UNUSED )
248{
249#ifdef HAVE_POSIX_FADVISE
250    return posix_fadvise( fd, offset, count, POSIX_FADV_WILLNEED );
251#elif defined(SYS_DARWIN)
252    struct radvisory radv;
253    radv.ra_offset = offset;
254    radv.ra_count = count;
255    return fcntl( fd, F_RDADVISE, &radv );
256#else
257    return 0;
258#endif
259}
260
261void
262tr_set_file_for_single_pass( int fd )
263{
264    if( fd >= 0 )
265    {
266        /* Set hints about the lookahead buffer and caching. It's okay
267           for these to fail silently, so don't let them affect errno */
268        const int err = errno;
269#ifdef HAVE_POSIX_FADVISE
270        posix_fadvise( fd, 0, 0, POSIX_FADV_SEQUENTIAL );
271#endif
272#ifdef SYS_DARWIN
273        fcntl( fd, F_RDAHEAD, 1 );
274        fcntl( fd, F_NOCACHE, 1 );
275#endif
276        errno = err;
277    }
278}
279
280int
281tr_open_file_for_writing( const char * filename )
282{
283    int fd;
284    int flags = O_WRONLY | O_CREAT;
285#ifdef O_BINARY
286    flags |= O_BINARY;
287#endif
288#ifdef O_LARGEFILE
289    flags |= O_LARGEFILE;
290#endif
291    fd = open( filename, flags, 0666 );
292    tr_set_file_for_single_pass( fd );
293    return fd;
294}
295
296int
297tr_open_file_for_scanning( const char * filename )
298{
299    int fd;
300    int flags;
301
302    /* build the flags */
303    flags = O_RDONLY;
304#ifdef O_SEQUENTIAL
305    flags |= O_SEQUENTIAL;
306#endif
307#ifdef O_BINARY
308    flags |= O_BINARY;
309#endif
310#ifdef O_LARGEFILE
311    flags |= O_LARGEFILE;
312#endif
313
314    /* open the file */
315    fd = open( filename, flags, 0666 );
316    tr_set_file_for_single_pass( fd );
317    return fd;
318}
319
320void
321tr_close_file( int fd )
322{
323#if defined(HAVE_POSIX_FADVISE)
324    /* Set hint about not caching this file.
325       It's okay for this to fail silently, so don't let it affect errno */
326    const int err = errno;
327    posix_fadvise( fd, 0, 0, POSIX_FADV_DONTNEED );
328    errno = err;
329#endif
330#ifdef SYS_DARWIN
331    /* it's unclear to me from the man pages if this actually flushes out the cache,
332     * but it couldn't hurt... */
333    fcntl( fd, F_NOCACHE, 1 );
334#endif
335    close( fd );
336}
337
338/**
339 * returns 0 on success, or an errno value on failure.
340 * errno values include ENOENT if the parent folder doesn't exist,
341 * plus the errno values set by tr_mkdirp() and open().
342 */
343static int
344TrOpenFile( tr_session             * session,
345            int                      i,
346            const char             * filename,
347            tr_bool                  doWrite,
348            tr_preallocation_mode    preallocationMode,
349            uint64_t                 desiredFileSize )
350{
351    int flags;
352    struct stat sb;
353    tr_bool alreadyExisted;
354    struct tr_openfile * file;
355
356    assert( tr_isSession( session ) );
357    assert( session->fdInfo != NULL );
358
359    file = &session->fdInfo->openFiles[i];
360
361    /* create subfolders, if any */
362    if( doWrite )
363    {
364        char * dir = tr_dirname( filename );
365        const int err = tr_mkdirp( dir, 0777 ) ? errno : 0;
366        if( err ) {
367            tr_err( _( "Couldn't create \"%1$s\": %2$s" ), dir, tr_strerror( err ) );
368            tr_free( dir );
369            return err;
370        }
371        tr_free( dir );
372    }
373
374    alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode );
375
376    if( doWrite && !alreadyExisted && ( preallocationMode == TR_PREALLOCATE_FULL ) )
377        if( preallocateFileFull( filename, desiredFileSize ) )
378            tr_dbg( _( "Preallocated file \"%s\"" ), filename );
379
380    /* open the file */
381    flags = doWrite ? ( O_RDWR | O_CREAT ) : O_RDONLY;
382#ifdef O_SEQUENTIAL
383    flags |= O_SEQUENTIAL;
384#endif
385#ifdef O_LARGEFILE
386    flags |= O_LARGEFILE;
387#endif
388#ifdef WIN32
389    flags |= O_BINARY;
390#endif
391    file->fd = open( filename, flags, 0666 );
392    if( file->fd == -1 )
393    {
394        const int err = errno;
395        tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename, tr_strerror( err ) );
396        return err;
397    }
398
399    /* If the file already exists and it's too large, truncate it.
400     * This is a fringe case that happens if a torrent's been updated
401     * and one of the updated torrent's files is smaller.
402     * http://trac.transmissionbt.com/ticket/2228
403     * https://bugs.launchpad.net/ubuntu/+source/transmission/+bug/318249
404     */
405    if( alreadyExisted && ( desiredFileSize < (uint64_t)sb.st_size ) )
406        ftruncate( file->fd, desiredFileSize );
407
408    if( doWrite && !alreadyExisted && ( preallocationMode == TR_PREALLOCATE_SPARSE ) )
409        preallocateFileSparse( file->fd, desiredFileSize );
410
411#ifdef HAVE_POSIX_FADVISE
412    /* this doubles the OS level readahead buffer, which in practice
413     * turns out to be a good thing, because many (most?) clients request
414     * chunks of blocks in order.
415     * It's okay for this to fail silently, so don't let it affect errno */
416    {
417        const int err = errno;
418        posix_fadvise( file->fd, 0, 0, POSIX_FADV_SEQUENTIAL );
419        errno = err;
420    }
421#endif
422
423#if defined( SYS_DARWIN )
424    /**
425     * 1. Enable readahead for reasons described above w/POSIX_FADV_SEQUENTIAL.
426     *
427     * 2. Disable OS-level caching due to user reports of adverse effects of
428     *    excessive inactive memory.  However this is experimental because
429     *    previous attempts at this have *also* had adverse effects (see r8198)
430     *
431     * It's okay for this to fail silently, so don't let it affect errno
432     */
433    {
434        const int err = errno;
435        fcntl( file->fd, F_NOCACHE, 1 );
436        fcntl( file->fd, F_RDAHEAD, 1 );
437        errno = err;
438    }
439#endif
440
441    return 0;
442}
443
444static inline tr_bool
445fileIsOpen( const struct tr_openfile * o )
446{
447    return o->fd >= 0;
448}
449
450static void
451TrCloseFile( struct tr_openfile * o )
452{
453    assert( o != NULL );
454    assert( fileIsOpen( o ) );
455
456    tr_close_file( o->fd );
457    o->fd = -1;
458}
459
460int
461tr_fdFileGetCached( tr_session       * session,
462                    int                torrentId,
463                    tr_file_index_t    fileNum,
464                    tr_bool            doWrite )
465{
466    struct tr_openfile * match = NULL;
467    struct tr_fdInfo * gFd;
468
469    assert( tr_isSession( session ) );
470    assert( session->fdInfo != NULL );
471    assert( torrentId > 0 );
472    assert( tr_isBool( doWrite ) );
473
474    gFd = session->fdInfo;
475
476    /* is it already open? */
477    {
478        int i;
479        struct tr_openfile * o;
480        for( i=0; i<gFd->openFileLimit; ++i )
481        {
482            o = &gFd->openFiles[i];
483
484            if( torrentId != o->torrentId )
485                continue;
486            if( fileNum != o->fileNum )
487                continue;
488            if( !fileIsOpen( o ) )
489                continue;
490
491            match = o;
492            break;
493        }
494    }
495
496    if( ( match != NULL ) && ( !doWrite || match->isWritable ) )
497    {
498        match->date = tr_time( );
499        return match->fd;
500    }
501
502    return -1;
503}
504
505/* returns an fd on success, or a -1 on failure and sets errno */
506int
507tr_fdFileCheckout( tr_session             * session,
508                   int                      torrentId,
509                   tr_file_index_t          fileNum,
510                   const char             * filename,
511                   tr_bool                  doWrite,
512                   tr_preallocation_mode    preallocationMode,
513                   uint64_t                 desiredFileSize )
514{
515    int i, winner = -1;
516    struct tr_fdInfo * gFd;
517    struct tr_openfile * o;
518
519    assert( tr_isSession( session ) );
520    assert( session->fdInfo != NULL );
521    assert( torrentId > 0 );
522    assert( filename && *filename );
523    assert( tr_isBool( doWrite ) );
524
525    gFd = session->fdInfo;
526
527    dbgmsg( "looking for file '%s', writable %c", filename, doWrite ? 'y' : 'n' );
528
529    /* is it already open? */
530    for( i=0; i<gFd->openFileLimit; ++i )
531    {
532        o = &gFd->openFiles[i];
533
534        if( torrentId != o->torrentId )
535            continue;
536        if( fileNum != o->fileNum )
537            continue;
538        if( !fileIsOpen( o ) )
539            continue;
540
541        if( doWrite && !o->isWritable )
542        {
543            dbgmsg( "found it!  it's open and available, but isn't writable. closing..." );
544            TrCloseFile( o );
545            break;
546        }
547
548        dbgmsg( "found it!  it's ready for use!" );
549        winner = i;
550        break;
551    }
552
553    dbgmsg( "it's not already open.  looking for an open slot or an old file." );
554    while( winner < 0 )
555    {
556        time_t date = tr_time( ) + 1;
557
558        /* look for the file that's been open longest */
559        for( i=0; i<gFd->openFileLimit; ++i )
560        {
561            o = &gFd->openFiles[i];
562
563            if( !fileIsOpen( o ) )
564            {
565                winner = i;
566                dbgmsg( "found an empty slot in %d", winner );
567                break;
568            }
569
570            if( date > o->date )
571            {
572                date = o->date;
573                winner = i;
574            }
575        }
576
577        assert( winner >= 0 );
578
579        if( fileIsOpen( &gFd->openFiles[winner] ) )
580        {
581            dbgmsg( "closing file \"%s\"", gFd->openFiles[winner].filename );
582            TrCloseFile( &gFd->openFiles[winner] );
583        }
584    }
585
586    assert( winner >= 0 );
587    o = &gFd->openFiles[winner];
588    if( !fileIsOpen( o ) )
589    {
590        const int err = TrOpenFile( session, winner, filename, doWrite,
591                                    preallocationMode, desiredFileSize );
592        if( err ) {
593            errno = err;
594            return -1;
595        }
596
597        dbgmsg( "opened '%s' in slot %d, doWrite %c", filename, winner,
598                doWrite ? 'y' : 'n' );
599        tr_strlcpy( o->filename, filename, sizeof( o->filename ) );
600        o->isWritable = doWrite;
601    }
602
603    dbgmsg( "checking out '%s' in slot %d", filename, winner );
604    o->torrentId = torrentId;
605    o->fileNum = fileNum;
606    o->date = tr_time( );
607    return o->fd;
608}
609
610void
611tr_fdFileClose( tr_session        * session,
612                const tr_torrent  * tor,
613                tr_file_index_t     fileNum )
614{
615    struct tr_openfile * o;
616    struct tr_fdInfo * gFd;
617    const struct tr_openfile * end;
618    const int torrentId = tr_torrentId( tor );
619
620    assert( tr_isSession( session ) );
621    assert( session->fdInfo != NULL );
622    assert( tr_isTorrent( tor ) );
623    assert( fileNum < tor->info.fileCount );
624
625    gFd = session->fdInfo;
626
627    for( o=gFd->openFiles, end=o+gFd->openFileLimit; o!=end; ++o )
628    {
629        if( torrentId != o->torrentId )
630            continue;
631        if( fileNum != o->fileNum )
632            continue;
633        if( !fileIsOpen( o ) )
634            continue;
635
636        dbgmsg( "tr_fdFileClose closing \"%s\"", o->filename );
637        TrCloseFile( o );
638    }
639}
640
641void
642tr_fdTorrentClose( tr_session * session, int torrentId )
643{
644    assert( tr_isSession( session ) );
645
646    if( session->fdInfo != NULL )
647    {
648        struct tr_openfile * o;
649        const struct tr_openfile * end;
650        struct tr_fdInfo * gFd = session->fdInfo;
651
652        for( o=gFd->openFiles, end=o+gFd->openFileLimit; o!=end; ++o )
653            if( fileIsOpen( o ) && ( o->torrentId == torrentId ) )
654                TrCloseFile( o );
655    }
656}
657
658/***
659****
660****  Sockets
661****
662***/
663
664int
665tr_fdSocketCreate( tr_session * session, int domain, int type )
666{
667    int s = -1;
668    struct tr_fdInfo * gFd;
669
670    assert( tr_isSession( session ) );
671    assert( session->fdInfo != NULL );
672
673    gFd = session->fdInfo;
674
675    if( gFd->socketCount < gFd->socketLimit )
676        if( ( s = socket( domain, type, 0 ) ) < 0 )
677        {
678            if( sockerrno != EAFNOSUPPORT )
679                tr_err( _( "Couldn't create socket: %s" ),
680                        tr_strerror( sockerrno ) );
681        }
682
683    if( s > -1 )
684        ++gFd->socketCount;
685
686    assert( gFd->socketCount >= 0 );
687
688    if( s >= 0 )
689    {
690        static tr_bool buf_logged = FALSE;
691        if( !buf_logged )
692        {
693            int i;
694            socklen_t size = sizeof( int );
695            buf_logged = TRUE;
696            getsockopt( s, SOL_SOCKET, SO_SNDBUF, &i, &size );
697            tr_dbg( "SO_SNDBUF size is %d", i );
698            getsockopt( s, SOL_SOCKET, SO_RCVBUF, &i, &size );
699            tr_dbg( "SO_RCVBUF size is %d", i );
700        }
701    }
702
703    return s;
704}
705
706int
707tr_fdSocketAccept( tr_session  * session,
708                   int           b,
709                   tr_address  * addr,
710                   tr_port     * port )
711{
712    int s;
713    unsigned int len;
714    struct tr_fdInfo * gFd;
715    struct sockaddr_storage sock;
716
717    assert( tr_isSession( session ) );
718    assert( session->fdInfo != NULL );
719    assert( addr );
720    assert( port );
721
722    gFd = session->fdInfo;
723
724    len = sizeof( struct sockaddr_storage );
725    s = accept( b, (struct sockaddr *) &sock, &len );
726
727    if( ( s >= 0 ) && gFd->socketCount > gFd->socketLimit )
728    {
729        tr_netCloseSocket( s );
730        s = -1;
731    }
732
733    if( s >= 0 )
734    {
735        /* "The ss_family field of the sockaddr_storage structure will always
736         * align with the family field of any protocol-specific structure." */
737        if( sock.ss_family == AF_INET )
738        {
739            struct sockaddr_in *si;
740            union { struct sockaddr_storage dummy; struct sockaddr_in si; } s;
741            s.dummy = sock;
742            si = &s.si;
743            addr->type = TR_AF_INET;
744            addr->addr.addr4.s_addr = si->sin_addr.s_addr;
745            *port = si->sin_port;
746        }
747        else
748        {
749            struct sockaddr_in6 *si;
750            union { struct sockaddr_storage dummy; struct sockaddr_in6 si; } s;
751            s.dummy = sock;
752            si = &s.si;
753            addr->type = TR_AF_INET6;
754            addr->addr.addr6 = si->sin6_addr;
755            *port = si->sin6_port;
756        }
757        ++gFd->socketCount;
758    }
759
760    return s;
761}
762
763void
764tr_fdSocketClose( tr_session * session, int fd )
765{
766    assert( tr_isSession( session ) );
767
768    if( session->fdInfo != NULL )
769    {
770        struct tr_fdInfo * gFd = session->fdInfo;
771
772        if( fd >= 0 )
773        {
774            tr_netCloseSocket( fd );
775            --gFd->socketCount;
776        }
777
778        assert( gFd->socketCount >= 0 );
779    }
780}
781
782/***
783****
784****  Startup / Shutdown
785****
786***/
787
788static void
789ensureSessionFdInfoExists( tr_session * session )
790{
791    assert( tr_isSession( session ) );
792
793    if( session->fdInfo == NULL )
794        session->fdInfo = tr_new0( struct tr_fdInfo, 1 );
795}
796
797void
798tr_fdClose( tr_session * session )
799{
800    struct tr_fdInfo * gFd;
801    struct tr_openfile * o;
802    const struct tr_openfile * end;
803
804    assert( tr_isSession( session ) );
805    assert( session->fdInfo != NULL );
806
807    gFd = session->fdInfo;
808
809    for( o=gFd->openFiles, end=o+gFd->openFileLimit; o!=end; ++o )
810        if( fileIsOpen( o ) )
811            TrCloseFile( o );
812
813    tr_free( gFd->openFiles );
814    tr_free( gFd );
815    session->fdInfo = NULL;
816}
817
818/***
819****
820***/
821
822void
823tr_fdSetFileLimit( tr_session * session, int limit )
824{
825    struct tr_fdInfo * gFd;
826
827    ensureSessionFdInfoExists( session );
828
829    gFd = session->fdInfo;
830
831    if( gFd->openFileLimit != limit )
832    {
833        int i;
834        struct tr_openfile * o;
835        const struct tr_openfile * end;
836
837        /* close any files we've got open  */
838        for( o=gFd->openFiles, end=o+gFd->openFileLimit; o!=end; ++o )
839            if( fileIsOpen( o ) )
840                TrCloseFile( o );
841
842        /* rebuild the openFiles array */
843        tr_free( gFd->openFiles );
844        gFd->openFiles = tr_new0( struct tr_openfile, limit );
845        gFd->openFileLimit = limit;
846        for( i=0; i<gFd->openFileLimit; ++i )
847            gFd->openFiles[i].fd = -1;
848    }
849}
850
851int
852tr_fdGetFileLimit( const tr_session * session )
853{
854    return session && session->fdInfo ? session->fdInfo->openFileLimit : -1;
855}
856
857void
858tr_fdSetPeerLimit( tr_session * session, int socketLimit )
859{
860    struct tr_fdInfo * gFd;
861
862    ensureSessionFdInfoExists( session );
863
864    gFd = session->fdInfo;
865
866#ifdef HAVE_GETRLIMIT
867    {
868        struct rlimit rlim;
869        const int NOFILE_BUFFER = 512;
870        const int open_max = sysconf( _SC_OPEN_MAX );
871        getrlimit( RLIMIT_NOFILE, &rlim );
872        rlim.rlim_cur = MAX( 1024, open_max );
873        rlim.rlim_cur = MIN( rlim.rlim_cur, rlim.rlim_max );
874        setrlimit( RLIMIT_NOFILE, &rlim );
875        tr_dbg( "setrlimit( RLIMIT_NOFILE, %d )", (int)rlim.rlim_cur );
876        gFd->socketLimit = MIN( socketLimit, (int)rlim.rlim_cur - NOFILE_BUFFER );
877    }
878#else
879    gFd->socketLimit = socketLimit;
880#endif
881    gFd->publicSocketLimit = socketLimit;
882
883    tr_dbg( "socket limit is %d", (int)gFd->socketLimit );
884}
885
886int
887tr_fdGetPeerLimit( const tr_session * session )
888{
889    return session && session->fdInfo ? session->fdInfo->publicSocketLimit : -1;
890}
Note: See TracBrowser for help on using the repository browser.