transmission: update from 2.13 to 2.22
[tomato.git] / release / src / router / transmission / libtransmission / platform.c
blobfb3a7fdda0c2e6515fae2345808b5a879bbdeec7
1 /*
2 * This file Copyright (C) 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 11785 2011-01-30 01:33:53Z jordan $
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 #ifdef SYS_DARWIN
38 #define HAVE_SYS_STATVFS_H
39 #define HAVE_STATVFS
40 #endif
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #ifdef HAVE_SYS_STATVFS_H
45 #include <sys/statvfs.h>
46 #endif
47 #ifdef WIN32
48 #include <libgen.h>
49 #endif
50 #include <dirent.h>
51 #include <fcntl.h>
52 #include <unistd.h> /* getuid getpid close */
54 #include "transmission.h"
55 #include "session.h"
56 #include "list.h"
57 #include "platform.h"
58 #include "utils.h"
60 /***
61 **** THREADS
62 ***/
64 #ifdef WIN32
65 typedef DWORD tr_thread_id;
66 #else
67 typedef pthread_t tr_thread_id;
68 #endif
70 static tr_thread_id
71 tr_getCurrentThread( void )
73 #ifdef WIN32
74 return GetCurrentThreadId( );
75 #else
76 return pthread_self( );
77 #endif
80 static tr_bool
81 tr_areThreadsEqual( tr_thread_id a, tr_thread_id b )
83 #ifdef WIN32
84 return a == b;
85 #else
86 return pthread_equal( a, b ) != 0;
87 #endif
90 /** @brief portability wrapper around OS-dependent threads */
91 struct tr_thread
93 void ( * func )( void * );
94 void * arg;
95 tr_thread_id thread;
96 #ifdef WIN32
97 HANDLE thread_handle;
98 #endif
101 tr_bool
102 tr_amInThread( const tr_thread * t )
104 return tr_areThreadsEqual( tr_getCurrentThread( ), t->thread );
107 #ifdef WIN32
108 #define ThreadFuncReturnType unsigned WINAPI
109 #else
110 #define ThreadFuncReturnType void
111 #endif
113 static ThreadFuncReturnType
114 ThreadFunc( void * _t )
116 tr_thread * t = _t;
118 t->func( t->arg );
120 tr_free( t );
121 #ifdef WIN32
122 _endthreadex( 0 );
123 return 0;
124 #endif
127 tr_thread *
128 tr_threadNew( void ( *func )(void *),
129 void * arg )
131 tr_thread * t = tr_new0( tr_thread, 1 );
133 t->func = func;
134 t->arg = arg;
136 #ifdef WIN32
138 unsigned int id;
139 t->thread_handle =
140 (HANDLE) _beginthreadex( NULL, 0, &ThreadFunc, t, 0,
141 &id );
142 t->thread = (DWORD) id;
144 #else
145 pthread_create( &t->thread, NULL, (void*(*)(void*))ThreadFunc, t );
146 pthread_detach( t->thread );
148 #endif
150 return t;
153 /***
154 **** LOCKS
155 ***/
157 /** @brief portability wrapper around OS-dependent thread mutexes */
158 struct tr_lock
160 int depth;
161 #ifdef WIN32
162 CRITICAL_SECTION lock;
163 DWORD lockThread;
164 #else
165 pthread_mutex_t lock;
166 pthread_t lockThread;
167 #endif
170 tr_lock*
171 tr_lockNew( void )
173 tr_lock * l = tr_new0( tr_lock, 1 );
175 #ifdef WIN32
176 InitializeCriticalSection( &l->lock ); /* supports recursion */
177 #else
178 pthread_mutexattr_t attr;
179 pthread_mutexattr_init( &attr );
180 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
181 pthread_mutex_init( &l->lock, &attr );
182 #endif
184 return l;
187 void
188 tr_lockFree( tr_lock * l )
190 #ifdef WIN32
191 DeleteCriticalSection( &l->lock );
192 #else
193 pthread_mutex_destroy( &l->lock );
194 #endif
195 tr_free( l );
198 void
199 tr_lockLock( tr_lock * l )
201 #ifdef WIN32
202 EnterCriticalSection( &l->lock );
203 #else
204 pthread_mutex_lock( &l->lock );
205 #endif
206 assert( l->depth >= 0 );
207 if( l->depth )
208 assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
209 l->lockThread = tr_getCurrentThread( );
210 ++l->depth;
214 tr_lockHave( const tr_lock * l )
216 return ( l->depth > 0 )
217 && ( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
220 void
221 tr_lockUnlock( tr_lock * l )
223 assert( l->depth > 0 );
224 assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread( ) ) );
226 --l->depth;
227 assert( l->depth >= 0 );
228 #ifdef WIN32
229 LeaveCriticalSection( &l->lock );
230 #else
231 pthread_mutex_unlock( &l->lock );
232 #endif
235 /***
236 **** PATHS
237 ***/
239 #ifndef WIN32
240 #include <pwd.h>
241 #endif
243 static const char *
244 getHomeDir( void )
246 static char * home = NULL;
248 if( !home )
250 home = tr_strdup( getenv( "HOME" ) );
252 if( !home )
254 #ifdef WIN32
255 char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
256 *appdata = '\0';
257 SHGetFolderPath( NULL, CSIDL_PERSONAL, NULL, 0, appdata );
258 home = tr_strdup( appdata );
259 #else
260 struct passwd * pw = getpwuid( getuid( ) );
261 if( pw )
262 home = tr_strdup( pw->pw_dir );
263 endpwent( );
264 #endif
267 if( !home )
268 home = tr_strdup( "" );
271 return home;
274 static const char *
275 getOldConfigDir( void )
277 static char * path = NULL;
279 if( !path )
281 #ifdef SYS_DARWIN
282 path = tr_buildPath( getHomeDir( ), "Library",
283 "Application Support",
284 "Transmission", NULL );
285 #elif defined( WIN32 )
286 char appdata[MAX_PATH]; /* SHGetFolderPath() requires MAX_PATH */
287 SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
288 path = tr_buildPath( appdata, "Transmission", NULL );
289 #elif defined( __HAIKU__ )
290 char buf[TR_PATH_MAX];
291 find_directory( B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof(buf) );
292 path = tr_buildPath( buf, "Transmission", NULL );
293 #else
294 path = tr_buildPath( getHomeDir( ), ".transmission", NULL );
295 #endif
298 return path;
301 #if defined(SYS_DARWIN) || defined(WIN32)
302 #define RESUME_SUBDIR "Resume"
303 #define TORRENT_SUBDIR "Torrents"
304 #else
305 #define RESUME_SUBDIR "resume"
306 #define TORRENT_SUBDIR "torrents"
307 #endif
309 static const char *
310 getOldTorrentsDir( void )
312 static char * path = NULL;
314 if( !path )
315 path = tr_buildPath( getOldConfigDir( ), TORRENT_SUBDIR, NULL );
317 return path;
320 static const char *
321 getOldCacheDir( void )
323 static char * path = NULL;
325 if( !path )
327 #if defined( WIN32 )
328 path = tr_buildPath( getOldConfigDir( ), "Cache", NULL );
329 #elif defined( SYS_DARWIN )
330 path = tr_buildPath( getHomeDir( ), "Library", "Caches", "Transmission", NULL );
331 #else
332 path = tr_buildPath( getOldConfigDir( ), "cache", NULL );
333 #endif
336 return path;
339 static void
340 moveFiles( const char * oldDir,
341 const char * newDir )
343 if( oldDir && newDir && strcmp( oldDir, newDir ) )
345 DIR * dirh = opendir( oldDir );
346 if( dirh )
348 int count = 0;
349 struct dirent * dirp;
350 while( ( dirp = readdir( dirh ) ) )
352 const char * name = dirp->d_name;
353 if( name && strcmp( name, "." ) && strcmp( name, ".." ) )
355 char * o = tr_buildPath( oldDir, name, NULL );
356 char * n = tr_buildPath( newDir, name, NULL );
357 rename( o, n );
358 ++count;
359 tr_free( n );
360 tr_free( o );
364 if( count )
365 tr_inf( _( "Migrated %1$d files from \"%2$s\" to \"%3$s\"" ),
366 count, oldDir, newDir );
367 closedir( dirh );
373 * This function is for transmission-gtk users to migrate the config files
374 * from $HOME/.transmission/ (where they were kept before Transmission 1.30)
375 * to $HOME/.config/$appname as per the XDG directory spec.
377 static void
378 migrateFiles( const tr_session * session )
380 static int migrated = FALSE;
381 const tr_bool should_migrate = strstr( getOldConfigDir(), ".transmission" ) != NULL;
383 if( !migrated && should_migrate )
385 const char * oldDir;
386 const char * newDir;
387 migrated = TRUE;
389 oldDir = getOldTorrentsDir( );
390 newDir = tr_getTorrentDir( session );
391 moveFiles( oldDir, newDir );
393 oldDir = getOldCacheDir( );
394 newDir = tr_getResumeDir( session );
395 moveFiles( oldDir, newDir );
399 void
400 tr_setConfigDir( tr_session * session, const char * configDir )
402 char * path;
404 session->configDir = tr_strdup( configDir );
406 path = tr_buildPath( configDir, RESUME_SUBDIR, NULL );
407 tr_mkdirp( path, 0777 );
408 session->resumeDir = path;
410 path = tr_buildPath( configDir, TORRENT_SUBDIR, NULL );
411 tr_mkdirp( path, 0777 );
412 session->torrentDir = path;
414 migrateFiles( session );
417 const char *
418 tr_sessionGetConfigDir( const tr_session * session )
420 return session->configDir;
423 const char *
424 tr_getTorrentDir( const tr_session * session )
426 return session->torrentDir;
429 const char *
430 tr_getResumeDir( const tr_session * session )
432 return session->resumeDir;
435 const char*
436 tr_getDefaultConfigDir( const char * appname )
438 static char * s = NULL;
440 if( !appname || !*appname )
441 appname = "Transmission";
443 if( !s )
445 if( ( s = getenv( "TRANSMISSION_HOME" ) ) )
447 s = tr_strdup( s );
449 else
451 #ifdef SYS_DARWIN
452 s = tr_buildPath( getHomeDir( ), "Library", "Application Support",
453 appname, NULL );
454 #elif defined( WIN32 )
455 char appdata[TR_PATH_MAX]; /* SHGetFolderPath() requires MAX_PATH */
456 SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
457 s = tr_buildPath( appdata, appname, NULL );
458 #elif defined( __HAIKU__ )
459 char buf[TR_PATH_MAX];
460 find_directory( B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof(buf) );
461 s = tr_buildPath( buf, appname, NULL );
462 #else
463 if( ( s = getenv( "XDG_CONFIG_HOME" ) ) )
464 s = tr_buildPath( s, appname, NULL );
465 else
466 s = tr_buildPath( getHomeDir( ), ".config", appname, NULL );
467 #endif
471 return s;
474 const char*
475 tr_getDefaultDownloadDir( void )
477 static char * user_dir = NULL;
479 if( user_dir == NULL )
481 const char * config_home;
482 char * config_file;
483 char * content;
484 size_t content_len;
486 /* figure out where to look for user-dirs.dirs */
487 config_home = getenv( "XDG_CONFIG_HOME" );
488 if( config_home && *config_home )
489 config_file = tr_buildPath( config_home, "user-dirs.dirs", NULL );
490 else
491 config_file = tr_buildPath( getHomeDir( ), ".config", "user-dirs.dirs", NULL );
493 /* read in user-dirs.dirs and look for the download dir entry */
494 content = (char *) tr_loadFile( config_file, &content_len );
495 if( content && content_len>0 )
497 const char * key = "XDG_DOWNLOAD_DIR=\"";
498 char * line = strstr( content, key );
499 if( line != NULL )
501 char * value = line + strlen( key );
502 char * end = strchr( value, '"' );
504 if( end )
506 *end = '\0';
508 if( !memcmp( value, "$HOME/", 6 ) )
509 user_dir = tr_buildPath( getHomeDir( ), value+6, NULL );
510 else
511 user_dir = tr_strdup( value );
516 if( user_dir == NULL )
517 #ifdef __HAIKU__
518 user_dir = tr_buildPath( getHomeDir( ), "Desktop", NULL );
519 #else
520 user_dir = tr_buildPath( getHomeDir( ), "Downloads", NULL );
521 #endif
523 tr_free( content );
524 tr_free( config_file );
527 return user_dir;
530 /***
531 ****
532 ***/
534 static int
535 isWebClientDir( const char * path )
537 struct stat sb;
538 char * tmp = tr_buildPath( path, "index.html", NULL );
539 const int ret = !stat( tmp, &sb );
540 tr_inf( _( "Searching for web interface file \"%s\"" ), tmp );
541 tr_free( tmp );
542 return ret;
545 const char *
546 tr_getWebClientDir( const tr_session * session UNUSED )
548 static char * s = NULL;
550 if( !s )
552 if( ( s = getenv( "CLUTCH_HOME" ) ) )
554 s = tr_strdup( s );
556 else if( ( s = getenv( "TRANSMISSION_WEB_HOME" ) ) )
558 s = tr_strdup( s );
560 else
563 #ifdef SYS_DARWIN /* on Mac, look in the Application Support folder first, then in the app bundle. */
565 /* Look in the Application Support folder */
566 s = tr_buildPath( tr_sessionGetConfigDir( session ), "web", NULL );
568 if( !isWebClientDir( s ) ) {
569 tr_free( s );
571 CFURLRef appURL = CFBundleCopyBundleURL( CFBundleGetMainBundle( ) );
572 CFStringRef appRef = CFURLCopyFileSystemPath( appURL,
573 kCFURLPOSIXPathStyle );
574 CFIndex appLength = CFStringGetMaximumSizeForEncoding( CFStringGetLength(appRef),
575 CFStringGetFastestEncoding( appRef ));
577 char * appString = tr_malloc( appLength + 1 );
578 tr_bool success = CFStringGetCString( appRef,
579 appString,
580 appLength + 1,
581 CFStringGetFastestEncoding( appRef ));
582 assert( success );
584 CFRelease( appURL );
585 CFRelease( appRef );
587 /* Fallback to the app bundle */
588 s = tr_buildPath( appString, "Contents", "Resources", "web", NULL );
589 if( !isWebClientDir( s ) ) {
590 tr_free( s );
591 s = NULL;
594 tr_free( appString );
597 #elif defined( WIN32 )
599 /* SHGetFolderPath explicitly requires MAX_PATH length */
600 char dir[MAX_PATH];
602 /* Generally, Web interface should be stored in a Web subdir of
603 * calling executable dir. */
605 if( s == NULL ) {
606 /* First, we should check personal AppData/Transmission/Web */
607 SHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, NULL, 0, dir );
608 s = tr_buildPath( dir, "Transmission", "Web", NULL );
609 if( !isWebClientDir( s ) ) {
610 tr_free( s );
611 s = NULL;
615 if( s == NULL ) {
616 /* check personal AppData */
617 SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, dir );
618 s = tr_buildPath( dir, "Transmission", "Web", NULL );
619 if( !isWebClientDir( s ) ) {
620 tr_free( s );
621 s = NULL;
625 if( s == NULL) {
626 /* check calling module place */
627 GetModuleFileName( GetModuleHandle( NULL ), dir, sizeof( dir ) );
628 s = tr_buildPath( dirname( dir ), "Web", NULL );
629 if( !isWebClientDir( s ) ) {
630 tr_free( s );
631 s = NULL;
635 #else /* everyone else, follow the XDG spec */
637 tr_list *candidates = NULL, *l;
638 const char * tmp;
640 /* XDG_DATA_HOME should be the first in the list of candidates */
641 tmp = getenv( "XDG_DATA_HOME" );
642 if( tmp && *tmp )
643 tr_list_append( &candidates, tr_strdup( tmp ) );
644 else {
645 char * dhome = tr_buildPath( getHomeDir( ), ".local", "share", NULL );
646 tr_list_append( &candidates, dhome );
649 /* XDG_DATA_DIRS are the backup directories */
651 const char * pkg = PACKAGE_DATA_DIR;
652 const char * xdg = getenv( "XDG_DATA_DIRS" );
653 const char * fallback = "/usr/local/share:/usr/share";
654 char * buf = tr_strdup_printf( "%s:%s:%s", (pkg?pkg:""), (xdg?xdg:""), fallback );
655 tmp = buf;
656 while( tmp && *tmp ) {
657 const char * end = strchr( tmp, ':' );
658 if( end ) {
659 if( ( end - tmp ) > 1 )
660 tr_list_append( &candidates, tr_strndup( tmp, end - tmp ) );
661 tmp = end + 1;
662 } else if( tmp && *tmp ) {
663 tr_list_append( &candidates, tr_strdup( tmp ) );
664 break;
667 tr_free( buf );
670 /* walk through the candidates & look for a match */
671 for( l=candidates; l; l=l->next ) {
672 char * path = tr_buildPath( l->data, "transmission", "web", NULL );
673 const int found = isWebClientDir( path );
674 if( found ) {
675 s = path;
676 break;
678 tr_free( path );
681 tr_list_free( &candidates, tr_free );
683 #endif
688 return s;
691 /***
692 ****
693 ***/
695 int64_t
696 tr_getFreeSpace( const char * path )
698 #ifdef WIN32
699 uint64_t freeBytesAvailable = 0;
700 return GetDiskFreeSpaceEx( path, &freeBytesAvailable, NULL, NULL)
701 ? (int64_t)freeBytesAvailable
702 : -1;
703 #elif defined(HAVE_STATVFS)
704 struct statvfs buf;
705 return statvfs( path, &buf ) ? -1 : (int64_t)buf.f_bavail * (int64_t)buf.f_bsize;
706 #else
707 #warning FIXME: not implemented
708 return -1;
709 #endif
712 /***
713 ****
714 ***/
716 #ifdef WIN32
718 /* The following mmap functions are by Joerg Walter, and were taken from
719 * his paper at: http://www.genesys-e.de/jwalter/mix4win.htm
722 #if defined(_MSC_VER)
723 __declspec( align( 4 ) ) static LONG volatile g_sl;
724 #else
725 static LONG volatile g_sl __attribute__ ( ( aligned ( 4 ) ) );
726 #endif
728 /* Wait for spin lock */
729 static int
730 slwait( LONG volatile *sl )
732 while( InterlockedCompareExchange ( sl, 1, 0 ) != 0 )
733 Sleep ( 0 );
735 return 0;
738 /* Release spin lock */
739 static int
740 slrelease( LONG volatile *sl )
742 InterlockedExchange ( sl, 0 );
743 return 0;
746 /* getpagesize for windows */
747 static long
748 getpagesize( void )
750 static long g_pagesize = 0;
752 if( !g_pagesize )
754 SYSTEM_INFO system_info;
755 GetSystemInfo ( &system_info );
756 g_pagesize = system_info.dwPageSize;
758 return g_pagesize;
761 static long
762 getregionsize( void )
764 static long g_regionsize = 0;
766 if( !g_regionsize )
768 SYSTEM_INFO system_info;
769 GetSystemInfo ( &system_info );
770 g_regionsize = system_info.dwAllocationGranularity;
772 return g_regionsize;
775 void *
776 mmap( void *ptr,
777 long size,
778 long prot,
779 long type,
780 long handle,
781 long arg )
783 static long g_pagesize;
784 static long g_regionsize;
786 /* Wait for spin lock */
787 slwait ( &g_sl );
788 /* First time initialization */
789 if( !g_pagesize )
790 g_pagesize = getpagesize ( );
791 if( !g_regionsize )
792 g_regionsize = getregionsize ( );
793 /* Allocate this */
794 ptr = VirtualAlloc ( ptr, size,
795 MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
796 PAGE_READWRITE );
797 if( !ptr )
799 ptr = (void *) -1;
800 goto mmap_exit;
802 mmap_exit:
803 /* Release spin lock */
804 slrelease ( &g_sl );
805 return ptr;
808 long
809 munmap( void *ptr,
810 long size )
812 static long g_pagesize;
813 static long g_regionsize;
814 int rc = -1;
816 /* Wait for spin lock */
817 slwait ( &g_sl );
818 /* First time initialization */
819 if( !g_pagesize )
820 g_pagesize = getpagesize ( );
821 if( !g_regionsize )
822 g_regionsize = getregionsize ( );
823 /* Free this */
824 if( !VirtualFree ( ptr, 0,
825 MEM_RELEASE ) )
826 goto munmap_exit;
827 rc = 0;
828 munmap_exit:
829 /* Release spin lock */
830 slrelease ( &g_sl );
831 return rc;
834 #endif