source: trunk/libtransmission/platform.c @ 9868

Last change on this file since 9868 was 9868, checked in by charles, 11 years ago

happy new year!

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