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