source: trunk/libtransmission/platform.c @ 6319

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

(1) add clutch to the tarball.
(2) on autoconf-based installs, install the clutch files in $(datadir)/transmission/web
(3) new function tr_getClutchDir(). Default implementation follows the XDG spec.

  • Property svn:keywords set to Date Rev Author Id
File size: 15.3 KB
Line 
1/******************************************************************************
2 * $Id: platform.c 6319 2008-07-11 04:07:14Z 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 "list.h"
54#include "platform.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   
164/***
165****  LOCKS
166***/
167
168struct tr_lock
169{
170    int depth;
171#ifdef __BEOS__
172    sem_id lock;
173    thread_id lockThread;
174#elif defined(WIN32)
175    CRITICAL_SECTION lock;
176    DWORD lockThread;
177#else
178    pthread_mutex_t lock;
179    pthread_t lockThread;
180#endif
181};
182
183tr_lock*
184tr_lockNew( void )
185{
186    tr_lock * l = tr_new0( tr_lock, 1 );
187
188#ifdef __BEOS__
189    l->lock = create_sem( 1, "" );
190#elif defined(WIN32)
191    InitializeCriticalSection( &l->lock ); /* supports recursion */
192#else
193    pthread_mutexattr_t attr;
194    pthread_mutexattr_init( &attr );
195    pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
196    pthread_mutex_init( &l->lock, &attr );
197#endif
198
199    return l;
200}
201
202void
203tr_lockFree( tr_lock * l )
204{
205#ifdef __BEOS__
206    delete_sem( l->lock );
207#elif defined(WIN32)
208    DeleteCriticalSection( &l->lock );
209#else
210    pthread_mutex_destroy( &l->lock );
211#endif
212    tr_free( l );
213}
214
215void
216tr_lockLock( tr_lock * l )
217{
218#ifdef __BEOS__
219    acquire_sem( l->lock );
220#elif defined(WIN32)
221    EnterCriticalSection( &l->lock );
222#else
223    pthread_mutex_lock( &l->lock );
224#endif
225    assert( l->depth >= 0 );
226    if( l->depth )
227        assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ) );
228    l->lockThread = tr_getCurrentThread( );
229    ++l->depth;
230}
231
232int
233tr_lockHave( const tr_lock * l )
234{
235    return ( l->depth > 0 )
236        && ( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ) );
237}
238
239void
240tr_lockUnlock( tr_lock * l )
241{
242    assert( l->depth > 0 );
243    assert( tr_areThreadsEqual( l->lockThread, tr_getCurrentThread() ));
244
245    --l->depth;
246    assert( l->depth >= 0 );
247#ifdef __BEOS__
248    release_sem( l->lock );
249#elif defined(WIN32)
250    LeaveCriticalSection( &l->lock );
251#else
252    pthread_mutex_unlock( &l->lock );
253#endif
254}
255
256/***
257****  PATHS
258***/
259
260#if !defined(WIN32) && !defined(__BEOS__) && !defined(__AMIGAOS4__)
261#include <pwd.h>
262#endif
263
264static const char *
265getHomeDir( void )
266{
267    static char * home = NULL;
268
269    if( !home )
270    {
271        home = tr_strdup( getenv( "HOME" ) );
272
273        if( !home )
274        {
275#ifdef WIN32
276            SHGetFolderPath( NULL, CSIDL_PROFILE, NULL, 0, home );
277#elif defined(__BEOS__) || defined(__AMIGAOS4__)
278            home = tr_strdup( "" );
279#else
280            struct passwd * pw = getpwuid( getuid() );
281            if( pw )
282                home = tr_strdup( pw->pw_dir );
283            endpwent( );
284#endif
285        }
286
287        if( !home )
288            home = tr_strdup( "" );
289    }
290
291    return home;
292}
293
294static const char *
295getOldConfigDir( void )
296{
297    static char * path = NULL;
298
299    if( !path )
300    {
301        char buf[MAX_PATH_LENGTH];
302#ifdef __BEOS__
303        find_directory( B_USER_SETTINGS_DIRECTORY,
304                        dev_for_path("/boot"), true,
305                        buf, sizeof( buf ) );
306        strcat( buf, "/Transmission" );
307#elif defined( SYS_DARWIN )
308        tr_buildPath ( buf, sizeof( buf ), getHomeDir( ),
309                       "Library", "Application Support",
310                       "Transmission", NULL );
311#elif defined(__AMIGAOS4__)
312        tr_strlcpy( buf, "PROGDIR:.transmission", sizeof( buf ) );
313#elif defined(WIN32)
314        char appdata[MAX_PATH_LENGTH];
315        SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
316        tr_buildPath( buf, sizeof(buf),
317                      appdata, "Transmission", NULL );
318#else
319        tr_buildPath ( buf, sizeof(buf),
320                       getHomeDir( ), ".transmission", NULL );
321#endif
322        path = tr_strdup( buf );
323    }
324
325    return path;
326}
327
328static const char *
329getOldTorrentsDir( void )
330{
331    static char * path = NULL;
332
333    if( !path )
334    {
335        char buf[MAX_PATH_LENGTH];
336        const char * p = getOldConfigDir();
337#if defined(__BEOS__) || defined(WIN32) || defined(SYS_DARWIN)
338        tr_buildPath( buf, sizeof( buf ), p, "Torrents", NULL );
339#else
340        tr_buildPath( buf, sizeof( buf ), p, "torrents", NULL );
341#endif
342
343        path = tr_strdup( buf );
344    }
345
346    return path;
347}
348static const char *
349getOldCacheDir( void )
350{
351    static char * path = NULL;
352
353    if( !path )
354    {
355        char buf[MAX_PATH_LENGTH];
356#if defined(__BEOS__) || defined(WIN32)
357        const char * p = getOldConfigDir( );
358        tr_buildPath( buf, sizeof( buf ), p, "Cache", NULL );
359#elif defined( SYS_DARWIN )
360        tr_buildPath( buf, sizeof( buf ), getHomeDir(),
361                      "Library", "Caches", "Transmission", NULL );
362#else
363        const char * p = getOldConfigDir( );
364        tr_buildPath( buf, sizeof( buf ), p, "cache", NULL );
365#endif
366        path = tr_strdup( buf );
367    }
368
369    return path;
370}
371
372static void
373moveFiles( const char * oldDir, const char * newDir )
374{
375    if( oldDir && newDir && strcmp( oldDir, newDir ) )
376    {
377        DIR * dirh = opendir( oldDir );
378        if( dirh )
379        {
380            int count = 0;
381            struct dirent * dirp;
382            while(( dirp = readdir( dirh )))
383            {
384                if( strcmp( dirp->d_name, "." ) && strcmp( dirp->d_name, ".." ) )
385                {
386                    char o[MAX_PATH_LENGTH];
387                    char n[MAX_PATH_LENGTH];
388                    tr_buildPath( o, sizeof(o), oldDir, dirp->d_name, NULL );
389                    tr_buildPath( n, sizeof(n), newDir, dirp->d_name, NULL );
390                    rename( o, n );
391                    ++count;
392                }
393            }
394
395            if( count )
396                tr_inf( _( "Migrated %1$d files from \"%2$s\" to \"%3$s\"" ),
397                        count, oldDir, newDir );
398            closedir( dirh );
399        }
400    }
401}
402
403static void
404migrateFiles( const tr_handle * handle )
405{
406    static int migrated = FALSE;
407
408    if( !migrated )
409    {
410        const char * oldDir;
411        const char * newDir;
412        migrated = TRUE;
413
414        oldDir = getOldTorrentsDir( );
415        newDir = tr_getTorrentDir( handle );
416        moveFiles( oldDir, newDir );
417
418        oldDir = getOldCacheDir( );
419        newDir = tr_getResumeDir( handle );
420        moveFiles( oldDir, newDir );
421    }
422}
423
424#ifdef SYS_DARWIN
425#define RESUME_SUBDIR  "Resume"
426#define TORRENT_SUBDIR "Torrents"
427#else
428#define RESUME_SUBDIR  "resume"
429#define TORRENT_SUBDIR "torrents"
430#endif
431
432void
433tr_setConfigDir( tr_handle * handle, const char * configDir )
434{
435    char buf[MAX_PATH_LENGTH];
436
437    handle->configDir = tr_strdup( configDir );
438
439    tr_buildPath( buf, sizeof( buf ), configDir, RESUME_SUBDIR, NULL );
440    tr_mkdirp( buf, 0777 );
441    handle->resumeDir = tr_strdup( buf );
442
443    tr_buildPath( buf, sizeof( buf ), configDir, TORRENT_SUBDIR, NULL );
444    tr_mkdirp( buf, 0777 );
445    handle->torrentDir = tr_strdup( buf );
446
447    migrateFiles( handle );
448}
449
450const char *
451tr_sessionGetConfigDir( const tr_handle * handle )
452{
453    return handle->configDir;
454}
455
456const char *
457tr_getTorrentDir( const tr_handle * handle )
458{
459    return handle->torrentDir;
460}
461
462const char *
463tr_getResumeDir( const tr_handle * handle )
464{
465    return handle->resumeDir;
466}
467
468const char*
469tr_getDefaultConfigDir( void )
470{
471    static char * s = NULL;
472
473    if( !s )
474    {
475        char path[MAX_PATH_LENGTH];
476
477        if(( s = getenv( "TRANSMISSION_HOME" )))
478        {
479            snprintf( path, sizeof( path ), s );
480        }
481        else
482        {
483#ifdef SYS_DARWIN
484            tr_buildPath( path, sizeof( path ),
485                          getHomeDir( ), "Library", "Application Support",
486                          "Transmission", NULL );
487#elif defined(WIN32)
488            char appdata[MAX_PATH_LENGTH];
489            SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
490            tr_buildPath( path, sizeof( path ),
491                          appdata, "Transmission", NULL );
492#else
493            if(( s = getenv( "XDG_CONFIG_HOME" )))
494                tr_buildPath( path, sizeof( path ),
495                              s, "transmission", NULL );
496            else
497                tr_buildPath( path, sizeof( path ),
498                              getHomeDir(), ".config", "transmission", NULL );
499#endif
500        }
501
502        s = tr_strdup( path );
503    }
504
505    return s;
506}
507
508/***
509****
510***/
511
512static int
513isClutchDir( const char * path )
514{
515    struct stat sb;
516    char tmp[MAX_PATH_LENGTH];
517    tr_buildPath( tmp, sizeof( tmp ), path, "javascript", "transmission.js", NULL );
518fprintf( stderr, "path is [%s]; testing [%s] for clutch\n", path, tmp );
519    return !stat( tmp, &sb );
520}
521
522const char *
523tr_getClutchDir( const tr_session * session UNUSED )
524{
525    static char * s = NULL;
526
527    if( !s )
528    {
529        char path[MAX_PATH_LENGTH];
530
531        if(( s = getenv( "CLUTCH_HOME" )))
532        {
533            snprintf( path, sizeof( path ), s );
534        }
535        else
536        {
537#ifdef SYS_DARWIN
538#error not implemented
539#elif defined(WIN32)
540#warning hey win32 people is this good or is there a better implementation of the next four lines
541            char appdata[MAX_PATH_LENGTH];
542            SHGetFolderPath( NULL, CSIDL_APPDATA, NULL, 0, appdata );
543            tr_buildPath( path, sizeof( path ),
544                          appdata, "Transmission", NULL );
545#else
546            tr_list *candidates=NULL, *l;
547
548            /* XDG_DATA_HOME should be the first in the list of candidates */
549            s = getenv( "XDG_DATA_HOME" );
550            if( s && *s )
551                tr_list_append( &candidates, tr_strdup( s ) );
552            else {
553                char tmp[MAX_PATH_LENGTH];
554                tr_buildPath( tmp, sizeof( tmp ), getHomeDir(), ".local", "share", NULL );
555                tr_list_append( &candidates, tr_strdup( tmp ) );
556            }
557
558            /* XDG_DATA_DIRS are the backup directories */
559            s = getenv( "XDG_DATA_DIRS" );
560            if( !s || !*s )
561                s = "/usr/local/share/:/usr/share/";
562            while( s && *s ) {
563                char * end = strchr( s, ':' );
564                if( end ) {
565                    tr_list_append( &candidates, tr_strndup( s, end-s ) );
566                    s = end + 1;
567                } else {
568                    tr_list_append( &candidates, tr_strdup( s ) );
569                    break;
570                }
571            }
572
573            for( l=candidates; l; l=l->next ) {
574                tr_buildPath( path, sizeof( path ), l->data, "transmission", "clutch", NULL );
575                if( isClutchDir( path ) )
576                    break;
577                *path = '\0';
578            }
579
580            tr_list_free( &candidates, tr_free );
581#endif
582        }
583
584        if( !*path )
585        {
586            tr_err( _( "Unable to find web interface files" ) );
587            tr_strlcpy( path, "/dev/null", sizeof( path ) );
588        }
589
590        s = tr_strdup( path );
591    }
592
593    return s;
594}
595
596
597/***
598****
599***/
600
601int
602tr_lockfile( const char * filename )
603{
604    int ret;
605
606#ifdef WIN32
607
608    HANDLE file = CreateFile( filename,
609                              GENERIC_READ|GENERIC_WRITE,
610                              FILE_SHARE_READ|FILE_SHARE_WRITE,
611                              NULL,
612                              OPEN_ALWAYS,
613                              FILE_ATTRIBUTE_NORMAL,
614                              NULL );
615    if( file == INVALID_HANDLE_VALUE )
616        ret = TR_LOCKFILE_EOPEN;
617    else if( !LockFile( file, 0, 0, 1, 1 ) )
618        ret = TR_LOCKFILE_ELOCK;
619    else
620        ret = TR_LOCKFILE_SUCCESS;
621
622#else
623
624    int fd = open( filename, O_RDWR | O_CREAT, 0666 );
625    if( fd < 0 )
626        ret = TR_LOCKFILE_EOPEN;
627    else {
628        struct flock lk;
629        memset( &lk, 0,  sizeof( lk ) );
630        lk.l_start = 0;
631        lk.l_len = 0;
632        lk.l_type = F_WRLCK;
633        lk.l_whence = SEEK_SET;
634        if( -1 == fcntl( fd, F_SETLK, &lk ) )
635            ret = TR_LOCKFILE_ELOCK;
636        else
637            ret = TR_LOCKFILE_SUCCESS;
638    }
639
640#endif
641
642    return ret;
643}
Note: See TracBrowser for help on using the repository browser.