]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/hastctl/hastctl.c
zfs: merge openzfs/zfs@229b9f4ed
[FreeBSD/FreeBSD.git] / sbin / hastctl / hastctl.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2009-2010 The FreeBSD Foundation
5  * All rights reserved.
6  *
7  * This software was developed by Pawel Jakub Dawidek under sponsorship from
8  * the FreeBSD Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
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
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
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
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
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
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
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
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
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
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 }