1/* $NetBSD$ */ 2 3/* 4 * Copyright (c) 1997 - 2008 Kungliga Tekniska H��gskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * All rights reserved. 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 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 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 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "iprop.h" 37#include <krb5/rtbl.h> 38 39static krb5_log_facility *log_facility; 40 41const char *slave_stats_file; 42const char *slave_time_missing = "2 min"; 43const char *slave_time_gone = "5 min"; 44 45static int time_before_missing; 46static int time_before_gone; 47 48const char *master_hostname; 49 50static krb5_socket_t 51make_signal_socket (krb5_context context) 52{ 53#ifndef NO_UNIX_SOCKETS 54 struct sockaddr_un addr; 55 const char *fn; 56 krb5_socket_t fd; 57 58 fn = kadm5_log_signal_socket(context); 59 60 fd = socket (AF_UNIX, SOCK_DGRAM, 0); 61 if (fd < 0) 62 krb5_err (context, 1, errno, "socket AF_UNIX"); 63 memset (&addr, 0, sizeof(addr)); 64 addr.sun_family = AF_UNIX; 65 strlcpy (addr.sun_path, fn, sizeof(addr.sun_path)); 66 unlink (addr.sun_path); 67 if (bind (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 68 krb5_err (context, 1, errno, "bind %s", addr.sun_path); 69 return fd; 70#else 71 struct addrinfo *ai = NULL; 72 krb5_socket_t fd; 73 74 kadm5_log_signal_socket_info(context, 1, &ai); 75 76 fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 77 if (rk_IS_BAD_SOCKET(fd)) 78 krb5_err (context, 1, rk_SOCK_ERRNO, "socket AF=%d", ai->ai_family); 79 80 if (rk_IS_SOCKET_ERROR( bind (fd, ai->ai_addr, ai->ai_addrlen) )) 81 krb5_err (context, 1, rk_SOCK_ERRNO, "bind"); 82 return fd; 83#endif 84} 85 86static krb5_socket_t 87make_listen_socket (krb5_context context, const char *port_str) 88{ 89 krb5_socket_t fd; 90 int one = 1; 91 struct sockaddr_in addr; 92 93 fd = socket (AF_INET, SOCK_STREAM, 0); 94 if (rk_IS_BAD_SOCKET(fd)) 95 krb5_err (context, 1, rk_SOCK_ERRNO, "socket AF_INET"); 96 setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); 97 memset (&addr, 0, sizeof(addr)); 98 addr.sin_family = AF_INET; 99 100 if (port_str) { 101 addr.sin_port = krb5_getportbyname (context, 102 port_str, "tcp", 103 0); 104 if (addr.sin_port == 0) { 105 char *ptr; 106 long port; 107 108 port = strtol (port_str, &ptr, 10); 109 if (port == 0 && ptr == port_str) 110 krb5_errx (context, 1, "bad port `%s'", port_str); 111 addr.sin_port = htons(port); 112 } 113 } else { 114 addr.sin_port = krb5_getportbyname (context, IPROP_SERVICE, 115 "tcp", IPROP_PORT); 116 } 117 if(bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 118 krb5_err (context, 1, errno, "bind"); 119 if (listen(fd, SOMAXCONN) < 0) 120 krb5_err (context, 1, errno, "listen"); 121 return fd; 122} 123 124struct slave { 125 krb5_socket_t fd; 126 struct sockaddr_in addr; 127 char *name; 128 krb5_auth_context ac; 129 uint32_t version; 130 time_t seen; 131 unsigned long flags; 132#define SLAVE_F_DEAD 0x1 133#define SLAVE_F_AYT 0x2 134 struct slave *next; 135}; 136 137typedef struct slave slave; 138 139static int 140check_acl (krb5_context context, const char *name) 141{ 142 const char *fn; 143 FILE *fp; 144 char buf[256]; 145 int ret = 1; 146 char *slavefile; 147 148 asprintf(&slavefile, "%s/slaves", hdb_db_dir(context)); 149 150 fn = krb5_config_get_string_default(context, 151 NULL, 152 slavefile, 153 "kdc", 154 "iprop-acl", 155 NULL); 156 157 fp = fopen (fn, "r"); 158 free(slavefile); 159 if (fp == NULL) 160 return 1; 161 while (fgets(buf, sizeof(buf), fp) != NULL) { 162 buf[strcspn(buf, "\r\n")] = '\0'; 163 if (strcmp (buf, name) == 0) { 164 ret = 0; 165 break; 166 } 167 } 168 fclose (fp); 169 return ret; 170} 171 172static void 173slave_seen(slave *s) 174{ 175 s->flags &= ~SLAVE_F_AYT; 176 s->seen = time(NULL); 177} 178 179static int 180slave_missing_p (slave *s) 181{ 182 if (time(NULL) > s->seen + time_before_missing) 183 return 1; 184 return 0; 185} 186 187static int 188slave_gone_p (slave *s) 189{ 190 if (time(NULL) > s->seen + time_before_gone) 191 return 1; 192 return 0; 193} 194 195static void 196slave_dead(krb5_context context, slave *s) 197{ 198 krb5_warnx(context, "slave %s dead", s->name); 199 200 if (!rk_IS_BAD_SOCKET(s->fd)) { 201 rk_closesocket (s->fd); 202 s->fd = rk_INVALID_SOCKET; 203 } 204 s->flags |= SLAVE_F_DEAD; 205 slave_seen(s); 206} 207 208static void 209remove_slave (krb5_context context, slave *s, slave **root) 210{ 211 slave **p; 212 213 if (!rk_IS_BAD_SOCKET(s->fd)) 214 rk_closesocket (s->fd); 215 if (s->name) 216 free (s->name); 217 if (s->ac) 218 krb5_auth_con_free (context, s->ac); 219 220 for (p = root; *p; p = &(*p)->next) 221 if (*p == s) { 222 *p = s->next; 223 break; 224 } 225 free (s); 226} 227 228static void 229add_slave (krb5_context context, krb5_keytab keytab, slave **root, 230 krb5_socket_t fd) 231{ 232 krb5_principal server; 233 krb5_error_code ret; 234 slave *s; 235 socklen_t addr_len; 236 krb5_ticket *ticket = NULL; 237 char hostname[128]; 238 239 s = malloc(sizeof(*s)); 240 if (s == NULL) { 241 krb5_warnx (context, "add_slave: no memory"); 242 return; 243 } 244 s->name = NULL; 245 s->ac = NULL; 246 247 addr_len = sizeof(s->addr); 248 s->fd = accept (fd, (struct sockaddr *)&s->addr, &addr_len); 249 if (rk_IS_BAD_SOCKET(s->fd)) { 250 krb5_warn (context, rk_SOCK_ERRNO, "accept"); 251 goto error; 252 } 253 if (master_hostname) 254 strlcpy(hostname, master_hostname, sizeof(hostname)); 255 else 256 gethostname(hostname, sizeof(hostname)); 257 258 ret = krb5_sname_to_principal (context, hostname, IPROP_NAME, 259 KRB5_NT_SRV_HST, &server); 260 if (ret) { 261 krb5_warn (context, ret, "krb5_sname_to_principal"); 262 goto error; 263 } 264 265 ret = krb5_recvauth (context, &s->ac, &s->fd, 266 IPROP_VERSION, server, 0, keytab, &ticket); 267 krb5_free_principal (context, server); 268 if (ret) { 269 krb5_warn (context, ret, "krb5_recvauth"); 270 goto error; 271 } 272 ret = krb5_unparse_name (context, ticket->client, &s->name); 273 krb5_free_ticket (context, ticket); 274 if (ret) { 275 krb5_warn (context, ret, "krb5_unparse_name"); 276 goto error; 277 } 278 if (check_acl (context, s->name)) { 279 krb5_warnx (context, "%s not in acl", s->name); 280 goto error; 281 } 282 283 { 284 slave *l = *root; 285 286 while (l) { 287 if (strcmp(l->name, s->name) == 0) 288 break; 289 l = l->next; 290 } 291 if (l) { 292 if (l->flags & SLAVE_F_DEAD) { 293 remove_slave(context, l, root); 294 } else { 295 krb5_warnx (context, "second connection from %s", s->name); 296 goto error; 297 } 298 } 299 } 300 301 krb5_warnx (context, "connection from %s", s->name); 302 303 s->version = 0; 304 s->flags = 0; 305 slave_seen(s); 306 s->next = *root; 307 *root = s; 308 return; 309error: 310 remove_slave(context, s, root); 311} 312 313struct prop_context { 314 krb5_auth_context auth_context; 315 krb5_socket_t fd; 316}; 317 318static int 319prop_one (krb5_context context, HDB *db, hdb_entry_ex *entry, void *v) 320{ 321 krb5_error_code ret; 322 krb5_storage *sp; 323 krb5_data data; 324 struct slave *s = (struct slave *)v; 325 326 ret = hdb_entry2value (context, &entry->entry, &data); 327 if (ret) 328 return ret; 329 ret = krb5_data_realloc (&data, data.length + 4); 330 if (ret) { 331 krb5_data_free (&data); 332 return ret; 333 } 334 memmove ((char *)data.data + 4, data.data, data.length - 4); 335 sp = krb5_storage_from_data(&data); 336 if (sp == NULL) { 337 krb5_data_free (&data); 338 return ENOMEM; 339 } 340 krb5_store_int32(sp, ONE_PRINC); 341 krb5_storage_free(sp); 342 343 ret = krb5_write_priv_message (context, s->ac, &s->fd, &data); 344 krb5_data_free (&data); 345 return ret; 346} 347 348static int 349send_complete (krb5_context context, slave *s, 350 const char *database, uint32_t current_version) 351{ 352 krb5_error_code ret; 353 krb5_storage *sp; 354 HDB *db; 355 krb5_data data; 356 char buf[8]; 357 358 ret = hdb_create (context, &db, database); 359 if (ret) 360 krb5_err (context, 1, ret, "hdb_create: %s", database); 361 ret = db->hdb_open (context, db, O_RDONLY, 0); 362 if (ret) 363 krb5_err (context, 1, ret, "db->open"); 364 365 sp = krb5_storage_from_mem (buf, 4); 366 if (sp == NULL) 367 krb5_errx (context, 1, "krb5_storage_from_mem"); 368 krb5_store_int32 (sp, TELL_YOU_EVERYTHING); 369 krb5_storage_free (sp); 370 371 data.data = buf; 372 data.length = 4; 373 374 ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 375 376 if (ret) { 377 krb5_warn (context, ret, "krb5_write_priv_message"); 378 slave_dead(context, s); 379 return ret; 380 } 381 382 ret = hdb_foreach (context, db, HDB_F_ADMIN_DATA, prop_one, s); 383 if (ret) { 384 krb5_warn (context, ret, "hdb_foreach"); 385 slave_dead(context, s); 386 return ret; 387 } 388 389 (*db->hdb_close)(context, db); 390 (*db->hdb_destroy)(context, db); 391 392 sp = krb5_storage_from_mem (buf, 8); 393 if (sp == NULL) 394 krb5_errx (context, 1, "krb5_storage_from_mem"); 395 krb5_store_int32 (sp, NOW_YOU_HAVE); 396 krb5_store_int32 (sp, current_version); 397 krb5_storage_free (sp); 398 399 data.length = 8; 400 401 s->version = current_version; 402 403 ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 404 if (ret) { 405 slave_dead(context, s); 406 krb5_warn (context, ret, "krb5_write_priv_message"); 407 return ret; 408 } 409 410 slave_seen(s); 411 412 return 0; 413} 414 415static int 416send_are_you_there (krb5_context context, slave *s) 417{ 418 krb5_storage *sp; 419 krb5_data data; 420 char buf[4]; 421 int ret; 422 423 if (s->flags & (SLAVE_F_DEAD|SLAVE_F_AYT)) 424 return 0; 425 426 krb5_warnx(context, "slave %s missing, sending AYT", s->name); 427 428 s->flags |= SLAVE_F_AYT; 429 430 data.data = buf; 431 data.length = 4; 432 433 sp = krb5_storage_from_mem (buf, 4); 434 if (sp == NULL) { 435 krb5_warnx (context, "are_you_there: krb5_data_alloc"); 436 slave_dead(context, s); 437 return 1; 438 } 439 krb5_store_int32 (sp, ARE_YOU_THERE); 440 krb5_storage_free (sp); 441 442 ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 443 444 if (ret) { 445 krb5_warn (context, ret, "are_you_there: krb5_write_priv_message"); 446 slave_dead(context, s); 447 return 1; 448 } 449 450 return 0; 451} 452 453static int 454send_diffs (krb5_context context, slave *s, int log_fd, 455 const char *database, uint32_t current_version) 456{ 457 krb5_storage *sp; 458 uint32_t ver; 459 time_t timestamp; 460 enum kadm_ops op; 461 uint32_t len; 462 off_t right, left; 463 krb5_data data; 464 int ret = 0; 465 466 if (s->version == current_version) { 467 krb5_warnx(context, "slave %s in sync already at version %ld", 468 s->name, (long)s->version); 469 return 0; 470 } 471 472 if (s->flags & SLAVE_F_DEAD) 473 return 0; 474 475 /* if slave is a fresh client, starting over */ 476 if (s->version == 0) { 477 krb5_warnx(context, "sending complete log to fresh slave %s", 478 s->name); 479 return send_complete (context, s, database, current_version); 480 } 481 482 sp = kadm5_log_goto_end (log_fd); 483 right = krb5_storage_seek(sp, 0, SEEK_CUR); 484 for (;;) { 485 ret = kadm5_log_previous (context, sp, &ver, ×tamp, &op, &len); 486 if (ret) 487 krb5_err(context, 1, ret, 488 "send_diffs: failed to find previous entry"); 489 left = krb5_storage_seek(sp, -16, SEEK_CUR); 490 if (ver == s->version) 491 return 0; 492 if (ver == s->version + 1) 493 break; 494 if (left == 0) { 495 krb5_storage_free(sp); 496 krb5_warnx(context, 497 "slave %s (version %lu) out of sync with master " 498 "(first version in log %lu), sending complete database", 499 s->name, (unsigned long)s->version, (unsigned long)ver); 500 return send_complete (context, s, database, current_version); 501 } 502 } 503 504 krb5_warnx(context, 505 "syncing slave %s from version %lu to version %lu", 506 s->name, (unsigned long)s->version, 507 (unsigned long)current_version); 508 509 ret = krb5_data_alloc (&data, right - left + 4); 510 if (ret) { 511 krb5_storage_free(sp); 512 krb5_warn (context, ret, "send_diffs: krb5_data_alloc"); 513 slave_dead(context, s); 514 return 1; 515 } 516 krb5_storage_read (sp, (char *)data.data + 4, data.length - 4); 517 krb5_storage_free(sp); 518 519 sp = krb5_storage_from_data (&data); 520 if (sp == NULL) { 521 krb5_warnx (context, "send_diffs: krb5_storage_from_data"); 522 slave_dead(context, s); 523 return 1; 524 } 525 krb5_store_int32 (sp, FOR_YOU); 526 krb5_storage_free(sp); 527 528 ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 529 krb5_data_free(&data); 530 531 if (ret) { 532 krb5_warn (context, ret, "send_diffs: krb5_write_priv_message"); 533 slave_dead(context, s); 534 return 1; 535 } 536 slave_seen(s); 537 538 s->version = current_version; 539 540 return 0; 541} 542 543static int 544process_msg (krb5_context context, slave *s, int log_fd, 545 const char *database, uint32_t current_version) 546{ 547 int ret = 0; 548 krb5_data out; 549 krb5_storage *sp; 550 int32_t tmp; 551 552 ret = krb5_read_priv_message(context, s->ac, &s->fd, &out); 553 if(ret) { 554 krb5_warn (context, ret, "error reading message from %s", s->name); 555 return 1; 556 } 557 558 sp = krb5_storage_from_mem (out.data, out.length); 559 if (sp == NULL) { 560 krb5_warnx (context, "process_msg: no memory"); 561 krb5_data_free (&out); 562 return 1; 563 } 564 if (krb5_ret_int32 (sp, &tmp) != 0) { 565 krb5_warnx (context, "process_msg: client send too short command"); 566 krb5_data_free (&out); 567 return 1; 568 } 569 switch (tmp) { 570 case I_HAVE : 571 ret = krb5_ret_int32 (sp, &tmp); 572 if (ret != 0) { 573 krb5_warnx (context, "process_msg: client send too I_HAVE data"); 574 break; 575 } 576 /* new started slave that have old log */ 577 if (s->version == 0 && tmp != 0) { 578 if (current_version < tmp) { 579 krb5_warnx (context, "Slave %s (version %lu) have later version " 580 "the master (version %lu) OUT OF SYNC", 581 s->name, (unsigned long)tmp, 582 (unsigned long)current_version); 583 } 584 s->version = tmp; 585 } 586 if (tmp < s->version) { 587 krb5_warnx (context, "Slave claims to not have " 588 "version we already sent to it"); 589 } else { 590 ret = send_diffs (context, s, log_fd, database, current_version); 591 } 592 break; 593 case I_AM_HERE : 594 break; 595 case ARE_YOU_THERE: 596 case FOR_YOU : 597 default : 598 krb5_warnx (context, "Ignoring command %d", tmp); 599 break; 600 } 601 602 krb5_data_free (&out); 603 krb5_storage_free (sp); 604 605 slave_seen(s); 606 607 return ret; 608} 609 610#define SLAVE_NAME "Name" 611#define SLAVE_ADDRESS "Address" 612#define SLAVE_VERSION "Version" 613#define SLAVE_STATUS "Status" 614#define SLAVE_SEEN "Last Seen" 615 616static FILE * 617open_stats(krb5_context context) 618{ 619 char *statfile = NULL; 620 const char *fn; 621 FILE *f; 622 623 if (slave_stats_file) 624 fn = slave_stats_file; 625 else { 626 asprintf(&statfile, "%s/slaves-stats", hdb_db_dir(context)); 627 fn = krb5_config_get_string_default(context, 628 NULL, 629 statfile, 630 "kdc", 631 "iprop-stats", 632 NULL); 633 } 634 f = fopen(fn, "w"); 635 if (statfile) 636 free(statfile); 637 638 return f; 639} 640 641static void 642write_master_down(krb5_context context) 643{ 644 char str[100]; 645 time_t t = time(NULL); 646 FILE *fp; 647 648 fp = open_stats(context); 649 if (fp == NULL) 650 return; 651 krb5_format_time(context, t, str, sizeof(str), TRUE); 652 fprintf(fp, "master down at %s\n", str); 653 654 fclose(fp); 655} 656 657static void 658write_stats(krb5_context context, slave *slaves, uint32_t current_version) 659{ 660 char str[100]; 661 rtbl_t tbl; 662 time_t t = time(NULL); 663 FILE *fp; 664 665 fp = open_stats(context); 666 if (fp == NULL) 667 return; 668 669 krb5_format_time(context, t, str, sizeof(str), TRUE); 670 fprintf(fp, "Status for slaves, last updated: %s\n\n", str); 671 672 fprintf(fp, "Master version: %lu\n\n", (unsigned long)current_version); 673 674 tbl = rtbl_create(); 675 if (tbl == NULL) { 676 fclose(fp); 677 return; 678 } 679 680 rtbl_add_column(tbl, SLAVE_NAME, 0); 681 rtbl_add_column(tbl, SLAVE_ADDRESS, 0); 682 rtbl_add_column(tbl, SLAVE_VERSION, RTBL_ALIGN_RIGHT); 683 rtbl_add_column(tbl, SLAVE_STATUS, 0); 684 rtbl_add_column(tbl, SLAVE_SEEN, 0); 685 686 rtbl_set_prefix(tbl, " "); 687 rtbl_set_column_prefix(tbl, SLAVE_NAME, ""); 688 689 while (slaves) { 690 krb5_address addr; 691 krb5_error_code ret; 692 rtbl_add_column_entry(tbl, SLAVE_NAME, slaves->name); 693 ret = krb5_sockaddr2address (context, 694 (struct sockaddr*)&slaves->addr, &addr); 695 if(ret == 0) { 696 krb5_print_address(&addr, str, sizeof(str), NULL); 697 krb5_free_address(context, &addr); 698 rtbl_add_column_entry(tbl, SLAVE_ADDRESS, str); 699 } else 700 rtbl_add_column_entry(tbl, SLAVE_ADDRESS, "<unknown>"); 701 702 snprintf(str, sizeof(str), "%u", (unsigned)slaves->version); 703 rtbl_add_column_entry(tbl, SLAVE_VERSION, str); 704 705 if (slaves->flags & SLAVE_F_DEAD) 706 rtbl_add_column_entry(tbl, SLAVE_STATUS, "Down"); 707 else 708 rtbl_add_column_entry(tbl, SLAVE_STATUS, "Up"); 709 710 ret = krb5_format_time(context, slaves->seen, str, sizeof(str), TRUE); 711 rtbl_add_column_entry(tbl, SLAVE_SEEN, str); 712 713 slaves = slaves->next; 714 } 715 716 rtbl_format(tbl, fp); 717 rtbl_destroy(tbl); 718 719 fclose(fp); 720} 721 722 723static char *realm; 724static int version_flag; 725static int help_flag; 726static char *keytab_str = "HDB:"; 727static char *database; 728static char *config_file; 729static char *port_str; 730#ifdef SUPPORT_DETACH 731static int detach_from_console = 0; 732#endif 733 734static struct getargs args[] = { 735 { "config-file", 'c', arg_string, &config_file }, 736 { "realm", 'r', arg_string, &realm }, 737 { "keytab", 'k', arg_string, &keytab_str, 738 "keytab to get authentication from", "kspec" }, 739 { "database", 'd', arg_string, &database, "database", "file"}, 740 { "slave-stats-file", 0, arg_string, rk_UNCONST(&slave_stats_file), 741 "file for slave status information", "file"}, 742 { "time-missing", 0, arg_string, rk_UNCONST(&slave_time_missing), 743 "time before slave is polled for presence", "time"}, 744 { "time-gone", 0, arg_string, rk_UNCONST(&slave_time_gone), 745 "time of inactivity after which a slave is considered gone", "time"}, 746 { "port", 0, arg_string, &port_str, 747 "port ipropd will listen to", "port"}, 748#ifdef SUPPORT_DETACH 749 { "detach", 0, arg_flag, &detach_from_console, 750 "detach from console" }, 751#endif 752 { "hostname", 0, arg_string, rk_UNCONST(&master_hostname), 753 "hostname of master (if not same as hostname)", "hostname" }, 754 { "version", 0, arg_flag, &version_flag }, 755 { "help", 0, arg_flag, &help_flag } 756}; 757static int num_args = sizeof(args) / sizeof(args[0]); 758 759int 760main(int argc, char **argv) 761{ 762 krb5_error_code ret; 763 krb5_context context; 764 void *kadm_handle; 765 kadm5_server_context *server_context; 766 kadm5_config_params conf; 767 krb5_socket_t signal_fd, listen_fd; 768 int log_fd; 769 slave *slaves = NULL; 770 uint32_t current_version = 0, old_version = 0; 771 krb5_keytab keytab; 772 int optidx; 773 char **files; 774 775 optidx = krb5_program_setup(&context, argc, argv, args, num_args, NULL); 776 777 if(help_flag) 778 krb5_std_usage(0, args, num_args); 779 if(version_flag) { 780 print_version(NULL); 781 exit(0); 782 } 783 784 setup_signal(); 785 786 if (config_file == NULL) { 787 asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)); 788 if (config_file == NULL) 789 errx(1, "out of memory"); 790 } 791 792 ret = krb5_prepend_config_files_default(config_file, &files); 793 if (ret) 794 krb5_err(context, 1, ret, "getting configuration files"); 795 796 ret = krb5_set_config_files(context, files); 797 krb5_free_config_files(files); 798 if (ret) 799 krb5_err(context, 1, ret, "reading configuration files"); 800 801 time_before_gone = parse_time (slave_time_gone, "s"); 802 if (time_before_gone < 0) 803 krb5_errx (context, 1, "couldn't parse time: %s", slave_time_gone); 804 time_before_missing = parse_time (slave_time_missing, "s"); 805 if (time_before_missing < 0) 806 krb5_errx (context, 1, "couldn't parse time: %s", slave_time_missing); 807 808#ifdef SUPPORT_DETACH 809 if (detach_from_console) 810 daemon(0, 0); 811#endif 812 pidfile (NULL); 813 krb5_openlog (context, "ipropd-master", &log_facility); 814 krb5_set_warn_dest(context, log_facility); 815 816 ret = krb5_kt_register(context, &hdb_kt_ops); 817 if(ret) 818 krb5_err(context, 1, ret, "krb5_kt_register"); 819 820 ret = krb5_kt_resolve(context, keytab_str, &keytab); 821 if(ret) 822 krb5_err(context, 1, ret, "krb5_kt_resolve: %s", keytab_str); 823 824 memset(&conf, 0, sizeof(conf)); 825 if(realm) { 826 conf.mask |= KADM5_CONFIG_REALM; 827 conf.realm = realm; 828 } 829 ret = kadm5_init_with_skey_ctx (context, 830 KADM5_ADMIN_SERVICE, 831 NULL, 832 KADM5_ADMIN_SERVICE, 833 &conf, 0, 0, 834 &kadm_handle); 835 if (ret) 836 krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); 837 838 server_context = (kadm5_server_context *)kadm_handle; 839 840 log_fd = open (server_context->log_context.log_file, O_RDONLY, 0); 841 if (log_fd < 0) 842 krb5_err (context, 1, errno, "open %s", 843 server_context->log_context.log_file); 844 845 signal_fd = make_signal_socket (context); 846 listen_fd = make_listen_socket (context, port_str); 847 848 kadm5_log_get_version_fd (log_fd, ¤t_version); 849 850 krb5_warnx(context, "ipropd-master started at version: %lu", 851 (unsigned long)current_version); 852 853 while(exit_flag == 0){ 854 slave *p; 855 fd_set readset; 856 int max_fd = 0; 857 struct timeval to = {30, 0}; 858 uint32_t vers; 859 860#ifndef NO_LIMIT_FD_SETSIZE 861 if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE) 862 krb5_errx (context, 1, "fd too large"); 863#endif 864 865 FD_ZERO(&readset); 866 FD_SET(signal_fd, &readset); 867 max_fd = max(max_fd, signal_fd); 868 FD_SET(listen_fd, &readset); 869 max_fd = max(max_fd, listen_fd); 870 871 for (p = slaves; p != NULL; p = p->next) { 872 if (p->flags & SLAVE_F_DEAD) 873 continue; 874 FD_SET(p->fd, &readset); 875 max_fd = max(max_fd, p->fd); 876 } 877 878 ret = select (max_fd + 1, 879 &readset, NULL, NULL, &to); 880 if (ret < 0) { 881 if (errno == EINTR) 882 continue; 883 else 884 krb5_err (context, 1, errno, "select"); 885 } 886 887 if (ret == 0) { 888 old_version = current_version; 889 kadm5_log_get_version_fd (log_fd, ¤t_version); 890 891 if (current_version > old_version) { 892 krb5_warnx(context, 893 "Missed a signal, updating slaves %lu to %lu", 894 (unsigned long)old_version, 895 (unsigned long)current_version); 896 for (p = slaves; p != NULL; p = p->next) { 897 if (p->flags & SLAVE_F_DEAD) 898 continue; 899 send_diffs (context, p, log_fd, database, current_version); 900 } 901 } 902 } 903 904 if (ret && FD_ISSET(signal_fd, &readset)) { 905#ifndef NO_UNIX_SOCKETS 906 struct sockaddr_un peer_addr; 907#else 908 struct sockaddr_storage peer_addr; 909#endif 910 socklen_t peer_len = sizeof(peer_addr); 911 912 if(recvfrom(signal_fd, (void *)&vers, sizeof(vers), 0, 913 (struct sockaddr *)&peer_addr, &peer_len) < 0) { 914 krb5_warn (context, errno, "recvfrom"); 915 continue; 916 } 917 --ret; 918 assert(ret >= 0); 919 old_version = current_version; 920 kadm5_log_get_version_fd (log_fd, ¤t_version); 921 if (current_version > old_version) { 922 krb5_warnx(context, 923 "Got a signal, updating slaves %lu to %lu", 924 (unsigned long)old_version, 925 (unsigned long)current_version); 926 for (p = slaves; p != NULL; p = p->next) { 927 if (p->flags & SLAVE_F_DEAD) 928 continue; 929 send_diffs (context, p, log_fd, database, current_version); 930 } 931 } else { 932 krb5_warnx(context, 933 "Got a signal, but no update in log version %lu", 934 (unsigned long)current_version); 935 } 936 } 937 938 for(p = slaves; p != NULL; p = p->next) { 939 if (p->flags & SLAVE_F_DEAD) 940 continue; 941 if (ret && FD_ISSET(p->fd, &readset)) { 942 --ret; 943 assert(ret >= 0); 944 if(process_msg (context, p, log_fd, database, current_version)) 945 slave_dead(context, p); 946 } else if (slave_gone_p (p)) 947 slave_dead(context, p); 948 else if (slave_missing_p (p)) 949 send_are_you_there (context, p); 950 } 951 952 if (ret && FD_ISSET(listen_fd, &readset)) { 953 add_slave (context, keytab, &slaves, listen_fd); 954 --ret; 955 assert(ret >= 0); 956 } 957 write_stats(context, slaves, current_version); 958 } 959 960 if(exit_flag == SIGINT || exit_flag == SIGTERM) 961 krb5_warnx(context, "%s terminated", getprogname()); 962#ifdef SIGXCPU 963 else if(exit_flag == SIGXCPU) 964 krb5_warnx(context, "%s CPU time limit exceeded", getprogname()); 965#endif 966 else 967 krb5_warnx(context, "%s unexpected exit reason: %ld", 968 getprogname(), (long)exit_flag); 969 970 write_master_down(context); 971 972 return 0; 973} 974