summaryrefslogtreecommitdiffstats
path: root/usr/klibc/system.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/klibc/system.c')
-rw-r--r--usr/klibc/system.c60
1 files changed, 60 insertions, 0 deletions
diff --git a/usr/klibc/system.c b/usr/klibc/system.c
new file mode 100644
index 0000000..13e9fbe
--- /dev/null
+++ b/usr/klibc/system.c
@@ -0,0 +1,60 @@
+/*
+ * system.c
+ *
+ * The system() function. If this turns out to actually be *used*,
+ * we may want to try to detect the very simple cases (no shell magic)
+ * and handle them internally, instead of requiring that /bin/sh be
+ * present.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+int system(const char *string)
+{
+ pid_t pid;
+ struct sigaction ignore, old_int, old_quit;
+ sigset_t masked, oldmask;
+ static const char *argv[] = { "/bin/sh", "-c", NULL, NULL };
+ int status;
+
+ /* Block SIGCHLD and ignore SIGINT and SIGQUIT */
+ /* Do this before the fork() to avoid races */
+
+ ignore.sa_handler = SIG_IGN;
+ sigemptyset(&ignore.sa_mask);
+ ignore.sa_flags = 0;
+ sigaction(SIGINT, &ignore, &old_int);
+ sigaction(SIGQUIT, &ignore, &old_quit);
+
+ sigemptyset(&masked);
+ sigaddset(&masked, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &masked, &oldmask);
+
+ pid = fork();
+
+ if (pid < 0)
+ return -1;
+ else if (pid == 0) {
+ sigaction(SIGINT, &old_int, NULL);
+ sigaction(SIGQUIT, &old_quit, NULL);
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+
+ argv[2] = string;
+
+ execve(argv[0], (char *const *)argv, (char *const *)environ);
+ _exit(127);
+ }
+
+ /* else... */
+
+ waitpid(pid, &status, 0);
+
+ sigaction(SIGINT, &old_int, NULL);
+ sigaction(SIGQUIT, &old_quit, NULL);
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+
+ return status;
+}