Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/hastctl/hastctl.c
39475 views
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
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
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
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
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
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
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
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
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
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
584