1 2 /* 3 * Copyright (C) Valentin V. Bartenev 4 * Copyright (C) NGINX, Inc. 5 */ 6 7 #include <nxt_main.h> 8 #include <nxt_conf.h> 9 #include <nxt_router.h> 10 11 12 typedef enum { 13 NXT_CONF_VLDT_NULL = 1 << NXT_CONF_NULL, 14 NXT_CONF_VLDT_BOOLEAN = 1 << NXT_CONF_BOOLEAN, 15 NXT_CONF_VLDT_INTEGER = 1 << NXT_CONF_INTEGER, 16 NXT_CONF_VLDT_NUMBER = 1 << NXT_CONF_NUMBER, 17 NXT_CONF_VLDT_STRING = 1 << NXT_CONF_STRING, 18 NXT_CONF_VLDT_ARRAY = 1 << NXT_CONF_ARRAY, 19 NXT_CONF_VLDT_OBJECT = 1 << NXT_CONF_OBJECT, 20 } nxt_conf_vldt_type_t; 21 22 23 typedef struct { 24 nxt_str_t name; 25 nxt_conf_vldt_type_t type; 26 nxt_int_t (*validator)(nxt_conf_validation_t *vldt, 27 nxt_conf_value_t *value, void *data); 28 void *data; 29 } nxt_conf_vldt_object_t; 30 31 32 #define NXT_CONF_VLDT_NEXT(f) { nxt_null_string, 0, NULL, (f) } 33 #define NXT_CONF_VLDT_END { nxt_null_string, 0, NULL, NULL } 34 35 36 typedef nxt_int_t (*nxt_conf_vldt_member_t)(nxt_conf_validation_t *vldt, 37 nxt_str_t *name, 38 nxt_conf_value_t *value); 39 40 typedef nxt_int_t (*nxt_conf_vldt_system_t)(nxt_conf_validation_t *vldt, 41 char *name); 42 43 44 static nxt_int_t nxt_conf_vldt_type(nxt_conf_validation_t *vldt, 45 nxt_str_t *name, nxt_conf_value_t *value, nxt_conf_vldt_type_t type); 46 static nxt_int_t nxt_conf_vldt_error(nxt_conf_validation_t *vldt, 47 const char *fmt, ...); 48 49 static nxt_int_t nxt_conf_vldt_listener(nxt_conf_validation_t *vldt, 50 nxt_str_t *name, nxt_conf_value_t *value); 51 static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt, 52 nxt_conf_value_t *value, void *data); 53 static nxt_int_t nxt_conf_vldt_app(nxt_conf_validation_t *vldt, 54 nxt_str_t *name, nxt_conf_value_t *value); 55 static nxt_int_t nxt_conf_vldt_object(nxt_conf_validation_t *vldt, 56 nxt_conf_value_t *value, void *data); 57 static nxt_int_t nxt_conf_vldt_processes(nxt_conf_validation_t *vldt, 58 nxt_conf_value_t *value, void *data); 59 static nxt_int_t nxt_conf_vldt_object_iterator(nxt_conf_validation_t *vldt, 60 nxt_conf_value_t *value, void *data); 61 static nxt_int_t nxt_conf_vldt_system(nxt_conf_validation_t *vldt, 62 nxt_conf_value_t *value, void *data); 63 static nxt_int_t nxt_conf_vldt_user(nxt_conf_validation_t *vldt, char *name); 64 static nxt_int_t nxt_conf_vldt_group(nxt_conf_validation_t *vldt, char *name); 65 66 67 static nxt_conf_vldt_object_t nxt_conf_vldt_root_members[] = { 68 { nxt_string("listeners"), 69 NXT_CONF_VLDT_OBJECT, 70 &nxt_conf_vldt_object_iterator, 71 (void *) &nxt_conf_vldt_listener }, 72 73 { nxt_string("applications"), 74 NXT_CONF_VLDT_OBJECT, 75 &nxt_conf_vldt_object_iterator, 76 (void *) &nxt_conf_vldt_app }, 77 78 NXT_CONF_VLDT_END 79 }; 80 81 82 static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = { 83 { nxt_string("application"), 84 NXT_CONF_VLDT_STRING, 85 &nxt_conf_vldt_app_name, 86 NULL }, 87 88 NXT_CONF_VLDT_END 89 }; 90 91 92 static nxt_conf_vldt_object_t nxt_conf_vldt_app_limits_members[] = { 93 { nxt_string("timeout"), 94 NXT_CONF_VLDT_INTEGER, 95 NULL, 96 NULL }, 97 98 { nxt_string("reschedule_timeout"), 99 NXT_CONF_VLDT_INTEGER, 100 NULL, 101 NULL }, 102 103 { nxt_string("requests"), 104 NXT_CONF_VLDT_INTEGER, 105 NULL, 106 NULL }, 107 108 NXT_CONF_VLDT_END 109 }; 110 111 112 static nxt_conf_vldt_object_t nxt_conf_vldt_app_processes_members[] = { 113 { nxt_string("spare"), 114 NXT_CONF_VLDT_INTEGER, 115 NULL, 116 NULL }, 117 118 { nxt_string("max"), 119 NXT_CONF_VLDT_INTEGER, 120 NULL, 121 NULL }, 122 123 { nxt_string("idle_timeout"), 124 NXT_CONF_VLDT_INTEGER, 125 NULL, 126 NULL }, 127 128 NXT_CONF_VLDT_END 129 }; 130 131 132 static nxt_conf_vldt_object_t nxt_conf_vldt_common_members[] = { 133 { nxt_string("type"), 134 NXT_CONF_VLDT_STRING, 135 NULL, 136 NULL }, 137 138 { nxt_string("limits"), 139 NXT_CONF_VLDT_OBJECT, 140 &nxt_conf_vldt_object, 141 (void *) &nxt_conf_vldt_app_limits_members }, 142 143 { nxt_string("processes"), 144 NXT_CONF_VLDT_INTEGER | NXT_CONF_VLDT_OBJECT, 145 &nxt_conf_vldt_processes, 146 (void *) &nxt_conf_vldt_app_processes_members }, 147 148 { nxt_string("user"), 149 NXT_CONF_VLDT_STRING, 150 nxt_conf_vldt_system, 151 (void *) &nxt_conf_vldt_user }, 152 153 { nxt_string("group"), 154 NXT_CONF_VLDT_STRING, 155 nxt_conf_vldt_system, 156 (void *) &nxt_conf_vldt_group }, 157 158 { nxt_string("working_directory"), 159 NXT_CONF_VLDT_STRING, 160 NULL, 161 NULL }, 162 163 NXT_CONF_VLDT_END 164 }; 165 166 167 static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { 168 { nxt_string("home"), 169 NXT_CONF_VLDT_STRING, 170 NULL, 171 NULL }, 172 173 { nxt_string("path"), 174 NXT_CONF_VLDT_STRING, 175 NULL, 176 NULL }, 177 178 { nxt_string("module"), 179 NXT_CONF_VLDT_STRING, 180 NULL, 181 NULL }, 182 183 NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) 184 }; 185 186 187 static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = { 188 { nxt_string("root"), 189 NXT_CONF_VLDT_STRING, 190 NULL, 191 NULL }, 192 193 { nxt_string("script"), 194 NXT_CONF_VLDT_STRING, 195 NULL, 196 NULL }, 197 198 { nxt_string("index"), 199 NXT_CONF_VLDT_STRING, 200 NULL, 201 NULL }, 202 203 NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) 204 }; 205 206 207 static nxt_conf_vldt_object_t nxt_conf_vldt_go_members[] = { 208 { nxt_string("executable"), 209 NXT_CONF_VLDT_STRING, 210 NULL, 211 NULL }, 212 213 NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) 214 }; 215 216 217 static nxt_conf_vldt_object_t nxt_conf_vldt_perl_members[] = { 218 { nxt_string("script"), 219 NXT_CONF_VLDT_STRING, 220 NULL, 221 NULL }, 222 223 NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) 224 }; 225 226 227 nxt_int_t 228 nxt_conf_validate(nxt_conf_validation_t *vldt) 229 { 230 nxt_int_t ret; 231 232 ret = nxt_conf_vldt_type(vldt, NULL, vldt->conf, NXT_CONF_VLDT_OBJECT); 233 234 if (ret != NXT_OK) { 235 return ret; 236 } 237 238 return nxt_conf_vldt_object(vldt, vldt->conf, nxt_conf_vldt_root_members); 239 } 240 241 242 #define NXT_CONF_VLDT_ANY_TYPE \ 243 "either a null, a boolean, an integer, " \ 244 "a number, a string, an array, or an object" 245 246 247 static nxt_int_t 248 nxt_conf_vldt_type(nxt_conf_validation_t *vldt, nxt_str_t *name, 249 nxt_conf_value_t *value, nxt_conf_vldt_type_t type) 250 { 251 u_char *p; 252 nxt_str_t expected; 253 nxt_bool_t serial; 254 nxt_uint_t value_type, n, t; 255 u_char buf[sizeof(NXT_CONF_VLDT_ANY_TYPE) - 1]; 256 257 static nxt_str_t type_name[] = { 258 nxt_string("a null"), 259 nxt_string("a boolean"), 260 nxt_string("an integer"), 261 nxt_string("a number"), 262 nxt_string("a string"), 263 nxt_string("an array"), 264 nxt_string("an object"), 265 }; 266 267 value_type = nxt_conf_type(value); 268 269 if ((1 << value_type) & type) { 270 return NXT_OK; 271 } 272 273 p = buf; 274 275 n = __builtin_popcount(type); 276 277 if (n > 1) { 278 p = nxt_cpymem(p, "either ", 7); 279 } 280 281 serial = (n > 2); 282 283 for ( ;; ) { 284 t = __builtin_ffs(type) - 1; 285 286 p = nxt_cpymem(p, type_name[t].start, type_name[t].length); 287 288 n--; 289 290 if (n == 0) { 291 break; 292 } 293 294 if (n > 1 || serial) { 295 *p++ = ','; 296 } 297 298 if (n == 1) { 299 p = nxt_cpymem(p, " or", 3); 300 } 301 302 *p++ = ' '; 303 304 type = type & ~(1 << t); 305 } 306 307 expected.length = p - buf; 308 expected.start = buf; 309 310 if (name == NULL) { 311 return nxt_conf_vldt_error(vldt, 312 "The configuration must be %V, but not %V.", 313 &expected, &type_name[value_type]); 314 } 315 316 return nxt_conf_vldt_error(vldt, 317 "The \"%V\" value must be %V, but not %V.", 318 name, &expected, &type_name[value_type]); 319 } 320 321 322 static nxt_int_t 323 nxt_conf_vldt_error(nxt_conf_validation_t *vldt, const char *fmt, ...) 324 { 325 u_char *p, *end; 326 size_t size; 327 va_list args; 328 u_char error[NXT_MAX_ERROR_STR]; 329 330 va_start(args, fmt); 331 end = nxt_vsprintf(error, error + NXT_MAX_ERROR_STR, fmt, args); 332 va_end(args); 333 334 size = end - error; 335 336 p = nxt_mp_nget(vldt->pool, size); 337 if (p == NULL) { 338 return NXT_ERROR; 339 } 340 341 nxt_memcpy(p, error, size); 342 343 vldt->error.length = size; 344 vldt->error.start = p; 345 346 return NXT_DECLINED; 347 } 348 349 350 static nxt_int_t 351 nxt_conf_vldt_listener(nxt_conf_validation_t *vldt, nxt_str_t *name, 352 nxt_conf_value_t *value) 353 { 354 nxt_int_t ret; 355 356 ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT); 357 358 if (ret != NXT_OK) { 359 return ret; 360 } 361 362 return nxt_conf_vldt_object(vldt, value, nxt_conf_vldt_listener_members); 363 } 364 365 366 static nxt_int_t 367 nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, 368 void *data) 369 { 370 nxt_str_t name; 371 nxt_conf_value_t *apps, *app; 372 373 static nxt_str_t apps_str = nxt_string("applications"); 374 375 nxt_conf_get_string(value, &name); 376 377 apps = nxt_conf_get_object_member(vldt->conf, &apps_str, NULL); 378 379 if (nxt_slow_path(apps == NULL)) { 380 goto error; 381 } 382 383 app = nxt_conf_get_object_member(apps, &name, NULL); 384 385 if (nxt_slow_path(app == NULL)) { 386 goto error; 387 } 388 389 return NXT_OK; 390 391 error: 392 393 return nxt_conf_vldt_error(vldt, "Listening socket is assigned for " 394 "a non existing application \"%V\".", 395 &name); 396 } 397 398 399 static nxt_int_t 400 nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name, 401 nxt_conf_value_t *value) 402 { 403 nxt_int_t ret; 404 nxt_str_t type; 405 nxt_thread_t *thread; 406 nxt_conf_value_t *type_value; 407 nxt_app_lang_module_t *lang; 408 409 static nxt_str_t type_str = nxt_string("type"); 410 411 static void *members[] = { 412 nxt_conf_vldt_python_members, 413 nxt_conf_vldt_php_members, 414 nxt_conf_vldt_go_members, 415 nxt_conf_vldt_perl_members, 416 }; 417 418 ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT); 419 420 if (ret != NXT_OK) { 421 return ret; 422 } 423 424 type_value = nxt_conf_get_object_member(value, &type_str, NULL); 425 426 if (type_value == NULL) { 427 return nxt_conf_vldt_error(vldt, 428 "Application must have the \"type\" property set."); 429 } 430 431 ret = nxt_conf_vldt_type(vldt, &type_str, type_value, NXT_CONF_VLDT_STRING); 432 433 if (ret != NXT_OK) { 434 return ret; 435 } 436 437 nxt_conf_get_string(type_value, &type); 438 439 thread = nxt_thread(); 440 441 lang = nxt_app_lang_module(thread->runtime, &type); 442 if (lang == NULL) { 443 return nxt_conf_vldt_error(vldt, 444 "The module to run \"%V\" is not found " 445 "among the available application modules.", 446 &type); 447 } 448 449 return nxt_conf_vldt_object(vldt, value, members[lang->type]); 450 } 451 452 453 static nxt_int_t 454 nxt_conf_vldt_object(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, 455 void *data) 456 { 457 uint32_t index; 458 nxt_int_t ret; 459 nxt_str_t name; 460 nxt_conf_value_t *member; 461 nxt_conf_vldt_object_t *vals; 462 463 index = 0; 464 465 for ( ;; ) { 466 member = nxt_conf_next_object_member(value, &name, &index); 467 468 if (member == NULL) { 469 return NXT_OK; 470 } 471 472 vals = data; 473 474 for ( ;; ) { 475 if (vals->name.length == 0) { 476 477 if (vals->data != NULL) { 478 vals = vals->data; 479 continue; 480 } 481 482 return nxt_conf_vldt_error(vldt, "Unknown parameter \"%V\".", 483 &name); 484 } 485 486 if (!nxt_strstr_eq(&vals->name, &name)) { 487 vals++; 488 continue; 489 } 490 491 ret = nxt_conf_vldt_type(vldt, &name, member, vals->type); 492 493 if (ret != NXT_OK) { 494 return ret; 495 } 496 497 if (vals->validator != NULL) { 498 ret = vals->validator(vldt, member, vals->data); 499 500 if (ret != NXT_OK) { 501 return ret; 502 } 503 } 504 505 break; 506 } 507 } 508 } 509 510 511 typedef struct { 512 int64_t spare; 513 int64_t max; 514 int64_t idle_timeout; 515 } nxt_conf_vldt_processes_conf_t; 516 517 518 static nxt_conf_map_t nxt_conf_vldt_processes_conf_map[] = { 519 { 520 nxt_string("spare"), 521 NXT_CONF_MAP_INT64, 522 offsetof(nxt_conf_vldt_processes_conf_t, spare), 523 }, 524 525 { 526 nxt_string("max"), 527 NXT_CONF_MAP_INT64, 528 offsetof(nxt_conf_vldt_processes_conf_t, max), 529 }, 530 531 { 532 nxt_string("idle_timeout"), 533 NXT_CONF_MAP_INT64, 534 offsetof(nxt_conf_vldt_processes_conf_t, idle_timeout), 535 }, 536 }; 537 538 539 static nxt_int_t 540 nxt_conf_vldt_processes(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, 541 void *data) 542 { 543 int64_t int_value; 544 nxt_int_t ret; 545 nxt_conf_vldt_processes_conf_t proc; 546 547 static nxt_str_t max_str = nxt_string("max"); 548 549 if (nxt_conf_type(value) == NXT_CONF_INTEGER) { 550 int_value = nxt_conf_get_integer(value); 551 552 if (int_value < 1) { 553 return nxt_conf_vldt_error(vldt, "The \"processes\" number must be " 554 "equal to or greater than 1."); 555 } 556 557 if (int_value > NXT_INT32_T_MAX) { 558 return nxt_conf_vldt_error(vldt, "The \"processes\" number must " 559 "not exceed %d.", NXT_INT32_T_MAX); 560 } 561 562 return NXT_OK; 563 } 564 565 ret = nxt_conf_vldt_object(vldt, value, data); 566 if (ret != NXT_OK) { 567 return ret; 568 } 569 570 proc.spare = 1; 571 proc.max = 1; 572 proc.idle_timeout = 15; 573 574 ret = nxt_conf_map_object(vldt->pool, value, 575 nxt_conf_vldt_processes_conf_map, 576 nxt_nitems(nxt_conf_vldt_processes_conf_map), 577 &proc); 578 if (ret != NXT_OK) { 579 return ret; 580 } 581 582 if (proc.spare < 0) { 583 return nxt_conf_vldt_error(vldt, "The \"spare\" number must not be " 584 "negative."); 585 } 586 587 if (proc.spare > NXT_INT32_T_MAX) { 588 return nxt_conf_vldt_error(vldt, "The \"spare\" number must not " 589 "not exceed %d.", NXT_INT32_T_MAX); 590 } 591 592 if (nxt_conf_get_object_member(value, &max_str, NULL) != NULL) { 593 594 if (proc.max < 1) { 595 return nxt_conf_vldt_error(vldt, "The \"max\" number must be equal " 596 "to or greater than 1."); 597 } 598 599 if (proc.max > NXT_INT32_T_MAX) { 600 return nxt_conf_vldt_error(vldt, "The \"max\" number must not " 601 "not exceed %d.", NXT_INT32_T_MAX); 602 } 603 604 if (proc.max < proc.spare) { 605 return nxt_conf_vldt_error(vldt, "The \"spare\" number must be " 606 "lower than \"max\"."); 607 } 608 } 609 610 if (proc.idle_timeout < 0) { 611 return nxt_conf_vldt_error(vldt, "The \"idle_timeout\" number must not " 612 "be negative."); 613 } 614 615 if (proc.idle_timeout > NXT_INT32_T_MAX / 1000) { 616 return nxt_conf_vldt_error(vldt, "The \"idle_timeout\" number must not " 617 "not exceed %d.", NXT_INT32_T_MAX / 1000); 618 } 619 620 return NXT_OK; 621 } 622 623 624 static nxt_int_t 625 nxt_conf_vldt_object_iterator(nxt_conf_validation_t *vldt, 626 nxt_conf_value_t *value, void *data) 627 { 628 uint32_t index; 629 nxt_int_t ret; 630 nxt_str_t name; 631 nxt_conf_value_t *member; 632 nxt_conf_vldt_member_t validator; 633 634 validator = (nxt_conf_vldt_member_t) data; 635 index = 0; 636 637 for ( ;; ) { 638 member = nxt_conf_next_object_member(value, &name, &index); 639 640 if (member == NULL) { 641 return NXT_OK; 642 } 643 644 ret = validator(vldt, &name, member); 645 646 if (ret != NXT_OK) { 647 return ret; 648 } 649 } 650 } 651 652 653 static nxt_int_t 654 nxt_conf_vldt_system(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, 655 void *data) 656 { 657 size_t length; 658 nxt_str_t name; 659 nxt_conf_vldt_system_t validator; 660 char string[32]; 661 662 /* The cast is required by Sun C. */ 663 validator = (nxt_conf_vldt_system_t) data; 664 665 nxt_conf_get_string(value, &name); 666 667 length = name.length + 1; 668 length = nxt_min(length, sizeof(string)); 669 670 nxt_cpystrn((u_char *) string, name.start, length); 671 672 return validator(vldt, string); 673 } 674 675 676 static nxt_int_t 677 nxt_conf_vldt_user(nxt_conf_validation_t *vldt, char *user) 678 { 679 struct passwd *pwd; 680 681 nxt_errno = 0; 682 683 pwd = getpwnam(user); 684 685 if (pwd != NULL) { 686 return NXT_OK; 687 } 688 689 if (nxt_errno == 0) { 690 return nxt_conf_vldt_error(vldt, "User \"%s\" is not found.", user); 691 } 692 693 return NXT_ERROR; 694 } 695 696 697 static nxt_int_t 698 nxt_conf_vldt_group(nxt_conf_validation_t *vldt, char *group) 699 { 700 struct group *grp; 701 702 nxt_errno = 0; 703 704 grp = getgrnam(group); 705 706 if (grp != NULL) { 707 return NXT_OK; 708 } 709 710 if (nxt_errno == 0) { 711 return nxt_conf_vldt_error(vldt, "Group \"%s\" is not found.", group); 712 } 713 714 return NXT_ERROR; 715 } 716 717 718