/*
* Copyright (C) 2015 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* .
*/
#include
#include
#include "testutils.h"
#include "virerror.h"
#include "rpc/virnetdaemon.h"
#define VIR_FROM_THIS VIR_FROM_RPC
#if !defined(WIN32) && defined(WITH_JSON)
struct testClientPriv {
int magic;
};
static void *
testClientNew(virNetServerClient *client G_GNUC_UNUSED,
void *opaque G_GNUC_UNUSED)
{
struct testClientPriv *priv;
priv = g_new0(struct testClientPriv, 1);
priv->magic = 1729;
return priv;
}
static virJSONValue *
testClientPreExec(virNetServerClient *client G_GNUC_UNUSED,
void *data)
{
struct testClientPriv *priv = data;
return virJSONValueNewNumberInt(priv->magic);
}
static void *
testClientNewPostExec(virNetServerClient *client,
virJSONValue *object,
void *opaque)
{
int magic;
if (virJSONValueGetNumberInt(object, &magic) < 0)
return NULL;
if (magic != 1729)
return NULL;
return testClientNew(client, opaque);
}
static void
testClientFree(void *opaque)
{
g_free(opaque);
}
static virNetServer *
testCreateServer(const char *server_name, const char *host, int family)
{
virNetServer *srv = NULL;
virNetServerService *svc1 = NULL;
virNetServerService *svc2 = NULL;
virNetServerClient *cln1 = NULL;
virNetServerClient *cln2 = NULL;
virNetSocket *sk1 = NULL;
virNetSocket *sk2 = NULL;
int fdclient[2];
if (socketpair(PF_UNIX, SOCK_STREAM, 0, fdclient) < 0) {
virReportSystemError(errno, "%s",
"Cannot create socket pair");
goto cleanup;
}
if (!(srv = virNetServerNew(server_name, 1,
10, 50, 5, 100, 10,
120, 5,
testClientNew,
testClientPreExec,
testClientFree,
NULL)))
goto error;
if (!(svc1 = virNetServerServiceNewTCP(host,
NULL,
family,
VIR_NET_SERVER_SERVICE_AUTH_NONE,
NULL,
true,
5,
2)))
goto error;
if (!(svc2 = virNetServerServiceNewTCP(host,
NULL,
family,
VIR_NET_SERVER_SERVICE_AUTH_POLKIT,
NULL,
false,
25,
5)))
goto error;
virNetServerAddService(srv, svc1);
virNetServerAddService(srv, svc2);
if (virNetSocketNewConnectSockFD(fdclient[0], &sk1) < 0)
goto error;
if (virNetSocketNewConnectSockFD(fdclient[1], &sk2) < 0)
goto error;
if (!(cln1 = virNetServerClientNew(virNetServerNextClientID(srv),
sk1,
VIR_NET_SERVER_SERVICE_AUTH_SASL,
true,
15,
NULL,
testClientNew,
testClientPreExec,
testClientFree,
NULL)))
goto error;
if (!(cln2 = virNetServerClientNew(virNetServerNextClientID(srv),
sk2,
VIR_NET_SERVER_SERVICE_AUTH_POLKIT,
true,
66,
NULL,
testClientNew,
testClientPreExec,
testClientFree,
NULL)))
goto error;
if (virNetServerAddClient(srv, cln1) < 0)
goto error;
if (virNetServerAddClient(srv, cln2) < 0)
goto error;
cleanup:
if (!srv)
virDispatchError(NULL);
virObjectUnref(cln1);
virObjectUnref(cln2);
virObjectUnref(svc1);
virObjectUnref(svc2);
virObjectUnref(sk1);
virObjectUnref(sk2);
return srv;
error:
g_clear_pointer(&srv, virObjectUnref);
goto cleanup;
}
static char *testGenerateJSON(const char *server_name)
{
virNetDaemon *dmn = NULL;
virNetServer *srv = NULL;
g_autoptr(virJSONValue) json = NULL;
char *jsonstr = NULL;
bool has_ipv4, has_ipv6;
/* Our pre-saved JSON file is created so that each service
* only has one socket. If we let libvirt bind to IPv4 and
* IPv6 we might end up with two sockets, so force one or
* the other based on what's available on thehost
*/
if (virNetSocketCheckProtocols(&has_ipv4,
&has_ipv6) < 0)
return NULL;
if (!has_ipv4 && !has_ipv6)
return NULL;
if (!(srv = testCreateServer(server_name,
has_ipv4 ? "127.0.0.1" : "::1",
has_ipv4 ? AF_INET : AF_INET6)))
goto cleanup;
if (!(dmn = virNetDaemonNew()))
goto cleanup;
if (virNetDaemonAddServer(dmn, srv) < 0)
goto cleanup;
if (!(json = virNetDaemonPreExecRestart(dmn)))
goto cleanup;
if (!(jsonstr = virJSONValueToString(json, true)))
goto cleanup;
fprintf(stderr, "%s\n", jsonstr);
cleanup:
virNetServerClose(srv);
virObjectUnref(srv);
virObjectUnref(dmn);
if (!jsonstr)
virDispatchError(NULL);
return jsonstr;
}
struct testExecRestartData {
const char *jsonfile;
const char **serverNames;
int nservers;
bool pass;
};
static virNetServer *
testNewServerPostExecRestart(virNetDaemon *dmn G_GNUC_UNUSED,
const char *name,
virJSONValue *object,
void *opaque)
{
struct testExecRestartData *data = opaque;
size_t i;
for (i = 0; i < data->nservers; i++) {
if (STREQ(data->serverNames[i], name)) {
return virNetServerNewPostExecRestart(object,
name,
testClientNew,
testClientNewPostExec,
testClientPreExec,
testClientFree,
NULL);
}
}
virReportError(VIR_ERR_INTERNAL_ERROR, "Unexpected server name '%s'", name);
return NULL;
}
static int testExecRestart(const void *opaque)
{
size_t i;
int ret = -1;
virNetDaemon *dmn = NULL;
const struct testExecRestartData *data = opaque;
g_autofree char *infile = NULL;
g_autofree char *outfile = NULL;
g_autofree char *injsonstr = NULL;
g_autofree char *outjsonstr = NULL;
g_autoptr(virJSONValue) injson = NULL;
g_autoptr(virJSONValue) outjson = NULL;
int fdclient[2] = { -1, -1 }, fdserver[2] = { -1, -1 };
if (socketpair(PF_UNIX, SOCK_STREAM, 0, fdclient) < 0) {
virReportSystemError(errno, "%s",
"Cannot create socket pair");
goto cleanup;
}
if (socketpair(PF_UNIX, SOCK_STREAM, 0, fdserver) < 0) {
virReportSystemError(errno, "%s",
"Cannot create socket pair");
goto cleanup;
}
/* We're blindly assuming the test case isn't using
* fds 100->103 for something else, which is probably
* fairly reasonable in general
*/
if (dup2(fdserver[0], 100) < 0 ||
dup2(fdserver[1], 101) < 0 ||
dup2(fdclient[0], 102) < 0 ||
dup2(fdclient[1], 103) < 0) {
virReportSystemError(errno, "%s", "dup2() failed");
goto cleanup;
}
infile = g_strdup_printf("%s/virnetdaemondata/input-data-%s.json", abs_srcdir,
data->jsonfile);
outfile = g_strdup_printf("%s/virnetdaemondata/output-data-%s.%s",
abs_srcdir, data->jsonfile, data->pass ? "json" : "err");
if (virFileReadAll(infile, 8192, &injsonstr) < 0)
goto cleanup;
if (!(injson = virJSONValueFromString(injsonstr)))
goto cleanup;
if (!(dmn = virNetDaemonNewPostExecRestart(injson,
data->nservers,
data->serverNames,
testNewServerPostExecRestart,
(void *)data)))
goto cleanup;
for (i = 0; i < data->nservers; i++) {
if (!virNetDaemonHasServer(dmn, data->serverNames[i])) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"Server %s was not created",
data->serverNames[i]);
goto cleanup;
}
}
if (!(outjson = virNetDaemonPreExecRestart(dmn)))
goto cleanup;
if (!data->pass)
goto cleanup;
if (!(outjsonstr = virJSONValueToString(outjson, true)))
goto cleanup;
if (virTestCompareToFile(outjsonstr, outfile) < 0)
goto cleanup;
ret = 0;
cleanup:
if (ret < 0) {
if (injson && !data->pass) {
ret = virTestCompareToFile(virGetLastErrorMessage(), outfile);
if (ret < 0)
VIR_TEST_DEBUG("Test failed with different error message");
virResetLastError();
}
} else if (!data->pass) {
VIR_TEST_DEBUG("Test should have failed");
ret = -1;
}
virObjectUnref(dmn);
VIR_FORCE_CLOSE(fdserver[0]);
VIR_FORCE_CLOSE(fdserver[1]);
VIR_FORCE_CLOSE(fdclient[0]);
VIR_FORCE_CLOSE(fdclient[1]);
return ret;
}
static int
mymain(void)
{
int ret = 0;
const char *server_names[] = { "testServer0", "testServer1" };
if (virInitialize() < 0 ||
virEventRegisterDefaultImpl() < 0) {
virDispatchError(NULL);
return EXIT_FAILURE;
}
/* Hack to make it easier to generate new JSON files when
* the RPC classes change. Just set this env var, save
* the generated JSON, and replace the file descriptor
* numbers with 100, 101, 102, 103.
*/
if (getenv("VIR_GENERATE_JSON")) {
char *json = testGenerateJSON(server_names[0]);
if (!json)
return EXIT_FAILURE;
fprintf(stdout, "%s\n", json);
VIR_FREE(json);
return ret;
}
# define EXEC_RESTART_TEST_FULL(file, nservers, pass) \
do { \
struct testExecRestartData data = { \
file, server_names, nservers, pass \
}; \
if (virTestRun("ExecRestart " file, \
testExecRestart, &data) < 0) \
ret = -1; \
} while (0)
# define EXEC_RESTART_TEST(file, N) EXEC_RESTART_TEST_FULL(file, N, true)
# define EXEC_RESTART_TEST_FAIL(file, N) EXEC_RESTART_TEST_FULL(file, N, false)
EXEC_RESTART_TEST("initial", 1);
EXEC_RESTART_TEST("anon-clients", 1);
EXEC_RESTART_TEST("admin", 2);
EXEC_RESTART_TEST("admin-server-names", 2);
EXEC_RESTART_TEST("no-keepalive-required", 2);
EXEC_RESTART_TEST("client-ids", 1);
EXEC_RESTART_TEST("client-timestamp", 1);
EXEC_RESTART_TEST_FAIL("anon-clients", 2);
EXEC_RESTART_TEST("client-auth-pending", 1);
EXEC_RESTART_TEST_FAIL("client-auth-pending-failure", 1);
EXEC_RESTART_TEST_FAIL("invalid-max-clients-failure", 1);
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
VIR_TEST_MAIN_PRELOAD(mymain, VIR_TEST_MOCK("virnetdaemon"))
#else
static int
mymain(void)
{
return EXIT_AM_SKIP;
}
VIR_TEST_MAIN(mymain);
#endif