• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
filelock_lock(FileLock * lock)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
filelock_release(FileLock * lock)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
filelock_atexit(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*
filelock_create(const char * file)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