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