source: trunk/libtransmission/platform.c @ 3254

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

simplify libT locks now that it's (more-or-less) single-threaded. fix deadlocks. make tr_locks nestable.

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