1 /* $NetBSD: dmeventd_mirror.c,v 1.1.1.2 2009/12/02 00:27:12 haad Exp $ */ 2 3 /* 4 * Copyright (C) 2005 Red Hat, Inc. All rights reserved. 5 * 6 * This file is part of LVM2. 7 * 8 * This copyrighted material is made available to anyone wishing to use, 9 * modify, copy, or redistribute it subject to the terms and conditions 10 * of the GNU Lesser General Public License v.2.1. 11 * 12 * You should have received a copy of the GNU Lesser General Public License 13 * along with this program; if not, write to the Free Software Foundation, 14 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 */ 16 17 #include "lvm2cmd.h" 18 #include "errors.h" 19 20 #include <libdevmapper.h> 21 #include <libdevmapper-event.h> 22 #include <errno.h> 23 #include <signal.h> 24 #include <string.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <pthread.h> 28 #include <unistd.h> 29 30 #include <syslog.h> /* FIXME Replace syslog with multilog */ 31 /* FIXME Missing openlog? */ 32 33 #define ME_IGNORE 0 34 #define ME_INSYNC 1 35 #define ME_FAILURE 2 36 37 /* 38 * register_device() is called first and performs initialisation. 39 * Only one device may be registered or unregistered at a time. 40 */ 41 static pthread_mutex_t _register_mutex = PTHREAD_MUTEX_INITIALIZER; 42 43 /* 44 * Number of active registrations. 45 */ 46 static int _register_count = 0; 47 48 static struct dm_pool *_mem_pool = NULL; 49 static void *_lvm_handle = NULL; 50 51 /* 52 * Currently only one event can be processed at a time. 53 */ 54 static pthread_mutex_t _event_mutex = PTHREAD_MUTEX_INITIALIZER; 55 56 static int _get_mirror_event(char *params) 57 { 58 int i, r = ME_INSYNC; 59 char **args = NULL; 60 char *dev_status_str; 61 char *log_status_str; 62 char *sync_str; 63 char *p = NULL; 64 int log_argc, num_devs; 65 66 /* 67 * dm core parms: 0 409600 mirror 68 * Mirror core parms: 2 253:4 253:5 400/400 69 * New-style failure params: 1 AA 70 * New-style log params: 3 cluster 253:3 A 71 * or 3 disk 253:3 A 72 * or 1 core 73 */ 74 75 /* number of devices */ 76 if (!dm_split_words(params, 1, 0, &p)) 77 goto out_parse; 78 79 if (!(num_devs = atoi(p))) 80 goto out_parse; 81 p += strlen(p) + 1; 82 83 /* devices names + "400/400" + "1 AA" + 1 or 3 log parms + NULL */ 84 args = dm_malloc((num_devs + 7) * sizeof(char *)); 85 if (!args || dm_split_words(p, num_devs + 7, 0, args) < num_devs + 5) 86 goto out_parse; 87 88 dev_status_str = args[2 + num_devs]; 89 log_argc = atoi(args[3 + num_devs]); 90 log_status_str = args[3 + num_devs + log_argc]; 91 sync_str = args[num_devs]; 92 93 /* Check for bad mirror devices */ 94 for (i = 0; i < num_devs; i++) 95 if (dev_status_str[i] == 'D') { 96 syslog(LOG_ERR, "Mirror device, %s, has failed.\n", args[i]); 97 r = ME_FAILURE; 98 } 99 100 /* Check for bad disk log device */ 101 if (log_argc > 1 && log_status_str[0] == 'D') { 102 syslog(LOG_ERR, "Log device, %s, has failed.\n", 103 args[2 + num_devs + log_argc]); 104 r = ME_FAILURE; 105 } 106 107 if (r == ME_FAILURE) 108 goto out; 109 110 p = strstr(sync_str, "/"); 111 if (p) { 112 p[0] = '\0'; 113 if (strcmp(sync_str, p+1)) 114 r = ME_IGNORE; 115 p[0] = '/'; 116 } else 117 goto out_parse; 118 119 out: 120 if (args) 121 dm_free(args); 122 return r; 123 124 out_parse: 125 if (args) 126 dm_free(args); 127 syslog(LOG_ERR, "Unable to parse mirror status string."); 128 return ME_IGNORE; 129 } 130 131 static void _temporary_log_fn(int level, 132 const char *file __attribute((unused)), 133 int line __attribute((unused)), 134 int dm_errno __attribute((unused)), 135 const char *format) 136 { 137 if (!strncmp(format, "WARNING: ", 9) && (level < 5)) 138 syslog(LOG_CRIT, "%s", format); 139 else 140 syslog(LOG_DEBUG, "%s", format); 141 } 142 143 static int _remove_failed_devices(const char *device) 144 { 145 int r; 146 #define CMD_SIZE 256 /* FIXME Use system restriction */ 147 char cmd_str[CMD_SIZE]; 148 char *vg = NULL, *lv = NULL, *layer = NULL; 149 150 if (strlen(device) > 200) /* FIXME Use real restriction */ 151 return -ENAMETOOLONG; /* FIXME These return code distinctions are not used so remove them! */ 152 153 if (!dm_split_lvm_name(_mem_pool, device, &vg, &lv, &layer)) { 154 syslog(LOG_ERR, "Unable to determine VG name from %s", 155 device); 156 return -ENOMEM; /* FIXME Replace with generic error return - reason for failure has already got logged */ 157 } 158 159 /* FIXME Is any sanity-checking required on %s? */ 160 if (CMD_SIZE <= snprintf(cmd_str, CMD_SIZE, "lvconvert --config devices{ignore_suspended_devices=1} --repair --use-policies %s/%s", vg, lv)) { 161 /* this error should be caught above, but doesn't hurt to check again */ 162 syslog(LOG_ERR, "Unable to form LVM command: Device name too long"); 163 dm_pool_empty(_mem_pool); /* FIXME: not safe with multiple threads */ 164 return -ENAMETOOLONG; /* FIXME Replace with generic error return - reason for failure has already got logged */ 165 } 166 167 r = lvm2_run(_lvm_handle, cmd_str); 168 169 if (r == ECMD_PROCESSED) { 170 snprintf(cmd_str, CMD_SIZE, "vgreduce --removemissing %s", vg); 171 if (lvm2_run(_lvm_handle, cmd_str) != 1) 172 syslog(LOG_ERR, "Unable to remove failed PVs from VG %s", vg); 173 } 174 175 dm_pool_empty(_mem_pool); /* FIXME: not safe with multiple threads */ 176 return (r == ECMD_PROCESSED) ? 0 : -1; 177 } 178 179 void process_event(struct dm_task *dmt, 180 enum dm_event_mask event __attribute((unused)), 181 void **unused __attribute((unused))) 182 { 183 void *next = NULL; 184 uint64_t start, length; 185 char *target_type = NULL; 186 char *params; 187 const char *device = dm_task_get_name(dmt); 188 189 if (pthread_mutex_trylock(&_event_mutex)) { 190 syslog(LOG_NOTICE, "Another thread is handling an event. Waiting..."); 191 pthread_mutex_lock(&_event_mutex); 192 } 193 do { 194 next = dm_get_next_target(dmt, next, &start, &length, 195 &target_type, ¶ms); 196 197 if (!target_type) { 198 syslog(LOG_INFO, "%s mapping lost.\n", device); 199 continue; 200 } 201 202 if (strcmp(target_type, "mirror")) { 203 syslog(LOG_INFO, "%s has unmirrored portion.\n", device); 204 continue; 205 } 206 207 switch(_get_mirror_event(params)) { 208 case ME_INSYNC: 209 /* FIXME: all we really know is that this 210 _part_ of the device is in sync 211 Also, this is not an error 212 */ 213 syslog(LOG_NOTICE, "%s is now in-sync\n", device); 214 break; 215 case ME_FAILURE: 216 syslog(LOG_ERR, "Device failure in %s\n", device); 217 if (_remove_failed_devices(device)) 218 /* FIXME Why are all the error return codes unused? Get rid of them? */ 219 syslog(LOG_ERR, "Failed to remove faulty devices in %s\n", 220 device); 221 /* Should check before warning user that device is now linear 222 else 223 syslog(LOG_NOTICE, "%s is now a linear device.\n", 224 device); 225 */ 226 break; 227 case ME_IGNORE: 228 break; 229 default: 230 /* FIXME Provide value then! */ 231 syslog(LOG_INFO, "Unknown event received.\n"); 232 } 233 } while (next); 234 235 pthread_mutex_unlock(&_event_mutex); 236 } 237 238 int register_device(const char *device, 239 const char *uuid __attribute((unused)), 240 int major __attribute((unused)), 241 int minor __attribute((unused)), 242 void **unused __attribute((unused))) 243 { 244 int r = 0; 245 246 pthread_mutex_lock(&_register_mutex); 247 248 /* 249 * Need some space for allocations. 1024 should be more 250 * than enough for what we need (device mapper name splitting) 251 */ 252 if (!_mem_pool && !(_mem_pool = dm_pool_create("mirror_dso", 1024))) 253 goto out; 254 255 if (!_lvm_handle) { 256 lvm2_log_fn(_temporary_log_fn); 257 if (!(_lvm_handle = lvm2_init())) { 258 dm_pool_destroy(_mem_pool); 259 _mem_pool = NULL; 260 goto out; 261 } 262 lvm2_log_level(_lvm_handle, LVM2_LOG_SUPPRESS); 263 /* FIXME Temporary: move to dmeventd core */ 264 lvm2_run(_lvm_handle, "_memlock_inc"); 265 } 266 267 syslog(LOG_INFO, "Monitoring mirror device %s for events\n", device); 268 269 _register_count++; 270 r = 1; 271 272 out: 273 pthread_mutex_unlock(&_register_mutex); 274 275 return r; 276 } 277 278 int unregister_device(const char *device, 279 const char *uuid __attribute((unused)), 280 int major __attribute((unused)), 281 int minor __attribute((unused)), 282 void **unused __attribute((unused))) 283 { 284 pthread_mutex_lock(&_register_mutex); 285 286 syslog(LOG_INFO, "No longer monitoring mirror device %s for events\n", 287 device); 288 289 if (!--_register_count) { 290 dm_pool_destroy(_mem_pool); 291 _mem_pool = NULL; 292 lvm2_run(_lvm_handle, "_memlock_dec"); 293 lvm2_exit(_lvm_handle); 294 _lvm_handle = NULL; 295 } 296 297 pthread_mutex_unlock(&_register_mutex); 298 299 return 1; 300 } 301