source: trunk/libtransmission/platform.c @ 6559

Last change on this file since 6559 was 6559, checked in by muks, 12 years ago

Don't show an error when transmission is run twice

Instead, present the main window. This commit also auto-generates
the dbus bindings.

  • 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 6559 2008-08-17 12:39:26Z 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
606tr_lockfile_state_t
607tr_lockfile( const char * filename )
608{
609    tr_lockfile_state_t 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.