source: trunk/libtransmission/platform.c @ 7404

Last change on this file since 7404 was 7404, checked in by charles, 12 years ago

updated email address

  • Property svn:keywords set to Date Rev Author Id
File size: 17.2 KB
Line 
1/*
2 * This file Copyright (C) 2008 Charles Kerr <charles@transmissionbt.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: platform.c 7404 2008-12-16 00:20:44Z charles $
11 */
12
13#include <assert.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17
18#ifdef WIN32
19 #include <windows.h>
20 #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_MYDOCUMENTS */
21#else
22 #ifdef SYS_DARWIN
23  #include <CoreFoundation/CoreFoundation.h>
24 #endif
25
26 #define _XOPEN_SOURCE 500  /* needed for recursive locks. */
27 #ifndef __USE_UNIX98
28  #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
29 #endif
30 #include <pthread.h>
31#endif
32
33#include <sys/stat.h>
34#include <sys/types.h>
35#ifdef WIN32
36#include <libgen.h>
37#endif
38#include <dirent.h>
39#include <fcntl.h>
40#include <unistd.h> /* getuid getpid close */
41
42#include "transmission.h"
43#include "list.h"
44#include "platform.h"
45#include "utils.h"
46
47#include <event.h>
48
49/***
50****  THREADS
51***/
52
53#ifdef WIN32
54typedef DWORD tr_thread_id;
55#else
56typedef pthread_t tr_thread_id;
57#endif
58
59static tr_thread_id
60tr_getCurrentThread( void )
61{
62#ifdef WIN32
63    return GetCurrentThreadId( );
64#else
65    return pthread_self( );
66#endif
67}
68
69static int
70tr_areThreadsEqual( tr_thread_id a,
71                    tr_thread_id b )
72{
73#ifdef WIN32
74    return a == b;
75#else
76    return pthread_equal( a, b );
77#endif
78}
79
80struct tr_thread
81{
82    void            ( * func )( void * );
83    void *          arg;
84    tr_thread_id    thread;
85#ifdef WIN32
86    HANDLE          thread_handle;
87#endif
88};
89
90int
91tr_amInThread( const tr_thread * t )
92{
93    return tr_areThreadsEqual( tr_getCurrentThread( ), t->thread );
94}
95
96#ifdef WIN32
97 #define ThreadFuncReturnType unsigned WINAPI
98#else
99 #define ThreadFuncReturnType void
100#endif
101
102static ThreadFuncReturnType
103ThreadFunc( void * _t )
104{
105    tr_thread * t = _t;
106
107    t->func( t->arg );
108
109#ifdef WIN32
110    _endthreadex( 0 );
111    return 0;
112#endif
113}
114
115tr_thread *
116tr_threadNew( void   ( *func )(void *),
117              void * arg )
118{
119    tr_thread * t = tr_new0( tr_thread, 1 );
120
121    t->func = func;
122    t->arg  = arg;
123
124#ifdef WIN32
125    {
126        unsigned int id;
127        t->thread_handle =
128            (HANDLE) _beginthreadex( NULL, 0, &ThreadFunc, t, 0,
129                                     &id );
130        t->thread = (DWORD) id;
131    }
132#else
133    pthread_create( &t->thread, NULL, ( void * ( * )(
134                                           void * ) )ThreadFunc, t );
135#endif
136
137    return t;
138}
139
140/***
141****  LOCKS
142***/
143
144struct tr_lock
145{
146    int                 depth;
147#ifdef WIN32
148    CRITICAL_SECTION    lock;
149    DWORD               lockThread;
150#else
151    pthread_mutex_t     lock;
152    pthread_t           lockThread;
153#endif
154};
155
156tr_lock*
157tr_lockNew( void )
158{
159    tr_lock *           l = tr_new0( tr_lock, 1 );
160
161#ifdef WIN32
162    InitializeCriticalSection( &l->lock ); /* supports recursion */
163#else
164    pthread_mutexattr_t attr;
165    pthread_mutexattr_init( &attr );
166    pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
167    pthread_mutex_init( &l->lock, &attr );
168#endif
169
170    return l;
171}
172
173void
174tr_lockFree( tr_lock * l )
175{
176#ifdef WIN32
177    DeleteCriticalSection( &l->lock );
178#else
179    pthread_mutex_destroy( &l->lock );
180#endif
181    tr_free( l );
182}
183
184void
185tr_lockLock( tr_lock * l )
186{
187#ifdef WIN32
188    EnterCriticalSection( &l->lock );
189#else
190    pthread_mutex_lock( &l->lock );
191#endif
192    assert( l->depth >= 0 );
193    if( l->depth )
194        assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
195    l->lockThread = tr_getCurrentThread( );
196    ++l->depth;
197}
198
199int
200tr_lockHave( const tr_lock * l )
201{
202    return ( l->depth > 0 )
203           && ( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
204}
205
206void
207tr_lockUnlock( tr_lock * l )
208{
209    assert( l->depth > 0 );
210    assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
211
212    --l->depth;
213    assert( l->depth >= 0 );
214#ifdef WIN32
215    LeaveCriticalSection( &l->lock );
216#else
217    pthread_mutex_unlock( &l->lock );
218#endif
219}
220
221/***
222****  PATHS
223***/
224
225#ifndef WIN32
226 #include <pwd.h>
227#endif
228
229static const char *
230getHomeDir( void )
231{
232    static char * home = NULL;
233
234    if( !home )
235    {
236        home = tr_strdup( getenv( "HOME" ) );
237
238        if( !home )
239        {
240#ifdef WIN32
241            char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
242            *appdata = '\0';
243            SHGetFolderPath( NULL, CSIDL_MYDOCUMENTS, NULL, 0, appdata );
244            home = tr_strdup( appdata );
245#else
246            struct passwd * pw = getpwuid( getuid( ) );
247            if( pw )
248                home = tr_strdup( pw->pw_dir );
249            endpwent( );
250#endif
251        }
252
253        if( !home )
254            home = tr_strdup( "" );
255    }
256
257    return home;
258}
259
260static const char *
261getOldConfigDir( void )
262{
263    static char * path = NULL;
264
265    if( !path )
266    {
267#ifdef SYS_DARWIN
268        path = tr_buildPath( getHomeDir( ), "Library",
269                              "Application Support",
270                              "Transmission", NULL );
271#elif defined( WIN32 )
272        char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
273        SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
274        path = tr_buildPath( appdata, "Transmission", NULL );
275#else
276        path = tr_buildPath( getHomeDir( ), ".transmission", NULL );
277#endif
278    }
279
280    return path;
281}
282
283#if defined(SYS_DARWIN) || defined(WIN32)
284 #define RESUME_SUBDIR  "Resume"
285 #define TORRENT_SUBDIR "Torrents"
286#else
287 #define RESUME_SUBDIR  "resume"
288 #define TORRENT_SUBDIR "torrents"
289#endif
290
291static const char *
292getOldTorrentsDir( void )
293{
294    static char * path = NULL;
295
296    if( !path )
297        path = tr_buildPath( getOldConfigDir( ), TORRENT_SUBDIR, NULL );
298
299    return path;
300}
301
302static const char *
303getOldCacheDir( void )
304{
305    static char * path = NULL;
306
307    if( !path )
308    {
309#if defined( WIN32 )
310        path = tr_buildPath( getOldConfigDir( ), "Cache", NULL );
311#elif defined( SYS_DARWIN )
312        path = tr_buildPath( getHomeDir( ), "Library", "Caches", "Transmission", NULL );
313#else
314        path = tr_buildPath( getOldConfigDir( ), "cache", NULL );
315#endif
316    }
317
318    return path;
319}
320
321static void
322moveFiles( const char * oldDir,
323           const char * newDir )
324{
325    if( oldDir && newDir && strcmp( oldDir, newDir ) )
326    {
327        DIR * dirh = opendir( oldDir );
328        if( dirh )
329        {
330            int             count = 0;
331            struct dirent * dirp;
332            while( ( dirp = readdir( dirh ) ) )
333            {
334                if( strcmp( dirp->d_name,
335                            "." ) && strcmp( dirp->d_name, ".." ) )
336                {
337                    char * o = tr_buildPath( oldDir, dirp->d_name, NULL );
338                    char * n = tr_buildPath( newDir, dirp->d_name, NULL );
339                    rename( o, n );
340                    ++count;
341                    tr_free( n );
342                    tr_free( o );
343                }
344            }
345
346            if( count )
347                tr_inf( _( "Migrated %1$d files from \"%2$s\" to \"%3$s\"" ),
348                        count, oldDir, newDir );
349            closedir( dirh );
350        }
351    }
352}
353
354static void
355migrateFiles( const tr_session * session )
356{
357    static int migrated = FALSE;
358
359    if( !migrated )
360    {
361        const char * oldDir;
362        const char * newDir;
363        migrated = TRUE;
364
365        oldDir = getOldTorrentsDir( );
366        newDir = tr_getTorrentDir( session );
367        moveFiles( oldDir, newDir );
368
369        oldDir = getOldCacheDir( );
370        newDir = tr_getResumeDir( session );
371        moveFiles( oldDir, newDir );
372    }
373}
374
375void
376tr_setConfigDir( tr_session * session,
377                 const char * configDir )
378{
379    char * path;
380
381    session->configDir = tr_strdup( configDir );
382
383    path = tr_buildPath( configDir, RESUME_SUBDIR, NULL );
384    tr_mkdirp( path, 0777 );
385    session->resumeDir = path;
386
387    path = tr_buildPath( configDir, TORRENT_SUBDIR, NULL );
388    tr_mkdirp( path, 0777 );
389    session->torrentDir = path;
390
391    migrateFiles( session );
392}
393
394const char *
395tr_sessionGetConfigDir( const tr_session * session )
396{
397    return session->configDir;
398}
399
400const char *
401tr_getTorrentDir( const tr_session * session )
402{
403    return session->torrentDir;
404}
405
406const char *
407tr_getResumeDir( const tr_session * session )
408{
409    return session->resumeDir;
410}
411
412const char*
413tr_getDefaultConfigDir( const char * appname )
414{
415    static char * s = NULL;
416
417    if( !appname || !*appname )
418        appname = "Transmission";
419
420    if( !s )
421    {
422        if( ( s = getenv( "TRANSMISSION_HOME" ) ) )
423        {
424            s = tr_strdup( s );
425        }
426        else
427        {
428#ifdef SYS_DARWIN
429            s = tr_buildPath( getHomeDir( ), "Library", "Application Support",
430                              appname, NULL );
431#elif defined( WIN32 )
432            char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
433            SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
434            s = tr_buildPath( appdata, appname, NULL );
435#else
436            if( ( s = getenv( "XDG_CONFIG_HOME" ) ) )
437                s = tr_buildPath( s, appname, NULL );
438            else
439                s = tr_buildPath( getHomeDir( ), ".config", appname, NULL );
440#endif
441        }
442    }
443
444    return s;
445}
446
447const char*
448tr_getDefaultDownloadDir( void )
449{
450    static char * s = NULL;
451
452    if( s == NULL )
453        s = tr_buildPath( getHomeDir( ), "Downloads", NULL );
454
455    return s;
456}
457
458/***
459****
460***/
461
462static int
463isClutchDir( const char * path )
464{
465    struct stat sb;
466    char * tmp = tr_buildPath( path, "javascript", "transmission.js", NULL );
467    const int ret = !stat( tmp, &sb );
468    tr_inf( _( "Searching for web interface file \"%s\"" ), tmp );
469    tr_free( tmp );
470    return ret;
471   
472}
473
474const char *
475tr_getClutchDir( const tr_session * session UNUSED )
476{
477    static char * s = NULL;
478
479    if( !s )
480    {
481        if( ( s = getenv( "CLUTCH_HOME" ) ) )
482        {
483            s = tr_strdup( s );
484        }
485        else if( ( s = getenv( "TRANSMISSION_WEB_HOME" ) ) )
486        {
487            s = tr_strdup( s );
488        }
489        else
490        {
491
492#ifdef SYS_DARWIN
493
494            CFURLRef appURL = CFBundleCopyBundleURL( CFBundleGetMainBundle( ) );
495            CFStringRef appRef = CFURLCopyFileSystemPath( appURL,
496                                                         kCFURLPOSIXPathStyle );
497            const char * appString = CFStringGetCStringPtr( appRef,
498                                         CFStringGetFastestEncoding( appRef ) );
499            CFRelease( appURL );
500            CFRelease( appRef );
501
502            s = tr_buildPath( appString, "Contents", "Resources", "web", NULL );
503
504#elif defined( WIN32 )
505
506            /* SHGetFolderPath explicitly requires MAX_PATH length */
507            char dir[MAX_PATH];
508           
509            /* Generally, Web interface should be stored in a Web subdir of
510             * calling executable dir. */
511
512            if( s == NULL ) { 
513                /* First, we should check personal AppData/Transmission/Web */
514                SHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, NULL, 0, dir );
515                s = tr_buildPath( dir, "Transmission", "Web", NULL );
516                if( !isClutchDir( s ) ) {
517                    tr_free( s );
518                    s = NULL;
519                }
520            }
521
522            if( s == NULL ) {
523                /* check personal AppData */
524                SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, dir );
525                s = tr_buildPath( dir, "Transmission", "Web", NULL );
526                if( !isClutchDir( s ) ) {
527                    tr_free( s );
528                    s = NULL;
529                }
530            }
531
532            if( s == NULL) {
533                /* check calling module place */
534                GetModuleFileName( GetModuleHandle( NULL ), dir, sizeof( dir ) );
535                s = tr_buildPath( dirname( dir ), "Web", NULL );
536                if( !isClutchDir( s ) ) {
537                    tr_free( s );
538                    s = NULL;
539                }
540            }
541
542#else /* everyone else, follow the XDG spec */
543
544            tr_list *candidates = NULL, *l;
545            const char * tmp;
546
547            /* XDG_DATA_HOME should be the first in the list of candidates */
548            tmp = getenv( "XDG_DATA_HOME" );
549            if( tmp && *tmp )
550                tr_list_append( &candidates, tr_strdup( tmp ) );
551            else {
552                char * dhome = tr_buildPath( getHomeDir( ), ".local", "share", NULL );
553                tr_list_append( &candidates, dhome );
554            }
555
556            /* XDG_DATA_DIRS are the backup directories */
557            {
558                struct evbuffer * buf = evbuffer_new( );
559                evbuffer_add_printf( buf, "%s:", PACKAGE_DATA_DIR );
560                if(( tmp = getenv( "XDG_DATA_DIRS" )))
561                    evbuffer_add_printf( buf, "%s:", tmp );
562                evbuffer_add_printf( buf, "%s:", "/usr/local/share" );
563                evbuffer_add_printf( buf, "%s:", "/usr/share" );
564                tmp = (const char*) EVBUFFER_DATA( buf );
565                while( tmp && *tmp ) {
566                    const char * end = strchr( tmp, ':' );
567                    if( end ) {
568                        tr_list_append( &candidates, tr_strndup( tmp, end - tmp ) );
569                        tmp = end + 1;
570                    } else if( tmp && *tmp ) {
571                        tr_list_append( &candidates, tr_strdup( tmp ) );
572                        break;
573                    }
574                }
575                evbuffer_free( buf );
576            }
577
578            /* walk through the candidates & look for a match */
579            for( l=candidates; l; l=l->next ) {
580                char * path = tr_buildPath( l->data, "transmission", "web", NULL );
581                const int found = isClutchDir( path );
582                if( found ) {
583                    s = path;
584                    break;
585                }
586                tr_free( path );
587            }
588
589            tr_list_free( &candidates, tr_free );
590
591#endif
592
593        }
594    }
595
596    return s;
597}
598
599/***
600****
601***/
602
603tr_lockfile_state_t
604tr_lockfile( const char * filename )
605{
606    tr_lockfile_state_t ret;
607
608#ifdef WIN32
609
610    HANDLE              file = CreateFile(
611        filename,
612        GENERIC_READ | GENERIC_WRITE,
613        FILE_SHARE_READ |
614        FILE_SHARE_WRITE,
615        NULL,
616        OPEN_ALWAYS,
617        FILE_ATTRIBUTE_NORMAL,
618        NULL );
619    if( file == INVALID_HANDLE_VALUE )
620        ret = TR_LOCKFILE_EOPEN;
621    else if( !LockFile( file, 0, 0, 1, 1 ) )
622        ret = TR_LOCKFILE_ELOCK;
623    else
624        ret = TR_LOCKFILE_SUCCESS;
625
626#else
627
628    int fd = open( filename, O_RDWR | O_CREAT, 0666 );
629    if( fd < 0 )
630        ret = TR_LOCKFILE_EOPEN;
631    else
632    {
633        struct flock lk;
634        memset( &lk, 0,  sizeof( lk ) );
635        lk.l_start = 0;
636        lk.l_len = 0;
637        lk.l_type = F_WRLCK;
638        lk.l_whence = SEEK_SET;
639        if( -1 == fcntl( fd, F_SETLK, &lk ) )
640            ret = TR_LOCKFILE_ELOCK;
641        else
642            ret = TR_LOCKFILE_SUCCESS;
643    }
644
645#endif
646
647    return ret;
648}
649
650#ifdef WIN32
651
652/* The following mmap functions are by Joerg Walter, and were taken from
653 * his paper at: http://www.genesys-e.de/jwalter/mix4win.htm
654 */
655
656#if defined(_MSC_VER)
657__declspec( align( 4 ) ) static LONG volatile g_sl;
658#else
659static LONG volatile g_sl __attribute__ ( ( aligned ( 4 ) ) );
660#endif
661
662/* Wait for spin lock */
663static int
664slwait( LONG volatile *sl )
665{
666    while( InterlockedCompareExchange ( sl, 1, 0 ) != 0 )
667        Sleep ( 0 );
668
669    return 0;
670}
671
672/* Release spin lock */
673static int
674slrelease( LONG volatile *sl )
675{
676    InterlockedExchange ( sl, 0 );
677    return 0;
678}
679
680/* getpagesize for windows */
681static long
682getpagesize( void )
683{
684    static long g_pagesize = 0;
685
686    if( !g_pagesize )
687    {
688        SYSTEM_INFO system_info;
689        GetSystemInfo ( &system_info );
690        g_pagesize = system_info.dwPageSize;
691    }
692    return g_pagesize;
693}
694
695static long
696getregionsize( void )
697{
698    static long g_regionsize = 0;
699
700    if( !g_regionsize )
701    {
702        SYSTEM_INFO system_info;
703        GetSystemInfo ( &system_info );
704        g_regionsize = system_info.dwAllocationGranularity;
705    }
706    return g_regionsize;
707}
708
709void *
710mmap( void *ptr,
711      long  size,
712      long  prot,
713      long  type,
714      long  handle,
715      long  arg )
716{
717    static long g_pagesize;
718    static long g_regionsize;
719
720    /* Wait for spin lock */
721    slwait ( &g_sl );
722    /* First time initialization */
723    if( !g_pagesize )
724        g_pagesize = getpagesize ( );
725    if( !g_regionsize )
726        g_regionsize = getregionsize ( );
727    /* Allocate this */
728    ptr = VirtualAlloc ( ptr, size,
729                         MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
730                         PAGE_READWRITE );
731    if( !ptr )
732    {
733        ptr = (void *) -1;
734        goto mmap_exit;
735    }
736mmap_exit:
737    /* Release spin lock */
738    slrelease ( &g_sl );
739    return ptr;
740}
741
742long
743munmap( void *ptr,
744        long  size )
745{
746    static long g_pagesize;
747    static long g_regionsize;
748    int         rc = -1;
749
750    /* Wait for spin lock */
751    slwait ( &g_sl );
752    /* First time initialization */
753    if( !g_pagesize )
754        g_pagesize = getpagesize ( );
755    if( !g_regionsize )
756        g_regionsize = getregionsize ( );
757    /* Free this */
758    if( !VirtualFree ( ptr, 0,
759                       MEM_RELEASE ) )
760        goto munmap_exit;
761    rc = 0;
762munmap_exit:
763    /* Release spin lock */
764    slrelease ( &g_sl );
765    return rc;
766}
767
768#endif
769
Note: See TracBrowser for help on using the repository browser.