diff --git a/README.md b/README.md index 570b209ac0..3d39691140 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Open the project in Xcode, open iSH.xcconfig, and change `ROOT_BUNDLE_IDENTIFIER To set up your environment, cd to the project and run `meson build` to create a build directory in `build`. Then cd to the build directory and run `ninja`. -To set up a self-contained Alpine linux filesystem, download the Alpine minirootfs tarball for i386 from the [Alpine website](https://alpinelinux.org/downloads/) and run `./tools/fakefsify`, with the minirootfs tarball as the first argument and the name of the output directory as the second argument. Then you can run things inside the Alpine filesystem with `./ish -f alpine /bin/login -f root`, assuming the output directory is called `alpine`. If `tools/fakefsify` doesn't exist for you in your build directory, that might be because it couldn't find libarchive on your system (see above for ways to install it.) +To set up a self-contained Alpine linux filesystem, download the Alpine minirootfs tarball for i386 from the [Alpine website](https://alpinelinux.org/downloads/) and run `./tools/fakefsify`, with the minirootfs tarball as the first argument and the name of the output directory as the second argument. Then you can run things inside the Alpine filesystem with `./ish -f alpine /bin/sh`, assuming the output directory is called `alpine`. If `tools/fakefsify` doesn't exist for you in your build directory, that might be because it couldn't find libarchive on your system (see above for ways to install it.) You can replace `ish` with `tools/ptraceomatic` to run the program in a real process and single step and compare the registers at each step. I use it for debugging. Requires 64-bit Linux 4.11 or later. diff --git a/README_KO.md b/README_KO.md index 29092ef193..f4aad23198 100644 --- a/README_KO.md +++ b/README_KO.md @@ -43,7 +43,7 @@ Xcode로 프로젝트를 열고, iSH.xcconfig 연 후에 `ROOT_BUNDLE_IDENTIFIER 환경을 세팅하기 위해서는 프로젝트 디렉토리로 이동하고 `meson build`를 커맨드 라인에 입력하세요. 그 후 빌드 된 디렉토리로 cd 후 `ninja` 커맨드를 입력해 실행하세요. -자체적으로 컨테이너 화 된 Alpine 리눅스 파일 시스템으로 실행하고 싶다면, [Alpine 웹사이트](https://alpinelinux.org/downloads/) 에서 i386을 위한 Alpine minirootfs(Mini Root Filesystem) tarball 을 다운로드 받고 `./tools/fakefsify`으로 실행하세요. 매개인자로 다운로드 받은 minirootfs tarball 파일을 입력하고 출력 받을 디렉토리의 이름을 두번째 인자로 입력하면 됩니다. 그 후에는 `./ish -f {출력받을 디렉토리 이름} /bin/login -f root` 명령어를 사용하여 Alpine 시스템 내에서 원하는 것을 실행할 수 있습니다. 만약 `tools/fakefsify` 가 빌드 디렉토리에 존재하지 않는다면, libarchive를 찾을 수 없어서 그런 것일 수 있습니다. 위를 참고하여 시스템에 설치하는 방법을 참고해주세요. +자체적으로 컨테이너 화 된 Alpine 리눅스 파일 시스템으로 실행하고 싶다면, [Alpine 웹사이트](https://alpinelinux.org/downloads/) 에서 i386을 위한 Alpine minirootfs(Mini Root Filesystem) tarball 을 다운로드 받고 `./tools/fakefsify`으로 실행하세요. 매개인자로 다운로드 받은 minirootfs tarball 파일을 입력하고 출력 받을 디렉토리의 이름을 두번째 인자로 입력하면 됩니다. 그 후에는 `./ish -f {출력받을 디렉토리 이름} /bin/sh` 명령어를 사용하여 Alpine 시스템 내에서 원하는 것을 실행할 수 있습니다. 만약 `tools/fakefsify` 가 빌드 디렉토리에 존재하지 않는다면, libarchive를 찾을 수 없어서 그런 것일 수 있습니다. 위를 참고하여 시스템에 설치하는 방법을 참고해주세요. 실제 프로세스로 프로그램을 실행하고 각 단계의 레지스터를 비교하기 위해서 `ish`를 `tools/ptraceomatic`로 바꿔 실행할 수 있습니다. 디버깅을 위해 저는 사용합니다. 64-bit Linux 4.11 이후 버전이 필요합니다. @@ -67,4 +67,4 @@ iSH에서 추가한 것 중 가장 흥미로운 것은 JIT 컴파일러 일 것 불행하게도 저는 어셈블리어로 대부분의 이러한 gadget을 작성했습니다. 이것은 성능적으로는 좋은 선택이었을 지 몰라도(실제로는 알 도리가 없지만), 가독성, 유지보수, 그리고 제 정신상태에 대해서는 좋지 않은 선택이 되었습니다. 컴파일러/어셈블러/링커로 인한 여러 고충은 말도 할 수 없을 정도입니다. 거의 무슨 제 코드의 가독성을 해치지 않으면 컴파일을 막는 그러한 악마가 있는 것 같았습니다. 이 코드를 작성하는 도중 제정신을 유지하기 위해서 저는 네이밍과 코드 구조론을 따른 최적의 선택을 하지 못하였습니다. `ss`, `s` 그리고 `a`와 같은 매크로 그리고 변수 명을 찾을 수 있을 것입니다. 주석 또한 찾기 힘들 것입니다. -그렇기에 주의 하세요: 해당 코드를 장기간 접할 경우 정신질환을 앓게되거나 GAS 매크로와 링커오류에 대한 악몽에 시달리고 또다른 부작용이 있을 수 있습니다. 암, 선천적 결함, 또는 생식기 질환을 야기한다고 질병관리청에서 인정했습니다. 암튼 그랬습니다. \ No newline at end of file +그렇기에 주의 하세요: 해당 코드를 장기간 접할 경우 정신질환을 앓게되거나 GAS 매크로와 링커오류에 대한 악몽에 시달리고 또다른 부작용이 있을 수 있습니다. 암, 선천적 결함, 또는 생식기 질환을 야기한다고 질병관리청에서 인정했습니다. 암튼 그랬습니다. diff --git a/README_ZH.md b/README_ZH.md index e067563a3e..dd7039daab 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -40,7 +40,7 @@ iSH 是一个运行在 iOS 上的 Linux shell。本项目使用了 x86 用户模 在项目目录中运行命令 `meson build`,之后 `build` 目录会被创建。进入到 `build` 目录并运行命令 `ninja`。 -为了建立一个自有的 Alpine linux 文件系统,请从 [Alpine 网站](https://alpinelinux.org/downloads/) 下载 `Alpine minirotfs tarball for i386` 并运行 `tools/fakefsify` 。将 minirotfs tarball 指定为第一个参数,将输出目录的名称(如`alpine`)指定为第二个参数,即 `tools/fakefsify $MinirotfsTarballFilename alpine` 然后在 Alpine 文件系统中运行 `/ish -f alpine/bin/login -f root`。如果 `build` 目录下找不到 `tools/fakefsify`,可能是系统上找不到 `libarchive` 的依赖(请参照前面的章节进行安装)。 +为了建立一个自有的 Alpine linux 文件系统,请从 [Alpine 网站](https://alpinelinux.org/downloads/) 下载 `Alpine minirotfs tarball for i386` 并运行 `tools/fakefsify` 。将 minirotfs tarball 指定为第一个参数,将输出目录的名称(如`alpine`)指定为第二个参数,即 `tools/fakefsify $MinirotfsTarballFilename alpine` 然后在 Alpine 文件系统中运行 `/ish -f alpine/bin/sh`。如果 `build` 目录下找不到 `tools/fakefsify`,可能是系统上找不到 `libarchive` 的依赖(请参照前面的章节进行安装)。 除了可以使用 `ish`,你也可以使用 `tools/ptraceomatic` 替代它,以便在某个真实进程中单步比较寄存器。我通常使用它来进行调试(需要 64 位 Linux 4.11 或更高版本)。 @@ -64,4 +64,4 @@ iSH 是一个运行在 iOS 上的 Linux shell。本项目使用了 x86 用户模 但不幸的是,我最开始决定用汇编语言编写几乎所有的 gadgets。这可能从性能方面来说是一个好的决定(虽然我永远也无法确定),但是对可读性、可维护性和我的理智来说,这是一个可怕的决定。我承受了大量来自编译器、汇编程序以及链接器的乱七八糟的东西。那里面就像有一个魔鬼,把我的代码搞得畸形,就算没有畸形,也会编造一些愚蠢的理由说它不能够编译。为了在编写代码时保持理智,我不得不忽略代码结构和命名方面的最佳实践。你会发现宏和变量具有诸如 `ss`、`s` 和 `a` 等描述性的名称,并且汇编器的宏嵌套层数超乎你的想象。最重要的是,代码中几乎没有任何注释。 -所以这是一个警告: 长期接触此代码可能会使你失去理智,对 GAS 宏和链接器错误产生噩梦,或是任何其他使人虚弱的副作用。在加利福尼亚,众所周知这样的代码会导致癌症、生产缺陷和重复伤害。 \ No newline at end of file +所以这是一个警告: 长期接触此代码可能会使你失去理智,对 GAS 宏和链接器错误产生噩梦,或是任何其他使人虚弱的副作用。在加利福尼亚,众所周知这样的代码会导致癌症、生产缺陷和重复伤害。 diff --git a/fs/pty.c b/fs/pty.c index af0b117f87..1b272296cf 100644 --- a/fs/pty.c +++ b/fs/pty.c @@ -34,12 +34,30 @@ static int pty_master_init(struct tty *tty) { return 0; } + +static void pty_hangup(struct tty *tty) { + if (tty == NULL) + return; + lock(&tty->lock); + tty_hangup(tty); + unlock(&tty->lock); +} + +static struct tty *pty_hangup_other(struct tty *tty) { + struct tty *other = tty->pty.other; + if (other == NULL) + return NULL; + pty_hangup(other); + return other; +} + +static void pty_slave_cleanup(struct tty *tty) { + pty_hangup_other(tty); +} + static void pty_master_cleanup(struct tty *tty) { - struct tty *slave = tty->pty.other; + struct tty *slave = pty_hangup_other(tty); slave->pty.other = NULL; - lock(&slave->lock); - tty_hangup(slave); - unlock(&slave->lock); tty_release(slave); } @@ -51,6 +69,16 @@ static int pty_slave_open(struct tty *tty) { return 0; } +static int pty_slave_close(struct tty *tty) { + // If userland's reference count on the pty slave will go to 0, + // hang up the pty master. But the session leader may have a + // reference, and the pty master always has a reference. + if (tty->refcount - 1 == (tty->session ? 2 : 1)) { + pty_hangup_other(tty); + } + return 0; +} + static int pty_master_ioctl(struct tty *tty, int cmd, void *arg) { struct tty *slave = tty->pty.other; switch (cmd) { @@ -94,7 +122,9 @@ DEFINE_TTY_DRIVER(pty_master, &pty_master_ops, TTY_PSEUDO_MASTER_MAJOR, MAX_PTYS const struct tty_driver_ops pty_slave_ops = { .init = pty_return_eio, .open = pty_slave_open, + .close = pty_slave_close, .write = pty_write, + .cleanup = pty_slave_cleanup, }; DEFINE_TTY_DRIVER(pty_slave, &pty_slave_ops, TTY_PSEUDO_SLAVE_MAJOR, MAX_PTYS); diff --git a/fs/tty.c b/fs/tty.c index 9cd3a08c98..3a70ca2943 100644 --- a/fs/tty.c +++ b/fs/tty.c @@ -110,16 +110,7 @@ void tty_release(struct tty *tty) { cond_destroy(&tty->produced); free(tty); } else { - // bit of a hack - struct tty *master = NULL; - if (tty->driver == &pty_slave && tty->refcount == 1) - master = tty->pty.other; unlock(&tty->lock); - if (master != NULL) { - lock(&master->lock); - tty_poll_wakeup(master, POLL_READ | POLL_HUP); - unlock(&master->lock); - } } } @@ -207,11 +198,14 @@ static int tty_device_open(int major, int minor, struct fd *fd) { static int tty_close(struct fd *fd) { if (fd->tty != NULL) { - lock(&fd->tty->fds_lock); + struct tty *tty = fd->tty; + lock(&tty->fds_lock); list_remove_safe(&fd->tty_other_fds); - unlock(&fd->tty->fds_lock); + unlock(&tty->fds_lock); lock(&ttys_lock); - tty_release(fd->tty); + if (tty->driver->ops->close) + tty->driver->ops->close(tty); + tty_release(tty); unlock(&ttys_lock); } return 0; @@ -414,7 +408,7 @@ static bool pty_is_half_closed_master(struct tty *tty) { struct tty *slave = tty->pty.other; // only time one tty lock is nested in another lock(&slave->lock); - bool half_closed = slave->ever_opened && slave->refcount == 1; + bool half_closed = slave->ever_opened && (slave->refcount == 1 || slave->hung_up); unlock(&slave->lock); return half_closed; } @@ -450,7 +444,7 @@ static ssize_t tty_read(struct fd *fd, void *buf, size_t bufsize) { struct tty *tty = fd->tty; lock(&pids_lock); lock(&tty->lock); - if (tty->hung_up) { + if (tty->hung_up || pty_is_half_closed_master(tty)) { unlock(&pids_lock); goto error; } @@ -546,7 +540,7 @@ static ssize_t tty_read(struct fd *fd, void *buf, size_t bufsize) { static ssize_t tty_write(struct fd *fd, const void *buf, size_t bufsize) { struct tty *tty = fd->tty; lock(&tty->lock); - if (tty->hung_up) { + if (tty->hung_up || pty_is_half_closed_master(tty)) { unlock(&tty->lock); return _EIO; } @@ -672,7 +666,7 @@ static int tiocgpgrp(struct tty *tty, pid_t_ *fg_group) { lock(&slave->lock); } - if (tty == slave && !tty_is_current(slave) || slave->fg_group == 0) { + if (tty == slave && (!tty_is_current(slave) || slave->fg_group == 0)) { err = _ENOTTY; goto error_no_ctrl_tty; } @@ -800,7 +794,7 @@ void tty_set_winsize(struct tty *tty, struct winsize_ winsize) { void tty_hangup(struct tty *tty) { tty->hung_up = true; - tty_poll_wakeup(tty, POLL_READ | POLL_WRITE | POLL_ERR | POLL_HUP); + tty_input_wakeup(tty); } struct dev_ops tty_dev = { diff --git a/fs/tty.h b/fs/tty.h index 400e408cf0..ff70dc4235 100644 --- a/fs/tty.h +++ b/fs/tty.h @@ -93,6 +93,7 @@ struct tty_driver { struct tty_driver_ops { int (*init)(struct tty *tty); int (*open)(struct tty *tty); + int (*close)(struct tty *tty); int (*write)(struct tty *tty, const void *buf, size_t len, bool blocking); int (*ioctl)(struct tty *tty, int cmd, void *arg); void (*cleanup)(struct tty *tty); diff --git a/ish-lldb.lldb b/ish-lldb.lldb new file mode 100644 index 0000000000..b23802fa50 --- /dev/null +++ b/ish-lldb.lldb @@ -0,0 +1 @@ +process handle -n 0 -p 1 -s 0 SIGUSR1 SIGTTIN SIGPIPE diff --git a/kernel/exit.c b/kernel/exit.c index efac2f9c84..2a6de25af5 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -148,12 +148,28 @@ noreturn void do_exit_group(int status) { // always called from init process static void halt_system(void) { - // brutally murder everything - // which will leave everything in an inconsistent state. I will solve this problem later. - for (int i = 2; i < MAX_PID; i++) { - struct task *task = pid_get_task(i); - if (task != NULL) - pthread_kill(task->thread, SIGKILL); + for (int state = 0; state < 3; state++) { + int tasks_found = 0; + for (int i = 2; i < MAX_PID; i++) { + struct task *task = pid_get_task(i); + if (task != NULL) { + tasks_found++; + switch (state) { + case 0: + deliver_signal(task, SIGTERM_, SIGINFO_NIL); + break; + case 1: + deliver_signal(task, SIGKILL_, SIGINFO_NIL); + break; + case 2: + pthread_kill(task->thread, SIGTERM); + } + } + } + if (tasks_found == 0) + break; + if (state != 2) + sleep(1); } // unmount all filesystems diff --git a/kernel/log.c b/kernel/log.c index 2b58d47dfb..6b019f658e 100644 --- a/kernel/log.c +++ b/kernel/log.c @@ -154,6 +154,10 @@ static void log_line(const char *line) { static void log_line(const char *line) { os_log_fault(OS_LOG_DEFAULT, "%s", line); } +#elif LOG_HANDLER_STDERR +static void log_line(const char *line) { + fprintf(stderr, "%s\n", line); +} #endif static void default_die_handler(const char *msg) {