Add unix domain client server support
[vd_agent/hramrach.git] / vdagent.c
blob1d140c0056142b8c67aed26ad708ec720efb2edb
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <inttypes.h>
5 #include <unistd.h>
6 #include <fcntl.h>
7 #include <errno.h>
8 #include <sys/select.h>
10 #include <linux/input.h>
11 #include <linux/uinput.h>
13 /* spice structs */
15 #include <spice/vd_agent.h>
17 #include "udscs.h"
18 #include "vdagentd-proto.h"
20 typedef struct VDAgentHeader {
21 uint32_t port;
22 uint32_t size;
23 } VDAgentHeader;
25 /* variables */
27 static const char *portdev = "/dev/virtio-ports/com.redhat.spice.0";
28 static const char *uinput = "/dev/uinput";
30 static int vdagent, tablet;
31 static int debug = 0;
32 static int width = 1024, height = 768; /* FIXME: don't hardcode */
33 static struct udscs_server *server = NULL;
35 /* uinput */
37 static void uinput_setup(void)
39 struct uinput_user_dev device = {
40 .name = "spice vdagent tablet",
41 .absmax [ ABS_X ] = width,
42 .absmax [ ABS_Y ] = height,
44 int rc;
46 rc = write(tablet, &device, sizeof(device));
47 if (rc != sizeof(device)) {
48 fprintf(stderr, "%s: write error\n", __FUNCTION__);
49 exit(1);
52 /* buttons */
53 ioctl(tablet, UI_SET_EVBIT, EV_KEY);
54 ioctl(tablet, UI_SET_KEYBIT, BTN_LEFT);
55 ioctl(tablet, UI_SET_KEYBIT, BTN_MIDDLE);
56 ioctl(tablet, UI_SET_KEYBIT, BTN_RIGHT);
57 ioctl(tablet, UI_SET_KEYBIT, BTN_GEAR_UP);
58 ioctl(tablet, UI_SET_KEYBIT, BTN_GEAR_DOWN);
60 /* abs ptr */
61 ioctl(tablet, UI_SET_EVBIT, EV_ABS);
62 ioctl(tablet, UI_SET_ABSBIT, ABS_X);
63 ioctl(tablet, UI_SET_ABSBIT, ABS_Y);
65 rc = ioctl(tablet, UI_DEV_CREATE);
66 if (rc < 0) {
67 fprintf(stderr, "%s: create error\n", __FUNCTION__);
68 exit(1);
72 static void uinput_send_event(__u16 type, __u16 code, __s32 value)
74 struct input_event event = {
75 .type = type,
76 .code = code,
77 .value = value,
79 int rc;
81 rc = write(tablet, &event, sizeof(event));
82 if (rc != sizeof(event)) {
83 fprintf(stderr, "%s: write error\n", __FUNCTION__);
84 exit(1);
88 /* spice port */
90 static void do_mouse(VDAgentMouseState *mouse)
92 static const struct {
93 const char *name;
94 int mask;
95 int btn;
96 } btns[] = {
97 { .name = "left", .mask = VD_AGENT_LBUTTON_MASK, .btn = BTN_LEFT },
98 { .name = "middle", .mask = VD_AGENT_MBUTTON_MASK, .btn = BTN_MIDDLE },
99 { .name = "right", .mask = VD_AGENT_RBUTTON_MASK, .btn = BTN_RIGHT },
100 { .name = "up", .mask = VD_AGENT_UBUTTON_MASK, .btn = BTN_GEAR_UP },
101 { .name = "down", .mask = VD_AGENT_DBUTTON_MASK, .btn = BTN_GEAR_DOWN },
103 static VDAgentMouseState last;
104 int i, down;
106 if (last.x != mouse->x) {
107 if (debug)
108 fprintf(stderr, "mouse: abs-x %d\n", mouse->x);
109 uinput_send_event(EV_ABS, ABS_X, mouse->x);
111 if (last.y != mouse->y) {
112 if (debug)
113 fprintf(stderr, "mouse: abs-y %d\n", mouse->y);
114 uinput_send_event(EV_ABS, ABS_Y, mouse->y);
116 for (i = 0; i < sizeof(btns)/sizeof(btns[0]); i++) {
117 if ((last.buttons & btns[i].mask) == (mouse->buttons & btns[i].mask))
118 continue;
119 down = !!(mouse->buttons & btns[i].mask);
120 if (debug)
121 fprintf(stderr, "mouse: btn-%s %s\n",
122 btns[i].name, down ? "down" : "up");
123 uinput_send_event(EV_KEY, btns[i].btn, down);
125 if (debug)
126 fprintf(stderr, "mouse: syn\n");
127 uinput_send_event(EV_SYN, SYN_REPORT, 0);
129 last = *mouse;
132 static void do_monitors(VDAgentMonitorsConfig *monitors)
134 int i;
136 if (!debug)
137 return;
138 fprintf(stderr, "monitors: %d\n", monitors->num_of_monitors);
139 for (i = 0; i < monitors->num_of_monitors; i++) {
140 fprintf(stderr, " #%d: size %dx%d pos +%d+%d depth %d\n", i,
141 monitors->monitors[i].width, monitors->monitors[i].height,
142 monitors->monitors[i].x, monitors->monitors[i].y,
143 monitors->monitors[i].depth);
147 void vdagent_read(void)
149 VDAgentHeader header;
150 VDAgentMessage *message;
151 void *data;
152 int rc;
154 rc = read(vdagent, &header, sizeof(header));
155 if (rc != sizeof(header)) {
156 fprintf(stderr, "vdagent header read error (%d/%zd)\n", rc, sizeof(header));
157 exit(1);
160 message = malloc(header.size);
161 rc = read(vdagent, message, header.size);
162 if (rc != header.size) {
163 fprintf(stderr, "vdagent message read error (%d/%d)\n", rc, header.size);
164 exit(1);
166 data = message->data;
168 switch (message->type) {
169 case VD_AGENT_MOUSE_STATE:
170 do_mouse(data);
171 break;
172 case VD_AGENT_MONITORS_CONFIG:
173 do_monitors(data);
174 break;
175 default:
176 if (debug)
177 fprintf(stderr, "unknown message type %d\n", message->type);
178 break;
181 free(message);
184 /* main */
186 static void usage(FILE *fp)
188 fprintf(fp,
189 "vdagent -- handle spice agent mouse via uinput\n"
190 "options:\n"
191 " -h print this text\n"
192 " -d print debug messages (and don't daemonize)\n"
193 " -x width set display width [%d]\n"
194 " -y height set display height [%d]\n"
195 " -s <port> set virtio serial port [%s]\n"
196 " -u <dev> set uinput device [%s]\n",
197 width, height, portdev, uinput);
200 void daemonize(void)
202 /* detach from terminal */
203 switch (fork()) {
204 case -1:
205 perror("fork");
206 exit(1);
207 case 0:
208 close(0); close(1); close(2);
209 setsid();
210 open("/dev/null",O_RDWR); dup(0); dup(0);
211 break;
212 default:
213 exit(0);
217 void client_read_complete(struct udscs_connection *conn,
218 struct udscs_message_header *header, const uint8_t *data)
220 switch (header->type) {
221 case VDAGENTD_GUEST_XORG_RESOLUTION: {
222 struct vdagentd_guest_xorg_resolution *res =
223 (struct vdagentd_guest_xorg_resolution *)data;
225 if (header->size != sizeof(*res)) {
226 /* FIXME destroy connection, but this will cause a double
227 free of data */
228 break;
231 width = res->width;
232 height = res->height;
233 close(tablet);
234 tablet = open(uinput, O_RDWR);
235 if (-1 == tablet) {
236 fprintf(stderr, "open %s: %s\n", uinput, strerror(errno));
237 exit(1);
239 uinput_setup();
240 break;
242 default:
243 fprintf(stderr, "unknown message from vdagent client: %u, ignoring\n",
244 header->type);
248 void main_loop(void)
250 fd_set readfds, writefds;
251 int n, nfds;
253 for (;;) {
254 FD_ZERO(&readfds);
255 FD_ZERO(&writefds);
257 nfds = udscs_server_fill_fds(server, &readfds, &writefds);
259 FD_SET(vdagent, &readfds);
260 if (vdagent >= nfds)
261 nfds = vdagent + 1;
263 n = select(nfds, &readfds, &writefds, NULL, NULL);
264 if (n == -1) {
265 if (errno == EINTR)
266 continue;
267 perror("select");
268 exit(1);
271 udscs_server_handle_fds(server, &readfds, &writefds);
272 if (FD_ISSET(vdagent, &readfds))
273 vdagent_read();
277 int main(int argc, char *argv[])
279 int c;
281 for (;;) {
282 if (-1 == (c = getopt(argc, argv, "dhx:y:s:u:")))
283 break;
284 switch (c) {
285 case 'd':
286 debug++;
287 break;
288 case 'x':
289 width = atoi(optarg);
290 break;
291 case 'y':
292 height = atoi(optarg);
293 break;
294 case 's':
295 portdev = optarg;
296 break;
297 case 'u':
298 uinput = optarg;
299 break;
300 case 'h':
301 usage(stdout);
302 exit(0);
303 default:
304 usage(stderr);
305 exit(1);
309 /* Open virtio port connection */
310 vdagent = open(portdev, O_RDWR);
311 if (-1 == vdagent) {
312 fprintf(stderr, "open %s: %s\n", portdev, strerror(errno));
313 exit(1);
316 /* Setup communication with vdagent process(es) */
317 server = udscs_create_server(VDAGENTD_SOCKET, client_read_complete, NULL);
318 if (!server)
319 exit(1);
321 tablet = open(uinput, O_RDWR);
322 if (-1 == tablet) {
323 fprintf(stderr, "open %s: %s\n", uinput, strerror(errno));
324 exit(1);
326 uinput_setup();
328 if (!debug)
329 daemonize();
331 main_loop();
333 udscs_destroy_server(server);
335 return 0;