Skip to content

Commit

Permalink
IPC: daemon: add batch-mode support
Browse files Browse the repository at this point in the history
Add support to read multiple commands from client and invoke them.
Example of possible (but not yet implemented) usage of smcroutectl:

smcroutectl --batch - << EOF
join  eth0 225.1.2.3
add   eth0 192.168.1.42 225.1.2.3 eth1 eth2
rem   eth1 225.3.4.5 eth3
leave eth1 225.3.4.5
EOF

Signed-off-by: Alexey Smirnov <s.alexey@gmail.com>
  • Loading branch information
alexeys85 committed Oct 3, 2022
1 parent 6a097da commit 3ffc919
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 40 deletions.
121 changes: 83 additions & 38 deletions src/ipc.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,26 +49,59 @@ static void ipc_read(int sd)
{
char buf[MX_CMDPKT_SZ];
struct ipc_msg *msg;
const char* buf_ptr;
ssize_t rx = 0, rx_curr;

memset(buf, 0, sizeof(buf));
msg = (struct ipc_msg *)ipc_receive(sd, buf, sizeof(buf));
if (!msg) {
/* Skip logging client disconnects */
if (errno != ECONNRESET)
smclog(LOG_WARNING, "Failed receiving IPC message from client: %s", strerror(errno));
return;
}
/* since client message would be big enough and couldn't fit into buffer
we have to make multiple iterations to receive all data */
while(1) {
rx_curr = ipc_receive(sd, buf + rx, sizeof(buf) - rx);
if(rx_curr <= 0) {
if (errno == EAGAIN)
return; /* no more data from client */
/* Skip logging client disconnects */
else if (errno != ECONNRESET)
smclog(LOG_WARNING, "Failed receiving IPC message from client: %s", strerror(errno));
return;
}

if (msg_do(sd, msg)) {
if (EINVAL == errno)
smclog(LOG_WARNING, "Unknown or malformed IPC message '%c' from client.", msg->cmd);
errno = 0;
ipc_send(sd, log_message, strlen(log_message) + 1);
} else {
ipc_send(sd, "", 1);
}
rx += rx_curr;

free(msg);
/* Make sure to always have at least one NUL, for strlen() */
buf[rx] = 0;

buf_ptr = buf;
while(rx > 0) {
/* extract one command at a time */
msg = (struct ipc_msg *)ipc_parse(buf_ptr, rx);
if (!msg) {
if (EAGAIN == errno) {
/* need more data from client?
move last unused bytes (if any) to the begging of the buffer
and lets try to receive more data */
memmove(buf, buf_ptr, rx);
break;
}
smclog(LOG_WARNING, "Failed to parse IPC message from client: %s", strerror(errno));
return;
}

if (msg_do(sd, msg)) {
if (EINVAL == errno)
smclog(LOG_WARNING, "Unknown or malformed IPC message '%c' from client.", msg->cmd);
errno = 0;
ipc_send(sd, log_message, strlen(log_message) + 1);
} else {
ipc_send(sd, "", 1);
}
/* shift to the next command if any and reduce remaining bytes in buffer */
buf_ptr += msg->len;
rx -= msg->len;

free(msg);
}
}
}

static void ipc_accept(int sd, void *arg)
Expand Down Expand Up @@ -149,7 +182,7 @@ void ipc_exit(void)
* Returns:
* Number of bytes successfully sent, or -1 with @errno on failure.
*/
int ipc_send(int sd, char *buf, size_t len)
int ipc_send(int sd, const char *buf, size_t len)
{
if (write(sd, buf, len) != (ssize_t)len)
return -1;
Expand All @@ -163,33 +196,46 @@ int ipc_send(int sd, char *buf, size_t len)
* @buf: Buffer for message
* @len: Size of @buf in bytes
*
* Reads a message from the IPC socket and stores in @buf, respecting
* Reads a message(s) from the IPC socket and stores in @buf, respecting
* the size @len. Connects and resets connection as necessary.
*
* Returns:
* Pointer to a successfuly read command packet in @buf, or %NULL on error.
* Size of a successfuly read command packet in @buf, or 0 on error.
*/
void *ipc_receive(int sd, char *buf, size_t len)
ssize_t ipc_receive(int sd, char *buf, size_t len)
{
ssize_t sz;
/* since we can call this multiple times during receive of multipart
command lets pass `don't wait` flag to not block forever
in case client suddenly finished transmission */
sz = recv(sd, buf, len - 1, MSG_DONTWAIT);
if (!sz)
errno = ECONNRESET;

return sz;
}

sz = recv(sd, buf, len - 1, 0);
if (sz <= 0) {
if (!sz)
errno = ECONNRESET;
return NULL;
}

/**
* ipc_server_parse - Parse IPC message(s) from client
* @buf: Buffer of message(s)
* @sz: Size of @buf in bytes
*
* Parse message(s) from the IPC socket, respecting
* the size @sz.
*
* Returns:
* Pointer to a successfuly read command in @buf, or NULL on error.
*/
void *ipc_parse(const char *buf, size_t sz)
{
/* successful read */
if ((size_t)sz >= sizeof(struct ipc_msg)) {
if (sz >= sizeof(struct ipc_msg)) {
struct ipc_msg *msg = (struct ipc_msg *)buf;

/* Make sure to always have at least one NUL, for strlen() */
buf[sz] = 0;

if ((size_t)sz == msg->len) {
/* enough bytes to extract just one message? */
if (sz >= msg->len) {
size_t i, count;
char *ptr;
/* We are not going to modify anything here */
const char *ptr;

/* Upper bound: smcroutectl add in1 source group out1 out2 .. out32 */
count = msg->count;
Expand All @@ -207,21 +253,20 @@ void *ipc_receive(int sd, char *buf, size_t len)
ptr = buf + offsetof(struct ipc_msg, argv);
for (i = 0; i < count; i++) {
/* Verify ptr, attacker may set too large msg->count */
if (ptr >= (buf + len)) {
if (ptr >= (buf + msg->len)) {
free(msg);
errno = EBADMSG;
return NULL;
}

msg->argv[i] = ptr;
msg->argv[i] = (char*)ptr;
ptr += strlen(ptr) + 1;
}
msg->count = count;

return msg;
}
}

/* we've parsed all commands or not enough bytes to parse next */
errno = EAGAIN;
return NULL;
}
Expand Down
5 changes: 3 additions & 2 deletions src/ipc.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
int ipc_init (char *path);
void ipc_exit (void);

int ipc_send (int sd, char *buf, size_t len);
void *ipc_receive (int sd, char *buf, size_t len);
int ipc_send (int sd, const char *buf, size_t len);
ssize_t ipc_receive(int sd, char *buf, size_t len);
void *ipc_parse (const char *buf, size_t sz);

#endif /* SMCROUTE_IPC_H_ */

Expand Down

0 comments on commit 3ffc919

Please sign in to comment.