Update.
[glibc.git] / db2 / log / log_archive.c
blob7db0cc3e36dbfa116a5c7ee291f2f943680c7d6b
1 /*-
2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1997, 1998
5 * Sleepycat Software. All rights reserved.
6 */
8 #include "config.h"
10 #ifndef lint
11 static const char sccsid[] = "@(#)log_archive.c 10.37 (Sleepycat) 5/3/98";
12 #endif /* not lint */
14 #ifndef NO_SYSTEM_INCLUDES
15 #include <sys/types.h>
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #endif
23 #include "db_int.h"
24 #include "db_dispatch.h"
25 #include "shqueue.h"
26 #include "log.h"
27 #include "common_ext.h"
28 #include "clib_ext.h" /* XXX: needed for getcwd. */
30 static int __absname __P((char *, char *, char **));
31 static int __build_data __P((DB_LOG *, char *, char ***, void *(*)(size_t)));
32 static int __cmpfunc __P((const void *, const void *));
33 static int __usermem __P((char ***, void *(*)(size_t)));
36 * log_archive --
37 * Supporting function for db_archive(1).
39 int
40 log_archive(dblp, listp, flags, db_malloc)
41 DB_LOG *dblp;
42 char ***listp;
43 u_int32_t flags;
44 void *(*db_malloc) __P((size_t));
46 DBT rec;
47 DB_LSN stable_lsn;
48 u_int32_t fnum;
49 int array_size, n, ret;
50 char **array, **arrayp, *name, *p, *pref, buf[MAXPATHLEN];
52 COMPQUIET(fnum, 0);
54 #define OKFLAGS (DB_ARCH_ABS | DB_ARCH_DATA | DB_ARCH_LOG)
55 if (flags != 0) {
56 if ((ret =
57 __db_fchk(dblp->dbenv, "log_archive", flags, OKFLAGS)) != 0)
58 return (ret);
59 if ((ret =
60 __db_fcchk(dblp->dbenv,
61 "log_archive", flags, DB_ARCH_DATA, DB_ARCH_LOG)) != 0)
62 return (ret);
66 * Get the absolute pathname of the current directory. It would
67 * be nice to get the shortest pathname of the database directory,
68 * but that's just not possible.
70 if (LF_ISSET(DB_ARCH_ABS)) {
71 errno = 0;
72 if ((pref = getcwd(buf, sizeof(buf))) == NULL)
73 return (errno == 0 ? ENOMEM : errno);
74 } else
75 pref = NULL;
77 switch (LF_ISSET(~DB_ARCH_ABS)) {
78 case DB_ARCH_DATA:
79 return (__build_data(dblp, pref, listp, db_malloc));
80 case DB_ARCH_LOG:
81 memset(&rec, 0, sizeof(rec));
82 if (F_ISSET(dblp, DB_AM_THREAD))
83 F_SET(&rec, DB_DBT_MALLOC);
84 if ((ret = log_get(dblp, &stable_lsn, &rec, DB_LAST)) != 0)
85 return (ret);
86 if (F_ISSET(dblp, DB_AM_THREAD))
87 __db_free(rec.data);
88 fnum = stable_lsn.file;
89 break;
90 case 0:
91 if ((ret = __log_findckp(dblp, &stable_lsn)) != 0) {
93 * A return of DB_NOTFOUND means that we didn't find
94 * any records in the log (so we are not going to be
95 * deleting any log files).
97 if (ret != DB_NOTFOUND)
98 return (ret);
99 *listp = NULL;
100 return (0);
102 /* Remove any log files before the last stable LSN. */
103 fnum = stable_lsn.file - 1;
104 break;
107 #define LIST_INCREMENT 64
108 /* Get some initial space. */
109 if ((array =
110 (char **)__db_malloc(sizeof(char *) * (array_size = 10))) == NULL)
111 return (ENOMEM);
112 array[0] = NULL;
114 /* Build an array of the file names. */
115 for (n = 0; fnum > 0; --fnum) {
116 if ((ret = __log_name(dblp, fnum, &name)) != 0)
117 goto err;
118 if (__db_exists(name, NULL) != 0)
119 break;
121 if (n >= array_size - 1) {
122 array_size += LIST_INCREMENT;
123 if ((array = (char **)__db_realloc(array,
124 sizeof(char *) * array_size)) == NULL) {
125 ret = ENOMEM;
126 goto err;
130 if (LF_ISSET(DB_ARCH_ABS)) {
131 if ((ret = __absname(pref, name, &array[n])) != 0)
132 goto err;
133 FREES(name);
134 } else if ((p = __db_rpath(name)) != NULL) {
135 if ((array[n] = (char *)__db_strdup(p + 1)) == NULL) {
136 ret = ENOMEM;
137 goto err;
139 FREES(name);
140 } else
141 array[n] = name;
143 array[++n] = NULL;
146 /* If there's nothing to return, we're done. */
147 if (n == 0) {
148 *listp = NULL;
149 ret = 0;
150 goto err;
153 /* Sort the list. */
154 qsort(array, (size_t)n, sizeof(char *), __cmpfunc);
156 /* Rework the memory. */
157 if ((ret = __usermem(&array, db_malloc)) != 0)
158 goto err;
160 *listp = array;
161 return (0);
163 err: if (array != NULL) {
164 for (arrayp = array; *arrayp != NULL; ++arrayp)
165 FREES(*arrayp);
166 __db_free(array);
168 return (ret);
172 * __build_data --
173 * Build a list of datafiles for return.
175 static int
176 __build_data(dblp, pref, listp, db_malloc)
177 DB_LOG *dblp;
178 char *pref, ***listp;
179 void *(*db_malloc) __P((size_t));
181 DBT rec;
182 DB_LSN lsn;
183 __log_register_args *argp;
184 u_int32_t rectype;
185 int array_size, last, n, nxt, ret;
186 char **array, **arrayp, *p, *real_name;
188 /* Get some initial space. */
189 if ((array =
190 (char **)__db_malloc(sizeof(char *) * (array_size = 10))) == NULL)
191 return (ENOMEM);
192 array[0] = NULL;
194 memset(&rec, 0, sizeof(rec));
195 if (F_ISSET(dblp, DB_AM_THREAD))
196 F_SET(&rec, DB_DBT_MALLOC);
197 for (n = 0, ret = log_get(dblp, &lsn, &rec, DB_FIRST);
198 ret == 0; ret = log_get(dblp, &lsn, &rec, DB_NEXT)) {
199 if (rec.size < sizeof(rectype)) {
200 ret = EINVAL;
201 __db_err(dblp->dbenv, "log_archive: bad log record");
202 goto lg_free;
205 memcpy(&rectype, rec.data, sizeof(rectype));
206 if (rectype != DB_log_register) {
207 if (F_ISSET(dblp, DB_AM_THREAD)) {
208 __db_free(rec.data);
209 rec.data = NULL;
211 continue;
213 if ((ret = __log_register_read(rec.data, &argp)) != 0) {
214 ret = EINVAL;
215 __db_err(dblp->dbenv,
216 "log_archive: unable to read log record");
217 goto lg_free;
220 if (n >= array_size - 1) {
221 array_size += LIST_INCREMENT;
222 if ((array = (char **)__db_realloc(array,
223 sizeof(char *) * array_size)) == NULL) {
224 ret = ENOMEM;
225 goto lg_free;
229 if ((array[n] = (char *)__db_strdup(argp->name.data)) == NULL) {
230 ret = ENOMEM;
231 lg_free: if (F_ISSET(&rec, DB_DBT_MALLOC) && rec.data != NULL)
232 __db_free(rec.data);
233 goto err1;
236 array[++n] = NULL;
237 __db_free(argp);
239 if (F_ISSET(dblp, DB_AM_THREAD)) {
240 __db_free(rec.data);
241 rec.data = NULL;
245 /* If there's nothing to return, we're done. */
246 if (n == 0) {
247 ret = 0;
248 *listp = NULL;
249 goto err1;
252 /* Sort the list. */
253 qsort(array, (size_t)n, sizeof(char *), __cmpfunc);
256 * Build the real pathnames, discarding nonexistent files and
257 * duplicates.
259 for (last = nxt = 0; nxt < n;) {
261 * Discard duplicates. Last is the next slot we're going
262 * to return to the user, nxt is the next slot that we're
263 * going to consider.
265 if (last != nxt) {
266 array[last] = array[nxt];
267 array[nxt] = NULL;
269 for (++nxt; nxt < n &&
270 strcmp(array[last], array[nxt]) == 0; ++nxt) {
271 FREES(array[nxt]);
272 array[nxt] = NULL;
275 /* Get the real name. */
276 if ((ret = __db_appname(dblp->dbenv,
277 DB_APP_DATA, NULL, array[last], 0, NULL, &real_name)) != 0)
278 goto err2;
280 /* If the file doesn't exist, ignore it. */
281 if (__db_exists(real_name, NULL) != 0) {
282 FREES(real_name);
283 FREES(array[last]);
284 array[last] = NULL;
285 continue;
288 /* Rework the name as requested by the user. */
289 FREES(array[last]);
290 array[last] = NULL;
291 if (pref != NULL) {
292 ret = __absname(pref, real_name, &array[last]);
293 FREES(real_name);
294 if (ret != 0)
295 goto err2;
296 } else if ((p = __db_rpath(real_name)) != NULL) {
297 array[last] = (char *)__db_strdup(p + 1);
298 FREES(real_name);
299 if (array[last] == NULL)
300 goto err2;
301 } else
302 array[last] = real_name;
303 ++last;
306 /* NULL-terminate the list. */
307 array[last] = NULL;
309 /* Rework the memory. */
310 if ((ret = __usermem(&array, db_malloc)) != 0)
311 goto err1;
313 *listp = array;
314 return (0);
316 err2: /*
317 * XXX
318 * We've possibly inserted NULLs into the array list, so clean up a
319 * bit so that the other error processing works.
321 if (array != NULL)
322 for (; nxt < n; ++nxt)
323 FREES(array[nxt]);
324 /* FALLTHROUGH */
326 err1: if (array != NULL) {
327 for (arrayp = array; *arrayp != NULL; ++arrayp)
328 FREES(*arrayp);
329 __db_free(array);
331 return (ret);
335 * __absname --
336 * Return an absolute path name for the file.
338 static int
339 __absname(pref, name, newnamep)
340 char *pref, *name, **newnamep;
342 size_t l_pref, l_name;
343 int isabspath;
344 char *newname;
346 l_name = strlen(name);
347 isabspath = __db_abspath(name);
348 l_pref = isabspath ? 0 : strlen(pref);
350 /* Malloc space for concatenating the two. */
351 if ((*newnamep =
352 newname = (char *)__db_malloc(l_pref + l_name + 2)) == NULL)
353 return (ENOMEM);
355 /* Build the name. If `name' is an absolute path, ignore any prefix. */
356 if (!isabspath) {
357 memcpy(newname, pref, l_pref);
358 if (strchr(PATH_SEPARATOR, newname[l_pref - 1]) == NULL)
359 newname[l_pref++] = PATH_SEPARATOR[0];
361 memcpy(newname + l_pref, name, l_name + 1);
363 return (0);
367 * __usermem --
368 * Create a single chunk of memory that holds the returned information.
369 * If the user has their own malloc routine, use it.
371 static int
372 __usermem(listp, cmpfunc)
373 char ***listp;
374 void *(*cmpfunc) __P((size_t));
376 size_t len;
377 char **array, **arrayp, **orig, *strp;
379 /* Find out how much space we need. */
380 for (len = 0, orig = *listp; *orig != NULL; ++orig)
381 len += sizeof(char *) + strlen(*orig) + 1;
382 len += sizeof(char *);
385 * Allocate it and set up the pointers.
387 * XXX
388 * Don't simplify this expression, SunOS compilers don't like it.
390 if (cmpfunc == NULL)
391 array = (char **)__db_malloc(len);
392 else
393 array = (char **)cmpfunc(len);
394 if (array == NULL)
395 return (ENOMEM);
396 strp = (char *)(array + (orig - *listp) + 1);
398 /* Copy the original information into the new memory. */
399 for (orig = *listp, arrayp = array; *orig != NULL; ++orig, ++arrayp) {
400 len = strlen(*orig);
401 memcpy(strp, *orig, len + 1);
402 *arrayp = strp;
403 strp += len + 1;
405 FREES(*orig);
408 /* NULL-terminate the list. */
409 *arrayp = NULL;
411 __db_free(*listp);
412 *listp = array;
414 return (0);
417 static int
418 __cmpfunc(p1, p2)
419 const void *p1, *p2;
421 return (strcmp(*((char * const *)p1), *((char * const *)p2)));