source: trunk/libtransmission/fdlimit.c @ 2343

Last change on this file since 2343 was 2343, checked in by joshe, 15 years ago

Change a couple functions to take an in_addr pointer instead of an in_addr.
Forward declare struct in_addr and include the relevant headers in the .c files where it's used.

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