source: trunk/libtransmission/platform.c @ 2184

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

misc cleanup.. ansification; fix my own typos, etc...

  • Property svn:keywords set to Date Rev Author Id
File size: 15.8 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 2184 2007-06-22 04:30:39Z 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, void (*func)(void *), void * arg,
216                      char * name )
217{
218    t->func = func;
219    t->arg  = arg;
220    t->name = strdup( name );
221#ifdef SYS_BEOS
222    t->thread = spawn_thread( (void *) ThreadFunc, name,
223                              B_NORMAL_PRIORITY, t );
224    resume_thread( t->thread );
225#else
226    pthread_create( &t->thread, NULL, (void *) ThreadFunc, t );
227#endif
228}
229
230const tr_thread_t THREAD_EMPTY = { NULL, NULL, NULL, 0 };
231
232void tr_threadJoin( tr_thread_t * t )
233{
234    if (t->func != NULL)
235    {
236#ifdef SYS_BEOS
237        long exit;
238        wait_for_thread( t->thread, &exit );
239#else
240        pthread_join( t->thread, NULL );
241#endif
242        tr_dbg( "Thread '%s' joined", t->name );
243        free( t->name );
244        t->name = NULL;
245        t->func = NULL;
246    }
247}
248
249void tr_lockInit( tr_lock_t * l )
250{
251#ifdef SYS_BEOS
252    *l = create_sem( 1, "" );
253#else
254    pthread_mutex_init( l, NULL );
255#endif
256}
257
258void tr_lockClose( tr_lock_t * l )
259{
260#ifdef SYS_BEOS
261    delete_sem( *l );
262#else
263    pthread_mutex_destroy( l );
264#endif
265}
266
267void tr_lockLock( tr_lock_t * l )
268{
269#ifdef SYS_BEOS
270    acquire_sem( *l );
271#else
272    pthread_mutex_lock( l );
273#endif
274}
275
276void tr_lockUnlock( tr_lock_t * l )
277{
278#ifdef SYS_BEOS
279    release_sem( *l );
280#else
281    pthread_mutex_unlock( l );
282#endif
283}
284
285
286void tr_condInit( tr_cond_t * c )
287{
288#ifdef SYS_BEOS
289    *c = -1;
290#else
291    pthread_cond_init( c, NULL );
292#endif
293}
294
295void tr_condWait( tr_cond_t * c, tr_lock_t * l )
296{
297#ifdef SYS_BEOS
298    *c = find_thread( NULL );
299    release_sem( *l );
300    suspend_thread( *c );
301    acquire_sem( *l );
302    *c = -1;
303#else
304    pthread_cond_wait( c, l );
305#endif
306}
307
308void tr_condSignal( tr_cond_t * c )
309{
310#ifdef SYS_BEOS
311    while( *c != -1 )
312    {
313        thread_info info;
314        get_thread_info( *c, &info );
315        if( info.state == B_THREAD_SUSPENDED )
316        {
317            resume_thread( *c );
318            break;
319        }
320        snooze( 5000 );
321    }
322#else
323    pthread_cond_signal( c );
324#endif
325}
326
327void tr_condClose( tr_cond_t * c )
328{
329#ifdef SYS_BEOS
330    *c = -1; /* Shut up gcc */
331#else
332    pthread_cond_destroy( c );
333#endif
334}
335
336
337#if defined( BSD )
338
339#include <sys/sysctl.h>
340#include <net/route.h>
341
342static uint8_t *
343getroute( int * buflen );
344static int
345parseroutes( uint8_t * buf, int len, struct in_addr * addr );
346
347int
348tr_getDefaultRoute( struct in_addr * addr )
349{
350    uint8_t * buf;
351    int len;
352
353    buf = getroute( &len );
354    if( NULL == buf )
355    {
356        tr_err( "failed to get default route (BSD)" );
357        return 1;
358    }
359
360    len = parseroutes( buf, len, addr );
361    free( buf );
362
363    return len;
364}
365
366#ifndef SA_SIZE
367#define ROUNDUP( a, size ) \
368    ( ( (a) & ( (size) - 1 ) ) ? ( 1 + ( (a) | ( (size) - 1 ) ) ) : (a) )
369#define SA_SIZE( sap ) \
370    ( sap->sa_len ? ROUNDUP( (sap)->sa_len, sizeof( u_long ) ) : \
371                    sizeof( u_long ) )
372#endif /* !SA_SIZE */
373#define NEXT_SA( sap ) \
374    (struct sockaddr *) ( (caddr_t) (sap) + ( SA_SIZE( (sap) ) ) )
375
376static uint8_t *
377getroute( int * buflen )
378{
379    int     mib[6];
380    size_t  len;
381    uint8_t * buf;
382
383    mib[0] = CTL_NET;
384    mib[1] = PF_ROUTE;
385    mib[2] = 0;
386    mib[3] = AF_INET;
387    mib[4] = NET_RT_FLAGS;
388    mib[5] = RTF_GATEWAY;
389
390    if( sysctl( mib, 6, NULL, &len, NULL, 0 ) )
391    {
392        if( ENOENT != errno )
393        {
394            tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
395                    strerror( errno ) );
396        }
397        *buflen = 0;
398        return NULL;
399    }
400
401    buf = malloc( len );
402    if( NULL == buf )
403    {
404        *buflen = 0;
405        return NULL;
406    }
407
408    if( sysctl( mib, 6, buf, &len, NULL, 0 ) )
409    {
410        tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
411                strerror( errno ) );
412        free( buf );
413        *buflen = 0;
414        return NULL;
415    }
416
417    *buflen = len;
418
419    return buf;
420}
421
422static int
423parseroutes( uint8_t * buf, int len, struct in_addr * addr )
424{
425    uint8_t            * end;
426    struct rt_msghdr   * rtm;
427    struct sockaddr    * sa;
428    struct sockaddr_in * sin;
429    int                  ii;
430    struct in_addr       dest, gw;
431
432    end = buf + len;
433    while( end > buf + sizeof( *rtm ) )
434    {
435        rtm = (struct rt_msghdr *) buf;
436        buf += rtm->rtm_msglen;
437        if( end >= buf )
438        {
439            dest.s_addr = INADDR_NONE;
440            gw.s_addr   = INADDR_NONE;
441            sa = (struct sockaddr *) ( rtm + 1 );
442
443            for( ii = 0; ii < RTAX_MAX && (uint8_t *) sa < buf; ii++ )
444            {
445                if( buf < (uint8_t *) NEXT_SA( sa ) )
446                {
447                    break;
448                }
449
450                if( rtm->rtm_addrs & ( 1 << ii ) )
451                {
452                    if( AF_INET == sa->sa_family )
453                    {
454                        sin = (struct sockaddr_in *) sa;
455                        switch( ii )
456                        {
457                            case RTAX_DST:
458                                dest = sin->sin_addr;
459                                break;
460                            case RTAX_GATEWAY:
461                                gw = sin->sin_addr;
462                                break;
463                        }
464                    }
465                    sa = NEXT_SA( sa );
466                }
467            }
468
469            if( INADDR_ANY == dest.s_addr && INADDR_NONE != gw.s_addr )
470            {
471                *addr = gw;
472                return 0;
473            }
474        }
475    }
476
477    return 1;
478}
479
480#elif defined( linux ) || defined( __linux ) || defined( __linux__ )
481
482#include <linux/types.h>
483#include <linux/netlink.h>
484#include <linux/rtnetlink.h>
485
486#define SEQNUM 195909
487
488static int
489getsock( void );
490static uint8_t *
491getroute( int fd, unsigned int * buflen );
492static int
493parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr );
494
495int
496tr_getDefaultRoute( struct in_addr * addr )
497{
498    int fd, ret;
499    unsigned int len;
500    uint8_t * buf;
501
502    ret = 1;
503    fd = getsock();
504    if( 0 <= fd )
505    {
506        while( ret )
507        {
508            buf = getroute( fd, &len );
509            if( NULL == buf )
510            {
511                break;
512            }
513            ret = parseroutes( buf, len, addr );
514            free( buf );
515        }
516        close( fd );
517    }
518
519    if( ret )
520    {
521        tr_err( "failed to get default route (Linux)" );
522    }
523
524    return ret;
525}
526
527static int
528getsock( void )
529{
530    int fd, flags;
531    struct
532    {
533        struct nlmsghdr nlh;
534        struct rtgenmsg rtg;
535    } req;
536    struct sockaddr_nl snl;
537
538    fd = socket( PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
539    if( 0 > fd )
540    {
541        tr_err( "failed to create routing socket (%s)", strerror( errno ) );
542        return -1;
543    }
544
545    flags = fcntl( fd, F_GETFL );
546    if( 0 > flags || 0 > fcntl( fd, F_SETFL, O_NONBLOCK | flags ) )
547    {
548        tr_err( "failed to set socket nonblocking (%s)", strerror( errno ) );
549        close( fd );
550        return -1;
551    }
552
553    bzero( &snl, sizeof( snl ) );
554    snl.nl_family = AF_NETLINK;
555
556    bzero( &req, sizeof( req ) );
557    req.nlh.nlmsg_len = NLMSG_LENGTH( sizeof( req.rtg ) );
558    req.nlh.nlmsg_type = RTM_GETROUTE;
559    req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
560    req.nlh.nlmsg_seq = SEQNUM;
561    req.nlh.nlmsg_pid = 0;
562    req.rtg.rtgen_family = AF_INET;
563
564    if( 0 > sendto( fd, &req, sizeof( req ), 0,
565                    (struct sockaddr *) &snl, sizeof( snl ) ) )
566    {
567        tr_err( "failed to write to routing socket (%s)", strerror( errno ) );
568        close( fd );
569        return -1;
570    }
571
572    return fd;
573}
574
575static uint8_t *
576getroute( int fd, unsigned int * buflen )
577{
578    void             * buf;
579    unsigned int       len;
580    ssize_t            res;
581    struct sockaddr_nl snl;
582    socklen_t          slen;
583
584    len = 8192;
585    buf = calloc( 1, len );
586    if( NULL == buf )
587    {
588        *buflen = 0;
589        return NULL;
590    }
591
592    for( ;; )
593    {
594        bzero( &snl, sizeof( snl ) );
595        slen = sizeof( snl );
596        res = recvfrom( fd, buf, len, 0, (struct sockaddr *) &snl, &slen );
597        if( 0 > res )
598        {
599            if( EAGAIN != errno )
600            {
601                tr_err( "failed to read from routing socket (%s)",
602                        strerror( errno ) );
603            }
604            free( buf );
605            *buflen = 0;
606            return NULL;
607        }
608        if( slen < sizeof( snl ) || AF_NETLINK != snl.nl_family )
609        {
610            tr_err( "bad address" );
611            free( buf );
612            *buflen = 0;
613            return NULL;
614        }
615
616        if( 0 == snl.nl_pid )
617        {
618            break;
619        }
620    }
621
622    *buflen = res;
623
624    return buf;
625}
626
627static int
628parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr )
629{
630    struct nlmsghdr * nlm;
631    struct nlmsgerr * nle;
632    struct rtmsg    * rtm;
633    struct rtattr   * rta;
634    int               rtalen;
635    struct in_addr    gw, dst;
636
637    nlm = ( struct nlmsghdr * ) buf;
638    while( NLMSG_OK( nlm, len ) )
639    {
640        gw.s_addr = INADDR_ANY;
641        dst.s_addr = INADDR_ANY;
642        if( NLMSG_ERROR == nlm->nlmsg_type )
643        {
644            nle = (struct nlmsgerr *) NLMSG_DATA( nlm );
645            if( NLMSG_LENGTH( NLMSG_ALIGN( sizeof( struct nlmsgerr ) ) ) >
646                nlm->nlmsg_len )
647            {
648                tr_err( "truncated netlink error" );
649            }
650            else
651            {
652                tr_err( "netlink error (%s)", strerror( nle->error ) );
653            }
654            return 1;
655        }
656        else if( RTM_NEWROUTE == nlm->nlmsg_type && SEQNUM == nlm->nlmsg_seq &&
657                 getpid() == (pid_t) nlm->nlmsg_pid &&
658                 NLMSG_LENGTH( sizeof( struct rtmsg ) ) <= nlm->nlmsg_len )
659        {
660            rtm = NLMSG_DATA( nlm );
661            rta = RTM_RTA( rtm );
662            rtalen = RTM_PAYLOAD( nlm );
663
664            while( RTA_OK( rta, rtalen ) )
665            {
666                if( sizeof( struct in_addr ) <= RTA_PAYLOAD( rta ) )
667                {
668                    switch( rta->rta_type )
669                    {
670                        case RTA_GATEWAY:
671                            memcpy( &gw, RTA_DATA( rta ), sizeof( gw ) );
672                            break;
673                        case RTA_DST:
674                            memcpy( &dst, RTA_DATA( rta ), sizeof( dst ) );
675                            break;
676                    }
677                }
678                rta = RTA_NEXT( rta, rtalen );
679            }
680        }
681
682        if( INADDR_NONE != gw.s_addr && INADDR_ANY != gw.s_addr &&
683            INADDR_ANY == dst.s_addr )
684        {
685            *addr = gw;
686            return 0;
687        }
688
689        nlm = NLMSG_NEXT( nlm, len );
690    }
691
692    return 1;
693}
694
695#else /* not BSD or Linux */
696
697int
698tr_getDefaultRoute( struct in_addr * addr UNUSED )
699{
700    tr_inf( "don't know how to get default route on this platform" );
701    return 1;
702}
703
704#endif
Note: See TracBrowser for help on using the repository browser.