1/*	$NetBSD: snapshot.c,v 1.1.1.2 2009/12/02 00:26:27 haad Exp $	*/
2
3/*
4 * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
5 * Copyright (C) 2004-2008 Red Hat, Inc. All rights reserved.
6 *
7 * This file is part of LVM2.
8 *
9 * This copyrighted material is made available to anyone wishing to use,
10 * modify, copy, or redistribute it subject to the terms and conditions
11 * of the GNU Lesser General Public License v.2.1.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software Foundation,
15 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16 */
17
18#include "lib.h"
19#include "toolcontext.h"
20#include "metadata.h"
21#include "segtype.h"
22#include "text_export.h"
23#include "config.h"
24#include "activate.h"
25#include "str_list.h"
26#ifdef DMEVENTD
27#  include "sharedlib.h"
28#  include "libdevmapper-event.h"
29#endif
30
31static const char *_snap_name(const struct lv_segment *seg)
32{
33	return seg->segtype->name;
34}
35
36static int _snap_text_import(struct lv_segment *seg, const struct config_node *sn,
37			struct dm_hash_table *pv_hash __attribute((unused)))
38{
39	uint32_t chunk_size;
40	const char *org_name, *cow_name;
41	struct logical_volume *org, *cow;
42	int old_suppress;
43
44	if (!get_config_uint32(sn, "chunk_size", &chunk_size)) {
45		log_error("Couldn't read chunk size for snapshot.");
46		return 0;
47	}
48
49	old_suppress = log_suppress(1);
50
51	if (!(cow_name = find_config_str(sn, "cow_store", NULL))) {
52		log_suppress(old_suppress);
53		log_error("Snapshot cow storage not specified.");
54		return 0;
55	}
56
57	if (!(org_name = find_config_str(sn, "origin", NULL))) {
58		log_suppress(old_suppress);
59		log_error("Snapshot origin not specified.");
60		return 0;
61	}
62
63	log_suppress(old_suppress);
64
65	if (!(cow = find_lv(seg->lv->vg, cow_name))) {
66		log_error("Unknown logical volume specified for "
67			  "snapshot cow store.");
68		return 0;
69	}
70
71	if (!(org = find_lv(seg->lv->vg, org_name))) {
72		log_error("Unknown logical volume specified for "
73			  "snapshot origin.");
74		return 0;
75	}
76
77	init_snapshot_seg(seg, org, cow, chunk_size);
78
79	return 1;
80}
81
82static int _snap_text_export(const struct lv_segment *seg, struct formatter *f)
83{
84	outf(f, "chunk_size = %u", seg->chunk_size);
85	outf(f, "origin = \"%s\"", seg->origin->name);
86	outf(f, "cow_store = \"%s\"", seg->cow->name);
87
88	return 1;
89}
90
91#ifdef DEVMAPPER_SUPPORT
92static int _snap_target_percent(void **target_state __attribute((unused)),
93				percent_range_t *percent_range,
94				struct dm_pool *mem __attribute((unused)),
95				struct cmd_context *cmd __attribute((unused)),
96				struct lv_segment *seg __attribute((unused)),
97				char *params, uint64_t *total_numerator,
98				uint64_t *total_denominator)
99{
100	uint64_t numerator, denominator;
101
102	if (sscanf(params, "%" PRIu64 "/%" PRIu64,
103		   &numerator, &denominator) == 2) {
104		*total_numerator += numerator;
105		*total_denominator += denominator;
106		if (!numerator)
107			*percent_range = PERCENT_0;
108		else if (numerator == denominator)
109			*percent_range = PERCENT_100;
110		else
111			*percent_range = PERCENT_0_TO_100;
112	} else if (!strcmp(params, "Invalid"))
113		*percent_range = PERCENT_INVALID;
114	else
115		return 0;
116
117	return 1;
118}
119
120static int _snap_target_present(struct cmd_context *cmd,
121				const struct lv_segment *seg __attribute((unused)),
122				unsigned *attributes __attribute((unused)))
123{
124	static int _snap_checked = 0;
125	static int _snap_present = 0;
126
127	if (!_snap_checked)
128		_snap_present = target_present(cmd, "snapshot", 1) &&
129		    target_present(cmd, "snapshot-origin", 0);
130
131	_snap_checked = 1;
132
133	return _snap_present;
134}
135
136#ifdef DMEVENTD
137static int _get_snapshot_dso_path(struct cmd_context *cmd, char **dso)
138{
139	char *path;
140	const char *libpath;
141
142	if (!(path = dm_pool_alloc(cmd->mem, PATH_MAX))) {
143		log_error("Failed to allocate dmeventd library path.");
144		return 0;
145	}
146
147	libpath = find_config_tree_str(cmd, "dmeventd/snapshot_library", NULL);
148	if (!libpath)
149		return 0;
150
151	get_shared_library_path(cmd, libpath, path, PATH_MAX);
152
153	*dso = path;
154
155	return 1;
156}
157
158static struct dm_event_handler *_create_dm_event_handler(const char *dmname,
159							 const char *dso,
160							 const int timeout,
161							 enum dm_event_mask mask)
162{
163	struct dm_event_handler *dmevh;
164
165	if (!(dmevh = dm_event_handler_create()))
166		return_0;
167
168       if (dm_event_handler_set_dso(dmevh, dso))
169		goto fail;
170
171	if (dm_event_handler_set_dev_name(dmevh, dmname))
172		goto fail;
173
174	dm_event_handler_set_timeout(dmevh, timeout);
175	dm_event_handler_set_event_mask(dmevh, mask);
176	return dmevh;
177
178fail:
179	dm_event_handler_destroy(dmevh);
180	return NULL;
181}
182
183static int _target_registered(struct lv_segment *seg, int *pending)
184{
185	char *dso, *name;
186	struct logical_volume *lv;
187	struct volume_group *vg;
188	enum dm_event_mask evmask = 0;
189	struct dm_event_handler *dmevh;
190
191	lv = seg->lv;
192	vg = lv->vg;
193
194	*pending = 0;
195	if (!_get_snapshot_dso_path(vg->cmd, &dso))
196		return_0;
197
198	if (!(name = build_dm_name(vg->cmd->mem, vg->name, seg->cow->name, NULL)))
199		return_0;
200
201	if (!(dmevh = _create_dm_event_handler(name, dso, 0, DM_EVENT_ALL_ERRORS)))
202		return_0;
203
204	if (dm_event_get_registered_device(dmevh, 0)) {
205		dm_event_handler_destroy(dmevh);
206		return 0;
207	}
208
209	evmask = dm_event_handler_get_event_mask(dmevh);
210	if (evmask & DM_EVENT_REGISTRATION_PENDING) {
211		*pending = 1;
212		evmask &= ~DM_EVENT_REGISTRATION_PENDING;
213	}
214
215	dm_event_handler_destroy(dmevh);
216
217	return evmask;
218}
219
220/* FIXME This gets run while suspended and performs banned operations. */
221static int _target_set_events(struct lv_segment *seg,
222			      int events __attribute((unused)), int set)
223{
224	char *dso, *name;
225	struct volume_group *vg = seg->lv->vg;
226	struct dm_event_handler *dmevh;
227	int r;
228
229	if (!_get_snapshot_dso_path(vg->cmd, &dso))
230		return_0;
231
232	if (!(name = build_dm_name(vg->cmd->mem, vg->name, seg->cow->name, NULL)))
233		return_0;
234
235	/* FIXME: make timeout configurable */
236	if (!(dmevh = _create_dm_event_handler(name, dso, 10,
237		DM_EVENT_ALL_ERRORS|DM_EVENT_TIMEOUT)))
238		return_0;
239
240	r = set ? dm_event_register_handler(dmevh) : dm_event_unregister_handler(dmevh);
241	dm_event_handler_destroy(dmevh);
242	if (!r)
243		return_0;
244
245	log_info("%s %s for events", set ? "Registered" : "Unregistered", name);
246
247	return 1;
248}
249
250static int _target_register_events(struct lv_segment *seg,
251				   int events)
252{
253	return _target_set_events(seg, events, 1);
254}
255
256static int _target_unregister_events(struct lv_segment *seg,
257				     int events)
258{
259	return _target_set_events(seg, events, 0);
260}
261
262#endif /* DMEVENTD */
263#endif
264
265static int _snap_modules_needed(struct dm_pool *mem,
266				const struct lv_segment *seg __attribute((unused)),
267				struct dm_list *modules)
268{
269	if (!str_list_add(mem, modules, "snapshot")) {
270		log_error("snapshot string list allocation failed");
271		return 0;
272	}
273
274	return 1;
275}
276
277static void _snap_destroy(const struct segment_type *segtype)
278{
279	dm_free((void *)segtype);
280}
281
282static struct segtype_handler _snapshot_ops = {
283	.name = _snap_name,
284	.text_import = _snap_text_import,
285	.text_export = _snap_text_export,
286#ifdef DEVMAPPER_SUPPORT
287	.target_percent = _snap_target_percent,
288	.target_present = _snap_target_present,
289#ifdef DMEVENTD
290	.target_monitored = _target_registered,
291	.target_monitor_events = _target_register_events,
292	.target_unmonitor_events = _target_unregister_events,
293#endif
294#endif
295	.modules_needed = _snap_modules_needed,
296	.destroy = _snap_destroy,
297};
298
299#ifdef SNAPSHOT_INTERNAL
300struct segment_type *init_snapshot_segtype(struct cmd_context *cmd)
301#else				/* Shared */
302struct segment_type *init_segtype(struct cmd_context *cmd);
303struct segment_type *init_segtype(struct cmd_context *cmd)
304#endif
305{
306	struct segment_type *segtype = dm_malloc(sizeof(*segtype));
307#ifdef DMEVENTD
308	char *dso;
309#endif
310
311	if (!segtype)
312		return_NULL;
313
314	segtype->cmd = cmd;
315	segtype->ops = &_snapshot_ops;
316	segtype->name = "snapshot";
317	segtype->private = NULL;
318	segtype->flags = SEG_SNAPSHOT;
319
320#ifdef DMEVENTD
321	if (_get_snapshot_dso_path(cmd, &dso))
322		segtype->flags |= SEG_MONITORED;
323#endif
324	log_very_verbose("Initialised segtype: %s", segtype->name);
325
326	return segtype;
327}
328