Update.
[platform/upstream/glibc.git] / db2 / log / log_archive.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1997
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.23 (Sleepycat) 8/23/97";
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 "clib_ext.h"
28 #include "common_ext.h"
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(logp, listp, flags, db_malloc)
41         DB_LOG *logp;
42         char ***listp;
43         int 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         fnum = 0;                               /* XXX: Shut the compiler up. */
53
54 #define OKFLAGS (DB_ARCH_ABS | DB_ARCH_DATA | DB_ARCH_LOG)
55         if (flags != 0) {
56                 if ((ret =
57                     __db_fchk(logp->dbenv, "log_archive", flags, OKFLAGS)) != 0)
58                         return (ret);
59                 if ((ret =
60                     __db_fcchk(logp->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                 __set_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(logp, pref, listp, db_malloc));
80         case DB_ARCH_LOG:
81                 memset(&rec, 0, sizeof(rec));
82                 if (F_ISSET(logp, DB_AM_THREAD))
83                         F_SET(&rec, DB_DBT_MALLOC);
84                 if ((ret = log_get(logp, &stable_lsn, &rec, DB_LAST)) != 0)
85                         return (ret);
86                 if (F_ISSET(logp, DB_AM_THREAD))
87                         free(rec.data);
88                 fnum = stable_lsn.file;
89                 break;
90         case 0:
91                 if ((ret = __log_findckp(logp, &stable_lsn)) != 0) {
92                         if (ret != DB_NOTFOUND)
93                                 return (ret);
94                         *listp = NULL;
95                         return (0);
96                 }
97                 /* Remove any log files before the last stable LSN. */
98                 fnum = stable_lsn.file - 1;
99                 break;
100         }
101
102 #define LIST_INCREMENT  64
103         /* Get some initial space. */
104         if ((array =
105             (char **)malloc(sizeof(char *) * (array_size = 10))) == NULL)
106                 return (ENOMEM);
107         array[0] = NULL;
108
109         /* Build an array of the file names. */
110         for (n = 0; fnum > 0; --fnum) {
111                 if ((ret = __log_name(logp->dbenv, fnum, &name)) != 0)
112                         goto err;
113                 if (__db_exists(name, NULL) != 0)
114                         break;
115
116                 if (n >= array_size - 1) {
117                         array_size += LIST_INCREMENT;
118                         if ((array = (char **)realloc(array,
119                             sizeof(char *) * array_size)) == NULL) {
120                                 ret = ENOMEM;
121                                 goto err;
122                         }
123                 }
124
125                 if (LF_ISSET(DB_ARCH_ABS)) {
126                         if ((ret = absname(pref, name, &array[n])) != 0)
127                                 goto err;
128                         FREES(name);
129                 } else if ((p = __db_rpath(name)) != NULL) {
130                         if ((array[n] = (char *)strdup(p + 1)) == NULL) {
131                                 ret = ENOMEM;
132                                 goto err;
133                         }
134                         FREES(name);
135                 } else
136                         array[n] = name;
137
138                 array[++n] = NULL;
139         }
140
141         /* If there's nothing to return, we're done. */
142         if (n == 0) {
143                 *listp = NULL;
144                 ret = 0;
145                 goto err;
146         }
147
148         /* Sort the list. */
149         qsort(array, (size_t)n, sizeof(char *), cmpfunc);
150
151         /* Rework the memory. */
152         if ((ret = usermem(&array, db_malloc)) != 0)
153                 goto err;
154
155         *listp = array;
156         return (0);
157
158 err:    if (array != NULL) {
159                 for (arrayp = array; *arrayp != NULL; ++arrayp)
160                         FREES(*arrayp);
161                 free(array);
162         }
163         return (ret);
164 }
165
166 /*
167  * build_data --
168  *      Build a list of datafiles for return.
169  */
170 static int
171 build_data(logp, pref, listp, db_malloc)
172         DB_LOG *logp;
173         char *pref, ***listp;
174         void *(*db_malloc) __P((size_t));
175 {
176         DBT rec;
177         DB_LSN lsn;
178         __log_register_args *argp;
179         u_int32_t rectype;
180         int array_size, last, n, nxt, ret;
181         char **array, **arrayp, *p, *real_name;
182
183         /* Get some initial space. */
184         if ((array =
185             (char **)malloc(sizeof(char *) * (array_size = 10))) == NULL)
186                 return (ENOMEM);
187         array[0] = NULL;
188
189         memset(&rec, 0, sizeof(rec));
190         if (F_ISSET(logp, DB_AM_THREAD))
191                 F_SET(&rec, DB_DBT_MALLOC);
192         for (n = 0, ret = log_get(logp, &lsn, &rec, DB_FIRST);
193             ret == 0; ret = log_get(logp, &lsn, &rec, DB_NEXT)) {
194                 if (rec.size < sizeof(rectype)) {
195                         ret = EINVAL;
196                         __db_err(logp->dbenv, "log_archive: bad log record");
197                         goto lg_free;
198                 }
199
200                 memcpy(&rectype, rec.data, sizeof(rectype));
201                 if (rectype != DB_log_register) {
202                         if (F_ISSET(logp, DB_AM_THREAD)) {
203                                 free(rec.data);
204                                 rec.data = NULL;
205                         }
206                         continue;
207                 }
208                 if ((ret = __log_register_read(rec.data, &argp)) != 0) {
209                         ret = EINVAL;
210                         __db_err(logp->dbenv,
211                             "log_archive: unable to read log record");
212                         goto lg_free;
213                 }
214
215                 if (n >= array_size - 1) {
216                         array_size += LIST_INCREMENT;
217                         if ((array = (char **)realloc(array,
218                             sizeof(char *) * array_size)) == NULL) {
219                                 ret = ENOMEM;
220                                 goto lg_free;
221                         }
222                 }
223
224                 if ((array[n] = (char *)strdup(argp->name.data)) == NULL) {
225                         ret = ENOMEM;
226 lg_free:                if (F_ISSET(&rec, DB_DBT_MALLOC) && rec.data != NULL)
227                                 free(rec.data);
228                         goto err1;
229                 }
230
231                 array[++n] = NULL;
232                 free(argp);
233
234                 if (F_ISSET(logp, DB_AM_THREAD)) {
235                         free(rec.data);
236                         rec.data = NULL;
237                 }
238         }
239
240         /* If there's nothing to return, we're done. */
241         if (n == 0) {
242                 ret = 0;
243                 *listp = NULL;
244                 goto err1;
245         }
246
247         /* Sort the list. */
248         qsort(array, (size_t)n, sizeof(char *), cmpfunc);
249
250         /*
251          * Build the real pathnames, discarding nonexistent files and
252          * duplicates.
253          */
254         for (last = nxt = 0; nxt < n;) {
255                 /*
256                  * Discard duplicates.  Last is the next slot we're going
257                  * to return to the user, nxt is the next slot that we're
258                  * going to consider.
259                  */
260                 if (last != nxt) {
261                         array[last] = array[nxt];
262                         array[nxt] = NULL;
263                 }
264                 for (++nxt; nxt < n &&
265                     strcmp(array[last], array[nxt]) == 0; ++nxt) {
266                         FREES(array[nxt]);
267                         array[nxt] = NULL;
268                 }
269
270                 /* Get the real name. */
271                 if ((ret = __db_appname(logp->dbenv,
272                     DB_APP_DATA, NULL, array[last], NULL, &real_name)) != 0)
273                         goto err2;
274
275                 /* If the file doesn't exist, ignore it. */
276                 if (__db_exists(real_name, NULL) != 0) {
277                         FREES(real_name);
278                         FREES(array[last]);
279                         array[last] = NULL;
280                         continue;
281                 }
282
283                 /* Rework the name as requested by the user. */
284                 FREES(array[last]);
285                 array[last] = NULL;
286                 if (pref != NULL) {
287                         ret = absname(pref, real_name, &array[last]);
288                         FREES(real_name);
289                         if (ret != 0)
290                                 goto err2;
291                 } else if ((p = __db_rpath(real_name)) != NULL) {
292                         array[last] = (char *)strdup(p + 1);
293                         FREES(real_name);
294                         if (array[last] == NULL)
295                                 goto err2;
296                 } else
297                         array[last] = real_name;
298                 ++last;
299         }
300
301         /* NULL-terminate the list. */
302         array[last] = NULL;
303
304         /* Rework the memory. */
305         if ((ret = usermem(&array, db_malloc)) != 0)
306                 goto err1;
307
308         *listp = array;
309         return (0);
310
311 err2:   /*
312          * XXX
313          * We've possibly inserted NULLs into the array list, so clean up a
314          * bit so that the other error processing works.
315          */
316         if (array != NULL)
317                 for (; nxt < n; ++nxt)
318                         FREES(array[nxt]);
319         /* FALLTHROUGH */
320
321 err1:   if (array != NULL) {
322                 for (arrayp = array; *arrayp != NULL; ++arrayp)
323                         FREES(*arrayp);
324                 free(array);
325         }
326         return (ret);
327 }
328
329 /*
330  * absname --
331  *      Return an absolute path name for the file.
332  */
333 static int
334 absname(pref, name, newnamep)
335         char *pref, *name, **newnamep;
336 {
337         size_t l_pref, l_name;
338         char *newname;
339
340         l_pref = strlen(pref);
341         l_name = strlen(name);
342
343         /* Malloc space for concatenating the two. */
344         if ((newname = (char *)malloc(l_pref + l_name + 2)) == NULL)
345                 return (ENOMEM);
346
347         /* Build the name. */
348         memcpy(newname, pref, l_pref);
349         if (strchr(PATH_SEPARATOR, newname[l_pref - 1]) == NULL)
350                 newname[l_pref++] = PATH_SEPARATOR[0];
351         memcpy(newname + l_pref, name, l_name + 1);
352         *newnamep = newname;
353
354         return (0);
355 }
356
357 /*
358  * usermem --
359  *      Create a single chunk of memory that holds the returned information.
360  *      If the user has their own malloc routine, use it.
361  */
362 static int
363 usermem(listp, func)
364         char ***listp;
365         void *(*func) __P((size_t));
366 {
367         size_t len;
368         char **array, **arrayp, **orig, *strp;
369
370         /* Find out how much space we need. */
371         for (len = 0, orig = *listp; *orig != NULL; ++orig)
372                 len += sizeof(char *) + strlen(*orig) + 1;
373         len += sizeof(char *);
374
375         /*
376          * Allocate it and set up the pointers.
377          *
378          * XXX
379          * Don't simplify this expression, SunOS compilers don't like it.
380          */
381         if (func == NULL)
382                 array = (char **)malloc(len);
383         else
384                 array = (char **)func(len);
385         if (array == NULL)
386                 return (ENOMEM);
387         strp = (char *)(array + (orig - *listp) + 1);
388
389         /* Copy the original information into the new memory. */
390         for (orig = *listp, arrayp = array; *orig != NULL; ++orig, ++arrayp) {
391                 len = strlen(*orig);
392                 memcpy(strp, *orig, len + 1);
393                 *arrayp = strp;
394                 strp += len + 1;
395
396                 FREES(*orig);
397         }
398
399         /* NULL-terminate the list. */
400         *arrayp = NULL;
401
402         free(*listp);
403         *listp = array;
404
405         return (0);
406 }
407
408 static int
409 cmpfunc(p1, p2)
410         const void *p1, *p2;
411 {
412         return (strcmp(*((char **)p1), *((char **)p2)));
413 }