source: trunk/libtransmission/platform.c @ 14214

Last change on this file since 14214 was 14214, checked in by livings124, 7 years ago

#5304 daemon fails to build on mac

  • Property svn:keywords set to Date Rev Author Id
File size: 15.2 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2 (b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: platform.c 14214 2013-10-27 19:31:36Z livings124 $
11 */
12
13#define _XOPEN_SOURCE 600  /* needed for recursive locks. */
14#ifndef __USE_UNIX98
15 #define __USE_UNIX98 /* some older Linuxes need it spelt out for them */
16#endif
17
18#include <assert.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <unistd.h> /* getuid(), close() */
23#include <sys/stat.h>
24
25#ifdef WIN32
26 #include <w32api.h>
27 #define WINVER  WindowsXP
28 #include <windows.h>
29 #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_MYDOCUMENTS */
30#else
31 #ifdef BUILD_MAC_CLIENT
32  #include <CoreFoundation/CoreFoundation.h>
33 #endif
34 #ifdef __HAIKU__
35  #include <FindDirectory.h>
36 #endif
37 #include <pthread.h>
38#endif
39
40#ifdef WIN32
41#include <libgen.h> /* dirname() */
42#endif
43
44#include "transmission.h"
45#include "session.h"
46#include "list.h"
47#include "log.h"
48#include "platform.h"
49
50/***
51****  THREADS
52***/
53
54#ifdef WIN32
55typedef DWORD tr_thread_id;
56#else
57typedef pthread_t tr_thread_id;
58#endif
59
60static tr_thread_id
61tr_getCurrentThread (void)
62{
63#ifdef WIN32
64  return GetCurrentThreadId ();
65#else
66  return pthread_self ();
67#endif
68}
69
70static bool
71tr_areThreadsEqual (tr_thread_id a, tr_thread_id b)
72{
73#ifdef WIN32
74  return a == b;
75#else
76  return pthread_equal (a, b) != 0;
77#endif
78}
79
80/** @brief portability wrapper around OS-dependent threads */
81struct tr_thread
82{
83  void          (* func)(void *);
84  void           * arg;
85  tr_thread_id     thread;
86#ifdef WIN32
87  HANDLE           thread_handle;
88#endif
89};
90
91bool
92tr_amInThread (const tr_thread * t)
93{
94  return tr_areThreadsEqual (tr_getCurrentThread (), t->thread);
95}
96
97#ifdef WIN32
98 #define ThreadFuncReturnType unsigned WINAPI
99#else
100 #define ThreadFuncReturnType void
101#endif
102
103static ThreadFuncReturnType
104ThreadFunc (void * _t)
105{
106  tr_thread * t = _t;
107
108  t->func (t->arg);
109
110  tr_free (t);
111#ifdef WIN32
112  _endthreadex (0);
113  return 0;
114#endif
115}
116
117tr_thread *
118tr_threadNew (void (*func)(void *), void * arg)
119{
120  tr_thread * t = tr_new0 (tr_thread, 1);
121
122  t->func = func;
123  t->arg  = arg;
124
125#ifdef WIN32
126  {
127    unsigned int id;
128    t->thread_handle = (HANDLE) _beginthreadex (NULL, 0, &ThreadFunc, t, 0, &id);
129    t->thread = (DWORD) id;
130  }
131#else
132  pthread_create (&t->thread, NULL, (void* (*)(void*))ThreadFunc, t);
133  pthread_detach (t->thread);
134#endif
135
136  return t;
137}
138
139/***
140****  LOCKS
141***/
142
143/** @brief portability wrapper around OS-dependent thread mutexes */
144struct tr_lock
145{
146  int                 depth;
147#ifdef WIN32
148  CRITICAL_SECTION    lock;
149  DWORD               lockThread;
150#else
151  pthread_mutex_t     lock;
152  pthread_t           lockThread;
153#endif
154};
155
156tr_lock*
157tr_lockNew (void)
158{
159  tr_lock * l = tr_new0 (tr_lock, 1);
160
161#ifdef WIN32
162  InitializeCriticalSection (&l->lock); /* supports recursion */
163#else
164  pthread_mutexattr_t attr;
165  pthread_mutexattr_init (&attr);
166  pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
167  pthread_mutex_init (&l->lock, &attr);
168#endif
169
170  return l;
171}
172
173void
174tr_lockFree (tr_lock * l)
175{
176#ifdef WIN32
177    DeleteCriticalSection (&l->lock);
178#else
179    pthread_mutex_destroy (&l->lock);
180#endif
181    tr_free (l);
182}
183
184void
185tr_lockLock (tr_lock * l)
186{
187#ifdef WIN32
188  EnterCriticalSection (&l->lock);
189#else
190  pthread_mutex_lock (&l->lock);
191#endif
192
193  assert (l->depth >= 0);
194  assert (!l->depth || tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
195  l->lockThread = tr_getCurrentThread ();
196  ++l->depth;
197}
198
199int
200tr_lockHave (const tr_lock * l)
201{
202  return (l->depth > 0) &&
203         (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
204}
205
206void
207tr_lockUnlock (tr_lock * l)
208{
209  assert (l->depth > 0);
210  assert (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
211
212  --l->depth;
213  assert (l->depth >= 0);
214#ifdef WIN32
215  LeaveCriticalSection (&l->lock);
216#else
217  pthread_mutex_unlock (&l->lock);
218#endif
219}
220
221/***
222****  PATHS
223***/
224
225#ifndef WIN32
226 #include <pwd.h>
227#endif
228
229static const char *
230getHomeDir (void)
231{
232  static char * home = NULL;
233
234  if (!home)
235    {
236      home = tr_strdup (getenv ("HOME"));
237
238      if (!home)
239        {
240#ifdef WIN32
241          char appdata[MAX_PATH]; /* SHGetFolderPath () requires MAX_PATH */
242          *appdata = '\0';
243          SHGetFolderPath (NULL, CSIDL_PERSONAL, NULL, 0, appdata);
244          home = tr_strdup (appdata);
245#else
246          struct passwd * pw = getpwuid (getuid ());
247          if (pw)
248            home = tr_strdup (pw->pw_dir);
249          endpwent ();
250#endif
251        }
252
253      if (!home)
254        home = tr_strdup ("");
255    }
256
257  return home;
258}
259
260#if defined (SYS_DARWIN) || defined (WIN32)
261 #define RESUME_SUBDIR  "Resume"
262 #define TORRENT_SUBDIR "Torrents"
263#else
264 #define RESUME_SUBDIR  "resume"
265 #define TORRENT_SUBDIR "torrents"
266#endif
267
268void
269tr_setConfigDir (tr_session * session, const char * configDir)
270{
271  char * path;
272
273  session->configDir = tr_strdup (configDir);
274
275  path = tr_buildPath (configDir, RESUME_SUBDIR, NULL);
276  tr_mkdirp (path, 0777);
277  session->resumeDir = path;
278
279  path = tr_buildPath (configDir, TORRENT_SUBDIR, NULL);
280  tr_mkdirp (path, 0777);
281  session->torrentDir = path;
282}
283
284const char *
285tr_sessionGetConfigDir (const tr_session * session)
286{
287  return session->configDir;
288}
289
290const char *
291tr_getTorrentDir (const tr_session * session)
292{
293  return session->torrentDir;
294}
295
296const char *
297tr_getResumeDir (const tr_session * session)
298{
299  return session->resumeDir;
300}
301
302const char*
303tr_getDefaultConfigDir (const char * appname)
304{
305  static char * s = NULL;
306
307  if (!appname || !*appname)
308    appname = "Transmission";
309
310  if (!s)
311    {
312      if ((s = getenv ("TRANSMISSION_HOME")))
313        {
314          s = tr_strdup (s);
315        }
316      else
317        {
318#ifdef SYS_DARWIN
319          s = tr_buildPath (getHomeDir (), "Library", "Application Support", appname, NULL);
320#elif defined (WIN32)
321          char appdata[TR_PATH_MAX]; /* SHGetFolderPath () requires MAX_PATH */
322          SHGetFolderPath (NULL, CSIDL_APPDATA, NULL, 0, appdata);
323          s = tr_buildPath (appdata, appname, NULL);
324#elif defined (__HAIKU__)
325          char buf[TR_PATH_MAX];
326          find_directory (B_USER_SETTINGS_DIRECTORY, -1, true, buf, sizeof (buf));
327          s = tr_buildPath (buf, appname, NULL);
328#else
329          if ((s = getenv ("XDG_CONFIG_HOME")))
330            s = tr_buildPath (s, appname, NULL);
331          else
332            s = tr_buildPath (getHomeDir (), ".config", appname, NULL);
333#endif
334        }
335    }
336
337  return s;
338}
339
340const char*
341tr_getDefaultDownloadDir (void)
342{
343  static char * user_dir = NULL;
344
345  if (user_dir == NULL)
346    {
347      const char * config_home;
348      char * config_file;
349      char * content;
350      size_t content_len;
351
352      /* figure out where to look for user-dirs.dirs */
353      config_home = getenv ("XDG_CONFIG_HOME");
354      if (config_home && *config_home)
355        config_file = tr_buildPath (config_home, "user-dirs.dirs", NULL);
356      else
357        config_file = tr_buildPath (getHomeDir (), ".config", "user-dirs.dirs", NULL);
358
359      /* read in user-dirs.dirs and look for the download dir entry */
360      content = (char *) tr_loadFile (config_file, &content_len);
361      if (content && content_len>0)
362        {
363          const char * key = "XDG_DOWNLOAD_DIR=\"";
364          char * line = strstr (content, key);
365          if (line != NULL)
366            {
367              char * value = line + strlen (key);
368              char * end = strchr (value, '"');
369
370              if (end)
371                {
372                  *end = '\0';
373
374                  if (!memcmp (value, "$HOME/", 6))
375                    user_dir = tr_buildPath (getHomeDir (), value+6, NULL);
376                  else if (!strcmp (value, "$HOME"))
377                    user_dir = tr_strdup (getHomeDir ());
378                  else
379                    user_dir = tr_strdup (value);
380                }
381            }
382        }
383
384      if (user_dir == NULL)
385#ifdef __HAIKU__
386        user_dir = tr_buildPath (getHomeDir (), "Desktop", NULL);
387#else
388        user_dir = tr_buildPath (getHomeDir (), "Downloads", NULL);
389#endif
390
391      tr_free (content);
392      tr_free (config_file);
393    }
394
395  return user_dir;
396}
397
398/***
399****
400***/
401
402static int
403isWebClientDir (const char * path)
404{
405  struct stat sb;
406  char * tmp = tr_buildPath (path, "index.html", NULL);
407  const int ret = !stat (tmp, &sb);
408  tr_logAddInfo (_("Searching for web interface file \"%s\""), tmp);
409  tr_free (tmp);
410
411  return ret;
412}
413
414const char *
415tr_getWebClientDir (const tr_session * session UNUSED)
416{
417  static char * s = NULL;
418
419  if (!s)
420    {
421      if ((s = getenv ("CLUTCH_HOME")))
422        {
423          s = tr_strdup (s);
424        }
425      else if ((s = getenv ("TRANSMISSION_WEB_HOME")))
426        {
427          s = tr_strdup (s);
428        }
429      else
430        {
431
432#ifdef BUILD_MAC_CLIENT /* on Mac, look in the Application Support folder first, then in the app bundle. */
433
434          /* Look in the Application Support folder */
435          s = tr_buildPath (tr_sessionGetConfigDir (session), "web", NULL);
436
437          if (!isWebClientDir (s))
438            {
439              tr_free (s);
440
441              CFURLRef appURL = CFBundleCopyBundleURL (CFBundleGetMainBundle ());
442              CFStringRef appRef = CFURLCopyFileSystemPath (appURL,
443                                                            kCFURLPOSIXPathStyle);
444              const CFIndex appStringLength = CFStringGetMaximumSizeOfFileSystemRepresentation (appRef);
445
446              char * appString = tr_malloc (appStringLength);
447              const bool success = CFStringGetFileSystemRepresentation (appRef, appString, appStringLength);
448              assert (success);
449
450              CFRelease (appURL);
451              CFRelease (appRef);
452
453              /* Fallback to the app bundle */
454              s = tr_buildPath (appString, "Contents", "Resources", "web", NULL);
455              if (!isWebClientDir (s))
456                {
457                  tr_free (s);
458                  s = NULL;
459                }
460
461              tr_free (appString);
462            }
463
464#elif defined (WIN32)
465
466          /* SHGetFolderPath explicitly requires MAX_PATH length */
467          char dir[MAX_PATH];
468
469          /* Generally, Web interface should be stored in a Web subdir of
470           * calling executable dir. */
471
472          if (s == NULL) /* check personal AppData/Transmission/Web */
473            {
474              SHGetFolderPath (NULL, CSIDL_COMMON_APPDATA, NULL, 0, dir);
475              s = tr_buildPath (dir, "Transmission", "Web", NULL);
476              if (!isWebClientDir (s))
477                {
478                  tr_free (s);
479                  s = NULL;
480                }
481            }
482
483          if (s == NULL) /* check personal AppData */
484            {
485              SHGetFolderPath (NULL, CSIDL_APPDATA, NULL, 0, dir);
486              s = tr_buildPath (dir, "Transmission", "Web", NULL);
487              if (!isWebClientDir (s))
488                {
489                  tr_free (s);
490                  s = NULL;
491                }
492            }
493
494            if (s == NULL) /* check calling module place */
495              {
496                GetModuleFileName (GetModuleHandle (NULL), dir, sizeof (dir));
497                s = tr_buildPath (dirname (dir), "Web", NULL);
498                if (!isWebClientDir (s))
499                  {
500                    tr_free (s);
501                    s = NULL;
502                  }
503            }
504
505#else /* everyone else, follow the XDG spec */
506
507          tr_list *candidates = NULL, *l;
508          const char * tmp;
509
510          /* XDG_DATA_HOME should be the first in the list of candidates */
511          tmp = getenv ("XDG_DATA_HOME");
512          if (tmp && *tmp)
513            {
514              tr_list_append (&candidates, tr_strdup (tmp));
515            }
516          else
517            {
518              char * dhome = tr_buildPath (getHomeDir (), ".local", "share", NULL);
519              tr_list_append (&candidates, dhome);
520            }
521
522          /* XDG_DATA_DIRS are the backup directories */
523          {
524            const char * pkg = PACKAGE_DATA_DIR;
525            const char * xdg = getenv ("XDG_DATA_DIRS");
526            const char * fallback = "/usr/local/share:/usr/share";
527            char * buf = tr_strdup_printf ("%s:%s:%s", (pkg?pkg:""), (xdg?xdg:""), fallback);
528            tmp = buf;
529            while (tmp && *tmp)
530              {
531                const char * end = strchr (tmp, ':');
532                if (end)
533                  {
534                    if ((end - tmp) > 1)
535                      tr_list_append (&candidates, tr_strndup (tmp, end - tmp));
536                    tmp = end + 1;
537                  }
538                else if (tmp && *tmp)
539                  {
540                    tr_list_append (&candidates, tr_strdup (tmp));
541                    break;
542                  }
543              }
544            tr_free (buf);
545          }
546
547          /* walk through the candidates & look for a match */
548          for (l=candidates; l; l=l->next)
549            {
550              char * path = tr_buildPath (l->data, "transmission", "web", NULL);
551              const int found = isWebClientDir (path);
552              if (found)
553                {
554                  s = path;
555                  break;
556                }
557              tr_free (path);
558            }
559
560          tr_list_free (&candidates, tr_free);
561
562#endif
563
564        }
565    }
566
567  return s;
568}
569
570
571#ifdef WIN32
572
573/* The following mmap functions are by Joerg Walter, and were taken from
574 * his paper at: http://www.genesys-e.de/jwalter/mix4win.htm */
575
576#if defined (_MSC_VER)
577__declspec (align (4)) static LONG volatile g_sl;
578#else
579static LONG volatile g_sl __attribute__((aligned (4)));
580#endif
581
582/* Wait for spin lock */
583static int
584slwait (LONG volatile *sl)
585{
586  while (InterlockedCompareExchange (sl, 1, 0) != 0)
587    Sleep (0);
588
589  return 0;
590}
591
592/* Release spin lock */
593static int
594slrelease (LONG volatile *sl)
595{
596  InterlockedExchange (sl, 0);
597  return 0;
598}
599
600/* getpagesize for windows */
601static long
602getpagesize (void)
603{
604  static long g_pagesize = 0;
605
606  if (!g_pagesize)
607    {
608      SYSTEM_INFO system_info;
609      GetSystemInfo (&system_info);
610      g_pagesize = system_info.dwPageSize;
611    }
612
613  return g_pagesize;
614}
615
616static long
617getregionsize (void)
618{
619  static long g_regionsize = 0;
620
621  if (!g_regionsize)
622    {
623      SYSTEM_INFO system_info;
624      GetSystemInfo (&system_info);
625      g_regionsize = system_info.dwAllocationGranularity;
626    }
627
628  return g_regionsize;
629}
630
631void *
632mmap (void *ptr, long  size, long  prot, long  type, long  handle, long  arg)
633{
634  static long g_pagesize;
635  static long g_regionsize;
636
637  /* Wait for spin lock */
638  slwait (&g_sl);
639
640  /* First time initialization */
641  if (!g_pagesize)
642    g_pagesize = getpagesize ();
643  if (!g_regionsize)
644    g_regionsize = getregionsize ();
645
646  /* Allocate this */
647  ptr = VirtualAlloc (ptr, size, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE);
648  if (!ptr)
649    {
650      ptr = (void *) -1;
651      goto mmap_exit;
652    }
653
654mmap_exit:
655  /* Release spin lock */
656  slrelease (&g_sl);
657  return ptr;
658}
659
660long
661munmap (void *ptr, long size)
662{
663  static long g_pagesize;
664  static long g_regionsize;
665  int rc = -1;
666
667  /* Wait for spin lock */
668  slwait (&g_sl);
669
670  /* First time initialization */
671  if (!g_pagesize)
672    g_pagesize = getpagesize ();
673  if (!g_regionsize)
674    g_regionsize = getregionsize ();
675
676  /* Free this */
677  if (!VirtualFree (ptr, 0, MEM_RELEASE))
678    goto munmap_exit;
679
680  rc = 0;
681
682munmap_exit:
683  /* Release spin lock */
684  slrelease (&g_sl);
685  return rc;
686}
687
688#endif
Note: See TracBrowser for help on using the repository browser.