Tizen 2.1 base
[external/device-mapper.git] / daemons / dmeventd / plugins / mirror / dmeventd_mirror.c
1 /*
2  * Copyright (C) 2005-2010 Red Hat, Inc. All rights reserved.
3  *
4  * This file is part of LVM2.
5  *
6  * This copyrighted material is made available to anyone wishing to use,
7  * modify, copy, or redistribute it subject to the terms and conditions
8  * of the GNU Lesser General Public License v.2.1.
9  *
10  * You should have received a copy of the GNU Lesser General Public License
11  * along with this program; if not, write to the Free Software Foundation,
12  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
13  */
14
15 #include "lib.h"
16
17 #include "lvm2cmd.h"
18 #include "errors.h"
19 #include "libdevmapper-event.h"
20 #include "dmeventd_lvm.h"
21
22 #include <syslog.h> /* FIXME Replace syslog with multilog */
23 /* FIXME Missing openlog? */
24 /* FIXME Replace most syslogs with log_error() style messages and add complete context. */
25 /* FIXME Reformat to 80 char lines. */
26
27 #define ME_IGNORE    0
28 #define ME_INSYNC    1
29 #define ME_FAILURE   2
30
31 static int _process_status_code(const char status_code, const char *dev_name,
32                                 const char *dev_type, int r)
33 {
34         /*
35          *    A => Alive - No failures
36          *    D => Dead - A write failure occurred leaving mirror out-of-sync
37          *    F => Flush failed.
38          *    S => Sync - A sychronization failure occurred, mirror out-of-sync
39          *    R => Read - A read failure occurred, mirror data unaffected
40          *    U => Unclassified failure (bug)
41          */ 
42         if (status_code == 'F') {
43                 syslog(LOG_ERR, "%s device %s flush failed.",
44                        dev_type, dev_name);
45                 r = ME_FAILURE;
46         } else if (status_code == 'S')
47                 syslog(LOG_ERR, "%s device %s sync failed.",
48                        dev_type, dev_name);
49         else if (status_code == 'R')
50                 syslog(LOG_ERR, "%s device %s read failed.",
51                        dev_type, dev_name);
52         else if (status_code != 'A') {
53                 syslog(LOG_ERR, "%s device %s has failed (%c).",
54                        dev_type, dev_name, status_code);
55                 r = ME_FAILURE;
56         }
57
58         return r;
59 }
60
61 static int _get_mirror_event(char *params)
62 {
63         int i, r = ME_INSYNC;
64         char **args = NULL;
65         char *dev_status_str;
66         char *log_status_str;
67         char *sync_str;
68         char *p = NULL;
69         int log_argc, num_devs;
70
71         /*
72          * dm core parms:            0 409600 mirror
73          * Mirror core parms:        2 253:4 253:5 400/400
74          * New-style failure params: 1 AA
75          * New-style log params:     3 cluster 253:3 A
76          *                       or  3 disk 253:3 A
77          *                       or  1 core
78          */
79
80         /* number of devices */
81         if (!dm_split_words(params, 1, 0, &p))
82                 goto out_parse;
83
84         if (!(num_devs = atoi(p)))
85                 goto out_parse;
86         p += strlen(p) + 1;
87
88         /* devices names + "400/400" + "1 AA" + 1 or 3 log parms + NULL */
89         args = dm_malloc((num_devs + 7) * sizeof(char *));
90         if (!args || dm_split_words(p, num_devs + 7, 0, args) < num_devs + 5)
91                 goto out_parse;
92
93         dev_status_str = args[2 + num_devs];
94         log_argc = atoi(args[3 + num_devs]);
95         log_status_str = args[3 + num_devs + log_argc];
96         sync_str = args[num_devs];
97
98         /* Check for bad mirror devices */
99         for (i = 0; i < num_devs; i++)
100                 r = _process_status_code(dev_status_str[i], args[i],
101                         i ? "Secondary mirror" : "Primary mirror", r);
102
103         /* Check for bad disk log device */
104         if (log_argc > 1)
105                 r = _process_status_code(log_status_str[0],
106                                          args[2 + num_devs + log_argc],
107                                          "Log", r);
108
109         if (r == ME_FAILURE)
110                 goto out;
111
112         p = strstr(sync_str, "/");
113         if (p) {
114                 p[0] = '\0';
115                 if (strcmp(sync_str, p+1))
116                         r = ME_IGNORE;
117                 p[0] = '/';
118         } else
119                 goto out_parse;
120
121 out:
122         dm_free(args);
123         return r;
124         
125 out_parse:
126         dm_free(args);
127         syslog(LOG_ERR, "Unable to parse mirror status string.");
128         return ME_IGNORE;
129 }
130
131 static int _remove_failed_devices(const char *device)
132 {
133         int r;
134 #define CMD_SIZE 256    /* FIXME Use system restriction */
135         char cmd_str[CMD_SIZE];
136         char *vg = NULL, *lv = NULL, *layer = NULL;
137
138         if (strlen(device) > 200)  /* FIXME Use real restriction */
139                 return -ENAMETOOLONG;   /* FIXME These return code distinctions are not used so remove them! */
140
141         if (!dm_split_lvm_name(dmeventd_lvm2_pool(), device, &vg, &lv, &layer)) {
142                 syslog(LOG_ERR, "Unable to determine VG name from %s.",
143                        device);
144                 return -ENOMEM; /* FIXME Replace with generic error return - reason for failure has already got logged */
145         }
146
147         /* strip off the mirror component designations */
148         layer = strstr(lv, "_mlog");
149         if (layer)
150                 *layer = '\0';
151
152         /* FIXME Is any sanity-checking required on %s? */
153         if (CMD_SIZE <= snprintf(cmd_str, CMD_SIZE, "lvconvert --config devices{ignore_suspended_devices=1} --repair --use-policies %s/%s", vg, lv)) {
154                 /* this error should be caught above, but doesn't hurt to check again */
155                 syslog(LOG_ERR, "Unable to form LVM command: Device name too long.");
156                 return -ENAMETOOLONG; /* FIXME Replace with generic error return - reason for failure has already got logged */
157         }
158
159         r = dmeventd_lvm2_run(cmd_str);
160
161         syslog(LOG_INFO, "Repair of mirrored LV %s/%s %s.", vg, lv,
162                (r == ECMD_PROCESSED) ? "finished successfully" : "failed");
163
164         return (r == ECMD_PROCESSED) ? 0 : -1;
165 }
166
167 void process_event(struct dm_task *dmt,
168                    enum dm_event_mask event __attribute__((unused)),
169                    void **unused __attribute__((unused)))
170 {
171         void *next = NULL;
172         uint64_t start, length;
173         char *target_type = NULL;
174         char *params;
175         const char *device = dm_task_get_name(dmt);
176
177         dmeventd_lvm2_lock();
178
179         do {
180                 next = dm_get_next_target(dmt, next, &start, &length,
181                                           &target_type, &params);
182
183                 if (!target_type) {
184                         syslog(LOG_INFO, "%s mapping lost.", device);
185                         continue;
186                 }
187
188                 if (strcmp(target_type, "mirror")) {
189                         syslog(LOG_INFO, "%s has unmirrored portion.", device);
190                         continue;
191                 }
192
193                 switch(_get_mirror_event(params)) {
194                 case ME_INSYNC:
195                         /* FIXME: all we really know is that this
196                            _part_ of the device is in sync
197                            Also, this is not an error
198                         */
199                         syslog(LOG_NOTICE, "%s is now in-sync.", device);
200                         break;
201                 case ME_FAILURE:
202                         syslog(LOG_ERR, "Device failure in %s.", device);
203                         if (_remove_failed_devices(device))
204                                 /* FIXME Why are all the error return codes unused? Get rid of them? */
205                                 syslog(LOG_ERR, "Failed to remove faulty devices in %s.",
206                                        device);
207                         /* Should check before warning user that device is now linear
208                         else
209                                 syslog(LOG_NOTICE, "%s is now a linear device.\n",
210                                         device);
211                         */
212                         break;
213                 case ME_IGNORE:
214                         break;
215                 default:
216                         /* FIXME Provide value then! */
217                         syslog(LOG_INFO, "Unknown event received.");
218                 }
219         } while (next);
220
221         dmeventd_lvm2_unlock();
222 }
223
224 int register_device(const char *device,
225                     const char *uuid __attribute__((unused)),
226                     int major __attribute__((unused)),
227                     int minor __attribute__((unused)),
228                     void **unused __attribute__((unused)))
229 {
230         int r = dmeventd_lvm2_init();
231         syslog(LOG_INFO, "Monitoring mirror device %s for events.", device);
232         return r;
233 }
234
235 int unregister_device(const char *device,
236                       const char *uuid __attribute__((unused)),
237                       int major __attribute__((unused)),
238                       int minor __attribute__((unused)),
239                       void **unused __attribute__((unused)))
240 {
241         syslog(LOG_INFO, "No longer monitoring mirror device %s for events.",
242                device);
243         dmeventd_lvm2_exit();
244         return 1;
245 }