/* * comm.c - Fakefile IPC functions * * Copyright 2012 by Werner Almesberger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include #include #include "util.h" #include "comm.h" #define BUF_SIZE 4096 struct fakefile_peer { int in, out; uint8_t buf[BUF_SIZE]; void *pos; const void *end; }; struct fakefile_msg { struct fakefile_peer *peer; void *buf; /* NULL if reading */ void *pos; /* current position in buffer; write only */ size_t len; /* amount of data read/written so far */ size_t msg_size; /* declared size */ size_t buf_size; /* allocated size; write only */ }; static int master_fd(const char *name) { const char *s; char *end; long n; s = getenv(name); if (!s) { fprintf(stderr, "%s not set\n", name); exit(1); } n = strtol(s, &end, 0); if (*end || n < 0) { fprintf(stderr, "invalid fd \"%s\" for %s\n", s, name); exit(1); } return n; } static struct fakefile_peer *sanitize_peer(struct fakefile_peer *peer) { static int have_peer = 0; static struct fakefile_peer master; if (peer) return peer; if (!have_peer) { master.in = master_fd(FAKEFILE_MASTER_OUT_VAR); master.out = master_fd(FAKEFILE_MASTER_IN_VAR); master.pos = master.buf; master.end = master.buf; have_peer = 1; } return &master; } struct fakefile_peer *fakefile_peer(int in, int out) { struct fakefile_peer *peer; peer = alloc_type(struct fakefile_peer); peer->in = in; peer->out = out; peer->pos = peer->buf; peer->end = peer->buf; return peer; } void fakefile_peer_close(struct fakefile_peer *peer) { (void) close(peer->in); (void) close(peer->out); free(peer); } int fakefile_peer_fd(const struct fakefile_peer *peer) { return peer->in; } struct fakefile_msg *fakefile_msg_new(struct fakefile_peer *peer, int size) { struct fakefile_msg *msg; size_t buf_size; size += sizeof(int); buf_size = size > BUF_SIZE ? BUF_SIZE : size; msg = alloc_size(sizeof(struct fakefile_msg)+buf_size); msg->peer = sanitize_peer(peer); msg->buf = msg->pos = msg+1; msg->len = 0; msg->msg_size = size; msg->buf_size = buf_size; fakefile_msg_add_int(msg, size); return msg; } static void flush_out(struct fakefile_msg *msg) { const void *p; ssize_t len; if (msg->pos == msg->buf) return; for (p = msg->buf; p != msg->pos; p += len) { len = write(msg->peer->out, p, msg->pos-p); if (len <= 0) { perror("fakefile IPC write"); exit(1); } } } struct fakefile_msg *fakefile_msg_recv(struct fakefile_peer *peer) { struct fakefile_msg *msg; msg = alloc_type(struct fakefile_msg); msg->peer = sanitize_peer(peer); msg->buf = NULL; msg->len = 0; msg->msg_size = sizeof(int); msg->msg_size = fakefile_msg_get_int(msg); return msg; } void fakefile_msg_end(struct fakefile_msg *msg) { #if 0 fprintf(stderr, "end: len %d size %d (%p)\n", (int) msg->len, (int) msg->msg_size, msg->buf); #endif assert(msg->len == msg->msg_size); if (msg->buf) flush_out(msg); free(msg); } static void add_chunk(struct fakefile_msg *msg, const void *buf, size_t len) { memcpy(msg->pos, buf, len); msg->pos += len; if (msg->pos == msg->buf+msg->buf_size) { flush_out(msg); msg->pos = msg->buf; } } void fakefile_msg_add(struct fakefile_msg *msg, const void *buf, size_t len) { size_t chunk; #if 0 fprintf(stderr, "add: %d+%d/%d (%p)\n", (int) msg->len, (int) len, (int) msg->msg_size, msg->buf); #endif assert(msg->len+len <= msg->msg_size); while (len && msg->pos+len > msg->buf+msg->buf_size) { chunk = msg->buf+msg->buf_size-msg->pos; if (chunk > len) chunk = len; add_chunk(msg, buf, chunk); buf += chunk; len -= chunk; msg->len += chunk; } if (len) { add_chunk(msg, buf, len); msg->len += len; } } static size_t get_chunk(struct fakefile_peer *peer, void *buf, size_t len) { ssize_t got; got = read(peer->in, buf, len); if (got < 0) { perror("fakefile IPC read"); exit(1); } if (!got) { fprintf(stderr, "fakefile EOF\n"); exit(1); } return got; } void fakefile_msg_get(struct fakefile_msg *msg, void *buf, size_t len) { struct fakefile_peer *peer = msg->peer; size_t chunk, got; assert(msg->len+len <= msg->msg_size); if (!len) return; if (peer->pos != peer->end) { chunk = peer->end-peer->pos; if (chunk > len) chunk = len; memcpy(buf, peer->pos, chunk); peer->pos += chunk; buf += chunk; len -= chunk; msg->len += chunk; } while (len >= BUF_SIZE) { got = get_chunk(peer, buf, BUF_SIZE); buf += got; len -= got; msg->len += got; } if (!len) return; peer->pos = peer->buf; peer->end = peer->buf+get_chunk(peer, peer->buf, BUF_SIZE); fakefile_msg_get(msg, buf, len); }