virtagent: Makefile fixups
[qemu/mdroth.git] / virtagent-daemon.c
blobae306b98c6cc480090cdef040de5aea595957d6d
1 /*
2 * virt-agent - host/guest RPC daemon functions
4 * Copyright IBM Corp. 2010
6 * Authors:
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.
14 #include <syslog.h>
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 { \
23 char msg_buf[1024]; \
24 if (!va_enable_syslog) { \
25 break; \
26 } \
27 sprintf(msg_buf, msg, ## __VA_ARGS__); \
28 syslog(LOG_INFO, "virtagent, %s", msg_buf); \
29 } while(0)
31 /* RPC functions common to guest/host daemons */
33 static xmlrpc_value *getfile(xmlrpc_env *env,
34 xmlrpc_value *param,
35 void *user_data)
37 const char *path;
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) {
46 return NULL;
49 SLOG("getfile(), path:%s", path);
51 fd = open(path, O_RDONLY);
52 if (fd == -1) {
53 LOG("open failed: %s", strerror(errno));
54 xmlrpc_faultf(env, "open failed: %s", strerror(errno));
55 return NULL;
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);
61 count += ret;
62 if (count > VA_GETFILE_MAX) {
63 xmlrpc_faultf(env, "max file size (%d bytes) exceeded",
64 VA_GETFILE_MAX);
65 goto EXIT_CLOSE_BAD;
68 if (ret == -1) {
69 LOG("read failed: %s", strerror(errno));
70 xmlrpc_faultf(env, "read failed: %s", strerror(errno));
71 goto EXIT_CLOSE_BAD;
74 result = xmlrpc_build_value(env, "6", file_contents, count);
76 EXIT_CLOSE_BAD:
77 if (file_contents) {
78 qemu_free(file_contents);
80 close(fd);
81 return result;
84 /* getdmesg(): return dmesg output
85 * rpc return values:
86 * - dmesg output as a string
88 static xmlrpc_value *getdmesg(xmlrpc_env *env,
89 xmlrpc_value *param,
90 void *user_data)
92 char *dmesg_buf = NULL, cmd[256];
93 int ret;
94 xmlrpc_value *result = NULL;
95 FILE *pipe;
97 SLOG("getdmesg()");
99 dmesg_buf = qemu_mallocz(VA_DMESG_LEN + 2048);
100 sprintf(cmd, "dmesg -s %d", VA_DMESG_LEN);
102 pipe = popen(cmd, "r");
103 if (pipe == NULL) {
104 LOG("popen failed: %s", strerror(errno));
105 xmlrpc_faultf(env, "popen failed: %s", strerror(errno));
106 goto EXIT_NOCLOSE;
109 ret = fread(dmesg_buf, sizeof(char), VA_DMESG_LEN, pipe);
110 if (!ferror(pipe)) {
111 dmesg_buf[ret] = '\0';
112 TRACE("dmesg:\n%s", dmesg_buf);
113 result = xmlrpc_build_value(env, "s", dmesg_buf);
114 } else {
115 LOG("fread failed");
116 xmlrpc_faultf(env, "popen failed: %s", strerror(errno));
119 pclose(pipe);
120 EXIT_NOCLOSE:
121 if (dmesg_buf) {
122 qemu_free(dmesg_buf);
125 return result;
128 /* va_shutdown(): initiate guest shutdown
129 * rpc return values: none
131 static xmlrpc_value *va_shutdown(xmlrpc_env *env,
132 xmlrpc_value *param,
133 void *user_data)
135 int ret;
136 const char *shutdown_type, *shutdown_flag;
137 xmlrpc_value *result = xmlrpc_build_value(env, "s", "dummy");
139 TRACE("called");
140 xmlrpc_decompose_value(env, param, "(s)", &shutdown_type);
141 if (env->fault_occurred) {
142 goto out_bad;
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";
151 } else {
152 xmlrpc_faultf(env, "invalid shutdown type: %s", shutdown_type);
153 goto out_bad;
156 SLOG("va_shutdown(), shutdown_type:%s", shutdown_type);
158 ret = fork();
159 if (ret == 0) {
160 /* child, start the shutdown */
161 setsid();
162 fclose(stdin);
163 fclose(stdout);
164 fclose(stderr);
166 sleep(5);
167 ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
168 "hypervisor initiated shutdown", (char*)NULL);
169 if (ret < 0) {
170 LOG("execl() failed: %s", strerror(errno));
171 exit(1);
173 TRACE("shouldn't be here");
174 exit(0);
175 } else if (ret < 0) {
176 xmlrpc_faultf(env, "fork() failed: %s", strerror(errno));
179 return result;
180 out_bad:
181 return NULL;
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,
189 xmlrpc_value *param,
190 void *user_data)
192 xmlrpc_value *result = xmlrpc_build_value(env, "s", "dummy");
193 SLOG("va_ping()");
194 return result;
197 /* va_hello(): handle client startup notification
198 * rpc return values: none
201 static xmlrpc_value *va_hello(xmlrpc_env *env,
202 xmlrpc_value *param,
203 void *user_data)
205 int ret = va_client_init_capabilities();
206 TRACE("called");
207 SLOG("va_hello()");
208 if (ret < 0) {
209 LOG("error setting initializing client capabilities");
211 return NULL;
214 static int va_accept(int listen_fd) {
215 struct sockaddr_in saddr;
216 struct sockaddr *addr;
217 socklen_t len;
218 int fd;
220 while (1) {
221 len = sizeof(saddr);
222 addr = (struct sockaddr *)&saddr;
223 fd = qemu_accept(listen_fd, addr, &len);
224 if (fd < 0 && errno != EINTR) {
225 LOG("accept() failed");
226 break;
227 } else if (fd >= 0) {
228 TRACE("accepted connection");
229 break;
232 return fd;
235 typedef struct RPCFunction {
236 xmlrpc_value *(*func)(xmlrpc_env *env, xmlrpc_value *param, void *unused);
237 const char *func_name;
238 } RPCFunction;
240 static RPCFunction guest_functions[] = {
241 { .func = getfile,
242 .func_name = "getfile" },
243 { .func = getdmesg,
244 .func_name = "getdmesg" },
245 { .func = va_shutdown,
246 .func_name = "va_shutdown" },
247 { .func = va_ping,
248 .func_name = "va_ping" },
249 { NULL, NULL }
251 static RPCFunction host_functions[] = {
252 { .func = va_ping,
253 .func_name = "va_ping" },
254 { .func = va_hello,
255 .func_name = "va_hello" },
256 { NULL, NULL }
259 static void va_register_functions(xmlrpc_env *env, xmlrpc_registry *registry,
260 RPCFunction *list)
262 int i;
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,
266 list[i].func, NULL);
270 typedef struct VARPCServerState {
271 VPDriver *vp;
272 int listen_fd;
273 xmlrpc_env env;
274 xmlrpc_registry *registry;
275 } VARPCServerState;
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;
287 TRACE("called");
288 if (rpc_data->status != VA_RPC_STATUS_OK) {
289 LOG("error sending RPC response");
290 } else {
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;
303 TRACE("called");
304 if (rpc_data->status != VA_RPC_STATUS_OK) {
305 LOG("error reading RPC request");
306 goto out_bad;
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");
314 goto out_bad;
317 rpc_data->cb = va_rpc_send_cb;
318 return;
320 out_bad:
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)
327 VARPCData *rpc_data;
328 int ret, fd;
330 TRACE("called");
331 fd = va_accept(server_state->listen_fd);
332 if (fd < 0) {
333 TRACE("connection error: %s", strerror(errno));
334 return;
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);
343 if (ret != 0) {
344 LOG("error setting up read handler");
345 qemu_free(rpc_data);
346 return;
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;
354 QemuOpts *opts;
355 int ret, fd;
356 const char *path, *service_id;
358 if (server_state) {
359 LOG("virtagent server already initialized");
360 return -1;
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
367 * qemu instances
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);
375 qemu_opts_del(opts);
376 if (fd < 0) {
377 LOG("error setting up listening socket");
378 goto out_bad;
381 /* tell virtproxy to forward incoming virtagent connections to the socket */
382 ret = vp_set_iforward(vp_drv, service_id, path, NULL, false);
383 if (ret < 0) {
384 LOG("error setting up virtproxy iforward");
385 goto out_bad;
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,
396 server_state);
398 return 0;
400 out_bad:
401 qemu_free(server_state);
402 server_state = NULL;
403 return -1;