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
|
# main project for qemu-riscv64-supervisor
|
||||||
MODULES += \
|
MODULES += \
|
||||||
|
app/irc \
|
||||||
app/shell
|
app/shell
|
||||||
SUBARCH := 64
|
SUBARCH := 64
|
||||||
RISCV_MODE := supervisor
|
RISCV_MODE := supervisor
|
||||||
|
|
||||||
include project/virtual/test.mk
|
include project/virtual/test.mk
|
||||||
include project/virtual/fs.mk
|
include project/virtual/fs.mk
|
||||||
include project/virtual/minip.mk
|
include project/virtual/inetapps.mk
|
||||||
include project/target/qemu-virt-riscv.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 related to the minip stack
|
||||||
|
|
||||||
MODULES += \
|
MODULES += \
|
||||||
lib/minip \
|
lib/minip
|
||||||
app/inetsrv
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user