155682Smarkm/* 2233294Sstas * Copyright (c) 1997 - 2008 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 555682Smarkm * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 955682Smarkm * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 1255682Smarkm * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 1655682Smarkm * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 2055682Smarkm * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 3255682Smarkm */ 3355682Smarkm 3455682Smarkm#include "iprop.h" 35102644Snectar#include <rtbl.h> 3655682Smarkm 3772445Sassarstatic krb5_log_facility *log_facility; 3872445Sassar 39178825Sdfrconst char *slave_stats_file; 40178825Sdfrconst char *slave_time_missing = "2 min"; 41178825Sdfrconst char *slave_time_gone = "5 min"; 42120945Snectar 43178825Sdfrstatic int time_before_missing; 44178825Sdfrstatic int time_before_gone; 45178825Sdfr 46178825Sdfrconst char *master_hostname; 47178825Sdfr 48233294Sstasstatic krb5_socket_t 4955682Smarkmmake_signal_socket (krb5_context context) 5055682Smarkm{ 51233294Sstas#ifndef NO_UNIX_SOCKETS 5255682Smarkm struct sockaddr_un addr; 53178825Sdfr const char *fn; 54233294Sstas krb5_socket_t fd; 5555682Smarkm 56178825Sdfr fn = kadm5_log_signal_socket(context); 57178825Sdfr 5855682Smarkm fd = socket (AF_UNIX, SOCK_DGRAM, 0); 5955682Smarkm if (fd < 0) 6055682Smarkm krb5_err (context, 1, errno, "socket AF_UNIX"); 6155682Smarkm memset (&addr, 0, sizeof(addr)); 6255682Smarkm addr.sun_family = AF_UNIX; 63178825Sdfr strlcpy (addr.sun_path, fn, sizeof(addr.sun_path)); 6455682Smarkm unlink (addr.sun_path); 6555682Smarkm if (bind (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 6655682Smarkm krb5_err (context, 1, errno, "bind %s", addr.sun_path); 6755682Smarkm return fd; 68233294Sstas#else 69233294Sstas struct addrinfo *ai = NULL; 70233294Sstas krb5_socket_t fd; 71233294Sstas 72233294Sstas kadm5_log_signal_socket_info(context, 1, &ai); 73233294Sstas 74233294Sstas fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); 75233294Sstas if (rk_IS_BAD_SOCKET(fd)) 76233294Sstas krb5_err (context, 1, rk_SOCK_ERRNO, "socket AF=%d", ai->ai_family); 77233294Sstas 78233294Sstas if (rk_IS_SOCKET_ERROR( bind (fd, ai->ai_addr, ai->ai_addrlen) )) 79233294Sstas krb5_err (context, 1, rk_SOCK_ERRNO, "bind"); 80233294Sstas return fd; 81233294Sstas#endif 8255682Smarkm} 8355682Smarkm 84233294Sstasstatic krb5_socket_t 85178825Sdfrmake_listen_socket (krb5_context context, const char *port_str) 8655682Smarkm{ 87233294Sstas krb5_socket_t fd; 8855682Smarkm int one = 1; 8955682Smarkm struct sockaddr_in addr; 9055682Smarkm 9155682Smarkm fd = socket (AF_INET, SOCK_STREAM, 0); 92233294Sstas if (rk_IS_BAD_SOCKET(fd)) 93233294Sstas krb5_err (context, 1, rk_SOCK_ERRNO, "socket AF_INET"); 9490926Snectar setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); 9555682Smarkm memset (&addr, 0, sizeof(addr)); 9655682Smarkm addr.sin_family = AF_INET; 97178825Sdfr 98178825Sdfr if (port_str) { 99178825Sdfr addr.sin_port = krb5_getportbyname (context, 100233294Sstas port_str, "tcp", 101178825Sdfr 0); 102178825Sdfr if (addr.sin_port == 0) { 103178825Sdfr char *ptr; 104178825Sdfr long port; 105178825Sdfr 106178825Sdfr port = strtol (port_str, &ptr, 10); 107178825Sdfr if (port == 0 && ptr == port_str) 108178825Sdfr krb5_errx (context, 1, "bad port `%s'", port_str); 109178825Sdfr addr.sin_port = htons(port); 110178825Sdfr } 111178825Sdfr } else { 112233294Sstas addr.sin_port = krb5_getportbyname (context, IPROP_SERVICE, 113178825Sdfr "tcp", IPROP_PORT); 114178825Sdfr } 11555682Smarkm if(bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 11655682Smarkm krb5_err (context, 1, errno, "bind"); 11755682Smarkm if (listen(fd, SOMAXCONN) < 0) 11855682Smarkm krb5_err (context, 1, errno, "listen"); 11955682Smarkm return fd; 12055682Smarkm} 12155682Smarkm 122250782Sbzstatic krb5_socket_t 123250782Sbzmake_listen6_socket (krb5_context context, const char *port_str) 124250782Sbz{ 125250782Sbz krb5_socket_t fd; 126250782Sbz int one = 1; 127250782Sbz struct sockaddr_in6 addr; 128250782Sbz 129250782Sbz fd = socket (AF_INET6, SOCK_STREAM, 0); 130250782Sbz if (rk_IS_BAD_SOCKET(fd)) 131250782Sbz krb5_err (context, 1, rk_SOCK_ERRNO, "socket AF_INET6"); 132250782Sbz setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)); 133250782Sbz memset (&addr, 0, sizeof(addr)); 134250782Sbz addr.sin6_family = AF_INET6; 135250782Sbz 136250782Sbz if (port_str) { 137250782Sbz addr.sin6_port = krb5_getportbyname (context, 138250782Sbz port_str, "tcp", 139250782Sbz 0); 140250782Sbz if (addr.sin6_port == 0) { 141250782Sbz char *ptr; 142250782Sbz long port; 143250782Sbz 144250782Sbz port = strtol (port_str, &ptr, 10); 145250782Sbz if (port == 0 && ptr == port_str) 146250782Sbz krb5_errx (context, 1, "bad port `%s'", port_str); 147250782Sbz addr.sin6_port = htons(port); 148250782Sbz } 149250782Sbz } else { 150250782Sbz addr.sin6_port = krb5_getportbyname (context, IPROP_SERVICE, 151250782Sbz "tcp", IPROP_PORT); 152250782Sbz } 153250782Sbz if(bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 154250782Sbz krb5_err (context, 1, errno, "bind6"); 155250782Sbz if (listen(fd, SOMAXCONN) < 0) 156250782Sbz krb5_err (context, 1, errno, "listen6"); 157250782Sbz return fd; 158250782Sbz} 159250782Sbz 160250782Sbz#ifndef _SOCKADDR_UNION 161250782Sbz#define _SOCKADDR_UNION 162250782Sbzunion sockaddr_union { 163250782Sbz struct sockaddr sa; 164250782Sbz struct sockaddr_in sin; 165250782Sbz struct sockaddr_in6 sin6; 166250782Sbz}; 167250782Sbz#endif /* _SOCKADDR_UNION */ 168250782Sbz 16955682Smarkmstruct slave { 170233294Sstas krb5_socket_t fd; 171250782Sbz union sockaddr_union addr; 17255682Smarkm char *name; 17355682Smarkm krb5_auth_context ac; 174178825Sdfr uint32_t version; 175102644Snectar time_t seen; 176102644Snectar unsigned long flags; 177102644Snectar#define SLAVE_F_DEAD 0x1 178178825Sdfr#define SLAVE_F_AYT 0x2 17955682Smarkm struct slave *next; 18055682Smarkm}; 18155682Smarkm 18255682Smarkmtypedef struct slave slave; 18355682Smarkm 18455682Smarkmstatic int 18555682Smarkmcheck_acl (krb5_context context, const char *name) 18655682Smarkm{ 187178825Sdfr const char *fn; 18855682Smarkm FILE *fp; 18955682Smarkm char buf[256]; 19055682Smarkm int ret = 1; 191233294Sstas char *slavefile = NULL; 19255682Smarkm 193233294Sstas if (asprintf(&slavefile, "%s/slaves", hdb_db_dir(context)) == -1 194233294Sstas || slavefile == NULL) 195233294Sstas errx(1, "out of memory"); 196178825Sdfr 197178825Sdfr fn = krb5_config_get_string_default(context, 198178825Sdfr NULL, 199178825Sdfr slavefile, 200178825Sdfr "kdc", 201178825Sdfr "iprop-acl", 202178825Sdfr NULL); 203178825Sdfr 204178825Sdfr fp = fopen (fn, "r"); 205178825Sdfr free(slavefile); 20655682Smarkm if (fp == NULL) 20755682Smarkm return 1; 20855682Smarkm while (fgets(buf, sizeof(buf), fp) != NULL) { 209178825Sdfr buf[strcspn(buf, "\r\n")] = '\0'; 21055682Smarkm if (strcmp (buf, name) == 0) { 21155682Smarkm ret = 0; 21255682Smarkm break; 21355682Smarkm } 21455682Smarkm } 21555682Smarkm fclose (fp); 21655682Smarkm return ret; 21755682Smarkm} 21855682Smarkm 21955682Smarkmstatic void 220102644Snectarslave_seen(slave *s) 221102644Snectar{ 222178825Sdfr s->flags &= ~SLAVE_F_AYT; 223102644Snectar s->seen = time(NULL); 224102644Snectar} 225102644Snectar 226178825Sdfrstatic int 227178825Sdfrslave_missing_p (slave *s) 228178825Sdfr{ 229178825Sdfr if (time(NULL) > s->seen + time_before_missing) 230178825Sdfr return 1; 231178825Sdfr return 0; 232178825Sdfr} 233178825Sdfr 234178825Sdfrstatic int 235178825Sdfrslave_gone_p (slave *s) 236178825Sdfr{ 237178825Sdfr if (time(NULL) > s->seen + time_before_gone) 238178825Sdfr return 1; 239178825Sdfr return 0; 240178825Sdfr} 241178825Sdfr 242102644Snectarstatic void 243178825Sdfrslave_dead(krb5_context context, slave *s) 244102644Snectar{ 245178825Sdfr krb5_warnx(context, "slave %s dead", s->name); 246178825Sdfr 247233294Sstas if (!rk_IS_BAD_SOCKET(s->fd)) { 248233294Sstas rk_closesocket (s->fd); 249233294Sstas s->fd = rk_INVALID_SOCKET; 250120945Snectar } 251102644Snectar s->flags |= SLAVE_F_DEAD; 252102644Snectar slave_seen(s); 253102644Snectar} 254102644Snectar 255102644Snectarstatic void 256102644Snectarremove_slave (krb5_context context, slave *s, slave **root) 257102644Snectar{ 258102644Snectar slave **p; 259102644Snectar 260233294Sstas if (!rk_IS_BAD_SOCKET(s->fd)) 261233294Sstas rk_closesocket (s->fd); 262102644Snectar if (s->name) 263102644Snectar free (s->name); 264102644Snectar if (s->ac) 265102644Snectar krb5_auth_con_free (context, s->ac); 266102644Snectar 267102644Snectar for (p = root; *p; p = &(*p)->next) 268102644Snectar if (*p == s) { 269102644Snectar *p = s->next; 270102644Snectar break; 271102644Snectar } 272102644Snectar free (s); 273102644Snectar} 274102644Snectar 275102644Snectarstatic void 276233294Sstasadd_slave (krb5_context context, krb5_keytab keytab, slave **root, 277233294Sstas krb5_socket_t fd) 27855682Smarkm{ 27955682Smarkm krb5_principal server; 28055682Smarkm krb5_error_code ret; 28155682Smarkm slave *s; 28272445Sassar socklen_t addr_len; 28355682Smarkm krb5_ticket *ticket = NULL; 28455682Smarkm char hostname[128]; 28555682Smarkm 28655682Smarkm s = malloc(sizeof(*s)); 28755682Smarkm if (s == NULL) { 28855682Smarkm krb5_warnx (context, "add_slave: no memory"); 28955682Smarkm return; 29055682Smarkm } 29155682Smarkm s->name = NULL; 29255682Smarkm s->ac = NULL; 29355682Smarkm 294250782Sbz addr_len = sizeof(s->addr.sin6); 295250782Sbz s->fd = accept (fd, (struct sockaddr *)&s->addr.sa, &addr_len); 296233294Sstas if (rk_IS_BAD_SOCKET(s->fd)) { 297233294Sstas krb5_warn (context, rk_SOCK_ERRNO, "accept"); 29855682Smarkm goto error; 29955682Smarkm } 300178825Sdfr if (master_hostname) 301178825Sdfr strlcpy(hostname, master_hostname, sizeof(hostname)); 302178825Sdfr else 303178825Sdfr gethostname(hostname, sizeof(hostname)); 304178825Sdfr 30555682Smarkm ret = krb5_sname_to_principal (context, hostname, IPROP_NAME, 30655682Smarkm KRB5_NT_SRV_HST, &server); 30755682Smarkm if (ret) { 30855682Smarkm krb5_warn (context, ret, "krb5_sname_to_principal"); 30955682Smarkm goto error; 31055682Smarkm } 31155682Smarkm 31255682Smarkm ret = krb5_recvauth (context, &s->ac, &s->fd, 31372445Sassar IPROP_VERSION, server, 0, keytab, &ticket); 31455682Smarkm krb5_free_principal (context, server); 31555682Smarkm if (ret) { 31655682Smarkm krb5_warn (context, ret, "krb5_recvauth"); 31755682Smarkm goto error; 31855682Smarkm } 31955682Smarkm ret = krb5_unparse_name (context, ticket->client, &s->name); 320233294Sstas krb5_free_ticket (context, ticket); 32155682Smarkm if (ret) { 32255682Smarkm krb5_warn (context, ret, "krb5_unparse_name"); 32355682Smarkm goto error; 32455682Smarkm } 32555682Smarkm if (check_acl (context, s->name)) { 32655682Smarkm krb5_warnx (context, "%s not in acl", s->name); 32755682Smarkm goto error; 32855682Smarkm } 329102644Snectar 330102644Snectar { 331102644Snectar slave *l = *root; 332102644Snectar 333102644Snectar while (l) { 334102644Snectar if (strcmp(l->name, s->name) == 0) 335102644Snectar break; 336102644Snectar l = l->next; 337102644Snectar } 338102644Snectar if (l) { 339102644Snectar if (l->flags & SLAVE_F_DEAD) { 340102644Snectar remove_slave(context, l, root); 341102644Snectar } else { 342102644Snectar krb5_warnx (context, "second connection from %s", s->name); 343102644Snectar goto error; 344102644Snectar } 345102644Snectar } 346102644Snectar } 347102644Snectar 34872445Sassar krb5_warnx (context, "connection from %s", s->name); 34955682Smarkm 35055682Smarkm s->version = 0; 351102644Snectar s->flags = 0; 352102644Snectar slave_seen(s); 35355682Smarkm s->next = *root; 35455682Smarkm *root = s; 35555682Smarkm return; 35655682Smarkmerror: 357102644Snectar remove_slave(context, s, root); 35855682Smarkm} 35955682Smarkm 36072445Sassarstruct prop_context { 36172445Sassar krb5_auth_context auth_context; 362233294Sstas krb5_socket_t fd; 36372445Sassar}; 36472445Sassar 36555682Smarkmstatic int 366178825Sdfrprop_one (krb5_context context, HDB *db, hdb_entry_ex *entry, void *v) 36755682Smarkm{ 36872445Sassar krb5_error_code ret; 369178825Sdfr krb5_storage *sp; 37072445Sassar krb5_data data; 371178825Sdfr struct slave *s = (struct slave *)v; 37272445Sassar 373178825Sdfr ret = hdb_entry2value (context, &entry->entry, &data); 37472445Sassar if (ret) 37572445Sassar return ret; 37672445Sassar ret = krb5_data_realloc (&data, data.length + 4); 37772445Sassar if (ret) { 37872445Sassar krb5_data_free (&data); 37972445Sassar return ret; 38072445Sassar } 38172445Sassar memmove ((char *)data.data + 4, data.data, data.length - 4); 382178825Sdfr sp = krb5_storage_from_data(&data); 383178825Sdfr if (sp == NULL) { 384178825Sdfr krb5_data_free (&data); 385178825Sdfr return ENOMEM; 386178825Sdfr } 387178825Sdfr krb5_store_int32(sp, ONE_PRINC); 388178825Sdfr krb5_storage_free(sp); 38972445Sassar 390178825Sdfr ret = krb5_write_priv_message (context, s->ac, &s->fd, &data); 39172445Sassar krb5_data_free (&data); 39272445Sassar return ret; 39355682Smarkm} 39455682Smarkm 39555682Smarkmstatic int 39672445Sassarsend_complete (krb5_context context, slave *s, 397178825Sdfr const char *database, uint32_t current_version) 39872445Sassar{ 39972445Sassar krb5_error_code ret; 400178825Sdfr krb5_storage *sp; 40172445Sassar HDB *db; 40272445Sassar krb5_data data; 40372445Sassar char buf[8]; 40472445Sassar 40572445Sassar ret = hdb_create (context, &db, database); 40672445Sassar if (ret) 40772445Sassar krb5_err (context, 1, ret, "hdb_create: %s", database); 408178825Sdfr ret = db->hdb_open (context, db, O_RDONLY, 0); 40972445Sassar if (ret) 41072445Sassar krb5_err (context, 1, ret, "db->open"); 41172445Sassar 412178825Sdfr sp = krb5_storage_from_mem (buf, 4); 413178825Sdfr if (sp == NULL) 414178825Sdfr krb5_errx (context, 1, "krb5_storage_from_mem"); 415178825Sdfr krb5_store_int32 (sp, TELL_YOU_EVERYTHING); 416178825Sdfr krb5_storage_free (sp); 41772445Sassar 41872445Sassar data.data = buf; 41972445Sassar data.length = 4; 42072445Sassar 42172445Sassar ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 42272445Sassar 423102644Snectar if (ret) { 424102644Snectar krb5_warn (context, ret, "krb5_write_priv_message"); 425178825Sdfr slave_dead(context, s); 426102644Snectar return ret; 427102644Snectar } 42872445Sassar 429233294Sstas ret = hdb_foreach (context, db, HDB_F_ADMIN_DATA, prop_one, s); 430102644Snectar if (ret) { 431102644Snectar krb5_warn (context, ret, "hdb_foreach"); 432178825Sdfr slave_dead(context, s); 433102644Snectar return ret; 434102644Snectar } 43572445Sassar 436178825Sdfr (*db->hdb_close)(context, db); 437178825Sdfr (*db->hdb_destroy)(context, db); 438178825Sdfr 439178825Sdfr sp = krb5_storage_from_mem (buf, 8); 440178825Sdfr if (sp == NULL) 441178825Sdfr krb5_errx (context, 1, "krb5_storage_from_mem"); 442178825Sdfr krb5_store_int32 (sp, NOW_YOU_HAVE); 443178825Sdfr krb5_store_int32 (sp, current_version); 444178825Sdfr krb5_storage_free (sp); 445178825Sdfr 44672445Sassar data.length = 8; 44772445Sassar 448102644Snectar s->version = current_version; 449102644Snectar 45072445Sassar ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 451102644Snectar if (ret) { 452178825Sdfr slave_dead(context, s); 453102644Snectar krb5_warn (context, ret, "krb5_write_priv_message"); 454102644Snectar return ret; 455102644Snectar } 45672445Sassar 457102644Snectar slave_seen(s); 45872445Sassar 45972445Sassar return 0; 46072445Sassar} 46172445Sassar 46272445Sassarstatic int 463178825Sdfrsend_are_you_there (krb5_context context, slave *s) 464178825Sdfr{ 465178825Sdfr krb5_storage *sp; 466178825Sdfr krb5_data data; 467178825Sdfr char buf[4]; 468178825Sdfr int ret; 469178825Sdfr 470178825Sdfr if (s->flags & (SLAVE_F_DEAD|SLAVE_F_AYT)) 471178825Sdfr return 0; 472178825Sdfr 473233294Sstas krb5_warnx(context, "slave %s missing, sending AYT", s->name); 474233294Sstas 475178825Sdfr s->flags |= SLAVE_F_AYT; 476178825Sdfr 477178825Sdfr data.data = buf; 478178825Sdfr data.length = 4; 479178825Sdfr 480178825Sdfr sp = krb5_storage_from_mem (buf, 4); 481178825Sdfr if (sp == NULL) { 482178825Sdfr krb5_warnx (context, "are_you_there: krb5_data_alloc"); 483178825Sdfr slave_dead(context, s); 484178825Sdfr return 1; 485178825Sdfr } 486178825Sdfr krb5_store_int32 (sp, ARE_YOU_THERE); 487178825Sdfr krb5_storage_free (sp); 488178825Sdfr 489178825Sdfr ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 490178825Sdfr 491178825Sdfr if (ret) { 492178825Sdfr krb5_warn (context, ret, "are_you_there: krb5_write_priv_message"); 493178825Sdfr slave_dead(context, s); 494178825Sdfr return 1; 495178825Sdfr } 496178825Sdfr 497178825Sdfr return 0; 498178825Sdfr} 499178825Sdfr 500178825Sdfrstatic int 50155682Smarkmsend_diffs (krb5_context context, slave *s, int log_fd, 502178825Sdfr const char *database, uint32_t current_version) 50355682Smarkm{ 50472445Sassar krb5_storage *sp; 505178825Sdfr uint32_t ver; 50655682Smarkm time_t timestamp; 50755682Smarkm enum kadm_ops op; 508178825Sdfr uint32_t len; 50955682Smarkm off_t right, left; 51055682Smarkm krb5_data data; 51155682Smarkm int ret = 0; 51255682Smarkm 513178825Sdfr if (s->version == current_version) { 514178825Sdfr krb5_warnx(context, "slave %s in sync already at version %ld", 515178825Sdfr s->name, (long)s->version); 51655682Smarkm return 0; 517178825Sdfr } 51855682Smarkm 519102644Snectar if (s->flags & SLAVE_F_DEAD) 520102644Snectar return 0; 521102644Snectar 522178825Sdfr /* if slave is a fresh client, starting over */ 523178825Sdfr if (s->version == 0) { 524178825Sdfr krb5_warnx(context, "sending complete log to fresh slave %s", 525178825Sdfr s->name); 526178825Sdfr return send_complete (context, s, database, current_version); 527178825Sdfr } 528178825Sdfr 52955682Smarkm sp = kadm5_log_goto_end (log_fd); 530102644Snectar right = krb5_storage_seek(sp, 0, SEEK_CUR); 53155682Smarkm for (;;) { 532178825Sdfr ret = kadm5_log_previous (context, sp, &ver, ×tamp, &op, &len); 533178825Sdfr if (ret) 534233294Sstas krb5_err(context, 1, ret, 535178825Sdfr "send_diffs: failed to find previous entry"); 536102644Snectar left = krb5_storage_seek(sp, -16, SEEK_CUR); 53755682Smarkm if (ver == s->version) 53855682Smarkm return 0; 53955682Smarkm if (ver == s->version + 1) 54055682Smarkm break; 541178825Sdfr if (left == 0) { 542233294Sstas krb5_storage_free(sp); 543178825Sdfr krb5_warnx(context, 544178825Sdfr "slave %s (version %lu) out of sync with master " 545178825Sdfr "(first version in log %lu), sending complete database", 546178825Sdfr s->name, (unsigned long)s->version, (unsigned long)ver); 54772445Sassar return send_complete (context, s, database, current_version); 548178825Sdfr } 54955682Smarkm } 550178825Sdfr 551178825Sdfr krb5_warnx(context, 552178825Sdfr "syncing slave %s from version %lu to version %lu", 553178825Sdfr s->name, (unsigned long)s->version, 554178825Sdfr (unsigned long)current_version); 555178825Sdfr 556178825Sdfr ret = krb5_data_alloc (&data, right - left + 4); 557178825Sdfr if (ret) { 558233294Sstas krb5_storage_free(sp); 559178825Sdfr krb5_warn (context, ret, "send_diffs: krb5_data_alloc"); 560178825Sdfr slave_dead(context, s); 561178825Sdfr return 1; 562178825Sdfr } 563102644Snectar krb5_storage_read (sp, (char *)data.data + 4, data.length - 4); 56455682Smarkm krb5_storage_free(sp); 56555682Smarkm 566178825Sdfr sp = krb5_storage_from_data (&data); 567178825Sdfr if (sp == NULL) { 568178825Sdfr krb5_warnx (context, "send_diffs: krb5_storage_from_data"); 569178825Sdfr slave_dead(context, s); 570178825Sdfr return 1; 571178825Sdfr } 572178825Sdfr krb5_store_int32 (sp, FOR_YOU); 573178825Sdfr krb5_storage_free(sp); 57455682Smarkm 57572445Sassar ret = krb5_write_priv_message(context, s->ac, &s->fd, &data); 576102644Snectar krb5_data_free(&data); 57755682Smarkm 57855682Smarkm if (ret) { 579178825Sdfr krb5_warn (context, ret, "send_diffs: krb5_write_priv_message"); 580178825Sdfr slave_dead(context, s); 58155682Smarkm return 1; 58255682Smarkm } 583102644Snectar slave_seen(s); 584102644Snectar 585178825Sdfr s->version = current_version; 586178825Sdfr 58755682Smarkm return 0; 58855682Smarkm} 58955682Smarkm 59055682Smarkmstatic int 59155682Smarkmprocess_msg (krb5_context context, slave *s, int log_fd, 592178825Sdfr const char *database, uint32_t current_version) 59355682Smarkm{ 59455682Smarkm int ret = 0; 59572445Sassar krb5_data out; 59655682Smarkm krb5_storage *sp; 59755682Smarkm int32_t tmp; 59855682Smarkm 59972445Sassar ret = krb5_read_priv_message(context, s->ac, &s->fd, &out); 60072445Sassar if(ret) { 60172445Sassar krb5_warn (context, ret, "error reading message from %s", s->name); 60255682Smarkm return 1; 60355682Smarkm } 60455682Smarkm 60555682Smarkm sp = krb5_storage_from_mem (out.data, out.length); 606178825Sdfr if (sp == NULL) { 607178825Sdfr krb5_warnx (context, "process_msg: no memory"); 608178825Sdfr krb5_data_free (&out); 609178825Sdfr return 1; 610178825Sdfr } 611178825Sdfr if (krb5_ret_int32 (sp, &tmp) != 0) { 612178825Sdfr krb5_warnx (context, "process_msg: client send too short command"); 613178825Sdfr krb5_data_free (&out); 614178825Sdfr return 1; 615178825Sdfr } 61655682Smarkm switch (tmp) { 61755682Smarkm case I_HAVE : 618178825Sdfr ret = krb5_ret_int32 (sp, &tmp); 619178825Sdfr if (ret != 0) { 620178825Sdfr krb5_warnx (context, "process_msg: client send too I_HAVE data"); 621178825Sdfr break; 622178825Sdfr } 623178825Sdfr /* new started slave that have old log */ 624178825Sdfr if (s->version == 0 && tmp != 0) { 625233294Sstas if (current_version < (uint32_t)tmp) { 626233294Sstas krb5_warnx (context, "Slave %s (version %lu) have later version " 627233294Sstas "the master (version %lu) OUT OF SYNC", 628233294Sstas s->name, (unsigned long)tmp, 629233294Sstas (unsigned long)current_version); 630178825Sdfr } 631233294Sstas s->version = tmp; 632178825Sdfr } 633233294Sstas if ((uint32_t)tmp < s->version) { 634178825Sdfr krb5_warnx (context, "Slave claims to not have " 635178825Sdfr "version we already sent to it"); 636178825Sdfr } else { 637178825Sdfr ret = send_diffs (context, s, log_fd, database, current_version); 638178825Sdfr } 63955682Smarkm break; 640178825Sdfr case I_AM_HERE : 641178825Sdfr break; 642178825Sdfr case ARE_YOU_THERE: 64355682Smarkm case FOR_YOU : 64455682Smarkm default : 64555682Smarkm krb5_warnx (context, "Ignoring command %d", tmp); 64655682Smarkm break; 64755682Smarkm } 64855682Smarkm 64955682Smarkm krb5_data_free (&out); 650233294Sstas krb5_storage_free (sp); 651102644Snectar 652102644Snectar slave_seen(s); 653102644Snectar 65455682Smarkm return ret; 65555682Smarkm} 65655682Smarkm 657102644Snectar#define SLAVE_NAME "Name" 658102644Snectar#define SLAVE_ADDRESS "Address" 659102644Snectar#define SLAVE_VERSION "Version" 660102644Snectar#define SLAVE_STATUS "Status" 661102644Snectar#define SLAVE_SEEN "Last Seen" 662102644Snectar 663178825Sdfrstatic FILE * 664178825Sdfropen_stats(krb5_context context) 665178825Sdfr{ 666178825Sdfr char *statfile = NULL; 667178825Sdfr const char *fn; 668178825Sdfr FILE *f; 669178825Sdfr 670178825Sdfr if (slave_stats_file) 671178825Sdfr fn = slave_stats_file; 672178825Sdfr else { 673178825Sdfr asprintf(&statfile, "%s/slaves-stats", hdb_db_dir(context)); 674178825Sdfr fn = krb5_config_get_string_default(context, 675178825Sdfr NULL, 676178825Sdfr statfile, 677178825Sdfr "kdc", 678178825Sdfr "iprop-stats", 679178825Sdfr NULL); 680178825Sdfr } 681178825Sdfr f = fopen(fn, "w"); 682178825Sdfr if (statfile) 683178825Sdfr free(statfile); 684178825Sdfr 685178825Sdfr return f; 686178825Sdfr} 687178825Sdfr 688102644Snectarstatic void 689178825Sdfrwrite_master_down(krb5_context context) 690102644Snectar{ 691120945Snectar char str[100]; 692178825Sdfr time_t t = time(NULL); 693178825Sdfr FILE *fp; 694178825Sdfr 695178825Sdfr fp = open_stats(context); 696178825Sdfr if (fp == NULL) 697178825Sdfr return; 698233294Sstas krb5_format_time(context, t, str, sizeof(str), TRUE); 699178825Sdfr fprintf(fp, "master down at %s\n", str); 700178825Sdfr 701178825Sdfr fclose(fp); 702178825Sdfr} 703178825Sdfr 704178825Sdfrstatic void 705178825Sdfrwrite_stats(krb5_context context, slave *slaves, uint32_t current_version) 706178825Sdfr{ 707178825Sdfr char str[100]; 708102644Snectar rtbl_t tbl; 709102644Snectar time_t t = time(NULL); 710102644Snectar FILE *fp; 711102644Snectar 712178825Sdfr fp = open_stats(context); 713102644Snectar if (fp == NULL) 714102644Snectar return; 715102644Snectar 716233294Sstas krb5_format_time(context, t, str, sizeof(str), TRUE); 717102644Snectar fprintf(fp, "Status for slaves, last updated: %s\n\n", str); 718102644Snectar 719102644Snectar fprintf(fp, "Master version: %lu\n\n", (unsigned long)current_version); 720102644Snectar 721102644Snectar tbl = rtbl_create(); 722102644Snectar if (tbl == NULL) { 723102644Snectar fclose(fp); 724102644Snectar return; 725102644Snectar } 726102644Snectar 727102644Snectar rtbl_add_column(tbl, SLAVE_NAME, 0); 728102644Snectar rtbl_add_column(tbl, SLAVE_ADDRESS, 0); 729102644Snectar rtbl_add_column(tbl, SLAVE_VERSION, RTBL_ALIGN_RIGHT); 730102644Snectar rtbl_add_column(tbl, SLAVE_STATUS, 0); 731102644Snectar rtbl_add_column(tbl, SLAVE_SEEN, 0); 732102644Snectar 733102644Snectar rtbl_set_prefix(tbl, " "); 734102644Snectar rtbl_set_column_prefix(tbl, SLAVE_NAME, ""); 735102644Snectar 736102644Snectar while (slaves) { 737102644Snectar krb5_address addr; 738102644Snectar krb5_error_code ret; 739102644Snectar rtbl_add_column_entry(tbl, SLAVE_NAME, slaves->name); 740233294Sstas ret = krb5_sockaddr2address (context, 741250782Sbz (struct sockaddr*)&slaves->addr.sa, &addr); 742102644Snectar if(ret == 0) { 743102644Snectar krb5_print_address(&addr, str, sizeof(str), NULL); 744102644Snectar krb5_free_address(context, &addr); 745102644Snectar rtbl_add_column_entry(tbl, SLAVE_ADDRESS, str); 746102644Snectar } else 747102644Snectar rtbl_add_column_entry(tbl, SLAVE_ADDRESS, "<unknown>"); 748233294Sstas 749102644Snectar snprintf(str, sizeof(str), "%u", (unsigned)slaves->version); 750102644Snectar rtbl_add_column_entry(tbl, SLAVE_VERSION, str); 751102644Snectar 752102644Snectar if (slaves->flags & SLAVE_F_DEAD) 753102644Snectar rtbl_add_column_entry(tbl, SLAVE_STATUS, "Down"); 754102644Snectar else 755102644Snectar rtbl_add_column_entry(tbl, SLAVE_STATUS, "Up"); 756102644Snectar 757233294Sstas ret = krb5_format_time(context, slaves->seen, str, sizeof(str), TRUE); 758102644Snectar rtbl_add_column_entry(tbl, SLAVE_SEEN, str); 759102644Snectar 760102644Snectar slaves = slaves->next; 761102644Snectar } 762102644Snectar 763102644Snectar rtbl_format(tbl, fp); 764102644Snectar rtbl_destroy(tbl); 765102644Snectar 766102644Snectar fclose(fp); 767102644Snectar} 768102644Snectar 769102644Snectar 770233294Sstasstatic char sHDB[] = "HDB:"; 77172445Sassarstatic char *realm; 77272445Sassarstatic int version_flag; 77372445Sassarstatic int help_flag; 774233294Sstasstatic char *keytab_str = sHDB; 77572445Sassarstatic char *database; 776178825Sdfrstatic char *config_file; 777178825Sdfrstatic char *port_str; 778233294Sstas#ifdef SUPPORT_DETACH 779178825Sdfrstatic int detach_from_console = 0; 780233294Sstas#endif 78172445Sassar 78272445Sassarstatic struct getargs args[] = { 783233294Sstas { "config-file", 'c', arg_string, &config_file, NULL, NULL }, 784233294Sstas { "realm", 'r', arg_string, &realm, NULL, NULL }, 78572445Sassar { "keytab", 'k', arg_string, &keytab_str, 78672445Sassar "keytab to get authentication from", "kspec" }, 78772445Sassar { "database", 'd', arg_string, &database, "database", "file"}, 788233294Sstas { "slave-stats-file", 0, arg_string, rk_UNCONST(&slave_stats_file), 789178825Sdfr "file for slave status information", "file"}, 790233294Sstas { "time-missing", 0, arg_string, rk_UNCONST(&slave_time_missing), 791178825Sdfr "time before slave is polled for presence", "time"}, 792233294Sstas { "time-gone", 0, arg_string, rk_UNCONST(&slave_time_gone), 793178825Sdfr "time of inactivity after which a slave is considered gone", "time"}, 794178825Sdfr { "port", 0, arg_string, &port_str, 795178825Sdfr "port ipropd will listen to", "port"}, 796233294Sstas#ifdef SUPPORT_DETACH 797233294Sstas { "detach", 0, arg_flag, &detach_from_console, 798233294Sstas "detach from console", NULL }, 799233294Sstas#endif 800233294Sstas { "hostname", 0, arg_string, rk_UNCONST(&master_hostname), 801178825Sdfr "hostname of master (if not same as hostname)", "hostname" }, 802233294Sstas { "version", 0, arg_flag, &version_flag, NULL, NULL }, 803233294Sstas { "help", 0, arg_flag, &help_flag, NULL, NULL } 80455682Smarkm}; 80572445Sassarstatic int num_args = sizeof(args) / sizeof(args[0]); 80655682Smarkm 80755682Smarkmint 80855682Smarkmmain(int argc, char **argv) 80955682Smarkm{ 81055682Smarkm krb5_error_code ret; 81155682Smarkm krb5_context context; 81255682Smarkm void *kadm_handle; 81355682Smarkm kadm5_server_context *server_context; 81455682Smarkm kadm5_config_params conf; 815250782Sbz krb5_socket_t signal_fd, listen_fd, listen6_fd; 81655682Smarkm int log_fd; 81755682Smarkm slave *slaves = NULL; 818178825Sdfr uint32_t current_version = 0, old_version = 0; 81972445Sassar krb5_keytab keytab; 820178825Sdfr int optidx; 821178825Sdfr char **files; 822233294Sstas 823178825Sdfr optidx = krb5_program_setup(&context, argc, argv, args, num_args, NULL); 824233294Sstas 82555682Smarkm if(help_flag) 82655682Smarkm krb5_std_usage(0, args, num_args); 82755682Smarkm if(version_flag) { 82855682Smarkm print_version(NULL); 82955682Smarkm exit(0); 83055682Smarkm } 83155682Smarkm 832178825Sdfr setup_signal(); 833178825Sdfr 834178825Sdfr if (config_file == NULL) { 835178825Sdfr asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)); 836178825Sdfr if (config_file == NULL) 837178825Sdfr errx(1, "out of memory"); 838178825Sdfr } 839178825Sdfr 840178825Sdfr ret = krb5_prepend_config_files_default(config_file, &files); 841178825Sdfr if (ret) 842178825Sdfr krb5_err(context, 1, ret, "getting configuration files"); 843178825Sdfr 844178825Sdfr ret = krb5_set_config_files(context, files); 845178825Sdfr krb5_free_config_files(files); 846178825Sdfr if (ret) 847178825Sdfr krb5_err(context, 1, ret, "reading configuration files"); 848178825Sdfr 849178825Sdfr time_before_gone = parse_time (slave_time_gone, "s"); 850178825Sdfr if (time_before_gone < 0) 851178825Sdfr krb5_errx (context, 1, "couldn't parse time: %s", slave_time_gone); 852178825Sdfr time_before_missing = parse_time (slave_time_missing, "s"); 853178825Sdfr if (time_before_missing < 0) 854178825Sdfr krb5_errx (context, 1, "couldn't parse time: %s", slave_time_missing); 855178825Sdfr 856233294Sstas#ifdef SUPPORT_DETACH 857178825Sdfr if (detach_from_console) 858178825Sdfr daemon(0, 0); 859233294Sstas#endif 86090926Snectar pidfile (NULL); 86172445Sassar krb5_openlog (context, "ipropd-master", &log_facility); 86272445Sassar krb5_set_warn_dest(context, log_facility); 86372445Sassar 86472445Sassar ret = krb5_kt_register(context, &hdb_kt_ops); 86572445Sassar if(ret) 86672445Sassar krb5_err(context, 1, ret, "krb5_kt_register"); 86772445Sassar 86872445Sassar ret = krb5_kt_resolve(context, keytab_str, &keytab); 86972445Sassar if(ret) 87072445Sassar krb5_err(context, 1, ret, "krb5_kt_resolve: %s", keytab_str); 871233294Sstas 87255682Smarkm memset(&conf, 0, sizeof(conf)); 87355682Smarkm if(realm) { 87455682Smarkm conf.mask |= KADM5_CONFIG_REALM; 87555682Smarkm conf.realm = realm; 87655682Smarkm } 87772445Sassar ret = kadm5_init_with_skey_ctx (context, 87872445Sassar KADM5_ADMIN_SERVICE, 87972445Sassar NULL, 88072445Sassar KADM5_ADMIN_SERVICE, 881233294Sstas &conf, 0, 0, 88272445Sassar &kadm_handle); 88355682Smarkm if (ret) 88455682Smarkm krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); 88555682Smarkm 88655682Smarkm server_context = (kadm5_server_context *)kadm_handle; 88755682Smarkm 88855682Smarkm log_fd = open (server_context->log_context.log_file, O_RDONLY, 0); 88955682Smarkm if (log_fd < 0) 89055682Smarkm krb5_err (context, 1, errno, "open %s", 89155682Smarkm server_context->log_context.log_file); 89255682Smarkm 89355682Smarkm signal_fd = make_signal_socket (context); 894178825Sdfr listen_fd = make_listen_socket (context, port_str); 895250782Sbz listen6_fd = make_listen6_socket (context, port_str); 89655682Smarkm 897178825Sdfr kadm5_log_get_version_fd (log_fd, ¤t_version); 89872445Sassar 899233294Sstas krb5_warnx(context, "ipropd-master started at version: %lu", 900178825Sdfr (unsigned long)current_version); 901178825Sdfr 902178825Sdfr while(exit_flag == 0){ 90355682Smarkm slave *p; 90455682Smarkm fd_set readset; 90555682Smarkm int max_fd = 0; 90655682Smarkm struct timeval to = {30, 0}; 907178825Sdfr uint32_t vers; 90855682Smarkm 909233294Sstas#ifndef NO_LIMIT_FD_SETSIZE 910250782Sbz if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE || 911250782Sbz listen6_fd >= FD_SETSIZE) 91272445Sassar krb5_errx (context, 1, "fd too large"); 913233294Sstas#endif 91472445Sassar 91555682Smarkm FD_ZERO(&readset); 91655682Smarkm FD_SET(signal_fd, &readset); 91755682Smarkm max_fd = max(max_fd, signal_fd); 91855682Smarkm FD_SET(listen_fd, &readset); 91955682Smarkm max_fd = max(max_fd, listen_fd); 920250782Sbz FD_SET(listen6_fd, &readset); 921250782Sbz max_fd = max(max_fd, listen6_fd); 92255682Smarkm 92355682Smarkm for (p = slaves; p != NULL; p = p->next) { 924120945Snectar if (p->flags & SLAVE_F_DEAD) 925120945Snectar continue; 92655682Smarkm FD_SET(p->fd, &readset); 92755682Smarkm max_fd = max(max_fd, p->fd); 92855682Smarkm } 92955682Smarkm 93055682Smarkm ret = select (max_fd + 1, 93155682Smarkm &readset, NULL, NULL, &to); 93255682Smarkm if (ret < 0) { 93355682Smarkm if (errno == EINTR) 93455682Smarkm continue; 93555682Smarkm else 93655682Smarkm krb5_err (context, 1, errno, "select"); 93755682Smarkm } 93855682Smarkm 93955682Smarkm if (ret == 0) { 94055682Smarkm old_version = current_version; 94172445Sassar kadm5_log_get_version_fd (log_fd, ¤t_version); 94255682Smarkm 943178825Sdfr if (current_version > old_version) { 944233294Sstas krb5_warnx(context, 945178825Sdfr "Missed a signal, updating slaves %lu to %lu", 946178825Sdfr (unsigned long)old_version, 947178825Sdfr (unsigned long)current_version); 948120945Snectar for (p = slaves; p != NULL; p = p->next) { 949120945Snectar if (p->flags & SLAVE_F_DEAD) 950120945Snectar continue; 95172445Sassar send_diffs (context, p, log_fd, database, current_version); 952120945Snectar } 953178825Sdfr } 95455682Smarkm } 95555682Smarkm 95655682Smarkm if (ret && FD_ISSET(signal_fd, &readset)) { 957233294Sstas#ifndef NO_UNIX_SOCKETS 95855682Smarkm struct sockaddr_un peer_addr; 959233294Sstas#else 960233294Sstas struct sockaddr_storage peer_addr; 961233294Sstas#endif 96272445Sassar socklen_t peer_len = sizeof(peer_addr); 96355682Smarkm 96490926Snectar if(recvfrom(signal_fd, (void *)&vers, sizeof(vers), 0, 96555682Smarkm (struct sockaddr *)&peer_addr, &peer_len) < 0) { 96655682Smarkm krb5_warn (context, errno, "recvfrom"); 96755682Smarkm continue; 96855682Smarkm } 96955682Smarkm --ret; 970178825Sdfr assert(ret >= 0); 97155682Smarkm old_version = current_version; 97272445Sassar kadm5_log_get_version_fd (log_fd, ¤t_version); 973178825Sdfr if (current_version > old_version) { 974233294Sstas krb5_warnx(context, 975178825Sdfr "Got a signal, updating slaves %lu to %lu", 976178825Sdfr (unsigned long)old_version, 977178825Sdfr (unsigned long)current_version); 978233294Sstas for (p = slaves; p != NULL; p = p->next) { 979233294Sstas if (p->flags & SLAVE_F_DEAD) 980233294Sstas continue; 981178825Sdfr send_diffs (context, p, log_fd, database, current_version); 982233294Sstas } 983178825Sdfr } else { 984233294Sstas krb5_warnx(context, 985178825Sdfr "Got a signal, but no update in log version %lu", 986178825Sdfr (unsigned long)current_version); 987178825Sdfr } 988178825Sdfr } 98955682Smarkm 990178825Sdfr for(p = slaves; p != NULL; p = p->next) { 991120945Snectar if (p->flags & SLAVE_F_DEAD) 992178825Sdfr continue; 993178825Sdfr if (ret && FD_ISSET(p->fd, &readset)) { 99478527Sassar --ret; 995178825Sdfr assert(ret >= 0); 99672445Sassar if(process_msg (context, p, log_fd, database, current_version)) 997178825Sdfr slave_dead(context, p); 998178825Sdfr } else if (slave_gone_p (p)) 999178825Sdfr slave_dead(context, p); 1000233294Sstas else if (slave_missing_p (p)) 1001178825Sdfr send_are_you_there (context, p); 1002120945Snectar } 100355682Smarkm 1004250782Sbz if (ret && FD_ISSET(listen6_fd, &readset)) { 1005250782Sbz add_slave (context, keytab, &slaves, listen6_fd); 1006250782Sbz --ret; 1007250782Sbz assert(ret >= 0); 1008250782Sbz } 100955682Smarkm if (ret && FD_ISSET(listen_fd, &readset)) { 101072445Sassar add_slave (context, keytab, &slaves, listen_fd); 101155682Smarkm --ret; 1012178825Sdfr assert(ret >= 0); 101355682Smarkm } 1014102644Snectar write_stats(context, slaves, current_version); 101555682Smarkm } 101655682Smarkm 1017233294Sstas if(exit_flag == SIGINT || exit_flag == SIGTERM) 1018233294Sstas krb5_warnx(context, "%s terminated", getprogname()); 1019233294Sstas#ifdef SIGXCPU 1020233294Sstas else if(exit_flag == SIGXCPU) 1021178825Sdfr krb5_warnx(context, "%s CPU time limit exceeded", getprogname()); 1022233294Sstas#endif 1023178825Sdfr else 1024233294Sstas krb5_warnx(context, "%s unexpected exit reason: %ld", 1025233294Sstas getprogname(), (long)exit_flag); 1026178825Sdfr 1027178825Sdfr write_master_down(context); 1028178825Sdfr 102955682Smarkm return 0; 103055682Smarkm} 1031