mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-07-18 00:58:31 +00:00
test/rpc: add page server address test
Verify that the page server binds to the address given in the RPC request when lazy_pages is enabled. Without the fix in the previous commit, the address was silently ignored and the page server would bind to 0.0.0.0 instead. The test sends a PAGE_SERVER request with ps.address set to 127.0.0.1 and lazy_pages enabled, then checks /proc/net/tcp to confirm the page server actually bound to that address. Acked-by: Andrei Vagin <avagin@gmail.com> Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
This commit is contained in:
parent
f513ac16a9
commit
ab28d44661
3 changed files with 280 additions and 2 deletions
|
|
@ -1,4 +1,4 @@
|
|||
all: test-c rpc_pb2.py criu
|
||||
all: test-c test-ps-addr rpc_pb2.py criu
|
||||
.PHONY: all
|
||||
|
||||
CFLAGS += -g -Werror -Wall -I.
|
||||
|
|
@ -44,6 +44,10 @@ test-c: rpc.pb-c.o test-c.o
|
|||
|
||||
test-c.o: test-c.c rpc.pb-c.c
|
||||
|
||||
test-ps-addr: rpc.pb-c.o test-ps-addr.o
|
||||
|
||||
test-ps-addr.o: test-ps-addr.c rpc.pb-c.c
|
||||
|
||||
rpc_pb2.py: rpc.proto
|
||||
protoc --proto_path=. --python_out=. rpc.proto
|
||||
|
||||
|
|
@ -51,5 +55,5 @@ rpc.pb-c.c: rpc.proto
|
|||
protoc --proto_path=. --c_out=. rpc.proto
|
||||
|
||||
clean:
|
||||
rm -rf build rpc.pb-c.o test-c.o test-c rpc.pb-c.c rpc.pb-c.h rpc_pb2.py rpc_pb2.pyc criu
|
||||
rm -rf build rpc.pb-c.o test-c.o test-c test-ps-addr.o test-ps-addr rpc.pb-c.c rpc.pb-c.h rpc_pb2.py rpc_pb2.pyc criu
|
||||
.PHONY: clean
|
||||
|
|
|
|||
|
|
@ -73,6 +73,13 @@ function test_ps {
|
|||
setsid ./ps_test.py build/criu_service.socket build/imgs_ps < /dev/null &>> build/output_ps
|
||||
}
|
||||
|
||||
function test_ps_addr {
|
||||
title_print "Run page server address test"
|
||||
# Reuse the dump images from test_c — the page server in
|
||||
# lazy-pages mode needs pstree.img and inventory.img.
|
||||
setsid ./test-ps-addr build/criu_service.socket build/imgs_c < /dev/null &>> build/output_ps_addr
|
||||
}
|
||||
|
||||
function test_errno {
|
||||
mkdir -p build/imgs_errno
|
||||
|
||||
|
|
@ -86,6 +93,7 @@ test_c
|
|||
test_py
|
||||
test_restore_loop
|
||||
test_ps
|
||||
test_ps_addr
|
||||
test_errno
|
||||
|
||||
FAIL=0
|
||||
|
|
|
|||
266
test/others/rpc/test-ps-addr.c
Normal file
266
test/others/rpc/test-ps-addr.c
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
/*
|
||||
* Test that the page server binds to the address from an RPC request.
|
||||
*
|
||||
* When lazy_pages is enabled, setup_opts_from_req() must copy the
|
||||
* address string from req->ps->address into opts.addr. Previously,
|
||||
* the copy was inside the !opts.lazy_pages branch and was skipped,
|
||||
* so the page server would bind to 0.0.0.0 instead of the requested
|
||||
* address.
|
||||
*
|
||||
* This test sends a PAGE_SERVER request with lazy_pages enabled and
|
||||
* ps.address set to 127.0.0.1, then checks /proc/net/tcp to confirm
|
||||
* the page server actually bound to that address.
|
||||
*
|
||||
* The images directory must contain pstree.img and inventory.img
|
||||
* because the page server reads them on startup.
|
||||
*/
|
||||
#include "rpc.pb-c.h"
|
||||
#include <arpa/inet.h>
|
||||
#include <signal.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MAX_MSG_SIZE 1024
|
||||
|
||||
static CriuResp *recv_resp(int socket_fd)
|
||||
{
|
||||
unsigned char buf[MAX_MSG_SIZE];
|
||||
int len;
|
||||
CriuResp *msg = 0;
|
||||
|
||||
len = read(socket_fd, buf, MAX_MSG_SIZE);
|
||||
if (len == -1) {
|
||||
perror("Can't read response");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
msg = criu_resp__unpack(NULL, len, buf);
|
||||
if (!msg) {
|
||||
perror("Failed unpacking response");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static int send_req(int socket_fd, CriuReq *req)
|
||||
{
|
||||
unsigned char buf[MAX_MSG_SIZE];
|
||||
int len;
|
||||
|
||||
len = criu_req__get_packed_size(req);
|
||||
|
||||
if (len > MAX_MSG_SIZE) {
|
||||
fprintf(stderr, "Request too large: %d > %d\n",
|
||||
len, MAX_MSG_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (criu_req__pack(req, buf) != len) {
|
||||
perror("Failed packing request");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write(socket_fd, buf, len) == -1) {
|
||||
perror("Can't send request");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int connect_to_service(const char *path)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
socklen_t addr_len;
|
||||
int fd;
|
||||
|
||||
fd = socket(AF_LOCAL, SOCK_SEQPACKET, 0);
|
||||
if (fd == -1) {
|
||||
perror("Can't create socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_LOCAL;
|
||||
|
||||
if (strlen(path) >= sizeof(addr.sun_path)) {
|
||||
fprintf(stderr, "Socket path too long: %s\n", path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
|
||||
addr_len = offsetof(struct sockaddr_un, sun_path) + strlen(path) + 1;
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&addr, addr_len) == -1) {
|
||||
perror("Can't connect to socket");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that a listening socket on @port is bound to @expect_addr
|
||||
* by parsing /proc/net/tcp.
|
||||
*
|
||||
* Each line looks like:
|
||||
* sl local_address rem_address st ...
|
||||
* 0: 0100007F:001B 00000000:0000 0A ...
|
||||
*
|
||||
* The local address is hex in network byte order. State 0A = LISTEN.
|
||||
*/
|
||||
static int check_listen_addr(int port, const char *expect_addr)
|
||||
{
|
||||
FILE *f;
|
||||
char line[512];
|
||||
unsigned int local_addr, local_port, state;
|
||||
struct in_addr expected, found;
|
||||
|
||||
if (inet_pton(AF_INET, expect_addr, &expected) != 1) {
|
||||
fprintf(stderr, "Bad expected address: %s\n", expect_addr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
f = fopen("/proc/net/tcp", "r");
|
||||
if (!f) {
|
||||
perror("Can't open /proc/net/tcp");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Skip header */
|
||||
if (!fgets(line, sizeof(line), f)) {
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), f)) {
|
||||
if (sscanf(line, " %*d: %X:%X %*X:%*X %X",
|
||||
&local_addr, &local_port, &state) != 3)
|
||||
continue;
|
||||
|
||||
if (local_port != (unsigned int)port || state != 0x0A)
|
||||
continue;
|
||||
|
||||
found.s_addr = local_addr;
|
||||
if (found.s_addr == expected.s_addr) {
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Page server bound to %s:%d, expected %s:%d\n",
|
||||
inet_ntoa(found), port, expect_addr, port);
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "No listening socket found on port %d\n", port);
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
CriuReq req = CRIU_REQ__INIT;
|
||||
CriuPageServerInfo ps = CRIU_PAGE_SERVER_INFO__INIT;
|
||||
CriuResp *resp = NULL;
|
||||
int fd = -1, dir_fd = -1, ret = 0;
|
||||
int ps_pid = 0;
|
||||
|
||||
if (argc != 3) {
|
||||
fprintf(stderr, "Usage: test-ps-addr socket imgs_dir\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dir_fd = open(argv[2], O_DIRECTORY);
|
||||
if (dir_fd == -1) {
|
||||
perror("Can't open imgs dir");
|
||||
return -1;
|
||||
}
|
||||
|
||||
req.type = CRIU_REQ_TYPE__PAGE_SERVER_CHLD;
|
||||
req.opts = malloc(sizeof(CriuOpts));
|
||||
if (!req.opts) {
|
||||
perror("Can't allocate memory");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
criu_opts__init(req.opts);
|
||||
req.opts->images_dir_fd = dir_fd;
|
||||
req.opts->has_log_level = true;
|
||||
req.opts->log_level = 4;
|
||||
req.opts->log_file = "page-server-addr-test.log";
|
||||
req.opts->has_lazy_pages = true;
|
||||
req.opts->lazy_pages = true;
|
||||
|
||||
ps.address = "127.0.0.1";
|
||||
ps.has_port = true;
|
||||
ps.port = 0;
|
||||
req.opts->ps = &ps;
|
||||
|
||||
fd = connect_to_service(argv[1]);
|
||||
if (fd == -1) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = send_req(fd, &req);
|
||||
if (ret == -1) {
|
||||
perror("Can't send request");
|
||||
goto out;
|
||||
}
|
||||
|
||||
resp = recv_resp(fd);
|
||||
if (!resp) {
|
||||
perror("Can't recv response");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (resp->type != CRIU_REQ_TYPE__PAGE_SERVER) {
|
||||
fprintf(stderr, "Unexpected response type %d\n", resp->type);
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!resp->success) {
|
||||
fprintf(stderr, "Page server request failed\n");
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ps_pid = resp->ps->pid;
|
||||
printf("Page server started on port %d (pid %d)\n",
|
||||
resp->ps->port, ps_pid);
|
||||
|
||||
if (check_listen_addr(resp->ps->port, "127.0.0.1")) {
|
||||
fprintf(stderr, "FAIL: page server did not bind to 127.0.0.1\n");
|
||||
ret = -1;
|
||||
} else {
|
||||
puts("PASS: page server bound to 127.0.0.1");
|
||||
}
|
||||
|
||||
out:
|
||||
if (ps_pid > 0) {
|
||||
kill(ps_pid, SIGKILL);
|
||||
waitpid(ps_pid, NULL, 0);
|
||||
}
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
if (dir_fd >= 0)
|
||||
close(dir_fd);
|
||||
if (resp)
|
||||
criu_resp__free_unpacked(resp, NULL);
|
||||
return ret;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue