Home | History | Annotate | Download | only in utils
      1 /* Copyright (C) 2007-2008 The Android Open Source Project
      2 **
      3 ** This software is licensed under the terms of the GNU General Public
      4 ** License version 2, as published by the Free Software Foundation, and
      5 ** may be copied, distributed, and modified under those terms.
      6 **
      7 ** This program is distributed in the hope that it will be useful,
      8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
      9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     10 ** GNU General Public License for more details.
     11 */
     12 
     13 #include "android/utils/filelock.h"
     14 #include "android/utils/path.h"
     15 #include <stdio.h>
     16 #include <stdlib.h>
     17 #include <errno.h>
     18 #include <sys/stat.h>
     19 #include <time.h>
     20 #include <fcntl.h>
     21 #ifdef _WIN32
     22 #  include <process.h>
     23 #  include <windows.h>
     24 #  include <tlhelp32.h>
     25 #else
     26 #  include <sys/types.h>
     27 #  include <unistd.h>
     28 #  include <signal.h>
     29 #endif
     30 
     31 #define  D(...)  ((void)0)
     32 
     33 #ifndef CHECKED
     34 #  ifdef _WIN32
     35 #    define   CHECKED(ret, call)    (ret) = (call)
     36 #  else
     37 #    define   CHECKED(ret, call)    do { (ret) = (call); } while ((ret) < 0 && errno == EINTR)
     38 #  endif
     39 #endif
     40 
     41 /** FILE LOCKS SUPPORT
     42  **
     43  ** a FileLock is useful to prevent several emulator instances from using the same
     44  ** writable file (e.g. the userdata.img disk images).
     45  **
     46  ** create a FileLock object with filelock_create(), ithis function should return NULL
     47  ** only if the corresponding file path could not be locked.
     48  **
     49  ** all file locks are automatically released and destroyed when the program exits.
     50  ** the filelock_lock() function can also detect stale file locks that can linger
     51  ** when the emulator crashes unexpectedly, and will happily clean them for you
     52  **
     53  **  here's how it works, three files are used:
     54  **     file  - the data file accessed by the emulator
     55  **     lock  - a lock file  (file + '.lock')
     56  **     temp  - a temporary file make unique with mkstemp
     57  **
     58  **  when locking:
     59  **      create 'temp' and store our pid in it
     60  **      attemp to link 'lock' to 'temp'
     61  **         if the link succeeds, we obtain the lock
     62  **      unlink 'temp'
     63  **
     64  **  when unlocking:
     65  **      unlink 'lock'
     66  **
     67  **
     68  **  on Windows, 'lock' is a directory name. locking is equivalent to
     69  **  creating it...
     70  **
     71  **/
     72 
     73 struct FileLock
     74 {
     75   const char*  file;
     76   const char*  lock;
     77   char*        temp;
     78   int          locked;
     79   FileLock*    next;
     80 };
     81 
     82 /* used to cleanup all locks at emulator exit */
     83 static FileLock*   _all_filelocks;
     84 
     85 
     86 #define  LOCK_NAME   ".lock"
     87 #define  TEMP_NAME   ".tmp-XXXXXX"
     88 
     89 #ifdef _WIN32
     90 #define  PIDFILE_NAME  "pid"
     91 #endif
     92 
     93 /* returns 0 on success, -1 on failure */
     94 static int
     95 filelock_lock( FileLock*  lock )
     96 {
     97     int    ret;
     98 #ifdef _WIN32
     99     int  pidfile_fd = -1;
    100 
    101     ret = _mkdir( lock->lock );
    102     if (ret < 0) {
    103         if (errno == ENOENT) {
    104             D( "could not access directory '%s', check path elements", lock->lock );
    105             return -1;
    106         } else if (errno != EEXIST) {
    107             D( "_mkdir(%s): %s", lock->lock, strerror(errno) );
    108             return -1;
    109         }
    110 
    111         /* if we get here, it's because the .lock directory already exists */
    112         /* check to see if there is a pid file in it                       */
    113         D("directory '%s' already exist, waiting a bit to ensure that no other emulator instance is starting", lock->lock );
    114         {
    115             int  _sleep = 200;
    116             int  tries;
    117 
    118             for ( tries = 4; tries > 0; tries-- )
    119             {
    120                 pidfile_fd = open( lock->temp, O_RDONLY );
    121 
    122                 if (pidfile_fd >= 0)
    123                     break;
    124 
    125                 Sleep( _sleep );
    126                 _sleep *= 2;
    127             }
    128         }
    129 
    130         if (pidfile_fd < 0) {
    131             D( "no pid file in '%s', assuming stale directory", lock->lock );
    132         }
    133         else
    134         {
    135             /* read the pidfile, and check wether the corresponding process is still running */
    136             char            buf[16];
    137             int             len, lockpid;
    138             HANDLE          processSnapshot;
    139             PROCESSENTRY32  pe32;
    140             int             is_locked = 0;
    141 
    142             len = read( pidfile_fd, buf, sizeof(buf)-1 );
    143             if (len < 0) {
    144                 D( "could not read pid file '%s'", lock->temp );
    145                 close( pidfile_fd );
    146                 return -1;
    147             }
    148             buf[len] = 0;
    149             lockpid  = atoi(buf);
    150 
    151             /* PID 0 is the IDLE process, and 0 is returned in case of invalid input */
    152             if (lockpid == 0)
    153                 lockpid = -1;
    154 
    155             close( pidfile_fd );
    156 
    157             pe32.dwSize     = sizeof( PROCESSENTRY32 );
    158             processSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
    159 
    160             if ( processSnapshot == INVALID_HANDLE_VALUE ) {
    161                 D( "could not retrieve the list of currently active processes\n" );
    162                 is_locked = 1;
    163             }
    164             else if ( !Process32First( processSnapshot, &pe32 ) )
    165             {
    166                 D( "could not retrieve first process id\n" );
    167                 CloseHandle( processSnapshot );
    168                 is_locked = 1;
    169             }
    170             else
    171             {
    172                 do {
    173                     if (pe32.th32ProcessID == lockpid) {
    174                         is_locked = 1;
    175                         break;
    176                     }
    177                 } while (Process32Next( processSnapshot, &pe32 ) );
    178 
    179                 CloseHandle( processSnapshot );
    180             }
    181 
    182             if (is_locked) {
    183                 D( "the file '%s' is locked by process ID %d\n", lock->file, lockpid );
    184                 return -1;
    185             }
    186         }
    187     }
    188 
    189     /* write our PID into the pid file */
    190     pidfile_fd = open( lock->temp, O_WRONLY | O_CREAT | O_TRUNC );
    191     if (pidfile_fd < 0) {
    192         if (errno == EACCES) {
    193             if ( path_delete_file( lock->temp ) < 0 ) {
    194                 D( "could not remove '%s': %s\n", lock->temp, strerror(errno) );
    195                 return -1;
    196             }
    197             pidfile_fd = open( lock->temp, O_WRONLY | O_CREAT | O_TRUNC );
    198         }
    199         if (pidfile_fd < 0) {
    200             D( "could not create '%s': %s\n", lock->temp, strerror(errno) );
    201             return -1;
    202         }
    203     }
    204 
    205     {
    206         char  buf[16];
    207         sprintf( buf, "%ld", GetCurrentProcessId() );
    208         ret = write( pidfile_fd, buf, strlen(buf) );
    209         close(pidfile_fd);
    210         if (ret < 0) {
    211             D( "could not write PID to '%s'\n", lock->temp );
    212             return -1;
    213         }
    214     }
    215 
    216     lock->locked = 1;
    217     return 0;
    218 #else
    219     int    temp_fd = -1;
    220     int    lock_fd = -1;
    221     int    rc, tries, _sleep;
    222     FILE*  f = NULL;
    223     char   pid[8];
    224     struct stat  st_temp;
    225 
    226     strcpy( lock->temp, lock->file );
    227     strcat( lock->temp, TEMP_NAME );
    228     temp_fd = mkstemp( lock->temp );
    229 
    230     if (temp_fd < 0) {
    231         D("cannot create locking temp file '%s'", lock->temp );
    232         goto Fail;
    233     }
    234 
    235     sprintf( pid, "%d", getpid() );
    236     ret = write( temp_fd, pid, strlen(pid)+1 );
    237     if (ret < 0) {
    238         D( "cannot write to locking temp file '%s'", lock->temp);
    239         goto Fail;
    240     }
    241     close( temp_fd );
    242     temp_fd = -1;
    243 
    244     CHECKED(rc, lstat( lock->temp, &st_temp ));
    245     if (rc < 0) {
    246         D( "can't properly stat our locking temp file '%s'", lock->temp );
    247         goto Fail;
    248     }
    249 
    250     /* now attempt to link the temp file to the lock file */
    251     _sleep = 0;
    252     for ( tries = 4; tries > 0; tries-- )
    253     {
    254         struct stat  st_lock;
    255         int          rc;
    256 
    257         if (_sleep > 0) {
    258             if (_sleep > 2000000) {
    259                 D( "cannot acquire lock file '%s'", lock->lock );
    260                 goto Fail;
    261             }
    262             usleep( _sleep );
    263         }
    264         _sleep += 200000;
    265 
    266         /* the return value of link() is buggy on NFS */
    267         CHECKED(rc, link( lock->temp, lock->lock ));
    268 
    269         CHECKED(rc, lstat( lock->lock, &st_lock ));
    270         if (rc == 0 &&
    271             st_temp.st_rdev == st_lock.st_rdev &&
    272             st_temp.st_ino  == st_lock.st_ino  )
    273         {
    274             /* SUCCESS */
    275             lock->locked = 1;
    276             CHECKED(rc, unlink( lock->temp ));
    277             return 0;
    278         }
    279 
    280         /* if we get there, it means that the link() call failed */
    281         /* check the lockfile to see if it is stale              */
    282         if (rc == 0) {
    283             char    buf[16];
    284             time_t  now;
    285             int     lockpid = 0;
    286             int     lockfd;
    287             int     stale = 2;  /* means don't know */
    288             struct stat  st;
    289 
    290             CHECKED(rc, time( &now));
    291             st.st_mtime = now - 120;
    292 
    293             CHECKED(lockfd, open( lock->lock,O_RDONLY ));
    294             if ( lockfd >= 0 ) {
    295                 int  len;
    296 
    297                 CHECKED(len, read( lockfd, buf, sizeof(buf)-1 ));
    298                 buf[len] = 0;
    299                 lockpid = atoi(buf);
    300 
    301                 CHECKED(rc, fstat( lockfd, &st ));
    302                 if (rc == 0)
    303                   now = st.st_atime;
    304 
    305                 CHECKED(rc, close(lockfd));
    306             }
    307             /* if there is a PID, check that it is still alive */
    308             if (lockpid > 0) {
    309                 CHECKED(rc, kill( lockpid, 0 ));
    310                 if (rc == 0 || errno == EPERM) {
    311                     stale = 0;
    312                 } else if (rc < 0 && errno == ESRCH) {
    313                     stale = 1;
    314                 }
    315             }
    316             if (stale == 2) {
    317                 /* no pid, stale if the file is older than 1 minute */
    318                 stale = (now >= st.st_mtime + 60);
    319             }
    320 
    321             if (stale) {
    322                 D( "removing stale lockfile '%s'", lock->lock );
    323                 CHECKED(rc, unlink( lock->lock ));
    324                 _sleep = 0;
    325                 tries++;
    326             }
    327         }
    328     }
    329     D("file '%s' is already in use by another process", lock->file );
    330 
    331 Fail:
    332     if (f)
    333         fclose(f);
    334 
    335     if (temp_fd >= 0) {
    336         close(temp_fd);
    337     }
    338 
    339     if (lock_fd >= 0) {
    340         close(lock_fd);
    341     }
    342 
    343     unlink( lock->lock );
    344     unlink( lock->temp );
    345     return -1;
    346 #endif
    347 }
    348 
    349 void
    350 filelock_release( FileLock*  lock )
    351 {
    352     if (lock->locked) {
    353 #ifdef _WIN32
    354         path_delete_file( (char*)lock->temp );
    355         rmdir( (char*)lock->lock );
    356 #else
    357         unlink( (char*)lock->lock );
    358 #endif
    359         lock->locked = 0;
    360     }
    361 }
    362 
    363 static void
    364 filelock_atexit( void )
    365 {
    366   FileLock*  lock;
    367 
    368   for (lock = _all_filelocks; lock != NULL; lock = lock->next)
    369      filelock_release( lock );
    370 }
    371 
    372 /* create a file lock */
    373 FileLock*
    374 filelock_create( const char*  file )
    375 {
    376     int    file_len = strlen(file);
    377     int    lock_len = file_len + sizeof(LOCK_NAME);
    378 #ifdef _WIN32
    379     int    temp_len = lock_len + 1 + sizeof(PIDFILE_NAME);
    380 #else
    381     int    temp_len = file_len + sizeof(TEMP_NAME);
    382 #endif
    383     int    total_len = sizeof(FileLock) + file_len + lock_len + temp_len + 3;
    384 
    385     FileLock*  lock = malloc(total_len);
    386 
    387     lock->file = (const char*)(lock + 1);
    388     memcpy( (char*)lock->file, file, file_len+1 );
    389 
    390     lock->lock = lock->file + file_len + 1;
    391     memcpy( (char*)lock->lock, file, file_len+1 );
    392     strcat( (char*)lock->lock, LOCK_NAME );
    393 
    394     lock->temp    = (char*)lock->lock + lock_len + 1;
    395 #ifdef _WIN32
    396     snprintf( (char*)lock->temp, temp_len, "%s\\" PIDFILE_NAME, lock->lock );
    397 #else
    398     lock->temp[0] = 0;
    399 #endif
    400     lock->locked = 0;
    401 
    402     if (filelock_lock(lock) < 0) {
    403         free(lock);
    404         return NULL;
    405     }
    406 
    407     lock->next     = _all_filelocks;
    408     _all_filelocks = lock;
    409 
    410     if (lock->next == NULL)
    411         atexit( filelock_atexit );
    412 
    413     return lock;
    414 }
    415