/usr.bin/csup/config.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 579 lines · 483 code · 41 blank · 55 comment · 171 complexity · 1282a2d41de7a9488c17933047e7e0b7 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/types.h>
  29. #include <sys/stat.h>
  30. #include <errno.h>
  31. #include <fcntl.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <unistd.h>
  36. #include "config.h"
  37. #include "globtree.h"
  38. #include "keyword.h"
  39. #include "misc.h"
  40. #include "parse.h"
  41. #include "stream.h"
  42. #include "token.h"
  43. static int config_parse_refusefiles(struct coll *);
  44. static int config_parse_refusefile(struct coll *, char *);
  45. extern FILE *yyin;
  46. /* These are globals because I can't think of a better way with yacc. */
  47. static STAILQ_HEAD(, coll) colls;
  48. static struct coll *cur_coll;
  49. static struct coll *defaults;
  50. static struct coll *ovcoll;
  51. static int ovmask;
  52. static const char *cfgfile;
  53. /*
  54. * Extract all the configuration information from the config
  55. * file and some command line parameters.
  56. */
  57. struct config *
  58. config_init(const char *file, struct coll *override, int overridemask)
  59. {
  60. struct config *config;
  61. struct coll *coll;
  62. size_t slen;
  63. char *prefix;
  64. int error;
  65. mode_t mask;
  66. config = xmalloc(sizeof(struct config));
  67. memset(config, 0, sizeof(struct config));
  68. STAILQ_INIT(&colls);
  69. defaults = coll_new(NULL);
  70. /* Set the default umask. */
  71. mask = umask(0);
  72. umask(mask);
  73. defaults->co_umask = mask;
  74. ovcoll = override;
  75. ovmask = overridemask;
  76. /* Extract a list of collections from the configuration file. */
  77. cur_coll = coll_new(defaults);
  78. yyin = fopen(file, "r");
  79. if (yyin == NULL) {
  80. lprintf(-1, "Cannot open \"%s\": %s\n", file, strerror(errno));
  81. goto bad;
  82. }
  83. cfgfile = file;
  84. error = yyparse();
  85. fclose(yyin);
  86. if (error)
  87. goto bad;
  88. memcpy(&config->colls, &colls, sizeof(colls));
  89. if (STAILQ_EMPTY(&config->colls)) {
  90. lprintf(-1, "Empty supfile\n");
  91. goto bad;
  92. }
  93. /* Fixup the list of collections. */
  94. STAILQ_FOREACH(coll, &config->colls, co_next) {
  95. if (coll->co_base == NULL)
  96. coll->co_base = xstrdup("/usr/local/etc/cvsup");
  97. if (coll->co_colldir == NULL)
  98. coll->co_colldir = "sup";
  99. if (coll->co_prefix == NULL) {
  100. coll->co_prefix = xstrdup(coll->co_base);
  101. /*
  102. * If prefix is not an absolute pathname, it is
  103. * interpreted relative to base.
  104. */
  105. } else if (coll->co_prefix[0] != '/') {
  106. slen = strlen(coll->co_base);
  107. if (slen > 0 && coll->co_base[slen - 1] != '/')
  108. xasprintf(&prefix, "%s/%s", coll->co_base,
  109. coll->co_prefix);
  110. else
  111. xasprintf(&prefix, "%s%s", coll->co_base,
  112. coll->co_prefix);
  113. free(coll->co_prefix);
  114. coll->co_prefix = prefix;
  115. }
  116. coll->co_prefixlen = strlen(coll->co_prefix);
  117. /* Determine whether to checksum RCS files or not. */
  118. if (coll->co_options & CO_EXACTRCS)
  119. coll->co_options |= CO_CHECKRCS;
  120. else
  121. coll->co_options &= ~CO_CHECKRCS;
  122. /* In recent versions, we always try to set the file modes. */
  123. coll->co_options |= CO_SETMODE;
  124. coll->co_options |= CO_NORSYNC;
  125. error = config_parse_refusefiles(coll);
  126. if (error)
  127. goto bad;
  128. }
  129. coll_free(cur_coll);
  130. coll_free(defaults);
  131. config->host = STAILQ_FIRST(&config->colls)->co_host;
  132. return (config);
  133. bad:
  134. coll_free(cur_coll);
  135. coll_free(defaults);
  136. config_free(config);
  137. return (NULL);
  138. }
  139. int
  140. config_checkcolls(struct config *config)
  141. {
  142. char linkname[4];
  143. struct stat sb;
  144. struct coll *coll;
  145. int error, numvalid, ret;
  146. numvalid = 0;
  147. STAILQ_FOREACH(coll, &config->colls, co_next) {
  148. error = stat(coll->co_prefix, &sb);
  149. if (error || !S_ISDIR(sb.st_mode)) {
  150. /* Skip this collection, and warn about it unless its
  151. prefix is a symbolic link pointing to "SKIP". */
  152. coll->co_options |= CO_SKIP;
  153. ret = readlink(coll->co_prefix, linkname,
  154. sizeof(linkname));
  155. if (ret != 4 || memcmp(linkname, "SKIP", 4) != 0) {
  156. lprintf(-1,"Nonexistent prefix \"%s\" for "
  157. "%s/%s\n", coll->co_prefix, coll->co_name,
  158. coll->co_release);
  159. }
  160. continue;
  161. }
  162. numvalid++;
  163. }
  164. return (numvalid);
  165. }
  166. static int
  167. config_parse_refusefiles(struct coll *coll)
  168. {
  169. char *collstem, *suffix, *supdir, *path;
  170. int error;
  171. if (coll->co_colldir[0] == '/')
  172. supdir = xstrdup(coll->co_colldir);
  173. else
  174. xasprintf(&supdir, "%s/%s", coll->co_base, coll->co_colldir);
  175. /* First, the global refuse file that applies to all collections. */
  176. xasprintf(&path, "%s/refuse", supdir);
  177. error = config_parse_refusefile(coll, path);
  178. free(path);
  179. if (error) {
  180. free(supdir);
  181. return (error);
  182. }
  183. /* Next the per-collection refuse files that applies to all release/tag
  184. combinations. */
  185. xasprintf(&collstem, "%s/%s/refuse", supdir, coll->co_name);
  186. free(supdir);
  187. error = config_parse_refusefile(coll, collstem);
  188. if (error) {
  189. free(collstem);
  190. return (error);
  191. }
  192. /* Finally, the per-release and per-tag refuse file. */
  193. suffix = coll_statussuffix(coll);
  194. if (suffix != NULL) {
  195. xasprintf(&path, "%s%s", collstem, suffix);
  196. free(suffix);
  197. error = config_parse_refusefile(coll, path);
  198. free(path);
  199. }
  200. free(collstem);
  201. return (error);
  202. }
  203. /*
  204. * Parses a "refuse" file, and records the relevant information in
  205. * coll->co_refusals. If the file does not exist, it is silently
  206. * ignored.
  207. */
  208. static int
  209. config_parse_refusefile(struct coll *coll, char *path)
  210. {
  211. struct stream *rd;
  212. char *cp, *line, *pat;
  213. rd = stream_open_file(path, O_RDONLY);
  214. if (rd == NULL)
  215. return (0);
  216. while ((line = stream_getln(rd, NULL)) != NULL) {
  217. pat = line;
  218. for (;;) {
  219. /* Trim leading whitespace. */
  220. pat += strspn(pat, " \t");
  221. if (pat[0] == '\0')
  222. break;
  223. cp = strpbrk(pat, " \t");
  224. if (cp != NULL)
  225. *cp = '\0';
  226. pattlist_add(coll->co_refusals, pat);
  227. if (cp == NULL)
  228. break;
  229. pat = cp + 1;
  230. }
  231. }
  232. if (!stream_eof(rd)) {
  233. stream_close(rd);
  234. lprintf(-1, "Read failure from \"%s\": %s\n", path,
  235. strerror(errno));
  236. return (-1);
  237. }
  238. stream_close(rd);
  239. return (0);
  240. }
  241. void
  242. config_free(struct config *config)
  243. {
  244. struct coll *coll;
  245. while (!STAILQ_EMPTY(&config->colls)) {
  246. coll = STAILQ_FIRST(&config->colls);
  247. STAILQ_REMOVE_HEAD(&config->colls, co_next);
  248. coll_free(coll);
  249. }
  250. if (config->server != NULL)
  251. stream_close(config->server);
  252. if (config->laddr != NULL)
  253. free(config->laddr);
  254. free(config);
  255. }
  256. /* Create a new collection, inheriting options from the default collection. */
  257. struct coll *
  258. coll_new(struct coll *def)
  259. {
  260. struct coll *new;
  261. new = xmalloc(sizeof(struct coll));
  262. memset(new, 0, sizeof(struct coll));
  263. if (def != NULL) {
  264. new->co_options = def->co_options;
  265. new->co_umask = def->co_umask;
  266. if (def->co_host != NULL)
  267. new->co_host = xstrdup(def->co_host);
  268. if (def->co_base != NULL)
  269. new->co_base = xstrdup(def->co_base);
  270. if (def->co_date != NULL)
  271. new->co_date = xstrdup(def->co_date);
  272. if (def->co_prefix != NULL)
  273. new->co_prefix = xstrdup(def->co_prefix);
  274. if (def->co_release != NULL)
  275. new->co_release = xstrdup(def->co_release);
  276. if (def->co_tag != NULL)
  277. new->co_tag = xstrdup(def->co_tag);
  278. if (def->co_listsuffix != NULL)
  279. new->co_listsuffix = xstrdup(def->co_listsuffix);
  280. } else {
  281. new->co_tag = xstrdup(".");
  282. new->co_date = xstrdup(".");
  283. }
  284. new->co_keyword = keyword_new();
  285. new->co_accepts = pattlist_new();
  286. new->co_refusals = pattlist_new();
  287. new->co_attrignore = FA_DEV | FA_INODE;
  288. return (new);
  289. }
  290. void
  291. coll_override(struct coll *coll, struct coll *from, int mask)
  292. {
  293. size_t i;
  294. int newoptions, oldoptions;
  295. newoptions = from->co_options & mask;
  296. oldoptions = coll->co_options & (CO_MASK & ~mask);
  297. if (from->co_release != NULL) {
  298. if (coll->co_release != NULL)
  299. free(coll->co_release);
  300. coll->co_release = xstrdup(from->co_release);
  301. }
  302. if (from->co_host != NULL) {
  303. if (coll->co_host != NULL)
  304. free(coll->co_host);
  305. coll->co_host = xstrdup(from->co_host);
  306. }
  307. if (from->co_base != NULL) {
  308. if (coll->co_base != NULL)
  309. free(coll->co_base);
  310. coll->co_base = xstrdup(from->co_base);
  311. }
  312. if (from->co_colldir != NULL)
  313. coll->co_colldir = from->co_colldir;
  314. if (from->co_prefix != NULL) {
  315. if (coll->co_prefix != NULL)
  316. free(coll->co_prefix);
  317. coll->co_prefix = xstrdup(from->co_prefix);
  318. }
  319. if (newoptions & CO_CHECKOUTMODE) {
  320. if (from->co_tag != NULL) {
  321. if (coll->co_tag != NULL)
  322. free(coll->co_tag);
  323. coll->co_tag = xstrdup(from->co_tag);
  324. }
  325. if (from->co_date != NULL) {
  326. if (coll->co_date != NULL)
  327. free(coll->co_date);
  328. coll->co_date = xstrdup(from->co_date);
  329. }
  330. }
  331. if (from->co_listsuffix != NULL) {
  332. if (coll->co_listsuffix != NULL)
  333. free(coll->co_listsuffix);
  334. coll->co_listsuffix = xstrdup(from->co_listsuffix);
  335. }
  336. for (i = 0; i < pattlist_size(from->co_accepts); i++) {
  337. pattlist_add(coll->co_accepts,
  338. pattlist_get(from->co_accepts, i));
  339. }
  340. for (i = 0; i < pattlist_size(from->co_refusals); i++) {
  341. pattlist_add(coll->co_refusals,
  342. pattlist_get(from->co_refusals, i));
  343. }
  344. coll->co_options = oldoptions | newoptions;
  345. }
  346. char *
  347. coll_statussuffix(struct coll *coll)
  348. {
  349. const char *tag;
  350. char *suffix;
  351. if (coll->co_listsuffix != NULL) {
  352. xasprintf(&suffix, ".%s", coll->co_listsuffix);
  353. } else if (coll->co_options & CO_USERELSUFFIX) {
  354. if (coll->co_tag == NULL)
  355. tag = ".";
  356. else
  357. tag = coll->co_tag;
  358. if (coll->co_release != NULL) {
  359. if (coll->co_options & CO_CHECKOUTMODE) {
  360. xasprintf(&suffix, ".%s:%s",
  361. coll->co_release, tag);
  362. } else {
  363. xasprintf(&suffix, ".%s", coll->co_release);
  364. }
  365. } else if (coll->co_options & CO_CHECKOUTMODE) {
  366. xasprintf(&suffix, ":%s", tag);
  367. }
  368. } else
  369. suffix = NULL;
  370. return (suffix);
  371. }
  372. char *
  373. coll_statuspath(struct coll *coll)
  374. {
  375. char *path, *suffix;
  376. suffix = coll_statussuffix(coll);
  377. if (suffix != NULL) {
  378. if (coll->co_colldir[0] == '/')
  379. xasprintf(&path, "%s/%s/checkouts%s", coll->co_colldir,
  380. coll->co_name, suffix);
  381. else
  382. xasprintf(&path, "%s/%s/%s/checkouts%s", coll->co_base,
  383. coll->co_colldir, coll->co_name, suffix);
  384. } else {
  385. if (coll->co_colldir[0] == '/')
  386. xasprintf(&path, "%s/%s/checkouts", coll->co_colldir,
  387. coll->co_name);
  388. else
  389. xasprintf(&path, "%s/%s/%s/checkouts", coll->co_base,
  390. coll->co_colldir, coll->co_name);
  391. }
  392. free(suffix);
  393. return (path);
  394. }
  395. void
  396. coll_add(char *name)
  397. {
  398. struct coll *coll;
  399. cur_coll->co_name = name;
  400. coll_override(cur_coll, ovcoll, ovmask);
  401. if (cur_coll->co_release == NULL) {
  402. lprintf(-1, "Release not specified for collection "
  403. "\"%s\"\n", cur_coll->co_name);
  404. exit(1);
  405. }
  406. if (cur_coll->co_host == NULL) {
  407. lprintf(-1, "Host not specified for collection "
  408. "\"%s\"\n", cur_coll->co_name);
  409. exit(1);
  410. }
  411. if (!STAILQ_EMPTY(&colls)) {
  412. coll = STAILQ_LAST(&colls, coll, co_next);
  413. if (strcmp(coll->co_host, cur_coll->co_host) != 0) {
  414. lprintf(-1, "All \"host\" fields in the supfile "
  415. "must be the same\n");
  416. exit(1);
  417. }
  418. }
  419. STAILQ_INSERT_TAIL(&colls, cur_coll, co_next);
  420. cur_coll = coll_new(defaults);
  421. }
  422. void
  423. coll_free(struct coll *coll)
  424. {
  425. if (coll == NULL)
  426. return;
  427. if (coll->co_host != NULL)
  428. free(coll->co_host);
  429. if (coll->co_base != NULL)
  430. free(coll->co_base);
  431. if (coll->co_date != NULL)
  432. free(coll->co_date);
  433. if (coll->co_prefix != NULL)
  434. free(coll->co_prefix);
  435. if (coll->co_release != NULL)
  436. free(coll->co_release);
  437. if (coll->co_tag != NULL)
  438. free(coll->co_tag);
  439. if (coll->co_cvsroot != NULL)
  440. free(coll->co_cvsroot);
  441. if (coll->co_name != NULL)
  442. free(coll->co_name);
  443. if (coll->co_listsuffix != NULL)
  444. free(coll->co_listsuffix);
  445. keyword_free(coll->co_keyword);
  446. if (coll->co_dirfilter != NULL)
  447. globtree_free(coll->co_dirfilter);
  448. if (coll->co_dirfilter != NULL)
  449. globtree_free(coll->co_filefilter);
  450. if (coll->co_norsync != NULL)
  451. globtree_free(coll->co_norsync);
  452. if (coll->co_accepts != NULL)
  453. pattlist_free(coll->co_accepts);
  454. if (coll->co_refusals != NULL)
  455. pattlist_free(coll->co_refusals);
  456. free(coll);
  457. }
  458. void
  459. coll_setopt(int opt, char *value)
  460. {
  461. struct coll *coll;
  462. int error, mask;
  463. coll = cur_coll;
  464. switch (opt) {
  465. case PT_HOST:
  466. if (coll->co_host != NULL)
  467. free(coll->co_host);
  468. coll->co_host = value;
  469. break;
  470. case PT_BASE:
  471. if (coll->co_base != NULL)
  472. free(coll->co_base);
  473. coll->co_base = value;
  474. break;
  475. case PT_DATE:
  476. if (coll->co_date != NULL)
  477. free(coll->co_date);
  478. coll->co_date = value;
  479. coll->co_options |= CO_CHECKOUTMODE;
  480. break;
  481. case PT_PREFIX:
  482. if (coll->co_prefix != NULL)
  483. free(coll->co_prefix);
  484. coll->co_prefix = value;
  485. break;
  486. case PT_RELEASE:
  487. if (coll->co_release != NULL)
  488. free(coll->co_release);
  489. coll->co_release = value;
  490. break;
  491. case PT_TAG:
  492. if (coll->co_tag != NULL)
  493. free(coll->co_tag);
  494. coll->co_tag = value;
  495. coll->co_options |= CO_CHECKOUTMODE;
  496. break;
  497. case PT_LIST:
  498. if (strchr(value, '/') != NULL) {
  499. lprintf(-1, "Parse error in \"%s\": \"list\" suffix "
  500. "must not contain slashes\n", cfgfile);
  501. exit(1);
  502. }
  503. if (coll->co_listsuffix != NULL)
  504. free(coll->co_listsuffix);
  505. coll->co_listsuffix = value;
  506. break;
  507. case PT_UMASK:
  508. error = asciitoint(value, &mask, 8);
  509. free(value);
  510. if (error) {
  511. lprintf(-1, "Parse error in \"%s\": Invalid "
  512. "umask value\n", cfgfile);
  513. exit(1);
  514. }
  515. coll->co_umask = mask;
  516. break;
  517. case PT_USE_REL_SUFFIX:
  518. coll->co_options |= CO_USERELSUFFIX;
  519. break;
  520. case PT_DELETE:
  521. coll->co_options |= CO_DELETE | CO_EXACTRCS;
  522. break;
  523. case PT_COMPRESS:
  524. coll->co_options |= CO_COMPRESS;
  525. break;
  526. case PT_NORSYNC:
  527. coll->co_options |= CO_NORSYNC;
  528. break;
  529. }
  530. }
  531. /* Set "coll" as being the default collection. */
  532. void
  533. coll_setdef(void)
  534. {
  535. coll_free(defaults);
  536. defaults = cur_coll;
  537. cur_coll = coll_new(defaults);
  538. }