source: trunk/libtransmission/platform.c @ 4043

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

fix very annoying recursive mutex + pthread cond bug reported by setatg, Waldorf, and many others

  • Property svn:keywords set to Date Rev Author Id
File size: 18.6 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 4043 2007-12-03 04:06:45Z 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  #define _XOPEN_SOURCE 500 /* needed for recursive locks. */
42  #ifndef __USE_UNIX98
43  #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
44  #endif
45  #include <pthread.h>
46#endif
47
48#include <sys/types.h>
49#include <dirent.h>
50#include <fcntl.h>
51#include <unistd.h> /* getuid getpid close */
52
53#include "transmission.h"
54#include "list.h"
55#include "net.h"
56#include "platform.h"
57#include "utils.h"
58
59/***
60****  THREADS
61***/
62
63#ifdef __BEOS__
64typedef thread_id tr_thread_id;
65#elif defined(WIN32)
66typedef DWORD tr_thread_id;
67#else
68typedef pthread_t tr_thread_id;
69#endif
70
71static tr_thread_id
72tr_getCurrentThread( void )
73{
74#ifdef __BEOS__
75    return find_thread( NULL );
76#elif defined(WIN32)
77    return GetCurrentThreadId();
78#else
79    return pthread_self( );
80#endif
81}
82
83static int
84tr_areThreadsEqual( tr_thread_id a, tr_thread_id b )
85{
86#ifdef __BEOS__
87    return a == b;
88#elif defined(WIN32)
89    return a == b;
90#else
91    return pthread_equal( a, b );
92#endif
93}
94
95struct tr_thread
96{
97    void          (* func ) ( void * );
98    void           * arg;
99    const char     * name;
100
101#ifdef __BEOS__
102    thread_id        thread;
103#elif defined(WIN32)
104    HANDLE           thread;
105    unsigned int     thread_id;
106#else
107    pthread_t        thread;
108#endif
109
110};
111
112int
113tr_amInThread ( const tr_thread * t )
114{
115    return tr_areThreadsEqual( tr_getCurrentThread(), t->thread );
116}
117
118#ifdef WIN32
119#define ThreadFuncReturnType unsigned WINAPI
120#else
121#define ThreadFuncReturnType void
122#endif
123
124static ThreadFuncReturnType
125ThreadFunc( void * _t )
126{
127    tr_thread * t = _t;
128    const char * name = t->name;
129
130#ifdef __BEOS__
131    /* This is required because on BeOS, SIGINT is sent to each thread,
132       which kills them not nicely */
133    signal( SIGINT, SIG_IGN );
134#endif
135
136    tr_dbg( "Thread '%s' started", name );
137    t->func( t->arg );
138    tr_dbg( "Thread '%s' exited", name );
139
140#ifdef WIN32
141    _endthreadex( 0 );
142    return 0;
143#endif
144}
145
146tr_thread *
147tr_threadNew( void (*func)(void *),
148              void * arg,
149              const char * name )
150{
151    tr_thread * t = tr_new0( tr_thread, 1 );
152    t->func = func;
153    t->arg  = arg;
154    t->name = name;
155
156#ifdef __BEOS__
157    t->thread = spawn_thread( (void*)ThreadFunc, name, B_NORMAL_PRIORITY, t );
158    resume_thread( t->thread );
159#elif defined(WIN32)
160    t->thread = (HANDLE) _beginthreadex( NULL, 0, &ThreadFunc, t, 0, &t->thread_id );
161#else
162    pthread_create( &t->thread, NULL, (void * (*) (void *)) ThreadFunc, t );
163#endif
164
165    return t;
166}
167   
168void
169tr_threadJoin( tr_thread * t )
170{
171    if( t != NULL )
172    {
173#ifdef __BEOS__
174        long exit;
175        wait_for_thread( t->thread, &exit );
176#elif defined(WIN32)
177        WaitForSingleObject( t->thread, INFINITE );
178        CloseHandle( t->thread );
179#else
180        pthread_join( t->thread, NULL );
181#endif
182
183        tr_dbg( "Thread '%s' joined", t->name );
184        t->name = NULL;
185        t->func = NULL;
186        tr_free( t );
187    }
188}
189
190/***
191****  LOCKS
192***/
193
194struct tr_lock
195{
196    int depth;
197#ifdef __BEOS__
198    sem_id lock;
199    thread_id lockThread;
200#elif defined(WIN32)
201    CRITICAL_SECTION lock;
202    DWORD lockThread;
203#else
204    pthread_mutex_t lock;
205    pthread_t lockThread;
206#endif
207};
208
209tr_lock*
210tr_lockNew( void )
211{
212    tr_lock * l = tr_new0( tr_lock, 1 );
213
214#ifdef __BEOS__
215    l->lock = create_sem( 1, "" );
216#elif defined(WIN32)
217    InitializeCriticalSection( &l->lock ); /* critical sections support recursion */
218#else
219    pthread_mutexattr_t attr;
220    pthread_mutexattr_init( &attr );
221    pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
222    pthread_mutex_init( &l->lock, &attr );
223#endif
224
225    return l;
226}
227
228void
229tr_lockFree( tr_lock * l )
230{
231#ifdef __BEOS__
232    delete_sem( l->lock );
233#elif defined(WIN32)
234    DeleteCriticalSection( &l->lock );
235#else
236    pthread_mutex_destroy( &l->lock );
237#endif
238    tr_free( l );
239}
240
241void
242tr_lockLock( tr_lock * l )
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    assert( l->depth >= 0 );
252    if( l->depth )
253        assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ) );
254    l->lockThread = tr_getCurrentThread( );
255    ++l->depth;
256}
257
258int
259tr_lockHave( const tr_lock * l )
260{
261    return ( l->depth > 0 )
262        && ( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ) );
263}
264
265void
266tr_lockUnlock( tr_lock * l )
267{
268    assert( l->depth > 0 );
269    assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ));
270
271    --l->depth;
272    assert( l->depth >= 0 );
273#ifdef __BEOS__
274    release_sem( l->lock );
275#elif defined(WIN32)
276    LeaveCriticalSection( &l->lock );
277#else
278    pthread_mutex_unlock( &l->lock );
279#endif
280}
281
282/***
283****  PATHS
284***/
285
286#if !defined(WIN32) && !defined(__BEOS__) && !defined(__AMIGAOS4__)
287#include <pwd.h>
288#endif
289
290const char *
291tr_getHomeDirectory( void )
292{
293    static char buf[MAX_PATH_LENGTH];
294    static int init = 0;
295    const char * envHome;
296
297    if( init )
298        return buf;
299
300    envHome = getenv( "HOME" );
301    if( envHome )
302        snprintf( buf, sizeof(buf), "%s", envHome );
303    else {
304#ifdef WIN32
305        SHGetFolderPath( NULL, CSIDL_PROFILE, NULL, 0, buf );
306#elif defined(__BEOS__) || defined(__AMIGAOS4__)
307        *buf = '\0';
308#else
309        struct passwd * pw = getpwuid( getuid() );
310        endpwent();
311        if( pw != NULL )
312            snprintf( buf, sizeof(buf), "%s", pw->pw_dir );
313#endif
314    }
315
316    init = 1;
317    return buf;
318}
319
320
321static void
322tr_migrateResume( const char *oldDirectory, const char *newDirectory )
323{
324    DIR * dirh = opendir( oldDirectory );
325
326    if( dirh != NULL )
327    {
328        struct dirent * dirp;
329
330        while( ( dirp = readdir( dirh ) ) )
331        {
332            if( !strncmp( "resume.", dirp->d_name, 7 ) )
333            {
334                char o[MAX_PATH_LENGTH];
335                char n[MAX_PATH_LENGTH];
336                tr_buildPath( o, sizeof(o), oldDirectory, dirp->d_name, NULL );
337                tr_buildPath( n, sizeof(n), newDirectory, dirp->d_name, NULL );
338                rename( o, n );
339            }
340        }
341
342        closedir( dirh );
343    }
344}
345
346const char *
347tr_getPrefsDirectory( void )
348{
349    static char   buf[MAX_PATH_LENGTH];
350    static int    init = 0;
351    static size_t buflen = sizeof(buf);
352    const char* h;
353
354    if( init )
355        return buf;
356
357    h = tr_getHomeDirectory();
358#ifdef __BEOS__
359    find_directory( B_USER_SETTINGS_DIRECTORY,
360                    dev_for_path("/boot"), true, buf, buflen );
361    strcat( buf, "/Transmission" );
362#elif defined( SYS_DARWIN )
363    tr_buildPath ( buf, buflen, h,
364                  "Library", "Application Support", "Transmission", NULL );
365#elif defined(__AMIGAOS4__)
366    snprintf( buf, buflen, "PROGDIR:.transmission" );
367#elif defined(WIN32)
368    {
369        char tmp[MAX_PATH_LENGTH];
370        SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, tmp );
371        tr_buildPath( buf, sizeof(buf), tmp, "Transmission", NULL );
372        buflen = strlen( buf );
373    }
374#else
375    tr_buildPath ( buf, buflen, h, ".transmission", NULL );
376#endif
377
378    tr_mkdirp( buf, 0700 );
379    init = 1;
380
381#ifdef SYS_DARWIN
382    char old[MAX_PATH_LENGTH];
383    tr_buildPath ( old, sizeof(old), h, ".transmission", NULL );
384    tr_migrateResume( old, buf );
385    rmdir( old );
386#endif
387
388    return buf;
389}
390
391const char *
392tr_getCacheDirectory( void )
393{
394    static char buf[MAX_PATH_LENGTH];
395    static int  init = 0;
396    static const size_t buflen = sizeof(buf);
397    const char * p;
398
399    if( init )
400        return buf;
401
402    p = tr_getPrefsDirectory();
403#if defined(__BEOS__) || defined(WIN32)
404    tr_buildPath( buf, buflen, p, "Cache", NULL );
405#elif defined( SYS_DARWIN )
406    tr_buildPath( buf, buflen, tr_getHomeDirectory(),
407                  "Library", "Caches", "Transmission", NULL );
408#else
409    tr_buildPath( buf, buflen, p, "cache", NULL );
410#endif
411
412    tr_mkdirp( buf, 0700 );
413    init = 1;
414
415    if( strcmp( p, buf ) )
416        tr_migrateResume( p, buf );
417
418    return buf;
419}
420
421const char *
422tr_getTorrentsDirectory( void )
423{
424    static char buf[MAX_PATH_LENGTH];
425    static int  init = 0;
426    static const size_t buflen = sizeof(buf);
427    const char * p;
428
429    if( init )
430        return buf;
431
432    p = tr_getPrefsDirectory ();
433
434#if defined(__BEOS__) || defined(WIN32)
435    tr_buildPath( buf, buflen, p, "Torrents", NULL );
436#elif defined( SYS_DARWIN )
437    tr_buildPath( buf, buflen, p, "Torrents", NULL );
438#else
439    tr_buildPath( buf, buflen, p, "torrents", NULL );
440#endif
441
442    tr_mkdirp( buf, 0700 );
443    init = 1;
444    return buf;
445}
446
447/***
448****  SOCKETS
449***/
450
451#ifdef BSD
452
453#include <sys/types.h>
454#include <sys/socket.h>
455#include <netinet/in.h>
456#include <arpa/inet.h>
457#include <netinet/in.h> /* struct in_addr */
458#include <sys/sysctl.h>
459#include <net/route.h>
460
461static uint8_t *
462getroute( int * buflen );
463static int
464parseroutes( uint8_t * buf, int len, struct in_addr * addr );
465
466int
467tr_getDefaultRoute( struct in_addr * addr )
468{
469    uint8_t * buf;
470    int len;
471
472    buf = getroute( &len );
473    if( NULL == buf )
474    {
475        tr_err( "failed to get default route (BSD)" );
476        return 1;
477    }
478
479    len = parseroutes( buf, len, addr );
480    free( buf );
481
482    return len;
483}
484
485#ifndef SA_SIZE
486#define ROUNDUP( a, size ) \
487    ( ( (a) & ( (size) - 1 ) ) ? ( 1 + ( (a) | ( (size) - 1 ) ) ) : (a) )
488#define SA_SIZE( sap ) \
489    ( sap->sa_len ? ROUNDUP( (sap)->sa_len, sizeof( u_long ) ) : \
490                    sizeof( u_long ) )
491#endif /* !SA_SIZE */
492#define NEXT_SA( sap ) \
493    (struct sockaddr *) ( (caddr_t) (sap) + ( SA_SIZE( (sap) ) ) )
494
495static uint8_t *
496getroute( int * buflen )
497{
498    int     mib[6];
499    size_t  len;
500    uint8_t * buf;
501
502    mib[0] = CTL_NET;
503    mib[1] = PF_ROUTE;
504    mib[2] = 0;
505    mib[3] = AF_INET;
506    mib[4] = NET_RT_FLAGS;
507    mib[5] = RTF_GATEWAY;
508
509    if( sysctl( mib, 6, NULL, &len, NULL, 0 ) )
510    {
511        if( ENOENT != errno )
512        {
513            tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
514                    strerror( sockerrno ) );
515        }
516        *buflen = 0;
517        return NULL;
518    }
519
520    buf = malloc( len );
521    if( NULL == buf )
522    {
523        *buflen = 0;
524        return NULL;
525    }
526
527    if( sysctl( mib, 6, buf, &len, NULL, 0 ) )
528    {
529        tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
530                strerror( sockerrno ) );
531        free( buf );
532        *buflen = 0;
533        return NULL;
534    }
535
536    *buflen = len;
537
538    return buf;
539}
540
541static int
542parseroutes( uint8_t * buf, int len, struct in_addr * addr )
543{
544    uint8_t            * end;
545    struct rt_msghdr   * rtm;
546    struct sockaddr    * sa;
547    struct sockaddr_in * sin;
548    int                  ii;
549    struct in_addr       dest, gw;
550
551    end = buf + len;
552    while( end > buf + sizeof( *rtm ) )
553    {
554        rtm = (struct rt_msghdr *) buf;
555        buf += rtm->rtm_msglen;
556        if( end >= buf )
557        {
558            dest.s_addr = INADDR_NONE;
559            gw.s_addr   = INADDR_NONE;
560            sa = (struct sockaddr *) ( rtm + 1 );
561
562            for( ii = 0; ii < RTAX_MAX && (uint8_t *) sa < buf; ii++ )
563            {
564                if( buf < (uint8_t *) NEXT_SA( sa ) )
565                {
566                    break;
567                }
568
569                if( rtm->rtm_addrs & ( 1 << ii ) )
570                {
571                    if( AF_INET == sa->sa_family )
572                    {
573                        sin = (struct sockaddr_in *) sa;
574                        switch( ii )
575                        {
576                            case RTAX_DST:
577                                dest = sin->sin_addr;
578                                break;
579                            case RTAX_GATEWAY:
580                                gw = sin->sin_addr;
581                                break;
582                        }
583                    }
584                    sa = NEXT_SA( sa );
585                }
586            }
587
588            if( INADDR_ANY == dest.s_addr && INADDR_NONE != gw.s_addr )
589            {
590                *addr = gw;
591                return 0;
592            }
593        }
594    }
595
596    return 1;
597}
598
599#elif defined( linux ) || defined( __linux ) || defined( __linux__ )
600
601#include <linux/types.h>
602#include <linux/netlink.h>
603#include <linux/rtnetlink.h>
604
605#define SEQNUM 195909
606
607static int
608getsock( void );
609static uint8_t *
610getroute( int fd, unsigned int * buflen );
611static int
612parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr );
613
614int
615tr_getDefaultRoute( struct in_addr * addr )
616{
617    int fd, ret;
618    unsigned int len;
619    uint8_t * buf;
620
621    ret = 1;
622    fd = getsock();
623    if( 0 <= fd )
624    {
625        while( ret )
626        {
627            buf = getroute( fd, &len );
628            if( NULL == buf )
629            {
630                break;
631            }
632            ret = parseroutes( buf, len, addr );
633            free( buf );
634        }
635        close( fd );
636    }
637
638    if( ret )
639    {
640        tr_err( "failed to get default route (Linux)" );
641    }
642
643    return ret;
644}
645
646static int
647getsock( void )
648{
649    int fd, flags;
650    struct
651    {
652        struct nlmsghdr nlh;
653        struct rtgenmsg rtg;
654    } req;
655    struct sockaddr_nl snl;
656
657    fd = socket( PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
658    if( 0 > fd )
659    {
660        tr_err( "failed to create routing socket (%s)", strerror( sockerrno ) );
661        return -1;
662    }
663
664    flags = fcntl( fd, F_GETFL );
665    if( 0 > flags || 0 > fcntl( fd, F_SETFL, O_NONBLOCK | flags ) )
666    {
667        tr_err( "failed to set socket nonblocking (%s)", strerror( sockerrno ) );
668        close( fd );
669        return -1;
670    }
671
672    memset( &snl, 0, sizeof(snl) );
673    snl.nl_family = AF_NETLINK;
674
675    memset( &req, 0, sizeof(req) );
676    req.nlh.nlmsg_len = NLMSG_LENGTH( sizeof( req.rtg ) );
677    req.nlh.nlmsg_type = RTM_GETROUTE;
678    req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
679    req.nlh.nlmsg_seq = SEQNUM;
680    req.nlh.nlmsg_pid = 0;
681    req.rtg.rtgen_family = AF_INET;
682
683    if( 0 > sendto( fd, &req, sizeof( req ), 0,
684                    (struct sockaddr *) &snl, sizeof( snl ) ) )
685    {
686        tr_err( "failed to write to routing socket (%s)", strerror( sockerrno ) );
687        close( fd );
688        return -1;
689    }
690
691    return fd;
692}
693
694static uint8_t *
695getroute( int fd, unsigned int * buflen )
696{
697    void             * buf;
698    unsigned int       len;
699    ssize_t            res;
700    struct sockaddr_nl snl;
701    socklen_t          slen;
702
703    len = 8192;
704    buf = calloc( 1, len );
705    if( NULL == buf )
706    {
707        *buflen = 0;
708        return NULL;
709    }
710
711    for( ;; )
712    {
713        slen = sizeof( snl );
714        memset( &snl, 0, slen );
715        res = recvfrom( fd, buf, len, 0, (struct sockaddr *) &snl, &slen );
716        if( 0 > res )
717        {
718            if( EAGAIN != sockerrno )
719            {
720                tr_err( "failed to read from routing socket (%s)",
721                        strerror( sockerrno ) );
722            }
723            free( buf );
724            *buflen = 0;
725            return NULL;
726        }
727        if( slen < sizeof( snl ) || AF_NETLINK != snl.nl_family )
728        {
729            tr_err( "bad address" );
730            free( buf );
731            *buflen = 0;
732            return NULL;
733        }
734
735        if( 0 == snl.nl_pid )
736        {
737            break;
738        }
739    }
740
741    *buflen = res;
742
743    return buf;
744}
745
746static int
747parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr )
748{
749    struct nlmsghdr * nlm;
750    struct nlmsgerr * nle;
751    struct rtmsg    * rtm;
752    struct rtattr   * rta;
753    int               rtalen;
754    struct in_addr    gw, dst;
755
756    nlm = ( struct nlmsghdr * ) buf;
757    while( NLMSG_OK( nlm, len ) )
758    {
759        gw.s_addr = INADDR_ANY;
760        dst.s_addr = INADDR_ANY;
761        if( NLMSG_ERROR == nlm->nlmsg_type )
762        {
763            nle = (struct nlmsgerr *) NLMSG_DATA( nlm );
764            if( NLMSG_LENGTH( NLMSG_ALIGN( sizeof( struct nlmsgerr ) ) ) >
765                nlm->nlmsg_len )
766            {
767                tr_err( "truncated netlink error" );
768            }
769            else
770            {
771                tr_err( "netlink error (%s)", strerror( nle->error ) );
772            }
773            return 1;
774        }
775        else if( RTM_NEWROUTE == nlm->nlmsg_type && SEQNUM == nlm->nlmsg_seq &&
776                 getpid() == (pid_t) nlm->nlmsg_pid &&
777                 NLMSG_LENGTH( sizeof( struct rtmsg ) ) <= nlm->nlmsg_len )
778        {
779            rtm = NLMSG_DATA( nlm );
780            rta = RTM_RTA( rtm );
781            rtalen = RTM_PAYLOAD( nlm );
782
783            while( RTA_OK( rta, rtalen ) )
784            {
785                if( sizeof( struct in_addr ) <= RTA_PAYLOAD( rta ) )
786                {
787                    switch( rta->rta_type )
788                    {
789                        case RTA_GATEWAY:
790                            memcpy( &gw, RTA_DATA( rta ), sizeof( gw ) );
791                            break;
792                        case RTA_DST:
793                            memcpy( &dst, RTA_DATA( rta ), sizeof( dst ) );
794                            break;
795                    }
796                }
797                rta = RTA_NEXT( rta, rtalen );
798            }
799        }
800
801        if( INADDR_NONE != gw.s_addr && INADDR_ANY != gw.s_addr &&
802            INADDR_ANY == dst.s_addr )
803        {
804            *addr = gw;
805            return 0;
806        }
807
808        nlm = NLMSG_NEXT( nlm, len );
809    }
810
811    return 1;
812}
813
814#else /* not BSD or Linux */
815
816int
817tr_getDefaultRoute( struct in_addr * addr UNUSED )
818{
819    tr_inf( "don't know how to get default route on this platform" );
820    return 1;
821}
822
823#endif
Note: See TracBrowser for help on using the repository browser.