source: trunk/libtransmission/platform.c @ 2197

Last change on this file since 2197 was 2197, checked in by charles, 14 years ago

building up our threading tools: implement RW locks

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