12 extern Dev drawdevtab;
13 extern Dev mousedevtab;
14 extern Dev consdevtab;
24 static char *msgname[] = {
25 [MPixFmt] = "MPixFmt",
26 [MFixCmap] = "MFixCmap",
27 [MSetEnc] = "MSetEnc",
28 [MFrameReq] = "MFrameReq",
34 static char *encname[] = {
36 [EncCopyRect] = "copy rect",
39 [EncHextile] = "hextile",
41 [EncTight] = "zlibtight",
42 [EncZHextile] = "zhextile",
43 [EncMouseWarp] = "mousewarp",
48 * list head. used to hold the list, the lock, dim, and pixelfmt
62 char *pixchan = "r5g6b5";
66 static Vncs **vncpriv;
68 static int parsedisplay(char*);
69 static void vnckill(char*, int, int);
70 static int vncannounce(char *net, int display, char *adir, int base);
71 static void noteshutdown(void*, char*);
72 static void vncaccept(Vncs*);
73 static int vncsfmt(Fmt*);
74 static void getremote(char*, char*);
75 static void vncname(char*, ...);
76 #pragma varargck argpos vncname 1
78 #pragma varargck type "V" Vncs*
83 fprint(2, "usage: vncs [-v] [-c cert] [-d :display] [-g widthXheight] [-p pixelfmt] [-A] [cmd [args]...]\n");
84 fprint(2, "\tto kill a server: vncs [-v] -k :display\n");
89 main(int argc, char **argv)
91 int baseport, cfd, display, exnum, fd, pid, h, killing, w;
92 char adir[NETPATHLEN], ldir[NETPATHLEN];
93 char net[NETPATHLEN], *p;
94 char *kbdfs[] = { "/bin/aux/kbdfs", "-dq", nil };
95 char *rc[] = { "/bin/rc", "-i", nil };
98 fmtinstall('V', vncsfmt);
104 setnetmtpt(net, sizeof net, nil);
109 cert = EARGF(usage());
115 display = parsedisplay(EARGF(usage()));
119 w = strtol(p, &p, 10);
120 if(*p != 'x' && *p != 'X' && *p != ' ' && *p != ' ')
122 h = strtol(p+1, &p, 10);
129 display = parsedisplay(EARGF(usage()));
133 pixchan = EARGF(usage());
137 sleeptime = atoi(EARGF(usage()));
145 setnetmtpt(net, sizeof net, p);
153 vnckill(net, display, baseport);
161 if(access(argv[0], AEXEC) < 0)
162 sysfatal("access %s for exec: %r", argv[0]);
164 /* background ourselves */
165 switch(rfork(RFPROC|RFNAMEG|RFFDG|RFNOTEG)){
167 sysfatal("rfork: %r");
174 vncpriv = privalloc();
176 sysfatal("privalloc: %r");
181 sysfatal("screeninit %dx%d %s: %s", w, h, pixchan, up->error);
183 fprint(2, "geometry is %dx%d\n", w, h);
184 screeninit(w, h, pixchan);
187 /* start file system device slaves */
188 exnum = exporter(devtab, &fd, &exportfd);
190 /* rebuild /dev because the underlying connection might go away (ick) */
191 unmount(nil, "/dev");
192 bind("#c", "/dev", MREPL);
195 if(mounter("/dev", MBEFORE, fd, exnum) < 0)
196 sysfatal("mounter: %r");
199 pid = rfork(RFPROC|RFMEM|RFFDG|RFNOTEG);
202 sysfatal("rfork: %r");
208 open("/dev/cons", OWRITE);
210 open("/dev/cons", OWRITE);
212 /* start and mount kbdfs */
213 pid = rfork(RFPROC|RFMEM|RFFDG|RFREND);
216 sysfatal("rfork: %r");
219 exec(kbdfs[0], kbdfs);
220 fprint(2, "exec %s: %r\n", kbdfs[0]);
223 if(waitpid() != pid){
224 rendezvous(&kbdin, nil);
225 sysfatal("waitpid: %s: %r", kbdfs[0]);
227 rendezvous(&kbdin, nil);
229 rfork(RFNAMEG|RFREND);
232 open("/dev/cons", OREAD);
234 open("/dev/cons", OWRITE);
236 open("/dev/cons", OWRITE);
239 fprint(2, "exec %s: %r\n", argv[0]);
244 /* wait for kbdfs to get mounted */
245 rendezvous(&kbdin, nil);
246 if((kbdin = open("/dev/kbdin", OWRITE)) < 0)
247 sysfatal("open /dev/kbdin: %r");
249 /* run the service */
250 srvfd = vncannounce(net, display, adir, baseport);
252 sysfatal("announce failed");
254 fprint(2, "announced in %s\n", adir);
257 notify(noteshutdown);
260 cfd = listen(adir, ldir);
264 fprint(2, "call in %s\n", ldir);
265 fd = accept(cfd, ldir);
270 v = mallocz(sizeof(Vncs), 1);
280 getremote(ldir, v->remote);
281 strcpy(v->netpath, ldir);
283 v->next = clients.head;
292 parsedisplay(char *p)
300 n = strtol(p+1, &p, 10);
307 getremote(char *ldir, char *remote)
309 char buf[NETPATHLEN];
312 snprint(buf, sizeof buf, "%s/remote", ldir);
313 strcpy(remote, "<none>");
314 if((fd = open(buf, OREAD)) < 0)
316 n = readn(fd, remote, NETPATHLEN-1);
321 if(n>0 && remote[n-1] == '\n')
330 v = va_arg(fmt->args, Vncs*);
331 return fmtprint(fmt, "[%d] %s %s", getpid(), v->remote, v->netpath);
335 * We register exiting as an atexit handler in each proc, so that
336 * client procs need merely exit when something goes wrong.
343 /* remove self from client list if there */
345 for(l=&clients.head; *l; l=&(*l)->next)
352 /* if last proc, free v */
354 if(++v->ndead < v->nproc){
359 freerlist(&v->rlist);
366 freememimage(v->image);
380 fprint(2, "%V: hangup\n", (Vncs*)v);
381 exits(0); /* atexit and exiting() will take care of everything */
385 * Kill all clients except safe.
386 * Used to start a non-shared client and at shutdown.
389 killclients(Vncs *safe)
394 for(v=clients.head; v; v=v->next){
409 * Kill the executing command and then kill everyone else.
410 * Called to close up shop at the end of the day
411 * and also if we get an unexpected note.
413 static char killkin[] = "die vnc kin";
417 postnote(PNGROUP, cmdpid, "hangup");
424 postnote(PNGROUP, getpid(), killkin);
431 fprint(2, "vnc server shutdown\n");
436 noteshutdown(void*, char *msg)
438 if(strcmp(msg, killkin) == 0) /* already shutting down */
445 * Kill a specific instance of a server.
448 vnckill(char *net, int display, int baseport)
451 char buf[NETPATHLEN], *p;
454 snprint(buf, sizeof buf, "%s/tcp/%d/local", net, i);
455 if((fd = open(buf, OREAD)) < 0)
456 sysfatal("did not find display");
457 n = read(fd, buf, sizeof buf-1);
462 p = strchr(buf, '!');
466 if(port != display+baseport)
468 snprint(buf, sizeof buf, "%s/tcp/%d/ctl", net, i);
469 fd = open(buf, OWRITE);
471 sysfatal("cannot open %s: %r", buf);
472 if(write(fd, "hangup", 6) != 6)
473 sysfatal("cannot hangup %s: %r", buf);
480 * Look for a port on which to announce.
481 * If display != -1, we only try that one.
484 * Returns the announce fd.
487 vncannounce(char *net, int display, char *adir, int base)
490 char addr[NETPATHLEN];
500 for(; port<=eport; port++){
501 snprint(addr, sizeof addr, "%s/tcp!*!%d", net, port);
502 if((fd = announce(addr, adir)) >= 0){
503 fprint(2, "server started on display :%d\n", port-base);
508 fprint(2, "could not find any ports to announce\n");
510 fprint(2, "announce: %r\n");
515 * Handle a new connection.
517 static void clientreadproc(Vncs*);
518 static void clientwriteproc(Vncs*);
519 static void chan2fmt(Pixfmt*, ulong);
520 static ulong fmt2chan(Pixfmt*);
528 /* caller returns to listen */
529 switch(rfork(RFPROC|RFMEM|RFNAMEG)){
531 fprint(2, "%V: fork failed: %r\n", v);
541 if(!atexit(exiting)){
542 fprint(2, "%V: could not register atexit handler: %r; hanging up\n", v);
550 memset(&conn, 0, sizeof conn);
551 conn.cert = readcert(cert, &conn.certlen);
552 if(conn.cert == nil){
553 fprint(2, "%V: could not read cert %s: %r; hanging up\n", v, cert);
556 fd = tlsServer(v->datafd, &conn);
558 fprint(2, "%V: tlsServer: %r; hanging up\n", v);
560 free(conn.sessionID);
565 free(conn.sessionID);
567 vncinit(v->datafd, v->ctlfd, v);
570 fprint(2, "%V: handshake\n", v);
571 if(vncsrvhandshake(v) < 0){
572 fprint(2, "%V: handshake failed; hanging up\n", v);
578 fprint(2, "%V: noauth\n", v);
579 vncwrlong(v, ANoAuth);
583 fprint(2, "%V: auth\n", v);
584 if(vncsrvauth(v) < 0){
585 fprint(2, "%V: auth failed; hanging up\n", v);
590 shared = vncrdchar(v);
593 fprint(2, "%V: %sshared\n", v, shared ? "" : "not ");
597 v->dim = (Point){Dx(gscreen->r), Dy(gscreen->r)};
598 vncwrpoint(v, v->dim);
600 fprint(2, "%V: send screen size %P (rect %R)\n", v, v->dim, gscreen->r);
602 v->bpp = gscreen->depth;
603 v->depth = gscreen->depth;
606 chan2fmt(v, gscreen->chan);
608 fprint(2, "%V: bpp=%d, depth=%d, chan=%s\n", v,
609 v->bpp, v->depth, chantostr(buf, gscreen->chan));
612 vncwrbytes(v, "Plan9 Desktop", 14);
616 fprint(2, "%V: handshaking done\n", v);
618 switch(rfork(RFPROC|RFMEM)){
620 fprint(2, "%V: cannot fork: %r; hanging up\n", v);
628 if(atexit(exiting) == 0){
630 fprint(2, "%V: could not register atexit handler: %r; hanging up\n", v);
639 vncname(char *fmt, ...)
642 char name[64], buf[32];
646 vsnprint(name, sizeof name, fmt, arg);
649 sprint(buf, "/proc/%d/args", getpid());
650 if((fd = open(buf, OWRITE)) >= 0){
651 write(fd, name, strlen(name));
657 * Set the pixel format being sent. Can only happen once.
658 * (Maybe a client would send this again if the screen changed
659 * underneath it? If we want to support this we need a way to
660 * make sure the current image is no longer in use, so we can free it.
668 v->Pixfmt = vncrdpixfmt(v);
671 fprint(2, "%V: bad pixel format; hanging up\n", v);
678 * Set the preferred encoding list. Can only happen once.
679 * If we want to support changing this more than once then
680 * we need to put a lock around the encoding functions
681 * so as not to conflict with updateimage.
700 if(v->countrect != nil)
705 v->countrect = countraw;
706 v->sendrect = sendraw;
710 v->countrect = countrre;
711 v->sendrect = sendrre;
714 v->encname = "corre";
715 v->countrect = countcorre;
716 v->sendrect = sendcorre;
719 v->encname = "hextile";
720 v->countrect = counthextile;
721 v->sendrect = sendhextile;
726 if(v->countrect == nil){
728 v->countrect = countraw;
729 v->sendrect = sendraw;
733 fprint(2, "Encoding with %s%s%s\n", v->encname,
734 v->copyrect ? ", copyrect" : "",
735 v->canwarp ? ", canwarp" : "");
739 * Continually read updates from one client.
742 clientreadproc(Vncs *v)
744 int incremental, key, keydown, buttons, type, x, y, n;
748 vncname("read %V", v);
754 fprint(2, "%V: unknown vnc message type %d; hanging up\n", v, type);
757 /* set pixel format */
762 /* ignore color map changes */
769 /* set encoding list */
774 /* request image update in rectangle */
776 incremental = vncrdchar(v);
780 v->updaterequest = 1;
783 drawlock(); /* protects rlist */
784 vnclock(v); /* protects updaterequest */
785 v->updaterequest = 1;
786 addtorlist(&v->rlist, r);
794 keydown = vncrdchar(v);
797 vncputc(!keydown, key);
800 /* send mouse event */
802 buttons = vncrdchar(v);
805 mousetrack(x, y, buttons, nsec()/(1000*1000LL));
814 vncrdbytes(v, buf, n);
816 vnclock(v); /* for snarfvers */
817 setsnarf(buf, n, &v->snarfvers);
832 for(; mask; mask>>=1)
837 typedef struct Col Col;
845 fmt2chan(Pixfmt *fmt)
848 int i, j, depth, n, nc;
851 /* unpack the Pixfmt channels */
852 c[0] = (Col){CRed, nbits(fmt->red.max), fmt->red.shift};
853 c[1] = (Col){CGreen, nbits(fmt->green.max), fmt->green.shift};
854 c[2] = (Col){CBlue, nbits(fmt->blue.max), fmt->blue.shift};
857 /* add an ignore channel if necessary */
858 depth = c[0].nbits+c[1].nbits+c[2].nbits;
859 if(fmt->bpp != depth){
860 /* BUG: assumes only one run of ignored bits */
864 mask = (1<<fmt->bpp)-1;
865 mask ^= fmt->red.max << fmt->red.shift;
866 mask ^= fmt->green.max << fmt->green.shift;
867 mask ^= fmt->blue.max << fmt->blue.shift;
871 for(; !(mask&1); mask>>=1)
873 c[3] = (Col){CIgnore, nbits(mask), n};
877 /* sort the channels, largest shift (leftmost bits) first */
880 if(c[j].shift > c[j-1].shift){
886 /* build the channel descriptor */
890 u |= CHAN1(c[i].type, c[i].nbits);
897 chan2fmt(Pixfmt *fmt, ulong chan)
902 for(rc = chan; rc; rc >>=8){
906 fmt->red = (Colorfmt){(1<<NBITS(c))-1, shift};
909 fmt->blue = (Colorfmt){(1<<NBITS(c))-1, shift};
912 fmt->green = (Colorfmt){(1<<NBITS(c))-1, shift};
920 * Note that r has changed on the screen.
921 * Updating the rlists is okay because they are protected by drawlock.
924 flushmemscreen(Rectangle r)
928 if(!rectclip(&r, gscreen->r))
931 for(v=clients.head; v; v=v->next)
932 addtorlist(&v->rlist, r);
937 * Queue a mouse warp note for the next update to each client.
940 mousewarpnote(Point p)
945 for(v=clients.head; v; v=v->next){
957 * Send a client his changed screen image.
958 * v is locked on entrance, locked on exit, but released during.
963 int i, ncount, nsend, docursor, needwarp;
969 int (*count)(Vncs*, Rectangle);
970 int (*send)(Vncs*, Rectangle);
975 /* warping info and unlock v so that updates can proceed */
976 needwarp = v->canwarp && v->needwarp;
981 /* copy the screen bits and then unlock the screen so updates can proceed */
984 memset(&v->rlist, 0, sizeof v->rlist);
986 /* if the cursor has moved or changed shape, we need to redraw its square */
988 if(v->cursorver != cursorver || !eqpt(v->cursorpos, cursorpos)){
990 v->cursorver = cursorver;
991 v->cursorpos = cursorpos;
1000 addtorlist(&rlist, v->cursorr);
1001 if(!rectclip(&cr, gscreen->r))
1003 addtorlist(&rlist, cr);
1006 /* copy changed screen parts, also check for parts overlapping cursor location */
1007 for(i=0; i<rlist.nrect; i++){
1009 docursor = rectXrect(v->cursorr, rlist.rect[i]);
1010 memimagedraw(v->image, rlist.rect[i], gscreen, rlist.rect[i].min, memopaque, ZP, S);
1014 cursordraw(v->image, cr);
1015 addtorlist(&rlist, v->cursorr);
1021 ooffset = Boffset(&v->out);
1022 /* no more locks are held; talk to the client */
1024 if(rlist.nrect == 0 && needwarp == 0){
1029 count = v->countrect;
1031 if(count == nil || send == nil){
1037 for(i=0; i<rlist.nrect; i++)
1038 ncount += (*count)(v, rlist.rect[i]);
1041 fprint(2, "sendupdate: rlist.nrect=%d, ncount=%d", rlist.nrect, ncount);
1044 vncwrchar(v, MFrameUpdate);
1046 vncwrshort(v, ncount+needwarp);
1049 for(i=0; i<rlist.nrect; i++)
1050 nsend += (*send)(v, rlist.rect[i]);
1052 if(ncount != nsend){
1053 fprint(2, "%V: ncount=%d, nsend=%d; hanging up\n", v, ncount, nsend);
1058 vncwrrect(v, Rect(warppt.x, warppt.y, warppt.x+1, warppt.y+1));
1059 vncwrlong(v, EncMouseWarp);
1064 fprint(2, " in %lldms, %lld bytes\n", t1/1000000, Boffset(&v->out) - ooffset);
1072 * Update the snarf buffer if it has changed.
1075 updatesnarf(Vncs *v)
1080 if(v->snarfvers == snarf.vers)
1091 memmove(buf, snarf.buf, len);
1092 v->snarfvers = snarf.vers;
1095 vncwrchar(v, MSCut);
1096 vncwrbytes(v, "pad", 3);
1098 vncwrbytes(v, buf, len);
1104 * Continually update one client.
1107 clientwriteproc(Vncs *v)
1109 char buf[32], buf2[32];
1112 vncname("write %V", v);
1117 if((v->image == nil && v->imagechan!=0)
1118 || (v->image && v->image->chan != v->imagechan)){
1120 freememimage(v->image);
1121 v->image = allocmemimage(Rpt(ZP, v->dim), v->imagechan);
1122 if(v->image == nil){
1123 fprint(2, "%V: allocmemimage: %r; hanging up\n", v);
1127 fprint(2, "%V: translating image from chan=%s to chan=%s\n",
1128 v, chantostr(buf, gscreen->chan), chantostr(buf2, v->imagechan));
1131 if(v->updaterequest){
1132 v->updaterequest = 0;
1134 sent = updateimage(v);
1136 v->updaterequest = 1;