source: trunk/libtransmission/platform.c @ 3984

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

move tr_getDefaultRoute() to natpmp.c because it's the only code that uses it

  • Property svn:keywords set to Date Rev Author Id
File size: 13.0 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 3984 2007-11-26 20:21:52Z charles $
3 *
4 * Copyright (c) 2005 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 <assert.h>
26#include <errno.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#ifdef __BEOS__
32  #include <signal.h> 
33  #include <fs_info.h>
34  #include <FindDirectory.h>
35  #include <kernel/OS.h>
36  #define BEOS_MAX_THREADS 256
37#elif defined(WIN32)
38  #include <windows.h>
39  #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_PROFILE */
40#else
41  #include <pthread.h>
42#endif
43
44#include <sys/types.h>
45#include <dirent.h>
46#include <unistd.h> /* getuid getpid */
47
48#include "transmission.h"
49#include "list.h"
50#include "net.h"
51#include "platform.h"
52#include "utils.h"
53
54/***
55****  THREADS
56***/
57
58#ifdef __BEOS__
59typedef thread_id tr_thread_id;
60#elif defined(WIN32)
61typedef DWORD tr_thread_id;
62#else
63typedef pthread_t tr_thread_id;
64#endif
65
66static tr_thread_id
67tr_getCurrentThread( void )
68{
69#ifdef __BEOS__
70    return find_thread( NULL );
71#elif defined(WIN32)
72    return GetCurrentThreadId();
73#else
74    return pthread_self( );
75#endif
76}
77
78static int
79tr_areThreadsEqual( tr_thread_id a, tr_thread_id b )
80{
81#ifdef __BEOS__
82    return a == b;
83#elif defined(WIN32)
84    return a == b;
85#else
86    return pthread_equal( a, b );
87#endif
88}
89
90struct tr_thread
91{
92    void          (* func ) ( void * );
93    void           * arg;
94    const char     * name;
95
96#ifdef __BEOS__
97    thread_id        thread;
98#elif defined(WIN32)
99    HANDLE           thread;
100    unsigned int     thread_id;
101#else
102    pthread_t        thread;
103#endif
104
105};
106
107int
108tr_amInThread ( const tr_thread * t )
109{
110    return tr_areThreadsEqual( tr_getCurrentThread(), t->thread );
111}
112
113#ifdef WIN32
114#define ThreadFuncReturnType unsigned WINAPI
115#else
116#define ThreadFuncReturnType void
117#endif
118
119static ThreadFuncReturnType
120ThreadFunc( void * _t )
121{
122    tr_thread * t = _t;
123    const char * name = t->name;
124
125#ifdef __BEOS__
126    /* This is required because on BeOS, SIGINT is sent to each thread,
127       which kills them not nicely */
128    signal( SIGINT, SIG_IGN );
129#endif
130
131    tr_dbg( "Thread '%s' started", name );
132    t->func( t->arg );
133    tr_dbg( "Thread '%s' exited", name );
134
135#ifdef WIN32
136    _endthreadex( 0 );
137    return 0;
138#endif
139}
140
141tr_thread *
142tr_threadNew( void (*func)(void *),
143              void * arg,
144              const char * name )
145{
146    tr_thread * t = tr_new0( tr_thread, 1 );
147    t->func = func;
148    t->arg  = arg;
149    t->name = name;
150
151#ifdef __BEOS__
152    t->thread = spawn_thread( (void*)ThreadFunc, name, B_NORMAL_PRIORITY, t );
153    resume_thread( t->thread );
154#elif defined(WIN32)
155    t->thread = (HANDLE) _beginthreadex( NULL, 0, &ThreadFunc, t, 0, &t->thread_id );
156#else
157    pthread_create( &t->thread, NULL, (void * (*) (void *)) ThreadFunc, t );
158#endif
159
160    return t;
161}
162   
163void
164tr_threadJoin( tr_thread * t )
165{
166    if( t != NULL )
167    {
168#ifdef __BEOS__
169        long exit;
170        wait_for_thread( t->thread, &exit );
171#elif defined(WIN32)
172        WaitForSingleObject( t->thread, INFINITE );
173        CloseHandle( t->thread );
174#else
175        pthread_join( t->thread, NULL );
176#endif
177
178        tr_dbg( "Thread '%s' joined", t->name );
179        t->name = NULL;
180        t->func = NULL;
181        tr_free( t );
182    }
183}
184
185/***
186****  LOCKS
187***/
188
189struct tr_lock
190{
191    uint32_t depth;
192#ifdef __BEOS__
193    sem_id lock;
194    thread_id lockThread;
195#elif defined(WIN32)
196    CRITICAL_SECTION lock;
197    DWORD lockThread;
198#else
199    pthread_mutex_t lock;
200    pthread_t lockThread;
201#endif
202};
203
204tr_lock*
205tr_lockNew( void )
206{
207    tr_lock * l = tr_new0( tr_lock, 1 );
208
209#ifdef __BEOS__
210    l->lock = create_sem( 1, "" );
211#elif defined(WIN32)
212    InitializeCriticalSection( &l->lock );
213#else
214    pthread_mutex_init( &l->lock, NULL );
215#endif
216
217    return l;
218}
219
220void
221tr_lockFree( tr_lock * l )
222{
223#ifdef __BEOS__
224    delete_sem( l->lock );
225#elif defined(WIN32)
226    DeleteCriticalSection( &l->lock );
227#else
228    pthread_mutex_destroy( &l->lock );
229#endif
230    tr_free( l );
231}
232
233void
234tr_lockLock( tr_lock * l )
235{
236    const tr_thread_id currentThread = tr_getCurrentThread( );
237
238    if( l->lockThread == currentThread )
239    {
240        ++l->depth;
241    }
242    else
243    {
244#ifdef __BEOS__
245        acquire_sem( l->lock );
246#elif defined(WIN32)
247        EnterCriticalSection( &l->lock );
248#else
249        pthread_mutex_lock( &l->lock );
250#endif
251        l->lockThread = currentThread;
252        l->depth = 1;
253    }
254}
255
256int
257tr_lockHave( const tr_lock * l )
258{
259    return ( l->depth > 0 )
260        && ( l->lockThread == tr_getCurrentThread() );
261}
262
263void
264tr_lockUnlock( tr_lock * l )
265{
266    assert( tr_lockHave( l ) );
267
268    if( !--l->depth )
269    {
270        l->lockThread = 0;
271#ifdef __BEOS__
272        release_sem( l->lock );
273#elif defined(WIN32)
274        LeaveCriticalSection( &l->lock );
275#else
276        pthread_mutex_unlock( &l->lock );
277#endif
278    }
279}
280
281/***
282****  COND
283***/
284
285struct tr_cond
286{
287#ifdef __BEOS__
288    sem_id sem;
289    thread_id threads[BEOS_MAX_THREADS];
290    int start, end;
291#elif defined(WIN32)
292    tr_list * events;
293    tr_lock * lock;
294#else
295    pthread_cond_t cond;
296#endif
297};
298
299#ifdef WIN32
300static DWORD getContEventTLS( void )
301{
302    static int inited = FALSE;
303    static DWORD event_tls;
304    if( !inited ) {
305        inited = TRUE;
306        event_tls = TlsAlloc();
307    }
308    return event_tls;
309}
310#endif
311
312tr_cond*
313tr_condNew( void )
314{
315    tr_cond * c = tr_new0( tr_cond, 1 );
316#ifdef __BEOS__
317    c->sem = create_sem( 1, "" );
318    c->start = 0;
319    c->end = 0;
320#elif defined(WIN32)
321    c->events = NULL;
322    c->lock = tr_lockNew( );
323#else
324    pthread_cond_init( &c->cond, NULL );
325#endif
326    return c;
327}
328
329void
330tr_condWait( tr_cond * c, tr_lock * l )
331{
332#ifdef __BEOS__
333
334    /* Keep track of that thread */
335    acquire_sem( c->sem );
336    c->threads[c->end] = find_thread( NULL );
337    c->end = ( c->end + 1 ) % BEOS_MAX_THREADS;
338    assert( c->end != c->start ); /* We hit BEOS_MAX_THREADS, arggh */
339    release_sem( c->sem );
340
341    release_sem( l->lock );
342    suspend_thread( find_thread( NULL ) ); /* Wait for signal */
343    acquire_sem( l->lock );
344
345#elif defined(WIN32)
346
347    /* get this thread's cond event */
348    DWORD key = getContEventTLS ( );
349    HANDLE hEvent = TlsGetValue( key );
350    if( !hEvent ) {
351        hEvent = CreateEvent( 0, FALSE, FALSE, 0 );
352        TlsSetValue( key, hEvent );
353    }
354
355    /* add it to the list of events waiting to be signaled */
356    tr_lockLock( c->lock );
357    tr_list_append( &c->events, hEvent );
358    tr_lockUnlock( c->lock );
359
360    /* now wait for it to be signaled */
361    tr_lockUnlock( l );
362    WaitForSingleObject( hEvent, INFINITE );
363    tr_lockLock( l );
364
365    /* remove it from the list of events waiting to be signaled */
366    tr_lockLock( c->lock );
367    tr_list_remove_data( &c->events, hEvent );
368    tr_lockUnlock( c->lock );
369
370#else
371
372    pthread_cond_wait( &c->cond, &l->lock );
373
374#endif
375}
376
377#ifdef __BEOS__
378static int condTrySignal( tr_cond * c )
379{
380    if( c->start == c->end )
381        return 1;
382
383    for( ;; )
384    {
385        thread_info info;
386        get_thread_info( c->threads[c->start], &info );
387        if( info.state == B_THREAD_SUSPENDED )
388        {
389            resume_thread( c->threads[c->start] );
390            c->start = ( c->start + 1 ) % BEOS_MAX_THREADS;
391            break;
392        }
393        /* The thread is not suspended yet, which can happen since
394         * tr_condWait does not atomically suspends after releasing
395         * the semaphore. Wait a bit and try again. */
396        snooze( 5000 );
397    }
398    return 0;
399}
400#endif
401void
402tr_condSignal( tr_cond * c )
403{
404#ifdef __BEOS__
405
406    acquire_sem( c->sem );
407    condTrySignal( c );
408    release_sem( c->sem );
409
410#elif defined(WIN32)
411
412    tr_lockLock( c->lock );
413    if( c->events != NULL )
414        SetEvent( (HANDLE)c->events->data );
415    tr_lockUnlock( c->lock );
416
417#else
418
419    pthread_cond_signal( &c->cond );
420
421#endif
422}
423
424void
425tr_condBroadcast( tr_cond * c )
426{
427#ifdef __BEOS__
428
429    acquire_sem( c->sem );
430    while( !condTrySignal( c ) );
431    release_sem( c->sem );
432
433#elif defined(WIN32)
434
435    tr_list * l;
436    tr_lockLock( c->lock );
437    for( l=c->events; l!=NULL; l=l->next )
438        SetEvent( (HANDLE)l->data );
439    tr_lockUnlock( c->lock );
440
441#else
442
443    pthread_cond_broadcast( &c->cond );
444
445#endif
446}
447
448void
449tr_condFree( tr_cond * c )
450{
451#ifdef __BEOS__
452    delete_sem( c->sem );
453#elif defined(WIN32)
454    tr_list_free( &c->events, NULL );
455    tr_lockFree( c->lock );
456#else
457    pthread_cond_destroy( &c->cond );
458#endif
459    tr_free( c );
460}
461
462
463/***
464****  PATHS
465***/
466
467#if !defined(WIN32) && !defined(__BEOS__) && !defined(__AMIGAOS4__)
468#include <pwd.h>
469#endif
470
471const char *
472tr_getHomeDirectory( void )
473{
474    static char buf[MAX_PATH_LENGTH];
475    static int init = 0;
476    const char * envHome;
477
478    if( init )
479        return buf;
480
481    envHome = getenv( "HOME" );
482    if( envHome )
483        snprintf( buf, sizeof(buf), "%s", envHome );
484    else {
485#ifdef WIN32
486        SHGetFolderPath( NULL, CSIDL_PROFILE, NULL, 0, buf );
487#elif defined(__BEOS__) || defined(__AMIGAOS4__)
488        *buf = '\0';
489#else
490        struct passwd * pw = getpwuid( getuid() );
491        endpwent();
492        if( pw != NULL )
493            snprintf( buf, sizeof(buf), "%s", pw->pw_dir );
494#endif
495    }
496
497    init = 1;
498    return buf;
499}
500
501
502static void
503tr_migrateResume( const char *oldDirectory, const char *newDirectory )
504{
505    DIR * dirh = opendir( oldDirectory );
506
507    if( dirh != NULL )
508    {
509        struct dirent * dirp;
510
511        while( ( dirp = readdir( dirh ) ) )
512        {
513            if( !strncmp( "resume.", dirp->d_name, 7 ) )
514            {
515                char o[MAX_PATH_LENGTH];
516                char n[MAX_PATH_LENGTH];
517                tr_buildPath( o, sizeof(o), oldDirectory, dirp->d_name, NULL );
518                tr_buildPath( n, sizeof(n), newDirectory, dirp->d_name, NULL );
519                rename( o, n );
520            }
521        }
522
523        closedir( dirh );
524    }
525}
526
527const char *
528tr_getPrefsDirectory( void )
529{
530    static char   buf[MAX_PATH_LENGTH];
531    static int    init = 0;
532    static size_t buflen = sizeof(buf);
533    const char* h;
534
535    if( init )
536        return buf;
537
538    h = tr_getHomeDirectory();
539#ifdef __BEOS__
540    find_directory( B_USER_SETTINGS_DIRECTORY,
541                    dev_for_path("/boot"), true, buf, buflen );
542    strcat( buf, "/Transmission" );
543#elif defined( SYS_DARWIN )
544    tr_buildPath ( buf, buflen, h,
545                  "Library", "Application Support", "Transmission", NULL );
546#elif defined(__AMIGAOS4__)
547    snprintf( buf, buflen, "PROGDIR:.transmission" );
548#elif defined(WIN32)
549    {
550        char tmp[MAX_PATH_LENGTH];
551        SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, tmp );
552        tr_buildPath( buf, sizeof(buf), tmp, "Transmission", NULL );
553        buflen = strlen( buf );
554    }
555#else
556    tr_buildPath ( buf, buflen, h, ".transmission", NULL );
557#endif
558
559    tr_mkdirp( buf, 0700 );
560    init = 1;
561
562#ifdef SYS_DARWIN
563    char old[MAX_PATH_LENGTH];
564    tr_buildPath ( old, sizeof(old), h, ".transmission", NULL );
565    tr_migrateResume( old, buf );
566    rmdir( old );
567#endif
568
569    return buf;
570}
571
572const char *
573tr_getCacheDirectory( void )
574{
575    static char buf[MAX_PATH_LENGTH];
576    static int  init = 0;
577    static const size_t buflen = sizeof(buf);
578    const char * p;
579
580    if( init )
581        return buf;
582
583    p = tr_getPrefsDirectory();
584#if defined(__BEOS__) || defined(WIN32)
585    tr_buildPath( buf, buflen, p, "Cache", NULL );
586#elif defined( SYS_DARWIN )
587    tr_buildPath( buf, buflen, tr_getHomeDirectory(),
588                  "Library", "Caches", "Transmission", NULL );
589#else
590    tr_buildPath( buf, buflen, p, "cache", NULL );
591#endif
592
593    tr_mkdirp( buf, 0700 );
594    init = 1;
595
596    if( strcmp( p, buf ) )
597        tr_migrateResume( p, buf );
598
599    return buf;
600}
601
602const char *
603tr_getTorrentsDirectory( void )
604{
605    static char buf[MAX_PATH_LENGTH];
606    static int  init = 0;
607    static const size_t buflen = sizeof(buf);
608    const char * p;
609
610    if( init )
611        return buf;
612
613    p = tr_getPrefsDirectory ();
614
615#if defined(__BEOS__) || defined(WIN32)
616    tr_buildPath( buf, buflen, p, "Torrents", NULL );
617#elif defined( SYS_DARWIN )
618    tr_buildPath( buf, buflen, p, "Torrents", NULL );
619#else
620    tr_buildPath( buf, buflen, p, "torrents", NULL );
621#endif
622
623    tr_mkdirp( buf, 0700 );
624    init = 1;
625    return buf;
626}
Note: See TracBrowser for help on using the repository browser.