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

gcoap/fileserver: add event callbacks #18414

Merged
merged 2 commits into from
Jan 17, 2023
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
1 change: 1 addition & 0 deletions examples/gcoap_fileserver/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ USEMODULE += shell_commands

# enable the fileserver module
USEMODULE += gcoap_fileserver
USEMODULE += gcoap_fileserver_callback
USEMODULE += gcoap_fileserver_delete
USEMODULE += gcoap_fileserver_put

Expand Down
26 changes: 26 additions & 0 deletions examples/gcoap_fileserver/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* @}
*/

#include <stdio.h>
#include "kernel_defines.h"
#include "net/gcoap.h"
#include "net/gcoap/fileserver.h"
Expand Down Expand Up @@ -45,11 +46,36 @@ static gcoap_listener_t _listener = {
.resources_len = ARRAY_SIZE(_resources),
};

static void _event_cb(gcoap_fileserver_event_t event, gcoap_fileserver_event_ctx_t *ctx)
{
switch (event) {
case GCOAP_FILESERVER_GET_FILE_START:
printf("gcoap fileserver: Download started: %s\n", ctx->path);
break;
case GCOAP_FILESERVER_GET_FILE_END:
printf("gcoap fileserver: Download finished: %s\n", ctx->path);
break;
case GCOAP_FILESERVER_PUT_FILE_START:
printf("gcoap fileserver: Upload started: %s\n", ctx->path);
break;
case GCOAP_FILESERVER_PUT_FILE_END:
printf("gcoap fileserver: Upload finished: %s\n", ctx->path);
break;
case GCOAP_FILESERVER_DELETE_FILE:
printf("gcoap fileserver: Delete %s\n", ctx->path);
break;
}
}

int main(void)
{
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);
gcoap_register_listener(&_listener);

if (IS_USED(MODULE_GCOAP_FILESERVER_CALLBACK)) {
gcoap_fileserver_set_event_cb(_event_cb, NULL);
}

char line_buf[SHELL_DEFAULT_BUFSIZE];
shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);

Expand Down
1 change: 1 addition & 0 deletions makefiles/pseudomodules.inc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ PSEUDOMODULES += fatfs_vfs_format
PSEUDOMODULES += fmt_%
PSEUDOMODULES += gcoap_forward_proxy
PSEUDOMODULES += gcoap_fileserver
PSEUDOMODULES += gcoap_fileserver_callback
PSEUDOMODULES += gcoap_fileserver_delete
PSEUDOMODULES += gcoap_fileserver_put
PSEUDOMODULES += gcoap_dtls
Expand Down
45 changes: 45 additions & 0 deletions sys/include/net/gcoap/fileserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,51 @@ extern "C" {
*/
#define COAPFILESERVER_DIR_DELETE_ETAG (0x6ce88b56u)

/**
* @brief GCoAP fileserver event types
*
* @note This requires the gcoap_fileserver_callback module.
*/
typedef enum {
GCOAP_FILESERVER_GET_FILE_START, /**< file download started */
GCOAP_FILESERVER_GET_FILE_END, /**< file download finished */
GCOAP_FILESERVER_PUT_FILE_START, /**< file upload started */
GCOAP_FILESERVER_PUT_FILE_END, /**< file upload finished */
GCOAP_FILESERVER_DELETE_FILE, /**< file deletion requested
(called before file is deleted) */
} gcoap_fileserver_event_t;

/**
* @brief GCoAP fileserver event context
*/
typedef struct {
const char *path; /**< VFS path of the affected file */
void *user_ctx; /**< Optional user supplied context */
} gcoap_fileserver_event_ctx_t;

/**
* @brief GCoAP fileserver event callback type
*
* @param[in] event Type of the event
* @param[in] ctx Event context information
*
*/
typedef void (*gcoap_fileserver_event_handler_t)(gcoap_fileserver_event_t event,
gcoap_fileserver_event_ctx_t *ctx);

/**
* @brief Register a consumer for GCoAP fileserver events
* Requires the `gcoap_fileserver_callback` module
*
* The Callback is called on each fileserver event and executed
* within the GCoAP thread.
*
* @param[in] cb The callback function to be called on events
* @param[in] arg Custom callback function context
*
*/
void gcoap_fileserver_set_event_cb(gcoap_fileserver_event_handler_t cb, void *arg);

/**
* @brief File server handler
*
Expand Down
63 changes: 63 additions & 0 deletions sys/net/application_layer/gcoap/fileserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,21 @@
/** Maximum length of an expressible path, including the trailing 0 character. */
#define COAPFILESERVER_PATH_MAX (64)

/**
* @brief fileserver event callback, only used with `gcoap_fileserver_callback`
*/
static gcoap_fileserver_event_handler_t _event_cb;

/**
* @brief fileserver event callback context, only used with `gcoap_fileserver_callback`
*/
static void *_event_ctx;

/**
* @brief fileserver event mutex, protects event cb and event ctx from concurrent access
*/
static mutex_t _event_mtx;

/**
* @brief Structure holding information about present options in a request
*/
Expand Down Expand Up @@ -146,6 +161,26 @@ static void _calc_szx2(coap_pkt_t *pdu, size_t reserve, coap_block1_t *block2)
}
}

