374 行
8.3 KiB
C
374 行
8.3 KiB
C
/**
|
|
* @PROJECT CGI Bash Shell Interface
|
|
* @COPYRIGHT See COPYING in the top level directory
|
|
* @FILE subshell.c
|
|
* @PURPOSE Subshell execution
|
|
* @DEVELOPERS Nathan Angelacos <nangel@users.sourceforge.net>
|
|
* Rafal Kupiec <belliash@asiotec.eu.org>
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <getopt.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "common.h"
|
|
#include "buffer.h"
|
|
#include "error.h"
|
|
#include "subshell.h"
|
|
#include "cbsi.h"
|
|
|
|
char open_tag[3] = "<%";
|
|
char close_tag[3] = "%>";
|
|
|
|
const char* g_tag[] = {
|
|
"",
|
|
"",
|
|
":",
|
|
"@",
|
|
""
|
|
};
|
|
|
|
token_t* build_token_list(script_t* scriptbuf, token_t* tokenlist) {
|
|
char* start;
|
|
char* end;
|
|
char* curpos;
|
|
char* endpos;
|
|
token_t* curtoken;
|
|
token_t* firsttoken;
|
|
|
|
curtoken = tokenlist;
|
|
firsttoken = tokenlist;
|
|
curpos = scriptbuf->buf + scriptbuf->curpos;
|
|
endpos = scriptbuf->buf + scriptbuf->size;
|
|
while(curpos < endpos) {
|
|
start = strstr(curpos, open_tag);
|
|
end = strstr(curpos, close_tag);
|
|
if(start && !end) {
|
|
die_with_message(scriptbuf, start, g_err_msg[E_NO_END_MARKER], open_tag[1]);
|
|
}
|
|
if((start > end) || (!start && end)) {
|
|
die_with_message(scriptbuf, end, g_err_msg[E_END_BEFORE_BEGIN], open_tag[1], open_tag[1]);
|
|
}
|
|
if(start && (strstr(start + 1, open_tag) && (strstr (start + 1, open_tag) < end)))
|
|
die_with_message(scriptbuf, start, g_err_msg[E_NO_END_MARKER], open_tag[1]);
|
|
if(end) {
|
|
curtoken = push_token_on_list(curtoken, scriptbuf, curpos, start - curpos);
|
|
if(firsttoken == NULL) {
|
|
firsttoken = curtoken;
|
|
}
|
|
curtoken = push_token_on_list(curtoken, scriptbuf, start, end - start);
|
|
if(firsttoken == NULL) {
|
|
firsttoken = curtoken;
|
|
}
|
|
curpos = end + 2;
|
|
} else {
|
|
curtoken = push_token_on_list(curtoken, scriptbuf, curpos, endpos - curpos);
|
|
if(firsttoken == NULL) {
|
|
firsttoken = curtoken;
|
|
}
|
|
curpos = endpos;
|
|
}
|
|
}
|
|
return firsttoken;
|
|
}
|
|
|
|
void free_script_list(script_t* script) {
|
|
script_t* next;
|
|
|
|
while(script) {
|
|
next = script->next;
|
|
if(script->name) {
|
|
free(script->name);
|
|
}
|
|
if(script->buf) {
|
|
free(script->buf);
|
|
}
|
|
free(script);
|
|
script = next;
|
|
}
|
|
}
|
|
|
|
void free_token_list(token_t* tokenlist) {
|
|
token_t* next;
|
|
|
|
while(tokenlist) {
|
|
next = tokenlist->next;
|
|
free(tokenlist);
|
|
tokenlist = next;
|
|
}
|
|
}
|
|
|
|
script_t* load_script(char* filename, script_t* scriptlist) {
|
|
script_t* scriptbuf;
|
|
int scriptfp;
|
|
struct stat filestat;
|
|
|
|
scriptfp = open(filename, O_NONBLOCK + O_RDONLY);
|
|
if(scriptfp == -1) {
|
|
die_with_message(NULL, NULL, g_err_msg[E_FILE_OPEN_FAIL], filename);
|
|
}
|
|
fstat(scriptfp, &filestat);
|
|
scriptbuf = (script_t *) xmalloc(sizeof (script_t));
|
|
scriptbuf->name = (char *) xmalloc(strlen (filename) + 1);
|
|
scriptbuf->buf = (char *) xmalloc(filestat.st_size + 1);
|
|
memset(scriptbuf->name, 0, strlen(filename) + 1);
|
|
memcpy(scriptbuf->name, filename, strlen(filename));
|
|
memset(scriptbuf->buf, 0, filestat.st_size + 1);
|
|
read(scriptfp, scriptbuf->buf, filestat.st_size);
|
|
scriptbuf->size = filestat.st_size;
|
|
scriptbuf->uid = filestat.st_uid;
|
|
scriptbuf->gid = filestat.st_gid;
|
|
scriptbuf->curpos = 0;
|
|
scriptbuf->next = NULL;
|
|
if(scriptlist != NULL) {
|
|
while(scriptlist->next) {
|
|
scriptlist = scriptlist->next;
|
|
}
|
|
scriptlist->next = scriptbuf;
|
|
}
|
|
if(memcmp(scriptbuf->buf, "#!", 2) == 0) {
|
|
while((scriptbuf->curpos < scriptbuf->size) && ((char) scriptbuf->buf[scriptbuf->curpos] != '\n')) {
|
|
(scriptbuf->curpos)++;
|
|
}
|
|
(scriptbuf->curpos)++;
|
|
}
|
|
close(scriptfp);
|
|
return scriptbuf;
|
|
}
|
|
|
|
void preprocess_token_list(token_t* tokenlist) {
|
|
script_t* newscript;
|
|
token_t* me;
|
|
char* cp;
|
|
|
|
me = tokenlist;
|
|
while(me) {
|
|
if(memcmp(me->buf, open_tag, 2)) {
|
|
me->tag = HTML;
|
|
} else {
|
|
me->tag = NOOP;
|
|
me->buf[me->len] = '\0';
|
|
cp = me->buf + 2;
|
|
if(memcmp(cp, g_tag[ECHO], 1) == 0) {
|
|
me->tag = ECHO;
|
|
me->buf = find_whitespace(me->buf);
|
|
me->len = strlen(me->buf);
|
|
} else if(memcmp(cp, g_tag[TRANSLATE], 1) == 0) {
|
|
me->tag = TRANSLATE;
|
|
me->buf = find_whitespace(me->buf);
|
|
me->len = strlen(me->buf);
|
|
}
|
|
if(isspace(*cp)) {
|
|
me->tag = RUN;
|
|
me->buf = cp;
|
|
}
|
|
if(me->tag == NOOP) {
|
|
die_with_message(me->script, cp, g_err_msg[E_NO_OP]);
|
|
}
|
|
me->len = strlen(me->buf);
|
|
}
|
|
me = me->next;
|
|
}
|
|
}
|
|
|
|
token_t* process_token_list(buffer_t* buf, token_t* token) {
|
|
char *c;
|
|
|
|
buffer_init(buf);
|
|
subshell_exec(buf, "\n");
|
|
while(token) {
|
|
switch(token->tag) {
|
|
case HTML:
|
|
c = token->buf;
|
|
while((c < (token->buf + token->len)) && (isspace(*c))) {
|
|
c++;
|
|
}
|
|
if(c != token->buf + token->len) {
|
|
subshell_echo (buf, token->buf, token->len);
|
|
}
|
|
break;
|
|
case RUN:
|
|
subshell_exec(buf, token->buf);
|
|
subshell_exec(buf, "\n");
|
|
break;
|
|
case ECHO:
|
|
subshell_eval(buf, token->buf, token->len);
|
|
break;
|
|
case TRANSLATE:
|
|
subshell_translate(buf, token->buf, token->len);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
token = token->next;
|
|
}
|
|
return token;
|
|
}
|
|
|
|
token_t* push_token_on_list(token_t* tokenlist, script_t* scriptbuf, char* start, size_t len) {
|
|
token_t* me;
|
|
token_t* next;
|
|
|
|
if(len == 0) {
|
|
return tokenlist;
|
|
}
|
|
me = (token_t*) xmalloc(sizeof(token_t));
|
|
if(tokenlist == NULL) {
|
|
next = NULL;
|
|
} else {
|
|
next = tokenlist->next;
|
|
tokenlist->next = me;
|
|
}
|
|
me->next = next;
|
|
me->script = scriptbuf;
|
|
me->buf = start;
|
|
me->len = len;
|
|
return me;
|
|
}
|
|
|
|
void subshell_destroy(void) {
|
|
int status;
|
|
waitpid(subshell_pid, &status, 0);
|
|
}
|
|
|
|
void subshell_doscript(buffer_t* script, char* name) {
|
|
static char postfix[] = "\nexit\n";
|
|
|
|
write(subshell_pipe[PARENT_OUT], script->data, script->ptr - script->data);
|
|
write(subshell_pipe[PARENT_OUT], postfix, strlen(postfix));
|
|
return;
|
|
}
|
|
|
|
void subshell_echo(buffer_t* buf, char* str, size_t len) {
|
|
static char echo_start[] = "printf '%s' '";
|
|
static char echo_quote[] = "'\\''";
|
|
static char echo_end[] = "'\n";
|
|
const size_t maxlen = 3096;
|
|
size_t pos;
|
|
|
|
if(len == 0) {
|
|
return;
|
|
}
|
|
pos = 0;
|
|
buffer_add(buf, echo_start, strlen(echo_start));
|
|
while(pos < len) {
|
|
if (str[pos] == '\'') {
|
|
buffer_add(buf, echo_quote, strlen(echo_quote));
|
|
} else {
|
|
buffer_add(buf, str + pos, 1);
|
|
}
|
|
pos++;
|
|
if((pos % maxlen) == 0) {
|
|
buffer_add(buf, echo_end, strlen(echo_end));
|
|
buffer_add(buf, echo_start, strlen(echo_start));
|
|
}
|
|
}
|
|
buffer_add(buf, echo_end, strlen(echo_end));
|
|
}
|
|
|
|
void subshell_eval(buffer_t* buf, char* str, size_t len) {
|
|
static char echo_start[] = "echo -n ";
|
|
static char echo_end[] = "\n";
|
|
|
|
if(len == 0) {
|
|
return;
|
|
}
|
|
str = trim(str);
|
|
if(!*str) {
|
|
return;
|
|
}
|
|
buffer_add(buf, echo_start, strlen(echo_start));
|
|
buffer_add(buf, str, len);
|
|
buffer_add(buf, echo_end, strlen(echo_end));
|
|
}
|
|
|
|
void subshell_exec(buffer_t* buf, char* str) {
|
|
buffer_add (buf, str, strlen (str));
|
|
return;
|
|
}
|
|
|
|
void subshell_setup (char* shell, list_t* env) {
|
|
int retcode = 0;
|
|
int count;
|
|
argv_t* argv;
|
|
char* av[20];
|
|
list_t* next;
|
|
|
|
if(shell == NULL) {
|
|
return;
|
|
}
|
|
retcode = pipe(&subshell_pipe[PARENT_IN]);
|
|
if(retcode == 0) {
|
|
subshell_pid = fork();
|
|
if(subshell_pid == -1) {
|
|
die_with_message(NULL, NULL, g_err_msg[E_SUBSHELL_FAIL]);
|
|
}
|
|
if(subshell_pid == 0) {
|
|
dup2(subshell_pipe[PARENT_IN], STDIN_FILENO);
|
|
close(subshell_pipe[PARENT_IN]);
|
|
close(subshell_pipe[PARENT_OUT]);
|
|
count = argc_argv(shell, &argv);
|
|
if(count > 19) {
|
|
av[19] = "\0";
|
|
count = 18;
|
|
}
|
|
while(count >= 0) {
|
|
av[count] = argv[count].string;
|
|
count--;
|
|
}
|
|
while(env) {
|
|
next = env->next;
|
|
putenv(env->buf);
|
|
env = next;
|
|
}
|
|
execv(argv[0].string, av);
|
|
free(argv);
|
|
die_with_message(NULL, NULL, g_err_msg[E_SUBSHELL_FAIL]);
|
|
} else {
|
|
close(subshell_pipe[PARENT_IN]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void subshell_translate(buffer_t* buf, char* str, size_t len) {
|
|
static char echo_start[] = "echo -n \"";
|
|
static char echo_end[] = "\"\n";
|
|
short hash;
|
|
lstr* i;
|
|
char* text = NULL;
|
|
|
|
if(len == 0) {
|
|
return;
|
|
}
|
|
str = trim(str);
|
|
if(!*str) {
|
|
return;
|
|
}
|
|
if(language != NULL && translations > 0) {
|
|
hash = generateHash(str);
|
|
i = ltable[hash];
|
|
while(text == NULL && i != NULL) {
|
|
if(strcmp(str, i->msgid) == 0) {
|
|
text = i->msgstr;
|
|
} else {
|
|
i = i->next;
|
|
}
|
|
}
|
|
}
|
|
if(text == NULL) {
|
|
text = str;
|
|
}
|
|
buffer_add(buf, echo_start, strlen(echo_start));
|
|
buffer_add(buf, text, strlen(text));
|
|
buffer_add(buf, echo_end, strlen(echo_end));
|
|
}
|