Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

native: add option to turn RIOT into a Linux CLI tool #20656

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 6 additions & 15 deletions boards/native/board_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,36 +48,27 @@ MTD_XFA_ADD(mtd0_dev.base, 0);

/* littlefs support */
#if defined(MODULE_LITTLEFS)
VFS_AUTO_MOUNT(littlefs, VFS_MTD(mtd0_dev), VFS_DEFAULT_NVM(0), 0);
VFS_AUTO_MOUNT(littlefs, VFS_MTD(mtd0_dev), CONFIG_NATIVE_MOUNTPOINT, 0);

/* littlefs2 support */
#elif defined(MODULE_LITTLEFS2)
VFS_AUTO_MOUNT(littlefs2, VFS_MTD(mtd0_dev), VFS_DEFAULT_NVM(0), 0);
VFS_AUTO_MOUNT(littlefs2, VFS_MTD(mtd0_dev), CONFIG_NATIVE_MOUNTPOINT, 0);

/* spiffs support */
#elif defined(MODULE_SPIFFS)
VFS_AUTO_MOUNT(spiffs, VFS_MTD(mtd0_dev), VFS_DEFAULT_NVM(0), 0);
VFS_AUTO_MOUNT(spiffs, VFS_MTD(mtd0_dev), CONFIG_NATIVE_MOUNTPOINT, 0);

/* FAT support */
#elif defined(MODULE_FATFS_VFS)
VFS_AUTO_MOUNT(fatfs, VFS_MTD(mtd0_dev), VFS_DEFAULT_NVM(0), 0);
VFS_AUTO_MOUNT(fatfs, VFS_MTD(mtd0_dev), CONFIG_NATIVE_MOUNTPOINT, 0);

/* ext2/3/4 support */
#elif defined(MODULE_LWEXT4)
VFS_AUTO_MOUNT(lwext4, VFS_MTD(mtd0_dev), VFS_DEFAULT_NVM(0), 0);
VFS_AUTO_MOUNT(lwext4, VFS_MTD(mtd0_dev), CONFIG_NATIVE_MOUNTPOINT, 0);

/* host fs pass-through */
#elif defined(MODULE_FS_NATIVE)
VFS_AUTO_MOUNT(native, { .hostpath = FS_NATIVE_DIR }, VFS_DEFAULT_NVM(0), 0);
VFS_AUTO_MOUNT(native, { .hostpath = FS_NATIVE_DIR }, CONFIG_NATIVE_MOUNTPOINT, 0);

#endif
#endif /* MODULE_VFS_DEFAULT */

/**
* Nothing to initialize at the moment.
* Turns the red LED on and the green LED off.
*/
void board_init(void)
{
puts("RIOT native board initialized.");
}
7 changes: 7 additions & 0 deletions boards/native/include/board.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ void _native_LED_RED_TOGGLE(void);
#endif
/** @} */

/**
* @brief The mountpoint at which the native fs gets mounted
*/
#ifndef CONFIG_NATIVE_MOUNTPOINT
#define CONFIG_NATIVE_MOUNTPOINT VFS_DEFAULT_NVM(0)
#endif

