Lock the cluster config using flock(), and leaks the file descriptor used to * acquire the lock so that the file will be locked forever. * * This works because we always update nodes.conf with a new version * in-place, reopening the file, and writing to it in place (later adjusting * the length with ftruncate()). * * On success C_OK is returned, otherwise an error is logged and * the funct
| 393 | * On success C_OK is returned, otherwise an error is logged and |
| 394 | * the function returns C_ERR to signal a lock was not acquired. */ |
| 395 | int clusterLockConfig(char *filename) { |
| 396 | /* flock() does not exist on Solaris |
| 397 | * and a fcntl-based solution won't help, as we constantly re-open that file, |
| 398 | * which will release _all_ locks anyway |
| 399 | */ |
| 400 | #if !defined(__sun) |
| 401 | /* To lock it, we need to open the file in a way it is created if |
| 402 | * it does not exist, otherwise there is a race condition with other |
| 403 | * processes. */ |
| 404 | int fd = open(filename,O_WRONLY|O_CREAT|O_CLOEXEC,0644); |
| 405 | if (fd == -1) { |
| 406 | serverLog(LL_WARNING, |
| 407 | "Can't open %s in order to acquire a lock: %s", |
| 408 | filename, strerror(errno)); |
| 409 | return C_ERR; |
| 410 | } |
| 411 | |
| 412 | if (flock(fd,LOCK_EX|LOCK_NB) == -1) { |
| 413 | if (errno == EWOULDBLOCK) { |
| 414 | serverLog(LL_WARNING, |
| 415 | "Sorry, the cluster configuration file %s is already used " |
| 416 | "by a different Redis Cluster node. Please make sure that " |
| 417 | "different nodes use different cluster configuration " |
| 418 | "files.", filename); |
| 419 | } else { |
| 420 | serverLog(LL_WARNING, |
| 421 | "Impossible to lock %s: %s", filename, strerror(errno)); |
| 422 | } |
| 423 | close(fd); |
| 424 | return C_ERR; |
| 425 | } |
| 426 | /* Lock acquired: leak the 'fd' by not closing it, so that we'll retain the |
| 427 | * lock to the file as long as the process exists. |
| 428 | * |
| 429 | * After fork, the child process will get the fd opened by the parent process, |
| 430 | * we need save `fd` to `cluster_config_file_lock_fd`, so that in redisFork(), |
| 431 | * it will be closed in the child process. |
| 432 | * If it is not closed, when the main process is killed -9, but the child process |
| 433 | * (redis-aof-rewrite) is still alive, the fd(lock) will still be held by the |
| 434 | * child process, and the main process will fail to get lock, means fail to start. */ |
| 435 | server.cluster_config_file_lock_fd = fd; |
| 436 | #else |
| 437 | UNUSED(filename); |
| 438 | #endif /* __sun */ |
| 439 | |
| 440 | return C_OK; |
| 441 | } |
| 442 | |
| 443 | /* Derives our ports to be announced in the cluster bus. */ |
| 444 | void deriveAnnouncedPorts(int *announced_port, int *announced_pport, |
no test coverage detected