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