source: trunk/libtransmission/platform.c @ 1000

Last change on this file since 1000 was 1000, checked in by joshe, 15 years ago

Try to get home directory with getpwuid(getuid()) if getenv("HOME") fails.

  • Property svn:keywords set to Date Rev Author Id
File size: 14.5 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 1000 2006-10-13 06:29:26Z 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#ifdef SYS_BEOS
26  #include <fs_info.h>
27  #include <FindDirectory.h>
28#endif
29#include <sys/types.h>
30#include <dirent.h>
31
32#include "transmission.h"
33
34#if !defined( SYS_BEOS ) && !defined( __AMIGAOS4__ )
35
36#include <pwd.h>
37
38static char * tr_getHomeDirectory()
39{
40    static char     homeDirectory[MAX_PATH_LENGTH];
41    static int      init = 0;
42    char          * envHome;
43    struct passwd * pw;
44
45    if( init )
46    {
47        return homeDirectory;
48    }
49
50    envHome = getenv( "HOME" );
51    if( NULL == envHome )
52    {
53        pw = getpwuid( getuid() );
54        endpwent();
55        if( NULL == pw )
56        {
57            /* XXX need to handle this case */
58            return NULL;
59        }
60        envHome = pw->pw_dir;
61    }
62
63    snprintf( homeDirectory, MAX_PATH_LENGTH, "%s", envHome );
64    init = 1;
65
66    return homeDirectory;
67}
68#endif /* !SYS_BEOS && !__AMIGAOS4__ */
69
70static void
71tr_migrateResume( const char *oldDirectory, const char *newDirectory )
72{
73    DIR * dirh;
74    struct dirent * dirp;
75    char oldFile[MAX_PATH_LENGTH];
76    char newFile[MAX_PATH_LENGTH];
77
78    if( ( dirh = opendir( oldDirectory ) ) )
79    {
80        while( ( dirp = readdir( dirh ) ) )
81        {
82            if( strncmp( "resume.", dirp->d_name, 7 ) )
83            {
84                continue;
85            }
86            snprintf( oldFile, MAX_PATH_LENGTH, "%s/%s",
87                      oldDirectory, dirp->d_name );
88            snprintf( newFile, MAX_PATH_LENGTH, "%s/%s",
89                      newDirectory, dirp->d_name );
90            rename( oldFile, newFile );
91        }
92
93        closedir( dirh );
94    }
95}
96
97char * tr_getPrefsDirectory()
98{
99    static char prefsDirectory[MAX_PATH_LENGTH];
100    static int  init = 0;
101
102    if( init )
103    {
104        return prefsDirectory;
105    }
106
107#ifdef SYS_BEOS
108        find_directory( B_USER_SETTINGS_DIRECTORY, dev_for_path("/boot"),
109                        true, prefsDirectory, MAX_PATH_LENGTH );
110        strcat( prefsDirectory, "/Transmission" );
111#elif defined( SYS_DARWIN )
112    snprintf( prefsDirectory, MAX_PATH_LENGTH,
113              "%s/Library/Caches/Transmission", tr_getHomeDirectory() );
114#elif defined(__AMIGAOS4__)
115    snprintf( prefsDirectory, MAX_PATH_LENGTH, "PROGDIR:.transmission" );
116#else
117    snprintf( prefsDirectory, MAX_PATH_LENGTH, "%s/.transmission",
118              tr_getHomeDirectory() );
119#endif
120
121    tr_mkdir( prefsDirectory );
122    init = 1;
123
124#ifdef SYS_DARWIN
125    char oldDirectory[MAX_PATH_LENGTH];
126    snprintf( oldDirectory, MAX_PATH_LENGTH, "%s/.transmission",
127              tr_getHomeDirectory() );
128    tr_migrateResume( oldDirectory, prefsDirectory );
129    rmdir( oldDirectory );
130#endif
131
132    return prefsDirectory;
133}
134
135char * tr_getCacheDirectory()
136{
137    static char cacheDirectory[MAX_PATH_LENGTH];
138    static int  init = 0;
139
140    if( init )
141    {
142        return cacheDirectory;
143    }
144
145#ifdef SYS_BEOS
146    /* XXX hey Bryan, is this fine with you? */
147    snprintf( cacheDirectory, MAX_PATH_LENGTH, "%s/Cache",
148              tr_getPrefsDirectory() );
149#elif defined( SYS_DARWIN )
150    snprintf( cacheDirectory, MAX_PATH_LENGTH, "%s",
151              tr_getPrefsDirectory() );
152#else
153    snprintf( cacheDirectory, MAX_PATH_LENGTH, "%s/cache",
154              tr_getPrefsDirectory() );
155#endif
156
157    tr_mkdir( cacheDirectory );
158    init = 1;
159
160    if( strcmp( tr_getPrefsDirectory(), cacheDirectory ) )
161    {
162        tr_migrateResume( tr_getPrefsDirectory(), cacheDirectory );
163    }
164
165    return cacheDirectory;
166}
167
168char * tr_getTorrentsDirectory()
169{
170    static char torrentsDirectory[MAX_PATH_LENGTH];
171    static int  init = 0;
172
173    if( init )
174    {
175        return torrentsDirectory;
176    }
177
178#ifdef SYS_BEOS
179    /* XXX hey Bryan, is this fine with you? */
180    snprintf( torrentsDirectory, MAX_PATH_LENGTH, "%s/Torrents",
181              tr_getPrefsDirectory() );
182#elif defined( SYS_DARWIN )
183    snprintf( torrentsDirectory, MAX_PATH_LENGTH,
184              "%s/Library/Application Support/Transmission/Torrents",
185              tr_getHomeDirectory() );
186#else
187    snprintf( torrentsDirectory, MAX_PATH_LENGTH, "%s/torrents",
188              tr_getPrefsDirectory() );
189#endif
190
191    tr_mkdir( torrentsDirectory );
192    init = 1;
193
194    return torrentsDirectory;
195}
196
197void tr_threadCreate( tr_thread_t * t, void (*func)(void *), void * arg )
198{
199#ifdef SYS_BEOS
200    *t = spawn_thread( (void *) func, "torrent-tx", B_NORMAL_PRIORITY, arg );
201    resume_thread( *t );
202#else
203    pthread_create( t, NULL, (void *) func, arg );
204#endif
205}
206
207void tr_threadJoin( tr_thread_t * t )
208{
209#ifdef SYS_BEOS
210    long exit;
211    wait_for_thread( *t, &exit );
212#else
213    pthread_join( *t, NULL );
214#endif
215}
216
217void tr_lockInit( tr_lock_t * l )
218{
219#ifdef SYS_BEOS
220    *l = create_sem( 1, "" );
221#else
222    pthread_mutex_init( l, NULL );
223#endif
224}
225
226void tr_lockClose( tr_lock_t * l )
227{
228#ifdef SYS_BEOS
229    delete_sem( *l );
230#else
231    pthread_mutex_destroy( l );
232#endif
233}
234
235#if defined( BSD )
236
237#include <sys/sysctl.h>
238#include <net/route.h>
239
240static uint8_t *
241getroute( int * buflen );
242static int
243parseroutes( uint8_t * buf, int len, struct in_addr * addr );
244
245int
246tr_getDefaultRoute( struct in_addr * addr )
247{
248    uint8_t * buf;
249    int len;
250
251    buf = getroute( &len );
252    if( NULL == buf )
253    {
254        tr_err( "failed to get default route (BSD)" );
255        return 1;
256    }
257
258    len = parseroutes( buf, len, addr );
259    free( buf );
260
261    return len;
262}
263
264#ifndef SA_SIZE
265#define ROUNDUP( a, size ) \
266    ( ( (a) & ( (size) - 1 ) ) ? ( 1 + ( (a) | ( (size) - 1 ) ) ) : (a) )
267#define SA_SIZE( sap ) \
268    ( sap->sa_len ? ROUNDUP( (sap)->sa_len, sizeof( u_long ) ) : \
269                    sizeof( u_long ) )
270#endif /* !SA_SIZE */
271#define NEXT_SA( sap ) \
272    (struct sockaddr *) ( (caddr_t) (sap) + ( SA_SIZE( (sap) ) ) )
273
274static uint8_t *
275getroute( int * buflen )
276{
277    int     mib[6];
278    size_t  len;
279    uint8_t * buf;
280
281    mib[0] = CTL_NET;
282    mib[1] = PF_ROUTE;
283    mib[2] = 0;
284    mib[3] = AF_INET;
285    mib[4] = NET_RT_FLAGS;
286    mib[5] = RTF_GATEWAY;
287
288    if( sysctl( mib, 6, NULL, &len, NULL, 0 ) )
289    {
290        if( ENOENT != errno )
291        {
292            tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
293                    strerror( errno ) );
294        }
295        *buflen = 0;
296        return NULL;
297    }
298
299    buf = malloc( len );
300    if( NULL == buf )
301    {
302        *buflen = 0;
303        return NULL;
304    }
305
306    if( sysctl( mib, 6, buf, &len, NULL, 0 ) )
307    {
308        tr_err( "sysctl net.route.0.inet.flags.gateway failed (%s)",
309                strerror( errno ) );
310        free( buf );
311        *buflen = 0;
312        return NULL;
313    }
314
315    *buflen = len;
316
317    return buf;
318}
319
320static int
321parseroutes( uint8_t * buf, int len, struct in_addr * addr )
322{
323    uint8_t            * end;
324    struct rt_msghdr   * rtm;
325    struct sockaddr    * sa;
326    struct sockaddr_in * sin;
327    int                  ii;
328    struct in_addr       dest, gw;
329
330    end = buf + len;
331    while( end > buf + sizeof( *rtm ) )
332    {
333        rtm = (struct rt_msghdr *) buf;
334        buf += rtm->rtm_msglen;
335        if( end >= buf )
336        {
337            dest.s_addr = INADDR_NONE;
338            gw.s_addr   = INADDR_NONE;
339            sa = (struct sockaddr *) ( rtm + 1 );
340
341            for( ii = 0; ii < RTAX_MAX && (uint8_t *) sa < buf; ii++ )
342            {
343                if( buf < (uint8_t *) NEXT_SA( sa ) )
344                {
345                    break;
346                }
347
348                if( rtm->rtm_addrs & ( 1 << ii ) )
349                {
350                    if( AF_INET == sa->sa_family )
351                    {
352                        sin = (struct sockaddr_in *) sa;
353                        switch( ii )
354                        {
355                            case RTAX_DST:
356                                dest = sin->sin_addr;
357                                break;
358                            case RTAX_GATEWAY:
359                                gw = sin->sin_addr;
360                                break;
361                        }
362                    }
363                    sa = NEXT_SA( sa );
364                }
365            }
366
367            if( INADDR_ANY == dest.s_addr && INADDR_NONE != gw.s_addr )
368            {
369                *addr = gw;
370                return 0;
371            }
372        }
373    }
374
375    return 1;
376}
377
378#elif defined( linux ) || defined( __linux ) || defined( __linux__ )
379
380#include <linux/types.h>
381#include <linux/netlink.h>
382#include <linux/rtnetlink.h>
383
384#define SEQNUM 195909
385
386static int
387getsock( void );
388static uint8_t *
389getroute( int fd, unsigned int * buflen );
390static int
391parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr );
392
393int
394tr_getDefaultRoute( struct in_addr * addr )
395{
396    int fd, ret;
397    unsigned int len;
398    uint8_t * buf;
399
400    ret = 1;
401    fd = getsock();
402    if( 0 <= fd )
403    {
404        while( ret )
405        {
406            buf = getroute( fd, &len );
407            if( NULL == buf )
408            {
409                break;
410            }
411            ret = parseroutes( buf, len, addr );
412            free( buf );
413        }
414        close( fd );
415    }
416
417    if( ret )
418    {
419        tr_err( "failed to get default route (Linux)" );
420    }
421
422    return ret;
423}
424
425static int
426getsock( void )
427{
428    int fd, flags;
429    struct
430    {
431        struct nlmsghdr nlh;
432        struct rtgenmsg rtg;
433    } req;
434    struct sockaddr_nl snl;
435
436    fd = socket( PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE );
437    if( 0 > fd )
438    {
439        tr_err( "failed to create routing socket (%s)", strerror( errno ) );
440        return -1;
441    }
442
443    flags = fcntl( fd, F_GETFL );
444    if( 0 > flags || 0 > fcntl( fd, F_SETFL, O_NONBLOCK | flags ) )
445    {
446        tr_err( "failed to set socket nonblocking (%s)", strerror( errno ) );
447        close( fd );
448        return -1;
449    }
450
451    bzero( &snl, sizeof( snl ) );
452    snl.nl_family = AF_NETLINK;
453
454    bzero( &req, sizeof( req ) );
455    req.nlh.nlmsg_len = NLMSG_LENGTH( sizeof( req.rtg ) );
456    req.nlh.nlmsg_type = RTM_GETROUTE;
457    req.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
458    req.nlh.nlmsg_seq = SEQNUM;
459    req.nlh.nlmsg_pid = 0;
460    req.rtg.rtgen_family = AF_INET;
461
462    if( 0 > sendto( fd, &req, sizeof( req ), 0,
463                    (struct sockaddr *) &snl, sizeof( snl ) ) )
464    {
465        tr_err( "failed to write to routing socket (%s)", strerror( errno ) );
466        close( fd );
467        return -1;
468    }
469
470    return fd;
471}
472
473static uint8_t *
474getroute( int fd, unsigned int * buflen )
475{
476    void             * buf;
477    unsigned int       len;
478    ssize_t            res;
479    struct sockaddr_nl snl;
480    socklen_t          slen;
481
482    len = 8192;
483    buf = calloc( 1, len );
484    if( NULL == buf )
485    {
486        *buflen = 0;
487        return NULL;
488    }
489
490    for( ;; )
491    {
492        bzero( &snl, sizeof( snl ) );
493        slen = sizeof( snl );
494        res = recvfrom( fd, buf, len, 0, (struct sockaddr *) &snl, &slen );
495        if( 0 > res )
496        {
497            if( EAGAIN != errno )
498            {
499                tr_err( "failed to read from routing socket (%s)",
500                        strerror( errno ) );
501            }
502            free( buf );
503            *buflen = 0;
504            return NULL;
505        }
506        if( slen < sizeof( snl ) || AF_NETLINK != snl.nl_family )
507        {
508            tr_err( "bad address" );
509            free( buf );
510            *buflen = 0;
511            return NULL;
512        }
513
514        if( 0 == snl.nl_pid )
515        {
516            break;
517        }
518    }
519
520    *buflen = res;
521
522    return buf;
523}
524
525static int
526parseroutes( uint8_t * buf, unsigned int len, struct in_addr * addr )
527{
528    struct nlmsghdr * nlm;
529    struct nlmsgerr * nle;
530    struct rtmsg    * rtm;
531    struct rtattr   * rta;
532    int               rtalen;
533    struct in_addr    gw, dst;
534
535    nlm = ( struct nlmsghdr * ) buf;
536    while( NLMSG_OK( nlm, len ) )
537    {
538        gw.s_addr = INADDR_ANY;
539        dst.s_addr = INADDR_ANY;
540        if( NLMSG_ERROR == nlm->nlmsg_type )
541        {
542            nle = (struct nlmsgerr *) NLMSG_DATA( nlm );
543            if( NLMSG_LENGTH( NLMSG_ALIGN( sizeof( struct nlmsgerr ) ) ) >
544                nlm->nlmsg_len )
545            {
546                tr_err( "truncated netlink error" );
547            }
548            else
549            {
550                tr_err( "netlink error (%s)", strerror( nle->error ) );
551            }
552            return 1;
553        }
554        else if( RTM_NEWROUTE == nlm->nlmsg_type && SEQNUM == nlm->nlmsg_seq &&
555                 getpid() == (pid_t) nlm->nlmsg_pid &&
556                 NLMSG_LENGTH( sizeof( struct rtmsg ) ) <= nlm->nlmsg_len )
557        {
558            rtm = NLMSG_DATA( nlm );
559            rta = RTM_RTA( rtm );
560            rtalen = RTM_PAYLOAD( nlm );
561
562            while( RTA_OK( rta, rtalen ) )
563            {
564                if( sizeof( struct in_addr ) <= RTA_PAYLOAD( rta ) )
565                {
566                    switch( rta->rta_type )
567                    {
568                        case RTA_GATEWAY:
569                            memcpy( &gw, RTA_DATA( rta ), sizeof( gw ) );
570                            break;
571                        case RTA_DST:
572                            memcpy( &dst, RTA_DATA( rta ), sizeof( dst ) );
573                            break;
574                    }
575                }
576                rta = RTA_NEXT( rta, rtalen );
577            }
578        }
579
580        if( INADDR_NONE != gw.s_addr && INADDR_ANY != gw.s_addr &&
581            INADDR_ANY == dst.s_addr )
582        {
583            *addr = gw;
584            return 0;
585        }
586
587        nlm = NLMSG_NEXT( nlm, len );
588    }
589
590    return 1;
591}
592
593#else /* not BSD or Linux */
594
595int
596tr_getDefaultRoute( struct in_addr * addr UNUSED )
597{
598    tr_inf( "don't know how to get default route on this platform" );
599    return 1;
600}
601
602#endif
Note: See TracBrowser for help on using the repository browser.