2 * This file Copyright (C) 2008-2010 Mnemosyne LLC
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.
10 * $Id: platform.c 11227 2010-09-18 22:13:46Z charles $
15 #define WINVER WindowsXP
17 #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_MYDOCUMENTS */
20 #include <CoreFoundation/CoreFoundation.h>
23 #include <FindDirectory.h>
25 #define _XOPEN_SOURCE 600 /* needed for recursive locks. */
27 #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
38 #include <sys/types.h>
44 #include <unistd.h> /* getuid getpid close */
46 #include "transmission.h"
57 typedef DWORD tr_thread_id
;
59 typedef pthread_t tr_thread_id
;
63 tr_getCurrentThread( void )
66 return GetCurrentThreadId( );
68 return pthread_self( );
73 tr_areThreadsEqual( tr_thread_id a
, tr_thread_id b
)
78 return pthread_equal( a
, b
) != 0;
82 /** @brief portability wrapper around OS-dependent threads */
85 void ( * func
)( void * );
94 tr_amInThread( const tr_thread
* t
)
96 return tr_areThreadsEqual( tr_getCurrentThread( ), t
->thread
);
100 #define ThreadFuncReturnType unsigned WINAPI
102 #define ThreadFuncReturnType void
105 static ThreadFuncReturnType
106 ThreadFunc( void * _t
)
120 tr_threadNew( void ( *func
)(void *),
123 tr_thread
* t
= tr_new0( tr_thread
, 1 );
132 (HANDLE
) _beginthreadex( NULL
, 0, &ThreadFunc
, t
, 0,
134 t
->thread
= (DWORD
) id
;
137 pthread_create( &t
->thread
, NULL
, (void*(*)(void*))ThreadFunc
, t
);
138 pthread_detach( t
->thread
);
149 /** @brief portability wrapper around OS-dependent thread mutexes */
154 CRITICAL_SECTION lock
;
157 pthread_mutex_t lock
;
158 pthread_t lockThread
;
165 tr_lock
* l
= tr_new0( tr_lock
, 1 );
168 InitializeCriticalSection( &l
->lock
); /* supports recursion */
170 pthread_mutexattr_t attr
;
171 pthread_mutexattr_init( &attr
);
172 pthread_mutexattr_settype( &attr
, PTHREAD_MUTEX_RECURSIVE
);
173 pthread_mutex_init( &l
->lock
, &attr
);
180 tr_lockFree( tr_lock
* l
)
183 DeleteCriticalSection( &l
->lock
);
185 pthread_mutex_destroy( &l
->lock
);
191 tr_lockLock( tr_lock
* l
)
194 EnterCriticalSection( &l
->lock
);
196 pthread_mutex_lock( &l
->lock
);
198 assert( l
->depth
>= 0 );
200 assert( tr_areThreadsEqual( l
->lockThread
, tr_getCurrentThread( ) ) );
201 l
->lockThread
= tr_getCurrentThread( );
206 tr_lockHave( const tr_lock
* l
)
208 return ( l
->depth
> 0 )
209 && ( tr_areThreadsEqual( l
->lockThread
, tr_getCurrentThread( ) ) );
213 tr_lockUnlock( tr_lock
* l
)
215 assert( l
->depth
> 0 );
216 assert( tr_areThreadsEqual( l
->lockThread
, tr_getCurrentThread( ) ) );
219 assert( l
->depth
>= 0 );
221 LeaveCriticalSection( &l
->lock
);
223 pthread_mutex_unlock( &l
->lock
);
238 static char * home
= NULL
;
242 home
= tr_strdup( getenv( "HOME" ) );
247 char appdata
[MAX_PATH
]; /* SHGetFolderPath() requires MAX_PATH */
249 SHGetFolderPath( NULL
, CSIDL_PERSONAL
, NULL
, 0, appdata
);
250 home
= tr_strdup( appdata
);
252 struct passwd
* pw
= getpwuid( getuid( ) );
254 home
= tr_strdup( pw
->pw_dir
);
260 home
= tr_strdup( "" );
267 getOldConfigDir( void )
269 static char * path
= NULL
;
274 path
= tr_buildPath( getHomeDir( ), "Library",
275 "Application Support",
276 "Transmission", NULL
);
277 #elif defined( WIN32 )
278 char appdata
[MAX_PATH
]; /* SHGetFolderPath() requires MAX_PATH */
279 SHGetFolderPath( NULL
, CSIDL_APPDATA
, NULL
, 0, appdata
);
280 path
= tr_buildPath( appdata
, "Transmission", NULL
);
281 #elif defined( __HAIKU__ )
282 char buf
[TR_PATH_MAX
];
283 find_directory( B_USER_SETTINGS_DIRECTORY
, -1, true, buf
, sizeof(buf
) );
284 path
= tr_buildPath( buf
, "Transmission", NULL
);
286 path
= tr_buildPath( getHomeDir( ), ".transmission", NULL
);
293 #if defined(SYS_DARWIN) || defined(WIN32)
294 #define RESUME_SUBDIR "Resume"
295 #define TORRENT_SUBDIR "Torrents"
297 #define RESUME_SUBDIR "resume"
298 #define TORRENT_SUBDIR "torrents"
302 getOldTorrentsDir( void )
304 static char * path
= NULL
;
307 path
= tr_buildPath( getOldConfigDir( ), TORRENT_SUBDIR
, NULL
);
313 getOldCacheDir( void )
315 static char * path
= NULL
;
320 path
= tr_buildPath( getOldConfigDir( ), "Cache", NULL
);
321 #elif defined( SYS_DARWIN )
322 path
= tr_buildPath( getHomeDir( ), "Library", "Caches", "Transmission", NULL
);
324 path
= tr_buildPath( getOldConfigDir( ), "cache", NULL
);
332 moveFiles( const char * oldDir
,
333 const char * newDir
)
335 if( oldDir
&& newDir
&& strcmp( oldDir
, newDir
) )
337 DIR * dirh
= opendir( oldDir
);
341 struct dirent
* dirp
;
342 while( ( dirp
= readdir( dirh
) ) )
344 const char * name
= dirp
->d_name
;
345 if( name
&& strcmp( name
, "." ) && strcmp( name
, ".." ) )
347 char * o
= tr_buildPath( oldDir
, name
, NULL
);
348 char * n
= tr_buildPath( newDir
, name
, NULL
);
357 tr_inf( _( "Migrated %1$d files from \"%2$s\" to \"%3$s\"" ),
358 count
, oldDir
, newDir
);
365 migrateFiles( const tr_session
* session
)
367 static int migrated
= FALSE
;
375 oldDir
= getOldTorrentsDir( );
376 newDir
= tr_getTorrentDir( session
);
377 moveFiles( oldDir
, newDir
);
379 oldDir
= getOldCacheDir( );
380 newDir
= tr_getResumeDir( session
);
381 moveFiles( oldDir
, newDir
);
386 tr_setConfigDir( tr_session
* session
, const char * configDir
)
390 session
->configDir
= tr_strdup( configDir
);
392 path
= tr_buildPath( configDir
, RESUME_SUBDIR
, NULL
);
393 tr_mkdirp( path
, 0777 );
394 session
->resumeDir
= path
;
396 path
= tr_buildPath( configDir
, TORRENT_SUBDIR
, NULL
);
397 tr_mkdirp( path
, 0777 );
398 session
->torrentDir
= path
;
400 migrateFiles( session
);
404 tr_sessionGetConfigDir( const tr_session
* session
)
406 return session
->configDir
;
410 tr_getTorrentDir( const tr_session
* session
)
412 return session
->torrentDir
;
416 tr_getResumeDir( const tr_session
* session
)
418 return session
->resumeDir
;
422 tr_getDefaultConfigDir( const char * appname
)
424 static char * s
= NULL
;
426 if( !appname
|| !*appname
)
427 appname
= "Transmission";
431 if( ( s
= getenv( "TRANSMISSION_HOME" ) ) )
438 s
= tr_buildPath( getHomeDir( ), "Library", "Application Support",
440 #elif defined( WIN32 )
441 char appdata
[TR_PATH_MAX
]; /* SHGetFolderPath() requires MAX_PATH */
442 SHGetFolderPath( NULL
, CSIDL_APPDATA
, NULL
, 0, appdata
);
443 s
= tr_buildPath( appdata
, appname
, NULL
);
444 #elif defined( __HAIKU__ )
445 char buf
[TR_PATH_MAX
];
446 find_directory( B_USER_SETTINGS_DIRECTORY
, -1, true, buf
, sizeof(buf
) );
447 s
= tr_buildPath( buf
, appname
, NULL
);
449 if( ( s
= getenv( "XDG_CONFIG_HOME" ) ) )
450 s
= tr_buildPath( s
, appname
, NULL
);
452 s
= tr_buildPath( getHomeDir( ), ".config", appname
, NULL
);
461 tr_getDefaultDownloadDir( void )
463 static char * user_dir
= NULL
;
465 if( user_dir
== NULL
)
467 const char * config_home
;
472 /* figure out where to look for user-dirs.dirs */
473 config_home
= getenv( "XDG_CONFIG_HOME" );
474 if( config_home
&& *config_home
)
475 config_file
= tr_buildPath( config_home
, "user-dirs.dirs", NULL
);
477 config_file
= tr_buildPath( getHomeDir( ), ".config", "user-dirs.dirs", NULL
);
479 /* read in user-dirs.dirs and look for the download dir entry */
480 content
= (char *) tr_loadFile( config_file
, &content_len
);
481 if( content
&& content_len
>0 )
483 const char * key
= "XDG_DOWNLOAD_DIR=\"";
484 char * line
= strstr( content
, key
);
487 char * value
= line
+ strlen( key
);
488 char * end
= strchr( value
, '"' );
494 if( !memcmp( value
, "$HOME/", 6 ) )
495 user_dir
= tr_buildPath( getHomeDir( ), value
+6, NULL
);
497 user_dir
= tr_strdup( value
);
502 if( user_dir
== NULL
)
504 user_dir
= tr_buildPath( getHomeDir( ), "Desktop", NULL
);
506 user_dir
= tr_buildPath( getHomeDir( ), "Downloads", NULL
);
510 tr_free( config_file
);
521 isWebClientDir( const char * path
)
524 char * tmp
= tr_buildPath( path
, "index.html", NULL
);
525 const int ret
= !stat( tmp
, &sb
);
526 tr_inf( _( "Searching for web interface file \"%s\"" ), tmp
);
532 tr_getWebClientDir( const tr_session
* session UNUSED
)
534 static char * s
= NULL
;
538 if( ( s
= getenv( "CLUTCH_HOME" ) ) )
542 else if( ( s
= getenv( "TRANSMISSION_WEB_HOME" ) ) )
549 #ifdef SYS_DARWIN /* on Mac, look in the Application Support folder first, then in the app bundle. */
551 /* Look in the Application Support folder */
552 s
= tr_buildPath( tr_sessionGetConfigDir( session
), "web", NULL
);
554 if( !isWebClientDir( s
) ) {
557 CFURLRef appURL
= CFBundleCopyBundleURL( CFBundleGetMainBundle( ) );
558 CFStringRef appRef
= CFURLCopyFileSystemPath( appURL
,
559 kCFURLPOSIXPathStyle
);
560 CFIndex appLength
= CFStringGetMaximumSizeForEncoding( CFStringGetLength(appRef
),
561 CFStringGetFastestEncoding( appRef
));
563 char * appString
= tr_malloc( appLength
+ 1 );
564 tr_bool success
= CFStringGetCString( appRef
,
567 CFStringGetFastestEncoding( appRef
));
573 /* Fallback to the app bundle */
574 s
= tr_buildPath( appString
, "Contents", "Resources", "web", NULL
);
575 if( !isWebClientDir( s
) ) {
580 tr_free( appString
);
583 #elif defined( WIN32 )
585 /* SHGetFolderPath explicitly requires MAX_PATH length */
588 /* Generally, Web interface should be stored in a Web subdir of
589 * calling executable dir. */
592 /* First, we should check personal AppData/Transmission/Web */
593 SHGetFolderPath( NULL
, CSIDL_COMMON_APPDATA
, NULL
, 0, dir
);
594 s
= tr_buildPath( dir
, "Transmission", "Web", NULL
);
595 if( !isWebClientDir( s
) ) {
602 /* check personal AppData */
603 SHGetFolderPath( NULL
, CSIDL_APPDATA
, NULL
, 0, dir
);
604 s
= tr_buildPath( dir
, "Transmission", "Web", NULL
);
605 if( !isWebClientDir( s
) ) {
612 /* check calling module place */
613 GetModuleFileName( GetModuleHandle( NULL
), dir
, sizeof( dir
) );
614 s
= tr_buildPath( dirname( dir
), "Web", NULL
);
615 if( !isWebClientDir( s
) ) {
621 #else /* everyone else, follow the XDG spec */
623 tr_list
*candidates
= NULL
, *l
;
626 /* XDG_DATA_HOME should be the first in the list of candidates */
627 tmp
= getenv( "XDG_DATA_HOME" );
629 tr_list_append( &candidates
, tr_strdup( tmp
) );
631 char * dhome
= tr_buildPath( getHomeDir( ), ".local", "share", NULL
);
632 tr_list_append( &candidates
, dhome
);
635 /* XDG_DATA_DIRS are the backup directories */
637 const char * pkg
= PACKAGE_DATA_DIR
;
638 const char * xdg
= getenv( "XDG_DATA_DIRS" );
639 const char * fallback
= "/usr/local/share:/usr/share";
640 char * buf
= tr_strdup_printf( "%s:%s:%s", (pkg
?pkg
:""), (xdg
?xdg
:""), fallback
);
642 while( tmp
&& *tmp
) {
643 const char * end
= strchr( tmp
, ':' );
645 if( ( end
- tmp
) > 1 )
646 tr_list_append( &candidates
, tr_strndup( tmp
, end
- tmp
) );
648 } else if( tmp
&& *tmp
) {
649 tr_list_append( &candidates
, tr_strdup( tmp
) );
656 /* walk through the candidates & look for a match */
657 for( l
=candidates
; l
; l
=l
->next
) {
658 char * path
= tr_buildPath( l
->data
, "transmission", "web", NULL
);
659 const int found
= isWebClientDir( path
);
667 tr_list_free( &candidates
, tr_free
);
683 /* The following mmap functions are by Joerg Walter, and were taken from
684 * his paper at: http://www.genesys-e.de/jwalter/mix4win.htm
687 #if defined(_MSC_VER)
688 __declspec( align( 4 ) ) static LONG
volatile g_sl
;
690 static LONG
volatile g_sl
__attribute__ ( ( aligned ( 4 ) ) );
693 /* Wait for spin lock */
695 slwait( LONG
volatile *sl
)
697 while( InterlockedCompareExchange ( sl
, 1, 0 ) != 0 )
703 /* Release spin lock */
705 slrelease( LONG
volatile *sl
)
707 InterlockedExchange ( sl
, 0 );
711 /* getpagesize for windows */
715 static long g_pagesize
= 0;
719 SYSTEM_INFO system_info
;
720 GetSystemInfo ( &system_info
);
721 g_pagesize
= system_info
.dwPageSize
;
727 getregionsize( void )
729 static long g_regionsize
= 0;
733 SYSTEM_INFO system_info
;
734 GetSystemInfo ( &system_info
);
735 g_regionsize
= system_info
.dwAllocationGranularity
;
748 static long g_pagesize
;
749 static long g_regionsize
;
751 /* Wait for spin lock */
753 /* First time initialization */
755 g_pagesize
= getpagesize ( );
757 g_regionsize
= getregionsize ( );
759 ptr
= VirtualAlloc ( ptr
, size
,
760 MEM_RESERVE
| MEM_COMMIT
| MEM_TOP_DOWN
,
768 /* Release spin lock */
777 static long g_pagesize
;
778 static long g_regionsize
;
781 /* Wait for spin lock */
783 /* First time initialization */
785 g_pagesize
= getpagesize ( );
787 g_regionsize
= getregionsize ( );
789 if( !VirtualFree ( ptr
, 0,
794 /* Release spin lock */