Revert "transmission: update from 2.13 to 2.22"
[tomato.git] / release / src / router / transmission / libtransmission / platform.c
blobd80367f9ecc01b0845e0f8cf1d8965379b3da121
1 /*
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 $
13 #ifdef WIN32
14 #include <w32api.h>
15 #define WINVER WindowsXP
16 #include <windows.h>
17 #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_MYDOCUMENTS */
18 #else
19 #ifdef SYS_DARWIN
20 #include <CoreFoundation/CoreFoundation.h>
21 #endif
22 #ifdef __HAIKU__
23 #include <FindDirectory.h>
24 #endif
25 #define _XOPEN_SOURCE 600 /* needed for recursive locks. */
26 #ifndef __USE_UNIX98
27 #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
28 #endif
29 #include <pthread.h>
30 #endif
32 #include <assert.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #ifdef WIN32
40 #include <libgen.h>
41 #endif
42 #include <dirent.h>
43 #include <fcntl.h>
44 #include <unistd.h> /* getuid getpid close */
46 #include "transmission.h"
47 #include "session.h"
48 #include "list.h"
49 #include "platform.h"
50 #include "utils.h"
52 /***
53 **** THREADS
54 ***/
56 #ifdef WIN32
57 typedef DWORD tr_thread_id;
58 #else
59 typedef pthread_t tr_thread_id;
60 #endif
62 static tr_thread_id
63 tr_getCurrentThread( void )
65 #ifdef WIN32
66 return GetCurrentThreadId( );
67 #else
68 return pthread_self( );
69 #endif
72 static tr_bool
73 tr_areThreadsEqual( tr_thread_id a, tr_thread_id b )
75 #ifdef WIN32
76 return a == b;
77 #else
78 return pthread_equal( a, b ) != 0;
79 #endif
82 /** @brief portability wrapper around OS-dependent threads */
83 struct tr_thread
85 void ( * func )( void * );
86 void * arg;
87 tr_thread_id thread;
88 #ifdef WIN32
89 HANDLE thread_handle;
90 #endif
93 tr_bool
94 tr_amInThread( const tr_thread * t )
96 return tr_areThreadsEqual( tr_getCurrentThread( ), t->thread );
99 #ifdef WIN32
100 #define ThreadFuncReturnType unsigned WINAPI
101 #else
102 #define ThreadFuncReturnType void
103 #endif
105 static ThreadFuncReturnType
106 ThreadFunc( void * _t )
108 tr_thread * t = _t;
110 t->func( t->arg );
112 tr_free( t );
113 #ifdef WIN32
114 _endthreadex( 0 );
115 return 0;
116 #endif
119 tr_thread *
120 tr_threadNew( void ( *func )(void *),
121 void * arg )
123 tr_thread * t = tr_new0( tr_thread, 1 );
125 t->func = func;
126 t->arg = arg;
128 #ifdef WIN32
130 unsigned int id;
131 t->thread_handle =
132 (HANDLE) _beginthreadex( NULL, 0, &ThreadFunc, t, 0,
133 &id );
134 t->thread = (DWORD) id;
136 #else
137 pthread_create( &t->thread, NULL, (void*(*)(void*))ThreadFunc, t );
138 pthread_detach( t->thread );
140 #endif
142 return t;
145 /***
146 **** LOCKS
147 ***/
149 /** @brief portability wrapper around OS-dependent thread mutexes */
150 struct tr_lock
152 int depth;
153 #ifdef WIN32
154 CRITICAL_SECTION lock;
155 DWORD lockThread;
156 #else
157 pthread_mutex_t lock;
158 pthread_t lockThread;
159 #endif
162 tr_lock*
163 tr_lockNew( void )
165 tr_lock * l = tr_new0( tr_lock, 1 );
167 #ifdef WIN32
168 InitializeCriticalSection( &l->lock ); /* supports recursion */
169 #else
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 );
174 #endif
176 return l;
179 void
180 tr_lockFree( tr_lock * l )
182 #ifdef WIN32
183 DeleteCriticalSection( &l->lock );
184 #else
185 pthread_mutex_destroy( &l->lock );
186 #endif
187 tr_free( l );
190 void
191 tr_lockLock( tr_lock * l )
193 #ifdef WIN32
194 EnterCriticalSection( &l->lock );
195 #else
196 pthread_mutex_lock( &l->lock );
197 #endif
198 assert( l->depth >= 0 );
199 if( l->depth )
200 assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
201 l->lockThread = tr_getCurrentThread( );
202 ++l->depth;
206 tr_lockHave( const tr_lock * l )
208 return ( l->depth > 0 )
209 && ( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
212 void
213 tr_lockUnlock( tr_lock * l )
215 assert( l->depth > 0 );
216 assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
218 --l->depth;
219 assert( l->depth >= 0 );
220 #ifdef WIN32
221 LeaveCriticalSection( &l->lock );
222 #else
223 pthread_mutex_unlock( &l->lock );
224 #endif
227 /***
228 **** PATHS
229 ***/
231 #ifndef WIN32
232 #include <pwd.h>
233 #endif
235 static const char *
236 getHomeDir( void )
238 static char * home = NULL;
240 if( !home )
242 home = tr_strdup( getenv( "HOME" ) );
244 if( !home )
246 #ifdef WIN32
247 char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
248 *appdata = '\0';
249 SHGetFolderPath( NULL, CSIDL_PERSONAL, NULL, 0, appdata );
250 home = tr_strdup( appdata );
251 #else
252 struct passwd * pw = getpwuid( getuid( ) );
253 if( pw )
254 home = tr_strdup( pw->pw_dir );
255 endpwent( );
256 #endif
259 if( !home )
260 home = tr_strdup( "" );
263 return home;
266 static const char *
267 getOldConfigDir( void )
269 static char * path = NULL;
271 if( !path )
273 #ifdef SYS_DARWIN
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 );
285 #else
286 path = tr_buildPath( getHomeDir( ), ".transmission", NULL );
287 #endif
290 return path;
293 #if defined(SYS_DARWIN) || defined(WIN32)
294 #define RESUME_SUBDIR "Resume"
295 #define TORRENT_SUBDIR "Torrents"
296 #else
297 #define RESUME_SUBDIR "resume"
298 #define TORRENT_SUBDIR "torrents"
299 #endif
301 static const char *
302 getOldTorrentsDir( void )
304 static char * path = NULL;
306 if( !path )
307 path = tr_buildPath( getOldConfigDir( ), TORRENT_SUBDIR, NULL );
309 return path;
312 static const char *
313 getOldCacheDir( void )
315 static char * path = NULL;
317 if( !path )
319 #if defined( WIN32 )
320 path = tr_buildPath( getOldConfigDir( ), "Cache", NULL );
321 #elif defined( SYS_DARWIN )
322 path = tr_buildPath( getHomeDir( ), "Library", "Caches", "Transmission", NULL );
323 #else
324 path = tr_buildPath( getOldConfigDir( ), "cache", NULL );
325 #endif
328 return path;
331 static void
332 moveFiles( const char * oldDir,
333 const char * newDir )
335 if( oldDir && newDir && strcmp( oldDir, newDir ) )
337 DIR * dirh = opendir( oldDir );
338 if( dirh )
340 int count = 0;
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 );
349 rename( o, n );
350 ++count;
351 tr_free( n );
352 tr_free( o );
356 if( count )
357 tr_inf( _( "Migrated %1$d files from \"%2$s\" to \"%3$s\"" ),
358 count, oldDir, newDir );
359 closedir( dirh );
364 static void
365 migrateFiles( const tr_session * session )
367 static int migrated = FALSE;
369 if( !migrated )
371 const char * oldDir;
372 const char * newDir;
373 migrated = TRUE;
375 oldDir = getOldTorrentsDir( );
376 newDir = tr_getTorrentDir( session );
377 moveFiles( oldDir, newDir );
379 oldDir = getOldCacheDir( );
380 newDir = tr_getResumeDir( session );
381 moveFiles( oldDir, newDir );
385 void
386 tr_setConfigDir( tr_session * session, const char * configDir )
388 char * path;
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 );
403 const char *
404 tr_sessionGetConfigDir( const tr_session * session )
406 return session->configDir;
409 const char *
410 tr_getTorrentDir( const tr_session * session )
412 return session->torrentDir;
415 const char *
416 tr_getResumeDir( const tr_session * session )
418 return session->resumeDir;
421 const char*
422 tr_getDefaultConfigDir( const char * appname )
424 static char * s = NULL;
426 if( !appname || !*appname )
427 appname = "Transmission";
429 if( !s )
431 if( ( s = getenv( "TRANSMISSION_HOME" ) ) )
433 s = tr_strdup( s );
435 else
437 #ifdef SYS_DARWIN
438 s = tr_buildPath( getHomeDir( ), "Library", "Application Support",
439 appname, NULL );
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 );
448 #else
449 if( ( s = getenv( "XDG_CONFIG_HOME" ) ) )
450 s = tr_buildPath( s, appname, NULL );
451 else
452 s = tr_buildPath( getHomeDir( ), ".config", appname, NULL );
453 #endif
457 return s;
460 const char*
461 tr_getDefaultDownloadDir( void )
463 static char * user_dir = NULL;
465 if( user_dir == NULL )
467 const char * config_home;
468 char * config_file;
469 char * content;
470 size_t content_len;
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 );
476 else
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 );
485 if( line != NULL )
487 char * value = line + strlen( key );
488 char * end = strchr( value, '"' );
490 if( end )
492 *end = '\0';
494 if( !memcmp( value, "$HOME/", 6 ) )
495 user_dir = tr_buildPath( getHomeDir( ), value+6, NULL );
496 else
497 user_dir = tr_strdup( value );
502 if( user_dir == NULL )
503 #ifdef __HAIKU__
504 user_dir = tr_buildPath( getHomeDir( ), "Desktop", NULL );
505 #else
506 user_dir = tr_buildPath( getHomeDir( ), "Downloads", NULL );
507 #endif
509 tr_free( content );
510 tr_free( config_file );
513 return user_dir;
516 /***
517 ****
518 ***/
520 static int
521 isWebClientDir( const char * path )
523 struct stat sb;
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 );
527 tr_free( tmp );
528 return ret;
531 const char *
532 tr_getWebClientDir( const tr_session * session UNUSED )
534 static char * s = NULL;
536 if( !s )
538 if( ( s = getenv( "CLUTCH_HOME" ) ) )
540 s = tr_strdup( s );
542 else if( ( s = getenv( "TRANSMISSION_WEB_HOME" ) ) )
544 s = tr_strdup( s );
546 else
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 ) ) {
555 tr_free( 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,
565 appString,
566 appLength + 1,
567 CFStringGetFastestEncoding( appRef ));
568 assert( success );
570 CFRelease( appURL );
571 CFRelease( appRef );
573 /* Fallback to the app bundle */
574 s = tr_buildPath( appString, "Contents", "Resources", "web", NULL );
575 if( !isWebClientDir( s ) ) {
576 tr_free( s );
577 s = NULL;
580 tr_free( appString );
583 #elif defined( WIN32 )
585 /* SHGetFolderPath explicitly requires MAX_PATH length */
586 char dir[MAX_PATH];
588 /* Generally, Web interface should be stored in a Web subdir of
589 * calling executable dir. */
591 if( s == NULL ) {
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 ) ) {
596 tr_free( s );
597 s = NULL;
601 if( s == NULL ) {
602 /* check personal AppData */
603 SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, dir );
604 s = tr_buildPath( dir, "Transmission", "Web", NULL );
605 if( !isWebClientDir( s ) ) {
606 tr_free( s );
607 s = NULL;
611 if( s == NULL) {
612 /* check calling module place */
613 GetModuleFileName( GetModuleHandle( NULL ), dir, sizeof( dir ) );
614 s = tr_buildPath( dirname( dir ), "Web", NULL );
615 if( !isWebClientDir( s ) ) {
616 tr_free( s );
617 s = NULL;
621 #else /* everyone else, follow the XDG spec */
623 tr_list *candidates = NULL, *l;
624 const char * tmp;
626 /* XDG_DATA_HOME should be the first in the list of candidates */
627 tmp = getenv( "XDG_DATA_HOME" );
628 if( tmp && *tmp )
629 tr_list_append( &candidates, tr_strdup( tmp ) );
630 else {
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 );
641 tmp = buf;
642 while( tmp && *tmp ) {
643 const char * end = strchr( tmp, ':' );
644 if( end ) {
645 if( ( end - tmp ) > 1 )
646 tr_list_append( &candidates, tr_strndup( tmp, end - tmp ) );
647 tmp = end + 1;
648 } else if( tmp && *tmp ) {
649 tr_list_append( &candidates, tr_strdup( tmp ) );
650 break;
653 tr_free( buf );
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 );
660 if( found ) {
661 s = path;
662 break;
664 tr_free( path );
667 tr_list_free( &candidates, tr_free );
669 #endif
674 return s;
677 /***
678 ****
679 ***/
681 #ifdef WIN32
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;
689 #else
690 static LONG volatile g_sl __attribute__ ( ( aligned ( 4 ) ) );
691 #endif
693 /* Wait for spin lock */
694 static int
695 slwait( LONG volatile *sl )
697 while( InterlockedCompareExchange ( sl, 1, 0 ) != 0 )
698 Sleep ( 0 );
700 return 0;
703 /* Release spin lock */
704 static int
705 slrelease( LONG volatile *sl )
707 InterlockedExchange ( sl, 0 );
708 return 0;
711 /* getpagesize for windows */
712 static long
713 getpagesize( void )
715 static long g_pagesize = 0;
717 if( !g_pagesize )
719 SYSTEM_INFO system_info;
720 GetSystemInfo ( &system_info );
721 g_pagesize = system_info.dwPageSize;
723 return g_pagesize;
726 static long
727 getregionsize( void )
729 static long g_regionsize = 0;
731 if( !g_regionsize )
733 SYSTEM_INFO system_info;
734 GetSystemInfo ( &system_info );
735 g_regionsize = system_info.dwAllocationGranularity;
737 return g_regionsize;
740 void *
741 mmap( void *ptr,
742 long size,
743 long prot,
744 long type,
745 long handle,
746 long arg )
748 static long g_pagesize;
749 static long g_regionsize;
751 /* Wait for spin lock */
752 slwait ( &g_sl );
753 /* First time initialization */
754 if( !g_pagesize )
755 g_pagesize = getpagesize ( );
756 if( !g_regionsize )
757 g_regionsize = getregionsize ( );
758 /* Allocate this */
759 ptr = VirtualAlloc ( ptr, size,
760 MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
761 PAGE_READWRITE );
762 if( !ptr )
764 ptr = (void *) -1;
765 goto mmap_exit;
767 mmap_exit:
768 /* Release spin lock */
769 slrelease ( &g_sl );
770 return ptr;
773 long
774 munmap( void *ptr,
775 long size )
777 static long g_pagesize;
778 static long g_regionsize;
779 int rc = -1;
781 /* Wait for spin lock */
782 slwait ( &g_sl );
783 /* First time initialization */
784 if( !g_pagesize )
785 g_pagesize = getpagesize ( );
786 if( !g_regionsize )
787 g_regionsize = getregionsize ( );
788 /* Free this */
789 if( !VirtualFree ( ptr, 0,
790 MEM_RELEASE ) )
791 goto munmap_exit;
792 rc = 0;
793 munmap_exit:
794 /* Release spin lock */
795 slrelease ( &g_sl );
796 return rc;
799 #endif