source: trunk/libtransmission/platform.c @ 2577

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

add Win32 implementations of tr_thread_t, tr_mutex_t, and tr_cond_t

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