1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996,2008 Oracle.  All rights reserved.
5 *
6 * $Id: db_checkpoint.c,v 12.22 2008/01/08 20:58:11 bostic Exp $
7 */
8
9#include "db_config.h"
10
11#include "db_int.h"
12
13#ifndef lint
14static const char copyright[] =
15    "Copyright (c) 1996,2008 Oracle.  All rights reserved.\n";
16#endif
17
18int	 main __P((int, char *[]));
19int	 usage __P((void));
20int	 version_check __P((void));
21
22const char *progname;
23
24int
25main(argc, argv)
26	int argc;
27	char *argv[];
28{
29	extern char *optarg;
30	extern int optind;
31	DB_ENV	*dbenv;
32	time_t now;
33	long argval;
34	u_int32_t flags, kbytes, minutes, seconds;
35	int ch, exitval, once, ret, verbose;
36	char *home, *logfile, *passwd, time_buf[CTIME_BUFLEN];
37
38	if ((progname = __db_rpath(argv[0])) == NULL)
39		progname = argv[0];
40	else
41		++progname;
42
43	if ((ret = version_check()) != 0)
44		return (ret);
45
46	/*
47	 * !!!
48	 * Don't allow a fully unsigned 32-bit number, some compilers get
49	 * upset and require it to be specified in hexadecimal and so on.
50	 */
51#define	MAX_UINT32_T	2147483647
52
53	dbenv = NULL;
54	kbytes = minutes = 0;
55	exitval = once = verbose = 0;
56	flags = 0;
57	home = logfile = passwd = NULL;
58	while ((ch = getopt(argc, argv, "1h:k:L:P:p:Vv")) != EOF)
59		switch (ch) {
60		case '1':
61			once = 1;
62			flags = DB_FORCE;
63			break;
64		case 'h':
65			home = optarg;
66			break;
67		case 'k':
68			if (__db_getlong(NULL, progname,
69			    optarg, 1, (long)MAX_UINT32_T, &argval))
70				return (EXIT_FAILURE);
71			kbytes = (u_int32_t)argval;
72			break;
73		case 'L':
74			logfile = optarg;
75			break;
76		case 'P':
77			passwd = strdup(optarg);
78			memset(optarg, 0, strlen(optarg));
79			if (passwd == NULL) {
80				fprintf(stderr, "%s: strdup: %s\n",
81				    progname, strerror(errno));
82				return (EXIT_FAILURE);
83			}
84			break;
85		case 'p':
86			if (__db_getlong(NULL, progname,
87			    optarg, 1, (long)MAX_UINT32_T, &argval))
88				return (EXIT_FAILURE);
89			minutes = (u_int32_t)argval;
90			break;
91		case 'V':
92			printf("%s\n", db_version(NULL, NULL, NULL));
93			return (EXIT_SUCCESS);
94		case 'v':
95			verbose = 1;
96			break;
97		case '?':
98		default:
99			return (usage());
100		}
101	argc -= optind;
102	argv += optind;
103
104	if (argc != 0)
105		return (usage());
106
107	if (once == 0 && kbytes == 0 && minutes == 0) {
108		(void)fprintf(stderr,
109		    "%s: at least one of -1, -k and -p must be specified\n",
110		    progname);
111		return (usage());
112	}
113
114	/* Handle possible interruptions. */
115	__db_util_siginit();
116
117	/* Log our process ID. */
118	if (logfile != NULL && __db_util_logset(progname, logfile))
119		goto shutdown;
120
121	/*
122	 * Create an environment object and initialize it for error
123	 * reporting.
124	 */
125	if ((ret = db_env_create(&dbenv, 0)) != 0) {
126		fprintf(stderr,
127		    "%s: db_env_create: %s\n", progname, db_strerror(ret));
128		goto shutdown;
129	}
130
131	dbenv->set_errfile(dbenv, stderr);
132	dbenv->set_errpfx(dbenv, progname);
133
134	if (passwd != NULL && (ret = dbenv->set_encrypt(dbenv,
135	    passwd, DB_ENCRYPT_AES)) != 0) {
136		dbenv->err(dbenv, ret, "set_passwd");
137		goto shutdown;
138	}
139
140	/*
141	 * If attaching to a pre-existing environment fails, create a
142	 * private one and try again.
143	 */
144	if ((ret = dbenv->open(dbenv, home, DB_USE_ENVIRON, 0)) != 0 &&
145	    (!once || ret == DB_VERSION_MISMATCH ||
146	    (ret = dbenv->open(dbenv, home,
147	    DB_CREATE | DB_INIT_TXN | DB_PRIVATE | DB_USE_ENVIRON, 0)) != 0)) {
148		dbenv->err(dbenv, ret, "DB_ENV->open");
149		goto shutdown;
150	}
151
152	/*
153	 * If we have only a time delay, then we'll sleep the right amount
154	 * to wake up when a checkpoint is necessary.  If we have a "kbytes"
155	 * field set, then we'll check every 30 seconds.
156	 */
157	seconds = kbytes != 0 ? 30 : minutes * 60;
158	while (!__db_util_interrupted()) {
159		if (verbose) {
160			(void)time(&now);
161			dbenv->errx(dbenv,
162		    "checkpoint begin: %s", __os_ctime(&now, time_buf));
163		}
164
165		if ((ret = dbenv->txn_checkpoint(dbenv,
166		    kbytes, minutes, flags)) != 0) {
167			dbenv->err(dbenv, ret, "txn_checkpoint");
168			goto shutdown;
169		}
170
171		if (verbose) {
172			(void)time(&now);
173			dbenv->errx(dbenv,
174		    "checkpoint complete: %s", __os_ctime(&now, time_buf));
175		}
176
177		if (once)
178			break;
179
180		__os_yield(dbenv->env, seconds, 0);
181	}
182
183	if (0) {
184shutdown:	exitval = 1;
185	}
186
187	/* Clean up the logfile. */
188	if (logfile != NULL)
189		(void)remove(logfile);
190
191	/* Clean up the environment. */
192	if (dbenv != NULL && (ret = dbenv->close(dbenv, 0)) != 0) {
193		exitval = 1;
194		fprintf(stderr,
195		    "%s: dbenv->close: %s\n", progname, db_strerror(ret));
196	}
197
198	if (passwd != NULL)
199		free(passwd);
200
201	/* Resend any caught signal. */
202	__db_util_sigresend();
203
204	return (exitval == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
205}
206
207int
208usage()
209{
210	(void)fprintf(stderr, "usage: %s [-1Vv]\n\t%s\n", progname,
211	    "[-h home] [-k kbytes] [-L file] [-P password] [-p min]");
212	return (EXIT_FAILURE);
213}
214
215int
216version_check()
217{
218	int v_major, v_minor, v_patch;
219
220	/* Make sure we're loaded with the right version of the DB library. */
221	(void)db_version(&v_major, &v_minor, &v_patch);
222	if (v_major != DB_VERSION_MAJOR || v_minor != DB_VERSION_MINOR) {
223		fprintf(stderr,
224	"%s: version %d.%d doesn't match library version %d.%d\n",
225		    progname, DB_VERSION_MAJOR, DB_VERSION_MINOR,
226		    v_major, v_minor);
227		return (EXIT_FAILURE);
228	}
229	return (0);
230}
231