summaryrefslogtreecommitdiff
path: root/main/app.c
diff options
context:
space:
mode:
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;