1*7c604eeaShaad /*	$NetBSD: polldaemon.c,v 1.1.1.2 2009/12/02 00:25:53 haad Exp $	*/
256a34939Shaad 
356a34939Shaad /*
456a34939Shaad  * Copyright (C) 2003-2004 Sistina Software, Inc. All rights reserved.
556a34939Shaad  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
656a34939Shaad  *
756a34939Shaad  * This file is part of LVM2.
856a34939Shaad  *
956a34939Shaad  * This copyrighted material is made available to anyone wishing to use,
1056a34939Shaad  * modify, copy, or redistribute it subject to the terms and conditions
1156a34939Shaad  * of the GNU Lesser General Public License v.2.1.
1256a34939Shaad  *
1356a34939Shaad  * You should have received a copy of the GNU Lesser General Public License
1456a34939Shaad  * along with this program; if not, write to the Free Software Foundation,
1556a34939Shaad  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1656a34939Shaad  */
1756a34939Shaad 
1856a34939Shaad #include "tools.h"
1956a34939Shaad #include "polldaemon.h"
2056a34939Shaad #include <signal.h>
2156a34939Shaad #include <sys/wait.h>
2256a34939Shaad 
_sigchld_handler(int sig __attribute ((unused)))2356a34939Shaad static void _sigchld_handler(int sig __attribute((unused)))
2456a34939Shaad {
2556a34939Shaad 	while (wait4(-1, NULL, WNOHANG | WUNTRACED, NULL) > 0) ;
2656a34939Shaad }
2756a34939Shaad 
_become_daemon(struct cmd_context * cmd)2856a34939Shaad static int _become_daemon(struct cmd_context *cmd)
2956a34939Shaad {
3056a34939Shaad 	pid_t pid;
3156a34939Shaad 	struct sigaction act = {
3256a34939Shaad 		{_sigchld_handler},
3356a34939Shaad 		.sa_flags = SA_NOCLDSTOP,
3456a34939Shaad 	};
3556a34939Shaad 
3656a34939Shaad 	log_verbose("Forking background process");
3756a34939Shaad 
3856a34939Shaad 	sigaction(SIGCHLD, &act, NULL);
3956a34939Shaad 
4056a34939Shaad 	if ((pid = fork()) == -1) {
4156a34939Shaad 		log_error("fork failed: %s", strerror(errno));
4256a34939Shaad 		return 1;
4356a34939Shaad 	}
4456a34939Shaad 
4556a34939Shaad 	/* Parent */
4656a34939Shaad 	if (pid > 0)
4756a34939Shaad 		return 0;
4856a34939Shaad 
4956a34939Shaad 	/* Child */
5056a34939Shaad 	if (setsid() == -1)
5156a34939Shaad 		log_error("Background process failed to setsid: %s",
5256a34939Shaad 			  strerror(errno));
5356a34939Shaad 	init_verbose(VERBOSE_BASE_LEVEL);
5456a34939Shaad 
5556a34939Shaad 	close(STDIN_FILENO);
5656a34939Shaad 	close(STDOUT_FILENO);
5756a34939Shaad 	close(STDERR_FILENO);
5856a34939Shaad 
5956a34939Shaad 	strncpy(*cmd->argv, "(lvm2copyd)", strlen(*cmd->argv));
6056a34939Shaad 
6156a34939Shaad 	reset_locking();
62*7c604eeaShaad 	lvmcache_init();
6356a34939Shaad 	dev_close_all();
6456a34939Shaad 
6556a34939Shaad 	return 1;
6656a34939Shaad }
6756a34939Shaad 
poll_mirror_progress(struct cmd_context * cmd,struct logical_volume * lv,const char * name,struct daemon_parms * parms)68*7c604eeaShaad progress_t poll_mirror_progress(struct cmd_context *cmd,
69*7c604eeaShaad 				struct logical_volume *lv, const char *name,
70*7c604eeaShaad 				struct daemon_parms *parms)
7156a34939Shaad {
7256a34939Shaad 	float segment_percent = 0.0, overall_percent = 0.0;
73*7c604eeaShaad 	percent_range_t percent_range, overall_percent_range;
7456a34939Shaad 	uint32_t event_nr = 0;
7556a34939Shaad 
76*7c604eeaShaad 	if (!lv_mirror_percent(cmd, lv, !parms->interval, &segment_percent,
77*7c604eeaShaad 			       &percent_range, &event_nr) ||
78*7c604eeaShaad 	    (percent_range == PERCENT_INVALID)) {
7956a34939Shaad 		log_error("ABORTING: Mirror percentage check failed.");
80*7c604eeaShaad 		return PROGRESS_CHECK_FAILED;
8156a34939Shaad 	}
8256a34939Shaad 
83*7c604eeaShaad 	overall_percent = copy_percent(lv, &overall_percent_range);
8456a34939Shaad 	if (parms->progress_display)
8556a34939Shaad 		log_print("%s: %s: %.1f%%", name, parms->progress_title,
8656a34939Shaad 			  overall_percent);
8756a34939Shaad 	else
8856a34939Shaad 		log_verbose("%s: %s: %.1f%%", name, parms->progress_title,
8956a34939Shaad 			    overall_percent);
9056a34939Shaad 
91*7c604eeaShaad 	if (percent_range != PERCENT_100)
92*7c604eeaShaad 		return PROGRESS_UNFINISHED;
93*7c604eeaShaad 
94*7c604eeaShaad 	if (overall_percent_range == PERCENT_100)
95*7c604eeaShaad 		return PROGRESS_FINISHED_ALL;
96*7c604eeaShaad 
97*7c604eeaShaad 	return PROGRESS_FINISHED_SEGMENT;
98*7c604eeaShaad }
99*7c604eeaShaad 
_check_lv_status(struct cmd_context * cmd,struct volume_group * vg,struct logical_volume * lv,const char * name,struct daemon_parms * parms,int * finished)100*7c604eeaShaad static int _check_lv_status(struct cmd_context *cmd,
101*7c604eeaShaad 			    struct volume_group *vg,
102*7c604eeaShaad 			    struct logical_volume *lv,
103*7c604eeaShaad 			    const char *name, struct daemon_parms *parms,
104*7c604eeaShaad 			    int *finished)
105*7c604eeaShaad {
106*7c604eeaShaad 	struct dm_list *lvs_changed;
107*7c604eeaShaad 	progress_t progress;
108*7c604eeaShaad 
109*7c604eeaShaad 	/* By default, caller should not retry */
110*7c604eeaShaad 	*finished = 1;
111*7c604eeaShaad 
112*7c604eeaShaad 	if (parms->aborting) {
113*7c604eeaShaad 		if (!(lvs_changed = lvs_using_lv(cmd, vg, lv))) {
114*7c604eeaShaad 			log_error("Failed to generate list of copied LVs: "
115*7c604eeaShaad 				  "can't abort.");
116*7c604eeaShaad 			return 0;
117*7c604eeaShaad 		}
118*7c604eeaShaad 		parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed);
119*7c604eeaShaad 		return 0;
120*7c604eeaShaad 	}
121*7c604eeaShaad 
122*7c604eeaShaad 	progress = parms->poll_fns->poll_progress(cmd, lv, name, parms);
123*7c604eeaShaad 	if (progress == PROGRESS_CHECK_FAILED)
124*7c604eeaShaad 		return_0;
125*7c604eeaShaad 
126*7c604eeaShaad 	if (progress == PROGRESS_UNFINISHED) {
12756a34939Shaad 		/* The only case the caller *should* try again later */
12856a34939Shaad 		*finished = 0;
12956a34939Shaad 		return 1;
13056a34939Shaad 	}
13156a34939Shaad 
132*7c604eeaShaad 	if (!(lvs_changed = lvs_using_lv(cmd, vg, lv))) {
13356a34939Shaad 		log_error("ABORTING: Failed to generate list of copied LVs");
13456a34939Shaad 		return 0;
13556a34939Shaad 	}
13656a34939Shaad 
13756a34939Shaad 	/* Finished? Or progress to next segment? */
138*7c604eeaShaad 	if (progress == PROGRESS_FINISHED_ALL) {
139*7c604eeaShaad 		if (!parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed))
14056a34939Shaad 			return 0;
14156a34939Shaad 	} else {
142*7c604eeaShaad 		if (!parms->poll_fns->update_metadata(cmd, vg, lv, lvs_changed,
143*7c604eeaShaad 						      0)) {
14456a34939Shaad 			log_error("ABORTING: Segment progression failed.");
145*7c604eeaShaad 			parms->poll_fns->finish_copy(cmd, vg, lv, lvs_changed);
14656a34939Shaad 			return 0;
14756a34939Shaad 		}
14856a34939Shaad 		*finished = 0;	/* Another segment */
14956a34939Shaad 	}
15056a34939Shaad 
15156a34939Shaad 	return 1;
15256a34939Shaad }
15356a34939Shaad 
_wait_for_single_lv(struct cmd_context * cmd,const char * name,const char * uuid,struct daemon_parms * parms)154*7c604eeaShaad static int _wait_for_single_lv(struct cmd_context *cmd, const char *name, const char *uuid,
15556a34939Shaad 				   struct daemon_parms *parms)
15656a34939Shaad {
15756a34939Shaad 	struct volume_group *vg;
158*7c604eeaShaad 	struct logical_volume *lv;
15956a34939Shaad 	int finished = 0;
16056a34939Shaad 
161*7c604eeaShaad 	/* Poll for completion */
16256a34939Shaad 	while (!finished) {
16356a34939Shaad 		/* FIXME Also needed in vg/lvchange -ay? */
16456a34939Shaad 		/* FIXME Use alarm for regular intervals instead */
16556a34939Shaad 		if (parms->interval && !parms->aborting) {
16656a34939Shaad 			sleep(parms->interval);
16756a34939Shaad 			/* Devices might have changed while we slept */
16856a34939Shaad 			init_full_scan_done(0);
16956a34939Shaad 		}
17056a34939Shaad 
17156a34939Shaad 		/* Locks the (possibly renamed) VG again */
172*7c604eeaShaad 		vg = parms->poll_fns->get_copy_vg(cmd, name, uuid);
173*7c604eeaShaad 		if (vg_read_error(vg)) {
174*7c604eeaShaad 			vg_release(vg);
17556a34939Shaad 			log_error("ABORTING: Can't reread VG for %s", name);
17656a34939Shaad 			/* What more could we do here? */
17756a34939Shaad 			return 0;
17856a34939Shaad 		}
17956a34939Shaad 
180*7c604eeaShaad 		if (!(lv = parms->poll_fns->get_copy_lv(cmd, vg, name, uuid,
18156a34939Shaad 							parms->lv_type))) {
18256a34939Shaad 			log_error("ABORTING: Can't find mirror LV in %s for %s",
18356a34939Shaad 				  vg->name, name);
184*7c604eeaShaad 			unlock_and_release_vg(cmd, vg, vg->name);
18556a34939Shaad 			return 0;
18656a34939Shaad 		}
18756a34939Shaad 
188*7c604eeaShaad 		if (!_check_lv_status(cmd, vg, lv, name, parms, &finished)) {
189*7c604eeaShaad 			unlock_and_release_vg(cmd, vg, vg->name);
19056a34939Shaad 			return 0;
19156a34939Shaad 		}
19256a34939Shaad 
193*7c604eeaShaad 		unlock_and_release_vg(cmd, vg, vg->name);
19456a34939Shaad 	}
19556a34939Shaad 
19656a34939Shaad 	return 1;
19756a34939Shaad }
19856a34939Shaad 
_poll_vg(struct cmd_context * cmd,const char * vgname,struct volume_group * vg,void * handle)19956a34939Shaad static int _poll_vg(struct cmd_context *cmd, const char *vgname,
200*7c604eeaShaad 		    struct volume_group *vg, void *handle)
20156a34939Shaad {
20256a34939Shaad 	struct daemon_parms *parms = (struct daemon_parms *) handle;
20356a34939Shaad 	struct lv_list *lvl;
204*7c604eeaShaad 	struct logical_volume *lv;
20556a34939Shaad 	const char *name;
20656a34939Shaad 	int finished;
20756a34939Shaad 
20856a34939Shaad 	dm_list_iterate_items(lvl, &vg->lvs) {
209*7c604eeaShaad 		lv = lvl->lv;
210*7c604eeaShaad 		if (!(lv->status & parms->lv_type))
21156a34939Shaad 			continue;
212*7c604eeaShaad 		if (!(name = parms->poll_fns->get_copy_name_from_lv(lv)))
21356a34939Shaad 			continue;
21456a34939Shaad 		/* FIXME Need to do the activation from _set_up_pvmove here
21556a34939Shaad 		 *       if it's not running and we're not aborting */
216*7c604eeaShaad 		if (_check_lv_status(cmd, vg, lv, name, parms, &finished) &&
217*7c604eeaShaad 		    !finished)
21856a34939Shaad 			parms->outstanding_count++;
21956a34939Shaad 	}
22056a34939Shaad 
22156a34939Shaad 	return ECMD_PROCESSED;
22256a34939Shaad 
22356a34939Shaad }
22456a34939Shaad 
_poll_for_all_vgs(struct cmd_context * cmd,struct daemon_parms * parms)22556a34939Shaad static void _poll_for_all_vgs(struct cmd_context *cmd,
22656a34939Shaad 			      struct daemon_parms *parms)
22756a34939Shaad {
22856a34939Shaad 	while (1) {
22956a34939Shaad 		parms->outstanding_count = 0;
230*7c604eeaShaad 		process_each_vg(cmd, 0, NULL, READ_FOR_UPDATE, parms, _poll_vg);
23156a34939Shaad 		if (!parms->outstanding_count)
23256a34939Shaad 			break;
23356a34939Shaad 		sleep(parms->interval);
23456a34939Shaad 	}
23556a34939Shaad }
23656a34939Shaad 
poll_daemon(struct cmd_context * cmd,const char * name,const char * uuid,unsigned background,uint32_t lv_type,struct poll_functions * poll_fns,const char * progress_title)237*7c604eeaShaad int poll_daemon(struct cmd_context *cmd, const char *name, const char *uuid,
238*7c604eeaShaad 		unsigned background,
23956a34939Shaad 		uint32_t lv_type, struct poll_functions *poll_fns,
24056a34939Shaad 		const char *progress_title)
24156a34939Shaad {
24256a34939Shaad 	struct daemon_parms parms;
24356a34939Shaad 
244*7c604eeaShaad 	parms.aborting = arg_is_set(cmd, abort_ARG);
24556a34939Shaad 	parms.background = background;
24656a34939Shaad 	parms.interval = arg_uint_value(cmd, interval_ARG, DEFAULT_INTERVAL);
24756a34939Shaad 	parms.progress_display = 1;
24856a34939Shaad 	parms.progress_title = progress_title;
24956a34939Shaad 	parms.lv_type = lv_type;
25056a34939Shaad 	parms.poll_fns = poll_fns;
25156a34939Shaad 
25256a34939Shaad 	if (parms.interval && !parms.aborting)
25356a34939Shaad 		log_verbose("Checking progress every %u seconds",
25456a34939Shaad 			    parms.interval);
25556a34939Shaad 
25656a34939Shaad 	if (!parms.interval) {
25756a34939Shaad 		parms.progress_display = 0;
25856a34939Shaad 
25956a34939Shaad 		/* FIXME Disabled multiple-copy wait_event */
26056a34939Shaad 		if (!name)
26156a34939Shaad 			parms.interval = DEFAULT_INTERVAL;
26256a34939Shaad 	}
26356a34939Shaad 
26456a34939Shaad 	if (parms.background) {
26556a34939Shaad 		if (!_become_daemon(cmd))
26656a34939Shaad 			return ECMD_PROCESSED;	/* Parent */
26756a34939Shaad 		parms.progress_display = 0;
26856a34939Shaad 		/* FIXME Use wait_event (i.e. interval = 0) and */
26956a34939Shaad 		/*       fork one daemon per copy? */
27056a34939Shaad 	}
27156a34939Shaad 
272*7c604eeaShaad 	/*
273*7c604eeaShaad 	 * Process one specific task or all incomplete tasks?
274*7c604eeaShaad 	 */
27556a34939Shaad 	if (name) {
276*7c604eeaShaad 		if (!_wait_for_single_lv(cmd, name, uuid, &parms)) {
277*7c604eeaShaad 			stack;
27856a34939Shaad 			return ECMD_FAILED;
279*7c604eeaShaad 		}
28056a34939Shaad 	} else
28156a34939Shaad 		_poll_for_all_vgs(cmd, &parms);
28256a34939Shaad 
28356a34939Shaad 	return ECMD_PROCESSED;
28456a34939Shaad }
285