1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2007 Diomidis Spinellis
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30#include <sys/types.h>
31#include <sys/acct.h>
32
33#include <ctype.h>
34#include <err.h>
35#include <errno.h>
36#include <db.h>
37#include <fcntl.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <limits.h>
42
43#include "extern.h"
44
45/* Key used to store the version of the database data elements. */
46static char VERSION_KEY[] = "\0VERSION";
47
48/*
49 * Create the in-memory database, *mdb.
50 * If iflag is not set, fill-in mdb with the records of the disk-based
51 * database dbname.
52 * Upgrade old-version records by calling v1_to_v2.
53 * Return 0 if OK, -1 on error.
54 */
55int
56db_copy_in(DB **mdb, const char *dbname, const char *uname, BTREEINFO *bti,
57    int (*v1_to_v2)(DBT *key, DBT *data))
58{
59	DBT key, data;
60	DB *ddb;
61	int error, rv, version;
62
63	if ((*mdb = dbopen(NULL, O_RDWR, 0, DB_BTREE, bti)) == NULL)
64		return (-1);
65
66	if (iflag)
67		return (0);
68
69	if ((ddb = dbopen(dbname, O_RDONLY, 0, DB_BTREE, bti)) == NULL) {
70		if (errno == ENOENT)
71			return (0);
72		warn("retrieving %s summary", uname);
73		db_destroy(*mdb, uname);
74		return (-1);
75	}
76
77	error = 0;
78
79	/* Obtain/set version. */
80	version = 1;
81	key.data = (void*)&VERSION_KEY;
82	key.size = sizeof(VERSION_KEY);
83
84	rv = DB_GET(ddb, &key, &data, 0);
85	if (rv < 0) {
86		warn("get version key from %s stats", uname);
87		error = -1;
88		goto closeout;
89	} else if (rv == 0) {	/* It's there; verify version. */
90		if (data.size != sizeof(version)) {
91			warnx("invalid version size %zd in %s",
92			    data.size, uname);
93			error = -1;
94			goto closeout;
95		}
96		memcpy(&version, data.data, data.size);
97		if (version != 2) {
98			warnx("unsupported version %d in %s",
99			    version, uname);
100			error = -1;
101			goto closeout;
102		}
103	}
104
105	for (rv = DB_SEQ(ddb, &key, &data, R_FIRST); rv == 0;
106	    rv = DB_SEQ(ddb, &key, &data, R_NEXT)) {
107
108		/* See if this is a version record. */
109		if (key.size == sizeof(VERSION_KEY) &&
110		    memcmp(key.data, VERSION_KEY, sizeof(VERSION_KEY)) == 0)
111			continue;
112
113		/* Convert record from v1, if needed. */
114		if (version == 1 && v1_to_v2(&key, &data) < 0) {
115			warn("converting %s stats", uname);
116			error = -1;
117			goto closeout;
118		}
119
120		/* Copy record to the in-memory database. */
121		if ((rv = DB_PUT(*mdb, &key, &data, 0)) < 0) {
122			warn("initializing %s stats", uname);
123			error = -1;
124			goto closeout;
125		}
126	}
127	if (rv < 0) {
128		warn("retrieving %s summary", uname);
129		error = -1;
130	}
131
132closeout:
133	if (DB_CLOSE(ddb) < 0) {
134		warn("closing %s summary", uname);
135		error = -1;
136	}
137
138	if (error)
139		db_destroy(*mdb, uname);
140	return (error);
141}
142
143/*
144 * Save the in-memory database mdb to the disk database dbname.
145 * Return 0 if OK, -1 on error.
146 */
147int
148db_copy_out(DB *mdb, const char *dbname, const char *uname, BTREEINFO *bti)
149{
150	DB *ddb;
151	DBT key, data;
152	int error, rv, version;
153
154	if ((ddb = dbopen(dbname, O_RDWR|O_CREAT|O_TRUNC, 0644,
155	    DB_BTREE, bti)) == NULL) {
156		warn("creating %s summary", uname);
157		return (-1);
158	}
159
160	error = 0;
161
162	for (rv = DB_SEQ(mdb, &key, &data, R_FIRST);
163	    rv == 0; rv = DB_SEQ(mdb, &key, &data, R_NEXT)) {
164		if ((rv = DB_PUT(ddb, &key, &data, 0)) < 0) {
165			warn("saving %s summary", uname);
166			error = -1;
167			goto out;
168		}
169	}
170	if (rv < 0) {
171		warn("retrieving %s stats", uname);
172		error = -1;
173	}
174
175out:
176	/* Add a version record. */
177	key.data = (void*)&VERSION_KEY;
178	key.size = sizeof(VERSION_KEY);
179	version = 2;
180	data.data = &version;
181	data.size = sizeof(version);
182	if ((rv = DB_PUT(ddb, &key, &data, 0)) < 0) {
183		warn("add version record to %s stats", uname);
184		error = -1;
185	} else if (rv == 1) {
186		warnx("duplicate version record in %s stats", uname);
187		error = -1;
188	}
189
190	if (DB_SYNC(ddb, 0) < 0) {
191		warn("syncing %s summary", uname);
192		error = -1;
193	}
194	if (DB_CLOSE(ddb) < 0) {
195		warn("closing %s summary", uname);
196		error = -1;
197	}
198	return error;
199}
200
201void
202db_destroy(DB *db, const char *uname)
203{
204	if (DB_CLOSE(db) < 0)
205		warn("destroying %s stats", uname);
206}
207