Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
804de7cec2 | ||
|
|
8b81805b0e |
338
app/irc/irc.cpp
Normal file
338
app/irc/irc.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <lk/debug.h>
|
||||
#include <lk/err.h>
|
||||
#include <lk/trace.h>
|
||||
#include <app.h>
|
||||
#include <lib/minip.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <kernel/mutex.h>
|
||||
|
||||
#include "lktl.h"
|
||||
|
||||
#define LOCAL_TRACE 1
|
||||
|
||||
//#define IRC_SERVER IPV4(176,58,122,119) // irc.libera.chat
|
||||
#define IRC_SERVER IPV4(88,99,244,30) // irc.sortix.org
|
||||
//#define IRC_SERVER IPV4(192,168,1,110) // localhost
|
||||
#define IRC_PORT 6667
|
||||
#define IRC_USER "geist"
|
||||
#define IRC_NICK "geist-lk"
|
||||
#define IRC_CHAN "#sortix"
|
||||
//#define IRC_CHAN "#osdev"
|
||||
|
||||
class irc_client {
|
||||
public:
|
||||
irc_client();
|
||||
~irc_client();
|
||||
|
||||
void init();
|
||||
void shutdown();
|
||||
status_t connect();
|
||||
status_t handshake();
|
||||
status_t read_loop();
|
||||
status_t console_input_line(const char *line, bool &exit);
|
||||
|
||||
void set_server(uint32_t server) { server_ip_ = server; }
|
||||
void set_server_port(uint16_t port) { server_port_ = port; }
|
||||
|
||||
private:
|
||||
mutex_t lock_ = MUTEX_INITIAL_VALUE(lock_);
|
||||
tcp_socket_t *sock_ = nullptr;
|
||||
|
||||
uint32_t server_ip_ = 0;
|
||||
uint16_t server_port_ = 0;
|
||||
enum {
|
||||
INITIAL,
|
||||
CONNECTED,
|
||||
HANDSHOOK,
|
||||
} state_ = INITIAL;
|
||||
};
|
||||
|
||||
irc_client::irc_client() = default;
|
||||
irc_client::~irc_client() = default;
|
||||
|
||||
void irc_client::init() {
|
||||
}
|
||||
|
||||
void irc_client::shutdown() {
|
||||
lktl::auto_lock al(&lock_);
|
||||
|
||||
if (sock_) {
|
||||
if (state_ == HANDSHOOK) {
|
||||
// send quit
|
||||
tcp_write(sock_, "QUIT\r\n", strlen("QUIT\r\n"));
|
||||
// XXX flush socket
|
||||
thread_sleep(1000);
|
||||
}
|
||||
tcp_close(sock_);
|
||||
sock_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
status_t irc_client::connect() {
|
||||
lktl::auto_lock al(&lock_);
|
||||
|
||||
if (server_ip_ == 0 || server_port_ == 0) {
|
||||
return ERR_NOT_CONFIGURED;
|
||||
}
|
||||
|
||||
auto err = tcp_connect(&sock_, server_ip_, server_port_);
|
||||
if (err < 0) {
|
||||
printf("err %d connecting to server\n", err);
|
||||
return ERR_CHANNEL_CLOSED; // TODO: better one
|
||||
}
|
||||
|
||||
state_ = CONNECTED;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t irc_client::read_loop() {
|
||||
char line[1024];
|
||||
int pos = 0;
|
||||
for (;;) {
|
||||
char c;
|
||||
ssize_t r = tcp_read(sock_, &c, sizeof(c));
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (r == 0) {
|
||||
TRACEF("tcp_read returns 0?\n");
|
||||
return ERR_GENERIC;
|
||||
}
|
||||
|
||||
// TODO: make sure we dont overwrite the line
|
||||
|
||||
// append the char to our accumulated line
|
||||
if (c == '\r') {
|
||||
// consume \r
|
||||
continue;
|
||||
}
|
||||
|
||||
// store the char
|
||||
line[pos++] = c;
|
||||
|
||||
if (c != '\n') {
|
||||
// we're done, loop around
|
||||
continue;
|
||||
}
|
||||
|
||||
// we've completed a line, process it
|
||||
line[pos] = 0; // terminate the string
|
||||
pos = 0; // next time around we start over
|
||||
|
||||
lktl::auto_lock al(&lock_);
|
||||
|
||||
if (strncmp(line, "PING", strlen("PING"))== 0) {
|
||||
// handle a PONG
|
||||
tcp_write(sock_, "PONG\r\n", strlen("PONG\r\n"));
|
||||
printf("%s", line);
|
||||
printf("PING/PONG\n");
|
||||
} else {
|
||||
printf("%s", line);
|
||||
}
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t irc_client::console_input_line(const char *line, bool &exit) {
|
||||
//printf("CONSOLE LINE '%s'\n", line);
|
||||
|
||||
if (strlen(line) == 0) {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// see if it starts with /
|
||||
if (line[0] == '/') {
|
||||
if (line[1] == 0) {
|
||||
// malformed command
|
||||
return NO_ERROR;
|
||||
}
|
||||
// look for quit command
|
||||
if (strncmp(&line[1], "quit", strlen("quit")) == 0) {
|
||||
shutdown();
|
||||
exit = true;
|
||||
return NO_ERROR;
|
||||
} else {
|
||||
printf("bad command\n");
|
||||
return NO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
lktl::auto_lock al(&lock_);
|
||||
|
||||
// send it as a privmsg
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "PRIVMSG #sortix :%s\r\n", line);
|
||||
status_t err = tcp_write(sock_, buf, strlen(buf));
|
||||
return err;
|
||||
}
|
||||
|
||||
status_t irc_client::handshake() {
|
||||
lktl::auto_lock al(&lock_);
|
||||
|
||||
// send USER and NICK
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "USER %s host server :geist\r\n", IRC_USER);
|
||||
status_t err = tcp_write(sock_, buf, strlen(buf));
|
||||
if (err < 0) {
|
||||
printf("error %d writing to server\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "NICK %s\r\n", IRC_NICK);
|
||||
err = tcp_write(sock_, buf, strlen(buf));
|
||||
if (err < 0) {
|
||||
printf("error %d writing to server\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "JOIN %s\r\n", IRC_CHAN);
|
||||
err = tcp_write(sock_, buf, strlen(buf));
|
||||
if (err < 0) {
|
||||
printf("error %d writing to server\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
state_ = HANDSHOOK;
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// console worker thread
|
||||
static int console_thread_worker(void *arg) {
|
||||
irc_client *irc = static_cast<irc_client *>(arg);
|
||||
|
||||
LTRACEF("top of console thread\n");
|
||||
|
||||
// read a line from the console, giving it to the irc client object at EOL
|
||||
status_t err;
|
||||
char line[256];
|
||||
int pos = 0;
|
||||
bool exit = false;
|
||||
while (!exit) {
|
||||
int c = getchar();
|
||||
if (c <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (pos == sizeof(line)) {
|
||||
printf("line too long, discarding\n");
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
//printf("char %c (%d)\n", c, c);
|
||||
switch (c) {
|
||||
case '\n':
|
||||
case '\r':
|
||||
if (pos > 0) {
|
||||
putchar('\n');
|
||||
// end of a line with characters in it, feed it to the irc client
|
||||
line[pos++] = 0; // null terminate
|
||||
err = irc->console_input_line(line, exit);
|
||||
if (err < 0) {
|
||||
exit = true;
|
||||
}
|
||||
pos = 0;
|
||||
}
|
||||
break;
|
||||
case '\b': // backspace
|
||||
case 127: // DEL
|
||||
if (pos > 0) {
|
||||
pos--;
|
||||
fputs("\b \b", stdout); // wipe out a character
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
line[pos++] = (char)c;
|
||||
putchar(c);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LTRACEF("console thread exiting\n");
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
||||
static void irc_app_entry(const struct app_descriptor *app, void *args) {
|
||||
LTRACE_ENTRY;
|
||||
printf("welcome to IRC!\n");
|
||||
|
||||
// create a local state object
|
||||
irc_client *irc = new irc_client();
|
||||
if (!irc) {
|
||||
return;
|
||||
}
|
||||
|
||||
status_t err;
|
||||
|
||||
irc->init();
|
||||
|
||||
// clean up and delete the object on the way out
|
||||
auto cleanup = [&irc]() {
|
||||
printf("cleaning up IRC\n");
|
||||
irc->shutdown();
|
||||
delete irc;
|
||||
};
|
||||
auto ac = lktl::make_auto_call(cleanup);
|
||||
|
||||
// configure the parameters
|
||||
irc->set_server(IRC_SERVER);
|
||||
irc->set_server_port(IRC_PORT);
|
||||
|
||||
err = irc->connect();
|
||||
if (err < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
err = irc->handshake();
|
||||
if (err < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// start two threads
|
||||
thread_t *console_thread;
|
||||
thread_t *server_thread;
|
||||
|
||||
console_thread = thread_create("irc console", console_thread_worker, irc, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
|
||||
thread_resume(console_thread);
|
||||
|
||||
auto server_thread_worker = [](void *arg) -> int {
|
||||
irc_client *_irc = static_cast<irc_client *>(arg);
|
||||
printf("top of server thread\n");
|
||||
|
||||
_irc->read_loop();
|
||||
|
||||
return 0;
|
||||
};
|
||||
server_thread = thread_create("irc socket", server_thread_worker, irc, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
|
||||
thread_resume(server_thread);
|
||||
|
||||
thread_join(server_thread, nullptr, INFINITE_TIME);
|
||||
thread_join(console_thread, nullptr, INFINITE_TIME);
|
||||
|
||||
LTRACE_EXIT;
|
||||
}
|
||||
|
||||
APP_START(irc)
|
||||
.init = nullptr,
|
||||
.entry = irc_app_entry,
|
||||
.flags = APP_FLAG_NO_AUTOSTART,
|
||||
.stack_size = 0,
|
||||
APP_END
|
||||
|
||||
92
app/irc/lktl.h
Normal file
92
app/irc/lktl.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
// Copyright 2016 The Fuchsia Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <kernel/thread.h>
|
||||
#include <kernel/mutex.h>
|
||||
|
||||
// Macro used to simplify the task of deleting all of the default copy
|
||||
// constructors and assignment operators.
|
||||
#define DISALLOW_COPY_ASSIGN_AND_MOVE(_class_name) \
|
||||
_class_name(const _class_name&) = delete; \
|
||||
_class_name(_class_name&&) = delete; \
|
||||
_class_name& operator=(const _class_name&) = delete; \
|
||||
_class_name& operator=(_class_name&&) = delete
|
||||
|
||||
// Macro used to simplify the task of deleting the non rvalue reference copy
|
||||
// constructors and assignment operators. (IOW - forcing move semantics)
|
||||
#define DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(_class_name) \
|
||||
_class_name(const _class_name&) = delete; \
|
||||
_class_name& operator=(const _class_name&) = delete
|
||||
|
||||
// Macro used to simplify the task of deleting the new and new[]
|
||||
// operators. (IOW - disallow heap allocations)
|
||||
#define DISALLOW_NEW \
|
||||
static void* operator new(size_t) = delete; \
|
||||
static void* operator new[](size_t) = delete
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace lktl {
|
||||
|
||||
// call a routine when the object goes out of scope
|
||||
template <typename T>
|
||||
class auto_call {
|
||||
public:
|
||||
constexpr explicit auto_call(T call) : call_(call) {}
|
||||
~auto_call() {
|
||||
if (armed_) {
|
||||
call_();
|
||||
}
|
||||
}
|
||||
|
||||
// move
|
||||
auto_call(auto_call &&ac) : armed_(ac.armed_), call_(ac.call_) {
|
||||
ac.cancel();
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
armed_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(auto_call);
|
||||
|
||||
bool armed_ = true;
|
||||
T call_;
|
||||
};
|
||||
|
||||
// create an auto caller with implicit template specialization.
|
||||
//
|
||||
// example:
|
||||
// auto ac = make_auto_call([]() { printf("a lambda!\n"); });
|
||||
template <typename T>
|
||||
inline auto_call<T> make_auto_call(T c) {
|
||||
return auto_call<T>(c);
|
||||
}
|
||||
|
||||
// auto mutex scope guard
|
||||
class auto_lock {
|
||||
public:
|
||||
explicit auto_lock(mutex_t *m) : m_(m) {
|
||||
mutex_acquire(m_);
|
||||
}
|
||||
|
||||
~auto_lock() {
|
||||
mutex_release(m_);
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_ASSIGN_AND_MOVE(auto_lock);
|
||||
|
||||
mutex_t *m_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace lktl
|
||||
12
app/irc/rules.mk
Normal file
12
app/irc/rules.mk
Normal file
@@ -0,0 +1,12 @@
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
MODULE := $(LOCAL_DIR)
|
||||
|
||||
MODULE_SRCS += \
|
||||
$(LOCAL_DIR)/irc.cpp \
|
||||
|
||||
MODULE_DEPS := \
|
||||
lib/libcpp \
|
||||
lib/minip
|
||||
|
||||
include make/module.mk
|
||||
12
app/telnetd/rules.mk
Normal file
12
app/telnetd/rules.mk
Normal file
@@ -0,0 +1,12 @@
|
||||
LOCAL_DIR := $(GET_LOCAL_DIR)
|
||||
|
||||
MODULE := $(LOCAL_DIR)
|
||||
|
||||
MODULE_SRCS += $(LOCAL_DIR)/telnetd.cpp
|
||||
|
||||
MODULE_DEPS := \
|
||||
lib/cksum \
|
||||
lib/libcpp \
|
||||
lib/minip
|
||||
|
||||
include make/module.mk
|
||||
72
app/telnetd/telnetd.cpp
Normal file
72
app/telnetd/telnetd.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Travis Geiselbrecht
|
||||
*
|
||||
* Use of this source code is governed by a MIT-style
|
||||
* license that can be found in the LICENSE file or at
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
#include <app.h>
|
||||
#include <lk/err.h>
|
||||
#include <lk/debug.h>
|
||||
#include <lk/trace.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <lk/compiler.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <lib/minip.h>
|
||||
#include <lib/tftp.h>
|
||||
#include <lib/cksum.h>
|
||||
#include <platform.h>
|
||||
|
||||
#define LOCAL_TRACE 1
|
||||
|
||||
static int telnet_worker(void *arg) {
|
||||
tcp_socket_t *s = static_cast<tcp_socket_t *>(arg);
|
||||
|
||||
char buf[128];
|
||||
for (;;) {
|
||||
ssize_t err = tcp_read(s, buf, sizeof(buf));
|
||||
if (err < 0) {
|
||||
printf("TELENT: error from read, exiting\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
hexdump8(buf, err);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void telnetd_entry(const struct app_descriptor *app, void *args) {
|
||||
printf("TELNET: waiting for network configuration\n");
|
||||
minip_wait_for_configured(INFINITE_TIME);
|
||||
printf("TELNET: starting telnet server\n");
|
||||
|
||||
// starting telnet stack
|
||||
tcp_socket_t *listen_socket;
|
||||
status_t err = tcp_open_listen(&listen_socket, 23);
|
||||
if (err < 0) {
|
||||
printf("tcp_open_listen returns %d\n", err);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
tcp_socket_t *accept_socket;
|
||||
|
||||
err = tcp_accept(listen_socket, &accept_socket);
|
||||
LTRACEF("tcp_accept returns returns %d, handle %p\n", err, accept_socket);
|
||||
if (err < 0) {
|
||||
TRACEF("error accepting socket, retrying\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("TELNET: starting worker\n");
|
||||
thread_detach_and_resume(thread_create("chargen_worker", &telnet_worker, accept_socket, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
|
||||
}
|
||||
}
|
||||
|
||||
APP_START(inetsrv)
|
||||
.init = nullptr,
|
||||
.entry = telnetd_entry,
|
||||
.flags = 0,
|
||||
.stack_size = 0,
|
||||
APP_END
|
||||
@@ -1,11 +1,12 @@
|
||||
# main project for qemu-riscv64-supervisor
|
||||
MODULES += \
|
||||
app/irc \
|
||||
app/shell
|
||||
SUBARCH := 64
|
||||
RISCV_MODE := supervisor
|
||||
|
||||
include project/virtual/test.mk
|
||||
include project/virtual/fs.mk
|
||||
include project/virtual/minip.mk
|
||||
include project/virtual/inetapps.mk
|
||||
include project/target/qemu-virt-riscv.mk
|
||||
|
||||
|
||||
8
project/virtual/inetapps.mk
Normal file
8
project/virtual/inetapps.mk
Normal file
@@ -0,0 +1,8 @@
|
||||
# some internet apps that depend on minip
|
||||
|
||||
MODULES += \
|
||||
app/inetsrv \
|
||||
app/irc \
|
||||
app/telnetd \
|
||||
|
||||
include project/virtual/minip.mk
|
||||
@@ -1,6 +1,4 @@
|
||||
# modules related to the minip stack
|
||||
|
||||
MODULES += \
|
||||
lib/minip \
|
||||
app/inetsrv
|
||||
|
||||
lib/minip
|
||||
|
||||
Reference in New Issue
Block a user