1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2015 iXsystems Inc. 5 * All rights reserved. 6 * 7 * This software was developed by Jakub Klama <jceel@FreeBSD.org> 8 * under sponsorship from iXsystems Inc. 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 AUTHOR 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 AUTHOR 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/types.h> 33 #include <sys/nv.h> 34 #include <sys/queue.h> 35 #include <assert.h> 36 #include <stdio.h> 37 #include <stdint.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <ucl++.h> 41 #include <netinet/in.h> 42 #include <netinet/ip.h> 43 44 #include <libutil++.hh> 45 #include <memory> 46 47 #include "conf.h" 48 #include "ctld.hh" 49 50 struct scope_exit { 51 using callback = void(); 52 scope_exit(callback *fn) : fn(fn) {} 53 54 ~scope_exit() { fn(); } 55 56 private: 57 callback *fn; 58 }; 59 60 static bool uclparse_toplevel(const ucl::Ucl &); 61 static bool uclparse_chap(const char *, const ucl::Ucl &); 62 static bool uclparse_chap_mutual(const char *, const ucl::Ucl &); 63 static bool uclparse_lun(const char *, const ucl::Ucl &); 64 static bool uclparse_lun_entries(const char *, const ucl::Ucl &); 65 static bool uclparse_auth_group(const char *, const ucl::Ucl &); 66 static bool uclparse_portal_group(const char *, const ucl::Ucl &); 67 static bool uclparse_transport_group(const char *, const ucl::Ucl &); 68 static bool uclparse_controller(const char *, const ucl::Ucl &); 69 static bool uclparse_controller_transport_group(const char *, const ucl::Ucl &); 70 static bool uclparse_controller_namespace(const char *, const ucl::Ucl &); 71 static bool uclparse_target(const char *, const ucl::Ucl &); 72 static bool uclparse_target_portal_group(const char *, const ucl::Ucl &); 73 static bool uclparse_target_lun(const char *, const ucl::Ucl &); 74 75 static bool 76 uclparse_chap(const char *ag_name, const ucl::Ucl &obj) 77 { 78 auto user = obj["user"]; 79 if (!user || user.type() != UCL_STRING) { 80 log_warnx("chap section in auth-group \"%s\" is missing " 81 "\"user\" string key", ag_name); 82 return (false); 83 } 84 85 auto secret = obj["secret"]; 86 if (!secret || secret.type() != UCL_STRING) { 87 log_warnx("chap section in auth-group \"%s\" is missing " 88 "\"secret\" string key", ag_name); 89 return (false); 90 } 91 92 return (auth_group_add_chap( 93 user.string_value().c_str(), 94 secret.string_value().c_str())); 95 } 96 97 static bool 98 uclparse_chap_mutual(const char *ag_name, const ucl::Ucl &obj) 99 { 100 auto user = obj["user"]; 101 if (!user || user.type() != UCL_STRING) { 102 log_warnx("chap-mutual section in auth-group \"%s\" is missing " 103 "\"user\" string key", ag_name); 104 return (false); 105 } 106 107 auto secret = obj["secret"]; 108 if (!secret || secret.type() != UCL_STRING) { 109 log_warnx("chap-mutual section in auth-group \"%s\" is missing " 110 "\"secret\" string key", ag_name); 111 return (false); 112 } 113 114 auto mutual_user = obj["mutual-user"]; 115 if (!mutual_user || mutual_user.type() != UCL_STRING) { 116 log_warnx("chap-mutual section in auth-group \"%s\" is missing " 117 "\"mutual-user\" string key", ag_name); 118 return (false); 119 } 120 121 auto mutual_secret = obj["mutual-secret"]; 122 if (!mutual_secret || mutual_secret.type() != UCL_STRING) { 123 log_warnx("chap-mutual section in auth-group \"%s\" is missing " 124 "\"mutual-secret\" string key", ag_name); 125 return (false); 126 } 127 128 return (auth_group_add_chap_mutual( 129 user.string_value().c_str(), 130 secret.string_value().c_str(), 131 mutual_user.string_value().c_str(), 132 mutual_secret.string_value().c_str())); 133 } 134 135 static bool 136 uclparse_target_chap(const char *t_name, const ucl::Ucl &obj) 137 { 138 auto user = obj["user"]; 139 if (!user || user.type() != UCL_STRING) { 140 log_warnx("chap section in target \"%s\" is missing " 141 "\"user\" string key", t_name); 142 return (false); 143 } 144 145 auto secret = obj["secret"]; 146 if (!secret || secret.type() != UCL_STRING) { 147 log_warnx("chap section in target \"%s\" is missing " 148 "\"secret\" string key", t_name); 149 return (false); 150 } 151 152 return (target_add_chap( 153 user.string_value().c_str(), 154 secret.string_value().c_str())); 155 } 156 157 static bool 158 uclparse_target_chap_mutual(const char *t_name, const ucl::Ucl &obj) 159 { 160 auto user = obj["user"]; 161 if (!user || user.type() != UCL_STRING) { 162 log_warnx("chap-mutual section in target \"%s\" is missing " 163 "\"user\" string key", t_name); 164 return (false); 165 } 166 167 auto secret = obj["secret"]; 168 if (!secret || secret.type() != UCL_STRING) { 169 log_warnx("chap-mutual section in target \"%s\" is missing " 170 "\"secret\" string key", t_name); 171 return (false); 172 } 173 174 auto mutual_user = obj["mutual-user"]; 175 if (!mutual_user || mutual_user.type() != UCL_STRING) { 176 log_warnx("chap-mutual section in target \"%s\" is missing " 177 "\"mutual-user\" string key", t_name); 178 return (false); 179 } 180 181 auto mutual_secret = obj["mutual-secret"]; 182 if (!mutual_secret || mutual_secret.type() != UCL_STRING) { 183 log_warnx("chap-mutual section in target \"%s\" is missing " 184 "\"mutual-secret\" string key", t_name); 185 return (false); 186 } 187 188 return (target_add_chap_mutual( 189 user.string_value().c_str(), 190 secret.string_value().c_str(), 191 mutual_user.string_value().c_str(), 192 mutual_secret.string_value().c_str())); 193 } 194 195 static bool 196 uclparse_target_portal_group(const char *t_name, const ucl::Ucl &obj) 197 { 198 /* 199 * If the value is a single string, assume it is a 200 * portal-group name. 201 */ 202 if (obj.type() == UCL_STRING) 203 return (target_add_portal_group(obj.string_value().c_str(), 204 NULL)); 205 206 if (obj.type() != UCL_OBJECT) { 207 log_warnx("portal-group section in target \"%s\" must be " 208 "an object or string", t_name); 209 return (false); 210 } 211 212 auto portal_group = obj["name"]; 213 if (!portal_group || portal_group.type() != UCL_STRING) { 214 log_warnx("portal-group section in target \"%s\" is missing " 215 "\"name\" string key", t_name); 216 return (false); 217 } 218 219 auto auth_group = obj["auth-group-name"]; 220 if (auth_group) { 221 if (auth_group.type() != UCL_STRING) { 222 log_warnx("\"auth-group-name\" property in " 223 "portal-group section for target \"%s\" is not " 224 "a string", t_name); 225 return (false); 226 } 227 return (target_add_portal_group( 228 portal_group.string_value().c_str(), 229 auth_group.string_value().c_str())); 230 } 231 232 return (target_add_portal_group(portal_group.string_value().c_str(), 233 NULL)); 234 } 235 236 static bool 237 uclparse_controller_transport_group(const char *t_name, const ucl::Ucl &obj) 238 { 239 /* 240 * If the value is a single string, assume it is a 241 * transport-group name. 242 */ 243 if (obj.type() == UCL_STRING) 244 return target_add_portal_group(obj.string_value().c_str(), 245 nullptr); 246 247 if (obj.type() != UCL_OBJECT) { 248 log_warnx("transport-group section in controller \"%s\" must " 249 "be an object or string", t_name); 250 return false; 251 } 252 253 auto portal_group = obj["name"]; 254 if (!portal_group || portal_group.type() != UCL_STRING) { 255 log_warnx("transport-group section in controller \"%s\" is " 256 "missing \"name\" string key", t_name); 257 return false; 258 } 259 260 auto auth_group = obj["auth-group-name"]; 261 if (auth_group) { 262 if (auth_group.type() != UCL_STRING) { 263 log_warnx("\"auth-group-name\" property in " 264 "transport-group section for controller \"%s\" is " 265 "not a string", t_name); 266 return false; 267 } 268 return target_add_portal_group( 269 portal_group.string_value().c_str(), 270 auth_group.string_value().c_str()); 271 } 272 273 return target_add_portal_group(portal_group.string_value().c_str(), 274 nullptr); 275 } 276 277 static bool 278 uclparse_target_lun(const char *t_name, const ucl::Ucl &obj) 279 { 280 char *end; 281 u_int id; 282 283 std::string key = obj.key(); 284 if (!key.empty()) { 285 id = strtoul(key.c_str(), &end, 0); 286 if (*end != '\0') { 287 log_warnx("lun key \"%s\" in target \"%s\" is invalid", 288 key.c_str(), t_name); 289 return (false); 290 } 291 292 if (obj.type() == UCL_STRING) 293 return (target_add_lun(id, obj.string_value().c_str())); 294 } 295 296 if (obj.type() != UCL_OBJECT) { 297 log_warnx("lun section entries in target \"%s\" must be objects", 298 t_name); 299 return (false); 300 } 301 302 if (key.empty()) { 303 auto num = obj["number"]; 304 if (!num || num.type() != UCL_INT) { 305 log_warnx("lun section in target \"%s\" is missing " 306 "\"number\" integer property", t_name); 307 return (false); 308 } 309 id = num.int_value(); 310 } 311 312 auto name = obj["name"]; 313 if (!name) { 314 if (!target_start_lun(id)) 315 return (false); 316 317 scope_exit finisher(lun_finish); 318 std::string lun_name = 319 freebsd::stringf("lun %u for target \"%s\"", id, t_name); 320 return (uclparse_lun_entries(lun_name.c_str(), obj)); 321 } 322 323 if (name.type() != UCL_STRING) { 324 log_warnx("\"name\" property for lun %u for target " 325 "\"%s\" is not a string", id, t_name); 326 return (false); 327 } 328 329 return (target_add_lun(id, name.string_value().c_str())); 330 } 331 332 static bool 333 uclparse_controller_namespace(const char *t_name, const ucl::Ucl &obj) 334 { 335 char *end; 336 u_int id; 337 338 std::string key = obj.key(); 339 if (!key.empty()) { 340 id = strtoul(key.c_str(), &end, 0); 341 if (*end != '\0') { 342 log_warnx("namespace key \"%s\" in controller \"%s\"" 343 " is invalid", key.c_str(), t_name); 344 return false; 345 } 346 347 if (obj.type() == UCL_STRING) 348 return controller_add_namespace(id, 349 obj.string_value().c_str()); 350 } 351 352 if (obj.type() != UCL_OBJECT) { 353 log_warnx("namespace section entries in controller \"%s\"" 354 " must be objects", t_name); 355 return false; 356 } 357 358 if (key.empty()) { 359 auto num = obj["number"]; 360 if (!num || num.type() != UCL_INT) { 361 log_warnx("namespace section in controller \"%s\" is " 362 "missing \"id\" integer property", t_name); 363 return (false); 364 } 365 id = num.int_value(); 366 } 367 368 auto name = obj["name"]; 369 if (!name) { 370 if (!controller_start_namespace(id)) 371 return false; 372 373 std::string lun_name = 374 freebsd::stringf("namespace %u for controller \"%s\"", id, 375 t_name); 376 return uclparse_lun_entries(lun_name.c_str(), obj); 377 } 378 379 if (name.type() != UCL_STRING) { 380 log_warnx("\"name\" property for namespace %u for " 381 "controller \"%s\" is not a string", id, t_name); 382 return (false); 383 } 384 385 return controller_add_namespace(id, name.string_value().c_str()); 386 } 387 388 static bool 389 uclparse_toplevel(const ucl::Ucl &top) 390 { 391 /* Pass 1 - everything except targets */ 392 for (const auto &obj : top) { 393 std::string key = obj.key(); 394 395 if (key == "debug") { 396 if (obj.type() == UCL_INT) 397 conf_set_debug(obj.int_value()); 398 else { 399 log_warnx("\"debug\" property value is not integer"); 400 return (false); 401 } 402 } 403 404 if (key == "timeout") { 405 if (obj.type() == UCL_INT) 406 conf_set_timeout(obj.int_value()); 407 else { 408 log_warnx("\"timeout\" property value is not integer"); 409 return (false); 410 } 411 } 412 413 if (key == "maxproc") { 414 if (obj.type() == UCL_INT) 415 conf_set_maxproc(obj.int_value()); 416 else { 417 log_warnx("\"maxproc\" property value is not integer"); 418 return (false); 419 } 420 } 421 422 if (key == "pidfile") { 423 if (obj.type() == UCL_STRING) { 424 if (!conf_set_pidfile_path( 425 obj.string_value().c_str())) 426 return (false); 427 } else { 428 log_warnx("\"pidfile\" property value is not string"); 429 return (false); 430 } 431 } 432 433 if (key == "isns-server") { 434 if (obj.type() == UCL_ARRAY) { 435 for (const auto &child : obj) { 436 if (child.type() != UCL_STRING) 437 return (false); 438 439 if (!isns_add_server( 440 child.string_value().c_str())) 441 return (false); 442 } 443 } else { 444 log_warnx("\"isns-server\" property value is " 445 "not an array"); 446 return (false); 447 } 448 } 449 450 if (key == "isns-period") { 451 if (obj.type() == UCL_INT) 452 conf_set_isns_period(obj.int_value()); 453 else { 454 log_warnx("\"isns-period\" property value is not integer"); 455 return (false); 456 } 457 } 458 459 if (key == "isns-timeout") { 460 if (obj.type() == UCL_INT) 461 conf_set_isns_timeout(obj.int_value()); 462 else { 463 log_warnx("\"isns-timeout\" property value is not integer"); 464 return (false); 465 } 466 } 467 468 if (key == "auth-group") { 469 if (obj.type() == UCL_OBJECT) { 470 for (const auto &child : obj) { 471 if (!uclparse_auth_group( 472 child.key().c_str(), child)) 473 return (false); 474 } 475 } else { 476 log_warnx("\"auth-group\" section is not an object"); 477 return (false); 478 } 479 } 480 481 if (key == "portal-group") { 482 if (obj.type() == UCL_OBJECT) { 483 for (const auto &child : obj) { 484 if (!uclparse_portal_group( 485 child.key().c_str(), child)) 486 return (false); 487 } 488 } else { 489 log_warnx("\"portal-group\" section is not an object"); 490 return (false); 491 } 492 } 493 494 if (key == "transport-group") { 495 if (obj.type() == UCL_OBJECT) { 496 for (const auto &child : obj) { 497 if (!uclparse_transport_group( 498 child.key().c_str(), child)) 499 return false; 500 } 501 } else { 502 log_warnx("\"transport-group\" section is not an object"); 503 return false; 504 } 505 } 506 507 if (key == "lun") { 508 if (obj.type() == UCL_OBJECT) { 509 for (const auto &child : obj) { 510 if (!uclparse_lun(child.key().c_str(), 511 child)) 512 return (false); 513 } 514 } else { 515 log_warnx("\"lun\" section is not an object"); 516 return (false); 517 } 518 } 519 } 520 521 /* Pass 2 - targets */ 522 for (const auto &obj : top) { 523 std::string key = obj.key(); 524 525 if (key == "controller") { 526 if (obj.type() == UCL_OBJECT) { 527 for (const auto &child : obj) { 528 if (!uclparse_controller( 529 child.key().c_str(), child)) 530 return false; 531 } 532 } else { 533 log_warnx("\"controller\" section is not an object"); 534 return false; 535 } 536 } 537 538 if (key == "target") { 539 if (obj.type() == UCL_OBJECT) { 540 for (const auto &child : obj) { 541 if (!uclparse_target( 542 child.key().c_str(), child)) 543 return (false); 544 } 545 } else { 546 log_warnx("\"target\" section is not an object"); 547 return (false); 548 } 549 } 550 } 551 552 return (true); 553 } 554 555 static bool 556 uclparse_auth_group(const char *name, const ucl::Ucl &top) 557 { 558 if (!auth_group_start(name)) 559 return (false); 560 561 scope_exit finisher(auth_group_finish); 562 for (const auto &obj : top) { 563 std::string key = obj.key(); 564 565 if (key == "auth-type") { 566 if (!auth_group_set_type(obj.string_value().c_str())) 567 return false; 568 } 569 570 if (key == "chap") { 571 if (obj.type() == UCL_OBJECT) { 572 if (!uclparse_chap(name, obj)) 573 return false; 574 } else if (obj.type() == UCL_ARRAY) { 575 for (const auto &tmp : obj) { 576 if (!uclparse_chap(name, tmp)) 577 return false; 578 } 579 } else { 580 log_warnx("\"chap\" property of auth-group " 581 "\"%s\" is not an array or object", 582 name); 583 return false; 584 } 585 } 586 587 if (key == "chap-mutual") { 588 if (obj.type() == UCL_OBJECT) { 589 if (!uclparse_chap_mutual(name, obj)) 590 return false; 591 } else if (obj.type() == UCL_ARRAY) { 592 for (const auto &tmp : obj) { 593 if (!uclparse_chap_mutual(name, tmp)) 594 return false; 595 } 596 } else { 597 log_warnx("\"chap-mutual\" property of " 598 "auth-group \"%s\" is not an array or object", 599 name); 600 return false; 601 } 602 } 603 604 if (key == "host-address") { 605 if (obj.type() == UCL_STRING) { 606 if (!auth_group_add_host_address( 607 obj.string_value().c_str())) 608 return false; 609 } else if (obj.type() == UCL_ARRAY) { 610 for (const auto &tmp : obj) { 611 if (!auth_group_add_host_address( 612 tmp.string_value().c_str())) 613 return false; 614 } 615 } else { 616 log_warnx("\"host-address\" property of " 617 "auth-group \"%s\" is not an array or string", 618 name); 619 return false; 620 } 621 } 622 623 if (key == "host-nqn") { 624 if (obj.type() == UCL_STRING) { 625 if (!auth_group_add_host_nqn( 626 obj.string_value().c_str())) 627 return false; 628 } else if (obj.type() == UCL_ARRAY) { 629 for (const auto &tmp : obj) { 630 if (!auth_group_add_host_nqn( 631 tmp.string_value().c_str())) 632 return false; 633 } 634 } else { 635 log_warnx("\"host-nqn\" property of " 636 "auth-group \"%s\" is not an array or string", 637 name); 638 return false; 639 } 640 } 641 642 if (key == "initiator-name") { 643 if (obj.type() == UCL_STRING) { 644 if (!auth_group_add_initiator_name( 645 obj.string_value().c_str())) 646 return false; 647 } else if (obj.type() == UCL_ARRAY) { 648 for (const auto &tmp : obj) { 649 if (!auth_group_add_initiator_name( 650 tmp.string_value().c_str())) 651 return false; 652 } 653 } else { 654 log_warnx("\"initiator-name\" property of " 655 "auth-group \"%s\" is not an array or string", 656 name); 657 return false; 658 } 659 } 660 661 if (key == "initiator-portal") { 662 if (obj.type() == UCL_STRING) { 663 if (!auth_group_add_initiator_portal( 664 obj.string_value().c_str())) 665 return false; 666 } else if (obj.type() == UCL_ARRAY) { 667 for (const auto &tmp : obj) { 668 if (!auth_group_add_initiator_portal( 669 tmp.string_value().c_str())) 670 return false; 671 } 672 } else { 673 log_warnx("\"initiator-portal\" property of " 674 "auth-group \"%s\" is not an array or string", 675 name); 676 return false; 677 } 678 } 679 } 680 681 return (true); 682 } 683 684 static bool 685 uclparse_dscp(const char *group_type, const char *pg_name, 686 const ucl::Ucl &obj) 687 { 688 if ((obj.type() != UCL_STRING) && (obj.type() != UCL_INT)) { 689 log_warnx("\"dscp\" property of %s group \"%s\" is not a " 690 "string or integer", group_type, pg_name); 691 return (false); 692 } 693 if (obj.type() == UCL_INT) 694 return (portal_group_set_dscp(obj.int_value())); 695 696 std::string key = obj.key(); 697 if (key == "be" || key == "cs0") 698 portal_group_set_dscp(IPTOS_DSCP_CS0 >> 2); 699 else if (key == "ef") 700 portal_group_set_dscp(IPTOS_DSCP_EF >> 2); 701 else if (key == "cs0") 702 portal_group_set_dscp(IPTOS_DSCP_CS0 >> 2); 703 else if (key == "cs1") 704 portal_group_set_dscp(IPTOS_DSCP_CS1 >> 2); 705 else if (key == "cs2") 706 portal_group_set_dscp(IPTOS_DSCP_CS2 >> 2); 707 else if (key == "cs3") 708 portal_group_set_dscp(IPTOS_DSCP_CS3 >> 2); 709 else if (key == "cs4") 710 portal_group_set_dscp(IPTOS_DSCP_CS4 >> 2); 711 else if (key == "cs5") 712 portal_group_set_dscp(IPTOS_DSCP_CS5 >> 2); 713 else if (key == "cs6") 714 portal_group_set_dscp(IPTOS_DSCP_CS6 >> 2); 715 else if (key == "cs7") 716 portal_group_set_dscp(IPTOS_DSCP_CS7 >> 2); 717 else if (key == "af11") 718 portal_group_set_dscp(IPTOS_DSCP_AF11 >> 2); 719 else if (key == "af12") 720 portal_group_set_dscp(IPTOS_DSCP_AF12 >> 2); 721 else if (key == "af13") 722 portal_group_set_dscp(IPTOS_DSCP_AF13 >> 2); 723 else if (key == "af21") 724 portal_group_set_dscp(IPTOS_DSCP_AF21 >> 2); 725 else if (key == "af22") 726 portal_group_set_dscp(IPTOS_DSCP_AF22 >> 2); 727 else if (key == "af23") 728 portal_group_set_dscp(IPTOS_DSCP_AF23 >> 2); 729 else if (key == "af31") 730 portal_group_set_dscp(IPTOS_DSCP_AF31 >> 2); 731 else if (key == "af32") 732 portal_group_set_dscp(IPTOS_DSCP_AF32 >> 2); 733 else if (key == "af33") 734 portal_group_set_dscp(IPTOS_DSCP_AF33 >> 2); 735 else if (key == "af41") 736 portal_group_set_dscp(IPTOS_DSCP_AF41 >> 2); 737 else if (key == "af42") 738 portal_group_set_dscp(IPTOS_DSCP_AF42 >> 2); 739 else if (key == "af43") 740 portal_group_set_dscp(IPTOS_DSCP_AF43 >> 2); 741 else { 742 log_warnx("\"dscp\" property value is not a supported textual value"); 743 return (false); 744 } 745 return (true); 746 } 747 748 static bool 749 uclparse_pcp(const char *group_type, const char *pg_name, 750 const ucl::Ucl &obj) 751 { 752 if (obj.type() != UCL_INT) { 753 log_warnx("\"pcp\" property of %s group \"%s\" is not an " 754 "integer", group_type, pg_name); 755 return (false); 756 } 757 return (portal_group_set_pcp(obj.int_value())); 758 } 759 760 static bool 761 uclparse_portal_group(const char *name, const ucl::Ucl &top) 762 { 763 if (!portal_group_start(name)) 764 return (false); 765 766 scope_exit finisher(portal_group_finish); 767 for (const auto &obj : top) { 768 std::string key = obj.key(); 769 770 if (key == "discovery-auth-group") { 771 if (obj.type() != UCL_STRING) { 772 log_warnx("\"discovery-auth-group\" property " 773 "of portal-group \"%s\" is not a string", 774 name); 775 return false; 776 } 777 778 if (!portal_group_set_discovery_auth_group( 779 obj.string_value().c_str())) 780 return false; 781 } 782 783 if (key == "discovery-filter") { 784 if (obj.type() != UCL_STRING) { 785 log_warnx("\"discovery-filter\" property of " 786 "portal-group \"%s\" is not a string", 787 name); 788 return false; 789 } 790 791 if (!portal_group_set_filter( 792 obj.string_value().c_str())) 793 return false; 794 } 795 796 if (key == "foreign") { 797 portal_group_set_foreign(); 798 } 799 800 if (key == "listen") { 801 if (obj.type() == UCL_STRING) { 802 if (!portal_group_add_listen( 803 obj.string_value().c_str(), false)) 804 return false; 805 } else if (obj.type() == UCL_ARRAY) { 806 for (const auto &tmp : obj) { 807 if (!portal_group_add_listen( 808 tmp.string_value().c_str(), 809 false)) 810 return false; 811 } 812 } else { 813 log_warnx("\"listen\" property of " 814 "portal-group \"%s\" is not a string", 815 name); 816 return false; 817 } 818 } 819 820 if (key == "listen-iser") { 821 if (obj.type() == UCL_STRING) { 822 if (!portal_group_add_listen( 823 obj.string_value().c_str(), true)) 824 return false; 825 } else if (obj.type() == UCL_ARRAY) { 826 for (const auto &tmp : obj) { 827 if (!portal_group_add_listen( 828 tmp.string_value().c_str(), 829 true)) 830 return false; 831 } 832 } else { 833 log_warnx("\"listen\" property of " 834 "portal-group \"%s\" is not a string", 835 name); 836 return false; 837 } 838 } 839 840 if (key == "offload") { 841 if (obj.type() != UCL_STRING) { 842 log_warnx("\"offload\" property of " 843 "portal-group \"%s\" is not a string", 844 name); 845 return false; 846 } 847 848 if (!portal_group_set_offload( 849 obj.string_value().c_str())) 850 return false; 851 } 852 853 if (key == "redirect") { 854 if (obj.type() != UCL_STRING) { 855 log_warnx("\"listen\" property of " 856 "portal-group \"%s\" is not a string", 857 name); 858 return false; 859 } 860 861 if (!portal_group_set_redirection( 862 obj.string_value().c_str())) 863 return false; 864 } 865 866 if (key == "options") { 867 if (obj.type() != UCL_OBJECT) { 868 log_warnx("\"options\" property of portal group " 869 "\"%s\" is not an object", name); 870 return false; 871 } 872 873 for (const auto &tmp : obj) { 874 if (!portal_group_add_option( 875 tmp.key().c_str(), 876 tmp.forced_string_value().c_str())) 877 return false; 878 } 879 } 880 881 if (key == "tag") { 882 if (obj.type() != UCL_INT) { 883 log_warnx("\"tag\" property of portal group " 884 "\"%s\" is not an integer", 885 name); 886 return false; 887 } 888 889 portal_group_set_tag(obj.int_value()); 890 } 891 892 if (key == "dscp") { 893 if (!uclparse_dscp("portal", name, obj)) 894 return false; 895 } 896 897 if (key == "pcp") { 898 if (!uclparse_pcp("portal", name, obj)) 899 return false; 900 } 901 } 902 903 return (true); 904 } 905 906 static bool 907 uclparse_transport_listen_obj(const char *pg_name, const ucl::Ucl &top) 908 { 909 for (const auto &obj : top) { 910 std::string key = obj.key(); 911 912 if (key.empty()) { 913 log_warnx("missing protocol for \"listen\" " 914 "property of transport-group \"%s\"", pg_name); 915 return false; 916 } 917 918 if (key == "tcp") { 919 if (obj.type() == UCL_STRING) { 920 if (!transport_group_add_listen_tcp( 921 obj.string_value().c_str())) 922 return false; 923 } else if (obj.type() == UCL_ARRAY) { 924 for (const auto &tmp : obj) { 925 if (!transport_group_add_listen_tcp( 926 tmp.string_value().c_str())) 927 return false; 928 } 929 } 930 } else if (key == "discovery-tcp") { 931 if (obj.type() == UCL_STRING) { 932 if (!transport_group_add_listen_discovery_tcp( 933 obj.string_value().c_str())) 934 return false; 935 } else if (obj.type() == UCL_ARRAY) { 936 for (const auto &tmp : obj) { 937 if (!transport_group_add_listen_discovery_tcp( 938 tmp.string_value().c_str())) 939 return false; 940 } 941 } 942 } else { 943 log_warnx("invalid listen protocol \"%s\" for " 944 "transport-group \"%s\"", key.c_str(), pg_name); 945 return false; 946 } 947 } 948 return true; 949 } 950 951 static bool 952 uclparse_transport_group(const char *name, const ucl::Ucl &top) 953 { 954 if (!transport_group_start(name)) 955 return false; 956 957 scope_exit finisher(portal_group_finish); 958 for (const auto &obj : top) { 959 std::string key = obj.key(); 960 961 if (key == "discovery-auth-group") { 962 if (obj.type() != UCL_STRING) { 963 log_warnx("\"discovery-auth-group\" property " 964 "of transport-group \"%s\" is not a string", 965 name); 966 return false; 967 } 968 969 if (!portal_group_set_discovery_auth_group( 970 obj.string_value().c_str())) 971 return false; 972 } 973 974 if (key == "discovery-filter") { 975 if (obj.type() != UCL_STRING) { 976 log_warnx("\"discovery-filter\" property of " 977 "transport-group \"%s\" is not a string", 978 name); 979 return false; 980 } 981 982 if (!portal_group_set_filter( 983 obj.string_value().c_str())) 984 return false; 985 } 986 987 if (key == "listen") { 988 if (obj.type() != UCL_OBJECT) { 989 log_warnx("\"listen\" property of " 990 "transport-group \"%s\" is not an object", 991 name); 992 return false; 993 } 994 if (!uclparse_transport_listen_obj(name, obj)) 995 return false; 996 } 997 998 if (key == "options") { 999 if (obj.type() != UCL_OBJECT) { 1000 log_warnx("\"options\" property of transport group " 1001 "\"%s\" is not an object", name); 1002 return false; 1003 } 1004 1005 for (const auto &tmp : obj) { 1006 if (!portal_group_add_option( 1007 tmp.key().c_str(), 1008 tmp.forced_string_value().c_str())) 1009 return false; 1010 } 1011 } 1012 1013 if (key == "dscp") { 1014 if (!uclparse_dscp("transport", name, obj)) 1015 return false; 1016 } 1017 1018 if (key == "pcp") { 1019 if (!uclparse_pcp("transport", name, obj)) 1020 return false; 1021 } 1022 } 1023 1024 return true; 1025 } 1026 1027 static bool 1028 uclparse_controller(const char *name, const ucl::Ucl &top) 1029 { 1030 if (!controller_start(name)) 1031 return false; 1032 1033 scope_exit finisher(target_finish); 1034 for (const auto &obj : top) { 1035 std::string key = obj.key(); 1036 1037 if (key == "auth-group") { 1038 if (obj.type() != UCL_STRING) { 1039 log_warnx("\"auth-group\" property of " 1040 "controller \"%s\" is not a string", name); 1041 return false; 1042 } 1043 1044 if (!target_set_auth_group(obj.string_value().c_str())) 1045 return false; 1046 } 1047 1048 if (key == "auth-type") { 1049 if (obj.type() != UCL_STRING) { 1050 log_warnx("\"auth-type\" property of " 1051 "controller \"%s\" is not a string", name); 1052 return false; 1053 } 1054 1055 if (!target_set_auth_type(obj.string_value().c_str())) 1056 return false; 1057 } 1058 1059 if (key == "host-address") { 1060 if (obj.type() == UCL_STRING) { 1061 if (!controller_add_host_address( 1062 obj.string_value().c_str())) 1063 return false; 1064 } else if (obj.type() == UCL_ARRAY) { 1065 for (const auto &tmp : obj) { 1066 if (!controller_add_host_address( 1067 tmp.string_value().c_str())) 1068 return false; 1069 } 1070 } else { 1071 log_warnx("\"host-address\" property of " 1072 "controller \"%s\" is not an array or " 1073 "string", name); 1074 return false; 1075 } 1076 } 1077 1078 if (key == "host-nqn") { 1079 if (obj.type() == UCL_STRING) { 1080 if (!controller_add_host_nqn( 1081 obj.string_value().c_str())) 1082 return false; 1083 } else if (obj.type() == UCL_ARRAY) { 1084 for (const auto &tmp : obj) { 1085 if (!controller_add_host_nqn( 1086 tmp.string_value().c_str())) 1087 return false; 1088 } 1089 } else { 1090 log_warnx("\"host-nqn\" property of " 1091 "controller \"%s\" is not an array or " 1092 "string", name); 1093 return false; 1094 } 1095 } 1096 1097 if (key == "transport-group") { 1098 if (obj.type() == UCL_ARRAY) { 1099 for (const auto &tmp : obj) { 1100 if (!uclparse_controller_transport_group(name, 1101 tmp)) 1102 return false; 1103 } 1104 } else { 1105 if (!uclparse_controller_transport_group(name, 1106 obj)) 1107 return false; 1108 } 1109 } 1110 1111 if (key == "namespace") { 1112 for (const auto &tmp : obj) { 1113 if (!uclparse_controller_namespace(name, tmp)) 1114 return false; 1115 } 1116 } 1117 } 1118 1119 return true; 1120 } 1121 1122 static bool 1123 uclparse_target(const char *name, const ucl::Ucl &top) 1124 { 1125 if (!target_start(name)) 1126 return (false); 1127 1128 scope_exit finisher(target_finish); 1129 for (const auto &obj : top) { 1130 std::string key = obj.key(); 1131 1132 if (key == "alias") { 1133 if (obj.type() != UCL_STRING) { 1134 log_warnx("\"alias\" property of target " 1135 "\"%s\" is not a string", name); 1136 return false; 1137 } 1138 1139 if (!target_set_alias(obj.string_value().c_str())) 1140 return false; 1141 } 1142 1143 if (key == "auth-group") { 1144 if (obj.type() != UCL_STRING) { 1145 log_warnx("\"auth-group\" property of target " 1146 "\"%s\" is not a string", name); 1147 return false; 1148 } 1149 1150 if (!target_set_auth_group(obj.string_value().c_str())) 1151 return false; 1152 } 1153 1154 if (key == "auth-type") { 1155 if (obj.type() != UCL_STRING) { 1156 log_warnx("\"auth-type\" property of target " 1157 "\"%s\" is not a string", name); 1158 return false; 1159 } 1160 1161 if (!target_set_auth_type(obj.string_value().c_str())) 1162 return false; 1163 } 1164 1165 if (key == "chap") { 1166 if (obj.type() == UCL_OBJECT) { 1167 if (!uclparse_target_chap(name, obj)) 1168 return false; 1169 } else if (obj.type() == UCL_ARRAY) { 1170 for (const auto &tmp : obj) { 1171 if (!uclparse_target_chap(name, tmp)) 1172 return false; 1173 } 1174 } else { 1175 log_warnx("\"chap\" property of target " 1176 "\"%s\" is not an array or object", 1177 name); 1178 return false; 1179 } 1180 } 1181 1182 if (key == "chap-mutual") { 1183 if (obj.type() == UCL_OBJECT) { 1184 if (!uclparse_target_chap_mutual(name, obj)) 1185 return false; 1186 } else if (obj.type() == UCL_ARRAY) { 1187 for (const auto &tmp : obj) { 1188 if (!uclparse_target_chap_mutual(name, 1189 tmp)) 1190 return false; 1191 } 1192 } else { 1193 log_warnx("\"chap-mutual\" property of target " 1194 "\"%s\" is not an array or object", 1195 name); 1196 return false; 1197 } 1198 } 1199 1200 if (key == "initiator-name") { 1201 if (obj.type() == UCL_STRING) { 1202 if (!target_add_initiator_name( 1203 obj.string_value().c_str())) 1204 return false; 1205 } else if (obj.type() == UCL_ARRAY) { 1206 for (const auto &tmp : obj) { 1207 if (!target_add_initiator_name( 1208 tmp.string_value().c_str())) 1209 return false; 1210 } 1211 } else { 1212 log_warnx("\"initiator-name\" property of " 1213 "target \"%s\" is not an array or string", 1214 name); 1215 return false; 1216 } 1217 } 1218 1219 if (key == "initiator-portal") { 1220 if (obj.type() == UCL_STRING) { 1221 if (!target_add_initiator_portal( 1222 obj.string_value().c_str())) 1223 return false; 1224 } else if (obj.type() == UCL_ARRAY) { 1225 for (const auto &tmp : obj) { 1226 if (!target_add_initiator_portal( 1227 tmp.string_value().c_str())) 1228 return false; 1229 } 1230 } else { 1231 log_warnx("\"initiator-portal\" property of " 1232 "target \"%s\" is not an array or string", 1233 name); 1234 return false; 1235 } 1236 } 1237 1238 if (key == "portal-group") { 1239 if (obj.type() == UCL_ARRAY) { 1240 for (const auto &tmp : obj) { 1241 if (!uclparse_target_portal_group(name, 1242 tmp)) 1243 return false; 1244 } 1245 } else { 1246 if (!uclparse_target_portal_group(name, obj)) 1247 return false; 1248 } 1249 } 1250 1251 if (key == "port") { 1252 if (obj.type() != UCL_STRING) { 1253 log_warnx("\"port\" property of target " 1254 "\"%s\" is not a string", name); 1255 return false; 1256 } 1257 1258 if (!target_set_physical_port(obj.string_value().c_str())) 1259 return false; 1260 } 1261 1262 if (key == "redirect") { 1263 if (obj.type() != UCL_STRING) { 1264 log_warnx("\"redirect\" property of target " 1265 "\"%s\" is not a string", name); 1266 return false; 1267 } 1268 1269 if (!target_set_redirection(obj.string_value().c_str())) 1270 return false; 1271 } 1272 1273 if (key == "lun") { 1274 for (const auto &tmp : obj) { 1275 if (!uclparse_target_lun(name, tmp)) 1276 return false; 1277 } 1278 } 1279 } 1280 1281 return (true); 1282 } 1283 1284 static bool 1285 uclparse_lun(const char *name, const ucl::Ucl &top) 1286 { 1287 if (!lun_start(name)) 1288 return (false); 1289 1290 scope_exit finisher(lun_finish); 1291 std::string lun_name = freebsd::stringf("lun \"%s\"", name); 1292 return (uclparse_lun_entries(lun_name.c_str(), top)); 1293 } 1294 1295 static bool 1296 uclparse_lun_entries(const char *name, const ucl::Ucl &top) 1297 { 1298 for (const auto &obj : top) { 1299 std::string key = obj.key(); 1300 1301 if (key == "backend") { 1302 if (obj.type() != UCL_STRING) { 1303 log_warnx("\"backend\" property of %s " 1304 "is not a string", name); 1305 return false; 1306 } 1307 1308 if (!lun_set_backend(obj.string_value().c_str())) 1309 return false; 1310 } 1311 1312 if (key == "blocksize") { 1313 if (obj.type() != UCL_INT) { 1314 log_warnx("\"blocksize\" property of %s " 1315 "is not an integer", name); 1316 return false; 1317 } 1318 1319 if (!lun_set_blocksize(obj.int_value())) 1320 return false; 1321 } 1322 1323 if (key == "device-id") { 1324 if (obj.type() != UCL_STRING) { 1325 log_warnx("\"device-id\" property of %s " 1326 "is not an integer", name); 1327 return false; 1328 } 1329 1330 if (!lun_set_device_id(obj.string_value().c_str())) 1331 return false; 1332 } 1333 1334 if (key == "device-type") { 1335 if (obj.type() != UCL_STRING) { 1336 log_warnx("\"device-type\" property of %s " 1337 "is not an integer", name); 1338 return false; 1339 } 1340 1341 if (!lun_set_device_type(obj.string_value().c_str())) 1342 return false; 1343 } 1344 1345 if (key == "ctl-lun") { 1346 if (obj.type() != UCL_INT) { 1347 log_warnx("\"ctl-lun\" property of %s " 1348 "is not an integer", name); 1349 return false; 1350 } 1351 1352 if (!lun_set_ctl_lun(obj.int_value())) 1353 return false; 1354 } 1355 1356 if (key == "options") { 1357 if (obj.type() != UCL_OBJECT) { 1358 log_warnx("\"options\" property of %s " 1359 "is not an object", name); 1360 return false; 1361 } 1362 1363 for (const auto &child : obj) { 1364 if (!lun_add_option(child.key().c_str(), 1365 child.forced_string_value().c_str())) 1366 return false; 1367 } 1368 } 1369 1370 if (key == "path") { 1371 if (obj.type() != UCL_STRING) { 1372 log_warnx("\"path\" property of %s " 1373 "is not a string", name); 1374 return false; 1375 } 1376 1377 if (!lun_set_path(obj.string_value().c_str())) 1378 return false; 1379 } 1380 1381 if (key == "serial") { 1382 if (obj.type() != UCL_STRING) { 1383 log_warnx("\"serial\" property of %s " 1384 "is not a string", name); 1385 return false; 1386 } 1387 1388 if (!lun_set_serial(obj.string_value().c_str())) 1389 return false; 1390 } 1391 1392 if (key == "size") { 1393 if (obj.type() != UCL_INT) { 1394 log_warnx("\"size\" property of %s " 1395 "is not an integer", name); 1396 return false; 1397 } 1398 1399 if (!lun_set_size(obj.int_value())) 1400 return false; 1401 } 1402 } 1403 1404 return (true); 1405 } 1406 1407 bool 1408 uclparse_conf(const char *path) 1409 { 1410 std::string err; 1411 ucl::Ucl top = ucl::Ucl::parse_from_file(path, err); 1412 if (!top) { 1413 log_warnx("unable to parse configuration file %s: %s", path, 1414 err.c_str()); 1415 return (false); 1416 } 1417 1418 bool parsed; 1419 try { 1420 parsed = uclparse_toplevel(top); 1421 } catch (std::bad_alloc &) { 1422 log_warnx("failed to allocate memory parsing %s", path); 1423 parsed = false; 1424 } catch (...) { 1425 log_warnx("unknown exception parsing %s", path); 1426 parsed = false; 1427 } 1428 1429 return (parsed); 1430 } 1431