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
23static void _sigchld_handler(int sig __attribute((unused)))
24{
25	while (wait4(-1, NULL, WNOHANG | WUNTRACED, NULL) > 0) ;
26}
27
28static 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
68progress_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
100static 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
154static 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
199static 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
225static 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
237int 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