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

Last change on this file since ed41962 was fde80323, checked in by Miroslav Cimerman <mc@…>, 10 months ago

hrctl: correctly handle manualy specified devices

  • Property mode set to 100644
File size: 12.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 <ctype.h>
37#include <errno.h>
38#include <getopt.h>
39#include <hr.h>
40#include <sif.h>
41#include <stdlib.h>
42#include <stdio.h>
43#include <str.h>
44#include <str_error.h>
45
46/* #define HRCTL_SAMPLE_CONFIG_PATH "/cfg/sample_hr_config.sif" */
47
48#define NAME "hrctl"
49
50static void usage(void);
51static errno_t fill_config_devs(int, char **, hr_config_t *);
52static errno_t load_config(const char *, hr_config_t *);
53
54static const char usage_str[] =
55 "Usage: hrctl [OPTION]...\n"
56 "\n"
57 "Options:\n"
58 " -h, --help Display this message and exit.\n"
59 "\n"
60 " -c, --create Create a volume, options:\n"
61 " name {-l , --level level} device... manual device specification, or\n"
62 " -f configuration.sif create from configuration file.\n"
63 "\n"
64 " -a, --assemble Assemble volume(s), options:\n"
65 " [device...] manual device specification, or\n"
66 " [-f configuration.sif] assemble from configuration file, or\n"
67 " no option is automatic assembly.\n"
68 "\n"
69 " -d, --disassemble Deactivate/disassemble, options:\n"
70 " [volume] specific volume, or\n"
71 " all volumes with no specified option.\n"
72 "\n"
73 " -m, --modify volume Modify a volume, options:\n"
74 " -f, --fail index fail an extent (DANGEROUS), or\n"
75 " -h, --hotspare device add hotspare.\n"
76 "\n"
77 " -s, --state Display state of active volumes.\n"
78 "\n"
79 "Example usage:\n"
80 "\t\thrctl --create hr0 --level 5 disk1 disk2 disk3\n"
81 "\t\thrctl -c hr0 -l 5 disk1 disk2 disk3\n"
82 "\t\thrctl -c -f cfg.sif\n"
83 "\t\thrctl --assemble disk1 disk2 disk3\n"
84 "\t\thrctl -a\n"
85 "\t\thrctl -d devices/hr0\n"
86 "\t\thrctl -d\n"
87 "\t\thrctl --modify devices/hr0 --fail 0\n"
88 "\t\thrctl --modify devices/hr0 --hotspare disk4\n"
89 "\t\thrctl -s\n"
90 "\n"
91 "Notes:\n"
92 " Volume service names are automatically prepended with \"devices/\" prefix.\n"
93 " Simulating an extent failure with -m volume -f index is dangerous. It marks\n"
94 " metadata as dirty in other healthy extents, and therefore invalidates\n"
95 " the specified extent.\n"
96 "\n"
97 "Limitations:\n"
98 "\t- volume name must be shorter than 32 characters\n";
99
100static void usage(void)
101{
102 printf("%s", usage_str);
103}
104
105static errno_t fill_config_devs(int argc, char **argv, hr_config_t *cfg)
106{
107 errno_t rc;
108 size_t i;
109
110 for (i = 0; i < HR_MAX_EXTENTS && optind < argc; i++) {
111 rc = loc_service_get_id(argv[optind], &cfg->devs[i], 0);
112 if (rc == ENOENT) {
113 printf(NAME ": device \"%s\" not found, aborting\n",
114 argv[optind]);
115 return ENOENT;
116 } else if (rc != EOK) {
117 printf(NAME ": error resolving device \"%s\", aborting\n",
118 argv[optind]);
119 return EINVAL;
120 }
121 optind++;
122 }
123
124 if (optind < argc) {
125 printf(NAME ": too many devices specified, max = %u\n",
126 HR_MAX_EXTENTS);
127 return ELIMIT;
128 }
129
130 cfg->dev_no = i;
131
132 return EOK;
133}
134
135static errno_t load_config(const char *path, hr_config_t *cfg)
136{
137 errno_t rc;
138 size_t i;
139 sif_doc_t *doc = NULL;
140 sif_node_t *narrays;
141 sif_node_t *rnode;
142 sif_node_t *narray;
143 sif_node_t *nextent;
144 const char *ntype;
145 const char *devname;
146 const char *level_str;
147 const char *dev_no_str;
148 const char *extent_devname;
149
150 rc = sif_load(path, &doc);
151 if (rc != EOK)
152 goto error;
153
154 rnode = sif_get_root(doc);
155
156 narrays = sif_node_first_child(rnode);
157 ntype = sif_node_get_type(narrays);
158 if (str_cmp(ntype, "arrays") != 0) {
159 rc = EIO;
160 goto error;
161 }
162
163 narray = sif_node_first_child(narrays);
164 ntype = sif_node_get_type(narray);
165 if (str_cmp(ntype, "array") != 0) {
166 rc = EIO;
167 goto error;
168 }
169
170 devname = sif_node_get_attr(narray, "devname");
171 if (devname == NULL) {
172 rc = EIO;
173 goto error;
174 }
175 str_cpy(cfg->devname, sizeof(cfg->devname), devname);
176
177 level_str = sif_node_get_attr(narray, "level");
178 if (level_str == NULL)
179 cfg->level = HR_LVL_UNKNOWN;
180 else
181 cfg->level = strtol(level_str, NULL, 10);
182
183 dev_no_str = sif_node_get_attr(narray, "n");
184 if (dev_no_str == NULL) {
185 rc = EIO;
186 goto error;
187 }
188 cfg->dev_no = strtol(dev_no_str, NULL, 10);
189
190 nextent = sif_node_first_child(narray);
191 for (i = 0; i < cfg->dev_no; i++) {
192 if (nextent == NULL) {
193 rc = EINVAL;
194 goto error;
195 }
196
197 ntype = sif_node_get_type(nextent);
198 if (str_cmp(ntype, "extent") != 0) {
199 rc = EIO;
200 goto error;
201 }
202
203 extent_devname = sif_node_get_attr(nextent, "devname");
204 if (extent_devname == NULL) {
205 rc = EIO;
206 goto error;
207 }
208
209 rc = loc_service_get_id(extent_devname, &cfg->devs[i], 0);
210 if (rc == ENOENT) {
211 printf("hrctl: no device \"%s\", marking as missing\n",
212 extent_devname);
213 cfg->devs[i] = 0;
214 rc = EOK;
215 } else if (rc != EOK) {
216 printf("hrctl: error resolving device \"%s\", aborting\n",
217 extent_devname);
218 return EINVAL;
219 }
220
221 nextent = sif_node_next_child(nextent);
222 }
223
224error:
225 if (doc != NULL)
226 sif_delete(doc);
227 return rc;
228}
229
230static int handle_create(hr_t *hr, int argc, char **argv)
231{
232 if (optind >= argc) {
233 printf(NAME ": no arguments to --create\n");
234 return EXIT_FAILURE;
235 }
236
237 hr_config_t *vol_config = calloc(1, sizeof(hr_config_t));
238 if (vol_config == NULL) {
239 printf(NAME ": not enough memory");
240 return ENOMEM;
241 }
242
243 if (str_cmp(argv[optind], "-f") == 0) {
244 if (++optind >= argc) {
245 printf(NAME ": not enough arguments\n");
246 goto error;
247 }
248 const char *config_path = argv[optind++];
249 errno_t rc = load_config(config_path, vol_config);
250 if (rc != EOK) {
251 printf(NAME ": config parsing failed\n");
252 goto error;
253 }
254 } else {
255 /* we need name + --level + arg + at least one extent */
256 if (optind + 3 >= argc) {
257 printf(NAME ": not enough arguments\n");
258 goto error;
259 }
260
261 const char *name = argv[optind++];
262 if (str_size(name) >= HR_DEVNAME_LEN) {
263 printf(NAME ": devname must be less then 32 bytes.\n");
264 goto error;
265 }
266
267 str_cpy(vol_config->devname, HR_DEVNAME_LEN, name);
268
269 const char *level_opt = argv[optind++];
270 if (str_cmp(level_opt, "--level") != 0 &&
271 str_cmp(level_opt, "-l") != 0) {
272 printf(NAME ": unknown option \"%s\"\n", level_opt);
273 goto error;
274 }
275
276 const char *level_str = argv[optind++];
277 if (str_size(level_str) != 1 && !isdigit(level_str[0])) {
278 printf(NAME ": unknown level \"%s\"\n", level_str);
279 goto error;
280 }
281
282 vol_config->level = strtol(level_str, NULL, 10);
283
284 errno_t rc = fill_config_devs(argc, argv, vol_config);
285 if (rc != EOK)
286 goto error;
287 }
288
289 if (optind < argc) {
290 printf(NAME ": unexpected arguments\n");
291 goto error;
292 }
293
294 errno_t rc = hr_create(hr, vol_config);
295 if (rc != EOK) {
296 printf(NAME ": creation failed: %s\n",
297 str_error(rc));
298 goto error;
299 }
300
301 free(vol_config);
302 return EXIT_SUCCESS;
303error:
304 free(vol_config);
305 return EXIT_FAILURE;
306}
307
308static int handle_assemble(hr_t *hr, int argc, char **argv)
309{
310 if (optind >= argc) {
311 size_t cnt;
312 errno_t rc = hr_auto_assemble(hr, &cnt);
313 if (rc != EOK) {
314 /* XXX: here have own error codes */
315 printf("hrctl: auto assemble rc: %s\n", str_error(rc));
316 return EXIT_FAILURE;
317 }
318
319 printf(NAME ": auto assembled %zu volumes\n", cnt);
320 return EXIT_SUCCESS;
321 }
322
323 hr_config_t *vol_config = calloc(1, sizeof(hr_config_t));
324 if (vol_config == NULL) {
325 printf(NAME ": not enough memory");
326 return ENOMEM;
327 }
328
329 if (str_cmp(argv[optind], "-f") == 0) {
330 if (++optind >= argc) {
331 printf(NAME ": not enough arguments\n");
332 goto error;
333 }
334 const char *config_path = argv[optind++];
335 errno_t rc = load_config(config_path, vol_config);
336 if (rc != EOK) {
337 printf(NAME ": config parsing failed\n");
338 goto error;
339 }
340 if (optind < argc) {
341 printf(NAME ": unexpected arguments\n");
342 goto error;
343 }
344 } else {
345 errno_t rc = fill_config_devs(argc, argv, vol_config);
346 if (rc != EOK)
347 goto error;
348 }
349
350 size_t cnt;
351 errno_t rc = hr_assemble(hr, vol_config, &cnt);
352 if (rc != EOK) {
353 printf(NAME ": assmeble failed: %s\n", str_error(rc));
354 goto error;
355 }
356
357 printf("hrctl: auto assembled %zu volumes\n", cnt);
358
359 free(vol_config);
360 return EXIT_SUCCESS;
361error:
362 free(vol_config);
363 return EXIT_FAILURE;
364}
365
366static int handle_disassemble(hr_t *hr, int argc, char **argv)
367{
368 if (optind >= argc) {
369 errno_t rc = hr_stop_all(hr);
370 if (rc != EOK) {
371 printf(NAME ": stopping some volumes failed: %s\n",
372 str_error(rc));
373 return EXIT_FAILURE;
374 }
375 return EXIT_SUCCESS;
376 }
377
378 if (optind + 1 < argc) {
379 printf(NAME ": only 1 device can be manually specified\n");
380 return EXIT_FAILURE;
381 }
382
383 const char *devname = argv[optind++];
384
385 errno_t rc = hr_stop(hr, devname);
386 if (rc != EOK) {
387 printf(NAME ": disassembly of device \"%s\" failed: %s\n",
388 devname, str_error(rc));
389 return EXIT_FAILURE;
390 }
391
392 return EXIT_SUCCESS;
393}
394
395static int handle_modify(hr_t *hr, int argc, char **argv)
396{
397 if (optind >= argc) {
398 printf(NAME ": no arguments to --modify\n");
399 return EXIT_FAILURE;
400 }
401
402 const char *volname = argv[optind++];
403
404 /* at least 1 option and its agument */
405 if (optind + 1 >= argc) {
406 printf(NAME ": not enough arguments\n");
407 return EXIT_FAILURE;
408 }
409
410 if (optind + 2 < argc) {
411 printf(NAME ": unexpected arguments\n");
412 return EXIT_FAILURE;
413 }
414
415 if (str_cmp(argv[optind], "--fail") == 0 ||
416 str_cmp(argv[optind], "-f") == 0) {
417 optind++;
418 unsigned long extent = strtol(argv[optind++], NULL, 10);
419 errno_t rc = hr_fail_extent(hr, volname, extent);
420 if (rc != EOK) {
421 printf(NAME ": failing extent failed: %s\n",
422 str_error(rc));
423 return EXIT_FAILURE;
424 }
425 } else if (str_cmp(argv[optind], "--hotspare") == 0 ||
426 str_cmp(argv[optind], "-h") == 0) {
427 optind++;
428 errno_t rc = hr_add_hotspare(hr, volname, argv[optind++]);
429 if (rc != EOK) {
430 printf(NAME ": adding hotspare failed: %s\n",
431 str_error(rc));
432 return EXIT_FAILURE;
433 }
434 } else {
435 printf(NAME ": unknown argument\n");
436 return EXIT_FAILURE;
437 }
438
439 return EXIT_SUCCESS;
440}
441
442static int handle_state(hr_t *hr, int argc, char **argv)
443{
444 (void)argc;
445 (void)argv;
446
447 errno_t rc = hr_print_state(hr);
448 if (rc != EOK) {
449 printf(NAME ": state printing failed: %s\n", str_error(rc));
450 return EXIT_FAILURE;
451 }
452
453 return EXIT_SUCCESS;
454}
455
456int main(int argc, char **argv)
457{
458 int rc = EXIT_SUCCESS;
459 hr_t *hr = NULL;
460
461 if (argc < 2) {
462 rc = EXIT_FAILURE;
463 goto end;
464 }
465
466 if (hr_sess_init(&hr) != EOK) {
467 printf(NAME ": hr server session init failed: %s\n",
468 str_error(rc));
469 return EXIT_FAILURE;
470 }
471
472 optreset = 1;
473 optind = 0;
474
475 struct option const top_level_opts[] = {
476 { "help", no_argument, 0, 'h' },
477 { "create", no_argument, 0, 'c' },
478 { "assemble", no_argument, 0, 'a' },
479 { "disassemble", no_argument, 0, 'd' },
480 { "modify", no_argument, 0, 'm' },
481 { "state", no_argument, 0, 's' },
482 { 0, 0, 0, 0 }
483 };
484
485 int c = getopt_long(argc, argv, "hcadms", top_level_opts, NULL);
486 switch (c) {
487 case 'h':
488 usage();
489 goto end;
490 case 'c':
491 rc = handle_create(hr, argc, argv);
492 goto end;
493 case 'a':
494 rc = handle_assemble(hr, argc, argv);
495 goto end;
496 case 'd':
497 rc = handle_disassemble(hr, argc, argv);
498 goto end;
499 case 'm':
500 rc = handle_modify(hr, argc, argv);
501 goto end;
502 case 's':
503 rc = handle_state(hr, argc, argv);
504 goto end;
505 default:
506 goto end;
507 }
508
509end:
510 hr_sess_destroy(hr);
511
512 if (rc != EXIT_SUCCESS)
513 printf(NAME ": use --help to see usage\n");
514 return rc;
515}
516
517/** @}
518 */
Note: See TracBrowser for help on using the repository browser.