source: trunk/libtransmission/platform.c @ 920

Last change on this file since 920 was 920, checked in by joshe, 15 years ago

Merge nat-traversal branch to trunk.

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