source: mainline/uspace/app/hrctl/hrctl.c

Last change on this file was 25830c2, checked in by Miroslav Cimerman <mc@…>, 12 days ago

hrctl: do not use devices/ prefix in usage

  • Property mode set to 100644
File size: 21.8 KB
Line 
1/*
2 * Copyright (c) 2025 Miroslav Cimerman
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup hrctl
30 * @{
31 */
32/**
33 * @file
34 */
35
36#include <capa.h>
37#include <ctype.h>
38#include <errno.h>
39#include <getopt.h>
40#include <hr.h>
41#include <sif.h>
42#include <stdlib.h>
43#include <stdio.h>
44#include <str.h>
45#include <str_error.h>
46
47/* #define HRCTL_SAMPLE_CONFIG_PATH "/cfg/sample_hr_config.sif" */
48
49#define NAME "hrctl"
50
51static void usage(void);
52static errno_t fill_config_devs(int, char **, hr_config_t *);
53static errno_t get_vol_configs_from_sif(const char *, hr_config_t **, size_t *);
54static int create_from_config(hr_t *, const char *, uint8_t);
55static int create_from_argv(hr_t *, int, char **, uint8_t);
56static int handle_create(hr_t *, int, char **);
57static int assemble_from_config(hr_t *, const char *, uint8_t);
58static int assemble_from_argv(hr_t *, int, char **, uint8_t);
59static int handle_assemble(hr_t *, int, char **);
60static int handle_disassemble(hr_t *, int, char **);
61static int handle_modify(hr_t *, int, char **);
62static errno_t print_vol_info(hr_vol_info_t *);
63static int handle_state(hr_t *, int, char **);
64
65static const char usage_str[] =
66 NAME ": HelenOS RAID configuration and management utility.\n"
67 "Usage: " NAME " [OPTION]...\n"
68 "\n"
69 "Options:\n"
70 " -h, --help Display this message and exit.\n"
71 "\n"
72 " -c, --create [--no-meta] [--read-only] Create a volume, options:\n"
73 " name {-l , --level level} device... manual device specification, or\n"
74 " -f configuration.sif create from configuration file.\n"
75 "\n"
76 " -a, --assemble Assemble volume(s), options:\n"
77 " [[--read-only] device...] manual device specification, or\n"
78 " [[--read-only] -f configuration.sif] assemble from configuration file, or\n"
79 " no option is automatic assembly.\n"
80 "\n"
81 " -d, --disassemble Deactivate/disassemble, options:\n"
82 " [volume] specific volume, or\n"
83 " all volumes with no specified option.\n"
84 "\n"
85 " -m, --modify volume Modify a volume, options:\n"
86 " -f, --fail index fail an extent (DANGEROUS), or\n"
87 " -h, --hotspare device add hotspare.\n"
88 "\n"
89 " -s, --state [volume] Display state of active volume(s).\n"
90 "\n"
91 "level can be one of:\n"
92 " 0 | stripe | striping |\n"
93 " 1 | mirror | mirroring |\n"
94 " 4 | parity_dedicated |\n"
95 " 5 | parity | parity_distributed\n"
96 "\n"
97 "Example usage:\n"
98 "\t\thrctl --create hr0 --level 5 disk1 disk2 disk3\n"
99 "\t\thrctl -c hr0 -l 5 disk1 disk2 disk3\n"
100 "\t\thrctl -c -f cfg.sif\n"
101 "\t\thrctl --assemble disk1 disk2 disk3\n"
102 "\t\thrctl -a\n"
103 "\t\thrctl -d hr0\n"
104 "\t\thrctl -d\n"
105 "\t\thrctl --modify hr0 --fail 0\n"
106 "\t\thrctl --modify hr0 --hotspare disk4\n"
107 "\t\thrctl -s\n"
108 "\n"
109 "Notes:\n"
110 " Add --no-meta after --create to disable storing on-disk metadata.\n"
111 " Simulating an extent failure with -m volume -f index is dangerous. It marks\n"
112 " metadata as dirty in other healthy extents, and zeroes out the superblock\n"
113 " on the specified extent.\n"
114 " Nested levels have to be created manually, or from a config file, and need to\n"
115 " be specified as separate volumes.\n"
116 "\n"
117 "Limitations:\n"
118 "\t- volume name must be shorter than 32 characters\n"
119 "\t- no explicit nested levels volume support\n"
120 "\t- automatic assembly and disassembly on nested volumes is undefine!\n";
121
122int main(int argc, char **argv)
123{
124 int rc = EXIT_SUCCESS;
125 hr_t *hr = NULL;
126
127 if (argc < 2) {
128 rc = EXIT_FAILURE;
129 goto end;
130 }
131
132 if (argc > 1 &&
133 ((str_cmp(argv[1], "-h") == 0) ||
134 str_cmp(argv[1], "--help") == 0)) {
135 usage();
136 return EXIT_SUCCESS;
137 }
138
139 rc = hr_sess_init(&hr);
140 if (rc != EOK) {
141 printf(NAME ": hr server session init failed: %s\n",
142 str_error(rc));
143 return EXIT_FAILURE;
144 }
145
146 optreset = 1;
147 optind = 0;
148
149 struct option const top_level_opts[] = {
150 { "help", no_argument, 0, 'h' },
151 { "create", no_argument, 0, 'c' },
152 { "assemble", no_argument, 0, 'a' },
153 { "disassemble", no_argument, 0, 'd' },
154 { "modify", no_argument, 0, 'm' },
155 { "state", no_argument, 0, 's' },
156 { 0, 0, 0, 0 }
157 };
158
159 int c = getopt_long(argc, argv, "hcadms", top_level_opts, NULL);
160 switch (c) {
161 case 'h':
162 usage();
163 goto end;
164 case 'c':
165 rc = handle_create(hr, argc, argv);
166 goto end;
167 case 'a':
168 rc = handle_assemble(hr, argc, argv);
169 goto end;
170 case 'd':
171 rc = handle_disassemble(hr, argc, argv);
172 goto end;
173 case 'm':
174 rc = handle_modify(hr, argc, argv);
175 goto end;
176 case 's':
177 rc = handle_state(hr, argc, argv);
178 goto end;
179 default:
180 goto end;
181 }
182
183end:
184 hr_sess_destroy(hr);
185
186 if (rc != EXIT_SUCCESS)
187 printf(NAME ": use --help to see usage\n");
188
189 return rc;
190}
191
192static void usage(void)
193{
194 printf("%s", usage_str);
195}
196
197static errno_t fill_config_devs(int argc, char **argv, hr_config_t *cfg)
198{
199 errno_t rc;
200 size_t i;
201
202 for (i = 0; i < HR_MAX_EXTENTS && optind < argc; i++) {
203 rc = loc_service_get_id(argv[optind], &cfg->devs[i], 0);
204 if (rc == ENOENT) {
205 printf(NAME ": device \"%s\" not found, aborting\n",
206 argv[optind]);
207 return ENOENT;
208 } else if (rc != EOK) {
209 printf(NAME ": error resolving device \"%s\", aborting\n",
210 argv[optind]);
211 return EINVAL;
212 }
213 optind++;
214 }
215
216 if (optind < argc) {
217 printf(NAME ": too many devices specified, max = %u\n",
218 HR_MAX_EXTENTS);
219 return ELIMIT;
220 }
221
222 cfg->dev_no = i;
223
224 return EOK;
225}
226
227static errno_t get_vol_configs_from_sif(const char *path, hr_config_t **rcfgs,
228 size_t *rcount)
229{
230 errno_t rc;
231 sif_doc_t *doc = NULL;
232 sif_node_t *hrconfig_node;
233 sif_node_t *root_node;
234 sif_node_t *volume_node;
235 sif_node_t *nextent;
236 const char *ntype;
237 const char *devname;
238 const char *level_str;
239 const char *extent_devname;
240 hr_config_t *vol_configs = NULL;
241
242 rc = sif_load(path, &doc);
243 if (rc != EOK)
244 goto error;
245
246 root_node = sif_get_root(doc);
247
248 hrconfig_node = sif_node_first_child(root_node);
249 ntype = sif_node_get_type(hrconfig_node);
250 if (str_cmp(ntype, "hrconfig") != 0) {
251 rc = EINVAL;
252 goto error;
253 }
254
255 size_t vol_count = 0;
256 volume_node = sif_node_first_child(hrconfig_node);
257 while (volume_node) {
258 ntype = sif_node_get_type(volume_node);
259 if (str_cmp(ntype, "volume") != 0) {
260 rc = EINVAL;
261 goto error;
262 }
263 vol_configs = realloc(vol_configs,
264 (vol_count + 1) * sizeof(hr_config_t));
265 if (vol_configs == NULL) {
266 rc = ENOMEM;
267 goto error;
268 }
269
270 hr_config_t *cfg = vol_configs + vol_count;
271
272 devname = sif_node_get_attr(volume_node, "devname");
273 if (devname == NULL) {
274 rc = EINVAL;
275 goto error;
276 }
277 str_cpy(cfg->devname, sizeof(cfg->devname), devname);
278
279 level_str = sif_node_get_attr(volume_node, "level");
280 if (level_str == NULL)
281 cfg->level = HR_LVL_UNKNOWN;
282 else
283 cfg->level = strtol(level_str, NULL, 10);
284
285 nextent = sif_node_first_child(volume_node);
286 size_t i = 0;
287 while (nextent && i < HR_MAX_EXTENTS) {
288 ntype = sif_node_get_type(nextent);
289 if (str_cmp(ntype, "extent") != 0) {
290 rc = EINVAL;
291 goto error;
292 }
293
294 extent_devname = sif_node_get_attr(nextent, "devname");
295 if (extent_devname == NULL) {
296 rc = EINVAL;
297 goto error;
298 }
299
300 rc = loc_service_get_id(extent_devname, &cfg->devs[i], 0);
301 if (rc == ENOENT) {
302 printf(NAME ": no device \"%s\", marking as missing\n",
303 extent_devname);
304 cfg->devs[i] = 0;
305 rc = EOK;
306 } else if (rc != EOK) {
307 printf(NAME ": error resolving device \"%s\", aborting\n",
308 extent_devname);
309 goto error;
310 }
311
312 nextent = sif_node_next_child(nextent);
313 i++;
314 }
315
316 if (i > HR_MAX_EXTENTS) {
317 printf(NAME ": too many devices specified in volume \"%s\", "
318 "skipping\n", devname);
319 memset(&vol_configs[vol_count], 0, sizeof(hr_config_t));
320 } else {
321 cfg->dev_no = i;
322 vol_count++;
323 }
324
325 volume_node = sif_node_next_child(volume_node);
326 }
327
328 if (rc == EOK) {
329 if (rcount)
330 *rcount = vol_count;
331 if (rcfgs)
332 *rcfgs = vol_configs;
333 }
334error:
335 if (doc != NULL)
336 sif_delete(doc);
337 if (rc != EOK) {
338 if (vol_configs)
339 free(vol_configs);
340 }
341 return rc;
342}
343
344static int create_from_config(hr_t *hr, const char *config_path,
345 uint8_t vol_flags)
346{
347 hr_config_t *vol_configs = NULL;
348 size_t vol_count = 0;
349 errno_t rc = get_vol_configs_from_sif(config_path, &vol_configs,
350 &vol_count);
351 if (rc != EOK) {
352 printf(NAME ": config parsing failed\n");
353 return EXIT_FAILURE;
354 }
355
356 for (size_t i = 0; i < vol_count; i++)
357 vol_configs[i].vol_flags |= vol_flags;
358
359 for (size_t i = 0; i < vol_count; i++) {
360 rc = hr_create(hr, &vol_configs[i]);
361 if (rc != EOK) {
362 printf(NAME ": creation of volume \"%s\" failed: %s, "
363 "but continuing\n",
364 vol_configs[i].devname, str_error(rc));
365 } else {
366 printf(NAME ": volume \"%s\" successfully created\n",
367 vol_configs[i].devname);
368 }
369 }
370
371 free(vol_configs);
372 return EXIT_SUCCESS;
373}
374
375static int create_from_argv(hr_t *hr, int argc, char **argv, uint8_t vol_flags)
376{
377 /* we need name + --level + arg + at least one extent */
378 if (optind + 3 >= argc) {
379 printf(NAME ": not enough arguments\n");
380 return EXIT_FAILURE;
381 }
382
383 hr_config_t *vol_config = calloc(1, sizeof(hr_config_t));
384 if (vol_config == NULL) {
385 printf(NAME ": not enough memory\n");
386 return EXIT_FAILURE;
387 }
388
389 vol_config->vol_flags |= vol_flags;
390
391 const char *name = argv[optind++];
392 if (str_size(name) >= HR_DEVNAME_LEN) {
393 printf(NAME ": devname must be less then 32 bytes.\n");
394 goto error;
395 }
396
397 str_cpy(vol_config->devname, HR_DEVNAME_LEN, name);
398
399 const char *level_opt = argv[optind++];
400 if (str_cmp(level_opt, "--level") != 0 &&
401 str_cmp(level_opt, "-l") != 0) {
402 printf(NAME ": unknown option \"%s\"\n", level_opt);
403 goto error;
404 }
405
406 const char *level_str = argv[optind++];
407 if (str_size(level_str) == 1 && isdigit(level_str[0])) {
408 vol_config->level = strtol(level_str, NULL, 10);
409 } else {
410 if (str_cmp(level_str, "mirror") == 0 ||
411 str_cmp(level_str, "mirroring") == 0) {
412 vol_config->level = HR_LVL_1;
413 } else if (str_cmp(level_str, "stripe") == 0 ||
414 str_cmp(level_str, "striping") == 0) {
415 vol_config->level = HR_LVL_0;
416 } else if (str_cmp(level_str, "parity") == 0 ||
417 str_cmp(level_str, "parity_distributed") == 0) {
418 vol_config->level = HR_LVL_5;
419 } else if (str_cmp(level_str, "parity_dedicated") == 0) {
420 vol_config->level = HR_LVL_4;
421 } else {
422 printf(NAME ": unknown level \"%s\"\n", level_str);
423 goto error;
424 }
425 }
426
427 errno_t rc = fill_config_devs(argc, argv, vol_config);
428 if (rc != EOK)
429 goto error;
430
431 rc = hr_create(hr, vol_config);
432 if (rc != EOK) {
433 printf(NAME ": creation failed: %s\n", str_error(rc));
434 goto error;
435 } else {
436 printf(NAME ": volume \"%s\" successfully created\n",
437 vol_config->devname);
438 }
439
440 free(vol_config);
441 return EXIT_SUCCESS;
442error:
443 free(vol_config);
444 return EXIT_FAILURE;
445}
446
447static bool try_to_get_additional_flags(int argc, char **argv,
448 uint8_t test_flags, uint8_t *flags)
449{
450 if (test_flags & HR_VOL_FLAG_NOOP_META) {
451 if (str_cmp(argv[optind], "--no-meta") == 0) {
452 *flags |= HR_VOL_FLAG_NOOP_META;
453 optind++;
454 return true;
455 }
456 }
457
458 if (test_flags & HR_VOL_FLAG_READ_ONLY) {
459 if (str_cmp(argv[optind], "--read-only") == 0) {
460 *flags |= HR_VOL_FLAG_READ_ONLY;
461 optind++;
462 return true;
463 }
464 }
465
466 return false;
467}
468
469static int handle_create(hr_t *hr, int argc, char **argv)
470{
471 int rc;
472 uint8_t vflags = 0;
473
474 if (optind >= argc) {
475 printf(NAME ": no arguments to --create\n");
476 return EXIT_FAILURE;
477 }
478
479 uint8_t test_flags = HR_VOL_FLAG_NOOP_META | HR_VOL_FLAG_READ_ONLY;
480 while (try_to_get_additional_flags(argc, argv, test_flags, &vflags))
481 ;
482
483 if (str_cmp(argv[optind], "-f") == 0) {
484 optind++;
485 if (optind >= argc) {
486 printf(NAME ": not enough arguments\n");
487 return EXIT_FAILURE;
488 }
489
490 const char *config_path = argv[optind++];
491
492 if (optind < argc) {
493 printf(NAME ": unexpected arguments\n");
494 return EXIT_FAILURE;
495 }
496
497 rc = create_from_config(hr, config_path, vflags);
498 } else {
499 rc = create_from_argv(hr, argc, argv, vflags);
500 }
501
502 return rc;
503}
504
505static int assemble_from_config(hr_t *hr, const char *config_path,
506 uint8_t vflags)
507{
508 hr_config_t *vol_configs = NULL;
509 size_t vol_count = 0;
510 errno_t rc = get_vol_configs_from_sif(config_path, &vol_configs,
511 &vol_count);
512 if (rc != EOK) {
513 printf(NAME ": config parsing failed\n");
514 return EXIT_FAILURE;
515 }
516
517 size_t cnt = 0;
518 for (size_t i = 0; i < vol_count; i++) {
519 size_t tmpcnt = 0;
520 vol_configs[i].vol_flags = vflags;
521 (void)hr_assemble(hr, &vol_configs[i], &tmpcnt);
522 cnt += tmpcnt;
523 }
524
525 printf(NAME ": assembled %zu volumes\n", cnt);
526
527 free(vol_configs);
528 return EXIT_SUCCESS;
529}
530
531static int assemble_from_argv(hr_t *hr, int argc, char **argv, uint8_t vflags)
532{
533 hr_config_t *vol_config = calloc(1, sizeof(hr_config_t));
534 if (vol_config == NULL) {
535 printf(NAME ": not enough memory\n");
536 return ENOMEM;
537 }
538
539 vol_config->vol_flags = vflags;
540
541 errno_t rc = fill_config_devs(argc, argv, vol_config);
542 if (rc != EOK)
543 goto error;
544
545 size_t cnt;
546 rc = hr_assemble(hr, vol_config, &cnt);
547 if (rc != EOK) {
548 printf(NAME ": assmeble failed: %s\n", str_error(rc));
549 goto error;
550 }
551
552 printf("hrctl: assembled %zu volumes\n", cnt);
553
554 free(vol_config);
555 return EXIT_SUCCESS;
556error:
557 free(vol_config);
558 return EXIT_FAILURE;
559}
560
561static int handle_assemble(hr_t *hr, int argc, char **argv)
562{
563 int rc;
564
565 if (optind >= argc) {
566 size_t cnt;
567 errno_t rc = hr_auto_assemble(hr, &cnt);
568 if (rc != EOK) {
569 /* XXX: here have own error codes */
570 printf("hrctl: auto assemble rc: %s\n", str_error(rc));
571 return EXIT_FAILURE;
572 }
573
574 printf(NAME ": auto assembled %zu volumes\n", cnt);
575 return EXIT_SUCCESS;
576 }
577
578 uint8_t vflags = 0;
579 uint8_t test_flags = HR_VOL_FLAG_NOOP_META | HR_VOL_FLAG_READ_ONLY;
580 while (try_to_get_additional_flags(argc, argv, test_flags, &vflags))
581 ;
582
583 if (test_flags & HR_VOL_FLAG_NOOP_META)
584 printf(NAME ": assembling, --no-meta flag will be ignored\n");
585
586 if (str_cmp(argv[optind], "-f") == 0) {
587 if (++optind >= argc) {
588 printf(NAME ": not enough arguments\n");
589 return EXIT_FAILURE;
590 }
591 const char *config_path = argv[optind++];
592
593 if (optind < argc) {
594 printf(NAME ": unexpected arguments\n");
595 return EXIT_FAILURE;
596 }
597
598 rc = assemble_from_config(hr, config_path, vflags);
599 } else {
600 rc = assemble_from_argv(hr, argc, argv, vflags);
601 }
602
603 return rc;
604}
605
606static int handle_disassemble(hr_t *hr, int argc, char **argv)
607{
608 if (optind >= argc) {
609 errno_t rc = hr_stop_all(hr);
610 if (rc != EOK) {
611 printf(NAME ": stopping some volumes failed: %s\n",
612 str_error(rc));
613 return EXIT_FAILURE;
614 }
615 return EXIT_SUCCESS;
616 }
617
618 if (optind + 1 < argc) {
619 printf(NAME ": only 1 device can be manually specified\n");
620 return EXIT_FAILURE;
621 }
622
623 const char *devname = argv[optind++];
624
625 errno_t rc = hr_stop(hr, devname);
626 if (rc != EOK) {
627 printf(NAME ": disassembly of device \"%s\" failed: %s\n",
628 devname, str_error(rc));
629 return EXIT_FAILURE;
630 }
631
632 return EXIT_SUCCESS;
633}
634
635static int handle_modify(hr_t *hr, int argc, char **argv)
636{
637 if (optind >= argc) {
638 printf(NAME ": no arguments to --modify\n");
639 return EXIT_FAILURE;
640 }
641
642 const char *volname = argv[optind++];
643
644 /* at least 1 option and its agument */
645 if (optind + 1 >= argc) {
646 printf(NAME ": not enough arguments\n");
647 return EXIT_FAILURE;
648 }
649
650 if (optind + 2 < argc) {
651 printf(NAME ": unexpected arguments\n");
652 return EXIT_FAILURE;
653 }
654
655 if (str_cmp(argv[optind], "--fail") == 0 ||
656 str_cmp(argv[optind], "-f") == 0) {
657 optind++;
658 unsigned long extent = strtol(argv[optind++], NULL, 10);
659 errno_t rc = hr_fail_extent(hr, volname, extent);
660 if (rc != EOK) {
661 printf(NAME ": failing extent failed: %s\n",
662 str_error(rc));
663 return EXIT_FAILURE;
664 }
665 } else if (str_cmp(argv[optind], "--hotspare") == 0 ||
666 str_cmp(argv[optind], "-h") == 0) {
667 optind++;
668 errno_t rc = hr_add_hotspare(hr, volname, argv[optind++]);
669 if (rc != EOK) {
670 printf(NAME ": adding hotspare failed: %s\n",
671 str_error(rc));
672 return EXIT_FAILURE;
673 }
674 } else {
675 printf(NAME ": unknown argument\n");
676 return EXIT_FAILURE;
677 }
678
679 return EXIT_SUCCESS;
680}
681
682static errno_t print_vol_info(hr_vol_info_t *info)
683{
684 errno_t rc;
685 size_t i;
686 hr_extent_t *ext;
687 const char *devname;
688
689 printf("volume: '%s' (%" PRIun ")\n", info->devname, info->svc_id);
690
691 printf("| metadata type: %s\n",
692 hr_get_metadata_type_str(info->meta_type));
693
694 printf("| vflags: ");
695 for (size_t v = 0; v < HR_VOL_FLAG_COUNT; v++) {
696 if (info->vflags & (1 << v)) {
697 printf("%s ",
698 hr_get_vol_flag_str(info->vflags & (1 << v)));
699 }
700 }
701 printf("\n");
702
703 printf("| level: %s\n", hr_get_level_str(info->level));
704 if (info->layout != HR_LAYOUT_NONE)
705 printf("| layout: %s\n",
706 hr_get_layout_str(info->layout));
707
708 if (info->strip_size > 0) {
709 if (info->strip_size < 1024) {
710 printf("| strip size: %" PRIu32 " B\n",
711 info->strip_size);
712 } else {
713 printf("| strip size: %" PRIu32 " KiB\n",
714 info->strip_size / 1024);
715 }
716 }
717
718 printf("| no. of extents: %zu\n", info->extent_no);
719 printf("|no. of hotspares: %zu\n", info->hotspare_no);
720 printf("|number of blocks: %" PRIu64 "\n", info->data_blkno);
721 printf("| block size: %zu B\n", info->bsize);
722
723 capa_spec_t capa;
724 char *scapa = NULL;
725 capa_from_blocks(info->data_blkno, info->bsize, &capa);
726 capa_simplify(&capa);
727 rc = capa_format(&capa, &scapa);
728 if (rc != EOK) {
729 printf(NAME ": failed to format capacity: %s\n", str_error(rc));
730 return rc;
731 }
732
733 printf("| volume capacity: %s\n", scapa);
734
735 free(scapa);
736
737 printf("| state: %s", hr_get_vol_state_str(info->state));
738 if (info->state == HR_VOL_REBUILD) {
739 unsigned int percent =
740 (info->rebuild_blk * 100) / info->data_blkno;
741 printf(" (%u%% done)\n", percent);
742 } else {
743 printf("\n");
744 }
745
746 printf("| extents:\n");
747
748 for (i = 0; i < info->extent_no; i++) {
749 ext = &info->extents[i];
750 char *tmpname = NULL;
751 if (ext->state == HR_EXT_MISSING || ext->state == HR_EXT_NONE) {
752 devname = "MISSING";
753 } else {
754 rc = loc_service_get_name(ext->svc_id, &tmpname);
755 if (rc != EOK)
756 devname = "MISSING";
757 else
758 devname = tmpname;
759 }
760 printf("| %zu %s\n", i, hr_get_ext_state_str(ext->state));
761 printf("| %s\n", devname);
762 if (tmpname != NULL)
763 free(tmpname);
764 }
765
766 if (info->hotspare_no == 0)
767 return EOK;
768
769 printf("| hotspares:\n");
770 for (i = 0; i < info->hotspare_no; i++) {
771 ext = &info->hotspares[i];
772 char *tmpname;
773 if (ext->state == HR_EXT_MISSING || ext->state == HR_EXT_NONE) {
774 devname = "MISSING";
775 } else {
776 rc = loc_service_get_name(ext->svc_id, &tmpname);
777 if (rc != EOK)
778 devname = "MISSING";
779 else
780 devname = tmpname;
781 }
782 printf("| %zu %s\n", i, hr_get_ext_state_str(ext->state));
783 printf("| %s\n", devname);
784 if (tmpname != NULL)
785 free(tmpname);
786 }
787
788 return EOK;
789}
790
791static int handle_state(hr_t *hr, int argc, char **argv)
792{
793 errno_t rc;
794 size_t cnt;
795 hr_pair_vol_state_t *pairs = NULL;
796 char *devname;
797
798 /* print state of all volumes */
799 if (optind >= argc) {
800 rc = hr_get_vol_states(hr, &pairs, &cnt);
801 if (rc != EOK) {
802 printf(NAME ": failed getting state of volumes: %s\n",
803 str_error(rc));
804 return EXIT_FAILURE;
805 }
806
807 if (cnt == 0) {
808 printf(NAME ": no active volumes\n");
809 return EXIT_SUCCESS;
810 }
811
812 for (size_t i = 0; i < cnt; i++) {
813 service_id_t svc_id = pairs[i].svc_id;
814 hr_vol_state_t state = pairs[i].state;
815 rc = loc_service_get_name(svc_id, &devname);
816 if (rc != EOK) {
817 printf(NAME ": getting service name failed: "
818 "%s\n", str_error(rc));
819 return EXIT_FAILURE;
820 }
821 printf("volume '%s' (%" PRIun ") %s\n", devname,
822 svc_id, hr_get_vol_state_str(state));
823
824 free(devname);
825 }
826 free(pairs);
827
828 return EXIT_SUCCESS;
829 }
830
831 /* print volume info of requested volumes */
832 while (optind < argc) {
833 service_id_t svc_id;
834 devname = argv[optind++];
835 rc = loc_service_get_id(devname, &svc_id, 0);
836 if (rc != EOK) {
837 printf(NAME ": getting service id of \"%s\" failed: "
838 "%s\n", devname, str_error(rc));
839 return EXIT_FAILURE;
840 }
841
842 hr_vol_info_t info;
843 rc = hr_get_vol_info(hr, svc_id, &info);
844 if (rc != EOK) {
845 printf(NAME ": getting volume info failed: %s\n",
846 str_error(rc));
847 return EXIT_FAILURE;
848 }
849
850 rc = print_vol_info(&info);
851 if (rc != EOK) {
852 printf(NAME ": volume info printing failed: %s\n",
853 str_error(rc));
854 return EXIT_FAILURE;
855 }
856 }
857
858 return EXIT_SUCCESS;
859}
860
861/** @}
862 */
Note: See TracBrowser for help on using the repository browser.