source: trunk/libtransmission/fdlimit.c @ 10336

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

(trunk libT) #2849 "when possible, use fallocate64() for file preallocation" -- do this even when sparse preallocation mode is selected.

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