/** * @PROJECT CGI Bash Shell Interface * @COPYRIGHT See COPYING in the top level directory * @FILE subshell.c * @PURPOSE Subshell execution * @DEVELOPERS Nathan Angelacos * Rafal Kupiec */ #include #include #include #include #include #include #include #include #include #include #include #include #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)); }