diff --git a/kernel-src/include/kernel/jobctl.h b/kernel-src/include/kernel/jobctl.h index 6e9f34d3..be9eb935 100644 --- a/kernel-src/include/kernel/jobctl.h +++ b/kernel-src/include/kernel/jobctl.h @@ -14,5 +14,7 @@ pid_t jobctl_getsid(proc_t *proc); void jobctl_setctty(proc_t *proc, void *ctty); void *jobctl_getctty(proc_t *proc); int jobctl_signal(proc_t *proc, int signal); +proc_t *jobctl_getforeground(proc_t *proc); +int jobctl_setforeground(proc_t *session, proc_t *pgrp); #endif diff --git a/kernel-src/include/kernel/tty.h b/kernel-src/include/kernel/tty.h index 8a69e40d..370a96ec 100644 --- a/kernel-src/include/kernel/tty.h +++ b/kernel-src/include/kernel/tty.h @@ -28,6 +28,8 @@ typedef struct { int minor; winsize_t winsize; vnode_t *mastervnode; + spinlock_t sessionlock; + struct proc_t *session; } tty_t; void tty_init(); diff --git a/kernel-src/io/net/tcp.c b/kernel-src/io/net/tcp.c new file mode 100644 index 00000000..1e1f613d --- /dev/null +++ b/kernel-src/io/net/tcp.c @@ -0,0 +1,127 @@ +#include +#include +#include + +#define WORKER_COUNT 8 +#define WORKER_BUFFER_SIZE (64 * 1024) +#define WORKER_INTERNAL_BUFFER_SIZE 2048 +// MSS will be min(MSS_LIMIT, mtu - sizeof(ipv4header_t) - sizeof(tcpheader_t)) +#define MSS_LIMIT = (WORKER_INTERNAL_BUFFER_SIZE - sizeof(tcpheader_t)) + +typedef struct { + spinlock_t lock; + semaphore_t semaphore; + // format: + // ipv4 pseudo header + // tcp packet + ringbuffer_t ringbuffer; +} tcpworker_t; + +static tcpworker_t worker[WORKER_COUNT]; +static uint64_t currentworker; + +typedef struct { + uint32_t peer; + uint16_t peerport; + uint16_t localport; +} connkey_t; + +typedef struct { + uint16_t srcport; + uint16_t dstport; + uint32_t seq; + uint32_t ack; + uint8_t dataoffset; + uint8_t control; + uint16_t window; + uint16_t checksum; + uint16_t urgentptr; +} __attribute__((packed)) tcpheader_t; + +typedef struct { + uint32_t source; + uint32_t dest; + uint8_t zero; + uint8_t protocol; + uint16_t length; +} __attribute__((packed)) ipv4pseudoheader_t; + +#define TCP_STATE_LISTEN 0 // listening for connections +#define TCP_STATE_SYNSENT 1 // sent a connection request +#define TCP_STATE_SYNRECEIVED 2 // received a connection request, waiting for ACK +#define TCP_STATE_ESTABILISHED 3 // got connection ACK, connection is oppen +#define TCP_STATE_FINWAIT1 4 // waiting for connection termination from peer or an ack for the already sent FIN +#define TCP_STATE_FINWAIT2 5 // waiting for connection termination from peer +#define TCP_STATE_CLOSEWAIT 7 // waiting for a termination request from local user +#define TCP_STATE_CLOSING 8 // waiting for termination ack + +typedef struct { + spinlock_t lock; + int state; + ringbuffer_t ringbuffer; + + uint32_t snduna; + uint32_t sndnext; + uint32_t sndwindow; + uint32_t sndurgent; + uint32_t sndseqwl; + uint32_t sndackwl; + uint32_t iss; + + uint32_t rcvnext; + uint32_t rcvwindow; + uint32_t rcvurgent; + uint32_t irs; +} tcb_t; + +// TODO dpc: +// construct ipv4header +// calculate checksum +// check against WORKER_INTERNAL_BUFFER_SIZE + +// ran in DPC context +void tcp_process(netdev_t *netdev, void *buffer, ipv4frame_t *ipv4frame) { + +} + +__attribute__((noreturn)) static void tcp_worker() { + tcpworker_t *self = _cpu()->thread->kernelarg; + ipv4pseudoheader_t ipv4; + uint8_t buffer[WORKER_INTERNAL_BUFFER_SIZE]; + + for (;;) { + // wait for a task + semaphore_wait(&self->semaphore, false); + + interrupt_set(false); + spinlock_acquire(&self->lock); + + // read pseudo ipv4 header constructed by tcp_process + // the checksum has already been calculated by it + __assert(ringbuffer_read(&self->ringbuffer, &ipv4, sizeof(ipv4pseudoheader_t)) == sizeof(ipv4pseudoheader_t)); + + // and then the actual tcp packet + __assert(ringbuffer_read(&self->ringbuffer, buffer, ipv4.length) == ipv4.length); + + spinlock_release(&self->lock); + interrupt_set(true); + + tcpheader_t *tcpheader = buffer; + + // TODO get tcb of connection (if any) + } +} + +void tcp_init() { + for (int i = 0; i < WORKER_COUNT; ++i) { + // initialize worker threads + + SPINLOCK_INIT(worker[i].lock); + SEMAPHORE_INIT(&worker[i].semaphore, 0); + __assert(ringbuffer_init(&worker[i].ringbuffer, WORKER_BUFFER_SIZE) == 0); + thread_t *thread = sched_newthread(tcp_worker, PAGE_SIZE * 3, 0, NULL, NULL); + __assert(thread); + thread->kernelarg = &worker[i]; + sched_queue(thread); + } +} diff --git a/kernel-src/io/pty.c b/kernel-src/io/pty.c index ec8b529c..d67b5452 100644 --- a/kernel-src/io/pty.c +++ b/kernel-src/io/pty.c @@ -96,7 +96,6 @@ static int close(int minor, int flags) { if (pty == NULL) return ENODEV; - // TODO do tty cleanup stuff like sending sighup bool intstatus = interrupt_set(false); spinlock_acquire(&pty->lock); pty->hangup = true; diff --git a/kernel-src/io/tty.c b/kernel-src/io/tty.c index b6185493..fb8f6a9a 100644 --- a/kernel-src/io/tty.c +++ b/kernel-src/io/tty.c @@ -376,6 +376,7 @@ tty_t *tty_create(char *name, ttydevicewritefn_t writefn, ttyinactivefn_t inacti strcpy(tty->name, name); MUTEX_INIT(&tty->readmutex); MUTEX_INIT(&tty->writemutex); + SPINLOCK_INIT(tty->sessionlock); tty->termios.c_iflag = ICRNL; tty->termios.c_oflag = ONLCR; @@ -412,6 +413,22 @@ tty_t *tty_create(char *name, ttydevicewritefn_t writefn, ttyinactivefn_t inacti void tty_unregister(tty_t *tty) { devfs_remove(tty->name, DEV_MAJOR_TTY, tty->minor); + bool intstatus = interrupt_set(false); + spinlock_acquire(&tty->sessionlock); + proc_t *session = tty->session; + if (session) { + PROC_HOLD(session); + } + spinlock_release(&tty->sessionlock); + interrupt_set(intstatus); + + if (session) { + proc_t *foreground = jobctl_getforeground(session); + jobctl_signal(foreground, SIGHUP); + + PROC_RELEASE(session); + PROC_RELEASE(foreground); + } } diff --git a/kernel-src/sys/jobctl.c b/kernel-src/sys/jobctl.c index 7291e1d8..5b1fe52a 100644 --- a/kernel-src/sys/jobctl.c +++ b/kernel-src/sys/jobctl.c @@ -49,11 +49,19 @@ void jobctl_setctty(proc_t *proc, void *_ctty) { if (tty == NULL && proc->session.controllingtty) { unholdtty = proc->session.controllingtty; proc->session.controllingtty = NULL; + spinlock_acquire(&unholdtty->sessionlock); + unholdtty->session = NULL; + spinlock_release(&unholdtty->sessionlock); } if (tty && proc->session.controllingtty == NULL) { - VOP_HOLD(tty->mastervnode); - proc->session.controllingtty = tty; + spinlock_acquire(&tty->sessionlock); + if (tty->session == NULL) { + VOP_HOLD(tty->mastervnode); + proc->session.controllingtty = tty; + tty->session = proc; + } + spinlock_release(&tty->sessionlock); } leave: @@ -226,6 +234,8 @@ void jobctl_detach(proc_t *proc) { // called on process exit (when the last userland thread is exiting) proc_t *unholdpgrp = NULL; proc_t *unholdsession = NULL; + proc_t *foreground = NULL; + bool intstatus = interrupt_set(false); spinlock_acquire(&proc->jobctllock); @@ -238,6 +248,10 @@ void jobctl_detach(proc_t *proc) { if (proc->session.leader) { unholdsession = proc->session.leader; proc->session.leader = NULL; + } else { + // send sighup and sigcont to all processes in the foreground group + foreground = proc->session.foreground ? proc->session.foreground : proc; + PROC_HOLD(foreground); } spinlock_release(&proc->jobctllock); @@ -249,6 +263,11 @@ void jobctl_detach(proc_t *proc) { if (unholdsession) { PROC_RELEASE(unholdsession); + } else { + jobctl_setctty(proc, NULL); + jobctl_signal(foreground, SIGHUP); + jobctl_signal(foreground, SIGCONT); + PROC_RELEASE(foreground); } } @@ -272,6 +291,63 @@ void jobctl_addproc(proc_t *parent, proc_t *proc) { interrupt_set(intstatus); } +proc_t *jobctl_getforeground(proc_t *proc) { + proc_t *foreground = NULL; + + bool intstatus = interrupt_set(false); + spinlock_acquire(&proc->jobctllock); + + if (proc->session.leader) + goto leave; + + foreground = proc->session.foreground ? proc->session.foreground : proc; + PROC_HOLD(foreground); + + leave: + spinlock_release(&proc->jobctllock); + interrupt_set(intstatus); + + return foreground; +} +int jobctl_setforeground(proc_t *session, proc_t *pgrp) { + proc_t *unhold = NULL; + int error = 0; + + bool intstatus = interrupt_set(false); + spinlock_acquire(&session->jobctllock); + if (session->session.leader) { + error = EINVAL; + goto leave; + } + + if (session != pgrp) { + spinlock_acquire(&pgrp->jobctllock); + if (pgrp->pgrp.leader || pgrp->session.leader != session) { + error = EPERM; + goto leave; + } + } + + if (session->session.foreground) + unhold = session->session.foreground; + + session->session.foreground = pgrp; + PROC_HOLD(pgrp); + + leave: + if (session != pgrp) + spinlock_release(&pgrp->jobctllock); + + spinlock_release(&session->jobctllock); + interrupt_set(intstatus); + + if (unhold) { + PROC_RELEASE(unhold); + } + + return error; +} + void jobctl_procremove(proc_t *proc) { // called after all refcounts to a proc are released // cleans up session stuff