source: trunk/libtransmission/platform.c @ 5843

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

RPC/IPC redesign

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