Compare commits

...

1 commit

Author SHA1 Message Date
55f6887bb9 Implement file execution 2024-12-21 16:11:26 +01:00
8 changed files with 446 additions and 18 deletions

12
examples/hello.asmsh Normal file
View file

@ -0,0 +1,12 @@
mov $0x0a6f6c6c, %rax
shl $(8*2), %rax
or $0x6548, %rax
push %rax
mov $1, %rax
mov %rax, %rdi
mov %rsp, %rsi
mov $6, %rdx
syscall
mov $60, %rax
xor %rdi, %rdi
syscall

13
examples/hello2.asmsh Normal file
View file

@ -0,0 +1,13 @@
.syntax intel
mov %rax, 0x0a6f6c6c
shl %rax, 16
or %rax, 0x6548
push %rax
mov %rax, 1
mov %rdi, %rax
mov %rsi, %rsp
mov %rdx, 6
syscall
mov %rax, 60
xor %rdi, %rdi
syscall

View file

@ -207,7 +207,7 @@ int asmsh_env_step(asmsh_env_t *env, int *status)
err_wstatus:
if(WIFEXITED(env->status))
{
dprintf(2, "Child exited with status %d\n", WEXITSTATUS(env->status));
asmsh_log_debug("Child exited with status %d", WEXITSTATUS(env->status));
}
else if(WIFSIGNALED(env->status))
{
@ -431,7 +431,7 @@ static int _asmsh_env_spawn(asmsh_env_t *env)
err_wstatus:
if(WIFEXITED(wstatus))
{
dprintf(2, "Child exited with status %d\n", WEXITSTATUS(wstatus));
asmsh_log_debug("Child exited with status %d", WEXITSTATUS(wstatus));
}
else if(WIFSIGNALED(wstatus))
{

View file

@ -79,11 +79,6 @@ int main(int argc, char *argv[], char *envp[])
return 0;
}
}
if(optind < argc)
{
dprintf(2, "File execution not supported yet\n");
exit(1);
}
asmsh_logger_t *logger;
@ -102,7 +97,21 @@ int main(int argc, char *argv[], char *envp[])
asmsh_logger_dprint(2, logger);
return 1;
}
int argleft = argc - optind;
if(argleft > 1)
{
help(argv[0]);
return 1;
}
/* file execution */
if(argleft > 0)
{
return asmsh_file_exec(&sh, argv[optind], logger);
}
/* interactive prompt */
#ifdef HAVE_LIBREADLINE
rl_special_prefixes="";
rl_basic_word_break_characters=" \t\n";
@ -112,7 +121,8 @@ int main(int argc, char *argv[], char *envp[])
load_history(hpath, add_history);
#endif
while(1)
char main_loop=1;
while(main_loop)
{
#ifdef HAVE_LIBREADLINE
char *cmd;
@ -142,19 +152,27 @@ int main(int argc, char *argv[], char *envp[])
cmd[readed] = '\0';
#endif
ret = asmsh_exec(&sh, cmd);
asmsh_logger_dprint_fmt(2, logger, asmsh_log_lvl_fmt);
if(ret > 0)
const char *split_cmd;
asmsh_cmdline_t cmdline;
asmsh_cmdline_init(&cmdline, cmd);
while((split_cmd = asmsh_cmdline_iter(&cmdline)))
{
//unrecoverable error or exit (if ret == 1)
ret--; // exit status
if(ret)
ret = asmsh_exec(&sh, split_cmd);
asmsh_logger_dprint_fmt(2, logger, asmsh_log_lvl_fmt);
if(ret > 0)
{
dprintf(2, "Exit with status %d\n", ret);
//unrecoverable error or exit (if ret == 1)
ret--; // exit status
if(ret)
{
dprintf(2, "Exit with status %d\n", ret);
}
main_loop=0;
break;
}
break;
//else if (ret < 0) {} //recoverable errors
}
//else if (ret < 0) {} //recoverable errors
#ifdef HAVE_LIBREADLINE
add_history(cmd);
#endif

View file

@ -135,6 +135,236 @@ int asmsh_exec(asmsh_t *sh, const char *_cmd)
return ret;
}
int asmsh_file_exec(asmsh_t *sh, const char *filename, asmsh_logger_t *logger)
{
asmsh_file_t file;
int retval = 0;
if(asmsh_file_open(&file, filename))
{
asmsh_logger_dprint_fmt(2, logger, asmsh_log_lvl_fmt);
return -1;
}
const char *cmd;
while((cmd = asmsh_file_iter(&file)))
{
int ret = asmsh_exec(sh, cmd);
asmsh_logger_dprint_fmt(2, logger, asmsh_log_lvl_fmt);
if(ret > 0)
{
//unrecoverable error
ret--;
if(ret)
{
asmsh_log_fatal("Child exited with satus %d",
ret);
retval = ret;
goto cleanup;
}
}
}
if(file.cmdline.err)
{
asmsh_log_fatal("Error while reading file : %s",
file.cmdline.err);
retval = -1;
}
cleanup:
asmsh_file_close(&file);
asmsh_logger_dprint_fmt(2, logger, asmsh_log_lvl_fmt);
return retval;
}
int asmsh_file_open(asmsh_file_t *file, const char *filename)
{
return _asmsh_file_open(file, filename, 0);
}
int _asmsh_file_open(asmsh_file_t *file, const char *filename, size_t bufsize)
{
bzero(file, sizeof(*file));
if(!(file->filename = strdup(filename)))
{
asmsh_log_perror("Unable to strdup filename");
return -1;
}
if((file->fd = open(file->filename, O_RDONLY)) < 0)
{
asmsh_log_fatal("Unable to open file '%s' : %s",
filename, strerror(errno));
goto err_open;
}
file->bufsize = bufsize ? bufsize : 4096;
if(!(file->buf = malloc(file->bufsize)))
{
asmsh_log_perror("Unable to allocate file read buffer");
goto err_malloc;
}
if(read(file->fd, file->buf, file->bufsize) == -1)
{
asmsh_log_fatal("Unable to read '%s' : %s",
file->filename, strerror(errno));
goto err_read;
}
return 0;
err_read:
free(file->buf);
err_malloc:
close(file->fd);
file->fd = -1;
err_open:
free(file->filename);
return -1;
}
const char* asmsh_file_iter(asmsh_file_t *file)
{
if(!file->cmdline.cmd)
{
asmsh_cmdline_init(&file->cmdline, file->buf);
}
const char *ret = asmsh_cmdline_iter(&file->cmdline);
if(!ret || !file->cmdline.prev_end)
{
if(file->eof)
{
return ret;
}
/* no new command returned or end of buffer after
returned command : attempt to read file again */
if(file->cmdline.prev_end && file->cmdline.cmd)
{
file->cmdline.cmd[file->cmdline.stop] = file->cmdline.prev_end;
}
size_t left_sz;
if(ret)
{
left_sz = strlen(&file->buf[file->cmdline.start]);
}
else
{
left_sz = 0;
}
memmove(file->buf, &file->buf[file->cmdline.start], left_sz);
file->buf[left_sz] = '\0';
size_t readed = read(file->fd, &file->buf[left_sz],
file->bufsize - left_sz);
if(readed == -1)
{
asmsh_log_fatal("Unable to read '%s' : %s",
file->filename, strerror(errno));
goto clean_exit;
}
if (!readed)
{
file->eof = 1;
if(!ret)
{
goto clean_exit;
}
}
file->buf[readed+left_sz] = '\0';
asmsh_cmdline_init(&file->cmdline, file->buf);
return asmsh_file_iter(file);
}
return ret;
clean_exit:
close(file->fd);
free(file->buf);
bzero(file, sizeof(*file));
return NULL;
}
void asmsh_file_close(asmsh_file_t *file)
{
if(file->fd)
{
close(file->fd);
}
if(file->buf)
{
free(file->buf);
}
bzero(file, sizeof(*file));
}
void asmsh_cmdline_init(asmsh_cmdline_t *cmdline, char *cmd)
{
cmdline->cmd = cmd;
cmdline->start = cmdline->stop = 0;
cmdline->prev_end = '\0';
cmdline->err = 0;
}
const char* asmsh_cmdline_iter(asmsh_cmdline_t *cmdline)
{
if(cmdline->prev_end && cmdline->stop)
{
cmdline->cmd[cmdline->stop] = cmdline->prev_end;
}
if(!cmdline->cmd[cmdline->stop])
{
goto clean_exit;
}
if(cmdline->start || cmdline->stop)
{
cmdline->stop++;
cmdline->start = cmdline->stop;
if(!cmdline->cmd[cmdline->stop])
{
goto clean_exit;
}
}
char chr;
while((chr = cmdline->cmd[cmdline->stop]))
{
if(chr == '\\')
{
cmdline->stop++;
}
else if(chr == '"' || chr == '\'')
{
char closing = chr;
do
{
if(chr == '\\') { cmdline->stop++; }
cmdline->stop++;
chr = cmdline->cmd[cmdline->stop];
}while(chr && chr != closing);
if(!chr)
{
cmdline->err = "Unterminated quoted string";
return NULL;
}
}
else if(strchr(ASMSH_IFS, (int)chr))
{
break;
}
cmdline->stop++;
}
if(cmdline->stop == cmdline->start)
{
return asmsh_cmdline_iter(cmdline);
}
cmdline->prev_end = cmdline->cmd[cmdline->stop];
cmdline->cmd[cmdline->stop] = '\0';
return &cmdline->cmd[cmdline->start];
clean_exit:
cmdline->cmd = NULL;
cmdline->err = NULL;
cmdline->start = cmdline->stop = 0;
return NULL;
}
static int _compile_step(asmsh_t *sh, const char *cmd)
{
int ret, status;
@ -299,4 +529,3 @@ size_t asmsh_parse_labels(asmsh_t *sh, char preffix, const char *cmd,
}
return res;
}

