1 /* $NetBSD: polldaemon.c,v 1.1.1.1 2008/12/22 00:19:05 haad Exp $ */ 2 3 /* 4 * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved. 5 * Copyright (C) 2004-2007 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 "tools.h" 19 #include "polldaemon.h" 20 #include <signal.h> 21 #include <sys/wait.h> 22 23 static void _sigchld_handler(int sig __attribute((unused))) 24 { 25 while (wait4(-1, NULL, WNOHANG | WUNTRACED, NULL) > 0) ; 26 } 27 28 static int _become_daemon(struct cmd_context *cmd) 29 { 30 pid_t pid; 31 struct sigaction act = { 32 {_sigchld_handler}, 33 .sa_flags = SA_NOCLDSTOP, 34 }; 35 36 log_verbose("Forking background process"); 37 38 sigaction(SIGCHLD, &act, NULL); 39 40 if ((pid = fork()) == -1) { 41 log_error("fork failed: %s", strerror(errno)); 42 return 1; 43 } 44 45 /* Parent */ 46 if (pid > 0) 47 return 0; 48 49 /* Child */ 50 if (setsid() == -1) 51 log_error("Background process failed to setsid: %s", 52 strerror(errno)); 53 init_verbose(VERBOSE_BASE_LEVEL); 54 55 close(STDIN_FILENO); 56 close(STDOUT_FILENO); 57 close(STDERR_FILENO); 58 59 strncpy(*cmd->argv, "(lvm2copyd)", strlen(*cmd->argv)); 60 61 reset_locking(); 62 dev_close_all(); 63 64 return 1; 65 } 66 67 static int _check_mirror_status(struct cmd_context *cmd, 68 struct volume_group *vg, 69 struct logical_volume *lv_mirr, 70 const char *name, struct daemon_parms *parms, 71 int *finished) 72 { 73 struct dm_list *lvs_changed; 74 float segment_percent = 0.0, overall_percent = 0.0; 75 uint32_t event_nr = 0; 76 77 /* By default, caller should not retry */ 78 *finished = 1; 79 80 if (parms->aborting) { 81 if (!(lvs_changed = lvs_using_lv(cmd, vg, lv_mirr))) { 82 log_error("Failed to generate list of copied LVs: " 83 "can't abort."); 84 return 0; 85 } 86 parms->poll_fns->finish_copy(cmd, vg, lv_mirr, lvs_changed); 87 return 0; 88 } 89 90 if (!lv_mirror_percent(cmd, lv_mirr, !parms->interval, &segment_percent, 91 &event_nr)) { 92 log_error("ABORTING: Mirror percentage check failed."); 93 return 0; 94 } 95 96 overall_percent = copy_percent(lv_mirr); 97 if (parms->progress_display) 98 log_print("%s: %s: %.1f%%", name, parms->progress_title, 99 overall_percent); 100 else 101 log_verbose("%s: %s: %.1f%%", name, parms->progress_title, 102 overall_percent); 103 104 if (segment_percent < 100.0) { 105 /* The only case the caller *should* try again later */ 106 *finished = 0; 107 return 1; 108 } 109 110 if (!(lvs_changed = lvs_using_lv(cmd, vg, lv_mirr))) { 111 log_error("ABORTING: Failed to generate list of copied LVs"); 112 return 0; 113 } 114 115 /* Finished? Or progress to next segment? */ 116 if (overall_percent >= 100.0) { 117 if (!parms->poll_fns->finish_copy(cmd, vg, lv_mirr, 118 lvs_changed)) 119 return 0; 120 } else { 121 if (!parms->poll_fns->update_metadata(cmd, vg, lv_mirr, 122 lvs_changed, 0)) { 123 log_error("ABORTING: Segment progression failed."); 124 parms->poll_fns->finish_copy(cmd, vg, lv_mirr, 125 lvs_changed); 126 return 0; 127 } 128 *finished = 0; /* Another segment */ 129 } 130 131 return 1; 132 } 133 134 static int _wait_for_single_mirror(struct cmd_context *cmd, const char *name, 135 struct daemon_parms *parms) 136 { 137 struct volume_group *vg; 138 struct logical_volume *lv_mirr; 139 int finished = 0; 140 141 /* Poll for mirror completion */ 142 while (!finished) { 143 /* FIXME Also needed in vg/lvchange -ay? */ 144 /* FIXME Use alarm for regular intervals instead */ 145 if (parms->interval && !parms->aborting) { 146 sleep(parms->interval); 147 /* Devices might have changed while we slept */ 148 init_full_scan_done(0); 149 } 150 151 /* Locks the (possibly renamed) VG again */ 152 if (!(vg = parms->poll_fns->get_copy_vg(cmd, name))) { 153 log_error("ABORTING: Can't reread VG for %s", name); 154 /* What more could we do here? */ 155 return 0; 156 } 157 158 if (!(lv_mirr = parms->poll_fns->get_copy_lv(cmd, vg, name, 159 parms->lv_type))) { 160 log_error("ABORTING: Can't find mirror LV in %s for %s", 161 vg->name, name); 162 unlock_vg(cmd, vg->name); 163 return 0; 164 } 165 166 if (!_check_mirror_status(cmd, vg, lv_mirr, name, parms, 167 &finished)) { 168 unlock_vg(cmd, vg->name); 169 return 0; 170 } 171 172 unlock_vg(cmd, vg->name); 173 } 174 175 return 1; 176 } 177 178 static int _poll_vg(struct cmd_context *cmd, const char *vgname, 179 struct volume_group *vg, int consistent, void *handle) 180 { 181 struct daemon_parms *parms = (struct daemon_parms *) handle; 182 struct lv_list *lvl; 183 struct logical_volume *lv_mirr; 184 const char *name; 185 int finished; 186 187 if (!vg) { 188 log_error("Couldn't read volume group %s", vgname); 189 return ECMD_FAILED; 190 } 191 192 if (!consistent) { 193 log_error("Volume Group %s inconsistent - skipping", vgname); 194 /* FIXME Should we silently recover it here or not? */ 195 return ECMD_FAILED; 196 } 197 198 if (!vg_check_status(vg, EXPORTED_VG)) 199 return ECMD_FAILED; 200 201 dm_list_iterate_items(lvl, &vg->lvs) { 202 lv_mirr = lvl->lv; 203 if (!(lv_mirr->status & parms->lv_type)) 204 continue; 205 if (!(name = parms->poll_fns->get_copy_name_from_lv(lv_mirr))) 206 continue; 207 /* FIXME Need to do the activation from _set_up_pvmove here 208 * if it's not running and we're not aborting */ 209 if (_check_mirror_status(cmd, vg, lv_mirr, name, 210 parms, &finished) && !finished) 211 parms->outstanding_count++; 212 } 213 214 return ECMD_PROCESSED; 215 216 } 217 218 static void _poll_for_all_vgs(struct cmd_context *cmd, 219 struct daemon_parms *parms) 220 { 221 while (1) { 222 parms->outstanding_count = 0; 223 process_each_vg(cmd, 0, NULL, LCK_VG_WRITE, 1, parms, _poll_vg); 224 if (!parms->outstanding_count) 225 break; 226 sleep(parms->interval); 227 } 228 } 229 230 int poll_daemon(struct cmd_context *cmd, const char *name, unsigned background, 231 uint32_t lv_type, struct poll_functions *poll_fns, 232 const char *progress_title) 233 { 234 struct daemon_parms parms; 235 236 parms.aborting = arg_count(cmd, abort_ARG) ? 1 : 0; 237 parms.background = background; 238 parms.interval = arg_uint_value(cmd, interval_ARG, DEFAULT_INTERVAL); 239 parms.progress_display = 1; 240 parms.progress_title = progress_title; 241 parms.lv_type = lv_type; 242 parms.poll_fns = poll_fns; 243 244 if (parms.interval && !parms.aborting) 245 log_verbose("Checking progress every %u seconds", 246 parms.interval); 247 248 if (!parms.interval) { 249 parms.progress_display = 0; 250 251 /* FIXME Disabled multiple-copy wait_event */ 252 if (!name) 253 parms.interval = DEFAULT_INTERVAL; 254 } 255 256 if (parms.background) { 257 if (!_become_daemon(cmd)) 258 return ECMD_PROCESSED; /* Parent */ 259 parms.progress_display = 0; 260 /* FIXME Use wait_event (i.e. interval = 0) and */ 261 /* fork one daemon per copy? */ 262 } 263 264 if (name) { 265 if (!_wait_for_single_mirror(cmd, name, &parms)) 266 return ECMD_FAILED; 267 } else 268 _poll_for_all_vgs(cmd, &parms); 269 270 return ECMD_PROCESSED; 271 } 272