#if defined(MODULE_SPIFFS) || DOXYGEN
/**
* @name SPIFFS default configuration
Expand Down
10 changes: 8 additions & 2 deletions core/lib/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@
RIOT_VERSION ")"
#endif

extern int main(void);

static char main_stack[THREAD_STACKSIZE_MAIN];
static char idle_stack[THREAD_STACKSIZE_IDLE];

Expand All @@ -60,7 +58,15 @@ static void *main_trampoline(void *arg)
LOG_INFO(CONFIG_BOOT_MSG_STRING "\n");
}

#ifdef CPU_NATIVE
extern int _native_argc_main;
extern char **_native_argv_main;
extern int main(int argc, char **argv);
int res = main(_native_argc_main, _native_argv_main);
#else
extern int main(void);
int res = main();
#endif

if (IS_USED(MODULE_TEST_UTILS_MAIN_EXIT_CB)) {
void test_utils_main_exit_cb(int res);
Expand Down
2 changes: 1 addition & 1 deletion cpu/native/irq_cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@
nleft = sizeof(int);
i = 0;

while ((nleft > 0) && ((nread = real_read(_sig_pipefd[0], ((uint8_t*)&sig) + i, nleft)) != -1)) {

Check warning on line 251 in cpu/native/irq_cpu.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
i += nread;
nleft -= nread;
}
Expand Down Expand Up @@ -280,7 +280,7 @@
warnx("native_irq_handler: ignoring SIGUSR1");
}
else {
errx(EXIT_FAILURE, "XXX: no handler for signal %i\nXXX: this should not have happened!\n", sig);

Check warning on line 283 in cpu/native/irq_cpu.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
}
}

Expand Down Expand Up @@ -359,7 +359,7 @@
_native_saved_eip = ((ucontext_t *)context)->uc_mcontext.gregs[REG_RIP];
((ucontext_t *)context)->uc_mcontext.gregs[REG_RIP] = (uintptr_t)&_native_sig_leave_tramp;
#else
//printf("\n\033[31mEIP:\t%p\ngo switching\n\n\033[0m", (void*)((ucontext_t *)context)->uc_mcontext.gregs[REG_EIP]);

Check warning on line 362 in cpu/native/irq_cpu.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
_native_saved_eip = ((ucontext_t *)context)->uc_mcontext.gregs[REG_EIP];
((ucontext_t *)context)->uc_mcontext.gregs[REG_EIP] = (unsigned int)&_native_sig_leave_tramp;
#endif
Expand Down Expand Up @@ -555,6 +555,6 @@
err(EXIT_FAILURE, "native_interrupt_init: sigaction");
}

puts("RIOT native interrupts/signals initialized.");
DEBUG_PUTS("RIOT native interrupts/signals initialized.");
}
/** @} */
2 changes: 1 addition & 1 deletion cpu/native/periph/pm.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ void pm_set(unsigned mode)

void pm_off(void)
{
puts("\nnative: exiting");
DEBUG_PUTS("\nnative: exiting");
#ifdef MODULE_PERIPH_SPIDEV_LINUX
spidev_linux_teardown();
#endif
Expand Down
10 changes: 9 additions & 1 deletion cpu/native/startup.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@
typedef void (*init_func_t)(int argc, char **argv, char **envp);
#ifdef __APPLE__
/* Taken from the sources of Apple's dyld launcher
* https://github.com/opensource-apple/dyld/blob/3f928f32597888c5eac6003b9199d972d49857b5/src/dyldInitialization.cpp#L85-L104

Check warning on line 453 in cpu/native/startup.c

View workflow job for this annotation

GitHub Actions / static-tests

line is longer than 100 characters
*/
/* Find the extents of the __DATA __mod_init_func section */
extern init_func_t __init_array_start __asm("section$start$__DATA$__mod_init_func");
Expand All @@ -466,6 +466,10 @@
pm_reboot();
}

/* allow to pass arguments to the application */
int _native_argc_main;
char **_native_argv_main;

__attribute__((constructor)) static void startup(int argc, char **argv, char **envp)
{
_native_init_syscalls();
Expand Down Expand Up @@ -718,8 +722,12 @@
netdev_tap_params[taps].tap_name = &argv[optind + i];
netdev_tap_params[taps].wired = true;
}
optind += taps;
#endif

_native_argc_main = argc - optind;
_native_argv_main = &argv[optind];

#ifdef MODULE_PERIPH_EEPROM
eeprom_native_read();
#endif
Expand All @@ -729,7 +737,7 @@

register_interrupt(SIGUSR1, _reset_handler);

puts("RIOT native hardware initialization complete.\n");
DEBUG_PUTS("RIOT native hardware initialization complete.\n");
irq_enable();
kernel_init();
}
47 changes: 47 additions & 0 deletions examples/riot_as_cli_tool/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# name of your application
APPLICATION = riot_as_cli_tool

# If no BOARD is found in the environment, use this default:
BOARD ?= native

# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../..

# Include packages that pull up and auto-init the link layer.
USEMODULE += netdev_default
USEMODULE += auto_init_gnrc_netif
# Specify the mandatory networking modules for IPv6
USEMODULE += gnrc_ipv6_default
# Additional networking modules that can be dropped if not needed
USEMODULE += gnrc_icmpv6_echo

USEMODULE += auto_init_sock_dns # add fall-back DNS server
USEMODULE += sock_dns # include DNS client
USEMODULE += gnrc_ipv6_nib_dns # include RDNSS option handling