View file

@ -25,8 +25,13 @@
#include "compile.h"
typedef struct asmsh_s asmsh_t;
typedef struct asmsh_file_s asmsh_file_t;
typedef struct asmsh_cmdline_s asmsh_cmdline_t;
#include "shell_sym.h"
#define ASMSH_IFS "\n;"
/** Structure storing all information needed to run a shell
* @todo Make it handle command history
* @todo Make it handle "modes" for function writing etc.
@ -52,6 +57,27 @@ struct asmsh_s
};
struct asmsh_cmdline_s
{
char *cmd;
size_t start;
size_t stop;
char prev_end;
const char *err;
};
struct asmsh_file_s
{
char *filename;
int fd;
short eof;
char *buf;
char cmd_end;
size_t bufsize;
asmsh_cmdline_t cmdline;
size_t lineno;
};
/** Initialize a shell environment
* @param asmsh_t* Pointer on the shell environment
* @param const char* The child process executable path (NULL is recommended
@ -69,6 +95,47 @@ void asmsh_cleanup(asmsh_t *sh);
* @return <0 on error 0 on ok 1 on exit */
int asmsh_exec(asmsh_t *sh, const char *cmd);
/** Execute a file with a shell
* @param asmsh_t* Pointer on the shell environment
* @param const char* The path of the file to exec
* @param asmsh_logger_t* A logger object
*/
int asmsh_file_exec(asmsh_t *sh, const char *filename, asmsh_logger_t *logger);
/** Initialize given asmsh_file_t*
* @param asmsh_file_t* Pointer on the struct to initialize
* @param const char* The filename to initialize with
* @return 0 if no error
*/
int asmsh_file_open(asmsh_file_t *file, const char *filename);
int _asmsh_file_open(asmsh_file_t *file, const char *filename, size_t bufsize);
/** Return the next command in a file
* @param asmsh_file_t* The file to iter on, initialized by
* @ref asmsh_file_open()
* @return A reference on the next command or NULL for EOF or error
*/
const char* asmsh_file_iter(asmsh_file_t *file);
/** File cleanup
* @param asmsh_file_t* the file to cleanup
*/
void asmsh_file_close(asmsh_file_t *file);
/** Initialize a command line iteration struct
* @param asmsh_cmdline_t* the structure to initialize
* @param char* the command line to iter on
*/
void asmsh_cmdline_init(asmsh_cmdline_t *cmdline, char *cmd);
/**
* @param asmsh_cmdline_t* The command line to iter on
* @return A reference on the next command in the command line
* or NULL on end or error
* @note On error @ref struct asmsh_cmdline_s::err is set to a string
*/
const char* asmsh_cmdline_iter(asmsh_cmdline_t *cmdline);
/** Replace all labels found in given command by their relative offset
* @param asmsh_t* The shell
* @param char The prefix character before a symbol name

View file

@ -0,0 +1,4 @@
cmd1;cmd2
cmd3
cmd 4

View file

@ -8,6 +8,8 @@
#include "asmsh_check.h"
#include "shell.h"
#define SAMPLE_FILE "samples/sample_file_iter.txt"
START_TEST(test_init)
{
asmsh_t sh;
@ -57,10 +59,93 @@ START_TEST(test_embed)
}
END_TEST
START_TEST(test_cmdline_iter)
{
asmsh_cmdline_t cmdline;
char *cmd = strdup("Hello world!");
asmsh_cmdline_init(&cmdline, cmd);
ck_assert_str_eq(asmsh_cmdline_iter(&cmdline), "Hello world!");
ck_assert_ptr_eq(asmsh_cmdline_iter(&cmdline), NULL);
ck_assert_ptr_eq(cmdline.cmd, NULL);
ck_assert_ptr_eq(cmdline.err, NULL);
free(cmd);
cmd = strdup("Hello \\\nworld !");
asmsh_cmdline_init(&cmdline, cmd);
ck_assert_str_eq(asmsh_cmdline_iter(&cmdline), "Hello \\\nworld !");
ck_assert_ptr_eq(asmsh_cmdline_iter(&cmdline), NULL);
ck_assert_ptr_eq(cmdline.cmd, NULL);
ck_assert_ptr_eq(cmdline.err, NULL);
free(cmd);
cmd = strdup("cmd1;cmd2\ncmd 3;cmd4\\;;cmd 5;;");
asmsh_cmdline_init(&cmdline, cmd);
ck_assert_str_eq(asmsh_cmdline_iter(&cmdline), "cmd1");
ck_assert_str_eq(asmsh_cmdline_iter(&cmdline), "cmd2");
ck_assert_str_eq(asmsh_cmdline_iter(&cmdline), "cmd 3");
ck_assert_str_eq(asmsh_cmdline_iter(&cmdline), "cmd4\\;");
ck_assert_str_eq(asmsh_cmdline_iter(&cmdline), "cmd 5");
ck_assert_ptr_eq(asmsh_cmdline_iter(&cmdline), NULL);
ck_assert_ptr_eq(cmdline.cmd, NULL);
ck_assert_ptr_eq(cmdline.err, NULL);
free(cmd);
cmd = strdup("cmd1 \"arg\n;1\"; cmd2 'arg2\\'';");
asmsh_cmdline_init(&cmdline, cmd);
ck_assert_str_eq(asmsh_cmdline_iter(&cmdline), "cmd1 \"arg\n;1\"");
ck_assert_str_eq(asmsh_cmdline_iter(&cmdline), " cmd2 'arg2\\''");
ck_assert_ptr_eq(asmsh_cmdline_iter(&cmdline), NULL);
ck_assert_ptr_eq(cmdline.cmd, NULL);
ck_assert_ptr_eq(cmdline.err, NULL);
free(cmd);
cmd = strdup("cmd1 \"foobar err;\n");
asmsh_cmdline_init(&cmdline, cmd);
ck_assert_ptr_eq(asmsh_cmdline_iter(&cmdline), NULL);
ck_assert_str_eq(cmdline.err, "Unterminated quoted string");
ck_assert_ptr_ne(cmdline.cmd, NULL);
}
END_TEST
START_TEST(test_file_iter)
{
asmsh_file_t file;
const char *cmd;
ck_assert_int_eq(_asmsh_file_open(&file, SAMPLE_FILE, 0), 0);
ck_assert_str_eq(asmsh_file_iter(&file), "cmd1");
ck_assert_str_eq(asmsh_file_iter(&file), "cmd2");
ck_assert_str_eq(asmsh_file_iter(&file), "cmd3");
ck_assert_str_eq(asmsh_file_iter(&file), "cmd 4");
ck_assert_ptr_eq(asmsh_file_iter(&file), NULL);
ck_assert_int_eq(file.fd, 0);
ck_assert_ptr_eq(file.buf, NULL);
for(size_t i=6; i<15; i++)
{
dprintf(2, "========= %ld ========\n", i);
ck_assert_int_eq(_asmsh_file_open(&file, SAMPLE_FILE, i), 0);
ck_assert_str_eq(asmsh_file_iter(&file), "cmd1");
ck_assert_str_eq(asmsh_file_iter(&file), "cmd2");
ck_assert_str_eq(asmsh_file_iter(&file), "cmd3");
ck_assert_str_eq(asmsh_file_iter(&file), "cmd 4");
const char *foo = asmsh_file_iter(&file);
if(foo) {
dprintf(2, "DEBIG %d FOO='%s'\n", i, foo);
}
ck_assert_ptr_eq(foo, NULL);
//ck_assert_ptr_eq(asmsh_file_iter(&file), NULL);
ck_assert_int_eq(file.fd, 0);
ck_assert_ptr_eq(file.buf, NULL);
}
}
END_TEST
ASMSH_CHECK_START("Testing shell", "testing shell init/exec functions")
ASMSH_ADD_TEST(test_init);
ASMSH_ADD_TEST(test_exit);
ASMSH_ADD_TEST(test_cmd);
ASMSH_ADD_TEST(test_embed);
ASMSH_ADD_TEST(test_cmdline_iter);
ASMSH_ADD_TEST(test_file_iter);
ASMSH_CHECK_END