source: trunk/libtransmission/platform.c @ 6861

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

(rpc) if libt can't find the Clutch files, give a helpful 404 message for end-users and binary packagers about how to use CLUTCH_HOME and PACKAGE_DATA_DIR.

  • Property svn:keywords set to Date Rev Author Id
File size: 17.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 6861 2008-10-07 01:25:29Z charles $
11 */
12
13#ifdef __BEOS__
14 #include <signal.h>
15 #include <fs_info.h>
16 #include <FindDirectory.h>
17 #include <kernel/OS.h>
18 #define BEOS_MAX_THREADS 256
19#elif defined( WIN32 )
20 #include <windows.h>
21 #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_PROFILE */
22#else
23 #ifdef SYS_DARWIN
24  #include <CoreFoundation/CoreFoundation.h>
25 #endif
26
27 #define _XOPEN_SOURCE 500  /* needed for recursive locks. */
28 #ifndef __USE_UNIX98
29  #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
30 #endif
31 #include <pthread.h>
32#endif
33
34#include <assert.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38
39#include <sys/stat.h>
40#include <sys/types.h>
41#include <dirent.h>
42#include <fcntl.h>
43#include <unistd.h> /* getuid getpid close */
44
45#include "transmission.h"
46#include "list.h"
47#include "platform.h"
48#include "utils.h"
49
50/***
51****  THREADS
52***/
53
54#ifdef __BEOS__
55typedef thread_id tr_thread_id;
56#elif defined( WIN32 )
57typedef DWORD tr_thread_id;
58#else
59typedef pthread_t tr_thread_id;
60#endif
61
62static tr_thread_id
63tr_getCurrentThread( void )
64{
65#ifdef __BEOS__
66    return find_thread( NULL );
67#elif defined( WIN32 )
68    return GetCurrentThreadId( );
69#else
70    return pthread_self( );
71#endif
72}
73
74static int
75tr_areThreadsEqual( tr_thread_id a,
76                    tr_thread_id b )
77{
78#ifdef __BEOS__
79    return a == b;
80#elif defined( WIN32 )
81    return a == b;
82#else
83    return pthread_equal( a, b );
84#endif
85}
86
87struct tr_thread
88{
89    void            ( * func )( void * );
90    void *          arg;
91    tr_thread_id    thread;
92#ifdef WIN32
93    HANDLE          thread_handle;
94#endif
95};
96
97int
98tr_amInThread( const tr_thread * t )
99{
100    return tr_areThreadsEqual( tr_getCurrentThread( ), t->thread );
101}
102
103#ifdef WIN32
104 #define ThreadFuncReturnType unsigned WINAPI
105#else
106 #define ThreadFuncReturnType void
107#endif
108
109static ThreadFuncReturnType
110ThreadFunc( void * _t )
111{
112    tr_thread * t = _t;
113
114#ifdef __BEOS__
115    /* This is required because on BeOS, SIGINT is sent to each thread,
116       which kills them not nicely */
117    signal( SIGINT, SIG_IGN );
118#endif
119
120    t->func( t->arg );
121
122#ifdef WIN32
123    _endthreadex( 0 );
124    return 0;
125#endif
126}
127
128tr_thread *
129tr_threadNew( void   ( *func )(void *),
130              void * arg )
131{
132    tr_thread * t = tr_new0( tr_thread, 1 );
133
134    t->func = func;
135    t->arg  = arg;
136
137#ifdef __BEOS__
138    t->thread =
139        spawn_thread( (void*)ThreadFunc, "beos thread", B_NORMAL_PRIORITY,
140                     t );
141    resume_thread( t->thread );
142#elif defined( WIN32 )
143    {
144        unsigned int id;
145        t->thread_handle =
146            (HANDLE) _beginthreadex( NULL, 0, &ThreadFunc, t, 0,
147                                     &id );
148        t->thread = (DWORD) id;
149    }
150#else
151    pthread_create( &t->thread, NULL, ( void * ( * )(
152                                           void * ) )ThreadFunc, t );
153#endif
154
155    return t;
156}
157
158/***
159****  LOCKS
160***/
161
162struct tr_lock
163{
164    int                 depth;
165#ifdef __BEOS__
166    sem_id              lock;
167    thread_id           lockThread;
168#elif defined( WIN32 )
169    CRITICAL_SECTION    lock;
170    DWORD               lockThread;
171#else
172    pthread_mutex_t     lock;
173    pthread_t           lockThread;
174#endif
175};
176
177tr_lock*
178tr_lockNew( void )
179{
180    tr_lock *           l = tr_new0( tr_lock, 1 );
181
182#ifdef __BEOS__
183    l->lock = create_sem( 1, "" );
184#elif defined( WIN32 )
185    InitializeCriticalSection( &l->lock ); /* supports recursion */
186#else
187    pthread_mutexattr_t attr;
188    pthread_mutexattr_init( &attr );
189    pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
190    pthread_mutex_init( &l->lock, &attr );
191#endif
192
193    return l;
194}
195
196void
197tr_lockFree( tr_lock * l )
198{
199#ifdef __BEOS__
200    delete_sem( l->lock );
201#elif defined( WIN32 )
202    DeleteCriticalSection( &l->lock );
203#else
204    pthread_mutex_destroy( &l->lock );
205#endif
206    tr_free( l );
207}
208
209void
210tr_lockLock( tr_lock * l )
211{
212#ifdef __BEOS__
213    acquire_sem( l->lock );
214#elif defined( WIN32 )
215    EnterCriticalSection( &l->lock );
216#else
217    pthread_mutex_lock( &l->lock );
218#endif
219    assert( l->depth >= 0 );
220    if( l->depth )
221        assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
222    l->lockThread = tr_getCurrentThread( );
223    ++l->depth;
224}
225
226int
227tr_lockHave( const tr_lock * l )
228{
229    return ( l->depth > 0 )
230           && ( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
231}
232
233void
234tr_lockUnlock( tr_lock * l )
235{
236    assert( l->depth > 0 );
237    assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
238
239    --l->depth;
240    assert( l->depth >= 0 );
241#ifdef __BEOS__
242    release_sem( l->lock );
243#elif defined( WIN32 )
244    LeaveCriticalSection( &l->lock );
245#else
246    pthread_mutex_unlock( &l->lock );
247#endif
248}
249
250/***
251****  PATHS
252***/
253
254#if !defined( WIN32 ) && !defined( __BEOS__ ) && !defined( __AMIGAOS4__ )
255 #include <pwd.h>
256#endif
257
258static const char *
259getHomeDir( void )
260{
261    static char * home = NULL;
262
263    if( !home )
264    {
265        home = tr_strdup( getenv( "HOME" ) );
266
267        if( !home )
268        {
269#ifdef WIN32
270            SHGetFolderPath( NULL, CSIDL_PROFILE, NULL, 0, home );
271#elif defined( __BEOS__ ) || defined( __AMIGAOS4__ )
272            home = tr_strdup( "" );
273#else
274            struct passwd * pw = getpwuid( getuid( ) );
275            if( pw )
276                home = tr_strdup( pw->pw_dir );
277            endpwent( );
278#endif
279        }
280
281        if( !home )
282            home = tr_strdup( "" );
283    }
284
285    return home;
286}
287
288static const char *
289getOldConfigDir( void )
290{
291    static char * path = NULL;
292
293    if( !path )
294    {
295        char buf[MAX_PATH_LENGTH];
296#ifdef __BEOS__
297        find_directory( B_USER_SETTINGS_DIRECTORY,
298                       dev_for_path( "/boot" ), true,
299                       buf, sizeof( buf ) );
300        strcat( buf, "/Transmission" );
301#elif defined( SYS_DARWIN )
302        tr_buildPath ( buf, sizeof( buf ), getHomeDir( ),
303                       "Library", "Application Support",
304                       "Transmission", NULL );
305#elif defined( __AMIGAOS4__ )
306        tr_strlcpy( buf, "PROGDIR:.transmission", sizeof( buf ) );
307#elif defined( WIN32 )
308        char appdata[MAX_PATH_LENGTH];
309        SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
310        tr_buildPath( buf, sizeof( buf ),
311                      appdata, "Transmission", NULL );
312#else
313        tr_buildPath ( buf, sizeof( buf ),
314                       getHomeDir( ), ".transmission", NULL );
315#endif
316        path = tr_strdup( buf );
317    }
318
319    return path;
320}
321
322static const char *
323getOldTorrentsDir( void )
324{
325    static char * path = NULL;
326
327    if( !path )
328    {
329        char         buf[MAX_PATH_LENGTH];
330        const char * p = getOldConfigDir( );
331#if defined( __BEOS__ ) || defined( WIN32 ) || defined( SYS_DARWIN )
332        tr_buildPath( buf, sizeof( buf ), p, "Torrents", NULL );
333#else
334        tr_buildPath( buf, sizeof( buf ), p, "torrents", NULL );
335#endif
336
337        path = tr_strdup( buf );
338    }
339
340    return path;
341}
342
343static const char *
344getOldCacheDir( void )
345{
346    static char * path = NULL;
347
348    if( !path )
349    {
350        char         buf[MAX_PATH_LENGTH];
351#if defined( __BEOS__ ) || defined( WIN32 )
352        const char * p = getOldConfigDir( );
353        tr_buildPath( buf, sizeof( buf ), p, "Cache", NULL );
354#elif defined( SYS_DARWIN )
355        tr_buildPath( buf, sizeof( buf ), getHomeDir( ),
356                      "Library", "Caches", "Transmission", NULL );
357#else
358        const char * p = getOldConfigDir( );
359        tr_buildPath( buf, sizeof( buf ), p, "cache", NULL );
360#endif
361        path = tr_strdup( buf );
362    }
363
364    return path;
365}
366
367static void
368moveFiles( const char * oldDir,
369           const char * newDir )
370{
371    if( oldDir && newDir && strcmp( oldDir, newDir ) )
372    {
373        DIR * dirh = opendir( oldDir );
374        if( dirh )
375        {
376            int             count = 0;
377            struct dirent * dirp;
378            while( ( dirp = readdir( dirh ) ) )
379            {
380                if( strcmp( dirp->d_name,
381                            "." ) && strcmp( dirp->d_name, ".." ) )
382                {
383                    char o[MAX_PATH_LENGTH];
384                    char n[MAX_PATH_LENGTH];
385                    tr_buildPath( o, sizeof( o ), oldDir, dirp->d_name,
386                                  NULL );
387                    tr_buildPath( n, sizeof( n ), newDir, dirp->d_name,
388                                  NULL );
389                    rename( o, n );
390                    ++count;
391                }
392            }
393
394            if( count )
395                tr_inf( _( "Migrated %1$d files from \"%2$s\" to \"%3$s\"" ),
396                        count, oldDir, newDir );
397            closedir( dirh );
398        }
399    }
400}
401
402static void
403migrateFiles( const tr_handle * handle )
404{
405    static int migrated = FALSE;
406
407    if( !migrated )
408    {
409        const char * oldDir;
410        const char * newDir;
411        migrated = TRUE;
412
413        oldDir = getOldTorrentsDir( );
414        newDir = tr_getTorrentDir( handle );
415        moveFiles( oldDir, newDir );
416
417        oldDir = getOldCacheDir( );
418        newDir = tr_getResumeDir( handle );
419        moveFiles( oldDir, newDir );
420    }
421}
422
423#ifdef SYS_DARWIN
424 #define RESUME_SUBDIR  "Resume"
425 #define TORRENT_SUBDIR "Torrents"
426#else
427 #define RESUME_SUBDIR  "resume"
428 #define TORRENT_SUBDIR "torrents"
429#endif
430
431void
432tr_setConfigDir( tr_handle *  handle,
433                 const char * configDir )
434{
435    char buf[MAX_PATH_LENGTH];
436
437    handle->configDir = tr_strdup( configDir );
438
439    tr_buildPath( buf, sizeof( buf ), configDir, RESUME_SUBDIR, NULL );
440    tr_mkdirp( buf, 0777 );
441    handle->resumeDir = tr_strdup( buf );
442
443    tr_buildPath( buf, sizeof( buf ), configDir, TORRENT_SUBDIR, NULL );
444    tr_mkdirp( buf, 0777 );
445    handle->torrentDir = tr_strdup( buf );
446
447    migrateFiles( handle );
448}
449
450const char *
451tr_sessionGetConfigDir( const tr_handle * handle )
452{
453    return handle->configDir;
454}
455
456const char *
457tr_getTorrentDir( const tr_handle * handle )
458{
459    return handle->torrentDir;
460}
461
462const char *
463tr_getResumeDir( const tr_handle * handle )
464{
465    return handle->resumeDir;
466}
467
468const char*
469tr_getDefaultConfigDir( void )
470{
471    static char * s = NULL;
472
473    if( !s )
474    {
475        char path[MAX_PATH_LENGTH];
476
477        if( ( s = getenv( "TRANSMISSION_HOME" ) ) )
478        {
479            tr_strlcpy( path, s, sizeof( path ) );
480        }
481        else
482        {
483#ifdef SYS_DARWIN
484            tr_buildPath( path, sizeof( path ),
485                          getHomeDir( ), "Library", "Application Support",
486                          "Transmission", NULL );
487#elif defined( WIN32 )
488            char appdata[MAX_PATH_LENGTH];
489            SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
490            tr_buildPath( path, sizeof( path ),
491                          appdata, "Transmission", NULL );
492#else
493            if( ( s = getenv( "XDG_CONFIG_HOME" ) ) )
494                tr_buildPath( path, sizeof( path ),
495                              s, "transmission", NULL );
496            else
497                tr_buildPath( path, sizeof( path ),
498                              getHomeDir( ), ".config", "transmission",
499                              NULL );
500#endif
501        }
502
503        s = tr_strdup( path );
504    }
505
506    return s;
507}
508
509/***
510****
511***/
512
513static int
514isClutchDir( const char * path )
515{
516    struct stat sb;
517    char        tmp[MAX_PATH_LENGTH];
518
519    tr_buildPath( tmp, sizeof( tmp ), path, "javascript", "transmission.js",
520                  NULL );
521    tr_inf( _( "Searching for web interface file \"%s\"" ), tmp );
522    return !stat( tmp, &sb );
523}
524
525const char *
526tr_getClutchDir( const tr_session * session UNUSED )
527{
528    static char * s = NULL;
529
530    if( !s )
531    {
532        char path[MAX_PATH_LENGTH] = { '\0' };
533
534        if( ( s = getenv( "CLUTCH_HOME" ) ) )
535        {
536            tr_strlcpy( path, s, sizeof( path ) );
537        }
538        else if( ( s = getenv( "TRANSMISSION_WEB_HOME" ) ) )
539        {
540            tr_strlcpy( path, s, sizeof( path ) );
541        }
542        else
543        {
544#ifdef SYS_DARWIN
545            CFURLRef     appURL = CFBundleCopyBundleURL(
546                 CFBundleGetMainBundle( ) );
547            CFStringRef  appRef = CFURLCopyFileSystemPath(
548                appURL, kCFURLPOSIXPathStyle );
549            const char * appString = CFStringGetCStringPtr(
550                 appRef, CFStringGetFastestEncoding( appRef ) );
551            CFRelease( appURL );
552            CFRelease( appRef );
553
554            tr_buildPath( path, sizeof( path ), appString, "Contents",
555                          "Resources", "web",
556                          NULL );
557#elif defined( WIN32 )
558
559 #warning\
560            hey win32 people is this good or is there a better implementation of the next four lines
561            char     appdata[MAX_PATH_LENGTH];
562            SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
563            tr_buildPath( path, sizeof( path ),
564                          appdata, "Transmission", NULL );
565#else
566            tr_list *candidates = NULL, *l;
567
568            /* XDG_DATA_HOME should be the first in the list of candidates */
569            s = getenv( "XDG_DATA_HOME" );
570            if( s && *s )
571                tr_list_append( &candidates, tr_strdup( s ) );
572            else
573            {
574                char tmp[MAX_PATH_LENGTH];
575                tr_buildPath( tmp, sizeof( tmp ),
576                              getHomeDir( ), ".local", "share", NULL );
577                tr_list_append( &candidates, tr_strdup( tmp ) );
578            }
579
580            /* XDG_DATA_DIRS are the backup directories */
581            s = getenv( "XDG_DATA_DIRS" );
582            if( !s || !*s )
583                s =  PACKAGE_DATA_DIR ":/usr/local/share/:/usr/share/";
584            while( s && *s )
585            {
586                char * end = strchr( s, ':' );
587                if( end )
588                {
589                    tr_list_append( &candidates, tr_strndup( s, end - s ) );
590                    s = end + 1;
591                }
592                else
593                {
594                    tr_list_append( &candidates, tr_strdup( s ) );
595                    break;
596                }
597            }
598
599            for( l = candidates; l; l = l->next )
600            {
601                tr_buildPath( path, sizeof( path ), l->data, "transmission",
602                              "web",
603                              NULL );
604                if( isClutchDir( path ) )
605                    break;
606                *path = '\0';
607            }
608
609            tr_list_free( &candidates, tr_free );
610#endif
611        }
612
613        s = tr_strdup( path );
614    }
615
616    return s;
617}
618
619/***
620****
621***/
622
623tr_lockfile_state_t
624tr_lockfile( const char * filename )
625{
626    tr_lockfile_state_t ret;
627
628#ifdef WIN32
629
630    HANDLE              file = CreateFile(
631        filename,
632        GENERIC_READ | GENERIC_WRITE,
633        FILE_SHARE_READ |
634        FILE_SHARE_WRITE,
635        NULL,
636        OPEN_ALWAYS,
637        FILE_ATTRIBUTE_NORMAL,
638        NULL );
639    if( file == INVALID_HANDLE_VALUE )
640        ret = TR_LOCKFILE_EOPEN;
641    else if( !LockFile( file, 0, 0, 1, 1 ) )
642        ret = TR_LOCKFILE_ELOCK;
643    else
644        ret = TR_LOCKFILE_SUCCESS;
645
646#else
647
648    int fd = open( filename, O_RDWR | O_CREAT, 0666 );
649    if( fd < 0 )
650        ret = TR_LOCKFILE_EOPEN;
651    else
652    {
653        struct flock lk;
654        memset( &lk, 0,  sizeof( lk ) );
655        lk.l_start = 0;
656        lk.l_len = 0;
657        lk.l_type = F_WRLCK;
658        lk.l_whence = SEEK_SET;
659        if( -1 == fcntl( fd, F_SETLK, &lk ) )
660            ret = TR_LOCKFILE_ELOCK;
661        else
662            ret = TR_LOCKFILE_SUCCESS;
663    }
664
665#endif
666
667    return ret;
668}
669
670#ifdef WIN32
671
672/* The following mmap functions are by Joerg Walter, and were taken from
673 * his paper at: http://www.genesys-e.de/jwalter/mix4win.htm
674 */
675
676static LONG volatile g_sl __attribute__ ( ( aligned ( 4 ) ) );
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 *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.