Version information for db-2 library.
[kopensolaris-gnu/glibc.git] / db2 / log / log_archive.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1997, 1998
5  *      Sleepycat Software.  All rights reserved.
6  */
7
8 #include "config.h"
9
10 #ifndef lint
11 static const char sccsid[] = "@(#)log_archive.c 10.37 (Sleepycat) 5/3/98";
12 #endif /* not lint */
13
14 #ifndef NO_SYSTEM_INCLUDES
15 #include <sys/types.h>
16
17 #include <errno.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #endif
22
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. */
29
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)));
34
35 /*
36  * log_archive --
37  *      Supporting function for db_archive(1).
38  */
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));
45 {
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];
51
52         COMPQUIET(fnum, 0);
53
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);
63         }
64
65         /*
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.
69          */
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;
76
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) {
92                         /*
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).
96                          */
97                         if (ret != DB_NOTFOUND)
98                                 return (ret);
99                         *listp = NULL;
100                         return (0);
101                 }
102                 /* Remove any log files before the last stable LSN. */
103                 fnum = stable_lsn.file - 1;
104                 break;
105         }
106
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;
113
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;
120
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;
127                         }
128                 }
129
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;
138                         }
139                         FREES(name);
140                 } else
141                         array[n] = name;
142
143                 array[++n] = NULL;
144         }
145
146         /* If there's nothing to return, we're done. */
147         if (n == 0) {
148                 *listp = NULL;
149                 ret = 0;
150                 goto err;
151         }
152
153         /* Sort the list. */
154         qsort(array, (size_t)n, sizeof(char *), __cmpfunc);
155
156         /* Rework the memory. */
157         if ((ret = __usermem(&array, db_malloc)) != 0)
158                 goto err;
159
160         *listp = array;
161         return (0);
162
163 err:    if (array != NULL) {
164                 for (arrayp = array; *arrayp != NULL; ++arrayp)
165                         FREES(*arrayp);
166                 __db_free(array);
167         }
168         return (ret);
169 }
170
171 /*
172  * __build_data --
173  *      Build a list of datafiles for return.
174  */
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));
180 {
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;
187
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;
193
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;
203                 }
204
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;
210                         }
211                         continue;
212                 }
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;
218                 }
219
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;
226                         }
227                 }
228
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;
234                 }
235
236                 array[++n] = NULL;
237                 __db_free(argp);
238
239                 if (F_ISSET(dblp, DB_AM_THREAD)) {
240                         __db_free(rec.data);
241                         rec.data = NULL;
242                 }
243         }
244
245         /* If there's nothing to return, we're done. */
246         if (n == 0) {
247                 ret = 0;
248                 *listp = NULL;
249                 goto err1;
250         }
251
252         /* Sort the list. */
253         qsort(array, (size_t)n, sizeof(char *), __cmpfunc);
254
255         /*
256          * Build the real pathnames, discarding nonexistent files and
257          * duplicates.
258          */
259         for (last = nxt = 0; nxt < n;) {
260                 /*
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.
264                  */
265                 if (last != nxt) {
266                         array[last] = array[nxt];
267                         array[nxt] = NULL;
268                 }
269                 for (++nxt; nxt < n &&
270                     strcmp(array[last], array[nxt]) == 0; ++nxt) {
271                         FREES(array[nxt]);
272                         array[nxt] = NULL;
273                 }
274
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;
279
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;
286                 }
287
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;
304         }
305
306         /* NULL-terminate the list. */
307         array[last] = NULL;
308
309         /* Rework the memory. */
310         if ((ret = __usermem(&array, db_malloc)) != 0)
311                 goto err1;
312
313         *listp = array;
314         return (0);
315
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.
320          */
321         if (array != NULL)
322                 for (; nxt < n; ++nxt)
323                         FREES(array[nxt]);
324         /* FALLTHROUGH */
325
326 err1:   if (array != NULL) {
327                 for (arrayp = array; *arrayp != NULL; ++arrayp)
328                         FREES(*arrayp);
329                 __db_free(array);
330         }
331         return (ret);
332 }
333
334 /*
335  * __absname --
336  *      Return an absolute path name for the file.
337  */
338 static int
339 __absname(pref, name, newnamep)
340         char *pref, *name, **newnamep;
341 {
342         size_t l_pref, l_name;
343         int isabspath;
344         char *newname;
345
346         l_name = strlen(name);
347         isabspath = __db_abspath(name);
348         l_pref = isabspath ? 0 : strlen(pref);
349
350         /* Malloc space for concatenating the two. */
351         if ((*newnamep =
352             newname = (char *)__db_malloc(l_pref + l_name + 2)) == NULL)
353                 return (ENOMEM);
354
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];
360         }
361         memcpy(newname + l_pref, name, l_name + 1);
362
363         return (0);
364 }
365
366 /*
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.
370  */
371 static int
372 __usermem(listp, cmpfunc)
373         char ***listp;
374         void *(*cmpfunc) __P((size_t));
375 {
376         size_t len;
377         char **array, **arrayp, **orig, *strp;
378
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 *);
383
384         /*
385          * Allocate it and set up the pointers.
386          *
387          * XXX
388          * Don't simplify this expression, SunOS compilers don't like it.
389          */
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);
397
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;
404
405                 FREES(*orig);
406         }
407
408         /* NULL-terminate the list. */
409         *arrayp = NULL;
410
411         __db_free(*listp);
412         *listp = array;
413
414         return (0);
415 }
416
417 static int
418 __cmpfunc(p1, p2)
419         const void *p1, *p2;
420 {
421         return (strcmp(*((char * const *)p1), *((char * const *)p2)));
422 }