source: trunk/libtransmission/platform.c @ 6389

Last change on this file since 6389 was 6389, checked in by charles, 13 years ago

(libT) make the licensing consistent across all the files which only contain my code

  • Property svn:keywords set to Date Rev Author Id
File size: 15.6 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 6389 2008-07-22 23:28:28Z 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_snprintf( path, sizeof( path ), s );
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_snprintf( path, sizeof( path ), s );
527        }
528        else if(( s = getenv( "TRANSMISSION_WEB_HOME" )))
529        {
530            tr_snprintf( path, sizeof( path ), s );
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.