mirror of
https://github.com/ish-app/ish.git
synced 2026-05-28 21:10:35 +00:00
Hook up linux pseudoterminals
This commit is contained in:
+1
-1
@@ -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
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#if ISH_LINUX
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <pthread.h>
|
||||
#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
|
||||
|
||||
+63
-15
@@ -8,11 +8,14 @@
|
||||
#include "LinuxInterop.h"
|
||||
#include <Block.h>
|
||||
#include <linux/start_kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/umh.h>
|
||||
#include <asm/irq.h>
|
||||
#include <user/fs.h>
|
||||
#include <user/irq.h>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+15
-4
@@ -12,29 +12,40 @@
|
||||
#include <sys/types.h>
|
||||
#else
|
||||
#include <linux/types.h>
|
||||
#include <linux/fs.h>
|
||||
#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);
|
||||
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
//
|
||||
// LinuxPTY.c
|
||||
// libiSHLinux
|
||||
//
|
||||
// Created by Theodore Dubois on 12/30/21.
|
||||
//
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/errname.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/init_task.h>
|
||||
#include <linux/termios.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <uapi/linux/mount.h>
|
||||
#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);
|
||||
+2
-2
@@ -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);
|
||||
|
||||
+11
-13
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -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<NSString *> *)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;
|
||||
|
||||
|
||||
+56
-39
@@ -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<NSNumber *, Terminal *> *terminals;
|
||||
static NSMapTable<NSUUID *, Terminal *> *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<NSUUID *, Terminal *> *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<NSUUID *, Terminal *> *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<NSUUID *, Terminal *> *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<NSUUID *, Terminal *> *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<NSUUID *, Terminal *> *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<NSUUID *, Terminal *> *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<NSUUID *, Terminal *> *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 {
|
||||
|
||||
+2
-2
@@ -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]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<NSString *> *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<NSString *> *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;
|
||||
}
|
||||
|
||||
Vendored
+1
-1
Submodule deps/linux updated: c96a726748...c401da8ce6
@@ -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 = "<group>"; };
|
||||
BBEEA9E7277D25090069495B /* LinuxRoot.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = LinuxRoot.c; sourceTree = "<group>"; };
|
||||
BBEEA9E9277DAB400069495B /* LinuxPTY.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = LinuxPTY.c; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
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;
|
||||
};
|
||||
|
||||
+1
-1
@@ -653,4 +653,4 @@ static int fakefs_init(void) {
|
||||
return register_filesystem(&fakefs_type);
|
||||
}
|
||||
|
||||
__initcall(fakefs_init);
|
||||
fs_initcall(fakefs_init);
|
||||
|
||||
Reference in New Issue
Block a user