source: trunk/packages/xen-common/xen-common/tools/xenfb/vncfb.c @ 95

Last change on this file since 95 was 34, checked in by hartmans, 17 years ago

Add xen and xen-common

File size: 10.5 KB
Line 
1#define _GNU_SOURCE
2#include <errno.h>
3#include <getopt.h>
4#include <stdlib.h>
5#include <signal.h>
6#include <unistd.h>
7#include <malloc.h>
8#include <rfb/rfb.h>
9#include <rfb/keysym.h>
10#include <linux/input.h>
11#include <xs.h>
12#include "xenfb.h"
13
14/* Grab key translation support routines from qemu directory. */
15#define qemu_mallocz(size) calloc(1, (size))
16static const char *bios_dir = "/usr/share/xen/qemu";
17#include "vnc_keysym.h"
18#include "keymaps.c"
19
20static unsigned char atkbd_set2_keycode[512] = {
21
22          0, 67, 65, 63, 61, 59, 60, 88,  0, 68, 66, 64, 62, 15, 41,117,
23          0, 56, 42, 93, 29, 16,  2,  0,  0,  0, 44, 31, 30, 17,  3,  0,
24          0, 46, 45, 32, 18,  5,  4, 95,  0, 57, 47, 33, 20, 19,  6,183,
25          0, 49, 48, 35, 34, 21,  7,184,  0,  0, 50, 36, 22,  8,  9,185,
26          0, 51, 37, 23, 24, 11, 10,  0,  0, 52, 53, 38, 39, 25, 12,  0,
27          0, 89, 40,  0, 26, 13,  0,  0, 58, 54, 28, 27,  0, 43,  0, 85,
28          0, 86, 91, 90, 92,  0, 14, 94,  0, 79,124, 75, 71,121,  0,  0,
29         82, 83, 80, 76, 77, 72,  1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
30
31          0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
32        217,100,255,  0, 97,165,  0,  0,156,  0,  0,  0,  0,  0,  0,125,
33        173,114,  0,113,  0,  0,  0,126,128,  0,  0,140,  0,  0,  0,127,
34        159,  0,115,  0,164,  0,  0,116,158,  0,150,166,  0,  0,  0,142,
35        157,  0,  0,  0,  0,  0,  0,  0,155,  0, 98,  0,  0,163,  0,  0,
36        226,  0,  0,  0,  0,  0,  0,  0,  0,255, 96,  0,  0,  0,143,  0,
37          0,  0,  0,  0,  0,  0,  0,  0,  0,107,  0,105,102,  0,  0,112,
38        110,111,108,112,106,103,  0,119,  0,118,109,  0, 99,104,119,  0,
39
40};
41
42static unsigned char atkbd_unxlate_table[128] = {
43
44          0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
45         21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
46         35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
47         50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88,  5,  6,  4, 12,  3,
48         11,  2, 10,  1,  9,119,126,108,117,125,123,107,115,116,121,105,
49        114,122,112,113,127, 96, 97,120,  7, 15, 23, 31, 39, 47, 55, 63,
50         71, 79, 86, 94,  8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
51         19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
52
53};
54
55unsigned char keycode_table[512];
56
57static void *kbd_layout;
58
59static int btnmap[] = {
60        BTN_LEFT, BTN_MIDDLE, BTN_RIGHT, BTN_SIDE,
61        BTN_EXTRA, BTN_FORWARD, BTN_BACK, BTN_TASK
62};
63
64static void on_kbd_event(rfbBool down, rfbKeySym keycode, rfbClientPtr cl)
65{
66        /*
67         * We need to map to the key's Linux input layer keycode.
68         * Unfortunately, we don't get the key here, only the
69         * rfbKeySym, which is what the key is mapped to.  Mapping
70         * back to the key is impossible in general, even when you
71         * know the keymap.  For instance, the standard German keymap
72         * maps both KEY_COMMA and KEY_102ND to XK_less.  We simply
73         * assume standard US layout.  This sucks.
74         */
75        rfbScreenInfoPtr server = cl->screen;
76        struct xenfb *xenfb = server->screenData;
77        int scancode;
78
79        if (keycode >= 'A' && keycode <= 'Z')
80                keycode += 'a' - 'A';
81
82        scancode = keycode_table[keysym2scancode(kbd_layout, keycode)];
83        if (scancode == 0)
84                return;
85        if (xenfb_send_key(xenfb, down, scancode) < 0)
86                fprintf(stderr, "Key %d %s lost (%s)\n",
87                        scancode, down ? "down" : "up",
88                        strerror(errno));
89}
90
91static void on_ptr_event(int buttonMask, int x, int y, rfbClientPtr cl)
92{
93        /* initial pointer state: at (0,0), buttons up */
94        static int last_x, last_y, last_button;
95        rfbScreenInfoPtr server = cl->screen;
96        struct xenfb *xenfb = server->screenData;
97        int i, last_down, down, ret;
98
99        for (i = 0; i < 8; i++) {
100                last_down = last_button & (1 << i);
101                down = buttonMask & (1 << i);
102                if (down == last_down)
103                        continue;
104                if (i >= sizeof(btnmap) / sizeof(*btnmap))
105                        break;
106                if (btnmap[i] == 0)
107                        break;
108                if (xenfb_send_key(xenfb, down != 0, btnmap[i]) < 0)
109                        fprintf(stderr, "Button %d %s lost (%s)\n",
110                                i, down ? "down" : "up", strerror(errno));
111        }
112
113        if (x != last_x || y != last_y) {
114                if (xenfb->abs_pointer_wanted) 
115                        ret = xenfb_send_position(xenfb, x, y);
116                else
117                        ret = xenfb_send_motion(xenfb, x - last_x, y - last_y);
118                if (ret < 0)
119                        fprintf(stderr, "Pointer to %d,%d lost (%s)\n",
120                                x, y, strerror(errno));
121        }
122
123        last_button = buttonMask;
124        last_x = x;
125        last_y = y;
126}
127
128static void xenstore_write_vncport(struct xs_handle *xsh, int port, int domid)
129{
130        char *buf, *path;
131        char portstr[10];
132
133        path = xs_get_domain_path(xsh, domid);
134        if (path == NULL) {
135                fprintf(stderr, "Can't get domain path (%s)\n",
136                        strerror(errno));
137                goto out;
138        }
139
140        if (asprintf(&buf, "%s/console/vnc-port", path) == -1) {
141                fprintf(stderr, "Can't make vncport path\n");
142                goto out;
143        }
144
145        if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) {
146                fprintf(stderr, "Can't make vncport value\n");
147                goto out;
148        }
149
150        if (!xs_write(xsh, XBT_NULL, buf, portstr, strlen(portstr)))
151                fprintf(stderr, "Can't set vncport (%s)\n",
152                        strerror(errno));
153
154 out:
155        free(buf);
156}
157
158
159static int xenstore_read_vncpasswd(struct xs_handle *xsh, int domid, char *pwbuf, int pwbuflen)
160{
161        char buf[256], *path, *uuid = NULL, *passwd = NULL;
162        unsigned int len, rc = 0;
163
164        if (xsh == NULL) {
165                return -1;
166        }
167
168        path = xs_get_domain_path(xsh, domid);
169        if (path == NULL) {
170                fprintf(stderr, "xs_get_domain_path() error\n");
171                return -1;
172        }
173
174        snprintf(buf, 256, "%s/vm", path);
175        uuid = xs_read(xsh, XBT_NULL, buf, &len);
176        if (uuid == NULL) {
177                fprintf(stderr, "xs_read(): uuid get error\n");
178                free(path);
179                return -1;
180        }
181
182        snprintf(buf, 256, "%s/vncpasswd", uuid);
183        passwd = xs_read(xsh, XBT_NULL, buf, &len);
184        if (passwd == NULL) {
185                free(uuid);
186                free(path);
187                return rc;
188        }
189
190        strncpy(pwbuf, passwd, pwbuflen-1);
191        pwbuf[pwbuflen-1] = '\0';
192
193        fprintf(stderr, "Got a VNC password read from XenStore\n");
194
195        passwd[0] = '\0';
196        snprintf(buf, 256, "%s/vncpasswd", uuid);
197        if (xs_write(xsh, XBT_NULL, buf, passwd, len) == 0) {
198                fprintf(stderr, "xs_write() vncpasswd failed\n");
199                rc = -1;
200        }
201
202        free(passwd);
203        free(uuid);
204        free(path);
205
206        return rc;
207}
208
209static void vnc_update(struct xenfb *xenfb, int x, int y, int w, int h)
210{
211        rfbScreenInfoPtr server = xenfb->user_data;
212        rfbMarkRectAsModified(server, x, y, x + w, y + h);
213}
214
215static struct option options[] = {
216        { "domid", 1, NULL, 'd' },
217        { "vncport", 1, NULL, 'p' },
218        { "title", 1, NULL, 't' },
219        { "unused", 0, NULL, 'u' },
220        { "listen", 1, NULL, 'l' },
221        { "keymap", 1, NULL, 'k' },
222        { NULL }
223};
224
225int main(int argc, char **argv)
226{
227        rfbScreenInfoPtr server;
228        char *fake_argv[7] = { "vncfb", "-rfbport", "5901", 
229                               "-desktop", "xen-vncfb", 
230                               "-listen", "127.0.0.1" };
231        int fake_argc = sizeof(fake_argv) / sizeof(fake_argv[0]);
232        int domid = -1, port = -1;
233        char *title = NULL;
234        char *listen = NULL;
235        char *keymap = NULL;
236        bool unused = false;
237        int opt;
238        struct xenfb *xenfb;
239        fd_set readfds;
240        int nfds;
241        char portstr[10];
242        char *endp;
243        int r;
244        struct xs_handle *xsh;
245        char vncpasswd[1024];
246        int i;
247
248        vncpasswd[0] = '\0';
249
250        while ((opt = getopt_long(argc, argv, "d:p:t:uk:", options,
251                                  NULL)) != -1) {
252                switch (opt) {
253                case 'd':
254                        errno = 0;
255                        domid = strtol(optarg, &endp, 10);
256                        if (endp == optarg || *endp || errno) {
257                                fprintf(stderr, "Invalid domain id specified\n");
258                                exit(1);
259                        }
260                        break;
261                case 'p':
262                        errno = 0;
263                        port = strtol(optarg, &endp, 10);
264                        if (endp == optarg || *endp || errno) {
265                                fprintf(stderr, "Invalid port specified\n");
266                                exit(1);
267                        }
268                        break;
269                case 't':
270                        title = strdup(optarg);
271                        break;
272                case 'u':
273                        unused = true;
274                        break;
275                case 'l':
276                        listen = strdup(optarg);
277                        break;
278                case 'k':
279                        keymap = strdup(optarg);
280                        break;
281                case '?':
282                        exit(1);
283                }
284        }
285        if (optind != argc) {
286                fprintf(stderr, "Invalid options!\n");
287                exit(1);
288        }
289        if (domid <= 0) {
290                fprintf(stderr, "Domain ID must be specified!\n");
291                exit(1);
292        }
293           
294        if (port <= 0)
295                port = 5900 + domid;
296        if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) {
297                fprintf(stderr, "Invalid port specified\n");
298                exit(1);
299        }
300
301        if (keymap == NULL){
302                keymap = "en-us";
303        }
304
305        kbd_layout = init_keyboard_layout(keymap);
306        if( !kbd_layout ){
307                fprintf(stderr, "Invalid keyboard_layout\n");
308                exit(1);
309        }
310
311        for (i = 0; i < 128; i++) {
312                keycode_table[i] = atkbd_set2_keycode[atkbd_unxlate_table[i]];
313                keycode_table[i | 0x80] = 
314                        atkbd_set2_keycode[atkbd_unxlate_table[i] | 0x80];
315        }
316
317        fake_argv[2] = portstr;
318
319        if (title != NULL)
320                fake_argv[4] = title;
321
322        if (listen != NULL)
323                fake_argv[6] = listen;
324
325        signal(SIGPIPE, SIG_IGN);
326
327        xenfb = xenfb_new();
328        if (xenfb == NULL) {
329                fprintf(stderr, "Could not create framebuffer (%s)\n",
330                        strerror(errno));
331                exit(1);
332        }
333
334        if (xenfb_attach_dom(xenfb, domid) < 0) {
335                fprintf(stderr, "Could not connect to domain (%s)\n",
336                        strerror(errno));
337                exit(1);
338        }
339
340        xsh = xs_daemon_open();
341        if (xsh == NULL) {
342                fprintf(stderr, "cannot open connection to xenstore\n");
343                exit(1);
344        }
345
346
347        if (xenstore_read_vncpasswd(xsh, domid, vncpasswd,
348                                    sizeof(vncpasswd)/sizeof(char)) < 0) {
349                fprintf(stderr, "cannot read VNC password from xenstore\n");
350                exit(1);
351        }
352         
353
354        server = rfbGetScreen(&fake_argc, fake_argv, 
355                              xenfb->width, xenfb->height,
356                              8, 3, xenfb->depth / 8);
357        if (server == NULL) {
358                fprintf(stderr, "Could not create VNC server\n");
359                exit(1);
360        }
361
362        xenfb->user_data = server;
363        xenfb->update = vnc_update;
364
365        if (unused)
366                server->autoPort = true;
367
368        if (vncpasswd[0]) {
369                char **passwds = malloc(sizeof(char**)*2);
370                if (!passwds) {
371                        fprintf(stderr, "cannot allocate memory (%s)\n",
372                                strerror(errno));
373                        exit(1);
374                }
375                fprintf(stderr, "Registered password\n");
376                passwds[0] = vncpasswd;
377                passwds[1] = NULL;
378
379                server->authPasswdData = passwds;
380                server->passwordCheck = rfbCheckPasswordByList;
381        } else {
382                fprintf(stderr, "Running with no password\n");
383        }
384        server->serverFormat.redShift = 16;
385        server->serverFormat.greenShift = 8;
386        server->serverFormat.blueShift = 0;
387        server->kbdAddEvent = on_kbd_event;
388        server->ptrAddEvent = on_ptr_event;
389        server->frameBuffer = xenfb->pixels;
390        server->screenData = xenfb;
391        server->cursor = NULL;
392        rfbInitServer(server);
393
394        rfbRunEventLoop(server, -1, true);
395
396        xenstore_write_vncport(xsh, server->port, domid);
397
398        for (;;) {
399                FD_ZERO(&readfds);
400                nfds = xenfb_select_fds(xenfb, &readfds);
401
402                if (select(nfds, &readfds, NULL, NULL, NULL) < 0) {
403                        if (errno == EINTR)
404                                continue;
405                        fprintf(stderr,
406                                "Can't select() on event channel (%s)\n",
407                                strerror(errno));
408                        break;
409                }
410
411                r = xenfb_poll(xenfb, &readfds);
412                if (r == -2)
413                    xenfb_teardown(xenfb);
414                if (r < 0)
415                    break;
416        }
417
418        rfbScreenCleanup(server);
419        xenfb_delete(xenfb);
420
421        return 0;
422}
Note: See TracBrowser for help on using the repository browser.