Browse Source

Move utilities for RPC into GameLib

These were in https://macoy.me/code/macoy/distributed-automation but I
found myself wanting them for other applications.
master
Macoy Madson 2 weeks ago
parent
commit
2ba07f0320
  1. 3
      ReadMe.org
  2. 50
      src/KeyManagement.cake
  3. 260
      src/Protocol.cake
  4. 339
      src/RemoteCommands.cake

3
ReadMe.org

@ -114,6 +114,7 @@ Here are the known compatibility results, where blank means untested/unknown:
| ImGui.cake | Yes | No[1] | Yes | |
| ImGuiSDLOpenGL.cpp | Yes | No[1] | Yes | |
| Introspection.cake | Yes | Yes | Yes | Yes |
| KeyManagement.cake | Yes | Yes | Yes | Yes |
| Licenses.cake | Yes | Yes | Yes | Yes |
| Math.cake | Yes | Yes | Yes | Yes |
| Network.cake | Yes | Yes | Yes | |
@ -124,7 +125,9 @@ Here are the known compatibility results, where blank means untested/unknown:
| OpenSSL.cake | Yes | | | |
| ProfilerAutoInstrument.cake | Yes | Yes | Yes | Yes |
| ProfilerNull.cake | Yes | Yes | Yes | Yes |
| Protocol.cake | Yes | Yes | Yes | Yes |
| Raylib.cake | Yes | | | |
| RemoteCommands.cake | Yes | Yes | Yes | Yes |
| SDL.cake | Yes | | Yes | |
| STB.cake | Yes | Yes | Yes | Yes |
| TaskSystem.cake | Yes | | Yes | |

50
src/KeyManagement.cake

@ -0,0 +1,50 @@
;; KeyManagement.cake A simple module for managing the application's encryption keys
(import
;; Cakelisp
"CHelpers.cake"
;; GameLib
"Cryptography.cake" "Hash.cake")
;; TODO: Refactor to hide crypto from the interface
;; I had to do this for the crypto_box_* defines
(c-import &with-decls "sodium.h")
(c-import "<string.h>"
&with-decls "<stdbool.h>")
(var-global g-my-secret-key (array crypto_box_SECRETKEYBYTES (unsigned char)) (array 0))
(var-global g-my-public-key (array crypto_box_PUBLICKEYBYTES (unsigned char)) (array 0))
(var-global g-my-machine-id (unsigned int) 0)
(defstruct encryption-keys
my-secret-key (array crypto_box_SECRETKEYBYTES (unsigned char))
their-public-key (array crypto_box_PUBLICKEYBYTES (unsigned char)))
;; While unlikely, it is possible a machine would conflict on ID. It doesn't mean that machine
;; would impersonate another, because the decryption would still fail.
;; TODO: Come up with more robust solution.
(defun machine-id-from-public-key (public-key (addr (unsigned char))
&return (unsigned int))
(var id (unsigned int) 0)
(hash-crc32 public-key crypto_box_PUBLICKEYBYTES (addr id))
(return id))
;; Load existing keys or generate new ones if necessary
(defun key-management-initialize (keys-filename (addr (const char)) &return bool)
(hash-crc32-initialize)
(unless (cryptography-initialize)
(return false))
(unless (load-box-keys-from-file keys-filename g-my-public-key g-my-secret-key)
(unless ;; Generate new keys
(and (generate-box-keys-to-file keys-filename)
(load-box-keys-from-file keys-filename g-my-public-key g-my-secret-key))
(return false)))
(da-log "My public key is:\n")
(each-in-array g-my-public-key i
(da-log "0x%02x " (at i g-my-public-key)))
(da-log "\n")
(set g-my-machine-id (machine-id-from-public-key g-my-public-key))
(return true))

260
src/Protocol.cake

