fix up lock breakage from bug #4245
authorKevin P. Fleming <kpfleming@digium.com>
Wed, 13 Jul 2005 19:27:09 +0000 (19:27 +0000)
committerKevin P. Fleming <kpfleming@digium.com>
Wed, 13 Jul 2005 19:27:09 +0000 (19:27 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@6125 65c4cc65-6c06-0410-ace0-fbb531ad65f3

app.c
apps/app_voicemail.c
include/asterisk/app.h

diff --git a/app.c b/app.c
index 5e5230e..5f96478 100755 (executable)
--- a/app.c
+++ b/app.c
@@ -1095,36 +1095,42 @@ int ast_separate_app_args(char *buf, char delim, char **array, int arraylen)
        return x;
 }
 
-int ast_lock_path(const char *path)
+enum AST_LOCK_RESULT ast_lock_path(const char *path)
 {
        char *s;
        char *fs;
        int res;
        int fd;
        time_t start;
+
        s = alloca(strlen(path) + 10);
        fs = alloca(strlen(path) + 20);
+
        if (!fs || !s) {
                ast_log(LOG_WARNING, "Out of memory!\n");
-               return -1;
+               return AST_LOCK_FAILURE;
        }
-       snprintf(fs, strlen(path) + 19, "%s/%s-%08x", path, ".lock", rand());
+
+       snprintf(fs, strlen(path) + 19, "%s/.lock-%08x", path, rand());
        fd = open(fs, O_WRONLY | O_CREAT | O_EXCL, 0600);
        if (fd < 0) {
-               fprintf(stderr, "Unable to create lock file: %s\n", strerror(errno));
-               return -1;
+               fprintf(stderr, "Unable to create lock file '%s': %s\n", path, strerror(errno));
+               return AST_LOCK_PATH_NOT_FOUND;
        }
        close(fd);
-       snprintf(s, strlen(path) + 9, "%s/%s", path, ".lock");
+
+       snprintf(s, strlen(path) + 9, "%s/.lock", path);
        time(&start);
        while (((res = link(fs, s)) < 0) && (errno == EEXIST) && (time(NULL) - start < 5))
                usleep(1);
-       if (res < 0) {
+       if (res) {
                ast_log(LOG_WARNING, "Failed to lock path '%s': %s\n", path, strerror(errno));
+               return AST_LOCK_TIMEOUT;
+       } else {
+               unlink(fs);
+               ast_log(LOG_DEBUG, "Locked path '%s'\n", path);
+               return AST_LOCK_SUCCESS;
        }
-       unlink(fs);
-       ast_log(LOG_DEBUG, "Locked path '%s'\n", path);
-       return res;
 }
 
 int ast_unlock_path(const char *path)
index 3e83996..0bdb40c 100755 (executable)
@@ -692,6 +692,19 @@ static int make_file(char *dest, int len, char *dir, int num)
        return snprintf(dest, len, "%s/msg%04d", dir, num);
 }
 
+/* only return failure if ast_lock_path returns 'timeout',
+   not if the path does not exist or any other reason
+*/
+static int vm_lock_path(const char *path)
+{
+       switch (ast_lock_path(path)) {
+       case AST_LOCK_TIMEOUT:
+               return -1;
+       default:
+               return 0;
+       }
+}
+
 
 #ifdef USE_ODBC_STORAGE
 static int retrieve_file(char *dir, int msgnum)
@@ -982,9 +995,9 @@ yuck:
  */
 static int count_messages(struct ast_vm_user *vmu, char *dir)
 {
-       int res = 0;
+       int res;
        
-       res = last_message_index(vmu, dir) + 1;
+       res = last_message_index(vmu, dir);
        return res >= 0 ? res + 1 : res;
 }
 
@@ -1259,23 +1272,19 @@ static int count_messages(struct ast_vm_user *vmu, char *dir)
        DIR *vmdir = NULL;
        struct dirent *vment = NULL;
 
