diff --git a/app/AppDelegate.m b/app/AppDelegate.m index c61f0553..571e25a8 100644 --- a/app/AppDelegate.m +++ b/app/AppDelegate.m @@ -65,7 +65,7 @@ static void ios_handle_die(const char *msg) { pthread_setname_np(newName.UTF8String); } #elif ISH_LINUX -void ReportPanic(const char *message, void (^completion)(void)) { +void ReportPanic(const char *message) { [NSNotificationCenter.defaultCenter postNotificationName:KernelPanicNotification object:nil userInfo:@{@"message":@(message)}]; } #endif diff --git a/app/IOSCalls.m b/app/IOSCalls.m index 7e934b11..0bcbbc52 100644 --- a/app/IOSCalls.m +++ b/app/IOSCalls.m @@ -8,6 +8,7 @@ #if ISH_LINUX #include +#include #include "Roots.h" #include "LinuxInterop.h" @@ -19,8 +20,31 @@ void ConsoleLog(const char *data, unsigned len) { NSLog(@"%.*s", len, data); } +nsobj_t objc_get(nsobj_t object) { + CFBridgingRetain((__bridge id) object); + return object; +} + void objc_put(nsobj_t object) { CFBridgingRelease(object); } +void sync_do_in_workqueue(void (^block)(void (^done)(void))) { + __block pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + __block pthread_cond_t cond = PTHREAD_COND_INITIALIZER; + __block bool flag = false; + async_do_in_workqueue(^{ + block(^{ + pthread_mutex_lock(&mutex); + flag = true; + pthread_mutex_unlock(&mutex); + pthread_cond_broadcast(&cond); + }); + }); + pthread_mutex_lock(&mutex); + while (!flag) + pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); +} + #endif diff --git a/app/LinuxInterop.c b/app/LinuxInterop.c index ccfe12b6..9a6b95ed 100644 --- a/app/LinuxInterop.c +++ b/app/LinuxInterop.c @@ -8,11 +8,14 @@ #include "LinuxInterop.h" #include #include +#include #include #include #include #include #include +#include +#include #include #include #include @@ -24,23 +27,10 @@ void actuate_kernel(const char *cmdline) { run_kernel(); } -void sync_do_in_ios(void (^block)(void (^done)(void))) { - DECLARE_COMPLETION(panic_report_done); - struct completion *done_ptr = &panic_report_done; - async_do_in_ios(^{ - block(^{ - async_do_in_irq(^{ - complete(done_ptr); - }); - }); - }); - wait_for_completion(done_ptr); -} - static int panic_report(struct notifier_block *nb, unsigned long action, void *data) { const char *message = data; - sync_do_in_ios(^(void (^done)(void)) { - ReportPanic(message, done); + async_do_in_ios(^{ + ReportPanic(message); }); return 0; } @@ -77,6 +67,27 @@ void async_do_in_irq(void (^block)(void)) { trigger_irq(CALL_BLOCK_IRQ); } +struct ios_work { + void (^block)(void); + struct work_struct work; +}; + +static void do_ios_work(struct work_struct *work) { + struct ios_work *ios_work = container_of(work, struct ios_work, work); + ios_work->block(); + Block_release(ios_work->block); + kfree(ios_work); +} + +void async_do_in_workqueue(void (^block)(void)) { + async_do_in_irq(^{ + struct ios_work *work = kzalloc(sizeof(*work), GFP_KERNEL); + work->block = Block_copy(block); + INIT_WORK(&work->work, do_ios_work); + schedule_work(&work->work); + }); +} + static int __init call_block_init(void) { int err = host_pipe(&block_request_read, &block_request_write); if (err < 0) @@ -90,3 +101,40 @@ static int __init call_block_init(void) { return 0; } subsys_initcall(call_block_init); + +struct ish_session { + struct file *tty; + nsobj_t terminal; + StartSessionDoneBlock callback; +}; + +static int session_init(struct subprocess_info *info, struct cred *cred) { + struct ish_session *session = info->data; + for (int fd = 0; fd <= 2; fd++) { + int err = replace_fd(fd, session->tty, 0); + if (err < 0) + return err; + } + return 0; +} + +static void session_cleanup(struct subprocess_info *info) { + struct ish_session *session = info->data; + if (info->pid != 0 || info->retval != 0) + session->callback(info->retval, info->pid, objc_get(session->terminal)); + else; // otherwise, there was a synchronous failure, returned directly from call_usermodehelper_exec + if (session->tty != NULL) + fput(session->tty); + objc_put(session->terminal); + kfree(session); +} + +void start_session(const char *exe, const char *const *argv, const char *const *envp, StartSessionDoneBlock done) { + struct ish_session *session = kzalloc(sizeof(*session), GFP_KERNEL); + session->tty = ios_pty_open(&session->terminal); + session->callback = done; + struct subprocess_info *proc = call_usermodehelper_setup(exe, (char **) argv, (char **) envp, GFP_KERNEL, session_init, session_cleanup, session); + int err = call_usermodehelper_exec(proc, UMH_WAIT_EXEC); + if (err < 0) + done(err, 0, NULL); +} diff --git a/app/LinuxInterop.h b/app/LinuxInterop.h index 48a28dc7..a9e64480 100644 --- a/app/LinuxInterop.h +++ b/app/LinuxInterop.h @@ -12,29 +12,40 @@ #include #else #include +#include #endif void actuate_kernel(const char *cmdline); void async_do_in_irq(void (^block)(void)); +void async_do_in_workqueue(void (^block)(void)); void async_do_in_ios(void (^block)(void)); -void sync_do_in_ios(void (^block)(void (^done)(void))); +void sync_do_in_workqueue(void (^block)(void (^done)(void))); -void ReportPanic(const char *message, void (^completion)(void)); +void ReportPanic(const char *message); void ConsoleLog(const char *data, unsigned len); -const char *DefaultRootPath(); +const char *DefaultRootPath(void); typedef const void *nsobj_t; +nsobj_t objc_get(nsobj_t object); void objc_put(nsobj_t object); struct linux_tty { struct linux_tty_callbacks *ops; }; struct linux_tty_callbacks { - void (*wakeup)(struct linux_tty *tty); + void (*can_output)(struct linux_tty *tty); void (*send_input)(struct linux_tty *tty, const char *data, size_t length); + void (*hangup)(struct linux_tty *tty); }; +#ifdef __KERNEL__ +struct file *ios_pty_open(nsobj_t *terminal_out); +#endif + +typedef void (^StartSessionDoneBlock)(int retval, int pid, nsobj_t terminal); +void start_session(const char *exe, const char *const *argv, const char *const *envp, StartSessionDoneBlock done); + nsobj_t Terminal_terminalWithType_number(int type, int number); void Terminal_setLinuxTTY(nsobj_t _self, struct linux_tty *tty); int Terminal_sendOutput_length(nsobj_t _self, const char *data, int size); diff --git a/app/LinuxPTY.c b/app/LinuxPTY.c new file mode 100644 index 00000000..cc84c0d6 --- /dev/null +++ b/app/LinuxPTY.c @@ -0,0 +1,175 @@ +// +// LinuxPTY.c +// libiSHLinux +// +// Created by Theodore Dubois on 12/30/21. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "LinuxInterop.h" + +static struct path ptmx_path; + +struct ios_pty_wq { + struct ios_pty *pty; + struct wait_queue_entry wq; + struct wait_queue_head *head; +}; + +struct ios_pty { + dev_t pts_rdev; + struct file *ptm; + nsobj_t terminal; + struct linux_tty linux_tty; + // pseudoterminals have multiple wait queues and you need a different wait_queue_entry for each one. fun fact! + int n_wqs; + struct ios_pty_wq wqs[4]; + poll_table pt; + + struct work_struct output_work; + struct work_struct cleanup_work; +}; + + +static void ios_pty_output_work(struct work_struct *output_work) { + struct ios_pty *pty = container_of(output_work, struct ios_pty, output_work); + size_t room = Terminal_roomForOutput(pty->terminal); + char *buf = kvmalloc(room, GFP_KERNEL); + ssize_t size; + for (;;) { + size = kernel_read(pty->ptm, buf, PAGE_SIZE, NULL); + if (size < 0) { + printk(KERN_WARNING "ios: pty read failed: %s\n", errname(size)); + break; + } + int sent = Terminal_sendOutput_length(pty->terminal, buf, size); + if (sent != size) { + printk(KERN_WARNING "ios: dropped %ld bytes of pty output\n", size - sent); + break; + } + } + kvfree(buf); +} + +static void ios_pty_cleanup_work(struct work_struct *cleanup_work) { + struct ios_pty *pty = container_of(cleanup_work, struct ios_pty, cleanup_work); + for (int i = 0; i < pty->n_wqs; i++) + remove_wait_queue(pty->wqs[i].head, &pty->wqs[i].wq); + fput(pty->ptm); + nsobj_t terminal = pty->terminal; + Terminal_setLinuxTTY(terminal, NULL); + objc_put(terminal); + kfree(pty); +} + +static void ios_pty_cb_can_output(struct linux_tty *linux_tty) { + struct ios_pty *pty = container_of(linux_tty, struct ios_pty, linux_tty); + schedule_work(&pty->output_work); +} + +static void ios_pty_cb_send_input(struct linux_tty *linux_tty, const char *data, size_t length) { + struct ios_pty *pty = container_of(linux_tty, struct ios_pty, linux_tty); + ssize_t written = kernel_write(pty->ptm, data, length, NULL); + if (written < 0) + printk(KERN_WARNING "ios: pty input failed: %s\n", errname(written)); + else if (written != length) + printk(KERN_WARNING "ios: dropped %ld bytes of pty input\n", length - written); +} + +static void ios_pty_cb_hangup(struct linux_tty *linux_tty) { + +} + +static struct linux_tty_callbacks ios_pty_callbacks = { + .can_output = ios_pty_cb_can_output, + .send_input = ios_pty_cb_send_input, + .hangup = ios_pty_cb_hangup, +}; + +static int ptm_callback(struct wait_queue_entry *wq_entry, unsigned mode, int flags, void *key) { + struct ios_pty *pty = container_of(wq_entry, struct ios_pty_wq, wq)->pty; + __poll_t events = vfs_poll(pty->ptm, NULL); + if (events & EPOLLHUP) + schedule_work(&pty->cleanup_work); + if (events & EPOLLIN) + schedule_work(&pty->output_work); + return 0; +} + +static void poll_callback(struct file *file, wait_queue_head_t *whead, poll_table *pt) { + struct ios_pty *pty = container_of(pt, struct ios_pty, pt); + if (pty->n_wqs >= ARRAY_SIZE(pty->wqs)) + panic("ios pty: too many wait queues!"); + struct ios_pty_wq *pty_wq = &pty->wqs[pty->n_wqs++]; + pty_wq->pty = pty; + pty_wq->head = whead; + init_waitqueue_func_entry(&pty_wq->wq, ptm_callback); + add_wait_queue(whead, &pty_wq->wq); +} + +struct file *ios_pty_open(nsobj_t *terminal_out) { + struct file *ptm_file = dentry_open(&ptmx_path, O_RDWR, current_cred()); + if (IS_ERR(ptm_file)) + return ptm_file; + + int lock_pty = 0; + vfs_ioctl(ptm_file, TIOCSPTLCK, (unsigned long) &lock_pty); + + // sadly this api can't just return a struct file * + int fd = vfs_ioctl(ptm_file, TIOCGPTPEER, O_RDWR); + if (fd < 0) { + fput(ptm_file); + return ERR_PTR(fd); + } + struct file *pts_file = fget(fd); + ksys_close(fd); + + struct ios_pty *pty = kzalloc(sizeof(*pty), GFP_KERNEL); + if (pty == NULL) { + fput(pts_file); + fput(ptm_file); + return ERR_PTR(-ENOMEM); + } + pty->ptm = ptm_file; + + INIT_WORK(&pty->output_work, ios_pty_output_work); + INIT_WORK(&pty->cleanup_work, ios_pty_cleanup_work); + + pty->pts_rdev = pts_file->f_inode->i_rdev; + pty->terminal = Terminal_terminalWithType_number(MAJOR(pty->pts_rdev), MINOR(pty->pts_rdev)); + pty->linux_tty.ops = &ios_pty_callbacks; + Terminal_setLinuxTTY(pty->terminal, &pty->linux_tty); + *terminal_out = pty->terminal; + + init_poll_funcptr(&pty->pt, poll_callback); + __poll_t revents = vfs_poll(pty->ptm, &pty->pt); + if (revents) + ptm_callback(&pty->wqs[pty->n_wqs-1].wq, 0, 0, NULL); + return pts_file; +} + +static __init int ios_pty_init(void) { + ksys_mkdir("/dev/pts", 0755); + int err = do_mount("devpts", "/dev/pts", "devpts", MS_SILENT, NULL); + if (err < 0) { + panic("ish: failed to mount devpts: %s", errname(err)); + } + err = kern_path("/dev/pts/ptmx", 0, &ptmx_path); + if (err < 0) { + panic("ish: failed to acquire ptmx: %s", errname(err)); + } + return 0; +} + +device_initcall(ios_pty_init); diff --git a/app/LinuxRoot.c b/app/LinuxRoot.c index 2e26aba4..f1a8d222 100644 --- a/app/LinuxRoot.c +++ b/app/LinuxRoot.c @@ -19,7 +19,7 @@ static __init int ish_rootfs(void) { const char *fakefs_path = DefaultRootPath(); int err = do_mount(fakefs_path, "/root", "fakefs", MS_SILENT, NULL); if (err < 0) { - pr_emerg("fakefs: failed to mount root from %s: %s\n", fakefs_path, errname(err)); + pr_emerg("ish: failed to mount fakefs root from %s: %s\n", fakefs_path, errname(err)); return err; } ksys_chdir("/root"); @@ -27,7 +27,7 @@ static __init int ish_rootfs(void) { devtmpfs_mount(); err = do_mount("proc", "proc", "proc", MS_SILENT, NULL); if (err < 0) { - pr_warn("procfs: failed to mount: %s", errname(err)); + pr_warn("ish: failed to mount procfs: %s", errname(err)); } do_mount(".", "/", NULL, MS_MOVE, NULL); diff --git a/app/LinuxTTY.c b/app/LinuxTTY.c index 186bbac5..125d05a9 100644 --- a/app/LinuxTTY.c +++ b/app/LinuxTTY.c @@ -42,11 +42,8 @@ static struct ios_tty ios_ttys[NUM_TTYS]; static int ios_tty_port_activate(struct tty_port *port, struct tty_struct *tty) { BUG_ON(port != &ios_ttys[tty->index].port); - sync_do_in_ios(^(void (^done)(void)) { - port->client_data = (void *) Terminal_terminalWithType_number(TTY_MAJOR, tty->index); - Terminal_setLinuxTTY(port->client_data, &container_of(port, struct ios_tty, port)->linux_tty); - done(); - }); + port->client_data = (void *) Terminal_terminalWithType_number(TTY_MAJOR, tty->index); + Terminal_setLinuxTTY(port->client_data, &container_of(port, struct ios_tty, port)->linux_tty); return 0; } static void ios_tty_port_destruct(struct tty_port *port) { @@ -62,7 +59,7 @@ static struct tty_port_operations ios_tty_port_ops = { .destruct = ios_tty_port_destruct, }; -static void ios_tty_cb_wakeup(struct linux_tty *linux_tty) { +static void ios_tty_cb_can_output(struct linux_tty *linux_tty) { struct ios_tty *tty = container_of(linux_tty, struct ios_tty, linux_tty); tty_port_tty_wakeup(&tty->port); } @@ -73,9 +70,14 @@ static void ios_tty_cb_send_input(struct linux_tty *linux_tty, const char *data, tty_flip_buffer_push(&tty->port); } +static void ios_tty_cb_hangup(struct linux_tty *linux_tty) { + // nah +} + static struct linux_tty_callbacks ios_tty_callbacks = { - .wakeup = ios_tty_cb_wakeup, + .can_output = ios_tty_cb_can_output, .send_input = ios_tty_cb_send_input, + .hangup = ios_tty_cb_hangup, }; static int ios_tty_open(struct tty_struct *tty, struct file *filp) { @@ -87,8 +89,7 @@ static int ios_tty_write(struct tty_struct *tty, const unsigned char *data, int } static int ios_tty_write_room(struct tty_struct *tty) { - int room = Terminal_roomForOutput(tty->port->client_data); - return room; + return Terminal_roomForOutput(tty->port->client_data); } static struct tty_operations ios_tty_ops = { @@ -98,10 +99,7 @@ static struct tty_operations ios_tty_ops = { }; static int ios_tty_console_setup(struct console *console, char *what) { - sync_do_in_ios(^(void (^done)(void)) { - console->data = (void *) Terminal_terminalWithType_number(TTY_MAJOR, 1); - done(); - }); + console->data = (void *) Terminal_terminalWithType_number(TTY_MAJOR, 1); return 0; } diff --git a/app/Terminal.h b/app/Terminal.h index 670c326c..8171169b 100644 --- a/app/Terminal.h +++ b/app/Terminal.h @@ -13,8 +13,10 @@ struct tty; @interface Terminal : NSObject + (Terminal *)terminalWithType:(int)type number:(int)number; +#if !ISH_LINUX // Returns a strong struct tty and a Terminal that has a weak reference to the same tty + (Terminal *)createPseudoTerminal:(struct tty **)tty; +#endif + (Terminal *)terminalWithUUID:(NSUUID *)uuid; @property (readonly) NSUUID *uuid; @@ -22,7 +24,7 @@ struct tty; + (void)convertCommand:(NSArray *)command toArgs:(char *)argv limitSize:(size_t)maxSize; - (int)sendOutput:(const void *)buf length:(int)len; -- (void)sendInput:(const char *)buf length:(size_t)len; +- (void)sendInput:(NSData *)input; - (NSString *)arrow:(char)direction; diff --git a/app/Terminal.m b/app/Terminal.m index bdf7cbb3..17658fb9 100644 --- a/app/Terminal.m +++ b/app/Terminal.m @@ -28,7 +28,6 @@ typedef struct linux_tty *tty_t; #endif } -@property WKWebView *webView; @property BOOL loaded; @property (nonatomic) tty_t tty; @property (nonatomic) NSMutableData *pendingData; @@ -62,27 +61,39 @@ typedef struct linux_tty *tty_t; @end @implementation Terminal +@synthesize webView = _webView; -static const int BUF_SIZE = 4096; +static const int BUF_SIZE = 1<<14; static NSMapTable *terminals; static NSMapTable *terminalsByUUID; - (instancetype)initWithType:(int)type number:(int)num { - self.terminalsKey = @(dev_make(type, num)); - Terminal *terminal = [terminals objectForKey:self.terminalsKey]; - if (terminal) - return terminal; - - if (self = [super init]) { - self.pendingData = [[NSMutableData alloc] initWithCapacity:BUF_SIZE]; - self.refreshTask = [[DelayedUITask alloc] initWithTarget:self action:@selector(refresh)]; - self.scrollToBottomTask = [[DelayedUITask alloc] initWithTarget:self action:@selector(scrollToBottom)]; + @synchronized (Terminal.class) { + self.terminalsKey = @(dev_make(type, num)); + Terminal *terminal = [terminals objectForKey:self.terminalsKey]; + if (terminal) + return terminal; + + if (self = [super init]) { + self.pendingData = [[NSMutableData alloc] initWithCapacity:BUF_SIZE]; + self.refreshTask = [[DelayedUITask alloc] initWithTarget:self action:@selector(refresh)]; + self.scrollToBottomTask = [[DelayedUITask alloc] initWithTarget:self action:@selector(scrollToBottom)]; #if !ISH_LINUX - lock_init(&_dataLock); - cond_init(&_dataConsumed); + lock_init(&_dataLock); + cond_init(&_dataConsumed); #endif - + + [terminals setObject:self forKey:self.terminalsKey]; + self.uuid = [NSUUID UUID]; + [terminalsByUUID setObject:self forKey:self.uuid]; + } + return self; + } +} + +- (WKWebView *)webView { + if (_webView == nil) { WKWebViewConfiguration *config = [WKWebViewConfiguration new]; [config.userContentController addScriptMessageHandler:self name:@"load"]; [config.userContentController addScriptMessageHandler:self name:@"log"]; @@ -91,16 +102,12 @@ static NSMapTable *terminalsByUUID; [config.userContentController addScriptMessageHandler:self name:@"propUpdate"]; // Make the web view really big so that if a program tries to write to the terminal before it's displayed, the text probably won't wrap too badly. CGRect webviewSize = CGRectMake(0, 0, 10000, 10000); - self.webView = [[CustomWebView alloc] initWithFrame:webviewSize configuration:config]; - self.webView.scrollView.scrollEnabled = NO; + _webView = [[CustomWebView alloc] initWithFrame:webviewSize configuration:config]; + _webView.scrollView.scrollEnabled = NO; NSURL *xtermHtmlFile = [NSBundle.mainBundle URLForResource:@"term" withExtension:@"html"]; - [self.webView loadFileURL:xtermHtmlFile allowingReadAccessToURL:xtermHtmlFile]; - - [terminals setObject:self forKey:self.terminalsKey]; - self.uuid = [NSUUID UUID]; - [terminalsByUUID setObject:self forKey:self.uuid]; + [_webView loadFileURL:xtermHtmlFile allowingReadAccessToURL:xtermHtmlFile]; } - return self; + return _webView; } #if !ISH_LINUX @@ -113,7 +120,9 @@ static NSMapTable *terminalsByUUID; #endif - (void)setTty:(tty_t)tty { - _tty = tty; + @synchronized (self) { + _tty = tty; + } dispatch_async(dispatch_get_main_queue(), ^{ [self syncWindowSize]; }); @@ -129,7 +138,7 @@ static NSMapTable *terminalsByUUID; NSLog(@"%@", message.body); } else if ([message.name isEqualToString:@"sendInput"]) { NSData *data = [message.body dataUsingEncoding:NSUTF8StringEncoding]; - [self sendInput:data.bytes length:data.length]; + [self sendInput:data]; } else if ([message.name isEqualToString:@"resize"]) { [self syncWindowSize]; } else if ([message.name isEqualToString:@"propUpdate"]) { @@ -142,9 +151,8 @@ static NSMapTable *terminalsByUUID; #if !ISH_LINUX int cols = dimensions[0].intValue; int rows = dimensions[1].intValue; - if (self.tty == NULL) { + if (self.tty == NULL) return; - } lock(&self.tty->lock); tty_set_winsize(self.tty, (struct winsize_) {.col = cols, .row = rows}); unlock(&self.tty->lock); @@ -193,14 +201,15 @@ static NSMapTable *terminalsByUUID; } #endif -- (void)sendInput:(const char *)buf length:(size_t)len { +- (void)sendInput:(NSData *)input { if (self.tty == NULL) return; #if !ISH_LINUX - tty_input(self.tty, buf, len, 0); + tty_input(self.tty, input.bytes, input.length, 0); #else - async_do_in_irq(^{ - self.tty->ops->send_input(self.tty, buf, len); + async_do_in_workqueue(^{ + NSData *inputRef = input; + self.tty->ops->send_input(self.tty, inputRef.bytes, inputRef.length); }); #endif [self.webView evaluateJavaScript:@"exports.setUserGesture()" completionHandler:nil]; @@ -259,7 +268,7 @@ static NSMapTable *terminalsByUUID; } if (self->_tty) { async_do_in_irq(^{ - self->_tty->ops->wakeup(self->_tty); + self->_tty->ops->can_output(self->_tty); }); } #endif @@ -289,19 +298,27 @@ static NSMapTable *terminalsByUUID; } + (Terminal *)terminalWithUUID:(NSUUID *)uuid { - return [terminalsByUUID objectForKey:uuid]; + @synchronized (Terminal.class) { + return [terminalsByUUID objectForKey:uuid]; + } } - (void)destroy { -#if !ISH_LINUX - struct tty *tty = self.tty; + tty_t tty = self.tty; if (tty != NULL) { - lock(&tty->lock); - tty_hangup(tty); - unlock(&tty->lock); - } +#if !ISH_LINUX + if (tty != NULL) { + lock(&tty->lock); + tty_hangup(tty); + unlock(&tty->lock); + } +#else + tty->ops->hangup(tty); #endif - [terminals removeObjectForKey:self.terminalsKey]; + } + @synchronized (Terminal.class) { + [terminals removeObjectForKey:self.terminalsKey]; + } } + (void)initialize { diff --git a/app/TerminalView.m b/app/TerminalView.m index 1d609333..7267c76f 100644 --- a/app/TerminalView.m +++ b/app/TerminalView.m @@ -271,7 +271,7 @@ static NSString *const HANDLERS[] = {@"syncFocus", @"focus", @"newScrollHeight", text = [text stringByReplacingOccurrencesOfString:@"\n" withString:@"\r"]; NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding]; - [self.terminal sendInput:data.bytes length:data.length]; + [self.terminal sendInput:data]; } - (void)insertControlChar:(char)ch { @@ -281,7 +281,7 @@ static NSString *const HANDLERS[] = {@"syncFocus", @"focus", @"newScrollHeight", if (ch == '6') ch = '^'; if (ch != '\0') ch = toupper(ch) ^ 0x40; - [self.terminal sendInput:&ch length:1]; + [self.terminal sendInput:[NSData dataWithBytes:&ch length:1]]; } } diff --git a/app/TerminalViewController.m b/app/TerminalViewController.m index 1a133ab0..72ad7cdf 100644 --- a/app/TerminalViewController.m +++ b/app/TerminalViewController.m @@ -14,6 +14,7 @@ #import "AboutViewController.h" #import "CurrentRoot.h" #import "NSObject+SaneKVO.h" +#import "LinuxInterop.h" #include "kernel/init.h" #include "kernel/task.h" #include "kernel/calls.h" @@ -162,6 +163,8 @@ } - (int)startSession { + NSArray *command = UserPreferences.shared.launchCommand; + #if !ISH_LINUX int err = become_new_init_child(); if (err < 0) @@ -179,9 +182,8 @@ if (err < 0) return err; tty_release(tty); - + char argv[4096]; - NSArray *command = UserPreferences.shared.launchCommand; [Terminal convertCommand:command toArgs:argv limitSize:sizeof(argv)]; const char *envp = "TERM=xterm-256color\0"; err = do_execve(command[0].UTF8String, command.count, argv, envp); @@ -190,7 +192,33 @@ self.sessionPid = current->pid; task_start(current); #else - self.sessionTerminal = [Terminal terminalWithType:4 number:1]; + const char *argv_arr[command.count + 1]; + for (NSUInteger i = 0; i < command.count; i++) + argv_arr[i] = command[i].UTF8String; + argv_arr[command.count] = NULL; + const char *envp_arr[] = { + "TERM=xterm256-color", + NULL, + }; + const char *const *argv = argv_arr; + const char *const *envp = envp_arr; + __block Terminal *terminal = nil; + __block int sessionPid = 0; + __block int err = 1; + sync_do_in_workqueue(^(void (^done)(void)) { + start_session(argv[0], argv, envp, ^(int retval, int pid, nsobj_t term) { + err = retval; + if (term) + terminal = CFBridgingRelease(term); + sessionPid = pid; + done(); + }); + }); + NSAssert(err <= 0, @"session start did not finish??"); + if (err < 0) + return err; + self.sessionTerminal = terminal; + self.sessionPid = sessionPid; #endif return 0; } diff --git a/deps/linux b/deps/linux index c96a7267..c401da8c 160000 --- a/deps/linux +++ b/deps/linux @@ -1 +1 @@ -Subproject commit c96a72674849165d0c1966cc2228b533e2512cfd +Subproject commit c401da8ce610b9fe6bdbb3d46c0bcc8292e7ca67 diff --git a/iSH.xcodeproj/project.pbxproj b/iSH.xcodeproj/project.pbxproj index 3455dd37..573acdff 100644 --- a/iSH.xcodeproj/project.pbxproj +++ b/iSH.xcodeproj/project.pbxproj @@ -181,6 +181,8 @@ BBECF3AE2691330F00DEC937 /* libiSHLinux.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBECF3A22691314C00DEC937 /* libiSHLinux.a */; }; BBECF3B9269136FB00DEC937 /* liblinux.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBECF3B8269136E100DEC937 /* liblinux.a */; }; BBECF3BC26913F1600DEC937 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = BB792B541F96D90D00FFB7A4 /* AppDelegate.m */; }; + BBEEA9E8277D25090069495B /* LinuxRoot.c in Sources */ = {isa = PBXBuildFile; fileRef = BBEEA9E7277D25090069495B /* LinuxRoot.c */; }; + BBEEA9EA277DAB400069495B /* LinuxPTY.c in Sources */ = {isa = PBXBuildFile; fileRef = BBEEA9E9277DAB400069495B /* LinuxPTY.c */; }; BBEF1970268066D1001225BD /* libiSHApp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBEF191E26806364001225BD /* libiSHApp.a */; }; BBEF1972268066D1001225BD /* libish_emu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BBFB2C5B2590257E00545EAB /* libish_emu.a */; }; BBEF1973268066D1001225BD /* libarchive.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BB10E5C8248DBAA1009C7A74 /* libarchive.a */; }; @@ -654,6 +656,8 @@ BBECF3A22691314C00DEC937 /* libiSHLinux.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libiSHLinux.a; sourceTree = BUILT_PRODUCTS_DIR; }; BBECF3B8269136E100DEC937 /* liblinux.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = liblinux.a; sourceTree = BUILT_PRODUCTS_DIR; }; BBECF3BA2691379100DEC937 /* StaticLibLinux.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = StaticLibLinux.xcconfig; sourceTree = ""; }; + BBEEA9E7277D25090069495B /* LinuxRoot.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = LinuxRoot.c; sourceTree = ""; }; + BBEEA9E9277DAB400069495B /* LinuxPTY.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = LinuxPTY.c; sourceTree = ""; }; BBEF191E26806364001225BD /* libiSHApp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libiSHApp.a; sourceTree = BUILT_PRODUCTS_DIR; }; BBEF192C26806546001225BD /* AppLib.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppLib.xcconfig; sourceTree = ""; }; BBEF199C268066D1001225BD /* iSH.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iSH.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1193,6 +1197,8 @@ BB123ACD26C9F13500419CDA /* IOSCalls.m */, BBECF39D2691313B00DEC937 /* LinuxInterop.c */, BB123ACB26C9EFD900419CDA /* LinuxTTY.c */, + BBEEA9E9277DAB400069495B /* LinuxPTY.c */, + BBEEA9E7277D25090069495B /* LinuxRoot.c */, BB8C3AFD26B7B8AF00E38DDC /* fakefs.c */, ); name = LinuxInterop; @@ -2017,7 +2023,9 @@ files = ( BBECF3AD269132F400DEC937 /* LinuxInterop.c in Sources */, BB8C3AFF26B7B8CF00E38DDC /* fakefs.c in Sources */, + BBEEA9E8277D25090069495B /* LinuxRoot.c in Sources */, BB123ACC26C9EFD900419CDA /* LinuxTTY.c in Sources */, + BBEEA9EA277DAB400069495B /* LinuxPTY.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/linux/fakefs.c b/linux/fakefs.c index 6d95f541..f91a9f06 100644 --- a/linux/fakefs.c +++ b/linux/fakefs.c @@ -653,4 +653,4 @@ static int fakefs_init(void) { return register_filesystem(&fakefs_type); } -__initcall(fakefs_init); +fs_initcall(fakefs_init);