source: trunk/libtransmission/platform.c

Last change on this file was 14718, checked in by mikedld, 5 years ago

Explicitly compare result of str(n)cmp/memcmp to signify that it's not boolean

  • Property svn:keywords set to Date Rev Author Id
File size: 13.7 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 14718 2016-03-13 22:11:01Z mikedld $
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
19#ifdef __HAIKU__
20 #include <limits.h> /* PATH_MAX */
21#endif
22
23#ifdef _WIN32
24 #include <process.h> /* _beginthreadex (), _endthreadex () */
25 #include <windows.h>
26 #include <shlobj.h> /* SHGetKnownFolderPath (), FOLDERID_... */
27#else
28#include <unistd.h> /* getuid() */
29 #ifdef BUILD_MAC_CLIENT
30  #include <CoreFoundation/CoreFoundation.h>
31 #endif
32 #ifdef __HAIKU__
33  #include <FindDirectory.h>
34 #endif
35 #include <pthread.h>
36#endif
37
38#include "transmission.h"
39#include "file.h"
40#include "list.h"
41#include "log.h"
42#include "platform.h"
43#include "session.h"
44#include "utils.h"
45
46/***
47****  THREADS
48***/
49
50#ifdef _WIN32
51typedef DWORD tr_thread_id;
52#else
53typedef pthread_t tr_thread_id;
54#endif
55
56static tr_thread_id
57tr_getCurrentThread (void)
58{
59#ifdef _WIN32
60  return GetCurrentThreadId ();
61#else
62  return pthread_self ();
63#endif
64}
65
66static bool
67tr_areThreadsEqual (tr_thread_id a, tr_thread_id b)
68{
69#ifdef _WIN32
70  return a == b;
71#else
72  return pthread_equal (a, b) != 0;
73#endif
74}
75
76/** @brief portability wrapper around OS-dependent threads */
77struct tr_thread
78{
79  void          (* func)(void *);
80  void           * arg;
81  tr_thread_id     thread;
82#ifdef _WIN32
83  HANDLE           thread_handle;
84#endif
85};
86
87bool
88tr_amInThread (const tr_thread * t)
89{
90  return tr_areThreadsEqual (tr_getCurrentThread (), t->thread);
91}
92
93#ifdef _WIN32
94 #define ThreadFuncReturnType unsigned WINAPI
95#else
96 #define ThreadFuncReturnType void
97#endif
98
99static ThreadFuncReturnType
100ThreadFunc (void * _t)
101{
102  tr_thread * t = _t;
103
104  t->func (t->arg);
105
106  tr_free (t);
107#ifdef _WIN32
108  _endthreadex (0);
109  return 0;
110#endif
111}
112
113tr_thread *
114tr_threadNew (void (*func)(void *), void * arg)
115{
116  tr_thread * t = tr_new0 (tr_thread, 1);
117
118  t->func = func;
119  t->arg  = arg;
120
121#ifdef _WIN32
122  {
123    unsigned int id;
124    t->thread_handle = (HANDLE) _beginthreadex (NULL, 0, &ThreadFunc, t, 0, &id);
125    t->thread = (DWORD) id;
126  }
127#else
128  pthread_create (&t->thread, NULL, (void* (*)(void*))ThreadFunc, t);
129  pthread_detach (t->thread);
130#endif
131
132  return t;
133}
134
135/***
136****  LOCKS
137***/
138
139/** @brief portability wrapper around OS-dependent thread mutexes */
140struct tr_lock
141{
142  int                 depth;
143#ifdef _WIN32
144  CRITICAL_SECTION    lock;
145  DWORD               lockThread;
146#else
147  pthread_mutex_t     lock;
148  pthread_t           lockThread;
149#endif
150};
151
152tr_lock*
153tr_lockNew (void)
154{
155  tr_lock * l = tr_new0 (tr_lock, 1);
156
157#ifdef _WIN32
158  InitializeCriticalSection (&l->lock); /* supports recursion */
159#else
160  pthread_mutexattr_t attr;
161  pthread_mutexattr_init (&attr);
162  pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
163  pthread_mutex_init (&l->lock, &attr);
164#endif
165
166  return l;
167}
168
169void
170tr_lockFree (tr_lock * l)
171{
172#ifdef _WIN32
173    DeleteCriticalSection (&l->lock);
174#else
175    pthread_mutex_destroy (&l->lock);
176#endif
177    tr_free (l);
178}
179
180void
181tr_lockLock (tr_lock * l)
182{
183#ifdef _WIN32
184  EnterCriticalSection (&l->lock);
185#else
186  pthread_mutex_lock (&l->lock);
187#endif
188
189  assert (l->depth >= 0);
190  assert (!l->depth || tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
191  l->lockThread = tr_getCurrentThread ();
192  ++l->depth;
193}
194
195bool
196tr_lockHave (const tr_lock * l)
197{
198  return (l->depth > 0) &&
199         (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
200}
201
202void
203tr_lockUnlock (tr_lock * l)
204{
205  assert (l->depth > 0);
206  assert (tr_areThreadsEqual (l->lockThread, tr_getCurrentThread ()));
207
208  --l->depth;
209  assert (l->depth >= 0);
210#ifdef _WIN32
211  LeaveCriticalSection (&l->lock);
212#else
213  pthread_mutex_unlock (&l->lock);
214#endif
215}
216
217/***
218****  PATHS
219***/
220
221#ifndef _WIN32
222 #include <pwd.h>
223#endif
224
225#ifdef _WIN32
226
227static char *
228win32_get_known_folder (REFKNOWNFOLDERID folder_id)
229{
230  char * ret = NULL;
231  PWSTR path;
232
233  if (SHGetKnownFolderPath (folder_id, KF_FLAG_DONT_UNEXPAND | KF_FLAG_DONT_VERIFY,
234                            NULL, &path) == S_OK)
235    {
236      ret = tr_win32_native_to_utf8 (path, -1);
237      CoTaskMemFree (path);
238    }
239
240  return ret;
241}
242
243#endif
244
245static const char *
246getHomeDir (void)
247{
248  static char * home = NULL;
249
250  if (!home)
251    {
252      home = tr_env_get_string ("HOME", NULL);
253
254      if (!home)
255        {
256#ifdef _WIN32
257          home = win32_get_known_folder (&FOLDERID_Profile);
258#else
259          struct passwd * pw = getpwuid (getuid ());
260          if (pw)
261            home = tr_strdup (pw->pw_dir);
262          endpwent ();
263#endif
264        }
265
266      if (!home)
267        home = tr_strdup ("");
268    }
269
270  return home;
271}
272
273#if defined (__APPLE__) || defined (_WIN32)
274 #define RESUME_SUBDIR  "Resume"
275 #define TORRENT_SUBDIR "Torrents"
276#else
277 #define RESUME_SUBDIR  "resume"
278 #define TORRENT_SUBDIR "torrents"
279#endif
280
281void
282tr_setConfigDir (tr_session * session, const char * configDir)
283{
284  char * path;
285
286  session->configDir = tr_strdup (configDir);
287
288  path = tr_buildPath (configDir, RESUME_SUBDIR, NULL);
289  tr_sys_dir_create (path, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
290  session->resumeDir = path;
291
292  path = tr_buildPath (configDir, TORRENT_SUBDIR, NULL);
293  tr_sys_dir_create (path, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
294  session->torrentDir = path;
295}
296
297const char *
298tr_sessionGetConfigDir (const tr_session * session)
299{
300  return session->configDir;
301}
302
303const char *
304tr_getTorrentDir (const tr_session * session)
305{
306  return session->torrentDir;
307}
308
309const char *
310tr_getResumeDir (const tr_session * session)
311{
312  return session->resumeDir;
313}
314
315const char*
316tr_getDefaultConfigDir (const char * appname)
317{
318  static char * s = NULL;
319
320  if (!appname || !*appname)
321    appname = "Transmission";
322
323  if (!s)
324    {
325      s = tr_env_get_string ("TRANSMISSION_HOME", NULL);
326
327      if (s == NULL)
328        {
329#ifdef __APPLE__
330          s = tr_buildPath (getHomeDir (), "Library", "Application Support", appname, NULL);
331#elif defined (_WIN32)
332          char * appdata = win32_get_known_folder (&FOLDERID_LocalAppData);
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          char * const xdg_config_home = tr_env_get_string ("XDG_CONFIG_HOME", NULL);
341          if (xdg_config_home != NULL)
342            {
343              s = tr_buildPath (xdg_config_home, appname, NULL);
344              tr_free (xdg_config_home);
345            }
346          else
347            {
348              s = tr_buildPath (getHomeDir (), ".config", appname, NULL);
349            }
350#endif
351        }
352    }
353
354  return s;
355}
356
357const char*
358tr_getDefaultDownloadDir (void)
359{
360  static char * user_dir = NULL;
361
362  if (user_dir == NULL)
363    {
364      char * config_home;
365      char * config_file;
366      char * content;
367      size_t content_len;
368
369      /* figure out where to look for user-dirs.dirs */
370      config_home = tr_env_get_string ("XDG_CONFIG_HOME", NULL);
371      if (config_home && *config_home)
372        config_file = tr_buildPath (config_home, "user-dirs.dirs", NULL);
373      else
374        config_file = tr_buildPath (getHomeDir (), ".config", "user-dirs.dirs", NULL);
375      tr_free (config_home);
376
377      /* read in user-dirs.dirs and look for the download dir entry */
378      content = (char *) tr_loadFile (config_file, &content_len, NULL);
379      if (content && content_len>0)
380        {
381          const char * key = "XDG_DOWNLOAD_DIR=\"";
382          char * line = strstr (content, key);
383          if (line != NULL)
384            {
385              char * value = line + strlen (key);
386              char * end = strchr (value, '"');
387
388              if (end)
389                {
390                  *end = '\0';
391
392                  if (memcmp (value, "$HOME/", 6) == 0)
393                    user_dir = tr_buildPath (getHomeDir (), value+6, NULL);
394                  else if (strcmp (value, "$HOME") == 0)
395                    user_dir = tr_strdup (getHomeDir ());
396                  else
397                    user_dir = tr_strdup (value);
398                }
399            }
400        }
401
402#ifdef _WIN32
403      if (user_dir == NULL)
404        user_dir = win32_get_known_folder (&FOLDERID_Downloads);
405#endif
406
407      if (user_dir == NULL)
408#ifdef __HAIKU__
409        user_dir = tr_buildPath (getHomeDir (), "Desktop", NULL);
410#else
411        user_dir = tr_buildPath (getHomeDir (), "Downloads", NULL);
412#endif
413
414      tr_free (content);
415      tr_free (config_file);
416    }
417
418  return user_dir;
419}
420
421/***
422****
423***/
424
425static bool
426isWebClientDir (const char * path)
427{
428  char * tmp = tr_buildPath (path, "index.html", NULL);
429  const bool ret = tr_sys_path_exists (tmp, NULL);
430  tr_logAddInfo (_("Searching for web interface file \"%s\""), tmp);
431  tr_free (tmp);
432
433  return ret;
434}
435
436const char *
437tr_getWebClientDir (const tr_session * session UNUSED)
438{
439  static char * s = NULL;
440
441  if (!s)
442    {
443      s = tr_env_get_string ("CLUTCH_HOME", NULL);
444      if (s == NULL)
445        s = tr_env_get_string ("TRANSMISSION_WEB_HOME", NULL);
446      if (s == NULL)
447        {
448
449#ifdef BUILD_MAC_CLIENT /* on Mac, look in the Application Support folder first, then in the app bundle. */
450
451          /* Look in the Application Support folder */
452          s = tr_buildPath (tr_sessionGetConfigDir (session), "web", NULL);
453
454          if (!isWebClientDir (s))
455            {
456              tr_free (s);
457
458              CFURLRef appURL = CFBundleCopyBundleURL (CFBundleGetMainBundle ());
459              CFStringRef appRef = CFURLCopyFileSystemPath (appURL,
460                                                            kCFURLPOSIXPathStyle);
461              const CFIndex appStringLength = CFStringGetMaximumSizeOfFileSystemRepresentation (appRef);
462
463              char * appString = tr_malloc (appStringLength);
464              const bool success = CFStringGetFileSystemRepresentation (appRef, appString, appStringLength);
465              assert (success);
466
467              CFRelease (appURL);
468              CFRelease (appRef);
469
470              /* Fallback to the app bundle */
471              s = tr_buildPath (appString, "Contents", "Resources", "web", NULL);
472              if (!isWebClientDir (s))
473                {
474                  tr_free (s);
475                  s = NULL;
476                }
477
478              tr_free (appString);
479            }
480
481#elif defined (_WIN32)
482
483          /* Generally, Web interface should be stored in a Web subdir of
484           * calling executable dir. */
485
486          static REFKNOWNFOLDERID known_folder_ids[] =
487            {
488              &FOLDERID_LocalAppData,
489              &FOLDERID_RoamingAppData,
490              &FOLDERID_ProgramData
491            };
492
493          for (size_t i = 0; s == NULL && i < ARRAYSIZE (known_folder_ids); ++i)
494            {
495              char * dir = win32_get_known_folder (known_folder_ids[i]);
496              s = tr_buildPath (dir, "Transmission", "Web", NULL);
497              tr_free (dir);
498              if (!isWebClientDir (s))
499                {
500                  tr_free (s);
501                  s = NULL;
502                }
503            }
504
505            if (s == NULL) /* check calling module place */
506              {
507                wchar_t wide_module_path[MAX_PATH];
508                char * module_path;
509                char * dir;
510                GetModuleFileNameW (NULL, wide_module_path,
511                                    sizeof (wide_module_path) / sizeof (*wide_module_path));
512                module_path = tr_win32_native_to_utf8 (wide_module_path, -1);
513                dir = tr_sys_path_dirname (module_path, NULL);
514                tr_free (module_path);
515                s = tr_buildPath (dir, "Web", NULL);
516                tr_free (dir);
517                if (!isWebClientDir (s))
518                  {
519                    tr_free (s);
520                    s = NULL;
521                  }
522            }
523
524#else /* everyone else, follow the XDG spec */
525
526          tr_list *candidates = NULL, *l;
527          char * tmp;
528
529          /* XDG_DATA_HOME should be the first in the list of candidates */
530          tmp = tr_env_get_string ("XDG_DATA_HOME", NULL);
531          if (tmp && *tmp)
532            {
533              tr_list_append (&candidates, tmp);
534            }
535          else
536            {
537              char * dhome = tr_buildPath (getHomeDir (), ".local", "share", NULL);
538              tr_list_append (&candidates, dhome);
539              tr_free (tmp);
540            }
541
542          /* XDG_DATA_DIRS are the backup directories */
543          {
544            const char * pkg = PACKAGE_DATA_DIR;
545            char * xdg = tr_env_get_string ("XDG_DATA_DIRS", NULL);
546            const char * fallback = "/usr/local/share:/usr/share";
547            char * buf = tr_strdup_printf ("%s:%s:%s", (pkg?pkg:""), (xdg?xdg:""), fallback);
548            tr_free (xdg);
549            tmp = buf;
550            while (tmp && *tmp)
551              {
552                const char * end = strchr (tmp, ':');
553                if (end)
554                  {
555                    if ((end - tmp) > 1)
556                      tr_list_append (&candidates, tr_strndup (tmp, (size_t) (end - tmp)));
557                    tmp = (char *) end + 1;
558                  }
559                else if (tmp && *tmp)
560                  {
561                    tr_list_append (&candidates, tr_strdup (tmp));
562                    break;
563                  }
564              }
565            tr_free (buf);
566          }
567
568          /* walk through the candidates & look for a match */
569          for (l=candidates; l; l=l->next)
570            {
571              char * path = tr_buildPath (l->data, "transmission", "web", NULL);
572              const int found = isWebClientDir (path);
573              if (found)
574                {
575                  s = path;
576                  break;
577                }
578              tr_free (path);
579            }
580
581          tr_list_free (&candidates, tr_free);
582
583#endif
584
585        }
586    }
587
588  return s;
589}
Note: See TracBrowser for help on using the repository browser.