-       if (!ast_lock_path(dir)) {
-               if ((vmdir = opendir(dir))) {
-                       while ((vment = readdir(vmdir)))
-                       {
-                               if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7,".txt",4))
-                               {
-                                       vmcount++;
-                               }
-                       }
-                       closedir(vmdir);
-               }
-               ast_unlock_path(dir);
-
-               return vmcount;
-       } else {
+       if (vm_lock_path(dir))
                return ERROR_LOCK_PATH;
+
+       if ((vmdir = opendir(dir))) {
+               while ((vment = readdir(vmdir))) {
+                       if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
+                               vmcount++;
+               }
+               closedir(vmdir);
        }
+       ast_unlock_path(dir);
+       
+       return vmcount;
 }
 
 static void rename_file(char *sfn, char *dfn)
@@ -1354,17 +1363,18 @@ static int last_message_index(struct ast_vm_user *vmu, char *dir)
 {
        int x;
        char fn[256];
-       if (!ast_lock_path(dir)) {
-               for (x = 0; x < vmu->maxmsg; x++) {
-                       make_file(fn, sizeof(fn), dir, x);
-                       if (ast_fileexists(fn, NULL, NULL) < 1)
-                               break;
-               }
-               ast_unlock_path(dir);
-               return x-1;
-       } else {
+
+       if (vm_lock_path(dir))
                return ERROR_LOCK_PATH;
+
+       for (x = 0; x < vmu->maxmsg; x++) {
+               make_file(fn, sizeof(fn), dir, x);
+               if (ast_fileexists(fn, NULL, NULL) < 1)
+                       break;
        }
+       ast_unlock_path(dir);
+
+       return x - 1;
 }
 
 static int vm_delete(char *file)
@@ -1997,24 +2007,24 @@ static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int i
 
        make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
        make_file(frompath, sizeof(frompath), fromdir, msgnum);
-       if (!ast_lock_path(topath)) {
-               recipmsgnum = 0;
-               do {
-                       make_file(topath, sizeof(topath), todir, recipmsgnum);
-                       if (!EXISTS(todir, recipmsgnum, topath, chan->language))
-                               break;
-                       recipmsgnum++;
+
+       if (vm_lock_path(topath))
+               return ERROR_LOCK_PATH;
+
+       recipmsgnum = 0;
+       do {
+               make_file(topath, sizeof(topath), todir, recipmsgnum);
+               if (!EXISTS(todir, recipmsgnum, topath, chan->language))
+                       break;
+               recipmsgnum++;
        } while (recipmsgnum < recip->maxmsg);
        if (recipmsgnum < recip->maxmsg) {
-                       COPY(fromdir, msgnum, todir, recipmsgnum, frompath, topath);
-               } else {
-                       ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
-               }
-               ast_unlock_path(topath);
-               notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
+               COPY(fromdir, msgnum, todir, recipmsgnum, frompath, topath);
        } else {
-               return ERROR_LOCK_PATH;
+               ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
        }
+       ast_unlock_path(topath);
+       notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
        
        return 0;
 }
@@ -2208,106 +2218,106 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int
                ast_copy_string(fmt, vmfmts, sizeof(fmt));
                if (!ast_strlen_zero(fmt)) {
                        msgnum = 0;
-                       if (!ast_lock_path(dir)) {
-                               if (res >= 0) {
-                                       /* Unless we're *really* silent, try to send the beep */
-                                       res = ast_streamfile(chan, "beep", chan->language);
-                                       if (!res)
-                                               res = ast_waitstream(chan, "");
-                               }
-                               do {
-                                       make_file(fn, sizeof(fn), dir, msgnum);
-                                       if (!EXISTS(dir,msgnum,fn,chan->language))
-                                               break;
-                                       msgnum++;
+
+                       if (vm_lock_path(dir)) {
+                               free_user(vmu);
+                               return ERROR_LOCK_PATH;
+                       }
+
+                       if (res >= 0) {
+                               /* Unless we're *really* silent, try to send the beep */
+                               res = ast_streamfile(chan, "beep", chan->language);
+                               if (!res)
+                                       res = ast_waitstream(chan, "");
+                       }
+                       do {
+                               make_file(fn, sizeof(fn), dir, msgnum);
+                               if (!EXISTS(dir,msgnum,fn,chan->language))
+                                       break;
+                               msgnum++;
                        } while (msgnum < vmu->maxmsg);
                        if (msgnum < vmu->maxmsg) {
-       
-                                       /* assign a variable with the name of the voicemail file */       
-                                       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
-       
-                                       /* Store information */
-                                       snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
-                                       txt = fopen(txtfile, "w+");
-                                       if (txt) {
-                                               get_date(date, sizeof(date));
-                                               fprintf(txt, 
-";\n"
-"; Message Information file\n"
-";\n"
-"[message]\n"
-"origmailbox=%s\n"
-"context=%s\n"
-"macrocontext=%s\n"
-"exten=%s\n"
-"priority=%d\n"
-"callerchan=%s\n"
-"callerid=%s\n"
-"origdate=%s\n"
-"origtime=%ld\n"
-"category=%s\n",
-               ext,
-               chan->context,
-               chan->macrocontext, 
-               chan->exten,
-               chan->priority,
-               chan->name,
-               ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
-               date, (long)time(NULL),
-               category ? category : ""); 
-                                       } else
-                                               ast_log(LOG_WARNING, "Error opening text file for output\n");
-                                       res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir);
-                                       if (res == '0') {
-                                               if (txt)
-                                                       fclose(txt);
-                                               goto transfer;
-                                       }
-                                       if (res > 0)
-                                               res = 0;
-                                       if (txt) {
-                                               fprintf(txt, "duration=%d\n", duration);
+                               /* assign a variable with the name of the voicemail file */       
+                               pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
+                               
+                               /* Store information */
+                               snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
+                               txt = fopen(txtfile, "w+");
+                               if (txt) {
+                                       get_date(date, sizeof(date));
+                                       fprintf(txt, 
+                                               ";\n"
+                                               "; Message Information file\n"
+                                               ";\n"
+                                               "[message]\n"
+                                               "origmailbox=%s\n"
+                                               "context=%s\n"
+                                               "macrocontext=%s\n"
+                                               "exten=%s\n"
+                                               "priority=%d\n"
+                                               "callerchan=%s\n"
+                                               "callerid=%s\n"
+                                               "origdate=%s\n"
+                                               "origtime=%ld\n"
+                                               "category=%s\n",
+                                               ext,
+                                               chan->context,
+                                               chan->macrocontext, 
+                                               chan->exten,
+                                               chan->priority,
+                                               chan->name,
+                                               ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
+                                               date, (long)time(NULL),
+                                               category ? category : ""); 
+                               } else
+                                       ast_log(LOG_WARNING, "Error opening text file for output\n");
+                               res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir);
+                               if (res == '0') {
+                                       if (txt)
                                                fclose(txt);
-                                       }
+                                       goto transfer;
+                               }
+                               if (res > 0)
+                                       res = 0;
+                               if (txt) {
+                                       fprintf(txt, "duration=%d\n", duration);
+                                       fclose(txt);
+                               }
                                
-                                       if (duration < vmminmessage) {
-                                               if (option_verbose > 2) 
-                                                       ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
-                                               DELETE(dir,msgnum,fn);
-                                               /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
-                                               goto leave_vm_out;
-                                       }
-                                       /* Are there to be more recipients of this message? */
-                                       while (tmpptr) {
-                                               struct ast_vm_user recipu, *recip;
-                                               char *exten, *context;
-       
-                                               exten = strsep(&tmpptr, "&");
-                                               context = strchr(exten, '@');
-                                               if (context) {
-                                                       *context = '\0';
-                                                       context++;
-                                               }
-                                               if ((recip = find_user(&recipu, context, exten))) {
-                                                       copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
-                                                       free_user(recip);
-                                               }
+                               if (duration < vmminmessage) {
+                                       if (option_verbose > 2) 
+                                               ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
+                                       DELETE(dir,msgnum,fn);
+                                       /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
+                                       goto leave_vm_out;
+                               }
+                               /* Are there to be more recipients of this message? */
+                               while (tmpptr) {
+                                       struct ast_vm_user recipu, *recip;
+                                       char *exten, *context;
+                                       
+                                       exten = strsep(&tmpptr, "&");
+                                       context = strchr(exten, '@');
+                                       if (context) {
+                                               *context = '\0';
+                                               context++;
                                        }
-                                       if (ast_fileexists(fn, NULL, NULL)) {
-                                               notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
-                                               STORE(dir, msgnum);
-                                               DISPOSE(dir, msgnum);
+                                       if ((recip = find_user(&recipu, context, exten))) {
+                                               copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
+                                               free_user(recip);
                                        }
-                               } else {
-                                       ast_unlock_path(dir);
-                                       res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
-                                       if (!res)
-                                               res = ast_waitstream(chan, "");
-                                       ast_log(LOG_WARNING, "No more messages possible\n");
+                               }
+                               if (ast_fileexists(fn, NULL, NULL)) {
+                                       notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
+                                       STORE(dir, msgnum);
+                                       DISPOSE(dir, msgnum);
                                }
                        } else {
-                               free_user(vmu);
-                               return ERROR_LOCK_PATH;
+                               ast_unlock_path(dir);
+                               res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
+                               if (!res)
+                                       res = ast_waitstream(chan, "");
+                               ast_log(LOG_WARNING, "No more messages possible\n");
                        }
                } else
                        ast_log(LOG_WARNING, "No format for saving voicemail?\n");
@@ -2316,8 +2326,7 @@ leave_vm_out:
        } else {
                ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
                /*Send the call to n+101 priority, where n is the current priority*/
-               if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num))
-                       chan->priority+=100;
+               ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
        }
 
        return res;
