Voicemail: INBOX locking issue

Hi everyone.

I met a problem some time ago, that when callers were leaving voice messages it wasn’t saved to DB, nor anywere.

Through a brief research I found several (in fact - two) “.lock” files in //INBOX OS directories (most commonly used). And, also, found tons of “Couldn’t lock directory …/INBOX. Voicemail will be lost.” in asterisk-log output.

So, just removing “.lock” files solves the problem. But… In fact, it solves the problem for some time only and, which is worse, we lost all voicemails leaved from the time “.lock”-file appeared.

First, using search, it seems that such a problem happens even with 1.8.* Asterisk (by the way, we are using 1.4.21.1), just maybe with different circumstances.

Next I dived into sources, and learned that asterisk uses “.lock” files to lock directories for internal needs (for example, to count messages) and, when anything is going Ok, Asterisk unlocks directories by removing those files. But when some errors occures (I couldn’t create erroneous situations by myself, thit is sad, but people in Internets say, that errors is possibly take place during leaving voicemail), the directory may not become unlocked and then noone tries to check for such horrible situation to solve it and directory stays locked, maybe, forever (or I’m wrong?)

So, at the end, we can’t afford loosing voicemails, and, as temporary solution, it was decided to make a patch in app_voicemail.c to copy files from //tmp before asterisk manages to delete them.

This is my code fragment, in app_voicemail.c, in function leave_voicemail (I hope you’ll easily find that place in vanilla sources), my code is between “this is where my code starts” and “this is where my code ends” :

               if (txt) {
                        if (duration < vmminmessage) {
                                fclose(txt);
                                if (option_verbose > 2)
                                        ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration
                                ast_filedelete(tmptxtfile, NULL);
                                unlink(tmptxtfile);
                        } else {
                                fprintf(txt, "duration=%d\n", duration);
                                fclose(txt);
                                if (vm_lock_path(dir)) {
                                        ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);

/* this is where my code starts */
                                        /* There we need to back up files which asterisk is supposing to delete right under */

                                        char backupdir[PATH_MAX]; /* for creating failed_vm directory in VM_SPOOL_DIR */
                                        mode_t backup_mode = VOICEMAIL_DIR_MODE; /* just copied from create_dirpath */
                                        char backupfile[PATH_MAX]; /* for backing up file in case of locked INBOX dir */
                                        char bak_fn[PATH_MAX]="\0";

                                        snprintf(backupdir, sizeof(backupdir), "%s%s", VM_SPOOL_DIR, "failed_vm");

                                        /* It's easier just to try to make it than to check for its existence */
                                        mkdir(backupdir, backup_mode);
                                        /* dir prepared (i hope =) ), if not, we'll just fail to copy later, so no difference in result */

                                        /* now we need in some manner copy only filename from tmptxtfile */
                                        // int path_len = strlen(tmptxtfile);

                                        const char *last_sep = strrchr(tmptxtfile, '/');

                                        if(last_sep != NULL)
                                        {
                                            last_sep++;
                                            strcat(bak_fn, last_sep);
                                        }

                                        snprintf(backupfile, sizeof(backupfile), "%s/%s", backupdir, bak_fn);

                                        /* and finaly - copying */
                                        ast_filecopy(tmptxtfile, backupfile, NULL); /* it supposed to copy WAV file */
                                        copy(tmptxtfile, backupfile); /* it supposed to copy only txt file */

                                       /* below we let vanilla asterisk to think it's working as it wants */
/* this is where my code ends */
                                        /* Delete files */
                                        ast_filedelete(tmptxtfile, NULL);
                                        unlink(tmptxtfile);
                                } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {

It was my issue, but the aim it to ask some help.
First - is my idea quite right?
Then, I ask you to look through code to think about security of it - this is what I’m personaly concerned with.
Third, maybe it’s possible to make some improvements (I tried to use native-asterisk methods, but I’m not sure if it was done proper way).
I mean, this code works fine, but maybe not as fine as it could be =).

Thanks.

If you want your contribution to be included in Asterisk, or even looked at by Digium, you must submit it through the proper procedures on issues.asterisk.org. In very brief summary:

Prepare a unified diff (diff -u) against the SVN trunk, or if you consider it to be a bug, rather than a new feature, the most recent 1.8, 1.6.2, or 1.4 SVN branch version on the nearest corresponding released version.
Fill in the licence grant form on issues.asterisk.org and submit it. These can be rejected if not considered legally safe.
After submitting the form, raise an issue and attach the unified diff, ticking the “code of documentation” box.

I haven’t looked at the code, but typically, these days, lock files rely on a combination of system imposed file locking and including the PID of the locking process to allow stale locks to be detected and removed. That assumes that removing the lock is the safest option. Sometimes manual cleanup can be advisable.

Firstly I supposed that asterisk uses OS file locking possibilities too. But it’s not true: asterisk uses it’s own gearing, at least in case of directory (not file) locking.

Thanks for summary, I will think over submiting my code to distribution!