remove unnecessary files and fix compilation of the half-completed 'done' action
#include <stdio.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <event2/event.h>
#include <event2/event_struct.h>
#include <event2/event_compat.h>
#include <event2/bufferevent.h>
#include "common.h"
#include "socket.h"
#include "render.h"
#include "render_struct.h"
#include "render_net.h"
#include "render_thread.h"
#include "render_thread_struct.h"
void sigpipe_ignore () {
struct sigaction sigpipe_action;
memset(&sigpipe_action, 0, sizeof(sigpipe_action));
sigpipe_action.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &sigpipe_action, NULL))
perr_exit("sigaction");
}
void log_null (int severity, const char *msg) {
// ignore
}
/*
* State needed to handle a client
*/
struct client_info {
// the client socket
evutil_socket_t socket;
// the read-a-command buffer
struct bufferevent *bufev;
// the write-a-mandelbrot stream
FILE *out_stream;
// the render_thread op
// thread_info.is_active is useful
struct render_thread thread_info;
};
static void client_free (struct client_info *ctx) {
// free the read-a-command buffer
if (ctx->bufev)
bufferevent_free(ctx->bufev);
// cancel the render thread if needed
if (ctx->thread_info.is_active)
render_thread_cancel(&ctx->thread_info);
// deinit it in any case
render_thread_deinit(&ctx->thread_info);
// close the write-a-mandelbrot stream, or just the socket
if (ctx->out_stream) {
if (fclose(ctx->out_stream))
PWARNING("fclose");
} else if (ctx->socket != -1) {
if (close(ctx->socket))
PWARNING("close");
}
// free the client info
free(ctx);
}
static void handle_render_done (struct render_thread *thread_info, int err, void *arg) {
struct client_info *ctx = arg;
#if INFO_ENABLED
INFO("client [%p]: %s", ctx, err ? "failed" : "done");
#endif
// just free it, it takes care of closing it as well
client_free(ctx);
}
static int handle_render_cmd (struct client_info *ctx, struct render_cmd *cmd) {
// the render ctx...
struct render render_info;
// open it as a normal FILE*
if (!(ctx->out_stream = fdopen(ctx->socket, "w")))
ERROR("fdopen");
#if INFO_ENABLED
INFO("client [%p]: render [%ux%u] (%f, %f) -> (%f, %f)", ctx, cmd->img_w, cmd->img_h, cmd->x1, cmd->y1, cmd->x2, cmd->y2);
#endif
// set up the render_info
if (
render_init(&render_info)
|| render_set_mode(&render_info, cmd->mode)
|| render_set_size(&render_info, cmd->img_w, cmd->img_h)
|| render_region_raw(&render_info, cmd->x1, cmd->y1, cmd->x2, cmd->y2)
|| render_io_stream(&render_info, ctx->out_stream)
)
ERROR("render_*");
// start the render thread
if (render_thread_init(&ctx->thread_info, &render_info, &handle_render_done, ctx))
ERROR("render_thread_init");
// ok, wait for it to complete
return 0;
error:
// FAAAIL
return -1;
}
static void handle_read (struct bufferevent *bev, void *arg) {
struct client_info *ctx = arg;
struct render_cmd cmd;
// meh, just read it in
size_t len;
// we set a watermark, so this should hold true
assert(len = bufferevent_read(bev, &cmd, sizeof(cmd)) == sizeof(cmd));
// fix the byte order
cmd.img_w = ntohl(cmd.img_w);
cmd.img_h = ntohl(cmd.img_h);
// handle it
if (handle_render_cmd(ctx, &cmd))
goto error;
// ok
return;
error:
client_free(ctx);
}
static void handle_error (struct bufferevent *bev, short what, void *arg) {
struct client_info *ctx = arg;
// read-EOF
if ((what & (EVBUFFER_READ | EVBUFFER_EOF)) && ctx->thread_info.is_active) {
// this is fine, expected, and doesn't matter
return;
}
PWARNING("client [%p]: eventbuffer error: %s %s", ctx,
(what & EVBUFFER_READ) ? "read" : ((what & EVBUFFER_WRITE) ? "write" : "???"),
(what & EVBUFFER_EOF) ? "eof" : ((what & EVBUFFER_ERROR) ? "error" : ((what & EVBUFFER_TIMEOUT) ? "timeout" : "???"))
);
client_free(ctx);
}
static void handle_accept (evutil_socket_t fd, short event, void *arg) {
struct client_info *ctx = NULL;
evutil_socket_t socket = -1;
struct sockaddr_storage addr;
socklen_t addr_len;
// arg is NULL and unused
(void) arg;
// accept the connection
addr_len = sizeof(struct sockaddr_storage);
if ((socket = accept(fd, (struct sockaddr *) &addr, &addr_len)) == -1)
PERROR("accept");
// alloc a new client_info
if (!(ctx = calloc(1, sizeof(*ctx))))
ERROR("calloc");
// store the socket
ctx->socket = socket;
#if INFO_ENABLED
assert(INET_ADDRSTRLEN < INET6_ADDRSTRLEN);
char addr_buf[INET6_ADDRSTRLEN];
const char *addr_str;
short nport;
if (addr.ss_family == AF_UNIX)
addr_str = "local";
else if (addr.ss_family == AF_INET || addr.ss_family == AF_INET6) {
const void *src;
if (addr.ss_family == AF_INET) {
src = &(((struct sockaddr_in *) &addr)->sin_addr);
nport = ((struct sockaddr_in *) &addr)->sin_port;
} else {
src = &(((struct sockaddr_in6 *) &addr)->sin6_addr);
nport = ((struct sockaddr_in6 *) &addr)->sin6_port;
}
if (!(inet_ntop(addr.ss_family, src, addr_buf, sizeof(addr_buf))))
PERROR("inet_ntop");
addr_str = addr_buf;
}
INFO("client [%p]: accept from %s:%hu", ctx, addr_str, ntohs(nport));
#endif
// then a bufferevent so that we can read in the command
if (!(ctx->bufev = bufferevent_new(ctx->socket, &handle_read, NULL, &handle_error, ctx)))
ERROR("bufferevent_new");
// and enable it for read only
if (bufferevent_enable(ctx->bufev, EV_READ))
ERROR("bufferevent_enable");
// set the watermark for receiving the render_cmd
bufferevent_setwatermark(ctx->bufev, EV_READ, sizeof(struct render_cmd), 0);
// now we just wait for the cmd...
return;
error:
if (ctx)
client_free(ctx);
else if (socket != -1)
close(socket);
}
int main (int argc, char** argv) {
struct event_base *ev_base;
struct config_endpoint endpoint;
int ssock;
// parse arguments
int opt;
const char *listen_spec = NULL;
int enable_debug = 0;
while ((opt = getopt(argc, argv, "l:")) != -1) {
switch (opt) {
case 'l':
if (listen_spec)
ERROR("only specify -l once");
listen_spec = optarg;
break;
case 'd':
// enable libevent debugging
enable_debug = 1;
break;
default:
err_exit("Usage: %s [-l addr_spec] [-d]", argv[0]);
}
}
// init libevent
if (!(ev_base = event_init()))
FATAL("event_init");
// per default it is enabled
if (!enable_debug)
event_set_log_callback(&log_null);
// create the socket
endpoint_init(&endpoint, RENDER_PORT);
if (endpoint_parse(&endpoint, listen_spec))
goto error;
if ((ssock = socket_listen(&endpoint, SOCK_STREAM)) == -1)
goto error;
// create the listen event
struct event listen_ev;
event_set(&listen_ev, ssock, EV_READ | EV_PERSIST, &handle_accept, NULL);
if (event_add(&listen_ev, NULL))
PERROR("event_add");
// ignore sigpipe
sigpipe_ignore();
// run the libevent mainloop
INFO("run");
if (event_base_dispatch(ev_base))
WARNING("event_dispatch");
INFO("SHUTDOWN");
event_base_free(ev_base);
// succesful exit
return EXIT_SUCCESS;
error:
// failure
return EXIT_FAILURE;
}