source: trunk/libtransmission/platform.c @ 2614

Last change on this file since 2614 was 2614, checked in by joshe, 14 years ago

Use BEOS to test for beos instead of relying on the build to set SYS_BEOS.
Add missing headers and other miscellaneous fixes for beos.

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