source: trunk/libtransmission/platform.c @ 6892

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

first batch of portability changes based on spry's winport:
(1) added tr_getcwd(), a simple portability wrapper for getcwd()
(2) cli: show the help page if no command-line arguments are provided
(3) daemon: use tr_wait() intead of sleep()
(4) daemon: SIGQUIT, SIGPIPE, SIGHUP aren't defined on windows
(5) libtransmission: a couple of small cleanups to bencode.c, list.c, web.c
(6) libtransmission: win32 portability fixes to platform.c

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