mirror of
https://github.com/falk-werner/webfuse-provider
synced 2024-10-27 20:44:10 +00:00
initial commit
This commit is contained in:
parent
a3927be8bf
commit
858fdb862b
71
CMakeLists.txt
Normal file
71
CMakeLists.txt
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
cmake_minimum_required (VERSION 2.8)
|
||||||
|
project(fuse-wsfs)
|
||||||
|
|
||||||
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(FUSE3 REQUIRED fuse3)
|
||||||
|
pkg_check_modules(LWS REQUIRED libwebsockets)
|
||||||
|
pkg_check_modules(JANSSON REQUIRED jansson)
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic -Wextra")
|
||||||
|
|
||||||
|
set(EXTRA_INCLUDE_DIRS
|
||||||
|
${FUSE3_INCLUDE_DIRS}
|
||||||
|
${LWS_INCLUDE_DIRS}
|
||||||
|
${JANSSON_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(EXTRA_LIBS
|
||||||
|
${EXTRA_LIBS}
|
||||||
|
${FUSE3_LIBRARIES}
|
||||||
|
${LWS_LIBRARIES}
|
||||||
|
${JANSSON_LIBRARIES}
|
||||||
|
${CMAKE_THREAD_LIBS_INIT}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(EXTRA_CFLAGS
|
||||||
|
${FUSE3_CFLAGS_OTHER}
|
||||||
|
${LWS_CFLAGS_OTHER}
|
||||||
|
${JANSSON_CFLAGS_OTHER}
|
||||||
|
"-pthread"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# libfuse-wsfs
|
||||||
|
|
||||||
|
add_library(fuse-wsfs
|
||||||
|
src/wsfs/operations.c
|
||||||
|
src/wsfs/filesystem.c
|
||||||
|
src/wsfs/response_parser.c
|
||||||
|
src/wsfs/server.c
|
||||||
|
src/wsfs/protocol.c
|
||||||
|
src/wsfs/jsonrpc.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(fuse-wsfs PUBLIC src ${EXTRA_INCLUDE_DIRS})
|
||||||
|
target_compile_options(fuse-wsfs PUBLIC ${EXTRA_CFLAGS})
|
||||||
|
|
||||||
|
# app
|
||||||
|
|
||||||
|
add_executable(wsfs
|
||||||
|
src/app/main.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(wsfs PUBLIC fuse-wsfs ${EXTRA_LIBS})
|
||||||
|
target_include_directories(wsfs PUBLIC src ${EXTRA_INCLUDE_DIRS})
|
||||||
|
target_compile_options(wsfs PUBLIC ${EXTRA_CFLAGS})
|
||||||
|
|
||||||
|
# tests
|
||||||
|
|
||||||
|
add_executable(alltests
|
||||||
|
test-src/test_main.c
|
||||||
|
test-src/test_util.c
|
||||||
|
test-src/test_response_parser.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(alltests PUBLIC fuse-wsfs ${EXTRA_LIBS})
|
||||||
|
target_include_directories(alltests PUBLIC src ${EXTRA_INCLUDE_DIRS})
|
||||||
|
target_compile_options(alltests PUBLIC ${EXTRA_CFLAGS})
|
||||||
|
|
103
src/app/main.c
Normal file
103
src/app/main.c
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "wsfs/fuse.h"
|
||||||
|
#include "wsfs/operations.h"
|
||||||
|
#include "wsfs/server.h"
|
||||||
|
|
||||||
|
struct options
|
||||||
|
{
|
||||||
|
struct wsfs_server_config config;
|
||||||
|
int show_help;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct fuse_opt const option_spec[] =
|
||||||
|
{
|
||||||
|
{"--document_root=%s" , offsetof(struct options, config.document_root), 1},
|
||||||
|
{"--server_cert_path=%s", offsetof(struct options, config.cert_path), 1},
|
||||||
|
{"--server_key_path=%s" , offsetof(struct options, config.key_path), 1},
|
||||||
|
{"--vhost_name=%s" , offsetof(struct options, config.vhost_name), 1},
|
||||||
|
{"--port=%d" , offsetof(struct options, config.port), 1},
|
||||||
|
{"-h" , offsetof(struct options, show_help), 1},
|
||||||
|
{"--help" , offsetof(struct options, show_help), 1},
|
||||||
|
FUSE_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
static void show_help(void)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"\n"
|
||||||
|
"File-system specific options:\n"
|
||||||
|
" --document_root=<s> Path of www directory (default: not set, www disabled)\n"
|
||||||
|
" --server_cert_path=<s> Path of servers own certificate (default: not set, TLS disabled)\n"
|
||||||
|
" --server_key_path=<s> Path of servers private key (default: not set, TLS disabled)\n"
|
||||||
|
" --vhost_name=<s> Name of virtual host (default: \"localhost\")\n"
|
||||||
|
" --port=<d> Number of servers port (default: 8080)\n"
|
||||||
|
"\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char * argv[])
|
||||||
|
{
|
||||||
|
struct options options =
|
||||||
|
{
|
||||||
|
.config =
|
||||||
|
{
|
||||||
|
.document_root = NULL,
|
||||||
|
.cert_path = NULL,
|
||||||
|
.key_path = NULL,
|
||||||
|
.vhost_name = strdup("localhost"),
|
||||||
|
.port = 8080,
|
||||||
|
},
|
||||||
|
.show_help = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
||||||
|
if (-1 == fuse_opt_parse(&args, &options, option_spec, NULL))
|
||||||
|
{
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fuse_operations operations;
|
||||||
|
wsfs_operations_init(&operations);
|
||||||
|
|
||||||
|
int result;
|
||||||
|
if (!options.show_help)
|
||||||
|
{
|
||||||
|
struct wsfs_server * server = wsfs_server_create(&options.config);
|
||||||
|
if (NULL != server)
|
||||||
|
{
|
||||||
|
wsfs_server_start(server);
|
||||||
|
struct wsfs_jsonrpc * const rpc = wsfs_server_get_jsonrpc_service(server);
|
||||||
|
|
||||||
|
result = fuse_main(args.argc, args.argv, &operations, rpc);
|
||||||
|
wsfs_server_dispose(server);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "fatal: unable start server\n");
|
||||||
|
result = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (0 == fuse_opt_add_arg(&args, "--help"))
|
||||||
|
{
|
||||||
|
fuse_main(args.argc, args.argv, &operations, NULL);
|
||||||
|
show_help();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "fatal: unable to show help\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
result = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
fuse_opt_free_args(&args);
|
||||||
|
wsfs_server_config_clear(&options.config);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
12
src/app/www/index.html
Normal file
12
src/app/www/index.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>LWS Example</title>
|
||||||
|
<meta charset='UTF-8' />
|
||||||
|
<meta http-equiv='Content-Security-Policy' content="script-src localhost:4711/script.js;" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<textarea id='content'></textarea>
|
||||||
|
<input id='sendButton' type='button' value='send' />
|
||||||
|
<script nonce='42' type='text/javascript' src='script.js'></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
98
src/app/www/script.js
Normal file
98
src/app/www/script.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
function startup()
|
||||||
|
{
|
||||||
|
var FileSystem = function(root) {
|
||||||
|
this.root = root;
|
||||||
|
};
|
||||||
|
|
||||||
|
FileSystem.BAD_NO_ENTRY = -1;
|
||||||
|
|
||||||
|
FileSystem.prototype.getEntry = function(path) {
|
||||||
|
var items = path.split('/');
|
||||||
|
var curItem = this.root;
|
||||||
|
var item;
|
||||||
|
var i, len;
|
||||||
|
|
||||||
|
for(i = 0, len = items.length; curItem && (i < len); i++) {
|
||||||
|
item = items[i];
|
||||||
|
if ("" !== item) {
|
||||||
|
curItem = curItem.entries && curItem.entries[item];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return curItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileSystem.prototype.getattr = function(path) {
|
||||||
|
entry = this.getEntry(path);
|
||||||
|
if (entry) {
|
||||||
|
return {
|
||||||
|
mode: entry.mode || 0755,
|
||||||
|
type: entry.type || 'file',
|
||||||
|
size: entry.size || 0,
|
||||||
|
atime: entry.atime || 0,
|
||||||
|
mtime: entry.mtime || 0,
|
||||||
|
ctime: entry.ctime || 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return FileSystem.BAD_NO_ENTRY;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var fs = new FileSystem({
|
||||||
|
mode: 0755,
|
||||||
|
type: "dir",
|
||||||
|
entries: {
|
||||||
|
"hello": { mode: 0755, type: "file", size: 10, contents: "Hello, World!"}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var ws = new WebSocket('ws://localhost:4711/', 'fs');
|
||||||
|
ws.onopen = function() {
|
||||||
|
console.log('open');
|
||||||
|
};
|
||||||
|
ws.onclose = function() {
|
||||||
|
console.log('close');
|
||||||
|
};
|
||||||
|
ws.onmessage = function(message) {
|
||||||
|
console.log(message);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var request = JSON.parse(message.data);
|
||||||
|
var result = -42;
|
||||||
|
var response;
|
||||||
|
|
||||||
|
if (("string" === typeof(request.method)) &&
|
||||||
|
("number" === typeof(request.id)) &&
|
||||||
|
(request.params)) {
|
||||||
|
switch(request.method)
|
||||||
|
{
|
||||||
|
case "getattr":
|
||||||
|
result = fs.getattr(request.params[0]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("number" !== typeof(result)) {
|
||||||
|
response = {result: result, id: request.id};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
response = {error: {code: result}, id: request.id};
|
||||||
|
}
|
||||||
|
console.log(response);
|
||||||
|
ws.send(JSON.stringify(response));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ex) { console.log(ex); }
|
||||||
|
};
|
||||||
|
|
||||||
|
var sendButton = document.getElementById('sendButton');
|
||||||
|
sendButton.addEventListener('click', function() {
|
||||||
|
var content = document.getElementById('content').value;
|
||||||
|
ws.send(content);
|
||||||
|
console.log(content);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.onload=startup();
|
87
src/main.c
Normal file
87
src/main.c
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "wsfs/fuse.h"
|
||||||
|
#include "wsfs/operations.h"
|
||||||
|
|
||||||
|
struct options
|
||||||
|
{
|
||||||
|
char const * document_root;
|
||||||
|
char const * server_cert_path;
|
||||||
|
char const * server_key_path;
|
||||||
|
char const * vhost_name;
|
||||||
|
int port;
|
||||||
|
int show_help;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct fuse_opt const option_spec[] =
|
||||||
|
{
|
||||||
|
{"--document_root=%s" , offsetof(struct options, document_root), 1},
|
||||||
|
{"--server_cert_path=%s", offsetof(struct options, server_cert_path), 1},
|
||||||
|
{"--server_key_path=%s" , offsetof(struct options, server_key_path), 1},
|
||||||
|
{"--vhost_name=%s" , offsetof(struct options, vhost_name), 1},
|
||||||
|
{"--port=%d" , offsetof(struct options, port), 1},
|
||||||
|
{"-h" , offsetof(struct options, show_help), 1},
|
||||||
|
{"--help" , offsetof(struct options, show_help), 1},
|
||||||
|
FUSE_OPT_END
|
||||||
|
};
|
||||||
|
|
||||||
|
static void show_help(void)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"\n"
|
||||||
|
"File-system specific options:\n"
|
||||||
|
" --document_root=<s> Path of www directory (default: not set, www disabled)\n"
|
||||||
|
" --server_cert_path=<s> Path of servers own certificate (default: not set, TLS disabled)\n"
|
||||||
|
" --server_key_path=<s> Path of servers private key (default: not set, TLS disabled)\n"
|
||||||
|
" --vhost_name=<s> Name of virtual host (default: \"localhost\")\n"
|
||||||
|
" --port=<d> Number of servers port (default: 8080)\n"
|
||||||
|
"\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char * argv[])
|
||||||
|
{
|
||||||
|
struct options options =
|
||||||
|
{
|
||||||
|
.document_root = NULL,
|
||||||
|
.server_cert_path = NULL,
|
||||||
|
.server_key_path = NULL,
|
||||||
|
.vhost_name = "localhost",
|
||||||
|
.port = 8080,
|
||||||
|
.show_help = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
||||||
|
if (-1 == fuse_opt_parse(&args, &options, option_spec, NULL))
|
||||||
|
{
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fuse_operations operations;
|
||||||
|
wsfs_operations_init(&operations);
|
||||||
|
|
||||||
|
int result;
|
||||||
|
if (!options.show_help)
|
||||||
|
{
|
||||||
|
result = fuse_main(args.argc, args.argv, &operations, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (0 == fuse_opt_add_arg(&args, "--help"))
|
||||||
|
{
|
||||||
|
fuse_main(args.argc, args.argv, &operations, NULL);
|
||||||
|
show_help();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "fatal: unable to show help\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
result = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
fuse_opt_free_args(&args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
103
src/wsfs/filesystem.c
Normal file
103
src/wsfs/filesystem.c
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#include "wsfs/filesystem.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <jansson.h>
|
||||||
|
|
||||||
|
#include "wsfs/util.h"
|
||||||
|
#include "wsfs/jsonrpc.h"
|
||||||
|
|
||||||
|
struct wsfs_filesystem
|
||||||
|
{
|
||||||
|
struct wsfs_jsonrpc * rpc;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wsfs_filesystem * wsfs_filesystem_create(
|
||||||
|
struct wsfs_jsonrpc * rpc)
|
||||||
|
{
|
||||||
|
struct wsfs_filesystem * filesystem = malloc(sizeof(struct wsfs_filesystem));
|
||||||
|
if (NULL != filesystem)
|
||||||
|
{
|
||||||
|
filesystem->rpc = rpc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return filesystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wsfs_filesystem_dispose(
|
||||||
|
struct wsfs_filesystem * filesystem)
|
||||||
|
{
|
||||||
|
free(filesystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wsfs_json_get_int(json_t * object, char const * key, int default_value)
|
||||||
|
{
|
||||||
|
int result = default_value;
|
||||||
|
|
||||||
|
json_t * holder = json_object_get(object, key);
|
||||||
|
if ((NULL != holder) && (json_is_integer(holder)))
|
||||||
|
{
|
||||||
|
result = json_integer_value(holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
wsfs_status wsfs_filesystem_getattr(
|
||||||
|
struct wsfs_filesystem * filesystem,
|
||||||
|
char const * path,
|
||||||
|
struct stat * result)
|
||||||
|
{
|
||||||
|
json_t * data = NULL;
|
||||||
|
wsfs_status status = wsfs_jsonrpc_invoke(filesystem->rpc, &data, "getattr", "s", path);
|
||||||
|
if (NULL != result)
|
||||||
|
{
|
||||||
|
json_t * mode_holder = json_object_get(data, "mode");
|
||||||
|
json_t * type_holder = json_object_get(data, "type");
|
||||||
|
if ((NULL != mode_holder) && (json_is_integer(mode_holder)) &&
|
||||||
|
(NULL != type_holder) && (json_is_string(type_holder)))
|
||||||
|
{
|
||||||
|
result->st_mode = json_integer_value(mode_holder) & 0777;
|
||||||
|
char const * type = json_string_value(type_holder);
|
||||||
|
if (0 == strcmp("file", type))
|
||||||
|
{
|
||||||
|
result->st_mode |= S_IFREG;
|
||||||
|
}
|
||||||
|
else if (0 == strcmp("dir", type))
|
||||||
|
{
|
||||||
|
result->st_mode |= S_IFDIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
result->st_atime = wsfs_json_get_int(data, "atime", 0);
|
||||||
|
result->st_mtime = wsfs_json_get_int(data, "mtime", 0);
|
||||||
|
result->st_ctime = wsfs_json_get_int(data, "ctime", 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = WSFS_BAD_PARSEERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
json_decref(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
wsfs_status wsfs_filesystem_readdir(
|
||||||
|
struct wsfs_filesystem * filesystem,
|
||||||
|
char const * path,
|
||||||
|
void * WSFS_UNUSED_PARAM(buffer),
|
||||||
|
wsfs_add_entry_fn * WSFS_UNUSED_PARAM(add_entry))
|
||||||
|
{
|
||||||
|
json_t * result = NULL;
|
||||||
|
wsfs_status const status = wsfs_jsonrpc_invoke(filesystem->rpc, &result, "readdir", "s", path);
|
||||||
|
if (NULL != result)
|
||||||
|
{
|
||||||
|
json_decref(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
38
src/wsfs/filesystem.h
Normal file
38
src/wsfs/filesystem.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#ifndef _WSFS_FILESYSTEM_H
|
||||||
|
#define _WSFS_FILESYSTEM_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "wsfs/status.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct wsfs_filesystem;
|
||||||
|
struct wsfs_jsonrpc;
|
||||||
|
|
||||||
|
typedef bool wsfs_add_entry_fn(void * buffer, char const * path);
|
||||||
|
|
||||||
|
extern struct wsfs_filesystem * wsfs_filesystem_create(
|
||||||
|
struct wsfs_jsonrpc * service);
|
||||||
|
|
||||||
|
extern void wsfs_filesystem_dispose(
|
||||||
|
struct wsfs_filesystem * filesystem);
|
||||||
|
|
||||||
|
extern wsfs_status wsfs_filesystem_getattr(
|
||||||
|
struct wsfs_filesystem * filesystem,
|
||||||
|
char const * path,
|
||||||
|
struct stat * result);
|
||||||
|
|
||||||
|
extern wsfs_status wsfs_filesystem_readdir(
|
||||||
|
struct wsfs_filesystem * filesystem,
|
||||||
|
char const * path,
|
||||||
|
void * buffer,
|
||||||
|
wsfs_add_entry_fn * add_entry);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
8
src/wsfs/fuse.h
Normal file
8
src/wsfs/fuse.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef _WSFS_FUSE_H
|
||||||
|
#define _WSFS_FUSE_H
|
||||||
|
|
||||||
|
#define FUSE_USE_VERSION 31
|
||||||
|
#include <fuse.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
232
src/wsfs/jsonrpc.c
Normal file
232
src/wsfs/jsonrpc.c
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
#include "wsfs/jsonrpc.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "wsfs/response_parser.h"
|
||||||
|
|
||||||
|
#define DEFAULT_TIMEOUT_SECS 10
|
||||||
|
|
||||||
|
struct wsfs_jsonrpc_response
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
wsfs_status status;
|
||||||
|
json_t * result;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wsfs_jsonrpc
|
||||||
|
{
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
pthread_cond_t finished;
|
||||||
|
pthread_condattr_t finished_attributes;
|
||||||
|
|
||||||
|
wsfs_create_message_fn * create_message;
|
||||||
|
wsfs_send_message_fn * send_message;
|
||||||
|
void * user_data;
|
||||||
|
|
||||||
|
bool is_finished;
|
||||||
|
struct wsfs_jsonrpc_response response;
|
||||||
|
};
|
||||||
|
|
||||||
|
static json_t * wsfs_request_create(
|
||||||
|
char const * method,
|
||||||
|
int id,
|
||||||
|
char const * param_info,
|
||||||
|
va_list args)
|
||||||
|
{
|
||||||
|
json_t * request = json_object();
|
||||||
|
json_object_set_new(request, "method", json_string(method));
|
||||||
|
json_t * params = json_array();
|
||||||
|
|
||||||
|
for (char const * param_type = param_info; '\0' != *param_type; param_type++)
|
||||||
|
{
|
||||||
|
switch(*param_type)
|
||||||
|
{
|
||||||
|
case 's':
|
||||||
|
{
|
||||||
|
char const * const value = va_arg(args, char const *);
|
||||||
|
json_array_append_new(params, json_string(value));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
{
|
||||||
|
int const value = va_arg(args, int);
|
||||||
|
json_array_append_new(params, json_integer(value));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "fatal: unknown param_type '%c'\n", *param_type);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
json_object_set_new(request, "params", params);
|
||||||
|
json_object_set_new(request, "id", json_integer(id));
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
wsfs_status wsfs_jsonrpc_invoke(
|
||||||
|
struct wsfs_jsonrpc * rpc,
|
||||||
|
json_t * * result,
|
||||||
|
char const * method,
|
||||||
|
char const * param_info,
|
||||||
|
...
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// enqueue message
|
||||||
|
pthread_mutex_lock(&rpc->lock);
|
||||||
|
wsfs_status status = WSFS_BAD;
|
||||||
|
|
||||||
|
char * message = NULL;
|
||||||
|
size_t length = 0;
|
||||||
|
if (-1 == rpc->response.id) {
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, param_info);
|
||||||
|
json_t * request = wsfs_request_create(method, 42, param_info, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
|
||||||
|
length = json_dumpb(request, NULL, 0, JSON_COMPACT);
|
||||||
|
if (0 < length)
|
||||||
|
{
|
||||||
|
rpc->is_finished = false;
|
||||||
|
rpc->response.id = 42;
|
||||||
|
rpc->response.result = NULL;
|
||||||
|
rpc->response.status = WSFS_GOOD;
|
||||||
|
|
||||||
|
message = rpc->create_message(length);
|
||||||
|
json_dumpb(request, message, length, JSON_COMPACT);
|
||||||
|
|
||||||
|
}
|
||||||
|
json_decref(request);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&rpc->lock);
|
||||||
|
|
||||||
|
|
||||||
|
if (NULL != message)
|
||||||
|
{
|
||||||
|
bool const success = rpc->send_message(message, length, rpc->user_data);
|
||||||
|
|
||||||
|
// wait for answer
|
||||||
|
pthread_mutex_lock(&rpc->lock);
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
struct timespec timeout;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &timeout);
|
||||||
|
timeout.tv_sec += DEFAULT_TIMEOUT_SECS;
|
||||||
|
int rc = 0;
|
||||||
|
while ((0 == rc) && (!rpc->is_finished)) {
|
||||||
|
rc = pthread_cond_timedwait(&rpc->finished, &rpc->lock, &timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rpc->is_finished)
|
||||||
|
{
|
||||||
|
status = rpc->response.status;
|
||||||
|
*result = rpc->response.result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = WSFS_BAD_TIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc->response.id = -1;
|
||||||
|
rpc->response.result = NULL;
|
||||||
|
rpc->response.status = WSFS_GOOD;
|
||||||
|
pthread_mutex_unlock(&rpc->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wsfs_jsonrpc * wsfs_jsonrpc_create(
|
||||||
|
wsfs_create_message_fn * create_message,
|
||||||
|
wsfs_send_message_fn * send_message,
|
||||||
|
void * user_data)
|
||||||
|
{
|
||||||
|
struct wsfs_jsonrpc * rpc = malloc(sizeof(struct wsfs_jsonrpc));
|
||||||
|
if (NULL != rpc)
|
||||||
|
{
|
||||||
|
pthread_mutex_init(&rpc->lock, NULL);
|
||||||
|
|
||||||
|
pthread_condattr_init(&rpc->finished_attributes);
|
||||||
|
pthread_condattr_setclock(&rpc->finished_attributes, CLOCK_MONOTONIC);
|
||||||
|
pthread_cond_init(&rpc->finished, &rpc->finished_attributes);
|
||||||
|
|
||||||
|
rpc->create_message = create_message;
|
||||||
|
rpc->send_message = send_message;
|
||||||
|
rpc->user_data = user_data;
|
||||||
|
rpc->is_finished = true;
|
||||||
|
rpc->response.id = -1;
|
||||||
|
rpc->response.status = WSFS_GOOD;
|
||||||
|
rpc->response.result = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rpc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wsfs_jsonrpc_set_user_data(
|
||||||
|
struct wsfs_jsonrpc * rpc,
|
||||||
|
void * user_data)
|
||||||
|
{
|
||||||
|
rpc->user_data = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wsfs_jsonrpc_dispose(
|
||||||
|
struct wsfs_jsonrpc * rpc)
|
||||||
|
{
|
||||||
|
if (NULL != rpc->response.result)
|
||||||
|
{
|
||||||
|
json_decref(rpc->response.result);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_cond_destroy(&rpc->finished);
|
||||||
|
pthread_condattr_destroy(&rpc->finished_attributes);
|
||||||
|
pthread_mutex_destroy(&rpc->lock);
|
||||||
|
|
||||||
|
|
||||||
|
free(rpc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wsfs_jsonrpc_on_message(
|
||||||
|
char const * message,
|
||||||
|
size_t length,
|
||||||
|
void * user_data)
|
||||||
|
{
|
||||||
|
struct wsfs_jsonrpc * rpc = user_data;
|
||||||
|
|
||||||
|
struct wsfs_response response;
|
||||||
|
wsfs_response_parse(message, length, &response);
|
||||||
|
|
||||||
|
if (-1 != response.id)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&rpc->lock);
|
||||||
|
|
||||||
|
if (response.id == rpc->response.id)
|
||||||
|
{
|
||||||
|
rpc->is_finished = true;
|
||||||
|
rpc->response.status = response.status;
|
||||||
|
rpc->response.result = response.result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (NULL != response.result)
|
||||||
|
{
|
||||||
|
json_decref(response.result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_cond_signal(&rpc->finished);
|
||||||
|
pthread_mutex_unlock(&rpc->lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
41
src/wsfs/jsonrpc.h
Normal file
41
src/wsfs/jsonrpc.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#ifndef _WSFS_JSONRPC_H
|
||||||
|
#define _WSFS_JSONRPC_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <jansson.h>
|
||||||
|
#include "wsfs/status.h"
|
||||||
|
|
||||||
|
struct wsfs_jsonrpc;
|
||||||
|
|
||||||
|
typedef char * wsfs_create_message_fn(size_t size);
|
||||||
|
typedef bool wsfs_send_message_fn(char * message, size_t length, void * user_data);
|
||||||
|
|
||||||
|
extern struct wsfs_jsonrpc * wsfs_jsonrpc_create(
|
||||||
|
wsfs_create_message_fn * create_message,
|
||||||
|
wsfs_send_message_fn * send_message,
|
||||||
|
void * user_data);
|
||||||
|
|
||||||
|
extern void wsfs_jsonrpc_set_user_data(
|
||||||
|
struct wsfs_jsonrpc * rpc,
|
||||||
|
void * user_data);
|
||||||
|
|
||||||
|
extern void wsfs_jsonrpc_dispose(
|
||||||
|
struct wsfs_jsonrpc * rpc);
|
||||||
|
|
||||||
|
extern wsfs_status wsfs_jsonrpc_invoke(
|
||||||
|
struct wsfs_jsonrpc * rpc,
|
||||||
|
json_t * * result,
|
||||||
|
char const * method,
|
||||||
|
char const * param_info,
|
||||||
|
...
|
||||||
|
);
|
||||||
|
|
||||||
|
extern void wsfs_jsonrpc_on_message(
|
||||||
|
char const * message,
|
||||||
|
size_t length,
|
||||||
|
void * user_data);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
105
src/wsfs/operations.c
Normal file
105
src/wsfs/operations.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#include "wsfs/operations.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "wsfs/util.h"
|
||||||
|
#include "wsfs/filesystem.h"
|
||||||
|
|
||||||
|
struct wsfs_readdir_context
|
||||||
|
{
|
||||||
|
void * buffer;
|
||||||
|
fuse_fill_dir_t filler;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int wsfs_result_from_status(wsfs_status status)
|
||||||
|
{
|
||||||
|
switch(status)
|
||||||
|
{
|
||||||
|
case WSFS_GOOD: return 0;
|
||||||
|
case WSFS_BAD_NOENTRY: return -ENOENT;
|
||||||
|
default: return -ENOENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wsfs_filesystem * wsfs_get_filesystem(void)
|
||||||
|
{
|
||||||
|
struct fuse_context * const context = fuse_get_context();
|
||||||
|
struct wsfs_filesystem * const filesystem = context->private_data;
|
||||||
|
|
||||||
|
return filesystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool wsfs_add_entry(void * buffer, char const * path)
|
||||||
|
{
|
||||||
|
struct wsfs_readdir_context * context = buffer;
|
||||||
|
int const result = context->filler(context->buffer, path, NULL, 0, 0);
|
||||||
|
|
||||||
|
return (0 == result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* wsfs_operation_init(
|
||||||
|
struct fuse_conn_info * WSFS_UNUSED_PARAM(connection),
|
||||||
|
struct fuse_config * config)
|
||||||
|
{
|
||||||
|
struct fuse_context * const context = fuse_get_context();
|
||||||
|
config->kernel_cache = 1;
|
||||||
|
|
||||||
|
return wsfs_filesystem_create(context->private_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wsfs_operation_destroy(void * private_data)
|
||||||
|
{
|
||||||
|
struct wsfs_filesystem * const filesystem = private_data;
|
||||||
|
wsfs_filesystem_dispose(filesystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wsfs_operation_getattr(
|
||||||
|
char const * path,
|
||||||
|
struct stat * buffer,
|
||||||
|
struct fuse_file_info * WSFS_UNUSED_PARAM(file_info))
|
||||||
|
{
|
||||||
|
struct fuse_context * const context = fuse_get_context();
|
||||||
|
struct wsfs_filesystem * const filesystem = wsfs_get_filesystem();
|
||||||
|
|
||||||
|
wsfs_status const status = wsfs_filesystem_getattr(filesystem, path, buffer);
|
||||||
|
if (WSFS_GOOD == status)
|
||||||
|
{
|
||||||
|
buffer->st_uid = context->uid;
|
||||||
|
buffer->st_gid = context->gid;
|
||||||
|
buffer->st_nlink = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return wsfs_result_from_status(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int wsfs_operation_readdir(
|
||||||
|
char const * path,
|
||||||
|
void * buffer,
|
||||||
|
fuse_fill_dir_t filler,
|
||||||
|
off_t WSFS_UNUSED_PARAM(offset),
|
||||||
|
struct fuse_file_info * WSFS_UNUSED_PARAM(file_info),
|
||||||
|
enum fuse_readdir_flags WSFS_UNUSED_PARAM(flags))
|
||||||
|
{
|
||||||
|
struct wsfs_filesystem * filesystem = wsfs_get_filesystem();
|
||||||
|
struct wsfs_readdir_context context =
|
||||||
|
{
|
||||||
|
.buffer = buffer,
|
||||||
|
.filler = filler
|
||||||
|
};
|
||||||
|
|
||||||
|
wsfs_status const status = wsfs_filesystem_readdir(filesystem, path, &context, &wsfs_add_entry);
|
||||||
|
return wsfs_result_from_status(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wsfs_operations_init(
|
||||||
|
struct fuse_operations * operations)
|
||||||
|
{
|
||||||
|
memset(operations, 0, sizeof(struct fuse_operations));
|
||||||
|
operations->init = &wsfs_operation_init;
|
||||||
|
operations->destroy = &wsfs_operation_destroy;
|
||||||
|
operations->getattr = &wsfs_operation_getattr;
|
||||||
|
operations->readdir = &wsfs_operation_readdir;
|
||||||
|
}
|
||||||
|
|
10
src/wsfs/operations.h
Normal file
10
src/wsfs/operations.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef _WSFS_OPERATIONS
|
||||||
|
#define _WSFS_OPERATIONS
|
||||||
|
|
||||||
|
#include "wsfs/fuse.h"
|
||||||
|
|
||||||
|
extern void wsfs_operations_init(
|
||||||
|
struct fuse_operations * operations);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
243
src/wsfs/protocol.c
Normal file
243
src/wsfs/protocol.c
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
#include "wsfs/protocol.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include "wsfs/util.h"
|
||||||
|
#include "wsfs/server.h"
|
||||||
|
|
||||||
|
struct wsfs_message
|
||||||
|
{
|
||||||
|
char * content;
|
||||||
|
size_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wsfs_protocol
|
||||||
|
{
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
struct lws * wsi;
|
||||||
|
struct wsfs_message pending_message;
|
||||||
|
struct wsfs_server * server;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wsfs_protocol * wsfs_protocol_from_wsi(
|
||||||
|
struct lws * wsi)
|
||||||
|
{
|
||||||
|
struct lws_protocols const * protocol = lws_get_protocol(wsi);
|
||||||
|
return protocol->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool wsfs_protocol_connect(
|
||||||
|
struct wsfs_protocol * protocol,
|
||||||
|
struct lws * wsi)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&protocol->lock);
|
||||||
|
|
||||||
|
bool const success = (NULL == protocol->wsi);
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
protocol->wsi = wsi;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&protocol->lock);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool wsfs_protocol_is_wsi_connected(
|
||||||
|
struct wsfs_protocol * protocol,
|
||||||
|
struct lws * wsi)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&protocol->lock);
|
||||||
|
bool const result = (wsi == protocol->wsi);
|
||||||
|
pthread_mutex_unlock(&protocol->lock);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wsfs_protocol_disconnect(
|
||||||
|
struct wsfs_protocol * protocol,
|
||||||
|
struct lws * wsi)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&protocol->lock);
|
||||||
|
if (wsi == protocol->wsi)
|
||||||
|
{
|
||||||
|
protocol->wsi = NULL;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&protocol->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wsfs_protocol_get_message(
|
||||||
|
struct wsfs_protocol * protocol,
|
||||||
|
struct wsfs_message * message)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&protocol->lock);
|
||||||
|
|
||||||
|
message->content = protocol->pending_message.content;
|
||||||
|
message->length = protocol->pending_message.length;
|
||||||
|
|
||||||
|
protocol->pending_message.content = NULL;
|
||||||
|
protocol->pending_message.length = 0;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&protocol->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int wsfs_protocol_callback(
|
||||||
|
struct lws *wsi,
|
||||||
|
enum lws_callback_reasons reason,
|
||||||
|
void * WSFS_UNUSED_PARAM(user),
|
||||||
|
void *in,
|
||||||
|
size_t len)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
struct wsfs_protocol * const protocol = wsfs_protocol_from_wsi(wsi);
|
||||||
|
|
||||||
|
switch (reason)
|
||||||
|
{
|
||||||
|
case LWS_CALLBACK_ESTABLISHED:
|
||||||
|
{
|
||||||
|
if (!wsfs_protocol_connect(protocol, wsi))
|
||||||
|
{
|
||||||
|
puts("connect failed");
|
||||||
|
lws_callback_on_writable(wsi);
|
||||||
|
result = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LWS_CALLBACK_CLOSED:
|
||||||
|
{
|
||||||
|
wsfs_protocol_disconnect(protocol, wsi);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LWS_CALLBACK_SERVER_WRITEABLE:
|
||||||
|
{
|
||||||
|
if (wsfs_protocol_is_wsi_connected(protocol, wsi))
|
||||||
|
{
|
||||||
|
struct wsfs_message message;
|
||||||
|
wsfs_protocol_get_message(protocol, &message);
|
||||||
|
if (NULL != message.content)
|
||||||
|
{
|
||||||
|
lws_write(wsi, (unsigned char*) message.content, message.length, LWS_WRITE_TEXT);
|
||||||
|
wsfs_protocol_message_dispose(message.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LWS_CALLBACK_RECEIVE:
|
||||||
|
{
|
||||||
|
wsfs_server_handle_message(protocol->server, in, len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct wsfs_protocol * wsfs_protocol_create(
|
||||||
|
struct wsfs_server * server)
|
||||||
|
{
|
||||||
|
struct wsfs_protocol * protocol = malloc(sizeof(struct wsfs_protocol));
|
||||||
|
if (NULL != protocol)
|
||||||
|
{
|
||||||
|
pthread_mutex_init(&protocol->lock, NULL);
|
||||||
|
protocol->wsi = NULL;
|
||||||
|
protocol->pending_message.content = NULL;
|
||||||
|
protocol->pending_message.length = 0;
|
||||||
|
protocol->server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
return protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wsfs_protocol_dispose(
|
||||||
|
struct wsfs_protocol * protocol)
|
||||||
|
{
|
||||||
|
pthread_mutex_destroy(&protocol->lock);
|
||||||
|
|
||||||
|
if (NULL != protocol->pending_message.content)
|
||||||
|
{
|
||||||
|
wsfs_protocol_message_dispose(protocol->pending_message.content);
|
||||||
|
protocol->pending_message.content = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wsfs_protocol_check(
|
||||||
|
struct wsfs_protocol * protocol)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&protocol->lock);
|
||||||
|
|
||||||
|
if ((NULL != protocol->wsi) && (NULL != protocol->pending_message.content))
|
||||||
|
{
|
||||||
|
lws_callback_on_writable(protocol->wsi);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&protocol->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wsfs_protocol_init_lws(
|
||||||
|
struct wsfs_protocol * protocol,
|
||||||
|
struct lws_protocols * lws_protocol)
|
||||||
|
{
|
||||||
|
lws_protocol->callback = &wsfs_protocol_callback;
|
||||||
|
lws_protocol->per_session_data_size = 1;
|
||||||
|
lws_protocol->user = protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * wsfs_protocol_message_create(
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
char * buffer = malloc(LWS_PRE + size);
|
||||||
|
return &buffer[LWS_PRE];
|
||||||
|
}
|
||||||
|
|
||||||
|
void wsfs_protocol_message_dispose(
|
||||||
|
char * message)
|
||||||
|
{
|
||||||
|
char * buffer = message - LWS_PRE;
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wsfs_protocol_send(
|
||||||
|
char * message,
|
||||||
|
size_t length,
|
||||||
|
void * user_data)
|
||||||
|
{
|
||||||
|
struct wsfs_protocol * protocol = user_data;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&protocol->lock);
|
||||||
|
|
||||||
|
struct wsfs_server * server = protocol->server;
|
||||||
|
bool result = (NULL != protocol->wsi) && (NULL == protocol->pending_message.content);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
protocol->pending_message.content = message;
|
||||||
|
protocol->pending_message.length = length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wsfs_protocol_message_dispose(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&protocol->lock);
|
||||||
|
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
wsfs_server_wakeup(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
37
src/wsfs/protocol.h
Normal file
37
src/wsfs/protocol.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef _WSFS_PROTOCOL_H
|
||||||
|
#define _WSFS_PROTOCOL_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <libwebsockets.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
struct wsfs_protocol;
|
||||||
|
struct wsfs_server;
|
||||||
|
|
||||||
|
extern struct wsfs_protocol * wsfs_protocol_create(
|
||||||
|
struct wsfs_server * server);
|
||||||
|
|
||||||
|
extern void wsfs_protocol_dispose(
|
||||||
|
struct wsfs_protocol * protocol);
|
||||||
|
|
||||||
|
extern void wsfs_protocol_check(
|
||||||
|
struct wsfs_protocol * protocol);
|
||||||
|
|
||||||
|
extern void wsfs_protocol_init_lws(
|
||||||
|
struct wsfs_protocol * protocl,
|
||||||
|
struct lws_protocols * lws_protocol);
|
||||||
|
|
||||||
|
extern char * wsfs_protocol_message_create(
|
||||||
|
size_t size);
|
||||||
|
|
||||||
|
extern void wsfs_protocol_message_dispose(
|
||||||
|
char * message);
|
||||||
|
|
||||||
|
extern bool wsfs_protocol_send(
|
||||||
|
char * message,
|
||||||
|
size_t length,
|
||||||
|
void * user_data);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
51
src/wsfs/response_parser.c
Normal file
51
src/wsfs/response_parser.c
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#include "wsfs/response_parser.h"
|
||||||
|
|
||||||
|
void wsfs_response_parse(
|
||||||
|
char const * buffer,
|
||||||
|
size_t length,
|
||||||
|
struct wsfs_response * result)
|
||||||
|
{
|
||||||
|
result->status = WSFS_BAD;
|
||||||
|
result->id = -1;
|
||||||
|
result->result = NULL;
|
||||||
|
|
||||||
|
json_t * response = json_loadb(buffer, length, 0, NULL);
|
||||||
|
if (NULL == response)
|
||||||
|
{
|
||||||
|
result->status = WSFS_BAD_PARSEERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t * id_holder = json_object_get(response, "id");
|
||||||
|
if ((NULL == id_holder) || (!json_is_integer(id_holder)))
|
||||||
|
{
|
||||||
|
result->status = WSFS_BAD_INVALIDID;
|
||||||
|
json_decref(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result->status = WSFS_GOOD;
|
||||||
|
result->id = json_integer_value(id_holder);
|
||||||
|
result->result = json_object_get(response, "result");
|
||||||
|
if (NULL != result->result)
|
||||||
|
{
|
||||||
|
json_incref(result->result);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result->status = WSFS_BAD_NODATA;
|
||||||
|
|
||||||
|
json_t * error = json_object_get(response, "error");
|
||||||
|
if (NULL != error)
|
||||||
|
{
|
||||||
|
json_t * error_code = json_object_get(error, "code");
|
||||||
|
if ((NULL != error_code) && (json_is_integer(error_code)))
|
||||||
|
{
|
||||||
|
result->status = json_integer_value(error_code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json_decref(response);
|
||||||
|
}
|
||||||
|
|
21
src/wsfs/response_parser.h
Normal file
21
src/wsfs/response_parser.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef _WSFS_RESPONSE_PARSER_H
|
||||||
|
#define _WFSF_RESPONSE_PARSER_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <jansson.h>
|
||||||
|
#include "wsfs/status.h"
|
||||||
|
|
||||||
|
struct wsfs_response
|
||||||
|
{
|
||||||
|
wsfs_status status;
|
||||||
|
int id;
|
||||||
|
json_t * result;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void wsfs_response_parse(
|
||||||
|
char const * buffer,
|
||||||
|
size_t buffer_length,
|
||||||
|
struct wsfs_response * response);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
220
src/wsfs/server.c
Normal file
220
src/wsfs/server.c
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
#include "wsfs/server.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include <libwebsockets.h>
|
||||||
|
|
||||||
|
#include "wsfs/util.h"
|
||||||
|
#include "wsfs/protocol.h"
|
||||||
|
#include "wsfs/jsonrpc.h"
|
||||||
|
|
||||||
|
#define WSFS_SERVICE_INTERVAL (1 * 1000)
|
||||||
|
|
||||||
|
struct wsfs_server
|
||||||
|
{
|
||||||
|
pthread_t thread;
|
||||||
|
bool shutdown_flag;
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
struct wsfs_server_config config;
|
||||||
|
struct wsfs_protocol * protocol;
|
||||||
|
struct wsfs_jsonrpc * rpc;
|
||||||
|
};
|
||||||
|
|
||||||
|
static char * wsfs_strdup(char const * value)
|
||||||
|
{
|
||||||
|
char * result = NULL;
|
||||||
|
if (NULL != value)
|
||||||
|
{
|
||||||
|
result = strdup(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool wsfs_server_isshutdown(
|
||||||
|
struct wsfs_server * server)
|
||||||
|
{
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&server->lock);
|
||||||
|
result = server->shutdown_flag;
|
||||||
|
pthread_mutex_unlock(&server->lock);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool wsfs_server_tls_enabled(
|
||||||
|
struct wsfs_server * server)
|
||||||
|
{
|
||||||
|
return ((server->config.key_path != NULL) && (server->config.cert_path != NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wsfs_server_request_shutdown(
|
||||||
|
struct wsfs_server * server)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&server->lock);
|
||||||
|
server->shutdown_flag = true;
|
||||||
|
pthread_mutex_unlock(&server->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wsfs_ignore_signal(int WSFS_UNUSED_PARAM(signal_id))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* wsfs_server_run(void * arg)
|
||||||
|
{
|
||||||
|
struct wsfs_server * const server = arg;
|
||||||
|
|
||||||
|
struct lws_protocols protocols[] =
|
||||||
|
{
|
||||||
|
{"http", lws_callback_http_dummy, 0, 0, 0, NULL, 0},
|
||||||
|
{ "fs", NULL, 0 , 0, 0, NULL, 0},
|
||||||
|
{ NULL, NULL, 0 , 0, 0, NULL, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lws_http_mount mount =
|
||||||
|
{
|
||||||
|
.mount_next = NULL,
|
||||||
|
.mountpoint = "/",
|
||||||
|
.origin = server->config.document_root,
|
||||||
|
.def = "index.html",
|
||||||
|
.protocol = NULL,
|
||||||
|
.cgienv = NULL,
|
||||||
|
.extra_mimetypes = NULL,
|
||||||
|
.interpret = NULL,
|
||||||
|
.cgi_timeout = 0,
|
||||||
|
.cache_max_age = 0,
|
||||||
|
.auth_mask = 0,
|
||||||
|
.cache_reusable = 0,
|
||||||
|
.cache_intermediaries = 0,
|
||||||
|
.origin_protocol = LWSMPRO_FILE,
|
||||||
|
.mountpoint_len = 1,
|
||||||
|
.basic_auth_login_file = NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
wsfs_protocol_init_lws(server->protocol, &protocols[1]);
|
||||||
|
|
||||||
|
struct lws_context_creation_info info;
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
info.port = server->config.port;
|
||||||
|
info.mounts = &mount;
|
||||||
|
info.protocols = protocols;
|
||||||
|
info.vhost_name = server->config.vhost_name;
|
||||||
|
info.ws_ping_pong_interval = 10;
|
||||||
|
info.options = LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE;
|
||||||
|
|
||||||
|
if (NULL == server->config.document_root)
|
||||||
|
{
|
||||||
|
// disable http
|
||||||
|
info.protocols = &(protocols[1]);
|
||||||
|
info.mounts = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wsfs_server_tls_enabled(server))
|
||||||
|
{
|
||||||
|
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
|
||||||
|
info.ssl_cert_filepath = server->config.cert_path;
|
||||||
|
info.ssl_private_key_filepath = server->config.key_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct lws_context * context = lws_create_context(&info);
|
||||||
|
if (NULL == context)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "error: unable to start websocket server\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
while ((0 <= n) && (!wsfs_server_isshutdown(server)))
|
||||||
|
{
|
||||||
|
wsfs_protocol_check(server->protocol);
|
||||||
|
n = lws_service(context, WSFS_SERVICE_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
lws_context_destroy(context);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wsfs_server_join(struct wsfs_server * server)
|
||||||
|
{
|
||||||
|
wsfs_server_request_shutdown(server);
|
||||||
|
pthread_join(server->thread, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wsfs_server * wsfs_server_create(
|
||||||
|
struct wsfs_server_config * config)
|
||||||
|
{
|
||||||
|
signal(SIGUSR1, &wsfs_ignore_signal);
|
||||||
|
|
||||||
|
struct wsfs_server * server = malloc(sizeof(struct wsfs_server));
|
||||||
|
if (NULL != server)
|
||||||
|
{
|
||||||
|
pthread_mutex_init(&server->lock, NULL);
|
||||||
|
server->shutdown_flag = false;
|
||||||
|
server->config.document_root = wsfs_strdup(config->document_root);
|
||||||
|
server->config.cert_path = wsfs_strdup(config->cert_path);
|
||||||
|
server->config.key_path = wsfs_strdup(config->key_path);
|
||||||
|
server->config.vhost_name = wsfs_strdup(config->vhost_name);
|
||||||
|
server->config.port = config->port;
|
||||||
|
|
||||||
|
server->rpc = wsfs_jsonrpc_create(&wsfs_protocol_message_create, &wsfs_protocol_send, NULL);
|
||||||
|
server->protocol = wsfs_protocol_create(server);
|
||||||
|
wsfs_jsonrpc_set_user_data(server->rpc, server->protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wsfs_server_dispose(
|
||||||
|
struct wsfs_server * server)
|
||||||
|
{
|
||||||
|
wsfs_server_join(server);
|
||||||
|
|
||||||
|
wsfs_jsonrpc_dispose(server->rpc);
|
||||||
|
wsfs_protocol_dispose(server->protocol);
|
||||||
|
wsfs_server_config_clear(&server->config);
|
||||||
|
pthread_mutex_destroy(&server->lock);
|
||||||
|
free(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wsfs_server_start(
|
||||||
|
struct wsfs_server * server)
|
||||||
|
{
|
||||||
|
pthread_create(&server->thread, NULL, &wsfs_server_run, server);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wsfs_server_config_clear(struct wsfs_server_config * config)
|
||||||
|
{
|
||||||
|
free(config->document_root);
|
||||||
|
free(config->cert_path);
|
||||||
|
free(config->key_path);
|
||||||
|
free(config->vhost_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wsfs_jsonrpc * wsfs_server_get_jsonrpc_service(
|
||||||
|
struct wsfs_server * server)
|
||||||
|
{
|
||||||
|
return server->rpc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wsfs_server_wakeup(
|
||||||
|
struct wsfs_server * server)
|
||||||
|
{
|
||||||
|
pthread_kill(server->thread, SIGUSR1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wsfs_server_handle_message(
|
||||||
|
struct wsfs_server * server,
|
||||||
|
char const * message,
|
||||||
|
size_t length)
|
||||||
|
{
|
||||||
|
wsfs_jsonrpc_on_message(message, length, server->rpc);
|
||||||
|
}
|
||||||
|
|
42
src/wsfs/server.h
Normal file
42
src/wsfs/server.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#ifndef _WSFS_SERVER_H
|
||||||
|
#define _WSFS_SERVER_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct wsfs_server;
|
||||||
|
struct wsfs_jsonrpc;
|
||||||
|
|
||||||
|
struct wsfs_server_config
|
||||||
|
{
|
||||||
|
char * document_root;
|
||||||
|
char * key_path;
|
||||||
|
char * cert_path;
|
||||||
|
char * vhost_name;
|
||||||
|
int port;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void wsfs_server_config_clear(
|
||||||
|
struct wsfs_server_config * config);
|
||||||
|
|
||||||
|
extern struct wsfs_server * wsfs_server_create(
|
||||||
|
struct wsfs_server_config * config);
|
||||||
|
|
||||||
|
extern void wsfs_server_dispose(
|
||||||
|
struct wsfs_server * server);
|
||||||
|
|
||||||
|
extern struct wsfs_jsonrpc * wsfs_server_get_jsonrpc_service(
|
||||||
|
struct wsfs_server * server);
|
||||||
|
|
||||||
|
extern void wsfs_server_start(
|
||||||
|
struct wsfs_server * server);
|
||||||
|
|
||||||
|
extern void wsfs_server_wakeup(
|
||||||
|
struct wsfs_server * server);
|
||||||
|
|
||||||
|
extern void wsfs_server_handle_message(
|
||||||
|
struct wsfs_server * server,
|
||||||
|
char const * message,
|
||||||
|
size_t length);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
18
src/wsfs/status.h
Normal file
18
src/wsfs/status.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef _WSFS_STATUS_H
|
||||||
|
#define _WSFS_STATUS_H
|
||||||
|
|
||||||
|
#define WSFS_GOOD 0
|
||||||
|
#define WSFS_BAD 1
|
||||||
|
|
||||||
|
#define WSFS_BAD_NOENTRY 101
|
||||||
|
#define WSFS_BAD_TIMEOUT 102
|
||||||
|
|
||||||
|
#define WSFS_BAD_PARSEERROR 200
|
||||||
|
#define WSFS_BAD_INVALIDID 201
|
||||||
|
#define WSFS_BAD_NODATA 202
|
||||||
|
|
||||||
|
typedef int wsfs_status;
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
10
src/wsfs/util.h
Normal file
10
src/wsfs/util.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef _WSFS_UTIL_H
|
||||||
|
#define _WSFS_UTIL_H
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define WSFS_UNUSED_PARAM(param) param __attribute__((unused))
|
||||||
|
#else
|
||||||
|
#define WSFS_UNUSED_PARAM(param)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
13
test-src/test_main.c
Normal file
13
test-src/test_main.c
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "test_util.h"
|
||||||
|
#include "wsfs/util.h"
|
||||||
|
|
||||||
|
extern void test_request_parser();
|
||||||
|
|
||||||
|
int main(int WSFS_UNUSED_PARAM(argc), char* WSFS_UNUSED_PARAM(argv[]))
|
||||||
|
{
|
||||||
|
test_request_parser();
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
62
test-src/test_response_parser.c
Normal file
62
test-src/test_response_parser.c
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "wsfs/response_parser.h"
|
||||||
|
|
||||||
|
static void wsfs_response_parse_str(
|
||||||
|
char const * buffer,
|
||||||
|
struct wsfs_response * response)
|
||||||
|
{
|
||||||
|
size_t length = strlen(buffer);
|
||||||
|
wsfs_response_parse(buffer, length, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_request_parser()
|
||||||
|
{
|
||||||
|
struct wsfs_response response;
|
||||||
|
|
||||||
|
// invalid json
|
||||||
|
wsfs_response_parse_str("", &response);
|
||||||
|
ASSERT_NE(WSFS_GOOD, response.status);
|
||||||
|
ASSERT_EQ(-1, response.id);
|
||||||
|
ASSERT_EQ(NULL, response.result);
|
||||||
|
|
||||||
|
// invalid json
|
||||||
|
wsfs_response_parse_str("invalid_json", &response);
|
||||||
|
ASSERT_NE(WSFS_GOOD, response.status);
|
||||||
|
ASSERT_EQ(-1, response.id);
|
||||||
|
ASSERT_EQ(NULL, response.result);
|
||||||
|
|
||||||
|
// no object
|
||||||
|
wsfs_response_parse_str("[]", &response);
|
||||||
|
ASSERT_NE(WSFS_GOOD, response.status);
|
||||||
|
ASSERT_EQ(-1, response.id);
|
||||||
|
ASSERT_EQ(NULL, response.result);
|
||||||
|
|
||||||
|
// empty
|
||||||
|
wsfs_response_parse_str("{}", &response);
|
||||||
|
ASSERT_NE(WSFS_GOOD, response.status);
|
||||||
|
ASSERT_EQ(-1, response.id);
|
||||||
|
ASSERT_EQ(NULL, response.result);
|
||||||
|
|
||||||
|
// no data
|
||||||
|
wsfs_response_parse_str("{\"id\":42}", &response);
|
||||||
|
ASSERT_NE(WSFS_GOOD, response.status);
|
||||||
|
ASSERT_EQ(42, response.id);
|
||||||
|
ASSERT_EQ(NULL, response.result);
|
||||||
|
|
||||||
|
// custom error code
|
||||||
|
wsfs_response_parse_str("{\"error\":{\"code\": 42}, \"id\": 42}", &response);
|
||||||
|
ASSERT_NE(WSFS_GOOD, response.status);
|
||||||
|
ASSERT_EQ(42, response.status);
|
||||||
|
ASSERT_EQ(42, response.id);
|
||||||
|
ASSERT_EQ(NULL, response.result);
|
||||||
|
|
||||||
|
// valid response
|
||||||
|
wsfs_response_parse_str("{\"result\": true, \"id\": 42}", &response);
|
||||||
|
ASSERT_EQ(WSFS_GOOD, response.status);
|
||||||
|
ASSERT_EQ(42, response.id);
|
||||||
|
ASSERT_NE(NULL, response.result);
|
||||||
|
json_decref(response.result);
|
||||||
|
}
|
15
test-src/test_util.c
Normal file
15
test-src/test_util.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include "test_util.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void fail(
|
||||||
|
char const * file_name,
|
||||||
|
int line,
|
||||||
|
char const * message
|
||||||
|
)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "error: %s:%d: %s\n", file_name, line, message);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
31
test-src/test_util.h
Normal file
31
test-src/test_util.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef _WSFS_TEST_UTIL_H
|
||||||
|
#define _WSFS_TEST_UTIL_H
|
||||||
|
|
||||||
|
#define ASSERT_EQ(expected, actual) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if ((expected) != (actual)) \
|
||||||
|
{ \
|
||||||
|
fail(__FILE__, __LINE__, "expected " #expected ", but was " #actual); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
while (0)
|
||||||
|
|
||||||
|
#define ASSERT_NE(expected, actual) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
if ((expected) == (actual)) \
|
||||||
|
{ \
|
||||||
|
fail(__FILE__, __LINE__, "expected " #expected ", but was " #actual); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
while (0)
|
||||||
|
|
||||||
|
extern void fail(
|
||||||
|
char const * file_name,
|
||||||
|
int line,
|
||||||
|
char const * message
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue
Block a user