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 
31 static const char *_snap_name(const struct lv_segment *seg)
32 {
33 	return seg->segtype->name;
34 }
35 
36 static 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 
82 static 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
92 static 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 
120 static 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
137 static 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 
158 static 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 
178 fail:
179 	dm_event_handler_destroy(dmevh);
180 	return NULL;
181 }
182 
183 static 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. */
221 static 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 
250 static int _target_register_events(struct lv_segment *seg,
251 				   int events)
252 {
253 	return _target_set_events(seg, events, 1);
254 }
255 
256 static 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 
265 static 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 
277 static void _snap_destroy(const struct segment_type *segtype)
278 {
279 	dm_free((void *)segtype);
280 }
281 
282 static 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
300 struct segment_type *init_snapshot_segtype(struct cmd_context *cmd)
301 #else				/* Shared */
302 struct segment_type *init_segtype(struct cmd_context *cmd);
303 struct 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