source: trunk/libtransmission/fdlimit.c @ 10651

Last change on this file since 10651 was 10651, checked in by charles, 12 years ago

(trunk libT) fdlimit.[ch] MIT -> GPL. There's not any MIT code left there.

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