source: trunk/libtransmission/fdlimit.c @ 1425

Last change on this file since 1425 was 1425, checked in by titer, 15 years ago

More simplifications

  • Property svn:keywords set to Date Rev Author Id
File size: 13.7 KB
Line 
1/******************************************************************************
2 * $Id: fdlimit.c 1425 2007-01-21 19:42:11Z titer $
3 *
4 * Copyright (c) 2005-2006 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#include "transmission.h"
26
27#define TR_MAX_OPEN_FILES 16 /* That is, real files, not sockets */
28#define TR_RESERVED_FDS   16 /* Number of sockets reserved for
29                                connections to trackers */
30
31/***********************************************************************
32 * Structures
33 **********************************************************************/
34typedef struct tr_openFile_s
35{
36    char       folder[MAX_PATH_LENGTH];
37    char       name[MAX_PATH_LENGTH];
38    int        file;
39    int        write;
40
41#define STATUS_INVALID 1
42#define STATUS_UNUSED  2
43#define STATUS_USED    4
44#define STATUS_CLOSING 8
45    int        status;
46
47    uint64_t   date;
48}
49tr_openFile_t;
50
51typedef struct tr_fd_s
52{
53    tr_lock_t       lock;
54    tr_cond_t       cond;
55   
56    int             reserved;
57
58    int             normal;
59    int             normalMax;
60
61    tr_openFile_t   open[TR_MAX_OPEN_FILES];
62}
63tr_fd_t;
64
65static tr_fd_t * gFd = NULL;
66
67/***********************************************************************
68 * Local prototypes
69 **********************************************************************/
70static int  ErrorFromErrno();
71static int  OpenFile( int i, char * folder, char * name, int write );
72static void CloseFile( int i );
73
74
75/***********************************************************************
76 * tr_fdInit
77 **********************************************************************/
78void tr_fdInit()
79{
80    int i, j, s[4096];
81
82    if( gFd )
83    {
84        tr_err( "tr_fdInit was called before!" );
85        return;
86    }
87
88    gFd = calloc( sizeof( tr_fd_t ), 1 );
89
90    /* Init lock and cond */
91    tr_lockInit( &gFd->lock );
92    tr_condInit( &gFd->cond );
93
94    /* Detect the maximum number of open files or sockets */
95    for( i = 0; i < 4096; i++ )
96    {
97        if( ( s[i] = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
98        {
99            break;
100        }
101    }
102    for( j = 0; j < i; j++ )
103    {
104#ifdef BEOS_NETSERVER
105            closesocket( s[j] );
106#else
107            close( s[j] );
108#endif
109    }
110
111    tr_dbg( "%d usable file descriptors", i );
112
113    gFd->reserved  = 0;
114    gFd->normal    = 0;
115
116    gFd->normalMax = i - TR_RESERVED_FDS - 10;
117        /* To be safe, in case the UI needs to write a preferences file
118           or something */
119
120    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
121    {
122        gFd->open[i].status = STATUS_INVALID;
123    }
124}
125
126/***********************************************************************
127 * tr_fdFileOpen
128 **********************************************************************/
129int tr_fdFileOpen( char * folder, char * name, int write )
130{
131    int i, winner, ret;
132    uint64_t date;
133
134    tr_lockLock( &gFd->lock );
135
136    /* Is it already open? */
137    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
138    {
139        if( gFd->open[i].status & STATUS_INVALID ||
140            strcmp( folder, gFd->open[i].folder ) ||
141            strcmp( name, gFd->open[i].name ) )
142        {
143            continue;
144        }
145        if( gFd->open[i].status & STATUS_CLOSING )
146        {
147            /* File is being closed by another thread, wait until
148             * it's done before we reopen it */
149            tr_condWait( &gFd->cond, &gFd->lock );
150            i = -1;
151            continue;
152        }
153        if( gFd->open[i].write < write )
154        {
155            /* File is open read-only and needs to be closed then
156             * re-opened read-write */
157            CloseFile( i );
158            continue;
159        }
160        winner = i;
161        goto done;
162    }
163
164    /* Can we open one more file? */
165    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
166    {
167        if( gFd->open[i].status & STATUS_INVALID )
168        {
169            winner = i;
170            goto open;
171        }
172    }
173
174    /* All slots taken - close the oldest currently unused file */
175    for( ;; )
176    {
177        date   = tr_date() + 1;
178        winner = -1;
179
180        for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
181        {
182            if( !( gFd->open[i].status & STATUS_UNUSED ) )
183            {
184                continue;
185            }
186            if( gFd->open[i].date < date )
187            {
188                winner = i;
189                date   = gFd->open[i].date;
190            }
191        }
192
193        if( winner >= 0 )
194        {
195            CloseFile( winner );
196            goto open;
197        }
198
199        /* All used! Wait a bit and try again */
200        tr_condWait( &gFd->cond, &gFd->lock );
201    }
202
203open:
204    if( ( ret = OpenFile( winner, folder, name, write ) ) )
205    {
206        tr_lockUnlock( &gFd->lock );
207        return ret;
208    }
209    snprintf( gFd->open[winner].folder, MAX_PATH_LENGTH, "%s", folder );
210    snprintf( gFd->open[winner].name, MAX_PATH_LENGTH, "%s", name );
211    gFd->open[winner].write = write;
212
213done:
214    gFd->open[winner].status = STATUS_USED;
215    gFd->open[winner].date   = tr_date();
216    tr_lockUnlock( &gFd->lock );
217   
218    return gFd->open[winner].file;
219}
220
221/***********************************************************************
222 * tr_fdFileRelease
223 **********************************************************************/
224void tr_fdFileRelease( int file )
225{
226    int i;
227    tr_lockLock( &gFd->lock );
228
229    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
230    {
231        if( gFd->open[i].file == file )
232        {
233            gFd->open[i].status = STATUS_UNUSED;
234            break;
235        }
236    }
237   
238    tr_condSignal( &gFd->cond );
239    tr_lockUnlock( &gFd->lock );
240}
241
242/***********************************************************************
243 * tr_fdFileClose
244 **********************************************************************/
245void tr_fdFileClose( char * folder, char * name )
246{
247    int i;
248
249    tr_lockLock( &gFd->lock );
250
251    for( i = 0; i < TR_MAX_OPEN_FILES; i++ )
252    {
253        if( gFd->open[i].status & STATUS_INVALID )
254        {
255            continue;
256        }
257        if( !strcmp( folder, gFd->open[i].folder ) &&
258            !strcmp( name, gFd->open[i].name ) )
259        {
260            CloseFile( i );
261        }
262    }
263
264    tr_lockUnlock( &gFd->lock );
265}
266
267
268/***********************************************************************
269 * Sockets
270 **********************************************************************/
271typedef struct
272{
273    int socket;
274    int priority;
275}
276tr_socket_t;
277
278/* Remember the priority of every socket we open, so that we can keep
279 * track of how many reserved file descriptors we are using */
280static tr_socket_t * gSockets = NULL;
281static int gSocketsSize = 0;
282static int gSocketsCount = 0;
283static void SocketSetPriority( int s, int priority )
284{
285    if( gSocketsSize < 1 )
286    {
287        gSocketsSize = 256;
288        gSockets = malloc( gSocketsSize * sizeof( tr_socket_t ) );
289    }
290    if( gSocketsSize <= gSocketsCount )
291    {
292        gSocketsSize *= 2;
293        gSockets = realloc( gSockets, gSocketsSize * sizeof( tr_socket_t ) );
294    }
295    gSockets[gSocketsCount].socket = s;
296    gSockets[gSocketsCount].priority = priority;
297    gSocketsCount++;
298}
299static int SocketGetPriority( int s )
300{
301    int i, ret;
302    for( i = 0; i < gSocketsCount; i++ )
303        if( gSockets[i].socket == s )
304            break;
305    if( i >= gSocketsCount )
306    {
307        tr_err( "could not find that socket (%d)!", s );
308        return -1;
309    }
310    ret = gSockets[i].priority;
311    gSocketsCount--;
312    memmove( &gSockets[i], &gSockets[i+1],
313            ( gSocketsCount - i ) * sizeof( tr_socket_t ) );
314}
315
316/***********************************************************************
317 * tr_fdSocketCreate
318 **********************************************************************/
319int tr_fdSocketCreate( int type, int priority )
320{
321    int s = -1;
322
323    tr_lockLock( &gFd->lock );
324    if( ( priority && gFd->reserved < TR_RESERVED_FDS ) ||
325        ( !priority && gFd->normal < gFd->normalMax ) )
326    {
327       if( ( s = socket( AF_INET, type, 0 ) ) < 0 )
328       {
329           tr_err( "Could not create socket (%s)", strerror( errno ) );
330       }
331    }
332    if( s > -1 )
333    {
334        SocketSetPriority( s, priority );
335        if( priority )
336            gFd->reserved++;
337        else
338            gFd->normal++;
339    }
340    tr_lockUnlock( &gFd->lock );
341
342    return s;
343}
344
345int tr_fdSocketAccept( int b, struct in_addr * addr, in_port_t * port )
346{
347    int s = -1;
348    unsigned len;
349    struct sockaddr_in sock;
350
351    tr_lockLock( &gFd->lock );
352    if( gFd->normal < gFd->normalMax )
353    {
354        len = sizeof( sock );
355        s = accept( b, (struct sockaddr *) &sock, &len );
356    }
357    if( s > -1 )
358    {
359        SocketSetPriority( s, 0 );
360        *addr = sock.sin_addr;
361        *port = sock.sin_port;
362        gFd->normal++;
363    }
364    tr_lockUnlock( &gFd->lock );
365
366    return s;
367}
368
369/***********************************************************************
370 * tr_fdSocketClose
371 **********************************************************************/
372void tr_fdSocketClose( int s )
373{
374    tr_lockLock( &gFd->lock );
375#ifdef BEOS_NETSERVER
376    closesocket( s );
377#else
378    close( s );
379#endif
380    if( SocketGetPriority( s ) )
381        gFd->reserved--;
382    else
383        gFd->normal--;
384    tr_lockUnlock( &gFd->lock );
385}
386
387/***********************************************************************
388 * tr_fdClose
389 **********************************************************************/
390void tr_fdClose()
391{
392    tr_lockClose( &gFd->lock );
393    tr_condClose( &gFd->cond );
394    free( gFd );
395}
396
397
398/***********************************************************************
399 * Local functions
400 **********************************************************************/
401
402/***********************************************************************
403 * ErrorFromErrno
404 **********************************************************************/
405static int ErrorFromErrno()
406{
407    if( errno == EACCES || errno == EROFS )
408        return TR_ERROR_IO_PERMISSIONS;
409    return TR_ERROR_IO_OTHER;
410}
411
412/***********************************************************************
413 * CheckFolder
414 ***********************************************************************
415 *
416 **********************************************************************/
417static int OpenFile( int i, char * folder, char * name, int write )
418{
419    tr_openFile_t * file = &gFd->open[i];
420    struct stat sb;
421    char * path;
422
423    tr_dbg( "Opening %s in %s (%d)", name, folder, write );
424
425    /* Make sure the parent folder exists */
426    if( stat( folder, &sb ) || !S_ISDIR( sb.st_mode ) )
427    {
428        return TR_ERROR_IO_PARENT;
429    }
430
431    asprintf( &path, "%s/%s", folder, name );
432
433    /* Create subfolders, if any */
434    if( write )
435    {
436        char * p = path + strlen( folder ) + 1;
437        char * s;
438
439        while( ( s = strchr( p, '/' ) ) )
440        {
441            *s = '\0';
442            if( stat( path, &sb ) )
443            {
444                if( mkdir( path, 0777 ) )
445                {
446                    tr_err( "Could not create folder '%s'", path );
447                    free( path );
448                    return ErrorFromErrno();
449                }
450            }
451            else
452            {
453                if( !S_ISDIR( sb.st_mode ) )
454                {
455                    tr_err( "Is not a folder: '%s'", path );
456                    free( path );
457                    return TR_ERROR_IO_OTHER;
458                }
459            }
460            *s = '/';
461            p = s + 1;
462        }
463    }
464
465    /* Now try to really open the file */
466    file->file = open( path, write ? ( O_RDWR | O_CREAT ) : O_RDONLY, 0666 );
467    free( path );
468
469    if( file->file < 0 )
470    {
471        int ret = ErrorFromErrno();
472        tr_err( "Could not open %s in %s (%d, %d)", name, folder, write, ret );
473        return ret;
474    }
475
476    return TR_OK;
477}
478
479/***********************************************************************
480 * CloseFile
481 ***********************************************************************
482 * We first mark it as closing then release the lock while doing so,
483 * because close() may take same time and we don't want to block other
484 * threads.
485 **********************************************************************/
486static void CloseFile( int i )
487{
488    tr_openFile_t * file = &gFd->open[i];
489
490    /* If it's already being closed by another thread, just wait till
491     * it is done */
492    while( file->status & STATUS_CLOSING )
493    {
494        tr_condWait( &gFd->cond, &gFd->lock );
495    }
496    if( file->status & STATUS_INVALID )
497    {
498        return;
499    }
500
501    /* Nobody is closing it already, so let's do it */
502    if( file->status & STATUS_USED )
503    {
504        tr_err( "CloseFile: closing a file that's being used!" );
505    }
506    tr_dbg( "Closing %s in %s (%d)", file->name, file->folder, file->write );
507    file->status = STATUS_CLOSING;
508    tr_lockUnlock( &gFd->lock );
509    close( file->file );
510    tr_lockLock( &gFd->lock );
511    file->status = STATUS_INVALID;
512    tr_condSignal( &gFd->cond );
513}
514
Note: See TracBrowser for help on using the repository browser.