source: trunk/libtransmission/platform.c @ 1356

Last change on this file since 1356 was 1356, checked in by titer, 14 years ago

Merge io branch into trunk

  • Property svn:keywords set to Date Rev Author Id
File size: 15.3 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 1356 2007-01-14 12:00:21Z titer $
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
38static char * tr_getHomeDirectory()
39{
40    static char     homeDirectory[MAX_PATH_LENGTH];
41    static int      init = 0;
42    char          * envHome;
43    struct passwd * pw;
44
45    if( init )
46    {
47        return homeDirectory;
48    }
49
50    envHome = getenv( "HOME" );
51    if( NULL == envHome )
52    {
53        pw = getpwuid( getuid() );
54        endpwent();
55        if( NULL == pw )
56        {
57            /* XXX need to handle this case */
58            return NULL;
59        }
60        envHome = pw->pw_dir;
61    }
62
63    snprintf( homeDirectory, MAX_PATH_LENGTH, "%s", envHome );
64    init = 1;
65
66    return homeDirectory;
67}
68#endif /* !SYS_BEOS && !__AMIGAOS4__ */
69
70static void
71tr_migrateResume( const char *oldDirectory, const char *newDirectory )
72{
73    DIR * dirh;
74    struct dirent * dirp;
75    char oldFile[MAX_PATH_LENGTH];
76    char newFile[MAX_PATH_LENGTH];
77
78    if( ( dirh = opendir( oldDirectory ) ) )
79    {
80        while( ( dirp = readdir( dirh ) ) )
81        {
82            if( strncmp( "resume.", dirp->d_name, 7 ) )
83            {
84                continue;
85            }
86            snprintf( oldFile, MAX_PATH_LENGTH, "%s/%s",
87                      oldDirectory, dirp->d_name );
88            snprintf( newFile, MAX_PATH_LENGTH, "%s/%s",
89                      newDirectory, dirp->d_name );
90            rename( oldFile, newFile );
91        }
92
93        closedir( dirh );
94    }
95}
96
97char * tr_getPrefsDirectory()
98{
99    static char prefsDirectory[MAX_PATH_LENGTH];
100    static int  init = 0;
101
102    if( init )
103    {
104        return prefsDirectory;
105    }
106
107#ifdef SYS_BEOS
108        find_directory( B_USER_SETTINGS_DIRECTORY, dev_for_path("/boot"),
109                        true, prefsDirectory, MAX_PATH_LENGTH );
110        strcat( prefsDirectory, "/Transmission" );
111#elif defined( SYS_DARWIN )
112    snprintf( prefsDirectory, MAX_PATH_LENGTH,
113              "%s/Library/Caches/Transmission", tr_getHomeDirectory() );
114#elif defined(__AMIGAOS4__)
115    snprintf( prefsDirectory, MAX_PATH_LENGTH, "PROGDIR:.transmission" );
116#else
117    snprintf( prefsDirectory, MAX_PATH_LENGTH, "%s/.transmission",
118              tr_getHomeDirectory() );
119#endif
120
121    tr_mkdir( prefsDirectory );
122    init = 1;
123
124#ifdef SYS_DARWIN
125    char oldDirectory[MAX_PATH_LENGTH];
126    snprintf( oldDirectory, MAX_PATH_LENGTH, "%s/.transmission",
127              tr_getHomeDirectory() );
128    tr_migrateResume( oldDirectory, prefsDirectory );
129    rmdir( oldDirectory );
130#endif
131
132    return prefsDirectory;
133}
134
135char * tr_getCacheDirectory()
136{
137    static char cacheDirectory[MAX_PATH_LENGTH];
138    static int  init = 0;
139
140    if( init )
141    {
142        return cacheDirectory;
143    }
144
145#ifdef SYS_BEOS
146    /* XXX hey Bryan, is this fine with you? */
147    snprintf( cacheDirectory, MAX_PATH_LENGTH, "%s/Cache",
148              tr_getPrefsDirectory() );
149#elif defined( SYS_DARWIN )
150    snprintf( cacheDirectory, MAX_PATH_LENGTH, "%s",
151              tr_getPrefsDirectory() );
152#else
153    snprintf( cacheDirectory, MAX_PATH_LENGTH, "%s/cache",
154              tr_getPrefsDirectory() );
155#endif
156
157    tr_mkdir( cacheDirectory );
158    init = 1;
159
160    if( strcmp( tr_getPrefsDirectory(), cacheDirectory ) )
161    {
162        tr_migrateResume( tr_getPrefsDirectory(), cacheDirectory );
163    }
164
165    return cacheDirectory;
166}
167
168char * tr_getTorrentsDirectory()
169{
170    static char torrentsDirectory[MAX_PATH_LENGTH];
171    static int  init = 0;
172
173    if( init )
174    {
175        return torrentsDirectory;
176    }
177
178#ifdef SYS_BEOS
179    /* XXX hey Bryan, is this fine with you? */
180    snprintf( torrentsDirectory, MAX_PATH_LENGTH, "%s/Torrents",
181              tr_getPrefsDirectory() );
182#elif defined( SYS_DARWIN )
183    snprintf( torrentsDirectory, MAX_PATH_LENGTH,
184              "%s/Library/Application Support/Transmission/Torrents",
185              tr_getHomeDirectory() );
186#else
187    snprintf( torrentsDirectory, MAX_PATH_LENGTH, "%s/torrents",
188              tr_getPrefsDirectory() );
189#endif
190
191    tr_mkdir( torrentsDirectory );
192    init = 1;
193
194    return torrentsDirectory;
195}
196
197void tr_threadCreate( tr_thread_t * t, void (*func)(void *), void * arg )
198{
199#ifdef SYS_BEOS
200    *t = spawn_thread( (void *) func, "torrent-tx", B_NORMAL_PRIORITY, arg );
201    resume_thread( *t );
202#else
203    pthread_create( t, NULL, (void *) func, arg );
204#endif
205}
206
207void tr_threadJoin( tr_thread_t * t )
208{
209#ifdef SYS_BEOS
210    long exit;
211    wait_for_thread( *t, &exit );
212#else
213    pthread_join( *t, NULL );
214#endif
215}
216
217void tr_lockInit( tr_lock_t * l )
218{
219#ifdef SYS_BEOS
220    *l = create_sem( 1, "" );
221#else
222    pthread_mutex_init( l, NULL );
223#endif
224}
225
226void tr_lockClose( tr_lock_t * l )
227{
228#ifdef SYS_BEOS
229    delete_sem( *l );
230#else
231    pthread_mutex_destroy( l );
232#endif
233}
234
235
236void tr_condInit( tr_cond_t * c )
237{
238#ifdef SYS_BEOS
239    *c = -1;
240#else
241    pthread_cond_init( c, NULL );
242#endif
243}
244
245void tr_condWait( tr_cond_t * c, tr_lock_t * l )
246{
247#ifdef SYS_BEOS
248    *c = find_thread( NULL );
249    release_sem( *l );
250    suspend_thread( *c );
251    acquire_sem( *l );
252    *c = -1;
253#else
254    pthread_cond_wait( c, l );
255#endif
256}
257
258void tr_condSignal( tr_cond_t * c )
259{
260#ifdef SYS_BEOS
261    while( *c != -1 )
262    {
263        thread_info info;
264        get_thread_info( *c, &info );
265        if( info.state == B_THREAD_SUSPENDED )
266        {
267            resume_thread( *c );
268            break;
269        }
270        snooze( 5000 );
271    }
272#else
273    pthread_cond_signal( c );
274#endif
275}
276
277void tr_condClose( tr_cond_t * c )
278{
279#ifndef SYS_BEOS
280    pthread_cond_destroy( c );
281#endif
282}
283
284
285#if defined( BSD )
286
287#include <sys/sysctl.h>
288#include <net/route.h>
289
290static uint8_t *
291getroute( int * buflen );
292static int
293parseroutes( uint8_t * buf, int len, struct in_addr * addr );
294
295int
296tr_getDefaultRoute( struct in_addr * addr )
297{
298    uint8_t * buf;
299    int len;
300
301    buf = getroute( &len );
302    if( NULL == buf )
303    {
304        tr_err( "failed to get default route (BSD)" );
305        return 1;
306    }
307
308    len = parseroutes( buf, len, addr );
309    free( buf );
310
311    return len;
312}
313
314#ifndef SA_SIZE
315#define ROUNDUP( a, size ) \
316    ( ( (a) & ( (size) - 1 ) ) ? ( 1 + ( (a) | ( (size) - 1 ) ) ) : (a) )
317#define SA_SIZE( sap ) \
318    ( sap->sa_len ? ROUNDUP( (sap)->sa_len, sizeof( u_long ) ) : \
319                    sizeof( u_long ) )
320#endif /* !SA_SIZE */
321#define NEXT_SA( sap ) \
322    (struct sockaddr *) ( (caddr_t) (sap) + ( SA_SIZE( (sap) ) ) )
323
324static uint8_t *
325getroute( int * buflen )
326{
327    int     mib[6];
328    size_t  len;
329    uint8_t * buf;
330
331    mib[0] = CTL_NET;
332    mib[1] = PF_ROUTE;
333    mib[2] = 0;
334    mib[3] = AF_INET;
335    mib[4] = NET_RT_FLAGS;
336    mib[5] = RTF_GATEWAY;
337
338    if( sysctl( mib, 6, NULL, &len, NULL, 0 ) )
339    {
340        if( ENOENT != errno )
341        {
342            tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
343                    strerror( errno ) );
344        }
345        *buflen = 0;
346        return NULL;
347    }
348
349    buf = malloc( len );
350    if( NULL == buf )
351    {
352        *buflen = 0;
353        return NULL;
354    }
355
356    if( sysctl( mib, 6, buf, &len, NULL, 0 ) )
357    {
358        tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
359                strerror( errno ) );
360        free( buf );
361        *buflen = 0;
362        return NULL;
363    }
364
365    *buflen = len;
366
367    return buf;
368}
369
370static int
371parseroutes( uint8_t * buf, int len, struct in_addr * addr )
372{
373    uint8_t            * end;
374    struct rt_msghdr   * rtm;
375    struct sockaddr    * sa;
376    struct sockaddr_in * sin;
377    int                  ii;
378    struct in_addr       dest, gw;
379
380    end = buf + len;
381    while( end > buf + sizeof( *rtm ) )
382    {
383        rtm = (struct rt_msghdr *) buf;
384        buf += rtm->rtm_msglen;
385        if( end >= buf )
386        {
387            dest.s_addr = INADDR_NONE;
388            gw.s_addr   = INADDR_NONE;
389            sa = (struct sockaddr *) ( rtm + 1 );
390
391            for( ii = 0; ii < RTAX_MAX && (uint8_t *) sa < buf; ii++ )
392            {
393                if( buf < (uint8_t *) NEXT_SA( sa ) )
394                {
395                    break;
396                }
397
398                if( rtm->rtm_addrs & ( 1 << ii ) )
399                {
400                    if( AF_INET == sa->sa_family )
401                    {
402                        sin = (struct sockaddr_in *) sa;
403                        switch( ii )
404                        {
405                            case RTAX_DST:
406                                dest = sin->sin_addr;
407                                break;
408                            case RTAX_GATEWAY:
409                                gw = sin->sin_addr;
410                                break;
411                        }
412                    }
413                    sa = NEXT_SA( sa );
414                }
415            }
416
417            if( INADDR_ANY == dest.s_addr && INADDR_NONE != gw.s_addr )
418            {
419                *addr = gw;
420                return 0;
421            }
422        }
423    }
424
425    return 1;
426}
427
428#elif defined( linux ) || defined( __linux ) || defined( __linux__ )
429
430#include <linux/types.h>
431#include <linux/netlink.h>
432#include <linux/rtnetlink.h>
433
434#define SEQNUM 195909
435
436static int
437getsock( void );
438static uint8_t *
439getroute( int fd, unsigned int * buflen );
440static int
441parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr );
442
443int
444tr_getDefaultRoute( struct in_addr * addr )
445{
446    int fd, ret;
447    unsigned int len;
448    uint8_t * buf;
449
450    ret = 1;
451    fd = getsock();
452    if( 0 <= fd )
453    {
454        while( ret )
455        {
456            buf = getroute( fd, &len );
457            if( NULL == buf )
458            {
459                break;
460            }
461            ret = parseroutes( buf, len, addr );
462            free( buf );
463        }
464        close( fd );
465    }
466
467    if( ret )
468    {
469        tr_err( "failed to get default route (Linux)" );
470    }
471
472    return ret;
473}
474
475static int
476getsock( void )
477{
478    int fd, flags;
479    struct
480    {
481        struct nlmsghdr nlh;
482        struct rtgenmsg rtg;
483    } req;
484    struct sockaddr_nl snl;
485
486    fd = socket( PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
487    if( 0 > fd )
488    {
489        tr_err( "failed to create routing socket (%s)", strerror( errno ) );
490        return -1;
491    }
492
493    flags = fcntl( fd, F_GETFL );
494    if( 0 > flags || 0 > fcntl( fd, F_SETFL, O_NONBLOCK | flags ) )
495    {
496        tr_err( "failed to set socket nonblocking (%s)", strerror( errno ) );
497        close( fd );
498        return -1;
499    }
500
501    bzero( &snl, sizeof( snl ) );
502    snl.nl_family = AF_NETLINK;
503
504    bzero( &req, sizeof( req ) );
505    req.nlh.nlmsg_len = NLMSG_LENGTH( sizeof( req.rtg ) );
506    req.nlh.nlmsg_type = RTM_GETROUTE;
507    req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
508    req.nlh.nlmsg_seq = SEQNUM;
509    req.nlh.nlmsg_pid = 0;
510    req.rtg.rtgen_family = AF_INET;
511
512    if( 0 > sendto( fd, &req, sizeof( req ), 0,
513                    (struct sockaddr *) &snl, sizeof( snl ) ) )
514    {
515        tr_err( "failed to write to routing socket (%s)", strerror( errno ) );
516        close( fd );
517        return -1;
518    }
519
520    return fd;
521}
522
523static uint8_t *
524getroute( int fd, unsigned int * buflen )
525{
526    void             * buf;
527    unsigned int       len;
528    ssize_t            res;
529    struct sockaddr_nl snl;
530    socklen_t          slen;
531
532    len = 8192;
533    buf = calloc( 1, len );
534    if( NULL == buf )
535    {
536        *buflen = 0;
537        return NULL;
538    }
539
540    for( ;; )
541    {
542        bzero( &snl, sizeof( snl ) );
543        slen = sizeof( snl );
544        res = recvfrom( fd, buf, len, 0, (struct sockaddr *) &snl, &slen );
545        if( 0 > res )
546        {
547            if( EAGAIN != errno )
548            {
549                tr_err( "failed to read from routing socket (%s)",
550                        strerror( errno ) );
551            }
552            free( buf );
553            *buflen = 0;
554            return NULL;
555        }
556        if( slen < sizeof( snl ) || AF_NETLINK != snl.nl_family )
557        {
558            tr_err( "bad address" );
559            free( buf );
560            *buflen = 0;
561            return NULL;
562        }
563
564        if( 0 == snl.nl_pid )
565        {
566            break;
567        }
568    }
569
570    *buflen = res;
571
572    return buf;
573}
574
575static int
576parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr )
577{
578    struct nlmsghdr * nlm;
579    struct nlmsgerr * nle;
580    struct rtmsg    * rtm;
581    struct rtattr   * rta;
582    int               rtalen;
583    struct in_addr    gw, dst;
584
585    nlm = ( struct nlmsghdr * ) buf;
586    while( NLMSG_OK( nlm, len ) )
587    {
588        gw.s_addr = INADDR_ANY;
589        dst.s_addr = INADDR_ANY;
590        if( NLMSG_ERROR == nlm->nlmsg_type )
591        {
592            nle = (struct nlmsgerr *) NLMSG_DATA( nlm );
593            if( NLMSG_LENGTH( NLMSG_ALIGN( sizeof( struct nlmsgerr ) ) ) >
594                nlm->nlmsg_len )
595            {
596                tr_err( "truncated netlink error" );
597            }
598            else
599            {
600                tr_err( "netlink error (%s)", strerror( nle->error ) );
601            }
602            return 1;
603        }
604        else if( RTM_NEWROUTE == nlm->nlmsg_type && SEQNUM == nlm->nlmsg_seq &&
605                 getpid() == (pid_t) nlm->nlmsg_pid &&
606                 NLMSG_LENGTH( sizeof( struct rtmsg ) ) <= nlm->nlmsg_len )
607        {
608            rtm = NLMSG_DATA( nlm );
609            rta = RTM_RTA( rtm );
610            rtalen = RTM_PAYLOAD( nlm );
611
612            while( RTA_OK( rta, rtalen ) )
613            {
614                if( sizeof( struct in_addr ) <= RTA_PAYLOAD( rta ) )
615                {
616                    switch( rta->rta_type )
617                    {
618                        case RTA_GATEWAY:
619                            memcpy( &gw, RTA_DATA( rta ), sizeof( gw ) );
620                            break;
621                        case RTA_DST:
622                            memcpy( &dst, RTA_DATA( rta ), sizeof( dst ) );
623                            break;
624                    }
625                }
626                rta = RTA_NEXT( rta, rtalen );
627            }
628        }
629
630        if( INADDR_NONE != gw.s_addr && INADDR_ANY != gw.s_addr &&
631            INADDR_ANY == dst.s_addr )
632        {
633            *addr = gw;
634            return 0;
635        }
636
637        nlm = NLMSG_NEXT( nlm, len );
638    }
639
640    return 1;
641}
642
643#else /* not BSD or Linux */
644
645int
646tr_getDefaultRoute( struct in_addr * addr UNUSED )
647{
648    tr_inf( "don't know how to get default route on this platform" );
649    return 1;
650}
651
652#endif
Note: See TracBrowser for help on using the repository browser.