Index: pbx/pbx_spool.c =================================================================== --- pbx/pbx_spool.c (revision 46875) +++ pbx/pbx_spool.c (working copy) @@ -316,8 +316,7 @@ bname++; snprintf(newfn, sizeof(newfn), "%s/%s", qdonedir, bname); /* a existing call file the archive dir is overwritten */ - unlink(newfn); - if (rename(o->fn, newfn) != 0) { + if (ast_file_rename(o->fn, newfn)) { unlink(o->fn); return -1; } else Index: apps/app_sms.c =================================================================== --- apps/app_sms.c (revision 46875) +++ apps/app_sms.c (working copy) @@ -932,8 +932,8 @@ if (h->rp) fprintf (o, "rp=1\n"); fclose (o); - if (rename (fn, fn2)) - unlink (fn); + if (ast_file_rename (fn, fn2)) + unlink (fn); /* TODO: I think it's not right to kill message if we can't process it now */ else ast_log (LOG_EVENT, "Received to %s\n", fn2); } Index: apps/app_voicemail.c =================================================================== --- apps/app_voicemail.c (revision 46875) +++ apps/app_voicemail.c (working copy) @@ -875,17 +875,14 @@ stat(tmpin, &statbuf); chmod(tmpout, statbuf.st_mode); chown(tmpout, statbuf.st_uid, statbuf.st_gid); - unlink(tmpin); - rename(tmpout, tmpin); + ast_file_rename(tmpout, tmpin); reset_user_pw(vmu->context, vmu->mailbox, newpassword); ast_copy_string(vmu->password, newpassword, sizeof(vmu->password)); } static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword) { - char buf[255]; - snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword); - if (!ast_safe_system(buf)) + if (!ast_spawn(ext_pass_cmd, vmu->context, vmu->mailbox, newpassword, NULL)) ast_copy_string(vmu->password, newpassword, sizeof(vmu->password)); } @@ -1565,66 +1562,30 @@ ast_filerename(sfn,dfn,NULL); snprintf(stxt, sizeof(stxt), "%s.txt", sfn); snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn); - rename(stxt, dtxt); + ast_file_rename(stxt, dtxt); } -static int copy(char *infile, char *outfile) -{ - int ifd; - int ofd; - int res; - int len; - char buf[4096]; - -#ifdef HARDLINK_WHEN_POSSIBLE - /* Hard link if possible; saves disk space & is faster */ - if (link(infile, outfile)) { -#endif - if ((ifd = open(infile, O_RDONLY)) < 0) { - ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile); - return -1; - } - if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) { - ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile); - close(ifd); - return -1; - } - do { - len = read(ifd, buf, sizeof(buf)); - if (len < 0) { - ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno)); - close(ifd); - close(ofd); - unlink(outfile); - } - if (len) { - res = write(ofd, buf, len); - if (errno == ENOMEM || errno == ENOSPC || res != len) { - ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno)); - close(ifd); - close(ofd); - unlink(outfile); - } - } - } while (len); - close(ifd); - close(ofd); - return 0; -#ifdef HARDLINK_WHEN_POSSIBLE - } else { - /* Hard link succeeded */ - return 0; - } -#endif -} - static void copy_file(char *frompath, char *topath) { char frompath2[256],topath2[256]; ast_filecopy(frompath, topath, NULL); snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath); snprintf(topath2, sizeof(topath2), "%s.txt", topath); - copy(frompath2, topath2); + +#ifdef HARDLINK_WHEN_POSSIBLE +#warn please read the comment below +#endif + +/* + * This function was used for copying message from one user to another and + * here was hardlinking instead real copying when defined HARDLINK_WHEN_POSSIBLE + * + * Maybe it's ok if user can't _change_ content of message. But if function name + * is "COPY" it must do exactly "copy" not "link". So, I removed tries to hardlink + * file + * + */ + ast_file_copy(frompath2, topath2); } /* @@ -1861,7 +1822,6 @@ char dur[256]; char tmp[80] = "/tmp/astmail-XXXXXX"; char tmp2[256]; - char tmpcmd[256]; struct tm tm; if (vmu && ast_strlen_zero(vmu->email)) { @@ -1999,8 +1959,14 @@ if (option_debug) ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp); if (vmu->volgain < -.001 || vmu->volgain > .001) { - snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format); - ast_safe_system(tmpcmd); + char param1[20], param2[256], param3[256]; + + snprintf(param1, sizeof(param1), "%.4f", vmu->volgain); + snprintf(param2, sizeof(param2), "%s.%s", attach, format); + snprintf(param3, sizeof(param3), "%s.%s", newtmp, format); + + ast_spawn("sox", "-v", param1, param2, param3, NULL); + attach = newtmp; if (option_debug) ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox); @@ -2665,7 +2631,6 @@ static void run_externnotify(char *context, char *extension) { - char arguments[255]; char ext_context[256] = ""; int newvoicemails = 0, oldvoicemails = 0; struct ast_smdi_mwi_message *mwi_msg; @@ -2697,10 +2662,9 @@ if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) { ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension); } else { - snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails); - if (option_debug) - ast_log(LOG_DEBUG, "Executing %s\n", arguments); - ast_safe_system(arguments); + char newvoicemails_s[20]; + snprintf(newvoicemails_s, sizeof(newvoicemails_s), "%d", newvoicemails); + ast_spawn_nowait(externnotify, context, extension, newvoicemails_s, NULL); } } } @@ -3026,7 +2990,7 @@ snprintf(txtfile, sizeof(txtfile), "%s.txt", fn); ast_filerename(tmptxtfile, fn, NULL); - rename(tmptxtfile, txtfile); + ast_file_rename(tmptxtfile, txtfile); ast_unlock_path(dir); /* Are there to be more recipients of this message? */ Index: include/asterisk/app.h =================================================================== --- include/asterisk/app.h (revision 46875) +++ include/asterisk/app.h (working copy) @@ -115,10 +115,39 @@ int ast_app_messagecount(const char *context, const char *mailbox, const char *folder); /*! Safely spawn an external program while closing file descriptors + and seting safe environment variables \note This replaces the \b system call in all Asterisk modules */ int ast_safe_system(const char *s); +/*! Safely spawn an external program while closing file descriptors + * and seting safe environment variables + * Example usage: ast_spawn("rm", "-rf", "/tmp/abc", NULL); + * Function waits until given command will be completed. + */ +int ast_spawn(char *filename, ...); + +/*! Safely spawn an external program while closing file descriptors + * and seting safe environment variables + * Example usage: ast_spawn_nowait("rm", "-rf", "/tmp/abc", NULL); + * Function does not wait until given command will be completed. + */ +int ast_spawn_nowait(char *filename, ...); + +/*! Safely spawn an external program while closing file descriptors + * and seting safe environment variables + * Example usage: ast_spawnv( {"rm", "-rf", "/tmp/abc", NULL} ); + * Function waits until given command will be completed. + */ +int ast_spawnv(char ** params); + +/*! Safely spawn an external program while closing file descriptors + * and seting safe environment variables + * Example usage: ast_spawnv_nowait( {"rm", "-rf", "/tmp/abc", NULL} ); + * Function does not wait until given command will be completed. + */ +int ast_spawnv_nowait(char ** params); + /*! * \brief Replace the SIGCHLD handler * Index: include/asterisk/file.h =================================================================== --- include/asterisk/file.h (revision 46875) +++ include/asterisk/file.h (working copy) @@ -216,6 +216,31 @@ */ int ast_filecopy(const char *oldname, const char *newname, const char *fmt); +/*! Copies a file */ +/*! + * \param infile input file name + * \param outfile output file name + * Copy a given file to specified location. + * if destination file exists it will be overwritten + * Returns 0 if success, -1 otherwise + */ +int ast_file_copy(const char *infile, const char *outfile); + +/*! Safely renames a file */ +/*! + * \param oldfile old file name + * \param newfile new file name + * renames file with checking for different filesystems + * if destination file exists it will be overwritten + * Returns 0 if success, -1 otherwise + */ +int ast_file_rename(const char *oldfile, const char *newfile); + +/*! + * \brief system("mkdir -p path") implementation + */ +void ast_file_mkdir_p(char *path); + /*! Waits for a stream to stop or digit to be pressed */ /*! * \param c channel to waitstream on Index: main/asterisk.c =================================================================== --- main/asterisk.c (revision 46875) +++ main/asterisk.c (working copy) @@ -640,57 +640,159 @@ ast_mutex_unlock(&safe_system_lock); } +int ast_spawn_nowait(char *filename, ...) +{ + va_list ap; + char * params[256]; /* Max 256 parameters supported */ + char **tmp = params; + *(tmp++) = filename; + va_start(ap, filename); + while( (*(tmp++) = va_arg(ap, char *)) ); + va_end(ap); + return ast_spawnv_nowait(params); +} + +static char *const env[] = { + "PATH=/bin:/usr/bin:/usr/local/bin", /* Attention!!! PATH variable must be first in list */ + "HOME=/tmp", + NULL +}; + +int ast_spawnv(char ** params) +{ + return ast_spawnv_helper(params, 1); +} + +int ast_spawnv_nowait(char ** params) +{ + return ast_spawnv_helper(params, 0); +} + + int ast_safe_system(const char *s) { - pid_t pid; -#ifdef HAVE_WORKING_FORK - int x; -#endif - int res; - struct rusage rusage; - int status; + return ast_spawn("/bin/sh", "-c", s, (char *) NULL); +} + + +int ast_spawn(char *filename, ...) +{ + va_list ap; + char * params[256]; /* Max 256 parameters supported */ + char ** tmp = params; + *(tmp++) = filename; + va_start(ap, filename); + while( (*(tmp++) = va_arg(ap, char *)) ); + va_end(ap); + return ast_spawnv(params); +} + + +static int ast_spawnv_helper(char ** params, int need_wait) +{ #if defined(HAVE_WORKING_FORK) || defined(HAVE_WORKING_VFORK) - ast_replace_sigchld(); + int pid; + + if(option_debug) { + char ** p = params; + char * str; + ast_log(LOG_DEBUG, "Executing %s (%s) with params:\n", params[0], need_wait ? "wait finished" : "no wait finished"); + + while( (str = *(p++)) ) { + ast_log(LOG_DEBUG, " * '%s'\n", str); + } + } + + char * filename = params[0]; + if(need_wait) ast_replace_sigchld(); + #ifdef HAVE_WORKING_FORK - pid = fork(); + pid = fork(); #else - pid = vfork(); -#endif + pid = vfork(); +#endif - if (pid == 0) { -#ifdef HAVE_WORKING_FORK - if (ast_opt_high_priority) - ast_set_priority(0); - /* Close file descriptors and launch system command */ - for (x = STDERR_FILENO + 1; x < 4096; x++) - close(x); -#endif - execl("/bin/sh", "/bin/sh", "-c", s, (char *) NULL); - _exit(1); - } else if (pid > 0) { - for(;;) { - res = wait4(pid, &status, 0, &rusage); - if (res > -1) { - res = WIFEXITED(status) ? WEXITSTATUS(status) : -1; - break; - } else if (errno != EINTR) - break; + if (pid < 0) { + ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno)); + if(need_wait) ast_unreplace_sigchld(); + return -1; + } + + if( pid ) { /* parent */ + + if( need_wait ) { + int status; + + for(;;) { + int res; + res = waitpid(pid, &status, 0); + if (res > -1) { + res = WIFEXITED(status) ? WEXITSTATUS(status) : -1; + break; + } else if (errno != EINTR) + break; + } + + ast_unreplace_sigchld(); + return status; } - } else { - ast_log(LOG_WARNING, "Fork failed: %s\n", strerror(errno)); - res = -1; - } + + } else { /* child */ +#ifdef HAVE_WORKING_FORK + if (ast_opt_high_priority) ast_set_priority(0); - ast_unreplace_sigchld(); + int i; + for( i = 0; i < 4096; i++ ) + close( i ); + open("/dev/null", O_RDONLY); + open("/dev/null", O_WRONLY); + open("/dev/null", O_WRONLY); +#endif + execve(filename, params, env); + +#ifdef HAVE_WORKING_FORK + /* Can't exec filename as given. Try another variants */ + + if( !strchr(filename, '/') ) { /* filenames with given path can't be located another way */ + + char * path = strchr(ast_strdupa(env[0]), '='); + + if( path ) { + + char * path_next; + + ++path; + + do { + char new_filename[256]; + + path_next = strchr(path, ':'); + if(path_next) + *path_next++ = '\0'; + + snprintf(new_filename, sizeof(new_filename), "%s/%s", path, filename); + + execve(new_filename, params, env); + path = path_next; + + } while (path); + } + } +#endif + + /* Can't use ast_log to report error since FD's are closed */ + _exit(1); + } + return 0; #else - res = -1; + return -1; #endif - - return res; } + + /*! * \brief mute or unmute a console from logging */ Index: main/logger.c =================================================================== --- main/logger.c (revision 46875) +++ main/logger.c (working copy) @@ -69,6 +69,8 @@ #include "asterisk/utils.h" #include "asterisk/manager.h" #include "asterisk/threadstorage.h" +#include "asterisk/file.h" +#include "asterisk/app.h" #if defined(__linux__) && !defined(__NR_gettid) #include @@ -421,7 +423,7 @@ snprintf(new, sizeof(new), "%s.%ld", f->filename, (long)time(NULL)); /* do it */ - if (rename(old,new)) + if (ast_file_rename(old,new)) fprintf(stderr, "Unable to rename file '%s' to '%s'\n", old, new); } } @@ -447,7 +449,7 @@ snprintf(new, sizeof(new), "%s/%s.%ld", (char *)ast_config_AST_LOG_DIR, EVENTLOG,(long)time(NULL)); /* do it */ - if (rename(old,new)) + if (ast_file_rename(old,new)) ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new); } @@ -478,7 +480,7 @@ } else snprintf(new, sizeof(new), "%s/%s.%ld", (char *)ast_config_AST_LOG_DIR, queue_log_name,(long)time(NULL)); /* do it */ - if (rename(old, new)) + if (ast_file_rename(old, new)) ast_log(LOG_ERROR, "Unable to rename file '%s' to '%s'\n", old, new); } @@ -721,7 +723,7 @@ } return; } - + /* don't display LOG_DEBUG messages unless option_verbose _or_ option_debug are non-zero; LOG_DEBUG messages can still be displayed if option_debug is zero, if option_verbose is non-zero (this allows for 'level zero' Index: main/file.c =================================================================== --- main/file.c (revision 46875) +++ main/file.c (working copy) @@ -205,8 +205,33 @@ return res; } -static int copy(const char *infile, const char *outfile) +/*! + * \brief system("mkdir -p path") implementation + */ +void ast_file_mkdir_p(char *path) { + char * s = ast_strdupa(path); + char * p = s; + + while( (p = strchr( p+1, '/' )) ) + { + *p = 0; + mkdir(s,0777); + *p = '/'; + } + + mkdir(s,0777); +} + + + +/*! + * \brief copies file to specified location. + * if destination file exists it will be overwritten + * Returns 0 if success, -1 otherwise + */ +int ast_file_copy(const char *infile, const char *outfile) +{ int ifd, ofd, len; char buf[4096]; /* XXX make it lerger. */ @@ -243,6 +268,36 @@ } /*! + * \brief renames file + * if destination file exists it will be overwritten + * if destination is directory result will fail + * if destination file exists and can't be removed result will fail + * Returns 0 if success, -1 otherwise + */ +int ast_file_rename(const char *oldfile, const char *newfile) +{ + if( unlink(newfile) != 0 ) + if( errno != ENOENT ) { + return -1; /* can't unlink new file and this error is not "file not found" */ + } + + if (rename(oldfile, newfile)) { + if (errno == EXDEV) { + /* source and destination files on different filesystems */ + if (ast_file_copy(oldfile, newfile)) { + return -1; + } + if (unlink(oldfile)) { + unlink(newfile); + return -1; + } + } + return -1; + } + return 0; +} + +/*! * \brief construct a filename. Absolute pathnames are preserved, * relative names are prefixed by the sounds/ directory. * The wav49 suffix is replaced by 'WAV'. @@ -440,7 +495,7 @@ if (!nfn) ast_log(LOG_WARNING, "Out of memory\n"); else { - res = action == ACTION_COPY ? copy(fn, nfn) : rename(fn, nfn); + res = action == ACTION_COPY ? ast_file_copy(fn, nfn) : ast_file_rename(fn, nfn); if (res) ast_log(LOG_WARNING, "%s(%s,%s) failed: %s\n", action == ACTION_COPY ? "copy" : "rename", @@ -699,8 +754,6 @@ int ast_closestream(struct ast_filestream *f) { - char *cmd = NULL; - size_t size = 0; /* Stop a running stream if there is one */ if (f->owner) { if (f->fmt->format < AST_FORMAT_MAX_AUDIO) { @@ -723,11 +776,7 @@ ast_translator_free_path(f->trans); if (f->realfilename && f->filename) { - size = strlen(f->filename) + strlen(f->realfilename) + 15; - cmd = alloca(size); - memset(cmd,0,size); - snprintf(cmd,size,"/bin/mv -f %s %s",f->filename,f->realfilename); - ast_safe_system(cmd); + ast_file_rename(f->filename,f->realfilename); } if (f->filename) Index: res/res_monitor.c =================================================================== --- res/res_monitor.c (revision 46875) +++ res/res_monitor.c (working copy) @@ -128,7 +128,6 @@ const char *fname_base, int need_lock) { int res = 0; - char tmp[256]; LOCK_IF_NEEDED(chan, need_lock); @@ -155,9 +154,8 @@ /* try creating the directory just in case it doesn't exist */ if (directory) { char *name = strdup(fname_base); - snprintf(tmp, sizeof(tmp), "mkdir -p \"%s\"",dirname(name)); + ast_file_mkdir_p(dirname(name)); free(name); - ast_safe_system(tmp); } snprintf(monitor->read_filename, FILENAME_MAX, "%s/%s-in", directory ? "" : ast_config_AST_MONITOR_DIR, fname_base); @@ -290,8 +288,6 @@ } if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) { - char tmp[1024]; - char tmp2[1024]; const char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format; char *name = chan->monitor->filename_base; int directory = strchr(name, '/') ? 1 : 0; @@ -309,16 +305,60 @@ if (ast_strlen_zero(execute_args)) { execute_args = ""; } - - snprintf(tmp, sizeof(tmp), "%s \"%s/%s-in.%s\" \"%s/%s-out.%s\" \"%s/%s.%s\" %s &", execute, dir, name, format, dir, name, format, dir, name, format,execute_args); - if (delfiles) { - snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s/%s-\"* ) &",tmp, dir ,name); /* remove legs when done mixing */ - ast_copy_string(tmp, tmp2, sizeof(tmp)); + +#ifdef HAVE_WORKING_FORK + if( !fork() ) /* child */ +#endif + { + char * params[256]; + char src_file1[256]; + char src_file2[256]; + char dst_file[256]; + char * p; + int i=0; + + snprintf( src_file1, sizeof(src_file1), "%s/%s-in.%s", dir, name, format ); + snprintf( src_file2, sizeof(src_file2), "%s/%s-out.%s", dir, name, format ); + snprintf( dst_file, sizeof(dst_file) , "%s/%s.%s", dir, name, format ); + + p = ast_strdupa(execute); + + params[i++] = p; + + while( (i < 255) && (p = strchr(p, ' ')) ) { + *p++ = '\0'; + while (*p == ' ') p++; + params[i++] = p; + } + + params[i++] = src_file1; + params[i++] = src_file2; + params[i++] = dst_file; + + if( strlen(execute_args) > 0 ) { + p = ast_strdupa(execute_args); + + params[i++] = p; + + while( (i < 255) && (p = strchr(p, ' ')) ) { + *p++ = '\0'; + while (*p == ' ') p++; + params[i++] = p; + } + } + + params[i] = NULL; + + if( !ast_spawnv( params ) ) { /* execute ok */ + if( delfiles ) { + unlink(src_file1); + unlink(src_file2); + } + } +#ifdef HAVE_WORKING_FORK + _exit(0); +#endif } - if (option_debug) - ast_log(LOG_DEBUG,"monitor executing %s\n",tmp); - if (ast_safe_system(tmp) == -1) - ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp); } free(chan->monitor->format); @@ -357,7 +397,6 @@ /* Change monitoring filename of a channel */ int ast_monitor_change_fname(struct ast_channel *chan, const char *fname_base, int need_lock) { - char tmp[256]; if (ast_strlen_zero(fname_base)) { ast_log(LOG_WARNING, "Cannot change monitor filename of channel %s to null\n", chan->name); return -1; @@ -370,9 +409,8 @@ /* try creating the directory just in case it doesn't exist */ if (directory) { char *name = strdup(fname_base); - snprintf(tmp, sizeof(tmp), "mkdir -p %s",dirname(name)); + ast_file_mkdir_p(dirname(name)); free(name); - ast_safe_system(tmp); } snprintf(chan->monitor->filename_base, FILENAME_MAX, "%s/%s", directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);