Improve the command interface so that it sends back acks or errors.
This commit is contained in:
1 parent
4fee3e8e11
commit
65ac65a6d2
5 files changed
+144
-80
No files matched your search
@@ -118,12 +118,14 @@ OFP_ASSERT(sizeof(struct nx_action_header) == 16);
|
||||
|
||||
/* Status bits for NXT_COMMAND_REPLY. */
|
||||
enum {
|
||||
NXT_STATUS_EXITED = 0x8000, /* Exited normally. */
|
||||
NXT_STATUS_SIGNALED = 0x4000, /* Exited due to signal. */
|
||||
NXT_STATUS_UNKNOWN = 0x2000, /* Exited for unknown reason. */
|
||||
NXT_STATUS_COREDUMP = 0x1000, /* Exited with core dump. */
|
||||
NXT_STATUS_EXITSTATUS = 0xff, /* Exit code mask if NXT_STATUS_EXITED. */
|
||||
NXT_STATUS_TERMSIG = 0xff, /* Signal number if NXT_STATUS_SIGNALED. */
|
||||
NXT_STATUS_EXITED = 1 << 31, /* Exited normally. */
|
||||
NXT_STATUS_SIGNALED = 1 << 30, /* Exited due to signal. */
|
||||
NXT_STATUS_UNKNOWN = 1 << 29, /* Exited for unknown reason. */
|
||||
NXT_STATUS_COREDUMP = 1 << 28, /* Exited with core dump. */
|
||||
NXT_STATUS_ERROR = 1 << 27, /* Command could not be executed. */
|
||||
NXT_STATUS_STARTED = 1 << 26, /* Command was started. */
|
||||
NXT_STATUS_EXITSTATUS = 0xff, /* Exit code mask if NXT_STATUS_EXITED. */
|
||||
NXT_STATUS_TERMSIG = 0xff, /* Signal number if NXT_STATUS_SIGNALED. */
|
||||
};
|
||||
|
||||
/* NXT_COMMAND_REPLY. */
|
||||
|
||||
@@ -54,6 +54,7 @@ uint32_t vconn_get_ip(const struct vconn *);
|
||||
int vconn_connect(struct vconn *);
|
||||
int vconn_recv(struct vconn *, struct ofpbuf **);
|
||||
int vconn_send(struct vconn *, struct ofpbuf *);
|
||||
int vconn_recv_xid(struct vconn *, uint32_t xid, struct ofpbuf **);
|
||||
int vconn_transact(struct vconn *, struct ofpbuf *, struct ofpbuf **);
|
||||
|
||||
int vconn_open_block(const char *name, int min_version, struct vconn **);
|
||||
|
||||
+33
-22
@@ -552,6 +552,37 @@ vconn_recv_block(struct vconn *vconn, struct ofpbuf **msgp)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Waits until a message with a transaction ID matching 'xid' is recived on
|
||||
* 'vconn'. Returns 0 if successful, in which case the reply is stored in
|
||||
* '*replyp' for the caller to examine and free. Otherwise returns a positive
|
||||
* errno value, or EOF, and sets '*replyp' to null.
|
||||
*
|
||||
* 'request' is always destroyed, regardless of the return value. */
|
||||
int
|
||||
vconn_recv_xid(struct vconn *vconn, uint32_t xid, struct ofpbuf **replyp)
|
||||
{
|
||||
for (;;) {
|
||||
uint32_t recv_xid;
|
||||
struct ofpbuf *reply;
|
||||
int error;
|
||||
|
||||
error = vconn_recv_block(vconn, &reply);
|
||||
if (error) {
|
||||
*replyp = NULL;
|
||||
return error;
|
||||
}
|
||||
recv_xid = ((struct ofp_header *) reply->data)->xid;
|
||||
if (xid == recv_xid) {
|
||||
*replyp = reply;
|
||||
return 0;
|
||||
}
|
||||
|
||||
VLOG_DBG_RL(&rl, "%s: received reply with xid %08"PRIx32" != expected "
|
||||
"%08"PRIx32, vconn->name, recv_xid, xid);
|
||||
ofpbuf_delete(reply);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sends 'request' to 'vconn' and blocks until it receives a reply with a
|
||||
* matching transaction ID. Returns 0 if successful, in which case the reply
|
||||
* is stored in '*replyp' for the caller to examine and free. Otherwise
|
||||
@@ -567,28 +598,8 @@ vconn_transact(struct vconn *vconn, struct ofpbuf *request,
|
||||
|
||||
*replyp = NULL;
|
||||
error = vconn_send_block(vconn, request);
|
||||
if (error) {
|
||||
ofpbuf_delete(request);
|
||||
return error;
|
||||
}
|
||||
for (;;) {
|
||||
uint32_t recv_xid;
|
||||
struct ofpbuf *reply;
|
||||
|
||||
error = vconn_recv_block(vconn, &reply);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
recv_xid = ((struct ofp_header *) reply->data)->xid;
|
||||
if (send_xid == recv_xid) {
|
||||
*replyp = reply;
|
||||
return 0;
|
||||
}
|
||||
|
||||
VLOG_DBG_RL(&rl, "%s: received reply with xid %08"PRIx32" != expected "
|
||||
"%08"PRIx32, vconn->name, recv_xid, send_xid);
|
||||
ofpbuf_delete(reply);
|
||||
}
|
||||
ofpbuf_delete(request);
|
||||
return error ? error : vconn_recv_xid(vconn, send_xid, replyp);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+60
-24
@@ -89,6 +89,11 @@ struct executer {
|
||||
int null_fd; /* FD for /dev/null. */
|
||||
};
|
||||
|
||||
static void send_child_status(struct relay *, uint32_t xid, uint32_t status,
|
||||
const void *data, size_t size);
|
||||
static void send_child_message(struct relay *, uint32_t xid, uint32_t status,
|
||||
const char *message);
|
||||
|
||||
/* Returns true if 'cmd' is allowed by 'acl', which is a command-separated
|
||||
* access control list in the format described for --command-acl in
|
||||
* secchan(8). */
|
||||
@@ -164,6 +169,8 @@ executer_remote_packet_cb(struct relay *r, void *e_)
|
||||
/* Verify limit on children not exceeded.
|
||||
* XXX should probably kill children when the connection drops? */
|
||||
if (e->n_children >= MAX_CHILDREN) {
|
||||
send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
|
||||
"too many child processes");
|
||||
VLOG_WARN("limit of %d child processes reached, dropping request",
|
||||
MAX_CHILDREN);
|
||||
return false;
|
||||
@@ -190,16 +197,22 @@ executer_remote_packet_cb(struct relay *r, void *e_)
|
||||
|
||||
/* Check permissions. */
|
||||
if (!executer_is_permitted(e->s->command_acl, argv[0])) {
|
||||
send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
|
||||
"command not allowed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Find the executable. */
|
||||
exec_file = xasprintf("%s/%s", e->s->command_dir, argv[0]);
|
||||
if (stat(exec_file, &s)) {
|
||||
send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
|
||||
"command not allowed");
|
||||
VLOG_WARN("failed to stat \"%s\": %s", exec_file, strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
if (!S_ISREG(s.st_mode)) {
|
||||
send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
|
||||
"command not allowed");
|
||||
VLOG_WARN("\"%s\" is not a regular file", exec_file);
|
||||
goto done;
|
||||
}
|
||||
@@ -207,6 +220,8 @@ executer_remote_packet_cb(struct relay *r, void *e_)
|
||||
|
||||
/* Arrange to capture output. */
|
||||
if (pipe(output_fds)) {
|
||||
send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
|
||||
"internal error (pipe)");
|
||||
VLOG_WARN("pipe failed: %s", strerror(errno));
|
||||
goto done;
|
||||
}
|
||||
@@ -237,6 +252,7 @@ executer_remote_packet_cb(struct relay *r, void *e_)
|
||||
/* Running in parent. */
|
||||
struct child *child;
|
||||
|
||||
send_child_status(r, request->header.xid, NXT_STATUS_STARTED, NULL, 0);
|
||||
VLOG_WARN("started \"%s\" subprocess", argv[0]);
|
||||
child = &e->children[e->n_children++];
|
||||
child->name = xstrdup(argv[0]);
|
||||
@@ -249,6 +265,8 @@ executer_remote_packet_cb(struct relay *r, void *e_)
|
||||
set_nonblocking(output_fds[0]);
|
||||
close(output_fds[1]);
|
||||
} else {
|
||||
send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
|
||||
"internal error (fork)");
|
||||
VLOG_WARN("fork failed: %s", strerror(errno));
|
||||
close(output_fds[0]);
|
||||
close(output_fds[1]);
|
||||
@@ -261,12 +279,42 @@ done:
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
send_child_status(struct relay *relay, uint32_t xid, uint32_t status,
|
||||
const void *data, size_t size)
|
||||
{
|
||||
if (relay) {
|
||||
struct nx_command_reply *r;
|
||||
struct ofpbuf *buffer;
|
||||
|
||||
r = make_openflow_xid(sizeof *r, OFPT_VENDOR, xid, &buffer);
|
||||
r->nxh.vendor = htonl(NX_VENDOR_ID);
|
||||
r->nxh.subtype = htonl(NXT_COMMAND_REPLY);
|
||||
r->status = htonl(status);
|
||||
ofpbuf_put(buffer, data, size);
|
||||
update_openflow_length(buffer);
|
||||
if (rconn_send(relay->halves[HALF_REMOTE].rconn, buffer, NULL)) {
|
||||
ofpbuf_delete(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
send_child_message(struct relay *relay, uint32_t xid, uint32_t status,
|
||||
const char *message)
|
||||
{
|
||||
send_child_status(relay, xid, status, message, strlen(message));
|
||||
}
|
||||
|
||||
/* 'child' died with 'status' as its return code. Deal with it. */
|
||||
static void
|
||||
child_terminated(struct child *child, int status)
|
||||
{
|
||||
struct ds ds;
|
||||
uint32_t ofp_status;
|
||||
|
||||
/* Log how it terminated. */
|
||||
struct ds ds = DS_EMPTY_INITIALIZER;
|
||||
ds_init(&ds);
|
||||
if (WIFEXITED(status)) {
|
||||
ds_put_format(&ds, "normally with status %d", WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
@@ -285,30 +333,18 @@ child_terminated(struct child *child, int status)
|
||||
|
||||
/* Send a status message back to the controller that requested the
|
||||
* command. */
|
||||
if (child->relay) {
|
||||
struct nx_command_reply *r;
|
||||
struct ofpbuf *buffer;
|
||||
|
||||
r = make_openflow_xid(sizeof *r, OFPT_VENDOR, child->xid, &buffer);
|
||||
r->nxh.vendor = htonl(NX_VENDOR_ID);
|
||||
r->nxh.subtype = htonl(NXT_COMMAND_REPLY);
|
||||
if (WIFEXITED(status)) {
|
||||
r->status = htonl(WEXITSTATUS(status) | NXT_STATUS_EXITED);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
r->status = htonl(WTERMSIG(status) | NXT_STATUS_SIGNALED);
|
||||
} else {
|
||||
r->status = htonl(NXT_STATUS_UNKNOWN);
|
||||
}
|
||||
if (WCOREDUMP(status)) {
|
||||
r->status |= htonl(NXT_STATUS_COREDUMP);
|
||||
}
|
||||
ofpbuf_put(buffer, child->output, child->output_size);
|
||||
update_openflow_length(buffer);
|
||||
if (rconn_send(child->relay->halves[HALF_REMOTE].rconn, buffer,
|
||||
NULL)) {
|
||||
ofpbuf_delete(buffer);
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
ofp_status = WEXITSTATUS(status) | NXT_STATUS_EXITED;
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
ofp_status = WTERMSIG(status) | NXT_STATUS_SIGNALED;
|
||||
} else {
|
||||
ofp_status = NXT_STATUS_UNKNOWN;
|
||||
}
|
||||
if (WCOREDUMP(status)) {
|
||||
ofp_status |= NXT_STATUS_COREDUMP;
|
||||
}
|
||||
send_child_status(child->relay, child->xid, ofp_status,
|
||||
child->output, child->output_size);
|
||||
}
|
||||
|
||||
/* Read output from 'child' and append it to its output buffer. */
|
||||
|
||||
+42
-28
@@ -1290,13 +1290,14 @@ static void
|
||||
do_execute(const struct settings *s, int argc, char *argv[])
|
||||
{
|
||||
struct vconn *vconn;
|
||||
struct ofpbuf *request, *reply;
|
||||
struct ofpbuf *request;
|
||||
struct nicira_header *nicira;
|
||||
struct nx_command_reply *ncr;
|
||||
int status;
|
||||
uint32_t xid;
|
||||
int i;
|
||||
|
||||
nicira = make_openflow(sizeof *nicira, OFPT_VENDOR, &request);
|
||||
xid = nicira->header.xid;
|
||||
nicira->vendor = htonl(NX_VENDOR_ID);
|
||||
nicira->subtype = htonl(NXT_COMMAND_REQUEST);
|
||||
ofpbuf_put(request, argv[2], strlen(argv[2]));
|
||||
@@ -1307,34 +1308,47 @@ do_execute(const struct settings *s, int argc, char *argv[])
|
||||
update_openflow_length(request);
|
||||
|
||||
open_vconn(argv[1], &vconn);
|
||||
run(vconn_transact(vconn, request, &reply), "transact");
|
||||
if (reply->size < sizeof *ncr) {
|
||||
ofp_fatal(0, "reply is too short (%zu bytes < %zu bytes)",
|
||||
reply->size, sizeof *ncr);
|
||||
}
|
||||
ncr = reply->data;
|
||||
if (ncr->nxh.header.type != OFPT_VENDOR
|
||||
|| ncr->nxh.vendor != htonl(NX_VENDOR_ID)
|
||||
|| ncr->nxh.subtype != htonl(NXT_COMMAND_REPLY)) {
|
||||
ofp_fatal(0, "reply is invalid");
|
||||
}
|
||||
run(vconn_send_block(vconn, request), "send");
|
||||
|
||||
status = ntohl(ncr->status);
|
||||
if (status & NXT_STATUS_EXITED) {
|
||||
fprintf(stderr, "process terminated normally with exit code %d",
|
||||
status & NXT_STATUS_EXITSTATUS);
|
||||
} else if (status & NXT_STATUS_SIGNALED) {
|
||||
fprintf(stderr, "process terminated by signal %d",
|
||||
status & NXT_STATUS_TERMSIG);
|
||||
} else {
|
||||
fprintf(stderr, "process terminated for unknown reason");
|
||||
}
|
||||
if (status & NXT_STATUS_COREDUMP) {
|
||||
fprintf(stderr, " (core dumped)");
|
||||
}
|
||||
putc('\n', stderr);
|
||||
for (;;) {
|
||||
struct ofpbuf *reply;
|
||||
uint32_t status;
|
||||
|
||||
fwrite(ncr + 1, reply->size - sizeof *ncr, 1, stdout);
|
||||
run(vconn_recv_xid(vconn, xid, &reply), "recv_xid");
|
||||
if (reply->size < sizeof *ncr) {
|
||||
ofp_fatal(0, "reply is too short (%zu bytes < %zu bytes)",
|
||||
reply->size, sizeof *ncr);
|
||||
}
|
||||
ncr = reply->data;
|
||||
if (ncr->nxh.header.type != OFPT_VENDOR
|
||||
|| ncr->nxh.vendor != htonl(NX_VENDOR_ID)
|
||||
|| ncr->nxh.subtype != htonl(NXT_COMMAND_REPLY)) {
|
||||
ofp_fatal(0, "reply is invalid");
|
||||
}
|
||||
|
||||
status = ntohl(ncr->status);
|
||||
if (status & NXT_STATUS_STARTED) {
|
||||
/* Wait for a second reply. */
|
||||
continue;
|
||||
} else if (status & NXT_STATUS_EXITED) {
|
||||
fprintf(stderr, "process terminated normally with exit code %d",
|
||||
status & NXT_STATUS_EXITSTATUS);
|
||||
} else if (status & NXT_STATUS_SIGNALED) {
|
||||
fprintf(stderr, "process terminated by signal %d",
|
||||
status & NXT_STATUS_TERMSIG);
|
||||
} else if (status & NXT_STATUS_ERROR) {
|
||||
fprintf(stderr, "error executing command");
|
||||
} else {
|
||||
fprintf(stderr, "process terminated for unknown reason");
|
||||
}
|
||||
if (status & NXT_STATUS_COREDUMP) {
|
||||
fprintf(stderr, " (core dumped)");
|
||||
}
|
||||
putc('\n', stderr);
|
||||
|
||||
fwrite(ncr + 1, reply->size - sizeof *ncr, 1, stdout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void do_help(const struct settings *s, int argc UNUSED,
|
||||
|
||||
Reference in new issue
Block a user