USEMODULE += nanocoap_sock
USEMODULE += nanocoap_vfs

USEMODULE += vfs_default

USEMODULE += shell
USEMODULE += shell_cmds_default

# export the whole fs to RIOT
CFLAGS += -DFS_NATIVE_DIR=\"/\"
# mount it as '/' in RIOT to match the host
CFLAGS += -DCONFIG_NATIVE_MOUNTPOINT=\"/\"
# use the current working directory as download destination
CFLAGS += -DCONFIG_NCGET_DEFAULT_DATA_DIR=\"/proc/self/cwd/\"

# terminate RIOT with main()
CFLAGS += -DCONFIG_CORE_EXIT_WITH_MAIN=1
# don't be verbose
CFLAGS += -DCONFIG_SKIP_BOOT_MSG=1

RIOT_TERMINAL = native
BOARD_WHITELIST = native native64

DEVELHELP ?= 1

include $(RIOTBASE)/Makefile.include
24 changes: 24 additions & 0 deletions examples/riot_as_cli_tool/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
RIOT as a CLI tool
==================

This example demonstrates running RIOT as a one-shot CLI tool.
This can be used to easiely integrate RIOT into a CI pipeline for integration tests
with software that is supposed to interact with RIOT.

This example demonstrates running the nanoCoAP CLI commands from the host system.

For this the whole host file system is exported to RIOT, we also need to create a
virtual network interface by running

sudo dist/tools/tapsetup/tapsetup -u eth0

where `eth0` is the network interface used for the internet uplink.
If no internet connectivity is needed, this can be left out.

RIOT commands can be invoked e.g. via

bin/native/nanocoap_tool.elf tap0 ping riot-os.org

or via the provided wrapper scripts

./ping.sh riot-os.org
45 changes: 45 additions & 0 deletions examples/riot_as_cli_tool/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2024 ML!PA Consulting GmbH
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup examples
* @{
*
* @file
* @brief Example application to access RIOT shell commands from Linux
*
* @author Benjamin Valentin <[email protected]>
*
* @}
*/

#include <errno.h>
#include "shell.h"
#include "ztimer.h"

int main(int argc, char **argv)
{
if (argc < 1) {
printf("usage: <command> [args]\n");
return 0;
}

/* argv[0] will be the first 'user' argument to native */
shell_command_handler_t handler = shell_find_handler(NULL, argv[0]);
if (handler == NULL) {
return -EINVAL;
}

/* wait some time for the network to be ready */
ztimer_sleep(ZTIMER_MSEC, 1000);

msg_t msg_queue[8];
msg_init_queue(msg_queue, ARRAY_SIZE(msg_queue));

return handler(argc, argv);
}
3 changes: 3 additions & 0 deletions examples/riot_as_cli_tool/ncget.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

bin/native/nanocoap_tool.elf tap0 ncget -- $*

Check warning on line 3 in examples/riot_as_cli_tool/ncget.sh

View workflow job for this annotation

GitHub Actions / static-tests

Double quote to prevent globbing and word splitting. [SC2086]

Check warning on line 3 in examples/riot_as_cli_tool/ncget.sh

View workflow job for this annotation

GitHub Actions / static-tests

Use "$@" (with quotes) to prevent whitespace problems. [SC2048]
3 changes: 3 additions & 0 deletions examples/riot_as_cli_tool/ncput.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

bin/native/nanocoap_tool.elf tap0 ncput -- $*

Check warning on line 3 in examples/riot_as_cli_tool/ncput.sh

View workflow job for this annotation

GitHub Actions / static-tests

Double quote to prevent globbing and word splitting. [SC2086]

Check warning on line 3 in examples/riot_as_cli_tool/ncput.sh

View workflow job for this annotation

GitHub Actions / static-tests

Use "$@" (with quotes) to prevent whitespace problems. [SC2048]
3 changes: 3 additions & 0 deletions examples/riot_as_cli_tool/ping.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/sh

bin/native/nanocoap_tool.elf tap0 ping -- $*

Check warning on line 3 in examples/riot_as_cli_tool/ping.sh

View workflow job for this annotation

GitHub Actions / static-tests

Double quote to prevent globbing and word splitting. [SC2086]

Check warning on line 3 in examples/riot_as_cli_tool/ping.sh

