source: trunk/libtransmission/platform.c @ 1401

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

clean up

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