source: trunk/libtransmission/platform.c @ 6338

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

(gtk) add "Launch Clutch" button to the web tab in the preferences dialog. that way users won't have to dig through documentation to find the "http://localhost:port/transmission/clutch" URL.

  • Property svn:keywords set to Date Rev Author Id
File size: 15.7 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 6338 2008-07-16 02:31:08Z charles $
3 *
4 * Copyright (c) 2005-2008 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#ifdef __BEOS__
26  #include <signal.h> 
27  #include <fs_info.h>
28  #include <FindDirectory.h>
29  #include <kernel/OS.h>
30  #define BEOS_MAX_THREADS 256
31#elif defined(WIN32)
32  #include <windows.h>
33  #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_PROFILE */
34#else
35  #define _XOPEN_SOURCE 500 /* needed for recursive locks. */
36  #ifndef __USE_UNIX98
37  #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
38  #endif
39  #include <pthread.h>
40#endif
41
42#include <assert.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46
47#include <sys/stat.h>
48#include <sys/types.h>
49#include <dirent.h>
50#include <fcntl.h>
51#include <unistd.h> /* getuid getpid close */
52
53#include "transmission.h"
54#include "list.h"
55#include "platform.h"
56#include "utils.h"
57
58/***
59****  THREADS
60***/
61
62#ifdef __BEOS__
63typedef thread_id tr_thread_id;
64#elif defined(WIN32)
65typedef DWORD tr_thread_id;
66#else
67typedef pthread_t tr_thread_id;
68#endif
69
70static tr_thread_id
71tr_getCurrentThread( void )
72{
73#ifdef __BEOS__
74    return find_thread( NULL );
75#elif defined(WIN32)
76    return GetCurrentThreadId();
77#else
78    return pthread_self( );
79#endif
80}
81
82static int
83tr_areThreadsEqual( tr_thread_id a, tr_thread_id b )
84{
85#ifdef __BEOS__
86    return a == b;
87#elif defined(WIN32)
88    return a == b;
89#else
90    return pthread_equal( a, b );
91#endif
92}
93
94struct tr_thread
95{
96    void          (* func ) ( void * );
97    void           * arg;
98    const char     * name;
99    tr_thread_id     thread;
100#ifdef WIN32
101    HANDLE           thread_handle;
102#endif
103};
104
105int
106tr_amInThread ( const tr_thread * t )
107{
108    return tr_areThreadsEqual( tr_getCurrentThread(), t->thread );
109}
110
111#ifdef WIN32
112#define ThreadFuncReturnType unsigned WINAPI
113#else
114#define ThreadFuncReturnType void
115#endif
116
117static ThreadFuncReturnType
118ThreadFunc( void * _t )
119{
120    tr_thread * t = _t;
121    const char * name = t->name;
122
123#ifdef __BEOS__
124    /* This is required because on BeOS, SIGINT is sent to each thread,
125       which kills them not nicely */
126    signal( SIGINT, SIG_IGN );
127#endif
128
129    tr_dbg( "Thread '%s' started", name );
130    t->func( t->arg );
131    tr_dbg( "Thread '%s' exited", name );
132
133#ifdef WIN32
134    _endthreadex( 0 );
135    return 0;
136#endif
137}
138
139tr_thread *
140tr_threadNew( void (*func)(void *),
141              void * arg,
142              const char * name )
143{
144    tr_thread * t = tr_new0( tr_thread, 1 );
145    t->func = func;
146    t->arg  = arg;
147    t->name = name;
148
149#ifdef __BEOS__
150    t->thread = spawn_thread( (void*)ThreadFunc, name, B_NORMAL_PRIORITY, t );
151    resume_thread( t->thread );
152#elif defined(WIN32)
153    {
154        unsigned int id;
155        t->thread_handle = (HANDLE) _beginthreadex( NULL, 0, &ThreadFunc, t, 0, &id );
156        t->thread = (DWORD) id;
157    }
158#else
159    pthread_create( &t->thread, NULL, (void * (*) (void *)) ThreadFunc, t );
160#endif
161
162    return t;
163}
164   
165/***
166****  LOCKS
167***/
168
169struct tr_lock
170{
171    int depth;
172#ifdef __BEOS__
173    sem_id lock;
174    thread_id lockThread;
175#elif defined(WIN32)
176    CRITICAL_SECTION lock;
177    DWORD lockThread;
178#else
179    pthread_mutex_t lock;
180    pthread_t lockThread;
181#endif
182};
183
184tr_lock*
185tr_lockNew( void )
186{
187    tr_lock * l = tr_new0( tr_lock, 1 );
188
189#ifdef __BEOS__
190    l->lock = create_sem( 1, "" );
191#elif defined(WIN32)
192    InitializeCriticalSection( &l->lock ); /* supports recursion */
193#else
194    pthread_mutexattr_t attr;
195    pthread_mutexattr_init( &attr );
196    pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
197    pthread_mutex_init( &l->lock, &attr );
198#endif
199
200    return l;
201}
202
203void
204tr_lockFree( tr_lock * l )
205{
206#ifdef __BEOS__
207    delete_sem( l->lock );
208#elif defined(WIN32)
209    DeleteCriticalSection( &l->lock );
210#else
211    pthread_mutex_destroy( &l->lock );
212#endif
213    tr_free( l );
214}
215
216void
217tr_lockLock( tr_lock * l )
218{
219#ifdef __BEOS__
220    acquire_sem( l->lock );
221#elif defined(WIN32)
222    EnterCriticalSection( &l->lock );
223#else
224    pthread_mutex_lock( &l->lock );
225#endif
226    assert( l->depth >= 0 );
227    if( l->depth )
228        assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ) );
229    l->lockThread = tr_getCurrentThread( );
230    ++l->depth;
231}
232
233int
234tr_lockHave( const tr_lock * l )
235{
236    return ( l->depth > 0 )
237        && ( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ) );
238}
239
240void
241tr_lockUnlock( tr_lock * l )
242{
243    assert( l->depth > 0 );
244    assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ));
245
246    --l->depth;
247    assert( l->depth >= 0 );
248#ifdef __BEOS__
249    release_sem( l->lock );
250#elif defined(WIN32)
251    LeaveCriticalSection( &l->lock );
252#else
253    pthread_mutex_unlock( &l->lock );
254#endif
255}
256
257/***
258****  PATHS
259***/
260
261#if !defined(WIN32) && !defined(__BEOS__) && !defined(__AMIGAOS4__)
262#include <pwd.h>
263#endif
264
265static const char *
266getHomeDir( void )
267{
268    static char * home = NULL;
269
270    if( !home )
271    {
272        home = tr_strdup( getenv( "HOME" ) );
273
274        if( !home )
275        {
276#ifdef WIN32
277            SHGetFolderPath( NULL, CSIDL_PROFILE, NULL, 0, home );
278#elif defined(__BEOS__) || defined(__AMIGAOS4__)
279            home = tr_strdup( "" );
280#else
281            struct passwd * pw = getpwuid( getuid() );
282            if( pw )
283                home = tr_strdup( pw->pw_dir );
284            endpwent( );
285#endif
286        }
287
288        if( !home )
289            home = tr_strdup( "" );
290    }
291
292    return home;
293}
294
295static const char *
296getOldConfigDir( void )
297{
298    static char * path = NULL;
299
300    if( !path )
301    {
302        char buf[MAX_PATH_LENGTH];
303#ifdef __BEOS__
304        find_directory( B_USER_SETTINGS_DIRECTORY,
305                        dev_for_path("/boot"), true,
306                        buf, sizeof( buf ) );
307        strcat( buf, "/Transmission" );
308#elif defined( SYS_DARWIN )
309        tr_buildPath ( buf, sizeof( buf ), getHomeDir( ),
310                       "Library", "Application Support",
311                       "Transmission", NULL );
312#elif defined(__AMIGAOS4__)
313        tr_strlcpy( buf, "PROGDIR:.transmission", sizeof( buf ) );
314#elif defined(WIN32)
315        char appdata[MAX_PATH_LENGTH];
316        SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
317        tr_buildPath( buf, sizeof(buf),
318                      appdata, "Transmission", NULL );
319#else
320        tr_buildPath ( buf, sizeof(buf),
321                       getHomeDir( ), ".transmission", NULL );
322#endif
323        path = tr_strdup( buf );
324    }
325
326    return path;
327}
328
329static const char *
330getOldTorrentsDir( void )
331{
332    static char * path = NULL;
333
334    if( !path )
335    {
336        char buf[MAX_PATH_LENGTH];
337        const char * p = getOldConfigDir();
338#if defined(__BEOS__) || defined(WIN32) || defined(SYS_DARWIN)
339        tr_buildPath( buf, sizeof( buf ), p, "Torrents", NULL );
340#else
341        tr_buildPath( buf, sizeof( buf ), p, "torrents", NULL );
342#endif
343
344        path = tr_strdup( buf );
345    }
346
347    return path;
348}
349static const char *
350getOldCacheDir( void )
351{
352    static char * path = NULL;
353
354    if( !path )
355    {
356        char buf[MAX_PATH_LENGTH];
357#if defined(__BEOS__) || defined(WIN32)
358        const char * p = getOldConfigDir( );
359        tr_buildPath( buf, sizeof( buf ), p, "Cache", NULL );
360#elif defined( SYS_DARWIN )
361        tr_buildPath( buf, sizeof( buf ), getHomeDir(),
362                      "Library", "Caches", "Transmission", NULL );
363#else
364        const char * p = getOldConfigDir( );
365        tr_buildPath( buf, sizeof( buf ), p, "cache", NULL );
366#endif
367        path = tr_strdup( buf );
368    }
369
370    return path;
371}
372
373static void
374moveFiles( const char * oldDir, const char * newDir )
375{
376    if( oldDir && newDir && strcmp( oldDir, newDir ) )
377    {
378        DIR * dirh = opendir( oldDir );
379        if( dirh )
380        {
381            int count = 0;
382            struct dirent * dirp;
383            while(( dirp = readdir( dirh )))
384            {
385                if( strcmp( dirp->d_name, "." ) && strcmp( dirp->d_name, ".." ) )
386                {
387                    char o[MAX_PATH_LENGTH];
388                    char n[MAX_PATH_LENGTH];
389                    tr_buildPath( o, sizeof(o), oldDir, dirp->d_name, NULL );
390                    tr_buildPath( n, sizeof(n), newDir, dirp->d_name, NULL );
391                    rename( o, n );
392                    ++count;
393                }
394            }
395
396            if( count )
397                tr_inf( _( "Migrated %1$d files from \"%2$s\" to \"%3$s\"" ),
398                        count, oldDir, newDir );
399            closedir( dirh );
400        }
401    }
402}
403
404static void
405migrateFiles( const tr_handle * handle )
406{
407    static int migrated = FALSE;
408
409    if( !migrated )
410    {
411        const char * oldDir;
412        const char * newDir;
413        migrated = TRUE;
414
415        oldDir = getOldTorrentsDir( );
416        newDir = tr_getTorrentDir( handle );
417        moveFiles( oldDir, newDir );
418
419        oldDir = getOldCacheDir( );
420        newDir = tr_getResumeDir( handle );
421        moveFiles( oldDir, newDir );
422    }
423}
424
425#ifdef SYS_DARWIN
426#define RESUME_SUBDIR  "Resume"
427#define TORRENT_SUBDIR "Torrents"
428#else
429#define RESUME_SUBDIR  "resume"
430#define TORRENT_SUBDIR "torrents"
431#endif
432
433void
434tr_setConfigDir( tr_handle * handle, const char * configDir )
435{
436    char buf[MAX_PATH_LENGTH];
437
438    handle->configDir = tr_strdup( configDir );
439
440    tr_buildPath( buf, sizeof( buf ), configDir, RESUME_SUBDIR, NULL );
441    tr_mkdirp( buf, 0777 );
442    handle->resumeDir = tr_strdup( buf );
443
444    tr_buildPath( buf, sizeof( buf ), configDir, TORRENT_SUBDIR, NULL );
445    tr_mkdirp( buf, 0777 );
446    handle->torrentDir = tr_strdup( buf );
447
448    migrateFiles( handle );
449}
450
451const char *
452tr_sessionGetConfigDir( const tr_handle * handle )
453{
454    return handle->configDir;
455}
456
457const char *
458tr_getTorrentDir( const tr_handle * handle )
459{
460    return handle->torrentDir;
461}
462
463const char *
464tr_getResumeDir( const tr_handle * handle )
465{
466    return handle->resumeDir;
467}
468
469const char*
470tr_getDefaultConfigDir( void )
471{
472    static char * s = NULL;
473
474    if( !s )
475    {
476        char path[MAX_PATH_LENGTH];
477
478        if(( s = getenv( "TRANSMISSION_HOME" )))
479        {
480            tr_snprintf( path, sizeof( path ), s );
481        }
482        else
483        {
484#ifdef SYS_DARWIN
485            tr_buildPath( path, sizeof( path ),
486                          getHomeDir( ), "Library", "Application Support",
487                          "Transmission", NULL );
488#elif defined(WIN32)
489            char appdata[MAX_PATH_LENGTH];
490            SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
491            tr_buildPath( path, sizeof( path ),
492                          appdata, "Transmission", NULL );
493#else
494            if(( s = getenv( "XDG_CONFIG_HOME" )))
495                tr_buildPath( path, sizeof( path ),
496                              s, "transmission", NULL );
497            else
498                tr_buildPath( path, sizeof( path ),
499                              getHomeDir(), ".config", "transmission", NULL );
500#endif
501        }
502
503        s = tr_strdup( path );
504    }
505
506    return s;
507}
508
509/***
510****
511***/
512
513static int
514isClutchDir( const char * path )
515{
516    struct stat sb;
517    char tmp[MAX_PATH_LENGTH];
518    tr_buildPath( tmp, sizeof( tmp ), path, "javascript", "transmission.js", NULL );
519    tr_inf( _( "Searching for web interface file \"%s\"" ), tmp );
520    return !stat( tmp, &sb );
521}
522
523const char *
524tr_getClutchDir( const tr_session * session UNUSED )
525{
526    static char * s = NULL;
527
528    if( !s )
529    {
530        char path[MAX_PATH_LENGTH] = { '\0' };
531
532        if(( s = getenv( "CLUTCH_HOME" )))
533        {
534            tr_snprintf( path, sizeof( path ), s );
535        }
536        else if(( s = getenv( "TRANSMISSION_WEB_HOME" )))
537        {
538            tr_snprintf( path, sizeof( path ), s );
539        }
540        else
541        {
542#ifdef SYS_DARWIN
543
544            #warning hey BentMyWookie can this be implemented here without passing anything into initFull()
545
546#elif defined(WIN32)
547
548            #warning hey win32 people is this good or is there a better implementation of the next four lines
549            char appdata[MAX_PATH_LENGTH];
550            SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
551            tr_buildPath( path, sizeof( path ),
552                          appdata, "Transmission", NULL );
553#else
554            tr_list *candidates=NULL, *l;
555
556            /* XDG_DATA_HOME should be the first in the list of candidates */
557            s = getenv( "XDG_DATA_HOME" );
558            if( s && *s )
559                tr_list_append( &candidates, tr_strdup( s ) );
560            else {
561                char tmp[MAX_PATH_LENGTH];
562                tr_buildPath( tmp, sizeof( tmp ), getHomeDir(), ".local", "share", NULL );
563                tr_list_append( &candidates, tr_strdup( tmp ) );
564            }
565
566            /* XDG_DATA_DIRS are the backup directories */
567            s = getenv( "XDG_DATA_DIRS" );
568            if( !s || !*s )
569                s = "/usr/local/share/:/usr/share/";
570            while( s && *s ) {
571                char * end = strchr( s, ':' );
572                if( end ) {
573                    tr_list_append( &candidates, tr_strndup( s, end-s ) );
574                    s = end + 1;
575                } else {
576                    tr_list_append( &candidates, tr_strdup( s ) );
577                    break;
578                }
579            }
580
581            for( l=candidates; l; l=l->next ) {
582                tr_buildPath( path, sizeof( path ), l->data, "transmission", "web", NULL );
583                if( isClutchDir( path ) )
584                    break;
585                *path = '\0';
586            }
587
588            tr_list_free( &candidates, tr_free );
589#endif
590        }
591
592        if( !*path )
593        {
594            tr_strlcpy( path, "/dev/null", sizeof( path ) );
595            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." ) );
596        }
597
598        s = tr_strdup( path );
599    }
600
601    return s;
602}
603
604
605/***
606****
607***/
608
609int
610tr_lockfile( const char * filename )
611{
612    int ret;
613
614#ifdef WIN32
615
616    HANDLE file = CreateFile( filename,
617                              GENERIC_READ|GENERIC_WRITE,
618                              FILE_SHARE_READ|FILE_SHARE_WRITE,
619                              NULL,
620                              OPEN_ALWAYS,
621                              FILE_ATTRIBUTE_NORMAL,
622                              NULL );
623    if( file == INVALID_HANDLE_VALUE )
624        ret = TR_LOCKFILE_EOPEN;
625    else if( !LockFile( file, 0, 0, 1, 1 ) )
626        ret = TR_LOCKFILE_ELOCK;
627    else
628        ret = TR_LOCKFILE_SUCCESS;
629
630#else
631
632    int fd = open( filename, O_RDWR | O_CREAT, 0666 );
633    if( fd < 0 )
634        ret = TR_LOCKFILE_EOPEN;
635    else {
636        struct flock lk;
637        memset( &lk, 0,  sizeof( lk ) );
638        lk.l_start = 0;
639        lk.l_len = 0;
640        lk.l_type = F_WRLCK;
641        lk.l_whence = SEEK_SET;
642        if( -1 == fcntl( fd, F_SETLK, &lk ) )
643            ret = TR_LOCKFILE_ELOCK;
644        else
645            ret = TR_LOCKFILE_SUCCESS;
646    }
647
648#endif
649
650    return ret;
651}
Note: See TracBrowser for help on using the repository browser.