transmission 2.83
[tomato.git] / release / src-rt-6.x.4708 / router / transmission / libtransmission / platform.c
blob8d1f3d639e4cab1eb080c3cbf696a241d9ed3ec3
1 /*
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 $
8 */
10 #define _XOPEN_SOURCE 600 /* needed for recursive locks. */
11 #ifndef __USE_UNIX98
12 #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
13 #endif
15 #include <assert.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h> /* getuid(), close() */
20 #include <sys/stat.h>
22 #ifdef WIN32
23 #include <w32api.h>
24 #define WINVER WindowsXP
25 #include <windows.h>
26 #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_MYDOCUMENTS */
27 #else
28 #ifdef BUILD_MAC_CLIENT
29 #include <CoreFoundation/CoreFoundation.h>
30 #endif
31 #ifdef __HAIKU__
32 #include <FindDirectory.h>
33 #endif
34 #include <pthread.h>
35 #endif
37 #ifdef WIN32
38 #include <libgen.h> /* dirname() */
39 #endif
41 #include "transmission.h"
42 #include "session.h"
43 #include "list.h"
44 #include "log.h"
45 #include "platform.h"
47 /***
48 **** THREADS
49 ***/
51 #ifdef WIN32
52 typedef DWORD tr_thread_id;
53 #else
54 typedef pthread_t tr_thread_id;
55 #endif
57 static tr_thread_id
58 tr_getCurrentThread (void)
60 #ifdef WIN32
61 return GetCurrentThreadId ();
62 #else
63 return pthread_self ();
64 #endif
67 static bool
68 tr_areThreadsEqual (tr_thread_id a, tr_thread_id b)
70 #ifdef WIN32
71 return a == b;
72 #else
73 return pthread_equal (a, b) != 0;
74 #endif
77 /** @brief portability wrapper around OS-dependent threads */
78 struct tr_thread
80 void (* func)(void *);
81 void * arg;
82 tr_thread_id thread;
83 #ifdef WIN32
84 HANDLE thread_handle;
85 #endif
88 bool
89 tr_amInThread (const tr_thread * t)
91 return tr_areThreadsEqual (tr_getCurrentThread (), t->thread);
94 #ifdef WIN32
95 #define ThreadFuncReturnType unsigned WINAPI
96 #else
97 #define ThreadFuncReturnType void
98 #endif
100 static ThreadFuncReturnType
101 ThreadFunc (void * _t)
103 tr_thread * t = _t;
105 t->func (t->arg);
107 tr_free (t);
108 #ifdef WIN32
109 _endthreadex (0);
110 return 0;
111 #endif
114 tr_thread *
115 tr_threadNew (void (*func)(void *), void * arg)
117 tr_thread * t = tr_new0 (tr_thread, 1);
119 t->func = func;
120 t->arg = arg;
122 #ifdef WIN32
124 unsigned int id;
125 t->thread_handle = (HANDLE) _beginthreadex (NULL, 0, &ThreadFunc, t, 0, &id);
126 t->thread = (DWORD) id;
128 #else
129 pthread_create (&t->thread, NULL, (void* (*)(void*))ThreadFunc, t);
130 pthread_detach (t->thread);
131 #endif
133 return t;
136 /***
137 **** LOCKS
138 ***/
140 /** @brief portability wrapper around OS-dependent thread mutexes */
141 struct tr_lock
143 int depth;
144 #ifdef WIN32
145 CRITICAL_SECTION lock;
146 DWORD lockThread;
147 #else
148 pthread_mutex_t lock;
149 pthread_t lockThread;
150 #endif
153 tr_lock*
154 tr_lockNew (void)
156 tr_lock * l = tr_new0 (tr_lock, 1);
158 #ifdef WIN32
159 InitializeCriticalSection (&l->lock); /* supports recursion */
160 #else
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);
165 #endif
167 return l;
170 void
171 tr_lockFree (tr_lock * l)
173 #ifdef WIN32
174 DeleteCriticalSection (&l->lock);
175 #else
176 pthread_mutex_destroy (&l->lock);
177 #endif
178 tr_free (l);
181 void
182 tr_lockLock (tr_lock * l)
184 #ifdef WIN32
185 EnterCriticalSection (&l->lock);
186 #else
187 pthread_mutex_lock (&l->lock);
188 #endif
190 assert (l->depth >= 0);
191 assert (!l->depth || tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
192 l->lockThread = tr_getCurrentThread ();
193 ++l->depth;
197 tr_lockHave (const tr_lock * l)
199 return (l->depth > 0) &&
200 (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
203 void
204 tr_lockUnlock (tr_lock * l)
206 assert (l->depth > 0);
207 assert (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
209 --l->depth;
210 assert (l->depth >= 0);
211 #ifdef WIN32
212 LeaveCriticalSection (&l->lock);
213 #else
214 pthread_mutex_unlock (&l->lock);
215 #endif
218 /***
219 **** PATHS
220 ***/
222 #ifndef WIN32
223 #include <pwd.h>
224 #endif
226 static const char *
227 getHomeDir (void)
229 static char * home = NULL;
231 if (!home)
233 home = tr_strdup (getenv ("HOME"));
235 if (!home)
237 #ifdef WIN32
238 char appdata[MAX_PATH]; /* SHGetFolderPath () requires MAX_PATH */
239 *appdata = '\0';
240 SHGetFolderPath (NULL, CSIDL_PERSONAL, NULL, 0, appdata);
241 home = tr_strdup (appdata);
242 #else
243 struct passwd * pw = getpwuid (getuid ());
244 if (pw)
245 home = tr_strdup (pw->pw_dir);
246 endpwent ();
247 #endif
250 if (!home)
251 home = tr_strdup ("");
254 return home;
257 #if defined (SYS_DARWIN) || defined (WIN32)
258 #define RESUME_SUBDIR "Resume"
259 #define TORRENT_SUBDIR "Torrents"
260 #else
261 #define RESUME_SUBDIR "resume"
262 #define TORRENT_SUBDIR "torrents"
263 #endif
265 void
266 tr_setConfigDir (tr_session * session, const char * configDir)
268 char * path;
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;
281 const char *
282 tr_sessionGetConfigDir (const tr_session * session)
284 return session->configDir;
287 const char *
288 tr_getTorrentDir (const tr_session * session)
290 return session->torrentDir;
293 const char *
294 tr_getResumeDir (const tr_session * session)
296 return session->resumeDir;
299 const char*
300 tr_getDefaultConfigDir (const char * appname)
302 static char * s = NULL;
304 if (!appname || !*appname)
305 appname = "Transmission";
307 if (!s)
309 if ((s = getenv ("TRANSMISSION_HOME")))
311 s = tr_strdup (s);
313 else
315 #ifdef SYS_DARWIN
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);
325 #else
326 if ((s = getenv ("XDG_CONFIG_HOME")))
327 s = tr_buildPath (s, appname, NULL);
328 else
329 s = tr_buildPath (getHomeDir (), ".config", appname, NULL);
330 #endif
334 return s;
337 const char*
338 tr_getDefaultDownloadDir (void)
340 static char * user_dir = NULL;
342 if (user_dir == NULL)
344 const char * config_home;
345 char * config_file;
346 char * content;
347 size_t content_len;
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);
353 else
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);
362 if (line != NULL)
364 char * value = line + strlen (key);
365 char * end = strchr (value, '"');
367 if (end)
369 *end = '\0';
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 ());
375 else
376 user_dir = tr_strdup (value);
381 if (user_dir == NULL)
382 #ifdef __HAIKU__
383 user_dir = tr_buildPath (getHomeDir (), "Desktop", NULL);
384 #else
385 user_dir = tr_buildPath (getHomeDir (), "Downloads", NULL);
386 #endif
388 tr_free (content);
389 tr_free (config_file);
392 return user_dir;
395 /***
396 ****
397 ***/
399 static int
400 isWebClientDir (const char * path)
402 struct stat sb;
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);
406 tr_free (tmp);
408 return ret;
411 const char *
412 tr_getWebClientDir (const tr_session * session UNUSED)
414 static char * s = NULL;
416 if (!s)
418 if ((s = getenv ("CLUTCH_HOME")))
420 s = tr_strdup (s);
422 else if ((s = getenv ("TRANSMISSION_WEB_HOME")))
424 s = tr_strdup (s);
426 else
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))
436 tr_free (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);
445 assert (success);
447 CFRelease (appURL);
448 CFRelease (appRef);
450 /* Fallback to the app bundle */
451 s = tr_buildPath (appString, "Contents", "Resources", "web", NULL);
452 if (!isWebClientDir (s))
454 tr_free (s);
455 s = NULL;
458 tr_free (appString);
461 #elif defined (WIN32)
463 /* SHGetFolderPath explicitly requires MAX_PATH length */
464 char dir[MAX_PATH];
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))
475 tr_free (s);
476 s = NULL;
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))
486 tr_free (s);
487 s = NULL;
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))
497 tr_free (s);
498 s = NULL;
502 #else /* everyone else, follow the XDG spec */
504 tr_list *candidates = NULL, *l;
505 const char * tmp;
507 /* XDG_DATA_HOME should be the first in the list of candidates */
508 tmp = getenv ("XDG_DATA_HOME");
509 if (tmp && *tmp)
511 tr_list_append (&candidates, tr_strdup (tmp));
513 else
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);
525 tmp = buf;
526 while (tmp && *tmp)
528 const char * end = strchr (tmp, ':');
529 if (end)
531 if ((end - tmp) > 1)
532 tr_list_append (&candidates, tr_strndup (tmp, end - tmp));
533 tmp = end + 1;
535 else if (tmp && *tmp)
537 tr_list_append (&candidates, tr_strdup (tmp));
538 break;
541 tr_free (buf);
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);
549 if (found)
551 s = path;
552 break;
554 tr_free (path);
557 tr_list_free (&candidates, tr_free);
559 #endif
564 return s;
568 #ifdef WIN32
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;
575 #else
576 static LONG volatile g_sl __attribute__((aligned (4)));
577 #endif
579 /* Wait for spin lock */
580 static int
581 slwait (LONG volatile *sl)
583 while (InterlockedCompareExchange (sl, 1, 0) != 0)
584 Sleep (0);
586 return 0;
589 /* Release spin lock */
590 static int
591 slrelease (LONG volatile *sl)
593 InterlockedExchange (sl, 0);
594 return 0;
597 /* getpagesize for windows */
598 static long
599 getpagesize (void)
601 static long g_pagesize = 0;
603 if (!g_pagesize)
605 SYSTEM_INFO system_info;
606 GetSystemInfo (&system_info);
607 g_pagesize = system_info.dwPageSize;
610 return g_pagesize;
613 static long
614 getregionsize (void)
616 static long g_regionsize = 0;
618 if (!g_regionsize)
620 SYSTEM_INFO system_info;
621 GetSystemInfo (&system_info);
622 g_regionsize = system_info.dwAllocationGranularity;
625 return g_regionsize;
628 void *
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 */
635 slwait (&g_sl);
637 /* First time initialization */
638 if (!g_pagesize)
639 g_pagesize = getpagesize ();
640 if (!g_regionsize)
641 g_regionsize = getregionsize ();
643 /* Allocate this */
644 ptr = VirtualAlloc (ptr, size, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE);
645 if (!ptr)
647 ptr = (void *) -1;
648 goto mmap_exit;
651 mmap_exit:
652 /* Release spin lock */
653 slrelease (&g_sl);
654 return ptr;
657 long
658 munmap (void *ptr, long size)
660 static long g_pagesize;
661 static long g_regionsize;
662 int rc = -1;
664 /* Wait for spin lock */
665 slwait (&g_sl);
667 /* First time initialization */
668 if (!g_pagesize)
669 g_pagesize = getpagesize ();
670 if (!g_regionsize)
671 g_regionsize = getregionsize ();
673 /* Free this */
674 if (!VirtualFree (ptr, 0, MEM_RELEASE))
675 goto munmap_exit;
677 rc = 0;
679 munmap_exit:
680 /* Release spin lock */
681 slrelease (&g_sl);
682 return rc;
685 #endif