1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2009-2010 The FreeBSD Foundation
5 *
6 * This software was developed by Pawel Jakub Dawidek under sponsorship from
7 * the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/param.h>
32
33 #include <err.h>
34 #include <libutil.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 #include <activemap.h>
40
41 #include "hast.h"
42 #include "hast_proto.h"
43 #include "metadata.h"
44 #include "nv.h"
45 #include "pjdlog.h"
46 #include "proto.h"
47 #include "subr.h"
48
49 /* Path to configuration file. */
50 static const char *cfgpath = HAST_CONFIG;
51 /* Hastd configuration. */
52 static struct hastd_config *cfg;
53 /* Control connection. */
54 static struct proto_conn *controlconn;
55
56 enum {
57 CMD_INVALID,
58 CMD_CREATE,
59 CMD_ROLE,
60 CMD_STATUS,
61 CMD_DUMP,
62 CMD_LIST
63 };
64
65 static __dead2 void
usage(void)66 usage(void)
67 {
68
69 fprintf(stderr,
70 "usage: %s create [-d] [-c config] [-e extentsize] [-k keepdirty]\n"
71 "\t\t[-m mediasize] name ...\n",
72 getprogname());
73 fprintf(stderr,
74 " %s role [-d] [-c config] <init | primary | secondary> all | name ...\n",
75 getprogname());
76 fprintf(stderr,
77 " %s list [-d] [-c config] [all | name ...]\n",
78 getprogname());
79 fprintf(stderr,
80 " %s status [-d] [-c config] [all | name ...]\n",
81 getprogname());
82 fprintf(stderr,
83 " %s dump [-d] [-c config] [all | name ...]\n",
84 getprogname());
85 exit(EX_USAGE);
86 }
87
88 static int
create_one(struct hast_resource * res,intmax_t mediasize,intmax_t extentsize,intmax_t keepdirty)89 create_one(struct hast_resource *res, intmax_t mediasize, intmax_t extentsize,
90 intmax_t keepdirty)
91 {
92 unsigned char *buf;
93 size_t mapsize;
94 int ec;
95
96 ec = 0;
97 pjdlog_prefix_set("[%s] ", res->hr_name);
98
99 if (provinfo(res, true) == -1) {
100 ec = EX_NOINPUT;
101 goto end;
102 }
103 if (mediasize == 0)
104 mediasize = res->hr_local_mediasize;
105 else if (mediasize > res->hr_local_mediasize) {
106 pjdlog_error("Provided mediasize is larger than provider %s size.",
107 res->hr_localpath);
108 ec = EX_DATAERR;
109 goto end;
110 }
111 if (!powerof2(res->hr_local_sectorsize)) {
112 pjdlog_error("Sector size of provider %s is not power of 2 (%u).",
113 res->hr_localpath, res->hr_local_sectorsize);
114 ec = EX_DATAERR;
115 goto end;
116 }
117 if (extentsize == 0)
118 extentsize = HAST_EXTENTSIZE;
119 if (extentsize < res->hr_local_sectorsize) {
120 pjdlog_error("Extent size (%jd) is less than sector size (%u).",
121 (intmax_t)extentsize, res->hr_local_sectorsize);
122 ec = EX_DATAERR;
123 goto end;
124 }
125 if ((extentsize % res->hr_local_sectorsize) != 0) {
126 pjdlog_error("Extent size (%jd) is not multiple of sector size (%u).",
127 (intmax_t)extentsize, res->hr_local_sectorsize);
128 ec = EX_DATAERR;
129 goto end;
130 }
131 mapsize = activemap_calc_ondisk_size(mediasize - METADATA_SIZE,
132 extentsize, res->hr_local_sectorsize);
133 if (keepdirty == 0)
134 keepdirty = HAST_KEEPDIRTY;
135 res->hr_datasize = mediasize - METADATA_SIZE - mapsize;
136 res->hr_extentsize = extentsize;
137 res->hr_keepdirty = keepdirty;
138
139 res->hr_localoff = METADATA_SIZE + mapsize;
140
141 if (metadata_write(res) == -1) {
142 ec = EX_IOERR;
143 goto end;
144 }
145 buf = calloc(1, mapsize);
146 if (buf == NULL) {
147 pjdlog_error("Unable to allocate %zu bytes of memory for initial bitmap.",
148 mapsize);
149 ec = EX_TEMPFAIL;
150 goto end;
151 }
152 if (pwrite(res->hr_localfd, buf, mapsize, METADATA_SIZE) !=
153 (ssize_t)mapsize) {
154 pjdlog_errno(LOG_ERR, "Unable to store initial bitmap on %s",
155 res->hr_localpath);
156 free(buf);
157 ec = EX_IOERR;
158 goto end;
159 }
160 free(buf);
161 end:
162 if (res->hr_localfd >= 0)
163 close(res->hr_localfd);
164 pjdlog_prefix_set("%s", "");
165 return (ec);
166 }
167
168 static void
control_create(int argc,char * argv[],intmax_t mediasize,intmax_t extentsize,intmax_t keepdirty)169 control_create(int argc, char *argv[], intmax_t mediasize, intmax_t extentsize,
170 intmax_t keepdirty)
171 {
172 struct hast_resource *res;
173 int ec, ii, ret;
174
175 /* Initialize the given resources. */
176 if (argc < 1)
177 usage();
178 ec = 0;
179 for (ii = 0; ii < argc; ii++) {
180 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
181 if (strcmp(argv[ii], res->hr_name) == 0)
182 break;
183 }
184 if (res == NULL) {
185 pjdlog_error("Unknown resource %s.", argv[ii]);
186 if (ec == 0)
187 ec = EX_DATAERR;
188 continue;
189 }
190 ret = create_one(res, mediasize, extentsize, keepdirty);
191 if (ret != 0 && ec == 0)
192 ec = ret;
193 }
194 exit(ec);
195 }
196
197 static int
dump_one(struct hast_resource * res)198 dump_one(struct hast_resource *res)
199 {
200 int ret;
201
202 ret = metadata_read(res, false);
203 if (ret != 0)
204 return (ret);
205
206 printf("resource: %s\n", res->hr_name);
207 printf(" datasize: %ju (%NB)\n", (uintmax_t)res->hr_datasize,
208 (intmax_t)res->hr_datasize);
209 printf(" extentsize: %d (%NB)\n", res->hr_extentsize,
210 (intmax_t)res->hr_extentsize);
211 printf(" keepdirty: %d\n", res->hr_keepdirty);
212 printf(" localoff: %ju\n", (uintmax_t)res->hr_localoff);
213 printf(" resuid: %ju\n", (uintmax_t)res->hr_resuid);
214 printf(" localcnt: %ju\n", (uintmax_t)res->hr_primary_localcnt);
215 printf(" remotecnt: %ju\n", (uintmax_t)res->hr_primary_remotecnt);
216 printf(" prevrole: %s\n", role2str(res->hr_previous_role));
217
218 return (0);
219 }
220
221 static void
control_dump(int argc,char * argv[])222 control_dump(int argc, char *argv[])
223 {
224 struct hast_resource *res;
225 int ec, ret;
226
227 /* Dump metadata of the given resource(s). */
228
229 ec = 0;
230 if (argc == 0 || (argc == 1 && strcmp(argv[0], "all") == 0)) {
231 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
232 ret = dump_one(res);
233 if (ret != 0 && ec == 0)
234 ec = ret;
235 }
236 } else {
237 int ii;
238
239 for (ii = 0; ii < argc; ii++) {
240 TAILQ_FOREACH(res, &cfg->hc_resources, hr_next) {
241 if (strcmp(argv[ii], res->hr_name) == 0)
242 break;
243 }
244 if (res == NULL) {
245 pjdlog_error("Unknown resource %s.", argv[ii]);
246 if (ec == 0)
247 ec = EX_DATAERR;
248 continue;
249 }
250 ret = dump_one(res);
251 if (ret != 0 && ec == 0)
252 ec = ret;
253 }
254 }
255 exit(ec);
256 }
257
258 static int
control_set_role(struct nv * nv,const char * newrole)259 control_set_role(struct nv *nv, const char *newrole)
260 {
261 const char *res, *oldrole;
262 unsigned int ii;
263 int error, ret;
264
265 ret = 0;
266
267 for (ii = 0; ; ii++) {
268 res = nv_get_string(nv, "resource%u", ii);
269 if (res == NULL)
270 break;
271 pjdlog_prefix_set("[%s] ", res);
272 error = nv_get_int16(nv, "error%u", ii);
273 if (error != 0) {
274 if (ret == 0)
275 ret = error;
276 pjdlog_warning("Received error %d from hastd.", error);
277 continue;
278 }
279 oldrole = nv_get_string(nv, "role%u", ii);
280 if (strcmp(oldrole, newrole) == 0)
281 pjdlog_debug(2, "Role unchanged (%s).", oldrole);
282 else {
283 pjdlog_debug(1, "Role changed from %s to %s.", oldrole,
284 newrole);
285 }
286 }
287 pjdlog_prefix_set("%s", "");
288 return (ret);
289 }
290
291 static int
control_list(struct nv * nv)292 control_list(struct nv *nv)
293 {
294 pid_t pid;
295 unsigned int ii;
296 const char *str;
297 int error, ret;
298
299 ret = 0;
300
301 for (ii = 0; ; ii++) {
302 str = nv_get_string(nv, "resource%u", ii);
303 if (str == NULL)
304 break;
305 printf("%s:\n", str);
306 error = nv_get_int16(nv, "error%u", ii);
307 if (error != 0) {
308 if (ret == 0)
309 ret = error;
310 printf(" error: %d\n", error);
311 continue;
312 }
313 printf(" role: %s\n", nv_get_string(nv, "role%u", ii));
314 printf(" provname: %s\n",
315 nv_get_string(nv, "provname%u", ii));
316 printf(" localpath: %s\n",
317 nv_get_string(nv, "localpath%u", ii));
318 printf(" extentsize: %u (%NB)\n",
319 (unsigned int)nv_get_uint32(nv, "extentsize%u", ii),
320 (intmax_t)nv_get_uint32(nv, "extentsize%u", ii));
321 printf(" keepdirty: %u\n",
322 (unsigned int)nv_get_uint32(nv, "keepdirty%u", ii));
323 printf(" remoteaddr: %s\n",
324 nv_get_string(nv, "remoteaddr%u", ii));
325 str = nv_get_string(nv, "sourceaddr%u", ii);
326 if (str != NULL)
327 printf(" sourceaddr: %s\n", str);
328 printf(" replication: %s\n",
329 nv_get_string(nv, "replication%u", ii));
330 str = nv_get_string(nv, "status%u", ii);
331 if (str != NULL)
332 printf(" status: %s\n", str);
333 pid = nv_get_int32(nv, "workerpid%u", ii);
334 if (pid != 0)
335 printf(" workerpid: %d\n", pid);
336 printf(" dirty: %ju (%NB)\n",
337 (uintmax_t)nv_get_uint64(nv, "dirty%u", ii),
338 (intmax_t)nv_get_uint64(nv, "dirty%u", ii));
339 printf(" statistics:\n");
340 printf(" reads: %ju\n",
341 (uintmax_t)nv_get_uint64(nv, "stat_read%u", ii));
342 printf(" writes: %ju\n",
343 (uintmax_t)nv_get_uint64(nv, "stat_write%u", ii));
344 printf(" deletes: %ju\n",
345 (uintmax_t)nv_get_uint64(nv, "stat_delete%u", ii));
346 printf(" flushes: %ju\n",
347 (uintmax_t)nv_get_uint64(nv, "stat_flush%u", ii));
348 printf(" activemap updates: %ju\n",
349 (uintmax_t)nv_get_uint64(nv, "stat_activemap_update%u", ii));
350 printf(" local errors: "
351 "read: %ju, write: %ju, delete: %ju, flush: %ju\n",
352 (uintmax_t)nv_get_uint64(nv, "stat_read_error%u", ii),
353 (uintmax_t)nv_get_uint64(nv, "stat_write_error%u", ii),
354 (uintmax_t)nv_get_uint64(nv, "stat_delete_error%u", ii),
355 (uintmax_t)nv_get_uint64(nv, "stat_flush_error%u", ii));
356 printf(" queues: "
357 "local: %ju, send: %ju, recv: %ju, done: %ju, idle: %ju\n",
358 (uintmax_t)nv_get_uint64(nv, "local_queue_size%u", ii),
359 (uintmax_t)nv_get_uint64(nv, "send_queue_size%u", ii),
360 (uintmax_t)nv_get_uint64(nv, "recv_queue_size%u", ii),
361 (uintmax_t)nv_get_uint64(nv, "done_queue_size%u", ii),
362 (uintmax_t)nv_get_uint64(nv, "idle_queue_size%u", ii));
363 }
364 return (ret);
365 }
366
367 static int
control_status(struct nv * nv)368 control_status(struct nv *nv)
369 {
370 unsigned int ii;
371 const char *str;
372 int error, hprinted, ret;
373
374 hprinted = 0;
375 ret = 0;
376
377 for (ii = 0; ; ii++) {
378 str = nv_get_string(nv, "resource%u", ii);
379 if (str == NULL)
380 break;
381 if (!hprinted) {
382 printf("Name\tStatus\t Role\t\tComponents\n");
383 hprinted = 1;
384 }
385 printf("%s\t", str);
386 error = nv_get_int16(nv, "error%u", ii);
387 if (error != 0) {
388 if (ret == 0)
389 ret = error;
390 printf("ERR%d\n", error);
391 continue;
392 }
393 str = nv_get_string(nv, "status%u", ii);
394 printf("%-9s", (str != NULL) ? str : "-");
395 printf("%-15s", nv_get_string(nv, "role%u", ii));
396 printf("%s\t",
397 nv_get_string(nv, "localpath%u", ii));
398 printf("%s\n",
399 nv_get_string(nv, "remoteaddr%u", ii));
400 }
401 return (ret);
402 }
403
404 int
main(int argc,char * argv[])405 main(int argc, char *argv[])
406 {
407 struct nv *nv;
408 int64_t mediasize, extentsize, keepdirty;
409 int cmd, debug, error, ii;
410 const char *optstr;
411
412 debug = 0;
413 mediasize = extentsize = keepdirty = 0;
414
415 if (argc == 1)
416 usage();
417
418 if (strcmp(argv[1], "create") == 0) {
419 cmd = CMD_CREATE;
420 optstr = "c:de:k:m:h";
421 } else if (strcmp(argv[1], "role") == 0) {
422 cmd = CMD_ROLE;
423 optstr = "c:dh";
424 } else if (strcmp(argv[1], "list") == 0) {
425 cmd = CMD_LIST;
426 optstr = "c:dh";
427 } else if (strcmp(argv[1], "status") == 0) {
428 cmd = CMD_STATUS;
429 optstr = "c:dh";
430 } else if (strcmp(argv[1], "dump") == 0) {
431 cmd = CMD_DUMP;
432 optstr = "c:dh";
433 } else
434 usage();
435
436 argc--;
437 argv++;
438
439 for (;;) {
440 int ch;
441
442 ch = getopt(argc, argv, optstr);
443 if (ch == -1)
444 break;
445 switch (ch) {
446 case 'c':
447 cfgpath = optarg;
448 break;
449 case 'd':
450 debug++;
451 break;
452 case 'e':
453 if (expand_number(optarg, &extentsize) == -1)
454 errx(EX_USAGE, "Invalid extentsize");
455 break;
456 case 'k':
457 if (expand_number(optarg, &keepdirty) == -1)
458 errx(EX_USAGE, "Invalid keepdirty");
459 break;
460 case 'm':
461 if (expand_number(optarg, &mediasize) == -1)
462 errx(EX_USAGE, "Invalid mediasize");
463 break;
464 case 'h':
465 default:
466 usage();
467 }
468 }
469 argc -= optind;
470 argv += optind;
471
472 switch (cmd) {
473 case CMD_CREATE:
474 case CMD_ROLE:
475 if (argc == 0)
476 usage();
477 break;
478 }
479
480 pjdlog_init(PJDLOG_MODE_STD);
481 pjdlog_debug_set(debug);
482
483 cfg = yy_config_parse(cfgpath, true);
484 PJDLOG_ASSERT(cfg != NULL);
485
486 switch (cmd) {
487 case CMD_CREATE:
488 control_create(argc, argv, mediasize, extentsize, keepdirty);
489 /* NOTREACHED */
490 PJDLOG_ABORT("What are we doing here?!");
491 break;
492 case CMD_DUMP:
493 /* Dump metadata from local component of the given resource. */
494 control_dump(argc, argv);
495 /* NOTREACHED */
496 PJDLOG_ABORT("What are we doing here?!");
497 break;
498 case CMD_ROLE:
499 /* Change role for the given resources. */
500 if (argc < 2)
501 usage();
502 nv = nv_alloc();
503 nv_add_uint8(nv, HASTCTL_CMD_SETROLE, "cmd");
504 if (strcmp(argv[0], "init") == 0)
505 nv_add_uint8(nv, HAST_ROLE_INIT, "role");
506 else if (strcmp(argv[0], "primary") == 0)
507 nv_add_uint8(nv, HAST_ROLE_PRIMARY, "role");
508 else if (strcmp(argv[0], "secondary") == 0)
509 nv_add_uint8(nv, HAST_ROLE_SECONDARY, "role");
510 else
511 usage();
512 for (ii = 0; ii < argc - 1; ii++)
513 nv_add_string(nv, argv[ii + 1], "resource%d", ii);
514 break;
515 case CMD_LIST:
516 case CMD_STATUS:
517 /* Obtain status of the given resources. */
518 nv = nv_alloc();
519 nv_add_uint8(nv, HASTCTL_CMD_STATUS, "cmd");
520 if (argc == 0)
521 nv_add_string(nv, "all", "resource%d", 0);
522 else {
523 for (ii = 0; ii < argc; ii++)
524 nv_add_string(nv, argv[ii], "resource%d", ii);
525 }
526 break;
527 default:
528 PJDLOG_ABORT("Impossible command!");
529 }
530
531 /* Setup control connection... */
532 if (proto_client(NULL, cfg->hc_controladdr, &controlconn) == -1) {
533 pjdlog_exit(EX_OSERR,
534 "Unable to setup control connection to %s",
535 cfg->hc_controladdr);
536 }
537 /* ...and connect to hastd. */
538 if (proto_connect(controlconn, HAST_TIMEOUT) == -1) {
539 pjdlog_exit(EX_OSERR, "Unable to connect to hastd via %s",
540 cfg->hc_controladdr);
541 }
542
543 if (drop_privs(NULL) != 0)
544 exit(EX_CONFIG);
545
546 /* Send the command to the server... */
547 if (hast_proto_send(NULL, controlconn, nv, NULL, 0) == -1) {
548 pjdlog_exit(EX_UNAVAILABLE,
549 "Unable to send command to hastd via %s",
550 cfg->hc_controladdr);
551 }
552 nv_free(nv);
553 /* ...and receive reply. */
554 if (hast_proto_recv_hdr(controlconn, &nv) == -1) {
555 pjdlog_exit(EX_UNAVAILABLE,
556 "cannot receive reply from hastd via %s",
557 cfg->hc_controladdr);
558 }
559
560 error = nv_get_int16(nv, "error");
561 if (error != 0) {
562 pjdlog_exitx(EX_SOFTWARE, "Error %d received from hastd.",
563 error);
564 }
565 nv_set_error(nv, 0);
566
567 switch (cmd) {
568 case CMD_ROLE:
569 error = control_set_role(nv, argv[0]);
570 break;
571 case CMD_LIST:
572 error = control_list(nv);
573 break;
574 case CMD_STATUS:
575 error = control_status(nv);
576 break;
577 default:
578 PJDLOG_ABORT("Impossible command!");
579 }
580
581 exit(error);
582 }
583