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