proto.c revision 186781
1/*-
2 * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/contrib/csup/proto.c 186781 2009-01-05 15:18:16Z lulf $
27 */
28
29#include <sys/param.h>
30#include <sys/select.h>
31#include <sys/socket.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34
35#include <assert.h>
36#include <err.h>
37#include <errno.h>
38#include <netdb.h>
39#include <pthread.h>
40#include <signal.h>
41#include <stdarg.h>
42#include <stddef.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47
48#include "config.h"
49#include "detailer.h"
50#include "fattr.h"
51#include "fixups.h"
52#include "globtree.h"
53#include "keyword.h"
54#include "lister.h"
55#include "misc.h"
56#include "mux.h"
57#include "proto.h"
58#include "queue.h"
59#include "stream.h"
60#include "threads.h"
61#include "updater.h"
62
63struct killer {
64	pthread_t thread;
65	sigset_t sigset;
66	struct mux *mux;
67	int killedby;
68};
69
70static void		 killer_start(struct killer *, struct mux *);
71static void		*killer_run(void *);
72static void		 killer_stop(struct killer *);
73
74static int		 proto_waitconnect(int);
75static int		 proto_greet(struct config *);
76static int		 proto_negproto(struct config *);
77static int		 proto_login(struct config *);
78static int		 proto_fileattr(struct config *);
79static int		 proto_xchgcoll(struct config *);
80static struct mux	*proto_mux(struct config *);
81
82static int		 proto_escape(struct stream *, const char *);
83static void		 proto_unescape(char *);
84
85static int
86proto_waitconnect(int s)
87{
88	fd_set readfd;
89	socklen_t len;
90	int error, rv, soerror;
91
92	FD_ZERO(&readfd);
93	FD_SET(s, &readfd);
94
95	do {
96		rv = select(s + 1, &readfd, NULL, NULL, NULL);
97	} while (rv == -1 && errno == EINTR);
98	if (rv == -1)
99		return (-1);
100	/* Check that the connection was really successful. */
101	len = sizeof(soerror);
102	error = getsockopt(s, SOL_SOCKET, SO_ERROR, &soerror, &len);
103	if (error) {
104		/* We have no choice but faking an error here. */
105		errno = ECONNREFUSED;
106		return (-1);
107	}
108	if (soerror) {
109		errno = soerror;
110		return (-1);
111	}
112	return (0);
113}
114
115/* Connect to the CVSup server. */
116int
117proto_connect(struct config *config, int family, uint16_t port)
118{
119	char addrbuf[NI_MAXHOST];
120	/* Enough to hold sizeof("cvsup") or any port number. */
121	char servname[8];
122	struct addrinfo *res, *ai, hints;
123	int error, opt, s;
124
125	s = -1;
126	if (port != 0)
127		snprintf(servname, sizeof(servname), "%d", port);
128	else {
129		strncpy(servname, "cvsup", sizeof(servname) - 1);
130		servname[sizeof(servname) - 1] = '\0';
131	}
132	memset(&hints, 0, sizeof(hints));
133	hints.ai_family = family;
134	hints.ai_socktype = SOCK_STREAM;
135	error = getaddrinfo(config->host, servname, &hints, &res);
136	/*
137	 * Try with the hardcoded port number for OSes that don't
138	 * have cvsup defined in the /etc/services file.
139	 */
140	if (error == EAI_SERVICE) {
141		strncpy(servname, "5999", sizeof(servname) - 1);
142		servname[sizeof(servname) - 1] = '\0';
143		error = getaddrinfo(config->host, servname, &hints, &res);
144	}
145	if (error) {
146		lprintf(0, "Name lookup failure for \"%s\": %s\n", config->host,
147		    gai_strerror(error));
148		return (STATUS_TRANSIENTFAILURE);
149	}
150	for (ai = res; ai != NULL; ai = ai->ai_next) {
151		s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
152		if (s != -1) {
153			error = 0;
154			if (config->laddr != NULL) {
155				opt = 1;
156				(void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
157				    &opt, sizeof(opt));
158				error = bind(s, config->laddr,
159				    config->laddrlen);
160			}
161			if (!error) {
162				error = connect(s, ai->ai_addr, ai->ai_addrlen);
163				if (error && errno == EINTR)
164					error = proto_waitconnect(s);
165			}
166			if (error)
167				close(s);
168		}
169		(void)getnameinfo(ai->ai_addr, ai->ai_addrlen, addrbuf,
170		    sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
171		if (s == -1 || error) {
172			lprintf(0, "Cannot connect to %s: %s\n", addrbuf,
173			    strerror(errno));
174			continue;
175		}
176		lprintf(1, "Connected to %s\n", addrbuf);
177		freeaddrinfo(res);
178		config->socket = s;
179		return (STATUS_SUCCESS);
180	}
181	freeaddrinfo(res);
182	return (STATUS_TRANSIENTFAILURE);
183}
184
185/* Greet the server. */
186static int
187proto_greet(struct config *config)
188{
189	char *line, *cmd, *msg, *swver;
190	struct stream *s;
191
192	s = config->server;
193	line = stream_getln(s, NULL);
194	cmd = proto_get_ascii(&line);
195	if (cmd == NULL)
196		goto bad;
197	if (strcmp(cmd, "OK") == 0) {
198		(void)proto_get_ascii(&line);	/* major number */
199		(void)proto_get_ascii(&line);	/* minor number */
200		swver = proto_get_ascii(&line);
201	} else if (strcmp(cmd, "!") == 0) {
202		msg = proto_get_rest(&line);
203		if (msg == NULL)
204			goto bad;
205		lprintf(-1, "Rejected by server: %s\n", msg);
206		return (STATUS_TRANSIENTFAILURE);
207	} else
208		goto bad;
209	lprintf(2, "Server software version: %s\n",
210	    swver != NULL ? swver : ".");
211	return (STATUS_SUCCESS);
212bad:
213	lprintf(-1, "Invalid greeting from server\n");
214	return (STATUS_FAILURE);
215}
216
217/* Negotiate protocol version with the server. */
218static int
219proto_negproto(struct config *config)
220{
221	struct stream *s;
222	char *cmd, *line, *msg;
223	int error, maj, min;
224
225	s = config->server;
226	proto_printf(s, "PROTO %d %d %s\n", PROTO_MAJ, PROTO_MIN, PROTO_SWVER);
227	stream_flush(s);
228	line = stream_getln(s, NULL);
229	cmd = proto_get_ascii(&line);
230	if (cmd == NULL || line == NULL)
231		goto bad;
232	if (strcmp(cmd, "!") == 0) {
233		msg = proto_get_rest(&line);
234		lprintf(-1, "Protocol negotiation failed: %s\n", msg);
235		return (1);
236	} else if (strcmp(cmd, "PROTO") != 0)
237		goto bad;
238	error = proto_get_int(&line, &maj, 10);
239	if (!error)
240		error = proto_get_int(&line, &min, 10);
241	if (error)
242		goto bad;
243	if (maj != PROTO_MAJ || min != PROTO_MIN) {
244		lprintf(-1, "Server protocol version %d.%d not supported "
245		    "by client\n", maj, min);
246		return (STATUS_FAILURE);
247	}
248	return (STATUS_SUCCESS);
249bad:
250	lprintf(-1, "Invalid PROTO command from server\n");
251	return (STATUS_FAILURE);
252}
253
254static int
255proto_login(struct config *config)
256{
257	struct stream *s;
258	char hostbuf[MAXHOSTNAMELEN];
259	char *line, *login, *host, *cmd, *realm, *challenge, *msg;
260	int error;
261
262	s = config->server;
263	error = gethostname(hostbuf, sizeof(hostbuf));
264	hostbuf[sizeof(hostbuf) - 1] = '\0';
265	if (error)
266		host = NULL;
267	else
268		host = hostbuf;
269	login = getlogin();
270	proto_printf(s, "USER %s %s\n", login != NULL ? login : "?",
271	    host != NULL ? host : "?");
272	stream_flush(s);
273	line = stream_getln(s, NULL);
274	cmd = proto_get_ascii(&line);
275	realm = proto_get_ascii(&line);
276	challenge = proto_get_ascii(&line);
277	if (challenge == NULL || line != NULL)
278		goto bad;
279	if (strcmp(realm, ".") != 0 || strcmp(challenge, ".") != 0) {
280		lprintf(-1, "Authentication required by the server and not "
281		    "supported by client\n");
282		return (STATUS_FAILURE);
283	}
284	proto_printf(s, "AUTHMD5 . . .\n");
285	stream_flush(s);
286	line = stream_getln(s, NULL);
287	cmd = proto_get_ascii(&line);
288	if (cmd == NULL || line == NULL)
289		goto bad;
290	if (strcmp(cmd, "OK") == 0)
291		return (STATUS_SUCCESS);
292	if (strcmp(cmd, "!") == 0) {
293		msg = proto_get_rest(&line);
294		if (msg == NULL)
295			goto bad;
296		lprintf(-1, "Server error: %s\n", msg);
297		return (STATUS_FAILURE);
298	}
299bad:
300	lprintf(-1, "Invalid server reply to AUTHMD5\n");
301	return (STATUS_FAILURE);
302}
303
304/*
305 * File attribute support negotiation.
306 */
307static int
308proto_fileattr(struct config *config)
309{
310	fattr_support_t support;
311	struct stream *s;
312	char *line, *cmd;
313	int error, i, n, attr;
314
315	s = config->server;
316	lprintf(2, "Negotiating file attribute support\n");
317	proto_printf(s, "ATTR %d\n", FT_NUMBER);
318	for (i = 0; i < FT_NUMBER; i++)
319		proto_printf(s, "%x\n", fattr_supported(i));
320	proto_printf(s, ".\n");
321	stream_flush(s);
322	line = stream_getln(s, NULL);
323	if (line == NULL)
324		goto bad;
325	cmd = proto_get_ascii(&line);
326	error = proto_get_int(&line, &n, 10);
327	if (error || line != NULL || strcmp(cmd, "ATTR") != 0 || n > FT_NUMBER)
328		goto bad;
329	for (i = 0; i < n; i++) {
330		line = stream_getln(s, NULL);
331		if (line == NULL)
332			goto bad;
333		error = proto_get_int(&line, &attr, 16);
334		if (error)
335			goto bad;
336		support[i] = fattr_supported(i) & attr;
337	}
338	for (i = n; i < FT_NUMBER; i++)
339		support[i] = 0;
340	line = stream_getln(s, NULL);
341	if (line == NULL || strcmp(line, ".") != 0)
342		goto bad;
343	memcpy(config->fasupport, support, sizeof(config->fasupport));
344	return (STATUS_SUCCESS);
345bad:
346	lprintf(-1, "Protocol error negotiating attribute support\n");
347	return (STATUS_FAILURE);
348}
349
350/*
351 * Exchange collection information.
352 */
353static int
354proto_xchgcoll(struct config *config)
355{
356	struct coll *coll;
357	struct stream *s;
358	struct globtree *diraccept, *dirrefuse;
359	struct globtree *fileaccept, *filerefuse;
360	char *line, *cmd, *collname, *pat;
361	char *msg, *release, *ident, *rcskey, *prefix;
362	size_t i, len;
363	int error, flags, options;
364
365	s = config->server;
366	lprintf(2, "Exchanging collection information\n");
367	STAILQ_FOREACH(coll, &config->colls, co_next) {
368		if (coll->co_options & CO_SKIP)
369			continue;
370		proto_printf(s, "COLL %s %s %o %d\n", coll->co_name,
371		    coll->co_release, coll->co_umask, coll->co_options);
372		for (i = 0; i < pattlist_size(coll->co_accepts); i++) {
373		    proto_printf(s, "ACC %s\n",
374			pattlist_get(coll->co_accepts, i));
375		}
376		for (i = 0; i < pattlist_size(coll->co_refusals); i++) {
377		    proto_printf(s, "REF %s\n",
378			pattlist_get(coll->co_refusals, i));
379		}
380		proto_printf(s, ".\n");
381	}
382	proto_printf(s, ".\n");
383	stream_flush(s);
384
385	STAILQ_FOREACH(coll, &config->colls, co_next) {
386		if (coll->co_options & CO_SKIP)
387			continue;
388		coll->co_norsync = globtree_false();
389		line = stream_getln(s, NULL);
390		if (line == NULL)
391			goto bad;
392		cmd = proto_get_ascii(&line);
393		collname = proto_get_ascii(&line);
394		release = proto_get_ascii(&line);
395		error = proto_get_int(&line, &options, 10);
396		if (error || line != NULL)
397			goto bad;
398		if (strcmp(cmd, "COLL") != 0 ||
399		    strcmp(collname, coll->co_name) != 0 ||
400		    strcmp(release, coll->co_release) != 0)
401			goto bad;
402		coll->co_options =
403		    (coll->co_options | (options & CO_SERVMAYSET)) &
404		    ~(~options & CO_SERVMAYCLEAR);
405		while ((line = stream_getln(s, NULL)) != NULL) {
406		 	if (strcmp(line, ".") == 0)
407				break;
408			cmd = proto_get_ascii(&line);
409			if (cmd == NULL)
410				goto bad;
411			if (strcmp(cmd, "!") == 0) {
412				msg = proto_get_rest(&line);
413				if (msg == NULL)
414					goto bad;
415				lprintf(-1, "Server message: %s\n", msg);
416			} else if (strcmp(cmd, "PRFX") == 0) {
417				prefix = proto_get_ascii(&line);
418				if (prefix == NULL || line != NULL)
419					goto bad;
420				coll->co_cvsroot = xstrdup(prefix);
421			} else if (strcmp(cmd, "KEYALIAS") == 0) {
422				ident = proto_get_ascii(&line);
423				rcskey = proto_get_ascii(&line);
424				if (rcskey == NULL || line != NULL)
425					goto bad;
426				error = keyword_alias(coll->co_keyword, ident,
427				    rcskey);
428				if (error)
429					goto bad;
430			} else if (strcmp(cmd, "KEYON") == 0) {
431				ident = proto_get_ascii(&line);
432				if (ident == NULL || line != NULL)
433					goto bad;
434				error = keyword_enable(coll->co_keyword, ident);
435				if (error)
436					goto bad;
437			} else if (strcmp(cmd, "KEYOFF") == 0) {
438				ident = proto_get_ascii(&line);
439				if (ident == NULL || line != NULL)
440					goto bad;
441				error = keyword_disable(coll->co_keyword,
442				    ident);
443				if (error)
444					goto bad;
445			} else if (strcmp(cmd, "NORS") == 0) {
446				pat = proto_get_ascii(&line);
447				if (pat == NULL || line != NULL)
448					goto bad;
449				coll->co_norsync = globtree_or(coll->co_norsync,
450				    globtree_match(pat, FNM_PATHNAME));
451			} else if (strcmp(cmd, "RNORS") == 0) {
452				pat = proto_get_ascii(&line);
453				if (pat == NULL || line != NULL)
454					goto bad;
455				coll->co_norsync = globtree_or(coll->co_norsync,
456				    globtree_match(pat, FNM_PATHNAME |
457				    FNM_LEADING_DIR));
458			} else
459				goto bad;
460		}
461		if (line == NULL)
462			goto bad;
463		keyword_prepare(coll->co_keyword);
464
465		diraccept = globtree_true();
466		fileaccept = globtree_true();
467		dirrefuse = globtree_false();
468		filerefuse = globtree_false();
469
470		if (pattlist_size(coll->co_accepts) > 0) {
471			globtree_free(diraccept);
472			globtree_free(fileaccept);
473			diraccept = globtree_false();
474			fileaccept = globtree_false();
475			flags = FNM_PATHNAME | FNM_LEADING_DIR |
476			    FNM_PREFIX_DIRS;
477			for (i = 0; i < pattlist_size(coll->co_accepts); i++) {
478				pat = pattlist_get(coll->co_accepts, i);
479				diraccept = globtree_or(diraccept,
480				    globtree_match(pat, flags));
481
482				len = strlen(pat);
483				if (coll->co_options & CO_CHECKOUTMODE &&
484				    (len == 0 || pat[len - 1] != '*')) {
485					/* We must modify the pattern so that it
486					   refers to the RCS file, rather than
487					   the checked-out file. */
488					xasprintf(&pat, "%s,v", pat);
489					fileaccept = globtree_or(fileaccept,
490					    globtree_match(pat, flags));
491					free(pat);
492				} else {
493					fileaccept = globtree_or(fileaccept,
494					    globtree_match(pat, flags));
495				}
496			}
497		}
498
499		for (i = 0; i < pattlist_size(coll->co_refusals); i++) {
500			pat = pattlist_get(coll->co_refusals, i);
501			dirrefuse = globtree_or(dirrefuse,
502			    globtree_match(pat, 0));
503			len = strlen(pat);
504			if (coll->co_options & CO_CHECKOUTMODE &&
505			    (len == 0 || pat[len - 1] != '*')) {
506				/* We must modify the pattern so that it refers
507				   to the RCS file, rather than the checked-out
508				   file. */
509				xasprintf(&pat, "%s,v", pat);
510				filerefuse = globtree_or(filerefuse,
511				    globtree_match(pat, 0));
512				free(pat);
513			} else {
514				filerefuse = globtree_or(filerefuse,
515				    globtree_match(pat, 0));
516			}
517		}
518
519		coll->co_dirfilter = globtree_and(diraccept,
520		    globtree_not(dirrefuse));
521		coll->co_filefilter = globtree_and(fileaccept,
522		    globtree_not(filerefuse));
523
524		/* At this point we don't need the pattern lists anymore. */
525		pattlist_free(coll->co_accepts);
526		pattlist_free(coll->co_refusals);
527		coll->co_accepts = NULL;
528		coll->co_refusals = NULL;
529
530		/* Set up a mask of file attributes that we don't want to sync
531		   with the server. */
532		if (!(coll->co_options & CO_SETOWNER))
533			coll->co_attrignore |= FA_OWNER | FA_GROUP;
534		if (!(coll->co_options & CO_SETMODE))
535			coll->co_attrignore |= FA_MODE;
536		if (!(coll->co_options & CO_SETFLAGS))
537			coll->co_attrignore |= FA_FLAGS;
538	}
539	return (STATUS_SUCCESS);
540bad:
541	lprintf(-1, "Protocol error during collection exchange\n");
542	return (STATUS_FAILURE);
543}
544
545static struct mux *
546proto_mux(struct config *config)
547{
548	struct mux *m;
549	struct stream *s, *wr;
550	struct chan *chan0, *chan1;
551	int id;
552
553	s = config->server;
554	lprintf(2, "Establishing multiplexed-mode data connection\n");
555	proto_printf(s, "MUX\n");
556	stream_flush(s);
557	m = mux_open(config->socket, &chan0);
558	if (m == NULL) {
559		lprintf(-1, "Cannot open the multiplexer\n");
560		return (NULL);
561	}
562	id = chan_listen(m);
563	if (id == -1) {
564		lprintf(-1, "ChannelMux.Listen failed: %s\n", strerror(errno));
565		mux_close(m);
566		return (NULL);
567	}
568	wr = stream_open(chan0, NULL, (stream_writefn_t *)chan_write, NULL);
569	proto_printf(wr, "CHAN %d\n", id);
570	stream_close(wr);
571	chan1 = chan_accept(m, id);
572	if (chan1 == NULL) {
573		lprintf(-1, "ChannelMux.Accept failed: %s\n", strerror(errno));
574		mux_close(m);
575		return (NULL);
576	}
577	config->chan0 = chan0;
578	config->chan1 = chan1;
579	return (m);
580}
581
582/*
583 * Initializes the connection to the CVSup server, that is handle
584 * the protocol negotiation, logging in, exchanging file attributes
585 * support and collections information, and finally run the update
586 * session.
587 */
588int
589proto_run(struct config *config)
590{
591	struct thread_args lister_args;
592	struct thread_args detailer_args;
593	struct thread_args updater_args;
594	struct thread_args *args;
595	struct killer killer;
596	struct threads *workers;
597	struct mux *m;
598	int i, status;
599
600	/*
601	 * We pass NULL for the close() function because we'll reuse
602	 * the socket after the stream is closed.
603	 */
604	config->server = stream_open_fd(config->socket, stream_read_fd,
605	    stream_write_fd, NULL);
606	status = proto_greet(config);
607	if (status == STATUS_SUCCESS)
608		status = proto_negproto(config);
609	if (status == STATUS_SUCCESS)
610		status = proto_login(config);
611	if (status == STATUS_SUCCESS)
612		status = proto_fileattr(config);
613	if (status == STATUS_SUCCESS)
614		status = proto_xchgcoll(config);
615	if (status != STATUS_SUCCESS)
616		return (status);
617
618	/* Multi-threaded action starts here. */
619	m = proto_mux(config);
620	if (m == NULL)
621		return (STATUS_FAILURE);
622
623	stream_close(config->server);
624	config->server = NULL;
625	config->fixups = fixups_new();
626	killer_start(&killer, m);
627
628	/* Start the worker threads. */
629	workers = threads_new();
630	args = &lister_args;
631	args->config = config;
632	args->status = -1;
633	args->errmsg = NULL;
634	args->rd = NULL;
635	args->wr = stream_open(config->chan0,
636	    NULL, (stream_writefn_t *)chan_write, NULL);
637	threads_create(workers, lister, args);
638
639	args = &detailer_args;
640	args->config = config;
641	args->status = -1;
642	args->errmsg = NULL;
643	args->rd = stream_open(config->chan0,
644	    (stream_readfn_t *)chan_read, NULL, NULL);
645	args->wr = stream_open(config->chan1,
646	    NULL, (stream_writefn_t *)chan_write, NULL);
647	threads_create(workers, detailer, args);
648
649	args = &updater_args;
650	args->config = config;
651	args->status = -1;
652	args->errmsg = NULL;
653	args->rd = stream_open(config->chan1,
654	    (stream_readfn_t *)chan_read, NULL, NULL);
655	args->wr = NULL;
656	threads_create(workers, updater, args);
657
658	lprintf(2, "Running\n");
659	/* Wait for all the worker threads to finish. */
660	status = STATUS_SUCCESS;
661	for (i = 0; i < 3; i++) {
662		args = threads_wait(workers);
663		if (args->rd != NULL)
664			stream_close(args->rd);
665		if (args->wr != NULL)
666			stream_close(args->wr);
667		if (args->status != STATUS_SUCCESS) {
668			assert(args->errmsg != NULL);
669			if (status == STATUS_SUCCESS) {
670				status = args->status;
671				/* Shutdown the multiplexer to wake up all
672				   the other threads. */
673				mux_shutdown(m, args->errmsg, status);
674			}
675			free(args->errmsg);
676		}
677	}
678	threads_free(workers);
679	if (status == STATUS_SUCCESS) {
680		lprintf(2, "Shutting down connection to server\n");
681		chan_close(config->chan0);
682		chan_close(config->chan1);
683		chan_wait(config->chan0);
684		chan_wait(config->chan1);
685		mux_shutdown(m, NULL, STATUS_SUCCESS);
686	}
687	killer_stop(&killer);
688	fixups_free(config->fixups);
689	status = mux_close(m);
690	if (status == STATUS_SUCCESS) {
691		lprintf(1, "Finished successfully\n");
692	} else if (status == STATUS_INTERRUPTED) {
693		lprintf(-1, "Interrupted\n");
694		if (killer.killedby != -1)
695			kill(getpid(), killer.killedby);
696	}
697	return (status);
698}
699
700/*
701 * Write a string into the stream, escaping characters as needed.
702 * Characters escaped:
703 *
704 * SPACE	-> "\_"
705 * TAB		->  "\t"
706 * NEWLINE	-> "\n"
707 * CR		-> "\r"
708 * \		-> "\\"
709 */
710static int
711proto_escape(struct stream *wr, const char *s)
712{
713	size_t len;
714	ssize_t n;
715	char c;
716
717	/* Handle characters that need escaping. */
718	do {
719		len = strcspn(s, " \t\r\n\\");
720		n = stream_write(wr, s, len);
721		if (n == -1)
722			return (-1);
723		c = s[len];
724		switch (c) {
725		case ' ':
726			n = stream_write(wr, "\\_", 2);
727			break;
728		case '\t':
729			n = stream_write(wr, "\\t", 2);
730			break;
731		case '\r':
732			n = stream_write(wr, "\\r", 2);
733			break;
734		case '\n':
735			n = stream_write(wr, "\\n", 2);
736			break;
737		case '\\':
738			n = stream_write(wr, "\\\\", 2);
739			break;
740		}
741		if (n == -1)
742			return (-1);
743		s += len + 1;
744	} while (c != '\0');
745	return (0);
746}
747
748/*
749 * A simple printf() implementation specifically tailored for csup.
750 * List of the supported formats:
751 *
752 * %c		Print a char.
753 * %d or %i	Print an int as decimal.
754 * %x		Print an int as hexadecimal.
755 * %o		Print an int as octal.
756 * %t		Print a time_t as decimal.
757 * %s		Print a char * escaping some characters as needed.
758 * %S		Print a char * without escaping.
759 * %f		Print an encoded struct fattr *.
760 * %F		Print an encoded struct fattr *, specifying the supported
761 * 		attributes.
762 */
763int
764proto_printf(struct stream *wr, const char *format, ...)
765{
766	fattr_support_t *support;
767	long long longval;
768	struct fattr *fa;
769	const char *fmt;
770	va_list ap;
771	char *cp, *s, *attr;
772	ssize_t n;
773	size_t size;
774	off_t off;
775	int rv, val, ignore;
776	char c;
777
778	n = 0;
779	rv = 0;
780	fmt = format;
781	va_start(ap, format);
782	while ((cp = strchr(fmt, '%')) != NULL) {
783		if (cp > fmt) {
784			n = stream_write(wr, fmt, cp - fmt);
785			if (n == -1)
786				return (-1);
787		}
788		if (*++cp == '\0')
789			goto done;
790		switch (*cp) {
791		case 'c':
792			c = va_arg(ap, int);
793			rv = stream_printf(wr, "%c", c);
794			break;
795		case 'd':
796		case 'i':
797			val = va_arg(ap, int);
798			rv = stream_printf(wr, "%d", val);
799			break;
800		case 'x':
801			val = va_arg(ap, int);
802			rv = stream_printf(wr, "%x", val);
803			break;
804		case 'o':
805			val = va_arg(ap, int);
806			rv = stream_printf(wr, "%o", val);
807			break;
808		case 'O':
809			off = va_arg(ap, off_t);
810			rv = stream_printf(wr, "%llu", off);
811			break;
812		case 'S':
813			s = va_arg(ap, char *);
814			assert(s != NULL);
815			rv = stream_printf(wr, "%s", s);
816			break;
817		case 's':
818			s = va_arg(ap, char *);
819			assert(s != NULL);
820			rv = proto_escape(wr, s);
821			break;
822		case 't':
823			longval = (long long)va_arg(ap, time_t);
824			rv = stream_printf(wr, "%lld", longval);
825			break;
826		case 'f':
827			fa = va_arg(ap, struct fattr *);
828			attr = fattr_encode(fa, NULL, 0);
829			rv = proto_escape(wr, attr);
830			free(attr);
831			break;
832		case 'F':
833			fa = va_arg(ap, struct fattr *);
834			support = va_arg(ap, fattr_support_t *);
835			ignore = va_arg(ap, int);
836			attr = fattr_encode(fa, *support, ignore);
837			rv = proto_escape(wr, attr);
838			free(attr);
839			break;
840		case 'z':
841			size = va_arg(ap, size_t);
842			rv = stream_printf(wr, "%zu", size);
843			break;
844
845		case '%':
846			n = stream_write(wr, "%", 1);
847			if (n == -1)
848				return (-1);
849			break;
850		}
851		if (rv == -1)
852			return (-1);
853		fmt = cp + 1;
854	}
855	if (*fmt != '\0') {
856		rv = stream_printf(wr, "%s", fmt);
857		if (rv == -1)
858			return (-1);
859	}
860done:
861	va_end(ap);
862	return (0);
863}
864
865/*
866 * Unescape the string, see proto_escape().
867 */
868static void
869proto_unescape(char *s)
870{
871	char *cp, *cp2;
872
873	cp = s;
874	while ((cp = strchr(cp, '\\')) != NULL) {
875		switch (cp[1]) {
876		case '_':
877			*cp = ' ';
878			break;
879		case 't':
880			*cp = '\t';
881			break;
882		case 'r':
883			*cp = '\r';
884			break;
885		case 'n':
886			*cp = '\n';
887			break;
888		case '\\':
889			*cp = '\\';
890			break;
891		default:
892			*cp = *(cp + 1);
893		}
894		cp2 = ++cp;
895		while (*cp2 != '\0') {
896			*cp2 = *(cp2 + 1);
897			cp2++;
898		}
899	}
900}
901
902/*
903 * Get an ascii token in the string.
904 */
905char *
906proto_get_ascii(char **s)
907{
908	char *ret;
909
910	ret = strsep(s, " ");
911	if (ret == NULL)
912		return (NULL);
913	/* Make sure we disallow 0-length fields. */
914	if (*ret == '\0') {
915		*s = NULL;
916		return (NULL);
917	}
918	proto_unescape(ret);
919	return (ret);
920}
921
922/*
923 * Get the rest of the string.
924 */
925char *
926proto_get_rest(char **s)
927{
928	char *ret;
929
930	if (s == NULL)
931		return (NULL);
932	ret = *s;
933	proto_unescape(ret);
934	*s = NULL;
935	return (ret);
936}
937
938/*
939 * Get an int token.
940 */
941int
942proto_get_int(char **s, int *val, int base)
943{
944	char *cp;
945	int error;
946
947	cp = proto_get_ascii(s);
948	if (cp == NULL)
949		return (-1);
950	error = asciitoint(cp, val, base);
951	return (error);
952}
953
954/*
955 * Get a size_t token.
956 */
957int
958proto_get_sizet(char **s, size_t *val, int base)
959{
960	unsigned long long tmp;
961	char *cp, *end;
962
963	cp = proto_get_ascii(s);
964	if (cp == NULL)
965		return (-1);
966	errno = 0;
967	tmp = strtoll(cp, &end, base);
968	if (errno || *end != '\0')
969		return (-1);
970	*val = (size_t)tmp;
971	return (0);
972}
973
974/*
975 * Get a time_t token.
976 *
977 * Ideally, we would use an intmax_t and strtoimax() here, but strtoll()
978 * is more portable and 64bits should be enough for a timestamp.
979 */
980int
981proto_get_time(char **s, time_t *val)
982{
983	long long tmp;
984	char *cp, *end;
985
986	cp = proto_get_ascii(s);
987	if (cp == NULL)
988		return (-1);
989	errno = 0;
990	tmp = strtoll(cp, &end, 10);
991	if (errno || *end != '\0')
992		return (-1);
993	*val = (time_t)tmp;
994	return (0);
995}
996
997/* Start the killer thread.  It is used to protect against some signals
998   during the multi-threaded run so that we can gracefully fail.  */
999static void
1000killer_start(struct killer *k, struct mux *m)
1001{
1002	int error;
1003
1004	k->mux = m;
1005	k->killedby = -1;
1006	sigemptyset(&k->sigset);
1007	sigaddset(&k->sigset, SIGINT);
1008	sigaddset(&k->sigset, SIGHUP);
1009	sigaddset(&k->sigset, SIGTERM);
1010	sigaddset(&k->sigset, SIGPIPE);
1011	pthread_sigmask(SIG_BLOCK, &k->sigset, NULL);
1012	error = pthread_create(&k->thread, NULL, killer_run, k);
1013	if (error)
1014		err(1, "pthread_create");
1015}
1016
1017/* The main loop of the killer thread. */
1018static void *
1019killer_run(void *arg)
1020{
1021	struct killer *k;
1022	int error, sig, old;
1023
1024	k = arg;
1025again:
1026	error = sigwait(&k->sigset, &sig);
1027	assert(!error);
1028	if (sig == SIGINT || sig == SIGHUP || sig == SIGTERM) {
1029		if (k->killedby == -1) {
1030			k->killedby = sig;
1031			/* Ensure we don't get canceled during the shutdown. */
1032			pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
1033			mux_shutdown(k->mux, "Cleaning up ...",
1034			    STATUS_INTERRUPTED);
1035			pthread_setcancelstate(old, NULL);
1036		}
1037	}
1038	goto again;
1039}
1040
1041/* Stop the killer thread. */
1042static void
1043killer_stop(struct killer *k)
1044{
1045	void *val;
1046	int error;
1047
1048	error = pthread_cancel(k->thread);
1049	assert(!error);
1050	pthread_join(k->thread, &val);
1051	assert(val == PTHREAD_CANCELED);
1052	pthread_sigmask(SIG_UNBLOCK, &k->sigset, NULL);
1053}
1054