Merge branch 'master' of git://git.samba.org/samba
[samba.git] / lib / tdb / tools / 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 3 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, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21
22   This program is meant for backup/restore of tdb databases. Typical usage would be:
23      tdbbackup *.tdb
24   when Samba shuts down cleanly, which will make a backup of all the local databases
25   to *.bak files. Then on Samba startup you would use:
26      tdbbackup -v *.tdb
27   and this will check the databases for corruption and if corruption is detected then
28   the backup will be restored.
29
30   You may also like to do a backup on a regular basis while Samba is
31   running, perhaps using cron.
32
33   The reason this program is needed is to cope with power failures
34   while Samba is running. A power failure could lead to database
35   corruption and Samba will then not start correctly.
36
37   Note that many of the databases in Samba are transient and thus
38   don't need to be backed up, so you can optimise the above a little
39   by only running the backup on the critical databases.
40
41  */
42
43 #include "replace.h"
44 #include "system/locale.h"
45 #include "system/time.h"
46 #include "system/filesys.h"
47 #include "system/wait.h"
48 #include "tdb.h"
49
50 #ifdef HAVE_GETOPT_H
51 #include <getopt.h>
52 #endif
53
54 static int failed;
55
56 static struct tdb_logging_context log_ctx;
57
58 #ifdef PRINTF_ATTRIBUTE
59 static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...) PRINTF_ATTRIBUTE(3,4);
60 #endif
61 static void tdb_log(struct tdb_context *tdb, enum tdb_debug_level level, const char *format, ...)
62 {
63         va_list ap;
64     
65         va_start(ap, format);
66         vfprintf(stdout, format, ap);
67         va_end(ap);
68         fflush(stdout);
69 }
70
71 static char *add_suffix(const char *name, const char *suffix)
72 {
73         char *ret;
74         int len = strlen(name) + strlen(suffix) + 1;
75         ret = (char *)malloc(len);
76         if (!ret) {
77                 fprintf(stderr,"Out of memory!\n");
78                 exit(1);
79         }
80         snprintf(ret, len, "%s%s", name, suffix);
81         return ret;
82 }
83
84 static int copy_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
85 {
86         TDB_CONTEXT *tdb_new = (TDB_CONTEXT *)state;
87
88         if (tdb_store(tdb_new, key, dbuf, TDB_INSERT) != 0) {
89                 fprintf(stderr,"Failed to insert into %s\n", tdb_name(tdb_new));
90                 failed = 1;
91                 return 1;
92         }
93         return 0;
94 }
95
96
97 static int test_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state)
98 {
99         return 0;
100 }
101
102 /*
103   carefully backup a tdb, validating the contents and
104   only doing the backup if its OK
105   this function is also used for restore
106 */
107 static int backup_tdb(const char *old_name, const char *new_name, int hash_size)
108 {
109         TDB_CONTEXT *tdb;
110         TDB_CONTEXT *tdb_new;
111         char *tmp_name;
112         struct stat st;
113         int count1, count2;
114
115         tmp_name = add_suffix(new_name, ".tmp");
116
117         /* stat the old tdb to find its permissions */
118         if (stat(old_name, &st) != 0) {
119                 perror(old_name);
120                 free(tmp_name);
121                 return 1;
122         }
123
124         /* open the old tdb */
125         tdb = tdb_open_ex(old_name, 0, 0, 
126                           O_RDWR, 0, &log_ctx, NULL);
127         if (!tdb) {
128                 printf("Failed to open %s\n", old_name);
129                 free(tmp_name);
130                 return 1;
131         }
132
133         /* create the new tdb */
134         unlink(tmp_name);
135         tdb_new = tdb_open_ex(tmp_name, 
136                               hash_size ? hash_size : tdb_hash_size(tdb), 
137                               TDB_DEFAULT, 
138                               O_RDWR|O_CREAT|O_EXCL, st.st_mode & 0777, 
139                               &log_ctx, NULL);
140         if (!tdb_new) {
141                 perror(tmp_name);
142                 free(tmp_name);
143                 return 1;
144         }
145
146         if (tdb_transaction_start(tdb) != 0) {
147                 printf("Failed to start transaction on old tdb\n");
148                 tdb_close(tdb);
149                 tdb_close(tdb_new);
150                 unlink(tmp_name);
151                 free(tmp_name);
152                 return 1;
153         }
154
155         if (tdb_transaction_start(tdb_new) != 0) {
156                 printf("Failed to start transaction on new tdb\n");
157                 tdb_close(tdb);
158                 tdb_close(tdb_new);
159                 unlink(tmp_name);
160                 free(tmp_name);
161                 return 1;
162         }
163
164         failed = 0;
165
166         /* traverse and copy */
167         count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new);
168         if (count1 < 0 || failed) {
169                 fprintf(stderr,"failed to copy %s\n", old_name);
170                 tdb_close(tdb);
171                 tdb_close(tdb_new);
172                 unlink(tmp_name);
173                 free(tmp_name);
174                 return 1;
175         }
176
177         /* close the old tdb */
178         tdb_close(tdb);
179
180         if (tdb_transaction_commit(tdb_new) != 0) {
181                 fprintf(stderr, "Failed to commit new tdb\n");
182                 tdb_close(tdb_new);
183                 unlink(tmp_name);
184                 free(tmp_name);         
185                 return 1;
186         }
187
188         /* close the new tdb and re-open read-only */
189         tdb_close(tdb_new);
190         tdb_new = tdb_open_ex(tmp_name, 
191                               0,
192                               TDB_DEFAULT, 
193                               O_RDONLY, 0,
194                               &log_ctx, NULL);
195         if (!tdb_new) {
196                 fprintf(stderr,"failed to reopen %s\n", tmp_name);
197                 unlink(tmp_name);
198                 perror(tmp_name);
199                 free(tmp_name);
200                 return 1;
201         }
202         
203         /* traverse the new tdb to confirm */
204         count2 = tdb_traverse(tdb_new, test_fn, NULL);
205         if (count2 != count1) {
206                 fprintf(stderr,"failed to copy %s\n", old_name);
207                 tdb_close(tdb_new);
208                 unlink(tmp_name);
209                 free(tmp_name);
210                 return 1;
211         }
212
213         /* close the new tdb and rename it to .bak */
214         tdb_close(tdb_new);
215         if (rename(tmp_name, new_name) != 0) {
216                 perror(new_name);
217                 free(tmp_name);
218                 return 1;
219         }
220
221         free(tmp_name);
222
223         return 0;
224 }
225
226 /*
227   verify a tdb and if it is corrupt then restore from *.bak
228 */
229 static int verify_tdb(const char *fname, const char *bak_name)
230 {
231         TDB_CONTEXT *tdb;
232         int count = -1;
233
234         /* open the tdb */
235         tdb = tdb_open_ex(fname, 0, 0, 
236                           O_RDONLY, 0, &log_ctx, NULL);
237
238         /* traverse the tdb, then close it */
239         if (tdb) {
240                 count = tdb_traverse(tdb, test_fn, NULL);
241                 tdb_close(tdb);
242         }
243
244         /* count is < 0 means an error */
245         if (count < 0) {
246                 printf("restoring %s\n", fname);
247                 return backup_tdb(bak_name, fname, 0);
248         }
249
250         printf("%s : %d records\n", fname, count);
251
252         return 0;
253 }
254
255 /*
256   see if one file is newer than another
257 */
258 static int file_newer(const char *fname1, const char *fname2)
259 {
260         struct stat st1, st2;
261         if (stat(fname1, &st1) != 0) {
262                 return 0;
263         }
264         if (stat(fname2, &st2) != 0) {
265                 return 1;
266         }
267         return (st1.st_mtime > st2.st_mtime);
268 }
269
270 static void usage(void)
271 {
272         printf("Usage: tdbbackup [options] <fname...>\n\n");
273         printf("   -h            this help message\n");
274         printf("   -s suffix     set the backup suffix\n");
275         printf("   -v            verify mode (restore if corrupt)\n");
276         printf("   -n hashsize   set the new hash size for the backup\n");
277 }
278                 
279
280  int main(int argc, char *argv[])
281 {
282         int i;
283         int ret = 0;
284         int c;
285         int verify = 0;
286         int hashsize = 0;
287         const char *suffix = ".bak";
288
289         log_ctx.log_fn = tdb_log;
290
291         while ((c = getopt(argc, argv, "vhs:n:")) != -1) {
292                 switch (c) {
293                 case 'h':
294                         usage();
295                         exit(0);
296                 case 'v':
297                         verify = 1;
298                         break;
299                 case 's':
300                         suffix = optarg;
301                         break;
302                 case 'n':
303                         hashsize = atoi(optarg);
304                         break;
305                 }
306         }
307
308         argc -= optind;
309         argv += optind;
310
311         if (argc < 1) {
312                 usage();
313                 exit(1);
314         }
315
316         for (i=0; i<argc; i++) {
317                 const char *fname = argv[i];
318                 char *bak_name;
319
320                 bak_name = add_suffix(fname, suffix);
321
322                 if (verify) {
323                         if (verify_tdb(fname, bak_name) != 0) {
324                                 ret = 1;
325                         }
326                 } else {
327                         if (file_newer(fname, bak_name) &&
328                             backup_tdb(fname, bak_name, hashsize) != 0) {
329                                 ret = 1;
330                         }
331                 }
332
333                 free(bak_name);
334         }
335
336         return ret;
337 }