source: trunk/libtransmission/platform.c @ 6488

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

(libT) safer environmental variable handling (muks)

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