source: trunk/libtransmission/platform.c @ 7367

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

(trunk) #1559: Simplify tr_sessionInitFull

  • 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 7367 2008-12-13 23:17:36Z 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
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( const char * appname )
412{
413    static char * s = NULL;
414
415    if( !appname || !*appname )
416        appname = "Transmission";
417
418    if( !s )
419    {
420        if( ( s = getenv( "TRANSMISSION_HOME" ) ) )
421        {
422            s = tr_strdup( s );
423        }
424        else
425        {
426#ifdef SYS_DARWIN
427            s = tr_buildPath( getHomeDir( ), "Library", "Application Support",
428                              appname, NULL );
429#elif defined( WIN32 )
430            char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
431            SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
432            s = tr_buildPath( appdata, appname, NULL );
433#else
434            if( ( s = getenv( "XDG_CONFIG_HOME" ) ) )
435                s = tr_buildPath( s, appname, NULL );
436            else
437                s = tr_buildPath( getHomeDir( ), ".config", appname, NULL );
438#endif
439        }
440    }
441
442    return s;
443}
444
445const char*
446tr_getDefaultDownloadDir( void )
447{
448    static char * s = NULL;
449
450    if( s == NULL )
451        s = tr_buildPath( getHomeDir( ), "Downloads", NULL );
452
453    return s;
454}
455
456/***
457****
458***/
459
460static int
461isClutchDir( const char * path )
462{
463    struct stat sb;
464    char * tmp = tr_buildPath( path, "javascript", "transmission.js", NULL );
465    const int ret = !stat( tmp, &sb );
466    tr_inf( _( "Searching for web interface file \"%s\"" ), tmp );
467    tr_free( tmp );
468    return ret;
469   
470}
471
472const char *
473tr_getClutchDir( const tr_session * session UNUSED )
474{
475    static char * s = NULL;
476
477    if( !s )
478    {
479        if( ( s = getenv( "CLUTCH_HOME" ) ) )
480        {
481            s = tr_strdup( s );
482        }
483        else if( ( s = getenv( "TRANSMISSION_WEB_HOME" ) ) )
484        {
485            s = tr_strdup( s );
486        }
487        else
488        {
489
490#ifdef SYS_DARWIN
491
492            CFURLRef appURL = CFBundleCopyBundleURL( CFBundleGetMainBundle( ) );
493            CFStringRef appRef = CFURLCopyFileSystemPath( appURL,
494                                                         kCFURLPOSIXPathStyle );
495            const char * appString = CFStringGetCStringPtr( appRef,
496                                         CFStringGetFastestEncoding( appRef ) );
497            CFRelease( appURL );
498            CFRelease( appRef );
499
500            s = tr_buildPath( appString, "Contents", "Resources", "web", NULL );
501
502#elif defined( WIN32 )
503
504            /* SHGetFolderPath explicitly requires MAX_PATH length */
505            char dir[MAX_PATH];
506           
507            /* Generally, Web interface should be stored in a Web subdir of
508             * calling executable dir. */
509
510            if( s == NULL ) { 
511                /* First, we should check personal AppData/Transmission/Web */
512                SHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, NULL, 0, dir );
513                s = tr_buildPath( dir, "Transmission", "Web", NULL );
514                if( !isClutchDir( s ) ) {
515                    tr_free( s );
516                    s = NULL;
517                }
518            }
519
520            if( s == NULL ) {
521                /* check personal AppData */
522                SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, dir );
523                s = tr_buildPath( dir, "Transmission", "Web", NULL );
524                if( !isClutchDir( s ) ) {
525                    tr_free( s );
526                    s = NULL;
527                }
528            }
529
530            if( s == NULL) {
531                /* check calling module place */
532                GetModuleFileName( GetModuleHandle( NULL ), dir, sizeof( dir ) );
533                s = tr_buildPath( dirname( dir ), "Web", NULL );
534                if( !isClutchDir( s ) ) {
535                    tr_free( s );
536                    s = NULL;
537                }
538            }
539
540#else /* everyone else, follow the XDG spec */
541
542            tr_list *candidates = NULL, *l;
543            const char * tmp;
544
545            /* XDG_DATA_HOME should be the first in the list of candidates */
546            tmp = getenv( "XDG_DATA_HOME" );
547            if( tmp && *tmp )
548                tr_list_append( &candidates, tr_strdup( tmp ) );
549            else {
550                char * dhome = tr_buildPath( getHomeDir( ), ".local", "share", NULL );
551                tr_list_append( &candidates, dhome );
552            }
553
554            /* XDG_DATA_DIRS are the backup directories */
555            tmp = getenv( "XDG_DATA_DIRS" );
556            if( !tmp || !*tmp )
557                tmp =  PACKAGE_DATA_DIR ":/usr/local/share/:/usr/share/";
558            while( tmp && *tmp ) {
559                const char * end = strchr( tmp, ':' );
560                if( end ) {
561                    tr_list_append( &candidates, tr_strndup( tmp, end - tmp ) );
562                    tmp = end + 1;
563                } else {
564                    tr_list_append( &candidates, tr_strdup( tmp ) );
565                    break;
566                }
567            }
568
569            /* walk through the candidates & look for a match */
570            for( l=candidates; l; l=l->next ) {
571                char * path = tr_buildPath( l->data, "transmission", "web", NULL );
572                const int found = isClutchDir( path );
573                if( found ) {
574                    s = path;
575                    break;
576                }
577                tr_free( path );
578            }
579
580            tr_list_free( &candidates, tr_free );
581
582#endif
583
584        }
585    }
586
587    return s;
588}
589
590/***
591****
592***/
593
594tr_lockfile_state_t
595tr_lockfile( const char * filename )
596{
597    tr_lockfile_state_t ret;
598
599#ifdef WIN32
600
601    HANDLE              file = CreateFile(
602        filename,
603        GENERIC_READ | GENERIC_WRITE,
604        FILE_SHARE_READ |
605        FILE_SHARE_WRITE,
606        NULL,
607        OPEN_ALWAYS,
608        FILE_ATTRIBUTE_NORMAL,
609        NULL );
610    if( file == INVALID_HANDLE_VALUE )
611        ret = TR_LOCKFILE_EOPEN;
612    else if( !LockFile( file, 0, 0, 1, 1 ) )
613        ret = TR_LOCKFILE_ELOCK;
614    else
615        ret = TR_LOCKFILE_SUCCESS;
616
617#else
618
619    int fd = open( filename, O_RDWR | O_CREAT, 0666 );
620    if( fd < 0 )
621        ret = TR_LOCKFILE_EOPEN;
622    else
623    {
624        struct flock lk;
625        memset( &lk, 0,  sizeof( lk ) );
626        lk.l_start = 0;
627        lk.l_len = 0;
628        lk.l_type = F_WRLCK;
629        lk.l_whence = SEEK_SET;
630        if( -1 == fcntl( fd, F_SETLK, &lk ) )
631            ret = TR_LOCKFILE_ELOCK;
632        else
633            ret = TR_LOCKFILE_SUCCESS;
634    }
635
636#endif
637
638    return ret;
639}
640
641#ifdef WIN32
642
643/* The following mmap functions are by Joerg Walter, and were taken from
644 * his paper at: http://www.genesys-e.de/jwalter/mix4win.htm
645 */
646
647#if defined(_MSC_VER)
648__declspec( align( 4 ) ) static LONG volatile g_sl;
649#else
650static LONG volatile g_sl __attribute__ ( ( aligned ( 4 ) ) );
651#endif
652
653/* Wait for spin lock */
654static int
655slwait( LONG volatile *sl )
656{
657    while( InterlockedCompareExchange ( sl, 1, 0 ) != 0 )
658        Sleep ( 0 );
659
660    return 0;
661}
662
663/* Release spin lock */
664static int
665slrelease( LONG volatile *sl )
666{
667    InterlockedExchange ( sl, 0 );
668    return 0;
669}
670
671/* getpagesize for windows */
672static long
673getpagesize( void )
674{
675    static long g_pagesize = 0;
676
677    if( !g_pagesize )
678    {
679        SYSTEM_INFO system_info;
680        GetSystemInfo ( &system_info );
681        g_pagesize = system_info.dwPageSize;
682    }
683    return g_pagesize;
684}
685
686static long
687getregionsize( void )
688{
689    static long g_regionsize = 0;
690
691    if( !g_regionsize )
692    {
693        SYSTEM_INFO system_info;
694        GetSystemInfo ( &system_info );
695        g_regionsize = system_info.dwAllocationGranularity;
696    }
697    return g_regionsize;
698}
699
700void *
701mmap( void *ptr,
702      long  size,
703      long  prot,
704      long  type,
705      long  handle,
706      long  arg )
707{
708    static long g_pagesize;
709    static long g_regionsize;
710
711    /* Wait for spin lock */
712    slwait ( &g_sl );
713    /* First time initialization */
714    if( !g_pagesize )
715        g_pagesize = getpagesize ( );
716    if( !g_regionsize )
717        g_regionsize = getregionsize ( );
718    /* Allocate this */
719    ptr = VirtualAlloc ( ptr, size,
720                         MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
721                         PAGE_READWRITE );
722    if( !ptr )
723    {
724        ptr = (void *) -1;
725        goto mmap_exit;
726    }
727mmap_exit:
728    /* Release spin lock */
729    slrelease ( &g_sl );
730    return ptr;
731}
732
733long
734munmap( void *ptr,
735        long  size )
736{
737    static long g_pagesize;
738    static long g_regionsize;
739    int         rc = -1;
740
741    /* Wait for spin lock */
742    slwait ( &g_sl );
743    /* First time initialization */
744    if( !g_pagesize )
745        g_pagesize = getpagesize ( );
746    if( !g_regionsize )
747        g_regionsize = getregionsize ( );
748    /* Free this */
749    if( !VirtualFree ( ptr, 0,
750                       MEM_RELEASE ) )
751        goto munmap_exit;
752    rc = 0;
753munmap_exit:
754    /* Release spin lock */
755    slrelease ( &g_sl );
756    return rc;
757}
758
759#endif
760
Note: See TracBrowser for help on using the repository browser.