mirror of https://github.com/sipwise/sems.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
378 lines
11 KiB
378 lines
11 KiB
/*
|
|
* Copyright (C) 2008 iptego GmbH
|
|
*
|
|
* This file is part of SEMS, a free SIP media server.
|
|
*
|
|
* SEMS is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version. This program is released under
|
|
* the GPL with the additional exemption that compiling, linking,
|
|
* and/or using OpenSSL is allowed.
|
|
*
|
|
* For a license to use the SEMS software under conditions
|
|
* other than those described here, or to purchase support for this
|
|
* software, please contact iptel.org by e-mail at the following addresses:
|
|
* info@iptel.org
|
|
*
|
|
* SEMS is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
#include "ModSys.h"
|
|
#include "log.h"
|
|
#include "AmUtils.h"
|
|
|
|
#include "DSMSession.h"
|
|
#include "AmSession.h"
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
SC_EXPORT(MOD_CLS_NAME);
|
|
|
|
MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) {
|
|
|
|
DEF_CMD("sys.mkdir", SCMkDirAction);
|
|
DEF_CMD("sys.mkdirRecursive", SCMkDirRecursiveAction);
|
|
DEF_CMD("sys.rename", SCRenameAction);
|
|
DEF_CMD("sys.unlink", SCUnlinkAction);
|
|
DEF_CMD("sys.unlinkArray", SCUnlinkArrayAction);
|
|
DEF_CMD("sys.tmpnam", SCTmpNamAction);
|
|
DEF_CMD("sys.popen", SCPopenAction);
|
|
|
|
DEF_CMD("sys.getTimestamp", SCSysGetTimestampAction);
|
|
DEF_CMD("sys.subTimestamp", SCSysSubTimestampAction);
|
|
|
|
} MOD_ACTIONEXPORT_END;
|
|
|
|
MOD_CONDITIONEXPORT_BEGIN(MOD_CLS_NAME) {
|
|
|
|
if (cmd == "sys.file_exists") {
|
|
return new FileExistsCondition(params, false);
|
|
}
|
|
|
|
if (cmd == "sys.file_not_exists") {
|
|
return new FileExistsCondition(params, true);
|
|
}
|
|
|
|
} MOD_CONDITIONEXPORT_END;
|
|
|
|
MATCH_CONDITION_START(FileExistsCondition) {
|
|
DBG("checking file '%s'\n", arg.c_str());
|
|
string fname = resolveVars(arg, sess, sc_sess, event_params);
|
|
bool ex = file_exists(fname);
|
|
DBG("file '%s' %s\n", fname.c_str(), ex?"exists":"does not exist");
|
|
if (inv) {
|
|
DBG("returning %s\n", (!ex)?"true":"false");
|
|
return !ex;
|
|
} else {
|
|
DBG("returning %s\n", (ex)?"true":"false");
|
|
return ex;
|
|
}
|
|
} MATCH_CONDITION_END;
|
|
|
|
bool sys_mkdir(const char* p) {
|
|
if (mkdir(p, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
|
|
ERROR("mkdir failed for '%s': %s\n",
|
|
p, strerror(errno));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
EXEC_ACTION_START(SCMkDirAction) {
|
|
string d = resolveVars(arg, sess, sc_sess, event_params);
|
|
DBG("mkdir '%s'\n", d.c_str());
|
|
if (sys_mkdir(d.c_str())) {
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_OK);
|
|
} else {
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
|
|
}
|
|
} EXEC_ACTION_END;
|
|
|
|
|
|
bool sys_get_parent_dir(const char* path, char* parentPath) {
|
|
|
|
const char* ptr = strrchr(path, '/'); // search char from end reverse
|
|
if (ptr == NULL) {
|
|
ptr = strrchr(path, '\\'); // search char from end reverse
|
|
if (ptr == NULL) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// copy the parent substring to parentPath
|
|
unsigned int i;
|
|
for (i = 0; &(path[i+1]) != ptr; i++) {
|
|
parentPath[i] = path[i];
|
|
}
|
|
parentPath[i] = '\0';
|
|
|
|
return true;
|
|
}
|
|
|
|
bool sys_mkdir_recursive(const char* p) {
|
|
if (!file_exists(p)) {
|
|
char* parent_dir = new char[strlen(p)+1];
|
|
bool has_parent = sys_get_parent_dir(p, parent_dir);
|
|
if (has_parent) {
|
|
bool parent_exists = sys_mkdir_recursive(parent_dir);
|
|
if (parent_exists) {
|
|
bool ret = sys_mkdir(p);
|
|
delete [] parent_dir;
|
|
return ret;
|
|
}
|
|
}
|
|
delete [] parent_dir;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
EXEC_ACTION_START(SCMkDirRecursiveAction) {
|
|
string d = resolveVars(arg, sess, sc_sess, event_params);
|
|
DBG("mkdir recursive '%s'\n", d.c_str());
|
|
if (sys_mkdir_recursive(d.c_str())) {
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_OK);
|
|
} else {
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
|
|
}
|
|
} EXEC_ACTION_END;
|
|
|
|
// copies ifp to ofp, blockwise
|
|
void filecopy(FILE* ifp, FILE* ofp) {
|
|
size_t nread;
|
|
char buf[1024];
|
|
|
|
rewind(ifp);
|
|
while (!feof(ifp)) {
|
|
nread = fread(buf, 1, 1024, ifp);
|
|
if (fwrite(buf, 1, nread, ofp) != nread)
|
|
break;
|
|
}
|
|
}
|
|
|
|
CONST_ACTION_2P(SCRenameAction, ',', true);
|
|
EXEC_ACTION_START(SCRenameAction) {
|
|
string src = resolveVars(par1, sess, sc_sess, event_params);
|
|
string dst = resolveVars(par2, sess, sc_sess, event_params);
|
|
|
|
int rres = rename(src.c_str(), dst.c_str());
|
|
if (!rres) {
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_OK);
|
|
} else if (rres == EXDEV) {
|
|
FILE* f1 = fopen(src.c_str(), "r");
|
|
if (NULL == f1) {
|
|
WARN("opening source file '%s' for copying failed: '%s'\n",
|
|
src.c_str(), strerror(errno));
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
|
|
return false;
|
|
}
|
|
|
|
FILE* f2 = fopen(dst.c_str(), "w");
|
|
if (NULL == f2) {
|
|
WARN("opening destination file '%s' for copying failed: '%s'\n",
|
|
dst.c_str(), strerror(errno));
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
|
|
return false;
|
|
}
|
|
|
|
filecopy(f1, f2);
|
|
|
|
fclose(f1);
|
|
fclose(f2);
|
|
|
|
if (unlink(src.c_str())) {
|
|
WARN("unlinking source file '%s' for copying failed: '%s'\n",
|
|
src.c_str(), strerror(errno));
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
|
|
return false;
|
|
}
|
|
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_OK);
|
|
} else {
|
|
WARN("renaming '%s' to '%s' failed: '%s'\n",
|
|
src.c_str(), dst.c_str(), strerror(errno));
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
|
|
}
|
|
|
|
} EXEC_ACTION_END;
|
|
|
|
EXEC_ACTION_START(SCUnlinkAction) {
|
|
string fname = resolveVars(arg, sess, sc_sess, event_params);
|
|
if (fname.empty())
|
|
return false;
|
|
|
|
if (!unlink(fname.c_str())) {
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_OK);
|
|
} else {
|
|
WARN("unlink '%s' failed: '%s'\n",
|
|
fname.c_str(), strerror(errno));
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
|
|
}
|
|
} EXEC_ACTION_END;
|
|
|
|
CONST_ACTION_2P(SCUnlinkArrayAction, ',', true);
|
|
EXEC_ACTION_START(SCUnlinkArrayAction) {
|
|
string fname = resolveVars(par1, sess, sc_sess, event_params);
|
|
if (fname.empty())
|
|
return false;
|
|
string prefix = resolveVars(par2, sess, sc_sess, event_params);
|
|
|
|
unsigned int arr_size = 0;
|
|
if (str2int(sc_sess->var[fname + "_size"], arr_size)) {
|
|
ERROR("_size not present/parseable '$%s'\n", sc_sess->var[fname + "_size"].c_str());
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG);
|
|
return false;
|
|
}
|
|
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_OK);
|
|
for (unsigned int i=0;i<arr_size;i++) {
|
|
|
|
string file_fullname = prefix + '/' + sc_sess->var[fname + "_"+int2str(i)];
|
|
DBG("unlinking '%s'\n", file_fullname.c_str());
|
|
if (unlink(file_fullname.c_str())) {
|
|
DBG("unlink '%s' failed: '%s'\n",
|
|
file_fullname.c_str(), strerror(errno));
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
|
|
}
|
|
}
|
|
} EXEC_ACTION_END;
|
|
|
|
/**
|
|
* Must only be used with awareness that dst directory exists,
|
|
* e.g. with `sys.mkdir` from this lib.
|
|
* Later, removal not handled automatically, so must be unlinked
|
|
* with something like `sys.unlink` from this lib.
|
|
*/
|
|
CONST_ACTION_2P(SCTmpNamAction, ',', true);
|
|
EXEC_ACTION_START(SCTmpNamAction) {
|
|
|
|
string varname = resolveVars(par1, sess, sc_sess, event_params);
|
|
string directory = resolveVars(par2, sess, sc_sess, event_params);
|
|
|
|
/* XXXXXX is required for mkstemp, this will be swapped with some random chars */
|
|
string path_template = directory + "/" + varname + "XXXXXX";
|
|
|
|
/* TODO: C-like strings here, possibly to rework later using C++ strings only? */
|
|
char tmpl[path_template.size() + 1];
|
|
strcpy(tmpl, path_template.c_str());
|
|
|
|
int fd = mkstemp(tmpl);
|
|
|
|
if (fd == -1) {
|
|
ERROR("unique name cannot be generated\n");
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_FILE);
|
|
} else {
|
|
close(fd); /* not needed atm */
|
|
sc_sess->var[varname] = tmpl;
|
|
sc_sess->SET_ERRNO(DSM_ERRNO_OK);
|
|
}
|
|
} EXEC_ACTION_END;
|
|
|
|
CONST_ACTION_2P(SCPopenAction, '=', false);
|
|
EXEC_ACTION_START(SCPopenAction) {
|
|
string dst_var = par1;
|
|
if (dst_var.length() && dst_var[0]=='$')
|
|
dst_var = dst_var.substr(1);
|
|
|
|
string cmd = resolveVars(par2, sess, sc_sess, event_params);
|
|
|
|
DBG("executing '%s' while saving output to $%s\n",
|
|
cmd.c_str(), dst_var.c_str());
|
|
|
|
char buf[100];
|
|
string res;
|
|
FILE* fp = popen(cmd.c_str(), "r");
|
|
if (fp==NULL) {
|
|
throw DSMException("sys", "type", "popen", "cause", strerror(errno));
|
|
}
|
|
|
|
size_t rlen;
|
|
|
|
while (true) {
|
|
rlen = fread(buf, 1, 100, fp);
|
|
if (rlen < 100) {
|
|
if (rlen)
|
|
res += string(buf, rlen);
|
|
break;
|
|
}
|
|
|
|
res += string(buf, rlen);
|
|
}
|
|
|
|
sc_sess->var[dst_var] = std::move(res);
|
|
|
|
int status = pclose(fp);
|
|
if (status==-1) {
|
|
throw DSMException("sys", "type", "pclose", "cause", strerror(errno));
|
|
}
|
|
sc_sess->var[dst_var+".status"] = int2str(status);
|
|
DBG("child process returned status %d\n", status);
|
|
|
|
} EXEC_ACTION_END;
|
|
|
|
EXEC_ACTION_START(SCSysGetTimestampAction) {
|
|
string varname = resolveVars(arg, sess, sc_sess, event_params);
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
|
|
// long unsigned msecs = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
|
|
|
char ms_buf[40];
|
|
snprintf(ms_buf, 40, "%li", tv.tv_sec);
|
|
sc_sess->var[varname+".tv_sec"] = ms_buf;
|
|
|
|
snprintf(ms_buf, 40, "%li", (long int)tv.tv_usec);
|
|
sc_sess->var[varname+".tv_usec"] = ms_buf;
|
|
|
|
DBG("got timestamp $%s=%s, $%s=%s, \n",
|
|
(varname+".tv_sec").c_str(), sc_sess->var[varname+".tv_sec"].c_str(),
|
|
(varname+".tv_usec").c_str(), sc_sess->var[varname+".tv_usec"].c_str()
|
|
);
|
|
|
|
} EXEC_ACTION_END;
|
|
|
|
CONST_ACTION_2P(SCSysSubTimestampAction, ',', false);
|
|
EXEC_ACTION_START(SCSysSubTimestampAction) {
|
|
string t1 = resolveVars(par1, sess, sc_sess, event_params);
|
|
string t2 = resolveVars(par2, sess, sc_sess, event_params);
|
|
|
|
struct timeval tv1;
|
|
struct timeval tv2;
|
|
|
|
tv1.tv_sec = atol(sc_sess->var[t1+".tv_sec"].c_str());
|
|
tv1.tv_usec = atol(sc_sess->var[t1+".tv_usec"].c_str());
|
|
|
|
tv2.tv_sec = atol(sc_sess->var[t2+".tv_sec"].c_str());
|
|
tv2.tv_usec = atol(sc_sess->var[t2+".tv_usec"].c_str());
|
|
|
|
struct timeval diff;
|
|
timersub(&tv1,&tv2,&diff);
|
|
|
|
char ms_buf[40];
|
|
snprintf(ms_buf, 40, "%li", diff.tv_sec);
|
|
sc_sess->var[t1+".tv_sec"] = ms_buf;
|
|
|
|
snprintf(ms_buf, 40, "%li", (long int)diff.tv_usec);
|
|
sc_sess->var[t1+".tv_usec"] = ms_buf;
|
|
|
|
// may be overflowing - use only if timestamps known
|
|
snprintf(ms_buf, 40, "%lu", diff.tv_sec * 1000 + diff.tv_usec / 1000);
|
|
sc_sess->var[t1+".msec"] = ms_buf;
|
|
|
|
DBG("sub $%s = %s, $%s = %s, $%s = %s\n",
|
|
(t1+".tv_sec").c_str(), sc_sess->var[t1+".tv_sec"].c_str(),
|
|
(t1+".tv_usec").c_str(), sc_sess->var[t1+".tv_usec"].c_str(),
|
|
(t1+".msec").c_str(), sc_sess->var[t1+".msec"].c_str()
|
|
);
|
|
} EXEC_ACTION_END;
|