@@ -2332,24 +2341,23 @@ static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
        char sfn[256];
        char dfn[256];
 
-       if (!ast_lock_path(dir)) {
-               for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
-                       make_file(sfn, sizeof(sfn), dir, x);
-                       if (EXISTS(dir, x, sfn, NULL)) {
-       
-                               if(x != dest) {
-                                       make_file(dfn, sizeof(dfn), dir, dest);
-                                       RENAME(dir, x, dir, dest, sfn, dfn);
-                               }
-       
-                               dest++;
+       if (vm_lock_path(dir))
+               return ERROR_LOCK_PATH;
+
+       for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
+               make_file(sfn, sizeof(sfn), dir, x);
+               if (EXISTS(dir, x, sfn, NULL)) {
+                       
+                       if(x != dest) {
+                               make_file(dfn, sizeof(dfn), dir, dest);
+                               RENAME(dir, x, dir, dest, sfn, dfn);
                        }
+                       
+                       dest++;
                }
-               ast_unlock_path(dir);
-       } else {
-               return ERROR_LOCK_PATH;
        }
-       
+       ast_unlock_path(dir);
+
        return 0;
 }
 
@@ -2371,23 +2379,23 @@ static int save_to_folder(struct ast_vm_user *vmu, char *dir, int msg, char *con
        make_file(sfn, sizeof(sfn), dir, msg);
        make_dir(ddir, sizeof(ddir), context, username, dbox);
        mkdir(ddir, 0700);
-       if (!ast_lock_path(ddir)) {
-               for (x = 0; x < vmu->maxmsg; x++) {
-                       make_file(dfn, sizeof(dfn), ddir, x);
-                       if (!EXISTS(ddir, x, dfn, NULL))
-                               break;
-               }
-               if (x >= vmu->maxmsg) {
-                       ast_unlock_path(ddir);
-                       return -1;
-               }
-               if (strcmp(sfn, dfn)) {
-                       COPY(dir, msg, ddir, x, sfn, dfn);
-               }
-               ast_unlock_path(ddir);
-       } else {
+
+       if (vm_lock_path(ddir))
                return ERROR_LOCK_PATH;
+
+       for (x = 0; x < vmu->maxmsg; x++) {
+               make_file(dfn, sizeof(dfn), ddir, x);
+               if (!EXISTS(ddir, x, dfn, NULL))
+                       break;
        }
+       if (x >= vmu->maxmsg) {
+               ast_unlock_path(ddir);
+               return -1;
+       }
+       if (strcmp(sfn, dfn)) {
+               COPY(dir, msg, ddir, x, sfn, dfn);
+       }
+       ast_unlock_path(ddir);
        
        return 0;
 }
@@ -3526,45 +3534,49 @@ static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
 {
        int x;
        int res = 0;
-       if (vms->lastmsg > -1) { 
-               /* Get the deleted messages fixed */ 
-               if (!ast_lock_path(vms->curdir)) {
-                       vms->curmsg = -1; 
-               for (x=0;x < vmu->maxmsg;x++) { 
-                               if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
-                                       /* Save this message.  It's not in INBOX or hasn't been heard */ 
-                                       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
-                                       if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
-                                               break;
-                                       vms->curmsg++; 
-                                       make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
-                                       if (strcmp(vms->fn, vms->fn2)) { 
-                                               RENAME(vms->curdir, x, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
-                                       } 
-                               } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
-                                       /* Move to old folder before deleting */ 
-                                       res = save_to_folder(vmu, vms->curdir, x, vmu->context, vms->username, 1);
-                                       if (res == ERROR_LOCK_PATH) {
-                                               /* If save failed do not delete the message */
-                                               vms->deleted[x] = 0;
-                                               vms->heard[x] = 0;
-                                               --x;
-                                       } 
-                               } 
+
+       if (vms->lastmsg <= -1)
+               goto done;
+
+       /* Get the deleted messages fixed */ 
+       if (vm_lock_path(vms->curdir))
+               return ERROR_LOCK_PATH;
+       
+       vms->curmsg = -1; 
+       for (x=0;x < vmu->maxmsg;x++) { 
+               if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
+                       /* Save this message.  It's not in INBOX or hasn't been heard */ 
+                       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
+                       if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
+                               break;
+                       vms->curmsg++; 
+                       make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
+                       if (strcmp(vms->fn, vms->fn2)) { 
+                               RENAME(vms->curdir, x, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
                        } 
-                       for (x = vms->curmsg + 1; x <= vmu->maxmsg; x++) { 
-                               make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
-                               if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
-                                       break;
-                               DELETE(vms->curdir, x, vms->fn);
+               } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
+                       /* Move to old folder before deleting */ 
+                       res = save_to_folder(vmu, vms->curdir, x, vmu->context, vms->username, 1);
+                       if (res == ERROR_LOCK_PATH) {
+                               /* If save failed do not delete the message */
+                               vms->deleted[x] = 0;
+                               vms->heard[x] = 0;
+                               --x;
                        } 
-                       ast_unlock_path(vms->curdir);
-               } else {
-                       return ERROR_LOCK_PATH;
-               }
+               } 
+       } 
+       for (x = vms->curmsg + 1; x <= vmu->maxmsg; x++) { 
+               make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
+               if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
+                       break;
+               DELETE(vms->curdir, x, vms->fn);
        } 
+       ast_unlock_path(vms->curdir);
+
+done:
        memset(vms->deleted, 0, sizeof(vms->deleted)); 
        memset(vms->heard, 0, sizeof(vms->heard)); 
+
        return 0;
 }
 
index 8d571e2..4ab7ef2 100755 (executable)
@@ -139,8 +139,19 @@ int ast_play_and_record(struct ast_channel *chan, const char *playfile, const ch
 /*   permitted silence time in milliseconds of 'maxsilence' under 'silencethreshold' or use '-1' for either or both parameters for defaults. */
 int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime_sec, char *fmt, int *duration, int beep, int silencethreshold, int maxsilence_ms);
 
-/* Lock a path */
-int ast_lock_path(const char *path);
+enum AST_LOCK_RESULT {
+       AST_LOCK_SUCCESS = 0,
+       AST_LOCK_TIMEOUT = -1,
+       AST_LOCK_PATH_NOT_FOUND = -2,
+       AST_LOCK_FAILURE = -3,
+};
+
+/*
+ * \brief Lock a filesystem path.
+ * \param path the path to be locked
+ * \return one of AST_LOCK_RESULT values
+ */
+enum AST_LOCK_RESULT ast_lock_path(const char *path);
 
 /* Unlock a path */
 int ast_unlock_path(const char *path);