0a9377e5f65ce508a4ba6d07e47162e2eef12afb
[platform/upstream/multipath-tools.git] / multipath / main.c
1 /*
2  * Soft:        multipath device mapper target autoconfig
3  *
4  * Version:     $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $
5  *
6  * Author:      Christophe Varoqui
7  *
8  *              This program is distributed in the hope that it will be useful,
9  *              but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  *              See the GNU General Public License for more details.
12  *
13  *              This program is free software; you can redistribute it and/or
14  *              modify it under the terms of the GNU General Public License
15  *              as published by the Free Software Foundation; either version
16  *              2 of the License, or (at your option) any later version.
17  *
18  * Copyright (c) 2003, 2004, 2005 Christophe Varoqui
19  * Copyright (c) 2005 Benjamin Marzinski, Redhat
20  * Copyright (c) 2005 Kiyoshi Ueda, NEC
21  * Copyright (c) 2005 Patrick Caulfield, Redhat
22  * Copyright (c) 2005 Edward Goggin, EMC
23  */
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <ctype.h>
31 #include <libudev.h>
32 #include <syslog.h>
33 #include <fcntl.h>
34
35 #include "checkers.h"
36 #include "prio.h"
37 #include "vector.h"
38 #include <libdevmapper.h>
39 #include "devmapper.h"
40 #include "util.h"
41 #include "defaults.h"
42 #include "config.h"
43 #include "structs.h"
44 #include "structs_vec.h"
45 #include "dmparser.h"
46 #include "sysfs.h"
47 #include "blacklist.h"
48 #include "discovery.h"
49 #include "debug.h"
50 #include "switchgroup.h"
51 #include "dm-generic.h"
52 #include "print.h"
53 #include "alias.h"
54 #include "configure.h"
55 #include "pgpolicies.h"
56 #include "version.h"
57 #include <errno.h>
58 #include "wwids.h"
59 #include "uxsock.h"
60 #include "mpath_cmd.h"
61 #include "foreign.h"
62 #include "propsel.h"
63 #include "time-util.h"
64 #include "file.h"
65 #include "valid.h"
66 #include "alias.h"
67
68 /*
69  * Return values of configure(), check_path_valid(), and main().
70  */
71 enum {
72         RTVL_OK = 0,
73         RTVL_FAIL = 1,
74         RTVL_RETRY, /* returned by configure(), not by main() */
75 };
76
77 static int
78 dump_config (struct config *conf, vector hwes, vector mpvec)
79 {
80         char * reply = snprint_config(conf, NULL, hwes, mpvec);
81
82         if (reply != NULL) {
83                 printf("%s", reply);
84                 free(reply);
85                 return 0;
86         } else
87                 return 1;
88 }
89
90 void rcu_register_thread_memb(void) {}
91
92 void rcu_unregister_thread_memb(void) {}
93
94 static int
95 filter_pathvec (vector pathvec, const char *refwwid)
96 {
97         int i;
98         struct path * pp;
99
100         if (!refwwid || !strlen(refwwid))
101                 return 0;
102
103         vector_foreach_slot (pathvec, pp, i) {
104                 if (strncmp(pp->wwid, refwwid, WWID_SIZE) != 0) {
105                         condlog(3, "skip path %s : out of scope", pp->dev);
106                         free_path(pp);
107                         vector_del_slot(pathvec, i);
108                         i--;
109                 }
110         }
111         return 0;
112 }
113
114 static void
115 usage (char * progname)
116 {
117         fprintf (stderr, VERSION_STRING);
118         fprintf (stderr, "Usage:\n");
119         fprintf (stderr, "  %s [-v level] [-B|-d|-i|-q|-r] [-b file] [-p policy] [device]\n", progname);
120         fprintf (stderr, "  %s [-v level] [-R retries] -f device\n", progname);
121         fprintf (stderr, "  %s [-v level] [-R retries] -F\n", progname);
122         fprintf (stderr, "  %s [-v level] [-l|-ll] [device]\n", progname);
123         fprintf (stderr, "  %s [-v level] [-a|-w] device\n", progname);
124         fprintf (stderr, "  %s [-v level] -W\n", progname);
125         fprintf (stderr, "  %s [-v level] [-i] [-c|-C] device\n", progname);
126         fprintf (stderr, "  %s [-v level] [-i] [-u|-U]\n", progname);
127         fprintf (stderr, "  %s [-h|-t|-T]\n", progname);
128         fprintf (stderr,
129                 "\n"
130                 "Where:\n"
131                 "  -h      print this usage text\n"
132                 "  -l      show multipath topology (sysfs and DM info)\n"
133                 "  -ll     show multipath topology (maximum info)\n"
134                 "  -e      enable foreign libraries with -l/-ll\n"
135                 "  -f      flush a multipath device map\n"
136                 "  -F      flush all multipath device maps\n"
137                 "  -a      add a device wwid to the wwids file\n"
138                 "  -c      check if a device should be a path in a multipath device\n"
139                 "  -C      check if a multipath device has usable paths\n"
140                 "  -q      allow queue_if_no_path when multipathd is not running\n"
141                 "  -d      dry run, do not create or update devmaps\n"
142                 "  -t      display the currently used multipathd configuration\n"
143                 "  -T      display the multipathd configuration without builtin defaults\n"
144                 "  -r      force devmap reload\n"
145                 "  -i      ignore wwids file\n"
146                 "  -B      treat the bindings file as read only\n"
147                 "  -b fil  bindings file location\n"
148                 "  -w      remove a device from the wwids file\n"
149                 "  -W      reset the wwids file include only the current devices\n"
150                 "  -R num  number of times to retry removes of in-use devices\n"
151                 "  -u      check if the device specified in the program environment should be a\n"
152                 "          path in a multipath device\n"
153                 "  -U      check if the device specified in the program environment is a\n"
154                 "          multipath device with usable paths, see -C flag\n"
155                 "  -p pol  force all maps to specified path grouping policy:\n"
156                 "          . failover            one path per priority group\n"
157                 "          . multibus            all paths in one priority group\n"
158                 "          . group_by_serial     one priority group per serial\n"
159                 "          . group_by_prio       one priority group per priority lvl\n"
160                 "          . group_by_node_name  one priority group per target node\n"
161                 "  -v lvl  verbosity level:\n"
162                 "          . 0 no output\n"
163                 "          . 1 print created devmap names only\n"
164                 "          . 2 default verbosity\n"
165                 "          . 3 print debug information\n"
166                 "  device  action limited to:\n"
167                 "          . multipath named 'device' (ex: mpath0)\n"
168                 "          . multipath whose wwid is 'device' (ex: 60051...)\n"
169                 "          . multipath including the path named 'device' (ex: /dev/sda or\n"
170                 "            /dev/dm-0)\n"
171                 "          . multipath including the path with maj:min 'device' (ex: 8:0)\n"
172                 );
173
174 }
175
176 static int
177 get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid)
178 {
179         int i;
180         struct multipath * mpp;
181         int flags = (cmd == CMD_LIST_SHORT ? DI_NOIO : DI_ALL);
182
183         if (dm_get_maps(curmp))
184                 return 1;
185
186         vector_foreach_slot (curmp, mpp, i) {
187                 /*
188                  * discard out of scope maps
189                  */
190                 if (refwwid && strlen(refwwid) &&
191                     strncmp(mpp->wwid, refwwid, WWID_SIZE)) {
192                         condlog(3, "skip map %s: out of scope", mpp->alias);
193                         remove_map(mpp, pathvec, curmp);
194                         i--;
195                         continue;
196                 }
197
198                 if (update_multipath_table(mpp, pathvec, flags) != DMP_OK) {
199                         condlog(1, "error parsing map %s", mpp->wwid);
200                         remove_map(mpp, pathvec, curmp);
201                         i--;
202                         continue;
203                 }
204
205                 if (cmd == CMD_LIST_LONG)
206                         mpp->bestpg = select_path_group(mpp);
207
208                 if (cmd == CMD_LIST_SHORT ||
209                     cmd == CMD_LIST_LONG)
210                         print_multipath_topology(mpp, libmp_verbosity);
211
212                 if (cmd == CMD_CREATE)
213                         reinstate_paths(mpp);
214         }
215
216         if (cmd == CMD_LIST_SHORT || cmd == CMD_LIST_LONG)
217                 print_foreign_topology(libmp_verbosity);
218
219         return 0;
220 }
221
222 static int check_usable_paths(struct config *conf,
223                               const char *devpath, enum devtypes dev_type)
224 {
225         struct udev_device *ud = NULL;
226         struct multipath *mpp = NULL;
227         struct pathgroup *pg;
228         struct path *pp;
229         char *mapname;
230         vector pathvec = NULL;
231         dev_t devt;
232         int r = 1, i, j;
233
234         ud = get_udev_device(devpath, dev_type);
235         if (ud == NULL)
236                 return r;
237
238         devt = udev_device_get_devnum(ud);
239         if (!dm_is_dm_major(major(devt))) {
240                 condlog(1, "%s is not a dm device", devpath);
241                 goto out;
242         }
243
244         mapname = dm_mapname(major(devt), minor(devt));
245         if (mapname == NULL) {
246                 condlog(1, "dm device not found: %s", devpath);
247                 goto out;
248         }
249
250         if (dm_is_mpath(mapname) != 1) {
251                 condlog(1, "%s is not a multipath map", devpath);
252                 goto free;
253         }
254
255         /* pathvec is needed for disassemble_map */
256         pathvec = vector_alloc();
257         if (pathvec == NULL)
258                 goto free;
259
260         mpp = dm_get_multipath(mapname);
261         if (mpp == NULL)
262                 goto free;
263
264         if (update_multipath_table(mpp, pathvec, 0) != DMP_OK)
265                     goto free;
266
267         vector_foreach_slot (mpp->pg, pg, i) {
268                 vector_foreach_slot (pg->paths, pp, j) {
269                         pp->udev = get_udev_device(pp->dev_t, DEV_DEVT);
270                         if (pp->udev == NULL)
271                                 continue;
272                         if (pathinfo(pp, conf, DI_SYSFS|DI_NOIO|DI_CHECKER) != PATHINFO_OK)
273                                 continue;
274
275                         if (pp->state == PATH_UP &&
276                             pp->dmstate == PSTATE_ACTIVE) {
277                                 condlog(3, "%s: path %s is usable",
278                                         devpath, pp->dev);
279                                 r = 0;
280                                 goto found;
281                         }
282                 }
283         }
284 found:
285         condlog(r == 0 ? 3 : 2, "%s:%s usable paths found",
286                 devpath, r == 0 ? "" : " no");
287 free:
288         free(mapname);
289         free_multipath(mpp, FREE_PATHS);
290         vector_free(pathvec);
291 out:
292         udev_device_unref(ud);
293         return r;
294 }
295
296 enum {
297         FIND_MULTIPATHS_WAIT_DONE = 0,
298         FIND_MULTIPATHS_WAITING = 1,
299         FIND_MULTIPATHS_ERROR = -1,
300         FIND_MULTIPATHS_NEVER = -2,
301 };
302
303 static const char shm_find_mp_dir[] = MULTIPATH_SHM_BASE "find_multipaths";
304
305 /**
306  * find_multipaths_check_timeout(wwid, tmo)
307  * Helper for "find_multipaths smart"
308  *
309  * @param[in] pp: path to check / record
310  * @param[in] tmo: configured timeout for this WWID, or value <= 0 for checking
311  * @param[out] until: timestamp until we must wait, CLOCK_REALTIME, if return
312  *             value is FIND_MULTIPATHS_WAITING
313  * @returns: FIND_MULTIPATHS_WAIT_DONE, if waiting has finished
314  * @returns: FIND_MULTIPATHS_ERROR, if internal error occurred
315  * @returns: FIND_MULTIPATHS_NEVER, if tmo is 0 and we didn't wait for this
316  *           device
317  * @returns: FIND_MULTIPATHS_WAITING, if timeout hasn't expired
318  */
319 static int find_multipaths_check_timeout(const struct path *pp, long tmo,
320                                          struct timespec *until)
321 {
322         char path[PATH_MAX];
323         struct timespec now, ftimes[2], tdiff;
324         struct stat st;
325         long fd;
326         int r, retries = 0;
327
328         clock_gettime(CLOCK_REALTIME, &now);
329
330         if (safe_sprintf(path, "%s/%s", shm_find_mp_dir, pp->dev_t)) {
331                 condlog(1, "%s: path name overflow", __func__);
332                 return FIND_MULTIPATHS_ERROR;
333         }
334
335         if (ensure_directories_exist(path, 0700)) {
336                 condlog(1, "%s: error creating directories", __func__);
337                 return FIND_MULTIPATHS_ERROR;
338         }
339
340 retry:
341         fd = open(path, O_RDONLY);
342         if (fd != -1) {
343                 pthread_cleanup_push(close_fd, (void *)fd);
344                 r = fstat(fd, &st);
345                 pthread_cleanup_pop(1);
346
347         } else if (tmo > 0) {
348                 if (errno == ENOENT)
349                         fd = open(path, O_RDWR|O_EXCL|O_CREAT, 0644);
350                 if (fd == -1) {
351                         if (errno == EEXIST && !retries++)
352                                 /* We could have raced with another process */
353                                 goto retry;
354                         condlog(1, "%s: error opening %s: %s",
355                                 __func__, path, strerror(errno));
356                         return FIND_MULTIPATHS_ERROR;
357                 };
358
359                 pthread_cleanup_push(close_fd, (void *)fd);
360                 /*
361                  * We just created the file. Set st_mtim to our desired
362                  * expiry time.
363                  */
364                 ftimes[0].tv_sec = 0;
365                 ftimes[0].tv_nsec = UTIME_OMIT;
366                 ftimes[1].tv_sec = now.tv_sec + tmo;
367                 ftimes[1].tv_nsec = now.tv_nsec;
368                 if (futimens(fd, ftimes) != 0) {
369                         condlog(1, "%s: error in futimens(%s): %s", __func__,
370                                 path, strerror(errno));
371                 }
372                 r = fstat(fd, &st);
373                 pthread_cleanup_pop(1);
374         } else
375                 return FIND_MULTIPATHS_NEVER;
376
377         if (r != 0) {
378                 condlog(1, "%s: error in fstat for %s: %m", __func__, path);
379                 return FIND_MULTIPATHS_ERROR;
380         }
381
382         timespecsub(&st.st_mtim, &now, &tdiff);
383
384         if (tdiff.tv_sec <= 0)
385                 return FIND_MULTIPATHS_WAIT_DONE;
386
387         *until = tdiff;
388         return FIND_MULTIPATHS_WAITING;
389 }
390
391 static int print_cmd_valid(int k, const vector pathvec,
392                            struct config *conf)
393 {
394         int wait = FIND_MULTIPATHS_NEVER;
395         struct timespec until;
396         struct path *pp;
397
398         if (k != PATH_IS_VALID && k != PATH_IS_NOT_VALID &&
399             k != PATH_IS_MAYBE_VALID)
400                 return PATH_IS_NOT_VALID;
401
402         if (k == PATH_IS_MAYBE_VALID) {
403                 /*
404                  * Caller ensures that pathvec[0] is the path to
405                  * examine.
406                  */
407                 pp = VECTOR_SLOT(pathvec, 0);
408                 select_find_multipaths_timeout(conf, pp);
409                 wait = find_multipaths_check_timeout(
410                         pp, pp->find_multipaths_timeout, &until);
411                 if (wait != FIND_MULTIPATHS_WAITING)
412                         k = PATH_IS_NOT_VALID;
413         } else if (pathvec != NULL && (pp = VECTOR_SLOT(pathvec, 0)))
414                 wait = find_multipaths_check_timeout(pp, 0, &until);
415         if (wait == FIND_MULTIPATHS_WAITING)
416                 printf("FIND_MULTIPATHS_WAIT_UNTIL=\"%ld.%06ld\"\n",
417                        (long)until.tv_sec, until.tv_nsec/1000);
418         else if (wait == FIND_MULTIPATHS_WAIT_DONE)
419                 printf("FIND_MULTIPATHS_WAIT_UNTIL=\"0\"\n");
420         printf("DM_MULTIPATH_DEVICE_PATH=\"%d\"\n",
421                k == PATH_IS_MAYBE_VALID ? 2 : k == PATH_IS_VALID ? 1 : 0);
422         /* Never return RTVL_MAYBE */
423         return k == PATH_IS_NOT_VALID ? PATH_IS_NOT_VALID : PATH_IS_VALID;
424 }
425
426 /*
427  * Returns true if this device has been handled before,
428  * and released to systemd.
429  *
430  * This must be called before get_refwwid(),
431  * otherwise udev_device_new_from_environment() will have
432  * destroyed environ(7).
433  */
434 static bool released_to_systemd(void)
435 {
436         static const char dmdp[] = "DM_MULTIPATH_DEVICE_PATH";
437         const char *dm_mp_dev_path = getenv(dmdp);
438         bool ret;
439
440         ret = dm_mp_dev_path != NULL && !strcmp(dm_mp_dev_path, "0");
441         condlog(4, "%s: %s=%s -> %d", __func__, dmdp, dm_mp_dev_path, ret);
442         return ret;
443 }
444
445 static struct vectors vecs;
446 static void cleanup_vecs(void)
447 {
448         free_multipathvec(vecs.mpvec, KEEP_PATHS);
449         free_pathvec(vecs.pathvec, FREE_PATHS);
450 }
451
452 static int
453 configure (struct config *conf, enum mpath_cmds cmd,
454            enum devtypes dev_type, char *devpath)
455 {
456         vector curmp = NULL;
457         vector pathvec = NULL;
458         vector newmp = NULL;
459         int r = RTVL_FAIL, rc;
460         int di_flag = 0;
461         char * refwwid = NULL;
462         char * dev = NULL;
463         fieldwidth_t *width __attribute__((cleanup(cleanup_ucharp))) = NULL;
464
465         /*
466          * allocate core vectors to store paths and multipaths
467          */
468         curmp = vector_alloc();
469         pathvec = vector_alloc();
470         newmp = vector_alloc();
471
472         if (!curmp || !pathvec || !newmp) {
473                 condlog(0, "can not allocate memory");
474                 goto out;
475         }
476         vecs.pathvec = pathvec;
477         vecs.mpvec = curmp;
478
479         dev = convert_dev(devpath, (dev_type == DEV_DEVNODE));
480
481         /*
482          * if we have a blacklisted device parameter, exit early
483          */
484         if (dev && (dev_type == DEV_DEVNODE ||
485                     dev_type == DEV_UEVENT) &&
486             cmd != CMD_REMOVE_WWID &&
487             (filter_devnode(conf->blist_devnode,
488                             conf->elist_devnode, dev) > 0)) {
489                 goto out;
490         }
491
492         /*
493          * scope limiting must be translated into a wwid
494          * failing the translation is fatal (by policy)
495          */
496         if (devpath) {
497                 get_refwwid(cmd, devpath, dev_type, pathvec, &refwwid);
498                 if (!refwwid) {
499                         condlog(4, "%s: failed to get wwid", devpath);
500                         condlog(3, "scope is null");
501                         goto out;
502                 }
503                 if (cmd == CMD_REMOVE_WWID) {
504                         rc = remove_wwid(refwwid);
505                         if (rc == 0) {
506                                 printf("wwid '%s' removed\n", refwwid);
507                                 r = RTVL_OK;
508                         } else if (rc == 1) {
509                                 printf("wwid '%s' not in wwids file\n",
510                                         refwwid);
511                                 r = RTVL_OK;
512                         }
513                         goto out;
514                 }
515                 if (cmd == CMD_ADD_WWID) {
516                         rc = remember_wwid(refwwid);
517                         if (rc >= 0) {
518                                 printf("wwid '%s' added\n", refwwid);
519                                 r = RTVL_OK;
520                         } else
521                                 printf("failed adding '%s' to wwids file\n",
522                                        refwwid);
523                         goto out;
524                 }
525                 condlog(3, "scope limited to %s", refwwid);
526         }
527
528         /*
529          * get a path list
530          */
531         if (devpath)
532                 di_flag = DI_WWID;
533
534         if (cmd == CMD_LIST_LONG)
535                 /* extended path info '-ll' */
536                 di_flag |= DI_SYSFS | DI_CHECKER | DI_SERIAL;
537         else if (cmd == CMD_LIST_SHORT)
538                 /* minimum path info '-l' */
539                 di_flag |= DI_SYSFS;
540         else
541                 /* maximum info */
542                 di_flag = DI_ALL;
543
544         if (path_discovery(pathvec, di_flag) < 0)
545                 goto out;
546
547         if (libmp_verbosity > 2)
548                 print_all_paths(pathvec, 1);
549
550         if ((width = alloc_path_layout()) == NULL)
551                 goto out;
552         get_path_layout(pathvec, 0, width);
553         foreign_path_layout(width);
554
555         if (get_dm_mpvec(cmd, curmp, pathvec, refwwid))
556                 goto out;
557
558         filter_pathvec(pathvec, refwwid);
559
560         if (cmd == CMD_DUMP_CONFIG) {
561                 vector hwes = get_used_hwes(pathvec);
562
563                 dump_config(conf, hwes, curmp);
564                 vector_free(hwes);
565                 r = RTVL_OK;
566                 goto out;
567         }
568
569         if (cmd != CMD_CREATE && cmd != CMD_DRY_RUN) {
570                 r = RTVL_OK;
571                 goto out;
572         }
573
574         /*
575          * core logic entry point
576          */
577         rc = coalesce_paths(&vecs, newmp, refwwid,
578                            conf->force_reload, cmd);
579         r = rc == CP_RETRY ? RTVL_RETRY : rc == CP_OK ? RTVL_OK : RTVL_FAIL;
580
581 out:
582         if (r == RTVL_OK &&
583             (cmd == CMD_LIST_SHORT || cmd == CMD_LIST_LONG ||
584              cmd == CMD_CREATE) &&
585             (VECTOR_SIZE(curmp) > 0 || VECTOR_SIZE(newmp) > 0) &&
586             !check_daemon())
587                 condlog(2, "Warning: multipath devices exist, but multipathd service is not running");
588
589         if (refwwid)
590                 free(refwwid);
591
592         free_multipathvec(curmp, KEEP_PATHS);
593         vecs.mpvec = NULL;
594         free_multipathvec(newmp, KEEP_PATHS);
595         free_pathvec(pathvec, FREE_PATHS);
596         vecs.pathvec = NULL;
597
598         return r;
599 }
600
601 static int
602 check_path_valid(const char *name, struct config *conf, bool is_uevent)
603 {
604         int fd, r = PATH_IS_ERROR;
605         struct path *pp;
606         vector pathvec = NULL;
607         const char *wwid;
608
609         pp = alloc_path();
610         if (!pp)
611                 return RTVL_FAIL;
612
613         r = is_path_valid(name, conf, pp, is_uevent);
614         if (r <= PATH_IS_ERROR || r >= PATH_MAX_VALID_RESULT)
615                 goto fail;
616
617         /* set path values if is_path_valid() didn't */
618         if (!pp->udev)
619                 pp->udev = udev_device_new_from_subsystem_sysname(udev, "block",
620                                                                   name);
621         if (!pp->udev)
622                 goto fail;
623
624         if (!strlen(pp->dev_t)) {
625                 dev_t devt = udev_device_get_devnum(pp->udev);
626                 if (major(devt) == 0 && minor(devt) == 0)
627                         goto fail;
628                 snprintf(pp->dev_t, BLK_DEV_SIZE, "%d:%d", major(devt),
629                          minor(devt));
630         }
631
632         if ((r == PATH_IS_VALID || r == PATH_IS_MAYBE_VALID) &&
633             released_to_systemd())
634                 r = PATH_IS_NOT_VALID;
635
636         /* This state is only used to skip the released_to_systemd() check */
637         if (r == PATH_IS_VALID_NO_CHECK)
638                 r = PATH_IS_VALID;
639
640         if (r != PATH_IS_MAYBE_VALID)
641                 goto out;
642
643         /*
644          * If opening the path with O_EXCL fails, the path
645          * is in use (e.g. mounted during initramfs processing).
646          * We know that it's not used by dm-multipath.
647          * We may not set SYSTEMD_READY=0 on such devices, it
648          * might cause systemd to umount the device.
649          * Use O_RDONLY, because udevd would trigger another
650          * uevent for close-after-write.
651          *
652          * The O_EXCL check is potentially dangerous, because it may
653          * race with other tasks trying to access the device. Therefore
654          * this code is only executed if the path hasn't been released
655          * to systemd earlier (see above).
656          */
657         fd = open(udev_device_get_devnode(pp->udev), O_RDONLY|O_EXCL);
658         if (fd >= 0)
659                 close(fd);
660         else {
661                 condlog(3, "%s: path %s is in use: %m", __func__, pp->dev);
662                 /* Check if we raced with multipathd */
663                 if (sysfs_is_multipathed(pp, false))
664                         r = PATH_IS_VALID;
665                 else
666                         r = PATH_IS_NOT_VALID;
667                 goto out;
668         }
669
670         pathvec = vector_alloc();
671         if (!pathvec)
672                 goto fail;
673
674         if (store_path(pathvec, pp) != 0) {
675                 free_path(pp);
676                 pp = NULL;
677                 goto fail;
678         } else {
679                 /* make sure path isn't freed twice */
680                 wwid = pp->wwid;
681                 pp = NULL;
682         }
683
684         /* For find_multipaths = SMART, if there is more than one path
685          * matching the refwwid, then the path is valid */
686         if (path_discovery(pathvec, DI_SYSFS | DI_WWID) < 0)
687                 goto fail;
688         filter_pathvec(pathvec, wwid);
689         if (VECTOR_SIZE(pathvec) > 1)
690                 r = PATH_IS_VALID;
691         else
692                 r = PATH_IS_MAYBE_VALID;
693
694 out:
695         r = print_cmd_valid(r, pathvec, conf);
696         /*
697          * multipath -u must exit with status 0, otherwise udev won't
698          * import its output.
699          */
700         if (!is_uevent && r == PATH_IS_NOT_VALID)
701                 r = RTVL_FAIL;
702         else
703                 r = RTVL_OK;
704         goto cleanup;
705
706 fail:
707         r = RTVL_FAIL;
708
709 cleanup:
710         if (pp != NULL)
711                 free_path(pp);
712         if (pathvec != NULL)
713                 free_pathvec(pathvec, FREE_PATHS);
714         return r;
715 }
716
717 static int
718 get_dev_type(char *dev) {
719         struct stat buf;
720         int i;
721
722         if (stat(dev, &buf) == 0 && S_ISBLK(buf.st_mode)) {
723                 if (dm_is_dm_major(major(buf.st_rdev)))
724                         return DEV_DEVMAP;
725                 return DEV_DEVNODE;
726         }
727         else if (sscanf(dev, "%d:%d", &i, &i) == 2)
728                 return DEV_DEVT;
729         else if (valid_alias(dev))
730                 return DEV_DEVMAP;
731         return DEV_NONE;
732 }
733
734 /*
735  * Some multipath commands are dangerous to run while multipathd is running.
736  * For example, "multipath -r" may apply a modified configuration to the kernel,
737  * while multipathd is still using the old configuration, leading to
738  * inconsistent state.
739  *
740  * It is safer to use equivalent multipathd client commands instead.
741  */
742 enum {
743         DELEGATE_OK = 0,
744         DELEGATE_ERROR = -1,
745         NOT_DELEGATED = 1,
746 };
747
748 int delegate_to_multipathd(enum mpath_cmds cmd,
749                            __attribute__((unused)) const char *dev,
750                            __attribute__((unused)) enum devtypes dev_type,
751                            const struct config *conf)
752 {
753         int fd;
754         char command[1024], *p, *reply = NULL;
755         int n, r = DELEGATE_ERROR;
756
757         p = command;
758         *p = '\0';
759         n = sizeof(command);
760
761         if (conf->skip_delegate)
762                 return NOT_DELEGATED;
763
764         if (cmd == CMD_CREATE && conf->force_reload == FORCE_RELOAD_YES) {
765                 p += snprintf(p, n, "reconfigure all");
766         }
767         else if (cmd == CMD_FLUSH_ONE && dev && dev_type == DEV_DEVMAP) {
768                 p += snprintf(p, n, "del map %s", dev);
769                 /* multipathd doesn't try as hard, to avoid potentially
770                  * hanging. If it fails, retry with the regular multipath
771                  * command */
772                 r = NOT_DELEGATED;
773         }
774         else if (cmd == CMD_FLUSH_ALL) {
775                 p += snprintf(p, n, "del maps");
776                 /* multipathd doesn't try as hard, to avoid potentially
777                  * hanging. If it fails, retry with the regular multipath
778                  * command */
779                 r = NOT_DELEGATED;
780         }
781         /* Add other translations here */
782
783         if (strlen(command) == 0)
784                 /* No command found, no need to delegate */
785                 return NOT_DELEGATED;
786
787         fd = mpath_connect();
788         if (fd == -1)
789                 return NOT_DELEGATED;
790
791         if (p >= command + sizeof(command)) {
792                 condlog(0, "internal error - command buffer overflow");
793                 goto out;
794         }
795
796         condlog(3, "delegating command to multipathd");
797
798         if (mpath_process_cmd(fd, command, &reply, conf->uxsock_timeout)
799             == -1) {
800                 condlog(1, "error in multipath command %s: %s",
801                         command, strerror(errno));
802                 goto out;
803         }
804
805         if (reply != NULL && *reply != '\0') {
806                 if (strcmp(reply, "fail\n"))
807                         r = DELEGATE_OK;
808                 if (r != NOT_DELEGATED && strcmp(reply, "ok\n"))
809                         printf("%s", reply);
810         }
811
812 out:
813         free(reply);
814         close(fd);
815         return r;
816 }
817
818 int
819 main (int argc, char *argv[])
820 {
821         int arg;
822         extern char *optarg;
823         extern int optind;
824         int r = RTVL_FAIL;
825         enum mpath_cmds cmd = CMD_CREATE;
826         enum devtypes dev_type = DEV_NONE;
827         char *dev = NULL;
828         struct config *conf;
829         int retries = -1;
830         bool enable_foreign = false;
831
832         libmultipath_init();
833         if (atexit(dm_lib_exit) || atexit(libmultipath_exit))
834                 condlog(1, "failed to register cleanup handler for libmultipath: %m");
835         logsink = LOGSINK_STDERR_WITH_TIME;
836         if (init_config(DEFAULT_CONFIGFILE))
837                 exit(RTVL_FAIL);
838         if (atexit(uninit_config))
839                 condlog(1, "failed to register cleanup handler for config: %m");
840         conf = get_multipath_config();
841         conf->retrigger_tries = 0;
842         conf->force_sync = 1;
843         if (atexit(cleanup_vecs))
844                 condlog(1, "failed to register cleanup handler for vecs: %m");
845         while ((arg = getopt(argc, argv, ":adDcChl::eFfM:v:p:b:BrR:itTquUwW")) != EOF ) {
846                 switch(arg) {
847                 case 'v':
848                         if (!isdigit(optarg[0])) {
849                                 usage (argv[0]);
850                                 exit(RTVL_FAIL);
851                         }
852
853                         libmp_verbosity = atoi(optarg);
854                         break;
855                 case 'b':
856                         conf->bindings_file = strdup(optarg);
857                         break;
858                 case 'B':
859                         conf->bindings_read_only = 1;
860                         break;
861                 case 'q':
862                         conf->allow_queueing = 1;
863                         break;
864                 case 'c':
865                         cmd = CMD_VALID_PATH;
866                         break;
867                 case 'C':
868                         cmd = CMD_USABLE_PATHS;
869                         break;
870                 case 'd':
871                         if (cmd == CMD_CREATE)
872                                 cmd = CMD_DRY_RUN;
873                         break;
874                 case 'D':
875                         conf->skip_delegate = 1;
876                         break;
877                 case 'f':
878                         cmd = CMD_FLUSH_ONE;
879                         break;
880                 case 'F':
881                         cmd = CMD_FLUSH_ALL;
882                         break;
883                 case 'l':
884                         if (optarg && !strncmp(optarg, "l", 1))
885                                 cmd = CMD_LIST_LONG;
886                         else
887                                 cmd = CMD_LIST_SHORT;
888
889                         break;
890                 case 'M':
891                         break;
892                 case 'p':
893                         conf->pgpolicy_flag = get_pgpolicy_id(optarg);
894                         if (conf->pgpolicy_flag == IOPOLICY_UNDEF) {
895                                 printf("'%s' is not a valid policy\n", optarg);
896                                 usage(argv[0]);
897                                 exit(RTVL_FAIL);
898                         }
899                         break;
900                 case 'r':
901                         conf->force_reload = FORCE_RELOAD_YES;
902                         break;
903                 case 'i':
904                         if (conf->find_multipaths == FIND_MULTIPATHS_ON ||
905                             conf->find_multipaths == FIND_MULTIPATHS_STRICT)
906                                 conf->find_multipaths = FIND_MULTIPATHS_SMART;
907                         else if (conf->find_multipaths == FIND_MULTIPATHS_OFF)
908                                 conf->find_multipaths = FIND_MULTIPATHS_GREEDY;
909                         break;
910                 case 't':
911                         r = dump_config(conf, NULL, NULL) ? RTVL_FAIL : RTVL_OK;
912                         goto out;
913                 case 'T':
914                         cmd = CMD_DUMP_CONFIG;
915                         break;
916                 case 'h':
917                         usage(argv[0]);
918                         exit(RTVL_OK);
919                 case 'u':
920                         cmd = CMD_VALID_PATH;
921                         dev_type = DEV_UEVENT;
922                         break;
923                 case 'U':
924                         cmd = CMD_USABLE_PATHS;
925                         dev_type = DEV_UEVENT;
926                         break;
927                 case 'w':
928                         cmd = CMD_REMOVE_WWID;
929                         break;
930                 case 'W':
931                         cmd = CMD_RESET_WWIDS;
932                         break;
933                 case 'a':
934                         cmd = CMD_ADD_WWID;
935                         break;
936                 case 'R':
937                         retries = atoi(optarg);
938                         break;
939                 case 'e':
940                         enable_foreign = true;
941                         break;
942                 case ':':
943                         fprintf(stderr, "Missing option argument\n");
944                         usage(argv[0]);
945                         exit(RTVL_FAIL);
946                 case '?':
947                         fprintf(stderr, "Unknown switch: %s\n", optarg);
948                         usage(argv[0]);
949                         exit(RTVL_FAIL);
950                 default:
951                         usage(argv[0]);
952                         exit(RTVL_FAIL);
953                 }
954         }
955
956         if (getuid() != 0) {
957                 fprintf(stderr, "need to be root\n");
958                 exit(RTVL_FAIL);
959         }
960
961         if (check_alias_settings(conf)) {
962                 fprintf(stderr, "fatal configuration error, aborting");
963                 exit(RTVL_FAIL);
964         }
965
966         if (optind < argc) {
967                 dev = calloc(1, FILE_NAME_SIZE);
968
969                 if (!dev)
970                         goto out;
971
972                 strlcpy(dev, argv[optind], FILE_NAME_SIZE);
973                 if (dev_type != DEV_UEVENT)
974                         dev_type = get_dev_type(dev);
975                 if (dev_type == DEV_NONE) {
976                         condlog(0, "'%s' is not a valid argument\n", dev);
977                         goto out;
978                 }
979                 if (dev_type == DEV_DEVNODE || dev_type == DEV_DEVT)
980                         strchop(dev);
981         }
982         if (dev_type == DEV_UEVENT) {
983                 openlog("multipath", 0, LOG_DAEMON);
984                 setlogmask(LOG_UPTO(libmp_verbosity + 3));
985                 logsink = LOGSINK_SYSLOG;
986         }
987
988         set_max_fds(conf->max_fds);
989
990         libmp_udev_set_sync_support(1);
991
992         if (init_checkers(conf->multipath_dir)) {
993                 condlog(0, "failed to initialize checkers");
994                 goto out;
995         }
996         if (init_prio(conf->multipath_dir)) {
997                 condlog(0, "failed to initialize prioritizers");
998                 goto out;
999         }
1000
1001         if ((cmd == CMD_LIST_SHORT || cmd == CMD_LIST_LONG) && enable_foreign)
1002                 conf->enable_foreign = strdup("");
1003
1004         /* Failing here is non-fatal */
1005         init_foreign(conf->multipath_dir, conf->enable_foreign);
1006         if (cmd == CMD_USABLE_PATHS) {
1007                 r = check_usable_paths(conf, dev, dev_type) ?
1008                         RTVL_FAIL : RTVL_OK;
1009                 goto out;
1010         }
1011         if (cmd == CMD_VALID_PATH &&
1012             (!dev || dev_type == DEV_DEVMAP)) {
1013                 condlog(0, "the -c option requires a path to check");
1014                 goto out;
1015         }
1016         if (cmd == CMD_VALID_PATH) {
1017                 char * name = convert_dev(dev, (dev_type == DEV_DEVNODE));
1018                 r = check_path_valid(name, conf, dev_type == DEV_UEVENT);
1019                 goto out;
1020         }
1021
1022         if (cmd == CMD_REMOVE_WWID && !dev) {
1023                 condlog(0, "the -w option requires a device");
1024                 goto out;
1025         }
1026         if (cmd == CMD_FLUSH_ONE && dev_type != DEV_DEVMAP) {
1027                 condlog(0, "the -f option requires a map name to remove");
1028                 goto out;
1029         }
1030
1031         switch(delegate_to_multipathd(cmd, dev, dev_type, conf)) {
1032         case DELEGATE_OK:
1033                 exit(RTVL_OK);
1034         case DELEGATE_ERROR:
1035                 exit(RTVL_FAIL);
1036         case NOT_DELEGATED:
1037                 break;
1038         }
1039
1040         if (cmd == CMD_RESET_WWIDS) {
1041                 struct multipath * mpp;
1042                 int i;
1043                 vector curmp;
1044
1045                 curmp = vector_alloc();
1046                 if (!curmp) {
1047                         condlog(0, "can't allocate memory for mp list");
1048                         goto out;
1049                 }
1050                 if (dm_get_maps(curmp) == 0)
1051                         r = replace_wwids(curmp) ? RTVL_FAIL : RTVL_OK;
1052                 if (r == RTVL_OK)
1053                         printf("successfully reset wwids\n");
1054                 vector_foreach_slot_backwards(curmp, mpp, i) {
1055                         vector_del_slot(curmp, i);
1056                         free_multipath(mpp, KEEP_PATHS);
1057                 }
1058                 vector_free(curmp);
1059                 goto out;
1060         }
1061         if (retries < 0)
1062                 retries = conf->remove_retries;
1063         if (cmd == CMD_FLUSH_ONE) {
1064                 r = dm_suspend_and_flush_map(dev, retries) ?
1065                     RTVL_FAIL : RTVL_OK;
1066                 goto out;
1067         }
1068         else if (cmd == CMD_FLUSH_ALL) {
1069                 r = dm_flush_maps(1, retries) ? RTVL_FAIL : RTVL_OK;
1070                 goto out;
1071         }
1072         while ((r = configure(conf, cmd, dev_type, dev)) == RTVL_RETRY)
1073                 condlog(3, "restart multipath configuration process");
1074
1075 out:
1076         put_multipath_config(conf);
1077         if (dev)
1078                 free(dev);
1079
1080         if (dev_type == DEV_UEVENT)
1081                 closelog();
1082
1083         return r;
1084 }