source: trunk/libtransmission/fdlimit.c @ 9931

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

(trunk) housekeeping: (1) add standard svn properties for $Id$ substitution in some files (2) add #include guards in a couple of libtransmission headers (3) refresh build instructions in README

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