source: trunk/libtransmission/platform.c @ 2975

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

(trunk) fix some of the crash-on-shutdown issues.

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