source: trunk/libtransmission/platform.c @ 14327

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

(trunk, libt) #4160 - the slow slog to catch trunk up to mike.dld's 4160 diff continues. This step applies 4160-03b-file.patch, which replaces native file operations with the tr_sys_file_*() portability wrappers added in r14321.

  • Property svn:keywords set to Date Rev Author Id
File size: 13.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 14327 2014-07-28 04:13:38Z 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
21#ifdef _WIN32
22 #include <w32api.h>
23 #define WINVER  WindowsXP
24 #include <windows.h>
25 #include <shlobj.h> /* for CSIDL_APPDATA, CSIDL_MYDOCUMENTS */
26#else
27 #ifdef BUILD_MAC_CLIENT
28  #include <CoreFoundation/CoreFoundation.h>
29 #endif
30 #ifdef __HAIKU__
31  #include <FindDirectory.h>
32 #endif
33 #include <pthread.h>
34#endif
35
36#ifdef _WIN32
37#include <libgen.h> /* dirname() */
38#endif
39
40#include "transmission.h"
41#include "file.h"
42#include "list.h"
43#include "log.h"
44#include "platform.h"
45#include "session.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 (__APPLE__) || 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 __APPLE__
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  char * tmp = tr_buildPath (path, "index.html", NULL);
403  const bool ret = tr_sys_path_exists (tmp, NULL);
404  tr_logAddInfo (_("Searching for web interface file \"%s\""), tmp);
405  tr_free (tmp);
406
407  return ret;
408}
409
410const char *
411tr_getWebClientDir (const tr_session * session UNUSED)
412{
413  static char * s = NULL;
414
415  if (!s)
416    {
417      if ((s = getenv ("CLUTCH_HOME")))
418        {
419          s = tr_strdup (s);
420        }
421      else if ((s = getenv ("TRANSMISSION_WEB_HOME")))
422        {
423          s = tr_strdup (s);
424        }
425      else
426        {
427
428#ifdef BUILD_MAC_CLIENT /* on Mac, look in the Application Support folder first, then in the app bundle. */
429
430          /* Look in the Application Support folder */
431          s = tr_buildPath (tr_sessionGetConfigDir (session), "web", NULL);
432
433          if (!isWebClientDir (s))
434            {
435              tr_free (s);
436
437              CFURLRef appURL = CFBundleCopyBundleURL (CFBundleGetMainBundle ());
438              CFStringRef appRef = CFURLCopyFileSystemPath (appURL,
439                                                            kCFURLPOSIXPathStyle);
440              const CFIndex appStringLength = CFStringGetMaximumSizeOfFileSystemRepresentation (appRef);
441
442              char * appString = tr_malloc (appStringLength);
443              const bool success = CFStringGetFileSystemRepresentation (appRef, appString, appStringLength);
444              assert (success);
445
446              CFRelease (appURL);
447              CFRelease (appRef);
448
449              /* Fallback to the app bundle */
450              s = tr_buildPath (appString, "Contents", "Resources", "web", NULL);
451              if (!isWebClientDir (s))
452                {
453                  tr_free (s);
454                  s = NULL;
455                }
456
457              tr_free (appString);
458            }
459
460#elif defined (_WIN32)
461
462          /* SHGetFolderPath explicitly requires MAX_PATH length */
463          char dir[MAX_PATH];
464
465          /* Generally, Web interface should be stored in a Web subdir of
466           * calling executable dir. */
467
468          if (s == NULL) /* check personal AppData/Transmission/Web */
469            {
470              SHGetFolderPath (NULL, CSIDL_COMMON_APPDATA, NULL, 0, dir);
471              s = tr_buildPath (dir, "Transmission", "Web", NULL);
472              if (!isWebClientDir (s))
473                {
474                  tr_free (s);
475                  s = NULL;
476                }
477            }
478
479          if (s == NULL) /* check personal AppData */
480            {
481              SHGetFolderPath (NULL, CSIDL_APPDATA, NULL, 0, dir);
482              s = tr_buildPath (dir, "Transmission", "Web", NULL);
483              if (!isWebClientDir (s))
484                {
485                  tr_free (s);
486                  s = NULL;
487                }
488            }
489
490            if (s == NULL) /* check calling module place */
491              {
492                char * tmp;
493                GetModuleFileName (GetModuleHandle (NULL), dir, sizeof (dir));
494                tmp = tr_sys_path_dirname (dir, NULL);
495                s = tr_buildPath (tmp, "Web", NULL);
496                tr_free (tmp);
497                if (!isWebClientDir (s))
498                  {
499                    tr_free (s);
500                    s = NULL;
501                  }
502            }
503
504#else /* everyone else, follow the XDG spec */
505
506          tr_list *candidates = NULL, *l;
507          const char * tmp;
508
509          /* XDG_DATA_HOME should be the first in the list of candidates */
510          tmp = getenv ("XDG_DATA_HOME");
511          if (tmp && *tmp)
512            {
513              tr_list_append (&candidates, tr_strdup (tmp));
514            }
515          else
516            {
517              char * dhome = tr_buildPath (getHomeDir (), ".local", "share", NULL);
518              tr_list_append (&candidates, dhome);
519            }
520
521          /* XDG_DATA_DIRS are the backup directories */
522          {
523            const char * pkg = PACKAGE_DATA_DIR;
524            const char * xdg = getenv ("XDG_DATA_DIRS");
525            const char * fallback = "/usr/local/share:/usr/share";
526            char * buf = tr_strdup_printf ("%s:%s:%s", (pkg?pkg:""), (xdg?xdg:""), fallback);
527            tmp = buf;
528            while (tmp && *tmp)
529              {
530                const char * end = strchr (tmp, ':');
531                if (end)
532                  {
533                    if ((end - tmp) > 1)
534                      tr_list_append (&candidates, tr_strndup (tmp, end - tmp));
535                    tmp = end + 1;
536                  }
537                else if (tmp && *tmp)
538                  {
539                    tr_list_append (&candidates, tr_strdup (tmp));
540                    break;
541                  }
542              }
543            tr_free (buf);
544          }
545
546          /* walk through the candidates & look for a match */
547          for (l=candidates; l; l=l->next)
548            {
549              char * path = tr_buildPath (l->data, "transmission", "web", NULL);
550              const int found = isWebClientDir (path);
551              if (found)
552                {
553                  s = path;
554                  break;
555                }
556              tr_free (path);
557            }
558
559          tr_list_free (&candidates, tr_free);
560
561#endif
562
563        }
564    }
565
566  return s;
567}
Note: See TracBrowser for help on using the repository browser.