2 * Copyright (c) 2010 ARM Limited
5 * The license below extends only to copyright in the software and shall
6 * not be construed as granting a license to any other intellectual
7 * property including but not limited to intellectual property relating
8 * to a hardware implementation of the functionality of the software
9 * licensed hereunder. You may use the software subject to the license
10 * terms below provided that you ensure that this notice is replicated
11 * unmodified and in its entirety in all distributions of the software,
12 * modified or unmodified, in source code or in binary form.
14 * Redistribution and use in source and binary forms, with or without
15 * modification, are permitted provided that the following conditions are
16 * met: redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer;
18 * redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution;
21 * neither the name of the copyright holders nor the names of its
22 * contributors may be used to endorse or promote products derived from
23 * this software without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42 * Implementiation of a VNC server
45 #include <sys/ioctl.h>
47 #include <sys/termios.h>
48 #include <sys/types.h>
56 #include "base/vnc/vncserver.hh"
57 #include "base/atomicio.hh"
58 #include "base/bitmap.hh"
59 #include "base/misc.hh"
60 #include "base/output.hh"
61 #include "base/socket.hh"
62 #include "base/trace.hh"
63 #include "debug/VNC.hh"
64 #include "sim/byteswap.hh"
65 #include "sim/core.hh"
70 * Poll event for the listen socket
72 VncServer::ListenEvent::ListenEvent(VncServer
*vs
, int fd
, int e
)
73 : PollEvent(fd
, e
), vncserver(vs
)
78 VncServer::ListenEvent::process(int revent
)
84 * Poll event for the data socket
86 VncServer::DataEvent::DataEvent(VncServer
*vs
, int fd
, int e
)
87 : PollEvent(fd
, e
), vncserver(vs
)
92 VncServer::DataEvent::process(int revent
)
96 else if (revent
& POLLNVAL
)
103 VncServer::VncServer(const Params
*p
)
104 : SimObject(p
), listenEvent(NULL
), dataEvent(NULL
), number(p
->number
),
105 dataFd(-1), _videoWidth(1), _videoHeight(1), clientRfb(0), keyboard(NULL
),
106 mouse(NULL
), sendUpdate(false), videoMode(VideoConvert::UnknownMode
),
107 vc(NULL
), captureEnabled(p
->frame_capture
), captureCurrentFrame(0),
108 captureLastHash(0), captureBitmap(0)
113 curState
= WaitForProtocolVersion
;
115 // currently we only support this one pixel format
116 // unpacked 32bit rgb (rgb888 + 8 bits of nothing/alpha)
117 // keep it around for telling the client and making
118 // sure the client cooperates
119 pixelFormat
.bpp
= 32;
120 pixelFormat
.depth
= 24;
121 pixelFormat
.bigendian
= 0;
122 pixelFormat
.truecolor
= 1;
123 pixelFormat
.redmax
= 0xff;
124 pixelFormat
.greenmax
= 0xff;
125 pixelFormat
.bluemax
= 0xff;
126 pixelFormat
.redshift
= 16;
127 pixelFormat
.greenshift
= 8;
128 pixelFormat
.blueshift
= 0;
130 if (captureEnabled
) {
131 // remove existing frame output directory if it exists, then create a
132 // clean empty directory
133 const string FRAME_OUTPUT_SUBDIR
= "frames_" + name();
134 simout
.remove(FRAME_OUTPUT_SUBDIR
, true);
135 captureOutputDirectory
= simout
.createSubdirectory(
136 FRAME_OUTPUT_SUBDIR
);
139 DPRINTF(VNC
, "Vnc server created at port %d\n", p
->port
);
142 VncServer::~VncServer()
155 //socket creation and vnc client attach
157 VncServer::listen(int port
)
159 if (ListenSocket::allDisabled()) {
160 warn_once("Sockets disabled, not accepting vnc client connections");
164 while (!listener
.listen(port
, true)) {
166 "can't bind address vnc server port %d in use PID %d\n",
172 p2
= name().rfind('.') - 1;
173 p1
= name().rfind('.', p2
);
174 ccprintf(cerr
, "Listening for %s connection on port %d\n",
175 name().substr(p1
+ 1, p2
- p1
), port
);
177 listenEvent
= new ListenEvent(this, listener
.getfd(), POLLIN
);
178 pollQueue
.schedule(listenEvent
);
181 // attach a vnc client
185 if (!listener
.islistening())
186 panic("%s: cannot accept a connection if not listening!", name());
188 int fd
= listener
.accept(true);
190 char message
[] = "vnc server already attached!\n";
191 atomic_write(fd
, message
, sizeof(message
));
198 // Send our version number to the client
199 write((uint8_t*)vncVersion(), strlen(vncVersion()));
201 // read the client response
202 dataEvent
= new DataEvent(this, dataFd
, POLLIN
);
203 pollQueue
.schedule(dataEvent
);
205 inform("VNC client attached\n");
208 // data called by data event
212 // We have new data, see if we can handle it
214 DPRINTF(VNC
, "Vnc client message recieved\n");
217 case WaitForProtocolVersion
:
218 checkProtocolVersion();
220 case WaitForSecurityResponse
:
223 case WaitForClientInit
:
224 // Don't care about shared, just need to read it out of the socket
229 // Send our idea of the frame buffer
234 uint8_t message_type
;
235 len
= read(&message_type
);
242 switch (message_type
) {
243 case ClientSetPixelFormat
:
246 case ClientSetEncodings
:
249 case ClientFrameBufferUpdate
:
255 case ClientPointerEvent
:
262 panic("Unimplemented message type recv from client: %d\n",
268 panic("Unknown vnc server state\n");
275 VncServer::read(uint8_t *buf
, size_t len
)
278 panic("vnc not properly attached.\n");
282 ret
= ::read(dataFd
, buf
, len
);
283 } while (ret
== -1 && errno
== EINTR
);
287 DPRINTF(VNC
, "Read failed.\n");
296 VncServer::read1(uint8_t *buf
, size_t len
)
298 size_t read_len M5_VAR_USED
;
299 read_len
= read(buf
+ 1, len
- 1);
300 assert(read_len
== len
- 1);
307 VncServer::read(T
* val
)
309 return read((uint8_t*)val
, sizeof(T
));
314 VncServer::write(const uint8_t *buf
, size_t len
)
317 panic("Vnc client not properly attached.\n");
320 ret
= atomic_write(dataFd
, buf
, len
);
330 VncServer::write(T
* val
)
332 return write((uint8_t*)val
, sizeof(T
));
336 VncServer::write(const char* str
)
338 return write((uint8_t*)str
, strlen(str
));
341 // detach a vnc client
350 if (!dataEvent
|| !dataEvent
->queued())
353 pollQueue
.remove(dataEvent
);
356 curState
= WaitForProtocolVersion
;
358 inform("VNC client detached\n");
359 DPRINTF(VNC
, "detach vnc client %d\n", number
);
363 VncServer::sendError(const char* error_msg
)
365 uint32_t len
= strlen(error_msg
);
371 VncServer::checkProtocolVersion()
373 assert(curState
== WaitForProtocolVersion
);
375 size_t len M5_VAR_USED
;
376 char version_string
[13];
378 // Null terminate the message so it's easier to work with
379 version_string
[12] = 0;
381 len
= read((uint8_t*)version_string
, 12);
384 uint32_t major
, minor
;
386 // Figure out the major/minor numbers
387 if (sscanf(version_string
, "RFB %03d.%03d\n", &major
, &minor
) != 2) {
388 warn(" Malformed protocol version %s\n", version_string
);
389 sendError("Malformed protocol version\n");
393 DPRINTF(VNC
, "Client request protocol version %d.%d\n", major
, minor
);
395 // If it's not 3.X we don't support it
396 if (major
!= 3 || minor
< 2) {
397 warn("Unsupported VNC client version... disconnecting\n");
398 uint8_t err
= AuthInvalid
;
402 // Auth is different based on version number
404 uint32_t sec_type
= htobe((uint32_t)AuthNone
);
408 uint8_t sec_type
= htobe((uint8_t)AuthNone
);
413 // Wait for client to respond
414 curState
= WaitForSecurityResponse
;
418 VncServer::checkSecurity()
420 assert(curState
== WaitForSecurityResponse
);
422 uint8_t security_type
;
423 size_t len M5_VAR_USED
= read(&security_type
);
427 if (security_type
!= AuthNone
) {
428 warn("Unknown VNC security type\n");
429 sendError("Unknown security type\n");
432 DPRINTF(VNC
, "Sending security auth OK\n");
434 uint32_t success
= htobe(VncOK
);
436 curState
= WaitForClientInit
;
440 VncServer::sendServerInit()
444 DPRINTF(VNC
, "Sending server init message to client\n");
446 msg
.fbWidth
= htobe(videoWidth());
447 msg
.fbHeight
= htobe(videoHeight());
449 msg
.px
.bpp
= htobe(pixelFormat
.bpp
);
450 msg
.px
.depth
= htobe(pixelFormat
.depth
);
451 msg
.px
.bigendian
= htobe(pixelFormat
.bigendian
);
452 msg
.px
.truecolor
= htobe(pixelFormat
.truecolor
);
453 msg
.px
.redmax
= htobe(pixelFormat
.redmax
);
454 msg
.px
.greenmax
= htobe(pixelFormat
.greenmax
);
455 msg
.px
.bluemax
= htobe(pixelFormat
.bluemax
);
456 msg
.px
.redshift
= htobe(pixelFormat
.redshift
);
457 msg
.px
.greenshift
= htobe(pixelFormat
.greenshift
);
458 msg
.px
.blueshift
= htobe(pixelFormat
.blueshift
);
459 memset(msg
.px
.padding
, 0, 3);
461 msg
.namelen
= htobe(msg
.namelen
);
462 memcpy(msg
.name
, "M5", 2);
465 curState
= NormalPhase
;
470 VncServer::setPixelFormat()
472 DPRINTF(VNC
, "Received pixel format from client message\n");
474 PixelFormatMessage pfm
;
475 read1((uint8_t*)&pfm
, sizeof(PixelFormatMessage
));
477 DPRINTF(VNC
, " -- bpp = %d; depth = %d; be = %d\n", pfm
.px
.bpp
,
478 pfm
.px
.depth
, pfm
.px
.bigendian
);
479 DPRINTF(VNC
, " -- true color = %d red,green,blue max = %d,%d,%d\n",
480 pfm
.px
.truecolor
, betoh(pfm
.px
.redmax
), betoh(pfm
.px
.greenmax
),
481 betoh(pfm
.px
.bluemax
));
482 DPRINTF(VNC
, " -- red,green,blue shift = %d,%d,%d\n", pfm
.px
.redshift
,
483 pfm
.px
.greenshift
, pfm
.px
.blueshift
);
485 if (betoh(pfm
.px
.bpp
) != pixelFormat
.bpp
||
486 betoh(pfm
.px
.depth
) != pixelFormat
.depth
||
487 betoh(pfm
.px
.bigendian
) != pixelFormat
.bigendian
||
488 betoh(pfm
.px
.truecolor
) != pixelFormat
.truecolor
||
489 betoh(pfm
.px
.redmax
) != pixelFormat
.redmax
||
490 betoh(pfm
.px
.greenmax
) != pixelFormat
.greenmax
||
491 betoh(pfm
.px
.bluemax
) != pixelFormat
.bluemax
||
492 betoh(pfm
.px
.redshift
) != pixelFormat
.redshift
||
493 betoh(pfm
.px
.greenshift
) != pixelFormat
.greenshift
||
494 betoh(pfm
.px
.blueshift
) != pixelFormat
.blueshift
)
495 fatal("VNC client doesn't support true color raw encoding\n");
499 VncServer::setEncodings()
501 DPRINTF(VNC
, "Received supported encodings from client\n");
503 PixelEncodingsMessage pem
;
504 read1((uint8_t*)&pem
, sizeof(PixelEncodingsMessage
));
506 pem
.num_encodings
= betoh(pem
.num_encodings
);
508 DPRINTF(VNC
, " -- %d encoding present\n", pem
.num_encodings
);
509 supportsRawEnc
= supportsResizeEnc
= false;
511 for (int x
= 0; x
< pem
.num_encodings
; x
++) {
513 size_t len M5_VAR_USED
;
514 len
= read(&encoding
);
515 assert(len
== sizeof(encoding
));
516 DPRINTF(VNC
, " -- supports %d\n", betoh(encoding
));
518 switch (betoh(encoding
)) {
520 supportsRawEnc
= true;
522 case EncodingDesktopSize
:
523 supportsResizeEnc
= true;
529 fatal("VNC clients must always support raw encoding\n");
533 VncServer::requestFbUpdate()
535 DPRINTF(VNC
, "Received frame buffer update request from client\n");
537 FrameBufferUpdateReq fbr
;
538 read1((uint8_t*)&fbr
, sizeof(FrameBufferUpdateReq
));
540 fbr
.x
= betoh(fbr
.x
);
541 fbr
.y
= betoh(fbr
.y
);
542 fbr
.width
= betoh(fbr
.width
);
543 fbr
.height
= betoh(fbr
.height
);
545 DPRINTF(VNC
, " -- x = %d y = %d w = %d h = %d\n", fbr
.x
, fbr
.y
, fbr
.width
,
548 sendFrameBufferUpdate();
552 VncServer::recvKeyboardInput()
554 DPRINTF(VNC
, "Received keyboard input from client\n");
556 read1((uint8_t*)&kem
, sizeof(KeyEventMessage
));
558 kem
.key
= betoh(kem
.key
);
559 DPRINTF(VNC
, " -- received key code %d (%s)\n", kem
.key
, kem
.down_flag
?
563 keyboard
->keyPress(kem
.key
, kem
.down_flag
);
567 VncServer::recvPointerInput()
569 DPRINTF(VNC
, "Received pointer input from client\n");
570 PointerEventMessage pem
;
572 read1((uint8_t*)&pem
, sizeof(PointerEventMessage
));;
574 pem
.x
= betoh(pem
.x
);
575 pem
.y
= betoh(pem
.y
);
576 DPRINTF(VNC
, " -- pointer at x = %d y = %d buttons = %#x\n", pem
.x
, pem
.y
,
580 mouse
->mouseAt(pem
.x
, pem
.y
, pem
.button_mask
);
584 VncServer::recvCutText()
586 DPRINTF(VNC
, "Received client copy buffer message\n");
588 ClientCutTextMessage cct
;
589 read1((uint8_t*)&cct
, sizeof(ClientCutTextMessage
));
592 size_t data_len
= betoh(cct
.length
);
593 DPRINTF(VNC
, "String length %d\n", data_len
);
594 while (data_len
> 0) {
596 size_t bytes_to_read
= data_len
> 1024 ? 1024 : data_len
;
597 len
= read((uint8_t*)&str
, bytes_to_read
);
598 str
[bytes_to_read
] = 0;
600 assert(data_len
>= 0);
601 DPRINTF(VNC
, "Buffer: %s\n", str
);
608 VncServer::sendFrameBufferUpdate()
611 if (!clientRfb
|| dataFd
<= 0 || curState
!= NormalPhase
|| !sendUpdate
) {
612 DPRINTF(VNC
, "NOT sending framebuffer update\n");
618 // The client will request data constantly, unless we throttle it
621 DPRINTF(VNC
, "Sending framebuffer update\n");
623 FrameBufferUpdate fbu
;
626 fbu
.type
= ServerFrameBufferUpdate
;
630 fbr
.width
= videoWidth();
631 fbr
.height
= videoHeight();
632 fbr
.encoding
= EncodingRaw
;
635 fbu
.num_rects
= htobe(fbu
.num_rects
);
636 fbr
.x
= htobe(fbr
.x
);
637 fbr
.y
= htobe(fbr
.y
);
638 fbr
.width
= htobe(fbr
.width
);
639 fbr
.height
= htobe(fbr
.height
);
640 fbr
.encoding
= htobe(fbr
.encoding
);
642 // send headers to client
648 uint8_t *tmp
= vc
->convert(clientRfb
);
649 write(tmp
, videoWidth() * videoHeight() * sizeof(uint32_t));
655 VncServer::sendFrameBufferResized()
657 assert(clientRfb
&& dataFd
> 0 && curState
== NormalPhase
);
658 DPRINTF(VNC
, "Sending framebuffer resize\n");
660 FrameBufferUpdate fbu
;
663 fbu
.type
= ServerFrameBufferUpdate
;
667 fbr
.width
= videoWidth();
668 fbr
.height
= videoHeight();
669 fbr
.encoding
= EncodingDesktopSize
;
672 fbu
.num_rects
= htobe(fbu
.num_rects
);
673 fbr
.x
= htobe(fbr
.x
);
674 fbr
.y
= htobe(fbr
.y
);
675 fbr
.width
= htobe(fbr
.width
);
676 fbr
.height
= htobe(fbr
.height
);
677 fbr
.encoding
= htobe(fbr
.encoding
);
679 // send headers to client
683 // No actual data is sent in this message
687 VncServer::setFrameBufferParams(VideoConvert::Mode mode
, int width
, int height
)
689 DPRINTF(VNC
, "Updating video params: mode: %d width: %d height: %d\n", mode
,
692 if (mode
!= videoMode
|| width
!= videoWidth() || height
!= videoHeight()) {
695 _videoHeight
= height
;
700 vc
= new VideoConvert(mode
, VideoConvert::rgb8888
, videoWidth(),
703 if (captureEnabled
) {
704 // create bitmap of the frame with new attributes
706 delete captureBitmap
;
709 captureBitmap
= new Bitmap(videoMode
, width
, height
, clientRfb
);
710 assert(captureBitmap
);
713 if (dataFd
> 0 && clientRfb
&& curState
== NormalPhase
) {
714 if (supportsResizeEnc
)
715 sendFrameBufferResized();
717 // The frame buffer changed size and we can't update the client
723 // create the VNC server object
725 VncServerParams::create()
727 return new VncServer(this);
731 VncServer::captureFrameBuffer()
733 assert(captureBitmap
);
735 // skip identical frames
736 uint64_t new_hash
= captureBitmap
->getHash();
737 if (captureLastHash
== new_hash
)
739 captureLastHash
= new_hash
;
741 // get the filename for the current frame
742 char frameFilenameBuffer
[64];
743 snprintf(frameFilenameBuffer
, 64, "fb.%06d.%lld.bmp.gz",
744 captureCurrentFrame
, static_cast<long long int>(curTick()));
745 const string
frameFilename(frameFilenameBuffer
);
747 // create the compressed framebuffer file
748 ostream
*fb_out
= simout
.create(captureOutputDirectory
+ frameFilename
,
750 captureBitmap
->write(fb_out
);
751 simout
.close(fb_out
);
753 ++captureCurrentFrame
;