xref: /netbsd-src/external/gpl2/lvm2/dist/tools/polldaemon.c (revision 274254cdae52594c1aa480a736aef78313d15c9c)
1 /*	$NetBSD: polldaemon.c,v 1.1.1.1 2008/12/22 00:19:05 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 	dev_close_all();
63 
64 	return 1;
65 }
66 
67 static int _check_mirror_status(struct cmd_context *cmd,
68 				struct volume_group *vg,
69 				struct logical_volume *lv_mirr,
70 				const char *name, struct daemon_parms *parms,
71 				int *finished)
72 {
73 	struct dm_list *lvs_changed;
74 	float segment_percent = 0.0, overall_percent = 0.0;
75 	uint32_t event_nr = 0;
76 
77 	/* By default, caller should not retry */
78 	*finished = 1;
79 
80 	if (parms->aborting) {
81 		if (!(lvs_changed = lvs_using_lv(cmd, vg, lv_mirr))) {
82 			log_error("Failed to generate list of copied LVs: "
83 				  "can't abort.");
84 			return 0;
85 		}
86 		parms->poll_fns->finish_copy(cmd, vg, lv_mirr, lvs_changed);
87 		return 0;
88 	}
89 
90 	if (!lv_mirror_percent(cmd, lv_mirr, !parms->interval, &segment_percent,
91 			       &event_nr)) {
92 		log_error("ABORTING: Mirror percentage check failed.");
93 		return 0;
94 	}
95 
96 	overall_percent = copy_percent(lv_mirr);
97 	if (parms->progress_display)
98 		log_print("%s: %s: %.1f%%", name, parms->progress_title,
99 			  overall_percent);
100 	else
101 		log_verbose("%s: %s: %.1f%%", name, parms->progress_title,
102 			    overall_percent);
103 
104 	if (segment_percent < 100.0) {
105 		/* The only case the caller *should* try again later */
106 		*finished = 0;
107 		return 1;
108 	}
109 
110 	if (!(lvs_changed = lvs_using_lv(cmd, vg, lv_mirr))) {
111 		log_error("ABORTING: Failed to generate list of copied LVs");
112 		return 0;
113 	}
114 
115 	/* Finished? Or progress to next segment? */
116 	if (overall_percent >= 100.0) {
117 		if (!parms->poll_fns->finish_copy(cmd, vg, lv_mirr,
118 						  lvs_changed))
119 			return 0;
120 	} else {
121 		if (!parms->poll_fns->update_metadata(cmd, vg, lv_mirr,
122 						      lvs_changed, 0)) {
123 			log_error("ABORTING: Segment progression failed.");
124 			parms->poll_fns->finish_copy(cmd, vg, lv_mirr,
125 						     lvs_changed);
126 			return 0;
127 		}
128 		*finished = 0;	/* Another segment */
129 	}
130 
131 	return 1;
132 }
133 
134 static int _wait_for_single_mirror(struct cmd_context *cmd, const char *name,
135 				   struct daemon_parms *parms)
136 {
137 	struct volume_group *vg;
138 	struct logical_volume *lv_mirr;
139 	int finished = 0;
140 
141 	/* Poll for mirror completion */
142 	while (!finished) {
143 		/* FIXME Also needed in vg/lvchange -ay? */
144 		/* FIXME Use alarm for regular intervals instead */
145 		if (parms->interval && !parms->aborting) {
146 			sleep(parms->interval);
147 			/* Devices might have changed while we slept */
148 			init_full_scan_done(0);
149 		}
150 
151 		/* Locks the (possibly renamed) VG again */
152 		if (!(vg = parms->poll_fns->get_copy_vg(cmd, name))) {
153 			log_error("ABORTING: Can't reread VG for %s", name);
154 			/* What more could we do here? */
155 			return 0;
156 		}
157 
158 		if (!(lv_mirr = parms->poll_fns->get_copy_lv(cmd, vg, name,
159 							     parms->lv_type))) {
160 			log_error("ABORTING: Can't find mirror LV in %s for %s",
161 				  vg->name, name);
162 			unlock_vg(cmd, vg->name);
163 			return 0;
164 		}
165 
166 		if (!_check_mirror_status(cmd, vg, lv_mirr, name, parms,
167 					  &finished)) {
168 			unlock_vg(cmd, vg->name);
169 			return 0;
170 		}
171 
172 		unlock_vg(cmd, vg->name);
173 	}
174 
175 	return 1;
176 }
177 
178 static int _poll_vg(struct cmd_context *cmd, const char *vgname,
179 		    struct volume_group *vg, int consistent, void *handle)
180 {
181 	struct daemon_parms *parms = (struct daemon_parms *) handle;
182 	struct lv_list *lvl;
183 	struct logical_volume *lv_mirr;
184 	const char *name;
185 	int finished;
186 
187 	if (!vg) {
188 		log_error("Couldn't read volume group %s", vgname);
189 		return ECMD_FAILED;
190 	}
191 
192 	if (!consistent) {
193 		log_error("Volume Group %s inconsistent - skipping", vgname);
194 		/* FIXME Should we silently recover it here or not? */
195 		return ECMD_FAILED;
196 	}
197 
198 	if (!vg_check_status(vg, EXPORTED_VG))
199 		return ECMD_FAILED;
200 
201 	dm_list_iterate_items(lvl, &vg->lvs) {
202 		lv_mirr = lvl->lv;
203 		if (!(lv_mirr->status & parms->lv_type))
204 			continue;
205 		if (!(name = parms->poll_fns->get_copy_name_from_lv(lv_mirr)))
206 			continue;
207 		/* FIXME Need to do the activation from _set_up_pvmove here
208 		 *       if it's not running and we're not aborting */
209 		if (_check_mirror_status(cmd, vg, lv_mirr, name,
210 					 parms, &finished) && !finished)
211 			parms->outstanding_count++;
212 	}
213 
214 	return ECMD_PROCESSED;
215 
216 }
217 
218 static void _poll_for_all_vgs(struct cmd_context *cmd,
219 			      struct daemon_parms *parms)
220 {
221 	while (1) {
222 		parms->outstanding_count = 0;
223 		process_each_vg(cmd, 0, NULL, LCK_VG_WRITE, 1, parms, _poll_vg);
224 		if (!parms->outstanding_count)
225 			break;
226 		sleep(parms->interval);
227 	}
228 }
229 
230 int poll_daemon(struct cmd_context *cmd, const char *name, unsigned background,
231 		uint32_t lv_type, struct poll_functions *poll_fns,
232 		const char *progress_title)
233 {
234 	struct daemon_parms parms;
235 
236 	parms.aborting = arg_count(cmd, abort_ARG) ? 1 : 0;
237 	parms.background = background;
238 	parms.interval = arg_uint_value(cmd, interval_ARG, DEFAULT_INTERVAL);
239 	parms.progress_display = 1;
240 	parms.progress_title = progress_title;
241 	parms.lv_type = lv_type;
242 	parms.poll_fns = poll_fns;
243 
244 	if (parms.interval && !parms.aborting)
245 		log_verbose("Checking progress every %u seconds",
246 			    parms.interval);
247 
248 	if (!parms.interval) {
249 		parms.progress_display = 0;
250 
251 		/* FIXME Disabled multiple-copy wait_event */
252 		if (!name)
253 			parms.interval = DEFAULT_INTERVAL;
254 	}
255 
256 	if (parms.background) {
257 		if (!_become_daemon(cmd))
258 			return ECMD_PROCESSED;	/* Parent */
259 		parms.progress_display = 0;
260 		/* FIXME Use wait_event (i.e. interval = 0) and */
261 		/*       fork one daemon per copy? */
262 	}
263 
264 	if (name) {
265 		if (!_wait_for_single_mirror(cmd, name, &parms))
266 			return ECMD_FAILED;
267 	} else
268 		_poll_for_all_vgs(cmd, &parms);
269 
270 	return ECMD_PROCESSED;
271 }
272