~ubuntu-branches/ubuntu/jaunty/transmission/jaunty-updates

« back to all changes in this revision

Viewing changes to libtransmission/fdlimit.c

  • Committer: Bazaar Package Importer
  • Author(s): Chris Coulson
  • Date: 2008-11-28 15:33:48 UTC
  • mfrom: (1.1.19 upstream)
  • Revision ID: james.westby@ubuntu.com-20081128153348-it70trfnxiroblmc
Tags: 1.40-0ubuntu1
* New upstream release (LP: #302672)
  - Tracker communication uses fewer resources
  - More accurate bandwidth limits
  - Reduce disk fragmentation by preallocating files (LP: #287726)
  - Stability, security and performance improvements to the RPC /
    Web UI server (closes LP: #290423)
  - Support compression when serving Web UI and RPC responses
  - Simplify the RPC whitelist
  - Fix bug that prevented handshakes with encrypted BitComet peers
  - Fix 1.3x bug that could re-download some data unnecessarily
    (LP: #295040)
  - Option to automatically update the blocklist weekly
  - Added off-hour bandwidth scheduling
  - Simplify file/priority selection in the details dialog
  - Fix a couple of crashes
  - New / updated translations
  - Don't inhibit hibernation by default (LP: #292929)
  - Use "close" animation when sending to notification area (LP: #130811)
  - Fix resize problems (LP: #269872)
  - Support "--version" option when launching from command line
    (LP: #292011)
  - Correctly parse announce URLs that have leading or trailing
    spaces (LP: #262411)
  - Display an error when "Open Torrent" fails (LP: #281463)
* Dropped 10_fix_crasher_from_upstream.dpatch: Fix is in this
  upstream release.
* debian/control: Don't just build-depend on libcurl-dev, which is
  a virtual package.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/******************************************************************************
2
 
 * $Id: fdlimit.c 6489 2008-08-11 19:05:00Z charles $
 
2
 * $Id: fdlimit.c 7051 2008-11-05 05:56:06Z charles $
3
3
 *
4
4
 * Copyright (c) 2005-2008 Transmission authors and contributors
5
5
 *
23
23
 *****************************************************************************/
24
24
 
25
25
#ifndef WIN32
26
 
#define HAVE_GETRLIMIT
 
26
 #define HAVE_GETRLIMIT
27
27
#endif
28
28
 
29
29
#include <assert.h>
32
32
#include <stdio.h>
33
33
#include <stdlib.h>
34
34
#include <string.h>
 
35
#ifdef SYS_DARWIN
 
36
#include <fcntl.h>
 
37
#endif
35
38
 
36
39
#include <sys/types.h>
37
40
#include <sys/stat.h>
38
41
#ifdef HAVE_GETRLIMIT
39
 
#include <sys/time.h> /* getrlimit */
40
 
#include <sys/resource.h> /* getrlimit */
 
42
 #include <sys/time.h> /* getrlimit */
 
43
 #include <sys/resource.h> /* getrlimit */
41
44
#endif
42
45
#include <unistd.h>
43
 
#include <libgen.h> /* dirname */
44
46
#include <fcntl.h> /* O_LARGEFILE */
45
47
 
46
48
#include <event.h>
53
55
#include "platform.h" /* tr_lock */
54
56
#include "utils.h"
55
57
 
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
 
#define dbgmsg(fmt...) tr_deepLog( __FILE__, __LINE__, NULL, ##fmt )
 
58
#define dbgmsg( ... ) \
 
59
    do { \
 
60
        if( tr_deepLoggingIsActive( ) ) \
 
61
            tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
 
62
    } while( 0 )
63
63
 
64
64
/**
65
65
***
75
75
 
76
76
struct tr_openfile
77
77
{
78
 
    unsigned int  isCheckedOut : 1;
79
 
    unsigned int  isWritable : 1;
80
 
    unsigned int  closeWhenDone : 1;
81
 
    char          filename[MAX_PATH_LENGTH];
82
 
    int           fd;
83
 
    uint64_t      date;
 
78
    unsigned int    isCheckedOut  : 1;
 
79
    unsigned int    isWritable    : 1;
 
80
    unsigned int    closeWhenDone : 1;
 
81
    char            filename[MAX_PATH_LENGTH];
 
82
    int             fd;
 
83
    uint64_t        date;
84
84
};
85
85
 
86
86
struct tr_fd_s
87
87
{
88
 
    int                  reserved;
89
 
    int                  normal;
90
 
    int                  normalMax;
91
 
    tr_lock            * lock;
92
 
    struct tr_openfile   open[TR_MAX_OPEN_FILES];
 
88
    int                   socketCount;
 
89
    int                   socketMax;
 
90
    tr_lock *             lock;
 
91
    struct tr_openfile    open[TR_MAX_OPEN_FILES];
93
92
};
94
93
 
95
94
static struct tr_fd_s * gFd = NULL;
100
99
****
101
100
***/
102
101
 
103
 
static tr_errno
104
 
TrOpenFile( int           i,
105
 
            const char  * folder,
106
 
            const char  * torrentFile,
107
 
            int           write )
 
102
static int
 
103
preallocateFile( int fd UNUSED, uint64_t length UNUSED )
 
104
{
 
105
#ifdef HAVE_FALLOCATE
 
106
 
 
107
    return fallocate( fd, 0, offset, length );
 
108
 
 
109
#elif defined(HAVE_POSIX_FALLOCATE)
 
110
 
 
111
    return posix_fallocate( fd, 0, length );
 
112
 
 
113
#elif defined(SYS_DARWIN) 
 
114
 
 
115
    fstore_t fst;
 
116
    fst.fst_flags = F_ALLOCATECONTIG;
 
117
    fst.fst_posmode = F_PEOFPOSMODE;
 
118
    fst.fst_offset = 0;
 
119
    fst.fst_length = length;
 
120
    fst.fst_bytesalloc = 0;
 
121
    return fcntl( fd, F_PREALLOCATE, &fst );
 
122
 
 
123
#else
 
124
 
 
125
    #warning no known method to preallocate files on this platform
 
126
    return -1;
 
127
 
 
128
#endif
 
129
}
 
130
 
 
131
/**
 
132
 * returns 0 on success, or an errno value on failure.
 
133
 * errno values include ENOENT if the parent folder doesn't exist,
 
134
 * plus the errno values set by tr_mkdirp() and open().
 
135
 */
 
136
static int
 
137
TrOpenFile( int          i,
 
138
            const char * folder,
 
139
            const char * torrentFile,
 
140
            int          doWrite,
 
141
            int          doPreallocate,
 
142
            uint64_t     desiredFileSize )
108
143
{
109
144
    struct tr_openfile * file = &gFd->open[i];
110
 
    int flags;
111
 
    char filename[MAX_PATH_LENGTH];
112
 
    struct stat sb;
 
145
    int                  flags;
 
146
    char               * filename;
 
147
    struct stat          sb;
 
148
    int                  alreadyExisted;
113
149
 
114
150
    /* confirm the parent folder exists */
115
151
    if( stat( folder, &sb ) || !S_ISDIR( sb.st_mode ) )
116
 
        return TR_ERROR_IO_PARENT;
 
152
        return ENOENT;
117
153
 
118
154
    /* create subfolders, if any */
119
 
    tr_buildPath ( filename, sizeof(filename), folder, torrentFile, NULL );
120
 
    if( write ) {
121
 
        char * tmp = tr_strdup( filename );
122
 
        const int err = tr_mkdirp( dirname(tmp), 0777 ) ? errno : 0;
 
155
    filename = tr_buildPath( folder, torrentFile, NULL );
 
156
    if( doWrite )
 
157
    {
 
158
        char * tmp = tr_dirname( filename );
 
159
        const int err = tr_mkdirp( tmp, 0777 ) ? errno : 0;
123
160
        tr_free( tmp );
124
 
        if( err )
125
 
            return tr_ioErrorFromErrno( err );
 
161
        if( err ) {
 
162
            tr_free( filename );
 
163
            return err;
 
164
        }
126
165
    }
127
166
 
 
167
    alreadyExisted = !stat( filename, &sb ) && S_ISREG( sb.st_mode );
 
168
    
128
169
    /* open the file */
129
 
    flags = write ? (O_RDWR | O_CREAT) : O_RDONLY;
 
170
    flags = doWrite ? ( O_RDWR | O_CREAT ) : O_RDONLY;
130
171
#ifdef O_LARGEFILE
131
172
    flags |= O_LARGEFILE;
132
173
#endif
134
175
    flags |= O_BINARY;
135
176
#endif
136
177
    file->fd = open( filename, flags, 0666 );
137
 
    if( file->fd == -1 ) {
 
178
    if( file->fd == -1 )
 
179
    {
138
180
        const int err = errno;
139
 
        tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename, tr_strerror(err) );
140
 
        return tr_ioErrorFromErrno( err );
 
181
        tr_err( _( "Couldn't open \"%1$s\": %2$s" ), filename,
 
182
               tr_strerror( err ) );
 
183
        tr_free( filename );
 
184
        return err;
141
185
    }
142
186
 
143
 
    return TR_OK;
 
187
    if( ( file->fd >= 0 ) && !alreadyExisted && doPreallocate )
 
188
        if( !preallocateFile( file->fd, desiredFileSize ) )
 
189
            tr_inf( _( "Preallocated file \"%s\"" ), filename );
 
190
       
 
191
    tr_free( filename );
 
192
    return 0;
144
193
}
145
194
 
146
195
static int
166
215
static int
167
216
fileIsCheckedOut( const struct tr_openfile * o )
168
217
{
169
 
    return fileIsOpen(o) && o->isCheckedOut;
 
218
    return fileIsOpen( o ) && o->isCheckedOut;
170
219
}
171
220
 
 
221
/* returns an fd on success, or a -1 on failure and sets errno */
172
222
int
173
223
tr_fdFileCheckout( const char * folder,
174
 
                   const char * torrentFile, 
175
 
                   int          write )
 
224
                   const char * torrentFile,
 
225
                   int          doWrite,
 
226
                   int          doPreallocate,
 
227
                   uint64_t     desiredFileSize )
176
228
{
177
 
    int i, winner = -1;
 
229
    int                  i, winner = -1;
178
230
    struct tr_openfile * o;
179
 
    char filename[MAX_PATH_LENGTH];
 
231
    char               * filename;
180
232
 
181
233
    assert( folder && *folder );
182
234
    assert( torrentFile && *torrentFile );
183
 
    assert( write==0 || write==1 );
 
235
    assert( doWrite == 0 || doWrite == 1 );
184
236
 
185
 
    tr_buildPath ( filename, sizeof(filename), folder, torrentFile, NULL );
186
 
    dbgmsg( "looking for file '%s', writable %c", filename, write?'y':'n' );
 
237
    filename = tr_buildPath( folder, torrentFile, NULL );
 
238
    dbgmsg( "looking for file '%s', writable %c", filename,
 
239
            doWrite ? 'y' : 'n' );
187
240
 
188
241
    tr_lockLock( gFd->lock );
189
242
 
190
243
    /* Is it already open? */
191
 
    for( i=0; i<TR_MAX_OPEN_FILES; ++i )
 
244
    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
192
245
    {
193
246
        o = &gFd->open[i];
194
247
 
198
251
        if( strcmp( filename, o->filename ) )
199
252
            continue;
200
253
 
201
 
        if( fileIsCheckedOut( o ) ) {
 
254
        if( fileIsCheckedOut( o ) )
 
255
        {
202
256
            dbgmsg( "found it!  it's open, but checked out.  waiting..." );
203
257
            tr_lockUnlock( gFd->lock );
204
258
            tr_wait( 200 );
207
261
            continue;
208
262
        }
209
263
 
210
 
        if( write && !o->isWritable ) {
211
 
            dbgmsg( "found it!  it's open and available, but isn't writable. closing..." );
 
264
        if( doWrite && !o->isWritable )
 
265
        {
 
266
            dbgmsg(
 
267
                "found it!  it's open and available, but isn't writable. closing..." );
212
268
            TrCloseFile( i );
213
269
            break;
214
270
        }
218
274
        break;
219
275
    }
220
276
 
221
 
    dbgmsg( "it's not already open.  looking for an open slot or an old file." );
 
277
    dbgmsg(
 
278
        "it's not already open.  looking for an open slot or an old file." );
222
279
    while( winner < 0 )
223
280
    {
224
281
        uint64_t date = tr_date( ) + 1;
225
282
 
226
283
        /* look for the file that's been open longest */
227
 
        for( i=0; i<TR_MAX_OPEN_FILES; ++i )
 
284
        for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
228
285
        {
229
286
            o = &gFd->open[i];
230
287
 
231
 
            if( !fileIsOpen( o ) ) {
 
288
            if( !fileIsOpen( o ) )
 
289
            {
232
290
                winner = i;
233
291
                dbgmsg( "found an empty slot in %d", winner );
234
292
                break;
235
293
            }
236
294
 
237
 
            if( date > o->date ) {
 
295
            if( date > o->date )
 
296
            {
238
297
                date = o->date;
239
298
                winner = i;
240
299
            }
241
300
        }
242
301
 
243
 
        if( winner >= 0 ) {
244
 
            if( fileIsOpen( &gFd->open[winner] ) ) {
245
 
                dbgmsg( "closing file '%s', slot #%d", gFd->open[winner].filename, winner );
 
302
        if( winner >= 0 )
 
303
        {
 
304
            if( fileIsOpen( &gFd->open[winner] ) )
 
305
            {
 
306
                dbgmsg( "closing file '%s', slot #%d",
 
307
                        gFd->open[winner].filename,
 
308
                        winner );
246
309
                TrCloseFile( winner );
247
310
            }
248
 
        } else { 
249
 
            dbgmsg( "everything's full!  waiting for someone else to finish something" );
 
311
        }
 
312
        else
 
313
        {
 
314
            dbgmsg(
 
315
                "everything's full!  waiting for someone else to finish something" );
250
316
            tr_lockUnlock( gFd->lock );
251
317
            tr_wait( 200 );
252
318
            tr_lockLock( gFd->lock );
257
323
    o = &gFd->open[winner];
258
324
    if( !fileIsOpen( o ) )
259
325
    {
260
 
        const tr_errno err = TrOpenFile( winner, folder, torrentFile, write );
 
326
        const int err = TrOpenFile( winner, folder, torrentFile, doWrite, doPreallocate, desiredFileSize );
261
327
        if( err ) {
262
328
            tr_lockUnlock( gFd->lock );
263
 
            return err;
 
329
            tr_free( filename );
 
330
            errno = err;
 
331
            return -1;
264
332
        }
265
333
 
266
 
        dbgmsg( "opened '%s' in slot %d, write %c", filename, winner, write?'y':'n' );
 
334
        dbgmsg( "opened '%s' in slot %d, doWrite %c", filename, winner,
 
335
                doWrite ? 'y' : 'n' );
267
336
        tr_strlcpy( o->filename, filename, sizeof( o->filename ) );
268
 
        o->isWritable = write;
 
337
        o->isWritable = doWrite;
269
338
    }
270
339
 
271
340
    dbgmsg( "checking out '%s' in slot %d", filename, winner );
272
341
    o->isCheckedOut = 1;
273
342
    o->closeWhenDone = 0;
274
343
    o->date = tr_date( );
 
344
    tr_free( filename );
275
345
    tr_lockUnlock( gFd->lock );
276
346
    return o->fd;
277
347
}
280
350
tr_fdFileReturn( int fd )
281
351
{
282
352
    int i;
 
353
 
283
354
    tr_lockLock( gFd->lock );
284
355
 
285
 
    for( i=0; i<TR_MAX_OPEN_FILES; ++i )
 
356
    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
286
357
    {
287
358
        struct tr_openfile * o = &gFd->open[i];
288
359
        if( o->fd != fd )
292
363
        o->isCheckedOut = 0;
293
364
        if( o->closeWhenDone )
294
365
            TrCloseFile( i );
295
 
        
 
366
 
296
367
        break;
297
368
    }
298
 
    
 
369
 
299
370
    tr_lockUnlock( gFd->lock );
300
371
}
301
372
 
303
374
tr_fdFileClose( const char * filename )
304
375
{
305
376
    int i;
 
377
 
306
378
    tr_lockLock( gFd->lock );
307
379
 
308
 
    for( i=0; i<TR_MAX_OPEN_FILES; ++i )
 
380
    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
309
381
    {
310
382
        struct tr_openfile * o = &gFd->open[i];
311
 
        if( !fileIsOpen(o) || strcmp(filename,o->filename) )
 
383
        if( !fileIsOpen( o ) || strcmp( filename, o->filename ) )
312
384
            continue;
313
385
 
314
386
        dbgmsg( "tr_fdFileClose closing '%s'", filename );
315
387
 
316
 
        if( !o->isCheckedOut ) {
 
388
        if( !o->isCheckedOut )
 
389
        {
317
390
            dbgmsg( "not checked out, so closing it now... '%s'", filename );
318
391
            TrCloseFile( i );
319
 
        } else {
320
 
            dbgmsg( "flagging file '%s', slot #%d to be closed when checked in", gFd->open[i].filename, i );
 
392
        }
 
393
        else
 
394
        {
 
395
            dbgmsg(
 
396
                "flagging file '%s', slot #%d to be closed when checked in",
 
397
                gFd->open[i].filename, i );
321
398
            o->closeWhenDone = 1;
322
399
        }
323
400
    }
324
 
    
 
401
 
325
402
    tr_lockUnlock( gFd->lock );
326
403
}
327
404
 
331
408
****
332
409
***/
333
410
 
334
 
static tr_list * reservedSockets = NULL;
335
 
 
336
 
static void
337
 
setSocketPriority( int fd, int isReserved )
338
 
{
339
 
    if( isReserved )
340
 
        tr_list_append( &reservedSockets, TR_UINT_TO_PTR(fd) );
341
 
}
342
 
 
343
 
static int
344
 
socketWasReserved( int fd )
345
 
{
346
 
    return tr_list_remove_data( &reservedSockets, TR_UINT_TO_PTR(fd) ) != NULL;
347
 
}
348
 
 
349
411
static int
350
412
getSocketMax( struct tr_fd_s * gFd )
351
413
{
352
 
    return gFd->normalMax;
 
414
    return gFd->socketMax;
353
415
}
354
416
 
355
417
int
356
 
tr_fdSocketCreate( int type, int isReserved )
 
418
tr_fdSocketCreate( int type )
357
419
{
358
420
    int s = -1;
 
421
 
359
422
    tr_lockLock( gFd->lock );
360
423
 
361
 
    if( isReserved || ( gFd->normal < getSocketMax( gFd ) ) )
 
424
    if( gFd->socketCount < getSocketMax( gFd ) )
362
425
        if( ( s = socket( AF_INET, type, 0 ) ) < 0 )
363
 
            tr_err( _( "Couldn't create socket: %s" ), tr_strerror( sockerrno ) );
 
426
            tr_err( _( "Couldn't create socket: %s" ),
 
427
                   tr_strerror( sockerrno ) );
364
428
 
365
429
    if( s > -1 )
366
 
    {
367
 
        setSocketPriority( s, isReserved );
368
 
 
369
 
        if( isReserved )
370
 
            ++gFd->reserved;
371
 
        else
372
 
            ++gFd->normal;
373
 
    }
374
 
 
375
 
    assert( gFd->reserved >= 0 );
376
 
    assert( gFd->normal >= 0 );
 
430
        ++gFd->socketCount;
 
431
 
 
432
    assert( gFd->socketCount >= 0 );
377
433
 
378
434
    tr_lockUnlock( gFd->lock );
379
435
    return s;
380
436
}
381
437
 
382
438
int
383
 
tr_fdSocketAccept( int b, struct in_addr * addr, tr_port_t * port )
 
439
tr_fdSocketAccept( int              b,
 
440
                   struct in_addr * addr,
 
441
                   tr_port_t *      port )
384
442
{
385
 
    int s = -1;
386
 
    unsigned int len;
 
443
    int                s = -1;
 
444
    unsigned int       len;
387
445
    struct sockaddr_in sock;
388
446
 
389
447
    assert( addr );
390
448
    assert( port );
391
449
 
392
450
    tr_lockLock( gFd->lock );
393
 
    if( gFd->normal < getSocketMax( gFd ) )
 
451
    if( gFd->socketCount < getSocketMax( gFd ) )
394
452
    {
395
453
        len = sizeof( sock );
396
454
        s = accept( b, (struct sockaddr *) &sock, &len );
397
455
    }
398
456
    if( s > -1 )
399
457
    {
400
 
        setSocketPriority( s, FALSE );
401
458
        *addr = sock.sin_addr;
402
459
        *port = sock.sin_port;
403
 
        gFd->normal++;
 
460
        ++gFd->socketCount;
404
461
    }
405
462
    tr_lockUnlock( gFd->lock );
406
463
 
422
479
{
423
480
    tr_lockLock( gFd->lock );
424
481
 
425
 
    if( s >= 0 ) {
 
482
    if( s >= 0 )
 
483
    {
426
484
        socketClose( s );
427
 
        if( socketWasReserved( s ) )
428
 
            --gFd->reserved;
429
 
        else
430
 
            --gFd->normal;
 
485
        --gFd->socketCount;
431
486
    }
432
487
 
433
 
    assert( gFd->reserved >= 0 );
434
 
    assert( gFd->normal >= 0 );
 
488
    assert( gFd->socketCount >= 0 );
435
489
 
436
490
    tr_lockUnlock( gFd->lock );
437
491
}
456
510
        struct rlimit rlim;
457
511
        getrlimit( RLIMIT_NOFILE, &rlim );
458
512
        rlim.rlim_cur = MIN( rlim.rlim_max,
459
 
               (rlim_t)(globalPeerLimit + NOFILE_BUFFER) );
 
513
                            (rlim_t)( globalPeerLimit + NOFILE_BUFFER ) );
460
514
        setrlimit( RLIMIT_NOFILE, &rlim );
461
 
        gFd->normalMax = rlim.rlim_cur - NOFILE_BUFFER;
 
515
        gFd->socketMax = rlim.rlim_cur - NOFILE_BUFFER;
462
516
        tr_dbg( "setrlimit( RLIMIT_NOFILE, %d )", (int)rlim.rlim_cur );
463
517
    }
464
518
#else
465
 
    gFd->normalMax = globalPeerLimit;
 
519
    gFd->socketMax = globalPeerLimit;
466
520
#endif
467
521
    tr_dbg( "%d usable file descriptors", globalPeerLimit );
468
522
 
469
 
    for( i=0; i<TR_MAX_OPEN_FILES; ++i ) 
 
523
    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
470
524
        gFd->open[i].fd = -1;
471
525
}
472
526
 
475
529
{
476
530
    int i = 0;
477
531
 
478
 
    for( i=0; i<TR_MAX_OPEN_FILES; ++i )
 
532
    for( i = 0; i < TR_MAX_OPEN_FILES; ++i )
479
533
        if( fileIsOpen( &gFd->open[i] ) )
480
534
            TrCloseFile( i );
481
535
 
482
536
    tr_lockFree( gFd->lock );
483
537
 
484
 
    tr_list_free( &reservedSockets, NULL );
485
538
    tr_free( gFd );
 
539
    gFd = NULL;
486
540
}
487
541
 
488
542
void
489
543
tr_fdSetPeerLimit( uint16_t n )
490
544
{
491
 
    assert( gFd!=NULL && "tr_fdInit() must be called first!" );
492
 
    gFd->normalMax = n;
 
545
    assert( gFd != NULL && "tr_fdInit() must be called first!" );
 
546
    gFd->socketMax = n;
493
547
}
494
548
 
495
549
uint16_t
496
550
tr_fdGetPeerLimit( void )
497
551
{
498
 
    return gFd ? gFd->normalMax : -1;
 
552
    return gFd ? gFd->socketMax : -1;
499
553
}
 
554