source: trunk/libtransmission/platform.c @ 14225

Last change on this file since 14225 was 14225, checked in by jordan, 7 years ago

Licensing changes:

  1. add the option the code to be used under GPLv2 or GPLv3; previously only GPLv2 was allowed
  1. add the "proxy option" as described in GPLv3 so we can add future licenses without having to bulk-edit everything again :)
  1. remove the awkward "exception for MIT code in Mac client" clause; it was unnecessary and confusing.
  • Property svn:keywords set to Date Rev Author Id
File size: 15.0 KB
Line 
1/*
2 * This file Copyright (C) 2009-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU Public License v2 or v3 licenses,
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: platform.c 14225 2014-01-19 01:09:44Z 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 <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <unistd.h> /* getuid(), close() */
20#include <sys/stat.h>
21
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
36
37#ifdef WIN32
38#include <libgen.h> /* dirname() */
39#endif
40
41#include "transmission.h"
42#include "session.h"
43#include "list.h"
44#include "log.h"
45#include "platform.h"
46
47/***
48****  THREADS
49***/
50
51#ifdef WIN32
52typedef DWORD tr_thread_id;
53#else
54typedef pthread_t tr_thread_id;
55#endif
56
57static tr_thread_id
58tr_getCurrentThread (void)
59{
60#ifdef WIN32
61  return GetCurrentThreadId ();
62#else
63  return pthread_self ();
64#endif
65}
66
67static bool
68tr_areThreadsEqual (tr_thread_id a, tr_thread_id b)
69{
70#ifdef WIN32
71  return a == b;
72#else
73  return pthread_equal (a, b) != 0;
74#endif
75}
76
77/** @brief portability wrapper around OS-dependent threads */
78struct tr_thread
79{
80  void          (* func)(void *);
81  void           * arg;
82  tr_thread_id     thread;
83#ifdef WIN32
84  HANDLE           thread_handle;
85#endif
86};
87
88bool
89tr_amInThread (const tr_thread * t)
90{
91  return tr_areThreadsEqual (tr_getCurrentThread (), t->thread);
92}
93
94#ifdef WIN32
95 #define ThreadFuncReturnType unsigned WINAPI
96#else
97 #define ThreadFuncReturnType void
98#endif
99
100static ThreadFuncReturnType
101ThreadFunc (void * _t)
102{
103  tr_thread * t = _t;
104
105  t->func (t->arg);
106
107  tr_free (t);
108#ifdef WIN32
109  _endthreadex (0);
110  return 0;
111#endif
112}
113
114tr_thread *
115tr_threadNew (void (*func)(void *), void * arg)
116{
117  tr_thread * t = tr_new0 (tr_thread, 1);
118
119  t->func = func;
120  t->arg  = arg;
121
122#ifdef WIN32
123  {
124    unsigned int id;
125    t->thread_handle = (HANDLE) _beginthreadex (NULL, 0, &ThreadFunc, t, 0, &id);
126    t->thread = (DWORD) id;
127  }
128#else
129  pthread_create (&t->thread, NULL, (void* (*)(void*))ThreadFunc, t);
130  pthread_detach (t->thread);
131#endif
132
133  return t;
134}
135
136/***
137****  LOCKS
138***/
139
140/** @brief portability wrapper around OS-dependent thread mutexes */
141struct tr_lock
142{
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
151};
152
153tr_lock*
154tr_lockNew (void)
155{
156  tr_lock * l = tr_new0 (tr_lock, 1);
157
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
166
167  return l;
168}
169
170void
171tr_lockFree (tr_lock * l)
172{
173#ifdef WIN32
174    DeleteCriticalSection (&l->lock);
175#else
176    pthread_mutex_destroy (&l->lock);
177#endif
178    tr_free (l);
179}
180
181void
182tr_lockLock (tr_lock * l)
183{
184#ifdef WIN32
185  EnterCriticalSection (&l->lock);
186#else
187  pthread_mutex_lock (&l->lock);
188#endif
189
190  assert (l->depth >= 0);
191  assert (!l->depth || tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
192  l->lockThread = tr_getCurrentThread ();
193  ++l->depth;
194}
195
196int
197tr_lockHave (const tr_lock * l)
198{
199  return (l->depth > 0) &&
200         (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
201}
202
203void
204tr_lockUnlock (tr_lock * l)
205{
206  assert (l->depth > 0);
207  assert (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
208
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
216}
217
218/***
219****  PATHS
220***/
221
222#ifndef WIN32
223 #include <pwd.h>
224#endif
225
226static const char *
227getHomeDir (void)
228{
229  static char * home = NULL;
230
231  if (!home)
232    {
233      home = tr_strdup (getenv ("HOME"));
234
235      if (!home)
236        {
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
248        }
249
250      if (!home)
251        home = tr_strdup ("");
252    }
253
254  return home;
255}
256
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
264
265void
266tr_setConfigDir (tr_session * session, const char * configDir)
267{
268  char * path;
269
270  session->configDir = tr_strdup (configDir);
271
272  path = tr_buildPath (configDir, RESUME_SUBDIR, NULL);
273  tr_mkdirp (path, 0777);
274  session->resumeDir = path;
275
276  path = tr_buildPath (configDir, TORRENT_SUBDIR, NULL);
277  tr_mkdirp (path, 0777);
278  session->torrentDir = path;
279}
280
281const char *
282tr_sessionGetConfigDir (const tr_session * session)
283{
284  return session->configDir;
285}
286
287const char *
288tr_getTorrentDir (const tr_session * session)
289{
290  return session->torrentDir;
291}
292
293const char *
294tr_getResumeDir (const tr_session * session)
295{
296  return session->resumeDir;
297}
298
299const char*
300tr_getDefaultConfigDir (const char * appname)
301{
302  static char * s = NULL;
303
304  if (!appname || !*appname)
305    appname = "Transmission";
306
307  if (!s)
308    {
309      if ((s = getenv ("TRANSMISSION_HOME")))
310        {
311          s = tr_strdup (s);
312        }
313      else
314        {
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
331        }
332    }
333
334  return s;
335}
336
337const char*
338tr_getDefaultDownloadDir (void)
339{
340  static char * user_dir = NULL;
341
342  if (user_dir == NULL)
343    {
344      const char * config_home;
345      char * config_file;
346      char * content;
347      size_t content_len;
348
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);
355
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)
359        {
360          const char * key = "XDG_DOWNLOAD_DIR=\"";
361          char * line = strstr (content, key);
362          if (line != NULL)
363            {
364              char * value = line + strlen (key);
365              char * end = strchr (value, '"');
366
367              if (end)
368                {
369                  *end = '\0';
370
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);
377                }
378            }
379        }
380
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
387
388      tr_free (content);
389      tr_free (config_file);
390    }
391
392  return user_dir;
393}
394
395/***
396****
397***/
398
399static int
400isWebClientDir (const char * path)
401{
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);
407
408  return ret;
409}
410
411const char *
412tr_getWebClientDir (const tr_session * session UNUSED)
413{
414  static char * s = NULL;
415
416  if (!s)
417    {
418      if ((s = getenv ("CLUTCH_HOME")))
419        {
420          s = tr_strdup (s);
421        }
422      else if ((s = getenv ("TRANSMISSION_WEB_HOME")))
423        {
424          s = tr_strdup (s);
425        }
426      else
427        {
428
429#ifdef BUILD_MAC_CLIENT /* on Mac, look in the Application Support folder first, then in the app bundle. */
430
431          /* Look in the Application Support folder */
432          s = tr_buildPath (tr_sessionGetConfigDir (session), "web", NULL);
433
434          if (!isWebClientDir (s))
435            {
436              tr_free (s);
437
438              CFURLRef appURL = CFBundleCopyBundleURL (CFBundleGetMainBundle ());
439              CFStringRef appRef = CFURLCopyFileSystemPath (appURL,
440                                                            kCFURLPOSIXPathStyle);
441              const CFIndex appStringLength = CFStringGetMaximumSizeOfFileSystemRepresentation (appRef);
442
443              char * appString = tr_malloc (appStringLength);
444              const bool success = CFStringGetFileSystemRepresentation (appRef, appString, appStringLength);
445              assert (success);
446
447              CFRelease (appURL);
448              CFRelease (appRef);
449
450              /* Fallback to the app bundle */
451              s = tr_buildPath (appString, "Contents", "Resources", "web", NULL);
452              if (!isWebClientDir (s))
453                {
454                  tr_free (s);
455                  s = NULL;
456                }
457
458              tr_free (appString);
459            }
460
461#elif defined (WIN32)
462
463          /* SHGetFolderPath explicitly requires MAX_PATH length */
464          char dir[MAX_PATH];
465
466          /* Generally, Web interface should be stored in a Web subdir of
467           * calling executable dir. */
468
469          if (s == NULL) /* check personal AppData/Transmission/Web */
470            {
471              SHGetFolderPath (NULL, CSIDL_COMMON_APPDATA, NULL, 0, dir);
472              s = tr_buildPath (dir, "Transmission", "Web", NULL);
473              if (!isWebClientDir (s))
474                {
475                  tr_free (s);
476                  s = NULL;
477                }
478            }
479
480          if (s == NULL) /* check personal AppData */
481            {
482              SHGetFolderPath (NULL, CSIDL_APPDATA, NULL, 0, dir);
483              s = tr_buildPath (dir, "Transmission", "Web", NULL);
484              if (!isWebClientDir (s))
485                {
486                  tr_free (s);
487                  s = NULL;
488                }
489            }
490
491            if (s == NULL) /* check calling module place */
492              {
493                GetModuleFileName (GetModuleHandle (NULL), dir, sizeof (dir));
494                s = tr_buildPath (dirname (dir), "Web", NULL);
495                if (!isWebClientDir (s))
496                  {
497                    tr_free (s);
498                    s = NULL;
499                  }
500            }
501
502#else /* everyone else, follow the XDG spec */
503
504          tr_list *candidates = NULL, *l;
505          const char * tmp;
506
507          /* XDG_DATA_HOME should be the first in the list of candidates */
508          tmp = getenv ("XDG_DATA_HOME");
509          if (tmp && *tmp)
510            {
511              tr_list_append (&candidates, tr_strdup (tmp));
512            }
513          else
514            {
515              char * dhome = tr_buildPath (getHomeDir (), ".local", "share", NULL);
516              tr_list_append (&candidates, dhome);
517            }
518
519          /* XDG_DATA_DIRS are the backup directories */
520          {
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)
527              {
528                const char * end = strchr (tmp, ':');
529                if (end)
530                  {
531                    if ((end - tmp) > 1)
532                      tr_list_append (&candidates, tr_strndup (tmp, end - tmp));
533                    tmp = end + 1;
534                  }
535                else if (tmp && *tmp)
536                  {
537                    tr_list_append (&candidates, tr_strdup (tmp));
538                    break;
539                  }
540              }
541            tr_free (buf);
542          }
543
544          /* walk through the candidates & look for a match */
545          for (l=candidates; l; l=l->next)
546            {
547              char * path = tr_buildPath (l->data, "transmission", "web", NULL);
548              const int found = isWebClientDir (path);
549              if (found)
550                {
551                  s = path;
552                  break;
553                }
554              tr_free (path);
555            }
556
557          tr_list_free (&candidates, tr_free);
558
559#endif
560
561        }
562    }
563
564  return s;
565}
566
567
568#ifdef WIN32
569
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 */
572
573#if defined (_MSC_VER)
574__declspec (align (4)) static LONG volatile g_sl;
575#else
576static LONG volatile g_sl __attribute__((aligned (4)));
577#endif
578
579/* Wait for spin lock */
580static int
581slwait (LONG volatile *sl)
582{
583  while (InterlockedCompareExchange (sl, 1, 0) != 0)
584    Sleep (0);
585
586  return 0;
587}
588
589/* Release spin lock */
590static int
591slrelease (LONG volatile *sl)
592{
593  InterlockedExchange (sl, 0);
594  return 0;
595}
596
597/* getpagesize for windows */
598static long
599getpagesize (void)
600{
601  static long g_pagesize = 0;
602
603  if (!g_pagesize)
604    {
605      SYSTEM_INFO system_info;
606      GetSystemInfo (&system_info);
607      g_pagesize = system_info.dwPageSize;
608    }
609
610  return g_pagesize;
611}
612
613static long
614getregionsize (void)
615{
616  static long g_regionsize = 0;
617
618  if (!g_regionsize)
619    {
620      SYSTEM_INFO system_info;
621      GetSystemInfo (&system_info);
622      g_regionsize = system_info.dwAllocationGranularity;
623    }
624
625  return g_regionsize;
626}
627
628void *
629mmap (void *ptr, long  size, long  prot, long  type, long  handle, long  arg)
630{
631  static long g_pagesize;
632  static long g_regionsize;
633
634  /* Wait for spin lock */
635  slwait (&g_sl);
636
637  /* First time initialization */
638  if (!g_pagesize)
639    g_pagesize = getpagesize ();
640  if (!g_regionsize)
641    g_regionsize = getregionsize ();
642
643  /* Allocate this */
644  ptr = VirtualAlloc (ptr, size, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE);
645  if (!ptr)
646    {
647      ptr = (void *) -1;
648      goto mmap_exit;
649    }
650
651mmap_exit:
652  /* Release spin lock */
653  slrelease (&g_sl);
654  return ptr;
655}
656
657long
658munmap (void *ptr, long size)
659{
660  static long g_pagesize;
661  static long g_regionsize;
662  int rc = -1;
663
664  /* Wait for spin lock */
665  slwait (&g_sl);
666
667  /* First time initialization */
668  if (!g_pagesize)
669    g_pagesize = getpagesize ();
670  if (!g_regionsize)
671    g_regionsize = getregionsize ();
672
673  /* Free this */
674  if (!VirtualFree (ptr, 0, MEM_RELEASE))
675    goto munmap_exit;
676
677  rc = 0;
678
679munmap_exit:
680  /* Release spin lock */
681  slrelease (&g_sl);
682  return rc;
683}
684
685#endif
Note: See TracBrowser for help on using the repository browser.