summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org.ua>2011-05-23 13:29:16 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2011-05-23 13:29:16 (GMT)
commitd538f8dbe5b24a4cafe6ac297436c382def519d9 (patch) (unidiff)
treedff477b5f27dd13ad0eced6f0d319b4ef9c17524
parent627feaab03df9153ae04f6e792e9a55ad6817d0c (diff)
downloadnssync-d538f8dbe5b24a4cafe6ac297436c382def519d9.tar.gz
nssync-d538f8dbe5b24a4cafe6ac297436c382def519d9.tar.bz2
Improve error handling.
* src/bindcf.c (bind_working_dir): Remove static qualifier. (filetab): New static. (find_file): New static function. (bindcf_zone_name): New function. (bindcf_lookup): Rename existing zone file if zone_file_pattern has changed. (flush_zone_list): Handle file name clashes gratiously. Don't delete file if it is already used by another zone. * src/nssync.c (create_hierarchy,trycreate): New function. (copy_file): Call trycreate if open failed with ENOENT. (move_file): New function. (main): Print additional diagnostics before exiting on error. * src/nssync.h (bind_working_dir): New extern. (move_file): New proto.
Diffstat (more/less context) (ignore whitespace changes)
m---------grecs0
-rw-r--r--src/bindcf.c180
-rw-r--r--src/nssync.c126
-rw-r--r--src/nssync.h2
4 files changed, 257 insertions, 51 deletions
diff --git a/grecs b/grecs
Subproject d253fa956809a1497583212109d33fd4f1a05b3 Subproject a32a9570602c9af7290e7664fb9f489aff040cd
diff --git a/src/bindcf.c b/src/bindcf.c
index e5fdd67..932b65b 100644
--- a/src/bindcf.c
+++ b/src/bindcf.c
@@ -21,9 +21,36 @@ struct grecs_list *bind_include_path;
21char *zone_file_pattern = "$zone.$synctag"; 21char *zone_file_pattern = "$zone.$synctag";
22char *zone_conf_file; 22char *zone_conf_file;
23 23
24static char *bind_working_dir; 24char *bind_working_dir;
25static struct grecs_node *bind_tree; 25static struct grecs_node *bind_tree;
26 26
27struct filesym {
28 const char *name;
29 struct grecs_node *node;
30};
31static struct grecs_symtab *filetab;
32
33static struct grecs_node *
34find_file(const char *name, struct grecs_node *node)
35{
36 int install = 1;
37 struct filesym key, *ret;
38 if (!filetab) {
39 filetab = grecs_symtab_create_default(sizeof(struct filesym));
40 if (!filetab)
41 grecs_alloc_die();
42 }
43 key.name = name;
44 ret = grecs_symtab_lookup_or_install(filetab, &key, &install);
45 if (!ret)
46 grecs_alloc_die();
47 if (install) {
48 ret->node = node;
49 return NULL;
50 }
51 return ret->node;
52}
53
27void 54void
28source_named_conf() 55source_named_conf()
29{ 56{
@@ -134,6 +161,10 @@ static void
134new_zone(struct nssync *sp, const char *zone, char *file) 161new_zone(struct nssync *sp, const char *zone, char *file)
135{ 162{
136 struct grecs_node *node, *np; 163 struct grecs_node *node, *np;
164 #define fake_locus(n) do { \
165 (n)->locus.file = __FILE__;\
166 (n)->locus.line = __LINE__;\
167 } while (0)
137 168
138 debug(1, ("%s: creating new zone file for %s, file %s", sp->tag, 169 debug(1, ("%s: creating new zone file for %s, file %s", sp->tag,
139 zone, file)); 170 zone, file));
@@ -143,20 +174,24 @@ new_zone(struct nssync *sp, const char *zone, char *file)
143 node->v.value = grecs_malloc(sizeof(node->v.value[0])); 174 node->v.value = grecs_malloc(sizeof(node->v.value[0]));
144 node->v.value->type = GRECS_TYPE_STRING; 175 node->v.value->type = GRECS_TYPE_STRING;
145 node->v.value->v.string = grecs_strdup(zone); 176 node->v.value->v.string = grecs_strdup(zone);
146 177 fake_locus(node);
178
147 np = grecs_node_create(grecs_node_stmt, NULL); 179 np = grecs_node_create(grecs_node_stmt, NULL);
148 np->ident = "file"; 180 np->ident = "file";
149 np->v.value = grecs_malloc(sizeof(np->v.value[0])); 181 np->v.value = grecs_malloc(sizeof(np->v.value[0]));
150 np->v.value->type = GRECS_TYPE_STRING; 182 np->v.value->type = GRECS_TYPE_STRING;
151 np->v.value->v.string = grecs_strdup(file); 183 np->v.value->v.string = grecs_strdup(file);
152 184 fake_locus(node);
185
153 node->down = np; 186 node->down = np;
154 187
155 np = grecs_node_create(grecs_node_stmt, NULL); 188 np = grecs_node_create(grecs_node_stmt, NULL);
156 np->ident = "$found$"; 189 np->ident = "$found$";
190 fake_locus(node);
157 grecs_node_bind(node, np, 1); 191 grecs_node_bind(node, np, 1);
158 192
159 grecs_node_bind(sp->zone_tree, node, 1); 193 grecs_node_bind(sp->zone_tree, node, 1);
194#undef fake_locus
160 } 195 }
161 196
162#define is_safe_char(c) \ 197#define is_safe_char(c) \
@@ -196,11 +231,35 @@ safe_filename(const char *file)
196 return name; 231 return name;
197} 232}
198 233
199char * 234static char *
200bindcf_lookup(struct nssync *sp, const char *zone) 235bindcf_zone_name(struct nssync *sp, const char *zone)
201{ 236{
237 char *name;
202 char *env[5]; 238 char *env[5];
203 struct wordsplit ws; 239 struct wordsplit ws;
240
241 env[0] = "zone";
242 env[1] = safe_filename(zone);
243 env[2] = "synctag";
244 env[3] = sp->tag;
245 env[4] = NULL;
246 ws.ws_env = (const char**)env;
247 if (wordsplit(sp->zone_file_pattern, &ws,
248 WRDSF_NOCMD | WRDSF_ENV | WRDSF_ENV_KV |
249 WRDSF_NOSPLIT | WRDSF_KEEPUNDEF)) {
250 error("cannot split zone-file-pattern: %s",
251 wordsplit_strerror(&ws));
252 exit(EX_SOFTWARE);
253 }
254 grecs_free(env[1]);
255 name = absolute_name(ws.ws_wordv[0], bind_working_dir);
256 wordsplit_free(&ws);
257 return name;
258}
259
260char *
261bindcf_lookup(struct nssync *sp, const char *zone)
262{
204 char *name; 263 char *name;
205 struct grecs_node *node; 264 struct grecs_node *node;
206 265
@@ -212,7 +271,10 @@ bindcf_lookup(struct nssync *sp, const char *zone)
212 strcasecmp(node->v.value->v.string, zone) == 0) 271 strcasecmp(node->v.value->v.string, zone) == 0)
213 break; 272 break;
214 273
274 name = bindcf_zone_name(sp, zone);
275
215 if (node) { 276 if (node) {
277 char *oname;
216 struct grecs_node *np = grecs_find_node(node->down, "file"); 278 struct grecs_node *np = grecs_find_node(node->down, "file");
217 if (!np) { 279 if (!np) {
218 error("%s:%d: no file statement in zone!", 280 error("%s:%d: no file statement in zone!",
@@ -223,13 +285,25 @@ bindcf_lookup(struct nssync *sp, const char *zone)
223 if (np->type != grecs_node_stmt || 285 if (np->type != grecs_node_stmt ||
224 !np->v.value || 286 !np->v.value ||
225 np->v.value->type != GRECS_TYPE_STRING) { 287 np->v.value->type != GRECS_TYPE_STRING) {
226 error("%s:%d: suspicious file statement", 288 grecs_error(&np->locus, 0,
227 np->locus.file, np->locus.line); 289 "suspicious file statement");
228 error_count++; 290 error_count++;
229 return NULL; 291 return NULL;
230 } 292 }
231 name = absolute_name(np->v.value->v.string, bind_working_dir); 293 oname = absolute_name(np->v.value->v.string, bind_working_dir);
232 294 if (strcmp(oname, name)) {
295 debug(1, ("renaming zone file %s to %s", oname, name));
296 if (move_file(oname, name)) {
297 free(oname);
298 free(name);
299 error_count++;
300 return NULL;
301 }
302 grecs_free(np->v.value->v.string);
303 np->v.value->v.string = name;
304 name = grecs_strdup(np->v.value->v.string);
305 }
306
233 np = grecs_node_create(grecs_node_stmt, NULL); 307 np = grecs_node_create(grecs_node_stmt, NULL);
234 np->ident = "$found$"; 308 np->ident = "$found$";
235 grecs_node_bind(node, np, 1); 309 grecs_node_bind(node, np, 1);
@@ -237,23 +311,7 @@ bindcf_lookup(struct nssync *sp, const char *zone)
237 } 311 }
238 312
239 /* No zone statement: new zone */ 313 /* No zone statement: new zone */
240 env[0] = "zone"; 314 name = bindcf_zone_name(sp, zone);
241 env[1] = safe_filename(zone);
242 env[2] = "synctag";
243 env[3] = sp->tag;
244 env[4] = NULL;
245 ws.ws_env = (const char**)env;
246 if (wordsplit(sp->zone_file_pattern, &ws,
247 WRDSF_NOCMD | WRDSF_ENV | WRDSF_ENV_KV |
248 WRDSF_NOSPLIT | WRDSF_KEEPUNDEF)) {
249 error("cannot split zone-file-pattern: %s",
250 wordsplit_strerror(&ws));
251 exit(EX_SOFTWARE);
252 }
253 grecs_free(env[1]);
254 name = absolute_name(ws.ws_wordv[0], bind_working_dir);
255 wordsplit_free(&ws);
256
257 new_zone(sp, zone, name); 315 new_zone(sp, zone, name);
258 return name; 316 return name;
259} 317}
@@ -304,27 +362,61 @@ flush_zone_list(struct nssync *sp)
304 PACKAGE_STRING); 362 PACKAGE_STRING);
305 fprintf(fp, "# Do not edit! See %s for details\n", config_file); 363 fprintf(fp, "# Do not edit! See %s for details\n", config_file);
306 for (node = sp->zone_tree->down; node; node = node->next) { 364 for (node = sp->zone_tree->down; node; node = node->next) {
365 struct grecs_node *file_node =
366 grecs_find_node(node->down, "file");
367 char *file = NULL;
368 struct grecs_node *np = NULL;
369
370 if (file_node) {
371 file = absolute_name(file_node->v.value->v.string,
372 bind_working_dir);
373 np = find_file(file, file_node);
374 }
375
307 if (grecs_find_node(node->down, "$found$")) { 376 if (grecs_find_node(node->down, "$found$")) {
377 if (np) {
378 int fd;
379 char *new_name = NULL;
380 size_t size = 0;
381
382 if (grecs_asprintf(&new_name, &size,
383 "%s.XXXXXX",
384 file_node->v.value->v.string))
385 grecs_alloc_die();
386 fd = mkstemp(new_name);
387 if (fd == -1) {
388 grecs_error(&node->locus, 0,
389 "can't create temporary: "
390 "%s",
391 strerror(errno));
392 error_count++;
393 continue;
394 }
395 close(fd);
396 grecs_warning(&node->locus, 0,
397 "file %s already in use; "
398 "using %s instead",
399 file_node->v.value->v.string,
400 new_name);
401 grecs_warning(&np->locus, 0,
402 "this is the location of "
403 "the previous definition");
404 grecs_free(file_node->v.value->v.string);
405 file_node->v.value->v.string = new_name;
406 }
308 output_zone(fp, node); 407 output_zone(fp, node);
309 zone_count++; 408 zone_count++;
310 } else { 409 } else if (!file_node) {
311 char *file; 410 error("about to delete zone %s, which has "
312 struct grecs_node *np = grecs_find_node(node->down, 411 "no file definition",
313 "file"); 412 node->v.value->v.string);
314 if (!np) { 413 } else if (file && !np) {
315 error("about to delete zone %s, which has " 414 debug(1, ("deleting zone %s, file %s",
316 "no file definition", 415 node->v.value->v.string,
317 node->v.value->v.string); 416 file));
318 } else if (file = 417 if (!dry_run_mode && unlink(file)) {
319 absolute_name(np->v.value->v.string, 418 error("unlink(%s): %s",
320 bind_working_dir)) { 419 file, strerror(errno));
321 debug(1, ("deleting zone %s, file %s",
322 node->v.value->v.string,
323 file));
324 if (!dry_run_mode && unlink(file)) {
325 error("unlink(%s): %s",
326 file, strerror(errno));
327 }
328 } 420 }
329 } 421 }
330 } 422 }
diff --git a/src/nssync.c b/src/nssync.c
index 9f8fb9d..75de83c 100644
--- a/src/nssync.c
+++ b/src/nssync.c
@@ -63,6 +63,66 @@ debug_printf(const char *fmt, ...)
63} 63}
64 64
65 65
66/* Create the directory DIR, eventually creating all intermediate directories
67 starting from DIR + BASELEN. */
68int
69create_hierarchy(char *dir, size_t baselen)
70{
71 int rc;
72 struct stat st;
73 char *p;
74
75 if (stat(dir, &st) == 0) {
76 if (!S_ISDIR(st.st_mode)) {
77 error(_("component %s is not a directory"), dir);
78 return 1;
79 }
80 return 0;
81 } else if (errno != ENOENT) {
82 error(_("cannot stat file %s: %s"), dir, strerror(errno));
83 return 1;
84 }
85
86 p = strrchr(dir, '/');
87 if (p) {
88 if (p - dir + 1 < baselen) {
89 error(_("base directory %s does not exist"), dir);
90 return 1;
91 }
92 *p = 0;
93 }
94
95 rc = create_hierarchy(dir, baselen);
96 if (rc == 0) {
97 if (p)
98 *p = '/';
99 if (mkdir(dir, 0744)) {
100 error(_("cannot create directory %s: %s"),
101 dir, strerror(errno));
102 rc = 1;
103 }
104 }
105 return rc;
106}
107
108static int
109trycreate(const char *file_name)
110{
111 int rc = 1;
112 char *dir_name = grecs_strdup(file_name);
113 char *p = strrchr(dir_name, '/');
114
115 if (p) {
116 size_t len = strlen(bind_working_dir);
117 if (strncmp(dir_name, bind_working_dir, len) == 0) {
118 *p = 0;
119 rc = create_hierarchy(dir_name, len);
120 }
121 }
122 free(dir_name);
123 return rc;
124}
125
66int 126int
67copy_file(const char *file_name, FILE *infile, const char *dst_file) 127copy_file(const char *file_name, FILE *infile, const char *dst_file)
68{ 128{
@@ -85,9 +145,17 @@ copy_file(const char *file_name, FILE *infile, const char *dst_file)
85 145
86 out_fd = open(dst_file, O_WRONLY|O_TRUNC|O_CREAT, 0640); 146 out_fd = open(dst_file, O_WRONLY|O_TRUNC|O_CREAT, 0640);
87 if (out_fd == -1) { 147 if (out_fd == -1) {
88 error("cannot create destination file %s: %s", 148 if (errno == ENOENT) {
89 dst_file, strerror(errno)); 149 if (trycreate(dst_file))
90 return 1; 150 return 1;
151 out_fd = open(dst_file, O_WRONLY|O_TRUNC|O_CREAT,
152 0640);
153 }
154 if (out_fd == -1) {
155 error("cannot create destination file %s: %s",
156 dst_file, strerror(errno));
157 return 1;
158 }
91 } 159 }
92 160
93 buf = NULL; 161 buf = NULL;
@@ -134,6 +202,46 @@ copy_file(const char *file_name, FILE *infile, const char *dst_file)
134 return rc; 202 return rc;
135} 203}
136 204
205/* Move FILE to DST_FILE. If they reside on different devices, use copy_file
206 + unlink. */
207int
208move_file(const char *file, const char *dst_file)
209{
210 int rc = 0;
211
212 rc = rename(file, dst_file);
213 if (rc && errno == ENOENT) {
214 if (trycreate(dst_file))
215 return 1;
216 rc = rename(file, dst_file);
217 }
218 if (rc) {
219 if (errno == EXDEV) {
220 FILE *fp = fopen(file, "r");
221 if (!fp) {
222 error("cannot open %s for reading: %s",
223 file, strerror(errno));
224 return 1;
225 }
226 rc = copy_file (file, fp, dst_file);
227 fclose(fp);
228 if (rc) {
229 error(_("cannot copy %s to %s: %s"),
230 file, dst_file, strerror(errno));
231 rc = 1;
232 } else if (unlink(file)) {
233 error(_("cannot unlink %s: %s"),
234 file, strerror(errno));
235 }
236 } else {
237 error(_("cannot move %s to %s: %s"),
238 file, dst_file, strerror(errno));
239 rc = 1;
240 }
241 }
242 return rc;
243}
244
137 245
138int 246int
139compare(const char *oldfile, const char *newfile) 247compare(const char *oldfile, const char *newfile)
@@ -327,15 +435,19 @@ main(int argc, char **argv)
327 for (ep = synclist->head; ep; ep = ep->next) 435 for (ep = synclist->head; ep; ep = ep->next)
328 synchronize(ep->data); 436 synchronize(ep->data);
329 437
330 if (error_count) 438 if (error_count) {
439 error("exiting due to errors");
331 exit(EX_UNAVAILABLE); 440 exit(EX_UNAVAILABLE);
332 441 }
442
333 for (ep = synclist->head; ep; ep = ep->next) 443 for (ep = synclist->head; ep; ep = ep->next)
334 flush_zone_list(ep->data); 444 flush_zone_list(ep->data);
335 445
336 if (error_count) 446 if (error_count) {
447 error("exiting due to errors");
337 exit(EX_UNAVAILABLE); 448 exit(EX_UNAVAILABLE);
338 449 }
450
339 if (changed_zones) { 451 if (changed_zones) {
340 debug(1,("about to run %s", reload_command)); 452 debug(1,("about to run %s", reload_command));
341 if (!dry_run_mode) { 453 if (!dry_run_mode) {
diff --git a/src/nssync.h b/src/nssync.h
index db710eb..7304e14 100644
--- a/src/nssync.h
+++ b/src/nssync.h
@@ -55,6 +55,7 @@ extern struct grecs_list *bind_include_path;
55 55
56extern char *zone_file_pattern; 56extern char *zone_file_pattern;
57extern char *zone_conf_file; 57extern char *zone_conf_file;
58extern char *bind_working_dir;
58 59
59extern struct grecs_list *synclist; 60extern struct grecs_list *synclist;
60 61
@@ -110,5 +111,6 @@ char *bindcf_lookup(struct nssync *sp, const char *zone);
110void flush_zone_list(struct nssync *sp); 111void flush_zone_list(struct nssync *sp);
111 112
112int compare(const char *oldfile, const char *newfile); 113int compare(const char *oldfile, const char *newfile);
114int move_file(const char *file, const char *dst_file);
113 115
114 116

Return to:

Send suggestions and report system problems to the System administrator.