xref: /freebsd/crypto/heimdal/lib/kadm5/ipropd_master.c (revision ae77177087c655fc883075af4f425b37e032cd05)
1b528cefcSMark Murray /*
2*ae771770SStanislav Sedov  * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
3b528cefcSMark Murray  * (Royal Institute of Technology, Stockholm, Sweden).
4b528cefcSMark Murray  * All rights reserved.
5b528cefcSMark Murray  *
6b528cefcSMark Murray  * Redistribution and use in source and binary forms, with or without
7b528cefcSMark Murray  * modification, are permitted provided that the following conditions
8b528cefcSMark Murray  * are met:
9b528cefcSMark Murray  *
10b528cefcSMark Murray  * 1. Redistributions of source code must retain the above copyright
11b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer.
12b528cefcSMark Murray  *
13b528cefcSMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
14b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer in the
15b528cefcSMark Murray  *    documentation and/or other materials provided with the distribution.
16b528cefcSMark Murray  *
17b528cefcSMark Murray  * 3. Neither the name of the Institute nor the names of its contributors
18b528cefcSMark Murray  *    may be used to endorse or promote products derived from this software
19b528cefcSMark Murray  *    without specific prior written permission.
20b528cefcSMark Murray  *
21b528cefcSMark Murray  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22b528cefcSMark Murray  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23b528cefcSMark Murray  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24b528cefcSMark Murray  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25b528cefcSMark Murray  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26b528cefcSMark Murray  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27b528cefcSMark Murray  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28b528cefcSMark Murray  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29b528cefcSMark Murray  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30b528cefcSMark Murray  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31b528cefcSMark Murray  * SUCH DAMAGE.
32b528cefcSMark Murray  */
33b528cefcSMark Murray 
34b528cefcSMark Murray #include "iprop.h"
358373020dSJacques Vidrine #include <rtbl.h>
36b528cefcSMark Murray 
375e9cd1aeSAssar Westerlund static krb5_log_facility *log_facility;
38b528cefcSMark Murray 
39c19800e8SDoug Rabson const char *slave_stats_file;
40c19800e8SDoug Rabson const char *slave_time_missing = "2 min";
41c19800e8SDoug Rabson const char *slave_time_gone = "5 min";
42c19800e8SDoug Rabson 
43c19800e8SDoug Rabson static int time_before_missing;
44c19800e8SDoug Rabson static int time_before_gone;
45c19800e8SDoug Rabson 
46c19800e8SDoug Rabson const char *master_hostname;
47bbd80c28SJacques Vidrine 
48*ae771770SStanislav Sedov static krb5_socket_t
49b528cefcSMark Murray make_signal_socket (krb5_context context)
50b528cefcSMark Murray {
51*ae771770SStanislav Sedov #ifndef NO_UNIX_SOCKETS
52b528cefcSMark Murray     struct sockaddr_un addr;
53c19800e8SDoug Rabson     const char *fn;
54*ae771770SStanislav Sedov     krb5_socket_t fd;
55b528cefcSMark Murray 
56c19800e8SDoug Rabson     fn = kadm5_log_signal_socket(context);
57c19800e8SDoug Rabson 
58b528cefcSMark Murray     fd = socket (AF_UNIX, SOCK_DGRAM, 0);
59b528cefcSMark Murray     if (fd < 0)
60b528cefcSMark Murray 	krb5_err (context, 1, errno, "socket AF_UNIX");
61b528cefcSMark Murray     memset (&addr, 0, sizeof(addr));
62b528cefcSMark Murray     addr.sun_family = AF_UNIX;
63c19800e8SDoug Rabson     strlcpy (addr.sun_path, fn, sizeof(addr.sun_path));
64b528cefcSMark Murray     unlink (addr.sun_path);
65b528cefcSMark Murray     if (bind (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
66b528cefcSMark Murray 	krb5_err (context, 1, errno, "bind %s", addr.sun_path);
67b528cefcSMark Murray     return fd;
68*ae771770SStanislav Sedov #else
69*ae771770SStanislav Sedov     struct addrinfo *ai = NULL;
70*ae771770SStanislav Sedov     krb5_socket_t fd;
71*ae771770SStanislav Sedov 
72*ae771770SStanislav Sedov     kadm5_log_signal_socket_info(context, 1, &ai);
73*ae771770SStanislav Sedov 
74*ae771770SStanislav Sedov     fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
75*ae771770SStanislav Sedov     if (rk_IS_BAD_SOCKET(fd))
76*ae771770SStanislav Sedov 	krb5_err (context, 1, rk_SOCK_ERRNO, "socket AF=%d", ai->ai_family);
77*ae771770SStanislav Sedov 
78*ae771770SStanislav Sedov     if (rk_IS_SOCKET_ERROR( bind (fd, ai->ai_addr, ai->ai_addrlen) ))
79*ae771770SStanislav Sedov 	krb5_err (context, 1, rk_SOCK_ERRNO, "bind");
80*ae771770SStanislav Sedov     return fd;
81*ae771770SStanislav Sedov #endif
82b528cefcSMark Murray }
83b528cefcSMark Murray 
84*ae771770SStanislav Sedov static krb5_socket_t
85c19800e8SDoug Rabson make_listen_socket (krb5_context context, const char *port_str)
86b528cefcSMark Murray {
87*ae771770SStanislav Sedov     krb5_socket_t fd;
88b528cefcSMark Murray     int one = 1;
89b528cefcSMark Murray     struct sockaddr_in addr;
90b528cefcSMark Murray 
91b528cefcSMark Murray     fd = socket (AF_INET, SOCK_STREAM, 0);
92*ae771770SStanislav Sedov     if (rk_IS_BAD_SOCKET(fd))
93*ae771770SStanislav Sedov 	krb5_err (context, 1, rk_SOCK_ERRNO, "socket AF_INET");
944137ff4cSJacques Vidrine     setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one));
95b528cefcSMark Murray     memset (&addr, 0, sizeof(addr));
96b528cefcSMark Murray     addr.sin_family = AF_INET;
97c19800e8SDoug Rabson 
98c19800e8SDoug Rabson     if (port_str) {
995e9cd1aeSAssar Westerlund 	addr.sin_port = krb5_getportbyname (context,
100c19800e8SDoug Rabson 					      port_str, "tcp",
101c19800e8SDoug Rabson 					      0);
102c19800e8SDoug Rabson 	if (addr.sin_port == 0) {
103c19800e8SDoug Rabson 	    char *ptr;
104c19800e8SDoug Rabson 	    long port;
105c19800e8SDoug Rabson 
106c19800e8SDoug Rabson 	    port = strtol (port_str, &ptr, 10);
107c19800e8SDoug Rabson 	    if (port == 0 && ptr == port_str)
108c19800e8SDoug Rabson 		krb5_errx (context, 1, "bad port `%s'", port_str);
109c19800e8SDoug Rabson 	    addr.sin_port = htons(port);
110c19800e8SDoug Rabson 	}
111c19800e8SDoug Rabson     } else {
112c19800e8SDoug Rabson 	addr.sin_port = krb5_getportbyname (context, IPROP_SERVICE,
113c19800e8SDoug Rabson 					    "tcp", IPROP_PORT);
114c19800e8SDoug Rabson     }
115b528cefcSMark Murray     if(bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
116b528cefcSMark Murray 	krb5_err (context, 1, errno, "bind");
117b528cefcSMark Murray     if (listen(fd, SOMAXCONN) < 0)
118b528cefcSMark Murray 	krb5_err (context, 1, errno, "listen");
119b528cefcSMark Murray     return fd;
120b528cefcSMark Murray }
121b528cefcSMark Murray 
122b528cefcSMark Murray struct slave {
123*ae771770SStanislav Sedov     krb5_socket_t fd;
124b528cefcSMark Murray     struct sockaddr_in addr;
125b528cefcSMark Murray     char *name;
126b528cefcSMark Murray     krb5_auth_context ac;
127c19800e8SDoug Rabson     uint32_t version;
1288373020dSJacques Vidrine     time_t seen;
1298373020dSJacques Vidrine     unsigned long flags;
1308373020dSJacques Vidrine #define SLAVE_F_DEAD	0x1
131c19800e8SDoug Rabson #define SLAVE_F_AYT	0x2
132b528cefcSMark Murray     struct slave *next;
133b528cefcSMark Murray };
134b528cefcSMark Murray 
135b528cefcSMark Murray typedef struct slave slave;
136b528cefcSMark Murray 
137b528cefcSMark Murray static int
138b528cefcSMark Murray check_acl (krb5_context context, const char *name)
139b528cefcSMark Murray {
140c19800e8SDoug Rabson     const char *fn;
141b528cefcSMark Murray     FILE *fp;
142b528cefcSMark Murray     char buf[256];
143b528cefcSMark Murray     int ret = 1;
144*ae771770SStanislav Sedov     char *slavefile = NULL;
145b528cefcSMark Murray 
146*ae771770SStanislav Sedov     if (asprintf(&slavefile, "%s/slaves", hdb_db_dir(context)) == -1
147*ae771770SStanislav Sedov 	|| slavefile == NULL)
148*ae771770SStanislav Sedov 	errx(1, "out of memory");
149c19800e8SDoug Rabson 
150c19800e8SDoug Rabson     fn = krb5_config_get_string_default(context,
151c19800e8SDoug Rabson 					NULL,
152c19800e8SDoug Rabson 					slavefile,
153c19800e8SDoug Rabson 					"kdc",
154c19800e8SDoug Rabson 					"iprop-acl",
155c19800e8SDoug Rabson 					NULL);
156c19800e8SDoug Rabson 
157c19800e8SDoug Rabson     fp = fopen (fn, "r");
158c19800e8SDoug Rabson     free(slavefile);
159b528cefcSMark Murray     if (fp == NULL)
160b528cefcSMark Murray 	return 1;
161b528cefcSMark Murray     while (fgets(buf, sizeof(buf), fp) != NULL) {
162c19800e8SDoug Rabson 	buf[strcspn(buf, "\r\n")] = '\0';
163b528cefcSMark Murray 	if (strcmp (buf, name) == 0) {
164b528cefcSMark Murray 	    ret = 0;
165b528cefcSMark Murray 	    break;
166b528cefcSMark Murray 	}
167b528cefcSMark Murray     }
168b528cefcSMark Murray     fclose (fp);
169b528cefcSMark Murray     return ret;
170b528cefcSMark Murray }
171b528cefcSMark Murray 
172b528cefcSMark Murray static void
1738373020dSJacques Vidrine slave_seen(slave *s)
1748373020dSJacques Vidrine {
175c19800e8SDoug Rabson     s->flags &= ~SLAVE_F_AYT;
1768373020dSJacques Vidrine     s->seen = time(NULL);
1778373020dSJacques Vidrine }
1788373020dSJacques Vidrine 
179c19800e8SDoug Rabson static int
180c19800e8SDoug Rabson slave_missing_p (slave *s)
1818373020dSJacques Vidrine {
182c19800e8SDoug Rabson     if (time(NULL) > s->seen + time_before_missing)
183c19800e8SDoug Rabson 	return 1;
184c19800e8SDoug Rabson     return 0;
185c19800e8SDoug Rabson }
186c19800e8SDoug Rabson 
187c19800e8SDoug Rabson static int
188c19800e8SDoug Rabson slave_gone_p (slave *s)
189c19800e8SDoug Rabson {
190c19800e8SDoug Rabson     if (time(NULL) > s->seen + time_before_gone)
191c19800e8SDoug Rabson 	return 1;
192c19800e8SDoug Rabson     return 0;
193c19800e8SDoug Rabson }
194c19800e8SDoug Rabson 
195c19800e8SDoug Rabson static void
196c19800e8SDoug Rabson slave_dead(krb5_context context, slave *s)
197c19800e8SDoug Rabson {
198c19800e8SDoug Rabson     krb5_warnx(context, "slave %s dead", s->name);
199c19800e8SDoug Rabson 
200*ae771770SStanislav Sedov     if (!rk_IS_BAD_SOCKET(s->fd)) {
201*ae771770SStanislav Sedov 	rk_closesocket (s->fd);
202*ae771770SStanislav Sedov 	s->fd = rk_INVALID_SOCKET;
203bbd80c28SJacques Vidrine     }
2048373020dSJacques Vidrine     s->flags |= SLAVE_F_DEAD;
2058373020dSJacques Vidrine     slave_seen(s);
2068373020dSJacques Vidrine }
2078373020dSJacques Vidrine 
2088373020dSJacques Vidrine static void
2098373020dSJacques Vidrine remove_slave (krb5_context context, slave *s, slave **root)
2108373020dSJacques Vidrine {
2118373020dSJacques Vidrine     slave **p;
2128373020dSJacques Vidrine 
213*ae771770SStanislav Sedov     if (!rk_IS_BAD_SOCKET(s->fd))
214*ae771770SStanislav Sedov 	rk_closesocket (s->fd);
2158373020dSJacques Vidrine     if (s->name)
2168373020dSJacques Vidrine 	free (s->name);
2178373020dSJacques Vidrine     if (s->ac)
2188373020dSJacques Vidrine 	krb5_auth_con_free (context, s->ac);
2198373020dSJacques Vidrine 
2208373020dSJacques Vidrine     for (p = root; *p; p = &(*p)->next)
2218373020dSJacques Vidrine 	if (*p == s) {
2228373020dSJacques Vidrine 	    *p = s->next;
2238373020dSJacques Vidrine 	    break;
2248373020dSJacques Vidrine 	}
2258373020dSJacques Vidrine     free (s);
2268373020dSJacques Vidrine }
2278373020dSJacques Vidrine 
2288373020dSJacques Vidrine static void
229*ae771770SStanislav Sedov add_slave (krb5_context context, krb5_keytab keytab, slave **root,
230*ae771770SStanislav Sedov 	   krb5_socket_t fd)
231b528cefcSMark Murray {
232b528cefcSMark Murray     krb5_principal server;
233b528cefcSMark Murray     krb5_error_code ret;
234b528cefcSMark Murray     slave *s;
2355e9cd1aeSAssar Westerlund     socklen_t addr_len;
236b528cefcSMark Murray     krb5_ticket *ticket = NULL;
237b528cefcSMark Murray     char hostname[128];
238b528cefcSMark Murray 
239b528cefcSMark Murray     s = malloc(sizeof(*s));
240b528cefcSMark Murray     if (s == NULL) {
241b528cefcSMark Murray 	krb5_warnx (context, "add_slave: no memory");
242b528cefcSMark Murray 	return;
243b528cefcSMark Murray     }
244b528cefcSMark Murray     s->name = NULL;
245b528cefcSMark Murray     s->ac = NULL;
246b528cefcSMark Murray 
247b528cefcSMark Murray     addr_len = sizeof(s->addr);
248b528cefcSMark Murray     s->fd = accept (fd, (struct sockaddr *)&s->addr, &addr_len);
249*ae771770SStanislav Sedov     if (rk_IS_BAD_SOCKET(s->fd)) {
250*ae771770SStanislav Sedov 	krb5_warn (context, rk_SOCK_ERRNO, "accept");
251b528cefcSMark Murray 	goto error;
252b528cefcSMark Murray     }
253c19800e8SDoug Rabson     if (master_hostname)
254c19800e8SDoug Rabson 	strlcpy(hostname, master_hostname, sizeof(hostname));
255c19800e8SDoug Rabson     else
256b528cefcSMark Murray 	gethostname(hostname, sizeof(hostname));
257c19800e8SDoug Rabson 
258b528cefcSMark Murray     ret = krb5_sname_to_principal (context, hostname, IPROP_NAME,
259b528cefcSMark Murray 				   KRB5_NT_SRV_HST, &server);
260b528cefcSMark Murray     if (ret) {
261b528cefcSMark Murray 	krb5_warn (context, ret, "krb5_sname_to_principal");
262b528cefcSMark Murray 	goto error;
263b528cefcSMark Murray     }
264b528cefcSMark Murray 
265b528cefcSMark Murray     ret = krb5_recvauth (context, &s->ac, &s->fd,
2665e9cd1aeSAssar Westerlund 			 IPROP_VERSION, server, 0, keytab, &ticket);
267b528cefcSMark Murray     krb5_free_principal (context, server);
268b528cefcSMark Murray     if (ret) {
269b528cefcSMark Murray 	krb5_warn (context, ret, "krb5_recvauth");
270b528cefcSMark Murray 	goto error;
271b528cefcSMark Murray     }
272b528cefcSMark Murray     ret = krb5_unparse_name (context, ticket->client, &s->name);
273*ae771770SStanislav Sedov     krb5_free_ticket (context, ticket);
274b528cefcSMark Murray     if (ret) {
275b528cefcSMark Murray 	krb5_warn (context, ret, "krb5_unparse_name");
276b528cefcSMark Murray 	goto error;
277b528cefcSMark Murray     }
278b528cefcSMark Murray     if (check_acl (context, s->name)) {
279b528cefcSMark Murray 	krb5_warnx (context, "%s not in acl", s->name);
280b528cefcSMark Murray 	goto error;
281b528cefcSMark Murray     }
2828373020dSJacques Vidrine 
2838373020dSJacques Vidrine     {
2848373020dSJacques Vidrine 	slave *l = *root;
2858373020dSJacques Vidrine 
2868373020dSJacques Vidrine 	while (l) {
2878373020dSJacques Vidrine 	    if (strcmp(l->name, s->name) == 0)
2888373020dSJacques Vidrine 		break;
2898373020dSJacques Vidrine 	    l = l->next;
2908373020dSJacques Vidrine 	}
2918373020dSJacques Vidrine 	if (l) {
2928373020dSJacques Vidrine 	    if (l->flags & SLAVE_F_DEAD) {
2938373020dSJacques Vidrine 		remove_slave(context, l, root);
2948373020dSJacques Vidrine 	    } else {
2958373020dSJacques Vidrine 		krb5_warnx (context, "second connection from %s", s->name);
2968373020dSJacques Vidrine 		goto error;
2978373020dSJacques Vidrine 	    }
2988373020dSJacques Vidrine 	}
2998373020dSJacques Vidrine     }
3008373020dSJacques Vidrine 
3015e9cd1aeSAssar Westerlund     krb5_warnx (context, "connection from %s", s->name);
302b528cefcSMark Murray 
303b528cefcSMark Murray     s->version = 0;
3048373020dSJacques Vidrine     s->flags = 0;
3058373020dSJacques Vidrine     slave_seen(s);
306b528cefcSMark Murray     s->next = *root;
307b528cefcSMark Murray     *root = s;
308b528cefcSMark Murray     return;
309b528cefcSMark Murray error:
3108373020dSJacques Vidrine     remove_slave(context, s, root);
311b528cefcSMark Murray }
312b528cefcSMark Murray 
3135e9cd1aeSAssar Westerlund struct prop_context {
3145e9cd1aeSAssar Westerlund     krb5_auth_context auth_context;
315*ae771770SStanislav Sedov     krb5_socket_t fd;
3165e9cd1aeSAssar Westerlund };
3175e9cd1aeSAssar Westerlund 
318b528cefcSMark Murray static int
319c19800e8SDoug Rabson prop_one (krb5_context context, HDB *db, hdb_entry_ex *entry, void *v)
320b528cefcSMark Murray {
3215e9cd1aeSAssar Westerlund     krb5_error_code ret;
322c19800e8SDoug Rabson     krb5_storage *sp;
3235e9cd1aeSAssar Westerlund     krb5_data data;
324c19800e8SDoug Rabson     struct slave *s = (struct slave *)v;
3255e9cd1aeSAssar Westerlund 
326c19800e8SDoug Rabson     ret = hdb_entry2value (context, &entry->entry, &data);
3275e9cd1aeSAssar Westerlund     if (ret)
3285e9cd1aeSAssar Westerlund 	return ret;
3295e9cd1aeSAssar Westerlund     ret = krb5_data_realloc (&data, data.length + 4);
3305e9cd1aeSAssar Westerlund     if (ret) {
3315e9cd1aeSAssar Westerlund 	krb5_data_free (&data);
3325e9cd1aeSAssar Westerlund 	return ret;
3335e9cd1aeSAssar Westerlund     }
3345e9cd1aeSAssar Westerlund     memmove ((char *)data.data + 4, data.data, data.length - 4);
335c19800e8SDoug Rabson     sp = krb5_storage_from_data(&data);
336c19800e8SDoug Rabson     if (sp == NULL) {
337c19800e8SDoug Rabson 	krb5_data_free (&data);
338c19800e8SDoug Rabson 	return ENOMEM;
339c19800e8SDoug Rabson     }
340c19800e8SDoug Rabson     krb5_store_int32(sp, ONE_PRINC);
341c19800e8SDoug Rabson     krb5_storage_free(sp);
3425e9cd1aeSAssar Westerlund 
343c19800e8SDoug Rabson     ret = krb5_write_priv_message (context, s->ac, &s->fd, &data);
3445e9cd1aeSAssar Westerlund     krb5_data_free (&data);
3455e9cd1aeSAssar Westerlund     return ret;
3465e9cd1aeSAssar Westerlund }
3475e9cd1aeSAssar Westerlund 
3485e9cd1aeSAssar Westerlund static int
3495e9cd1aeSAssar Westerlund send_complete (krb5_context context, slave *s,
350c19800e8SDoug Rabson 	       const char *database, uint32_t current_version)
3515e9cd1aeSAssar Westerlund {
3525e9cd1aeSAssar Westerlund     krb5_error_code ret;
353c19800e8SDoug Rabson     krb5_storage *sp;
3545e9cd1aeSAssar Westerlund     HDB *db;
3555e9cd1aeSAssar Westerlund     krb5_data data;
3565e9cd1aeSAssar Westerlund     char buf[8];
3575e9cd1aeSAssar Westerlund 
3585e9cd1aeSAssar Westerlund     ret = hdb_create (context, &db, database);
3595e9cd1aeSAssar Westerlund     if (ret)
3605e9cd1aeSAssar Westerlund 	krb5_err (context, 1, ret, "hdb_create: %s", database);
361c19800e8SDoug Rabson     ret = db->hdb_open (context, db, O_RDONLY, 0);
3625e9cd1aeSAssar Westerlund     if (ret)
3635e9cd1aeSAssar Westerlund 	krb5_err (context, 1, ret, "db->open");
3645e9cd1aeSAssar Westerlund 
365c19800e8SDoug Rabson     sp = krb5_storage_from_mem (buf, 4);
366c19800e8SDoug Rabson     if (sp == NULL)
367c19800e8SDoug Rabson 	krb5_errx (context, 1, "krb5_storage_from_mem");
368c19800e8SDoug Rabson     krb5_store_int32 (sp, TELL_YOU_EVERYTHING);
369c19800e8SDoug Rabson     krb5_storage_free (sp);
3705e9cd1aeSAssar Westerlund 
3715e9cd1aeSAssar Westerlund     data.data   = buf;
3725e9cd1aeSAssar Westerlund     data.length = 4;
3735e9cd1aeSAssar Westerlund 
3745e9cd1aeSAssar Westerlund     ret = krb5_write_priv_message(context, s->ac, &s->fd, &data);
3755e9cd1aeSAssar Westerlund 
3768373020dSJacques Vidrine     if (ret) {
3778373020dSJacques Vidrine 	krb5_warn (context, ret, "krb5_write_priv_message");
378c19800e8SDoug Rabson 	slave_dead(context, s);
3798373020dSJacques Vidrine 	return ret;
3808373020dSJacques Vidrine     }
3815e9cd1aeSAssar Westerlund 
382*ae771770SStanislav Sedov     ret = hdb_foreach (context, db, HDB_F_ADMIN_DATA, prop_one, s);
3838373020dSJacques Vidrine     if (ret) {
3848373020dSJacques Vidrine 	krb5_warn (context, ret, "hdb_foreach");
385c19800e8SDoug Rabson 	slave_dead(context, s);
3868373020dSJacques Vidrine 	return ret;
3878373020dSJacques Vidrine     }
3885e9cd1aeSAssar Westerlund 
389c19800e8SDoug Rabson     (*db->hdb_close)(context, db);
390c19800e8SDoug Rabson     (*db->hdb_destroy)(context, db);
391c19800e8SDoug Rabson 
392c19800e8SDoug Rabson     sp = krb5_storage_from_mem (buf, 8);
393c19800e8SDoug Rabson     if (sp == NULL)
394c19800e8SDoug Rabson 	krb5_errx (context, 1, "krb5_storage_from_mem");
395c19800e8SDoug Rabson     krb5_store_int32 (sp, NOW_YOU_HAVE);
396c19800e8SDoug Rabson     krb5_store_int32 (sp, current_version);
397c19800e8SDoug Rabson     krb5_storage_free (sp);
398c19800e8SDoug Rabson 
3995e9cd1aeSAssar Westerlund     data.length = 8;
4005e9cd1aeSAssar Westerlund 
4018373020dSJacques Vidrine     s->version = current_version;
4025e9cd1aeSAssar Westerlund 
4038373020dSJacques Vidrine     ret = krb5_write_priv_message(context, s->ac, &s->fd, &data);
4048373020dSJacques Vidrine     if (ret) {
405c19800e8SDoug Rabson 	slave_dead(context, s);
4068373020dSJacques Vidrine 	krb5_warn (context, ret, "krb5_write_priv_message");
4078373020dSJacques Vidrine 	return ret;
4088373020dSJacques Vidrine     }
4098373020dSJacques Vidrine 
4108373020dSJacques Vidrine     slave_seen(s);
4115e9cd1aeSAssar Westerlund 
4125e9cd1aeSAssar Westerlund     return 0;
413b528cefcSMark Murray }
414b528cefcSMark Murray 
415b528cefcSMark Murray static int
416c19800e8SDoug Rabson send_are_you_there (krb5_context context, slave *s)
417b528cefcSMark Murray {
4185e9cd1aeSAssar Westerlund     krb5_storage *sp;
419c19800e8SDoug Rabson     krb5_data data;
420c19800e8SDoug Rabson     char buf[4];
421c19800e8SDoug Rabson     int ret;
422c19800e8SDoug Rabson 
423c19800e8SDoug Rabson     if (s->flags & (SLAVE_F_DEAD|SLAVE_F_AYT))
424c19800e8SDoug Rabson 	return 0;
425c19800e8SDoug Rabson 
426*ae771770SStanislav Sedov     krb5_warnx(context, "slave %s missing, sending AYT", s->name);
427*ae771770SStanislav Sedov 
428c19800e8SDoug Rabson     s->flags |= SLAVE_F_AYT;
429c19800e8SDoug Rabson 
430c19800e8SDoug Rabson     data.data = buf;
431c19800e8SDoug Rabson     data.length = 4;
432c19800e8SDoug Rabson 
433c19800e8SDoug Rabson     sp = krb5_storage_from_mem (buf, 4);
434c19800e8SDoug Rabson     if (sp == NULL) {
435c19800e8SDoug Rabson 	krb5_warnx (context, "are_you_there: krb5_data_alloc");
436c19800e8SDoug Rabson 	slave_dead(context, s);
437c19800e8SDoug Rabson 	return 1;
438c19800e8SDoug Rabson     }
439c19800e8SDoug Rabson     krb5_store_int32 (sp, ARE_YOU_THERE);
440c19800e8SDoug Rabson     krb5_storage_free (sp);
441c19800e8SDoug Rabson 
442c19800e8SDoug Rabson     ret = krb5_write_priv_message(context, s->ac, &s->fd, &data);
443c19800e8SDoug Rabson 
444c19800e8SDoug Rabson     if (ret) {
445c19800e8SDoug Rabson 	krb5_warn (context, ret, "are_you_there: krb5_write_priv_message");
446c19800e8SDoug Rabson 	slave_dead(context, s);
447c19800e8SDoug Rabson 	return 1;
448c19800e8SDoug Rabson     }
449c19800e8SDoug Rabson 
450c19800e8SDoug Rabson     return 0;
451c19800e8SDoug Rabson }
452c19800e8SDoug Rabson 
453c19800e8SDoug Rabson static int
454c19800e8SDoug Rabson send_diffs (krb5_context context, slave *s, int log_fd,
455c19800e8SDoug Rabson 	    const char *database, uint32_t current_version)
456c19800e8SDoug Rabson {
457c19800e8SDoug Rabson     krb5_storage *sp;
458c19800e8SDoug Rabson     uint32_t ver;
459b528cefcSMark Murray     time_t timestamp;
460b528cefcSMark Murray     enum kadm_ops op;
461c19800e8SDoug Rabson     uint32_t len;
462b528cefcSMark Murray     off_t right, left;
463b528cefcSMark Murray     krb5_data data;
464b528cefcSMark Murray     int ret = 0;
465b528cefcSMark Murray 
466c19800e8SDoug Rabson     if (s->version == current_version) {
467c19800e8SDoug Rabson 	krb5_warnx(context, "slave %s in sync already at version %ld",
468c19800e8SDoug Rabson 		   s->name, (long)s->version);
469b528cefcSMark Murray 	return 0;
470c19800e8SDoug Rabson     }
471b528cefcSMark Murray 
4728373020dSJacques Vidrine     if (s->flags & SLAVE_F_DEAD)
4738373020dSJacques Vidrine 	return 0;
4748373020dSJacques Vidrine 
475c19800e8SDoug Rabson     /* if slave is a fresh client, starting over */
476c19800e8SDoug Rabson     if (s->version == 0) {
477c19800e8SDoug Rabson 	krb5_warnx(context, "sending complete log to fresh slave %s",
478c19800e8SDoug Rabson 		   s->name);
479c19800e8SDoug Rabson 	return send_complete (context, s, database, current_version);
480c19800e8SDoug Rabson     }
481c19800e8SDoug Rabson 
482b528cefcSMark Murray     sp = kadm5_log_goto_end (log_fd);
4838373020dSJacques Vidrine     right = krb5_storage_seek(sp, 0, SEEK_CUR);
484b528cefcSMark Murray     for (;;) {
485c19800e8SDoug Rabson 	ret = kadm5_log_previous (context, sp, &ver, &timestamp, &op, &len);
486c19800e8SDoug Rabson 	if (ret)
487c19800e8SDoug Rabson 	    krb5_err(context, 1, ret,
488c19800e8SDoug Rabson 		     "send_diffs: failed to find previous entry");
4898373020dSJacques Vidrine 	left = krb5_storage_seek(sp, -16, SEEK_CUR);
490b528cefcSMark Murray 	if (ver == s->version)
491b528cefcSMark Murray 	    return 0;
492b528cefcSMark Murray 	if (ver == s->version + 1)
493b528cefcSMark Murray 	    break;
494c19800e8SDoug Rabson 	if (left == 0) {
495*ae771770SStanislav Sedov 	    krb5_storage_free(sp);
496c19800e8SDoug Rabson 	    krb5_warnx(context,
497c19800e8SDoug Rabson 		       "slave %s (version %lu) out of sync with master "
498c19800e8SDoug Rabson 		       "(first version in log %lu), sending complete database",
499c19800e8SDoug Rabson 		       s->name, (unsigned long)s->version, (unsigned long)ver);
5005e9cd1aeSAssar Westerlund 	    return send_complete (context, s, database, current_version);
501b528cefcSMark Murray 	}
502c19800e8SDoug Rabson     }
503c19800e8SDoug Rabson 
504c19800e8SDoug Rabson     krb5_warnx(context,
505c19800e8SDoug Rabson 	       "syncing slave %s from version %lu to version %lu",
506c19800e8SDoug Rabson 	       s->name, (unsigned long)s->version,
507c19800e8SDoug Rabson 	       (unsigned long)current_version);
508c19800e8SDoug Rabson 
509c19800e8SDoug Rabson     ret = krb5_data_alloc (&data, right - left + 4);
510c19800e8SDoug Rabson     if (ret) {
511*ae771770SStanislav Sedov 	krb5_storage_free(sp);
512c19800e8SDoug Rabson 	krb5_warn (context, ret, "send_diffs: krb5_data_alloc");
513c19800e8SDoug Rabson 	slave_dead(context, s);
514c19800e8SDoug Rabson 	return 1;
515c19800e8SDoug Rabson     }
5168373020dSJacques Vidrine     krb5_storage_read (sp, (char *)data.data + 4, data.length - 4);
517b528cefcSMark Murray     krb5_storage_free(sp);
518b528cefcSMark Murray 
519c19800e8SDoug Rabson     sp = krb5_storage_from_data (&data);
520c19800e8SDoug Rabson     if (sp == NULL) {
521c19800e8SDoug Rabson 	krb5_warnx (context, "send_diffs: krb5_storage_from_data");
522c19800e8SDoug Rabson 	slave_dead(context, s);
523c19800e8SDoug Rabson 	return 1;
524c19800e8SDoug Rabson     }
525c19800e8SDoug Rabson     krb5_store_int32 (sp, FOR_YOU);
526c19800e8SDoug Rabson     krb5_storage_free(sp);
527b528cefcSMark Murray 
5285e9cd1aeSAssar Westerlund     ret = krb5_write_priv_message(context, s->ac, &s->fd, &data);
5298373020dSJacques Vidrine     krb5_data_free(&data);
530b528cefcSMark Murray 
531b528cefcSMark Murray     if (ret) {
532c19800e8SDoug Rabson 	krb5_warn (context, ret, "send_diffs: krb5_write_priv_message");
533c19800e8SDoug Rabson 	slave_dead(context, s);
534b528cefcSMark Murray 	return 1;
535b528cefcSMark Murray     }
5368373020dSJacques Vidrine     slave_seen(s);
5378373020dSJacques Vidrine 
538c19800e8SDoug Rabson     s->version = current_version;
539c19800e8SDoug Rabson 
540b528cefcSMark Murray     return 0;
541b528cefcSMark Murray }
542b528cefcSMark Murray 
543b528cefcSMark Murray static int
544b528cefcSMark Murray process_msg (krb5_context context, slave *s, int log_fd,
545c19800e8SDoug Rabson 	     const char *database, uint32_t current_version)
546b528cefcSMark Murray {
547b528cefcSMark Murray     int ret = 0;
5485e9cd1aeSAssar Westerlund     krb5_data out;
549b528cefcSMark Murray     krb5_storage *sp;
550b528cefcSMark Murray     int32_t tmp;
551b528cefcSMark Murray 
5525e9cd1aeSAssar Westerlund     ret = krb5_read_priv_message(context, s->ac, &s->fd, &out);
553b528cefcSMark Murray     if(ret) {
5545e9cd1aeSAssar Westerlund 	krb5_warn (context, ret, "error reading message from %s", s->name);
555b528cefcSMark Murray 	return 1;
556b528cefcSMark Murray     }
557b528cefcSMark Murray 
558b528cefcSMark Murray     sp = krb5_storage_from_mem (out.data, out.length);
559c19800e8SDoug Rabson     if (sp == NULL) {
560c19800e8SDoug Rabson 	krb5_warnx (context, "process_msg: no memory");
561c19800e8SDoug Rabson 	krb5_data_free (&out);
562c19800e8SDoug Rabson 	return 1;
563c19800e8SDoug Rabson     }
564c19800e8SDoug Rabson     if (krb5_ret_int32 (sp, &tmp) != 0) {
565c19800e8SDoug Rabson 	krb5_warnx (context, "process_msg: client send too short command");
566c19800e8SDoug Rabson 	krb5_data_free (&out);
567c19800e8SDoug Rabson 	return 1;
568c19800e8SDoug Rabson     }
569b528cefcSMark Murray     switch (tmp) {
570b528cefcSMark Murray     case I_HAVE :
571c19800e8SDoug Rabson 	ret = krb5_ret_int32 (sp, &tmp);
572c19800e8SDoug Rabson 	if (ret != 0) {
573c19800e8SDoug Rabson 	    krb5_warnx (context, "process_msg: client send too I_HAVE data");
574b528cefcSMark Murray 	    break;
575c19800e8SDoug Rabson 	}
576c19800e8SDoug Rabson 	/* new started slave that have old log */
577c19800e8SDoug Rabson 	if (s->version == 0 && tmp != 0) {
578*ae771770SStanislav Sedov 	    if (current_version < (uint32_t)tmp) {
579*ae771770SStanislav Sedov 		krb5_warnx (context, "Slave %s (version %lu) have later version "
580*ae771770SStanislav Sedov 			    "the master (version %lu) OUT OF SYNC",
581*ae771770SStanislav Sedov 			    s->name, (unsigned long)tmp,
582*ae771770SStanislav Sedov 			    (unsigned long)current_version);
583*ae771770SStanislav Sedov 	    }
584c19800e8SDoug Rabson 	    s->version = tmp;
585c19800e8SDoug Rabson 	}
586*ae771770SStanislav Sedov 	if ((uint32_t)tmp < s->version) {
587c19800e8SDoug Rabson 	    krb5_warnx (context, "Slave claims to not have "
588c19800e8SDoug Rabson 			"version we already sent to it");
589c19800e8SDoug Rabson 	} else {
590c19800e8SDoug Rabson 	    ret = send_diffs (context, s, log_fd, database, current_version);
591c19800e8SDoug Rabson 	}
592c19800e8SDoug Rabson 	break;
593c19800e8SDoug Rabson     case I_AM_HERE :
594c19800e8SDoug Rabson 	break;
595c19800e8SDoug Rabson     case ARE_YOU_THERE:
596b528cefcSMark Murray     case FOR_YOU :
597b528cefcSMark Murray     default :
598b528cefcSMark Murray 	krb5_warnx (context, "Ignoring command %d", tmp);
599b528cefcSMark Murray 	break;
600b528cefcSMark Murray     }
601b528cefcSMark Murray 
602b528cefcSMark Murray     krb5_data_free (&out);
603*ae771770SStanislav Sedov     krb5_storage_free (sp);
6048373020dSJacques Vidrine 
6058373020dSJacques Vidrine     slave_seen(s);
6068373020dSJacques Vidrine 
607b528cefcSMark Murray     return ret;
608b528cefcSMark Murray }
609b528cefcSMark Murray 
6108373020dSJacques Vidrine #define SLAVE_NAME	"Name"
6118373020dSJacques Vidrine #define SLAVE_ADDRESS	"Address"
6128373020dSJacques Vidrine #define SLAVE_VERSION	"Version"
6138373020dSJacques Vidrine #define SLAVE_STATUS	"Status"
6148373020dSJacques Vidrine #define SLAVE_SEEN	"Last Seen"
6158373020dSJacques Vidrine 
616c19800e8SDoug Rabson static FILE *
617c19800e8SDoug Rabson open_stats(krb5_context context)
618c19800e8SDoug Rabson {
619c19800e8SDoug Rabson     char *statfile = NULL;
620c19800e8SDoug Rabson     const char *fn;
621c19800e8SDoug Rabson     FILE *f;
622c19800e8SDoug Rabson 
623c19800e8SDoug Rabson     if (slave_stats_file)
624c19800e8SDoug Rabson 	fn = slave_stats_file;
625c19800e8SDoug Rabson     else {
626c19800e8SDoug Rabson 	asprintf(&statfile,  "%s/slaves-stats", hdb_db_dir(context));
627c19800e8SDoug Rabson 	fn = krb5_config_get_string_default(context,
628c19800e8SDoug Rabson 					    NULL,
629c19800e8SDoug Rabson 					    statfile,
630c19800e8SDoug Rabson 					    "kdc",
631c19800e8SDoug Rabson 					    "iprop-stats",
632c19800e8SDoug Rabson 					    NULL);
633c19800e8SDoug Rabson     }
634c19800e8SDoug Rabson     f = fopen(fn, "w");
635c19800e8SDoug Rabson     if (statfile)
636c19800e8SDoug Rabson 	free(statfile);
637c19800e8SDoug Rabson 
638c19800e8SDoug Rabson     return f;
639c19800e8SDoug Rabson }
640c19800e8SDoug Rabson 
6418373020dSJacques Vidrine static void
642c19800e8SDoug Rabson write_master_down(krb5_context context)
643c19800e8SDoug Rabson {
644c19800e8SDoug Rabson     char str[100];
645c19800e8SDoug Rabson     time_t t = time(NULL);
646c19800e8SDoug Rabson     FILE *fp;
647c19800e8SDoug Rabson 
648c19800e8SDoug Rabson     fp = open_stats(context);
649c19800e8SDoug Rabson     if (fp == NULL)
650c19800e8SDoug Rabson 	return;
651c19800e8SDoug Rabson     krb5_format_time(context, t, str, sizeof(str), TRUE);
652c19800e8SDoug Rabson     fprintf(fp, "master down at %s\n", str);
653c19800e8SDoug Rabson 
654c19800e8SDoug Rabson     fclose(fp);
655c19800e8SDoug Rabson }
656c19800e8SDoug Rabson 
657c19800e8SDoug Rabson static void
658c19800e8SDoug Rabson write_stats(krb5_context context, slave *slaves, uint32_t current_version)
6598373020dSJacques Vidrine {
660bbd80c28SJacques Vidrine     char str[100];
6618373020dSJacques Vidrine     rtbl_t tbl;
6628373020dSJacques Vidrine     time_t t = time(NULL);
6638373020dSJacques Vidrine     FILE *fp;
6648373020dSJacques Vidrine 
665c19800e8SDoug Rabson     fp = open_stats(context);
6668373020dSJacques Vidrine     if (fp == NULL)
6678373020dSJacques Vidrine 	return;
6688373020dSJacques Vidrine 
669c19800e8SDoug Rabson     krb5_format_time(context, t, str, sizeof(str), TRUE);
6708373020dSJacques Vidrine     fprintf(fp, "Status for slaves, last updated: %s\n\n", str);
6718373020dSJacques Vidrine 
6728373020dSJacques Vidrine     fprintf(fp, "Master version: %lu\n\n", (unsigned long)current_version);
6738373020dSJacques Vidrine 
6748373020dSJacques Vidrine     tbl = rtbl_create();
6758373020dSJacques Vidrine     if (tbl == NULL) {
6768373020dSJacques Vidrine 	fclose(fp);
6778373020dSJacques Vidrine 	return;
6788373020dSJacques Vidrine     }
6798373020dSJacques Vidrine 
6808373020dSJacques Vidrine     rtbl_add_column(tbl, SLAVE_NAME, 0);
6818373020dSJacques Vidrine     rtbl_add_column(tbl, SLAVE_ADDRESS, 0);
6828373020dSJacques Vidrine     rtbl_add_column(tbl, SLAVE_VERSION, RTBL_ALIGN_RIGHT);
6838373020dSJacques Vidrine     rtbl_add_column(tbl, SLAVE_STATUS, 0);
6848373020dSJacques Vidrine     rtbl_add_column(tbl, SLAVE_SEEN, 0);
6858373020dSJacques Vidrine 
6868373020dSJacques Vidrine     rtbl_set_prefix(tbl, "  ");
6878373020dSJacques Vidrine     rtbl_set_column_prefix(tbl, SLAVE_NAME, "");
6888373020dSJacques Vidrine 
6898373020dSJacques Vidrine     while (slaves) {
6908373020dSJacques Vidrine 	krb5_address addr;
6918373020dSJacques Vidrine 	krb5_error_code ret;
6928373020dSJacques Vidrine 	rtbl_add_column_entry(tbl, SLAVE_NAME, slaves->name);
6938373020dSJacques Vidrine 	ret = krb5_sockaddr2address (context,
6948373020dSJacques Vidrine 				     (struct sockaddr*)&slaves->addr, &addr);
6958373020dSJacques Vidrine 	if(ret == 0) {
6968373020dSJacques Vidrine 	    krb5_print_address(&addr, str, sizeof(str), NULL);
6978373020dSJacques Vidrine 	    krb5_free_address(context, &addr);
6988373020dSJacques Vidrine 	    rtbl_add_column_entry(tbl, SLAVE_ADDRESS, str);
6998373020dSJacques Vidrine 	} else
7008373020dSJacques Vidrine 	    rtbl_add_column_entry(tbl, SLAVE_ADDRESS, "<unknown>");
7018373020dSJacques Vidrine 
7028373020dSJacques Vidrine 	snprintf(str, sizeof(str), "%u", (unsigned)slaves->version);
7038373020dSJacques Vidrine 	rtbl_add_column_entry(tbl, SLAVE_VERSION, str);
7048373020dSJacques Vidrine 
7058373020dSJacques Vidrine 	if (slaves->flags & SLAVE_F_DEAD)
7068373020dSJacques Vidrine 	    rtbl_add_column_entry(tbl, SLAVE_STATUS, "Down");
7078373020dSJacques Vidrine 	else
7088373020dSJacques Vidrine 	    rtbl_add_column_entry(tbl, SLAVE_STATUS, "Up");
7098373020dSJacques Vidrine 
710c19800e8SDoug Rabson 	ret = krb5_format_time(context, slaves->seen, str, sizeof(str), TRUE);
7118373020dSJacques Vidrine 	rtbl_add_column_entry(tbl, SLAVE_SEEN, str);
7128373020dSJacques Vidrine 
7138373020dSJacques Vidrine 	slaves = slaves->next;
7148373020dSJacques Vidrine     }
7158373020dSJacques Vidrine 
7168373020dSJacques Vidrine     rtbl_format(tbl, fp);
7178373020dSJacques Vidrine     rtbl_destroy(tbl);
7188373020dSJacques Vidrine 
7198373020dSJacques Vidrine     fclose(fp);
7208373020dSJacques Vidrine }
7218373020dSJacques Vidrine 
7228373020dSJacques Vidrine 
723*ae771770SStanislav Sedov static char sHDB[] = "HDB:";
7245e9cd1aeSAssar Westerlund static char *realm;
7255e9cd1aeSAssar Westerlund static int version_flag;
7265e9cd1aeSAssar Westerlund static int help_flag;
727*ae771770SStanislav Sedov static char *keytab_str = sHDB;
7285e9cd1aeSAssar Westerlund static char *database;
729c19800e8SDoug Rabson static char *config_file;
730c19800e8SDoug Rabson static char *port_str;
731*ae771770SStanislav Sedov #ifdef SUPPORT_DETACH
732c19800e8SDoug Rabson static int detach_from_console = 0;
733*ae771770SStanislav Sedov #endif
7345e9cd1aeSAssar Westerlund 
7355e9cd1aeSAssar Westerlund static struct getargs args[] = {
736*ae771770SStanislav Sedov     { "config-file", 'c', arg_string, &config_file, NULL, NULL },
737*ae771770SStanislav Sedov     { "realm", 'r', arg_string, &realm, NULL, NULL },
7385e9cd1aeSAssar Westerlund     { "keytab", 'k', arg_string, &keytab_str,
7395e9cd1aeSAssar Westerlund       "keytab to get authentication from", "kspec" },
7405e9cd1aeSAssar Westerlund     { "database", 'd', arg_string, &database, "database", "file"},
741*ae771770SStanislav Sedov     { "slave-stats-file", 0, arg_string, rk_UNCONST(&slave_stats_file),
742c19800e8SDoug Rabson       "file for slave status information", "file"},
743*ae771770SStanislav Sedov     { "time-missing", 0, arg_string, rk_UNCONST(&slave_time_missing),
744c19800e8SDoug Rabson       "time before slave is polled for presence", "time"},
745*ae771770SStanislav Sedov     { "time-gone", 0, arg_string, rk_UNCONST(&slave_time_gone),
746c19800e8SDoug Rabson       "time of inactivity after which a slave is considered gone", "time"},
747c19800e8SDoug Rabson     { "port", 0, arg_string, &port_str,
748c19800e8SDoug Rabson       "port ipropd will listen to", "port"},
749*ae771770SStanislav Sedov #ifdef SUPPORT_DETACH
750c19800e8SDoug Rabson     { "detach", 0, arg_flag, &detach_from_console,
751*ae771770SStanislav Sedov       "detach from console", NULL },
752*ae771770SStanislav Sedov #endif
753*ae771770SStanislav Sedov     { "hostname", 0, arg_string, rk_UNCONST(&master_hostname),
754c19800e8SDoug Rabson       "hostname of master (if not same as hostname)", "hostname" },
755*ae771770SStanislav Sedov     { "version", 0, arg_flag, &version_flag, NULL, NULL },
756*ae771770SStanislav Sedov     { "help", 0, arg_flag, &help_flag, NULL, NULL }
757b528cefcSMark Murray };
7585e9cd1aeSAssar Westerlund static int num_args = sizeof(args) / sizeof(args[0]);
759b528cefcSMark Murray 
760b528cefcSMark Murray int
761b528cefcSMark Murray main(int argc, char **argv)
762b528cefcSMark Murray {
763b528cefcSMark Murray     krb5_error_code ret;
764b528cefcSMark Murray     krb5_context context;
765b528cefcSMark Murray     void *kadm_handle;
766b528cefcSMark Murray     kadm5_server_context *server_context;
767b528cefcSMark Murray     kadm5_config_params conf;
768*ae771770SStanislav Sedov     krb5_socket_t signal_fd, listen_fd;
769b528cefcSMark Murray     int log_fd;
770b528cefcSMark Murray     slave *slaves = NULL;
771c19800e8SDoug Rabson     uint32_t current_version = 0, old_version = 0;
7725e9cd1aeSAssar Westerlund     krb5_keytab keytab;
773c19800e8SDoug Rabson     int optidx;
774c19800e8SDoug Rabson     char **files;
775b528cefcSMark Murray 
776c19800e8SDoug Rabson     optidx = krb5_program_setup(&context, argc, argv, args, num_args, NULL);
777b528cefcSMark Murray 
778b528cefcSMark Murray     if(help_flag)
779b528cefcSMark Murray 	krb5_std_usage(0, args, num_args);
780b528cefcSMark Murray     if(version_flag) {
781b528cefcSMark Murray 	print_version(NULL);
782b528cefcSMark Murray 	exit(0);
783b528cefcSMark Murray     }
784b528cefcSMark Murray 
785c19800e8SDoug Rabson     setup_signal();
786c19800e8SDoug Rabson 
787c19800e8SDoug Rabson     if (config_file == NULL) {
788c19800e8SDoug Rabson 	asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context));
789c19800e8SDoug Rabson 	if (config_file == NULL)
790c19800e8SDoug Rabson 	    errx(1, "out of memory");
791c19800e8SDoug Rabson     }
792c19800e8SDoug Rabson 
793c19800e8SDoug Rabson     ret = krb5_prepend_config_files_default(config_file, &files);
794c19800e8SDoug Rabson     if (ret)
795c19800e8SDoug Rabson 	krb5_err(context, 1, ret, "getting configuration files");
796c19800e8SDoug Rabson 
797c19800e8SDoug Rabson     ret = krb5_set_config_files(context, files);
798c19800e8SDoug Rabson     krb5_free_config_files(files);
799c19800e8SDoug Rabson     if (ret)
800c19800e8SDoug Rabson 	krb5_err(context, 1, ret, "reading configuration files");
801c19800e8SDoug Rabson 
802c19800e8SDoug Rabson     time_before_gone = parse_time (slave_time_gone,  "s");
803c19800e8SDoug Rabson     if (time_before_gone < 0)
804c19800e8SDoug Rabson 	krb5_errx (context, 1, "couldn't parse time: %s", slave_time_gone);
805c19800e8SDoug Rabson     time_before_missing = parse_time (slave_time_missing,  "s");
806c19800e8SDoug Rabson     if (time_before_missing < 0)
807c19800e8SDoug Rabson 	krb5_errx (context, 1, "couldn't parse time: %s", slave_time_missing);
808c19800e8SDoug Rabson 
809*ae771770SStanislav Sedov #ifdef SUPPORT_DETACH
810c19800e8SDoug Rabson     if (detach_from_console)
811c19800e8SDoug Rabson 	daemon(0, 0);
812*ae771770SStanislav Sedov #endif
8134137ff4cSJacques Vidrine     pidfile (NULL);
8145e9cd1aeSAssar Westerlund     krb5_openlog (context, "ipropd-master", &log_facility);
8155e9cd1aeSAssar Westerlund     krb5_set_warn_dest(context, log_facility);
8165e9cd1aeSAssar Westerlund 
8175e9cd1aeSAssar Westerlund     ret = krb5_kt_register(context, &hdb_kt_ops);
8185e9cd1aeSAssar Westerlund     if(ret)
8195e9cd1aeSAssar Westerlund 	krb5_err(context, 1, ret, "krb5_kt_register");
8205e9cd1aeSAssar Westerlund 
8215e9cd1aeSAssar Westerlund     ret = krb5_kt_resolve(context, keytab_str, &keytab);
8225e9cd1aeSAssar Westerlund     if(ret)
8235e9cd1aeSAssar Westerlund 	krb5_err(context, 1, ret, "krb5_kt_resolve: %s", keytab_str);
8245e9cd1aeSAssar Westerlund 
825b528cefcSMark Murray     memset(&conf, 0, sizeof(conf));
826b528cefcSMark Murray     if(realm) {
827b528cefcSMark Murray 	conf.mask |= KADM5_CONFIG_REALM;
828b528cefcSMark Murray 	conf.realm = realm;
829b528cefcSMark Murray     }
8305e9cd1aeSAssar Westerlund     ret = kadm5_init_with_skey_ctx (context,
831b528cefcSMark Murray 				    KADM5_ADMIN_SERVICE,
832b528cefcSMark Murray 				    NULL,
833b528cefcSMark Murray 				    KADM5_ADMIN_SERVICE,
834b528cefcSMark Murray 				    &conf, 0, 0,
835b528cefcSMark Murray 				    &kadm_handle);
836b528cefcSMark Murray     if (ret)
837b528cefcSMark Murray 	krb5_err (context, 1, ret, "kadm5_init_with_password_ctx");
838b528cefcSMark Murray 
839b528cefcSMark Murray     server_context = (kadm5_server_context *)kadm_handle;
840b528cefcSMark Murray 
841b528cefcSMark Murray     log_fd = open (server_context->log_context.log_file, O_RDONLY, 0);
842b528cefcSMark Murray     if (log_fd < 0)
843b528cefcSMark Murray 	krb5_err (context, 1, errno, "open %s",
844b528cefcSMark Murray 		  server_context->log_context.log_file);
845b528cefcSMark Murray 
846b528cefcSMark Murray     signal_fd = make_signal_socket (context);
847c19800e8SDoug Rabson     listen_fd = make_listen_socket (context, port_str);
848b528cefcSMark Murray 
849c19800e8SDoug Rabson     kadm5_log_get_version_fd (log_fd, &current_version);
8505e9cd1aeSAssar Westerlund 
851c19800e8SDoug Rabson     krb5_warnx(context, "ipropd-master started at version: %lu",
852c19800e8SDoug Rabson 	       (unsigned long)current_version);
853c19800e8SDoug Rabson 
854c19800e8SDoug Rabson     while(exit_flag == 0){
855b528cefcSMark Murray 	slave *p;
856b528cefcSMark Murray 	fd_set readset;
857b528cefcSMark Murray 	int max_fd = 0;
858b528cefcSMark Murray 	struct timeval to = {30, 0};
859c19800e8SDoug Rabson 	uint32_t vers;
860b528cefcSMark Murray 
861*ae771770SStanislav Sedov #ifndef NO_LIMIT_FD_SETSIZE
8625e9cd1aeSAssar Westerlund 	if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE)
8635e9cd1aeSAssar Westerlund 	    krb5_errx (context, 1, "fd too large");
864*ae771770SStanislav Sedov #endif
8655e9cd1aeSAssar Westerlund 
866b528cefcSMark Murray 	FD_ZERO(&readset);
867b528cefcSMark Murray 	FD_SET(signal_fd, &readset);
868b528cefcSMark Murray 	max_fd = max(max_fd, signal_fd);
869b528cefcSMark Murray 	FD_SET(listen_fd, &readset);
870b528cefcSMark Murray 	max_fd = max(max_fd, listen_fd);
871b528cefcSMark Murray 
872b528cefcSMark Murray 	for (p = slaves; p != NULL; p = p->next) {
873bbd80c28SJacques Vidrine 	    if (p->flags & SLAVE_F_DEAD)
874bbd80c28SJacques Vidrine 		continue;
875b528cefcSMark Murray 	    FD_SET(p->fd, &readset);
876b528cefcSMark Murray 	    max_fd = max(max_fd, p->fd);
877b528cefcSMark Murray 	}
878b528cefcSMark Murray 
879b528cefcSMark Murray 	ret = select (max_fd + 1,
880b528cefcSMark Murray 		      &readset, NULL, NULL, &to);
881b528cefcSMark Murray 	if (ret < 0) {
882b528cefcSMark Murray 	    if (errno == EINTR)
883b528cefcSMark Murray 		continue;
884b528cefcSMark Murray 	    else
885b528cefcSMark Murray 		krb5_err (context, 1, errno, "select");
886b528cefcSMark Murray 	}
887b528cefcSMark Murray 
888b528cefcSMark Murray 	if (ret == 0) {
889b528cefcSMark Murray 	    old_version = current_version;
8905e9cd1aeSAssar Westerlund 	    kadm5_log_get_version_fd (log_fd, &current_version);
891b528cefcSMark Murray 
892c19800e8SDoug Rabson 	    if (current_version > old_version) {
893c19800e8SDoug Rabson 		krb5_warnx(context,
894c19800e8SDoug Rabson 			   "Missed a signal, updating slaves %lu to %lu",
895c19800e8SDoug Rabson 			   (unsigned long)old_version,
896c19800e8SDoug Rabson 			   (unsigned long)current_version);
897bbd80c28SJacques Vidrine 		for (p = slaves; p != NULL; p = p->next) {
898bbd80c28SJacques Vidrine 		    if (p->flags & SLAVE_F_DEAD)
899bbd80c28SJacques Vidrine 			continue;
9005e9cd1aeSAssar Westerlund 		    send_diffs (context, p, log_fd, database, current_version);
901b528cefcSMark Murray 		}
902bbd80c28SJacques Vidrine 	    }
903c19800e8SDoug Rabson 	}
904b528cefcSMark Murray 
905b528cefcSMark Murray 	if (ret && FD_ISSET(signal_fd, &readset)) {
906*ae771770SStanislav Sedov #ifndef NO_UNIX_SOCKETS
907b528cefcSMark Murray 	    struct sockaddr_un peer_addr;
908*ae771770SStanislav Sedov #else
909*ae771770SStanislav Sedov 	    struct sockaddr_storage peer_addr;
910*ae771770SStanislav Sedov #endif
9115e9cd1aeSAssar Westerlund 	    socklen_t peer_len = sizeof(peer_addr);
912b528cefcSMark Murray 
9134137ff4cSJacques Vidrine 	    if(recvfrom(signal_fd, (void *)&vers, sizeof(vers), 0,
914b528cefcSMark Murray 			(struct sockaddr *)&peer_addr, &peer_len) < 0) {
915b528cefcSMark Murray 		krb5_warn (context, errno, "recvfrom");
916b528cefcSMark Murray 		continue;
917b528cefcSMark Murray 	    }
918b528cefcSMark Murray 	    --ret;
919c19800e8SDoug Rabson 	    assert(ret >= 0);
920b528cefcSMark Murray 	    old_version = current_version;
9215e9cd1aeSAssar Westerlund 	    kadm5_log_get_version_fd (log_fd, &current_version);
922c19800e8SDoug Rabson 	    if (current_version > old_version) {
923c19800e8SDoug Rabson 		krb5_warnx(context,
924c19800e8SDoug Rabson 			   "Got a signal, updating slaves %lu to %lu",
925c19800e8SDoug Rabson 			   (unsigned long)old_version,
926c19800e8SDoug Rabson 			   (unsigned long)current_version);
927*ae771770SStanislav Sedov 		for (p = slaves; p != NULL; p = p->next) {
928*ae771770SStanislav Sedov 		    if (p->flags & SLAVE_F_DEAD)
929*ae771770SStanislav Sedov 			continue;
9305e9cd1aeSAssar Westerlund 		    send_diffs (context, p, log_fd, database, current_version);
931*ae771770SStanislav Sedov 		}
932c19800e8SDoug Rabson 	    } else {
933c19800e8SDoug Rabson 		krb5_warnx(context,
934c19800e8SDoug Rabson 			   "Got a signal, but no update in log version %lu",
935c19800e8SDoug Rabson 			   (unsigned long)current_version);
936c19800e8SDoug Rabson 	    }
937b528cefcSMark Murray         }
938b528cefcSMark Murray 
939c19800e8SDoug Rabson 	for(p = slaves; p != NULL; p = p->next) {
940bbd80c28SJacques Vidrine 	    if (p->flags & SLAVE_F_DEAD)
941bbd80c28SJacques Vidrine 	        continue;
942c19800e8SDoug Rabson 	    if (ret && FD_ISSET(p->fd, &readset)) {
943adb0ddaeSAssar Westerlund 		--ret;
944c19800e8SDoug Rabson 		assert(ret >= 0);
9455e9cd1aeSAssar Westerlund 		if(process_msg (context, p, log_fd, database, current_version))
946c19800e8SDoug Rabson 		    slave_dead(context, p);
947c19800e8SDoug Rabson 	    } else if (slave_gone_p (p))
948c19800e8SDoug Rabson 		slave_dead(context, p);
949*ae771770SStanislav Sedov 	    else if (slave_missing_p (p))
950c19800e8SDoug Rabson 		send_are_you_there (context, p);
951b528cefcSMark Murray 	}
952b528cefcSMark Murray 
953b528cefcSMark Murray 	if (ret && FD_ISSET(listen_fd, &readset)) {
9545e9cd1aeSAssar Westerlund 	    add_slave (context, keytab, &slaves, listen_fd);
955b528cefcSMark Murray 	    --ret;
956c19800e8SDoug Rabson 	    assert(ret >= 0);
957b528cefcSMark Murray 	}
9588373020dSJacques Vidrine 	write_stats(context, slaves, current_version);
959b528cefcSMark Murray     }
960b528cefcSMark Murray 
961*ae771770SStanislav Sedov     if(exit_flag == SIGINT || exit_flag == SIGTERM)
962c19800e8SDoug Rabson 	krb5_warnx(context, "%s terminated", getprogname());
963*ae771770SStanislav Sedov #ifdef SIGXCPU
964*ae771770SStanislav Sedov     else if(exit_flag == SIGXCPU)
965*ae771770SStanislav Sedov 	krb5_warnx(context, "%s CPU time limit exceeded", getprogname());
966*ae771770SStanislav Sedov #endif
967c19800e8SDoug Rabson     else
968*ae771770SStanislav Sedov 	krb5_warnx(context, "%s unexpected exit reason: %ld",
969*ae771770SStanislav Sedov 		   getprogname(), (long)exit_flag);
970c19800e8SDoug Rabson 
971c19800e8SDoug Rabson     write_master_down(context);
972c19800e8SDoug Rabson 
973b528cefcSMark Murray     return 0;
974b528cefcSMark Murray }
975