source: branches/1.4x/libtransmission/platform.c @ 7372

Last change on this file since 7372 was 7372, checked in by charles, 12 years ago

(1.4x libT) #1595: Always search in PACKAGE_DATA_DIR when serving Web files

  • Property svn:keywords set to Date Rev Author Id
File size: 18.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 7372 2008-12-14 01:19:49Z 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_MYDOCUMENTS */
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#ifdef WIN32
42#include <libgen.h>
43#endif
44#include <dirent.h>
45#include <fcntl.h>
46#include <unistd.h> /* getuid getpid close */
47
48#include "transmission.h"
49#include "list.h"
50#include "platform.h"
51#include "utils.h"
52
53/***
54****  THREADS
55***/
56
57#ifdef __BEOS__
58typedef thread_id tr_thread_id;
59#elif defined( WIN32 )
60typedef DWORD tr_thread_id;
61#else
62typedef pthread_t tr_thread_id;
63#endif
64
65static tr_thread_id
66tr_getCurrentThread( void )
67{
68#ifdef __BEOS__
69    return find_thread( NULL );
70#elif defined( WIN32 )
71    return GetCurrentThreadId( );
72#else
73    return pthread_self( );
74#endif
75}
76
77static int
78tr_areThreadsEqual( tr_thread_id a,
79                    tr_thread_id b )
80{
81#ifdef __BEOS__
82    return a == b;
83#elif defined( WIN32 )
84    return a == b;
85#else
86    return pthread_equal( a, b );
87#endif
88}
89
90struct tr_thread
91{
92    void            ( * func )( void * );
93    void *          arg;
94    tr_thread_id    thread;
95#ifdef WIN32
96    HANDLE          thread_handle;
97#endif
98};
99
100int
101tr_amInThread( const tr_thread * t )
102{
103    return tr_areThreadsEqual( tr_getCurrentThread( ), t->thread );
104}
105
106#include <event.h>
107
108#ifdef WIN32
109 #define ThreadFuncReturnType unsigned WINAPI
110#else
111 #define ThreadFuncReturnType void
112#endif
113
114static ThreadFuncReturnType
115ThreadFunc( void * _t )
116{
117    tr_thread * t = _t;
118
119#ifdef __BEOS__
120    /* This is required because on BeOS, SIGINT is sent to each thread,
121       which kills them not nicely */
122    signal( SIGINT, SIG_IGN );
123#endif
124
125    t->func( t->arg );
126
127#ifdef WIN32
128    _endthreadex( 0 );
129    return 0;
130#endif
131}
132
133tr_thread *
134tr_threadNew( void   ( *func )(void *),
135              void * arg )
136{
137    tr_thread * t = tr_new0( tr_thread, 1 );
138
139    t->func = func;
140    t->arg  = arg;
141
142#ifdef __BEOS__
143    t->thread =
144        spawn_thread( (void*)ThreadFunc, "beos thread", B_NORMAL_PRIORITY,
145                     t );
146    resume_thread( t->thread );
147#elif defined( WIN32 )
148    {
149        unsigned int id;
150        t->thread_handle =
151            (HANDLE) _beginthreadex( NULL, 0, &ThreadFunc, t, 0,
152                                     &id );
153        t->thread = (DWORD) id;
154    }
155#else
156    pthread_create( &t->thread, NULL, ( void * ( * )(
157                                           void * ) )ThreadFunc, t );
158#endif
159
160    return t;
161}
162
163/***
164****  LOCKS
165***/
166
167struct tr_lock
168{
169    int                 depth;
170#ifdef __BEOS__
171    sem_id              lock;
172    thread_id           lockThread;
173#elif defined( WIN32 )
174    CRITICAL_SECTION    lock;
175    DWORD               lockThread;
176#else
177    pthread_mutex_t     lock;
178    pthread_t           lockThread;
179#endif
180};
181
182tr_lock*
183tr_lockNew( void )
184{
185    tr_lock *           l = tr_new0( tr_lock, 1 );
186
187#ifdef __BEOS__
188    l->lock = create_sem( 1, "" );
189#elif defined( WIN32 )
190    InitializeCriticalSection( &l->lock ); /* supports recursion */
191#else
192    pthread_mutexattr_t attr;
193    pthread_mutexattr_init( &attr );
194    pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
195    pthread_mutex_init( &l->lock, &attr );
196#endif
197
198    return l;
199}
200
201void
202tr_lockFree( tr_lock * l )
203{
204#ifdef __BEOS__
205    delete_sem( l->lock );
206#elif defined( WIN32 )
207    DeleteCriticalSection( &l->lock );
208#else
209    pthread_mutex_destroy( &l->lock );
210#endif
211    tr_free( l );
212}
213
214void
215tr_lockLock( tr_lock * l )
216{
217#ifdef __BEOS__
218    acquire_sem( l->lock );
219#elif defined( WIN32 )
220    EnterCriticalSection( &l->lock );
221#else
222    pthread_mutex_lock( &l->lock );
223#endif
224    assert( l->depth >= 0 );
225    if( l->depth )
226        assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
227    l->lockThread = tr_getCurrentThread( );
228    ++l->depth;
229}
230
231int
232tr_lockHave( const tr_lock * l )
233{
234    return ( l->depth > 0 )
235           && ( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
236}
237
238void
239tr_lockUnlock( tr_lock * l )
240{
241    assert( l->depth > 0 );
242    assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
243
244    --l->depth;
245    assert( l->depth >= 0 );
246#ifdef __BEOS__
247    release_sem( l->lock );
248#elif defined( WIN32 )
249    LeaveCriticalSection( &l->lock );
250#else
251    pthread_mutex_unlock( &l->lock );
252#endif
253}
254
255/***
256****  PATHS
257***/
258
259#if !defined( WIN32 ) && !defined( __BEOS__ ) && !defined( __AMIGAOS4__ )
260 #include <pwd.h>
261#endif
262
263static const char *
264getHomeDir( void )
265{
266    static char * home = NULL;
267
268    if( !home )
269    {
270        home = tr_strdup( getenv( "HOME" ) );
271
272        if( !home )
273        {
274#ifdef WIN32
275            char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
276            *appdata = '\0';
277            SHGetFolderPath( NULL, CSIDL_MYDOCUMENTS, NULL, 0, appdata );
278            home = tr_strdup( appdata );
279#elif defined( __BEOS__ ) || defined( __AMIGAOS4__ )
280            home = tr_strdup( "" );
281#else
282            struct passwd * pw = getpwuid( getuid( ) );
283            if( pw )
284                home = tr_strdup( pw->pw_dir );
285            endpwent( );
286#endif
287        }
288
289        if( !home )
290            home = tr_strdup( "" );
291    }
292
293    return home;
294}
295
296static const char *
297getOldConfigDir( void )
298{
299    static char * path = NULL;
300
301    if( !path )
302    {
303#ifdef __BEOS__
304        char buf[MAX_PATH_LENGTH];
305        find_directory( B_USER_SETTINGS_DIRECTORY,
306                       dev_for_path( "/boot" ), true,
307                       buf, sizeof( buf ) );
308        path = tr_buildPath( buf, "Transmission", NULL );
309#elif defined( SYS_DARWIN )
310        path = tr_buildPath( getHomeDir( ), "Library",
311                              "Application Support",
312                              "Transmission", NULL );
313#elif defined( __AMIGAOS4__ )
314        path = tr_strdup( "PROGDIR:.transmission" );
315#elif defined( WIN32 )
316        char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
317        SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
318        path = tr_buildPath( appdata, "Transmission", NULL );
319#else
320        path = tr_buildPath( getHomeDir( ), ".transmission", NULL );
321#endif
322    }
323
324    return path;
325}
326
327#if defined(SYS_DARWIN) || defined(WIN32)
328 #define RESUME_SUBDIR  "Resume"
329 #define TORRENT_SUBDIR "Torrents"
330#else
331 #define RESUME_SUBDIR  "resume"
332 #define TORRENT_SUBDIR "torrents"
333#endif
334
335static const char *
336getOldTorrentsDir( void )
337{
338    static char * path = NULL;
339
340    if( !path )
341        path = tr_buildPath( getOldConfigDir( ), TORRENT_SUBDIR, NULL );
342
343    return path;
344}
345
346static const char *
347getOldCacheDir( void )
348{
349    static char * path = NULL;
350
351    if( !path )
352    {
353#if defined( __BEOS__ ) || defined( WIN32 )
354        path = tr_buildPath( getOldConfigDir( ), "Cache", NULL );
355#elif defined( SYS_DARWIN )
356        path = tr_buildPath( getHomeDir( ), "Library", "Caches", "Transmission", NULL );
357#else
358        path = tr_buildPath( getOldConfigDir( ), "cache", NULL );
359#endif
360    }
361
362    return path;
363}
364
365static void
366moveFiles( const char * oldDir,
367           const char * newDir )
368{
369    if( oldDir && newDir && strcmp( oldDir, newDir ) )
370    {
371        DIR * dirh = opendir( oldDir );
372        if( dirh )
373        {
374            int             count = 0;
375            struct dirent * dirp;
376            while( ( dirp = readdir( dirh ) ) )
377            {
378                if( strcmp( dirp->d_name,
379                            "." ) && strcmp( dirp->d_name, ".." ) )
380                {
381                    char * o = tr_buildPath( oldDir, dirp->d_name, NULL );
382                    char * n = tr_buildPath( newDir, dirp->d_name, NULL );
383                    rename( o, n );
384                    ++count;
385                    tr_free( n );
386                    tr_free( o );
387                }
388            }
389
390            if( count )
391                tr_inf( _( "Migrated %1$d files from \"%2$s\" to \"%3$s\"" ),
392                        count, oldDir, newDir );
393            closedir( dirh );
394        }
395    }
396}
397
398static void
399migrateFiles( const tr_handle * handle )
400{
401    static int migrated = FALSE;
402
403    if( !migrated )
404    {
405        const char * oldDir;
406        const char * newDir;
407        migrated = TRUE;
408
409        oldDir = getOldTorrentsDir( );
410        newDir = tr_getTorrentDir( handle );
411        moveFiles( oldDir, newDir );
412
413        oldDir = getOldCacheDir( );
414        newDir = tr_getResumeDir( handle );
415        moveFiles( oldDir, newDir );
416    }
417}
418
419void
420tr_setConfigDir( tr_handle *  handle,
421                 const char * configDir )
422{
423    char * path;
424
425    handle->configDir = tr_strdup( configDir );
426
427    path = tr_buildPath( configDir, RESUME_SUBDIR, NULL );
428    tr_mkdirp( path, 0777 );
429    handle->resumeDir = path;
430
431    path = tr_buildPath( configDir, TORRENT_SUBDIR, NULL );
432    tr_mkdirp( path, 0777 );
433    handle->torrentDir = path;
434
435    migrateFiles( handle );
436}
437
438const char *
439tr_sessionGetConfigDir( const tr_handle * handle )
440{
441    return handle->configDir;
442}
443
444const char *
445tr_getTorrentDir( const tr_handle * handle )
446{
447    return handle->torrentDir;
448}
449
450const char *
451tr_getResumeDir( const tr_handle * handle )
452{
453    return handle->resumeDir;
454}
455
456const char*
457tr_getDefaultConfigDir( void )
458{
459    static char * s = NULL;
460
461    if( !s )
462    {
463        if( ( s = getenv( "TRANSMISSION_HOME" ) ) )
464        {
465            s = tr_strdup( s );
466        }
467        else
468        {
469#ifdef SYS_DARWIN
470            s = tr_buildPath( getHomeDir( ), "Library",
471                              "Application Support", "Transmission", NULL );
472#elif defined( WIN32 )
473            char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
474            SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
475            s = tr_buildPath( appdata, "Transmission", NULL );
476#else
477            if( ( s = getenv( "XDG_CONFIG_HOME" ) ) )
478                s = tr_buildPath( s, "transmission", NULL );
479            else
480                s = tr_buildPath( getHomeDir( ), ".config", "transmission", NULL );
481#endif
482        }
483    }
484
485    return s;
486}
487
488/***
489****
490***/
491
492static int
493isClutchDir( const char * path )
494{
495    struct stat sb;
496    char * tmp = tr_buildPath( path, "javascript", "transmission.js", NULL );
497    const int ret = !stat( tmp, &sb );
498    tr_inf( _( "Searching for web interface file \"%s\"" ), tmp );
499    tr_free( tmp );
500    return ret;
501   
502}
503
504const char *
505tr_getClutchDir( const tr_session * session UNUSED )
506{
507    static char * s = NULL;
508
509    if( !s )
510    {
511        if( ( s = getenv( "CLUTCH_HOME" ) ) )
512        {
513            s = tr_strdup( s );
514        }
515        else if( ( s = getenv( "TRANSMISSION_WEB_HOME" ) ) )
516        {
517            s = tr_strdup( s );
518        }
519        else
520        {
521
522#ifdef SYS_DARWIN
523
524            CFURLRef appURL = CFBundleCopyBundleURL( CFBundleGetMainBundle( ) );
525            CFStringRef appRef = CFURLCopyFileSystemPath( appURL,
526                                                         kCFURLPOSIXPathStyle );
527            const char * appString = CFStringGetCStringPtr( appRef,
528                                         CFStringGetFastestEncoding( appRef ) );
529            CFRelease( appURL );
530            CFRelease( appRef );
531
532            s = tr_buildPath( appString, "Contents", "Resources", "web", NULL );
533
534#elif defined( WIN32 )
535
536            /* SHGetFolderPath explicitly requires MAX_PATH length */
537            char dir[MAX_PATH];
538           
539            /* Generally, Web interface should be stored in a Web subdir of
540             * calling executable dir. */
541
542            if( s == NULL ) { 
543                /* First, we should check personal AppData/Transmission/Web */
544                SHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, NULL, 0, dir );
545                s = tr_buildPath( dir, "Transmission", "Web", NULL );
546                if( !isClutchDir( s ) ) {
547                    tr_free( s );
548                    s = NULL;
549                }
550            }
551
552            if( s == NULL ) {
553                /* check personal AppData */
554                SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, dir );
555                s = tr_buildPath( dir, "Transmission", "Web", NULL );
556                if( !isClutchDir( s ) ) {
557                    tr_free( s );
558                    s = NULL;
559                }
560            }
561
562            if( s == NULL) {
563                /* check calling module place */
564                GetModuleFileName( GetModuleHandle( NULL ), dir, sizeof( dir ) );
565                s = tr_buildPath( dirname( dir ), "Web", NULL );
566                if( !isClutchDir( s ) ) {
567                    tr_free( s );
568                    s = NULL;
569                }
570            }
571
572#else /* everyone else, follow the XDG spec */
573
574            tr_list *candidates = NULL, *l;
575            const char * tmp;
576
577            /* XDG_DATA_HOME should be the first in the list of candidates */
578            tmp = getenv( "XDG_DATA_HOME" );
579            if( tmp && *tmp )
580                tr_list_append( &candidates, tr_strdup( tmp ) );
581            else {
582                char * dhome = tr_buildPath( getHomeDir( ), ".local", "share", NULL );
583                tr_list_append( &candidates, dhome );
584            }
585
586            /* XDG_DATA_DIRS are the backup directories */
587            {
588                struct evbuffer * buf = evbuffer_new( );
589                evbuffer_add_printf( buf, "%s:", PACKAGE_DATA_DIR );
590                if(( tmp = getenv( "XDG_DATA_DIRS" )))
591                    evbuffer_add_printf( buf, "%s:", tmp );
592                evbuffer_add_printf( buf, "%s:", "/usr/local/share" );
593                evbuffer_add_printf( buf, "%s:", "/usr/share" );
594                tmp = (const char*) EVBUFFER_DATA( buf );
595                while( tmp && *tmp ) {
596                    const char * end = strchr( tmp, ':' );
597                    if( end ) {
598                        tr_list_append( &candidates, tr_strndup( tmp, end - tmp ) );
599                        tmp = end + 1;
600                    } else if( tmp && *tmp ) {
601                        tr_list_append( &candidates, tr_strdup( tmp ) );
602                        break;
603                    }
604                }
605                evbuffer_free( buf );
606            }
607
608            /* walk through the candidates & look for a match */
609            for( l=candidates; l; l=l->next ) {
610                char * path = tr_buildPath( l->data, "transmission", "web", NULL );
611                const int found = isClutchDir( path );
612                if( found ) {
613                    s = path;
614                    break;
615                }
616                tr_free( path );
617            }
618
619            tr_list_free( &candidates, tr_free );
620
621#endif
622
623        }
624    }
625
626    return s;
627}
628
629/***
630****
631***/
632
633tr_lockfile_state_t
634tr_lockfile( const char * filename )
635{
636    tr_lockfile_state_t ret;
637
638#ifdef WIN32
639
640    HANDLE              file = CreateFile(
641        filename,
642        GENERIC_READ | GENERIC_WRITE,
643        FILE_SHARE_READ |
644        FILE_SHARE_WRITE,
645        NULL,
646        OPEN_ALWAYS,
647        FILE_ATTRIBUTE_NORMAL,
648        NULL );
649    if( file == INVALID_HANDLE_VALUE )
650        ret = TR_LOCKFILE_EOPEN;
651    else if( !LockFile( file, 0, 0, 1, 1 ) )
652        ret = TR_LOCKFILE_ELOCK;
653    else
654        ret = TR_LOCKFILE_SUCCESS;
655
656#else
657
658    int fd = open( filename, O_RDWR | O_CREAT, 0666 );
659    if( fd < 0 )
660        ret = TR_LOCKFILE_EOPEN;
661    else
662    {
663        struct flock lk;
664        memset( &lk, 0,  sizeof( lk ) );
665        lk.l_start = 0;
666        lk.l_len = 0;
667        lk.l_type = F_WRLCK;
668        lk.l_whence = SEEK_SET;
669        if( -1 == fcntl( fd, F_SETLK, &lk ) )
670            ret = TR_LOCKFILE_ELOCK;
671        else
672            ret = TR_LOCKFILE_SUCCESS;
673    }
674
675#endif
676
677    return ret;
678}
679
680#ifdef WIN32
681
682/* The following mmap functions are by Joerg Walter, and were taken from
683 * his paper at: http://www.genesys-e.de/jwalter/mix4win.htm
684 */
685
686#if defined(_MSC_VER)
687__declspec( align( 4 ) ) static LONG volatile g_sl;
688#else
689static LONG volatile g_sl __attribute__ ( ( aligned ( 4 ) ) );
690#endif
691
692/* Wait for spin lock */
693static int
694slwait( LONG volatile *sl )
695{
696    while( InterlockedCompareExchange ( sl, 1, 0 ) != 0 )
697        Sleep ( 0 );
698
699    return 0;
700}
701
702/* Release spin lock */
703static int
704slrelease( LONG volatile *sl )
705{
706    InterlockedExchange ( sl, 0 );
707    return 0;
708}
709
710/* getpagesize for windows */
711static long
712getpagesize( void )
713{
714    static long g_pagesize = 0;
715
716    if( !g_pagesize )
717    {
718        SYSTEM_INFO system_info;
719        GetSystemInfo ( &system_info );
720        g_pagesize = system_info.dwPageSize;
721    }
722    return g_pagesize;
723}
724
725static long
726getregionsize( void )
727{
728    static long g_regionsize = 0;
729
730    if( !g_regionsize )
731    {
732        SYSTEM_INFO system_info;
733        GetSystemInfo ( &system_info );
734        g_regionsize = system_info.dwAllocationGranularity;
735    }
736    return g_regionsize;
737}
738
739void *
740mmap( void *ptr,
741      long  size,
742      long  prot,
743      long  type,
744      long  handle,
745      long  arg )
746{
747    static long g_pagesize;
748    static long g_regionsize;
749
750    /* Wait for spin lock */
751    slwait ( &g_sl );
752    /* First time initialization */
753    if( !g_pagesize )
754        g_pagesize = getpagesize ( );
755    if( !g_regionsize )
756        g_regionsize = getregionsize ( );
757    /* Allocate this */
758    ptr = VirtualAlloc ( ptr, size,
759                         MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
760                         PAGE_READWRITE );
761    if( !ptr )
762    {
763        ptr = (void *) -1;
764        goto mmap_exit;
765    }
766mmap_exit:
767    /* Release spin lock */
768    slrelease ( &g_sl );
769    return ptr;
770}
771
772long
773munmap( void *ptr,
774        long  size )
775{
776    static long g_pagesize;
777    static long g_regionsize;
778    int         rc = -1;
779
780    /* Wait for spin lock */
781    slwait ( &g_sl );
782    /* First time initialization */
783    if( !g_pagesize )
784        g_pagesize = getpagesize ( );
785    if( !g_regionsize )
786        g_regionsize = getregionsize ( );
787    /* Free this */
788    if( !VirtualFree ( ptr, 0,
789                       MEM_RELEASE ) )
790        goto munmap_exit;
791    rc = 0;
792munmap_exit:
793    /* Release spin lock */
794    slrelease ( &g_sl );
795    return rc;
796}
797
798#endif
799
Note: See TracBrowser for help on using the repository browser.