background jobs & stdout,in,err redirect

This commit is contained in:
Night Kaly 2024-02-11 01:01:28 +00:00
parent 93979512d9
commit 44a74c50f2
No known key found for this signature in database
GPG key ID: 8E829D3381CFEBBE
8 changed files with 377 additions and 42 deletions

View file

@ -6,7 +6,7 @@ MANPREFIX = ${PREFIX}/share/man
CFLAGS = -std=gnu11 -O0 -Wall -DVERSION=\"${VERSION}\" CFLAGS = -std=gnu11 -O0 -Wall -DVERSION=\"${VERSION}\"
SRC = rush.c color.c constants.h history.c commands.c SRC = rush.c color.c constants.h history.c commands.c job.c
OBJ = ${SRC:.c=.o} OBJ = ${SRC:.c=.o}
.c.o: .c.o:

View file

@ -14,6 +14,7 @@ rush is a minimalistic shell for Unix systems written in C.
# Building # Building
```sh ```sh
$ make $ make
$ sudo make install
``` ```
# Usage # Usage
@ -30,6 +31,9 @@ $ ./rush
- Editing using left and right arrow keys - Editing using left and right arrow keys
- !! to repeat last command - !! to repeat last command
- Pipes - Pipes
- autojump to directorys
- stdin, stdout, stderr redirect
- background jobs
# Built in commands # Built in commands
- cd - cd
@ -38,10 +42,10 @@ $ ./rush
- history - history
- export - export
- source - source
- j
- bg
# Todo Features # Todo Features
- stdin, stdout, stderr redirect
- background jobs
- tab completion - tab completion
# Credits # Credits

View file

