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
_sigchld_handler(int sig __attribute ((unused)))23 static void _sigchld_handler(int sig __attribute((unused)))
24 {
25 while (wait4(-1, NULL, WNOHANG | WUNTRACED, NULL) > 0) ;
26 }
27
_become_daemon(struct cmd_context * cmd)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
poll_mirror_progress(struct cmd_context * cmd,struct logical_volume * lv,const char * name,struct daemon_parms * parms)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
_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 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
_wait_for_single_lv(struct cmd_context * cmd,const char * name,const char * uuid,struct daemon_parms * parms)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
_poll_vg(struct cmd_context * cmd,const char * vgname,struct volume_group * vg,void * handle)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
_poll_for_all_vgs(struct cmd_context * cmd,struct daemon_parms * parms)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
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 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