]>
Commit | Line | Data |
---|---|---|
cb919290 | 1 | /* |
67cdbd7e | 2 | * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved. |
be684599 | 3 | * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. |
cb919290 AK |
4 | * |
5 | * This file is part of LVM2. | |
6 | * | |
7 | * This copyrighted material is made available to anyone wishing to use, | |
8 | * modify, copy, or redistribute it subject to the terms and conditions | |
be684599 | 9 | * of the GNU Lesser General Public License v.2.1. |
cb919290 | 10 | * |
be684599 | 11 | * You should have received a copy of the GNU Lesser General Public License |
cb919290 AK |
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 | |
14 | */ | |
15 | ||
16 | #include "tools.h" | |
17 | #include "polldaemon.h" | |
94d4a90f | 18 | #include "lvm2cmdline.h" |
cb919290 AK |
19 | #include <signal.h> |
20 | #include <sys/wait.h> | |
21 | ||
08f1ddea | 22 | static void _sigchld_handler(int sig __attribute__((unused))) |
cb919290 AK |
23 | { |
24 | while (wait4(-1, NULL, WNOHANG | WUNTRACED, NULL) > 0) ; | |
25 | } | |
26 | ||
94d4a90f MS |
27 | /* |
28 | * returns: | |
29 | * -1 if the fork failed | |
30 | * 0 if the parent | |
31 | * 1 if the child | |
32 | */ | |
cb919290 AK |
33 | static int _become_daemon(struct cmd_context *cmd) |
34 | { | |
6f8bd07b ZK |
35 | static const char devnull[] = "/dev/null"; |
36 | int null_fd; | |
cb919290 AK |
37 | pid_t pid; |
38 | struct sigaction act = { | |
39 | {_sigchld_handler}, | |
40 | .sa_flags = SA_NOCLDSTOP, | |
41 | }; | |
42 | ||
43 | log_verbose("Forking background process"); | |
44 | ||
45 | sigaction(SIGCHLD, &act, NULL); | |
46 | ||
a8de2765 | 47 | sync_local_dev_names(cmd); /* Flush ops and reset dm cookie */ |
ad50450a | 48 | |
cb919290 AK |
49 | if ((pid = fork()) == -1) { |
50 | log_error("fork failed: %s", strerror(errno)); | |
94d4a90f | 51 | return -1; |
cb919290 AK |
52 | } |
53 | ||
54 | /* Parent */ | |
55 | if (pid > 0) | |
56 | return 0; | |
57 | ||
58 | /* Child */ | |
59 | if (setsid() == -1) | |
60 | log_error("Background process failed to setsid: %s", | |
61 | strerror(errno)); | |
cb919290 | 62 | |
6f8bd07b ZK |
63 | /* For poll debugging it's best to disable for compilation */ |
64 | #if 1 | |
65 | if ((null_fd = open(devnull, O_RDWR)) == -1) { | |
66 | log_sys_error("open", devnull); | |
67 | _exit(ECMD_FAILED); | |
68 | } | |
69 | ||
6ddce3b6 ZK |
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 */ | |
6f8bd07b ZK |
73 | log_sys_error("dup2", "redirect"); |
74 | (void) close(null_fd); | |
75 | _exit(ECMD_FAILED); | |
76 | } | |
cb919290 | 77 | |
6f8bd07b ZK |
78 | if (null_fd > STDERR_FILENO) |
79 | (void) close(null_fd); | |
80 | ||
81 | init_verbose(VERBOSE_BASE_LEVEL); | |
82 | #endif | |
c9f27b1c | 83 | strncpy(*cmd->argv, "(lvm2)", strlen(*cmd->argv)); |
cb919290 AK |
84 | |
85 | reset_locking(); | |
de0ae6a1 ZK |
86 | if (!lvmcache_init()) |
87 | /* FIXME Clean up properly here */ | |
88 | _exit(ECMD_FAILED); | |
cb919290 AK |
89 | dev_close_all(); |
90 | ||
91 | return 1; | |
92 | } | |
93 | ||
42dd692b AK |
94 | progress_t poll_mirror_progress(struct cmd_context *cmd, |
95 | struct logical_volume *lv, const char *name, | |
96 | struct daemon_parms *parms) | |
97 | { | |
8191fe4f | 98 | percent_t segment_percent = PERCENT_0, overall_percent = PERCENT_0; |
42dd692b AK |
99 | uint32_t event_nr = 0; |
100 | ||
6ec8c522 JEB |
101 | if (!lv_is_mirrored(lv) || |
102 | !lv_mirror_percent(cmd, lv, !parms->interval, &segment_percent, | |
8191fe4f PR |
103 | &event_nr) || |
104 | (segment_percent == PERCENT_INVALID)) { | |
42dd692b AK |
105 | log_error("ABORTING: Mirror percentage check failed."); |
106 | return PROGRESS_CHECK_FAILED; | |
107 | } | |
108 | ||
8191fe4f | 109 | overall_percent = copy_percent(lv); |
42dd692b AK |
110 | if (parms->progress_display) |
111 | log_print("%s: %s: %.1f%%", name, parms->progress_title, | |
8191fe4f | 112 | percent_to_float(overall_percent)); |
42dd692b AK |
113 | else |
114 | log_verbose("%s: %s: %.1f%%", name, parms->progress_title, | |
8191fe4f | 115 | percent_to_float(overall_percent)); |
42dd692b | 116 | |
8191fe4f | 117 | if (segment_percent != PERCENT_100) |
42dd692b AK |
118 | return PROGRESS_UNFINISHED; |
119 | ||
8191fe4f | 120 | if (overall_percent == PERCENT_100) |
42dd692b AK |
121 | return PROGRESS_FINISHED_ALL; |
122 | ||
123 | return PROGRESS_FINISHED_SEGMENT; | |
124 | } | |
125 | ||
724de279 AK |
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, | |
130 | int *finished) | |
cb919290 | 131 | { |
2c44337b | 132 | struct dm_list *lvs_changed; |
42dd692b | 133 | progress_t progress; |
cb919290 AK |
134 | |
135 | /* By default, caller should not retry */ | |
136 | *finished = 1; | |
137 | ||
138 | if (parms->aborting) { | |
724de279 | 139 | if (!(lvs_changed = lvs_using_lv(cmd, vg, lv))) { |
cb919290 AK |
140 | log_error("Failed to generate list of copied LVs: " |
141 | "can't abort."); | |
142 | return 0; | |
143 | } | |
e9848871 MB |
144 | if (!parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed)) |
145 | return_0; | |
146 | ||
147 | return 1; | |
cb919290 AK |
148 | } |
149 | ||
4b12fa13 | 150 | progress = parms->poll_fns->poll_progress(cmd, lv, name, parms); |
42dd692b AK |
151 | if (progress == PROGRESS_CHECK_FAILED) |
152 | return_0; | |
cb919290 | 153 | |
42dd692b | 154 | if (progress == PROGRESS_UNFINISHED) { |
cb919290 AK |
155 | /* The only case the caller *should* try again later */ |
156 | *finished = 0; | |
157 | return 1; | |
158 | } | |
159 | ||
724de279 | 160 | if (!(lvs_changed = lvs_using_lv(cmd, vg, lv))) { |
cb919290 AK |
161 | log_error("ABORTING: Failed to generate list of copied LVs"); |
162 | return 0; | |
163 | } | |
164 | ||
165 | /* Finished? Or progress to next segment? */ | |
42dd692b | 166 | if (progress == PROGRESS_FINISHED_ALL) { |
724de279 | 167 | if (!parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed)) |
45afc155 | 168 | return_0; |
cb919290 | 169 | } else { |
c9f27b1c MS |
170 | if (parms->poll_fns->update_metadata && |
171 | !parms->poll_fns->update_metadata(cmd, vg, lv, lvs_changed, 0)) { | |
cb919290 | 172 | log_error("ABORTING: Segment progression failed."); |
724de279 | 173 | parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed); |
cb919290 AK |
174 | return 0; |
175 | } | |
176 | *finished = 0; /* Another segment */ | |
177 | } | |
178 | ||
179 | return 1; | |
180 | } | |
181 | ||
43d99aab MS |
182 | static void _sleep_and_rescan_devices(struct daemon_parms *parms) |
183 | { | |
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); | |
189 | } | |
190 | } | |
191 | ||
724de279 | 192 | static int _wait_for_single_lv(struct cmd_context *cmd, const char *name, const char *uuid, |
43d99aab | 193 | struct daemon_parms *parms) |
cb919290 AK |
194 | { |
195 | struct volume_group *vg; | |
724de279 | 196 | struct logical_volume *lv; |
cb919290 AK |
197 | int finished = 0; |
198 | ||
724de279 | 199 | /* Poll for completion */ |
cb919290 | 200 | while (!finished) { |
43d99aab MS |
201 | if (parms->wait_before_testing) |
202 | _sleep_and_rescan_devices(parms); | |
cb919290 AK |
203 | |
204 | /* Locks the (possibly renamed) VG again */ | |
b8b3508c DW |
205 | vg = parms->poll_fns->get_copy_vg(cmd, name, uuid); |
206 | if (vg_read_error(vg)) { | |
077a6755 | 207 | release_vg(vg); |
cb919290 AK |
208 | log_error("ABORTING: Can't reread VG for %s", name); |
209 | /* What more could we do here? */ | |
210 | return 0; | |
211 | } | |
212 | ||
74863007 MB |
213 | lv = parms->poll_fns->get_copy_lv(cmd, vg, name, uuid, parms->lv_type); |
214 | ||
215 | if (!lv && parms->lv_type == PVMOVE) { | |
216 | log_print("%s: no pvmove in progress - already finished or aborted.", | |
217 | name); | |
077a6755 | 218 | unlock_and_release_vg(cmd, vg, vg->name); |
74863007 MB |
219 | return 1; |
220 | } | |
221 | ||
222 | if (!lv) { | |
c9f27b1c | 223 | log_error("ABORTING: Can't find LV in %s for %s", |
cb919290 | 224 | vg->name, name); |
077a6755 | 225 | unlock_and_release_vg(cmd, vg, vg->name); |
cb919290 AK |
226 | return 0; |
227 | } | |
228 | ||
724de279 | 229 | if (!_check_lv_status(cmd, vg, lv, name, parms, &finished)) { |
077a6755 | 230 | unlock_and_release_vg(cmd, vg, vg->name); |
45afc155 | 231 | return_0; |
cb919290 AK |
232 | } |
233 | ||
077a6755 | 234 | unlock_and_release_vg(cmd, vg, vg->name); |
43d99aab MS |
235 | |
236 | /* | |
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. | |
241 | * | |
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". | |
247 | */ | |
248 | if (!parms->wait_before_testing) | |
249 | _sleep_and_rescan_devices(parms); | |
cb919290 AK |
250 | } |
251 | ||
252 | return 1; | |
253 | } | |
254 | ||
255 | static int _poll_vg(struct cmd_context *cmd, const char *vgname, | |
13e8c7e4 | 256 | struct volume_group *vg, void *handle) |
cb919290 AK |
257 | { |
258 | struct daemon_parms *parms = (struct daemon_parms *) handle; | |
259 | struct lv_list *lvl; | |
724de279 | 260 | struct logical_volume *lv; |
cb919290 AK |
261 | const char *name; |
262 | int finished; | |
263 | ||
2c44337b | 264 | dm_list_iterate_items(lvl, &vg->lvs) { |
724de279 AK |
265 | lv = lvl->lv; |
266 | if (!(lv->status & parms->lv_type)) | |
cb919290 | 267 | continue; |
cf704d22 MB |
268 | name = parms->poll_fns->get_copy_name_from_lv(lv); |
269 | if (!name && !parms->aborting) | |
cb919290 | 270 | continue; |
cf704d22 | 271 | |
67cdbd7e | 272 | /* FIXME Need to do the activation from _set_up_pvmove here |
cb919290 | 273 | * if it's not running and we're not aborting */ |
724de279 AK |
274 | if (_check_lv_status(cmd, vg, lv, name, parms, &finished) && |
275 | !finished) | |
cb919290 AK |
276 | parms->outstanding_count++; |
277 | } | |
278 | ||
279 | return ECMD_PROCESSED; | |
280 | ||
281 | } | |
282 | ||
283 | static void _poll_for_all_vgs(struct cmd_context *cmd, | |
284 | struct daemon_parms *parms) | |
285 | { | |
286 | while (1) { | |
287 | parms->outstanding_count = 0; | |
13e8c7e4 | 288 | process_each_vg(cmd, 0, NULL, READ_FOR_UPDATE, parms, _poll_vg); |
cb919290 AK |
289 | if (!parms->outstanding_count) |
290 | break; | |
291 | sleep(parms->interval); | |
292 | } | |
293 | } | |
294 | ||
94d4a90f MS |
295 | /* |
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. | |
300 | */ | |
31f55a07 MB |
301 | int poll_daemon(struct cmd_context *cmd, const char *name, const char *uuid, |
302 | unsigned background, | |
2ef5b7cc | 303 | uint64_t lv_type, struct poll_functions *poll_fns, |
ba0c495d | 304 | const char *progress_title) |
cb919290 AK |
305 | { |
306 | struct daemon_parms parms; | |
94d4a90f MS |
307 | int daemon_mode = 0; |
308 | int ret = ECMD_PROCESSED; | |
43d99aab | 309 | sign_t interval_sign; |
cb919290 | 310 | |
a8fb89ad | 311 | parms.aborting = arg_is_set(cmd, abort_ARG); |
cb919290 | 312 | parms.background = background; |
fbf6b89a | 313 | interval_sign = arg_sign_value(cmd, interval_ARG, SIGN_NONE); |
43d99aab MS |
314 | if (interval_sign == SIGN_MINUS) |
315 | log_error("Argument to --interval cannot be negative"); | |
59131572 AK |
316 | parms.interval = arg_uint_value(cmd, interval_ARG, |
317 | find_config_tree_int(cmd, "activation/polling_interval", | |
318 | DEFAULT_INTERVAL)); | |
43d99aab | 319 | parms.wait_before_testing = (interval_sign == SIGN_PLUS); |
cb919290 | 320 | parms.progress_display = 1; |
ba0c495d | 321 | parms.progress_title = progress_title; |
cb919290 AK |
322 | parms.lv_type = lv_type; |
323 | parms.poll_fns = poll_fns; | |
324 | ||
325 | if (parms.interval && !parms.aborting) | |
43d99aab MS |
326 | log_verbose("Checking progress %s waiting every %u seconds", |
327 | (parms.wait_before_testing ? "after" : "before"), | |
cb919290 AK |
328 | parms.interval); |
329 | ||
330 | if (!parms.interval) { | |
331 | parms.progress_display = 0; | |
332 | ||
333 | /* FIXME Disabled multiple-copy wait_event */ | |
334 | if (!name) | |
59131572 AK |
335 | parms.interval = find_config_tree_int(cmd, "activation/polling_interval", |
336 | DEFAULT_INTERVAL); | |
cb919290 AK |
337 | } |
338 | ||
339 | if (parms.background) { | |
94d4a90f MS |
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 */ | |
cb919290 AK |
345 | /* FIXME Use wait_event (i.e. interval = 0) and */ |
346 | /* fork one daemon per copy? */ | |
347 | } | |
348 | ||
724de279 AK |
349 | /* |
350 | * Process one specific task or all incomplete tasks? | |
351 | */ | |
cb919290 | 352 | if (name) { |
724de279 | 353 | if (!_wait_for_single_lv(cmd, name, uuid, &parms)) { |
651ff9b3 | 354 | stack; |
94d4a90f | 355 | ret = ECMD_FAILED; |
651ff9b3 | 356 | } |
cb919290 AK |
357 | } else |
358 | _poll_for_all_vgs(cmd, &parms); | |
359 | ||
94d4a90f MS |
360 | if (parms.background && daemon_mode == 1) { |
361 | /* | |
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) | |
366 | */ | |
367 | /* FIXME Attempt proper cleanup */ | |
368 | _exit(lvm_return_code(ret)); | |
369 | } | |
370 | ||
371 | return ret; | |
cb919290 | 372 | } |