@ -9,6 +9,7 @@
#include "constants.h" #include "constants.h"
#include "history.h" #include "history.h"
#include "rush.h" #include "rush.h"
#include "job.h"
int execute(char **args); int execute(char **args);
@ -19,6 +20,8 @@ int quit(char **args);
int history(char **args); int history(char **args);
int export(char **args); int export(char **args);
int source(char **args); int source(char **args);
int j(char **args);
int bg(char **args);
// List of builtin commands' names // List of builtin commands' names
char *builtin_cmds[] = { char *builtin_cmds[] = {
@ -27,7 +30,9 @@ char *builtin_cmds[] = {
"exit", "exit",
"history", "history",
"export", "export",
"source" "source",
"j",
"bg",
}; };
int (*builtin_func[]) (char **) = { int (*builtin_func[]) (char **) = {
@ -36,26 +41,124 @@ int (*builtin_func[]) (char **) = {
&quit, // cant name it exit as it is taken &quit, // cant name it exit as it is taken
&history, &history,
&export, &export,
&source &source,
&j,
&bg,
}; };
char *shortcut_dirs[] = {
"rush",
"bin",
"localbin",
};
char *shortcut_expand_dirs[] = {
"~/.nky/Coding/C/rush",
"~/.local/bin",
"/usr/local/bin",
};
char *gethome() {
char *home = getenv("HOME");
if (home == NULL) {
fprintf(stderr, "Error: HOME environment variable not set.\n");
exit(EXIT_FAILURE);
}
return home;
}
char *replace_home_dir(char *str) {
char *home_path = gethome();
int path_len = strlen(str);
int home_len = strlen(home_path);
// Allocate memory for the new path
char* new_path = memalloc(sizeof(char) * (path_len + home_len + 1));
int i = 0, j = 0;
while (str[i] != '\0') {
if (str[i] == '~') {
// Copy HOME environment variable value
for (int k = 0; k < home_len; k++) {
new_path[j++] = home_path[k];
}
i++;
} else {
new_path[j++] = str[i++];
}
}
new_path[j] = '\0';
return new_path;
}
char *replace_absolute_home(char *str) {
char *home_path = gethome();
int path_len = strlen(str);
int home_len = strlen(home_path);
// Allocate memory for the new path
char* new_path = memalloc(sizeof(char) * (path_len - home_len + 2));
int i = 0, j = 0;
while (str[i] != '\0') {
if (strncmp(&str[i], home_path, home_len) == 0) {
// Copy HOME environment variable value
new_path[j++] = '~';
i += home_len;
} else {
new_path[j++] = str[i++];
}
}
new_path[j] = '\0';
return new_path;
}
// number of built in commands // number of built in commands
int num_builtins() { int num_builtins() {
return sizeof(builtin_cmds) / sizeof(char *); return sizeof(builtin_cmds) / sizeof(char *);
} }
// autojump
int j(char **args) {
if (args[1] == NULL) {
fprintf(stderr, "rush: not enough arguments\n");
return -1;
}
for (int i = 0; i < sizeof(shortcut_dirs) / sizeof(char *); i++) {
int len = strlen(shortcut_dirs[i]);
if (strncmp(args[1], shortcut_dirs[i], len) == 0) {
char **merged_cd = memalloc(sizeof(char *) * 3);
merged_cd[0] = "cd";
merged_cd[1] = shortcut_expand_dirs[i];
merged_cd[2] = NULL;
cd(merged_cd);
printf("jumped to %s\n", shortcut_expand_dirs[i]);
return 1;
}
}
return 1;
}
// change directory // change directory
int cd(char **args) { int cd(char **args) {
int i = 0;
if (args[1] == NULL) { if (args[1] == NULL) {
char *home = getenv("HOME"); char *home = gethome();
if (home == NULL) {
fprintf(stderr, "rush: HOME environment variable is missing\n");
exit(EXIT_FAILURE);
}
if (chdir(home) != 0) { if (chdir(home) != 0) {
perror("rush"); perror("rush");
} }
} else { } else {
while (args[1][i] != '\0') {
if (args[1][i] == '~') {
args[1] = replace_home_dir(args[1]);
break;
}
i++;
}
if (chdir(args[1]) != 0) { if (chdir(args[1]) != 0) {
perror("rush"); perror("rush");
} }
@ -144,6 +247,24 @@ int source(char **args) {
return status; // Indicate success return status; // Indicate success
} }
int bg(char **args) {
if (args[1] == NULL) {
fprintf(stderr, "rush: not enough arguments\n");
return -1;
}
int job_index = atoi(args[1]);
if (job_index == 0) {
fprintf(stderr, "rush: invalid job index\n");
return -1;
}
job *search = get_job(job_index - 1);
if (search == NULL) {
fprintf(stderr, "rush: no such job\n");
return -1;
}
printf("Job %i: %s\n", job_index, search->command);
return 1;
}
bool is_builtin(char *command) { bool is_builtin(char *command) {
for (int i = 0; i < num_builtins(); i++) { for (int i = 0; i < num_builtins(); i++) {
if (strcmp(command, builtin_cmds[i]) == 0) { if (strcmp(command, builtin_cmds[i]) == 0) {
@ -153,6 +274,58 @@ bool is_builtin(char *command) {
return false; return false;
} }
int launch(char **args, int fd, int options) {
int is_bgj = (options & OPT_BGJ) ? 1 : 0;
int redirect_stdout = (options & OPT_STDOUT) ? 1 : 0;
int redirect_stdin = (options & OPT_STDIN) ? 1 : 0;
int redirect_stderr = (options & OPT_STDERR) ? 1 : 0;
pid_t pid;
int status;
if ((pid = fork()) == 0) {
// Child process
if (fd > 2) {
// not stdin, stdout, or stderr
if (redirect_stdout) {
if (dup2(fd, STDOUT_FILENO) == -1) {
perror("rush");
}
}
if (redirect_stdin) {
if (dup2(fd, STDIN_FILENO) == -1) {
perror("rush");
}
}
if (redirect_stderr) {
if (dup2(fd, STDERR_FILENO) == -1) {
perror("rush");
}
}
close(fd); // close fd as it is duplicated already
}
if (execvp(args[0], args) == -1) {
if (errno == ENOENT) {
fprintf(stderr, "rush: command not found: %s\n", args[0]);
}
}
exit(EXIT_FAILURE); // exit the child
} else if (pid < 0) {
perror("fork failed");
} else {
// Parent process
if (is_bgj) {
int job_index = add_job(pid, args[0], true);
printf("[Job: %i] [Process ID: %i] [Command: %s]\n", job_index + 1, pid, args[0]);
return 1;
} else {
do {
waitpid(pid, &status, WUNTRACED); // wait child to be exited to return to prompt
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
}
return 1;
}
// execute built in commands or launch commands and wait it to terminate, return 1 to keep shell running // execute built in commands or launch commands and wait it to terminate, return 1 to keep shell running
int execute(char **args) { int execute(char **args) {
if (args[0] == NULL) { // An empty command was entered. if (args[0] == NULL) { // An empty command was entered.
@ -165,52 +338,94 @@ int execute(char **args) {
return (*builtin_func[i])(args); return (*builtin_func[i])(args);
} }
} }
int num_arg = 0; int num_arg = 0;
while (*args != NULL) { while (args[num_arg] != NULL) {
num_arg++; // count number of args if (strncmp(args[num_arg], "&", 1) == 0) {
args++; args[num_arg] = NULL;
if (args[num_arg + 1] != NULL) {
// have commands after &
execute(&args[num_arg + 1]);
} }
launch(args, STDOUT_FILENO, OPT_BGJ);
args -= num_arg;
pid_t pid;
int status;
if ((pid = fork()) == 0) {
// Child process
if (execvp(args[0], args) == -1) {
if (errno == ENOENT) {
fprintf(stderr, "rush: command not found: %s\n", args[0]);
}
}
exit(EXIT_FAILURE); // exit the child
} else if (pid < 0) {
perror("fork failed");
} else {
// Parent process
do {
waitpid(pid, &status, WUNTRACED); // wait child to be exited to return to prompt
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
return 1; return 1;
} }
if (strncmp(args[num_arg], ">", 1) == 0) {
int fd = fileno(fopen(args[num_arg + 1], "w+"));
if (fd == -1) {
perror("rush");
return 1;
}
args[num_arg] = NULL;
int asdf = 0;
while (args[asdf] != NULL) {
fprintf(stderr, "args[%i]: %s\n", asdf, args[asdf]);
asdf++;
}
return launch(args, fd, OPT_FGJ | OPT_STDOUT);
}
if (strncmp(args[num_arg], "<", 1) == 0) {
int fd = fileno(fopen(args[num_arg + 1], "r"));
if (fd == -1) {
perror("rush");
return 1;
}
args[num_arg] = NULL;
return launch(args, fd, OPT_FGJ | OPT_STDIN);
}
if (strncmp(args[num_arg], "2>", 2) == 0) {
int fd = fileno(fopen(args[num_arg + 1], "w+"));
if (fd == -1) {
perror("rush");
return 1;
}
args[num_arg] = NULL;
return launch(args, fd, OPT_FGJ | OPT_STDERR);
}
if (strncmp(args[num_arg], ">&", 2) == 0) {
int fd = fileno(fopen(args[num_arg + 1], "w+"));
if (fd == -1) {
perror("rush");
return 1;
}
args[num_arg] = NULL;
return launch(args, fd, OPT_FGJ | OPT_STDOUT | OPT_STDERR);
}
if (strncmp(args[num_arg], ">>", 2) == 0) {
int fd = fileno(fopen(args[num_arg + 1], "a+"));
if (fd == -1) {
perror("rush");
return 1;
}
args[num_arg] = NULL;
return launch(args, fd, OPT_FGJ | OPT_STDOUT);
}
num_arg++; // count number of args
}
return launch(args, STDOUT_FILENO, OPT_FGJ);
}
// execute_pipe with as many pipes as needed // execute_pipe with as many pipes as needed
int execute_pipe(char ***args) { int execute_pipe(char ***args) {
int pipefd[2]; int pipefd[2];
int status;
pid_t pid; pid_t pid;
int in = 0; int in = 0;
int num_cmds = 0; int num_cmds = 0;
while (args[num_cmds] != NULL) { while (args[num_cmds] != NULL) {
int num_args = 0;
while (args[num_cmds][num_args] != NULL) {
//printf("args [%i]: %s\n", num_cmds, args[num_cmds][num_args]);
num_args++;
}
num_cmds++; num_cmds++;
} }
for (int i = 0; i < num_cmds - 1; i++) { for (int i = 0; i < num_cmds - 1; i++) {
//printf("i: %d\n", i);
pipe(pipefd); pipe(pipefd);
if ((pid = fork()) == 0) { if ((pid = fork()) == 0) {
// then this (child) // then this (child)
@ -219,8 +434,8 @@ int execute_pipe(char ***args) {
dup2(pipefd[1], STDOUT_FILENO); // make output go to pipe (next output) dup2(pipefd[1], STDOUT_FILENO); // make output go to pipe (next output)
} }
close(pipefd[0]); // close original input close(pipefd[0]); // close original input
status = execute(args[i]); execute(args[i]);
exit(status); exit(EXIT_SUCCESS);
} else if (pid < 0) { } else if (pid < 0) {
perror("fork failed"); perror("fork failed");
} }
@ -230,16 +445,17 @@ int execute_pipe(char ***args) {
} }
if ((pid = fork()) == 0) { if ((pid = fork()) == 0) {
// printf("last command\n");
dup2(in, STDIN_FILENO); // get input from pipe dup2(in, STDIN_FILENO); // get input from pipe
status = execute(args[num_cmds - 1]); execute(args[num_cmds - 1]);
exit(status); exit(EXIT_SUCCESS);
} else if (pid < 0) { } else if (pid < 0) {
perror("fork failed"); perror("fork failed");
} }
close(in); close(in);
for (int i = 0; i < num_cmds; i++) { for (int i = 0; i < num_cmds; i++) {
wait(&status); waitpid(pid, NULL, 0);
} }
return 1; return 1;
} }

View file

@ -1,9 +1,10 @@
#ifndef COMMANDS_H_ #ifndef COMMANDS_H_
#define COMMANDS_H_ #define COMMANDS_H_
char *replace_absolute_home(char *str);
int num_builtins(); int num_builtins();
bool is_builtin(char *command); bool is_builtin(char *command);
int execute(char **args); int execute(char **args, int fd, int options);
int execute_pipe(char ***args); int execute_pipe(char ***args);
#endif #endif

View file

@ -6,4 +6,11 @@
#define RL_BUFSIZE 1024 // size of each command #define RL_BUFSIZE 1024 // size of each command
#define TOK_DELIM " \t\r\n\a" // delimiter for token #define TOK_DELIM " \t\r\n\a" // delimiter for token
#define MAX_HISTORY 8192 // maximum lines of reading history #define MAX_HISTORY 8192 // maximum lines of reading history
#define MAX_JOBS 64 // maximum number of jobs
#define OPT_STDIN 0x01 // option for stdin
#define OPT_STDOUT 0x02 // option for stdout
#define OPT_STDERR 0x04 // option for stderr
#define OPT_FGJ 0x08 // option for foreground job
#define OPT_BGJ 0x10 // option for background job
#endif #endif

62
job.c Normal file
View file

@ -0,0 +1,62 @@
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
#include "rush.h"
typedef struct job {
pid_t pid;
char *command;
bool status;
struct job *next;
} job;
job *jobs = NULL;
int num_jobs() {
job *current = jobs;
int count = 0;
while (current != NULL) {
count++;
current = current->next;
}
return count;
}
int add_job(pid_t pid, char *command, bool status) {
job *current = jobs;
job *new_job = memalloc(sizeof(job));
new_job->pid = pid;
char *buf = memalloc(strlen(command) + 1);
strcpy(buf, command);
new_job->command = buf;
new_job->status = status;
new_job->next = NULL;
if (current == NULL) {
jobs = new_job;
return 0;
}
int index = 1;
while (current->next != NULL) {
current = current->next;
index++;
}
current->next = new_job;
return index;
}
job *get_job(int index) {
job *current = jobs;
if (index == 0) {
return current;
}
if (index > num_jobs()) {
return NULL;
}
for (int i = 0; i < index; i++) {
printf("current: %s\n", current->command);
current = current->next;
printf("next: %s\n", current->command);
}
return current;
}

17
job.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef JOB_H_
#define JOB_H_
#include <unistd.h>
#include <stdbool.h>
typedef struct job {
pid_t pid;
char *command;
bool status;
struct job *next;
} job;
int add_job(pid_t pid, char *command, bool status);
job *get_job(pid_t pid);
#endif

28
log Normal file
View file

@ -0,0 +1,28 @@
drwxr-xr-x 3 n n 4.0K Feb 11 00:04 .
drwxr-xr-x 13 n n 4.0K Feb 6 21:29 ..
-rw-r--r-- 1 n n 522 Feb 10 22:42 color.c
-rw-r--r-- 1 n n 199 Feb 10 22:42 color.h
-rw-r--r-- 1 n n 6.0K Feb 10 22:44 color.o
-rw-r--r-- 1 n n 12K Feb 11 00:04 commands.c
-rw-r--r-- 1 n n 220 Feb 10 19:17 commands.h
-rw-r--r-- 1 n n 15K Feb 11 00:02 commands.o
-rw-r--r-- 1 n n 598 Feb 10 23:23 constants.h
drwxr-xr-x 8 n n 4.0K Feb 10 23:31 .git
-rw-r--r-- 1 n n 18 Feb 6 18:42 .gitignore
-rw-r--r-- 1 n n 4.0K Feb 10 16:10 history.c
-rw-r--r-- 1 n n 207 Feb 9 18:36 history.h
-rw-r--r-- 1 n n 13K Feb 10 22:19 history.o
-rw-r--r-- 1 n n 938 Feb 10 23:24 job.c
-rw-r--r-- 1 n n 133 Feb 10 19:06 job.h
-rw-r--r-- 1 n n 4.6K Feb 10 23:26 job.o
-rw-r--r-- 1 n n 35K Feb 6 18:24 LICENSE
-rw-r--r-- 1 n n 0 Feb 11 00:05 log
-rw-r--r-- 1 n n 878 Feb 10 23:27 Makefile
-rw-r--r-- 1 n n 1.3K Feb 10 19:11 README.md
-rwxr-xr-x 1 n n 43K Feb 11 00:02 rush
-rw-r--r-- 1 n n 23K Feb 8 22:25 rush-0.1.tar.gz
-rw-r--r-- 1 n n 373 Feb 6 18:56 rush.1
-rw-r--r-- 1 n n 17K Feb 10 23:30 rush.c
-rw-r--r-- 1 n n 119 Feb 10 16:06 rush.h
-rw-r--r-- 1 n n 15K Feb 10 23:50 rush.o
[Job: 1] [Process ID: 2150241] [Command: firefox]