source: trunk/libtransmission/platform.c @ 6514

Last change on this file since 6514 was 6514, checked in by muks, 13 years ago

Respect non-standard install paths for web interface files

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