multipath-tools: Fix more strncpy(X, Y, size - 1) calls
[multipath-tools/.git] / libmultipath / structs_vec.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <unistd.h>
4
5 #include "util.h"
6 #include "checkers.h"
7 #include "vector.h"
8 #include "defaults.h"
9 #include "debug.h"
10 #include "config.h"
11 #include "structs.h"
12 #include "structs_vec.h"
13 #include "sysfs.h"
14 #include "devmapper.h"
15 #include "dmparser.h"
16 #include "propsel.h"
17 #include "discovery.h"
18 #include "prio.h"
19 #include "configure.h"
20 #include "libdevmapper.h"
21 #include "io_err_stat.h"
22 #include "switchgroup.h"
23
24 /*
25  * creates or updates mpp->paths reading mpp->pg
26  */
27 int update_mpp_paths(struct multipath *mpp, vector pathvec)
28 {
29         struct pathgroup * pgp;
30         struct path * pp;
31         int i,j;
32
33         if (!mpp || !mpp->pg)
34                 return 0;
35
36         if (!mpp->paths &&
37             !(mpp->paths = vector_alloc()))
38                 return 1;
39
40         vector_foreach_slot (mpp->pg, pgp, i) {
41                 vector_foreach_slot (pgp->paths, pp, j) {
42                         if (!find_path_by_devt(mpp->paths, pp->dev_t) &&
43                             (find_path_by_devt(pathvec, pp->dev_t)) &&
44                             store_path(mpp->paths, pp))
45                                 return 1;
46                 }
47         }
48         return 0;
49 }
50
51 int adopt_paths(vector pathvec, struct multipath *mpp)
52 {
53         int i, ret;
54         struct path * pp;
55         struct config *conf;
56
57         if (!mpp)
58                 return 0;
59
60         if (update_mpp_paths(mpp, pathvec))
61                 return 1;
62
63         vector_foreach_slot (pathvec, pp, i) {
64                 if (!strncmp(mpp->wwid, pp->wwid, WWID_SIZE)) {
65                         if (pp->size != 0 && mpp->size != 0 &&
66                             pp->size != mpp->size) {
67                                 condlog(3, "%s: size mismatch for %s, not adding path",
68                                         pp->dev, mpp->alias);
69                                 continue;
70                         }
71                         condlog(3, "%s: ownership set to %s",
72                                 pp->dev, mpp->alias);
73                         pp->mpp = mpp;
74
75                         if (!mpp->paths && !(mpp->paths = vector_alloc()))
76                                 return 1;
77
78                         if (!find_path_by_dev(mpp->paths, pp->dev) &&
79                             store_path(mpp->paths, pp))
80                                         return 1;
81                         conf = get_multipath_config();
82                         pthread_cleanup_push(put_multipath_config, conf);
83                         ret = pathinfo(pp, conf,
84                                        DI_PRIO | DI_CHECKER);
85                         pthread_cleanup_pop(1);
86                         if (ret)
87                                 return 1;
88                 }
89         }
90         return 0;
91 }
92
93 void orphan_path(struct path *pp, const char *reason)
94 {
95         condlog(3, "%s: orphan path, %s", pp->dev, reason);
96         pp->mpp = NULL;
97         pp->dmstate = PSTATE_UNDEF;
98         pp->uid_attribute = NULL;
99         pp->getuid = NULL;
100         prio_put(&pp->prio);
101         checker_put(&pp->checker);
102         if (pp->fd >= 0)
103                 close(pp->fd);
104         pp->fd = -1;
105 }
106
107 void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason)
108 {
109         int i;
110         struct path * pp;
111
112         vector_foreach_slot (pathvec, pp, i) {
113                 if (pp->mpp == mpp) {
114                         orphan_path(pp, reason);
115                 }
116         }
117 }
118
119 void
120 remove_map(struct multipath * mpp, struct vectors * vecs, int purge_vec)
121 {
122         int i;
123
124         /*
125          * clear references to this map
126          */
127         orphan_paths(vecs->pathvec, mpp, "map removed internally");
128
129         if (purge_vec &&
130             (i = find_slot(vecs->mpvec, (void *)mpp)) != -1)
131                 vector_del_slot(vecs->mpvec, i);
132
133         /*
134          * final free
135          */
136         free_multipath(mpp, KEEP_PATHS);
137 }
138
139 void
140 remove_map_by_alias(const char *alias, struct vectors * vecs, int purge_vec)
141 {
142         struct multipath * mpp = find_mp_by_alias(vecs->mpvec, alias);
143         if (mpp) {
144                 condlog(2, "%s: removing map by alias", alias);
145                 remove_map(mpp, vecs, purge_vec);
146         }
147 }
148
149 void
150 remove_maps(struct vectors * vecs)
151 {
152         int i;
153         struct multipath * mpp;
154
155         if (!vecs)
156                 return;
157
158         vector_foreach_slot (vecs->mpvec, mpp, i) {
159                 remove_map(mpp, vecs, 1);
160                 i--;
161         }
162
163         vector_free(vecs->mpvec);
164         vecs->mpvec = NULL;
165 }
166
167 void
168 extract_hwe_from_path(struct multipath * mpp)
169 {
170         struct path * pp = NULL;
171         int i;
172
173         if (mpp->hwe || !mpp->paths)
174                 return;
175
176         condlog(3, "%s: searching paths for valid hwe", mpp->alias);
177         /* doing this in two passes seems like paranoia to me */
178         vector_foreach_slot(mpp->paths, pp, i) {
179                 if (pp->state != PATH_UP)
180                         continue;
181                 if (pp->hwe) {
182                         mpp->hwe = pp->hwe;
183                         return;
184                 }
185         }
186         vector_foreach_slot(mpp->paths, pp, i) {
187                 if (pp->state == PATH_UP)
188                         continue;
189                 if (pp->hwe) {
190                         mpp->hwe = pp->hwe;
191                         return;
192                 }
193         }
194 }
195
196 int
197 update_multipath_table (struct multipath *mpp, vector pathvec, int is_daemon)
198 {
199         char params[PARAMS_SIZE] = {0};
200
201         if (!mpp)
202                 return 1;
203
204         if (dm_get_map(mpp->alias, &mpp->size, params)) {
205                 condlog(3, "%s: cannot get map", mpp->alias);
206                 return 1;
207         }
208
209         if (disassemble_map(pathvec, params, mpp, is_daemon)) {
210                 condlog(3, "%s: cannot disassemble map", mpp->alias);
211                 return 1;
212         }
213
214         return 0;
215 }
216
217 int
218 update_multipath_status (struct multipath *mpp)
219 {
220         char status[PARAMS_SIZE] = {0};
221
222         if (!mpp)
223                 return 1;
224
225         if (dm_get_status(mpp->alias, status)) {
226                 condlog(3, "%s: cannot get status", mpp->alias);
227                 return 1;
228         }
229
230         if (disassemble_status(status, mpp)) {
231                 condlog(3, "%s: cannot disassemble status", mpp->alias);
232                 return 1;
233         }
234
235         return 0;
236 }
237
238 void sync_paths(struct multipath *mpp, vector pathvec)
239 {
240         struct path *pp;
241         struct pathgroup  *pgp;
242         int found, i, j;
243
244         vector_foreach_slot (mpp->paths, pp, i) {
245                 found = 0;
246                 vector_foreach_slot(mpp->pg, pgp, j) {
247                         if (find_slot(pgp->paths, (void *)pp) != -1) {
248                                 found = 1;
249                                 break;
250                         }
251                 }
252                 if (!found) {
253                         condlog(3, "%s dropped path %s", mpp->alias, pp->dev);
254                         vector_del_slot(mpp->paths, i--);
255                         orphan_path(pp, "path removed externally");
256                 }
257         }
258         update_mpp_paths(mpp, pathvec);
259         vector_foreach_slot (mpp->paths, pp, i)
260                 pp->mpp = mpp;
261 }
262
263 int
264 update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon)
265 {
266         struct pathgroup *pgp;
267         int i;
268
269         if (!mpp)
270                 return 1;
271
272         update_mpp_paths(mpp, pathvec);
273         condlog(4, "%s: %s", mpp->alias, __FUNCTION__);
274
275         free_multipath_attributes(mpp);
276         free_pgvec(mpp->pg, KEEP_PATHS);
277         mpp->pg = NULL;
278
279         if (update_multipath_table(mpp, pathvec, is_daemon))
280                 return 1;
281         sync_paths(mpp, pathvec);
282
283         if (update_multipath_status(mpp))
284                 return 1;
285
286         vector_foreach_slot(mpp->pg, pgp, i)
287                 if (pgp->paths)
288                         path_group_prio_update(pgp);
289
290         return 0;
291 }
292
293 void enter_recovery_mode(struct multipath *mpp)
294 {
295         int checkint;
296         struct config *conf = get_multipath_config();
297         checkint = conf->checkint;
298         put_multipath_config(conf);
299
300         /*
301          * Enter retry mode.
302          * meaning of +1: retry_tick may be decremented in checkerloop before
303          * starting retry.
304          */
305         mpp->stat_queueing_timeouts++;
306         mpp->retry_tick = mpp->no_path_retry * checkint + 1;
307         condlog(1, "%s: Entering recovery mode: max_retries=%d",
308                 mpp->alias, mpp->no_path_retry);
309 }
310
311 void
312 sync_map_state(struct multipath *mpp)
313 {
314         struct pathgroup *pgp;
315         struct path *pp;
316         unsigned int i, j;
317
318         if (!mpp->pg)
319                 return;
320
321         vector_foreach_slot (mpp->pg, pgp, i){
322                 vector_foreach_slot (pgp->paths, pp, j){
323                         if (pp->state == PATH_UNCHECKED ||
324                             pp->state == PATH_WILD ||
325                             pp->state == PATH_DELAYED)
326                                 continue;
327                         if (mpp->ghost_delay_tick > 0)
328                                 continue;
329                         if ((pp->dmstate == PSTATE_FAILED ||
330                              pp->dmstate == PSTATE_UNDEF) &&
331                             (pp->state == PATH_UP || pp->state == PATH_GHOST))
332                                 dm_reinstate_path(mpp->alias, pp->dev_t);
333                         else if ((pp->dmstate == PSTATE_ACTIVE ||
334                                   pp->dmstate == PSTATE_UNDEF) &&
335                                  (pp->state == PATH_DOWN ||
336                                   pp->state == PATH_SHAKY)) {
337                                 condlog(2, "sync_map_state: failing %s state %d dmstate %d",
338                                         pp->dev, pp->state, pp->dmstate);
339                                 dm_fail_path(mpp->alias, pp->dev_t);
340                         }
341                 }
342         }
343 }
344
345 static void
346 find_existing_alias (struct multipath * mpp,
347                      struct vectors *vecs)
348 {
349         struct multipath * mp;
350         int i;
351
352         vector_foreach_slot (vecs->mpvec, mp, i)
353                 if (strncmp(mp->wwid, mpp->wwid, WWID_SIZE - 1) == 0) {
354                         strlcpy(mpp->alias_old, mp->alias, WWID_SIZE);
355                         return;
356                 }
357 }
358
359 struct multipath *add_map_with_path(struct vectors *vecs, struct path *pp,
360                                     int add_vec)
361 {
362         struct multipath * mpp;
363         struct config *conf = NULL;
364
365         if (!strlen(pp->wwid))
366                 return NULL;
367
368         if (!(mpp = alloc_multipath()))
369                 return NULL;
370
371         conf = get_multipath_config();
372         mpp->mpe = find_mpe(conf->mptable, pp->wwid);
373         mpp->hwe = pp->hwe;
374         put_multipath_config(conf);
375
376         strcpy(mpp->wwid, pp->wwid);
377         find_existing_alias(mpp, vecs);
378         if (select_alias(conf, mpp))
379                 goto out;
380         mpp->size = pp->size;
381
382         if (adopt_paths(vecs->pathvec, mpp))
383                 goto out;
384
385         if (add_vec) {
386                 if (!vector_alloc_slot(vecs->mpvec))
387                         goto out;
388
389                 vector_set_slot(vecs->mpvec, mpp);
390         }
391
392         return mpp;
393
394 out:
395         remove_map(mpp, vecs, PURGE_VEC);
396         return NULL;
397 }
398
399 int verify_paths(struct multipath *mpp, struct vectors *vecs)
400 {
401         struct path * pp;
402         int count = 0;
403         int i, j;
404
405         if (!mpp)
406                 return 0;
407
408         vector_foreach_slot (mpp->paths, pp, i) {
409                 /*
410                  * see if path is in sysfs
411                  */
412                 if (sysfs_attr_get_value(pp->udev, "dev",
413                                          pp->dev_t, BLK_DEV_SIZE) < 0) {
414                         if (pp->state != PATH_DOWN) {
415                                 condlog(1, "%s: removing valid path %s in state %d",
416                                         mpp->alias, pp->dev, pp->state);
417                         } else {
418                                 condlog(3, "%s: failed to access path %s",
419                                         mpp->alias, pp->dev);
420                         }
421                         count++;
422                         vector_del_slot(mpp->paths, i);
423                         i--;
424
425                         /* Make sure mpp->hwe doesn't point to freed memory.
426                          * We call extract_hwe_from_path() below to restore
427                          * mpp->hwe
428                          */
429                         if (mpp->hwe == pp->hwe)
430                                 mpp->hwe = NULL;
431                         if ((j = find_slot(vecs->pathvec,
432                                            (void *)pp)) != -1)
433                                 vector_del_slot(vecs->pathvec, j);
434                         free_path(pp);
435                 } else {
436                         condlog(4, "%s: verified path %s dev_t %s",
437                                 mpp->alias, pp->dev, pp->dev_t);
438                 }
439         }
440         extract_hwe_from_path(mpp);
441         return count;
442 }
443
444 /*
445  * mpp->no_path_retry:
446  *   -2 (QUEUE) : queue_if_no_path enabled, never turned off
447  *   -1 (FAIL)  : fail_if_no_path
448  *    0 (UNDEF) : nothing
449  *   >0         : queue_if_no_path enabled, turned off after polling n times
450  */
451 void update_queue_mode_del_path(struct multipath *mpp)
452 {
453         if (--mpp->nr_active == 0) {
454                 if (mpp->no_path_retry > 0)
455                         enter_recovery_mode(mpp);
456                 else if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE)
457                         mpp->stat_map_failures++;
458         }
459         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
460 }
461
462 void update_queue_mode_add_path(struct multipath *mpp)
463 {
464         if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) {
465                 /* come back to normal mode from retry mode */
466                 mpp->retry_tick = 0;
467                 dm_queue_if_no_path(mpp->alias, 1);
468                 condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
469                 condlog(1, "%s: Recovered to normal mode", mpp->alias);
470         }
471         condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active);
472 }
473
474 vector get_used_hwes(const struct _vector *pathvec)
475 {
476         int i, j;
477         struct path *pp;
478         struct hwentry *hwe;
479         vector v = vector_alloc();
480
481         if (v == NULL)
482                 return NULL;
483
484         vector_foreach_slot(pathvec, pp, i) {
485                 vector_foreach_slot_backwards(pp->hwe, hwe, j) {
486                         vector_find_or_add_slot(v, hwe);
487                 }
488         }
489
490         return v;
491 }