source: trunk/libtransmission/platform.c @ 4041

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

possibly get the pthread changes compiling for old versions of Linux

  • Property svn:keywords set to Date Rev Author Id
File size: 22.1 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 4041 2007-12-02 17:15:52Z 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    const tr_thread_id currentThread = tr_getCurrentThread( );
245
246#ifdef __BEOS__
247    acquire_sem( l->lock );
248#elif defined(WIN32)
249    EnterCriticalSection( &l->lock );
250#else
251    pthread_mutex_lock( &l->lock );
252#endif
253    l->lockThread = currentThread;
254    ++l->depth;
255    assert( l->depth >= 1 );
256}
257
258int
259tr_lockHave( const tr_lock * l )
260{
261    return ( l->depth > 0 )
262        && ( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ) );
263}
264
265void
266tr_lockUnlock( tr_lock * l )
267{
268    assert( l->depth > 0 );
269    assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ));
270    assert( tr_lockHave( l ) );
271
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    --l->depth;
280    assert( l->depth >= 0 );
281}
282
283/***
284****  COND
285***/
286
287struct tr_cond
288{
289#ifdef __BEOS__
290    sem_id sem;
291    thread_id threads[BEOS_MAX_THREADS];
292    int start, end;
293#elif defined(WIN32)
294    tr_list * events;
295    tr_lock * lock;
296#else
297    pthread_cond_t cond;
298#endif
299};
300
301#ifdef WIN32
302static DWORD getContEventTLS( void )
303{
304    static int inited = FALSE;
305    static DWORD event_tls;
306    if( !inited ) {
307        inited = TRUE;
308        event_tls = TlsAlloc();
309    }
310    return event_tls;
311}
312#endif
313
314tr_cond*
315tr_condNew( void )
316{
317    tr_cond * c = tr_new0( tr_cond, 1 );
318#ifdef __BEOS__
319    c->sem = create_sem( 1, "" );
320    c->start = 0;
321    c->end = 0;
322#elif defined(WIN32)
323    c->events = NULL;
324    c->lock = tr_lockNew( );
325#else
326    pthread_cond_init( &c->cond, NULL );
327#endif
328    return c;
329}
330
331void
332tr_condWait( tr_cond * c, tr_lock * l )
333{
334#ifdef __BEOS__
335
336    /* Keep track of that thread */
337    acquire_sem( c->sem );
338    c->threads[c->end] = find_thread( NULL );
339    c->end = ( c->end + 1 ) % BEOS_MAX_THREADS;
340    assert( c->end != c->start ); /* We hit BEOS_MAX_THREADS, arggh */
341    release_sem( c->sem );
342
343    release_sem( l->lock );
344    suspend_thread( find_thread( NULL ) ); /* Wait for signal */
345    acquire_sem( l->lock );
346
347#elif defined(WIN32)
348
349    /* get this thread's cond event */
350    DWORD key = getContEventTLS ( );
351    HANDLE hEvent = TlsGetValue( key );
352    if( !hEvent ) {
353        hEvent = CreateEvent( 0, FALSE, FALSE, 0 );
354        TlsSetValue( key, hEvent );
355    }
356
357    /* add it to the list of events waiting to be signaled */
358    tr_lockLock( c->lock );
359    tr_list_append( &c->events, hEvent );
360    tr_lockUnlock( c->lock );
361
362    /* now wait for it to be signaled */
363    tr_lockUnlock( l );
364    WaitForSingleObject( hEvent, INFINITE );
365    tr_lockLock( l );
366
367    /* remove it from the list of events waiting to be signaled */
368    tr_lockLock( c->lock );
369    tr_list_remove_data( &c->events, hEvent );
370    tr_lockUnlock( c->lock );
371
372#else
373
374    pthread_cond_wait( &c->cond, &l->lock );
375
376#endif
377}
378
379#ifdef __BEOS__
380static int condTrySignal( tr_cond * c )
381{
382    if( c->start == c->end )
383        return 1;
384
385    for( ;; )
386    {
387        thread_info info;
388        get_thread_info( c->threads[c->start], &info );
389        if( info.state == B_THREAD_SUSPENDED )
390        {
391            resume_thread( c->threads[c->start] );
392            c->start = ( c->start + 1 ) % BEOS_MAX_THREADS;
393            break;
394        }
395        /* The thread is not suspended yet, which can happen since
396         * tr_condWait does not atomically suspends after releasing
397         * the semaphore. Wait a bit and try again. */
398        snooze( 5000 );
399    }
400    return 0;
401}
402#endif
403void
404tr_condSignal( tr_cond * c )
405{
406#ifdef __BEOS__
407
408    acquire_sem( c->sem );
409    condTrySignal( c );
410    release_sem( c->sem );
411
412#elif defined(WIN32)
413
414    tr_lockLock( c->lock );
415    if( c->events != NULL )
416        SetEvent( (HANDLE)c->events->data );
417    tr_lockUnlock( c->lock );
418
419#else
420
421    pthread_cond_signal( &c->cond );
422
423#endif
424}
425
426void
427tr_condBroadcast( tr_cond * c )
428{
429#ifdef __BEOS__
430
431    acquire_sem( c->sem );
432    while( !condTrySignal( c ) );
433    release_sem( c->sem );
434
435#elif defined(WIN32)
436
437    tr_list * l;
438    tr_lockLock( c->lock );
439    for( l=c->events; l!=NULL; l=l->next )
440        SetEvent( (HANDLE)l->data );
441    tr_lockUnlock( c->lock );
442
443#else
444
445    pthread_cond_broadcast( &c->cond );
446
447#endif
448}
449
450void
451tr_condFree( tr_cond * c )
452{
453#ifdef __BEOS__
454    delete_sem( c->sem );
455#elif defined(WIN32)
456    tr_list_free( &c->events, NULL );
457    tr_lockFree( c->lock );
458#else
459    pthread_cond_destroy( &c->cond );
460#endif
461    tr_free( c );
462}
463
464
465/***
466****  PATHS
467***/
468
469#if !defined(WIN32) && !defined(__BEOS__) && !defined(__AMIGAOS4__)
470#include <pwd.h>
471#endif
472
473const char *
474tr_getHomeDirectory( void )
475{
476    static char buf[MAX_PATH_LENGTH];
477    static int init = 0;
478    const char * envHome;
479
480    if( init )
481        return buf;
482
483    envHome = getenv( "HOME" );
484    if( envHome )
485        snprintf( buf, sizeof(buf), "%s", envHome );
486    else {
487#ifdef WIN32
488        SHGetFolderPath( NULL, CSIDL_PROFILE, NULL, 0, buf );
489#elif defined(__BEOS__) || defined(__AMIGAOS4__)
490        *buf = '\0';
491#else
492        struct passwd * pw = getpwuid( getuid() );
493        endpwent();
494        if( pw != NULL )
495            snprintf( buf, sizeof(buf), "%s", pw->pw_dir );
496#endif
497    }
498
499    init = 1;
500    return buf;
501}
502
503
504static void
505tr_migrateResume( const char *oldDirectory, const char *newDirectory )
506{
507    DIR * dirh = opendir( oldDirectory );
508
509    if( dirh != NULL )
510    {
511        struct dirent * dirp;
512
513        while( ( dirp = readdir( dirh ) ) )
514        {
515            if( !strncmp( "resume.", dirp->d_name, 7 ) )
516            {
517                char o[MAX_PATH_LENGTH];
518                char n[MAX_PATH_LENGTH];
519                tr_buildPath( o, sizeof(o), oldDirectory, dirp->d_name, NULL );
520                tr_buildPath( n, sizeof(n), newDirectory, dirp->d_name, NULL );
521                rename( o, n );
522            }
523        }
524
525        closedir( dirh );
526    }
527}
528
529const char *
530tr_getPrefsDirectory( void )
531{
532    static char   buf[MAX_PATH_LENGTH];
533    static int    init = 0;
534    static size_t buflen = sizeof(buf);
535    const char* h;
536
537    if( init )
538        return buf;
539
540    h = tr_getHomeDirectory();
541#ifdef __BEOS__
542    find_directory( B_USER_SETTINGS_DIRECTORY,
543                    dev_for_path("/boot"), true, buf, buflen );
544    strcat( buf, "/Transmission" );
545#elif defined( SYS_DARWIN )
546    tr_buildPath ( buf, buflen, h,
547                  "Library", "Application Support", "Transmission", NULL );
548#elif defined(__AMIGAOS4__)
549    snprintf( buf, buflen, "PROGDIR:.transmission" );
550#elif defined(WIN32)
551    {
552        char tmp[MAX_PATH_LENGTH];
553        SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, tmp );
554        tr_buildPath( buf, sizeof(buf), tmp, "Transmission", NULL );
555        buflen = strlen( buf );
556    }
557#else
558    tr_buildPath ( buf, buflen, h, ".transmission", NULL );
559#endif
560
561    tr_mkdirp( buf, 0700 );
562    init = 1;
563
564#ifdef SYS_DARWIN
565    char old[MAX_PATH_LENGTH];
566    tr_buildPath ( old, sizeof(old), h, ".transmission", NULL );
567    tr_migrateResume( old, buf );
568    rmdir( old );
569#endif
570
571    return buf;
572}
573
574const char *
575tr_getCacheDirectory( void )
576{
577    static char buf[MAX_PATH_LENGTH];
578    static int  init = 0;
579    static const size_t buflen = sizeof(buf);
580    const char * p;
581
582    if( init )
583        return buf;
584
585    p = tr_getPrefsDirectory();
586#if defined(__BEOS__) || defined(WIN32)
587    tr_buildPath( buf, buflen, p, "Cache", NULL );
588#elif defined( SYS_DARWIN )
589    tr_buildPath( buf, buflen, tr_getHomeDirectory(),
590                  "Library", "Caches", "Transmission", NULL );
591#else
592    tr_buildPath( buf, buflen, p, "cache", NULL );
593#endif
594
595    tr_mkdirp( buf, 0700 );
596    init = 1;
597
598    if( strcmp( p, buf ) )
599        tr_migrateResume( p, buf );
600
601    return buf;
602}
603
604const char *
605tr_getTorrentsDirectory( void )
606{
607    static char buf[MAX_PATH_LENGTH];
608    static int  init = 0;
609    static const size_t buflen = sizeof(buf);
610    const char * p;
611
612    if( init )
613        return buf;
614
615    p = tr_getPrefsDirectory ();
616
617#if defined(__BEOS__) || defined(WIN32)
618    tr_buildPath( buf, buflen, p, "Torrents", NULL );
619#elif defined( SYS_DARWIN )
620    tr_buildPath( buf, buflen, p, "Torrents", NULL );
621#else
622    tr_buildPath( buf, buflen, p, "torrents", NULL );
623#endif
624
625    tr_mkdirp( buf, 0700 );
626    init = 1;
627    return buf;
628}
629
630/***
631****  SOCKETS
632***/
633
634#ifdef BSD
635
636#include <sys/types.h>
637#include <sys/socket.h>
638#include <netinet/in.h>
639#include <arpa/inet.h>
640#include <netinet/in.h> /* struct in_addr */
641#include <sys/sysctl.h>
642#include <net/route.h>
643
644static uint8_t *
645getroute( int * buflen );
646static int
647parseroutes( uint8_t * buf, int len, struct in_addr * addr );
648
649int
650tr_getDefaultRoute( struct in_addr * addr )
651{
652    uint8_t * buf;
653    int len;
654
655    buf = getroute( &len );
656    if( NULL == buf )
657    {
658        tr_err( "failed to get default route (BSD)" );
659        return 1;
660    }
661
662    len = parseroutes( buf, len, addr );
663    free( buf );
664
665    return len;
666}
667
668#ifndef SA_SIZE
669#define ROUNDUP( a, size ) \
670    ( ( (a) & ( (size) - 1 ) ) ? ( 1 + ( (a) | ( (size) - 1 ) ) ) : (a) )
671#define SA_SIZE( sap ) \
672    ( sap->sa_len ? ROUNDUP( (sap)->sa_len, sizeof( u_long ) ) : \
673                    sizeof( u_long ) )
674#endif /* !SA_SIZE */
675#define NEXT_SA( sap ) \
676    (struct sockaddr *) ( (caddr_t) (sap) + ( SA_SIZE( (sap) ) ) )
677
678static uint8_t *
679getroute( int * buflen )
680{
681    int     mib[6];
682    size_t  len;
683    uint8_t * buf;
684
685    mib[0] = CTL_NET;
686    mib[1] = PF_ROUTE;
687    mib[2] = 0;
688    mib[3] = AF_INET;
689    mib[4] = NET_RT_FLAGS;
690    mib[5] = RTF_GATEWAY;
691
692    if( sysctl( mib, 6, NULL, &len, NULL, 0 ) )
693    {
694        if( ENOENT != errno )
695        {
696            tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
697                    strerror( sockerrno ) );
698        }
699        *buflen = 0;
700        return NULL;
701    }
702
703    buf = malloc( len );
704    if( NULL == buf )
705    {
706        *buflen = 0;
707        return NULL;
708    }
709
710    if( sysctl( mib, 6, buf, &len, NULL, 0 ) )
711    {
712        tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
713                strerror( sockerrno ) );
714        free( buf );
715        *buflen = 0;
716        return NULL;
717    }
718
719    *buflen = len;
720
721    return buf;
722}
723
724static int
725parseroutes( uint8_t * buf, int len, struct in_addr * addr )
726{
727    uint8_t            * end;
728    struct rt_msghdr   * rtm;
729    struct sockaddr    * sa;
730    struct sockaddr_in * sin;
731    int                  ii;
732    struct in_addr       dest, gw;
733
734    end = buf + len;
735    while( end > buf + sizeof( *rtm ) )
736    {
737        rtm = (struct rt_msghdr *) buf;
738        buf += rtm->rtm_msglen;
739        if( end >= buf )
740        {
741            dest.s_addr = INADDR_NONE;
742            gw.s_addr   = INADDR_NONE;
743            sa = (struct sockaddr *) ( rtm + 1 );
744
745            for( ii = 0; ii < RTAX_MAX && (uint8_t *) sa < buf; ii++ )
746            {
747                if( buf < (uint8_t *) NEXT_SA( sa ) )
748                {
749                    break;
750                }
751
752                if( rtm->rtm_addrs & ( 1 << ii ) )
753                {
754                    if( AF_INET == sa->sa_family )
755                    {
756                        sin = (struct sockaddr_in *) sa;
757                        switch( ii )
758                        {
759                            case RTAX_DST:
760                                dest = sin->sin_addr;
761                                break;
762                            case RTAX_GATEWAY:
763                                gw = sin->sin_addr;
764                                break;
765                        }
766                    }
767                    sa = NEXT_SA( sa );
768                }
769            }
770
771            if( INADDR_ANY == dest.s_addr && INADDR_NONE != gw.s_addr )
772            {
773                *addr = gw;
774                return 0;
775            }
776        }
777    }
778
779    return 1;
780}
781
782#elif defined( linux ) || defined( __linux ) || defined( __linux__ )
783
784#include <linux/types.h>
785#include <linux/netlink.h>
786#include <linux/rtnetlink.h>
787
788#define SEQNUM 195909
789
790static int
791getsock( void );
792static uint8_t *
793getroute( int fd, unsigned int * buflen );
794static int
795parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr );
796
797int
798tr_getDefaultRoute( struct in_addr * addr )
799{
800    int fd, ret;
801    unsigned int len;
802    uint8_t * buf;
803
804    ret = 1;
805    fd = getsock();
806    if( 0 <= fd )
807    {
808        while( ret )
809        {
810            buf = getroute( fd, &len );
811            if( NULL == buf )
812            {
813                break;
814            }
815            ret = parseroutes( buf, len, addr );
816            free( buf );
817        }
818        close( fd );
819    }
820
821    if( ret )
822    {
823        tr_err( "failed to get default route (Linux)" );
824    }
825
826    return ret;
827}
828
829static int
830getsock( void )
831{
832    int fd, flags;
833    struct
834    {
835        struct nlmsghdr nlh;
836        struct rtgenmsg rtg;
837    } req;
838    struct sockaddr_nl snl;
839
840    fd = socket( PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
841    if( 0 > fd )
842    {
843        tr_err( "failed to create routing socket (%s)", strerror( sockerrno ) );
844        return -1;
845    }
846
847    flags = fcntl( fd, F_GETFL );
848    if( 0 > flags || 0 > fcntl( fd, F_SETFL, O_NONBLOCK | flags ) )
849    {
850        tr_err( "failed to set socket nonblocking (%s)", strerror( sockerrno ) );
851        close( fd );
852        return -1;
853    }
854
855    memset( &snl, 0, sizeof(snl) );
856    snl.nl_family = AF_NETLINK;
857
858    memset( &req, 0, sizeof(req) );
859    req.nlh.nlmsg_len = NLMSG_LENGTH( sizeof( req.rtg ) );
860    req.nlh.nlmsg_type = RTM_GETROUTE;
861    req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
862    req.nlh.nlmsg_seq = SEQNUM;
863    req.nlh.nlmsg_pid = 0;
864    req.rtg.rtgen_family = AF_INET;
865
866    if( 0 > sendto( fd, &req, sizeof( req ), 0,
867                    (struct sockaddr *) &snl, sizeof( snl ) ) )
868    {
869        tr_err( "failed to write to routing socket (%s)", strerror( sockerrno ) );
870        close( fd );
871        return -1;
872    }
873
874    return fd;
875}
876
877static uint8_t *
878getroute( int fd, unsigned int * buflen )
879{
880    void             * buf;
881    unsigned int       len;
882    ssize_t            res;
883    struct sockaddr_nl snl;
884    socklen_t          slen;
885
886    len = 8192;
887    buf = calloc( 1, len );
888    if( NULL == buf )
889    {
890        *buflen = 0;
891        return NULL;
892    }
893
894    for( ;; )
895    {
896        slen = sizeof( snl );
897        memset( &snl, 0, slen );
898        res = recvfrom( fd, buf, len, 0, (struct sockaddr *) &snl, &slen );
899        if( 0 > res )
900        {
901            if( EAGAIN != sockerrno )
902            {
903                tr_err( "failed to read from routing socket (%s)",
904                        strerror( sockerrno ) );
905            }
906            free( buf );
907            *buflen = 0;
908            return NULL;
909        }
910        if( slen < sizeof( snl ) || AF_NETLINK != snl.nl_family )
911        {
912            tr_err( "bad address" );
913            free( buf );
914            *buflen = 0;
915            return NULL;
916        }
917
918        if( 0 == snl.nl_pid )
919        {
920            break;
921        }
922    }
923
924    *buflen = res;
925
926    return buf;
927}
928
929static int
930parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr )
931{
932    struct nlmsghdr * nlm;
933    struct nlmsgerr * nle;
934    struct rtmsg    * rtm;
935    struct rtattr   * rta;
936    int               rtalen;
937    struct in_addr    gw, dst;
938
939    nlm = ( struct nlmsghdr * ) buf;
940    while( NLMSG_OK( nlm, len ) )
941    {
942        gw.s_addr = INADDR_ANY;
943        dst.s_addr = INADDR_ANY;
944        if( NLMSG_ERROR == nlm->nlmsg_type )
945        {
946            nle = (struct nlmsgerr *) NLMSG_DATA( nlm );
947            if( NLMSG_LENGTH( NLMSG_ALIGN( sizeof( struct nlmsgerr ) ) ) >
948                nlm->nlmsg_len )
949            {
950                tr_err( "truncated netlink error" );
951            }
952            else
953            {
954                tr_err( "netlink error (%s)", strerror( nle->error ) );
955            }
956            return 1;
957        }
958        else if( RTM_NEWROUTE == nlm->nlmsg_type && SEQNUM == nlm->nlmsg_seq &&
959                 getpid() == (pid_t) nlm->nlmsg_pid &&
960                 NLMSG_LENGTH( sizeof( struct rtmsg ) ) <= nlm->nlmsg_len )
961        {
962            rtm = NLMSG_DATA( nlm );
963            rta = RTM_RTA( rtm );
964            rtalen = RTM_PAYLOAD( nlm );
965
966            while( RTA_OK( rta, rtalen ) )
967            {
968                if( sizeof( struct in_addr ) <= RTA_PAYLOAD( rta ) )
969                {
970                    switch( rta->rta_type )
971                    {
972                        case RTA_GATEWAY:
973                            memcpy( &gw, RTA_DATA( rta ), sizeof( gw ) );
974                            break;
975                        case RTA_DST:
976                            memcpy( &dst, RTA_DATA( rta ), sizeof( dst ) );
977                            break;
978                    }
979                }
980                rta = RTA_NEXT( rta, rtalen );
981            }
982        }
983
984        if( INADDR_NONE != gw.s_addr && INADDR_ANY != gw.s_addr &&
985            INADDR_ANY == dst.s_addr )
986        {
987            *addr = gw;
988            return 0;
989        }
990
991        nlm = NLMSG_NEXT( nlm, len );
992    }
993
994    return 1;
995}
996
997#else /* not BSD or Linux */
998
999int
1000tr_getDefaultRoute( struct in_addr * addr UNUSED )
1001{
1002    tr_inf( "don't know how to get default route on this platform" );
1003    return 1;
1004}
1005
1006#endif
Note: See TracBrowser for help on using the repository browser.