Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
3 : : * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
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
9 : : * of the GNU Lesser General Public License v.2.1.
10 : : *
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
14 : : */
15 : :
16 : : #include "tools.h"
17 : : #include "polldaemon.h"
18 : : #include "lvm2cmdline.h"
19 : : #include <signal.h>
20 : : #include <sys/wait.h>
21 : :
22 : 0 : static void _sigchld_handler(int sig __attribute((unused)))
23 : : {
24 [ # # ]: 0 : while (wait4(-1, NULL, WNOHANG | WUNTRACED, NULL) > 0) ;
25 : 0 : }
26 : :
27 : : /*
28 : : * returns:
29 : : * -1 if the fork failed
30 : : * 0 if the parent
31 : : * 1 if the child
32 : : */
33 : 0 : static int _become_daemon(struct cmd_context *cmd)
34 : : {
35 : : pid_t pid;
36 : : struct sigaction act = {
37 : : {_sigchld_handler},
38 : : .sa_flags = SA_NOCLDSTOP,
39 : 0 : };
40 : :
41 : 0 : log_verbose("Forking background process");
42 : :
43 : 0 : sigaction(SIGCHLD, &act, NULL);
44 : :
45 [ # # ]: 0 : if ((pid = fork()) == -1) {
46 : 0 : log_error("fork failed: %s", strerror(errno));
47 : 0 : return -1;
48 : : }
49 : :
50 : : /* Parent */
51 [ # # ]: 0 : if (pid > 0)
52 : 0 : return 0;
53 : :
54 : : /* Child */
55 [ # # ]: 0 : if (setsid() == -1)
56 : 0 : log_error("Background process failed to setsid: %s",
57 : : strerror(errno));
58 : 0 : init_verbose(VERBOSE_BASE_LEVEL);
59 : :
60 : 0 : close(STDIN_FILENO);
61 : 0 : close(STDOUT_FILENO);
62 : 0 : close(STDERR_FILENO);
63 : :
64 : 0 : strncpy(*cmd->argv, "(lvm2)", strlen(*cmd->argv));
65 : :
66 : 0 : reset_locking();
67 : 0 : lvmcache_init();
68 : 0 : dev_close_all();
69 : :
70 : 0 : return 1;
71 : : }
72 : :
73 : 0 : progress_t poll_mirror_progress(struct cmd_context *cmd,
74 : : struct logical_volume *lv, const char *name,
75 : : struct daemon_parms *parms)
76 : : {
77 : 0 : float segment_percent = 0.0, overall_percent = 0.0;
78 : : percent_range_t percent_range, overall_percent_range;
79 : 0 : uint32_t event_nr = 0;
80 : :
81 [ # # ][ # # ]: 0 : if (!lv_mirror_percent(cmd, lv, !parms->interval, &segment_percent,
82 : : &percent_range, &event_nr) ||
83 : 0 : (percent_range == PERCENT_INVALID)) {
84 : 0 : log_error("ABORTING: Mirror percentage check failed.");
85 : 0 : return PROGRESS_CHECK_FAILED;
86 : : }
87 : :
88 : 0 : overall_percent = copy_percent(lv, &overall_percent_range);
89 [ # # ]: 0 : if (parms->progress_display)
90 : 0 : log_print("%s: %s: %.1f%%", name, parms->progress_title,
91 : : overall_percent);
92 : : else
93 : 0 : log_verbose("%s: %s: %.1f%%", name, parms->progress_title,
94 : : overall_percent);
95 : :
96 [ # # ]: 0 : if (percent_range != PERCENT_100)
97 : 0 : return PROGRESS_UNFINISHED;
98 : :
99 [ # # ]: 0 : if (overall_percent_range == PERCENT_100)
100 : 0 : return PROGRESS_FINISHED_ALL;
101 : :
102 : 0 : return PROGRESS_FINISHED_SEGMENT;
103 : : }
104 : :
105 : 0 : 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,
109 : : int *finished)
110 : : {
111 : : struct dm_list *lvs_changed;
112 : : progress_t progress;
113 : :
114 : : /* By default, caller should not retry */
115 : 0 : *finished = 1;
116 : :
117 [ # # ]: 0 : if (parms->aborting) {
118 [ # # ]: 0 : if (!(lvs_changed = lvs_using_lv(cmd, vg, lv))) {
119 : 0 : log_error("Failed to generate list of copied LVs: "
120 : : "can't abort.");
121 : 0 : return 0;
122 : : }
123 : 0 : parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed);
124 : 0 : return 0;
125 : : }
126 : :
127 : 0 : progress = parms->poll_fns->poll_progress(cmd, lv, name, parms);
128 [ # # ]: 0 : if (progress == PROGRESS_CHECK_FAILED)
129 : 0 : return_0;
130 : :
131 [ # # ]: 0 : if (progress == PROGRESS_UNFINISHED) {
132 : : /* The only case the caller *should* try again later */
133 : 0 : *finished = 0;
134 : 0 : return 1;
135 : : }
136 : :
137 [ # # ]: 0 : if (!(lvs_changed = lvs_using_lv(cmd, vg, lv))) {
138 : 0 : log_error("ABORTING: Failed to generate list of copied LVs");
139 : 0 : return 0;
140 : : }
141 : :
142 : : /* Finished? Or progress to next segment? */
143 [ # # ]: 0 : if (progress == PROGRESS_FINISHED_ALL) {
144 [ # # ]: 0 : if (!parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed))
145 : 0 : return 0;
146 : : } else {
147 [ # # # # ]: 0 : if (parms->poll_fns->update_metadata &&
148 : 0 : !parms->poll_fns->update_metadata(cmd, vg, lv, lvs_changed, 0)) {
149 : 0 : log_error("ABORTING: Segment progression failed.");
150 : 0 : parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed);
151 : 0 : return 0;
152 : : }
153 : 0 : *finished = 0; /* Another segment */
154 : : }
155 : :
156 : 0 : return 1;
157 : : }
158 : :
159 : 0 : static void _sleep_and_rescan_devices(struct daemon_parms *parms)
160 : : {
161 : : /* FIXME Use alarm for regular intervals instead */
162 [ # # ][ # # ]: 0 : if (parms->interval && !parms->aborting) {
163 : 0 : sleep(parms->interval);
164 : : /* Devices might have changed while we slept */
165 : 0 : init_full_scan_done(0);
166 : : }
167 : 0 : }
168 : :
169 : 0 : static int _wait_for_single_lv(struct cmd_context *cmd, const char *name, const char *uuid,
170 : : struct daemon_parms *parms)
171 : : {
172 : : struct volume_group *vg;
173 : : struct logical_volume *lv;
174 : 0 : int finished = 0;
175 : :
176 : : /* Poll for completion */
177 [ # # ]: 0 : while (!finished) {
178 [ # # ]: 0 : if (parms->wait_before_testing)
179 : 0 : _sleep_and_rescan_devices(parms);
180 : :
181 : : /* Locks the (possibly renamed) VG again */
182 : 0 : vg = parms->poll_fns->get_copy_vg(cmd, name, uuid);
183 [ # # ]: 0 : if (vg_read_error(vg)) {
184 : 0 : vg_release(vg);
185 : 0 : log_error("ABORTING: Can't reread VG for %s", name);
186 : : /* What more could we do here? */
187 : 0 : return 0;
188 : : }
189 : :
190 [ # # ]: 0 : if (!(lv = parms->poll_fns->get_copy_lv(cmd, vg, name, uuid,
191 : : parms->lv_type))) {
192 : 0 : log_error("ABORTING: Can't find LV in %s for %s",
193 : : vg->name, name);
194 : 0 : unlock_and_release_vg(cmd, vg, vg->name);
195 : 0 : return 0;
196 : : }
197 : :
198 [ # # ]: 0 : if (!_check_lv_status(cmd, vg, lv, name, parms, &finished)) {
199 : 0 : unlock_and_release_vg(cmd, vg, vg->name);
200 : 0 : return 0;
201 : : }
202 : :
203 : 0 : unlock_and_release_vg(cmd, vg, vg->name);
204 : :
205 : : /*
206 : : * FIXME Sleeping after testing, while preferred, also works around
207 : : * unreliable "finished" state checking in _percent_run. If the
208 : : * above _check_lv_status is deferred until after the first sleep it
209 : : * may be that a polldaemon will run without ever completing.
210 : : *
211 : : * This happens when one snapshot-merge polldaemon is racing with
212 : : * another (polling the same LV). The first to see the LV status
213 : : * reach the "finished" state will alter the LV that the other
214 : : * polldaemon(s) are polling. These other polldaemon(s) can then
215 : : * continue polling an LV that doesn't have a "status".
216 : : */
217 [ # # ]: 0 : if (!parms->wait_before_testing)
218 : 0 : _sleep_and_rescan_devices(parms);
219 : : }
220 : :
221 : 0 : return 1;
222 : : }
223 : :
224 : 0 : static int _poll_vg(struct cmd_context *cmd, const char *vgname,
225 : : struct volume_group *vg, void *handle)
226 : : {
227 : 0 : struct daemon_parms *parms = (struct daemon_parms *) handle;
228 : : struct lv_list *lvl;
229 : : struct logical_volume *lv;
230 : : const char *name;
231 : : int finished;
232 : :
233 [ # # ]: 0 : dm_list_iterate_items(lvl, &vg->lvs) {
234 : 0 : lv = lvl->lv;
235 [ # # ]: 0 : if (!(lv->status & parms->lv_type))
236 : 0 : continue;
237 [ # # ]: 0 : if (!(name = parms->poll_fns->get_copy_name_from_lv(lv)))
238 : 0 : continue;
239 : : /* FIXME Need to do the activation from _set_up_pvmove here
240 : : * if it's not running and we're not aborting */
241 [ # # ][ # # ]: 0 : if (_check_lv_status(cmd, vg, lv, name, parms, &finished) &&
242 : 0 : !finished)
243 : 0 : parms->outstanding_count++;
244 : : }
245 : :
246 : 0 : return ECMD_PROCESSED;
247 : :
248 : : }
249 : :
250 : 0 : static void _poll_for_all_vgs(struct cmd_context *cmd,
251 : : struct daemon_parms *parms)
252 : : {
253 : : while (1) {
254 : 0 : parms->outstanding_count = 0;
255 : 0 : process_each_vg(cmd, 0, NULL, READ_FOR_UPDATE, parms, _poll_vg);
256 [ # # ]: 0 : if (!parms->outstanding_count)
257 : : break;
258 : 0 : sleep(parms->interval);
259 : 0 : }
260 : 0 : }
261 : :
262 : : /*
263 : : * Only allow *one* return from poll_daemon() (the parent).
264 : : * If there is a child it must exit (ignoring the memory leak messages).
265 : : * - 'background' is advisory so a child polldaemon may not be used even
266 : : * if it was requested.
267 : : */
268 : 0 : int poll_daemon(struct cmd_context *cmd, const char *name, const char *uuid,
269 : : unsigned background,
270 : : uint32_t lv_type, struct poll_functions *poll_fns,
271 : : const char *progress_title)
272 : : {
273 : : struct daemon_parms parms;
274 : 0 : int daemon_mode = 0;
275 : 0 : int ret = ECMD_PROCESSED;
276 : : sign_t interval_sign;
277 : :
278 : 0 : parms.aborting = arg_is_set(cmd, abort_ARG);
279 : 0 : parms.background = background;
280 : 0 : interval_sign = arg_sign_value(cmd, interval_ARG, 0);
281 [ # # ]: 0 : if (interval_sign == SIGN_MINUS)
282 : 0 : log_error("Argument to --interval cannot be negative");
283 : 0 : parms.interval = arg_uint_value(cmd, interval_ARG,
284 : 0 : find_config_tree_int(cmd, "activation/polling_interval",
285 : : DEFAULT_INTERVAL));
286 : 0 : parms.wait_before_testing = (interval_sign == SIGN_PLUS);
287 : 0 : parms.progress_display = 1;
288 : 0 : parms.progress_title = progress_title;
289 : 0 : parms.lv_type = lv_type;
290 : 0 : parms.poll_fns = poll_fns;
291 : :
292 [ # # # # ]: 0 : if (parms.interval && !parms.aborting)
293 [ # # ]: 0 : log_verbose("Checking progress %s waiting every %u seconds",
294 : : (parms.wait_before_testing ? "after" : "before"),
295 : : parms.interval);
296 : :
297 [ # # ]: 0 : if (!parms.interval) {
298 : 0 : parms.progress_display = 0;
299 : :
300 : : /* FIXME Disabled multiple-copy wait_event */
301 [ # # ]: 0 : if (!name)
302 : 0 : parms.interval = find_config_tree_int(cmd, "activation/polling_interval",
303 : : DEFAULT_INTERVAL);
304 : : }
305 : :
306 [ # # ]: 0 : if (parms.background) {
307 : 0 : daemon_mode = _become_daemon(cmd);
308 [ # # ]: 0 : if (daemon_mode == 0)
309 : 0 : return ECMD_PROCESSED; /* Parent */
310 [ # # ]: 0 : else if (daemon_mode == 1)
311 : 0 : parms.progress_display = 0; /* Child */
312 : : /* FIXME Use wait_event (i.e. interval = 0) and */
313 : : /* fork one daemon per copy? */
314 : : }
315 : :
316 : : /*
317 : : * Process one specific task or all incomplete tasks?
318 : : */
319 [ # # ]: 0 : if (name) {
320 [ # # ]: 0 : if (!_wait_for_single_lv(cmd, name, uuid, &parms)) {
321 : 0 : stack;
322 : 0 : ret = ECMD_FAILED;
323 : : }
324 : : } else
325 : 0 : _poll_for_all_vgs(cmd, &parms);
326 : :
327 [ # # ][ # # ]: 0 : if (parms.background && daemon_mode == 1) {
328 : : /*
329 : : * child was successfully forked:
330 : : * background polldaemon must not return to the caller
331 : : * because it will redundantly continue performing the
332 : : * caller's task (that the parent already performed)
333 : : */
334 : : /* FIXME Attempt proper cleanup */
335 : 0 : _exit(lvm_return_code(ret));
336 : : }
337 : :
338 : 0 : return ret;
339 : : }
|