source: trunk/libtransmission/platform.c @ 6356

Last change on this file since 6356 was 6356, checked in by livings124, 13 years ago

eliminate 2 tiny memory leaks from the last commit

  • Property svn:keywords set to Date Rev Author Id
File size: 16.5 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 6356 2008-07-18 04:57:38Z livings124 $
3 *
4 * Copyright (c) 2005-2008 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#ifdef __BEOS__
26  #include <signal.h> 
27  #include <fs_info.h>
28  #include <FindDirectory.h>
29  #include <kernel/OS.h>
30  #define BEOS_MAX_THREADS 256
31#elif defined(WIN32)
32  #include <windows.h>
33  #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_PROFILE */
34#else
35  #ifdef SYS_DARWIN
36    #include <CoreFoundation/CoreFoundation.h>
37  #endif
38   
39  #define _XOPEN_SOURCE 500 /* needed for recursive locks. */
40  #ifndef __USE_UNIX98
41  #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
42  #endif
43  #include <pthread.h>
44#endif
45
46#include <assert.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50
51#include <sys/stat.h>
52#include <sys/types.h>
53#include <dirent.h>
54#include <fcntl.h>
55#include <unistd.h> /* getuid getpid close */
56
57#include "transmission.h"
58#include "list.h"
59#include "platform.h"
60#include "utils.h"
61
62/***
63****  THREADS
64***/
65
66#ifdef __BEOS__
67typedef thread_id tr_thread_id;
68#elif defined(WIN32)
69typedef DWORD tr_thread_id;
70#else
71typedef pthread_t tr_thread_id;
72#endif
73
74static tr_thread_id
75tr_getCurrentThread( void )
76{
77#ifdef __BEOS__
78    return find_thread( NULL );
79#elif defined(WIN32)
80    return GetCurrentThreadId();
81#else
82    return pthread_self( );
83#endif
84}
85
86static int
87tr_areThreadsEqual( tr_thread_id a, tr_thread_id b )
88{
89#ifdef __BEOS__
90    return a == b;
91#elif defined(WIN32)
92    return a == b;
93#else
94    return pthread_equal( a, b );
95#endif
96}
97
98struct tr_thread
99{
100    void          (* func ) ( void * );
101    void           * arg;
102    const char     * name;
103    tr_thread_id     thread;
104#ifdef WIN32
105    HANDLE           thread_handle;
106#endif
107};
108
109int
110tr_amInThread ( const tr_thread * t )
111{
112    return tr_areThreadsEqual( tr_getCurrentThread(), t->thread );
113}
114
115#ifdef WIN32
116#define ThreadFuncReturnType unsigned WINAPI
117#else
118#define ThreadFuncReturnType void
119#endif
120
121static ThreadFuncReturnType
122ThreadFunc( void * _t )
123{
124    tr_thread * t = _t;
125    const char * name = t->name;
126
127#ifdef __BEOS__
128    /* This is required because on BeOS, SIGINT is sent to each thread,
129       which kills them not nicely */
130    signal( SIGINT, SIG_IGN );
131#endif
132
133    tr_dbg( "Thread '%s' started", name );
134    t->func( t->arg );
135    tr_dbg( "Thread '%s' exited", name );
136
137#ifdef WIN32
138    _endthreadex( 0 );
139    return 0;
140#endif
141}
142
143tr_thread *
144tr_threadNew( void (*func)(void *),
145              void * arg,
146              const char * name )
147{
148    tr_thread * t = tr_new0( tr_thread, 1 );
149    t->func = func;
150    t->arg  = arg;
151    t->name = name;
152
153#ifdef __BEOS__
154    t->thread = spawn_thread( (void*)ThreadFunc, name, B_NORMAL_PRIORITY, t );
155    resume_thread( t->thread );
156#elif defined(WIN32)
157    {
158        unsigned int id;
159        t->thread_handle = (HANDLE) _beginthreadex( NULL, 0, &ThreadFunc, t, 0, &id );
160        t->thread = (DWORD) id;
161    }
162#else
163    pthread_create( &t->thread, NULL, (void * (*) (void *)) ThreadFunc, t );
164#endif
165
166    return t;
167}
168   
169/***
170****  LOCKS
171***/
172
173struct tr_lock
174{
175    int depth;
176#ifdef __BEOS__
177    sem_id lock;
178    thread_id lockThread;
179#elif defined(WIN32)
180    CRITICAL_SECTION lock;
181    DWORD lockThread;
182#else
183    pthread_mutex_t lock;
184    pthread_t lockThread;
185#endif
186};
187
188tr_lock*
189tr_lockNew( void )
190{
191    tr_lock * l = tr_new0( tr_lock, 1 );
192
193#ifdef __BEOS__
194    l->lock = create_sem( 1, "" );
195#elif defined(WIN32)
196    InitializeCriticalSection( &l->lock ); /* supports recursion */
197#else
198    pthread_mutexattr_t attr;
199    pthread_mutexattr_init( &attr );
200    pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
201    pthread_mutex_init( &l->lock, &attr );
202#endif
203
204    return l;
205}
206
207void
208tr_lockFree( tr_lock * l )
209{
210#ifdef __BEOS__
211    delete_sem( l->lock );
212#elif defined(WIN32)
213    DeleteCriticalSection( &l->lock );
214#else
215    pthread_mutex_destroy( &l->lock );
216#endif
217    tr_free( l );
218}
219
220void
221tr_lockLock( tr_lock * l )
222{
223#ifdef __BEOS__
224    acquire_sem( l->lock );
225#elif defined(WIN32)
226    EnterCriticalSection( &l->lock );
227#else
228    pthread_mutex_lock( &l->lock );
229#endif
230    assert( l->depth >= 0 );
231    if( l->depth )
232        assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ) );
233    l->lockThread = tr_getCurrentThread( );
234    ++l->depth;
235}
236
237int
238tr_lockHave( const tr_lock * l )
239{
240    return ( l->depth > 0 )
241        && ( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ) );
242}
243
244void
245tr_lockUnlock( tr_lock * l )
246{
247    assert( l->depth > 0 );
248    assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ));
249
250    --l->depth;
251    assert( l->depth >= 0 );
252#ifdef __BEOS__
253    release_sem( l->lock );
254#elif defined(WIN32)
255    LeaveCriticalSection( &l->lock );
256#else
257    pthread_mutex_unlock( &l->lock );
258#endif
259}
260
261/***
262****  PATHS
263***/
264
265#if !defined(WIN32) && !defined(__BEOS__) && !defined(__AMIGAOS4__)
266#include <pwd.h>
267#endif
268
269static const char *
270getHomeDir( void )
271{
272    static char * home = NULL;
273
274    if( !home )
275    {
276        home = tr_strdup( getenv( "HOME" ) );
277
278        if( !home )
279        {
280#ifdef WIN32
281            SHGetFolderPath( NULL, CSIDL_PROFILE, NULL, 0, home );
282#elif defined(__BEOS__) || defined(__AMIGAOS4__)
283            home = tr_strdup( "" );
284#else
285            struct passwd * pw = getpwuid( getuid() );
286            if( pw )
287                home = tr_strdup( pw->pw_dir );
288            endpwent( );
289#endif
290        }
291
292        if( !home )
293            home = tr_strdup( "" );
294    }
295
296    return home;
297}
298
299static const char *
300getOldConfigDir( void )
301{
302    static char * path = NULL;
303
304    if( !path )
305    {
306        char buf[MAX_PATH_LENGTH];
307#ifdef __BEOS__
308        find_directory( B_USER_SETTINGS_DIRECTORY,
309                        dev_for_path("/boot"), true,
310                        buf, sizeof( buf ) );
311        strcat( buf, "/Transmission" );
312#elif defined( SYS_DARWIN )
313        tr_buildPath ( buf, sizeof( buf ), getHomeDir( ),
314                       "Library", "Application Support",
315                       "Transmission", NULL );
316#elif defined(__AMIGAOS4__)
317        tr_strlcpy( buf, "PROGDIR:.transmission", sizeof( buf ) );
318#elif defined(WIN32)
319        char appdata[MAX_PATH_LENGTH];
320        SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
321        tr_buildPath( buf, sizeof(buf),
322                      appdata, "Transmission", NULL );
323#else
324        tr_buildPath ( buf, sizeof(buf),
325                       getHomeDir( ), ".transmission", NULL );
326#endif
327        path = tr_strdup( buf );
328    }
329
330    return path;
331}
332
333static const char *
334getOldTorrentsDir( void )
335{
336    static char * path = NULL;
337
338    if( !path )
339    {
340        char buf[MAX_PATH_LENGTH];
341        const char * p = getOldConfigDir();
342#if defined(__BEOS__) || defined(WIN32) || defined(SYS_DARWIN)
343        tr_buildPath( buf, sizeof( buf ), p, "Torrents", NULL );
344#else
345        tr_buildPath( buf, sizeof( buf ), p, "torrents", NULL );
346#endif
347
348        path = tr_strdup( buf );
349    }
350
351    return path;
352}
353static const char *
354getOldCacheDir( void )
355{
356    static char * path = NULL;
357
358    if( !path )
359    {
360        char buf[MAX_PATH_LENGTH];
361#if defined(__BEOS__) || defined(WIN32)
362        const char * p = getOldConfigDir( );
363        tr_buildPath( buf, sizeof( buf ), p, "Cache", NULL );
364#elif defined( SYS_DARWIN )
365        tr_buildPath( buf, sizeof( buf ), getHomeDir(),
366                      "Library", "Caches", "Transmission", NULL );
367#else
368        const char * p = getOldConfigDir( );
369        tr_buildPath( buf, sizeof( buf ), p, "cache", NULL );
370#endif
371        path = tr_strdup( buf );
372    }
373
374    return path;
375}
376
377static void
378moveFiles( const char * oldDir, const char * newDir )
379{
380    if( oldDir && newDir && strcmp( oldDir, newDir ) )
381    {
382        DIR * dirh = opendir( oldDir );
383        if( dirh )
384        {
385            int count = 0;
386            struct dirent * dirp;
387            while(( dirp = readdir( dirh )))
388            {
389                if( strcmp( dirp->d_name, "." ) && strcmp( dirp->d_name, ".." ) )
390                {
391                    char o[MAX_PATH_LENGTH];
392                    char n[MAX_PATH_LENGTH];
393                    tr_buildPath( o, sizeof(o), oldDir, dirp->d_name, NULL );
394                    tr_buildPath( n, sizeof(n), newDir, dirp->d_name, NULL );
395                    rename( o, n );
396                    ++count;
397                }
398            }
399
400            if( count )
401                tr_inf( _( "Migrated %1$d files from \"%2$s\" to \"%3$s\"" ),
402                        count, oldDir, newDir );
403            closedir( dirh );
404        }
405    }
406}
407
408static void
409migrateFiles( const tr_handle * handle )
410{
411    static int migrated = FALSE;
412
413    if( !migrated )
414    {
415        const char * oldDir;
416        const char * newDir;
417        migrated = TRUE;
418
419        oldDir = getOldTorrentsDir( );
420        newDir = tr_getTorrentDir( handle );
421        moveFiles( oldDir, newDir );
422
423        oldDir = getOldCacheDir( );
424        newDir = tr_getResumeDir( handle );
425        moveFiles( oldDir, newDir );
426    }
427}
428
429#ifdef SYS_DARWIN
430#define RESUME_SUBDIR  "Resume"
431#define TORRENT_SUBDIR "Torrents"
432#else
433#define RESUME_SUBDIR  "resume"
434#define TORRENT_SUBDIR "torrents"
435#endif
436
437void
438tr_setConfigDir( tr_handle * handle, const char * configDir )
439{
440    char buf[MAX_PATH_LENGTH];
441
442    handle->configDir = tr_strdup( configDir );
443
444    tr_buildPath( buf, sizeof( buf ), configDir, RESUME_SUBDIR, NULL );
445    tr_mkdirp( buf, 0777 );
446    handle->resumeDir = tr_strdup( buf );
447
448    tr_buildPath( buf, sizeof( buf ), configDir, TORRENT_SUBDIR, NULL );
449    tr_mkdirp( buf, 0777 );
450    handle->torrentDir = tr_strdup( buf );
451
452    migrateFiles( handle );
453}
454
455const char *
456tr_sessionGetConfigDir( const tr_handle * handle )
457{
458    return handle->configDir;
459}
460
461const char *
462tr_getTorrentDir( const tr_handle * handle )
463{
464    return handle->torrentDir;
465}
466
467const char *
468tr_getResumeDir( const tr_handle * handle )
469{
470    return handle->resumeDir;
471}
472
473const char*
474tr_getDefaultConfigDir( void )
475{
476    static char * s = NULL;
477
478    if( !s )
479    {
480        char path[MAX_PATH_LENGTH];
481
482        if(( s = getenv( "TRANSMISSION_HOME" )))
483        {
484            tr_snprintf( path, sizeof( path ), s );
485        }
486        else
487        {
488#ifdef SYS_DARWIN
489            tr_buildPath( path, sizeof( path ),
490                          getHomeDir( ), "Library", "Application Support",
491                          "Transmission", NULL );
492#elif defined(WIN32)
493            char appdata[MAX_PATH_LENGTH];
494            SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
495            tr_buildPath( path, sizeof( path ),
496                          appdata, "Transmission", NULL );
497#else
498            if(( s = getenv( "XDG_CONFIG_HOME" )))
499                tr_buildPath( path, sizeof( path ),
500                              s, "transmission", NULL );
501            else
502                tr_buildPath( path, sizeof( path ),
503                              getHomeDir(), ".config", "transmission", NULL );
504#endif
505        }
506
507        s = tr_strdup( path );
508    }
509
510    return s;
511}
512
513/***
514****
515***/
516
517static int
518isClutchDir( const char * path )
519{
520    struct stat sb;
521    char tmp[MAX_PATH_LENGTH];
522    tr_buildPath( tmp, sizeof( tmp ), path, "javascript", "transmission.js", NULL );
523    tr_inf( _( "Searching for web interface file \"%s\"" ), tmp );
524    return !stat( tmp, &sb );
525}
526
527const char *
528tr_getClutchDir( const tr_session * session UNUSED )
529{
530    static char * s = NULL;
531
532    if( !s )
533    {
534        char path[MAX_PATH_LENGTH] = { '\0' };
535
536        if(( s = getenv( "CLUTCH_HOME" )))
537        {
538            tr_snprintf( path, sizeof( path ), s );
539        }
540        else if(( s = getenv( "TRANSMISSION_WEB_HOME" )))
541        {
542            tr_snprintf( path, sizeof( path ), s );
543        }
544        else
545        {
546#ifdef SYS_DARWIN
547           
548            CFURLRef appURL = CFBundleCopyBundleURL( CFBundleGetMainBundle() );
549            CFStringRef appRef = CFURLCopyPath( appURL );
550            const char * appString = CFStringGetCStringPtr( appRef, CFStringGetFastestEncoding( appRef ) );
551            CFRelease(appURL);
552            CFRelease(appRef);
553           
554            /*CFURLRef resourcesDirURL = CFBundleCopyResourcesDirectoryURL( CFBundleGetMainBundle() );
555            CFStringRef resourcesDirRef = CFURLCopyPath( resourcesDirURL );
556            const char * resourcesDirString = CFStringGetCStringPtr( resourcesDirRef, CFStringGetFastestEncoding( resourcesDirRef ) );
557            CFRelease(resourcesDirURL);
558            CFRelease(resourcesDirRef);*/
559           
560            sprintf( path, "%s%s", appString, "Contents/Resources/web" );
561
562#elif defined(WIN32)
563
564            #warning hey win32 people is this good or is there a better implementation of the next four lines
565            char appdata[MAX_PATH_LENGTH];
566            SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
567            tr_buildPath( path, sizeof( path ),
568                          appdata, "Transmission", NULL );
569#else
570            tr_list *candidates=NULL, *l;
571
572            /* XDG_DATA_HOME should be the first in the list of candidates */
573            s = getenv( "XDG_DATA_HOME" );
574            if( s && *s )
575                tr_list_append( &candidates, tr_strdup( s ) );
576            else {
577                char tmp[MAX_PATH_LENGTH];
578                tr_buildPath( tmp, sizeof( tmp ), getHomeDir(), ".local", "share", NULL );
579                tr_list_append( &candidates, tr_strdup( tmp ) );
580            }
581
582            /* XDG_DATA_DIRS are the backup directories */
583            s = getenv( "XDG_DATA_DIRS" );
584            if( !s || !*s )
585                s = "/usr/local/share/:/usr/share/";
586            while( s && *s ) {
587                char * end = strchr( s, ':' );
588                if( end ) {
589                    tr_list_append( &candidates, tr_strndup( s, end-s ) );
590                    s = end + 1;
591                } else {
592                    tr_list_append( &candidates, tr_strdup( s ) );
593                    break;
594                }
595            }
596
597            for( l=candidates; l; l=l->next ) {
598                tr_buildPath( path, sizeof( path ), l->data, "transmission", "web", NULL );
599                if( isClutchDir( path ) )
600                    break;
601                *path = '\0';
602            }
603
604            tr_list_free( &candidates, tr_free );
605#endif
606        }
607
608        if( !*path )
609        {
610            tr_strlcpy( path, "/dev/null", sizeof( path ) );
611            tr_err( _( "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." ) );
612        }
613
614        s = tr_strdup( path );
615    }
616
617    return s;
618}
619
620
621/***
622****
623***/
624
625int
626tr_lockfile( const char * filename )
627{
628    int ret;
629
630#ifdef WIN32
631
632    HANDLE file = CreateFile( filename,
633                              GENERIC_READ|GENERIC_WRITE,
634                              FILE_SHARE_READ|FILE_SHARE_WRITE,
635                              NULL,
636                              OPEN_ALWAYS,
637                              FILE_ATTRIBUTE_NORMAL,
638                              NULL );
639    if( file == INVALID_HANDLE_VALUE )
640        ret = TR_LOCKFILE_EOPEN;
641    else if( !LockFile( file, 0, 0, 1, 1 ) )
642        ret = TR_LOCKFILE_ELOCK;
643    else
644        ret = TR_LOCKFILE_SUCCESS;
645
646#else
647
648    int fd = open( filename, O_RDWR | O_CREAT, 0666 );
649    if( fd < 0 )
650        ret = TR_LOCKFILE_EOPEN;
651    else {
652        struct flock lk;
653        memset( &lk, 0,  sizeof( lk ) );
654        lk.l_start = 0;
655        lk.l_len = 0;
656        lk.l_type = F_WRLCK;
657        lk.l_whence = SEEK_SET;
658        if( -1 == fcntl( fd, F_SETLK, &lk ) )
659            ret = TR_LOCKFILE_ELOCK;
660        else
661            ret = TR_LOCKFILE_SUCCESS;
662    }
663
664#endif
665
666    return ret;
667}
Note: See TracBrowser for help on using the repository browser.