source: trunk/libtransmission/platform.c @ 7407

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

maybe make hudson-t's trunk linux builds happy

  • Property svn:keywords set to Date Rev Author Id
File size: 17.1 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 7407 2008-12-16 01:11:54Z 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_session * session )
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( session );
365        moveFiles( oldDir, newDir );
366
367        oldDir = getOldCacheDir( );
368        newDir = tr_getResumeDir( session );
369        moveFiles( oldDir, newDir );
370    }
371}
372
373void
374tr_setConfigDir( tr_session * session,
375                 const char * configDir )
376{
377    char * path;
378
379    session->configDir = tr_strdup( configDir );
380
381    path = tr_buildPath( configDir, RESUME_SUBDIR, NULL );
382    tr_mkdirp( path, 0777 );
383    session->resumeDir = path;
384
385    path = tr_buildPath( configDir, TORRENT_SUBDIR, NULL );
386    tr_mkdirp( path, 0777 );
387    session->torrentDir = path;
388
389    migrateFiles( session );
390}
391
392const char *
393tr_sessionGetConfigDir( const tr_session * session )
394{
395    return session->configDir;
396}
397
398const char *
399tr_getTorrentDir( const tr_session * session )
400{
401    return session->torrentDir;
402}
403
404const char *
405tr_getResumeDir( const tr_session * session )
406{
407    return session->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            {
556                const char * pkg = PACKAGE_DATA_DIR;
557                const char * xdg = getenv( "XDG_DATA_DIRS" );
558                const char * fallback = "/usr/local/share:/usr/share";
559                char * buf = tr_strdup_printf( "%s:%s:%s", (pkg?pkg:""), (xdg?xdg:""), fallback );
560                tmp = buf;
561                while( tmp && *tmp ) {
562                    const char * end = strchr( tmp, ':' );
563                    if( end ) {
564                        if( ( end - tmp ) > 1 )
565                            tr_list_append( &candidates, tr_strndup( tmp, end - tmp ) );
566                        tmp = end + 1;
567                    } else if( tmp && *tmp ) {
568                        tr_list_append( &candidates, tr_strdup( tmp ) );
569                        break;
570                    }
571                }
572                tr_free( buf );
573            }
574
575            /* walk through the candidates & look for a match */
576            for( l=candidates; l; l=l->next ) {
577                char * path = tr_buildPath( l->data, "transmission", "web", NULL );
578                const int found = isClutchDir( path );
579                if( found ) {
580                    s = path;
581                    break;
582                }
583                tr_free( path );
584            }
585
586            tr_list_free( &candidates, tr_free );
587
588#endif
589
590        }
591    }
592
593    return s;
594}
595
596/***
597****
598***/
599
600tr_lockfile_state_t
601tr_lockfile( const char * filename )
602{
603    tr_lockfile_state_t ret;
604
605#ifdef WIN32
606
607    HANDLE              file = CreateFile(
608        filename,
609        GENERIC_READ | GENERIC_WRITE,
610        FILE_SHARE_READ |
611        FILE_SHARE_WRITE,
612        NULL,
613        OPEN_ALWAYS,
614        FILE_ATTRIBUTE_NORMAL,
615        NULL );
616    if( file == INVALID_HANDLE_VALUE )
617        ret = TR_LOCKFILE_EOPEN;
618    else if( !LockFile( file, 0, 0, 1, 1 ) )
619        ret = TR_LOCKFILE_ELOCK;
620    else
621        ret = TR_LOCKFILE_SUCCESS;
622
623#else
624
625    int fd = open( filename, O_RDWR | O_CREAT, 0666 );
626    if( fd < 0 )
627        ret = TR_LOCKFILE_EOPEN;
628    else
629    {
630        struct flock lk;
631        memset( &lk, 0,  sizeof( lk ) );
632        lk.l_start = 0;
633        lk.l_len = 0;
634        lk.l_type = F_WRLCK;
635        lk.l_whence = SEEK_SET;
636        if( -1 == fcntl( fd, F_SETLK, &lk ) )
637            ret = TR_LOCKFILE_ELOCK;
638        else
639            ret = TR_LOCKFILE_SUCCESS;
640    }
641
642#endif
643
644    return ret;
645}
646
647#ifdef WIN32
648
649/* The following mmap functions are by Joerg Walter, and were taken from
650 * his paper at: http://www.genesys-e.de/jwalter/mix4win.htm
651 */
652
653#if defined(_MSC_VER)
654__declspec( align( 4 ) ) static LONG volatile g_sl;
655#else
656static LONG volatile g_sl __attribute__ ( ( aligned ( 4 ) ) );
657#endif
658
659/* Wait for spin lock */
660static int
661slwait( LONG volatile *sl )
662{
663    while( InterlockedCompareExchange ( sl, 1, 0 ) != 0 )
664        Sleep ( 0 );
665
666    return 0;
667}
668
669/* Release spin lock */
670static int
671slrelease( LONG volatile *sl )
672{
673    InterlockedExchange ( sl, 0 );
674    return 0;
675}
676
677/* getpagesize for windows */
678static long
679getpagesize( void )
680{
681    static long g_pagesize = 0;
682
683    if( !g_pagesize )
684    {
685        SYSTEM_INFO system_info;
686        GetSystemInfo ( &system_info );
687        g_pagesize = system_info.dwPageSize;
688    }
689    return g_pagesize;
690}
691
692static long
693getregionsize( void )
694{
695    static long g_regionsize = 0;
696
697    if( !g_regionsize )
698    {
699        SYSTEM_INFO system_info;
700        GetSystemInfo ( &system_info );
701        g_regionsize = system_info.dwAllocationGranularity;
702    }
703    return g_regionsize;
704}
705
706void *
707mmap( void *ptr,
708      long  size,
709      long  prot,
710      long  type,
711      long  handle,
712      long  arg )
713{
714    static long g_pagesize;
715    static long g_regionsize;
716
717    /* Wait for spin lock */
718    slwait ( &g_sl );
719    /* First time initialization */
720    if( !g_pagesize )
721        g_pagesize = getpagesize ( );
722    if( !g_regionsize )
723        g_regionsize = getregionsize ( );
724    /* Allocate this */
725    ptr = VirtualAlloc ( ptr, size,
726                         MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
727                         PAGE_READWRITE );
728    if( !ptr )
729    {
730        ptr = (void *) -1;
731        goto mmap_exit;
732    }
733mmap_exit:
734    /* Release spin lock */
735    slrelease ( &g_sl );
736    return ptr;
737}
738
739long
740munmap( void *ptr,
741        long  size )
742{
743    static long g_pagesize;
744    static long g_regionsize;
745    int         rc = -1;
746
747    /* Wait for spin lock */
748    slwait ( &g_sl );
749    /* First time initialization */
750    if( !g_pagesize )
751        g_pagesize = getpagesize ( );
752    if( !g_regionsize )
753        g_regionsize = getregionsize ( );
754    /* Free this */
755    if( !VirtualFree ( ptr, 0,
756                       MEM_RELEASE ) )
757        goto munmap_exit;
758    rc = 0;
759munmap_exit:
760    /* Release spin lock */
761    slrelease ( &g_sl );
762    return rc;
763}
764
765#endif
766
Note: See TracBrowser for help on using the repository browser.