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