Index: doc/asterisk-conf.txt =================================================================== --- doc/asterisk-conf.txt (revision 44249) +++ doc/asterisk-conf.txt (working copy) @@ -69,7 +69,11 @@ ; when off, sound files are searched as // ; when on, sound files are search as // ; (only affects relative paths for sound files) +lockmode = lockfile | flock ; Locking mode for voicemail + ; lockfile: default, for normal use + ; flock: for where the lockfile locking method doesn't work, eg. on SMB/CIFS mounts + [files] ; Changing the following lines may compromise your security ; Asterisk.ctl is the pipe that is used to connect the remote CLI Index: include/asterisk/app.h =================================================================== --- include/asterisk/app.h (revision 44249) +++ include/asterisk/app.h (working copy) @@ -190,7 +190,19 @@ AST_LOCK_FAILURE = -3, }; +/* Type of locking to use in ast_lock_path / ast_unlock_path */ +enum AST_LOCK_TYPE { + AST_LOCK_TYPE_LOCKFILE = 0, + AST_LOCK_TYPE_FLOCK = 1, +}; + /*! + * \brief Set the type of locks used by ast_lock_path() + * \param type the locking type to use + */ +void ast_set_lock_type(enum AST_LOCK_TYPE type); + +/*! * \brief Lock a filesystem path. * \param path the path to be locked * \return one of \ref AST_LOCK_RESULT values Index: main/app.c =================================================================== --- main/app.c (revision 44249) +++ main/app.c (working copy) @@ -37,6 +37,7 @@ #include #include #include +#include #include #include "asterisk/channel.h" @@ -52,6 +53,8 @@ #define MAX_OTHER_FORMATS 10 +/* The lock type used by ast_lock_path() / ast_unlock_path() */ +static enum AST_LOCK_TYPE ast_lock_type = AST_LOCK_TYPE_LOCKFILE; /* ! This function presents a dialtone and reads an extension into 'collect' @@ -898,7 +901,7 @@ return argc; } -enum AST_LOCK_RESULT ast_lock_path(const char *path) +static enum AST_LOCK_RESULT ast_lock_path_lockfile(const char *path) { char *s; char *fs; @@ -936,7 +939,7 @@ } } -int ast_unlock_path(const char *path) +static int ast_unlock_path_lockfile(const char *path) { char *s; int res; @@ -956,6 +959,178 @@ return res; } +struct path_lock { + AST_LIST_ENTRY(path_lock) le; + int fd; + char *path; +}; + +static AST_LIST_HEAD_STATIC(path_lock_list, path_lock); + +static void path_lock_destroy(struct path_lock *obj) +{ + if (obj->fd >= 0) + close(obj->fd); + if (obj->path) + free(obj->path); + free(obj); +} + +static enum AST_LOCK_RESULT ast_lock_path_flock(const char *path) +{ + char *fs; + int res; + int fd; + time_t start; + struct path_lock *pl; + struct stat st, ost; + + fs = alloca(strlen(path) + 20); + + if (!fs) { + ast_log(LOG_WARNING, "Out of memory!\n"); + return AST_LOCK_FAILURE; + } + + snprintf(fs, strlen(path) + 19, "%s/lock", path); + if (lstat(fs, &st) == 0) { + if ((st.st_mode & S_IFMT) == S_IFLNK) { + ast_log(LOG_WARNING, "Unable to create lock file " + "'%s': it's already a symbolic link\n", + fs); + return AST_LOCK_FAILURE; + } + if (st.st_nlink > 1) { + ast_log(LOG_WARNING, "Unable to create lock file " + "'%s': %u hard links exist\n", + fs, st.st_nlink); + return AST_LOCK_FAILURE; + } + } + fd = open(fs, O_WRONLY | O_CREAT, 0600); + if (fd < 0) { + ast_log(LOG_WARNING, "Unable to create lock file '%s': %s\n", + fs, strerror(errno)); + return AST_LOCK_PATH_NOT_FOUND; + } + pl = ast_calloc(1, sizeof(*pl)); + if (!pl) { + ast_log(LOG_WARNING, "Out of memory!\n"); + /* We don't unlink the lock file here, on the possibility that + * someone else created it - better to leave a little mess + * than create a big one by destroying someone elses lock + * and causing something to be corrupted. + */ + close(fd); + return AST_LOCK_FAILURE; + } + pl->fd = fd; + pl->path = strdup(path); + + time(&start); + while (((res = flock(pl->fd, LOCK_EX | LOCK_NB)) < 0) && + (errno == EWOULDBLOCK) && (time(NULL) - start < 5)) + usleep(1000); + if (res) { + ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n", + path, strerror(errno)); + /* No unlinking of lock done, since we tried and failed to + * flock() it. + */ + path_lock_destroy(pl); + return AST_LOCK_TIMEOUT; + } + + /* Check for the race where the file is recreated or deleted out from + * underneath us. + */ + if (lstat(fs, &st) != 0 && fstat(pl->fd, &ost) != 0 && + st.st_dev != ost.st_dev && + st.st_ino != ost.st_ino) { + ast_log(LOG_WARNING, "Unable to create lock file '%s': " + "file changed underneath us\n", fs); + path_lock_destroy(pl); + return AST_LOCK_FAILURE; + } + + /* Success: file created, flocked, and is the one we started with */ + AST_LIST_LOCK(&path_lock_list); + AST_LIST_INSERT_TAIL(&path_lock_list, pl, le); + AST_LIST_UNLOCK(&path_lock_list); + + ast_log(LOG_DEBUG, "Locked path '%s'\n", path); + + return AST_LOCK_SUCCESS; +} + +static int ast_unlock_path_flock(const char *path) +{ + char *s; + struct path_lock *p; + + s = alloca(strlen(path) + 20); + if (!s) + return -1; + + AST_LIST_LOCK(&path_lock_list); + AST_LIST_TRAVERSE_SAFE_BEGIN(&path_lock_list, p, le) { + if (!strcmp(p->path, path)) { + AST_LIST_REMOVE_CURRENT(&path_lock_list, le); + break; + } + } + AST_LIST_TRAVERSE_SAFE_END; + AST_LIST_UNLOCK(&path_lock_list); + + if (p) { + snprintf(s, strlen(path) + 19, "%s/lock", path); + unlink(s); + path_lock_destroy(p); + ast_log(LOG_DEBUG, "Unlocked path '%s'\n", path); + } else + ast_log(LOG_DEBUG, "Failed to unlock path '%s': " + "lock not found\n", path); + + return 0; +} + +void ast_set_lock_type(enum AST_LOCK_TYPE type) +{ + ast_lock_type = type; +} + +enum AST_LOCK_RESULT ast_lock_path(const char *path) +{ + enum AST_LOCK_RESULT r = AST_LOCK_FAILURE; + + switch (ast_lock_type) { + case AST_LOCK_TYPE_LOCKFILE: + r = ast_lock_path_lockfile(path); + break; + case AST_LOCK_TYPE_FLOCK: + r = ast_lock_path_flock(path); + break; + } + + return r; +} + +int ast_unlock_path(const char *path) +{ + int r = 0; + + switch (ast_lock_type) { + case AST_LOCK_TYPE_LOCKFILE: + r = ast_unlock_path_lockfile(path); + break; + case AST_LOCK_TYPE_FLOCK: + r = ast_unlock_path_flock(path); + break; + } + + return r; +} + int ast_record_review(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime, const char *fmt, int *duration, const char *path) { int silencethreshold = 128; Index: main/asterisk.c =================================================================== --- main/asterisk.c (revision 44249) +++ main/asterisk.c (working copy) @@ -2316,6 +2316,13 @@ ast_copy_string(ast_config_AST_SYSTEM_NAME, v->value, sizeof(ast_config_AST_SYSTEM_NAME)); } else if (!strcasecmp(v->name, "languageprefix")) { ast_language_is_prefix = ast_true(v->value); + } else if (!strcasecmp(v->name, "lockmode")) { + if (!strcasecmp(v->value, "lockfile")) { + ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE); + } else if (!strcasecmp(v->value, "flock")) { + ast_set_lock_type(AST_LOCK_TYPE_FLOCK); + } else + ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE); } } ast_config_destroy(cfg);