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

nanocoap_vfs: add nanocoap_vfs_get() #17937

Merged
merged 4 commits into from
May 24, 2022
Merged
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
2 changes: 1 addition & 1 deletion examples/suit_update/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ include $(RIOTBASE)/Makefile.include
# allow to use large blocks to utilize large MTUs (802.15.4g, Ethernet, WiFi)
LARGE_BLOCKS ?= 0
ifeq (1, $(LARGE_BLOCKS))
CFLAGS += -DCONFIG_SUIT_COAP_BLOCKSIZE=COAP_BLOCKSIZE_1024
CFLAGS += -DCONFIG_NANOCOAP_BLOCKSIZE_DEFAULT=COAP_BLOCKSIZE_1024
else
# lower pktbuf size to something sufficient for this application
# Set GNRC_PKTBUF_SIZE via CFLAGS if not being set via Kconfig.
Expand Down
5 changes: 5 additions & 0 deletions sys/Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,11 @@ ifneq (,$(filter nanocoap_cache,$(USEMODULE)))
USEMODULE += hashes
endif

ifneq (,$(filter nanocoap_vfs,$(USEMODULE)))
USEMODULE += nanocoap_sock
USEMODULE += vfs
endif

ifneq (,$(filter nanocoap_%,$(USEMODULE)))
USEMODULE += nanocoap
endif
Expand Down
7 changes: 7 additions & 0 deletions sys/include/net/nanocoap.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,13 @@ extern "C" {
#define CONFIG_NANOCOAP_BLOCK_SIZE_EXP_MAX (6)
#endif

/**
* @brief CoAP block-wise-transfer size that should be used by default
*/
#ifndef CONFIG_NANOCOAP_BLOCKSIZE_DEFAULT
#define CONFIG_NANOCOAP_BLOCKSIZE_DEFAULT COAP_BLOCKSIZE_64
#endif

/** @brief Maximum length of a query string written to a message */
#ifndef CONFIG_NANOCOAP_QS_MAX
#define CONFIG_NANOCOAP_QS_MAX (64)
Expand Down
58 changes: 58 additions & 0 deletions sys/include/net/nanocoap_vfs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2022 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 net_nanosock
* @brief VFS NanoCoAP helper functions
*
* @{
*
* @file
* @brief VFS NanoCoAP helper functions
*
* @author Benjamin Valentin <[email protected]>
*/
#ifndef NET_NANOCOAP_VFS_H
#define NET_NANOCOAP_VFS_H

#include "net/nanocoap_sock.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Downloads the resource behind @p url via blockwise
* GET and stores it in the file @p dst.
*
* @param[in] url URL to the resource
* @param[in] dst Path to the destination file
*
* @returns 0 on success
* @returns <0 on error
*/
int nanocoap_vfs_get_url(const char *url, const char *dst);

/**
* @brief Downloads the resource behind @p path via blockwise
* GET and stores it in the file @p dst.
*
* @param[in] sock Connection to the server
* @param[in] path Remote query path to the resource
* @param[in] dst Local path to the destination file
*
* @returns 0 on success
* @returns <0 on error
Comment on lines +49 to +50
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @returns 0 on success
* @returns <0 on error
* @retval 0 success
* @retval <0 error

*/
int nanocoap_vfs_get(nanocoap_sock_t *sock, const char *path, const char *dst);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whenever there is a pair like this (get vs. get_url), I wonder when to use which, if the use cases overlap or whether there are combinations that would be needed. (For example, can one use an established socket with a URL that has query components, or a Uri-Host component?)

Here it's probably motivated by the duality of nanocoap_sock_get_blockwise and nanocoap_get_blockwise_url, where I should have asked the question earlier.

I'd appreciate a note on when to use which, but maybe there are no good answers until #13827 is through.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nanocoap_vfs_get_url() is just there for convenience to avoid boilerplate in the common case.

The path component (and everything that goes with it) will be parsed the same way for both in nanocoap_sock_get_blockwise().

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the path argument is really more path and query? If so, that should be documented (dejavu, I think we've had this somewhere else already.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I just call it query path?


#ifdef __cplusplus
}
#endif
#endif /* NET_NANOCOAP_VFS_H */
/** @} */
2 changes: 1 addition & 1 deletion sys/include/suit/transport/coap.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ extern const coap_resource_subtree_t coap_resource_subtree_suit;
* @brief Coap block-wise-transfer size used for SUIT
*/
#ifndef CONFIG_SUIT_COAP_BLOCKSIZE
#define CONFIG_SUIT_COAP_BLOCKSIZE COAP_BLOCKSIZE_64
#define CONFIG_SUIT_COAP_BLOCKSIZE CONFIG_NANOCOAP_BLOCKSIZE_DEFAULT
#endif

/**
Expand Down
94 changes: 94 additions & 0 deletions sys/net/application_layer/nanocoap/vfs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright (C) 2022 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 net_nanocoap
* @{
*
* @file
* @brief Nanocoap VFS helpers
*
* @author Benjamin Valentin <[email protected]>
*
* @}
*/

#include <fcntl.h>
#include "net/nanocoap_sock.h"
#include "net/sock/util.h"
#include "vfs.h"

#define ENABLE_DEBUG 0
#include "debug.h"

static int _2file(void *arg, size_t offset, uint8_t *buf, size_t len, int more)
{
(void)more;
int *fd = arg;

vfs_lseek(*fd, offset, SEEK_SET);
return vfs_write(*fd, buf, len);
}

static int _prepare_file(const char *dst, char *dst_tmp, size_t len)
{
/* download to temp file, rename it later */
if (snprintf(dst_tmp, len, "%s.t", dst) > (int)len) {
return -ENOBUFS;
}

return vfs_open(dst_tmp, O_CREAT | O_WRONLY | O_TRUNC, 0644);
}

static int _finalize_file(int fd, int res, const char *dst, const char *dst_tmp)
{
vfs_close(fd);

/* move file to it's final location */
if (res >= 0) {
DEBUG("nanocoap: moving %s to %s\n", dst_tmp, dst);
vfs_unlink(dst);
res = vfs_rename(dst_tmp, dst);
}

vfs_unlink(dst_tmp);

return res;
}

int nanocoap_vfs_get(nanocoap_sock_t *sock, const char *path, const char *dst)
{
int fd, res;
char dst_tmp[CONFIG_SOCK_URLPATH_MAXLEN];

DEBUG("nanocoap: downloading %s to %s\n", path, dst_tmp);

fd = _prepare_file(dst, dst_tmp, sizeof(dst_tmp));
if (fd < 0) {
return fd;
}
res = nanocoap_sock_get_blockwise(sock, path, CONFIG_NANOCOAP_BLOCKSIZE_DEFAULT,
_2file, &fd);
return _finalize_file(fd, res, dst, dst_tmp);
}

int nanocoap_vfs_get_url(const char *url, const char *dst)
{
int fd, res;
char dst_tmp[CONFIG_SOCK_URLPATH_MAXLEN];

DEBUG("nanocoap: downloading %s to %s\n", url, dst_tmp);

fd = _prepare_file(dst, dst_tmp, sizeof(dst_tmp));
if (fd < 0) {
return fd;
}
res = nanocoap_get_blockwise_url(url, CONFIG_NANOCOAP_BLOCKSIZE_DEFAULT,
_2file, &fd);
return _finalize_file(fd, res, dst, dst_tmp);
}
4 changes: 4 additions & 0 deletions sys/shell/commands/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ ifneq (,$(filter semtech-loramac,$(USEPKG)))
SRC += sc_loramac.c
endif

ifneq (,$(filter nanocoap_vfs,$(USEMODULE)))
SRC += sc_nanocoap_vfs.c
endif

ifneq (,$(filter nimble_netif,$(USEMODULE)))
SRC += sc_nimble_netif.c
endif
Expand Down
139 changes: 139 additions & 0 deletions sys/shell/commands/sc_nanocoap_vfs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright (C) 2022 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 sys_shell_commands
* @{
*
* @file
* @brief NanoCoAP commands that interact with the filesystem
*
* @author Benjamin Valentin <[email protected]>
*
* @}
*/

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>

#include "vfs_default.h"
#include "net/nanocoap_vfs.h"
#include "net/nanocoap_sock.h"

struct dir_list_ctx {
char *buf;
char *cur;
char *end;
};

static bool _is_dir(const char *url)
{
int len = strlen(url);
return url[len - 1] == '/';
}

static int _print_cb(void *arg, size_t offset, uint8_t *buf, size_t len, int more)
{
(void)arg;
(void)offset;

write(STDOUT_FILENO, buf, len);
if (!more) {
puts("");
}

return 0;
}

static int _print_dir_cb(void *arg, size_t offset, uint8_t *buf, size_t len, int more)
{
(void)offset;
(void)more;

struct dir_list_ctx *ctx = arg;

char *end = (char *)buf + len;
for (char *c = (char *)buf; c < end; ++c) {
if (ctx->cur) {
if (*c == '>' || ctx->cur == ctx->end) {
*ctx->cur = 0;
puts(ctx->buf);
ctx->cur = NULL;
} else {
*ctx->cur++ = *c;
}
} else if (*c == '<') {
ctx->cur = ctx->buf;
}
}

return 0;
}

static int _print_dir(const char *url, char *buf, size_t len)
{
struct dir_list_ctx ctx = {
.buf = buf,
.end = buf + len,
};
return nanocoap_get_blockwise_url(url, CONFIG_NANOCOAP_BLOCKSIZE_DEFAULT,
_print_dir_cb, &ctx);
}

int _nanocoap_get_handler(int argc, char **argv)
{
int res;
char buffer[CONFIG_NANOCOAP_QS_MAX];
char *dst, *url = argv[1];

if (argc < 2) {
printf("Usage: %s <url> [destination]\n", argv[0]);
printf("Default destination: %s\n", VFS_DEFAULT_DATA);
return -EINVAL;
}

if (_is_dir(url) && argc < 3) {
res = _print_dir(url, buffer, sizeof(buffer));
if (res) {
printf("Request failed: %s\n", strerror(-res));
}
return res;
}

if (argc < 3) {
dst = strrchr(url, '/');
if (dst == NULL) {
printf("invalid url: '%s'\n", url);
return -EINVAL;
}
if (snprintf(buffer, sizeof(buffer), "%s%s",
VFS_DEFAULT_DATA, dst) >= (int)sizeof(buffer)) {
printf("Output file path too long\n");
return -ENOBUFS;
}
dst = buffer;
} else {
dst = argv[2];
}

/* alternatively write the file to stdout */
if (strcmp(dst, "-") == 0) {
return nanocoap_get_blockwise_url(url, CONFIG_NANOCOAP_BLOCKSIZE_DEFAULT,
_print_cb, NULL);
}

res = nanocoap_vfs_get_url(url, dst);
if (res < 0) {
printf("Download failed: %s\n", strerror(-res));
} else {
printf("Saved as %s\n", dst);
}
return res;
}
7 changes: 7 additions & 0 deletions sys/shell/commands/shell_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ extern int _i2c_scan(int argc, char **argv);
extern int _loramac_handler(int argc, char **argv);
#endif

#ifdef MODULE_NANOCOAP_VFS
extern int _nanocoap_get_handler(int argc, char **argv);
#endif

#ifdef MODULE_NICE
extern int _sc_nice(int argc, char **argv);
#endif
Expand Down Expand Up @@ -289,6 +293,9 @@ const shell_command_t _shell_command_list[] = {
#ifdef MODULE_SHA256SUM
{"sha256sum", "Compute and check SHA256 message digest", _vfs_sha256sum_cmd},
#endif
#ifdef MODULE_NANOCOAP_VFS
{"ncget", "download a file from a CoAP server", _nanocoap_get_handler},
#endif
#ifdef MODULE_GNRC_IPV6_NIB
{"nib", "Configure neighbor information base", _gnrc_ipv6_nib},
#endif
Expand Down
18 changes: 18 additions & 0 deletions tests/nanocoap_cli/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,24 @@ USEMODULE += gnrc_ipv6_default

USEMODULE += nanocoap_sock

# boards where basic nanocoap functionality fits, but no VFS
LOW_MEMORY_BOARDS := atmega1284p atxmega-a3bu-xplained derfmega128 \
saml11-xpro bluepill saml10-xpro blackpill nucleo-f302r8 \
stm32mp157c-dk2 stm32f7508-dk

# Don't enable VFS functions on small boards
ifeq (,$(filter $(BOARD),$(LOW_MEMORY_BOARDS)))
USEMODULE += nanocoap_vfs
USEMODULE += vfs_default
# USEMODULE += vfs_auto_format
USEMODULE += shell_commands

# always enable auto-format for native
ifeq ($(BOARD),native)
USEMODULE += vfs_auto_format
endif
endif

# Required by test
USEMODULE += od
USEMODULE += shell
Expand Down