xref: /dragonfly/contrib/lvm2/dist/tools/polldaemon.c (revision 73610d44)
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