@ -0,0 +1,260 @@
;; Protocol.cake: A home-rolled network protocol that encrypts packets with libsodium
(import
"RemoteCommands.cake"
;; Cakelisp
"CHelpers.cake"
;; GameLib
"Network.cake" "Cryptography.cake"
;; Note: KeyManagement.cake comes after Cryptography.cake so that the sodium defines exist
"KeyManagement.cake")
(forward-declare (struct remote-command-queue)
(struct encryption-keys))
(c-import &with-decls "<stdbool.h>")
(var-global g-protocol-verbose bool false)
(var c-protocol-introduction-magic (addr (const char)) "intr")
(var c-protocol-magic (addr (const char)) "cake")
(var c-max-payload-size (const (unsigned int)) (* 1024 1024))
(defmacro proto-send (socket any payload any payload-size any)
(comptime-cond
('Windows
(tokenize-push output
(send (token-splice socket) (type-cast (token-splice payload) (addr (const char)))
(token-splice payload-size) 0)))
(true
(tokenize-push output
;; Do not signal on Linux so we can handle the error without installing a signal handler
(send (token-splice socket) (token-splice payload)
(token-splice payload-size) MSG_NOSIGNAL))))
(return true))
(defmacro proto-recv (socket any payload any payload-size any)
(comptime-cond
('Windows
(tokenize-push output
(recv (token-splice socket) (type-cast (token-splice payload) (* char))
(token-splice payload-size) 0)))
(true
(tokenize-push output
;; Do not signal on Linux so we can handle the error without installing a signal handler
(recv (token-splice socket) (token-splice payload)
(token-splice payload-size) MSG_NOSIGNAL))))
(return true))
;; Cake protocol:
;; First, an introduction needs to occur, unless the two nodes know each other already:
;; - "intr" (Magic)
;; - Identifier, (e.g. a CRC32 of sender's public key)
;; This is sent to help identify senders when they are initially unknown. Because all possible
;; connections are hard-coded, the full public key will be found, or the connection will be rejected.
;; Then, payloads can be sent:
;; - "cake" (Magic)
;; - Nonce (effectively "salt" for payload. See:
;; https://doc.libsodium.org/public-key_cryptography/authenticated_encryption)
;; - 4 byte unsigned int payload size (DOES include additional cryptography bytes; is NOT itself encrypted)
;; - Encrypted payload
(defun send-cake-protocol-introduction (to-socket socket-type
identifier (unsigned int)
&return bool)
;; Magic
(unless (= (proto-send to-socket c-protocol-introduction-magic
(strlen c-protocol-introduction-magic))
(strlen c-protocol-introduction-magic))
(return false))
;; Identifier
(var identifier-network (unsigned int) (htonl identifier))
(unless (= (proto-send to-socket (addr identifier-network) (sizeof identifier-network))
(sizeof identifier-network))
(return false))
(return true))
(defun receive-cake-protocol-introduction (from-socket socket-type
&return (unsigned int))
;; Magic
(var received-magic (array 4 char) (array 0))
(var num-read int (proto-recv from-socket received-magic (array-size received-magic)))
(unless (and (= num-read (array-size received-magic))
(= 0 (memcmp received-magic c-protocol-introduction-magic (array-size received-magic))))
(if num-read
(da-log "Received data which did not match cake protocol: the magic string did not match.\n")
;; Read size of 0 can indicate severed connection
(da-log "The connection has been closed unexpectedly.\n"))
(return 0))
;; Identifier
(var identifier-network (unsigned int) 0)
(set num-read (proto-recv from-socket (addr identifier-network) (sizeof identifier-network)))
(unless (= num-read (sizeof identifier-network))
(da-log "Received data which did not match cake protocol: the identifier was not the expected size.\n")
(return 0))
(return (ntohl identifier-network)))
;;
;; Payload send/receive (after introduction completes)
;;
(defun send-cake-protocol (to-socket socket-type
keys (addr encryption-keys)
payload (addr (const void))
payload-size (unsigned int)
&return bool)
(unless (and payload-size (< payload-size c-max-payload-size))
(da-log "Payload is size %d. Payload must not be empty, and must not be larger than %d bytes.\n"
payload-size c-max-payload-size)
(return false))
;; Encrypt the payload
(var nonce (array crypto_box_NONCEBYTES (unsigned char)))
(randombytes_buf nonce (sizeof nonce))
(var encrypted-payload (array (+ crypto_box_MACBYTES (* 1024 1024)) (unsigned char)) (array 0))
(unless (= 0 (crypto_box_easy encrypted-payload (type-cast payload (addr (const (unsigned char))))
payload-size nonce
(path keys > their-public-key)
(path keys > my-secret-key)))
(da-log "Failed to encrypt payload\n")
(return false))
;; Magic
(unless (= (proto-send to-socket c-protocol-magic (strlen c-protocol-magic))
(strlen c-protocol-magic))
(return false))
;; Nonce
(unless (= (proto-send to-socket nonce (sizeof nonce))
(sizeof nonce))
(return false))
;; Size (the size itself is NOT encrypted, INCLUDES added crypto bytes)
(var encrypted-payload-size int (+ crypto_box_MACBYTES payload-size))
(var encrypted-payload-size-network (unsigned int)
(htonl encrypted-payload-size))
(unless (= (proto-send to-socket (addr encrypted-payload-size-network)
(sizeof encrypted-payload-size-network))
(sizeof encrypted-payload-size-network))
(return false))
;; Payload
(unless (= (proto-send to-socket encrypted-payload encrypted-payload-size)
encrypted-payload-size)
(return false))
(return true))
(defun receive-cake-protocol (from-socket socket-type
keys (addr encryption-keys)
output-buffer (addr void)
output-buffer-size int
&return int)
;; Magic
(var received-magic (array 4 char) (array 0))
(var num-read int (proto-recv from-socket received-magic (array-size received-magic)))
(unless (and (= num-read (array-size received-magic))
(= 0 (memcmp received-magic c-protocol-magic (array-size received-magic))))
(if num-read
(da-log "Received data which did not match cake protocol: the magic string did not match.\n")
(da-log "The connection has been closed unexpectedly.\n"))
(return -1))
;; Nonce
(var nonce (array crypto_box_NONCEBYTES (unsigned char)))
(set num-read (proto-recv from-socket nonce (array-size nonce)))
(unless (= num-read (array-size nonce))
(da-log "Received data which did not match cake protocol: the nonce was not the expected size.\n")
(return -1))
;; Size (INCLUDES added crypto bytes)
(var encrypted-payload-size (unsigned int) 0)
(var payload-size (unsigned int) 0)
(scope
(var payload-size-network (unsigned int) 0)
(unless (= (proto-recv from-socket (addr payload-size-network) (sizeof payload-size-network))
(sizeof payload-size-network))
(da-log "Expected payload size, but it could not be read.\n")
(return -1))
(set encrypted-payload-size (ntohl payload-size-network))
(set payload-size (- encrypted-payload-size crypto_box_MACBYTES))
(unless (and payload-size
(< payload-size c-max-payload-size))
(da-log "Payload is an invalid size %d. The max payload size is %d.\n"
payload-size c-max-payload-size)
(return -1))
(unless (<= payload-size output-buffer-size)
(da-log "Payload is %d bytes, but the output buffer is only %d bytes.\n"
payload-size output-buffer-size)
(return -1)))
;; Payload
(var encrypted-payload (array (+ crypto_box_MACBYTES (* 1024 1024)) (unsigned char)) (array 0))
(set num-read 0)
(while (< num-read encrypted-payload-size)
(var num-read-this-read int
(proto-recv from-socket (+ encrypted-payload num-read)
(- encrypted-payload-size num-read)))
(set num-read (+ num-read num-read-this-read)))
(unless (= num-read encrypted-payload-size)
(da-log "Expected to read %d bytes, but only received %d bytes.\n"
encrypted-payload-size num-read)
(return -1))
(unless (= 0 (crypto_box_open_easy (type-cast output-buffer (addr (unsigned char)))
encrypted-payload encrypted-payload-size nonce
(path keys > their-public-key)
(path keys > my-secret-key)))
(da-log "The message could not be decrypted.\n")
(return -1))
(return payload-size))
;;
;; RemoteCommands.cake support. Should probably be elsewhere.
;;
(defun send-command-queue (command-queue (addr remote-command-queue)
to-socket socket-type
keys (addr encryption-keys)
&return bool)
(var requested-commands-buffer (array (* 16 1024) char) (array 0))
(var serialize-result int
(serialize-requested-remote-commands command-queue
requested-commands-buffer
(sizeof requested-commands-buffer)))
(cond
((> serialize-result 0)
(when g-protocol-verbose
(da-log "Sending: \"%s\"\n" requested-commands-buffer))
(unless (send-cake-protocol to-socket keys
requested-commands-buffer serialize-result)
(return false)))
((= serialize-result -1)
(da-log "Failed to serialize requested command queue.\n")
(return false)))
(return true))
(defun receive-command-queue (command-queue (addr remote-command-queue)
from-socket socket-type
keys (addr encryption-keys)
&return bool)
(var received-commands (array (* 16 1024) char) (array 0))
(var num-read int (receive-cake-protocol from-socket keys
received-commands (sizeof received-commands)))
(cond
((< num-read 0)
(perror "Reading socket for commands")
(return false))
((= num-read 0)
(da-log "No commands at this time.\n"))
((> num-read 0)
(if (clear-receive-requested-remote-commands
command-queue received-commands num-read)
(when g-protocol-verbose
(da-log "Commands received: \"%s\"\n" received-commands))
(return false))))
(return true))

339
src/RemoteCommands.cake

@ -0,0 +1,339 @@
;; RemoteCommands.cake: Define commands with arguments that can be serialized and requested
;; or responded to over the network. The goal interface should be about as easy as calling a
;; local procedure.
(import "Introspection.cake" "DynamicArray.cake")
;; We generate code in the calling module that requires introspection
(export
(import "Introspection.cake")
(c-import &with-decls "<stddef.h>"
;; For malloc
&with-defs "<stdlib.h>"))
(c-import "<stdio.h>" "<string.h>"
&with-decls "<stdbool.h>")
(var-global g-remote-commands-verbose bool false)
;; TODO: Determine the right #defines to properly get strdup from system headers
(declare-extern-function strdup (str (addr (const char)) &return (addr char)))
;;
;; Types
;;
(forward-declare (struct remote-command-queue))
(def-introspect-struct remote-command-request
command-name-hash int
;; TODO This is gross, but we'll deal with it for now
payload (addr char))
;; NOTE: These should both be module-local, but Introspection doesn't have those yet
(def-introspect-struct remote-command-queue
commands (addr remote-command-request) (override 'dynarray))
(defun create-remote-command-queue (&return (addr remote-command-queue))
(var-cast-to new-queue (addr remote-command-queue)
(malloc (sizeof (type remote-command-queue))))
(memset new-queue 0 (sizeof (type remote-command-queue)))
(return new-queue))
;; Re-use memory
(defun clear-remote-command-queue (queue (addr remote-command-queue))
(each-item-addr-in-dynarray (path queue > commands) i command (addr remote-command-request)
(when (path command > payload)
(free (path command > payload))))
(dynarray-clear (path queue > commands)))
(defun free-remote-command-queue (queue (addr remote-command-queue))
(clear-remote-command-queue queue)
(dynarray-free (path queue > commands))
(free queue))
(defun append-remote-command-queue (destination-queue (addr remote-command-queue)
queue-to-append (addr remote-command-queue))
(dynarray-set-capacity (path destination-queue > commands)
(+ (dynarray-length (path destination-queue > commands))
(dynarray-length (path queue-to-append > commands))))
(each-item-addr-in-dynarray (path queue-to-append > commands) i command (addr remote-command-request)
(var new-command remote-command-request (deref command))
;; TODO: Shouldn't need to do this!
(set (field new-command payload) (strdup (path command > payload)))
(dynarray-push (path destination-queue > commands) new-command)))
;; Returns only whether the command parsing succeeded
;; Do not update without also updating the forward-declarations block in set-remote-command-definitions
(def-function-signature-global remote-command-parse-call-function (packed-arguments (addr (const char))
userdata (addr void)
&return bool))
(defstruct remote-command-definition
name (addr (const char))
name-hash int
;; Use this to introspect on the struct used to create the packed arguments. This can be useful for
;; e.g. providing an interface for the user to set the arguments to a command.
metadata (addr (const metadata-struct))
function remote-command-parse-call-function)
;; Filled in at compile-time
(splice-point remote-command-forward-declarations)
(var-global g-remote-command-definitions (array remote-command-definition) (array 0))
;; Filled in at compile-time; necessary to set some fields which are not constant initializable
(defun initialize-remote-command-definitions ())
;;
;; Command definition interface
;;
(defun-comptime set-remote-command-definitions (environment (ref EvaluatorEnvironment)
&return bool)
(get-or-create-comptime-var num-remote-commands-installed int 0)
(get-or-create-comptime-var remote-commands
(template (in std unordered_map) (in std string)
(addr (const Token))))
(unless (> (call-on-ptr size remote-commands) (deref num-remote-commands-installed))
(return true))
(var command-forward-declarations (addr (template (in std vector) Token)) (new (template (in std vector) Token)))
(call-on push_back (field environment comptimeTokens) command-forward-declarations)
(var commands-tokens (template (in std vector) Token))
(var commands-initialize-tokens (template (in std vector) Token))
(var metadata-tokens (template (in std vector) Token))
(var command-index int 0)
(for-in remote-command-pair (ref (const (template (in std pair) (in std string)
(addr (const Token)))))
(deref remote-commands)
(var command-function-name-token Token (at 2 (field remote-command-pair second)))
(var command-name-token Token command-function-name-token)
(call-on append (field command-function-name-token contents) "-parse-arguments-then-call")
(set (field command-name-token type) TokenType_String)
(var arguments-struct-metadata-name Token (at 2 (field remote-command-pair second)))
(call-on append (field arguments-struct-metadata-name contents) "-arguments--metadata")
(var command-name-hash-token Token (at 2 (field remote-command-pair second)))
(var command-name-hash int 0)
;; TODO: Check for hash collisions!
(crc32 (call-on c_str (field command-name-token contents))
(call-on length (field command-name-token contents))
(type-cast (addr command-name-hash) (addr uint32_t)))
(token-contents-snprintf command-name-hash-token "%d" command-name-hash)
(tokenize-push (deref command-forward-declarations)
;; Needs to match remote-command-function signature
(declare-external (var (token-splice-addr arguments-struct-metadata-name)
(const (addr (const metadata-struct)))))
(declare-extern-function (token-splice-addr command-function-name-token)
(packed-arguments (addr (const char))
userdata (addr void)
&return bool)))
(tokenize-push commands-tokens
(array (token-splice-addr command-name-token)
(token-splice-addr command-name-hash-token)
;; This is not constant initializer in C; we need to do it at runtime
null ;;(token-splice-addr arguments-struct-metadata-name)
(token-splice-addr command-function-name-token)))
(var command-index-token Token (at 2 (field remote-command-pair second)))
(token-contents-snprintf command-index-token "%d" command-index)
(tokenize-push commands-initialize-tokens
(set (field (at (token-splice-addr command-index-token) g-remote-command-definitions)
metadata) (token-splice-addr arguments-struct-metadata-name)))
(incr command-index))
(var commands-definitions-tokens (template (in std vector) Token))
(tokenize-push commands-definitions-tokens
(var g-remote-command-definitions (array remote-command-definition)
(array (token-splice-array commands-tokens))))
(unless (ReplaceAndEvaluateDefinition
environment
"g-remote-command-definitions"
commands-definitions-tokens)
(return false))
(unless (ClearAndEvaluateAtSplicePoint environment "remote-command-forward-declarations"
command-forward-declarations)
(return false))
(var commands-initialization-tokens (template (in std vector) Token))
(tokenize-push commands-initialization-tokens
(defun initialize-remote-command-definitions ()
(token-splice-array commands-initialize-tokens)))
(unless (ReplaceAndEvaluateDefinition
environment
"initialize-remote-command-definitions"
commands-initialization-tokens)
(return false))
(set (deref num-remote-commands-installed) (call-on-ptr size remote-commands))
(return true))
(add-compile-time-hook post-references-resolved set-remote-command-definitions)
(defmacro define-remote-command (name symbol arguments array &rest body any)
(get-or-create-comptime-var remote-commands
(template (in std unordered_map) (in std string)
(addr (const Token))))
(set (at (path name > contents) (deref remote-commands)) (addr (at startTokenIndex tokens)))
(var arguments-to-struct (template (in std vector) Token))
(var passing-arguments (template (in std vector) Token))
(var arguments-start-index int (- arguments (addr (at 0 tokens))))
(var end-arguments-index int (FindCloseParenTokenIndex tokens arguments-start-index))
(each-token-argument-in tokens (+ 1 arguments-start-index) end-arguments-index argument-index
(var argument-token (addr (const Token)) (addr (at argument-index tokens)))
(tokenize-push arguments-to-struct
(set (field packed-arguments (token-splice argument-token)) (token-splice argument-token)))
(tokenize-push passing-arguments
(field unpacked-arguments (token-splice argument-token)))
;; Skip over type
(incr argument-index))
;; TODO: This is nasty
(var arguments-with-userdata (template (in std vector) Token))
(tokenize-push arguments-with-userdata
(token-splice-rest (+ 1 arguments) tokens)
userdata (addr void))
(var arguments-struct-name Token (deref name))
(call-on append (field arguments-struct-name contents) "-arguments")
(var arguments-struct-metadata-name Token arguments-struct-name)
(call-on append (field arguments-struct-metadata-name contents) "--metadata")
(var fill-arguments-function-name Token (deref name))
(call-on append (field fill-arguments-function-name contents) "-fill-arguments")
(var parse-arguments-call-function-name Token (deref name))
(call-on append (field parse-arguments-call-function-name contents) "-parse-arguments-then-call")
(tokenize-push output
;; TODO: This struct can be module-local once introspect-struct supports that
(def-introspect-struct (token-splice-addr arguments-struct-name)
(token-splice-rest (+ 1 arguments) tokens))
(defun (token-splice-addr fill-arguments-function-name) ((token-splice-rest (+ 1 arguments) tokens)
output-buffer (addr char) output-buffer-size size_t
&return bool)
;; Pack arguments into struct, then write into string buffer
(var packed-arguments (token-splice-addr arguments-struct-name) (array 0))
(token-splice-array arguments-to-struct)
(var buffer-write-data write-introspect-struct-buffer-data
(array output-buffer output-buffer output-buffer-size))
(unless (write-introspect-struct-s-expr
(token-splice-addr arguments-struct-metadata-name) (addr packed-arguments)
write-introspect-struct-buffer-writer (addr buffer-write-data)
write-introspect-struct-default)
(return false))
(return true))
(defun (token-splice-addr parse-arguments-call-function-name)
(packed-arguments (addr (const char))
userdata (addr void)
&return bool)
(var unpacked-arguments (token-splice-addr arguments-struct-name) (array 0))
(var characters-read (unsigned int) 0)
(unless (read-introspect-struct-s-expr
(token-splice-addr arguments-struct-metadata-name)
(addr unpacked-arguments) packed-arguments
malloc (addr characters-read))
(da-log "Failed to de-serialize command arguments\n")
(return false))
(call (token-splice name) (token-splice-array passing-arguments) userdata)
(free-introspect-struct-fields (token-splice-addr arguments-struct-metadata-name)
(addr unpacked-arguments) free)
(return true))
(defun (token-splice name) ((token-splice-array arguments-with-userdata))
(token-splice-rest body tokens)))
(return true))
;;
;; Functions
;;
;; Note that this will copy the packed arguments if provided, so don't worry about the lifetime
(defun queue-remote-command-internal (queue (addr remote-command-queue)
command-name-hash int
packed-arguments (addr (const char)))
(var command-request remote-command-request (array 0))
(set (field command-request command-name-hash) command-name-hash)
(when packed-arguments
(set (field command-request payload) (strdup packed-arguments)))
(dynarray-push (path queue > commands) command-request))
(defmacro queue-remote-command (output-queue any command symbol &optional &rest arguments any)
(var command-name-hash int 0)
(crc32 (call-on c_str (path command > contents))
(call-on length (path command > contents))
(type-cast (addr command-name-hash) (addr uint32_t)))
(var hash-token Token (deref command))
(token-contents-snprintf hash-token "%d" command-name-hash)
(var fill-arguments-function-name Token (deref command))
(call-on append (field fill-arguments-function-name contents) "-fill-arguments")
(tokenize-push output
(scope
(var packed-arguments-buffer (array (* 8 1024) char) (array 0))
(if (call (token-splice-addr fill-arguments-function-name)
(token-splice-rest arguments tokens)
packed-arguments-buffer (sizeof packed-arguments-buffer))
(queue-remote-command-internal (token-splice output-queue)
(token-splice-addr hash-token)
packed-arguments-buffer)
(da-log "Failed to serialize arguments.\n"))))
(return true))
;; Returns -1 if failed to serialize. Otherwise, returns number of bytes written to buffer
(defun serialize-requested-remote-commands (queue (addr remote-command-queue)
write-buffer (addr char)
write-buffer-size (unsigned int)
&return int)
(var buffer-write-data write-introspect-struct-buffer-data
(array write-buffer write-buffer write-buffer-size))
(unless (write-introspect-struct-s-expr
remote-command-queue--metadata queue
write-introspect-struct-buffer-writer (addr buffer-write-data)
write-introspect-struct-default)
(return -1))
(return (- (field buffer-write-data write-head)
(field buffer-write-data buffer))))
(defun clear-receive-requested-remote-commands (queue (addr remote-command-queue)
read-buffer (addr (const char))
read-size (unsigned int)
&return bool)
;; We need to clear to not confuse the introspect read
(clear-remote-command-queue queue)
(unless read-size
(return true))
(var characters-read (unsigned int) 0)
(unless (read-introspect-struct-s-expr remote-command-queue--metadata
queue read-buffer
malloc (addr characters-read))
(da-log "Failed to de-serialize requested remote commands\n")
(return false))
(return true))
(defun execute-requested-remote-commands (queue (addr remote-command-queue)
userdata (addr void)
&return bool)
(var is-successful bool true)
(each-item-addr-in-dynarray (path queue > commands) i request (addr remote-command-request)
(var found bool false)
(each-in-array g-remote-command-definitions definition-index
(var current-definition (const (addr (const remote-command-definition)))
(addr (at definition-index g-remote-command-definitions)))
(when (!= (path request > command-name-hash) (path current-definition > name-hash))
(continue))
(set found true)
(when g-remote-commands-verbose
(da-log "Execute %s\n" (path current-definition > name)))
(unless (call (path current-definition > function) (path request > payload) userdata)
(set is-successful false))
(break))
(unless found
(da-log "Error: Did not find command with hash %d\n" (path request > command-name-hash)))
(unless is-successful
(da-log "Error: Remote command request could not be parsed. Queue execution aborted.\n")
(break)))
(return is-successful))
Loading…
Cancel
Save