Upload files to "/"
This commit is contained in:
parent
7f8b47803d
commit
321e50f409
438
shell_syscalls_practice.c
Normal file
438
shell_syscalls_practice.c
Normal file
@ -0,0 +1,438 @@
|
|||||||
|
/***************************************************************************//**
|
||||||
|
|
||||||
|
@file main.c
|
||||||
|
|
||||||
|
@author Stephen Brennan; Revised by Evan Niederwerfer
|
||||||
|
|
||||||
|
@date 03/09/25
|
||||||
|
|
||||||
|
@brief Custom bash shell written in C
|
||||||
|
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int size = 64;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Function Declarations for builtin shell commands:
|
||||||
|
*/
|
||||||
|
|
||||||
|
int lsh_cd(char **args);
|
||||||
|
int lsh_help(char **args);
|
||||||
|
int lsh_exit(char **args);
|
||||||
|
int lsh_about(char **args);
|
||||||
|
|
||||||
|
|
||||||
|
char *builtin_str[] = {
|
||||||
|
"cd",
|
||||||
|
"help",
|
||||||
|
"exit",
|
||||||
|
"about"
|
||||||
|
};
|
||||||
|
|
||||||
|
int (*builtin_func[]) (char **) = {
|
||||||
|
&lsh_cd,
|
||||||
|
&lsh_help,
|
||||||
|
&lsh_exit,
|
||||||
|
&lsh_about
|
||||||
|
};
|
||||||
|
|
||||||
|
int lsh_num_builtins() {
|
||||||
|
return sizeof(builtin_str) / sizeof(char *);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Builtin function implementations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Bultin command: change directory.
|
||||||
|
@param args List of args. args[0] is "cd". args[1] is the directory.
|
||||||
|
@return Always returns 1, to continue executing.
|
||||||
|
*/
|
||||||
|
int lsh_cd(char **args)
|
||||||
|
{
|
||||||
|
if (args[1] == NULL) {
|
||||||
|
fprintf(stderr, "lsh: expected argument to \"cd\"\n");
|
||||||
|
} else {
|
||||||
|
if (chdir(args[1]) != 0) {
|
||||||
|
perror("lsh");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Builtin command: print help.
|
||||||
|
@param args List of args. Not examined.
|
||||||
|
@return Always returns 1, to continue executing.
|
||||||
|
*/
|
||||||
|
int lsh_help(char **args)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
printf("Stephen Brennan's (revised by Evan Niederwerfer) LSH\n");
|
||||||
|
printf("Type program names and arguments, and hit enter.\n");
|
||||||
|
printf("The following are built in:\n");
|
||||||
|
|
||||||
|
for (i = 0; i < lsh_num_builtins(); i++) {
|
||||||
|
printf(" %s\n", builtin_str[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Use the man command for information on other programs.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Builtin command: exit.
|
||||||
|
@param args List of args. Not examined.
|
||||||
|
@return Always returns 0, to terminate execution.
|
||||||
|
*/
|
||||||
|
int lsh_exit(char **args)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lsh_about(char **args) {
|
||||||
|
|
||||||
|
printf("Bash shell written in C - Stephen Brennan's shell revised by Evan Niederwerfer \n\n");
|
||||||
|
|
||||||
|
FILE *cmd = popen("bash --version", "r");
|
||||||
|
if (cmd == NULL) {
|
||||||
|
perror("popen");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buffer[128];
|
||||||
|
while (fgets(buffer, sizeof(buffer), cmd) != NULL) {
|
||||||
|
printf("%s", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(cmd);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Launch a program and wait for it to terminate.
|
||||||
|
@param args Null terminated list of arguments (including program).
|
||||||
|
@return Always returns 1, to continue execution.
|
||||||
|
*/
|
||||||
|
int lsh_launch(char **args)
|
||||||
|
{
|
||||||
|
pid_t pid;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if (pid == 0) {
|
||||||
|
// Child process
|
||||||
|
if (execvp(args[0], args) == -1) {
|
||||||
|
perror("lsh");
|
||||||
|
}
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
} else if (pid < 0) {
|
||||||
|
// Error forking
|
||||||
|
perror("lsh");
|
||||||
|
} else {
|
||||||
|
// Parent process
|
||||||
|
do {
|
||||||
|
waitpid(pid, &status, WUNTRACED);
|
||||||
|
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int myPipe(char **args, int j)
|
||||||
|
{
|
||||||
|
pid_t pid, pid2;
|
||||||
|
int pipefd[2];
|
||||||
|
|
||||||
|
if (pipe(pipefd) == -1) {
|
||||||
|
perror("pipe");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if (pid == 0) { // Child
|
||||||
|
|
||||||
|
close(pipefd[0]);
|
||||||
|
dup2(pipefd[1], STDOUT_FILENO); // redirect stdout to the grandchild
|
||||||
|
close(pipefd[1]);
|
||||||
|
|
||||||
|
args[j] = NULL;
|
||||||
|
|
||||||
|
execvp(args[0], args);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pid2 = fork();
|
||||||
|
if (pid2 == 0) { // Grandchild
|
||||||
|
close(pipefd[1]);
|
||||||
|
dup2(pipefd[0], STDIN_FILENO); // take in input from the child pipe
|
||||||
|
close(pipefd[0]);
|
||||||
|
|
||||||
|
execvp(args[j+1], &args[j+1]);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//parent
|
||||||
|
|
||||||
|
close(pipefd[0]);
|
||||||
|
close(pipefd[1]);
|
||||||
|
|
||||||
|
waitpid(pid, NULL, 0);
|
||||||
|
waitpid(pid2, NULL, 0);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int myAmphersand(char **args, int j)
|
||||||
|
{
|
||||||
|
|
||||||
|
pid_t pid;
|
||||||
|
pid = fork();
|
||||||
|
|
||||||
|
if (pid == -1) {
|
||||||
|
perror("Fork Failed");
|
||||||
|
return(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
args[j] = NULL;
|
||||||
|
lsh_launch(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
args = &args[j+1];
|
||||||
|
lsh_launch(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
waitpid(pid, NULL, 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mySemicolon(char **args, int j)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
args[j] = NULL;
|
||||||
|
|
||||||
|
lsh_launch(args);
|
||||||
|
|
||||||
|
args = &args[j+1];
|
||||||
|
|
||||||
|
lsh_launch(args);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Execute shell built-in or launch program.
|
||||||
|
@param args Null terminated list of arguments.
|
||||||
|
@return 1 if the shell should continue running, 0 if it should terminate
|
||||||
|
*/
|
||||||
|
int lsh_execute(char **args)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (args[0] == NULL) {
|
||||||
|
// An empty command was entered.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int j = 0; args[j] != NULL; j++)
|
||||||
|
{
|
||||||
|
|
||||||
|
// compare "|", "&", ";" with args[j]
|
||||||
|
// and go to coorresponding functions.
|
||||||
|
|
||||||
|
for (int j = 0; args[j] != NULL; j++) {
|
||||||
|
if (strcmp(args[j], "|") == 0) {
|
||||||
|
return myPipe(args, j); // j is the index where the pipe symbol is found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; args[j] != NULL; j++) {
|
||||||
|
if (strcmp(args[j], ";") == 0) {
|
||||||
|
return mySemicolon(args, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; args[j] != NULL; j++) {
|
||||||
|
if (strcmp(args[j], "&") == 0) {
|
||||||
|
return myAmphersand(args, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < lsh_num_builtins(); i++) {
|
||||||
|
if (strcmp(args[0], builtin_str[i]) == 0) {
|
||||||
|
return (*builtin_func[i])(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return lsh_launch(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LSH_RL_BUFSIZE 1024
|
||||||
|
/**
|
||||||
|
@brief Read a line of input from stdin.
|
||||||
|
@return The line from stdin.
|
||||||
|
*/
|
||||||
|
char *lsh_read_line(void)
|
||||||
|
{
|
||||||
|
int bufsize = LSH_RL_BUFSIZE;
|
||||||
|
int position = 0;
|
||||||
|
char *buffer = malloc(sizeof(char) * bufsize);
|
||||||
|
int c;
|
||||||
|
|
||||||
|
if (!buffer) {
|
||||||
|
fprintf(stderr, "lsh: allocation error\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// Read a character
|
||||||
|
c = getchar();
|
||||||
|
|
||||||
|
if (c == EOF) {
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
} else if (c == '\n') {
|
||||||
|
buffer[position] = '\0';
|
||||||
|
return buffer;
|
||||||
|
} else {
|
||||||
|
buffer[position] = c;
|
||||||
|
}
|
||||||
|
position++;
|
||||||
|
|
||||||
|
// If we have exceeded the buffer, reallocate.
|
||||||
|
if (position >= bufsize) {
|
||||||
|
bufsize += LSH_RL_BUFSIZE;
|
||||||
|
buffer = realloc(buffer, bufsize);
|
||||||
|
if (!buffer) {
|
||||||
|
fprintf(stderr, "lsh: allocation error\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* process_line(char *oline)
|
||||||
|
{
|
||||||
|
|
||||||
|
int length = strlen(oline);
|
||||||
|
|
||||||
|
if (length > 100) {
|
||||||
|
oline[100] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; oline[i] != '\0'; i++) {
|
||||||
|
if (oline[i] < 0 || oline[i] > 127) { // ASCII Range
|
||||||
|
printf("Error: Invalid character detected. Only ASCII characters (A–Z, a–z, 0–9, dash, dot, forward slash) are allowed.\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return oline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define LSH_TOK_BUFSIZE 64
|
||||||
|
#define LSH_TOK_DELIM " \t\r\n\a"
|
||||||
|
/**
|
||||||
|
@brief Split a line into tokens (very naively).
|
||||||
|
@param line The line.
|
||||||
|
@return Null-terminated array of tokens.
|
||||||
|
*/
|
||||||
|
char **lsh_split_line(char *line)
|
||||||
|
{
|
||||||
|
int bufsize = LSH_TOK_BUFSIZE, position = 0;
|
||||||
|
char **tokens = malloc(bufsize * sizeof(char*));
|
||||||
|
char *token, **tokens_backup;
|
||||||
|
|
||||||
|
if (!tokens) {
|
||||||
|
fprintf(stderr, "lsh: allocation error\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
token = strtok(line, LSH_TOK_DELIM);
|
||||||
|
while (token != NULL) {
|
||||||
|
tokens[position] = token;
|
||||||
|
position++;
|
||||||
|
|
||||||
|
if (position >= bufsize) {
|
||||||
|
bufsize += LSH_TOK_BUFSIZE;
|
||||||
|
tokens_backup = tokens;
|
||||||
|
tokens = realloc(tokens, bufsize * sizeof(char*));
|
||||||
|
if (!tokens) {
|
||||||
|
free(tokens_backup);
|
||||||
|
fprintf(stderr, "lsh: allocation error\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token = strtok(NULL, LSH_TOK_DELIM);
|
||||||
|
}
|
||||||
|
tokens[position] = NULL;
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Loop getting input and executing it.
|
||||||
|
*/
|
||||||
|
void lsh_loop(void)
|
||||||
|
{
|
||||||
|
char *line, *oline;
|
||||||
|
char **args;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
printf("CS3230-Evan>> ");
|
||||||
|
|
||||||
|
oline = lsh_read_line();
|
||||||
|
|
||||||
|
line = process_line(oline); // process the input
|
||||||
|
args = lsh_split_line(line);
|
||||||
|
status = lsh_execute(args);
|
||||||
|
|
||||||
|
free(line);
|
||||||
|
free(args);
|
||||||
|
} while (status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Main entry point.
|
||||||
|
@param argc Argument count.
|
||||||
|
@param argv Argument vector.
|
||||||
|
@return status code
|
||||||
|
*/
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
// Load config files, if any.
|
||||||
|
|
||||||
|
// Run command loop.
|
||||||
|
lsh_loop();
|
||||||
|
|
||||||
|
// Perform any shutdown/cleanup.
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user