source: trunk/libtransmission/platform.c @ 14336

Last change on this file since 14336 was 14336, checked in by jordan, 6 years ago

(trunk) #4160: mike.dld patch: 4160-07-env.patch

  • Property svn:keywords set to Date Rev Author Id
File size: 13.6 KB
Line 
1/*
2 * This file Copyright (C) 2009-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: platform.c 14336 2014-09-21 18:05:14Z jordan $
8 */
9
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
14
15#include <assert.h>
16#include <stdlib.h>
17#include <string.h>
18#include <unistd.h> /* getuid() */
19
20#ifdef __HAIKU__
21 #include <limits.h> /* PATH_MAX */
22#endif
23
24#ifdef _WIN32
25 #include <w32api.h>
26 #define WINVER  WindowsXP
27 #include <windows.h>
28 #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_MYDOCUMENTS */
29#else
30 #ifdef BUILD_MAC_CLIENT
31  #include <CoreFoundation/CoreFoundation.h>
32 #endif
33 #ifdef __HAIKU__
34  #include <FindDirectory.h>
35 #endif
36 #include <pthread.h>
37#endif
38
39#ifdef _WIN32
40#include <libgen.h> /* dirname() */
41#endif
42
43#include "transmission.h"
44#include "file.h"
45#include "list.h"
46#include "log.h"
47#include "platform.h"
48#include "session.h"
49#include "utils.h"
50
51/***
52****  THREADS
53***/
54
55#ifdef _WIN32
56typedef DWORD tr_thread_id;
57#else
58typedef pthread_t tr_thread_id;
59#endif
60
61static tr_thread_id
62tr_getCurrentThread (void)
63{
64#ifdef _WIN32
65  return GetCurrentThreadId ();
66#else
67  return pthread_self ();
68#endif
69}
70
71static bool
72tr_areThreadsEqual (tr_thread_id a, tr_thread_id b)
73{
74#ifdef _WIN32
75  return a == b;
76#else
77  return pthread_equal (a, b) != 0;
78#endif
79}
80
81/** @brief portability wrapper around OS-dependent threads */
82struct tr_thread
83{
84  void          (* func)(void *);
85  void           * arg;
86  tr_thread_id     thread;
87#ifdef _WIN32
88  HANDLE           thread_handle;
89#endif
90};
91
92bool
93tr_amInThread (const tr_thread * t)
94{
95  return tr_areThreadsEqual (tr_getCurrentThread (), t->thread);
96}
97
98#ifdef _WIN32
99 #define ThreadFuncReturnType unsigned WINAPI
100#else
101 #define ThreadFuncReturnType void
102#endif
103
104static ThreadFuncReturnType
105ThreadFunc (void * _t)
106{
107  tr_thread * t = _t;
108
109  t->func (t->arg);
110
111  tr_free (t);
112#ifdef _WIN32
113  _endthreadex (0);
114  return 0;
115#endif
116}
117
118tr_thread *
119tr_threadNew (void (*func)(void *), void * arg)
120{
121  tr_thread * t = tr_new0 (tr_thread, 1);
122
123  t->func = func;
124  t->arg  = arg;
125
126#ifdef _WIN32
127  {
128    unsigned int id;
129    t->thread_handle = (HANDLE) _beginthreadex (NULL, 0, &ThreadFunc, t, 0, &id);
130    t->thread = (DWORD) id;
131  }
132#else
133  pthread_create (&t->thread, NULL, (void* (*)(void*))ThreadFunc, t);
134  pthread_detach (t->thread);
135#endif
136
137  return t;
138}
139
140/***
141****  LOCKS
142***/
143
144/** @brief portability wrapper around OS-dependent thread mutexes */
145struct tr_lock
146{
147  int                 depth;
148#ifdef _WIN32
149  CRITICAL_SECTION    lock;
150  DWORD               lockThread;
151#else
152  pthread_mutex_t     lock;
153  pthread_t           lockThread;
154#endif
155};
156
157tr_lock*
158tr_lockNew (void)
159{
160  tr_lock * l = tr_new0 (tr_lock, 1);
161
162#ifdef _WIN32
163  InitializeCriticalSection (&l->lock); /* supports recursion */
164#else
165  pthread_mutexattr_t attr;
166  pthread_mutexattr_init (&attr);
167  pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
168  pthread_mutex_init (&l->lock, &attr);
169#endif
170
171  return l;
172}
173
174void
175tr_lockFree (tr_lock * l)
176{
177#ifdef _WIN32
178    DeleteCriticalSection (&l->lock);
179#else
180    pthread_mutex_destroy (&l->lock);
181#endif
182    tr_free (l);
183}
184
185void
186tr_lockLock (tr_lock * l)
187{
188#ifdef _WIN32
189  EnterCriticalSection (&l->lock);
190#else
191  pthread_mutex_lock (&l->lock);
192#endif
193
194  assert (l->depth >= 0);
195  assert (!l->depth || tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
196  l->lockThread = tr_getCurrentThread ();
197  ++l->depth;
198}
199
200int
201tr_lockHave (const tr_lock * l)
202{
203  return (l->depth > 0) &&
204         (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
205}
206
207void
208tr_lockUnlock (tr_lock * l)
209{
210  assert (l->depth > 0);
211  assert (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
212
213  --l->depth;
214  assert (l->depth >= 0);
215#ifdef _WIN32
216  LeaveCriticalSection (&l->lock);
217#else
218  pthread_mutex_unlock (&l->lock);
219#endif
220}
221
222/***
223****  PATHS
224***/
225
226#ifndef _WIN32
227 #include <pwd.h>
228#endif
229
230#ifdef _WIN32
231
232char *
233win32_get_special_folder (int folder_id)
234{
235  wchar_t path[MAX_PATH]; /* SHGetFolderPath () requires MAX_PATH */
236  *path = L'\0';
237  SHGetFolderPathW (NULL, folder_id, NULL, 0, path);
238  return tr_win32_native_to_utf8 (path, -1);
239}
240
241#endif
242
243static const char *
244getHomeDir (void)
245{
246  static char * home = NULL;
247
248  if (!home)
249    {
250      home = tr_env_get_string ("HOME", NULL);
251
252      if (!home)
253        {
254#ifdef _WIN32
255          home = win32_get_special_folder (CSIDL_PERSONAL);
256#else
257          struct passwd * pw = getpwuid (getuid ());
258          if (pw)
259            home = tr_strdup (pw->pw_dir);
260          endpwent ();
261#endif
262        }
263
264      if (!home)
265        home = tr_strdup ("");
266    }
267
268  return home;
269}
270
271#if defined (__APPLE__) || defined (WIN32)
272 #define RESUME_SUBDIR  "Resume"
273 #define TORRENT_SUBDIR "Torrents"
274#else
275 #define RESUME_SUBDIR  "resume"
276 #define TORRENT_SUBDIR "torrents"
277#endif
278
279void
280tr_setConfigDir (tr_session * session, const char * configDir)
281{
282  char * path;
283
284  session->configDir = tr_strdup (configDir);
285
286  path = tr_buildPath (configDir, RESUME_SUBDIR, NULL);
287  tr_sys_dir_create (path, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
288  session->resumeDir = path;
289
290  path = tr_buildPath (configDir, TORRENT_SUBDIR, NULL);
291  tr_sys_dir_create (path, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
292  session->torrentDir = path;
293}
294
295const char *
296tr_sessionGetConfigDir (const tr_session * session)
297{
298  return session->configDir;
299}
300
301const char *
302tr_getTorrentDir (const tr_session * session)
303{
304  return session->torrentDir;
305}
306
307const char *
308tr_getResumeDir (const tr_session * session)
309{
310  return session->resumeDir;
311}
312
313const char*
314tr_getDefaultConfigDir (const char * appname)
315{
316  static char * s = NULL;
317
318  if (!appname || !*appname)
319    appname = "Transmission";
320
321  if (!s)
322    {
323      s = tr_env_get_string ("TRANSMISSION_HOME", NULL);
324
325      if (s == NULL)
326        {
327#ifdef __APPLE__
328          s = tr_buildPath (getHomeDir (), "Library", "Application Support", appname, NULL);
329#elif defined (_WIN32)
330          char * appdata = win32_get_special_folder (CSIDL_APPDATA);
331          s = tr_buildPath (appdata, appname, NULL);
332          tr_free (appdata);
333#elif defined (__HAIKU__)
334          char buf[PATH_MAX];
335          find_directory (B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof (buf));
336          s = tr_buildPath (buf, appname, NULL);
337#else
338          if ((s = tr_env_get_string ("XDG_CONFIG_HOME", NULL)))
339            {
340              s = tr_buildPath (s, appname, NULL);
341              tr_free (s);
342            }
343          else
344            {
345              s = tr_buildPath (getHomeDir (), ".config", appname, NULL);
346            }
347#endif
348        }
349    }
350
351  return s;
352}
353
354const char*
355tr_getDefaultDownloadDir (void)
356{
357  static char * user_dir = NULL;
358
359  if (user_dir == NULL)
360    {
361      char * config_home;
362      char * config_file;
363      char * content;
364      size_t content_len;
365
366      /* figure out where to look for user-dirs.dirs */
367      config_home = tr_env_get_string ("XDG_CONFIG_HOME", NULL);
368      if (config_home && *config_home)
369        config_file = tr_buildPath (config_home, "user-dirs.dirs", NULL);
370      else
371        config_file = tr_buildPath (getHomeDir (), ".config", "user-dirs.dirs", NULL);
372      tr_free (config_home);
373
374      /* read in user-dirs.dirs and look for the download dir entry */
375      content = (char *) tr_loadFile (config_file, &content_len);
376      if (content && content_len>0)
377        {
378          const char * key = "XDG_DOWNLOAD_DIR=\"";
379          char * line = strstr (content, key);
380          if (line != NULL)
381            {
382              char * value = line + strlen (key);
383              char * end = strchr (value, '"');
384
385              if (end)
386                {
387                  *end = '\0';
388
389                  if (!memcmp (value, "$HOME/", 6))
390                    user_dir = tr_buildPath (getHomeDir (), value+6, NULL);
391                  else if (!strcmp (value, "$HOME"))
392                    user_dir = tr_strdup (getHomeDir ());
393                  else
394                    user_dir = tr_strdup (value);
395                }
396            }
397        }
398
399      if (user_dir == NULL)
400#ifdef __HAIKU__
401        user_dir = tr_buildPath (getHomeDir (), "Desktop", NULL);
402#else
403        user_dir = tr_buildPath (getHomeDir (), "Downloads", NULL);
404#endif
405
406      tr_free (content);
407      tr_free (config_file);
408    }
409
410  return user_dir;
411}
412
413/***
414****
415***/
416
417static bool
418isWebClientDir (const char * path)
419{
420  char * tmp = tr_buildPath (path, "index.html", NULL);
421  const bool ret = tr_sys_path_exists (tmp, NULL);
422  tr_logAddInfo (_("Searching for web interface file \"%s\""), tmp);
423  tr_free (tmp);
424
425  return ret;
426}
427
428const char *
429tr_getWebClientDir (const tr_session * session UNUSED)
430{
431  static char * s = NULL;
432
433  if (!s)
434    {
435      s = tr_env_get_string ("CLUTCH_HOME", NULL);
436      if (s == NULL)
437        s = tr_env_get_string ("TRANSMISSION_WEB_HOME", NULL);
438      if (s == NULL)
439        {
440
441#ifdef BUILD_MAC_CLIENT /* on Mac, look in the Application Support folder first, then in the app bundle. */
442
443          /* Look in the Application Support folder */
444          s = tr_buildPath (tr_sessionGetConfigDir (session), "web", NULL);
445
446          if (!isWebClientDir (s))
447            {
448              tr_free (s);
449
450              CFURLRef appURL = CFBundleCopyBundleURL (CFBundleGetMainBundle ());
451              CFStringRef appRef = CFURLCopyFileSystemPath (appURL,
452                                                            kCFURLPOSIXPathStyle);
453              const CFIndex appStringLength = CFStringGetMaximumSizeOfFileSystemRepresentation (appRef);
454
455              char * appString = tr_malloc (appStringLength);
456              const bool success = CFStringGetFileSystemRepresentation (appRef, appString, appStringLength);
457              assert (success);
458
459              CFRelease (appURL);
460              CFRelease (appRef);
461
462              /* Fallback to the app bundle */
463              s = tr_buildPath (appString, "Contents", "Resources", "web", NULL);
464              if (!isWebClientDir (s))
465                {
466                  tr_free (s);
467                  s = NULL;
468                }
469
470              tr_free (appString);
471            }
472
473#elif defined (_WIN32)
474
475          /* Generally, Web interface should be stored in a Web subdir of
476           * calling executable dir. */
477
478          if (s == NULL) /* check personal AppData/Transmission/Web */
479            {
480              char * dir = win32_get_special_folder (CSIDL_COMMON_APPDATA);
481              s = tr_buildPath (dir, "Transmission", "Web", NULL);
482              tr_free (dir);
483              if (!isWebClientDir (s))
484                {
485                  tr_free (s);
486                  s = NULL;
487                }
488            }
489
490          if (s == NULL) /* check personal AppData */
491            {
492              char * dir = win32_get_special_folder (CSIDL_APPDATA);
493              s = tr_buildPath (dir, "Transmission", "Web", NULL);
494              tr_free (dir);
495              if (!isWebClientDir (s))
496                {
497                  tr_free (s);
498                  s = NULL;
499                }
500            }
501
502            if (s == NULL) /* check calling module place */
503              {
504                wchar_t wide_module_path[MAX_PATH];
505                char * module_path;
506                char * dir;
507                GetModuleFileNameW (NULL, wide_module_path,
508                                    sizeof (wide_module_path) / sizeof (*wide_module_path));
509                module_path = tr_win32_native_to_utf8 (wide_module_path, -1);
510                dir = tr_sys_path_dirname (module_path, NULL);
511                tr_free (module_path);
512                s = tr_buildPath (dir, "Web", NULL);
513                tr_free (dir);
514                if (!isWebClientDir (s))
515                  {
516                    tr_free (s);
517                    s = NULL;
518                  }
519            }
520
521#else /* everyone else, follow the XDG spec */
522
523          tr_list *candidates = NULL, *l;
524          char * tmp;
525
526          /* XDG_DATA_HOME should be the first in the list of candidates */
527          tmp = tr_env_get_string ("XDG_DATA_HOME", NULL);
528          if (tmp && *tmp)
529            {
530              tr_list_append (&candidates, tmp);
531            }
532          else
533            {
534              char * dhome = tr_buildPath (getHomeDir (), ".local", "share", NULL);
535              tr_list_append (&candidates, dhome);
536              tr_free (tmp);
537            }
538
539          /* XDG_DATA_DIRS are the backup directories */
540          {
541            const char * pkg = PACKAGE_DATA_DIR;
542            char * xdg = tr_env_get_string ("XDG_DATA_DIRS", NULL);
543            const char * fallback = "/usr/local/share:/usr/share";
544            char * buf = tr_strdup_printf ("%s:%s:%s", (pkg?pkg:""), (xdg?xdg:""), fallback);
545            tr_free (xdg);
546            tmp = buf;
547            while (tmp && *tmp)
548              {
549                const char * end = strchr (tmp, ':');
550                if (end)
551                  {
552                    if ((end - tmp) > 1)
553                      tr_list_append (&candidates, tr_strndup (tmp, end - tmp));
554                    tmp = (char *) end + 1;
555                  }
556                else if (tmp && *tmp)
557                  {
558                    tr_list_append (&candidates, tr_strdup (tmp));
559                    break;
560                  }
561              }
562            tr_free (buf);
563          }
564
565          /* walk through the candidates & look for a match */
566          for (l=candidates; l; l=l->next)
567            {
568              char * path = tr_buildPath (l->data, "transmission", "web", NULL);
569              const int found = isWebClientDir (path);
570              if (found)
571                {
572                  s = path;
573                  break;
574                }
575              tr_free (path);
576            }
577
578          tr_list_free (&candidates, tr_free);
579
580#endif
581
582        }
583    }
584
585  return s;
586}
Note: See TracBrowser for help on using the repository browser.