source: trunk/libtransmission/platform.c @ 4042

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

more mutex work

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