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

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

hrctl: initialize hr session struct

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