aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFranciszek Malinka <franciszek.malinka@gmail.com>2021-12-03 22:41:34 +0100
committerFranciszek Malinka <franciszek.malinka@gmail.com>2021-12-03 22:41:34 +0100
commitdc4c955e6fb39597abb43e59aecc9e64e78c87d1 (patch)
treea421e94a6139ae287f9a6156b465be6122809a72
parentb0bef722a4b903f12a98858d2dc26ccb492797b3 (diff)
first try
-rw-r--r--command.c17
-rw-r--r--jobs.c186
-rw-r--r--shell.c134
3 files changed, 322 insertions, 15 deletions
diff --git a/command.c b/command.c
index f26326e..041a9c1 100644
--- a/command.c
+++ b/command.c
@@ -113,6 +113,23 @@ noreturn void external_command(char **argv) {
if (!index(argv[0], '/') && path) {
/* TODO: For all paths in PATH construct an absolute path and execve it. */
#ifdef STUDENT
+ const char *dir = path;
+ const char *dir_end;
+ char *absolute_path = alloca(strlen(path) + strlen(argv[0]));
+ /* MY-TODO: this is ugly. UPDATE: BUT IT WORKS! */
+ do {
+ dir_end = index(dir, ':');
+ int pnt = 0;
+ while (*dir && dir != dir_end) {
+ absolute_path[pnt++] = *dir++;
+ }
+ if (dir_end)
+ dir_end++;
+ absolute_path[pnt] = 0;
+ strcat(absolute_path, "/");
+ strcat(absolute_path, argv[0]);
+ (void)execve(absolute_path, argv, environ);
+ } while ((dir = dir_end));
#endif /* !STUDENT */
} else {
(void)execve(argv[0], argv, environ);
diff --git a/jobs.c b/jobs.c
index 61f0280..e7120e1 100644
--- a/jobs.c
+++ b/jobs.c
@@ -27,8 +27,82 @@ static void sigchld_handler(int sig) {
/* TODO: Change state (FINISHED, RUNNING, STOPPED) of processes and jobs.
* Bury all children that finished saving their status in jobs. */
#ifdef STUDENT
- (void)status;
- (void)pid;
+ /* MY-TODO: we waitpid all processes, but what if there is nothing to wait
+ * for and some child dies in some code after the while? Will we reap it? */
+
+ job_t *cur_job;
+ proc_t *cur_proc;
+ // msg("sig> Hello from signal!\n");
+ /* Change state of each process */
+ while (true) {
+ pid = waitpid(-1, &status, WCONTINUED | WUNTRACED | WNOHANG);
+ /* MY-TODO: Maybe some other error can occur? */
+ // msg("sig> XD %d\n", pid);
+ if (pid == 0 || (pid < 0 && errno == ECHILD))
+ break;
+ for (int j = 0; j < njobmax; j++) {
+ cur_job = jobs + j;
+ if (!cur_job->pgid)
+ continue;
+ for (int p = 0; p < cur_job->nproc; p++) {
+ cur_proc = &cur_job->proc[p];
+ if (cur_proc->pid == pid) {
+ // msg("What happened to the child?\n");
+ if (WIFSTOPPED(status)) {
+ cur_proc->state = STOPPED;
+ // msg("Stopped\n");
+ } else if (WIFCONTINUED(status)) {
+ // msg("Continued\n");
+ cur_proc->state = RUNNING;
+ } else {
+ cur_proc->state = FINISHED;
+ if (WIFSIGNALED(status))
+ cur_proc->exitcode = WTERMSIG(status) + 128;
+ else
+ cur_proc->exitcode = WEXITSTATUS(status);
+ }
+ }
+ }
+ }
+ }
+
+ /* Go through jobs list and change jobs' states only if
+ * every process in given job has the same state
+ * OR if all states other than finished are the same!
+ */
+
+ /* MY-TODO: this is not correct: what if two processes are finished,
+ * and one is suspended in a job? Then the job should change state
+ * to suspended for sure
+ * Maybe move this logic to jobstate?
+ * Nope
+ * Should be correct now!
+ */
+ for (int j = 0; j < njobmax; j++) {
+ cur_job = jobs + j;
+ if (!cur_job->pgid)
+ continue;
+ bool is_running = false;
+ bool is_stopped = false;
+ for (int p = 0; p < cur_job->nproc; p++) {
+ cur_proc = &cur_job->proc[p];
+ if (cur_proc->state == RUNNING)
+ is_running = true;
+ if (cur_proc->state == STOPPED) {
+ // msg("Henlo :)\n");
+ is_stopped = true;
+ }
+ }
+ if (is_running) {
+ cur_job->state = RUNNING;
+ } else if (is_stopped) {
+ // msg("Henlo\n");
+ cur_job->state = STOPPED;
+ } else {
+ // msg("Henlo????\n");
+ cur_job->state = FINISHED;
+ }
+ }
#endif /* !STUDENT */
errno = old_errno;
}
@@ -117,7 +191,11 @@ static int jobstate(int j, int *statusp) {
/* TODO: Handle case where job has finished. */
#ifdef STUDENT
- (void)exitcode;
+ /* MY-TODO: shouldn't we go through the processes list and check the state? */
+ if (state == FINISHED) {
+ *statusp = exitcode(job);
+ deljob(job);
+ }
#endif /* !STUDENT */
return state;
@@ -142,7 +220,18 @@ bool resumejob(int j, int bg, sigset_t *mask) {
/* TODO: Continue stopped job. Possibly move job to foreground slot. */
#ifdef STUDENT
- (void)movejob;
+ job_t *job = &jobs[j];
+ if (job->pgid == 0)
+ return false;
+
+ printf("[%d] continue \'%s\'\n", j, job->command);
+ Kill(job->pgid, SIGCONT);
+ /* MY-TODO: what if there is some job in the foreground?
+ * I think it shouldn't happen, but what if? */
+ if (bg == FG) {
+ movejob(j, FG);
+ monitorjob(mask);
+ }
#endif /* !STUDENT */
return true;
@@ -156,6 +245,15 @@ bool killjob(int j) {
/* TODO: I love the smell of napalm in the morning. */
#ifdef STUDENT
+ /* MY-TODO: shouldn't we check if jobs[j].pgid != 0?
+ * answer: yeah, we should!
+ * kontr-answer: or do we?
+ */
+ if (jobs[j].pgid == 0)
+ return false;
+ /* If job is stopped, then it won't handle the SIGTERM */
+ Kill(jobs[j].pgid, SIGCONT);
+ Kill(jobs[j].pgid, SIGTERM);
#endif /* !STUDENT */
return true;
@@ -169,7 +267,38 @@ void watchjobs(int which) {
/* TODO: Report job number, state, command and exit code or signal. */
#ifdef STUDENT
- (void)deljob;
+ /* MY-TODO: Maybe this can be done cleaner? */
+ #define KILLED 3
+ const char *state_to_name[] = {"exited", "running", "suspended", "killed"};
+ sigset_t mask;
+
+ /* MY-TODO: shouldn't we do this outside of the loop? Ask Cahir */
+ Sigprocmask(SIG_BLOCK, &sigchld_mask, &mask);
+
+ job_t *job = &jobs[j];
+ int status = 0;
+ int state = job->state;
+ if (which == ALL || state == which) {
+ /* Potentially deleting this job, so we have to copy the command */
+ if (state == FINISHED) {
+ status = exitcode(job);
+ if (status > 128) {
+ state = KILLED;
+ }
+ }
+
+ printf("[%d] %s \'%s\'", j, state_to_name[state], job->command);
+ if (state == KILLED) {
+ printf(" by signal %d", status - 128);
+ deljob(job);
+ }
+ if (state == FINISHED) {
+ printf(", status=%d", status);
+ deljob(job);
+ }
+ printf("\n");
+ }
+ Sigprocmask(SIG_SETMASK, &mask, NULL);
#endif /* !STUDENT */
}
}
@@ -181,9 +310,32 @@ int monitorjob(sigset_t *mask) {
/* TODO: Following code requires use of Tcsetpgrp of tty_fd. */
#ifdef STUDENT
- (void)jobstate;
- (void)exitcode;
- (void)state;
+ /* MY-TODO: don't we have to check for jobs[FG] existance first? */
+ /* MY-TODO: use state somehow */
+ /* MY-TODO: we probably want to block sigchld, that's what mask is for */
+
+ job_t *j = &jobs[FG];
+ Tcsetpgrp(tty_fd, j->pgid);
+ Tcsetattr(tty_fd, TCSADRAIN, &j->tmodes);
+
+ do {
+ // msg("> Suspedning\n");
+ Sigsuspend(mask);
+ // msg("> Resumed\n");
+ state = jobstate(FG, &exitcode);
+ } while (state == RUNNING);
+
+ // msg("> State: %d\n", state);
+ if (state == STOPPED) {
+ int new_j = addjob(0, BG);
+ j = &jobs[new_j];
+ movejob(FG, new_j);
+ }
+
+ Tcgetattr(tty_fd, &j->tmodes);
+ Tcsetattr(tty_fd, TCSADRAIN, &shell_tmodes);
+ Tcsetpgrp(tty_fd, getpid());
+
#endif /* !STUDENT */
return exitcode;
@@ -224,6 +376,24 @@ void shutdownjobs(void) {
/* TODO: Kill remaining jobs and wait for them to finish. */
#ifdef STUDENT
+ /* MY-TODO: if job is stopped, then we cannot kill it :P */
+ for (int j = 0; j < njobmax; j++) {
+
+ if (!jobs[j].pgid) {
+ continue;
+ }
+ /* MY-TODO: Calling killjob? Not sure about that */
+ killjob(j);
+ // Kill(jobs[j].pgid, SIGKILL);
+ }
+
+ /* MY-TODO: what if some job get the signal after the wait?
+ * We shouldn't wait here i think :/
+ * Nope, we should. Just wait for every specific job.
+ */
+ int ret;
+ while ((ret = wait(NULL)) > 0)
+ ;
#endif /* !STUDENT */
watchjobs(FINISHED);
diff --git a/shell.c b/shell.c
index bd62afb..fac0486 100644
--- a/shell.c
+++ b/shell.c
@@ -31,8 +31,30 @@ static int do_redir(token_t *token, int ntokens, int *inputp, int *outputp) {
for (int i = 0; i < ntokens; i++) {
/* TODO: Handle tokens and open files as requested. */
#ifdef STUDENT
- (void)mode;
- (void)MaybeClose;
+ /* MY-TODO: I assume that tokens are only WORD, T_INPUT, T_OUTPUT. */
+ /* MY-TODO: MaybeClose? What for? */
+ // msg("do_redir: ntokens: %d ", ntokens);
+ // for (int i = 0; i < ntokens; i++) {
+ // msg("%s ", token[i]);
+ // }
+ // msg("\n");
+ mode = token[i];
+ if (mode == T_INPUT) {
+ // MaybeClose(STDIN_FILENO);
+ *inputp = Open(token[i + 1], O_RDONLY, 0);
+ token[i] = T_NULL;
+ token[i + 1] = T_NULL;
+ } else if (mode == T_OUTPUT) {
+ // MaybeClose(STDOUT_FILENO);
+ *outputp = Open(token[i + 1], O_WRONLY | O_TRUNC | O_CREAT, 0644);
+ token[i] = T_NULL;
+ token[i + 1] = T_NULL;
+ } else if (mode != T_NULL) {
+ token[n] = token[i];
+ if (i != n)
+ token[i] = T_NULL;
+ n++;
+ }
#endif /* !STUDENT */
}
@@ -58,6 +80,45 @@ static int do_job(token_t *token, int ntokens, bool bg) {
/* TODO: Start a subprocess, create a job and monitor it. */
#ifdef STUDENT
+ /* MY-TODO: we have to copy the tokens to some other array */
+ // msg("do_job after do_redir: %d ", ntokens);
+ // for (int i = 0; i < ntokens; i++) {
+ // msg("%s ", token[i]);
+ // }
+ // msg("\n");
+ // msg("Whadupp, sigchld is blocked!\n");
+ pid_t pid = Fork();
+ if (pid) { /* shell */
+ /* This can result with an error: child can not yet execute
+ * and therefore setpgid will error with errno set to EPERM
+ */
+ setpgid(pid, pid);
+ MaybeClose(&input);
+ MaybeClose(&output);
+ int j = addjob(pid, bg);
+ addproc(j, pid, token);
+ if (bg == FG)
+ exitcode = monitorjob(&mask);
+ else {
+ watchjobs(RUNNING);
+ }
+ } else { /* job */
+ /* Reset signal mask */
+ Signal(SIGINT, SIG_DFL);
+ Signal(SIGTSTP, SIG_DFL);
+ Signal(SIGTTIN, SIG_DFL);
+ Signal(SIGTTOU, SIG_DFL);
+ if (input != -1) {
+ Dup2(input, STDIN_FILENO);
+ MaybeClose(&input);
+ }
+ if (output != -1) {
+ Dup2(output, STDOUT_FILENO);
+ MaybeClose(&output);
+ }
+ Setpgid(0, 0);
+ external_command(token);
+ }
#endif /* !STUDENT */
Sigprocmask(SIG_SETMASK, &mask, NULL);
@@ -76,6 +137,32 @@ static pid_t do_stage(pid_t pgid, sigset_t *mask, int input, int output,
/* TODO: Start a subprocess and make sure it's moved to a process group. */
pid_t pid = Fork();
#ifdef STUDENT
+ /* if pgid == 0, then this is the first process in the pipe,
+ * so let the pipeline be in the group of this process */
+ if (pid) { /* shell */
+ if (pgid == 0)
+ pgid = pid;
+ setpgid(pid, pgid);
+ MaybeClose(&input);
+ MaybeClose(&output);
+ } else { /* job */
+ if (pgid == 0)
+ pgid = getpid();
+ Setpgid(pid, pgid);
+ Signal(SIGINT, SIG_DFL);
+ Signal(SIGTSTP, SIG_DFL);
+ Signal(SIGTTIN, SIG_DFL);
+ Signal(SIGTTOU, SIG_DFL);
+ if (input != -1) {
+ Dup2(input, STDIN_FILENO);
+ MaybeClose(&input);
+ }
+ if (output != -1) {
+ Dup2(output, STDOUT_FILENO);
+ MaybeClose(&output);
+ }
+ external_command(token);
+ }
#endif /* !STUDENT */
return pid;
@@ -107,11 +194,44 @@ static int do_pipeline(token_t *token, int ntokens, bool bg) {
/* TODO: Start pipeline subprocesses, create a job and monitor it.
* Remember to close unused pipe ends! */
#ifdef STUDENT
- (void)input;
- (void)job;
- (void)pid;
- (void)pgid;
- (void)do_stage;
+ // (void)input;
+ // (void)job;
+ // (void)pid;
+ // (void)pgid;
+ // (void)do_stage;
+ int stage_begin = 0;
+ int pipe_where = 0;
+ while (stage_begin < ntokens) {
+ while (pipe_where < ntokens && token[pipe_where] != T_PIPE)
+ pipe_where++;
+ if (pipe_where < ntokens) {
+ token[pipe_where] = T_NULL;
+ }
+ /* If this is the last job in the pipeline, then don't redir the output */
+ if (pipe_where >= ntokens) {
+ MaybeClose(&output);
+ output = -1;
+ }
+ pid = do_stage(pgid, &mask, input, output, token + stage_begin,
+ pipe_where - stage_begin, bg);
+ /* MY-TODO: close those fds somehow, but how?? */
+ // MaybeClose(&input);
+ // MaybeClose(&output);
+ if (!pgid) {
+ pgid = pid;
+ job = addjob(pgid, bg);
+ }
+ addproc(job, pid, token + stage_begin);
+ stage_begin = ++pipe_where;
+ input = next_input;
+ mkpipe(&next_input, &output);
+ }
+ MaybeClose(&next_input);
+ MaybeClose(&output);
+ MaybeClose(&input);
+ if (bg == FG) {
+ monitorjob(&mask);
+ }
#endif /* !STUDENT */
Sigprocmask(SIG_SETMASK, &mask, NULL);