2 * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
3 * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
5 * This file is part of LVM2.
7 * This copyrighted material is made available to anyone wishing to use,
8 * modify, copy, or redistribute it subject to the terms and conditions
9 * of the GNU Lesser General Public License v.2.1.
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, write to the Free Software Foundation,
13 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 #include "polldaemon.h"
18 #include "lvm2cmdline.h"
22 static void _sigchld_handler(int sig __attribute__((unused)))
24 while (wait4(-1, NULL, WNOHANG | WUNTRACED, NULL) > 0) ;
29 * -1 if the fork failed
33 static int _become_daemon(struct cmd_context *cmd)
36 struct sigaction act = {
38 .sa_flags = SA_NOCLDSTOP,
41 log_verbose("Forking background process");
43 sigaction(SIGCHLD, &act, NULL);
45 if ((pid = fork()) == -1) {
46 log_error("fork failed: %s", strerror(errno));
56 log_error("Background process failed to setsid: %s",
58 init_verbose(VERBOSE_BASE_LEVEL);
64 strncpy(*cmd->argv, "(lvm2)", strlen(*cmd->argv));
73 progress_t poll_mirror_progress(struct cmd_context *cmd,
74 struct logical_volume *lv, const char *name,
75 struct daemon_parms *parms)
77 percent_t segment_percent = PERCENT_0, overall_percent = PERCENT_0;
78 uint32_t event_nr = 0;
80 if (!lv_is_mirrored(lv) ||
81 !lv_mirror_percent(cmd, lv, !parms->interval, &segment_percent,
83 (segment_percent == PERCENT_INVALID)) {
84 log_error("ABORTING: Mirror percentage check failed.");
85 return PROGRESS_CHECK_FAILED;
88 overall_percent = copy_percent(lv);
89 if (parms->progress_display)
90 log_print("%s: %s: %.1f%%", name, parms->progress_title,
91 percent_to_float(overall_percent));
93 log_verbose("%s: %s: %.1f%%", name, parms->progress_title,
94 percent_to_float(overall_percent));
96 if (segment_percent != PERCENT_100)
97 return PROGRESS_UNFINISHED;
99 if (overall_percent == PERCENT_100)
100 return PROGRESS_FINISHED_ALL;
102 return PROGRESS_FINISHED_SEGMENT;
105 static int _check_lv_status(struct cmd_context *cmd,
106 struct volume_group *vg,
107 struct logical_volume *lv,
108 const char *name, struct daemon_parms *parms,
111 struct dm_list *lvs_changed;
114 /* By default, caller should not retry */
117 if (parms->aborting) {
118 if (!(lvs_changed = lvs_using_lv(cmd, vg, lv))) {
119 log_error("Failed to generate list of copied LVs: "
123 if (!parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed))
129 progress = parms->poll_fns->poll_progress(cmd, lv, name, parms);
130 if (progress == PROGRESS_CHECK_FAILED)
133 if (progress == PROGRESS_UNFINISHED) {
134 /* The only case the caller *should* try again later */
139 if (!(lvs_changed = lvs_using_lv(cmd, vg, lv))) {
140 log_error("ABORTING: Failed to generate list of copied LVs");
144 /* Finished? Or progress to next segment? */
145 if (progress == PROGRESS_FINISHED_ALL) {
146 if (!parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed))
149 if (parms->poll_fns->update_metadata &&
150 !parms->poll_fns->update_metadata(cmd, vg, lv, lvs_changed, 0)) {
151 log_error("ABORTING: Segment progression failed.");
152 parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed);
155 *finished = 0; /* Another segment */
161 static void _sleep_and_rescan_devices(struct daemon_parms *parms)
163 /* FIXME Use alarm for regular intervals instead */
164 if (parms->interval && !parms->aborting) {
165 sleep(parms->interval);
166 /* Devices might have changed while we slept */
167 init_full_scan_done(0);
171 static int _wait_for_single_lv(struct cmd_context *cmd, const char *name, const char *uuid,
172 struct daemon_parms *parms)
174 struct volume_group *vg;
175 struct logical_volume *lv;
178 /* Poll for completion */
180 if (parms->wait_before_testing)
181 _sleep_and_rescan_devices(parms);
183 /* Locks the (possibly renamed) VG again */
184 vg = parms->poll_fns->get_copy_vg(cmd, name, uuid);
185 if (vg_read_error(vg)) {
187 log_error("ABORTING: Can't reread VG for %s", name);
188 /* What more could we do here? */
192 if (!(lv = parms->poll_fns->get_copy_lv(cmd, vg, name, uuid,
194 log_error("ABORTING: Can't find LV in %s for %s",
196 unlock_and_free_vg(cmd, vg, vg->name);
200 if (!_check_lv_status(cmd, vg, lv, name, parms, &finished)) {
201 unlock_and_free_vg(cmd, vg, vg->name);
205 unlock_and_free_vg(cmd, vg, vg->name);
208 * FIXME Sleeping after testing, while preferred, also works around
209 * unreliable "finished" state checking in _percent_run. If the
210 * above _check_lv_status is deferred until after the first sleep it
211 * may be that a polldaemon will run without ever completing.
213 * This happens when one snapshot-merge polldaemon is racing with
214 * another (polling the same LV). The first to see the LV status
215 * reach the "finished" state will alter the LV that the other
216 * polldaemon(s) are polling. These other polldaemon(s) can then
217 * continue polling an LV that doesn't have a "status".
219 if (!parms->wait_before_testing)
220 _sleep_and_rescan_devices(parms);
226 static int _poll_vg(struct cmd_context *cmd, const char *vgname,
227 struct volume_group *vg, void *handle)
229 struct daemon_parms *parms = (struct daemon_parms *) handle;
231 struct logical_volume *lv;
235 dm_list_iterate_items(lvl, &vg->lvs) {
237 if (!(lv->status & parms->lv_type))
239 name = parms->poll_fns->get_copy_name_from_lv(lv);
240 if (!name && !parms->aborting)
243 /* FIXME Need to do the activation from _set_up_pvmove here
244 * if it's not running and we're not aborting */
245 if (_check_lv_status(cmd, vg, lv, name, parms, &finished) &&
247 parms->outstanding_count++;
250 return ECMD_PROCESSED;
254 static void _poll_for_all_vgs(struct cmd_context *cmd,
255 struct daemon_parms *parms)
258 parms->outstanding_count = 0;
259 process_each_vg(cmd, 0, NULL, READ_FOR_UPDATE, parms, _poll_vg);
260 if (!parms->outstanding_count)
262 sleep(parms->interval);
267 * Only allow *one* return from poll_daemon() (the parent).
268 * If there is a child it must exit (ignoring the memory leak messages).
269 * - 'background' is advisory so a child polldaemon may not be used even
270 * if it was requested.
272 int poll_daemon(struct cmd_context *cmd, const char *name, const char *uuid,
274 uint32_t lv_type, struct poll_functions *poll_fns,
275 const char *progress_title)
277 struct daemon_parms parms;
279 int ret = ECMD_PROCESSED;
280 sign_t interval_sign;
282 parms.aborting = arg_is_set(cmd, abort_ARG);
283 parms.background = background;
284 interval_sign = arg_sign_value(cmd, interval_ARG, 0);
285 if (interval_sign == SIGN_MINUS)
286 log_error("Argument to --interval cannot be negative");
287 parms.interval = arg_uint_value(cmd, interval_ARG,
288 find_config_tree_int(cmd, "activation/polling_interval",
290 parms.wait_before_testing = (interval_sign == SIGN_PLUS);
291 parms.progress_display = 1;
292 parms.progress_title = progress_title;
293 parms.lv_type = lv_type;
294 parms.poll_fns = poll_fns;
296 if (parms.interval && !parms.aborting)
297 log_verbose("Checking progress %s waiting every %u seconds",
298 (parms.wait_before_testing ? "after" : "before"),
301 if (!parms.interval) {
302 parms.progress_display = 0;
304 /* FIXME Disabled multiple-copy wait_event */
306 parms.interval = find_config_tree_int(cmd, "activation/polling_interval",
310 if (parms.background) {
311 daemon_mode = _become_daemon(cmd);
312 if (daemon_mode == 0)
313 return ECMD_PROCESSED; /* Parent */
314 else if (daemon_mode == 1)
315 parms.progress_display = 0; /* Child */
316 /* FIXME Use wait_event (i.e. interval = 0) and */
317 /* fork one daemon per copy? */
321 * Process one specific task or all incomplete tasks?
324 if (!_wait_for_single_lv(cmd, name, uuid, &parms)) {
329 _poll_for_all_vgs(cmd, &parms);
331 if (parms.background && daemon_mode == 1) {
333 * child was successfully forked:
334 * background polldaemon must not return to the caller
335 * because it will redundantly continue performing the
336 * caller's task (that the parent already performed)
338 /* FIXME Attempt proper cleanup */
339 _exit(lvm_return_code(ret));