source: trunk/libtransmission/fdlimit.c @ 11530

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

(trunk libT) #3826 "don't use pread() or pwrite() on uclibc" -- fixed.

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