aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2011-05-23 16:29:16 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2011-05-23 16:29:16 +0300
commitd538f8dbe5b24a4cafe6ac297436c382def519d9 (patch)
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.
m---------grecs0
-rw-r--r--src/bindcf.c156
-rw-r--r--src/nssync.c116
-rw-r--r--src/nssync.h2
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);

Return to:

Send suggestions and report system problems to the System administrator.