source: trunk/libtransmission/platform.c @ 5636

Last change on this file since 5636 was 5636, checked in by charles, 13 years ago

fix a couple more minor leaks

  • Property svn:keywords set to Date Rev Author Id
File size: 13.2 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 5636 2008-04-17 19:54:22Z charles $
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  #define _XOPEN_SOURCE 500 /* needed for recursive locks. */
36  #ifndef __USE_UNIX98
37  #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
38  #endif
39  #include <pthread.h>
40#endif
41
42#include <assert.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46
47#include <sys/types.h>
48#include <dirent.h>
49#include <fcntl.h>
50#include <unistd.h> /* getuid getpid close */
51
52#include "transmission.h"
53#include "platform.h"
54#include "trcompat.h"
55#include "utils.h"
56
57/***
58****  THREADS
59***/
60
61#ifdef __BEOS__
62typedef thread_id tr_thread_id;
63#elif defined(WIN32)
64typedef DWORD tr_thread_id;
65#else
66typedef pthread_t tr_thread_id;
67#endif
68
69static tr_thread_id
70tr_getCurrentThread( void )
71{
72#ifdef __BEOS__
73    return find_thread( NULL );
74#elif defined(WIN32)
75    return GetCurrentThreadId();
76#else
77    return pthread_self( );
78#endif
79}
80
81static int
82tr_areThreadsEqual( tr_thread_id a, tr_thread_id b )
83{
84#ifdef __BEOS__
85    return a == b;
86#elif defined(WIN32)
87    return a == b;
88#else
89    return pthread_equal( a, b );
90#endif
91}
92
93struct tr_thread
94{
95    void          (* func ) ( void * );
96    void           * arg;
97    const char     * name;
98    tr_thread_id     thread;
99#ifdef WIN32
100    HANDLE           thread_handle;
101#endif
102};
103
104int
105tr_amInThread ( const tr_thread * t )
106{
107    return tr_areThreadsEqual( tr_getCurrentThread(), t->thread );
108}
109
110#ifdef WIN32
111#define ThreadFuncReturnType unsigned WINAPI
112#else
113#define ThreadFuncReturnType void
114#endif
115
116static ThreadFuncReturnType
117ThreadFunc( void * _t )
118{
119    tr_thread * t = _t;
120    const char * name = t->name;
121
122#ifdef __BEOS__
123    /* This is required because on BeOS, SIGINT is sent to each thread,
124       which kills them not nicely */
125    signal( SIGINT, SIG_IGN );
126#endif
127
128    tr_dbg( "Thread '%s' started", name );
129    t->func( t->arg );
130    tr_dbg( "Thread '%s' exited", name );
131
132#ifdef WIN32
133    _endthreadex( 0 );
134    return 0;
135#endif
136}
137
138tr_thread *
139tr_threadNew( void (*func)(void *),
140              void * arg,
141              const char * name )
142{
143    tr_thread * t = tr_new0( tr_thread, 1 );
144    t->func = func;
145    t->arg  = arg;
146    t->name = name;
147
148#ifdef __BEOS__
149    t->thread = spawn_thread( (void*)ThreadFunc, name, B_NORMAL_PRIORITY, t );
150    resume_thread( t->thread );
151#elif defined(WIN32)
152    {
153        unsigned int id;
154        t->thread_handle = (HANDLE) _beginthreadex( NULL, 0, &ThreadFunc, t, 0, &id );
155        t->thread = (DWORD) id;
156    }
157#else
158    pthread_create( &t->thread, NULL, (void * (*) (void *)) ThreadFunc, t );
159#endif
160
161    return t;
162}
163   
164void
165tr_threadJoin( tr_thread * t )
166{
167    if( t != NULL )
168    {
169#ifdef __BEOS__
170        long exit;
171        wait_for_thread( t->thread, &exit );
172#elif defined(WIN32)
173        WaitForSingleObject( t->thread_handle, INFINITE );
174        CloseHandle( t->thread_handle );
175#else
176        pthread_join( t->thread, NULL );
177#endif
178
179        tr_dbg( "Thread '%s' joined", t->name );
180        t->name = NULL;
181        t->func = NULL;
182        tr_free( t );
183    }
184}
185
186/***
187****  LOCKS
188***/
189
190struct tr_lock
191{
192    int depth;
193#ifdef __BEOS__
194    sem_id lock;
195    thread_id lockThread;
196#elif defined(WIN32)
197    CRITICAL_SECTION lock;
198    DWORD lockThread;
199#else
200    pthread_mutex_t lock;
201    pthread_t lockThread;
202#endif
203};
204
205tr_lock*
206tr_lockNew( void )
207{
208    tr_lock * l = tr_new0( tr_lock, 1 );
209
210#ifdef __BEOS__
211    l->lock = create_sem( 1, "" );
212#elif defined(WIN32)
213    InitializeCriticalSection( &l->lock ); /* critical sections support recursion */
214#else
215    pthread_mutexattr_t attr;
216    pthread_mutexattr_init( &attr );
217    pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
218    pthread_mutex_init( &l->lock, &attr );
219#endif
220
221    return l;
222}
223
224void
225tr_lockFree( tr_lock * l )
226{
227#ifdef __BEOS__
228    delete_sem( l->lock );
229#elif defined(WIN32)
230    DeleteCriticalSection( &l->lock );
231#else
232    pthread_mutex_destroy( &l->lock );
233#endif
234    tr_free( l );
235}
236
237void
238tr_lockLock( tr_lock * l )
239{
240#ifdef __BEOS__
241    acquire_sem( l->lock );
242#elif defined(WIN32)
243    EnterCriticalSection( &l->lock );
244#else
245    pthread_mutex_lock( &l->lock );
246#endif
247    assert( l->depth >= 0 );
248    if( l->depth )
249        assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ) );
250    l->lockThread = tr_getCurrentThread( );
251    ++l->depth;
252}
253
254int
255tr_lockHave( const tr_lock * l )
256{
257    return ( l->depth > 0 )
258        && ( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ) );
259}
260
261void
262tr_lockUnlock( tr_lock * l )
263{
264    assert( l->depth > 0 );
265    assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ));
266
267    --l->depth;
268    assert( l->depth >= 0 );
269#ifdef __BEOS__
270    release_sem( l->lock );
271#elif defined(WIN32)
272    LeaveCriticalSection( &l->lock );
273#else
274    pthread_mutex_unlock( &l->lock );
275#endif
276}
277
278/***
279****  PATHS
280***/
281
282#if !defined(WIN32) && !defined(__BEOS__) && !defined(__AMIGAOS4__)
283#include <pwd.h>
284#endif
285
286static const char *
287getHomeDir( void )
288{
289    static char * home = NULL;
290
291    if( !home )
292    {
293        home = tr_strdup( getenv( "HOME" ) );
294
295        if( !home )
296        {
297#ifdef WIN32
298            SHGetFolderPath( NULL, CSIDL_PROFILE, NULL, 0, buf );
299#elif defined(__BEOS__) || defined(__AMIGAOS4__)
300            home = tr_strdup( "" );
301#else
302            struct passwd * pw = getpwuid( getuid() );
303            endpwent( );
304            if( pw )
305                home = tr_strdup( pw->pw_dir );
306#endif
307        }
308
309        if( !home )
310            home = tr_strdup( "" );
311    }
312
313    return home;
314}
315
316static const char *
317getOldConfigDir( void )
318{
319    static char * path = NULL;
320
321    if( !path )
322    {
323        char buf[MAX_PATH_LENGTH];
324#ifdef __BEOS__
325        find_directory( B_USER_SETTINGS_DIRECTORY,
326                        dev_for_path("/boot"), true,
327                        buf, sizeof( buf ) );
328        strcat( buf, "/Transmission" );
329#elif defined( SYS_DARWIN )
330        tr_buildPath ( buf, sizeof( buf ), getHomeDir( ),
331                       "Library", "Application Support",
332                       "Transmission", NULL );
333#elif defined(__AMIGAOS4__)
334        strlcpy( buf, "PROGDIR:.transmission", sizeof( buf ) );
335#elif defined(WIN32)
336        char appdata[MAX_PATH_LENGTH];
337        SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
338        tr_buildPath( buf, sizeof(buf),
339                      appdata, "Transmission", NULL );
340#else
341        tr_buildPath ( buf, sizeof(buf),
342                       getHomeDir( ), ".transmission", NULL );
343#endif
344        path = tr_strdup( buf );
345    }
346
347    return path;
348}
349
350static const char *
351getOldTorrentsDir( void )
352{
353    static char * path = NULL;
354
355    if( !path )
356    {
357        char buf[MAX_PATH_LENGTH];
358        const char * p = getOldConfigDir();
359#if defined(__BEOS__) || defined(WIN32) || defined(SYS_DARWIN)
360        tr_buildPath( buf, sizeof( buf ), p, "Torrents", NULL );
361#else
362        tr_buildPath( buf, sizeof( buf ), p, "torrents", NULL );
363#endif
364
365        path = tr_strdup( buf );
366    }
367
368    return path;
369}
370static const char *
371getOldCacheDir( void )
372{
373    static char * path = NULL;
374
375    if( !path )
376    {
377        char buf[MAX_PATH_LENGTH];
378        const char * p = getOldConfigDir( );
379#if defined(__BEOS__) || defined(WIN32)
380        tr_buildPath( buf, sizeof( buf ), p, "Cache", NULL );
381#elif defined( SYS_DARWIN )
382        tr_buildPath( buf, sizeof( buf ), getHomeDir(),
383                      "Library", "Caches", "Transmission", NULL );
384#else
385        tr_buildPath( buf, sizeof( buf ), p, "cache", NULL );
386#endif
387        path = tr_strdup( buf );
388    }
389
390    return path;
391}
392
393static void
394moveFiles( const char * oldDir, const char * newDir )
395{
396    if( oldDir && newDir && strcmp( oldDir, newDir ) )
397    {
398        DIR * dirh = opendir( oldDir );
399        if( dirh )
400        {
401            int count = 0;
402            struct dirent * dirp;
403            while(( dirp = readdir( dirh )))
404            {
405                if( strcmp( dirp->d_name, "." ) && strcmp( dirp->d_name, ".." ) )
406                {
407                    char o[MAX_PATH_LENGTH];
408                    char n[MAX_PATH_LENGTH];
409                    tr_buildPath( o, sizeof(o), oldDir, dirp->d_name, NULL );
410                    tr_buildPath( n, sizeof(n), newDir, dirp->d_name, NULL );
411                    rename( o, n );
412                    ++count;
413                }
414            }
415
416            if( count )
417                tr_inf( _( "Migrated %1$d files from \"%2$s\" to \"%3$s\"" ),
418                        count, oldDir, newDir );
419            closedir( dirh );
420        }
421    }
422}
423
424static void
425migrateFiles( const tr_handle * handle )
426{
427    static int migrated = FALSE;
428
429    if( !migrated )
430    {
431        const char * oldDir;
432        const char * newDir;
433        migrated = TRUE;
434
435        oldDir = getOldTorrentsDir( );
436        newDir = tr_getTorrentDir( handle );
437        moveFiles( oldDir, newDir );
438
439        oldDir = getOldCacheDir( );
440        newDir = tr_getResumeDir( handle );
441        moveFiles( oldDir, newDir );
442    }
443}
444
445#ifdef SYS_DARWIN
446#define RESUME_SUBDIR  "Resume"
447#define TORRENT_SUBDIR "Torrents"
448#else
449#define RESUME_SUBDIR  "resume"
450#define TORRENT_SUBDIR "torrents"
451#endif
452
453void
454tr_setConfigDir( tr_handle * handle, const char * configDir )
455{
456    char buf[MAX_PATH_LENGTH];
457
458    handle->configDir = tr_strdup( configDir );
459
460    tr_buildPath( buf, sizeof( buf ), configDir, RESUME_SUBDIR, NULL );
461    tr_mkdirp( buf, 0777 );
462    handle->resumeDir = tr_strdup( buf );
463
464    tr_buildPath( buf, sizeof( buf ), configDir, TORRENT_SUBDIR, NULL );
465    tr_mkdirp( buf, 0777 );
466    handle->torrentDir = tr_strdup( buf );
467
468    migrateFiles( handle );
469}
470
471const char *
472tr_getConfigDir( const tr_handle * handle )
473{
474    return handle->configDir;
475}
476
477const char *
478tr_getTorrentDir( const tr_handle * handle )
479{
480    return handle->torrentDir;
481}
482
483const char *
484tr_getResumeDir( const tr_handle * handle )
485{
486    return handle->resumeDir;
487}
488
489const char*
490tr_getDefaultConfigDir( void )
491{
492    static char * s = NULL;
493
494    if( !s )
495    {
496        char path[MAX_PATH_LENGTH];
497
498        if(( s = getenv( "TRANSMISSION_HOME" )))
499        {
500            snprintf( path, sizeof( path ), s );
501        }
502        else
503        {
504#ifdef SYS_DARWIN
505            tr_buildPath( path, sizeof( path ),
506                          getHomeDir( ), "Library", "Application Support",
507                          "Transmission", NULL );
508#elif defined(WIN32)
509            char appdata[MAX_PATH_LENGTH];
510            SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
511            tr_buildPath( path, sizeof( path ),
512                          appdata, "Transmission", NULL );
513#else
514            if(( s = getenv( "XDG_CONFIG_HOME" )))
515                tr_buildPath( path, sizeof( path ),
516                              s, "transmission", NULL );
517            else
518                tr_buildPath( path, sizeof( path ),
519                              getHomeDir(), ".config", "transmission", NULL );
520#endif
521        }
522
523        s = tr_strdup( path );
524    }
525
526    return s;
527}
528
529/***
530****
531***/
532
533int
534tr_lockfile( const char * filename )
535{
536    int ret;
537
538#ifdef WIN32
539
540    HANDLE file = CreateFile( filename,
541                              GENERIC_READ|GENERIC_WRITE,
542                              FILE_SHARE_READ|FILE_SHARE_WRITE,
543                              NULL,
544                              OPEN_ALWAYS,
545                              FILE_ATTRIBUTE_NORMAL,
546                              NULL );
547    if( file == INVALID_HANDLE_VALUE )
548        ret = TR_LOCKFILE_EOPEN;
549    else if( !LockFile( file, 0, 0, 1, 1 ) )
550        ret = TR_LOCKFILE_ELOCK;
551    else
552        ret = TR_LOCKFILE_SUCCESS;
553
554#else
555
556    int fd = open( filename, O_RDWR | O_CREAT, 0666 );
557    if( fd < 0 )
558        ret = TR_LOCKFILE_EOPEN;
559    else {
560        struct flock lk;
561        memset( &lk, 0,  sizeof( lk ) );
562        lk.l_start = 0;
563        lk.l_len = 0;
564        lk.l_type = F_WRLCK;
565        lk.l_whence = SEEK_SET;
566        if( -1 == fcntl( fd, F_SETLK, &lk ) )
567            ret = TR_LOCKFILE_ELOCK;
568        else
569            ret = TR_LOCKFILE_SUCCESS;
570    }
571
572#endif
573
574    return ret;
575}
Note: See TracBrowser for help on using the repository browser.