sipcap/netmap/examples/testmmap.c

1084 lines
20 KiB
C
Raw Normal View History

#define TEST_NETMAP
#include <inttypes.h>
#include <sys/param.h> /* ULONG_MAX */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/poll.h>
#include <sys/wait.h>
#include <sys/mman.h> /* PROT_* */
#include <fcntl.h> /* O_RDWR */
#include <pthread.h>
#include <signal.h>
#define MAX_VARS 100
char *variables[MAX_VARS];
int curr_var;
#define VAR_FAILED ((void*)1)
char *firstarg(char *buf)
{
int v;
char *arg = strtok(buf, " \t\n");
char *ret;
if (!arg)
return NULL;
if (arg[0] != '$' && arg[0] != '?')
return arg;
v = atoi(arg+1);
if (v < 0 || v >= MAX_VARS)
return "";
ret = variables[v];
if (ret == NULL)
return "NULL";
if (ret == VAR_FAILED) {
printf("reading failed var, exit\n");
exit(1);
}
if (arg[0] == '?')
return ret;
ret = rindex(ret, '=') + 1;
return ret;
}
char *nextarg()
{
return firstarg(NULL);
}
char *restofline()
{
return strtok(NULL, "\n");
}
void resetvar(int v, char *b)
{
if (variables[v] != VAR_FAILED)
free(variables[v]);
variables[v] = b;
}
#define outecho(format, args...) \
do {\
printf("%u:%lu: " format "\n", getpid(), (unsigned long) pthread_self(), ##args);\
fflush(stdout);\
} while (0)
#define output(format, args...) \
do {\
resetvar(curr_var, (char*)malloc(1024));\
snprintf(variables[curr_var], 1024, format, ##args);\
outecho(format, ##args);\
} while (0)
#define output_err(ret, format, args...)\
do {\
if (ret < 0) {\
resetvar(curr_var, VAR_FAILED);\
outecho(format, ##args);\
outecho("error: %s", strerror(errno));\
} else {\
output(format, ##args);\
}\
} while (0)
struct chan {
FILE *out;
pid_t pid;
pthread_t tid;
};
int chan_search_free(struct chan* c[], int max)
{
int i;
for (i = 0; i < max && c[i]; i++)
;
return i;
}
void chan_clear_all(struct chan *c[], int max)
{
int i;
for (i = 0; i < max; i++) {
if (c[i]) {
fclose(c[i]->out);
free(c[i]);
c[i] = NULL;
}
}
}
int last_fd = -1;
size_t last_memsize = 0;
void* last_mmap_addr = NULL;
char* last_access_addr = NULL;
struct nmreq curr_nmr;
char nmr_name[64];
void do_open()
{
last_fd = open("/dev/netmap", O_RDWR);
output_err(last_fd, "open(\"/dev/netmap\", O_RDWR)=%d", last_fd);
}
void do_close()
{
int ret, fd;
char *arg = nextarg();
fd = arg ? atoi(arg) : last_fd;
ret = close(fd);
output_err(ret, "close(%d)=%d", fd, ret);
}
#ifdef TEST_NETMAP
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <net/netmap_user.h>
void parse_nmr_config(char* w, struct nmreq *nmr)
{
char *tok;
int i, v;
nmr->nr_tx_rings = nmr->nr_rx_rings = 0;
nmr->nr_tx_slots = nmr->nr_rx_slots = 0;
if (w == NULL || ! *w)
return;
for (i = 0, tok = strtok(w, ","); tok; i++, tok = strtok(NULL, ",")) {
v = atoi(tok);
switch (i) {
case 0:
nmr->nr_tx_slots = nmr->nr_rx_slots = v;
break;
case 1:
nmr->nr_rx_slots = v;
break;
case 2:
nmr->nr_tx_rings = nmr->nr_rx_rings = v;
break;
case 3:
nmr->nr_rx_rings = v;
break;
default:
break;
}
}
}
void do_getinfo()
{
int ret;
char *arg, *name;
int fd;
bzero(&curr_nmr, sizeof(curr_nmr));
curr_nmr.nr_version = NETMAP_API;
name = nextarg();
if (name) {
strncpy(curr_nmr.nr_name, name, sizeof(curr_nmr.nr_name));
} else {
name = "any";
}
arg = nextarg();
if (!arg) {
fd = last_fd;
goto doit;
}
fd = atoi(arg);
arg = nextarg();
parse_nmr_config(arg, &curr_nmr);
doit:
ret = ioctl(fd, NIOCGINFO, &curr_nmr);
last_memsize = curr_nmr.nr_memsize;
output_err(ret, "ioctl(%d, NIOCGINFO) for %s: region %d memsize=%zu",
fd, name, curr_nmr.nr_arg2, last_memsize);
}
void do_regif()
{
int ret;
char *arg, *name;
int fd = last_fd;
name = nextarg();
if (!name) {
name = nmr_name;
goto doit;
}
bzero(&curr_nmr, sizeof(curr_nmr));
curr_nmr.nr_version = NETMAP_API;
strncpy(curr_nmr.nr_name, name, sizeof(curr_nmr.nr_name));
arg = nextarg();
if (!arg) {
goto doit;
}
fd = atoi(arg);
arg = nextarg();
parse_nmr_config(arg, &curr_nmr);
doit:
ret = ioctl(fd, NIOCREGIF, &curr_nmr);
last_memsize = curr_nmr.nr_memsize;
output_err(ret, "ioctl(%d, NIOCREGIF) for %s: region %d memsize=%zu",
fd, name, curr_nmr.nr_arg2, last_memsize);
}
void
do_txsync()
{
char *arg = nextarg();
int fd = arg ? atoi(arg) : last_fd;
int ret = ioctl(fd, NIOCTXSYNC, NULL);
output_err(ret, "ioctl(%d, NIOCTXSYNC)=%d", fd, ret);
}
void
do_rxsync()
{
char *arg = nextarg();
int fd = arg ? atoi(arg) : last_fd;
int ret = ioctl(fd, NIOCRXSYNC, NULL);
output_err(ret, "ioctl(%d, NIOCRXSYNC)=%d", fd, ret);
}
#endif /* TEST_NETMAP */
volatile char tmp1;
void do_access()
{
char *arg = nextarg();
char *p;
if (!arg) {
if (!last_access_addr) {
output("missing address");
return;
}
p = last_access_addr;
} else {
p = (char *)strtoul((void *)arg, NULL, 0);
}
last_access_addr = p + 4096;
tmp1 = *p;
}
void do_mmap()
{
size_t memsize;
off_t off = 0;
int fd;
char *arg;
arg = nextarg();
if (!arg) {
memsize = last_memsize;
fd = last_fd;
goto doit;
}
memsize = atoi(arg);
arg = nextarg();
if (!arg) {
fd = last_fd;
goto doit;
}
fd = atoi(arg);
arg = nextarg();
if (arg) {
off = (off_t)atol(arg);
}
doit:
last_mmap_addr = mmap(0, memsize,
PROT_WRITE | PROT_READ,
MAP_SHARED, fd, off);
if (last_access_addr == NULL)
last_access_addr = last_mmap_addr;
output_err(last_mmap_addr == MAP_FAILED ? -1 : 0,
"mmap(0, %zu, PROT_WRITE|PROT_READ, MAP_SHARED, %d, %jd)=%p",
memsize, fd, (intmax_t)off, last_mmap_addr);
}
void do_munmap()
{
void *mmap_addr;
size_t memsize;
char *arg;
int ret;
arg = nextarg();
if (!arg) {
mmap_addr = last_mmap_addr;
memsize = last_memsize;
goto doit;
}
mmap_addr = (void*)strtoul(arg, NULL, 0);
arg = nextarg();
if (!arg) {
memsize = last_memsize;
goto doit;
}
memsize = (size_t)strtoul(arg, NULL, 0);
doit:
ret = munmap(mmap_addr, memsize);
output_err(ret, "munmap(%p, %zu)=%d", mmap_addr, memsize, ret);
}
void do_poll()
{
/* timeout fd fd... */
nfds_t nfds = 0, allocated_fds = 10, i;
struct pollfd *fds;
int timeout = 500; /* 1/2 second */
char *arg;
int ret;
arg = nextarg();
if (arg)
timeout = atoi(arg);
fds = malloc(allocated_fds * sizeof(struct pollfd));
if (fds == NULL) {
output_err(-1, "out of memory");
return;
}
while ( (arg = nextarg()) ) {
if (nfds >= allocated_fds) {
allocated_fds *= 2;
fds = realloc(fds, allocated_fds * sizeof(struct pollfd));
if (fds == NULL) {
output_err(-1, "out of memory");
return;
}
}
fds[nfds].fd = atoi(arg);
fds[nfds].events = POLLIN;
nfds++;
}
ret = poll(fds, nfds, timeout);
for (i = 0; i < nfds; i++) {
output("poll(%d)=%s%s%s%s%s", fds[i].fd,
(fds[i].revents & POLLIN) ? "IN " : "- ",
(fds[i].revents & POLLOUT)? "OUT " : "- ",
(fds[i].revents & POLLERR)? "ERR " : "- ",
(fds[i].revents & POLLHUP)? "HUP " : "- ",
(fds[i].revents & POLLNVAL)?"NVAL" : "-");
}
output_err(ret, "poll(...)=%d", ret);
free(fds);
}
void
do_expr()
{
unsigned long stack[11];
int top = 10;
char *arg;
int err = 0;
stack[10] = ULONG_MAX;
while ( (arg = nextarg()) ) {
errno = 0;
char *rest;
unsigned long n = strtoul(arg, &rest, 0);
if (!errno && rest != arg) {
if (top <= 0) {
err = -1;
break;
}
stack[--top] = n;
continue;
}
if (top <= 8) {
unsigned long n1 = stack[top++];
unsigned long n2 = stack[top++];
unsigned long r = 0;
switch (arg[0]) {
case '+':
r = n1 + n2;
break;
case '-':
r = n1 - n2;
break;
case '*':
r = n1 * n2;
break;
case '/':
if (n2)
r = n1 / n2;
else {
errno = EDOM;
err = -1;
}
break;
default:
err = -1;
break;
}
stack[--top] = r;
continue;
}
err = -1;
break;
}
output_err(err, "expr=%lu", stack[top]);
}
void
do_echo()
{
char *arg;
for (arg = nextarg(); arg; arg = nextarg()) {
printf("%s\n", arg);
}
}
void
do_vars()
{
int i;
for (i = 0; i < MAX_VARS; i++) {
const char *v = variables[i];
if (v == NULL)
continue;
printf("?%d\t%s\n", i, v == VAR_FAILED ? "FAILED" : v);
}
}
struct cmd_def {
const char *name;
void (*f)(void);
};
int _find_command(const struct cmd_def *cmds, int ncmds, const char* cmd)
{
int i;
for (i = 0; i < ncmds; i++) {
if (strcmp(cmds[i].name, cmd) == 0)
break;
}
return i;
}
typedef void (*nmr_arg_interp_fun)();
#define nmr_arg_unexpected(n) \
printf("arg%d: %d%s\n", n, curr_nmr.nr_arg ## n, \
(curr_nmr.nr_arg ## n ? "???" : ""))
void
nmr_arg_bdg_attach()
{
uint16_t v = curr_nmr.nr_arg1;
printf("arg1: %d [", v);
if (v == 0) {
printf("no host rings");
} else if (v == NETMAP_BDG_HOST) {
printf("BDG_HOST");
} else {
printf("???");
}
printf("]\n");
nmr_arg_unexpected(2);
nmr_arg_unexpected(3);
}
void
nmr_arg_bdg_detach()
{
nmr_arg_unexpected(1);
nmr_arg_unexpected(2);
nmr_arg_unexpected(3);
}
void
nmr_arg_bdg_list()
{
}
void
nmr_arg_lookup_reg()
{
}
void
nmr_arg_vnet_hdr()
{
printf("arg1: %d [vnet hdr len]", curr_nmr.nr_arg1);
nmr_arg_unexpected(2);
nmr_arg_unexpected(3);
}
void
nmr_arg_error()
{
nmr_arg_unexpected(1);
nmr_arg_unexpected(2);
nmr_arg_unexpected(3);
}
void
nmr_arg_extra()
{
printf("arg1: %d [%sextra rings]\n", curr_nmr.nr_arg1,
(curr_nmr.nr_arg1 ? "" : "no "));
printf("arg2: %d [%s memory allocator]\n", curr_nmr.nr_arg2,
(curr_nmr.nr_arg2 == 0 ? "global" : "private"));
printf("arg3: %d [%sextra buffers]\n", curr_nmr.nr_arg3,
(curr_nmr.nr_arg3 ? "" : "no "));
}
void
do_nmr_dump()
{
u_int ringid = curr_nmr.nr_ringid & NETMAP_RING_MASK;
nmr_arg_interp_fun arg_interp;
snprintf(nmr_name, IFNAMSIZ + 1, "%s", curr_nmr.nr_name);
nmr_name[IFNAMSIZ] = '\0';
printf("name: %s\n", nmr_name);
printf("version: %d\n", curr_nmr.nr_version);
printf("offset: %d\n", curr_nmr.nr_offset);
printf("memsize: %d [", curr_nmr.nr_memsize);
if (curr_nmr.nr_memsize < (1<<20)) {
printf("%d KiB", curr_nmr.nr_memsize >> 10);
} else {
printf("%d MiB", curr_nmr.nr_memsize >> 20);
}
printf("]\n");
printf("tx_slots: %d\n", curr_nmr.nr_tx_slots);
printf("rx_slots: %d\n", curr_nmr.nr_rx_slots);
printf("tx_rings: %d\n", curr_nmr.nr_tx_rings);
printf("rx_rings: %d\n", curr_nmr.nr_rx_rings);
printf("ringid: %x [", curr_nmr.nr_ringid);
if (curr_nmr.nr_ringid & NETMAP_SW_RING) {
printf("host rings");
} else if (curr_nmr.nr_ringid & NETMAP_HW_RING) {
printf("hw ring %d", ringid);
} else {
printf("hw rings");
}
if (curr_nmr.nr_ringid & NETMAP_NO_TX_POLL) {
printf(", no tx poll");
}
printf(", region %d", curr_nmr.nr_arg2);
printf("]\n");
printf("cmd: %d", curr_nmr.nr_cmd);
if (curr_nmr.nr_cmd) {
printf("[");
switch (curr_nmr.nr_cmd) {
case NETMAP_BDG_ATTACH:
printf("BDG_ATTACH");
arg_interp = nmr_arg_bdg_attach;
break;
case NETMAP_BDG_DETACH:
printf("BDG_DETACH");
arg_interp = nmr_arg_bdg_detach;
break;
case NETMAP_BDG_LIST:
printf("BDG_LIST");
arg_interp = nmr_arg_bdg_list;
break;
case NETMAP_BDG_LOOKUP_REG:
printf("BDG_LOOKUP_REG");
arg_interp = nmr_arg_lookup_reg;
break;
case NETMAP_BDG_VNET_HDR:
printf("BDG_VNET_HDR");
arg_interp = nmr_arg_vnet_hdr;
break;
default:
printf("???");
arg_interp = nmr_arg_error;
break;
}
printf("]\n");
} else {
arg_interp = nmr_arg_extra;
}
printf("\n");
arg_interp();
printf("flags: %x [", curr_nmr.nr_flags);
switch (curr_nmr.nr_flags & NR_REG_MASK) {
case NR_REG_DEFAULT:
printf("obey ringid");
break;
case NR_REG_ALL_NIC:
printf("ALL_NIC");
break;
case NR_REG_SW:
printf("SW");
break;
case NR_REG_NIC_SW:
printf("NIC_SW");
break;
case NR_REG_ONE_NIC:
printf("ONE_NIC(%d)", ringid);
break;
case NR_REG_PIPE_MASTER:
printf("PIPE_MASTER(%d)", ringid);
break;
case NR_REG_PIPE_SLAVE:
printf("PIPE_SLAVE(%d)", ringid);
break;
default:
printf("???");
break;
}
if (curr_nmr.nr_flags & NR_MONITOR_TX) {
printf(", MONITOR_TX");
}
if (curr_nmr.nr_flags & NR_MONITOR_RX) {
printf(", MONITOR_RX");
}
printf("]\n");
printf("spare2[0]: %x\n", curr_nmr.spare2[0]);
}
void
do_nmr_reset()
{
bzero(&curr_nmr, sizeof(curr_nmr));
}
void
do_nmr_name()
{
char *name = nextarg();
if (name) {
strncpy(curr_nmr.nr_name, name, IFNAMSIZ);
}
strncpy(nmr_name, curr_nmr.nr_name, IFNAMSIZ);
nmr_name[IFNAMSIZ] = '\0';
output("name=%s", nmr_name);
}
void
do_nmr_ringid()
{
char *arg;
uint16_t ringid = curr_nmr.nr_ringid;
int n;
for (n = 0, arg = nextarg(); arg; arg = nextarg(), n++) {
if (strcmp(arg, "hw-ring") == 0) {
ringid |= NETMAP_HW_RING;
} else if (strcmp(arg, "sw-ring") == 0) {
ringid |= NETMAP_SW_RING;
} else if (strcmp(arg, "no-tx-poll") == 0) {
ringid |= NETMAP_NO_TX_POLL;
} else if (strcmp(arg, "default") == 0) {
ringid = 0;
} else {
ringid &= ~NETMAP_RING_MASK;
ringid |= (atoi(arg) & NETMAP_RING_MASK);
}
}
if (n)
curr_nmr.nr_ringid = ringid;
output("ringid=%x", curr_nmr.nr_ringid);
}
void
do_nmr_cmd()
{
}
void
do_nmr_flags()
{
char *arg;
uint32_t flags = curr_nmr.nr_flags;
int n;
for (n = 0, arg = nextarg(); arg; arg = nextarg(), n++) {
if (strcmp(arg, "all-nic") == 0) {
flags &= ~NR_REG_MASK;
flags |= NR_REG_ALL_NIC;
} else if (strcmp(arg, "sw") == 0) {
flags &= ~NR_REG_MASK;
flags |= NR_REG_SW;
} else if (strcmp(arg, "nic-sw") == 0) {
flags &= ~NR_REG_MASK;
flags |= NR_REG_NIC_SW;
} else if (strcmp(arg, "pipe-master") == 0) {
flags &= ~NR_REG_MASK;
flags |= NR_REG_PIPE_MASTER;
} else if (strcmp(arg, "pipe-slave") == 0) {
flags &= ~NR_REG_MASK;
flags |= NR_REG_PIPE_SLAVE;
} else if (strcmp(arg, "monitor-tx") == 0) {
flags |= NR_MONITOR_TX;
} else if (strcmp(arg, "monitor-rx") == 0) {
flags |= NR_MONITOR_RX;
} else if (strcmp(arg, "default") == 0) {
flags = 0;
}
}
if (n)
curr_nmr.nr_flags = flags;
output("flags=%x", curr_nmr.nr_flags);
}
struct cmd_def nmr_commands[] = {
{ "dump", do_nmr_dump },
{ "reset", do_nmr_reset },
{ "name", do_nmr_name },
{ "ringid", do_nmr_ringid },
{ "cmd", do_nmr_cmd },
{ "flags", do_nmr_flags },
};
const int N_NMR_CMDS = sizeof(nmr_commands) / sizeof(struct cmd_def);
int
find_nmr_command(const char *cmd)
{
return _find_command(nmr_commands, N_NMR_CMDS, cmd);
}
#define nmr_arg_update(f) \
({ \
int __ret = 0; \
if (strcmp(cmd, #f) == 0) { \
char *arg = nextarg(); \
if (arg) { \
curr_nmr.nr_##f = strtol(arg, NULL, 0); \
} \
output(#f "=%d", curr_nmr.nr_##f); \
__ret = 1; \
} \
__ret; \
})
/* prepare the curr_nmr */
void
do_nmr()
{
char *cmd = nextarg();
int i;
if (cmd == NULL) {
do_nmr_dump();
return;
}
if (cmd[0] == '.') {
cmd++;
} else {
i = find_nmr_command(cmd);
if (i < N_NMR_CMDS) {
nmr_commands[i].f();
return;
}
}
if (nmr_arg_update(version) ||
nmr_arg_update(offset) ||
nmr_arg_update(memsize) ||
nmr_arg_update(tx_slots) ||
nmr_arg_update(rx_slots) ||
nmr_arg_update(tx_rings) ||
nmr_arg_update(rx_rings) ||
nmr_arg_update(ringid) ||
nmr_arg_update(cmd) ||
nmr_arg_update(arg1) ||
nmr_arg_update(arg2) ||
nmr_arg_update(arg3) ||
nmr_arg_update(flags))
return;
output("unknown field: %s", cmd);
}
struct cmd_def commands[] = {
{ "open", do_open, },
{ "close", do_close, },
#ifdef TEST_NETMAP
{ "getinfo", do_getinfo, },
{ "regif", do_regif, },
{ "txsync", do_txsync, },
{ "rxsync", do_rxsync, },
#endif /* TEST_NETMAP */
{ "mmap", do_mmap, },
{ "access", do_access, },
{ "munmap", do_munmap, },
{ "poll", do_poll, },
{ "expr", do_expr, },
{ "echo", do_echo, },
{ "vars", do_vars, },
{ "nmr", do_nmr, }
};
const int N_CMDS = sizeof(commands) / sizeof(struct cmd_def);
int find_command(const char* cmd)
{
return _find_command(commands, N_CMDS, cmd);
}
#define MAX_CHAN 10
void prompt()
{
if (isatty(STDIN_FILENO)) {
printf("> ");
}
}
struct chan *channels[MAX_CHAN];
void*
thread_cmd_loop(void *arg)
{
char buf[1024];
FILE *in = (FILE*)arg;
while (fgets(buf, 1024, in)) {
char *cmd;
int i;
cmd = firstarg(buf);
i = find_command(cmd);
if (i < N_CMDS) {
commands[i].f();
continue;
}
output("unknown cmd %s", cmd);
}
fclose(in);
return NULL;
}
void do_exit()
{
output("quit");
}
void
cmd_loop()
{
char buf[1024];
int i;
struct chan *c;
bzero(channels, sizeof(*channels) * MAX_CHAN);
atexit(do_exit);
for (prompt(); fgets(buf, 1024, stdin); prompt()) {
char *cmd;
int slot;
cmd = firstarg(buf);
if (!cmd)
continue;
if (cmd[0] == '@') {
curr_var = atoi(cmd + 1);
if (curr_var < 0 || curr_var >= MAX_VARS)
curr_var = 0;
cmd = nextarg();
if (!cmd)
continue;
} else {
curr_var = 0;
}
if (strcmp(cmd, "fork") == 0) {
int slot = chan_search_free(channels, MAX_CHAN);
struct chan *c = NULL;
pid_t pid;
int p1[2] = { -1, -1};
if (slot == MAX_CHAN) {
output("too many channels");
continue;
}
c = channels[slot] = (struct chan*)malloc(sizeof(struct chan));
if (c == NULL) {
output_err(-1, "malloc");
continue;
}
bzero(c, sizeof(*c));
if (pipe(p1) < 0) {
output_err(-1, "pipe");
goto clean1;
}
c->out = fdopen(p1[1], "w");
if (c->out == NULL) {
output_err(-1, "fdopen");
goto clean1;
}
pid = fork();
switch (pid) {
case -1:
output_err(-1, "fork");
goto clean1;
case 0:
fclose(stdin);
if (dup(p1[0]) < 0) {
output_err(-1, "dup");
exit(1);
}
close(p1[1]);
stdin = fdopen(0, "r");
chan_clear_all(channels, MAX_CHAN);
goto out;
default:
break;
}
c->pid = pid;
close(p1[0]);
output("fork()=%d slot=%d", pid, slot);
continue;
clean1:
if (c) {
fclose(c->out);
}
close(p1[0]);
close(p1[1]);
free(c);
out:
continue;
}
if (strcmp(cmd, "kill") == 0) {
int ret;
cmd = nextarg();
if (!cmd) {
output("missing slot");
continue;
}
slot = atoi(cmd);
if (slot < 0 || slot >= MAX_CHAN || !channels[slot]) {
output("invalid slot: %s", cmd);
continue;
}
c = channels[slot];
ret = kill(c->pid, SIGTERM);
output_err(ret, "kill(%d, SIGTERM)=%d", c->pid, ret);
if (ret != -1) {
wait(NULL);
fclose(c->out);
free(c);
channels[slot] = NULL;
}
continue;
}
if (strcmp(cmd, "thread") == 0) {
int slot = chan_search_free(channels, MAX_CHAN);
struct chan *c = NULL;
pthread_t tid;
int p1[2] = { -1, -1};
int ret;
FILE *in = NULL;
if (slot == MAX_CHAN) {
output("too many channels");
continue;
}
c = channels[slot] = (struct chan*)malloc(sizeof(struct chan));
bzero(c, sizeof(*c));
if (pipe(p1) < 0) {
output_err(-1, "pipe");
goto clean2;
}
c->out = fdopen(p1[1], "w");
if (c->out == NULL) {
output_err(-1, "fdopen");
goto clean2;
}
in = fdopen(p1[0], "r");
if (in == NULL) {
output_err(-1, "fdopen");
goto clean2;
}
ret = pthread_create(&tid, NULL, thread_cmd_loop, in);
output_err(ret, "pthread_create() tid=%lu slot=%d",
(unsigned long) tid, slot);
if (ret < 0)
goto clean2;
c->pid = getpid();
c->tid = tid;
continue;
clean2:
fclose(in);
fclose(c->out);
close(p1[0]);
close(p1[1]);
free(c);
continue;
}
if (strcmp(cmd, "cancel") == 0) {
int ret;
cmd = nextarg();
if (!cmd) {
output("missing slot");
continue;
}
slot = atoi(cmd);
if (slot < 0 || slot >= MAX_CHAN || !channels[slot]) {
output("invalid slot: %s", cmd);
continue;
}
c = channels[slot];
fclose(c->out);
ret = pthread_join(c->tid, NULL);
output_err(ret, "pthread_join(%lu)=%d",
(unsigned long) c->tid, ret);
if (ret > 0) {
free(c);
channels[slot] = NULL;
}
continue;
}
i = find_command(cmd);
if (i < N_CMDS) {
commands[i].f();
continue;
}
slot = atoi(cmd);
if (slot < 0 || slot > MAX_CHAN || !channels[slot]) {
output("invalid cmd/slot: %s", cmd);
continue;
}
cmd = restofline();
if (!cmd) {
output("missing command");
continue;
}
fprintf(channels[slot]->out, "%s\n", cmd);
fflush(channels[slot]->out);
sleep(1);
}
}
int
main(int argc, char **argv)
{
(void) argc;
(void) argv;
cmd_loop();
return 0;
}