source: trunk/libtransmission/platform.c @ 6642

Last change on this file since 6642 was 6642, checked in by livings124, 12 years ago

#1210 on Mac, the web ui should now work when Transmission's path contains a space

  • Property svn:keywords set to Date Rev Author Id
File size: 15.0 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 6642 2008-08-22 23:55:33Z livings124 $
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            CFURLRef appURL = CFBundleCopyBundleURL( CFBundleGetMainBundle() );
529            CFStringRef appRef = CFURLCopyFileSystemPath( appURL, kCFURLPOSIXPathStyle );
530            const char * appString = CFStringGetCStringPtr( appRef, CFStringGetFastestEncoding( appRef ) );
531            CFRelease( appURL );
532            CFRelease( appRef );
533           
534            tr_buildPath( path, sizeof( path ), appString, "Contents", "Resources", "web", NULL );
535#elif defined(WIN32)
536
537            #warning hey win32 people is this good or is there a better implementation of the next four lines
538            char appdata[MAX_PATH_LENGTH];
539            SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
540            tr_buildPath( path, sizeof( path ),
541                          appdata, "Transmission", NULL );
542#else
543            tr_list *candidates=NULL, *l;
544
545            /* XDG_DATA_HOME should be the first in the list of candidates */
546            s = getenv( "XDG_DATA_HOME" );
547            if( s && *s )
548                tr_list_append( &candidates, tr_strdup( s ) );
549            else {
550                char tmp[MAX_PATH_LENGTH];
551                tr_buildPath( tmp, sizeof( tmp ), getHomeDir(), ".local", "share", NULL );
552                tr_list_append( &candidates, tr_strdup( tmp ) );
553            }
554
555            /* XDG_DATA_DIRS are the backup directories */
556            s = getenv( "XDG_DATA_DIRS" );
557            if( !s || !*s )
558                s =  PACKAGE_DATA_DIR ":/usr/local/share/:/usr/share/";
559            while( s && *s ) {
560                char * end = strchr( s, ':' );
561                if( end ) {
562                    tr_list_append( &candidates, tr_strndup( s, end-s ) );
563                    s = end + 1;
564                } else {
565                    tr_list_append( &candidates, tr_strdup( s ) );
566                    break;
567                }
568            }
569
570            for( l=candidates; l; l=l->next ) {
571                tr_buildPath( path, sizeof( path ), l->data, "transmission", "web", NULL );
572                if( isClutchDir( path ) )
573                    break;
574                *path = '\0';
575            }
576
577            tr_list_free( &candidates, tr_free );
578#endif
579        }
580
581        if( !*path )
582        {
583            tr_strlcpy( path, "/dev/null", sizeof( path ) );
584            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." ) );
585        }
586
587        s = tr_strdup( path );
588    }
589
590    return s;
591}
592
593
594/***
595****
596***/
597
598tr_lockfile_state_t
599tr_lockfile( const char * filename )
600{
601    tr_lockfile_state_t ret;
602
603#ifdef WIN32
604
605    HANDLE file = CreateFile( filename,
606                              GENERIC_READ|GENERIC_WRITE,
607                              FILE_SHARE_READ|FILE_SHARE_WRITE,
608                              NULL,
609                              OPEN_ALWAYS,
610                              FILE_ATTRIBUTE_NORMAL,
611                              NULL );
612    if( file == INVALID_HANDLE_VALUE )
613        ret = TR_LOCKFILE_EOPEN;
614    else if( !LockFile( file, 0, 0, 1, 1 ) )
615        ret = TR_LOCKFILE_ELOCK;
616    else
617        ret = TR_LOCKFILE_SUCCESS;
618
619#else
620
621    int fd = open( filename, O_RDWR | O_CREAT, 0666 );
622    if( fd < 0 )
623        ret = TR_LOCKFILE_EOPEN;
624    else {
625        struct flock lk;
626        memset( &lk, 0,  sizeof( lk ) );
627        lk.l_start = 0;
628        lk.l_len = 0;
629        lk.l_type = F_WRLCK;
630        lk.l_whence = SEEK_SET;
631        if( -1 == fcntl( fd, F_SETLK, &lk ) )
632            ret = TR_LOCKFILE_ELOCK;
633        else
634            ret = TR_LOCKFILE_SUCCESS;
635    }
636
637#endif
638
639    return ret;
640}
Note: See TracBrowser for help on using the repository browser.