source: branches/1.0x/libtransmission/fdlimit.c @ 4775

Last change on this file since 4775 was 4775, checked in by charles, 15 years ago

(1.0x) backport more portability fixes from trunk...

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