1 /* $NetBSD: polldaemon.c,v 1.1.1.2 2009/12/02 00:25:53 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 lvmcache_init(); 63 dev_close_all(); 64 65 return 1; 66 } 67 68 progress_t poll_mirror_progress(struct cmd_context *cmd, 69 struct logical_volume *lv, const char *name, 70 struct daemon_parms *parms) 71 { 72 float segment_percent = 0.0, overall_percent = 0.0; 73 percent_range_t percent_range, overall_percent_range; 74 uint32_t event_nr = 0; 75 76 if (!lv_mirror_percent(cmd, lv, !parms->interval, &segment_percent, 77 &percent_range, &event_nr) || 78 (percent_range == PERCENT_INVALID)) { 79 log_error("ABORTING: Mirror percentage check failed."); 80 return PROGRESS_CHECK_FAILED; 81 } 82 83 overall_percent = copy_percent(lv, &overall_percent_range); 84 if (parms->progress_display) 85 log_print("%s: %s: %.1f%%", name, parms->progress_title, 86 overall_percent); 87 else 88 log_verbose("%s: %s: %.1f%%", name, parms->progress_title, 89 overall_percent); 90 91 if (percent_range != PERCENT_100) 92 return PROGRESS_UNFINISHED; 93 94 if (overall_percent_range == PERCENT_100) 95 return PROGRESS_FINISHED_ALL; 96 97 return PROGRESS_FINISHED_SEGMENT; 98 } 99 100 static int _check_lv_status(struct cmd_context *cmd, 101 struct volume_group *vg, 102 struct logical_volume *lv, 103 const char *name, struct daemon_parms *parms, 104 int *finished) 105 { 106 struct dm_list *lvs_changed; 107 progress_t progress; 108 109 /* By default, caller should not retry */ 110 *finished = 1; 111 112 if (parms->aborting) { 113 if (!(lvs_changed = lvs_using_lv(cmd, vg, lv))) { 114 log_error("Failed to generate list of copied LVs: " 115 "can't abort."); 116 return 0; 117 } 118 parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed); 119 return 0; 120 } 121 122 progress = parms->poll_fns->poll_progress(cmd, lv, name, parms); 123 if (progress == PROGRESS_CHECK_FAILED) 124 return_0; 125 126 if (progress == PROGRESS_UNFINISHED) { 127 /* The only case the caller *should* try again later */ 128 *finished = 0; 129 return 1; 130 } 131 132 if (!(lvs_changed = lvs_using_lv(cmd, vg, lv))) { 133 log_error("ABORTING: Failed to generate list of copied LVs"); 134 return 0; 135 } 136 137 /* Finished? Or progress to next segment? */ 138 if (progress == PROGRESS_FINISHED_ALL) { 139 if (!parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed)) 140 return 0; 141 } else { 142 if (!parms->poll_fns->update_metadata(cmd, vg, lv, lvs_changed, 143 0)) { 144 log_error("ABORTING: Segment progression failed."); 145 parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed); 146 return 0; 147 } 148 *finished = 0; /* Another segment */ 149 } 150 151 return 1; 152 } 153 154 static int _wait_for_single_lv(struct cmd_context *cmd, const char *name, const char *uuid, 155 struct daemon_parms *parms) 156 { 157 struct volume_group *vg; 158 struct logical_volume *lv; 159 int finished = 0; 160 161 /* Poll for completion */ 162 while (!finished) { 163 /* FIXME Also needed in vg/lvchange -ay? */ 164 /* FIXME Use alarm for regular intervals instead */ 165 if (parms->interval && !parms->aborting) { 166 sleep(parms->interval); 167 /* Devices might have changed while we slept */ 168 init_full_scan_done(0); 169 } 170 171 /* Locks the (possibly renamed) VG again */ 172 vg = parms->poll_fns->get_copy_vg(cmd, name, uuid); 173 if (vg_read_error(vg)) { 174 vg_release(vg); 175 log_error("ABORTING: Can't reread VG for %s", name); 176 /* What more could we do here? */ 177 return 0; 178 } 179 180 if (!(lv = parms->poll_fns->get_copy_lv(cmd, vg, name, uuid, 181 parms->lv_type))) { 182 log_error("ABORTING: Can't find mirror LV in %s for %s", 183 vg->name, name); 184 unlock_and_release_vg(cmd, vg, vg->name); 185 return 0; 186 } 187 188 if (!_check_lv_status(cmd, vg, lv, name, parms, &finished)) { 189 unlock_and_release_vg(cmd, vg, vg->name); 190 return 0; 191 } 192 193 unlock_and_release_vg(cmd, vg, vg->name); 194 } 195 196 return 1; 197 } 198 199 static int _poll_vg(struct cmd_context *cmd, const char *vgname, 200 struct volume_group *vg, void *handle) 201 { 202 struct daemon_parms *parms = (struct daemon_parms *) handle; 203 struct lv_list *lvl; 204 struct logical_volume *lv; 205 const char *name; 206 int finished; 207 208 dm_list_iterate_items(lvl, &vg->lvs) { 209 lv = lvl->lv; 210 if (!(lv->status & parms->lv_type)) 211 continue; 212 if (!(name = parms->poll_fns->get_copy_name_from_lv(lv))) 213 continue; 214 /* FIXME Need to do the activation from _set_up_pvmove here 215 * if it's not running and we're not aborting */ 216 if (_check_lv_status(cmd, vg, lv, name, parms, &finished) && 217 !finished) 218 parms->outstanding_count++; 219 } 220 221 return ECMD_PROCESSED; 222 223 } 224 225 static void _poll_for_all_vgs(struct cmd_context *cmd, 226 struct daemon_parms *parms) 227 { 228 while (1) { 229 parms->outstanding_count = 0; 230 process_each_vg(cmd, 0, NULL, READ_FOR_UPDATE, parms, _poll_vg); 231 if (!parms->outstanding_count) 232 break; 233 sleep(parms->interval); 234 } 235 } 236 237 int poll_daemon(struct cmd_context *cmd, const char *name, const char *uuid, 238 unsigned background, 239 uint32_t lv_type, struct poll_functions *poll_fns, 240 const char *progress_title) 241 { 242 struct daemon_parms parms; 243 244 parms.aborting = arg_is_set(cmd, abort_ARG); 245 parms.background = background; 246 parms.interval = arg_uint_value(cmd, interval_ARG, DEFAULT_INTERVAL); 247 parms.progress_display = 1; 248 parms.progress_title = progress_title; 249 parms.lv_type = lv_type; 250 parms.poll_fns = poll_fns; 251 252 if (parms.interval && !parms.aborting) 253 log_verbose("Checking progress every %u seconds", 254 parms.interval); 255 256 if (!parms.interval) { 257 parms.progress_display = 0; 258 259 /* FIXME Disabled multiple-copy wait_event */ 260 if (!name) 261 parms.interval = DEFAULT_INTERVAL; 262 } 263 264 if (parms.background) { 265 if (!_become_daemon(cmd)) 266 return ECMD_PROCESSED; /* Parent */ 267 parms.progress_display = 0; 268 /* FIXME Use wait_event (i.e. interval = 0) and */ 269 /* fork one daemon per copy? */ 270 } 271 272 /* 273 * Process one specific task or all incomplete tasks? 274 */ 275 if (name) { 276 if (!_wait_for_single_lv(cmd, name, uuid, &parms)) { 277 stack; 278 return ECMD_FAILED; 279 } 280 } else 281 _poll_for_all_vgs(cmd, &parms); 282 283 return ECMD_PROCESSED; 284 } 285