From bc2a8379536d2ce33ba0d203585a5a2bd96a83a1 Mon Sep 17 00:00:00 2001 From: Andrea Mazzoleni Date: Mon, 17 Jun 2013 23:50:26 +0200 Subject: [PATCH] Adds pid file support. --- HISTORY | 3 +++ configure.ac | 4 ++-- portable.h | 11 ++++++++++ snapraid.c | 60 ++++++++++++++++++++++++++++++++++++++++---------- state.c | 7 ++++++ state.h | 1 + util.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ util.h | 15 +++++++++++++ 8 files changed, 149 insertions(+), 14 deletions(-) diff --git a/HISTORY b/HISTORY index 31c448cf..854673f1 100644 --- a/HISTORY +++ b/HISTORY @@ -9,6 +9,9 @@ SnapRAID HISTORY * Added RAID5/RAID6 mmx and sse2 implementations with unrolling by 4. They are a little faster than the previous unroll by 2. * Added a hash seed to make harder intentional collision attacks. + * Added a pid file to avoid multiple running instances on the same array. + The file is named as the first content file adding the .pid extension. + There is also the undocumented --test-skip-pid to avoid to check it. 3.1 2013/5 ========== diff --git a/configure.ac b/configure.ac index 66f9f63c..7b6af848 100644 --- a/configure.ac +++ b/configure.ac @@ -23,7 +23,7 @@ AC_HEADER_STDC AC_HEADER_DIRENT AC_HEADER_TIME AC_CHECK_HEADERS([fcntl.h stddef.h stdint.h stdlib.h string.h limits.h]) -AC_CHECK_HEADERS([sys/time.h unistd.h getopt.h fnmatch.h io.h inttypes.h byteswap.h]) +AC_CHECK_HEADERS([sys/time.h sys/file.h unistd.h getopt.h fnmatch.h io.h inttypes.h byteswap.h]) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST @@ -49,7 +49,7 @@ AC_SYS_LARGEFILE AC_CHECK_FUNCS([memset strchr strerror strrchr mkdir gettimeofday strtoul]) AC_CHECK_FUNCS([getopt getopt_long snprintf vsnprintf]) AC_CHECK_FUNCS([ftruncate fallocate fsync access posix_fallocate posix_fadvise pread pwrite getc_unlocked ferror_unlocked fnmatch]) -AC_CHECK_FUNCS([futimes futimens futimesat fstatat]) +AC_CHECK_FUNCS([futimes futimens futimesat fstatat flock]) dnl Checks for architecture AC_C_BIGENDIAN diff --git a/portable.h b/portable.h index 9ce0cafb..a976f3bc 100644 --- a/portable.h +++ b/portable.h @@ -66,6 +66,10 @@ #endif #endif +#if HAVE_SYS_FILE_H +#include +#endif + #if HAVE_DIRENT_H #include #define NAMLEN(dirent) strlen((dirent)->d_name) @@ -120,6 +124,13 @@ #define SWITCH_GETOPT_LONG(a,b) b #endif +/** + * Enable PID file support. + */ +#if HAVE_FLOCK && HAVE_FTRUNCATE +#define HAVE_PIDFILE 1 +#endif + /** * Includes specific support for Windows or Linux. */ diff --git a/snapraid.c b/snapraid.c index 9803c01d..33770852 100644 --- a/snapraid.c +++ b/snapraid.c @@ -84,7 +84,7 @@ void usage(void) #define OPT_TEST_SKIP_SEQUENTIAL 263 #define OPT_TEST_FORCE_MURMUR3 264 #define OPT_TEST_FORCE_SPOOKY2 265 - +#define OPT_TEST_SKIP_PID 266 #if HAVE_GETOPT_LONG struct option long_options[] = { @@ -139,6 +139,9 @@ struct option long_options[] = { /* Force Spooky2 hash */ { "test-force-spooky2", 0, 0, OPT_TEST_FORCE_SPOOKY2 }, + /* Skip the use of pid file */ + { "test-skip-pid", 0, 0, OPT_TEST_SKIP_PID }, + { 0, 0, 0, 0 } }; #endif @@ -185,6 +188,7 @@ int main(int argc, char* argv[]) int test_skip_device; int test_force_murmur3; int test_force_spooky2; + int test_skip_pid; const char* conf; struct snapraid_state state; int operation; @@ -198,6 +202,7 @@ int main(int argc, char* argv[]) const char* command; const char* import; const char* log; + int pid; os_init(); @@ -220,6 +225,7 @@ int main(int argc, char* argv[]) test_skip_device = 0; test_force_murmur3 = 0; test_force_spooky2 = 0; + test_skip_pid = 0; blockstart = 0; blockcount = 0; tommy_list_init(&filterlist_file); @@ -227,6 +233,7 @@ int main(int argc, char* argv[]) filter_missing = 0; import = 0; log = 0; + pid = 0; opterr = 0; while ((c = @@ -347,6 +354,9 @@ int main(int argc, char* argv[]) case OPT_TEST_FORCE_SPOOKY2 : test_force_spooky2 = 1; break; + case OPT_TEST_SKIP_PID : + test_skip_pid = 1; + break; default: fprintf(stderr, "Unknown option '%c'\n", (char)c); exit(EXIT_FAILURE); @@ -427,6 +437,30 @@ int main(int argc, char* argv[]) raid_init(); + if (!test_skip_self) + selftest(gui); + + state_init(&state); + + /* read the configuration file */ + state_config(&state, conf, command, verbose, gui, force_zero, force_empty, force_uuid, find_by_name, test_expect_unrecoverable, test_expect_recoverable, test_skip_sign, test_skip_fallocate, test_skip_sequential, test_skip_device, test_force_murmur3, test_force_spooky2); + +#if HAVE_PIDFILE + /* create the pid file */ + if (!test_skip_pid) { + pid = pid_lock(state.pidfile); + if (pid == -1) { + if (errno != EWOULDBLOCK) { + fprintf(stderr, "Error creating the pid file '%s'. %s.\n", state.pidfile, strerror(errno)); + } else { + fprintf(stderr, "The pid file '%s' is already locked!\n", state.pidfile); + fprintf(stderr, "SnapRAID is already in use!\n"); + } + exit(EXIT_FAILURE); + } + } +#endif + /* open the log file */ if (log == 0) log = "2"; @@ -442,13 +476,6 @@ int main(int argc, char* argv[]) } } - if (!test_skip_self) - selftest(gui); - - state_init(&state); - - state_config(&state, conf, command, verbose, gui, force_zero, force_empty, force_uuid, find_by_name, test_expect_unrecoverable, test_expect_recoverable, test_skip_sign, test_skip_fallocate, test_skip_sequential, test_skip_device, test_force_murmur3, test_force_spooky2); - if (operation == OPERATION_DIFF) { state_read(&state); @@ -545,10 +572,6 @@ int main(int argc, char* argv[]) } } - state_done(&state); - tommy_list_foreach(&filterlist_file, (tommy_foreach_func*)filter_free); - tommy_list_foreach(&filterlist_disk, (tommy_foreach_func*)filter_free); - /* close log file */ if (stdlog != stdout && stdlog != stderr) { if (fclose(stdlog) != 0) { @@ -557,6 +580,19 @@ int main(int argc, char* argv[]) } } +#if HAVE_PIDFILE + if (!test_skip_pid) { + if (pid_unlock(pid, state.pidfile) == -1) { + fprintf(stderr, "Error closing the pid file '%s'. %s.\n", state.pidfile, strerror(errno)); + exit(EXIT_FAILURE); + } + } +#endif + + state_done(&state); + tommy_list_foreach(&filterlist_file, (tommy_foreach_func*)filter_free); + tommy_list_foreach(&filterlist_disk, (tommy_foreach_func*)filter_free); + os_done(); return EXIT_SUCCESS; diff --git a/state.c b/state.c index a478d731..3a41ecc8 100644 --- a/state.c +++ b/state.c @@ -45,6 +45,7 @@ void state_init(struct snapraid_state* state) state->qarity_device = 0; state->pool[0] = 0; state->pool_device = 0; + state->pidfile[0] = 0; state->level = 1; /* default is the lowest protection */ tommy_list_init(&state->disklist); @@ -462,6 +463,12 @@ void state_config(struct snapraid_state* state, const char* path, const char* co content = content_alloc(buffer, st.st_dev); tommy_list_insert_tail(&state->contentlist, &content->node, content); + + /* if not set pid file, set it as the first content file */ + if (!state->pidfile[0]) { + pathcpy(state->pidfile, sizeof(state->pidfile), buffer); + pathcat(state->pidfile, sizeof(state->pidfile), ".pid"); + } } else if (strcmp(tag, "disk") == 0) { char dir[PATH_MAX]; char device[PATH_MAX]; diff --git a/state.h b/state.h index 4184ef00..34e67f36 100644 --- a/state.h +++ b/state.h @@ -50,6 +50,7 @@ struct snapraid_state { uint64_t qarity_device; /**< Device identifier of the qarity. */ char pool[PATH_MAX]; /**< Path of the pool tree. */ uint64_t pool_device; /**< Device identifier of the pool. */ + char pidfile[PATH_MAX]; /**< Path of the pid file to use. */ unsigned char hashseed[HASH_SIZE]; /**< Hash seed. */ unsigned level; /**< Number of parity levels. 1 for RAID5, 2 for RAID6. */ unsigned hash; /**< Hash kind used. */ diff --git a/util.c b/util.c index 8381bc1f..14de2d3c 100644 --- a/util.c +++ b/util.c @@ -866,3 +866,65 @@ void randomize(void* void_ptr, unsigned size) ptr[i] = rand(); } +/****************************************************************************/ +/* pid */ + +#if HAVE_PIDFILE +int pid_lock(const char* file) +{ + char buffer[64]; + ssize_t len; + int f; + + f = open(file, O_CREAT | O_WRONLY, 0600); + if (f == -1) { + return -1; + } + + /* exclusive lock, not blocking */ + if (flock(f, LOCK_EX | LOCK_NB) == -1) { + close(f); + return -1; + } + + /* write the pid into the file */ + snprintf(buffer, sizeof(buffer), "%ld", (long)getpid()); + + len = strlen(buffer); + if (write(f, buffer, len) != len) { + close(f); + return -1; + } + + if (ftruncate(f, len) == -1) { + close(f); + return -1; + } + + return 0; +} +#endif + +#if HAVE_PIDFILE +int pid_unlock(int f, const char* file) +{ + /* + * Remove file before closing it. + * This opens a race course in case another process already opened it. + * but has not yet failed the next flock() call. + * But removing it after the close, opens a similar race course. + * It seems that deleting a pid file is something just impossible to do it right. + */ + if (remove(file) == -1) { + close(f); + return -1; + } + + if (close(f) == -1) { + return -1; + } + + return 0; +} +#endif + diff --git a/util.h b/util.h index 93621425..e3dd4cc3 100644 --- a/util.h +++ b/util.h @@ -486,5 +486,20 @@ static inline oathash_t oathashm(oathash_t hash, const unsigned char* value, uns return hash; } +/****************************************************************************/ +/* pid */ + +/** + * Creates and locks the pid file. + * Returns -1 on error, otherwise it's the file handle to pass to pid_unlock(). + */ +int pid_lock(const char* file); + +/** + * Unlocks the pid file. + * Returns -1 on error. + */ +int pid_unlock(int f, const char* file); + #endif