2 * Soft: Description here...
4 * Version: $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $
6 * Author: Copyright (C) 2003 Christophe Varoqui
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.
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.
23 #include <sys/types.h>
25 #include <linux/kdev_t.h>
27 #include <sys/ioctl.h>
29 #include <libdevmapper.h>
33 do_inq(int sg_fd
, int cmddt
, int evpd
, unsigned int pg_op
,
34 void *resp
, int mx_resp_len
, int noisy
)
36 unsigned char inqCmdBlk
[INQUIRY_CMDLEN
] =
37 { INQUIRY_CMD
, 0, 0, 0, 0, 0 };
38 unsigned char sense_b
[SENSE_BUFF_LEN
];
39 struct sg_io_hdr io_hdr
;
45 inqCmdBlk
[2] = (unsigned char) pg_op
;
46 inqCmdBlk
[4] = (unsigned char) mx_resp_len
;
47 memset(&io_hdr
, 0, sizeof (struct sg_io_hdr
));
48 io_hdr
.interface_id
= 'S';
49 io_hdr
.cmd_len
= sizeof (inqCmdBlk
);
50 io_hdr
.mx_sb_len
= sizeof (sense_b
);
51 io_hdr
.dxfer_direction
= SG_DXFER_FROM_DEV
;
52 io_hdr
.dxfer_len
= mx_resp_len
;
54 io_hdr
.cmdp
= inqCmdBlk
;
56 io_hdr
.timeout
= DEF_TIMEOUT
;
58 if (ioctl(sg_fd
, SG_IO
, &io_hdr
) < 0) {
59 perror("SG_IO (inquiry) error");
63 /* treat SG_ERR here to get rid of sg_err.[ch] */
64 io_hdr
.status
&= 0x7e;
65 if ((0 == io_hdr
.status
) && (0 == io_hdr
.host_status
) &&
66 (0 == io_hdr
.driver_status
))
68 if ((SCSI_CHECK_CONDITION
== io_hdr
.status
) ||
69 (SCSI_COMMAND_TERMINATED
== io_hdr
.status
) ||
70 (SG_ERR_DRIVER_SENSE
== (0xf & io_hdr
.driver_status
))) {
71 if (io_hdr
.sbp
&& (io_hdr
.sb_len_wr
> 2)) {
73 unsigned char * sense_buffer
= io_hdr
.sbp
;
74 if (sense_buffer
[0] & 0x2)
75 sense_key
= sense_buffer
[1] & 0xf;
77 sense_key
= sense_buffer
[2] & 0xf;
78 if(RECOVERED_ERROR
== sense_key
)
88 unsigned char turCmdBlk
[TUR_CMD_LEN
] = { 0x00, 0, 0, 0, 0, 0 };
89 struct sg_io_hdr io_hdr
;
90 unsigned char sense_buffer
[32];
92 memset(&io_hdr
, 0, sizeof (struct sg_io_hdr
));
93 io_hdr
.interface_id
= 'S';
94 io_hdr
.cmd_len
= sizeof (turCmdBlk
);
95 io_hdr
.mx_sb_len
= sizeof (sense_buffer
);
96 io_hdr
.dxfer_direction
= SG_DXFER_NONE
;
97 io_hdr
.cmdp
= turCmdBlk
;
98 io_hdr
.sbp
= sense_buffer
;
99 io_hdr
.timeout
= 20000;
101 if (ioctl(fd
, SG_IO
, &io_hdr
) < 0) {
105 if (io_hdr
.info
& SG_INFO_OK_MASK
) {
112 sprint_wwid(char * buff
, const char * str
)
121 for (i
= 0; i
<= WWID_SIZE
/ 2 - 1; i
++) {
123 sprintf(cursor
, "%.2x", (int) (unsigned char) c
);
126 buff
[WWID_SIZE
- 1] = '\0';
130 get_lun_strings(int fd
, struct path
* mypath
)
134 if (0 == do_inq(fd
, 0, 0, 0, buff
, 36, 1)) {
135 memcpy(mypath
->vendor_id
, &buff
[8], 8);
136 memcpy(mypath
->product_id
, &buff
[16], 16);
137 memcpy(mypath
->rev
, &buff
[32], 4);
145 get_serial (int fd, char * str)
147 char buff[MX_ALLOC_LEN + 1];
150 if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0)) {
153 memcpy(str, buff + 4, len);
162 /* hardware vendor specifics : add support for new models below */
164 get_storageworks_wwid(int fd
, char *str
)
168 if (0 == do_inq(fd
, 0, 1, 0x83, buff
, sizeof (buff
), 1)) {
169 sprint_wwid(str
, &buff
[8]);
175 /* White list switch */
177 get_unique_id(int fd
, struct path
* mypath
)
179 if (strncmp(mypath
->product_id
, "HSV110 (C)COMPAQ", 16) == 0 ||
180 strncmp(mypath
->product_id
, "HSG80 ", 16) == 0) {
181 get_storageworks_wwid(fd
, mypath
->wwid
);
189 basename(char * str1
, char * str2
)
191 char *p
= str1
+ (strlen(str1
) - 1);
199 get_all_paths_sysfs(struct env
* conf
, struct path
* all_paths
)
203 struct sysfs_directory
* sdir
;
204 struct sysfs_directory
* devp
;
205 struct sysfs_link
* linkp
;
206 char buff
[FILE_NAME_SIZE
];
208 char block_path
[FILE_NAME_SIZE
];
210 sprintf(block_path
, "%s/block", conf
->sysfs_path
);
211 sdir
= sysfs_open_directory(block_path
);
212 sysfs_read_directory(sdir
);
213 dlist_for_each_data(sdir
->subdirs
, devp
, struct sysfs_directory
) {
214 sysfs_read_directory(devp
);
215 if(devp
->links
== NULL
)
217 dlist_for_each_data(devp
->links
, linkp
, struct sysfs_link
) {
218 if (!strncmp(linkp
->name
, "device", 6))
225 basename(devp
->path
, buff
);
226 sprintf(all_paths
[k
].sg_dev
, "/dev/%s", buff
);
227 strcpy(all_paths
[k
].dev
, all_paths
[k
].sg_dev
);
228 if ((sg_fd
= open(all_paths
[k
].sg_dev
, O_RDONLY
)) < 0) {
231 get_lun_strings(sg_fd
, &all_paths
[k
]);
232 get_unique_id(sg_fd
, &all_paths
[k
]);
233 all_paths
[k
].state
= do_tur(sg_fd
);
235 basename(linkp
->target
, buff
);
236 sscanf(buff
, "%i:%i:%i:%i",
237 &all_paths
[k
].sg_id
.host_no
,
238 &all_paths
[k
].sg_id
.channel
,
239 &all_paths
[k
].sg_id
.scsi_id
,
240 &all_paths
[k
].sg_id
.lun
);
243 sysfs_close_directory(sdir
);
249 get_all_paths_nosysfs(struct env
* conf
, struct path
* all_paths
,
250 struct scsi_dev
* all_scsi_ids
)
253 char buff
[FILE_NAME_SIZE
];
254 char file_name
[FILE_NAME_SIZE
];
256 for (k
= 0; k
< conf
->max_devs
; k
++) {
257 strcpy(file_name
, "/dev/sg");
258 sprintf(buff
, "%d", k
);
259 strncat(file_name
, buff
, FILE_NAME_SIZE
);
260 strcpy(all_paths
[k
].sg_dev
, file_name
);
261 if ((sg_fd
= open(file_name
, O_RDONLY
)) < 0)
263 get_lun_strings(sg_fd
, &all_paths
[k
]);
264 get_unique_id(sg_fd
, &all_paths
[k
]);
265 all_paths
[k
].state
= do_tur(sg_fd
);
266 if (0 > ioctl(sg_fd
, SG_GET_SCSI_ID
, &(all_paths
[k
].sg_id
)))
267 printf("device %s failed on sg ioctl, skip\n",
272 for (i
= 0; i
< conf
->max_devs
; i
++) {
273 if ((all_paths
[k
].sg_id
.host_no
==
274 all_scsi_ids
[i
].host_no
)
275 && (all_paths
[k
].sg_id
.scsi_id
==
276 (all_scsi_ids
[i
].scsi_id
.dev_id
& 0xff))
277 && (all_paths
[k
].sg_id
.lun
==
278 ((all_scsi_ids
[i
].scsi_id
.dev_id
>> 8) & 0xff))
279 && (all_paths
[k
].sg_id
.channel
==
280 ((all_scsi_ids
[i
].scsi_id
.
281 dev_id
>> 16) & 0xff))) {
282 strcpy(all_paths
[k
].dev
, all_scsi_ids
[i
].dev
);
291 get_all_scsi_ids(struct env
* conf
, struct scsi_dev
* all_scsi_ids
)
293 int k
, big
, little
, res
, host_no
, fd
;
295 char fname
[FILE_NAME_SIZE
];
296 struct scsi_idlun my_scsi_id
;
298 for (k
= 0; k
< conf
->max_devs
; k
++) {
299 strcpy(fname
, "/dev/sd");
301 buff
[0] = 'a' + (char) k
;
304 } else if (k
<= 255) { /* assumes sequence goes x,y,z,aa,ab,ac etc */
306 little
= k
- (26 * big
);
309 buff
[0] = 'a' + (char) big
;
310 buff
[1] = 'a' + (char) little
;
314 strcat(fname
, "xxxx");
316 if ((fd
= open(fname
, O_RDONLY
)) < 0)
319 res
= ioctl(fd
, SCSI_IOCTL_GET_IDLUN
, &my_scsi_id
);
322 printf("Could not get scsi idlun\n");
326 res
= ioctl(fd
, SCSI_IOCTL_GET_BUS_NUMBER
, &host_no
);
329 printf("Could not get host_no\n");
335 strcpy(all_scsi_ids
[k
].dev
, fname
);
336 all_scsi_ids
[k
].scsi_id
= my_scsi_id
;
337 all_scsi_ids
[k
].host_no
= host_no
;
342 /* print_path style */
347 print_path(struct path
* all_paths
, int k
, int style
)
350 printf("%s ", all_paths
[k
].wwid
);
353 printf("(%i %i %i %i) ",
354 all_paths
[k
].sg_id
.host_no
,
355 all_paths
[k
].sg_id
.channel
,
356 all_paths
[k
].sg_id
.scsi_id
, all_paths
[k
].sg_id
.lun
);
357 printf("%s ", all_paths
[k
].sg_dev
);
358 printf("op:%i ", all_paths
[k
].state
);
359 printf("%s ", all_paths
[k
].dev
);
360 printf("[%.16s]\n", all_paths
[k
].product_id
);
364 print_all_path(struct env
* conf
, struct path
* all_paths
)
367 char empty_buff
[WWID_SIZE
];
369 memset(empty_buff
, 0, WWID_SIZE
);
370 for (k
= 0; k
< conf
->max_devs
; k
++) {
371 if (memcmp(empty_buff
, all_paths
[k
].wwid
, WWID_SIZE
) == 0)
373 print_path(all_paths
, k
, ALL
);
378 print_all_mp(struct path
* all_paths
, struct multipath
* mp
, int nmp
)
382 for (k
= 0; k
<= nmp
; k
++) {
383 printf("%s\n", mp
[k
].wwid
);
384 for (i
= 0; i
<= mp
[k
].npaths
; i
++)
385 print_path(all_paths
, PINDEX(k
,i
), NOWWID
);
390 coalesce_paths(struct env
* conf
, struct multipath
* mp
,
391 struct path
* all_paths
)
393 int k
, i
, nmp
, np
, already_done
;
394 char empty_buff
[WWID_SIZE
];
398 memset(empty_buff
, 0, WWID_SIZE
);
400 for (k
= 0; k
< conf
->max_devs
- 1; k
++) {
401 /* skip this path if no unique id has been found */
402 if (memcmp(empty_buff
, all_paths
[k
].wwid
, WWID_SIZE
) == 0)
406 for (i
= 0; i
<= nmp
; i
++) {
407 if (0 == strcmp(mp
[i
].wwid
, all_paths
[k
].wwid
))
417 strcpy(mp
[nmp
].wwid
, all_paths
[k
].wwid
);
420 for (i
= k
+ 1; i
< conf
->max_devs
; i
++) {
421 if (0 == strcmp(all_paths
[k
].wwid
, all_paths
[i
].wwid
)) {
432 get_disk_size (struct env
* conf
, char * dev
) {
435 char attr_path
[FILE_NAME_SIZE
];
436 char buff
[FILE_NAME_SIZE
];
437 char basedev
[FILE_NAME_SIZE
];
439 if(conf
->with_sysfs
) {
440 basename(dev
, basedev
);
441 sprintf(attr_path
, "%s/block/%s/size",
442 conf
->sysfs_path
, basedev
);
443 if (0 > sysfs_read_attribute_value(attr_path
, buff
,
444 FILE_NAME_SIZE
* sizeof(char)))
449 if ((fd
= open(dev
, O_RDONLY
)) < 0)
451 if(!ioctl(fd
, BLKGETSIZE
, &size
))
458 make_dm_node(char * str
)
462 char buff
[FILE_NAME_SIZE
];
464 struct dm_names
* names
;
468 if (!(dmt
= dm_task_create(DM_DEVICE_LIST
)))
471 if (!dm_task_run(dmt
))
474 if (!(names
= dm_task_get_names(dmt
)))
483 if (0 == strcmp(names
->name
, str
))
486 names
= (void *) names
+ next
;
489 major
= (int) MAJOR(names
->dev
);
490 minor
= (int) MINOR(names
->dev
);
492 dev
= major
<< sizeof(dev_t
);
494 sprintf(buff
, "/dev/mapper/%s", str
);
496 mknod(buff
, 0600 | S_IFBLK
, dev
);
499 dm_task_destroy(dmt
);
506 del_map(char * str) {
509 if (!(dmt = dm_task_create(DM_DEVICE_REMOVE)))
511 if (!dm_task_set_name(dmt, str))
513 if (!dm_task_run(dmt))
516 printf("Deleted device map : %s\n", str);
519 dm_task_destroy(dmt);
525 add_map(struct env
* conf
, struct path
* all_paths
,
526 struct multipath
* mp
, int index
, int op
)
534 if (!(dmt
= dm_task_create(op
)))
537 if (!dm_task_set_name(dmt
, mp
[index
].wwid
))
539 params_p
= ¶ms
[0];
542 for (i
=0; i
<=mp
[index
].npaths
; i
++) {
543 if ((1 == all_paths
[PINDEX(index
,i
)].state
) &&
544 (0 == all_paths
[PINDEX(index
,i
)].sg_id
.scsi_type
))
549 params_p
+= sprintf(params_p
, "%i 32", np
);
551 for (i
=0; i
<=mp
[index
].npaths
; i
++) {
552 if (( 0 == all_paths
[PINDEX(index
,i
)].state
) ||
553 (0 != all_paths
[PINDEX(index
,i
)].sg_id
.scsi_type
))
556 size
= get_disk_size(conf
, all_paths
[PINDEX(index
,0)].dev
);
557 params_p
+= sprintf(params_p
, " %s %i",
558 all_paths
[PINDEX(index
,i
)].dev
, 0);
565 if (op
== DM_DEVICE_RELOAD
)
567 if (op
== DM_DEVICE_CREATE
)
569 printf("%s : 0 %li %s %s\n",
570 mp
[index
].wwid
, size
, DM_TARGET
, params
);
573 if (!dm_task_add_target(dmt
, 0, size
, DM_TARGET
, params
))
576 if (!dm_task_run(dmt
))
580 make_dm_node(mp
[index
].wwid
);
583 dm_task_destroy(dmt
);
589 get_table(const char * str)
594 uint64_t start, length;
595 char *target_type = NULL;
598 if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
601 if (!dm_task_set_name(dmt, str))
604 if (!dm_task_run(dmt))
608 next = dm_get_next_target(dmt, next, &start, &length,
609 &target_type, ¶ms);
611 printf("%" PRIu64 " %" PRIu64 " %s %s\n",
612 start, length, target_type, params);
619 dm_task_destroy(dmt);
626 map_present(char * str
)
630 struct dm_names
*names
;
633 if (!(dmt
= dm_task_create(DM_DEVICE_LIST
)))
636 if (!dm_task_run(dmt
))
639 if (!(names
= dm_task_get_names(dmt
)))
647 if (0 == strcmp(names
->name
, str
))
650 names
= (void *) names
+ next
;
654 dm_task_destroy(dmt
);
659 usage(char * progname
)
661 fprintf(stderr
, VERSION_STRING
);
662 fprintf(stderr
, "Usage: %s [-v|-q] [-d] [-m max_devs]\n", progname
);
663 fprintf(stderr
, "\t-v\t\tverbose, print all paths and multipaths\n");
664 fprintf(stderr
, "\t-q\t\tquiet, no output at all\n");
665 fprintf(stderr
, "\t-d\t\tdry run, do not create or update devmaps\n");
666 fprintf(stderr
, "\t-m max_devs\tscan {max_devs} devices at most\n");
671 main(int argc
, char *argv
[])
673 struct multipath
* mp
;
674 struct path
* all_paths
;
675 struct scsi_dev
* all_scsi_ids
;
679 /* Default behaviour */
680 conf
.max_devs
= MAX_DEVS
;
681 conf
.dry_run
= 0; /* 1 == Do not Create/Update devmaps */
682 conf
.verbose
= 0; /* 1 == Print all_paths and mp */
683 conf
.quiet
= 0; /* 1 == Do not even print devmaps */
684 conf
.with_sysfs
= 0; /* Default to compat / suboptimal behaviour */
686 /* kindly provided by libsysfs */
687 if (0 == sysfs_get_mnt_path(conf
.sysfs_path
, FILE_NAME_SIZE
))
690 for (i
= 1; i
< argc
; ++i
) {
691 if (0 == strcmp("-v", argv
[i
])) {
695 } else if (0 == strcmp("-m", argv
[i
])) {
696 conf
.max_devs
= atoi(argv
[++i
]);
697 if (conf
.max_devs
< 2)
699 } else if (0 == strcmp("-q", argv
[i
])) {
700 if (conf
.verbose
== 1)
703 } else if (0 == strcmp("-d", argv
[i
]))
705 else if (*argv
[i
] == '-') {
706 fprintf(stderr
, "Unknown switch: %s\n", argv
[i
]);
708 } else if (*argv
[i
] != '-') {
709 fprintf(stderr
, "Unknown argument\n");
715 /* dynamic allocations */
716 mp
= malloc(conf
.max_devs
* sizeof(struct multipath
));
717 all_paths
= malloc(conf
.max_devs
* sizeof(struct path
));
718 all_scsi_ids
= malloc(conf
.max_devs
* sizeof(struct scsi_dev
));
719 if (mp
== NULL
|| all_paths
== NULL
|| all_scsi_ids
== NULL
)
722 if (!conf
.with_sysfs
) {
723 get_all_scsi_ids(&conf
, all_scsi_ids
);
724 get_all_paths_nosysfs(&conf
, all_paths
, all_scsi_ids
);
726 get_all_paths_sysfs(&conf
, all_paths
);
728 nmp
= coalesce_paths(&conf
, mp
, all_paths
);
731 print_all_path(&conf
, all_paths
);
733 print_all_mp(all_paths
, mp
, nmp
);
740 for (k
=0; k
<=nmp
; k
++) {
741 if (map_present(mp
[k
].wwid
)) {
742 add_map(&conf
, all_paths
, mp
, k
, DM_DEVICE_RELOAD
);
744 add_map(&conf
, all_paths
, mp
, k
, DM_DEVICE_CREATE
);