source: trunk/libtransmission/platform.c @ 4001

Last change on this file since 4001 was 4001, checked in by charles, 13 years ago

housekeeping. revert r3984 at tiennou's request

  • Property svn:keywords set to Date Rev Author Id
File size: 21.7 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 4001 2007-11-27 15:39:59Z 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#include <assert.h>
26#include <errno.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30
31#ifdef __BEOS__
32  #include <signal.h> 
33  #include <fs_info.h>
34  #include <FindDirectory.h>
35  #include <kernel/OS.h>
36  #define BEOS_MAX_THREADS 256
37#elif defined(WIN32)
38  #include <windows.h>
39  #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_PROFILE */
40#else
41  #include <pthread.h>
42#endif
43
44#include <sys/types.h>
45#include <dirent.h>
46#include <fcntl.h>
47#include <unistd.h> /* getuid getpid close */
48
49#include "transmission.h"
50#include "list.h"
51#include "net.h"
52#include "platform.h"
53#include "utils.h"
54
55/***
56****  THREADS
57***/
58
59#ifdef __BEOS__
60typedef thread_id tr_thread_id;
61#elif defined(WIN32)
62typedef DWORD tr_thread_id;
63#else
64typedef pthread_t tr_thread_id;
65#endif
66
67static tr_thread_id
68tr_getCurrentThread( void )
69{
70#ifdef __BEOS__
71    return find_thread( NULL );
72#elif defined(WIN32)
73    return GetCurrentThreadId();
74#else
75    return pthread_self( );
76#endif
77}
78
79static int
80tr_areThreadsEqual( tr_thread_id a, tr_thread_id b )
81{
82#ifdef __BEOS__
83    return a == b;
84#elif defined(WIN32)
85    return a == b;
86#else
87    return pthread_equal( a, b );
88#endif
89}
90
91struct tr_thread
92{
93    void          (* func ) ( void * );
94    void           * arg;
95    const char     * name;
96
97#ifdef __BEOS__
98    thread_id        thread;
99#elif defined(WIN32)
100    HANDLE           thread;
101    unsigned int     thread_id;
102#else
103    pthread_t        thread;
104#endif
105
106};
107
108int
109tr_amInThread ( const tr_thread * t )
110{
111    return tr_areThreadsEqual( tr_getCurrentThread(), t->thread );
112}
113
114#ifdef WIN32
115#define ThreadFuncReturnType unsigned WINAPI
116#else
117#define ThreadFuncReturnType void
118#endif
119
120static ThreadFuncReturnType
121ThreadFunc( void * _t )
122{
123    tr_thread * t = _t;
124    const char * name = t->name;
125
126#ifdef __BEOS__
127    /* This is required because on BeOS, SIGINT is sent to each thread,
128       which kills them not nicely */
129    signal( SIGINT, SIG_IGN );
130#endif
131
132    tr_dbg( "Thread '%s' started", name );
133    t->func( t->arg );
134    tr_dbg( "Thread '%s' exited", name );
135
136#ifdef WIN32
137    _endthreadex( 0 );
138    return 0;
139#endif
140}
141
142tr_thread *
143tr_threadNew( void (*func)(void *),
144              void * arg,
145              const char * name )
146{
147    tr_thread * t = tr_new0( tr_thread, 1 );
148    t->func = func;
149    t->arg  = arg;
150    t->name = name;
151
152#ifdef __BEOS__
153    t->thread = spawn_thread( (void*)ThreadFunc, name, B_NORMAL_PRIORITY, t );
154    resume_thread( t->thread );
155#elif defined(WIN32)
156    t->thread = (HANDLE) _beginthreadex( NULL, 0, &ThreadFunc, t, 0, &t->thread_id );
157#else
158    pthread_create( &t->thread, NULL, (void * (*) (void *)) ThreadFunc, t );
159#endif
160
161    return t;
162}
163   
164void
165tr_threadJoin( tr_thread * t )
166{
167    if( t != NULL )
168    {
169#ifdef __BEOS__
170        long exit;
171        wait_for_thread( t->thread, &exit );
172#elif defined(WIN32)
173        WaitForSingleObject( t->thread, INFINITE );
174        CloseHandle( t->thread );
175#else
176        pthread_join( t->thread, NULL );
177#endif
178
179        tr_dbg( "Thread '%s' joined", t->name );
180        t->name = NULL;
181        t->func = NULL;
182        tr_free( t );
183    }
184}
185
186/***
187****  LOCKS
188***/
189
190struct tr_lock
191{
192    uint32_t depth;
193#ifdef __BEOS__
194    sem_id lock;
195    thread_id lockThread;
196#elif defined(WIN32)
197    CRITICAL_SECTION lock;
198    DWORD lockThread;
199#else
200    pthread_mutex_t lock;
201    pthread_t lockThread;
202#endif
203};
204
205tr_lock*
206tr_lockNew( void )
207{
208    tr_lock * l = tr_new0( tr_lock, 1 );
209
210#ifdef __BEOS__
211    l->lock = create_sem( 1, "" );
212#elif defined(WIN32)
213    InitializeCriticalSection( &l->lock );
214#else
215    pthread_mutex_init( &l->lock, NULL );
216#endif
217
218    return l;
219}
220
221void
222tr_lockFree( tr_lock * l )
223{
224#ifdef __BEOS__
225    delete_sem( l->lock );
226#elif defined(WIN32)
227    DeleteCriticalSection( &l->lock );
228#else
229    pthread_mutex_destroy( &l->lock );
230#endif
231    tr_free( l );
232}
233
234void
235tr_lockLock( tr_lock * l )
236{
237    const tr_thread_id currentThread = tr_getCurrentThread( );
238
239    if( l->lockThread == currentThread )
240    {
241        ++l->depth;
242    }
243    else
244    {
245#ifdef __BEOS__
246        acquire_sem( l->lock );
247#elif defined(WIN32)
248        EnterCriticalSection( &l->lock );
249#else
250        pthread_mutex_lock( &l->lock );
251#endif
252        l->lockThread = currentThread;
253        l->depth = 1;
254    }
255}
256
257int
258tr_lockHave( const tr_lock * l )
259{
260    return ( l->depth > 0 )
261        && ( l->lockThread == tr_getCurrentThread() );
262}
263
264void
265tr_lockUnlock( tr_lock * l )
266{
267    assert( tr_lockHave( l ) );
268
269    if( !--l->depth )
270    {
271        l->lockThread = 0;
272#ifdef __BEOS__
273        release_sem( l->lock );
274#elif defined(WIN32)
275        LeaveCriticalSection( &l->lock );
276#else
277        pthread_mutex_unlock( &l->lock );
278#endif
279    }
280}
281
282/***
283****  COND
284***/
285
286struct tr_cond
287{
288#ifdef __BEOS__
289    sem_id sem;
290    thread_id threads[BEOS_MAX_THREADS];
291    int start, end;
292#elif defined(WIN32)
293    tr_list * events;
294    tr_lock * lock;
295#else
296    pthread_cond_t cond;
297#endif
298};
299
300#ifdef WIN32
301static DWORD getContEventTLS( void )
302{
303    static int inited = FALSE;
304    static DWORD event_tls;
305    if( !inited ) {
306        inited = TRUE;
307        event_tls = TlsAlloc();
308    }
309    return event_tls;
310}
311#endif
312
313tr_cond*
314tr_condNew( void )
315{
316    tr_cond * c = tr_new0( tr_cond, 1 );
317#ifdef __BEOS__
318    c->sem = create_sem( 1, "" );
319    c->start = 0;
320    c->end = 0;
321#elif defined(WIN32)
322    c->events = NULL;
323    c->lock = tr_lockNew( );
324#else
325    pthread_cond_init( &c->cond, NULL );
326#endif
327    return c;
328}
329
330void
331tr_condWait( tr_cond * c, tr_lock * l )
332{
333#ifdef __BEOS__
334
335    /* Keep track of that thread */
336    acquire_sem( c->sem );
337    c->threads[c->end] = find_thread( NULL );
338    c->end = ( c->end + 1 ) % BEOS_MAX_THREADS;
339    assert( c->end != c->start ); /* We hit BEOS_MAX_THREADS, arggh */
340    release_sem( c->sem );
341
342    release_sem( l->lock );
343    suspend_thread( find_thread( NULL ) ); /* Wait for signal */
344    acquire_sem( l->lock );
345
346#elif defined(WIN32)
347
348    /* get this thread's cond event */
349    DWORD key = getContEventTLS ( );
350    HANDLE hEvent = TlsGetValue( key );
351    if( !hEvent ) {
352        hEvent = CreateEvent( 0, FALSE, FALSE, 0 );
353        TlsSetValue( key, hEvent );
354    }
355
356    /* add it to the list of events waiting to be signaled */
357    tr_lockLock( c->lock );
358    tr_list_append( &c->events, hEvent );
359    tr_lockUnlock( c->lock );
360
361    /* now wait for it to be signaled */
362    tr_lockUnlock( l );
363    WaitForSingleObject( hEvent, INFINITE );
364    tr_lockLock( l );
365
366    /* remove it from the list of events waiting to be signaled */
367    tr_lockLock( c->lock );
368    tr_list_remove_data( &c->events, hEvent );
369    tr_lockUnlock( c->lock );
370
371#else
372
373    pthread_cond_wait( &c->cond, &l->lock );
374
375#endif
376}
377
378#ifdef __BEOS__
379static int condTrySignal( tr_cond * c )
380{
381    if( c->start == c->end )
382        return 1;
383
384    for( ;; )
385    {
386        thread_info info;
387        get_thread_info( c->threads[c->start], &info );
388        if( info.state == B_THREAD_SUSPENDED )
389        {
390            resume_thread( c->threads[c->start] );
391            c->start = ( c->start + 1 ) % BEOS_MAX_THREADS;
392            break;
393        }
394        /* The thread is not suspended yet, which can happen since
395         * tr_condWait does not atomically suspends after releasing
396         * the semaphore. Wait a bit and try again. */
397        snooze( 5000 );
398    }
399    return 0;
400}
401#endif
402void
403tr_condSignal( tr_cond * c )
404{
405#ifdef __BEOS__
406
407    acquire_sem( c->sem );
408    condTrySignal( c );
409    release_sem( c->sem );
410
411#elif defined(WIN32)
412
413    tr_lockLock( c->lock );
414    if( c->events != NULL )
415        SetEvent( (HANDLE)c->events->data );
416    tr_lockUnlock( c->lock );
417
418#else
419
420    pthread_cond_signal( &c->cond );
421
422#endif
423}
424
425void
426tr_condBroadcast( tr_cond * c )
427{
428#ifdef __BEOS__
429
430    acquire_sem( c->sem );
431    while( !condTrySignal( c ) );
432    release_sem( c->sem );
433
434#elif defined(WIN32)
435
436    tr_list * l;
437    tr_lockLock( c->lock );
438    for( l=c->events; l!=NULL; l=l->next )
439        SetEvent( (HANDLE)l->data );
440    tr_lockUnlock( c->lock );
441
442#else
443
444    pthread_cond_broadcast( &c->cond );
445
446#endif
447}
448
449void
450tr_condFree( tr_cond * c )
451{
452#ifdef __BEOS__
453    delete_sem( c->sem );
454#elif defined(WIN32)
455    tr_list_free( &c->events, NULL );
456    tr_lockFree( c->lock );
457#else
458    pthread_cond_destroy( &c->cond );
459#endif
460    tr_free( c );
461}
462
463
464/***
465****  PATHS
466***/
467
468#if !defined(WIN32) && !defined(__BEOS__) && !defined(__AMIGAOS4__)
469#include <pwd.h>
470#endif
471
472const char *
473tr_getHomeDirectory( void )
474{
475    static char buf[MAX_PATH_LENGTH];
476    static int init = 0;
477    const char * envHome;
478
479    if( init )
480        return buf;
481
482    envHome = getenv( "HOME" );
483    if( envHome )
484        snprintf( buf, sizeof(buf), "%s", envHome );
485    else {
486#ifdef WIN32
487        SHGetFolderPath( NULL, CSIDL_PROFILE, NULL, 0, buf );
488#elif defined(__BEOS__) || defined(__AMIGAOS4__)
489        *buf = '\0';
490#else
491        struct passwd * pw = getpwuid( getuid() );
492        endpwent();
493        if( pw != NULL )
494            snprintf( buf, sizeof(buf), "%s", pw->pw_dir );
495#endif
496    }
497
498    init = 1;
499    return buf;
500}
501
502
503static void
504tr_migrateResume( const char *oldDirectory, const char *newDirectory )
505{
506    DIR * dirh = opendir( oldDirectory );
507
508    if( dirh != NULL )
509    {
510        struct dirent * dirp;
511
512        while( ( dirp = readdir( dirh ) ) )
513        {
514            if( !strncmp( "resume.", dirp->d_name, 7 ) )
515            {
516                char o[MAX_PATH_LENGTH];
517                char n[MAX_PATH_LENGTH];
518                tr_buildPath( o, sizeof(o), oldDirectory, dirp->d_name, NULL );
519                tr_buildPath( n, sizeof(n), newDirectory, dirp->d_name, NULL );
520                rename( o, n );
521            }
522        }
523
524        closedir( dirh );
525    }
526}
527
528const char *
529tr_getPrefsDirectory( void )
530{
531    static char   buf[MAX_PATH_LENGTH];
532    static int    init = 0;
533    static size_t buflen = sizeof(buf);
534    const char* h;
535
536    if( init )
537        return buf;
538
539    h = tr_getHomeDirectory();
540#ifdef __BEOS__
541    find_directory( B_USER_SETTINGS_DIRECTORY,
542                    dev_for_path("/boot"), true, buf, buflen );
543    strcat( buf, "/Transmission" );
544#elif defined( SYS_DARWIN )
545    tr_buildPath ( buf, buflen, h,
546                  "Library", "Application Support", "Transmission", NULL );
547#elif defined(__AMIGAOS4__)
548    snprintf( buf, buflen, "PROGDIR:.transmission" );
549#elif defined(WIN32)
550    {
551        char tmp[MAX_PATH_LENGTH];
552        SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, tmp );
553        tr_buildPath( buf, sizeof(buf), tmp, "Transmission", NULL );
554        buflen = strlen( buf );
555    }
556#else
557    tr_buildPath ( buf, buflen, h, ".transmission", NULL );
558#endif
559
560    tr_mkdirp( buf, 0700 );
561    init = 1;
562
563#ifdef SYS_DARWIN
564    char old[MAX_PATH_LENGTH];
565    tr_buildPath ( old, sizeof(old), h, ".transmission", NULL );
566    tr_migrateResume( old, buf );
567    rmdir( old );
568#endif
569
570    return buf;
571}
572
573const char *
574tr_getCacheDirectory( void )
575{
576    static char buf[MAX_PATH_LENGTH];
577    static int  init = 0;
578    static const size_t buflen = sizeof(buf);
579    const char * p;
580
581    if( init )
582        return buf;
583
584    p = tr_getPrefsDirectory();
585#if defined(__BEOS__) || defined(WIN32)
586    tr_buildPath( buf, buflen, p, "Cache", NULL );
587#elif defined( SYS_DARWIN )
588    tr_buildPath( buf, buflen, tr_getHomeDirectory(),
589                  "Library", "Caches", "Transmission", NULL );
590#else
591    tr_buildPath( buf, buflen, p, "cache", NULL );
592#endif
593
594    tr_mkdirp( buf, 0700 );
595    init = 1;
596
597    if( strcmp( p, buf ) )
598        tr_migrateResume( p, buf );
599
600    return buf;
601}
602
603const char *
604tr_getTorrentsDirectory( void )
605{
606    static char buf[MAX_PATH_LENGTH];
607    static int  init = 0;
608    static const size_t buflen = sizeof(buf);
609    const char * p;
610
611    if( init )
612        return buf;
613
614    p = tr_getPrefsDirectory ();
615
616#if defined(__BEOS__) || defined(WIN32)
617    tr_buildPath( buf, buflen, p, "Torrents", NULL );
618#elif defined( SYS_DARWIN )
619    tr_buildPath( buf, buflen, p, "Torrents", NULL );
620#else
621    tr_buildPath( buf, buflen, p, "torrents", NULL );
622#endif
623
624    tr_mkdirp( buf, 0700 );
625    init = 1;
626    return buf;
627}
628
629/***
630****  SOCKETS
631***/
632
633#ifdef BSD
634
635#include <sys/types.h>
636#include <sys/socket.h>
637#include <netinet/in.h>
638#include <arpa/inet.h>
639#include <netinet/in.h> /* struct in_addr */
640#include <sys/sysctl.h>
641#include <net/route.h>
642
643static uint8_t *
644getroute( int * buflen );
645static int
646parseroutes( uint8_t * buf, int len, struct in_addr * addr );
647
648int
649tr_getDefaultRoute( struct in_addr * addr )
650{
651    uint8_t * buf;
652    int len;
653
654    buf = getroute( &len );
655    if( NULL == buf )
656    {
657        tr_err( "failed to get default route (BSD)" );
658        return 1;
659    }
660
661    len = parseroutes( buf, len, addr );
662    free( buf );
663
664    return len;
665}
666
667#ifndef SA_SIZE
668#define ROUNDUP( a, size ) \
669    ( ( (a) & ( (size) - 1 ) ) ? ( 1 + ( (a) | ( (size) - 1 ) ) ) : (a) )
670#define SA_SIZE( sap ) \
671    ( sap->sa_len ? ROUNDUP( (sap)->sa_len, sizeof( u_long ) ) : \
672                    sizeof( u_long ) )
673#endif /* !SA_SIZE */
674#define NEXT_SA( sap ) \
675    (struct sockaddr *) ( (caddr_t) (sap) + ( SA_SIZE( (sap) ) ) )
676
677static uint8_t *
678getroute( int * buflen )
679{
680    int     mib[6];
681    size_t  len;
682    uint8_t * buf;
683
684    mib[0] = CTL_NET;
685    mib[1] = PF_ROUTE;
686    mib[2] = 0;
687    mib[3] = AF_INET;
688    mib[4] = NET_RT_FLAGS;
689    mib[5] = RTF_GATEWAY;
690
691    if( sysctl( mib, 6, NULL, &len, NULL, 0 ) )
692    {
693        if( ENOENT != errno )
694        {
695            tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
696                    strerror( sockerrno ) );
697        }
698        *buflen = 0;
699        return NULL;
700    }
701
702    buf = malloc( len );
703    if( NULL == buf )
704    {
705        *buflen = 0;
706        return NULL;
707    }
708
709    if( sysctl( mib, 6, buf, &len, NULL, 0 ) )
710    {
711        tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
712                strerror( sockerrno ) );
713        free( buf );
714        *buflen = 0;
715        return NULL;
716    }
717
718    *buflen = len;
719
720    return buf;
721}
722
723static int
724parseroutes( uint8_t * buf, int len, struct in_addr * addr )
725{
726    uint8_t            * end;
727    struct rt_msghdr   * rtm;
728    struct sockaddr    * sa;
729    struct sockaddr_in * sin;
730    int                  ii;
731    struct in_addr       dest, gw;
732
733    end = buf + len;
734    while( end > buf + sizeof( *rtm ) )
735    {
736        rtm = (struct rt_msghdr *) buf;
737        buf += rtm->rtm_msglen;
738        if( end >= buf )
739        {
740            dest.s_addr = INADDR_NONE;
741            gw.s_addr   = INADDR_NONE;
742            sa = (struct sockaddr *) ( rtm + 1 );
743
744            for( ii = 0; ii < RTAX_MAX && (uint8_t *) sa < buf; ii++ )
745            {
746                if( buf < (uint8_t *) NEXT_SA( sa ) )
747                {
748                    break;
749                }
750
751                if( rtm->rtm_addrs & ( 1 << ii ) )
752                {
753                    if( AF_INET == sa->sa_family )
754                    {
755                        sin = (struct sockaddr_in *) sa;
756                        switch( ii )
757                        {
758                            case RTAX_DST:
759                                dest = sin->sin_addr;
760                                break;
761                            case RTAX_GATEWAY:
762                                gw = sin->sin_addr;
763                                break;
764                        }
765                    }
766                    sa = NEXT_SA( sa );
767                }
768            }
769
770            if( INADDR_ANY == dest.s_addr && INADDR_NONE != gw.s_addr )
771            {
772                *addr = gw;
773                return 0;
774            }
775        }
776    }
777
778    return 1;
779}
780
781#elif defined( linux ) || defined( __linux ) || defined( __linux__ )
782
783#include <linux/types.h>
784#include <linux/netlink.h>
785#include <linux/rtnetlink.h>
786
787#define SEQNUM 195909
788
789static int
790getsock( void );
791static uint8_t *
792getroute( int fd, unsigned int * buflen );
793static int
794parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr );
795
796int
797tr_getDefaultRoute( struct in_addr * addr )
798{
799    int fd, ret;
800    unsigned int len;
801    uint8_t * buf;
802
803    ret = 1;
804    fd = getsock();
805    if( 0 <= fd )
806    {
807        while( ret )
808        {
809            buf = getroute( fd, &len );
810            if( NULL == buf )
811            {
812                break;
813            }
814            ret = parseroutes( buf, len, addr );
815            free( buf );
816        }
817        close( fd );
818    }
819
820    if( ret )
821    {
822        tr_err( "failed to get default route (Linux)" );
823    }
824
825    return ret;
826}
827
828static int
829getsock( void )
830{
831    int fd, flags;
832    struct
833    {
834        struct nlmsghdr nlh;
835        struct rtgenmsg rtg;
836    } req;
837    struct sockaddr_nl snl;
838
839    fd = socket( PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
840    if( 0 > fd )
841    {
842        tr_err( "failed to create routing socket (%s)", strerror( sockerrno ) );
843        return -1;
844    }
845
846    flags = fcntl( fd, F_GETFL );
847    if( 0 > flags || 0 > fcntl( fd, F_SETFL, O_NONBLOCK | flags ) )
848    {
849        tr_err( "failed to set socket nonblocking (%s)", strerror( sockerrno ) );
850        close( fd );
851        return -1;
852    }
853
854    bzero( &snl, sizeof( snl ) );
855    snl.nl_family = AF_NETLINK;
856
857    bzero( &req, sizeof( req ) );
858    req.nlh.nlmsg_len = NLMSG_LENGTH( sizeof( req.rtg ) );
859    req.nlh.nlmsg_type = RTM_GETROUTE;
860    req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
861    req.nlh.nlmsg_seq = SEQNUM;
862    req.nlh.nlmsg_pid = 0;
863    req.rtg.rtgen_family = AF_INET;
864
865    if( 0 > sendto( fd, &req, sizeof( req ), 0,
866                    (struct sockaddr *) &snl, sizeof( snl ) ) )
867    {
868        tr_err( "failed to write to routing socket (%s)", strerror( sockerrno ) );
869        close( fd );
870        return -1;
871    }
872
873    return fd;
874}
875
876static uint8_t *
877getroute( int fd, unsigned int * buflen )
878{
879    void             * buf;
880    unsigned int       len;
881    ssize_t            res;
882    struct sockaddr_nl snl;
883    socklen_t          slen;
884
885    len = 8192;
886    buf = calloc( 1, len );
887    if( NULL == buf )
888    {
889        *buflen = 0;
890        return NULL;
891    }
892
893    for( ;; )
894    {
895        bzero( &snl, sizeof( snl ) );
896        slen = sizeof( snl );
897        res = recvfrom( fd, buf, len, 0, (struct sockaddr *) &snl, &slen );
898        if( 0 > res )
899        {
900            if( EAGAIN != sockerrno )
901            {
902                tr_err( "failed to read from routing socket (%s)",
903                        strerror( sockerrno ) );
904            }
905            free( buf );
906            *buflen = 0;
907            return NULL;
908        }
909        if( slen < sizeof( snl ) || AF_NETLINK != snl.nl_family )
910        {
911            tr_err( "bad address" );
912            free( buf );
913            *buflen = 0;
914            return NULL;
915        }
916
917        if( 0 == snl.nl_pid )
918        {
919            break;
920        }
921    }
922
923    *buflen = res;
924
925    return buf;
926}
927
928static int
929parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr )
930{
931    struct nlmsghdr * nlm;
932    struct nlmsgerr * nle;
933    struct rtmsg    * rtm;
934    struct rtattr   * rta;
935    int               rtalen;
936    struct in_addr    gw, dst;
937
938    nlm = ( struct nlmsghdr * ) buf;
939    while( NLMSG_OK( nlm, len ) )
940    {
941        gw.s_addr = INADDR_ANY;
942        dst.s_addr = INADDR_ANY;
943        if( NLMSG_ERROR == nlm->nlmsg_type )
944        {
945            nle = (struct nlmsgerr *) NLMSG_DATA( nlm );
946            if( NLMSG_LENGTH( NLMSG_ALIGN( sizeof( struct nlmsgerr ) ) ) >
947                nlm->nlmsg_len )
948            {
949                tr_err( "truncated netlink error" );
950            }
951            else
952            {
953                tr_err( "netlink error (%s)", strerror( nle->error ) );
954            }
955            return 1;
956        }
957        else if( RTM_NEWROUTE == nlm->nlmsg_type && SEQNUM == nlm->nlmsg_seq &&
958                 getpid() == (pid_t) nlm->nlmsg_pid &&
959                 NLMSG_LENGTH( sizeof( struct rtmsg ) ) <= nlm->nlmsg_len )
960        {
961            rtm = NLMSG_DATA( nlm );
962            rta = RTM_RTA( rtm );
963            rtalen = RTM_PAYLOAD( nlm );
964
965            while( RTA_OK( rta, rtalen ) )
966            {
967                if( sizeof( struct in_addr ) <= RTA_PAYLOAD( rta ) )
968                {
969                    switch( rta->rta_type )
970                    {
971                        case RTA_GATEWAY:
972                            memcpy( &gw, RTA_DATA( rta ), sizeof( gw ) );
973                            break;
974                        case RTA_DST:
975                            memcpy( &dst, RTA_DATA( rta ), sizeof( dst ) );
976                            break;
977                    }
978                }
979                rta = RTA_NEXT( rta, rtalen );
980            }
981        }
982
983        if( INADDR_NONE != gw.s_addr && INADDR_ANY != gw.s_addr &&
984            INADDR_ANY == dst.s_addr )
985        {
986            *addr = gw;
987            return 0;
988        }
989
990        nlm = NLMSG_NEXT( nlm, len );
991    }
992
993    return 1;
994}
995
996#else /* not BSD or Linux */
997
998int
999tr_getDefaultRoute( struct in_addr * addr UNUSED )
1000{
1001    tr_inf( "don't know how to get default route on this platform" );
1002    return 1;
1003}
1004
1005#endif
Note: See TracBrowser for help on using the repository browser.