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 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 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 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* 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