/usr.bin/csup/proto.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 1006 lines · 827 code · 60 blank · 119 comment · 252 complexity · 0c37925f01509670ecb3b301f44d1c73 MD5 · raw file

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