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