source: trunk/libtransmission/fdlimit.c @ 6906

Last change on this file since 6906 was 6906, checked in by charles, 13 years ago

(libT) dead code removal

  • Property svn:keywords set to Date Rev Author Id
File size: 11.5 KB
Line 
1/******************************************************************************
2 * $Id: fdlimit.c 6906 2008-10-14 20:44:41Z charles $
3 *
4 * Copyright (c) 2005-2008 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#include <assert.h>
30#include <errno.h>
31#include <inttypes.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35
36#include <sys/types.h>
37#include <sys/stat.h>
38#ifdef HAVE_GETRLIMIT
39 #include <sys/time.h> /* getrlimit */
40 #include <sys/resource.h> /* getrlimit */
41#endif
42#include <unistd.h>
43#include <fcntl.h> /* O_LARGEFILE */
44
45#include <event.h>
46#include <evutil.h>
47
48#include "transmission.h"
49#include "fdlimit.h"
50#include "list.h"
51#include "net.h"
52#include "platform.h" /* tr_lock */
53#include "utils.h"
54
55#define dbgmsg( ... ) tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ )
56
57/**
58***
59**/
60
61enum
62{
63    TR_MAX_OPEN_FILES = 16, /* real files, not sockets */
64
65    NOFILE_BUFFER = 512, /* the process' number of open files is
66                            globalMaxPeers + NOFILE_BUFFER */
67};
68
69struct tr_openfile
70{
71    unsigned int    isCheckedOut  : 1;
72    unsigned int    isWritable    : 1;
73    unsigned int    closeWhenDone : 1;
74    char            filename[MAX_PATH_LENGTH];
75    int             fd;
76    uint64_t        date;
77};
78
79struct tr_fd_s
80{
81    int                   socketCount;
82    int                   socketMax;
83    tr_lock *             lock;
84    struct tr_openfile    open[TR_MAX_OPEN_FILES];
85};
86
87static struct tr_fd_s * gFd = NULL;
88
89/***
90****
91****  Local Files
92****
93***/
94
95/**
96 * returns 0 on success, or an errno value on failure.
97 * errno values include ENOENT if the parent folder doesn't exist,
98 * plus the errno values set by tr_mkdirp() and open().
99 */
100static int
101TrOpenFile( int          i,
102            const char * folder,
103            const char * torrentFile,
104            int          write )
105{
106    struct tr_openfile * file = &gFd->open[i];
107    int                  flags;
108    char               * filename;
109    struct stat          sb;
110
111    /* confirm the parent folder exists */
112    if( stat( folder, &sb ) || !S_ISDIR( sb.st_mode ) )
113        return ENOENT;
114
115    /* create subfolders, if any */
116    filename = tr_buildPath( folder, torrentFile, NULL );
117    if( write )
118    {
119        char * tmp = tr_dirname( filename );
120        const int err = tr_mkdirp( tmp, 0777 ) ? errno : 0;
121        tr_free( tmp );
122        if( err ) {
123            tr_free( filename );
124            return err;
125        }
126    }
127
128    /* open the file */
129    flags = write ? ( O_RDWR | O_CREAT ) : O_RDONLY;
130#ifdef O_LARGEFILE
131    flags |= O_LARGEFILE;
132#endif
133#ifdef WIN32
134    flags |= O_BINARY;
135#endif
136    file->fd = open( filename, flags, 0666 );
137    if( file->fd == -1 )
138    {
139        const int err = errno;
140        tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename,
141               tr_strerror( err ) );
142        tr_free( filename );
143        return err;
144    }
145
146    tr_free( filename );
147    return 0;
148}
149
150static int
151fileIsOpen( const struct tr_openfile * o )
152{
153    return o->fd >= 0;
154}
155
156static void
157TrCloseFile( int i )
158{
159    struct tr_openfile * o = &gFd->open[i];
160
161    assert( i >= 0 );
162    assert( i < TR_MAX_OPEN_FILES );
163    assert( fileIsOpen( o ) );
164
165    close( o->fd );
166    o->fd = -1;
167    o->isCheckedOut = 0;
168}
169
170static int
171fileIsCheckedOut( const struct tr_openfile * o )
172{
173    return fileIsOpen( o ) && o->isCheckedOut;
174}
175
176/* returns an fd on success, or a -1 on failure and sets errno */
177int
178tr_fdFileCheckout( const char * folder,
179                   const char * torrentFile,
180                   int          write )
181{
182    int                  i, winner = -1;
183    struct tr_openfile * o;
184    char               * filename;
185
186    assert( folder && *folder );
187    assert( torrentFile && *torrentFile );
188    assert( write == 0 || write == 1 );
189
190    filename = tr_buildPath( folder, torrentFile, NULL );
191    dbgmsg( "looking for file '%s', writable %c", filename,
192            write ? 'y' : 'n' );
193
194    tr_lockLock( gFd->lock );
195
196    /* Is it already open? */
197    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
198    {
199        o = &gFd->open[i];
200
201        if( !fileIsOpen( o ) )
202            continue;
203
204        if( strcmp( filename, o->filename ) )
205            continue;
206
207        if( fileIsCheckedOut( o ) )
208        {
209            dbgmsg( "found it!  it's open, but checked out.  waiting..." );
210            tr_lockUnlock( gFd->lock );
211            tr_wait( 200 );
212            tr_lockLock( gFd->lock );
213            i = -1; /* reloop */
214            continue;
215        }
216
217        if( write && !o->isWritable )
218        {
219            dbgmsg(
220                "found it!  it's open and available, but isn't writable. closing..." );
221            TrCloseFile( i );
222            break;
223        }
224
225        dbgmsg( "found it!  it's ready for use!" );
226        winner = i;
227        break;
228    }
229
230    dbgmsg(
231        "it's not already open.  looking for an open slot or an old file." );
232    while( winner < 0 )
233    {
234        uint64_t date = tr_date( ) + 1;
235
236        /* look for the file that's been open longest */
237        for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
238        {
239            o = &gFd->open[i];
240
241            if( !fileIsOpen( o ) )
242            {
243                winner = i;
244                dbgmsg( "found an empty slot in %d", winner );
245                break;
246            }
247
248            if( date > o->date )
249            {
250                date = o->date;
251                winner = i;
252            }
253        }
254
255        if( winner >= 0 )
256        {
257            if( fileIsOpen( &gFd->open[winner] ) )
258            {
259                dbgmsg( "closing file '%s', slot #%d",
260                        gFd->open[winner].filename,
261                        winner );
262                TrCloseFile( winner );
263            }
264        }
265        else
266        {
267            dbgmsg(
268                "everything's full!  waiting for someone else to finish something" );
269            tr_lockUnlock( gFd->lock );
270            tr_wait( 200 );
271            tr_lockLock( gFd->lock );
272        }
273    }
274
275    assert( winner >= 0 );
276    o = &gFd->open[winner];
277    if( !fileIsOpen( o ) )
278    {
279        const int err = TrOpenFile( winner, folder, torrentFile, write );
280        if( err ) {
281            tr_lockUnlock( gFd->lock );
282            tr_free( filename );
283            errno = err;
284            return -1;
285        }
286
287        dbgmsg( "opened '%s' in slot %d, write %c", filename, winner,
288                write ? 'y' : 'n' );
289        tr_strlcpy( o->filename, filename, sizeof( o->filename ) );
290        o->isWritable = write;
291    }
292
293    dbgmsg( "checking out '%s' in slot %d", filename, winner );
294    o->isCheckedOut = 1;
295    o->closeWhenDone = 0;
296    o->date = tr_date( );
297    tr_free( filename );
298    tr_lockUnlock( gFd->lock );
299    return o->fd;
300}
301
302void
303tr_fdFileReturn( int fd )
304{
305    int i;
306
307    tr_lockLock( gFd->lock );
308
309    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
310    {
311        struct tr_openfile * o = &gFd->open[i];
312        if( o->fd != fd )
313            continue;
314
315        dbgmsg( "releasing file '%s' in slot #%d", o->filename, i );
316        o->isCheckedOut = 0;
317        if( o->closeWhenDone )
318            TrCloseFile( i );
319
320        break;
321    }
322
323    tr_lockUnlock( gFd->lock );
324}
325
326void
327tr_fdFileClose( const char * filename )
328{
329    int i;
330
331    tr_lockLock( gFd->lock );
332
333    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
334    {
335        struct tr_openfile * o = &gFd->open[i];
336        if( !fileIsOpen( o ) || strcmp( filename, o->filename ) )
337            continue;
338
339        dbgmsg( "tr_fdFileClose closing '%s'", filename );
340
341        if( !o->isCheckedOut )
342        {
343            dbgmsg( "not checked out, so closing it now... '%s'", filename );
344            TrCloseFile( i );
345        }
346        else
347        {
348            dbgmsg(
349                "flagging file '%s', slot #%d to be closed when checked in",
350                gFd->open[i].filename, i );
351            o->closeWhenDone = 1;
352        }
353    }
354
355    tr_lockUnlock( gFd->lock );
356}
357
358/***
359****
360****  Sockets
361****
362***/
363
364static int
365getSocketMax( struct tr_fd_s * gFd )
366{
367    return gFd->socketMax;
368}
369
370int
371tr_fdSocketCreate( int type )
372{
373    int s = -1;
374
375    tr_lockLock( gFd->lock );
376
377    if( gFd->socketCount < getSocketMax( gFd ) )
378        if( ( s = socket( AF_INET, type, 0 ) ) < 0 )
379            tr_err( _( "Couldn't create socket: %s" ),
380                   tr_strerror( sockerrno ) );
381
382    if( s > -1 )
383        ++gFd->socketCount;
384
385    assert( gFd->socketCount >= 0 );
386
387    tr_lockUnlock( gFd->lock );
388    return s;
389}
390
391int
392tr_fdSocketAccept( int              b,
393                   struct in_addr * addr,
394                   tr_port_t *      port )
395{
396    int                s = -1;
397    unsigned int       len;
398    struct sockaddr_in sock;
399
400    assert( addr );
401    assert( port );
402
403    tr_lockLock( gFd->lock );
404    if( gFd->socketCount < getSocketMax( gFd ) )
405    {
406        len = sizeof( sock );
407        s = accept( b, (struct sockaddr *) &sock, &len );
408    }
409    if( s > -1 )
410    {
411        *addr = sock.sin_addr;
412        *port = sock.sin_port;
413        ++gFd->socketCount;
414    }
415    tr_lockUnlock( gFd->lock );
416
417    return s;
418}
419
420static void
421socketClose( int fd )
422{
423#ifdef BEOS_NETSERVER
424    closesocket( fd );
425#else
426    EVUTIL_CLOSESOCKET( fd );
427#endif
428}
429
430void
431tr_fdSocketClose( int s )
432{
433    tr_lockLock( gFd->lock );
434
435    if( s >= 0 )
436    {
437        socketClose( s );
438        --gFd->socketCount;
439    }
440
441    assert( gFd->socketCount >= 0 );
442
443    tr_lockUnlock( gFd->lock );
444}
445
446/***
447****
448****  Startup / Shutdown
449****
450***/
451
452void
453tr_fdInit( int globalPeerLimit )
454{
455    int i;
456
457    assert( gFd == NULL );
458    gFd = tr_new0( struct tr_fd_s, 1 );
459    gFd->lock = tr_lockNew( );
460
461#ifdef HAVE_GETRLIMIT
462    {
463        struct rlimit rlim;
464        getrlimit( RLIMIT_NOFILE, &rlim );
465        rlim.rlim_cur = MIN( rlim.rlim_max,
466                            (rlim_t)( globalPeerLimit + NOFILE_BUFFER ) );
467        setrlimit( RLIMIT_NOFILE, &rlim );
468        gFd->socketMax = rlim.rlim_cur - NOFILE_BUFFER;
469        tr_dbg( "setrlimit( RLIMIT_NOFILE, %d )", (int)rlim.rlim_cur );
470    }
471#else
472    gFd->socketMax = globalPeerLimit;
473#endif
474    tr_dbg( "%d usable file descriptors", globalPeerLimit );
475
476    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
477        gFd->open[i].fd = -1;
478}
479
480void
481tr_fdClose( void )
482{
483    int i = 0;
484
485    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
486        if( fileIsOpen( &gFd->open[i] ) )
487            TrCloseFile( i );
488
489    tr_lockFree( gFd->lock );
490
491    tr_free( gFd );
492}
493
494void
495tr_fdSetPeerLimit( uint16_t n )
496{
497    assert( gFd != NULL && "tr_fdInit() must be called first!" );
498    gFd->socketMax = n;
499}
500
501uint16_t
502tr_fdGetPeerLimit( void )
503{
504    return gFd ? gFd->socketMax : -1;
505}
506
Note: See TracBrowser for help on using the repository browser.