source: trunk/libtransmission/platform.c @ 14241

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

Copyedit the license's revised text: (1) remove unnecessary repitition use of the word 'license' from the top of the header and source files (2) add the standard 'we hope it's useful, but no warranty' clause to COPYING (3) make explicit that linking OpenSSL is allowed (see https://people.gnome.org/~markmc/openssl-and-the-gpl.html for background) (4) sync the Qt and GTK+ clients' license popups with COPYING's revised text

  • 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 GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: platform.c 14241 2014-01-21 03:10:30Z 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.