This commit was generated by cvs2svn to compensate for changes in r30,
[samba.git] / source4 / tdb / tdbbackup.c
1 /* 
2    Unix SMB/CIFS implementation.
3    low level tdb backup and restore utility
4    Copyright (C) Andrew Tridgell              2002
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /*
22
23   This program is meant for backup/restore of tdb databases. Typical usage would be:
24      tdbbackup *.tdb
25   when Samba shuts down cleanly, which will make a backup of all the local databases
26   to *.bak files. Then on Samba startup you would use:
27      tdbbackup -v *.tdb
28   and this will check the databases for corruption and if corruption is detected then
29   the backup will be restored.
30
31   You may also like to do a backup on a regular basis while Samba is
32   running, perhaps using cron.
33
34   The reason this program is needed is to cope with power failures
35   while Samba is running. A power failure could lead to database
36   corruption and Samba will then not start correctly.
37
38   Note that many of the databases in Samba are transient and thus
39   don't need to be backed up, so you can optimise the above a little
40   by only running the backup on the critical databases.
41
42  */
43
44 #include <errno.h>
45 #include <stdlib.h>
46 #include <stdio.h>
47 #include <fcntl.h>
48 #include <unistd.h>
49 #include <string.h>
50 #include <fcntl.h>
51 #include <time.h>
52 #include <sys/mman.h>
53 #include <sys/stat.h>
54 #include <sys/time.h>
55 #include <ctype.h>
56 #include <signal.h>
57 #include "tdb.h"
58
59 static int failed;
60
61 static char *add_suffix(const char *name, const char *suffix)
62 {
63         char *ret;
64         int len = strlen(name) + strlen(suffix) + 1;
65         ret = malloc(len);
66         if (!ret) {
67                 fprintf(stderr,"Out of memory!\n");
68                 exit(1);
69         }
70         strncpy(ret, name, len);
71         strncat(ret, suffix, len);
72         return ret;
73 }
74
75 static int copy_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
76 {
77         TDB_CONTEXT *tdb_new = (TDB_CONTEXT *)state;
78
79         if (tdb_store(tdb_new, key, dbuf, TDB_INSERT) != 0) {
80                 fprintf(stderr,"Failed to insert into %s\n", tdb_new->name);
81                 failed = 1;
82                 return 1;
83         }
84         return 0;
85 }
86
87
88 static int test_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
89 {
90         return 0;
91 }
92
93 /*
94   carefully backup a tdb, validating the contents and
95   only doing the backup if its OK
96   this function is also used for restore
97 */
98 static int backup_tdb(const char *old_name, const char *new_name)
99 {
100         TDB_CONTEXT *tdb;
101         TDB_CONTEXT *tdb_new;
102         char *tmp_name;
103         struct stat st;
104         int count1, count2;
105
106         tmp_name = add_suffix(new_name, ".tmp");
107
108         /* stat the old tdb to find its permissions */
109         if (stat(old_name, &st) != 0) {
110                 perror(old_name);
111                 return 1;
112         }
113
114         /* open the old tdb */
115         tdb = tdb_open(old_name, 0, 0, O_RDWR, 0);
116         if (!tdb) {
117                 printf("Failed to open %s\n", old_name);
118                 return 1;
119         }
120
121         /* create the new tdb */
122         unlink(tmp_name);
123         tdb_new = tdb_open(tmp_name, tdb->header.hash_size, 
124                            TDB_DEFAULT, O_RDWR|O_CREAT|O_EXCL, 
125                            st.st_mode & 0777);
126         if (!tdb_new) {
127                 perror(tmp_name);
128                 free(tmp_name);
129                 return 1;
130         }
131
132         /* lock the old tdb */
133         if (tdb_lockall(tdb) != 0) {
134                 fprintf(stderr,"Failed to lock %s\n", old_name);
135                 tdb_close(tdb);
136                 tdb_close(tdb_new);
137                 unlink(tmp_name);
138                 free(tmp_name);
139                 return 1;
140         }
141
142         failed = 0;
143
144         /* traverse and copy */
145         count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new);
146         if (count1 < 0 || failed) {
147                 fprintf(stderr,"failed to copy %s\n", old_name);
148                 tdb_close(tdb);
149                 tdb_close(tdb_new);
150                 unlink(tmp_name);
151                 free(tmp_name);
152                 return 1;
153         }
154
155         /* close the old tdb */
156         tdb_close(tdb);
157
158         /* close the new tdb and re-open read-only */
159         tdb_close(tdb_new);
160         tdb_new = tdb_open(tmp_name, 0, TDB_DEFAULT, O_RDONLY, 0);
161         if (!tdb_new) {
162                 fprintf(stderr,"failed to reopen %s\n", tmp_name);
163                 unlink(tmp_name);
164                 perror(tmp_name);
165                 free(tmp_name);
166                 return 1;
167         }
168         
169         /* traverse the new tdb to confirm */
170         count2 = tdb_traverse(tdb_new, test_fn, 0);
171         if (count2 != count1) {
172                 fprintf(stderr,"failed to copy %s\n", old_name);
173                 tdb_close(tdb_new);
174                 unlink(tmp_name);
175                 free(tmp_name);
176                 return 1;
177         }
178
179         /* make sure the new tdb has reached stable storage */
180         fsync(tdb_new->fd);
181
182         /* close the new tdb and rename it to .bak */
183         tdb_close(tdb_new);
184         unlink(new_name);
185         if (rename(tmp_name, new_name) != 0) {
186                 perror(new_name);
187                 free(tmp_name);
188                 return 1;
189         }
190
191         printf("%s : %d records\n", old_name, count1);
192         free(tmp_name);
193
194         return 0;
195 }
196
197
198
199 /*
200   verify a tdb and if it is corrupt then restore from *.bak
201 */
202 static int verify_tdb(const char *fname, const char *bak_name)
203 {
204         TDB_CONTEXT *tdb;
205         int count = -1;
206
207         /* open the tdb */
208         tdb = tdb_open(fname, 0, 0, O_RDONLY, 0);
209
210         /* traverse the tdb, then close it */
211         if (tdb) {
212                 count = tdb_traverse(tdb, test_fn, NULL);
213                 tdb_close(tdb);
214         }
215
216         /* count is < 0 means an error */
217         if (count < 0) {
218                 printf("restoring %s\n", fname);
219                 return backup_tdb(bak_name, fname);
220         }
221
222         printf("%s : %d records\n", fname, count);
223
224         return 0;
225 }
226
227
228 /*
229   see if one file is newer than another
230 */
231 static int file_newer(const char *fname1, const char *fname2)
232 {
233         struct stat st1, st2;
234         if (stat(fname1, &st1) != 0) {
235                 return 0;
236         }
237         if (stat(fname2, &st2) != 0) {
238                 return 1;
239         }
240         return (st1.st_mtime > st2.st_mtime);
241 }
242
243 static void usage(void)
244 {
245         printf("Usage: tdbbackup [options] <fname...>\n\n");
246         printf("   -h            this help message\n");
247         printf("   -s suffix     set the backup suffix\n");
248         printf("   -v            veryify mode (restore if corrupt)\n");
249 }
250                 
251
252  int main(int argc, char *argv[])
253 {
254         int i;
255         int ret = 0;
256         int c;
257         int verify = 0;
258         char *suffix = ".bak";
259         extern int optind;
260         extern char *optarg;
261
262         while ((c = getopt(argc, argv, "vhs:")) != -1) {
263                 switch (c) {
264                 case 'h':
265                         usage();
266                         exit(0);
267                 case 'v':
268                         verify = 1;
269                         break;
270                 case 's':
271                         suffix = optarg;
272                         break;
273                 }
274         }
275
276         argc -= optind;
277         argv += optind;
278
279         if (argc < 1) {
280                 usage();
281                 exit(1);
282         }
283
284         for (i=0; i<argc; i++) {
285                 const char *fname = argv[i];
286                 char *bak_name;
287
288                 bak_name = add_suffix(fname, suffix);
289
290                 if (verify) {
291                         if (verify_tdb(fname, bak_name) != 0) {
292                                 ret = 1;
293                         }
294                 } else {
295                         if (file_newer(fname, bak_name) &&
296                             backup_tdb(fname, bak_name) != 0) {
297                                 ret = 1;
298                         }
299                 }
300
301                 free(bak_name);
302         }
303
304         return ret;
305 }
306
307 #ifdef VALGRIND
308 size_t valgrind_strlen(const char *s)
309 {
310         size_t count;
311         for(count = 0; *s++; count++)
312                 ;
313         return count;
314 }
315 #endif