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
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, _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
filelock_release(FileLock * lock)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
filelock_atexit(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*
filelock_create(const char * file)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