2 * This file Copyright (C) 2009-2014 Mnemosyne LLC
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
7 * $Id: platform.c 14241 2014-01-21 03:10:30Z jordan $
10 #define _XOPEN_SOURCE 600 /* needed for recursive locks. */
12 #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
19 #include <unistd.h> /* getuid(), close() */
24 #define WINVER WindowsXP
26 #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_MYDOCUMENTS */
28 #ifdef BUILD_MAC_CLIENT
29 #include <CoreFoundation/CoreFoundation.h>
32 #include <FindDirectory.h>
38 #include <libgen.h> /* dirname() */
41 #include "transmission.h"
52 typedef DWORD tr_thread_id
;
54 typedef pthread_t tr_thread_id
;
58 tr_getCurrentThread (void)
61 return GetCurrentThreadId ();
63 return pthread_self ();
68 tr_areThreadsEqual (tr_thread_id a
, tr_thread_id b
)
73 return pthread_equal (a
, b
) != 0;
77 /** @brief portability wrapper around OS-dependent threads */
80 void (* func
)(void *);
89 tr_amInThread (const tr_thread
* t
)
91 return tr_areThreadsEqual (tr_getCurrentThread (), t
->thread
);
95 #define ThreadFuncReturnType unsigned WINAPI
97 #define ThreadFuncReturnType void
100 static ThreadFuncReturnType
101 ThreadFunc (void * _t
)
115 tr_threadNew (void (*func
)(void *), void * arg
)
117 tr_thread
* t
= tr_new0 (tr_thread
, 1);
125 t
->thread_handle
= (HANDLE
) _beginthreadex (NULL
, 0, &ThreadFunc
, t
, 0, &id
);
126 t
->thread
= (DWORD
) id
;
129 pthread_create (&t
->thread
, NULL
, (void* (*)(void*))ThreadFunc
, t
);
130 pthread_detach (t
->thread
);
140 /** @brief portability wrapper around OS-dependent thread mutexes */
145 CRITICAL_SECTION lock
;
148 pthread_mutex_t lock
;
149 pthread_t lockThread
;
156 tr_lock
* l
= tr_new0 (tr_lock
, 1);
159 InitializeCriticalSection (&l
->lock
); /* supports recursion */
161 pthread_mutexattr_t attr
;
162 pthread_mutexattr_init (&attr
);
163 pthread_mutexattr_settype (&attr
, PTHREAD_MUTEX_RECURSIVE
);
164 pthread_mutex_init (&l
->lock
, &attr
);
171 tr_lockFree (tr_lock
* l
)
174 DeleteCriticalSection (&l
->lock
);
176 pthread_mutex_destroy (&l
->lock
);
182 tr_lockLock (tr_lock
* l
)
185 EnterCriticalSection (&l
->lock
);
187 pthread_mutex_lock (&l
->lock
);
190 assert (l
->depth
>= 0);
191 assert (!l
->depth
|| tr_areThreadsEqual (l
->lockThread
, tr_getCurrentThread ()));
192 l
->lockThread
= tr_getCurrentThread ();
197 tr_lockHave (const tr_lock
* l
)
199 return (l
->depth
> 0) &&
200 (tr_areThreadsEqual (l
->lockThread
, tr_getCurrentThread ()));
204 tr_lockUnlock (tr_lock
* l
)
206 assert (l
->depth
> 0);
207 assert (tr_areThreadsEqual (l
->lockThread
, tr_getCurrentThread ()));
210 assert (l
->depth
>= 0);
212 LeaveCriticalSection (&l
->lock
);
214 pthread_mutex_unlock (&l
->lock
);
229 static char * home
= NULL
;
233 home
= tr_strdup (getenv ("HOME"));
238 char appdata
[MAX_PATH
]; /* SHGetFolderPath () requires MAX_PATH */
240 SHGetFolderPath (NULL
, CSIDL_PERSONAL
, NULL
, 0, appdata
);
241 home
= tr_strdup (appdata
);
243 struct passwd
* pw
= getpwuid (getuid ());
245 home
= tr_strdup (pw
->pw_dir
);
251 home
= tr_strdup ("");
257 #if defined (SYS_DARWIN) || defined (WIN32)
258 #define RESUME_SUBDIR "Resume"
259 #define TORRENT_SUBDIR "Torrents"
261 #define RESUME_SUBDIR "resume"
262 #define TORRENT_SUBDIR "torrents"
266 tr_setConfigDir (tr_session
* session
, const char * configDir
)
270 session
->configDir
= tr_strdup (configDir
);
272 path
= tr_buildPath (configDir
, RESUME_SUBDIR
, NULL
);
273 tr_mkdirp (path
, 0777);
274 session
->resumeDir
= path
;
276 path
= tr_buildPath (configDir
, TORRENT_SUBDIR
, NULL
);
277 tr_mkdirp (path
, 0777);
278 session
->torrentDir
= path
;
282 tr_sessionGetConfigDir (const tr_session
* session
)
284 return session
->configDir
;
288 tr_getTorrentDir (const tr_session
* session
)
290 return session
->torrentDir
;
294 tr_getResumeDir (const tr_session
* session
)
296 return session
->resumeDir
;
300 tr_getDefaultConfigDir (const char * appname
)
302 static char * s
= NULL
;
304 if (!appname
|| !*appname
)
305 appname
= "Transmission";
309 if ((s
= getenv ("TRANSMISSION_HOME")))
316 s
= tr_buildPath (getHomeDir (), "Library", "Application Support", appname
, NULL
);
317 #elif defined (WIN32)
318 char appdata
[TR_PATH_MAX
]; /* SHGetFolderPath () requires MAX_PATH */
319 SHGetFolderPath (NULL
, CSIDL_APPDATA
, NULL
, 0, appdata
);
320 s
= tr_buildPath (appdata
, appname
, NULL
);
321 #elif defined (__HAIKU__)
322 char buf
[TR_PATH_MAX
];
323 find_directory (B_USER_SETTINGS_DIRECTORY
, -1, true, buf
, sizeof (buf
));
324 s
= tr_buildPath (buf
, appname
, NULL
);
326 if ((s
= getenv ("XDG_CONFIG_HOME")))
327 s
= tr_buildPath (s
, appname
, NULL
);
329 s
= tr_buildPath (getHomeDir (), ".config", appname
, NULL
);
338 tr_getDefaultDownloadDir (void)
340 static char * user_dir
= NULL
;
342 if (user_dir
== NULL
)
344 const char * config_home
;
349 /* figure out where to look for user-dirs.dirs */
350 config_home
= getenv ("XDG_CONFIG_HOME");
351 if (config_home
&& *config_home
)
352 config_file
= tr_buildPath (config_home
, "user-dirs.dirs", NULL
);
354 config_file
= tr_buildPath (getHomeDir (), ".config", "user-dirs.dirs", NULL
);
356 /* read in user-dirs.dirs and look for the download dir entry */
357 content
= (char *) tr_loadFile (config_file
, &content_len
);
358 if (content
&& content_len
>0)
360 const char * key
= "XDG_DOWNLOAD_DIR=\"";
361 char * line
= strstr (content
, key
);
364 char * value
= line
+ strlen (key
);
365 char * end
= strchr (value
, '"');
371 if (!memcmp (value
, "$HOME/", 6))
372 user_dir
= tr_buildPath (getHomeDir (), value
+6, NULL
);
373 else if (!strcmp (value
, "$HOME"))
374 user_dir
= tr_strdup (getHomeDir ());
376 user_dir
= tr_strdup (value
);
381 if (user_dir
== NULL
)
383 user_dir
= tr_buildPath (getHomeDir (), "Desktop", NULL
);
385 user_dir
= tr_buildPath (getHomeDir (), "Downloads", NULL
);
389 tr_free (config_file
);
400 isWebClientDir (const char * path
)
403 char * tmp
= tr_buildPath (path
, "index.html", NULL
);
404 const int ret
= !stat (tmp
, &sb
);
405 tr_logAddInfo (_("Searching for web interface file \"%s\""), tmp
);
412 tr_getWebClientDir (const tr_session
* session UNUSED
)
414 static char * s
= NULL
;
418 if ((s
= getenv ("CLUTCH_HOME")))
422 else if ((s
= getenv ("TRANSMISSION_WEB_HOME")))
429 #ifdef BUILD_MAC_CLIENT /* on Mac, look in the Application Support folder first, then in the app bundle. */
431 /* Look in the Application Support folder */
432 s
= tr_buildPath (tr_sessionGetConfigDir (session
), "web", NULL
);
434 if (!isWebClientDir (s
))
438 CFURLRef appURL
= CFBundleCopyBundleURL (CFBundleGetMainBundle ());
439 CFStringRef appRef
= CFURLCopyFileSystemPath (appURL
,
440 kCFURLPOSIXPathStyle
);
441 const CFIndex appStringLength
= CFStringGetMaximumSizeOfFileSystemRepresentation (appRef
);
443 char * appString
= tr_malloc (appStringLength
);
444 const bool success
= CFStringGetFileSystemRepresentation (appRef
, appString
, appStringLength
);
450 /* Fallback to the app bundle */
451 s
= tr_buildPath (appString
, "Contents", "Resources", "web", NULL
);
452 if (!isWebClientDir (s
))
461 #elif defined (WIN32)
463 /* SHGetFolderPath explicitly requires MAX_PATH length */
466 /* Generally, Web interface should be stored in a Web subdir of
467 * calling executable dir. */
469 if (s
== NULL
) /* check personal AppData/Transmission/Web */
471 SHGetFolderPath (NULL
, CSIDL_COMMON_APPDATA
, NULL
, 0, dir
);
472 s
= tr_buildPath (dir
, "Transmission", "Web", NULL
);
473 if (!isWebClientDir (s
))
480 if (s
== NULL
) /* check personal AppData */
482 SHGetFolderPath (NULL
, CSIDL_APPDATA
, NULL
, 0, dir
);
483 s
= tr_buildPath (dir
, "Transmission", "Web", NULL
);
484 if (!isWebClientDir (s
))
491 if (s
== NULL
) /* check calling module place */
493 GetModuleFileName (GetModuleHandle (NULL
), dir
, sizeof (dir
));
494 s
= tr_buildPath (dirname (dir
), "Web", NULL
);
495 if (!isWebClientDir (s
))
502 #else /* everyone else, follow the XDG spec */
504 tr_list
*candidates
= NULL
, *l
;
507 /* XDG_DATA_HOME should be the first in the list of candidates */
508 tmp
= getenv ("XDG_DATA_HOME");
511 tr_list_append (&candidates
, tr_strdup (tmp
));
515 char * dhome
= tr_buildPath (getHomeDir (), ".local", "share", NULL
);
516 tr_list_append (&candidates
, dhome
);
519 /* XDG_DATA_DIRS are the backup directories */
521 const char * pkg
= PACKAGE_DATA_DIR
;
522 const char * xdg
= getenv ("XDG_DATA_DIRS");
523 const char * fallback
= "/usr/local/share:/usr/share";
524 char * buf
= tr_strdup_printf ("%s:%s:%s", (pkg
?pkg
:""), (xdg
?xdg
:""), fallback
);
528 const char * end
= strchr (tmp
, ':');
532 tr_list_append (&candidates
, tr_strndup (tmp
, end
- tmp
));
535 else if (tmp
&& *tmp
)
537 tr_list_append (&candidates
, tr_strdup (tmp
));
544 /* walk through the candidates & look for a match */
545 for (l
=candidates
; l
; l
=l
->next
)
547 char * path
= tr_buildPath (l
->data
, "transmission", "web", NULL
);
548 const int found
= isWebClientDir (path
);
557 tr_list_free (&candidates
, tr_free
);
570 /* The following mmap functions are by Joerg Walter, and were taken from
571 * his paper at: http://www.genesys-e.de/jwalter/mix4win.htm */
573 #if defined (_MSC_VER)
574 __declspec (align (4)) static LONG
volatile g_sl
;
576 static LONG
volatile g_sl
__attribute__((aligned (4)));
579 /* Wait for spin lock */
581 slwait (LONG
volatile *sl
)
583 while (InterlockedCompareExchange (sl
, 1, 0) != 0)
589 /* Release spin lock */
591 slrelease (LONG
volatile *sl
)
593 InterlockedExchange (sl
, 0);
597 /* getpagesize for windows */
601 static long g_pagesize
= 0;
605 SYSTEM_INFO system_info
;
606 GetSystemInfo (&system_info
);
607 g_pagesize
= system_info
.dwPageSize
;
616 static long g_regionsize
= 0;
620 SYSTEM_INFO system_info
;
621 GetSystemInfo (&system_info
);
622 g_regionsize
= system_info
.dwAllocationGranularity
;
629 mmap (void *ptr
, long size
, long prot
, long type
, long handle
, long arg
)
631 static long g_pagesize
;
632 static long g_regionsize
;
634 /* Wait for spin lock */
637 /* First time initialization */
639 g_pagesize
= getpagesize ();
641 g_regionsize
= getregionsize ();
644 ptr
= VirtualAlloc (ptr
, size
, MEM_RESERVE
| MEM_COMMIT
| MEM_TOP_DOWN
, PAGE_READWRITE
);
652 /* Release spin lock */
658 munmap (void *ptr
, long size
)
660 static long g_pagesize
;
661 static long g_regionsize
;
664 /* Wait for spin lock */
667 /* First time initialization */
669 g_pagesize
= getpagesize ();
671 g_regionsize
= getregionsize ();
674 if (!VirtualFree (ptr
, 0, MEM_RELEASE
))
680 /* Release spin lock */