source: mainline/uspace/app/hrctl/hrctl.c@ 640250b

Last change on this file since 640250b was 150adbd2, checked in by Miroslav Cimerman <mc@…>, 6 weeks ago

hr: add NOOP metadata type

  • Property mode set to 100644
File size: 20.7 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 *);
58static int assemble_from_argv(hr_t *, int, char **);
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] 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 " [device...] manual device specification, or\n"
78 " [-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 devices/hr0\n"
104 "\t\thrctl -d\n"
105 "\t\thrctl --modify devices/hr0 --fail 0\n"
106 "\t\thrctl --modify devices/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 int handle_create(hr_t *hr, int argc, char **argv)
448{
449 int rc;
450 uint8_t vol_flags = 0;
451
452 if (optind >= argc) {
453 printf(NAME ": no arguments to --create\n");
454 return EXIT_FAILURE;
455 }
456
457 if (str_cmp(argv[optind], "--no_meta") == 0) {
458 vol_flags |= HR_VOL_FLAG_NOOP_META;
459 optind++;
460 }
461
462 if (str_cmp(argv[optind], "-f") == 0) {
463 optind++;
464 if (optind >= argc) {
465 printf(NAME ": not enough arguments\n");
466 return EXIT_FAILURE;
467 }
468
469 const char *config_path = argv[optind++];
470
471 if (optind < argc) {
472 printf(NAME ": unexpected arguments\n");
473 return EXIT_FAILURE;
474 }
475
476 rc = create_from_config(hr, config_path, vol_flags);
477 } else {
478 rc = create_from_argv(hr, argc, argv, vol_flags);
479 }
480
481 return rc;
482}
483
484static int assemble_from_config(hr_t *hr, const char *config_path)
485{
486 hr_config_t *vol_configs = NULL;
487 size_t vol_count = 0;
488 errno_t rc = get_vol_configs_from_sif(config_path, &vol_configs,
489 &vol_count);
490 if (rc != EOK) {
491 printf(NAME ": config parsing failed\n");
492 return EXIT_FAILURE;
493 }
494
495 size_t cnt = 0;
496 for (size_t i = 0; i < vol_count; i++) {
497 size_t tmpcnt = 0;
498 (void)hr_assemble(hr, &vol_configs[i], &tmpcnt);
499 cnt += tmpcnt;
500 }
501
502 printf(NAME ": assembled %zu volumes\n", cnt);
503
504 free(vol_configs);
505 return EXIT_SUCCESS;
506}
507
508static int assemble_from_argv(hr_t *hr, int argc, char **argv)
509{
510 hr_config_t *vol_config = calloc(1, sizeof(hr_config_t));
511 if (vol_config == NULL) {
512 printf(NAME ": not enough memory\n");
513 return ENOMEM;
514 }
515
516 errno_t rc = fill_config_devs(argc, argv, vol_config);
517 if (rc != EOK)
518 goto error;
519
520 size_t cnt;
521 rc = hr_assemble(hr, vol_config, &cnt);
522 if (rc != EOK) {
523 printf(NAME ": assmeble failed: %s\n", str_error(rc));
524 goto error;
525 }
526
527 printf("hrctl: assembled %zu volumes\n", cnt);
528
529 free(vol_config);
530 return EXIT_SUCCESS;
531error:
532 free(vol_config);
533 return EXIT_FAILURE;
534}
535
536static int handle_assemble(hr_t *hr, int argc, char **argv)
537{
538 int rc;
539
540 if (optind >= argc) {
541 size_t cnt;
542 errno_t rc = hr_auto_assemble(hr, &cnt);
543 if (rc != EOK) {
544 /* XXX: here have own error codes */
545 printf("hrctl: auto assemble rc: %s\n", str_error(rc));
546 return EXIT_FAILURE;
547 }
548
549 printf(NAME ": auto assembled %zu volumes\n", cnt);
550 return EXIT_SUCCESS;
551 }
552
553 if (str_cmp(argv[optind], "-f") == 0) {
554 if (++optind >= argc) {
555 printf(NAME ": not enough arguments\n");
556 return EXIT_FAILURE;
557 }
558 const char *config_path = argv[optind++];
559
560 if (optind < argc) {
561 printf(NAME ": unexpected arguments\n");
562 return EXIT_FAILURE;
563 }
564
565 rc = assemble_from_config(hr, config_path);
566 } else {
567 rc = assemble_from_argv(hr, argc, argv);
568 }
569
570 return rc;
571}
572
573static int handle_disassemble(hr_t *hr, int argc, char **argv)
574{
575 if (optind >= argc) {
576 errno_t rc = hr_stop_all(hr);
577 if (rc != EOK) {
578 printf(NAME ": stopping some volumes failed: %s\n",
579 str_error(rc));
580 return EXIT_FAILURE;
581 }
582 return EXIT_SUCCESS;
583 }
584
585 if (optind + 1 < argc) {
586 printf(NAME ": only 1 device can be manually specified\n");
587 return EXIT_FAILURE;
588 }
589
590 const char *devname = argv[optind++];
591
592 errno_t rc = hr_stop(hr, devname);
593 if (rc != EOK) {
594 printf(NAME ": disassembly of device \"%s\" failed: %s\n",
595 devname, str_error(rc));
596 return EXIT_FAILURE;
597 }
598
599 return EXIT_SUCCESS;
600}
601
602static int handle_modify(hr_t *hr, int argc, char **argv)
603{
604 if (optind >= argc) {
605 printf(NAME ": no arguments to --modify\n");
606 return EXIT_FAILURE;
607 }
608
609 const char *volname = argv[optind++];
610
611 /* at least 1 option and its agument */
612 if (optind + 1 >= argc) {
613 printf(NAME ": not enough arguments\n");
614 return EXIT_FAILURE;
615 }
616
617 if (optind + 2 < argc) {
618 printf(NAME ": unexpected arguments\n");
619 return EXIT_FAILURE;
620 }
621
622 if (str_cmp(argv[optind], "--fail") == 0 ||
623 str_cmp(argv[optind], "-f") == 0) {
624 optind++;
625 unsigned long extent = strtol(argv[optind++], NULL, 10);
626 errno_t rc = hr_fail_extent(hr, volname, extent);
627 if (rc != EOK) {
628 printf(NAME ": failing extent failed: %s\n",
629 str_error(rc));
630 return EXIT_FAILURE;
631 }
632 } else if (str_cmp(argv[optind], "--hotspare") == 0 ||
633 str_cmp(argv[optind], "-h") == 0) {
634 optind++;
635 errno_t rc = hr_add_hotspare(hr, volname, argv[optind++]);
636 if (rc != EOK) {
637 printf(NAME ": adding hotspare failed: %s\n",
638 str_error(rc));
639 return EXIT_FAILURE;
640 }
641 } else {
642 printf(NAME ": unknown argument\n");
643 return EXIT_FAILURE;
644 }
645
646 return EXIT_SUCCESS;
647}
648
649static errno_t print_vol_info(hr_vol_info_t *info)
650{
651 errno_t rc;
652 size_t i;
653 hr_extent_t *ext;
654 const char *devname;
655
656 printf("volume: '%s' (%" PRIun ")\n", info->devname, info->svc_id);
657
658 printf("| metadata type: %s\n",
659 hr_get_metadata_type_str(info->meta_type));
660 printf("| level: %s\n", hr_get_level_str(info->level));
661 if (info->layout != HR_LAYOUT_NONE)
662 printf("| layout: %s\n",
663 hr_get_layout_str(info->layout));
664
665 if (info->strip_size > 0) {
666 if (info->strip_size < 1024) {
667 printf("| strip size: %" PRIu32 " B\n",
668 info->strip_size);
669 } else {
670 printf("| strip size: %" PRIu32 " KiB\n",
671 info->strip_size / 1024);
672 }
673 }
674
675 printf("| no. of extents: %zu\n", info->extent_no);
676 printf("|no. of hotspares: %zu\n", info->hotspare_no);
677 printf("|number of blocks: %" PRIu64 "\n", info->data_blkno);
678 printf("| block size: %zu B\n", info->bsize);
679
680 capa_spec_t capa;
681 char *scapa = NULL;
682 capa_from_blocks(info->data_blkno, info->bsize, &capa);
683 capa_simplify(&capa);
684 rc = capa_format(&capa, &scapa);
685 if (rc != EOK) {
686 printf(NAME ": failed to format capacity: %s\n", str_error(rc));
687 return rc;
688 }
689
690 printf("| volume capacity: %s\n", scapa);
691
692 free(scapa);
693
694 printf("| state: %s", hr_get_vol_state_str(info->state));
695 if (info->state == HR_VOL_REBUILD) {
696 unsigned int percent =
697 (info->rebuild_blk * 100) / info->data_blkno;
698 printf(" (%u%% done)\n", percent);
699 } else {
700 printf("\n");
701 }
702
703 printf("| extents:\n");
704
705 for (i = 0; i < info->extent_no; i++) {
706 ext = &info->extents[i];
707 char *tmpname = NULL;
708 if (ext->state == HR_EXT_MISSING || ext->state == HR_EXT_NONE) {
709 devname = "MISSING";
710 } else {
711 rc = loc_service_get_name(ext->svc_id, &tmpname);
712 if (rc != EOK)
713 devname = "MISSING";
714 else
715 devname = tmpname;
716 }
717 printf("| %zu %s\n", i, hr_get_ext_state_str(ext->state));
718 printf("| %s\n", devname);
719 if (tmpname != NULL)
720 free(tmpname);
721 }
722
723 if (info->hotspare_no == 0)
724 return EOK;
725
726 printf("| hotspares:\n");
727 for (i = 0; i < info->hotspare_no; i++) {
728 ext = &info->hotspares[i];
729 char *tmpname;
730 if (ext->state == HR_EXT_MISSING || ext->state == HR_EXT_NONE) {
731 devname = "MISSING";
732 } else {
733 rc = loc_service_get_name(ext->svc_id, &tmpname);
734 if (rc != EOK)
735 devname = "MISSING";
736 else
737 devname = tmpname;
738 }
739 printf("| %zu %s\n", i, hr_get_ext_state_str(ext->state));
740 printf("| %s\n", devname);
741 if (tmpname != NULL)
742 free(tmpname);
743 }
744
745 return EOK;
746}
747
748static int handle_state(hr_t *hr, int argc, char **argv)
749{
750 errno_t rc;
751 size_t cnt;
752 hr_pair_vol_state_t *pairs = NULL;
753 char *devname;
754
755 /* print state of all volumes */
756 if (optind >= argc) {
757 rc = hr_get_vol_states(hr, &pairs, &cnt);
758 if (rc != EOK) {
759 printf(NAME ": failed getting state of volumes: %s\n",
760 str_error(rc));
761 return EXIT_FAILURE;
762 }
763
764 if (cnt == 0) {
765 printf(NAME ": no active volumes\n");
766 return EXIT_SUCCESS;
767 }
768
769 for (size_t i = 0; i < cnt; i++) {
770 service_id_t svc_id = pairs[i].svc_id;
771 hr_vol_state_t state = pairs[i].state;
772 rc = loc_service_get_name(svc_id, &devname);
773 if (rc != EOK) {
774 printf(NAME ": getting service name failed: "
775 "%s\n", str_error(rc));
776 return EXIT_FAILURE;
777 }
778 printf("volume '%s' (%" PRIun ") %s\n", devname,
779 svc_id, hr_get_vol_state_str(state));
780
781 free(devname);
782 }
783 free(pairs);
784
785 return EXIT_SUCCESS;
786 }
787
788 /* print volume info of requested volumes */
789 while (optind < argc) {
790 service_id_t svc_id;
791 devname = argv[optind++];
792 rc = loc_service_get_id(devname, &svc_id, 0);
793 if (rc != EOK) {
794 printf(NAME ": getting service id of \"%s\" failed: "
795 "%s\n", devname, str_error(rc));
796 return EXIT_FAILURE;
797 }
798
799 hr_vol_info_t info;
800 rc = hr_get_vol_info(hr, svc_id, &info);
801 if (rc != EOK) {
802 printf(NAME ": getting volume info failed: %s\n",
803 str_error(rc));
804 return EXIT_FAILURE;
805 }
806
807 rc = print_vol_info(&info);
808 if (rc != EOK) {
809 printf(NAME ": volume info printing failed: %s\n",
810 str_error(rc));
811 return EXIT_FAILURE;
812 }
813 }
814
815 return EXIT_SUCCESS;
816}
817
818/** @}
819 */
Note: See TracBrowser for help on using the repository browser.