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