static inline void _event_file(gcoap_fileserver_event_t event, struct requestdata *request)
{
if (!IS_USED(MODULE_GCOAP_FILESERVER_CALLBACK)) {
return;
}

mutex_lock(&_event_mtx);
gcoap_fileserver_event_ctx_t ctx = {
.path = request->namebuf,
.user_ctx = _event_ctx,
};

gcoap_fileserver_event_handler_t cb = _event_cb;
mutex_unlock(&_event_mtx);

if (cb) {
cb(event, &ctx);
}
}

static ssize_t _get_file(coap_pkt_t *pdu, uint8_t *buf, size_t len,
struct requestdata *request)
{
Expand Down Expand Up @@ -193,6 +228,10 @@ static ssize_t _get_file(coap_pkt_t *pdu, uint8_t *buf, size_t len,
goto late_err;
}

if (block2.blknum == 0) {
_event_file(GCOAP_FILESERVER_GET_FILE_START, request);
}

/* That'd only happen if the buffer is too small for even a 16-byte block,
* or if the above calculations were wrong.
*
Expand All @@ -218,6 +257,10 @@ static ssize_t _get_file(coap_pkt_t *pdu, uint8_t *buf, size_t len,
read -= 1;
}

if (!more) {
_event_file(GCOAP_FILESERVER_GET_FILE_END, request);
}

return resp_len + read;

late_err:
Expand Down Expand Up @@ -257,6 +300,8 @@ static ssize_t _put_file(coap_pkt_t *pdu, uint8_t *buf, size_t len,
ret = COAP_CODE_PRECONDITION_FAILED;
goto unlink_on_error;
}

_event_file(GCOAP_FILESERVER_PUT_FILE_START, request);
}
if (request->options.exists.if_match) {
stat_etag(&stat, &etag); /* Etag before write */
Expand Down Expand Up @@ -310,6 +355,9 @@ static ssize_t _put_file(coap_pkt_t *pdu, uint8_t *buf, size_t len,
goto unlink_on_error;
}
}

_event_file(GCOAP_FILESERVER_PUT_FILE_END, request);

stat_etag(&stat, &etag); /* Etag after write */
gcoap_resp_init(pdu, buf, len, create ? COAP_CODE_CREATED : COAP_CODE_CHANGED);
coap_opt_add_opaque(pdu, COAP_OPT_ETAG, &etag, sizeof(etag));
Expand Down Expand Up @@ -347,6 +395,9 @@ static ssize_t _delete_file(coap_pkt_t *pdu, uint8_t *buf, size_t len,
return gcoap_fileserver_error_handler(pdu, buf, len, COAP_CODE_PRECONDITION_FAILED);
}
}

_event_file(GCOAP_FILESERVER_DELETE_FILE, request);

if ((ret = vfs_unlink(request->namebuf)) < 0) {
return gcoap_fileserver_error_handler(pdu, buf, len, ret);
}
Expand Down Expand Up @@ -644,4 +695,16 @@ ssize_t gcoap_fileserver_handler(coap_pkt_t *pdu, uint8_t *buf, size_t len,
return gcoap_response(pdu, buf, len, errorcode);
}

#ifdef MODULE_GCOAP_FILESERVER_CALLBACK
fabian18 marked this conversation as resolved.
Show resolved Hide resolved
void gcoap_fileserver_set_event_cb(gcoap_fileserver_event_handler_t cb, void *ctx)
{
mutex_lock(&_event_mtx);

_event_cb = cb;
_event_ctx = ctx;

mutex_unlock(&_event_mtx);
}
#endif

/** @} */