source: trunk/libtransmission/platform.c @ 6321

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

don't break the mac build

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