summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Bryant <russell@russellbryant.com>2007-08-28 16:28:26 +0000
committerRussell Bryant <russell@russellbryant.com>2007-08-28 16:28:26 +0000
commit43e9b0f67cb87f430b71a646f1283764d94fdd09 (patch)
tree7c4fc12b586885d85fc9342e07f133056bb5f621
parent01490ecd70cfad4f7d6719a326c2a3fed71bd4eb (diff)
(closes issue #7852)
Reported by: nic_bellamy Patches: 2006-10-03_svn_44249_voicemail_lockmode_v3.patch uploaded by nic_bellamy (license 213) Add support for configurable file locking methods. The default is "lockfile", which is the old behavior. There is an additional option, "flock", which is intended for use in situations where the lockfile method will not work, such as with SMB/CIFS mounts. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@81233 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r--CHANGES5
-rw-r--r--doc/tex/asterisk-conf.tex8
-rw-r--r--include/asterisk/app.h12
-rw-r--r--main/app.c182
-rw-r--r--main/asterisk.c10
5 files changed, 206 insertions, 11 deletions
diff --git a/CHANGES b/CHANGES
index 3534ab9b9..8ca22c855 100644
--- a/CHANGES
+++ b/CHANGES
@@ -125,6 +125,11 @@ Voicemail Changes
* Added support for storage of greetings using an IMAP server
* Added ability to customize forward, reverse, stop, and pause keys for message playback
* SMDI is now enabled in voicemail using the smdienable option.
+ * A "lockmode" option has been added to asterisk.conf to configure the file
+ locking method used for voicemail, and potentially other things in the
+ future. The default is the old behavior, lockfile. However, there is a
+ new method, "flock", that uses a different method for situations where the
+ lockfile will not work, such as on SMB/CIFS mounts.
Queue changes
-------------
diff --git a/doc/tex/asterisk-conf.tex b/doc/tex/asterisk-conf.tex
index eef727960..e25bc996f 100644
--- a/doc/tex/asterisk-conf.tex
+++ b/doc/tex/asterisk-conf.tex
@@ -118,6 +118,12 @@ systemname = <a_string>
; (only affects relative paths for sound files)
languageprefix = yes | no
+; Locking mode for voicemail
+; - lockfile: default, for normal use
+; - flock: for where the lockfile locking method doesn't work
+; eh. on SMB/CIFS mounts
+lockmode = lockfile | flock
+
[files]
; Changing the following lines may compromise your security
@@ -132,4 +138,4 @@ languageprefix = yes | no
;astctl = asterisk.ctl
\end{verbatim}
-\end{astlisting} \ No newline at end of file
+\end{astlisting}
diff --git a/include/asterisk/app.h b/include/asterisk/app.h
index 4bee632b9..6e5d993c3 100644
--- a/include/asterisk/app.h
+++ b/include/asterisk/app.h
@@ -202,6 +202,18 @@ enum AST_LOCK_RESULT {
AST_LOCK_FAILURE = -3,
};
+/*! \brief 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
diff --git a/main/app.c b/main/app.c
index d3304b9fd..a026f2b8b 100644
--- a/main/app.c
+++ b/main/app.c
@@ -37,6 +37,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/file.h>
#include <regex.h>
#include "asterisk/channel.h"
@@ -158,6 +159,8 @@ int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxl
return res;
}
+/* The lock type used by ast_lock_path() / ast_unlock_path() */
+static enum AST_LOCK_TYPE ast_lock_type = AST_LOCK_TYPE_LOCKFILE;
int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd)
{
@@ -1026,7 +1029,7 @@ unsigned int ast_app_separate_args(char *buf, char delim, char **array, int arra
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;
@@ -1035,10 +1038,8 @@ enum AST_LOCK_RESULT ast_lock_path(const char *path)
int lp = strlen(path);
time_t start;
- if (!(s = alloca(lp + 10)) || !(fs = alloca(lp + 20))) {
- ast_log(LOG_WARNING, "Out of memory!\n");
- return AST_LOCK_FAILURE;
- }
+ s = alloca(lp + 10);
+ fs = alloca(lp + 20);
snprintf(fs, strlen(path) + 19, "%s/.lock-%08lx", path, ast_random());
fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, AST_FILE_MODE);
@@ -1064,15 +1065,12 @@ enum AST_LOCK_RESULT ast_lock_path(const char *path)
}
}
-int ast_unlock_path(const char *path)
+static int ast_unlock_path_lockfile(const char *path)
{
char *s;
int res;
- if (!(s = alloca(strlen(path) + 10))) {
- ast_log(LOG_WARNING, "Out of memory!\n");
- return -1;
- }
+ s = alloca(strlen(path) + 10);
snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock");
@@ -1085,6 +1083,170 @@ int ast_unlock_path(const char *path)
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);
+
+ 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, (unsigned int) 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) {
+ /* 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_debug(1, "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);
+
+ 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;
diff --git a/main/asterisk.c b/main/asterisk.c
index 052bf629d..86d300e35 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -2453,6 +2453,16 @@ static void ast_readconfig(void)
}
} 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_log(LOG_WARNING, "'%s' is not a valid setting for the lockmode option, "
+ "defaulting to 'lockfile'\n", v->value);
+ ast_set_lock_type(AST_LOCK_TYPE_LOCKFILE);
+ }
#if defined(HAVE_SYSINFO)
} else if (!strcasecmp(v->name, "minmemfree")) {
/* specify the minimum amount of free memory to retain. Asterisk should stop accepting new calls