Home | History | Annotate | Line # | Download | only in snapshot
      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