source: trunk/libtransmission/platform.c @ 7232

Last change on this file since 7232 was 7232, checked in by livings124, 12 years ago

BeOS and Amiga support is, at least for now, a lost cause and hasn't worked for long over a year - removing from the source (if you for whatever unknown reason still need this, get it through SVN)

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