12 extern Dev drawdevtab;
13 extern Dev mousedevtab;
14 extern Dev consdevtab;
25 * list head. used to hold the list, the lock, dim, and pixelfmt
39 char *pixchan = "r5g6b5";
43 static Vncs **vncpriv;
45 static int parsedisplay(char*);
46 static void vnckill(char*, int, int);
47 static int vncannounce(char *net, int display, char *adir, int base);
48 static void noteshutdown(void*, char*);
49 static void vncaccept(Vncs*);
50 static int vncsfmt(Fmt*);
51 static void getremote(char*, char*);
52 static void vncname(char*, ...);
53 #pragma varargck argpos vncname 1
55 #pragma varargck type "V" Vncs*
60 fprint(2, "usage: vncs [-v] [-c cert] [-d :display] [-g widthXheight] [-p pixelfmt] [-A] [cmd [args]...]\n");
61 fprint(2, "\tto kill a server: vncs [-v] -k :display\n");
66 main(int argc, char **argv)
68 int baseport, cfd, display, exnum, fd, pid, h, killing, w;
69 char adir[NETPATHLEN], ldir[NETPATHLEN];
70 char net[NETPATHLEN], *p;
71 char *kbdfs[] = { "/bin/aux/kbdfs", "-dq", nil };
72 char *rc[] = { "/bin/rc", "-i", nil };
75 fmtinstall('V', vncsfmt);
81 setnetmtpt(net, sizeof net, nil);
86 cert = EARGF(usage());
92 display = parsedisplay(EARGF(usage()));
96 w = strtol(p, &p, 10);
97 if(*p != 'x' && *p != 'X' && *p != ' ' && *p != ' ')
99 h = strtol(p+1, &p, 10);
106 display = parsedisplay(EARGF(usage()));
110 pixchan = EARGF(usage());
114 sleeptime = atoi(EARGF(usage()));
122 setnetmtpt(net, sizeof net, p);
130 vnckill(net, display, baseport);
138 if(access(argv[0], AEXEC) < 0)
139 sysfatal("access %s for exec: %r", argv[0]);
141 /* background ourselves */
142 switch(rfork(RFPROC|RFNAMEG|RFFDG|RFNOTEG)){
144 sysfatal("rfork: %r");
151 vncpriv = privalloc();
153 sysfatal("privalloc: %r");
158 sysfatal("screeninit %dx%d %s: %s", w, h, pixchan, up->error);
160 fprint(2, "geometry is %dx%d\n", w, h);
161 screeninit(w, h, pixchan);
164 /* start file system device slaves */
165 exnum = exporter(devtab, &fd, &exportfd);
167 /* rebuild /dev because the underlying connection might go away (ick) */
168 unmount(nil, "/dev");
169 bind("#c", "/dev", MREPL);
172 if(mounter("/dev", MBEFORE, fd, exnum) < 0)
173 sysfatal("mounter: %r");
176 pid = rfork(RFPROC|RFMEM|RFFDG|RFNOTEG);
179 sysfatal("rfork: %r");
185 open("/dev/cons", OWRITE);
187 open("/dev/cons", OWRITE);
189 /* start and mount kbdfs */
190 pid = rfork(RFPROC|RFMEM|RFFDG|RFREND);
193 sysfatal("rfork: %r");
196 exec(kbdfs[0], kbdfs);
197 fprint(2, "exec %s: %r\n", kbdfs[0]);
200 if(waitpid() != pid){
201 rendezvous(&kbdin, nil);
202 sysfatal("waitpid: %s: %r", kbdfs[0]);
204 rendezvous(&kbdin, nil);
206 rfork(RFNAMEG|RFREND);
209 open("/dev/cons", OREAD);
211 open("/dev/cons", OWRITE);
213 open("/dev/cons", OWRITE);
216 fprint(2, "exec %s: %r\n", argv[0]);
221 /* wait for kbdfs to get mounted */
222 rendezvous(&kbdin, nil);
223 if((kbdin = open("/dev/kbdin", OWRITE)) < 0)
224 sysfatal("open /dev/kbdin: %r");
226 /* run the service */
227 srvfd = vncannounce(net, display, adir, baseport);
229 sysfatal("announce failed");
231 fprint(2, "announced in %s\n", adir);
234 notify(noteshutdown);
237 cfd = listen(adir, ldir);
241 fprint(2, "call in %s\n", ldir);
242 fd = accept(cfd, ldir);
247 v = mallocz(sizeof(Vncs), 1);
257 getremote(ldir, v->remote);
258 strcpy(v->netpath, ldir);
260 v->next = clients.head;
269 parsedisplay(char *p)
277 n = strtol(p+1, &p, 10);
284 getremote(char *ldir, char *remote)
286 char buf[NETPATHLEN];
289 snprint(buf, sizeof buf, "%s/remote", ldir);
290 strcpy(remote, "<none>");
291 if((fd = open(buf, OREAD)) < 0)
293 n = readn(fd, remote, NETPATHLEN-1);
298 if(n>0 && remote[n-1] == '\n')
307 v = va_arg(fmt->args, Vncs*);
308 return fmtprint(fmt, "[%d] %s %s", getpid(), v->remote, v->netpath);
312 * We register exiting as an atexit handler in each proc, so that
313 * client procs need merely exit when something goes wrong.
320 /* remove self from client list if there */
322 for(l=&clients.head; *l; l=&(*l)->next)
329 /* if last proc, free v */
331 if(++v->ndead < v->nproc){
336 freerlist(&v->rlist);
343 freememimage(v->image);
357 fprint(2, "%V: hangup\n", (Vncs*)v);
358 exits(0); /* atexit and exiting() will take care of everything */
362 * Kill all clients except safe.
363 * Used to start a non-shared client and at shutdown.
366 killclients(Vncs *safe)
371 for(v=clients.head; v; v=v->next){
386 * Kill the executing command and then kill everyone else.
387 * Called to close up shop at the end of the day
388 * and also if we get an unexpected note.
390 static char killkin[] = "die vnc kin";
394 postnote(PNGROUP, cmdpid, "hangup");
401 postnote(PNGROUP, getpid(), killkin);
408 fprint(2, "vnc server shutdown\n");
413 noteshutdown(void*, char *msg)
415 if(strcmp(msg, killkin) == 0) /* already shutting down */
422 * Kill a specific instance of a server.
425 vnckill(char *net, int display, int baseport)
428 char buf[NETPATHLEN], *p;
431 snprint(buf, sizeof buf, "%s/tcp/%d/local", net, i);
432 if((fd = open(buf, OREAD)) < 0)
433 sysfatal("did not find display");
434 n = read(fd, buf, sizeof buf-1);
439 p = strchr(buf, '!');
443 if(port != display+baseport)
445 snprint(buf, sizeof buf, "%s/tcp/%d/ctl", net, i);
446 fd = open(buf, OWRITE);
448 sysfatal("cannot open %s: %r", buf);
449 if(write(fd, "hangup", 6) != 6)
450 sysfatal("cannot hangup %s: %r", buf);
457 * Look for a port on which to announce.
458 * If display != -1, we only try that one.
461 * Returns the announce fd.
464 vncannounce(char *net, int display, char *adir, int base)
467 char addr[NETPATHLEN];
477 for(; port<=eport; port++){
478 snprint(addr, sizeof addr, "%s/tcp!*!%d", net, port);
479 if((fd = announce(addr, adir)) >= 0){
480 fprint(2, "server started on display :%d\n", port-base);
485 fprint(2, "could not find any ports to announce\n");
487 fprint(2, "announce: %r\n");
492 * Handle a new connection.
494 static void clientreadproc(Vncs*);
495 static void clientwriteproc(Vncs*);
496 static void chan2fmt(Pixfmt*, ulong);
497 static ulong fmt2chan(Pixfmt*);
505 /* caller returns to listen */
506 switch(rfork(RFPROC|RFMEM|RFNAMEG)){
508 fprint(2, "%V: fork failed: %r\n", v);
518 if(!atexit(exiting)){
519 fprint(2, "%V: could not register atexit handler: %r; hanging up\n", v);
527 memset(&conn, 0, sizeof conn);
528 conn.cert = readcert(cert, &conn.certlen);
529 if(conn.cert == nil){
530 fprint(2, "%V: could not read cert %s: %r; hanging up\n", v, cert);
533 fd = tlsServer(v->datafd, &conn);
535 fprint(2, "%V: tlsServer: %r; hanging up\n", v);
537 free(conn.sessionID);
542 free(conn.sessionID);
544 vncinit(v->datafd, v->ctlfd, v);
547 fprint(2, "%V: handshake\n", v);
548 if(vncsrvhandshake(v) < 0){
549 fprint(2, "%V: handshake failed; hanging up\n", v);
555 fprint(2, "%V: noauth\n", v);
556 vncwrlong(v, ANoAuth);
560 fprint(2, "%V: auth\n", v);
561 if(vncsrvauth(v) < 0){
562 fprint(2, "%V: auth failed; hanging up\n", v);
567 shared = vncrdchar(v);
570 fprint(2, "%V: %sshared\n", v, shared ? "" : "not ");
574 v->dim = rectsubpt(gscreen->clipr, gscreen->clipr.min);
575 vncwrpoint(v, v->dim.max);
577 fprint(2, "%V: send screen size %R\n", v, v->dim);
579 v->bpp = gscreen->depth;
580 v->depth = gscreen->depth;
583 chan2fmt(v, gscreen->chan);
585 fprint(2, "%V: bpp=%d, depth=%d, chan=%s\n", v,
586 v->bpp, v->depth, chantostr(buf, gscreen->chan));
589 vncwrbytes(v, "Plan9 Desktop", 14);
593 fprint(2, "%V: handshaking done\n", v);
597 switch(rfork(RFPROC|RFMEM)){
599 fprint(2, "%V: cannot fork: %r; hanging up\n", v);
607 if(atexit(exiting) == 0){
609 fprint(2, "%V: could not register atexit handler: %r; hanging up\n", v);
618 vncname(char *fmt, ...)
621 char name[64], buf[32];
625 vsnprint(name, sizeof name, fmt, arg);
628 sprint(buf, "/proc/%d/args", getpid());
629 if((fd = open(buf, OWRITE)) >= 0){
630 write(fd, name, strlen(name));
636 * Set the pixel format being sent. Can only happen once.
637 * (Maybe a client would send this again if the screen changed
638 * underneath it? If we want to support this we need a way to
639 * make sure the current image is no longer in use, so we can free it.
647 v->Pixfmt = vncrdpixfmt(v);
650 fprint(2, "%V: bad pixel format; hanging up\n", v);
657 * Set the preferred encoding list. Can only happen once.
658 * If we want to support changing this more than once then
659 * we need to put a lock around the encoding functions
660 * so as not to conflict with updateimage.
681 case EncXDesktopSize:
685 if(v->countrect != nil)
690 v->countrect = countraw;
691 v->sendrect = sendraw;
695 v->countrect = countrre;
696 v->sendrect = sendrre;
699 v->encname = "corre";
700 v->countrect = countcorre;
701 v->sendrect = sendcorre;
704 v->encname = "hextile";
705 v->countrect = counthextile;
706 v->sendrect = sendhextile;
711 if(v->countrect == nil){
713 v->countrect = countraw;
714 v->sendrect = sendraw;
718 fprint(2, "Encoding with %s%s%s%s\n", v->encname,
719 v->copyrect ? ", copyrect" : "",
720 v->canwarp ? ", canwarp" : "",
721 v->canresize ? ", resize" : "");
725 * Continually read updates from one client.
728 clientreadproc(Vncs *v)
730 int incremental, key, keydown, buttons, type, x, y, n;
734 vncname("read %V", v);
740 fprint(2, "%V: unknown vnc message type %d; hanging up\n", v, type);
743 /* set pixel format */
748 /* ignore color map changes */
755 /* set encoding list */
760 /* request image update in rectangle */
762 incremental = vncrdchar(v);
765 qlock(&drawlock); /* protects rlist */
766 addtorlist(&v->rlist, r);
772 case MSetDesktopSize:
774 vncrdpoint(v); // desktop size
781 vncrdlong(v); // flags
788 if(!rectclip(&r, gscreen->r)){
802 keydown = vncrdchar(v);
805 vncputc(!keydown, key);
808 /* send mouse event */
810 buttons = vncrdchar(v);
813 absmousetrack(x, y, buttons, nsec()/(1000*1000LL));
822 vncrdbytes(v, buf, n);
824 vnclock(v); /* for snarfvers */
825 setsnarf(buf, n, &v->snarfvers);
840 for(; mask; mask>>=1)
845 typedef struct Col Col;
853 fmt2chan(Pixfmt *fmt)
856 int i, j, depth, n, nc;
859 /* unpack the Pixfmt channels */
860 c[0] = (Col){CRed, nbits(fmt->red.max), fmt->red.shift};
861 c[1] = (Col){CGreen, nbits(fmt->green.max), fmt->green.shift};
862 c[2] = (Col){CBlue, nbits(fmt->blue.max), fmt->blue.shift};
865 /* add an ignore channel if necessary */
866 depth = c[0].nbits+c[1].nbits+c[2].nbits;
867 if(fmt->bpp != depth){
868 /* BUG: assumes only one run of ignored bits */
872 mask = (1<<fmt->bpp)-1;
873 mask ^= fmt->red.max << fmt->red.shift;
874 mask ^= fmt->green.max << fmt->green.shift;
875 mask ^= fmt->blue.max << fmt->blue.shift;
879 for(; !(mask&1); mask>>=1)
881 c[3] = (Col){CIgnore, nbits(mask), n};
885 /* sort the channels, largest shift (leftmost bits) first */
888 if(c[j].shift > c[j-1].shift){
894 /* build the channel descriptor */
898 u |= CHAN1(c[i].type, c[i].nbits);
905 chan2fmt(Pixfmt *fmt, ulong chan)
910 for(rc = chan; rc; rc >>=8){
914 fmt->red = (Colorfmt){(1<<NBITS(c))-1, shift};
917 fmt->blue = (Colorfmt){(1<<NBITS(c))-1, shift};
920 fmt->green = (Colorfmt){(1<<NBITS(c))-1, shift};
928 * Note that r has changed on the screen.
929 * Updating the rlists is okay because they are protected by drawlock.
932 flushmemscreen(Rectangle r)
936 if(!rectclip(&r, gscreen->clipr))
939 for(v=clients.head; v; v=v->next)
940 addtorlist(&v->rlist, r);
945 * Queue a mouse warp note for the next update to each client.
948 mousewarpnote(Point p)
953 for(v=clients.head; v; v=v->next){
965 * Send a client his changed screen image.
966 * v is locked on entrance, locked on exit, but released during.
971 int i, j, ncount, nsend, docursor, dowarp, doresize;
972 vlong ooffset = 0, t1 = 0;
976 int (*count)(Vncs*, Rectangle);
977 int (*send)(Vncs*, Rectangle);
980 dowarp = v->canwarp && v->dowarp;
985 /* copy the screen bits and then unlock the screen so updates can proceed */
988 memset(&v->rlist, 0, sizeof v->rlist);
990 if(v->canresize && !eqrect(v->screen[0].rect, gscreen->clipr)){
991 v->screen[0].rect = gscreen->clipr;
992 v->dim = rectsubpt(gscreen->clipr, gscreen->clipr.min);
998 || (v->image == nil && v->imagechan != 0)
999 || (v->image != nil && v->image->chan != v->imagechan)){
1001 freememimage(v->image);
1002 v->image = allocmemimage(v->dim, v->imagechan);
1003 if(v->image == nil){
1004 fprint(2, "%V: allocmemimage: %r; hanging up\n", v);
1010 /* if the cursor has moved or changed shape, we need to redraw its square */
1012 if(v->cursorver != cursorver || !eqpt(v->cursorpos, cursorpos)){
1014 v->cursorver = cursorver;
1015 v->cursorpos = cursorpos;
1024 addtorlist(&rlist, v->cursorr);
1025 if(!rectclip(&cr, gscreen->clipr))
1027 addtorlist(&rlist, cr);
1030 /* copy changed screen parts, also check for parts overlapping cursor location */
1031 for(i=0; i<rlist.nrect; i++){
1033 docursor = rectXrect(v->cursorr, rlist.rect[i]);
1034 memimagedraw(v->image, rlist.rect[i], gscreen, rlist.rect[i].min, memopaque, ZP, S);
1038 cursordraw(v->image, cr);
1039 addtorlist(&rlist, v->cursorr);
1045 count = v->countrect;
1047 if(count == nil || send == nil){
1053 for(i=j=0; i<rlist.nrect; i++){
1055 rlist.rect[j] = rlist.rect[i];
1056 if(rectclip(&rlist.rect[j], v->dim))
1057 ncount += (*count)(v, rlist.rect[j++]);
1061 if(doresize == 0 && ncount == 0 && dowarp == 0)
1065 fprint(2, "sendupdate: rlist.nrect=%d, ncount=%d\n", rlist.nrect, ncount);
1067 ooffset = Boffset(&v->out);
1070 if(doresize && v->canresize == 1){
1073 vncwrchar(v, MFrameUpdate);
1076 vncwrrect(v, v->dim);
1077 vncwrlong(v, EncDesktopSize);
1080 vncwrchar(v, MFrameUpdate);
1082 vncwrshort(v, doresize+ncount+dowarp);
1085 vncwrrect(v, gscreen->r);
1086 vncwrlong(v, EncXDesktopSize);
1087 vncwrlong(v, 1<<24);
1088 vncwrlong(v, v->screen[0].id);
1089 vncwrrect(v, v->screen[0].rect);
1090 vncwrlong(v, v->screen[0].flags);
1094 for(i=0; i<rlist.nrect; i++)
1095 nsend += (*send)(v, rlist.rect[i]);
1097 if(ncount != nsend){
1098 fprint(2, "%V: ncount=%d, nsend=%d; hanging up\n", v, ncount, nsend);
1103 vncwrrect(v, Rect(warppt.x, warppt.y, warppt.x+1, warppt.y+1));
1104 vncwrlong(v, EncMouseWarp);
1109 fprint(2, " in %lldms, %lld bytes\n", t1/1000000, Boffset(&v->out) - ooffset);
1117 * Update the snarf buffer if it has changed.
1120 updatesnarf(Vncs *v)
1125 if(v->snarfvers == snarf.vers)
1134 memmove(buf, snarf.buf, len);
1135 v->snarfvers = snarf.vers;
1138 vncwrchar(v, MSCut);
1139 vncwrbytes(v, "pad", 3);
1141 vncwrbytes(v, buf, len);
1146 * Continually update one client.
1149 clientwriteproc(Vncs *v)
1153 vncname("write %V", v);
1157 if(v->updatereq != last && updateimage(v))