1 /*-
2 * Copyright (c) 2003-2005 MAEKAWA Masahide <maekawa@cvsync.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 * 3. Neither the name of the author nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <sys/stat.h>
33
34 #include <stdio.h>
35 #include <stdlib.h>
36
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <limits.h>
40 #include <pthread.h>
41 #include <string.h>
42 #include <strings.h>
43 #include <unistd.h>
44
45 #include "compat_stdbool.h"
46 #include "compat_stdint.h"
47 #include "compat_stdio.h"
48 #include "compat_stdlib.h"
49 #include "compat_inttypes.h"
50 #include "compat_limits.h"
51 #include "compat_strings.h"
52
53 #include "attribute.h"
54 #include "collection.h"
55 #include "cvsync.h"
56 #include "hash.h"
57 #include "logmsg.h"
58 #include "network.h"
59 #include "token.h"
60
61 #include "config_common.h"
62 #include "defs.h"
63
64 /* errormode */
65 static const struct token_keyword errormode_keywords[] = {
66 { "abort", 5, CVSYNC_ERRORMODE_ABORT },
67 { "fixup", 5, CVSYNC_ERRORMODE_FIXUP },
68 { "ignore", 6, CVSYNC_ERRORMODE_IGNORE },
69 { NULL, 0, CVSYNC_ERRORMODE_UNSPEC },
70 };
71
72 struct config_args *
config_open(const char * cfname)73 config_open(const char *cfname)
74 {
75 struct config_args *ca;
76 struct stat st;
77 int fd;
78
79 if ((ca = malloc(sizeof(*ca))) == NULL) {
80 logmsg_err("%s: %s", cfname, strerror(errno));
81 return (NULL);
82 }
83
84 if ((fd = open(cfname, O_RDONLY, 0)) == -1) {
85 logmsg_err("%s: %s", cfname, strerror(errno));
86 free(ca);
87 return (NULL);
88 }
89 if (fstat(fd, &st) == -1) {
90 logmsg_err("%s: %s", cfname, strerror(errno));
91 (void)close(fd);
92 free(ca);
93 return (NULL);
94 }
95 ca->ca_mtime = st.st_mtime;
96 if ((ca->ca_fp = fdopen(fd, "r")) == NULL) {
97 logmsg_err("%s: %s", cfname, strerror(errno));
98 (void)close(fd);
99 free(ca);
100 return (NULL);
101 }
102
103 return (ca);
104 }
105
106 bool
config_close(struct config_args * ca)107 config_close(struct config_args *ca)
108 {
109 FILE *fp = ca->ca_fp;
110
111 free(ca);
112
113 if (fclose(fp) == EOF) {
114 logmsg_err("%s", strerror(errno));
115 return (false);
116 }
117
118 return (true);
119 }
120
121 bool
config_insert_collection(struct config * cf,struct collection * cl)122 config_insert_collection(struct config *cf, struct collection *cl)
123 {
124 struct collection *prev = cf->cf_collections;
125
126 if (prev == NULL) {
127 cf->cf_collections = cl;
128 return (true);
129 }
130
131 for (;;) {
132 if ((strcasecmp(prev->cl_name, cl->cl_name) == 0) &&
133 (strcasecmp(prev->cl_release, cl->cl_release) == 0)) {
134 logmsg_err("collection %s/%s: duplicate", cl->cl_name,
135 cl->cl_release);
136 return (false);
137 }
138 if (prev->cl_next == NULL) {
139 prev->cl_next = cl;
140 break;
141 }
142 prev = prev->cl_next;
143 }
144
145 return (true);
146 }
147
148 bool
config_parse_base(struct config_args * ca,struct config * cf)149 config_parse_base(struct config_args *ca, struct config *cf)
150 {
151 struct stat st;
152
153 ca->ca_buffer = cf->cf_base;
154 ca->ca_bufsize = sizeof(cf->cf_base);
155
156 if (!config_set_string(ca))
157 return (false);
158
159 if (cf->cf_base[0] != '/') {
160 logmsg_err("line %u: base %s: must be the absolute path",
161 lineno, cf->cf_base);
162 return (false);
163 }
164
165 if (stat(cf->cf_base, &st) == -1) {
166 logmsg_err("line %u: base %s: %s", lineno, cf->cf_base,
167 strerror(errno));
168 return (false);
169 }
170 if (!S_ISDIR(st.st_mode)) {
171 logmsg_err("line %u: base %s: %s", lineno, cf->cf_base,
172 strerror(ENOTDIR));
173 return (false);
174 }
175
176 return (true);
177 }
178
179 bool
config_parse_base_prefix(struct config_args * ca,struct config * cf)180 config_parse_base_prefix(struct config_args *ca, struct config *cf)
181 {
182 struct stat st;
183
184 ca->ca_buffer = cf->cf_base_prefix;
185 ca->ca_bufsize = sizeof(cf->cf_base_prefix);
186
187 if (!config_set_string(ca))
188 return (false);
189
190 if (cf->cf_base_prefix[0] != '/') {
191 logmsg_err("line %u: base-prefix %s: must be the absolute "
192 "path", lineno, cf->cf_base_prefix);
193 return (false);
194 }
195
196 if (stat(cf->cf_base_prefix, &st) == -1) {
197 logmsg_err("line %u: base-prefix %s: %s", lineno,
198 cf->cf_base_prefix, strerror(errno));
199 return (false);
200 }
201 if (!S_ISDIR(st.st_mode)) {
202 logmsg_err("line %u: base-prefix %s: %s", lineno,
203 cf->cf_base_prefix, strerror(ENOTDIR));
204 return (false);
205 }
206
207 return (true);
208 }
209
210 bool
config_parse_errormode(struct config_args * ca,struct collection * cl)211 config_parse_errormode(struct config_args *ca, struct collection *cl)
212 {
213 const struct token_keyword *key;
214
215 if (cl->cl_errormode != CVSYNC_ERRORMODE_UNSPEC) {
216 logmsg_err("line %u: found duplication of the 'errormode'",
217 lineno);
218 return (false);
219 }
220
221 if ((key = token_get_keyword(ca->ca_fp, errormode_keywords)) == NULL)
222 return (false);
223
224 cl->cl_errormode = key->type;
225
226 return (true);
227 }
228
229 bool
config_parse_hash(struct config_args * ca,struct config * cf)230 config_parse_hash(struct config_args *ca, struct config *cf)
231 {
232 struct token *tk = &ca->ca_token;
233
234 if (cf->cf_hash != HASH_UNSPEC) {
235 logmsg_err("line %u: found duplication of the 'hash'", lineno);
236 return (false);
237 }
238
239 if (!token_get_string(ca->ca_fp, tk))
240 return (false);
241
242 if ((cf->cf_hash = hash_pton(tk->token, tk->length)) == HASH_UNSPEC) {
243 logmsg_err("line %u: %s: unsupported hash type", lineno,
244 tk->token);
245 return (false);
246 }
247
248 return (true);
249 }
250
251 bool
config_parse_release(struct config_args * ca,struct collection * cl)252 config_parse_release(struct config_args *ca, struct collection *cl)
253 {
254 ca->ca_buffer = cl->cl_release;
255 ca->ca_bufsize = sizeof(cl->cl_release);
256
257 if (!config_set_string(ca))
258 return (false);
259
260 if (cvsync_release_pton(cl->cl_release) == CVSYNC_RELEASE_UNKNOWN) {
261 logmsg_err("line %u: %s: unsupported release type", lineno,
262 cl->cl_release);
263 return (false);
264 }
265
266 return (true);
267 }
268
269 bool
config_parse_umask(struct config_args * ca,struct collection * cl)270 config_parse_umask(struct config_args *ca, struct collection *cl)
271 {
272 unsigned long ul;
273
274 if (cl->cl_umask != CVSYNC_UMASK_UNSPEC) {
275 logmsg_err("line %u: found duplication of the 'umask'",
276 lineno);
277 return (false);
278 }
279
280 if (!token_get_number(ca->ca_fp, &ul))
281 return (false);
282
283 if (ul & ~CVSYNC_ALLPERMS) {
284 logmsg_err("line %u: umask %lu: %s", lineno, ul,
285 strerror(ERANGE));
286 return (false);
287 }
288
289 cl->cl_umask = (uint16_t)ul;
290
291 return (true);
292 }
293
294 bool
config_resolv_prefix(struct config * cf,struct collection * cl,bool rdonly)295 config_resolv_prefix(struct config *cf, struct collection *cl, bool rdonly)
296 {
297 struct stat st;
298 char path[PATH_MAX];
299 int fd, wn;
300
301 if (cl->cl_prefix[0] != '/') {
302 if (strlen(cf->cf_base_prefix) == 0) {
303 if (strlen(cl->cl_prefix) == 0) {
304 logmsg_err("collection %s/%s: no prefix",
305 cl->cl_name, cl->cl_release);
306 } else {
307 logmsg_err("collection %s/%s: prefix %s: "
308 "must be the absolute path",
309 cl->cl_name, cl->cl_release,
310 cl->cl_prefix);
311 }
312 return (false);
313 }
314 wn = snprintf(path, sizeof(path), "%s/%s", cf->cf_base_prefix,
315 cl->cl_prefix);
316 } else {
317 wn = snprintf(path, sizeof(path), "%s", cl->cl_prefix);
318 }
319 if ((wn <= 0) || ((size_t)wn >= sizeof(path))) {
320 logmsg_err("collection %s/%s: prefix %s: %s", cl->cl_name,
321 cl->cl_release, cl->cl_prefix, strerror(EINVAL));
322 return (false);
323 }
324
325 if (stat(path, &st) == -1) {
326 logmsg_err("collection %s/%s: prefix %s: %s", cl->cl_name,
327 cl->cl_release, cl->cl_prefix, strerror(errno));
328 return (false);
329 }
330 if (!S_ISDIR(st.st_mode)) {
331 logmsg_err("collection %s/%s: prefix %s: %s", cl->cl_name,
332 cl->cl_release, cl->cl_prefix, strerror(ENOTDIR));
333 return (false);
334 }
335 if (realpath(path, cl->cl_prefix) == NULL) {
336 logmsg_err("collection %s/%s: prefix %s: %s", cl->cl_name,
337 cl->cl_release, cl->cl_prefix, strerror(errno));
338 return (false);
339 }
340 cl->cl_prefixlen = strlen(cl->cl_prefix);
341 if (cl->cl_prefixlen + 1 >= sizeof(cl->cl_prefix)) {
342 logmsg_err("collection %s/%s: prefix %s: %s", cl->cl_name,
343 cl->cl_release, cl->cl_prefix,
344 strerror(ENAMETOOLONG));
345 return (false);
346 }
347 cl->cl_prefix[cl->cl_prefixlen++] = '/';
348 cl->cl_prefix[cl->cl_prefixlen] = '\0';
349
350 if (!rdonly) {
351 (void)memcpy(&cl->cl_prefix[cl->cl_prefixlen], CVSYNC_TMPFILE,
352 CVSYNC_TMPFILE_LEN);
353 cl->cl_prefix[cl->cl_prefixlen + CVSYNC_TMPFILE_LEN] = '\0';
354
355 if ((fd = mkstemp(cl->cl_prefix)) == -1) {
356 cl->cl_prefix[cl->cl_prefixlen] = '\0';
357 logmsg_err("collection %s/%s: prefix %s: %s",
358 cl->cl_name, cl->cl_release, cl->cl_prefix,
359 strerror(errno));
360 return (false);
361 }
362 if (unlink(cl->cl_prefix) == -1) {
363 cl->cl_prefix[cl->cl_prefixlen] = '\0';
364 logmsg_err("collection %s/%s: prefix %s: %s",
365 cl->cl_name, cl->cl_release, cl->cl_prefix,
366 strerror(errno));
367 (void)close(fd);
368 return (false);
369 }
370 if (close(fd) == -1) {
371 cl->cl_prefix[cl->cl_prefixlen] = '\0';
372 logmsg_err("collection %s/%s: prefix %s: %s",
373 cl->cl_name, cl->cl_release, cl->cl_prefix,
374 strerror(errno));
375 return (false);
376 }
377
378 cl->cl_prefix[cl->cl_prefixlen] = '\0';
379 }
380
381 return (true);
382 }
383
384 bool
config_resolv_scanfile(struct config * cf,struct collection * cl)385 config_resolv_scanfile(struct config *cf, struct collection *cl)
386 {
387 char path[PATH_MAX + CVSYNC_NAME_MAX + 1];
388 int wn;
389
390 if (strlen(cl->cl_scan_name) == 0)
391 return (true);
392
393 if (cl->cl_scan_name[0] != '/') {
394 if (strlen(cf->cf_base) == 0) {
395 logmsg_err("collection %s/%s: scanfile %s: must be an "
396 "absolute path", cl->cl_name,
397 cl->cl_release, cl->cl_scan_name);
398 return (false);
399 }
400 wn = snprintf(path, sizeof(path), "%s/%s", cf->cf_base,
401 cl->cl_scan_name);
402 } else {
403 wn = snprintf(path, sizeof(path), "%s", cl->cl_scan_name);
404 }
405 if ((wn <= 0) || ((size_t)wn >= sizeof(path))) {
406 logmsg_err("collection %s/%s: scanfile %s: %s", cl->cl_name,
407 cl->cl_release, cl->cl_scan_name, strerror(EINVAL));
408 return (false);
409 }
410 wn = snprintf(cl->cl_scan_name, sizeof(cl->cl_scan_name), "%s", path);
411 if ((wn <= 0) || ((size_t)wn >= sizeof(cl->cl_scan_name))) {
412 logmsg_err("collection %s/%s: scanfile %s: %s", cl->cl_name,
413 cl->cl_release, path, strerror(EINVAL));
414 return (false);
415 }
416
417 return (true);
418 }
419
420 bool
config_set_string(struct config_args * ca)421 config_set_string(struct config_args *ca)
422 {
423 struct token *tk = &ca->ca_token;
424
425 if (!token_get_string(ca->ca_fp, tk))
426 return (false);
427
428 if (strlen(ca->ca_buffer) != 0) {
429 logmsg_err("line %u: found duplication of the '%s'",
430 lineno, ca->ca_key->name);
431 return (false);
432 }
433 if (tk->length >= ca->ca_bufsize) {
434 logmsg_err("line %u: %s: %s", lineno, ca->ca_key->name,
435 strerror(ENAMETOOLONG));
436 return (false);
437 }
438 (void)memcpy(ca->ca_buffer, tk->token, tk->length);
439 ca->ca_buffer[tk->length] = '\0';
440
441 return (true);
442 }
443