source: trunk/libtransmission/platform.c @ 6795

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

run libT, cli, daemon, gtk through the source-code formatter "uncrustify" as promised/threatened

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