View workflow job for this annotation

GitHub Actions / static-tests

Use "$@" (with quotes) to prevent whitespace problems. [SC2048]
13 changes: 12 additions & 1 deletion sys/include/shell.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ extern "C" {
* passing stdin (`isrpipe_t stdin_isrpipe`) does not support backpressure
* and overflows silently. As a consequence, commands through such terminals
* appear to be truncated at @ref STDIO_RX_BUFSIZE bytes (defaulting to 64)
* unless the command is sent in parts (on many terminals, by presing Ctrl-D
* unless the command is sent in parts (on many terminals, by pressing Ctrl-D
* half way through the command).
*
* For example, this affects systems with direct USB stdio (@ref
Expand Down Expand Up @@ -225,6 +225,17 @@ static inline void shell_run(const shell_command_t *commands,
shell_run_forever(commands, line_buf, len);
}

/**
* @brief Searches for a handler function associated with a command
*
* @param[in] command_list ptr to array of command structs
* @param[in] command the command name to search for
*
* @returns handler function associated with @p command
* NULL if the command could not be found
*/
shell_command_handler_t shell_find_handler(const shell_command_t *command_list,
const char *command);
/**
* @brief Parse and run a line of text as a shell command with
* arguments.
Expand Down
19 changes: 17 additions & 2 deletions sys/shell/cmds/nanocoap_vfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,19 +169,34 @@ static int _nanocoap_put_handler(int argc, char **argv)
file = argv[1];
url = argv[2];

/* append filename to remote URL */
if (_is_dir(url)) {
const char *basename = strrchr(file, '/');
if (basename == NULL) {
return -EINVAL;
basename = file;
} else {
++basename;
}

if (snprintf(buffer, sizeof(buffer), "%s%s",
url, basename + 1) >= (int)sizeof(buffer)) {
url, basename) >= (int)sizeof(buffer)) {
puts("Constructed URI too long");
return -ENOBUFS;
}
url = buffer;
}

/* file path is relative to CONFIG_NCGET_DEFAULT_DATA_DIR */
if (file[0] != '/') {
if ((unsigned)snprintf(work_buf, sizeof(work_buf), "%s%s",
CONFIG_NCGET_DEFAULT_DATA_DIR, file) >= sizeof(work_buf)) {
puts("Constructed URI too long");
return -ENOBUFS;
}
file = work_buf;
}

/* read from 'stdin' / 3rd argument */
if (strcmp(file, "-") == 0) {
if (argc < 4) {
printf("Usage: %s - <url> <data>\n", argv[0]);
Expand Down
12 changes: 7 additions & 5 deletions sys/shell/shell.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ static enum parse_state escape_toggle(enum parse_state s)
}

static shell_command_handler_t search_commands(const shell_command_t *entry,
char *command)
const char *command)
{
for (; entry->name != NULL; entry++) {
if (strcmp(entry->name, command) == 0) {
Expand All @@ -100,7 +100,7 @@ static shell_command_handler_t search_commands(const shell_command_t *entry,
return NULL;
}

static shell_command_handler_t search_commands_xfa(char *command)
static shell_command_handler_t search_commands_xfa(const char *command)
{
unsigned n = XFA_LEN(shell_command_t*, shell_commands_xfa);

Expand All @@ -113,11 +113,13 @@ static shell_command_handler_t search_commands_xfa(char *command)
return NULL;
}

static shell_command_handler_t find_handler(
const shell_command_t *command_list, char *command)
shell_command_handler_t shell_find_handler(const shell_command_t *command_list,
const char *command)
{
shell_command_handler_t handler = NULL;

benpicco marked this conversation as resolved.
Show resolved Hide resolved
assert(command);

if (command_list != NULL) {
handler = search_commands(command_list, command);
}
Expand Down Expand Up @@ -326,7 +328,7 @@ int shell_handle_input_line(const shell_command_t *command_list, char *line)
argv[argc] = NULL;

/* then we call the appropriate handler */
shell_command_handler_t handler = find_handler(command_list, argv[0]);
shell_command_handler_t handler = shell_find_handler(command_list, argv[0]);
if (handler != NULL) {
if (IS_USED(MODULE_SHELL_HOOKS)) {
shell_pre_command_hook(argc, argv);
Expand Down
Loading