source: trunk/libtransmission/platform.c @ 2607

Last change on this file since 2607 was 2607, checked in by charles, 14 years ago

BeOS portability fixes from ModeenF

  • Property svn:keywords set to Date Rev Author Id
File size: 22.9 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 2607 2007-08-03 16:28:32Z 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 SYS_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 SYS_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 SYS_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 SYS_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 SYS_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 SYS_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 SYS_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 SYS_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 SYS_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 SYS_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 SYS_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 SYS_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 SYS_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 SYS_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 SYS_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 SYS_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 SYS_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 SYS_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(SYS_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(SYS_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 SYS_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(SYS_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(SYS_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.