aboutsummaryrefslogtreecommitdiffstats
path: root/main/app.c
diff options
context:
space:
mode:
authorrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2007-08-28 16:28:26 +0000
committerrussell <russell@f38db490-d61c-443f-a65b-d21fe96a405b>2007-08-28 16:28:26 +0000
commit5525846c14b619c422b4b1373a2b600438de9c96 (patch)
tree7c4fc12b586885d85fc9342e07f133056bb5f621 /main/app.c
parent37e6efff7b51c5ac5e75a6c29db2470cf67d0911 (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: http://svn.digium.com/svn/asterisk/trunk@81233 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'main/app.c')
-rw-r--r--main/app.c182
1 files changed, 172 insertions, 10 deletions
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;