source: trunk/libtransmission/platform.c @ 1998

Last change on this file since 1998 was 1998, checked in by livings124, 14 years ago

patches from Charles Kerr to update the Inspector in GTK and fix some memory leaks

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