source: trunk/libtransmission/platform.c @ 14335

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

#4160: mike.dld patch: 4160-06-misc.patch

  • Property svn:keywords set to Date Rev Author Id
File size: 13.5 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 14335 2014-09-21 18:03:13Z 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_strdup (getenv ("HOME"));
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      if ((s = getenv ("TRANSMISSION_HOME")))
324        {
325          s = tr_strdup (s);
326        }
327      else
328        {
329#ifdef __APPLE__
330          s = tr_buildPath (getHomeDir (), "Library", "Application Support", appname, NULL);
331#elif defined (_WIN32)
332          char * appdata = win32_get_special_folder (CSIDL_APPDATA);
333          s = tr_buildPath (appdata, appname, NULL);
334          tr_free (appdata);
335#elif defined (__HAIKU__)
336          char buf[PATH_MAX];
337          find_directory (B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof (buf));
338          s = tr_buildPath (buf, appname, NULL);
339#else
340          if ((s = getenv ("XDG_CONFIG_HOME")))
341            s = tr_buildPath (s, appname, NULL);
342          else
343            s = tr_buildPath (getHomeDir (), ".config", appname, NULL);
344#endif
345        }
346    }
347
348  return s;
349}
350
351const char*
352tr_getDefaultDownloadDir (void)
353{
354  static char * user_dir = NULL;
355
356  if (user_dir == NULL)
357    {
358      const char * config_home;
359      char * config_file;
360      char * content;
361      size_t content_len;
362
363      /* figure out where to look for user-dirs.dirs */
364      config_home = getenv ("XDG_CONFIG_HOME");
365      if (config_home && *config_home)
366        config_file = tr_buildPath (config_home, "user-dirs.dirs", NULL);
367      else
368        config_file = tr_buildPath (getHomeDir (), ".config", "user-dirs.dirs", NULL);
369
370      /* read in user-dirs.dirs and look for the download dir entry */
371      content = (char *) tr_loadFile (config_file, &content_len);
372      if (content && content_len>0)
373        {
374          const char * key = "XDG_DOWNLOAD_DIR=\"";
375          char * line = strstr (content, key);
376          if (line != NULL)
377            {
378              char * value = line + strlen (key);
379              char * end = strchr (value, '"');
380
381              if (end)
382                {
383                  *end = '\0';
384
385                  if (!memcmp (value, "$HOME/", 6))
386                    user_dir = tr_buildPath (getHomeDir (), value+6, NULL);
387                  else if (!strcmp (value, "$HOME"))
388                    user_dir = tr_strdup (getHomeDir ());
389                  else
390                    user_dir = tr_strdup (value);
391                }
392            }
393        }
394
395      if (user_dir == NULL)
396#ifdef __HAIKU__
397        user_dir = tr_buildPath (getHomeDir (), "Desktop", NULL);
398#else
399        user_dir = tr_buildPath (getHomeDir (), "Downloads", NULL);
400#endif
401
402      tr_free (content);
403      tr_free (config_file);
404    }
405
406  return user_dir;
407}
408
409/***
410****
411***/
412
413static bool
414isWebClientDir (const char * path)
415{
416  char * tmp = tr_buildPath (path, "index.html", NULL);
417  const bool ret = tr_sys_path_exists (tmp, NULL);
418  tr_logAddInfo (_("Searching for web interface file \"%s\""), tmp);
419  tr_free (tmp);
420
421  return ret;
422}
423
424const char *
425tr_getWebClientDir (const tr_session * session UNUSED)
426{
427  static char * s = NULL;
428
429  if (!s)
430    {
431      if ((s = getenv ("CLUTCH_HOME")))
432        {
433          s = tr_strdup (s);
434        }
435      else if ((s = getenv ("TRANSMISSION_WEB_HOME")))
436        {
437          s = tr_strdup (s);
438        }
439      else
440        {
441
442#ifdef BUILD_MAC_CLIENT /* on Mac, look in the Application Support folder first, then in the app bundle. */
443
444          /* Look in the Application Support folder */
445          s = tr_buildPath (tr_sessionGetConfigDir (session), "web", NULL);
446
447          if (!isWebClientDir (s))
448            {
449              tr_free (s);
450
451              CFURLRef appURL = CFBundleCopyBundleURL (CFBundleGetMainBundle ());
452              CFStringRef appRef = CFURLCopyFileSystemPath (appURL,
453                                                            kCFURLPOSIXPathStyle);
454              const CFIndex appStringLength = CFStringGetMaximumSizeOfFileSystemRepresentation (appRef);
455
456              char * appString = tr_malloc (appStringLength);
457              const bool success = CFStringGetFileSystemRepresentation (appRef, appString, appStringLength);
458              assert (success);
459
460              CFRelease (appURL);
461              CFRelease (appRef);
462
463              /* Fallback to the app bundle */
464              s = tr_buildPath (appString, "Contents", "Resources", "web", NULL);
465              if (!isWebClientDir (s))
466                {
467                  tr_free (s);
468                  s = NULL;
469                }
470
471              tr_free (appString);
472            }
473
474#elif defined (_WIN32)
475
476          /* Generally, Web interface should be stored in a Web subdir of
477           * calling executable dir. */
478
479          if (s == NULL) /* check personal AppData/Transmission/Web */
480            {
481              char * dir = win32_get_special_folder (CSIDL_COMMON_APPDATA);
482              s = tr_buildPath (dir, "Transmission", "Web", NULL);
483              tr_free (dir);
484              if (!isWebClientDir (s))
485                {
486                  tr_free (s);
487                  s = NULL;
488                }
489            }
490
491          if (s == NULL) /* check personal AppData */
492            {
493              char * dir = win32_get_special_folder (CSIDL_APPDATA);
494              s = tr_buildPath (dir, "Transmission", "Web", NULL);
495              tr_free (dir);
496              if (!isWebClientDir (s))
497                {
498                  tr_free (s);
499                  s = NULL;
500                }
501            }
502
503            if (s == NULL) /* check calling module place */
504              {
505                wchar_t wide_module_path[MAX_PATH];
506                char * module_path;
507                char * dir;
508                GetModuleFileNameW (NULL, wide_module_path,
509                                    sizeof (wide_module_path) / sizeof (*wide_module_path));
510                module_path = tr_win32_native_to_utf8 (wide_module_path, -1);
511                dir = tr_sys_path_dirname (module_path, NULL);
512                tr_free (module_path);
513                s = tr_buildPath (dir, "Web", NULL);
514                tr_free (dir);
515                if (!isWebClientDir (s))
516                  {
517                    tr_free (s);
518                    s = NULL;
519                  }
520            }
521
522#else /* everyone else, follow the XDG spec */
523
524          tr_list *candidates = NULL, *l;
525          const char * tmp;
526
527          /* XDG_DATA_HOME should be the first in the list of candidates */
528          tmp = getenv ("XDG_DATA_HOME");
529          if (tmp && *tmp)
530            {
531              tr_list_append (&candidates, tr_strdup (tmp));
532            }
533          else
534            {
535              char * dhome = tr_buildPath (getHomeDir (), ".local", "share", NULL);
536              tr_list_append (&candidates, dhome);
537            }
538
539          /* XDG_DATA_DIRS are the backup directories */
540          {
541            const char * pkg = PACKAGE_DATA_DIR;
542            const char * xdg = getenv ("XDG_DATA_DIRS");
543            const char * fallback = "/usr/local/share:/usr/share";
544            char * buf = tr_strdup_printf ("%s:%s:%s", (pkg?pkg:""), (xdg?xdg:""), fallback);
545            tmp = buf;
546            while (tmp && *tmp)
547              {
548                const char * end = strchr (tmp, ':');
549                if (end)
550                  {
551                    if ((end - tmp) > 1)
552                      tr_list_append (&candidates, tr_strndup (tmp, end - tmp));
553                    tmp = end + 1;
554                  }
555                else if (tmp && *tmp)
556                  {
557                    tr_list_append (&candidates, tr_strdup (tmp));
558                    break;
559                  }
560              }
561            tr_free (buf);
562          }
563
564          /* walk through the candidates & look for a match */
565          for (l=candidates; l; l=l->next)
566            {
567              char * path = tr_buildPath (l->data, "transmission", "web", NULL);
568              const int found = isWebClientDir (path);
569              if (found)
570                {
571                  s = path;
572                  break;
573                }
574              tr_free (path);
575            }
576
577          tr_list_free (&candidates, tr_free);
578
579#endif
580
581        }
582    }
583
584  return s;
585}
Note: See TracBrowser for help on using the repository browser.