xref: /plan9/sys/src/cmd/vnc/vncs.c (revision 14cc0f535177405a84c5b73603a98e5db6674719)
19a747e4fSDavid du Colombier #define	Image	IMAGE
29a747e4fSDavid du Colombier #include "vnc.h"
39a747e4fSDavid du Colombier #include "vncs.h"
49a747e4fSDavid du Colombier #include "compat.h"
59a747e4fSDavid du Colombier #include <cursor.h>
69a747e4fSDavid du Colombier #include "screen.h"
79a747e4fSDavid du Colombier #include "kbd.h"
89a747e4fSDavid du Colombier 
96a9fc400SDavid du Colombier #include <mp.h>
106a9fc400SDavid du Colombier #include <libsec.h>
116a9fc400SDavid du Colombier 
129a747e4fSDavid du Colombier extern	Dev	drawdevtab;
139a747e4fSDavid du Colombier extern	Dev	mousedevtab;
149a747e4fSDavid du Colombier extern	Dev	consdevtab;
159a747e4fSDavid du Colombier 
169a747e4fSDavid du Colombier Dev	*devtab[] =
179a747e4fSDavid du Colombier {
189a747e4fSDavid du Colombier 	&drawdevtab,
199a747e4fSDavid du Colombier 	&mousedevtab,
209a747e4fSDavid du Colombier 	&consdevtab,
219a747e4fSDavid du Colombier 	nil
229a747e4fSDavid du Colombier };
239a747e4fSDavid du Colombier 
249a747e4fSDavid du Colombier static char *msgname[] = {
259a747e4fSDavid du Colombier 	[MPixFmt] = "MPixFmt",
269a747e4fSDavid du Colombier 	[MFixCmap] = "MFixCmap",
279a747e4fSDavid du Colombier 	[MSetEnc] = "MSetEnc",
289a747e4fSDavid du Colombier 	[MFrameReq] = "MFrameReq",
299a747e4fSDavid du Colombier 	[MKey] = "MKey",
309a747e4fSDavid du Colombier 	[MMouse] = "MMouse",
319a747e4fSDavid du Colombier 	[MCCut] = "MCCut",
329a747e4fSDavid du Colombier };
339a747e4fSDavid du Colombier 
349a747e4fSDavid du Colombier static char *encname[] = {
359a747e4fSDavid du Colombier 	[EncRaw] = "raw",
369a747e4fSDavid du Colombier 	[EncCopyRect] = "copy rect",
379a747e4fSDavid du Colombier 	[EncRre] = "rre",
389a747e4fSDavid du Colombier 	[EncCorre] = "corre",
399a747e4fSDavid du Colombier 	[EncHextile] = "hextile",
409a747e4fSDavid du Colombier 	[EncZlib]	= "zlib",
419a747e4fSDavid du Colombier 	[EncTight]	= "zlibtight",
429a747e4fSDavid du Colombier 	[EncZHextile]	= "zhextile",
439a747e4fSDavid du Colombier 	[EncMouseWarp]	= "mousewarp",
449a747e4fSDavid du Colombier 
459a747e4fSDavid du Colombier };
469a747e4fSDavid du Colombier 
479a747e4fSDavid du Colombier /*
489a747e4fSDavid du Colombier  * list head. used to hold the list, the lock, dim, and pixelfmt
499a747e4fSDavid du Colombier  */
50f8e525acSDavid du Colombier struct {
51f8e525acSDavid du Colombier 	QLock;
52f8e525acSDavid du Colombier 	Vncs *head;
53f8e525acSDavid du Colombier } clients;
549a747e4fSDavid du Colombier 
55f8e525acSDavid du Colombier int	shared;
56f8e525acSDavid du Colombier int	sleeptime = 5;
57f8e525acSDavid du Colombier int	verbose = 0;
58f8e525acSDavid du Colombier char *cert;
59f8e525acSDavid du Colombier char *pixchan = "r5g6b5";
60f8e525acSDavid du Colombier static int	cmdpid;
61f8e525acSDavid du Colombier static int	srvfd;
62f8e525acSDavid du Colombier static int	exportfd;
63f8e525acSDavid du Colombier static Vncs	**vncpriv;
64f8e525acSDavid du Colombier 
65f8e525acSDavid du Colombier static int parsedisplay(char*);
66f8e525acSDavid du Colombier static void vnckill(char*, int, int);
67f8e525acSDavid du Colombier static int vncannounce(char *net, int display, char *adir, int base);
68f8e525acSDavid du Colombier static void noteshutdown(void*, char*);
69f8e525acSDavid du Colombier static void vncaccept(Vncs*);
70f8e525acSDavid du Colombier static int vncsfmt(Fmt*);
71f8e525acSDavid du Colombier static void getremote(char*, char*);
72f8e525acSDavid du Colombier static void vncname(char*, ...);
73f8e525acSDavid du Colombier #pragma varargck argpos vncname 1
74f8e525acSDavid du Colombier 
75f8e525acSDavid du Colombier #pragma varargck type "V" Vncs*
76f8e525acSDavid du Colombier 
77f8e525acSDavid du Colombier void
usage(void)78f8e525acSDavid du Colombier usage(void)
79f8e525acSDavid du Colombier {
8034e04225SDavid du Colombier 	fprint(2, "usage: vncs [-v] [-c cert] [-d :display] [-g widthXheight] [-p pixelfmt] [cmd [args]...]\n");
81f8e525acSDavid du Colombier 	fprint(2, "\tto kill a server: vncs [-v] -k :display\n");
82f8e525acSDavid du Colombier 	exits("usage");
83f8e525acSDavid du Colombier }
84f8e525acSDavid du Colombier 
85f8e525acSDavid du Colombier void
main(int argc,char ** argv)86f8e525acSDavid du Colombier main(int argc, char **argv)
87f8e525acSDavid du Colombier {
88f8e525acSDavid du Colombier 	int altnet, baseport, cfd, display, exnum, fd, h, killing, w;
89f8e525acSDavid du Colombier 	char adir[NETPATHLEN], ldir[NETPATHLEN];
90f8e525acSDavid du Colombier 	char net[NETPATHLEN], *p;
91f8e525acSDavid du Colombier 	char *rc[] = { "/bin/rc", "-i", nil };
92f8e525acSDavid du Colombier 	Vncs *v;
93f8e525acSDavid du Colombier 
94f8e525acSDavid du Colombier 	fmtinstall('V', vncsfmt);
95f8e525acSDavid du Colombier 	display = -1;
96f8e525acSDavid du Colombier 	killing = 0;
97f8e525acSDavid du Colombier 	altnet = 0;
98f8e525acSDavid du Colombier 	w = 1024;
99f8e525acSDavid du Colombier 	h = 768;
100f8e525acSDavid du Colombier 	baseport = 5900;
101f8e525acSDavid du Colombier 	setnetmtpt(net, sizeof net, nil);
102f8e525acSDavid du Colombier 	ARGBEGIN{
103f8e525acSDavid du Colombier 	default:
104f8e525acSDavid du Colombier 		usage();
105f8e525acSDavid du Colombier 	case 'c':
106f8e525acSDavid du Colombier 		cert = EARGF(usage());
107f8e525acSDavid du Colombier 		baseport = 35729;
108f8e525acSDavid du Colombier 		break;
109f8e525acSDavid du Colombier 	case 'd':
110f8e525acSDavid du Colombier 		if(display != -1)
111f8e525acSDavid du Colombier 			usage();
112f8e525acSDavid du Colombier 		display = parsedisplay(EARGF(usage()));
113f8e525acSDavid du Colombier 		break;
114f8e525acSDavid du Colombier 	case 'g':
115f8e525acSDavid du Colombier 		p = EARGF(usage());
116f8e525acSDavid du Colombier 		w = strtol(p, &p, 10);
117f8e525acSDavid du Colombier 		if(*p != 'x' && *p != 'X' && *p != ' ' && *p != '	')
118f8e525acSDavid du Colombier 			usage();
119f8e525acSDavid du Colombier 		h = strtol(p+1, &p, 10);
120f8e525acSDavid du Colombier 		if(*p != 0)
121f8e525acSDavid du Colombier 			usage();
122f8e525acSDavid du Colombier 		break;
123f8e525acSDavid du Colombier 	case 'k':
124f8e525acSDavid du Colombier 		if(display != -1)
125f8e525acSDavid du Colombier 			usage();
126f8e525acSDavid du Colombier 		display = parsedisplay(EARGF(usage()));
127f8e525acSDavid du Colombier 		killing = 1;
128f8e525acSDavid du Colombier 		break;
129f8e525acSDavid du Colombier 	case 'p':
130f8e525acSDavid du Colombier 		pixchan = EARGF(usage());
131f8e525acSDavid du Colombier 		break;
132f8e525acSDavid du Colombier /* DEBUGGING
133f8e525acSDavid du Colombier 	case 's':
134f8e525acSDavid du Colombier 		sleeptime = atoi(EARGF(usage()));
135f8e525acSDavid du Colombier 		break;
136f8e525acSDavid du Colombier */
137f8e525acSDavid du Colombier 	case 'v':
138f8e525acSDavid du Colombier 		verbose++;
139f8e525acSDavid du Colombier 		break;
140f8e525acSDavid du Colombier 	case 'x':
141f8e525acSDavid du Colombier 		p = EARGF(usage());
142f8e525acSDavid du Colombier 		setnetmtpt(net, sizeof net, p);
143f8e525acSDavid du Colombier 		altnet = 1;
144f8e525acSDavid du Colombier 		break;
145f8e525acSDavid du Colombier 	}ARGEND
146f8e525acSDavid du Colombier 
147f8e525acSDavid du Colombier 	if(killing){
148f8e525acSDavid du Colombier 		vnckill(net, display, baseport);
149f8e525acSDavid du Colombier 		exits(nil);
150f8e525acSDavid du Colombier 	}
151f8e525acSDavid du Colombier 
152f8e525acSDavid du Colombier 	if(altnet && !cert)
153f8e525acSDavid du Colombier 		sysfatal("announcing on alternate network requires TLS (-c)");
154f8e525acSDavid du Colombier 
155f8e525acSDavid du Colombier 	if(argc == 0)
156f8e525acSDavid du Colombier 		argv = rc;
157f8e525acSDavid du Colombier 
158f8e525acSDavid du Colombier 	/* easy exit */
159f8e525acSDavid du Colombier 	if(access(argv[0], AEXEC) < 0)
160f8e525acSDavid du Colombier 		sysfatal("access %s for exec: %r", argv[0]);
161f8e525acSDavid du Colombier 
162f8e525acSDavid du Colombier 	/* background ourselves */
163f8e525acSDavid du Colombier 	switch(rfork(RFPROC|RFNAMEG|RFFDG|RFNOTEG)){
164f8e525acSDavid du Colombier 	case -1:
165f8e525acSDavid du Colombier 		sysfatal("rfork: %r");
166f8e525acSDavid du Colombier 	default:
167f8e525acSDavid du Colombier 		exits(nil);
168f8e525acSDavid du Colombier 	case 0:
169f8e525acSDavid du Colombier 		break;
170f8e525acSDavid du Colombier 	}
171f8e525acSDavid du Colombier 
172f8e525acSDavid du Colombier 	vncpriv = privalloc();
173f8e525acSDavid du Colombier 	if(vncpriv == nil)
174f8e525acSDavid du Colombier 		sysfatal("privalloc: %r");
175f8e525acSDavid du Colombier 
176f8e525acSDavid du Colombier 	/* start screen */
177f8e525acSDavid du Colombier 	initcompat();
178f8e525acSDavid du Colombier 	if(waserror())
179*14cc0f53SDavid du Colombier 		sysfatal("screeninit %dx%d %s: %s", w, h, pixchan, up->error);
180f8e525acSDavid du Colombier 	if(verbose)
181f8e525acSDavid du Colombier 		fprint(2, "geometry is %dx%d\n", w, h);
182f8e525acSDavid du Colombier 	screeninit(w, h, pixchan);
183f8e525acSDavid du Colombier 	poperror();
184f8e525acSDavid du Colombier 
185f8e525acSDavid du Colombier 	/* start file system device slaves */
186f8e525acSDavid du Colombier 	exnum = exporter(devtab, &fd, &exportfd);
187f8e525acSDavid du Colombier 
188f8e525acSDavid du Colombier 	/* rebuild /dev because the underlying connection might go away (ick) */
189f8e525acSDavid du Colombier 	unmount(nil, "/dev");
190f8e525acSDavid du Colombier 	bind("#c", "/dev", MREPL);
191f8e525acSDavid du Colombier 
192f8e525acSDavid du Colombier 	/* run the command */
193f8e525acSDavid du Colombier 	switch(cmdpid = rfork(RFPROC|RFFDG|RFNOTEG|RFNAMEG|RFREND)){
194f8e525acSDavid du Colombier 	case -1:
195f8e525acSDavid du Colombier 		sysfatal("rfork: %r");
196f8e525acSDavid du Colombier 		break;
197f8e525acSDavid du Colombier 	case 0:
198f8e525acSDavid du Colombier 		if(mounter("/dev", MBEFORE, fd, exnum) < 0)
199f8e525acSDavid du Colombier 			sysfatal("mounter: %r");
200f8e525acSDavid du Colombier 		close(exportfd);
201f8e525acSDavid du Colombier 		close(0);
202f8e525acSDavid du Colombier 		close(1);
203f8e525acSDavid du Colombier 		close(2);
204f8e525acSDavid du Colombier 		open("/dev/cons", OREAD);
205f8e525acSDavid du Colombier 		open("/dev/cons", OWRITE);
206f8e525acSDavid du Colombier 		open("/dev/cons", OWRITE);
207f8e525acSDavid du Colombier 		exec(argv[0], argv);
208f8e525acSDavid du Colombier 		fprint(2, "exec %s: %r\n", argv[0]);
209f8e525acSDavid du Colombier 		_exits(nil);
210f8e525acSDavid du Colombier 	default:
211f8e525acSDavid du Colombier 		close(fd);
212f8e525acSDavid du Colombier 		break;
213f8e525acSDavid du Colombier 	}
214f8e525acSDavid du Colombier 
215f8e525acSDavid du Colombier 	/* run the service */
216f8e525acSDavid du Colombier 	srvfd = vncannounce(net, display, adir, baseport);
217f8e525acSDavid du Colombier 	if(srvfd < 0)
218f8e525acSDavid du Colombier 		sysfatal("announce failed");
219f8e525acSDavid du Colombier 	if(verbose)
220f8e525acSDavid du Colombier 		fprint(2, "announced in %s\n", adir);
221f8e525acSDavid du Colombier 
222f8e525acSDavid du Colombier 	atexit(shutdown);
223f8e525acSDavid du Colombier 	notify(noteshutdown);
224f8e525acSDavid du Colombier 	for(;;){
225f8e525acSDavid du Colombier 		vncname("listener");
226f8e525acSDavid du Colombier 		cfd = listen(adir, ldir);
227f8e525acSDavid du Colombier 		if(cfd < 0)
228f8e525acSDavid du Colombier 			break;
229f8e525acSDavid du Colombier 		if(verbose)
230f8e525acSDavid du Colombier 			fprint(2, "call in %s\n", ldir);
231f8e525acSDavid du Colombier 		fd = accept(cfd, ldir);
232f8e525acSDavid du Colombier 		if(fd < 0){
233f8e525acSDavid du Colombier 			close(cfd);
234f8e525acSDavid du Colombier 			continue;
235f8e525acSDavid du Colombier 		}
236f8e525acSDavid du Colombier 		v = mallocz(sizeof(Vncs), 1);
237f8e525acSDavid du Colombier 		if(v == nil){
238f8e525acSDavid du Colombier 			close(cfd);
239f8e525acSDavid du Colombier 			close(fd);
240f8e525acSDavid du Colombier 			continue;
241f8e525acSDavid du Colombier 		}
242f8e525acSDavid du Colombier 		v->ctlfd = cfd;
243f8e525acSDavid du Colombier 		v->datafd = fd;
244f8e525acSDavid du Colombier 		v->nproc = 1;
245f8e525acSDavid du Colombier 		v->ndead = 0;
246f8e525acSDavid du Colombier 		getremote(ldir, v->remote);
247f8e525acSDavid du Colombier 		strcpy(v->netpath, ldir);
248f8e525acSDavid du Colombier 		qlock(&clients);
249f8e525acSDavid du Colombier 		v->next = clients.head;
250f8e525acSDavid du Colombier 		clients.head = v;
251f8e525acSDavid du Colombier 		qunlock(&clients);
252f8e525acSDavid du Colombier 		vncaccept(v);
253f8e525acSDavid du Colombier 	}
254f8e525acSDavid du Colombier 	exits(0);
255f8e525acSDavid du Colombier }
256f8e525acSDavid du Colombier 
257f8e525acSDavid du Colombier static int
parsedisplay(char * p)258f8e525acSDavid du Colombier parsedisplay(char *p)
259f8e525acSDavid du Colombier {
260f8e525acSDavid du Colombier 	int n;
261f8e525acSDavid du Colombier 
262f8e525acSDavid du Colombier 	if(*p != ':')
263f8e525acSDavid du Colombier 		usage();
264f8e525acSDavid du Colombier 	if(*p == 0)
265f8e525acSDavid du Colombier 		usage();
266f8e525acSDavid du Colombier 	n = strtol(p+1, &p, 10);
267f8e525acSDavid du Colombier 	if(*p != 0)
268f8e525acSDavid du Colombier 		usage();
269f8e525acSDavid du Colombier 	return n;
270f8e525acSDavid du Colombier }
271f8e525acSDavid du Colombier 
272f8e525acSDavid du Colombier static void
getremote(char * ldir,char * remote)273f8e525acSDavid du Colombier getremote(char *ldir, char *remote)
274f8e525acSDavid du Colombier {
275f8e525acSDavid du Colombier 	char buf[NETPATHLEN];
276f8e525acSDavid du Colombier 	int fd, n;
277f8e525acSDavid du Colombier 
278f8e525acSDavid du Colombier 	snprint(buf, sizeof buf, "%s/remote", ldir);
279f8e525acSDavid du Colombier 	strcpy(remote, "<none>");
280f8e525acSDavid du Colombier 	if((fd = open(buf, OREAD)) < 0)
281f8e525acSDavid du Colombier 		return;
282f8e525acSDavid du Colombier 	n = readn(fd, remote, NETPATHLEN-1);
283f8e525acSDavid du Colombier 	close(fd);
284f8e525acSDavid du Colombier 	if(n < 0)
285f8e525acSDavid du Colombier 		return;
286f8e525acSDavid du Colombier 	remote[n] = 0;
287f8e525acSDavid du Colombier 	if(n>0 && remote[n-1] == '\n')
288f8e525acSDavid du Colombier 		remote[n-1] = 0;
289f8e525acSDavid du Colombier }
290f8e525acSDavid du Colombier 
291f8e525acSDavid du Colombier static int
vncsfmt(Fmt * fmt)292f8e525acSDavid du Colombier vncsfmt(Fmt *fmt)
293f8e525acSDavid du Colombier {
294f8e525acSDavid du Colombier 	Vncs *v;
295f8e525acSDavid du Colombier 
296f8e525acSDavid du Colombier 	v = va_arg(fmt->args, Vncs*);
297f8e525acSDavid du Colombier 	return fmtprint(fmt, "[%d] %s %s", getpid(), v->remote, v->netpath);
298f8e525acSDavid du Colombier }
299f8e525acSDavid du Colombier 
300f8e525acSDavid du Colombier /*
301f8e525acSDavid du Colombier  * We register exiting as an atexit handler in each proc, so that
302f8e525acSDavid du Colombier  * client procs need merely exit when something goes wrong.
303f8e525acSDavid du Colombier  */
304f8e525acSDavid du Colombier static void
vncclose(Vncs * v)305f8e525acSDavid du Colombier vncclose(Vncs *v)
306f8e525acSDavid du Colombier {
307f8e525acSDavid du Colombier 	Vncs **l;
308f8e525acSDavid du Colombier 
309f8e525acSDavid du Colombier 	/* remove self from client list if there */
310f8e525acSDavid du Colombier 	qlock(&clients);
311f8e525acSDavid du Colombier 	for(l=&clients.head; *l; l=&(*l)->next)
312f8e525acSDavid du Colombier 		if(*l == v){
313f8e525acSDavid du Colombier 			*l = v->next;
314f8e525acSDavid du Colombier 			break;
315f8e525acSDavid du Colombier 		}
316f8e525acSDavid du Colombier 	qunlock(&clients);
317f8e525acSDavid du Colombier 
318f8e525acSDavid du Colombier 	/* if last proc, free v */
319f8e525acSDavid du Colombier 	vnclock(v);
320f8e525acSDavid du Colombier 	if(++v->ndead < v->nproc){
321f8e525acSDavid du Colombier 		vncunlock(v);
322f8e525acSDavid du Colombier 		return;
323f8e525acSDavid du Colombier 	}
324f8e525acSDavid du Colombier 
325f8e525acSDavid du Colombier 	freerlist(&v->rlist);
326f8e525acSDavid du Colombier 	vncterm(v);
327f8e525acSDavid du Colombier 	if(v->ctlfd >= 0)
328f8e525acSDavid du Colombier 		close(v->ctlfd);
329f8e525acSDavid du Colombier 	if(v->datafd >= 0)
330f8e525acSDavid du Colombier 		close(v->datafd);
331f8e525acSDavid du Colombier 	if(v->image)
332f8e525acSDavid du Colombier 		freememimage(v->image);
333f8e525acSDavid du Colombier 	free(v);
334f8e525acSDavid du Colombier }
335f8e525acSDavid du Colombier 
336f8e525acSDavid du Colombier static void
exiting(void)337f8e525acSDavid du Colombier exiting(void)
338f8e525acSDavid du Colombier {
339f8e525acSDavid du Colombier 	vncclose(*vncpriv);
340f8e525acSDavid du Colombier }
341f8e525acSDavid du Colombier 
342f8e525acSDavid du Colombier void
vnchungup(Vnc * v)343f8e525acSDavid du Colombier vnchungup(Vnc *v)
344f8e525acSDavid du Colombier {
345f8e525acSDavid du Colombier 	if(verbose)
346f8e525acSDavid du Colombier 		fprint(2, "%V: hangup\n", (Vncs*)v);
347f8e525acSDavid du Colombier 	exits(0);	/* atexit and exiting() will take care of everything */
348f8e525acSDavid du Colombier }
349f8e525acSDavid du Colombier 
350f8e525acSDavid du Colombier /*
351f8e525acSDavid du Colombier  * Kill all clients except safe.
352f8e525acSDavid du Colombier  * Used to start a non-shared client and at shutdown.
353f8e525acSDavid du Colombier  */
354f8e525acSDavid du Colombier static void
killclients(Vncs * safe)355f8e525acSDavid du Colombier killclients(Vncs *safe)
356f8e525acSDavid du Colombier {
357f8e525acSDavid du Colombier 	Vncs *v;
358f8e525acSDavid du Colombier 
359f8e525acSDavid du Colombier 	qlock(&clients);
360f8e525acSDavid du Colombier 	for(v=clients.head; v; v=v->next){
361f8e525acSDavid du Colombier 		if(v == safe)
362f8e525acSDavid du Colombier 			continue;
363f8e525acSDavid du Colombier 		if(v->ctlfd >= 0){
364f8e525acSDavid du Colombier 			hangup(v->ctlfd);
365f8e525acSDavid du Colombier 			close(v->ctlfd);
366f8e525acSDavid du Colombier 			v->ctlfd = -1;
367f8e525acSDavid du Colombier 			close(v->datafd);
368f8e525acSDavid du Colombier 			v->datafd = -1;
369f8e525acSDavid du Colombier 		}
370f8e525acSDavid du Colombier 	}
371f8e525acSDavid du Colombier 	qunlock(&clients);
372f8e525acSDavid du Colombier }
373f8e525acSDavid du Colombier 
374f8e525acSDavid du Colombier /*
375f8e525acSDavid du Colombier  * Kill the executing command and then kill everyone else.
376f8e525acSDavid du Colombier  * Called to close up shop at the end of the day
377f8e525acSDavid du Colombier  * and also if we get an unexpected note.
378f8e525acSDavid du Colombier  */
379f8e525acSDavid du Colombier static char killkin[] = "die vnc kin";
380f8e525acSDavid du Colombier static void
killall(void)381f8e525acSDavid du Colombier killall(void)
382f8e525acSDavid du Colombier {
383f8e525acSDavid du Colombier 	postnote(PNGROUP, cmdpid, "hangup");
384f8e525acSDavid du Colombier 	close(srvfd);
385f8e525acSDavid du Colombier 	srvfd = -1;
386f8e525acSDavid du Colombier 	close(exportfd);
387f8e525acSDavid du Colombier 	exportfd = -1;
388f8e525acSDavid du Colombier 	postnote(PNGROUP, getpid(), killkin);
389f8e525acSDavid du Colombier }
390f8e525acSDavid du Colombier 
391f8e525acSDavid du Colombier void
shutdown(void)392f8e525acSDavid du Colombier shutdown(void)
393f8e525acSDavid du Colombier {
394f8e525acSDavid du Colombier 	if(verbose)
395f8e525acSDavid du Colombier 		fprint(2, "vnc server shutdown\n");
396f8e525acSDavid du Colombier 	killall();
397f8e525acSDavid du Colombier }
398f8e525acSDavid du Colombier 
399f8e525acSDavid du Colombier static void
noteshutdown(void *,char * msg)400f8e525acSDavid du Colombier noteshutdown(void*, char *msg)
401f8e525acSDavid du Colombier {
402f8e525acSDavid du Colombier 	if(strcmp(msg, killkin) == 0)	/* already shutting down */
403f8e525acSDavid du Colombier 		noted(NDFLT);
404f8e525acSDavid du Colombier 	killall();
405f8e525acSDavid du Colombier 	noted(NDFLT);
406f8e525acSDavid du Colombier }
407f8e525acSDavid du Colombier 
408f8e525acSDavid du Colombier /*
409f8e525acSDavid du Colombier  * Kill a specific instance of a server.
410f8e525acSDavid du Colombier  */
411f8e525acSDavid du Colombier static void
vnckill(char * net,int display,int baseport)412f8e525acSDavid du Colombier vnckill(char *net, int display, int baseport)
413f8e525acSDavid du Colombier {
414f8e525acSDavid du Colombier 	int fd, i, n, port;
415f8e525acSDavid du Colombier 	char buf[NETPATHLEN], *p;
416f8e525acSDavid du Colombier 
417f8e525acSDavid du Colombier 	for(i=0;; i++){
418f8e525acSDavid du Colombier 		snprint(buf, sizeof buf, "%s/tcp/%d/local", net, i);
419f8e525acSDavid du Colombier 		if((fd = open(buf, OREAD)) < 0)
420f8e525acSDavid du Colombier 			sysfatal("did not find display");
421f8e525acSDavid du Colombier 		n = read(fd, buf, sizeof buf-1);
422f8e525acSDavid du Colombier 		close(fd);
423f8e525acSDavid du Colombier 		if(n <= 0)
424f8e525acSDavid du Colombier 			continue;
425f8e525acSDavid du Colombier 		buf[n] = 0;
426f8e525acSDavid du Colombier 		p = strchr(buf, '!');
427f8e525acSDavid du Colombier 		if(p == 0)
428f8e525acSDavid du Colombier 			continue;
429f8e525acSDavid du Colombier 		port = atoi(p+1);
430f8e525acSDavid du Colombier 		if(port != display+baseport)
431f8e525acSDavid du Colombier 			continue;
432f8e525acSDavid du Colombier 		snprint(buf, sizeof buf, "%s/tcp/%d/ctl", net, i);
433f8e525acSDavid du Colombier 		fd = open(buf, OWRITE);
434f8e525acSDavid du Colombier 		if(fd < 0)
435f8e525acSDavid du Colombier 			sysfatal("cannot open %s: %r", buf);
436f8e525acSDavid du Colombier 		if(write(fd, "hangup", 6) != 6)
437f8e525acSDavid du Colombier 			sysfatal("cannot hangup %s: %r", buf);
438f8e525acSDavid du Colombier 		close(fd);
439f8e525acSDavid du Colombier 		break;
440f8e525acSDavid du Colombier 	}
441f8e525acSDavid du Colombier }
442f8e525acSDavid du Colombier 
443f8e525acSDavid du Colombier /*
444f8e525acSDavid du Colombier  * Look for a port on which to announce.
445f8e525acSDavid du Colombier  * If display != -1, we only try that one.
446f8e525acSDavid du Colombier  * Otherwise we hunt.
447f8e525acSDavid du Colombier  *
448f8e525acSDavid du Colombier  * Returns the announce fd.
449f8e525acSDavid du Colombier  */
450f8e525acSDavid du Colombier static int
vncannounce(char * net,int display,char * adir,int base)451f8e525acSDavid du Colombier vncannounce(char *net, int display, char *adir, int base)
452f8e525acSDavid du Colombier {
453f8e525acSDavid du Colombier 	int port, eport, fd;
454f8e525acSDavid du Colombier 	char addr[NETPATHLEN];
455f8e525acSDavid du Colombier 
456f8e525acSDavid du Colombier 	if(display == -1){
457f8e525acSDavid du Colombier 		port = base;
458f8e525acSDavid du Colombier 		eport = base+50;
459f8e525acSDavid du Colombier 	}else{
460f8e525acSDavid du Colombier 		port = base+display;
461f8e525acSDavid du Colombier 		eport = port;
462f8e525acSDavid du Colombier 	}
463f8e525acSDavid du Colombier 
464f8e525acSDavid du Colombier 	for(; port<=eport; port++){
465f8e525acSDavid du Colombier 		snprint(addr, sizeof addr, "%s/tcp!*!%d", net, port);
466f8e525acSDavid du Colombier 		if((fd = announce(addr, adir)) >= 0){
467f8e525acSDavid du Colombier 			fprint(2, "server started on display :%d\n", port-base);
468f8e525acSDavid du Colombier 			return fd;
469f8e525acSDavid du Colombier 		}
470f8e525acSDavid du Colombier 	}
471f8e525acSDavid du Colombier 	if(display == -1)
472f8e525acSDavid du Colombier 		fprint(2, "could not find any ports to announce\n");
473f8e525acSDavid du Colombier 	else
474f8e525acSDavid du Colombier 		fprint(2, "announce: %r\n");
475f8e525acSDavid du Colombier 	return -1;
476f8e525acSDavid du Colombier }
477f8e525acSDavid du Colombier 
478f8e525acSDavid du Colombier /*
479f8e525acSDavid du Colombier  * Handle a new connection.
480f8e525acSDavid du Colombier  */
481f8e525acSDavid du Colombier static void clientreadproc(Vncs*);
482f8e525acSDavid du Colombier static void clientwriteproc(Vncs*);
483f8e525acSDavid du Colombier static void chan2fmt(Pixfmt*, ulong);
484f8e525acSDavid du Colombier static ulong fmt2chan(Pixfmt*);
485f8e525acSDavid du Colombier 
486f8e525acSDavid du Colombier static void
vncaccept(Vncs * v)487f8e525acSDavid du Colombier vncaccept(Vncs *v)
488f8e525acSDavid du Colombier {
489f8e525acSDavid du Colombier 	char buf[32];
490f8e525acSDavid du Colombier 	int fd;
491f8e525acSDavid du Colombier 	TLSconn conn;
492f8e525acSDavid du Colombier 
493f8e525acSDavid du Colombier 	/* caller returns to listen */
494f8e525acSDavid du Colombier 	switch(rfork(RFPROC|RFMEM|RFNAMEG)){
495f8e525acSDavid du Colombier 	case -1:
496f8e525acSDavid du Colombier 		fprint(2, "%V: fork failed: %r\n", v);
497f8e525acSDavid du Colombier 		vncclose(v);
498f8e525acSDavid du Colombier 		return;
499f8e525acSDavid du Colombier 	default:
500f8e525acSDavid du Colombier 		return;
501f8e525acSDavid du Colombier 	case 0:
502f8e525acSDavid du Colombier 		break;
503f8e525acSDavid du Colombier 	}
504f8e525acSDavid du Colombier 	*vncpriv = v;
505f8e525acSDavid du Colombier 
506f8e525acSDavid du Colombier 	if(!atexit(exiting)){
507f8e525acSDavid du Colombier 		fprint(2, "%V: could not register atexit handler: %r; hanging up\n", v);
508f8e525acSDavid du Colombier 		exiting();
509f8e525acSDavid du Colombier 		exits(nil);
510f8e525acSDavid du Colombier 	}
511f8e525acSDavid du Colombier 
512f8e525acSDavid du Colombier 	if(cert != nil){
513f8e525acSDavid du Colombier 		memset(&conn, 0, sizeof conn);
514f8e525acSDavid du Colombier 		conn.cert = readcert(cert, &conn.certlen);
515f8e525acSDavid du Colombier 		if(conn.cert == nil){
516f8e525acSDavid du Colombier 			fprint(2, "%V: could not read cert %s: %r; hanging up\n", v, cert);
517f8e525acSDavid du Colombier 			exits(nil);
518f8e525acSDavid du Colombier 		}
519f8e525acSDavid du Colombier 		fd = tlsServer(v->datafd, &conn);
520f8e525acSDavid du Colombier 		if(fd < 0){
521f8e525acSDavid du Colombier 			fprint(2, "%V: tlsServer: %r; hanging up\n", v);
522f8e525acSDavid du Colombier 			free(conn.cert);
523f8e525acSDavid du Colombier 			if(conn.sessionID)
524f8e525acSDavid du Colombier 				free(conn.sessionID);
525f8e525acSDavid du Colombier 			exits(nil);
526f8e525acSDavid du Colombier 		}
527f8e525acSDavid du Colombier 		close(v->datafd);
528f8e525acSDavid du Colombier 		v->datafd = fd;
529f8e525acSDavid du Colombier 		free(conn.cert);
530f8e525acSDavid du Colombier 		free(conn.sessionID);
531f8e525acSDavid du Colombier 	}
532f8e525acSDavid du Colombier 	vncinit(v->datafd, v->ctlfd, v);
533f8e525acSDavid du Colombier 
534f8e525acSDavid du Colombier 	if(verbose)
535f8e525acSDavid du Colombier 		fprint(2, "%V: handshake\n", v);
536f8e525acSDavid du Colombier 	if(vncsrvhandshake(v) < 0){
537f8e525acSDavid du Colombier 		fprint(2, "%V: handshake failed; hanging up\n", v);
538f8e525acSDavid du Colombier 		exits(0);
539f8e525acSDavid du Colombier 	}
540f8e525acSDavid du Colombier 	if(verbose)
541f8e525acSDavid du Colombier 		fprint(2, "%V: auth\n", v);
542f8e525acSDavid du Colombier 	if(vncsrvauth(v) < 0){
543f8e525acSDavid du Colombier 		fprint(2, "%V: auth failed; hanging up\n", v);
544f8e525acSDavid du Colombier 		exits(0);
545f8e525acSDavid du Colombier 	}
546f8e525acSDavid du Colombier 
547f8e525acSDavid du Colombier 	shared = vncrdchar(v);
548f8e525acSDavid du Colombier 
549f8e525acSDavid du Colombier 	if(verbose)
550f8e525acSDavid du Colombier 		fprint(2, "%V: %sshared\n", v, shared ? "" : "not ");
551f8e525acSDavid du Colombier 	if(!shared)
552f8e525acSDavid du Colombier 		killclients(v);
553f8e525acSDavid du Colombier 
554f8e525acSDavid du Colombier 	v->dim = (Point){Dx(gscreen->r), Dy(gscreen->r)};
555f8e525acSDavid du Colombier 	vncwrpoint(v, v->dim);
556f8e525acSDavid du Colombier 	if(verbose)
557f8e525acSDavid du Colombier 		fprint(2, "%V: send screen size %P (rect %R)\n", v, v->dim, gscreen->r);
558f8e525acSDavid du Colombier 
559f8e525acSDavid du Colombier 	v->bpp = gscreen->depth;
560f8e525acSDavid du Colombier 	v->depth = gscreen->depth;
561f8e525acSDavid du Colombier 	v->truecolor = 1;
562f8e525acSDavid du Colombier 	v->bigendian = 0;
563f8e525acSDavid du Colombier 	chan2fmt(v, gscreen->chan);
564f8e525acSDavid du Colombier 	if(verbose)
565f8e525acSDavid du Colombier 		fprint(2, "%V: bpp=%d, depth=%d, chan=%s\n", v,
566f8e525acSDavid du Colombier 			v->bpp, v->depth, chantostr(buf, gscreen->chan));
567f8e525acSDavid du Colombier 	vncwrpixfmt(v, v);
568f8e525acSDavid du Colombier 	vncwrlong(v, 14);
569f8e525acSDavid du Colombier 	vncwrbytes(v, "Plan9 Desktop", 14);
570f8e525acSDavid du Colombier 	vncflush(v);
571f8e525acSDavid du Colombier 
572f8e525acSDavid du Colombier 	if(verbose)
573f8e525acSDavid du Colombier 		fprint(2, "%V: handshaking done\n", v);
574f8e525acSDavid du Colombier 
575f8e525acSDavid du Colombier 	switch(rfork(RFPROC|RFMEM)){
576f8e525acSDavid du Colombier 	case -1:
577f8e525acSDavid du Colombier 		fprint(2, "%V: cannot fork: %r; hanging up\n", v);
578f8e525acSDavid du Colombier 		vnchungup(v);
579f8e525acSDavid du Colombier 	default:
580f8e525acSDavid du Colombier 		clientreadproc(v);
581f8e525acSDavid du Colombier 		exits(nil);
582f8e525acSDavid du Colombier 	case 0:
583f8e525acSDavid du Colombier 		*vncpriv = v;
584f8e525acSDavid du Colombier 		v->nproc++;
585f8e525acSDavid du Colombier 		if(atexit(exiting) == 0){
586f8e525acSDavid du Colombier 			exiting();
587f8e525acSDavid du Colombier 			fprint(2, "%V: could not register atexit handler: %r; hanging up\n", v);
588f8e525acSDavid du Colombier 			exits(nil);
589f8e525acSDavid du Colombier 		}
590f8e525acSDavid du Colombier 		clientwriteproc(v);
591f8e525acSDavid du Colombier 		exits(nil);
592f8e525acSDavid du Colombier 	}
593f8e525acSDavid du Colombier }
594f8e525acSDavid du Colombier 
595f8e525acSDavid du Colombier static void
vncname(char * fmt,...)596f8e525acSDavid du Colombier vncname(char *fmt, ...)
597f8e525acSDavid du Colombier {
598f8e525acSDavid du Colombier 	int fd;
599f8e525acSDavid du Colombier 	char name[64], buf[32];
600f8e525acSDavid du Colombier 	va_list arg;
601f8e525acSDavid du Colombier 
602f8e525acSDavid du Colombier 	va_start(arg, fmt);
603f8e525acSDavid du Colombier 	vsnprint(name, sizeof name, fmt, arg);
604f8e525acSDavid du Colombier 	va_end(arg);
605f8e525acSDavid du Colombier 
606f8e525acSDavid du Colombier 	sprint(buf, "/proc/%d/args", getpid());
607f8e525acSDavid du Colombier 	if((fd = open(buf, OWRITE)) >= 0){
608f8e525acSDavid du Colombier 		write(fd, name, strlen(name));
609f8e525acSDavid du Colombier 		close(fd);
610f8e525acSDavid du Colombier 	}
611f8e525acSDavid du Colombier }
612f8e525acSDavid du Colombier 
613f8e525acSDavid du Colombier /*
614f8e525acSDavid du Colombier  * Set the pixel format being sent.  Can only happen once.
615f8e525acSDavid du Colombier  * (Maybe a client would send this again if the screen changed
616f8e525acSDavid du Colombier  * underneath it?  If we want to support this we need a way to
617f8e525acSDavid du Colombier  * make sure the current image is no longer in use, so we can free it.
618f8e525acSDavid du Colombier  */
619f8e525acSDavid du Colombier static void
setpixelfmt(Vncs * v)620f8e525acSDavid du Colombier setpixelfmt(Vncs *v)
621f8e525acSDavid du Colombier {
622f8e525acSDavid du Colombier 	ulong chan;
623f8e525acSDavid du Colombier 
624f8e525acSDavid du Colombier 	vncgobble(v, 3);
625f8e525acSDavid du Colombier 	v->Pixfmt = vncrdpixfmt(v);
626f8e525acSDavid du Colombier 	chan = fmt2chan(v);
627f8e525acSDavid du Colombier 	if(chan == 0){
628f8e525acSDavid du Colombier 		fprint(2, "%V: bad pixel format; hanging up\n", v);
629f8e525acSDavid du Colombier 		vnchungup(v);
630f8e525acSDavid du Colombier 	}
6310b459c2cSDavid du Colombier 	v->imagechan = chan;
632f8e525acSDavid du Colombier }
633f8e525acSDavid du Colombier 
634f8e525acSDavid du Colombier /*
635f8e525acSDavid du Colombier  * Set the preferred encoding list.  Can only happen once.
636f8e525acSDavid du Colombier  * If we want to support changing this more than once then
637f8e525acSDavid du Colombier  * we need to put a lock around the encoding functions
638f8e525acSDavid du Colombier  * so as not to conflict with updateimage.
639f8e525acSDavid du Colombier  */
640f8e525acSDavid du Colombier static void
setencoding(Vncs * v)641f8e525acSDavid du Colombier setencoding(Vncs *v)
642f8e525acSDavid du Colombier {
643f8e525acSDavid du Colombier 	int n, x;
644f8e525acSDavid du Colombier 
645f8e525acSDavid du Colombier 	vncrdchar(v);
646f8e525acSDavid du Colombier 	n = vncrdshort(v);
647f8e525acSDavid du Colombier 	while(n-- > 0){
648f8e525acSDavid du Colombier 		x = vncrdlong(v);
649f8e525acSDavid du Colombier 		switch(x){
650f8e525acSDavid du Colombier 		case EncCopyRect:
651f8e525acSDavid du Colombier 			v->copyrect = 1;
652f8e525acSDavid du Colombier 			continue;
653f8e525acSDavid du Colombier 		case EncMouseWarp:
654f8e525acSDavid du Colombier 			v->canwarp = 1;
655f8e525acSDavid du Colombier 			continue;
656f8e525acSDavid du Colombier 		}
657f8e525acSDavid du Colombier 		if(v->countrect != nil)
658f8e525acSDavid du Colombier 			continue;
659f8e525acSDavid du Colombier 		switch(x){
660f8e525acSDavid du Colombier 		case EncRaw:
661f8e525acSDavid du Colombier 			v->encname = "raw";
662f8e525acSDavid du Colombier 			v->countrect = countraw;
663f8e525acSDavid du Colombier 			v->sendrect = sendraw;
664f8e525acSDavid du Colombier 			break;
665f8e525acSDavid du Colombier 		case EncRre:
666f8e525acSDavid du Colombier 			v->encname = "rre";
667f8e525acSDavid du Colombier 			v->countrect = countrre;
668f8e525acSDavid du Colombier 			v->sendrect = sendrre;
669f8e525acSDavid du Colombier 			break;
670f8e525acSDavid du Colombier 		case EncCorre:
671f8e525acSDavid du Colombier 			v->encname = "corre";
672f8e525acSDavid du Colombier 			v->countrect = countcorre;
673f8e525acSDavid du Colombier 			v->sendrect = sendcorre;
674f8e525acSDavid du Colombier 			break;
675f8e525acSDavid du Colombier 		case EncHextile:
676f8e525acSDavid du Colombier 			v->encname = "hextile";
677f8e525acSDavid du Colombier 			v->countrect = counthextile;
678f8e525acSDavid du Colombier 			v->sendrect = sendhextile;
679f8e525acSDavid du Colombier 			break;
680f8e525acSDavid du Colombier 		}
681f8e525acSDavid du Colombier 	}
682f8e525acSDavid du Colombier 
683f8e525acSDavid du Colombier 	if(v->countrect == nil){
684f8e525acSDavid du Colombier 		v->encname = "raw";
685f8e525acSDavid du Colombier 		v->countrect = countraw;
686f8e525acSDavid du Colombier 		v->sendrect = sendraw;
687f8e525acSDavid du Colombier 	}
688f8e525acSDavid du Colombier 
689f8e525acSDavid du Colombier 	if(verbose)
690f8e525acSDavid du Colombier 		fprint(2, "Encoding with %s%s%s\n", v->encname,
691f8e525acSDavid du Colombier 			v->copyrect ? ", copyrect" : "",
692f8e525acSDavid du Colombier 			v->canwarp ? ", canwarp" : "");
693f8e525acSDavid du Colombier }
694f8e525acSDavid du Colombier 
695f8e525acSDavid du Colombier /*
696f8e525acSDavid du Colombier  * Continually read updates from one client.
697f8e525acSDavid du Colombier  */
698f8e525acSDavid du Colombier static void
clientreadproc(Vncs * v)699f8e525acSDavid du Colombier clientreadproc(Vncs *v)
700f8e525acSDavid du Colombier {
701f8e525acSDavid du Colombier 	int incremental, key, keydown, buttons, type, x, y, n;
702f8e525acSDavid du Colombier 	char *buf;
703f8e525acSDavid du Colombier 	Rectangle r;
704f8e525acSDavid du Colombier 
705f8e525acSDavid du Colombier 	vncname("read %V", v);
706f8e525acSDavid du Colombier 
707f8e525acSDavid du Colombier 	for(;;){
708f8e525acSDavid du Colombier 		type = vncrdchar(v);
709f8e525acSDavid du Colombier 		switch(type){
710f8e525acSDavid du Colombier 		default:
711f8e525acSDavid du Colombier 			fprint(2, "%V: unknown vnc message type %d; hanging up\n", v, type);
712f8e525acSDavid du Colombier 			vnchungup(v);
713f8e525acSDavid du Colombier 
714f8e525acSDavid du Colombier 		/* set pixel format */
715f8e525acSDavid du Colombier 		case MPixFmt:
716f8e525acSDavid du Colombier 			setpixelfmt(v);
717f8e525acSDavid du Colombier 			break;
718f8e525acSDavid du Colombier 
719f8e525acSDavid du Colombier 		/* ignore color map changes */
720f8e525acSDavid du Colombier 		case MFixCmap:
721f8e525acSDavid du Colombier 			vncgobble(v, 3);
722f8e525acSDavid du Colombier 			n = vncrdshort(v);
723f8e525acSDavid du Colombier 			vncgobble(v, n*6);
724f8e525acSDavid du Colombier 			break;
725f8e525acSDavid du Colombier 
726f8e525acSDavid du Colombier 		/* set encoding list */
727f8e525acSDavid du Colombier 		case MSetEnc:
728f8e525acSDavid du Colombier 			setencoding(v);
729f8e525acSDavid du Colombier 			break;
730f8e525acSDavid du Colombier 
731f8e525acSDavid du Colombier 		/* request image update in rectangle */
732f8e525acSDavid du Colombier 		case MFrameReq:
733f8e525acSDavid du Colombier 			incremental = vncrdchar(v);
734f8e525acSDavid du Colombier 			r = vncrdrect(v);
735f8e525acSDavid du Colombier 			if(incremental){
736f8e525acSDavid du Colombier 				vnclock(v);
737f8e525acSDavid du Colombier 				v->updaterequest = 1;
738f8e525acSDavid du Colombier 				vncunlock(v);
739f8e525acSDavid du Colombier 			}else{
740f8e525acSDavid du Colombier 				drawlock();	/* protects rlist */
741f8e525acSDavid du Colombier 				vnclock(v);	/* protects updaterequest */
742f8e525acSDavid du Colombier 				v->updaterequest = 1;
743f8e525acSDavid du Colombier 				addtorlist(&v->rlist, r);
744f8e525acSDavid du Colombier 				vncunlock(v);
745f8e525acSDavid du Colombier 				drawunlock();
746f8e525acSDavid du Colombier 			}
747f8e525acSDavid du Colombier 			break;
748f8e525acSDavid du Colombier 
749f8e525acSDavid du Colombier 		/* send keystroke */
750f8e525acSDavid du Colombier 		case MKey:
751f8e525acSDavid du Colombier 			keydown = vncrdchar(v);
752f8e525acSDavid du Colombier 			vncgobble(v, 2);
753f8e525acSDavid du Colombier 			key = vncrdlong(v);
754f8e525acSDavid du Colombier 			vncputc(!keydown, key);
755f8e525acSDavid du Colombier 			break;
756f8e525acSDavid du Colombier 
757f8e525acSDavid du Colombier 		/* send mouse event */
758f8e525acSDavid du Colombier 		case MMouse:
759f8e525acSDavid du Colombier 			buttons = vncrdchar(v);
760f8e525acSDavid du Colombier 			x = vncrdshort(v);
761f8e525acSDavid du Colombier 			y = vncrdshort(v);
762f8e525acSDavid du Colombier 			mousetrack(x, y, buttons, nsec()/(1000*1000LL));
763f8e525acSDavid du Colombier 			break;
764f8e525acSDavid du Colombier 
765f8e525acSDavid du Colombier 		/* send cut text */
766f8e525acSDavid du Colombier 		case MCCut:
767f8e525acSDavid du Colombier 			vncgobble(v, 3);
768f8e525acSDavid du Colombier 			n = vncrdlong(v);
769f8e525acSDavid du Colombier 			buf = malloc(n+1);
770f8e525acSDavid du Colombier 			if(buf){
771f8e525acSDavid du Colombier 				vncrdbytes(v, buf, n);
772f8e525acSDavid du Colombier 				buf[n] = 0;
773f8e525acSDavid du Colombier 				vnclock(v);	/* for snarfvers */
774f8e525acSDavid du Colombier 				setsnarf(buf, n, &v->snarfvers);
775f8e525acSDavid du Colombier 				vncunlock(v);
776e6c6b7f8SDavid du Colombier 			}else
777e6c6b7f8SDavid du Colombier 				vncgobble(v, n);
778f8e525acSDavid du Colombier 			break;
779f8e525acSDavid du Colombier 		}
780f8e525acSDavid du Colombier 	}
781f8e525acSDavid du Colombier }
7829a747e4fSDavid du Colombier 
7839a747e4fSDavid du Colombier static int
nbits(ulong mask)7849a747e4fSDavid du Colombier nbits(ulong mask)
7859a747e4fSDavid du Colombier {
786f8e525acSDavid du Colombier 	int n;
7879a747e4fSDavid du Colombier 
788f8e525acSDavid du Colombier 	n = 0;
789f8e525acSDavid du Colombier 	for(; mask; mask>>=1)
790f8e525acSDavid du Colombier 		n += mask&1;
791f8e525acSDavid du Colombier 	return n;
7929a747e4fSDavid du Colombier }
7939a747e4fSDavid du Colombier 
794f8e525acSDavid du Colombier typedef struct Col Col;
795f8e525acSDavid du Colombier struct Col {
796f8e525acSDavid du Colombier 	int type;
797f8e525acSDavid du Colombier 	int nbits;
798f8e525acSDavid du Colombier 	int shift;
7999a747e4fSDavid du Colombier };
8009a747e4fSDavid du Colombier 
8019a747e4fSDavid du Colombier static ulong
fmt2chan(Pixfmt * fmt)8029a747e4fSDavid du Colombier fmt2chan(Pixfmt *fmt)
8039a747e4fSDavid du Colombier {
804f8e525acSDavid du Colombier 	Col c[4], t;
805f8e525acSDavid du Colombier 	int i, j, depth, n, nc;
806f8e525acSDavid du Colombier 	ulong mask, u;
8079a747e4fSDavid du Colombier 
808f8e525acSDavid du Colombier 	/* unpack the Pixfmt channels */
809f8e525acSDavid du Colombier 	c[0] = (Col){CRed, nbits(fmt->red.max), fmt->red.shift};
810f8e525acSDavid du Colombier 	c[1] = (Col){CGreen, nbits(fmt->green.max), fmt->green.shift};
811f8e525acSDavid du Colombier 	c[2] = (Col){CBlue, nbits(fmt->blue.max), fmt->blue.shift};
812f8e525acSDavid du Colombier 	nc = 3;
8139a747e4fSDavid du Colombier 
814f8e525acSDavid du Colombier 	/* add an ignore channel if necessary */
815f8e525acSDavid du Colombier 	depth = c[0].nbits+c[1].nbits+c[2].nbits;
816f8e525acSDavid du Colombier 	if(fmt->bpp != depth){
817f8e525acSDavid du Colombier 		/* BUG: assumes only one run of ignored bits */
818f8e525acSDavid du Colombier 		if(fmt->bpp == 32)
819f8e525acSDavid du Colombier 			mask = ~0;
820f8e525acSDavid du Colombier 		else
821f8e525acSDavid du Colombier 			mask = (1<<fmt->bpp)-1;
822f8e525acSDavid du Colombier 		mask ^= fmt->red.max << fmt->red.shift;
823f8e525acSDavid du Colombier 		mask ^= fmt->green.max << fmt->green.shift;
824f8e525acSDavid du Colombier 		mask ^= fmt->blue.max << fmt->blue.shift;
825f8e525acSDavid du Colombier 		if(mask == 0)
826f8e525acSDavid du Colombier 			abort();
827f8e525acSDavid du Colombier 		n = 0;
828f8e525acSDavid du Colombier 		for(; !(mask&1); mask>>=1)
829f8e525acSDavid du Colombier 			n++;
830f8e525acSDavid du Colombier 		c[3] = (Col){CIgnore, nbits(mask), n};
831f8e525acSDavid du Colombier 		nc++;
8329a747e4fSDavid du Colombier 	}
8339a747e4fSDavid du Colombier 
834f8e525acSDavid du Colombier 	/* sort the channels, largest shift (leftmost bits) first */
835f8e525acSDavid du Colombier 	for(i=1; i<nc; i++)
836f8e525acSDavid du Colombier 		for(j=i; j>0; j--)
837f8e525acSDavid du Colombier 			if(c[j].shift > c[j-1].shift){
838f8e525acSDavid du Colombier 				t = c[j];
839f8e525acSDavid du Colombier 				c[j] = c[j-1];
840f8e525acSDavid du Colombier 				c[j-1] = t;
8419a747e4fSDavid du Colombier 			}
8429a747e4fSDavid du Colombier 
843f8e525acSDavid du Colombier 	/* build the channel descriptor */
844f8e525acSDavid du Colombier 	u = 0;
845f8e525acSDavid du Colombier 	for(i=0; i<nc; i++){
846f8e525acSDavid du Colombier 		u <<= 8;
847f8e525acSDavid du Colombier 		u |= CHAN1(c[i].type, c[i].nbits);
8489a747e4fSDavid du Colombier 	}
849f8e525acSDavid du Colombier 
850f8e525acSDavid du Colombier 	return u;
851f8e525acSDavid du Colombier }
8529a747e4fSDavid du Colombier 
8539a747e4fSDavid du Colombier static void
chan2fmt(Pixfmt * fmt,ulong chan)8549a747e4fSDavid du Colombier chan2fmt(Pixfmt *fmt, ulong chan)
8559a747e4fSDavid du Colombier {
8569a747e4fSDavid du Colombier 	ulong c, rc, shift;
8579a747e4fSDavid du Colombier 
8589a747e4fSDavid du Colombier 	shift = 0;
8599a747e4fSDavid du Colombier 	for(rc = chan; rc; rc >>=8){
8609a747e4fSDavid du Colombier 		c = rc & 0xFF;
8619a747e4fSDavid du Colombier 		switch(TYPE(c)){
8629a747e4fSDavid du Colombier 		case CRed:
8639a747e4fSDavid du Colombier 			fmt->red = (Colorfmt){(1<<NBITS(c))-1, shift};
8649a747e4fSDavid du Colombier 			break;
8659a747e4fSDavid du Colombier 		case CBlue:
8669a747e4fSDavid du Colombier 			fmt->blue = (Colorfmt){(1<<NBITS(c))-1, shift};
8679a747e4fSDavid du Colombier 			break;
8689a747e4fSDavid du Colombier 		case CGreen:
8699a747e4fSDavid du Colombier 			fmt->green = (Colorfmt){(1<<NBITS(c))-1, shift};
8709a747e4fSDavid du Colombier 			break;
8719a747e4fSDavid du Colombier 		}
8729a747e4fSDavid du Colombier 		shift += NBITS(c);
8739a747e4fSDavid du Colombier 	}
8749a747e4fSDavid du Colombier }
8759a747e4fSDavid du Colombier 
876f8e525acSDavid du Colombier /*
877f8e525acSDavid du Colombier  * Note that r has changed on the screen.
878f8e525acSDavid du Colombier  * Updating the rlists is okay because they are protected by drawlock.
879f8e525acSDavid du Colombier  */
8809a747e4fSDavid du Colombier void
flushmemscreen(Rectangle r)881f8e525acSDavid du Colombier flushmemscreen(Rectangle r)
8829a747e4fSDavid du Colombier {
8839a747e4fSDavid du Colombier 	Vncs *v;
8849a747e4fSDavid du Colombier 
885f8e525acSDavid du Colombier 	if(!rectclip(&r, gscreen->r))
886f8e525acSDavid du Colombier 		return;
887f8e525acSDavid du Colombier 	qlock(&clients);
888f8e525acSDavid du Colombier 	for(v=clients.head; v; v=v->next)
889f8e525acSDavid du Colombier 		addtorlist(&v->rlist, r);
890f8e525acSDavid du Colombier 	qunlock(&clients);
8919a747e4fSDavid du Colombier }
8929a747e4fSDavid du Colombier 
893f8e525acSDavid du Colombier /*
894f8e525acSDavid du Colombier  * Queue a mouse warp note for the next update to each client.
895f8e525acSDavid du Colombier  */
896d9306527SDavid du Colombier void
mousewarpnote(Point p)897d9306527SDavid du Colombier mousewarpnote(Point p)
8989a747e4fSDavid du Colombier {
8999a747e4fSDavid du Colombier 	Vncs *v;
9009a747e4fSDavid du Colombier 
901f8e525acSDavid du Colombier 	qlock(&clients);
902f8e525acSDavid du Colombier 	for(v=clients.head; v; v=v->next){
903d9306527SDavid du Colombier 		if(v->canwarp){
9049a747e4fSDavid du Colombier 			vnclock(v);
905d9306527SDavid du Colombier 			v->needwarp = 1;
906d9306527SDavid du Colombier 			v->warppt = p;
9079a747e4fSDavid du Colombier 			vncunlock(v);
9089a747e4fSDavid du Colombier 		}
9099a747e4fSDavid du Colombier 	}
910f8e525acSDavid du Colombier 	qunlock(&clients);
9119a747e4fSDavid du Colombier }
9129a747e4fSDavid du Colombier 
913f8e525acSDavid du Colombier /*
914f8e525acSDavid du Colombier  * Send a client his changed screen image.
915f8e525acSDavid du Colombier  * v is locked on entrance, locked on exit, but released during.
916f8e525acSDavid du Colombier  */
9179a747e4fSDavid du Colombier static int
updateimage(Vncs * v)918f8e525acSDavid du Colombier updateimage(Vncs *v)
9199a747e4fSDavid du Colombier {
920f8e525acSDavid du Colombier 	int i, ncount, nsend, docursor, needwarp;
921f8e525acSDavid du Colombier 	vlong ooffset;
922f8e525acSDavid du Colombier 	Point warppt;
923f8e525acSDavid du Colombier 	Rectangle cr;
924f8e525acSDavid du Colombier 	Rlist rlist;
9259a747e4fSDavid du Colombier 	vlong t1;
926f8e525acSDavid du Colombier 	int (*count)(Vncs*, Rectangle);
927f8e525acSDavid du Colombier 	int (*send)(Vncs*, Rectangle);
9289a747e4fSDavid du Colombier 
9290b459c2cSDavid du Colombier 	if(v->image == nil)
9300b459c2cSDavid du Colombier 		return 0;
9310b459c2cSDavid du Colombier 
932f8e525acSDavid du Colombier 	/* warping info and unlock v so that updates can proceed */
933f8e525acSDavid du Colombier 	needwarp = v->canwarp && v->needwarp;
934f8e525acSDavid du Colombier 	warppt = v->warppt;
935f8e525acSDavid du Colombier 	v->needwarp = 0;
936f8e525acSDavid du Colombier 	vncunlock(v);
937f8e525acSDavid du Colombier 
938f8e525acSDavid du Colombier 	/* copy the screen bits and then unlock the screen so updates can proceed */
9399a747e4fSDavid du Colombier 	drawlock();
940f8e525acSDavid du Colombier 	rlist = v->rlist;
941f8e525acSDavid du Colombier 	memset(&v->rlist, 0, sizeof v->rlist);
9429a747e4fSDavid du Colombier 
943f8e525acSDavid du Colombier 	/* if the cursor has moved or changed shape, we need to redraw its square */
9449a747e4fSDavid du Colombier 	lock(&cursor);
9459a747e4fSDavid du Colombier 	if(v->cursorver != cursorver || !eqpt(v->cursorpos, cursorpos)){
9469a747e4fSDavid du Colombier 		docursor = 1;
9479a747e4fSDavid du Colombier 		v->cursorver = cursorver;
9489a747e4fSDavid du Colombier 		v->cursorpos = cursorpos;
9499a747e4fSDavid du Colombier 		cr = cursorrect();
950f8e525acSDavid du Colombier 	}else{
951f8e525acSDavid du Colombier 		docursor = 0;
9529a747e4fSDavid du Colombier 		cr = v->cursorr;
953f8e525acSDavid du Colombier 	}
9549a747e4fSDavid du Colombier 	unlock(&cursor);
955f8e525acSDavid du Colombier 
956f8e525acSDavid du Colombier 	if(docursor){
957f8e525acSDavid du Colombier 		addtorlist(&rlist, v->cursorr);
958f8e525acSDavid du Colombier 		if(!rectclip(&cr, gscreen->r))
959f8e525acSDavid du Colombier 			cr.max = cr.min;
960f8e525acSDavid du Colombier 		addtorlist(&rlist, cr);
961f8e525acSDavid du Colombier 	}
962f8e525acSDavid du Colombier 
963f8e525acSDavid du Colombier 	/* copy changed screen parts, also check for parts overlapping cursor location */
964f8e525acSDavid du Colombier 	for(i=0; i<rlist.nrect; i++){
9659a747e4fSDavid du Colombier 		if(!docursor)
966f8e525acSDavid du Colombier 			docursor = rectXrect(v->cursorr, rlist.rect[i]);
967f8e525acSDavid du Colombier 		memimagedraw(v->image, rlist.rect[i], gscreen, rlist.rect[i].min, memopaque, ZP, S);
9689a747e4fSDavid du Colombier 	}
9699a747e4fSDavid du Colombier 
9709a747e4fSDavid du Colombier 	if(docursor){
971f8e525acSDavid du Colombier 		cursordraw(v->image, cr);
972f8e525acSDavid du Colombier 		addtorlist(&rlist, v->cursorr);
9739a747e4fSDavid du Colombier 		v->cursorr = cr;
9749a747e4fSDavid du Colombier 	}
975f8e525acSDavid du Colombier 
9769a747e4fSDavid du Colombier 	drawunlock();
977f8e525acSDavid du Colombier 
978f8e525acSDavid du Colombier 	ooffset = Boffset(&v->out);
979f8e525acSDavid du Colombier 	/* no more locks are held; talk to the client */
980f8e525acSDavid du Colombier 
981f8e525acSDavid du Colombier 	if(rlist.nrect == 0 && needwarp == 0){
982f8e525acSDavid du Colombier 		vnclock(v);
983f8e525acSDavid du Colombier 		return 0;
9849a747e4fSDavid du Colombier 	}
9859a747e4fSDavid du Colombier 
986f8e525acSDavid du Colombier 	count = v->countrect;
987f8e525acSDavid du Colombier 	send = v->sendrect;
988f8e525acSDavid du Colombier 	if(count == nil || send == nil){
989f8e525acSDavid du Colombier 		count = countraw;
990f8e525acSDavid du Colombier 		send = sendraw;
991f8e525acSDavid du Colombier 	}
992f8e525acSDavid du Colombier 
993f8e525acSDavid du Colombier 	ncount = 0;
994f8e525acSDavid du Colombier 	for(i=0; i<rlist.nrect; i++)
995f8e525acSDavid du Colombier 		ncount += (*count)(v, rlist.rect[i]);
9969a747e4fSDavid du Colombier 
9979a747e4fSDavid du Colombier 	if(verbose > 1)
998f8e525acSDavid du Colombier 		fprint(2, "sendupdate: rlist.nrect=%d, ncount=%d", rlist.nrect, ncount);
999f8e525acSDavid du Colombier 
10009a747e4fSDavid du Colombier 	t1 = nsec();
10019a747e4fSDavid du Colombier 	vncwrchar(v, MFrameUpdate);
10029a747e4fSDavid du Colombier 	vncwrchar(v, 0);
1003f8e525acSDavid du Colombier 	vncwrshort(v, ncount+needwarp);
10049a747e4fSDavid du Colombier 
1005f8e525acSDavid du Colombier 	nsend = 0;
1006f8e525acSDavid du Colombier 	for(i=0; i<rlist.nrect; i++)
1007f8e525acSDavid du Colombier 		nsend += (*send)(v, rlist.rect[i]);
10089a747e4fSDavid du Colombier 
1009f8e525acSDavid du Colombier 	if(ncount != nsend){
1010f8e525acSDavid du Colombier 		fprint(2, "%V: ncount=%d, nsend=%d; hanging up\n", v, ncount, nsend);
1011f8e525acSDavid du Colombier 		vnchungup(v);
10129a747e4fSDavid du Colombier 	}
1013f8e525acSDavid du Colombier 
1014f8e525acSDavid du Colombier 	if(needwarp){
1015f8e525acSDavid du Colombier 		vncwrrect(v, Rect(warppt.x, warppt.y, warppt.x+1, warppt.y+1));
10169a747e4fSDavid du Colombier 		vncwrlong(v, EncMouseWarp);
10179a747e4fSDavid du Colombier 	}
10189a747e4fSDavid du Colombier 
1019f8e525acSDavid du Colombier 	t1 = nsec() - t1;
1020f8e525acSDavid du Colombier 	if(verbose > 1)
1021f8e525acSDavid du Colombier 		fprint(2, " in %lldms, %lld bytes\n", t1/1000000, Boffset(&v->out) - ooffset);
1022f8e525acSDavid du Colombier 
1023f8e525acSDavid du Colombier 	freerlist(&rlist);
1024f8e525acSDavid du Colombier 	vnclock(v);
1025f8e525acSDavid du Colombier 	return 1;
10269a747e4fSDavid du Colombier }
10279a747e4fSDavid du Colombier 
1028f8e525acSDavid du Colombier /*
1029f8e525acSDavid du Colombier  * Update the snarf buffer if it has changed.
1030f8e525acSDavid du Colombier  */
10319a747e4fSDavid du Colombier static void
updatesnarf(Vncs * v)1032f8e525acSDavid du Colombier updatesnarf(Vncs *v)
10339a747e4fSDavid du Colombier {
10349a747e4fSDavid du Colombier 	char *buf;
1035f8e525acSDavid du Colombier 	int len;
10369a747e4fSDavid du Colombier 
1037f8e525acSDavid du Colombier 	if(v->snarfvers == snarf.vers)
1038f8e525acSDavid du Colombier 		return;
10399a747e4fSDavid du Colombier 	vncunlock(v);
10409a747e4fSDavid du Colombier 	qlock(&snarf);
1041f8e525acSDavid du Colombier 	len = snarf.n;
1042f8e525acSDavid du Colombier 	buf = malloc(len);
1043f8e525acSDavid du Colombier 	if(buf == nil){
1044f8e525acSDavid du Colombier 		qunlock(&snarf);
1045f8e525acSDavid du Colombier 		vnclock(v);
1046f8e525acSDavid du Colombier 		return;
1047f8e525acSDavid du Colombier 	}
1048f8e525acSDavid du Colombier 	memmove(buf, snarf.buf, len);
10499a747e4fSDavid du Colombier 	v->snarfvers = snarf.vers;
10509a747e4fSDavid du Colombier 	qunlock(&snarf);
10519a747e4fSDavid du Colombier 
10529a747e4fSDavid du Colombier 	vncwrchar(v, MSCut);
10539a747e4fSDavid du Colombier 	vncwrbytes(v, "pad", 3);
1054f8e525acSDavid du Colombier 	vncwrlong(v, len);
1055f8e525acSDavid du Colombier 	vncwrbytes(v, buf, len);
10569a747e4fSDavid du Colombier 	free(buf);
1057d9306527SDavid du Colombier 	vnclock(v);
10589a747e4fSDavid du Colombier }
10599a747e4fSDavid du Colombier 
1060f8e525acSDavid du Colombier /*
1061f8e525acSDavid du Colombier  * Continually update one client.
1062f8e525acSDavid du Colombier  */
10639a747e4fSDavid du Colombier static void
clientwriteproc(Vncs * v)1064f8e525acSDavid du Colombier clientwriteproc(Vncs *v)
10659a747e4fSDavid du Colombier {
10660b459c2cSDavid du Colombier 	char buf[32], buf2[32];
1067f8e525acSDavid du Colombier 	int sent;
10689a747e4fSDavid du Colombier 
1069f8e525acSDavid du Colombier 	vncname("write %V", v);
1070d9306527SDavid du Colombier 	for(;;){
10719a747e4fSDavid du Colombier 		vnclock(v);
1072f8e525acSDavid du Colombier 		if(v->ndead)
1073f8e525acSDavid du Colombier 			break;
10740b459c2cSDavid du Colombier 		if((v->image == nil && v->imagechan!=0)
10750b459c2cSDavid du Colombier 		|| (v->image && v->image->chan != v->imagechan)){
10760b459c2cSDavid du Colombier 			if(v->image)
10770b459c2cSDavid du Colombier 				freememimage(v->image);
10780b459c2cSDavid du Colombier 			v->image = allocmemimage(Rpt(ZP, v->dim), v->imagechan);
10790b459c2cSDavid du Colombier 			if(v->image == nil){
10800b459c2cSDavid du Colombier 				fprint(2, "%V: allocmemimage: %r; hanging up\n", v);
10810b459c2cSDavid du Colombier 				vnchungup(v);
10820b459c2cSDavid du Colombier 			}
10830b459c2cSDavid du Colombier 			if(verbose)
10840b459c2cSDavid du Colombier 				fprint(2, "%V: translating image from chan=%s to chan=%s\n",
10850b459c2cSDavid du Colombier 					v, chantostr(buf, gscreen->chan), chantostr(buf2, v->imagechan));
10860b459c2cSDavid du Colombier 		}
1087f8e525acSDavid du Colombier 		sent = 0;
1088f8e525acSDavid du Colombier 		if(v->updaterequest){
1089f8e525acSDavid du Colombier 			v->updaterequest = 0;
1090f8e525acSDavid du Colombier 			updatesnarf(v);
1091f8e525acSDavid du Colombier 			sent = updateimage(v);
1092f8e525acSDavid du Colombier 			if(!sent)
1093f8e525acSDavid du Colombier 				v->updaterequest = 1;
1094f8e525acSDavid du Colombier 		}
10959a747e4fSDavid du Colombier 		vncunlock(v);
1096f8e525acSDavid du Colombier 		vncflush(v);
1097f8e525acSDavid du Colombier 		if(!sent)
1098f8e525acSDavid du Colombier 			sleep(sleeptime);
10999a747e4fSDavid du Colombier 	}
11009a747e4fSDavid du Colombier 	vncunlock(v);
1101f8e525acSDavid du Colombier 	vnchungup(v);
11029a747e4fSDavid du Colombier }
11039a747e4fSDavid du Colombier 
1104