Packages
nerves_runtime
0.6.1
0.13.13
0.13.12
0.13.11
retired
0.13.10
0.13.9
0.13.8
0.13.7
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.0
0.11.10
0.11.9
0.11.8
0.11.7
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.3
0.10.2
0.10.1
0.10.0
retired
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.0
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.3
0.5.2
0.5.1
0.5.0
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.1
0.3.0
0.2.0
0.1.2
0.1.1
0.1.0
Small, general runtime utilities for Nerves devices
Current section
Files
Jump to
Current section
Files
src/erlcmd.c
/*
* Copyright 2014 Frank Hunleth
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Common Erlang->C port communications code
*/
#include "erlcmd.h"
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/**
* Initialize an Erlang command handler.
*
* @param handler the structure to initialize
* @param request_handler callback for each message received
* @param cookie optional data to pass back to the handler
*/
void erlcmd_init(struct erlcmd *handler,
void (*request_handler)(const char *req, void *cookie),
void *cookie)
{
memset(handler, 0, sizeof(*handler));
handler->request_handler = request_handler;
handler->cookie = cookie;
}
/**
* @brief Synchronously send a response back to Erlang
*
* @param response what to send back
*/
void erlcmd_send(char *response, size_t len)
{
uint16_t be_len = htons(len - sizeof(uint16_t));
memcpy(response, &be_len, sizeof(be_len));
size_t wrote = 0;
do {
ssize_t amount_written = write(STDOUT_FILENO, response + wrote, len - wrote);
if (amount_written < 0) {
if (errno == EINTR)
continue;
err(EXIT_FAILURE, "write");
}
wrote += amount_written;
} while (wrote < len);
}
/**
* @brief Dispatch commands in the buffer
* @return the number of bytes processed
*/
static size_t erlcmd_try_dispatch(struct erlcmd *handler)
{
/* Check for length field */
if (handler->index < sizeof(uint16_t))
return 0;
uint16_t be_len;
memcpy(&be_len, handler->buffer, sizeof(uint16_t));
size_t msglen = ntohs(be_len);
if (msglen + sizeof(uint16_t) > sizeof(handler->buffer))
errx(EXIT_FAILURE, "Message too long");
/* Check whether we've received the entire message */
if (msglen + sizeof(uint16_t) > handler->index)
return 0;
handler->request_handler(handler->buffer, handler->cookie);
return msglen + sizeof(uint16_t);
}
/**
* @brief call to process any new requests from Erlang
*/
void erlcmd_process(struct erlcmd *handler)
{
ssize_t amount_read = read(STDIN_FILENO, handler->buffer + handler->index,
sizeof(handler->buffer) - handler->index);
if (amount_read < 0) {
/* EINTR is ok to get, since we were interrupted by a signal. */
if (errno == EINTR)
return;
/* Everything else is unexpected. */
err(EXIT_FAILURE, "read");
} else if (amount_read == 0) {
/* EOF. Erlang process was terminated. This happens after a release or if there was an error. */
exit(EXIT_SUCCESS);
}
handler->index += amount_read;
for (;;) {
size_t bytes_processed = erlcmd_try_dispatch(handler);
if (bytes_processed == 0) {
/* Only have part of the command to process. */
break;
} else if (handler->index > bytes_processed) {
/* Processed the command and there's more data. */
memmove(handler->buffer, &handler->buffer[bytes_processed], handler->index - bytes_processed);
handler->index -= bytes_processed;
} else {
/* Processed the whole buffer. */
handler->index = 0;
break;
}
}
}