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

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

hr: use <inttypes.h> macro specifiers

  • Property mode set to 100644
File size: 11.1 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 <errno.h>
37#include <getopt.h>
38#include <hr.h>
39#include <sif.h>
40#include <stdlib.h>
41#include <stdio.h>
42#include <str.h>
43#include <str_error.h>
44
45#define HRCTL_SAMPLE_CONFIG_PATH "/cfg/sample_hr_config.sif"
46
47static void usage(void);
48static errno_t fill_config_devs(int, char **, int, hr_config_t *);
49static errno_t load_config(const char *, hr_config_t *);
50
51static const char usage_str[] =
52 "Usage: hrctl [OPTION]... -n <dev_no> <devices>...\n"
53 "\n"
54 "Options:\n"
55 " -h, --help display this message and exit\n"
56 " -C, --create-file=PATH create an array from file,\n"
57 " sample file at: " HRCTL_SAMPLE_CONFIG_PATH "\n"
58 " -A, --auto-assemble try to auto assemble all valid arrays\n"
59 " -s, --status display status of active arrays\n"
60 " -H, --hotspare=DEV add hotspare extent\n"
61 " -D, --destroy destroy/disassemble an active array\n"
62 " -F, --fail-extent fail an extent, use with -D and set it before\n"
63 " -c, --create=NAME create new array\n"
64 " -a, --assemble try to assemble from specified extents\n"
65 " -n non-zero number of devices\n"
66 " -l, --level=LEVEL set the RAID level,\n"
67 " valid values: 0, 1, 4, 5\n"
68 " -0 striping\n"
69 " -1 mirroring\n"
70 " -4 parity on one extent\n"
71 " -5 distributed parity\n"
72 "\n"
73 "When specifying name for creation or assembly, the device name\n"
74 "is automatically prepended with \"devices/\" prefix.\n"
75 "\n"
76 "Example usage:\n"
77 " hrctl --create hr0 -0 -n 2 devices/\\hw\\0 devices/\\hw\\1\n"
78 " - creates new mirroring RAID device named /hr0 consisting\n"
79 " of 2 drives\n"
80 " hrctl --assemble -n 2 devices/\\hw\\0 devices/\\hw\\1\n"
81 " - assembles RAID devices from specified extents,\n"
82 " that were previously in an (active) array\n"
83 " hrctl devices/hr0 --hotspare=devices/disk10\n"
84 " - adds \"devices/disk10\" as hotspare extent\n"
85 " hrctl -F 0 -D devices/hr0\n"
86 " - marks first extent as FAILED\n"
87 "Limitations:\n"
88 " - device name must be less than 32 characters in size\n";
89
90static struct option const long_options[] = {
91 { "help", no_argument, 0, 'h' },
92 { "status", no_argument, 0, 's' },
93 { "assemble", no_argument, 0, 'a' },
94 { "create", required_argument, 0, 'c' },
95 { "level", required_argument, 0, 'l' },
96 { "create-file", required_argument, 0, 'C' },
97 { "auto-assemble", no_argument, 0, 'A' },
98 { "destroy", required_argument, 0, 'D' },
99 { "fail-extent", required_argument, 0, 'F' },
100 { "hotspare", required_argument, 0, 'H' },
101 { 0, 0, 0, 0 }
102};
103
104static void usage(void)
105{
106 printf("%s", usage_str);
107}
108
109static errno_t fill_config_devs(int argc, char **argv, int optind,
110 hr_config_t *cfg)
111{
112 errno_t rc;
113 size_t i;
114
115 for (i = 0; i < cfg->dev_no; i++) {
116 rc = loc_service_get_id(argv[optind], &cfg->devs[i], 0);
117 if (rc == ENOENT) {
118 printf("hrctl: no device \"%s\", marking as missing\n",
119 argv[optind]);
120 cfg->devs[i] = 0;
121 } else if (rc != EOK) {
122 printf("hrctl: error resolving device \"%s\", aborting\n",
123 argv[optind]);
124 return EINVAL;
125 }
126
127 optind++;
128 }
129
130 return EOK;
131}
132
133static errno_t load_config(const char *path, hr_config_t *cfg)
134{
135 errno_t rc;
136 size_t i;
137 sif_doc_t *doc = NULL;
138 sif_node_t *narrays;
139 sif_node_t *rnode;
140 sif_node_t *narray;
141 sif_node_t *nextent;
142 const char *ntype;
143 const char *devname;
144 const char *level_str;
145 const char *dev_no_str;
146 const char *extent_devname;
147
148 rc = sif_load(path, &doc);
149 if (rc != EOK)
150 goto error;
151
152 rnode = sif_get_root(doc);
153
154 narrays = sif_node_first_child(rnode);
155 ntype = sif_node_get_type(narrays);
156 if (str_cmp(ntype, "arrays") != 0) {
157 rc = EIO;
158 goto error;
159 }
160
161 narray = sif_node_first_child(narrays);
162 ntype = sif_node_get_type(narray);
163 if (str_cmp(ntype, "array") != 0) {
164 rc = EIO;
165 goto error;
166 }
167
168 devname = sif_node_get_attr(narray, "devname");
169 if (devname == NULL) {
170 rc = EIO;
171 goto error;
172 }
173 str_cpy(cfg->devname, sizeof(cfg->devname), devname);
174
175 level_str = sif_node_get_attr(narray, "level");
176 if (level_str == NULL)
177 cfg->level = HR_LVL_UNKNOWN;
178 else
179 cfg->level = strtol(level_str, NULL, 10);
180
181 dev_no_str = sif_node_get_attr(narray, "n");
182 if (dev_no_str == NULL) {
183 rc = EIO;
184 goto error;
185 }
186 cfg->dev_no = strtol(dev_no_str, NULL, 10);
187
188 nextent = sif_node_first_child(narray);
189 for (i = 0; i < cfg->dev_no; i++) {
190 if (nextent == NULL) {
191 rc = EINVAL;
192 goto error;
193 }
194
195 ntype = sif_node_get_type(nextent);
196 if (str_cmp(ntype, "extent") != 0) {
197 rc = EIO;
198 goto error;
199 }
200
201 extent_devname = sif_node_get_attr(nextent, "devname");
202 if (extent_devname == NULL) {
203 rc = EIO;
204 goto error;
205 }
206
207 rc = loc_service_get_id(extent_devname, &cfg->devs[i], 0);
208 if (rc == ENOENT) {
209 printf("hrctl: no device \"%s\", marking as missing\n",
210 extent_devname);
211 cfg->devs[i] = 0;
212 rc = EOK;
213 } else if (rc != EOK) {
214 printf("hrctl: error resolving device \"%s\", aborting\n",
215 extent_devname);
216 return EINVAL;
217 }
218
219 nextent = sif_node_next_child(nextent);
220 }
221
222error:
223 if (doc != NULL)
224 sif_delete(doc);
225 return rc;
226}
227
228int main(int argc, char **argv)
229{
230 errno_t rc;
231 int retval, c;
232 bool create, assemble;
233 long fail_extent = -1;
234 hr_t *hr = NULL;
235 hr_config_t *cfg;
236
237 cfg = calloc(1, sizeof(hr_config_t));
238 if (cfg == NULL)
239 return 1;
240
241 retval = 0;
242 cfg->level = HR_LVL_UNKNOWN;
243 cfg->dev_no = 0;
244 create = assemble = false;
245
246 if (argc < 2) {
247 goto bad;
248 }
249
250 c = 0;
251 optreset = 1;
252 optind = 0;
253
254 while (c != -1) {
255 c = getopt_long(argc, argv, "hsC:c:Aal:0145Ln:D:F:H:",
256 long_options, NULL);
257 switch (c) {
258 case 's':
259 free(cfg);
260 rc = hr_print_status();
261 if (rc != EOK)
262 return 1;
263 return 0;
264 case 'C':
265 /* only support 1 array inside config for now XXX */
266 rc = load_config(optarg, cfg);
267 if (rc != EOK) {
268 printf("hrctl: failed to load config\n");
269 free(cfg);
270 return 1;
271 }
272 create = true;
273 goto skip;
274 case 'c':
275 if (str_size(optarg) > sizeof(cfg->devname) - 1) {
276 printf("hrctl: device name too long\n");
277 free(cfg);
278 return 1;
279 }
280 str_cpy(cfg->devname, sizeof(cfg->devname), optarg);
281 create = true;
282 break;
283 case 'A':
284 size_t cnt;
285 rc = hr_auto_assemble(&cnt);
286 if (rc != EOK) {
287 /* XXX: here have own error codes */
288 printf("hrctl: auto assemble rc: %s\n",
289 str_error(rc));
290 } else {
291 printf("hrctl: auto assembled %zu volumes\n",
292 cnt);
293 }
294 return rc;
295 case 'a':
296 /*
297 * XXX: redo this, no devname, only svc ids...
298 *
299 * find some other way to rename the arrays...,
300 *
301 * some metadata editation tool... something like
302 * fdisk but can only change devnames on inactive
303 * extents...
304 */
305
306 /*
307 * XXX: remake whole parsing, don't allow -a
308 * to have any levels set or anything
309 */
310 assemble = true;
311 break;
312 case 'D':
313 rc = hr_stop(optarg, fail_extent);
314 free(cfg);
315 if (rc != EOK) {
316 printf("hrctl: got %s\n", str_error(rc));
317 return rc;
318 }
319 return 0;
320 case 'F':
321 fail_extent = strtol(optarg, NULL, 10);
322 break;
323 case 'l':
324 if (cfg->level != HR_LVL_UNKNOWN)
325 goto bad;
326 cfg->level = strtol(optarg, NULL, 10);
327 break;
328 case '0':
329 if (cfg->level != HR_LVL_UNKNOWN)
330 goto bad;
331 cfg->level = HR_LVL_0;
332 break;
333 case '1':
334 if (cfg->level != HR_LVL_UNKNOWN)
335 goto bad;
336 cfg->level = HR_LVL_1;
337 break;
338 case '4':
339 if (cfg->level != HR_LVL_UNKNOWN)
340 goto bad;
341 cfg->level = HR_LVL_4;
342 break;
343 case '5':
344 if (cfg->level != HR_LVL_UNKNOWN)
345 goto bad;
346 cfg->level = HR_LVL_5;
347 break;
348 case 'n':
349 cfg->dev_no = strtol(optarg, NULL, 10);
350 if ((int)cfg->dev_no + optind != argc)
351 goto bad;
352 rc = fill_config_devs(argc, argv, optind, cfg);
353 if (rc != EOK) {
354 free(cfg);
355 return 1;
356 }
357 goto skip;
358 case 'H':
359 if (optind != 3 && argc != 4)
360 goto bad;
361
362 service_id_t hotspare;
363 service_id_t vol_svc_id;
364
365 rc = loc_service_get_id(argv[1], &vol_svc_id, 0);
366 if (rc != EOK) {
367 printf("hrctl: error resolving volume \"%s\", "
368 "aborting extent addition\n", argv[1]);
369 goto bad;
370 }
371
372 rc = loc_service_get_id(optarg, &hotspare, 0);
373 if (rc != EOK) {
374 printf("hrctl: error resolving device \"%s\", "
375 "aborting extent addition\n", optarg);
376 goto bad;
377 }
378
379 rc = hr_add_hotspare(vol_svc_id, hotspare);
380 if (rc != EOK)
381 printf("hrctl: hr_add_hotspare() rc: %s\n",
382 str_error(rc));
383
384 free(cfg);
385 if (rc != EOK)
386 return 1;
387 else
388 return 0;
389 case 'h':
390 default:
391 usage();
392 free(cfg);
393 return 0;
394 }
395 }
396
397skip:
398 if ((create && assemble) || (!create && !assemble))
399 goto bad;
400
401 if (create && cfg->level == HR_LVL_UNKNOWN) {
402 printf("hrctl: invalid level, exiting\n");
403 goto bad;
404 }
405
406 if (cfg->dev_no > HR_MAX_EXTENTS) {
407 printf("hrctl: too many devices, exiting\n");
408 goto bad;
409 }
410
411 if (cfg->dev_no == 0) {
412 printf("hrctl: invalid number of devices, exiting\n");
413 goto bad;
414 }
415
416 rc = hr_sess_init(&hr);
417 if (rc != EOK) {
418 printf("hrctl: hr_sess_init() rc: %s\n", str_error(rc));
419 retval = 1;
420 goto end;
421 }
422
423 if (create) {
424 rc = hr_create(hr, cfg);
425 printf("hrctl: hr_create() rc: %s\n", str_error(rc));
426 } else if (assemble) {
427 size_t assembled_cnt = 0;
428 rc = hr_assemble(hr, cfg, &assembled_cnt);
429 if (rc != EOK) {
430 /* XXX: here have own error codes */
431 printf("hrctl: auto assemble rc: %s\n", str_error(rc));
432 } else {
433 printf("hrctl: auto assembled %zu volumes\n",
434 assembled_cnt);
435 }
436
437 }
438
439end:
440 free(cfg);
441 hr_sess_destroy(hr);
442 return retval;
443bad:
444 free(cfg);
445 printf("hrctl: bad usage, try hrctl --help\n");
446 return 1;
447}
448
449/** @}
450 */
Note: See TracBrowser for help on using the repository browser.