lib/c: extend receive to handle incoming FDs

When using libcriu with the notify callback functionality CRIU transmits
an FD during 'orphan-pts-master' back to libcriu user. This is message
is sent via sendmsg() to transmit the FD and not via write() as all
other protobuf messages.

libcriu was using recv() and to be able to receive the FD this needs to
be changed to recvmsg() and if an FD is attached to it (currently only
for 'orphan-pts-master' this FD is stored in a variable which can be
retrieved with the function criu_get_orphan_pts_master_fd().

Signed-off-by: Adrian Reber <areber@redhat.com>
This commit is contained in:
Adrian Reber 2020-05-13 14:11:05 +00:00 committed by Andrei Vagin
parent 20a24c11ea
commit 83be11f1f4
2 changed files with 63 additions and 3 deletions

View file

@ -34,6 +34,7 @@ struct criu_opts {
static criu_opts *global_opts;
static int saved_errno;
static int orphan_pts_master_fd = -1;
void criu_free_service(criu_opts *opts)
{
@ -1145,29 +1146,72 @@ int criu_set_page_server_address_port(const char *address, int port)
static CriuResp *recv_resp(int socket_fd)
{
struct msghdr msg_hdr = {0};
unsigned char *buf = NULL;
int len;
struct cmsghdr *cmsg;
CriuResp *msg = 0;
struct iovec io;
int cmsg_len;
int len;
/* Check the size of the waiting data. */
len = recv(socket_fd, NULL, 0, MSG_TRUNC | MSG_PEEK);
if (len == -1) {
perror("Can't read request");
goto err;
}
buf = malloc(len);
/*
* If there is an FD attached to the protobuf message from CRIU
* the FD will be in the ancillary data. Let's reserve additional
* memory for that.
*/
cmsg_len = CMSG_LEN(sizeof(int));
buf = malloc(len + cmsg_len);
if (!buf) {
errno = ENOMEM;
perror("Can't receive response");
goto err;
}
len = recv(socket_fd, buf, len, MSG_TRUNC);
io.iov_base = buf;
io.iov_len = len;
msg_hdr.msg_iov = &io;
msg_hdr.msg_iovlen = 1;
msg_hdr.msg_control = buf + len;
msg_hdr.msg_controllen = cmsg_len;
len = recvmsg(socket_fd, &msg_hdr, MSG_TRUNC);
if (len == -1) {
perror("Can't read request");
goto err;
}
/*
* This will be NULL if no FD is in the message. Currently
* only a response with script set to 'orphan-pts-master'
* has an FD in the ancillary data.
*/
cmsg = CMSG_FIRSTHDR(&msg_hdr);
if (cmsg) {
/* We probably got an FD from CRIU. */
if (cmsg->cmsg_type != SCM_RIGHTS) {
errno = EINVAL;
goto err;
}
/* CTRUNC will be set if msg_hdr.msg_controllen is too small. */
if (msg_hdr.msg_flags & MSG_CTRUNC) {
errno = ENFILE;
goto err;
}
/*
* Not using 'orphan_pts_master_fd = *(int *)CMSG_DATA(cmsg)'
* as that fails with some compilers with:
* 'error: dereferencing type-punned pointer will break strict-aliasing rules'
*/
memcpy(&orphan_pts_master_fd, CMSG_DATA(cmsg), sizeof(int));
}
msg = criu_resp__unpack(NULL, len, buf);
if (!msg) {
perror("Failed unpacking response");
@ -1733,3 +1777,8 @@ int criu_check_version(int minimum)
{
return criu_local_check_version(global_opts, minimum);
}
int criu_get_orphan_pts_master_fd(void)
{
return orphan_pts_master_fd;
}

View file

@ -117,6 +117,17 @@ void criu_set_notify_cb(int (*cb)(char *action, criu_notify_arg_t na));
/* Get pid of root task. 0 if not available */
int criu_notify_pid(criu_notify_arg_t na);
/*
* If CRIU sends and FD in the case of 'orphan-pts-master',
* this FD can be retrieved with criu_get_orphan_pts_master_fd().
*
* If no FD has been received this will return -1.
*
* To make sure the FD returned is valid this function has to be
* called after the callback with the 'action' 'orphan-pts-master'.
*/
int criu_get_orphan_pts_master_fd(void);
/* Here is a table of return values and errno's of functions
* from the list down below.
*