• 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/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