source: mainline/uspace/app/hrctl/hrctl.c@ 8b51009

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

hr: auto assembly, refactor

Added automatic assembly (with hrctl -A). All disks or their partitions
are scanned for HelenRAID metadata and assembly is attempted.

Main volume list is now locked with RW lock. The volume list
manipulation functions are moved into util.c.

hr_{create,destroy}_vol_struct() are implemented for better reusability
and modularity.

Volume destroy/stop (hrctl -D) now returns EBUSY if someone has still
the volume open()-ed.

  • Property mode set to 100644
File size: 10.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 <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=NAME 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 hr0 -n 2 devices/\\hw\\0 devices/\\hw\\1\n"
81 " - assembles RAID device named /hr0 consisting of 2 drives,\n"
82 " that were previously in an 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", required_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:Aa:l: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 %lu volumes\n",
292 cnt);
293 }
294 return rc;
295 case 'a':
296 if (str_size(optarg) > sizeof(cfg->devname) - 1) {
297 printf("hrctl: device name too long\n");
298 free(cfg);
299 return 1;
300 }
301 str_cpy(cfg->devname, sizeof(cfg->devname), optarg);
302 assemble = true;
303 break;
304 case 'D':
305 rc = hr_stop(optarg, fail_extent);
306 free(cfg);
307 if (rc != EOK) {
308 printf("hrctl: got %s\n", str_error(rc));
309 return rc;
310 }
311 return 0;
312 case 'F':
313 fail_extent = strtol(optarg, NULL, 10);
314 break;
315 case 'l':
316 if (cfg->level != HR_LVL_UNKNOWN)
317 goto bad;
318 cfg->level = strtol(optarg, NULL, 10);
319 break;
320 case '0':
321 if (cfg->level != HR_LVL_UNKNOWN)
322 goto bad;
323 cfg->level = HR_LVL_0;
324 break;
325 case '1':
326 if (cfg->level != HR_LVL_UNKNOWN)
327 goto bad;
328 cfg->level = HR_LVL_1;
329 break;
330 case '4':
331 if (cfg->level != HR_LVL_UNKNOWN)
332 goto bad;
333 cfg->level = HR_LVL_4;
334 break;
335 case '5':
336 if (cfg->level != HR_LVL_UNKNOWN)
337 goto bad;
338 cfg->level = HR_LVL_5;
339 break;
340 case 'n':
341 cfg->dev_no = strtol(optarg, NULL, 10);
342 if ((int)cfg->dev_no + optind != argc)
343 goto bad;
344 rc = fill_config_devs(argc, argv, optind, cfg);
345 if (rc != EOK) {
346 free(cfg);
347 return 1;
348 }
349 goto skip;
350 case 'H':
351 if (optind != 3 && argc != 4)
352 goto bad;
353
354 service_id_t hotspare;
355 service_id_t vol_svc_id;
356
357 rc = loc_service_get_id(argv[1], &vol_svc_id, 0);
358 if (rc != EOK) {
359 printf("hrctl: error resolving volume \"%s\", "
360 "aborting extent addition\n", argv[1]);
361 goto bad;
362 }
363
364 rc = loc_service_get_id(optarg, &hotspare, 0);
365 if (rc != EOK) {
366 printf("hrctl: error resolving device \"%s\", "
367 "aborting extent addition\n", optarg);
368 goto bad;
369 }
370
371 rc = hr_add_hotspare(vol_svc_id, hotspare);
372 if (rc != EOK)
373 printf("hrctl: hr_add_hotspare() rc: %s\n",
374 str_error(rc));
375
376 free(cfg);
377 if (rc != EOK)
378 return 1;
379 else
380 return 0;
381 case 'h':
382 default:
383 usage();
384 free(cfg);
385 return 0;
386 }
387 }
388
389skip:
390 if ((create && assemble) || (!create && !assemble))
391 goto bad;
392
393 if (create && cfg->level == HR_LVL_UNKNOWN) {
394 printf("hrctl: invalid level, exiting\n");
395 goto bad;
396 }
397
398 if (cfg->dev_no > HR_MAX_EXTENTS) {
399 printf("hrctl: too many devices, exiting\n");
400 goto bad;
401 }
402
403 if (cfg->dev_no == 0) {
404 printf("hrctl: invalid number of devices, exiting\n");
405 goto bad;
406 }
407
408 rc = hr_sess_init(&hr);
409 if (rc != EOK) {
410 printf("hrctl: hr_sess_init() rc: %s\n", str_error(rc));
411 retval = 1;
412 goto end;
413 }
414
415 if (create) {
416 rc = hr_create(hr, cfg, false);
417 printf("hrctl: hr_create() rc: %s\n", str_error(rc));
418 } else if (assemble) {
419 rc = hr_create(hr, cfg, true);
420 printf("hrctl: hr_assemble() rc: %s\n", str_error(rc));
421 }
422
423end:
424 free(cfg);
425 hr_sess_destroy(hr);
426 return retval;
427bad:
428 free(cfg);
429 printf("hrctl: bad usage, try hrctl --help\n");
430 return 1;
431}
432
433/** @}
434 */
Note: See TracBrowser for help on using the repository browser.