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/eintr_wrapper.h"
     14 #include "android/utils/filelock.h"
     15 #include "android/utils/path.h"
     16 #include <stdio.h>
     17 #include <stdlib.h>
     18 #include <errno.h>
     19 #include <sys/stat.h>
     20 #include <time.h>
     21 #include <fcntl.h>
     22 #ifdef _WIN32
     23 #  include <process.h>
     24 #  include <windows.h>
     25 #  include <tlhelp32.h>
     26 #else
     27 #  include <sys/types.h>
     28 #  include <unistd.h>
     29 #  include <signal.h>
     30 #endif
     31 
     32 // Set to 1 to enable debug traces here.
     33 #if 0
     34 #define  D(...)  printf(__VA_ARGS__), printf("\n"), fflush(stdout)
     35 #else
     36 #define D(...) ((void)0)
     37 #endif
     38 
     39 /** FILE LOCKS SUPPORT
     40  **
     41  ** A FileLock is useful to prevent several emulator instances from using
     42  ** the same writable file (e.g. the userdata.img disk images).
     43  **
     44  ** Create a FileLock object with filelock_create(), this function should
     45  ** return NULL only if the corresponding file path could not be locked.
     46  **
     47  ** All file locks are automatically released and destroyed when the program
     48  ** exits. The filelock_lock() function can also detect stale file locks
     49  ** that can linger when the emulator crashes unexpectedly, and will happily
     50  ** clean them for you
     51  **
     52  ** Here's how it works, three files are used:
     53  **     file  - the data file accessed by the emulator
     54  **     lock  - a lock file  (file + '.lock')
     55  **     temp  - a temporary file made unique with mkstemp
     56  **
     57  ** When locking:
     58  **      create 'temp' and store our pid in it
     59  **      attemp to link 'lock' to 'temp'
     60  **         if the link succeeds, we obtain the lock
     61  **      unlink 'temp'
     62  **
     63  ** When unlocking:
     64  **      unlink 'lock'
     65  **
     66  **
     67  ** On Windows, 'lock' is a directory name. locking is equivalent to
     68  ** creating it. The directory will contain a file named 'pid' that
     69  ** contains the locking process' PID.
     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;
    222     FILE*  f = NULL;
    223     char   pid[8];
    224     struct stat  st_temp;
    225 
    226     temp_fd = mkstemp(lock->temp);
    227 
    228     if (temp_fd < 0) {
    229         D("Cannot create locking temp file '%s'", lock->temp);
    230         goto Fail;
    231     }
    232 
    233     snprintf(pid, sizeof pid, "%d", getpid());
    234     ret = HANDLE_EINTR(write(temp_fd, pid, strlen(pid) + 1));
    235     if (ret < 0) {
    236         D("Cannot write to locking temp file '%s'", lock->temp);
    237         goto Fail;
    238     }
    239     close(temp_fd);
    240     temp_fd = -1;
    241 
    242     rc = HANDLE_EINTR(lstat(lock->temp, &st_temp));
    243     if (rc < 0) {
    244         D("Can't properly stat our locking temp file '%s'", lock->temp);
    245         goto Fail;
    246     }
    247 
    248     /* now attempt to link the temp file to the lock file */
    249     int sleep_duration_us = 0;
    250     for (tries = 4; tries > 0; tries--)
    251     {
    252         const int kSleepDurationUsMax = 2000000;  // 2 seconds.
    253         const int kSleepDurationUsIncrement = 200000;  // 0.2 seconds
    254 
    255         if (sleep_duration_us > 0) {
    256             if (sleep_duration_us > kSleepDurationUsMax) {
    257                 D("Cannot acquire lock file '%s'", lock->lock);
    258                 goto Fail;
    259             }
    260             usleep(sleep_duration_us);
    261         }
    262         sleep_duration_us += kSleepDurationUsIncrement;
    263 
    264         // The return value of link() is buggy on NFS, so ignore it.
    265         // and use lstat() to look at the result.
    266         rc = HANDLE_EINTR(link(lock->temp, lock->lock));
    267 
    268         struct stat st_lock;
    269         rc = HANDLE_EINTR(lstat(lock->lock, &st_lock));
    270         if (rc != 0) {
    271             // Try again after sleeping a little.
    272             continue;
    273         }
    274 
    275         if (st_temp.st_rdev == st_lock.st_rdev &&
    276             st_temp.st_ino  == st_lock.st_ino  ) {
    277             /* The link() operation suceeded */
    278             lock->locked = 1;
    279             rc = HANDLE_EINTR(unlink(lock->temp));
    280             return 0;
    281         }
    282 
    283         if (S_ISDIR(st_lock.st_mode)) {
    284             // The .lock file is a directory. This can only happen
    285             // when the AVD was previously used by a Win32 emulator
    286             // instance running under Wine on the same machine.
    287             fprintf(stderr,
    288                     "Stale Win32 lock file detected: %s\n",
    289                     lock->lock);
    290             goto Fail;
    291         }
    292 
    293         /* if we get there, it means that the link() call failed */
    294         /* check the lockfile to see if it is stale              */
    295         typedef enum {
    296             FRESHNESS_UNKNOWN = 0,
    297             FRESHNESS_FRESH,
    298             FRESHNESS_STALE,
    299         } Freshness;
    300 
    301         Freshness freshness = FRESHNESS_UNKNOWN;
    302 
    303         struct stat st;
    304         time_t now;
    305         rc = HANDLE_EINTR(time(&now));
    306         st.st_mtime = now - 120;
    307 
    308         int lockpid = 0;
    309         int lockfd = HANDLE_EINTR(open(lock->lock,O_RDONLY));
    310         if (lockfd >= 0) {
    311             char buf[16];
    312             int len = HANDLE_EINTR(read(lockfd, buf, sizeof(buf) - 1U));
    313             if (len < 0) {
    314                 len = 0;
    315             }
    316             buf[len] = 0;
    317             lockpid = atoi(buf);
    318 
    319             rc = HANDLE_EINTR(fstat(lockfd, &st));
    320             if (rc == 0) {
    321                 now = st.st_atime;
    322             }
    323             IGNORE_EINTR(close(lockfd));
    324         }
    325         /* if there is a PID, check that it is still alive */
    326         if (lockpid > 0) {
    327             rc = HANDLE_EINTR(kill(lockpid, 0));
    328             if (rc == 0 || errno == EPERM) {
    329                 freshness = FRESHNESS_FRESH;
    330             } else if (rc < 0 && errno == ESRCH) {
    331                 freshness = FRESHNESS_STALE;
    332             }
    333         }
    334         if (freshness == FRESHNESS_UNKNOWN) {
    335             /* no pid, stale if the file is older than 1 minute */
    336             freshness = (now >= st.st_mtime + 60) ?
    337                     FRESHNESS_STALE :
    338                     FRESHNESS_FRESH;
    339         }
    340 
    341         if (freshness == FRESHNESS_STALE) {
    342             D("Removing stale lockfile '%s'", lock->lock);
    343             rc = HANDLE_EINTR(unlink(lock->lock));
    344             sleep_duration_us = 0;
    345             tries++;
    346         }
    347     }
    348     D("file '%s' is already in use by another process", lock->file);
    349 
    350 Fail:
    351     if (f) {
    352         fclose(f);
    353     }
    354 
    355     if (temp_fd >= 0) {
    356         IGNORE_EINTR(close(temp_fd));
    357     }
    358 
    359     if (lock_fd >= 0) {
    360         IGNORE_EINTR(close(lock_fd));
    361     }
    362 
    363     HANDLE_EINTR(unlink(lock->lock));
    364     HANDLE_EINTR(unlink(lock->temp));
    365     return -1;
    366 #endif
    367 }
    368 
    369 void
    370 filelock_release( FileLock*  lock )
    371 {
    372     if (lock->locked) {
    373 #ifdef _WIN32
    374         path_delete_file( (char*)lock->temp );
    375         rmdir( (char*)lock->lock );
    376 #else
    377         unlink( (char*)lock->lock );
    378 #endif
    379         lock->locked = 0;
    380     }
    381 }
    382 
    383 static void
    384 filelock_atexit( void )
    385 {
    386   FileLock*  lock;
    387 
    388   for (lock = _all_filelocks; lock != NULL; lock = lock->next)
    389      filelock_release( lock );
    390 }
    391 
    392 /* create a file lock */
    393 FileLock*
    394 filelock_create( const char*  file )
    395 {
    396     int    file_len = strlen(file);
    397     int    lock_len = file_len + sizeof(LOCK_NAME);
    398 #ifdef _WIN32
    399     int    temp_len = lock_len + 1 + sizeof(PIDFILE_NAME);
    400 #else
    401     int    temp_len = file_len + sizeof(TEMP_NAME);
    402 #endif
    403     int    total_len = sizeof(FileLock) + file_len + lock_len + temp_len + 3;
    404 
    405     FileLock*  lock = malloc(total_len);
    406 
    407     lock->file = (const char*)(lock + 1);
    408     memcpy( (char*)lock->file, file, file_len+1 );
    409 
    410     lock->lock = lock->file + file_len + 1;
    411     memcpy( (char*)lock->lock, file, file_len+1 );
    412     strcat( (char*)lock->lock, LOCK_NAME );
    413 
    414     lock->temp    = (char*)lock->lock + lock_len + 1;
    415 #ifdef _WIN32
    416     snprintf( (char*)lock->temp, temp_len, "%s\\" PIDFILE_NAME, lock->lock );
    417 #else
    418     snprintf((char*)lock->temp, temp_len, "%s%s", lock->file, TEMP_NAME);
    419 #endif
    420     lock->locked = 0;
    421 
    422     if (filelock_lock(lock) < 0) {
    423         free(lock);
    424         return NULL;
    425     }
    426 
    427     lock->next     = _all_filelocks;
    428     _all_filelocks = lock;
    429 
    430     if (lock->next == NULL)
    431         atexit( filelock_atexit );
    432 
    433     return lock;
    434 }
    435