#include "../external/cbs/cbs.c"
-#define SRC1 "context", "history", "input"
-#define SRC2 "main", "options", "parse", "run", "utils"
+#define SRC1 "context", "history"
+#define SRC2 "input", "main", "options", "parse", "run", "utils"
int main(void) {
char **src;
static struct {
struct bglink entries[MAXBG], *active, *free;
} bgjobs;
+struct sigaction bgaction;
+
+void removebg(pid_t id) {
+ struct bglink *p, *prev;
+
+ for (prev = NULL, p = bgjobs.active; p; prev = p, p = p->next)
+ if (p->job.id == id) break;
+ if (!p) return;
+
+ if (prev) prev->next = p->next; else bgjobs.active = p->next;
+ p->next = bgjobs.free;
+ bgjobs.free = p;
+}
+
+void sigchldbghandler(int sig) {
+ int e, s;
+ struct bglink *p;
+ pid_t id;
+
+ (void)sig;
+
+ e = errno;
+ p = bgjobs.active;
+ while (p) {
+ while ((id = waitpid(-p->job.id, &s, WNOHANG | WUNTRACED)) > 0)
+ if (WIFSTOPPED(s)) {
+ p->job.suspended = 1;
+ break;
+ }
+ if (id == -1) {
+ id = p->job.id;
+ p = p->next;
+ removebg(id);
+ } else p = p->next;
+ }
+ errno = e;
+}
void initbg(void) {
size_t i;
+ bgaction = (struct sigaction){.sa_handler = sigchldbghandler};
+
for (i = 0; i < MAXBG - 1; ++i)
bgjobs.entries[i].next = &bgjobs.entries[i + 1];
bgjobs.free = bgjobs.entries;
return 0;
}
-void removebg(pid_t id) {
- struct bglink *p, *prev;
-
- for (prev = NULL, p = bgjobs.active; p; prev = p, p = p->next)
- if (p->job.id == id) break;
- if (!p) return;
-
- if (prev) prev->next = p->next; else bgjobs.active = p->next;
- p->next = bgjobs.free;
- bgjobs.free = p;
-}
-
-void waitbg(int sig) {
- int e, s;
- struct bglink *p;
- pid_t id;
-
- (void)sig;
- e = errno;
- p = bgjobs.active;
- while (p) {
- while ((id = waitpid(-p->job.id, &s, WNOHANG | WUNTRACED)) > 0)
- if (WIFSTOPPED(s)) {
- p->job.suspended = 1;
- break;
- }
- if (id == -1) {
- id = p->job.id;
- p = p->next;
- removebg(id);
- } else p = p->next;
- }
- errno = e;
-}
-
void deinitbg(void) {
struct bglink *p;
int suspended;
};
+extern struct sigaction bgaction;
+
+void removebg(pid_t id);
+void sigchldbghandler(int sig);
void initbg(void);
int bgfull(void);
int pushbg(struct bgjob job);
int pushbgid(pid_t id);
int peekbg(struct bgjob *job);
int searchbg(pid_t id, struct bgjob *job);
-void removebg(pid_t id);
-void waitbg(int sig);
void deinitbg(void);
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/errno.h>
#include <sys/wait.h>
#include <termios.h>
#include "bg.h"
#include "builtin.h"
+#include "context.h"
+#include "input.h"
+#include "options.h"
#include "utils.h"
+int sigquit, sigint;
static struct {
pid_t id;
int status, done;
} fgjob;
+static sigset_t shellsigmask;
+static struct sigaction fgaction;
+struct sigaction defaultaction;
+static pid_t pgid;
+sigset_t childsigmask;
struct termios canonical;
static struct termios raw;
-struct sigaction actbg, actdefault;
-static struct sigaction actall, actignore;
-static int setconfig(struct termios *mode) {
- if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) {
- note("Unable to set termios config");
- return 0;
- }
- return 1;
+static void sigquithandler(int sig) {
+ (void)sig;
+
+ sigquit = 1;
+}
+
+static void siginthandler(int sig) {
+ (void)sig;
+
+ sigint = 1;
}
-static void waitall(int sig) {
+static void sigchldfghandler(int sig) {
int e, s;
pid_t id;
}
}
if (id == -1) fgjob.done = 1;
- waitbg(sig);
+ sigchldbghandler(sig);
errno = e;
}
+void setsig(int sig, struct sigaction *act) {
+ if (sigaction(sig, act, NULL) == -1)
+ fatal("Unable to install %s handler", strsignal(sig));
+}
+
+static int setconfig(struct termios *mode) {
+ if (tcsetattr(STDIN_FILENO, TCSANOW, mode) == -1) {
+ note("Unable to configure TTY");
+ return 0;
+ }
+ return 1;
+}
+
void initfg(void) {
+ struct sigaction action;
+ pid_t pid;
+
+ sigemptyset(&shellsigmask);
+ sigaddset(&shellsigmask, SIGTSTP);
+ sigaddset(&shellsigmask, SIGTTIN);
+ sigaddset(&shellsigmask, SIGTTOU);
+
+ action = (struct sigaction){.sa_handler = sigquithandler};
+ setsig(SIGHUP, &action);
+ setsig(SIGQUIT, &action);
+ setsig(SIGTERM, &action);
+
+ action = (struct sigaction){.sa_handler = siginthandler};
+ setsig(SIGINT, &action);
+
+ fgaction = (struct sigaction){.sa_handler = sigchldfghandler};
+
+ defaultaction = (struct sigaction){.sa_handler = SIG_DFL};
+ setsig(SIGTSTP, &defaultaction);
+ setsig(SIGTTOU, &defaultaction);
+ setsig(SIGTTIN, &defaultaction);
+
+ pid = getpid();
+ pgid = getpgrp();
+ if (login && pid != pgid && setpgid(0, pgid = pid) == -1) exit(errno);
+
+ if (sigprocmask(SIG_BLOCK, &shellsigmask, &childsigmask) == -1
+ || tcsetpgrp(STDIN_FILENO, pgid) == -1)
+ exit(errno);
+
if (tcgetattr(STDIN_FILENO, &canonical) == -1) exit(errno);
raw = canonical;
- raw.c_lflag &= ~(ICANON | ECHO | ISIG);
+ raw.c_lflag &= ~(ICANON | ECHO);
if (!setconfig(&raw)) exit(EXIT_FAILURE);
-
- actbg.sa_handler = waitbg;
- actall.sa_handler = waitall;
- actdefault.sa_handler = SIG_DFL;
- actignore.sa_handler = SIG_IGN;
-}
-
-void setsigchld(struct sigaction *act) {
- if (sigaction(SIGCHLD, act, NULL) == -1)
- fatal("Unable to install SIGCHLD handler");
}
int runfg(pid_t id) {
}
removebg(id);
- /* SIGCHLD handler is really the function that reaps the foreground process,
- * the waitpid() below is just to block the current thread of execution until
- * the foreground process has been reaped. */
+ /* The handler in `fgaction' is really what reaps the foreground process; the
+ * `sigsuspend()' just blocks the current thread of execution until the
+ * foreground process has been reaped. */
fgjob.id = id;
- setsigchld(&actall);
- while (!fgjob.done) sigsuspend(&actall.sa_mask);
- setsigchld(&actdefault);
+ setsig(SIGCHLD, &fgaction);
+ while (!fgjob.done) {
+ sigsuspend(&shellsigmask);
+ if (sigquit) {
+ deinit();
+ exit(EXIT_SUCCESS);
+ }
+ if (sigint) sigint = 0;
+ }
+ setsig(SIGCHLD, &defaultaction);
fgjob.done = errno = 0;
- if (sigaction(SIGTTOU, &actignore, NULL) == -1
- || tcsetpgrp(STDIN_FILENO, getpgrp()) == -1
- || sigaction(SIGTTOU, &actdefault, NULL) == -1) {
+ if (tcsetpgrp(STDIN_FILENO, pgid) == -1) {
deinit();
exit(errno);
}
return runfg(id);
}
} else status = WTERMSIG(fgjob.status);
-
+
return 1;
}
+extern int sigquit, sigint;
+extern struct sigaction defaultaction;
+extern sigset_t childsigmask;
extern struct termios canonical;
-extern struct sigaction actbg, actdefault;
+void setsig(int sig, struct sigaction *act);
void initfg(void);
-void setsigchld(struct sigaction *act);
int runfg(pid_t id);
void deinitfg(void);
#include <unistd.h>
#include "context.h"
+#include "fg.h"
#include "history.h"
#include "utils.h"
enum {
- CTRLC = '\003',
- CTRLD,
+ CTRLD = '\004',
CLEAR = '\014',
ESCAPE = '\033',
- /* See ESCAPE case in userinput() */
+ /* See `ESCAPE' case in `userinput()' */
ALT = '2' + 1,
UP = 'A',
end = c->string;
while (*end && *end != '\n') ++end;
l = end - c->string;
- while (*end == '\n') ++end; /* scriptinput() repeatedly uses stringinput() */
+ while (*end == '\n') ++end; /* `scriptinput()' uses `stringinput()' */
if (l > MAXCHARS) {
note("Line too long, exceeds %d character limit", MAXCHARS);
return 0;
c->string = c->map;
c->input = stringinput;
+ /* We want errors from this point forward to be reported by the script, not the
+ * shell. */
+ arglist[0] = c->script;
+
return c->input(c);
}
for (i = end - cursor; i > 0; --i) putchar('\b');
}
break;
- case CTRLC:
- putchar('\n');
- return quit(c);
- case CTRLD:
- putchar('\n');
- return 0;
+ case EOF:
+ if (sigquit) {
+ case CTRLD:
+ putchar('\n');
+ return 0;
+ }
+ if (sigint) {
+ sigint = 0;
+ putchar('\n');
+ prompt();
+ }
+ break;
case CLEAR:
fputs("\033[H\033[J", stdout);
prompt();
int login, interactive;
void options(struct context *context) {
- int opt;
char *p, *message = "[file | -c string] [arg ...] [-hl]\n"
" <file> [arg ...] Run script\n"
" -c <string> [arg ...] Run string\n"
" -h Show this help message\n"
" -l Run as a login shell";
+ int opt;
- opt = 0;
if (arglist[0][0] == '-') {
++arglist[0];
login = 1;
interactive = 1;
context->input = userinput;
+ opt = 0;
while (opt != 'c' && (opt = getopt(argcount, arglist, ":c:hl")) != -1)
switch (opt) {
case 'c':
interactive = 0;
context->script = arglist[optind];
context->input = scriptinput;
+
+ /* Until we're able to load the script, errors need to be reported by the
+ * shell, not the script. */
+ arglist[optind] = arglist[0];
}
if (!interactive) {
argcount -= optind;
static void exec(char *path, struct context *c) {
redirectfiles(c->redirects);
+ if (sigprocmask(SIG_SETMASK, &childsigmask, NULL) == -1)
+ note("Unable to unblock TTY signals");
+
if (isbuiltin(c->tokens)) exit(status);
execve(path, c->tokens, environ);
fatal("Couldn't find `%s' command", c->current.name);
pid_t cpid, jobid;
static pid_t pipeid;
- setsigchld(&actbg);
+ setsig(SIGCHLD, &bgaction);
if (!parse(c)) return 0;
- setsigchld(&actdefault);
+ setsig(SIGCHLD, &defaultaction);
islist = c->prev.term > BG || c->current.term > BG;
if (c->t) {