dbcreate.c revision 1.1.1.7 1 /*
2 * dbcreate.c -- routines to create an nsd(8) name database
3 *
4 * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
5 *
6 * See LICENSE for the license.
7 *
8 */
9
10 #include "config.h"
11
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19
20 #include "namedb.h"
21 #include "udb.h"
22 #include "options.h"
23 #include "nsd.h"
24 #include "ixfr.h"
25
26 /* pathname directory separator character */
27 #define PATHSEP '/'
28
29 int
30 print_rrs(FILE* out, struct zone* zone)
31 {
32 rrset_type *rrset;
33 domain_type *domain = zone->apex;
34 region_type* region = region_create(xalloc, free);
35 region_type* rr_region = region_create(xalloc, free);
36 buffer_type* rr_buffer = buffer_create(region, MAX_RDLENGTH);
37 struct state_pretty_rr* state = create_pretty_rr(region);
38 /* first print the SOA record for the zone */
39 if(zone->soa_rrset) {
40 size_t i;
41 for(i=0; i < zone->soa_rrset->rr_count; i++) {
42 if(!print_rr(out, state, zone->soa_rrset->rrs[i],
43 rr_region, rr_buffer)){
44 log_msg(LOG_ERR, "There was an error "
45 "printing SOARR to zone %s",
46 zone->opts->name);
47 region_destroy(region);
48 region_destroy(rr_region);
49 return 0;
50 }
51 }
52 }
53 /* go through entire tree below the zone apex (incl subzones) */
54 while(domain && domain_is_subdomain(domain, zone->apex))
55 {
56 for(rrset = domain->rrsets; rrset; rrset=rrset->next)
57 {
58 size_t i;
59 if(rrset->zone != zone || rrset == zone->soa_rrset)
60 continue;
61 for(i=0; i < rrset->rr_count; i++) {
62 if(!print_rr(out, state, rrset->rrs[i],
63 rr_region, rr_buffer)){
64 log_msg(LOG_ERR, "There was an error "
65 "printing RR to zone %s",
66 zone->opts->name);
67 region_destroy(region);
68 region_destroy(rr_region);
69 return 0;
70 }
71 }
72 }
73 domain = domain_next(domain);
74 }
75 region_destroy(region);
76 region_destroy(rr_region);
77 return 1;
78 }
79
80 static int
81 print_header(zone_type* zone, FILE* out, time_t* now, const char* logs)
82 {
83 char buf[4096+16];
84 /* ctime prints newline at end of this line */
85 snprintf(buf, sizeof(buf), "; zone %s written by NSD %s on %s",
86 zone->opts->name, PACKAGE_VERSION, ctime(now));
87 if(!write_data(out, buf, strlen(buf)))
88 return 0;
89 if(!logs || logs[0] == 0) return 1;
90 snprintf(buf, sizeof(buf), "; %s\n", logs);
91 return write_data(out, buf, strlen(buf));
92 }
93
94 static int
95 write_to_zonefile(zone_type* zone, const char* filename, const char* logs)
96 {
97 time_t now = time(0);
98 FILE *out = fopen(filename, "w");
99 if(!out) {
100 log_msg(LOG_ERR, "cannot write zone %s file %s: %s",
101 zone->opts->name, filename, strerror(errno));
102 return 0;
103 }
104 if(!print_header(zone, out, &now, logs)) {
105 fclose(out);
106 log_msg(LOG_ERR, "There was an error printing "
107 "the header to zone %s", zone->opts->name);
108 return 0;
109 }
110 if(!print_rrs(out, zone)) {
111 fclose(out);
112 return 0;
113 }
114 if(fclose(out) != 0) {
115 log_msg(LOG_ERR, "cannot write zone %s to file %s: fclose: %s",
116 zone->opts->name, filename, strerror(errno));
117 return 0;
118 }
119 return 1;
120 }
121
122 /** create directories above this file, .../dir/dir/dir/file */
123 int
124 create_dirs(const char* path)
125 {
126 char dir[4096];
127 char* p;
128 strlcpy(dir, path, sizeof(dir));
129 /* if we start with / then do not try to create '' */
130 if(dir[0] == PATHSEP)
131 p = strchr(dir+1, PATHSEP);
132 else p = strchr(dir, PATHSEP);
133 /* create each directory component from the left */
134 while(p) {
135 assert(*p == PATHSEP);
136 *p = 0; /* end the directory name here */
137 if(mkdir(dir
138 #ifndef MKDIR_HAS_ONE_ARG
139 , 0750
140 #endif
141 ) == -1) {
142 if(errno != EEXIST) {
143 log_msg(LOG_ERR, "create dir %s: %s",
144 dir, strerror(errno));
145 *p = PATHSEP; /* restore input string */
146 return 0;
147 }
148 /* it already exists, OK, continue */
149 }
150 *p = PATHSEP;
151 p = strchr(p+1, PATHSEP);
152 }
153 return 1;
154 }
155
156 /** create pathname components and check if file exists */
157 static int
158 create_path_components(const char* path, int* notexist)
159 {
160 /* stat the file, to see if it exists, and if its directories exist */
161 struct stat s;
162 if(stat(path, &s) != 0) {
163 if(errno == ENOENT) {
164 *notexist = 1;
165 /* see if we need to create pathname components */
166 return create_dirs(path);
167 }
168 log_msg(LOG_ERR, "cannot stat %s: %s", path, strerror(errno));
169 return 0;
170 }
171 *notexist = 0;
172 return 1;
173 }
174
175 void
176 namedb_write_zonefile(struct nsd* nsd, struct zone_options* zopt)
177 {
178 const char* zfile;
179 int notexist = 0;
180 zone_type* zone;
181 /* if no zone exists, it has no contents or it has no zonefile
182 * configured, then no need to write data to disk */
183 if(!zopt->pattern->zonefile)
184 return;
185 zone = namedb_find_zone(nsd->db, (const dname_type*)zopt->node.key);
186 if(!zone || !zone->apex || !zone->soa_rrset)
187 return;
188 /* write if file does not exist, or if changed */
189 /* so, determine filename, create directory components, check exist*/
190 zfile = config_make_zonefile(zopt, nsd);
191 if(!create_path_components(zfile, ¬exist)) {
192 log_msg(LOG_ERR, "could not write zone %s to file %s because "
193 "the path could not be created", zopt->name, zfile);
194 return;
195 }
196
197 /* if not changed, do not write. */
198 if(notexist || zone->is_changed) {
199 char logs[4096];
200 char bakfile[4096];
201 struct timespec mtime;
202 /* write to zfile~ first, then rename if that works */
203 snprintf(bakfile, sizeof(bakfile), "%s~", zfile);
204 if(zone->logstr)
205 strlcpy(logs, zone->logstr, sizeof(logs));
206 else
207 logs[0] = 0;
208 VERBOSITY(1, (LOG_INFO, "writing zone %s to file %s",
209 zone->opts->name, zfile));
210 if(!write_to_zonefile(zone, bakfile, logs)) {
211 (void)unlink(bakfile); /* delete failed file */
212 return; /* error already printed */
213 }
214 if(rename(bakfile, zfile) == -1) {
215 log_msg(LOG_ERR, "rename(%s to %s) failed: %s",
216 bakfile, zfile, strerror(errno));
217 (void)unlink(bakfile); /* delete failed file */
218 return;
219 }
220 zone->is_changed = 0;
221 VERBOSITY(3, (LOG_INFO, "zone %s written to file %s",
222 zone->opts->name, zfile));
223 /* fetch the mtime of the just created zonefile so we
224 * do not waste effort reading it back in */
225 if(!file_get_mtime(zfile, &mtime, ¬exist)) {
226 get_time(&mtime);
227 }
228 zone->mtime = mtime;
229 if(zone->filename)
230 region_recycle(nsd->db->region, zone->filename,
231 strlen(zone->filename)+1);
232 zone->filename = region_strdup(nsd->db->region, zfile);
233 if(zone->logstr)
234 region_recycle(nsd->db->region, zone->logstr,
235 strlen(zone->logstr)+1);
236 zone->logstr = NULL;
237 if(zone_is_ixfr_enabled(zone) && zone->ixfr)
238 ixfr_write_to_file(zone, zfile);
239 }
240 }
241
242 void
243 namedb_write_zonefiles(struct nsd* nsd, struct nsd_options* options)
244 {
245 struct zone_options* zo;
246 RBTREE_FOR(zo, struct zone_options*, options->zone_options) {
247 namedb_write_zonefile(nsd, zo);
248 }
249 }
250