src/platform_unix.c
changeset 972 254427fc42ab
parent 889 0364fa566f29
child 987 ae7b616e7d0e
equal deleted inserted replaced
969:0bc902336e00 972:254427fc42ab
       
     1 /*
       
     2  * Unix support routines for PhysicsFS.
       
     3  *
       
     4  * Please see the file LICENSE.txt in the source's root directory.
       
     5  *
       
     6  *  This file written by Ryan C. Gordon.
       
     7  */
       
     8 
       
     9 #define __PHYSICSFS_INTERNAL__
       
    10 #include "physfs_platforms.h"
       
    11 
       
    12 #ifdef PHYSFS_PLATFORM_UNIX
       
    13 
       
    14 #include <stdio.h>
       
    15 #include <stdlib.h>
       
    16 #include <string.h>
       
    17 #include <ctype.h>
       
    18 #include <unistd.h>
       
    19 #include <sys/types.h>
       
    20 #include <pwd.h>
       
    21 #include <sys/stat.h>
       
    22 #include <sys/param.h>
       
    23 #include <dirent.h>
       
    24 #include <time.h>
       
    25 #include <errno.h>
       
    26 #include <sys/mount.h>
       
    27 
       
    28 #if (!defined PHYSFS_NO_PTHREADS_SUPPORT)
       
    29 #include <pthread.h>
       
    30 #endif
       
    31 
       
    32 #ifdef PHYSFS_HAVE_SYS_UCRED_H
       
    33 #  ifdef PHYSFS_HAVE_MNTENT_H
       
    34 #    undef PHYSFS_HAVE_MNTENT_H /* don't do both... */
       
    35 #  endif
       
    36 #  include <sys/ucred.h>
       
    37 #endif
       
    38 
       
    39 #ifdef PHYSFS_HAVE_MNTENT_H
       
    40 #include <mntent.h>
       
    41 #endif
       
    42 
       
    43 #include "physfs_internal.h"
       
    44 
       
    45 
       
    46 int __PHYSFS_platformInit(void)
       
    47 {
       
    48     return(1);  /* always succeed. */
       
    49 } /* __PHYSFS_platformInit */
       
    50 
       
    51 
       
    52 int __PHYSFS_platformDeinit(void)
       
    53 {
       
    54     return(1);  /* always succeed. */
       
    55 } /* __PHYSFS_platformDeinit */
       
    56 
       
    57 
       
    58 #ifdef PHYSFS_NO_CDROM_SUPPORT
       
    59 
       
    60 /* Stub version for platforms without CD-ROM support. */
       
    61 void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
       
    62 {
       
    63 } /* __PHYSFS_platformDetectAvailableCDs */
       
    64 
       
    65 #elif (defined PHYSFS_HAVE_SYS_UCRED_H)
       
    66 
       
    67 void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
       
    68 {
       
    69     int i;
       
    70     struct statfs *mntbufp = NULL;
       
    71     int mounts = getmntinfo(&mntbufp, MNT_WAIT);
       
    72 
       
    73     for (i = 0; i < mounts; i++)
       
    74     {
       
    75         int add_it = 0;
       
    76 
       
    77         if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0)
       
    78             add_it = 1;
       
    79         else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0)
       
    80             add_it = 1;
       
    81 
       
    82         /* add other mount types here */
       
    83 
       
    84         if (add_it)
       
    85             cb(data, mntbufp[i].f_mntonname);
       
    86     } /* for */
       
    87 } /* __PHYSFS_platformDetectAvailableCDs */
       
    88 
       
    89 #elif (defined PHYSFS_HAVE_MNTENT_H)
       
    90 
       
    91 void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
       
    92 {
       
    93     FILE *mounts = NULL;
       
    94     struct mntent *ent = NULL;
       
    95 
       
    96     mounts = setmntent("/etc/mtab", "r");
       
    97     BAIL_IF_MACRO(mounts == NULL, ERR_IO_ERROR, /*return void*/);
       
    98 
       
    99     while ( (ent = getmntent(mounts)) != NULL )
       
   100     {
       
   101         int add_it = 0;
       
   102         if (strcmp(ent->mnt_type, "iso9660") == 0)
       
   103             add_it = 1;
       
   104 
       
   105         /* add other mount types here */
       
   106 
       
   107         if (add_it)
       
   108             cb(data, ent->mnt_dir);
       
   109     } /* while */
       
   110 
       
   111     endmntent(mounts);
       
   112 
       
   113 } /* __PHYSFS_platformDetectAvailableCDs */
       
   114 
       
   115 #endif
       
   116 
       
   117 
       
   118 /* this is in posix.c ... */
       
   119 extern char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname);
       
   120 
       
   121 
       
   122 /*
       
   123  * See where program (bin) resides in the $PATH specified by (envr).
       
   124  *  returns a copy of the first element in envr that contains it, or NULL
       
   125  *  if it doesn't exist or there were other problems. PHYSFS_SetError() is
       
   126  *  called if we have a problem.
       
   127  *
       
   128  * (envr) will be scribbled over, and you are expected to allocator.Free() the
       
   129  *  return value when you're done with it.
       
   130  */
       
   131 static char *findBinaryInPath(const char *bin, char *envr)
       
   132 {
       
   133     size_t alloc_size = 0;
       
   134     char *exe = NULL;
       
   135     char *start = envr;
       
   136     char *ptr;
       
   137 
       
   138     BAIL_IF_MACRO(bin == NULL, ERR_INVALID_ARGUMENT, NULL);
       
   139     BAIL_IF_MACRO(envr == NULL, ERR_INVALID_ARGUMENT, NULL);
       
   140 
       
   141     do
       
   142     {
       
   143         size_t size;
       
   144         ptr = strchr(start, ':');  /* find next $PATH separator. */
       
   145         if (ptr)
       
   146             *ptr = '\0';
       
   147 
       
   148         size = strlen(start) + strlen(bin) + 2;
       
   149         if (size > alloc_size)
       
   150         {
       
   151             char *x = (char *) allocator.Realloc(exe, size);
       
   152             if (x == NULL)
       
   153             {
       
   154                 if (exe != NULL)
       
   155                     allocator.Free(exe);
       
   156                 BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
       
   157             } /* if */
       
   158 
       
   159             alloc_size = size;
       
   160             exe = x;
       
   161         } /* if */
       
   162 
       
   163         /* build full binary path... */
       
   164         strcpy(exe, start);
       
   165         if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
       
   166             strcat(exe, "/");
       
   167         strcat(exe, bin);
       
   168 
       
   169         if (access(exe, X_OK) == 0)  /* Exists as executable? We're done. */
       
   170         {
       
   171             strcpy(exe, start);  /* i'm lazy. piss off. */
       
   172             return(exe);
       
   173         } /* if */
       
   174 
       
   175         start = ptr + 1;  /* start points to beginning of next element. */
       
   176     } while (ptr != NULL);
       
   177 
       
   178     if (exe != NULL)
       
   179         allocator.Free(exe);
       
   180 
       
   181     return(NULL);  /* doesn't exist in path. */
       
   182 } /* findBinaryInPath */
       
   183 
       
   184 
       
   185 char *__PHYSFS_platformCalcBaseDir(const char *argv0)
       
   186 {
       
   187     const char *PROC_SELF_EXE = "/proc/self/exe";
       
   188     char *retval = NULL;
       
   189     char *envr = NULL;
       
   190     struct stat stbuf;
       
   191 
       
   192     /* fast path: default behaviour can handle this. */
       
   193     if ( (argv0 != NULL) && (strchr(argv0, '/') != NULL) )
       
   194         return(NULL);  /* higher level will parse out real path from argv0. */
       
   195 
       
   196     /*
       
   197      * Try to avoid using argv0 unless forced to. If there's a Linux-like
       
   198      *  /proc filesystem, you can get the full path to the current process from
       
   199      *  the /proc/self/exe symlink.
       
   200      */
       
   201     if ((lstat(PROC_SELF_EXE, &stbuf) != -1) && (S_ISLNK(stbuf.st_mode)))
       
   202     {
       
   203         const size_t len = stbuf.st_size;
       
   204         char *buf = (char *) allocator.Malloc(len+1);
       
   205         if (buf != NULL)  /* if NULL, maybe you'll get lucky later. */
       
   206         {
       
   207             if (readlink(PROC_SELF_EXE, buf, len) != len)
       
   208                 allocator.Free(buf);
       
   209             else
       
   210             {
       
   211                 buf[len] = '\0';  /* readlink doesn't null-terminate. */
       
   212                 retval = buf;  /* we're good to go. */
       
   213             } /* else */
       
   214         } /* if */
       
   215     } /* if */
       
   216 
       
   217     if ((retval == NULL) && (argv0 != NULL))
       
   218     {
       
   219         /* If there's no dirsep on argv0, then look through $PATH for it. */
       
   220         envr = __PHYSFS_platformCopyEnvironmentVariable("PATH");
       
   221         BAIL_IF_MACRO(!envr, NULL, NULL);
       
   222         retval = findBinaryInPath(argv0, envr);
       
   223         allocator.Free(envr);
       
   224     } /* if */
       
   225 
       
   226     return(retval);
       
   227 } /* __PHYSFS_platformCalcBaseDir */
       
   228 
       
   229 
       
   230 char *__PHYSFS_platformRealPath(const char *path)
       
   231 {
       
   232     char resolved_path[MAXPATHLEN];
       
   233     char *retval = NULL;
       
   234 
       
   235     errno = 0;
       
   236     BAIL_IF_MACRO(!realpath(path, resolved_path), strerror(errno), NULL);
       
   237     retval = (char *) allocator.Malloc(strlen(resolved_path) + 1);
       
   238     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
       
   239     strcpy(retval, resolved_path);
       
   240 
       
   241     return(retval);
       
   242 } /* __PHYSFS_platformRealPath */
       
   243 
       
   244 
       
   245 char *__PHYSFS_platformCurrentDir(void)
       
   246 {
       
   247     /*
       
   248      * This can't just do platformRealPath("."), since that would eventually
       
   249      *  just end up calling back into here.
       
   250      */
       
   251 
       
   252     int allocSize = 0;
       
   253     char *retval = NULL;
       
   254     char *ptr;
       
   255 
       
   256     do
       
   257     {
       
   258         allocSize += 100;
       
   259         ptr = (char *) allocator.Realloc(retval, allocSize);
       
   260         if (ptr == NULL)
       
   261         {
       
   262             if (retval != NULL)
       
   263                 allocator.Free(retval);
       
   264             BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
       
   265         } /* if */
       
   266 
       
   267         retval = ptr;
       
   268         ptr = getcwd(retval, allocSize);
       
   269     } while (ptr == NULL && errno == ERANGE);
       
   270 
       
   271     if (ptr == NULL && errno)
       
   272     {
       
   273             /*
       
   274              * getcwd() failed for some reason, for example current
       
   275              * directory not existing.
       
   276              */
       
   277         if (retval != NULL)
       
   278             allocator.Free(retval);
       
   279         BAIL_MACRO(ERR_NO_SUCH_FILE, NULL);
       
   280     } /* if */
       
   281 
       
   282     return(retval);
       
   283 } /* __PHYSFS_platformCurrentDir */
       
   284 
       
   285 
       
   286 int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
       
   287 {
       
   288     return(0);  /* just use malloc() and friends. */
       
   289 } /* __PHYSFS_platformSetDefaultAllocator */
       
   290 
       
   291 
       
   292 #if (defined PHYSFS_NO_PTHREADS_SUPPORT)
       
   293 
       
   294 PHYSFS_uint64 __PHYSFS_platformGetThreadID(void) { return(0x0001); }
       
   295 void *__PHYSFS_platformCreateMutex(void) { return((void *) 0x0001); }
       
   296 void __PHYSFS_platformDestroyMutex(void *mutex) {}
       
   297 int __PHYSFS_platformGrabMutex(void *mutex) { return(1); }
       
   298 void __PHYSFS_platformReleaseMutex(void *mutex) {}
       
   299 
       
   300 #else
       
   301 
       
   302 typedef struct
       
   303 {
       
   304     pthread_mutex_t mutex;
       
   305     pthread_t owner;
       
   306     PHYSFS_uint32 count;
       
   307 } PthreadMutex;
       
   308 
       
   309 /* Just in case; this is a panic value. */
       
   310 #if ((!defined SIZEOF_INT) || (SIZEOF_INT <= 0))
       
   311 #  define SIZEOF_INT 4
       
   312 #endif
       
   313 
       
   314 #if (SIZEOF_INT == 4)
       
   315 #  define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint32) (thr)) )
       
   316 #elif (SIZEOF_INT == 2)
       
   317 #  define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint16) (thr)) )
       
   318 #elif (SIZEOF_INT == 1)
       
   319 #  define PHTREAD_TO_UI64(thr) ( (PHYSFS_uint64) ((PHYSFS_uint8) (thr)) )
       
   320 #else
       
   321 #  define PHTREAD_TO_UI64(thr) ((PHYSFS_uint64) (thr))
       
   322 #endif
       
   323 
       
   324 PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
       
   325 {
       
   326     return(PHTREAD_TO_UI64(pthread_self()));
       
   327 } /* __PHYSFS_platformGetThreadID */
       
   328 
       
   329 
       
   330 void *__PHYSFS_platformCreateMutex(void)
       
   331 {
       
   332     int rc;
       
   333     PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex));
       
   334     BAIL_IF_MACRO(m == NULL, ERR_OUT_OF_MEMORY, NULL);
       
   335     rc = pthread_mutex_init(&m->mutex, NULL);
       
   336     if (rc != 0)
       
   337     {
       
   338         allocator.Free(m);
       
   339         BAIL_MACRO(strerror(rc), NULL);
       
   340     } /* if */
       
   341 
       
   342     m->count = 0;
       
   343     m->owner = (pthread_t) 0xDEADBEEF;
       
   344     return((void *) m);
       
   345 } /* __PHYSFS_platformCreateMutex */
       
   346 
       
   347 
       
   348 void __PHYSFS_platformDestroyMutex(void *mutex)
       
   349 {
       
   350     PthreadMutex *m = (PthreadMutex *) mutex;
       
   351 
       
   352     /* Destroying a locked mutex is a bug, but we'll try to be helpful. */
       
   353     if ((m->owner == pthread_self()) && (m->count > 0))
       
   354         pthread_mutex_unlock(&m->mutex);
       
   355 
       
   356     pthread_mutex_destroy(&m->mutex);
       
   357     allocator.Free(m);
       
   358 } /* __PHYSFS_platformDestroyMutex */
       
   359 
       
   360 
       
   361 int __PHYSFS_platformGrabMutex(void *mutex)
       
   362 {
       
   363     PthreadMutex *m = (PthreadMutex *) mutex;
       
   364     pthread_t tid = pthread_self();
       
   365     if (m->owner != tid)
       
   366     {
       
   367         if (pthread_mutex_lock(&m->mutex) != 0)
       
   368             return(0);
       
   369         m->owner = tid;
       
   370     } /* if */
       
   371 
       
   372     m->count++;
       
   373     return(1);
       
   374 } /* __PHYSFS_platformGrabMutex */
       
   375 
       
   376 
       
   377 void __PHYSFS_platformReleaseMutex(void *mutex)
       
   378 {
       
   379     PthreadMutex *m = (PthreadMutex *) mutex;
       
   380     if (m->owner == pthread_self())
       
   381     {
       
   382         if (--m->count == 0)
       
   383         {
       
   384             m->owner = (pthread_t) 0xDEADBEEF;
       
   385             pthread_mutex_unlock(&m->mutex);
       
   386         } /* if */
       
   387     } /* if */
       
   388 } /* __PHYSFS_platformReleaseMutex */
       
   389 
       
   390 #endif /* !PHYSFS_NO_PTHREADS_SUPPORT */
       
   391 
       
   392 #endif /* PHYSFS_PLATFORM_UNIX */
       
   393 
       
   394 /* end of unix.c ... */
       
   395