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

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

hr: cstyle

  • Property mode set to 100644
File size: 10.7 KB
Line 
1/*
2 * Copyright (c) 2024 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 help 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, --assemble-file=PATH create an array from file\n"
59 " -s, --status display status of active arrays\n"
60 " -D, --destroy destroy/disassemble an active array\n"
61 " -F, --fail-extent fail an extent, use with -T and set it before\n"
62 " -c, --create=NAME create new array\n"
63 " -a, --assemble=NAME assemble an existing array\n"
64 " -n non-zero number of devices\n"
65 " -l, --level=LEVEL set the RAID level,\n"
66 " valid values: 0, 1, 4, 5\n"
67 " -0 striping\n"
68 " -1 mirroring\n"
69 " -4 parity on one extent\n"
70 " -5 distributed parity\n"
71 " -H, --hotspare=DEV add hotspare extent\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 "Limitations:\n"
86 " - device name must be less than 32 characters in size\n";
87
88static struct option const long_options[] = {
89 { "help", no_argument, 0, 'h' },
90 { "status", no_argument, 0, 's' },
91 { "assemble", required_argument, 0, 'a' },
92 { "create", required_argument, 0, 'c' },
93 { "level", required_argument, 0, 'l' },
94 { "create-file", required_argument, 0, 'C' },
95 { "assemble-file", required_argument, 0, 'A' },
96 { "destroy", required_argument, 0, 'D' },
97 { "fail-extent", required_argument, 0, 'F' },
98 { "hotspare", required_argument, 0, 'H' },
99 { 0, 0, 0, 0 }
100};
101
102static void usage(void)
103{
104 printf("%s", usage_str);
105}
106
107static errno_t fill_config_devs(int argc, char **argv, int optind,
108 hr_config_t *cfg)
109{
110 errno_t rc;
111 size_t i;
112
113 for (i = 0; i < cfg->dev_no; i++) {
114 rc = loc_service_get_id(argv[optind], &cfg->devs[i], 0);
115 if (rc == ENOENT) {
116 printf("hrctl: no device \"%s\", marking as missing\n",
117 argv[optind]);
118 cfg->devs[i] = 0;
119 } else if (rc != EOK) {
120 printf("hrctl: error resolving device \"%s\", aborting\n",
121 argv[optind]);
122 return EINVAL;
123 }
124
125 optind++;
126 }
127
128 return EOK;
129}
130
131static errno_t load_config(const char *path, hr_config_t *cfg)
132{
133 errno_t rc;
134 size_t i;
135 sif_doc_t *doc = NULL;
136 sif_node_t *narrays;
137 sif_node_t *rnode;
138 sif_node_t *narray;
139 sif_node_t *nextent;
140 const char *ntype;
141 const char *devname;
142 const char *level_str;
143 const char *dev_no_str;
144 const char *extent_devname;
145
146 rc = sif_load(path, &doc);
147 if (rc != EOK)
148 goto error;
149
150 rnode = sif_get_root(doc);
151
152 narrays = sif_node_first_child(rnode);
153 ntype = sif_node_get_type(narrays);
154 if (str_cmp(ntype, "arrays") != 0) {
155 rc = EIO;
156 goto error;
157 }
158
159 narray = sif_node_first_child(narrays);
160 ntype = sif_node_get_type(narray);
161 if (str_cmp(ntype, "array") != 0) {
162 rc = EIO;
163 goto error;
164 }
165
166 devname = sif_node_get_attr(narray, "devname");
167 if (devname == NULL) {
168 rc = EIO;
169 goto error;
170 }
171 str_cpy(cfg->devname, sizeof(cfg->devname), devname);
172
173 level_str = sif_node_get_attr(narray, "level");
174 if (level_str == NULL)
175 cfg->level = HR_LVL_UNKNOWN;
176 else
177 cfg->level = strtol(level_str, NULL, 10);
178
179 dev_no_str = sif_node_get_attr(narray, "n");
180 if (dev_no_str == NULL) {
181 rc = EIO;
182 goto error;
183 }
184 cfg->dev_no = strtol(dev_no_str, NULL, 10);
185
186 nextent = sif_node_first_child(narray);
187 for (i = 0; i < cfg->dev_no; i++) {
188 if (nextent == NULL) {
189 rc = EINVAL;
190 goto error;
191 }
192
193 ntype = sif_node_get_type(nextent);
194 if (str_cmp(ntype, "extent") != 0) {
195 rc = EIO;
196 goto error;
197 }
198
199 extent_devname = sif_node_get_attr(nextent, "devname");
200 if (extent_devname == NULL) {
201 rc = EIO;
202 goto error;
203 }
204
205 rc = loc_service_get_id(extent_devname, &cfg->devs[i], 0);
206 if (rc == ENOENT) {
207 printf("hrctl: no device \"%s\", marking as missing\n",
208 extent_devname);
209 cfg->devs[i] = 0;
210 rc = EOK;
211 } else if (rc != EOK) {
212 printf("hrctl: error resolving device \"%s\", aborting\n",
213 extent_devname);
214 return EINVAL;
215 }
216
217 nextent = sif_node_next_child(nextent);
218 }
219
220error:
221 if (doc != NULL)
222 sif_delete(doc);
223 return rc;
224}
225
226int main(int argc, char **argv)
227{
228 errno_t rc;
229 int retval, c;
230 bool create, assemble;
231 long fail_extent = -1;
232 hr_t *hr;
233 hr_config_t *cfg;
234
235 cfg = calloc(1, sizeof(hr_config_t));
236 if (cfg == NULL)
237 return 1;
238
239 retval = 0;
240 cfg->level = HR_LVL_UNKNOWN;
241 cfg->dev_no = 0;
242 create = assemble = false;
243
244 if (argc < 2) {
245 goto bad;
246 }
247
248 c = 0;
249 optreset = 1;
250 optind = 0;
251
252 while (c != -1) {
253 c = getopt_long(argc, argv, "hsC:c:A:a:l:0145Ln:D:F:H:",
254 long_options, NULL);
255 switch (c) {
256 case 'h':
257 usage();
258 free(cfg);
259 return 0;
260 case 's':
261 free(cfg);
262 rc = hr_print_status();
263 if (rc != EOK)
264 return 1;
265 return 0;
266 case 'C':
267 /* only support 1 array inside config for now XXX */
268 rc = load_config(optarg, cfg);
269 if (rc != EOK) {
270 printf("hrctl: failed to load config\n");
271 free(cfg);
272 return 1;
273 }
274 create = true;
275 goto skip;
276 case 'c':
277 if (str_size(optarg) > sizeof(cfg->devname) - 1) {
278 printf("hrctl: device name too long\n");
279 free(cfg);
280 return 1;
281 }
282 str_cpy(cfg->devname, sizeof(cfg->devname), optarg);
283 create = true;
284 break;
285 case 'A':
286 rc = load_config(optarg, cfg);
287 if (rc != EOK) {
288 printf("hrctl: failed to load config\n");
289 free(cfg);
290 return 1;
291 }
292 assemble = true;
293 goto skip;
294 case 'a':
295 if (str_size(optarg) > sizeof(cfg->devname) - 1) {
296 printf("hrctl: device name too long\n");
297 free(cfg);
298 return 1;
299 }
300 str_cpy(cfg->devname, sizeof(cfg->devname), optarg);
301 assemble = true;
302 break;
303 case 'D':
304 rc = hr_stop(optarg, fail_extent);
305 free(cfg);
306 if (rc != EOK) {
307 if (rc == ENOENT)
308 printf("hrctl: service named \"%s\" does not exist\n",
309 optarg);
310 return 1;
311 }
312 return 0;
313 case 'F':
314 fail_extent = strtol(optarg, NULL, 10);
315 break;
316 case 'l':
317 if (cfg->level != HR_LVL_UNKNOWN)
318 goto bad;
319 cfg->level = strtol(optarg, NULL, 10);
320 break;
321 case '0':
322 if (cfg->level != HR_LVL_UNKNOWN)
323 goto bad;
324 cfg->level = HR_LVL_0;
325 break;
326 case '1':
327 if (cfg->level != HR_LVL_UNKNOWN)
328 goto bad;
329 cfg->level = HR_LVL_1;
330 break;
331 case '4':
332 if (cfg->level != HR_LVL_UNKNOWN)
333 goto bad;
334 cfg->level = HR_LVL_4;
335 break;
336 case '5':
337 if (cfg->level != HR_LVL_UNKNOWN)
338 goto bad;
339 cfg->level = HR_LVL_5;
340 break;
341 case 'n':
342 cfg->dev_no = strtol(optarg, NULL, 10);
343 if ((int)cfg->dev_no + optind != argc)
344 goto bad;
345 rc = fill_config_devs(argc, argv, optind, cfg);
346 if (rc != EOK) {
347 free(cfg);
348 return 1;
349 }
350 break;
351 case 'H':
352 if (optind != 3 && argc != 4)
353 goto bad;
354
355 service_id_t hotspare;
356 service_id_t vol_svc_id;
357
358 rc = loc_service_get_id(argv[1], &vol_svc_id, 0);
359 if (rc != EOK) {
360 printf("hrctl: error resolving volume \"%s\", "
361 "aborting extent addition\n", argv[1]);
362 goto bad;
363 }
364
365 rc = loc_service_get_id(optarg, &hotspare, 0);
366 if (rc != EOK) {
367 printf("hrctl: error resolving device \"%s\", "
368 "aborting extent addition\n", optarg);
369 goto bad;
370 }
371
372 rc = hr_add_hotspare(vol_svc_id, hotspare);
373 if (rc != EOK)
374 printf("hrctl: hr_add_hotspare() rc: %s\n",
375 str_error(rc));
376
377 free(cfg);
378 if (rc != EOK)
379 return 1;
380 else
381 return 0;
382 }
383 }
384
385skip:
386 if ((create && assemble) || (!create && !assemble))
387 goto bad;
388
389 if (create && cfg->level == HR_LVL_UNKNOWN) {
390 printf("hrctl: invalid level, exiting\n");
391 goto bad;
392 }
393
394 if (cfg->dev_no > HR_MAX_EXTENTS) {
395 printf("hrctl: too many devices, exiting\n");
396 goto bad;
397 }
398
399 if (cfg->dev_no == 0) {
400 printf("hrctl: invalid number of devices, exiting\n");
401 goto bad;
402 }
403
404 rc = hr_sess_init(&hr);
405 if (rc != EOK) {
406 printf("hrctl: hr_sess_init() rc: %s\n", str_error(rc));
407 retval = 1;
408 goto end;
409 }
410
411 if (create) {
412 rc = hr_create(hr, cfg, false);
413 printf("hrctl: hr_create() rc: %s\n", str_error(rc));
414 } else if (assemble) {
415 rc = hr_create(hr, cfg, true);
416 printf("hrctl: hr_assemble() rc: %s\n", str_error(rc));
417 }
418
419end:
420 free(cfg);
421 hr_sess_destroy(hr);
422 return retval;
423bad:
424 free(cfg);
425 printf("hrctl: bad usage, try hrctl --help\n");
426 return 1;
427}
428
429/** @}
430 */
Note: See TracBrowser for help on using the repository browser.