2 * virt-agent - host/guest RPC daemon functions
4 * Copyright IBM Corp. 2010
7 * Adam Litke <aglitke@linux.vnet.ibm.com>
8 * Michael Roth <mdroth@linux.vnet.ibm.com>
10 * This work is licensed under the terms of the GNU GPL, version 2 or later.
11 * See the COPYING file in the top-level directory.
15 #include "qemu_socket.h"
16 #include "virtagent-daemon.h"
17 #include "virtagent-common.h"
18 #include "virtagent.h"
20 static bool va_enable_syslog
= false; /* enable syslog'ing of RPCs */
22 #define SLOG(msg, ...) do { \
24 if (!va_enable_syslog) { \
27 sprintf(msg_buf, msg, ## __VA_ARGS__); \
28 syslog(LOG_INFO, "virtagent, %s", msg_buf); \
31 /* RPC functions common to guest/host daemons */
33 static xmlrpc_value
*getfile(xmlrpc_env
*env
,
38 char *file_contents
= NULL
;
39 char buf
[VA_FILEBUF_LEN
];
40 int fd
, ret
, count
= 0;
41 xmlrpc_value
*result
= NULL
;
43 /* parse argument array */
44 xmlrpc_decompose_value(env
, param
, "(s)", &path
);
45 if (env
->fault_occurred
) {
49 SLOG("getfile(), path:%s", path
);
51 fd
= open(path
, O_RDONLY
);
53 LOG("open failed: %s", strerror(errno
));
54 xmlrpc_faultf(env
, "open failed: %s", strerror(errno
));
58 while ((ret
= read(fd
, buf
, VA_FILEBUF_LEN
)) > 0) {
59 file_contents
= qemu_realloc(file_contents
, count
+ VA_FILEBUF_LEN
);
60 memcpy(file_contents
+ count
, buf
, ret
);
62 if (count
> VA_GETFILE_MAX
) {
63 xmlrpc_faultf(env
, "max file size (%d bytes) exceeded",
69 LOG("read failed: %s", strerror(errno
));
70 xmlrpc_faultf(env
, "read failed: %s", strerror(errno
));
74 result
= xmlrpc_build_value(env
, "6", file_contents
, count
);
78 qemu_free(file_contents
);
84 /* getdmesg(): return dmesg output
86 * - dmesg output as a string
88 static xmlrpc_value
*getdmesg(xmlrpc_env
*env
,
92 char *dmesg_buf
= NULL
, cmd
[256];
94 xmlrpc_value
*result
= NULL
;
99 dmesg_buf
= qemu_mallocz(VA_DMESG_LEN
+ 2048);
100 sprintf(cmd
, "dmesg -s %d", VA_DMESG_LEN
);
102 pipe
= popen(cmd
, "r");
104 LOG("popen failed: %s", strerror(errno
));
105 xmlrpc_faultf(env
, "popen failed: %s", strerror(errno
));
109 ret
= fread(dmesg_buf
, sizeof(char), VA_DMESG_LEN
, pipe
);
111 dmesg_buf
[ret
] = '\0';
112 TRACE("dmesg:\n%s", dmesg_buf
);
113 result
= xmlrpc_build_value(env
, "s", dmesg_buf
);
116 xmlrpc_faultf(env
, "popen failed: %s", strerror(errno
));
122 qemu_free(dmesg_buf
);
128 /* va_shutdown(): initiate guest shutdown
129 * rpc return values: none
131 static xmlrpc_value
*va_shutdown(xmlrpc_env
*env
,
136 const char *shutdown_type
, *shutdown_flag
;
137 xmlrpc_value
*result
= xmlrpc_build_value(env
, "s", "dummy");
140 xmlrpc_decompose_value(env
, param
, "(s)", &shutdown_type
);
141 if (env
->fault_occurred
) {
145 if (strcmp(shutdown_type
, "halt") == 0) {
146 shutdown_flag
= "-H";
147 } else if (strcmp(shutdown_type
, "powerdown") == 0) {
148 shutdown_flag
= "-P";
149 } else if (strcmp(shutdown_type
, "reboot") == 0) {
150 shutdown_flag
= "-r";
152 xmlrpc_faultf(env
, "invalid shutdown type: %s", shutdown_type
);
156 SLOG("va_shutdown(), shutdown_type:%s", shutdown_type
);
160 /* child, start the shutdown */
167 ret
= execl("/sbin/shutdown", "shutdown", shutdown_flag
, "+0",
168 "hypervisor initiated shutdown", (char*)NULL
);
170 LOG("execl() failed: %s", strerror(errno
));
173 TRACE("shouldn't be here");
175 } else if (ret
< 0) {
176 xmlrpc_faultf(env
, "fork() failed: %s", strerror(errno
));
184 /* va_ping(): respond to client. response without error in env
185 * variable indicates successful response
186 * rpc return values: none
188 static xmlrpc_value
*va_ping(xmlrpc_env
*env
,
192 xmlrpc_value
*result
= xmlrpc_build_value(env
, "s", "dummy");
197 /* va_hello(): handle client startup notification
198 * rpc return values: none
201 static xmlrpc_value
*va_hello(xmlrpc_env
*env
,
205 int ret
= va_client_init_capabilities();
209 LOG("error setting initializing client capabilities");
214 static int va_accept(int listen_fd
) {
215 struct sockaddr_in saddr
;
216 struct sockaddr
*addr
;
222 addr
= (struct sockaddr
*)&saddr
;
223 fd
= qemu_accept(listen_fd
, addr
, &len
);
224 if (fd
< 0 && errno
!= EINTR
) {
225 LOG("accept() failed");
227 } else if (fd
>= 0) {
228 TRACE("accepted connection");
235 typedef struct RPCFunction
{
236 xmlrpc_value
*(*func
)(xmlrpc_env
*env
, xmlrpc_value
*param
, void *unused
);
237 const char *func_name
;
240 static RPCFunction guest_functions
[] = {
242 .func_name
= "getfile" },
244 .func_name
= "getdmesg" },
245 { .func
= va_shutdown
,
246 .func_name
= "va_shutdown" },
248 .func_name
= "va_ping" },
251 static RPCFunction host_functions
[] = {
253 .func_name
= "va_ping" },
255 .func_name
= "va_hello" },
259 static void va_register_functions(xmlrpc_env
*env
, xmlrpc_registry
*registry
,
263 for (i
= 0; list
[i
].func
!= NULL
; ++i
) {
264 TRACE("adding func: %s", list
[i
].func_name
);
265 xmlrpc_registry_add_method(env
, registry
, NULL
, list
[i
].func_name
,
270 typedef struct VARPCServerState
{
274 xmlrpc_registry
*registry
;
277 /* only one virtagent server instance can exist at a time */
278 static VARPCServerState
*server_state
= NULL
;
280 static void va_accept_handler(void *opaque
);
282 static void va_rpc_send_cb(void *opaque
)
284 VARPCData
*rpc_data
= opaque
;
285 VARPCServerState
*s
= server_state
;
288 if (rpc_data
->status
!= VA_RPC_STATUS_OK
) {
289 LOG("error sending RPC response");
291 TRACE("RPC completed");
294 TRACE("waiting for RPC request...");
295 vp_set_fd_handler(s
->listen_fd
, va_accept_handler
, NULL
, s
);
298 static void va_rpc_read_cb(void *opaque
)
300 VARPCData
*rpc_data
= opaque
;
301 VARPCServerState
*s
= server_state
;
304 if (rpc_data
->status
!= VA_RPC_STATUS_OK
) {
305 LOG("error reading RPC request");
309 rpc_data
->send_resp_xml
=
310 xmlrpc_registry_process_call(&s
->env
, s
->registry
, NULL
,
311 rpc_data
->req_xml
, rpc_data
->req_xml_len
);
312 if (rpc_data
->send_resp_xml
== NULL
) {
313 LOG("error handling RPC request");
317 rpc_data
->cb
= va_rpc_send_cb
;
321 TRACE("waiting for RPC request...");
322 vp_set_fd_handler(s
->listen_fd
, va_accept_handler
, NULL
, s
);
325 static void va_accept_handler(void *opaque
)
331 fd
= va_accept(server_state
->listen_fd
);
333 TRACE("connection error: %s", strerror(errno
));
336 ret
= fcntl(fd
, F_GETFL
);
337 ret
= fcntl(fd
, F_SETFL
, ret
| O_NONBLOCK
);
339 TRACE("RPC client connected, reading RPC request...");
340 rpc_data
= qemu_mallocz(sizeof(VARPCData
));
341 rpc_data
->cb
= va_rpc_read_cb
;
342 ret
= va_rpc_read_request(rpc_data
, fd
);
344 LOG("error setting up read handler");
348 vp_set_fd_handler(server_state
->listen_fd
, NULL
, NULL
, NULL
);
351 int va_server_init(VPDriver
*vp_drv
, bool is_host
)
353 RPCFunction
*func_list
= is_host
? host_functions
: guest_functions
;
356 const char *path
, *service_id
;
359 LOG("virtagent server already initialized");
362 va_enable_syslog
= !is_host
; /* enable logging for guest agent */
364 server_state
= qemu_mallocz(sizeof(VARPCServerState
));
365 service_id
= is_host
? HOST_AGENT_SERVICE_ID
: GUEST_AGENT_SERVICE_ID
;
366 /* TODO: host agent path needs to be made unique amongst multiple
369 path
= is_host
? HOST_AGENT_PATH
: GUEST_AGENT_PATH
;
371 /* setup listening socket for server */
372 opts
= qemu_opts_create(qemu_find_opts("net"), "va_server_opts", 0);
373 qemu_opt_set(opts
, "path", path
);
374 fd
= unix_listen_opts(opts
);
377 LOG("error setting up listening socket");
381 /* tell virtproxy to forward incoming virtagent connections to the socket */
382 ret
= vp_set_iforward(vp_drv
, service_id
, path
, NULL
, false);
384 LOG("error setting up virtproxy iforward");
388 server_state
->vp
= vp_drv
;
389 server_state
->listen_fd
= fd
;
390 xmlrpc_env_init(&server_state
->env
);
391 server_state
->registry
= xmlrpc_registry_new(&server_state
->env
);
392 va_register_functions(&server_state
->env
, server_state
->registry
, func_list
);
394 TRACE("waiting for RPC request...");
395 vp_set_fd_handler(server_state
->listen_fd
, va_accept_handler
, NULL
,
401 qemu_free(server_state
);