diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-05-23 16:29:16 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-05-23 16:29:16 +0300 |
commit | d538f8dbe5b24a4cafe6ac297436c382def519d9 (patch) | |
tree | dff477b5f27dd13ad0eced6f0d319b4ef9c17524 | |
parent | 627feaab03df9153ae04f6e792e9a55ad6817d0c (diff) | |
download | nssync-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.
m--------- | grecs | 0 | ||||
-rw-r--r-- | src/bindcf.c | 156 | ||||
-rw-r--r-- | src/nssync.c | 116 | ||||
-rw-r--r-- | src/nssync.h | 2 |
4 files changed, 240 insertions, 34 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 @@ -18,15 +18,42 @@ char *named_conf_file; struct grecs_list *bind_include_path; char *zone_file_pattern = "$zone.$synctag"; char *zone_conf_file; -static char *bind_working_dir; +char *bind_working_dir; static struct grecs_node *bind_tree; +struct filesym { + const char *name; + struct grecs_node *node; +}; +static struct grecs_symtab *filetab; + +static struct grecs_node * +find_file(const char *name, struct grecs_node *node) +{ + int install = 1; + struct filesym key, *ret; + if (!filetab) { + filetab = grecs_symtab_create_default(sizeof(struct filesym)); + if (!filetab) + grecs_alloc_die(); + } + key.name = name; + ret = grecs_symtab_lookup_or_install(filetab, &key, &install); + if (!ret) + grecs_alloc_die(); + if (install) { + ret->node = node; + return NULL; + } + return ret->node; +} + void source_named_conf() { struct grecs_list_entry *ep; struct grecs_node *node; @@ -131,35 +158,43 @@ strip_bind_working_dir(const char *name) } static void new_zone(struct nssync *sp, const char *zone, char *file) { struct grecs_node *node, *np; +#define fake_locus(n) do { \ + (n)->locus.file = __FILE__; \ + (n)->locus.line = __LINE__; \ + } while (0) debug(1, ("%s: creating new zone file for %s, file %s", sp->tag, zone, file)); node = grecs_node_create(grecs_node_block, NULL); node->ident = "zone"; node->v.value = grecs_malloc(sizeof(node->v.value[0])); node->v.value->type = GRECS_TYPE_STRING; node->v.value->v.string = grecs_strdup(zone); + fake_locus(node); np = grecs_node_create(grecs_node_stmt, NULL); np->ident = "file"; np->v.value = grecs_malloc(sizeof(np->v.value[0])); np->v.value->type = GRECS_TYPE_STRING; np->v.value->v.string = grecs_strdup(file); + fake_locus(node); node->down = np; np = grecs_node_create(grecs_node_stmt, NULL); np->ident = "$found$"; + fake_locus(node); grecs_node_bind(node, np, 1); grecs_node_bind(sp->zone_tree, node, 1); +#undef fake_locus } #define is_safe_char(c) \ (isascii(c) && (isalnum(c) || strchr("_.-", (c)) != NULL)) static char * @@ -193,70 +228,93 @@ safe_filename(const char *file) } } *q = 0; return name; } -char * -bindcf_lookup(struct nssync *sp, const char *zone) +static char * +bindcf_zone_name(struct nssync *sp, const char *zone) { + char *name; char *env[5]; struct wordsplit ws; + + env[0] = "zone"; + env[1] = safe_filename(zone); + env[2] = "synctag"; + env[3] = sp->tag; + env[4] = NULL; + ws.ws_env = (const char**)env; + if (wordsplit(sp->zone_file_pattern, &ws, + WRDSF_NOCMD | WRDSF_ENV | WRDSF_ENV_KV | + WRDSF_NOSPLIT | WRDSF_KEEPUNDEF)) { + error("cannot split zone-file-pattern: %s", + wordsplit_strerror(&ws)); + exit(EX_SOFTWARE); + } + grecs_free(env[1]); + name = absolute_name(ws.ws_wordv[0], bind_working_dir); + wordsplit_free(&ws); + return name; +} + +char * +bindcf_lookup(struct nssync *sp, const char *zone) +{ char *name; struct grecs_node *node; for (node = sp->zone_tree->down; node; node = node->next) if (node->type == grecs_node_block && strcmp(node->ident, "zone") == 0 && node->v.value && node->v.value->type == GRECS_TYPE_STRING && strcasecmp(node->v.value->v.string, zone) == 0) break; + name = bindcf_zone_name(sp, zone); + if (node) { + char *oname; struct grecs_node *np = grecs_find_node(node->down, "file"); if (!np) { error("%s:%d: no file statement in zone!", node->locus.file, node->locus.line); error_count++; return NULL; } if (np->type != grecs_node_stmt || !np->v.value || np->v.value->type != GRECS_TYPE_STRING) { - error("%s:%d: suspicious file statement", - np->locus.file, np->locus.line); + grecs_error(&np->locus, 0, + "suspicious file statement"); + error_count++; + return NULL; + } + oname = absolute_name(np->v.value->v.string, bind_working_dir); + if (strcmp(oname, name)) { + debug(1, ("renaming zone file %s to %s", oname, name)); + if (move_file(oname, name)) { + free(oname); + free(name); error_count++; return NULL; } - name = absolute_name(np->v.value->v.string, bind_working_dir); + grecs_free(np->v.value->v.string); + np->v.value->v.string = name; + name = grecs_strdup(np->v.value->v.string); + } np = grecs_node_create(grecs_node_stmt, NULL); np->ident = "$found$"; grecs_node_bind(node, np, 1); return name; } /* No zone statement: new zone */ - env[0] = "zone"; - env[1] = safe_filename(zone); - env[2] = "synctag"; - env[3] = sp->tag; - env[4] = NULL; - ws.ws_env = (const char**)env; - if (wordsplit(sp->zone_file_pattern, &ws, - WRDSF_NOCMD | WRDSF_ENV | WRDSF_ENV_KV | - WRDSF_NOSPLIT | WRDSF_KEEPUNDEF)) { - error("cannot split zone-file-pattern: %s", - wordsplit_strerror(&ws)); - exit(EX_SOFTWARE); - } - grecs_free(env[1]); - name = absolute_name(ws.ws_wordv[0], bind_working_dir); - wordsplit_free(&ws); - + name = bindcf_zone_name(sp, zone); new_zone(sp, zone, name); return name; } static void output_zone(FILE *fp, struct grecs_node *node) @@ -301,36 +359,70 @@ flush_zone_list(struct nssync *sp) fp = fdopen(fd, "w+"); fprintf(fp, "# Bind configuration include file generated by %s\n", PACKAGE_STRING); fprintf(fp, "# Do not edit! See %s for details\n", config_file); for (node = sp->zone_tree->down; node; node = node->next) { + struct grecs_node *file_node = + grecs_find_node(node->down, "file"); + char *file = NULL; + struct grecs_node *np = NULL; + + if (file_node) { + file = absolute_name(file_node->v.value->v.string, + bind_working_dir); + np = find_file(file, file_node); + } + if (grecs_find_node(node->down, "$found$")) { + if (np) { + int fd; + char *new_name = NULL; + size_t size = 0; + + if (grecs_asprintf(&new_name, &size, + "%s.XXXXXX", + file_node->v.value->v.string)) + grecs_alloc_die(); + fd = mkstemp(new_name); + if (fd == -1) { + grecs_error(&node->locus, 0, + "can't create temporary: " + "%s", + strerror(errno)); + error_count++; + continue; + } + close(fd); + grecs_warning(&node->locus, 0, + "file %s already in use; " + "using %s instead", + file_node->v.value->v.string, + new_name); + grecs_warning(&np->locus, 0, + "this is the location of " + "the previous definition"); + grecs_free(file_node->v.value->v.string); + file_node->v.value->v.string = new_name; + } output_zone(fp, node); zone_count++; - } else { - char *file; - struct grecs_node *np = grecs_find_node(node->down, - "file"); - if (!np) { + } else if (!file_node) { error("about to delete zone %s, which has " "no file definition", node->v.value->v.string); - } else if (file = - absolute_name(np->v.value->v.string, - bind_working_dir)) { + } else if (file && !np) { debug(1, ("deleting zone %s, file %s", node->v.value->v.string, file)); if (!dry_run_mode && unlink(file)) { error("unlink(%s): %s", file, strerror(errno)); } } } - } debug(1, ("number of zones: %lu", zone_count)); fflush(fp); if (compare(fname, sp->zone_conf_file)) { debug(1, ("recreating %s", sp->zone_conf_file)); diff --git a/src/nssync.c b/src/nssync.c index 9f8fb9d..75de83c 100644 --- a/src/nssync.c +++ b/src/nssync.c @@ -60,12 +60,72 @@ debug_printf(const char *fmt, ...) vfprintf(stderr, fmt, ap); fputc('\n', stderr); va_end(ap); } +/* Create the directory DIR, eventually creating all intermediate directories + starting from DIR + BASELEN. */ +int +create_hierarchy(char *dir, size_t baselen) +{ + int rc; + struct stat st; + char *p; + + if (stat(dir, &st) == 0) { + if (!S_ISDIR(st.st_mode)) { + error(_("component %s is not a directory"), dir); + return 1; + } + return 0; + } else if (errno != ENOENT) { + error(_("cannot stat file %s: %s"), dir, strerror(errno)); + return 1; + } + + p = strrchr(dir, '/'); + if (p) { + if (p - dir + 1 < baselen) { + error(_("base directory %s does not exist"), dir); + return 1; + } + *p = 0; + } + + rc = create_hierarchy(dir, baselen); + if (rc == 0) { + if (p) + *p = '/'; + if (mkdir(dir, 0744)) { + error(_("cannot create directory %s: %s"), + dir, strerror(errno)); + rc = 1; + } + } + return rc; +} + +static int +trycreate(const char *file_name) +{ + int rc = 1; + char *dir_name = grecs_strdup(file_name); + char *p = strrchr(dir_name, '/'); + + if (p) { + size_t len = strlen(bind_working_dir); + if (strncmp(dir_name, bind_working_dir, len) == 0) { + *p = 0; + rc = create_hierarchy(dir_name, len); + } + } + free(dir_name); + return rc; +} + int copy_file(const char *file_name, FILE *infile, const char *dst_file) { int out_fd; struct stat st; int rc; @@ -82,16 +142,24 @@ copy_file(const char *file_name, FILE *infile, const char *dst_file) file_name, strerror(errno)); return 1; } out_fd = open(dst_file, O_WRONLY|O_TRUNC|O_CREAT, 0640); if (out_fd == -1) { + if (errno == ENOENT) { + if (trycreate(dst_file)) + return 1; + out_fd = open(dst_file, O_WRONLY|O_TRUNC|O_CREAT, + 0640); + } + if (out_fd == -1) { error("cannot create destination file %s: %s", dst_file, strerror(errno)); return 1; } + } buf = NULL; fsize = st.st_size; for (bufsize = fsize; bufsize > 0 && (buf = malloc (bufsize)) == NULL; bufsize /= 2) @@ -131,12 +199,52 @@ copy_file(const char *file_name, FILE *infile, const char *dst_file) if (rc) unlink(dst_file); return rc; } +/* Move FILE to DST_FILE. If they reside on different devices, use copy_file + + unlink. */ +int +move_file(const char *file, const char *dst_file) +{ + int rc = 0; + + rc = rename(file, dst_file); + if (rc && errno == ENOENT) { + if (trycreate(dst_file)) + return 1; + rc = rename(file, dst_file); + } + if (rc) { + if (errno == EXDEV) { + FILE *fp = fopen(file, "r"); + if (!fp) { + error("cannot open %s for reading: %s", + file, strerror(errno)); + return 1; + } + rc = copy_file (file, fp, dst_file); + fclose(fp); + if (rc) { + error(_("cannot copy %s to %s: %s"), + file, dst_file, strerror(errno)); + rc = 1; + } else if (unlink(file)) { + error(_("cannot unlink %s: %s"), + file, strerror(errno)); + } + } else { + error(_("cannot move %s to %s: %s"), + file, dst_file, strerror(errno)); + rc = 1; + } + } + return rc; +} + int compare(const char *oldfile, const char *newfile) { const char *env[5]; struct wordsplit ws; @@ -324,20 +432,24 @@ main(int argc, char **argv) if (slave_status_file) check_slave_status(); for (ep = synclist->head; ep; ep = ep->next) synchronize(ep->data); - if (error_count) + if (error_count) { + error("exiting due to errors"); exit(EX_UNAVAILABLE); + } for (ep = synclist->head; ep; ep = ep->next) flush_zone_list(ep->data); - if (error_count) + if (error_count) { + error("exiting due to errors"); exit(EX_UNAVAILABLE); + } if (changed_zones) { debug(1,("about to run %s", reload_command)); if (!dry_run_mode) { int rc = system(reload_command); if (rc) { diff --git a/src/nssync.h b/src/nssync.h index db710eb..7304e14 100644 --- a/src/nssync.h +++ b/src/nssync.h @@ -52,12 +52,13 @@ extern int sql_port; extern char *named_conf_file; extern struct grecs_list *bind_include_path; extern char *zone_file_pattern; extern char *zone_conf_file; +extern char *bind_working_dir; extern struct grecs_list *synclist; extern unsigned error_count; extern unsigned changed_zones; @@ -107,8 +108,9 @@ void format_zones(struct nssync *); void source_named_conf(void); char *bindcf_lookup(struct nssync *sp, const char *zone); void flush_zone_list(struct nssync *sp); int compare(const char *oldfile, const char *newfile); +int move_file(const char *file, const char *dst_file); |