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
)
35 static const char devnull
[] = "/dev/null";
38 struct sigaction act
= {
40 .sa_flags
= SA_NOCLDSTOP
,
43 log_verbose("Forking background process");
45 sigaction(SIGCHLD
, &act
, NULL
);
47 sync_local_dev_names(cmd
); /* Flush ops and reset dm cookie */
49 if ((pid
= fork()) == -1) {
50 log_error("fork failed: %s", strerror(errno
));
60 log_error("Background process failed to setsid: %s",
63 /* For poll debugging it's best to disable for compilation */
65 if ((null_fd
= open(devnull
, O_RDWR
)) == -1) {
66 log_sys_error("open", devnull
);
70 if ((dup2(null_fd
, STDIN_FILENO
) < 0) || /* reopen stdin */
71 (dup2(null_fd
, STDOUT_FILENO
) < 0) || /* reopen stdout */
72 (dup2(null_fd
, STDERR_FILENO
) < 0)) { /* reopen stderr */
73 log_sys_error("dup2", "redirect");
74 (void) close(null_fd
);
78 if (null_fd
> STDERR_FILENO
)
79 (void) close(null_fd
);
81 init_verbose(VERBOSE_BASE_LEVEL
);
83 strncpy(*cmd
->argv
, "(lvm2)", strlen(*cmd
->argv
));
87 /* FIXME Clean up properly here */
94 progress_t
poll_mirror_progress(struct cmd_context
*cmd
,
95 struct logical_volume
*lv
, const char *name
,
96 struct daemon_parms
*parms
)
98 percent_t segment_percent
= PERCENT_0
, overall_percent
= PERCENT_0
;
99 uint32_t event_nr
= 0;
101 if (!lv_is_mirrored(lv
) ||
102 !lv_mirror_percent(cmd
, lv
, !parms
->interval
, &segment_percent
,
104 (segment_percent
== PERCENT_INVALID
)) {
105 log_error("ABORTING: Mirror percentage check failed.");
106 return PROGRESS_CHECK_FAILED
;
109 overall_percent
= copy_percent(lv
);
110 if (parms
->progress_display
)
111 log_print("%s: %s: %.1f%%", name
, parms
->progress_title
,
112 percent_to_float(overall_percent
));
114 log_verbose("%s: %s: %.1f%%", name
, parms
->progress_title
,
115 percent_to_float(overall_percent
));
117 if (segment_percent
!= PERCENT_100
)
118 return PROGRESS_UNFINISHED
;
120 if (overall_percent
== PERCENT_100
)
121 return PROGRESS_FINISHED_ALL
;
123 return PROGRESS_FINISHED_SEGMENT
;
126 static int _check_lv_status(struct cmd_context
*cmd
,
127 struct volume_group
*vg
,
128 struct logical_volume
*lv
,
129 const char *name
, struct daemon_parms
*parms
,
132 struct dm_list
*lvs_changed
;
135 /* By default, caller should not retry */
138 if (parms
->aborting
) {
139 if (!(lvs_changed
= lvs_using_lv(cmd
, vg
, lv
))) {
140 log_error("Failed to generate list of copied LVs: "
144 if (!parms
->poll_fns
->finish_copy(cmd
, vg
, lv
, lvs_changed
))
150 progress
= parms
->poll_fns
->poll_progress(cmd
, lv
, name
, parms
);
151 if (progress
== PROGRESS_CHECK_FAILED
)
154 if (progress
== PROGRESS_UNFINISHED
) {
155 /* The only case the caller *should* try again later */
160 if (!(lvs_changed
= lvs_using_lv(cmd
, vg
, lv
))) {
161 log_error("ABORTING: Failed to generate list of copied LVs");
165 /* Finished? Or progress to next segment? */
166 if (progress
== PROGRESS_FINISHED_ALL
) {
167 if (!parms
->poll_fns
->finish_copy(cmd
, vg
, lv
, lvs_changed
))
170 if (parms
->poll_fns
->update_metadata
&&
171 !parms
->poll_fns
->update_metadata(cmd
, vg
, lv
, lvs_changed
, 0)) {
172 log_error("ABORTING: Segment progression failed.");
173 parms
->poll_fns
->finish_copy(cmd
, vg
, lv
, lvs_changed
);
176 *finished
= 0; /* Another segment */
182 static void _sleep_and_rescan_devices(struct daemon_parms
*parms
)
184 /* FIXME Use alarm for regular intervals instead */
185 if (parms
->interval
&& !parms
->aborting
) {
186 sleep(parms
->interval
);
187 /* Devices might have changed while we slept */
188 init_full_scan_done(0);
192 static int _wait_for_single_lv(struct cmd_context
*cmd
, const char *name
, const char *uuid
,
193 struct daemon_parms
*parms
)
195 struct volume_group
*vg
;
196 struct logical_volume
*lv
;
199 /* Poll for completion */
201 if (parms
->wait_before_testing
)
202 _sleep_and_rescan_devices(parms
);
204 /* Locks the (possibly renamed) VG again */
205 vg
= parms
->poll_fns
->get_copy_vg(cmd
, name
, uuid
);
206 if (vg_read_error(vg
)) {
208 log_error("ABORTING: Can't reread VG for %s", name
);
209 /* What more could we do here? */
213 lv
= parms
->poll_fns
->get_copy_lv(cmd
, vg
, name
, uuid
, parms
->lv_type
);
215 if (!lv
&& parms
->lv_type
== PVMOVE
) {
216 log_print("%s: no pvmove in progress - already finished or aborted.",
218 unlock_and_release_vg(cmd
, vg
, vg
->name
);
223 log_error("ABORTING: Can't find LV in %s for %s",
225 unlock_and_release_vg(cmd
, vg
, vg
->name
);
229 if (!_check_lv_status(cmd
, vg
, lv
, name
, parms
, &finished
)) {
230 unlock_and_release_vg(cmd
, vg
, vg
->name
);
234 unlock_and_release_vg(cmd
, vg
, vg
->name
);
237 * FIXME Sleeping after testing, while preferred, also works around
238 * unreliable "finished" state checking in _percent_run. If the
239 * above _check_lv_status is deferred until after the first sleep it
240 * may be that a polldaemon will run without ever completing.
242 * This happens when one snapshot-merge polldaemon is racing with
243 * another (polling the same LV). The first to see the LV status
244 * reach the "finished" state will alter the LV that the other
245 * polldaemon(s) are polling. These other polldaemon(s) can then
246 * continue polling an LV that doesn't have a "status".
248 if (!parms
->wait_before_testing
)
249 _sleep_and_rescan_devices(parms
);
255 static int _poll_vg(struct cmd_context
*cmd
, const char *vgname
,
256 struct volume_group
*vg
, void *handle
)
258 struct daemon_parms
*parms
= (struct daemon_parms
*) handle
;
260 struct logical_volume
*lv
;
264 dm_list_iterate_items(lvl
, &vg
->lvs
) {
266 if (!(lv
->status
& parms
->lv_type
))
268 name
= parms
->poll_fns
->get_copy_name_from_lv(lv
);
269 if (!name
&& !parms
->aborting
)
272 /* FIXME Need to do the activation from _set_up_pvmove here
273 * if it's not running and we're not aborting */
274 if (_check_lv_status(cmd
, vg
, lv
, name
, parms
, &finished
) &&
276 parms
->outstanding_count
++;
279 return ECMD_PROCESSED
;
283 static void _poll_for_all_vgs(struct cmd_context
*cmd
,
284 struct daemon_parms
*parms
)
287 parms
->outstanding_count
= 0;
288 process_each_vg(cmd
, 0, NULL
, READ_FOR_UPDATE
, parms
, _poll_vg
);
289 if (!parms
->outstanding_count
)
291 sleep(parms
->interval
);
296 * Only allow *one* return from poll_daemon() (the parent).
297 * If there is a child it must exit (ignoring the memory leak messages).
298 * - 'background' is advisory so a child polldaemon may not be used even
299 * if it was requested.
301 int poll_daemon(struct cmd_context
*cmd
, const char *name
, const char *uuid
,
303 uint64_t lv_type
, struct poll_functions
*poll_fns
,
304 const char *progress_title
)
306 struct daemon_parms parms
;
308 int ret
= ECMD_PROCESSED
;
309 sign_t interval_sign
;
311 parms
.aborting
= arg_is_set(cmd
, abort_ARG
);
312 parms
.background
= background
;
313 interval_sign
= arg_sign_value(cmd
, interval_ARG
, SIGN_NONE
);
314 if (interval_sign
== SIGN_MINUS
)
315 log_error("Argument to --interval cannot be negative");
316 parms
.interval
= arg_uint_value(cmd
, interval_ARG
,
317 find_config_tree_int(cmd
, "activation/polling_interval",
319 parms
.wait_before_testing
= (interval_sign
== SIGN_PLUS
);
320 parms
.progress_display
= 1;
321 parms
.progress_title
= progress_title
;
322 parms
.lv_type
= lv_type
;
323 parms
.poll_fns
= poll_fns
;
325 if (parms
.interval
&& !parms
.aborting
)
326 log_verbose("Checking progress %s waiting every %u seconds",
327 (parms
.wait_before_testing
? "after" : "before"),
330 if (!parms
.interval
) {
331 parms
.progress_display
= 0;
333 /* FIXME Disabled multiple-copy wait_event */
335 parms
.interval
= find_config_tree_int(cmd
, "activation/polling_interval",
339 if (parms
.background
) {
340 daemon_mode
= _become_daemon(cmd
);
341 if (daemon_mode
== 0)
342 return ECMD_PROCESSED
; /* Parent */
343 else if (daemon_mode
== 1)
344 parms
.progress_display
= 0; /* Child */
345 /* FIXME Use wait_event (i.e. interval = 0) and */
346 /* fork one daemon per copy? */
350 * Process one specific task or all incomplete tasks?
353 if (!_wait_for_single_lv(cmd
, name
, uuid
, &parms
)) {
358 _poll_for_all_vgs(cmd
, &parms
);
360 if (parms
.background
&& daemon_mode
== 1) {
362 * child was successfully forked:
363 * background polldaemon must not return to the caller
364 * because it will redundantly continue performing the
365 * caller's task (that the parent already performed)
367 /* FIXME Attempt proper cleanup */
368 _exit(lvm_return_code(ret
));