1290001Sglebius#include <config.h>
2290001Sglebius#include <string.h>
3290001Sglebius#include <sys/types.h>
4290001Sglebius#include <sys/stat.h>
5290001Sglebius
6290001Sglebius#include "kod_management.h"
7290001Sglebius#include "log.h"
8290001Sglebius#include "sntp-opts.h"
9290001Sglebius#include "ntp_stdlib.h"
10290001Sglebius#include "ntp_worker.h"
11290001Sglebius#include "ntp_debug.h"
12290001Sglebius
13290001Sglebiusint kod_init = 0, kod_db_cnt = 0;
14290001Sglebiusconst char *kod_db_file;
15290001Sglebiusstruct kod_entry **kod_db;	/* array of pointers to kod_entry */
16290001Sglebius
17290001Sglebius
18290001Sglebius/*
19290001Sglebius * Search for a KOD entry
20290001Sglebius */
21290001Sglebiusint
22290001Sglebiussearch_entry(
23290001Sglebius	const char *hostname,
24290001Sglebius	struct kod_entry **dst
25290001Sglebius	)
26290001Sglebius{
27290001Sglebius	register int a, b, resc = 0;
28290001Sglebius
29290001Sglebius	for (a = 0; a < kod_db_cnt; a++)
30290001Sglebius		if (!strcmp(kod_db[a]->hostname, hostname))
31290001Sglebius			resc++;
32290001Sglebius
33290001Sglebius	if (!resc) {
34290001Sglebius		*dst = NULL;
35290001Sglebius		return 0;
36290001Sglebius	}
37290001Sglebius
38290001Sglebius	*dst = eallocarray(resc, sizeof(**dst));
39290001Sglebius
40290001Sglebius	b = 0;
41290001Sglebius	for (a = 0; a < kod_db_cnt; a++)
42290001Sglebius		if (!strcmp(kod_db[a]->hostname, hostname)) {
43290001Sglebius			(*dst)[b] = *kod_db[a];
44290001Sglebius			b++;
45290001Sglebius		}
46290001Sglebius
47290001Sglebius	return resc;
48290001Sglebius}
49290001Sglebius
50290001Sglebius
51290001Sglebiusvoid
52290001Sglebiusadd_entry(
53290001Sglebius	const char *	hostname,
54290001Sglebius	const char *	type	/* 4 bytes not \0 terminated */
55290001Sglebius	)
56290001Sglebius{
57290001Sglebius	int n;
58290001Sglebius	struct kod_entry *pke;
59290001Sglebius
60290001Sglebius	pke = emalloc_zero(sizeof(*pke));
61290001Sglebius	pke->timestamp = time(NULL);
62290001Sglebius	memcpy(pke->type, type, 4);
63290001Sglebius	pke->type[sizeof(pke->type) - 1] = '\0';
64290001Sglebius	strlcpy(pke->hostname, hostname, sizeof(pke->hostname));
65290001Sglebius
66290001Sglebius	/*
67290001Sglebius	 * insert in address ("hostname") order to find duplicates
68290001Sglebius	 */
69290001Sglebius	for (n = 0; n < kod_db_cnt; n++)
70290001Sglebius		if (strcmp(kod_db[n]->hostname, pke->hostname) >= 0)
71290001Sglebius			break;
72290001Sglebius
73290001Sglebius	if (n < kod_db_cnt &&
74290001Sglebius	    0 == strcmp(kod_db[n]->hostname, pke->hostname)) {
75290001Sglebius		kod_db[n]->timestamp = pke->timestamp;
76290001Sglebius		free(pke);
77290001Sglebius		return;
78290001Sglebius	}
79290001Sglebius
80290001Sglebius	kod_db_cnt++;
81290001Sglebius	kod_db = erealloc(kod_db, kod_db_cnt * sizeof(kod_db[0]));
82290001Sglebius	if (n != kod_db_cnt - 1)
83290001Sglebius		memmove(&kod_db[n + 1], &kod_db[n],
84290001Sglebius			sizeof(kod_db[0]) * ((kod_db_cnt - 1) - n));
85290001Sglebius	kod_db[n] = pke;
86290001Sglebius}
87290001Sglebius
88290001Sglebius
89290001Sglebiusvoid
90290001Sglebiusdelete_entry(
91290001Sglebius	const char *	hostname,
92290001Sglebius	const char *	type
93290001Sglebius	)
94290001Sglebius{
95290001Sglebius	int a;
96290001Sglebius
97290001Sglebius	for (a = 0; a < kod_db_cnt; a++)
98290001Sglebius		if (!strcmp(kod_db[a]->hostname, hostname)
99290001Sglebius		    && !strcmp(kod_db[a]->type, type))
100290001Sglebius			break;
101290001Sglebius
102290001Sglebius	if (a == kod_db_cnt)
103290001Sglebius		return;
104290001Sglebius
105290001Sglebius	free(kod_db[a]);
106290001Sglebius	kod_db_cnt--;
107290001Sglebius
108290001Sglebius	if (a < kod_db_cnt)
109290001Sglebius		memmove(&kod_db[a], &kod_db[a + 1],
110290001Sglebius			(kod_db_cnt - a) * sizeof(kod_db[0]));
111290001Sglebius}
112290001Sglebius
113290001Sglebius
114290001Sglebiusvoid
115290001Sglebiusatexit_write_kod_db(void)
116290001Sglebius{
117290001Sglebius#ifdef WORK_FORK
118290001Sglebius	if (worker_process)
119290001Sglebius		return;
120290001Sglebius#endif
121290001Sglebius	write_kod_db();
122290001Sglebius}
123290001Sglebius
124290001Sglebius
125290001Sglebiusint
126290001Sglebiuswrite_kod_db(void)
127290001Sglebius{
128290001Sglebius	FILE *db_s;
129290001Sglebius	char *pch;
130290001Sglebius	int dirmode;
131290001Sglebius	register int a;
132290001Sglebius
133290001Sglebius	db_s = fopen(kod_db_file, "w");
134290001Sglebius
135290001Sglebius	/*
136290001Sglebius	 * If opening fails, blindly attempt to create each directory
137290001Sglebius	 * in the path first, then retry the open.
138290001Sglebius	 */
139290001Sglebius	if (NULL == db_s && strlen(kod_db_file)) {
140290001Sglebius		dirmode = S_IRUSR | S_IWUSR | S_IXUSR
141290001Sglebius			| S_IRGRP | S_IXGRP
142290001Sglebius			| S_IROTH | S_IXOTH;
143290001Sglebius		pch = strchr(kod_db_file + 1, DIR_SEP);
144290001Sglebius		while (NULL != pch) {
145290001Sglebius			*pch = '\0';
146290001Sglebius			if (-1 == mkdir(kod_db_file, dirmode)
147290001Sglebius			    && errno != EEXIST) {
148290001Sglebius				msyslog(LOG_ERR, "mkdir(%s) failed: %m",
149290001Sglebius					kod_db_file);
150290001Sglebius				return FALSE;
151290001Sglebius			}
152290001Sglebius			*pch = DIR_SEP;
153290001Sglebius			pch = strchr(pch + 1, DIR_SEP);
154290001Sglebius		}
155290001Sglebius		db_s = fopen(kod_db_file, "w");
156290001Sglebius	}
157290001Sglebius
158290001Sglebius	if (NULL == db_s) {
159290001Sglebius		msyslog(LOG_WARNING, "Can't open KOD db file %s for writing: %m",
160290001Sglebius			kod_db_file);
161290001Sglebius
162290001Sglebius		return FALSE;
163290001Sglebius	}
164290001Sglebius
165290001Sglebius	for (a = 0; a < kod_db_cnt; a++) {
166290001Sglebius		fprintf(db_s, "%16.16llx %s %s\n", (unsigned long long)
167290001Sglebius			kod_db[a]->timestamp, kod_db[a]->type,
168290001Sglebius			kod_db[a]->hostname);
169290001Sglebius	}
170290001Sglebius
171290001Sglebius	fflush(db_s);
172290001Sglebius	fclose(db_s);
173290001Sglebius
174290001Sglebius	return TRUE;
175290001Sglebius}
176290001Sglebius
177290001Sglebius
178290001Sglebiusvoid
179290001Sglebiuskod_init_kod_db(
180290001Sglebius	const char *	db_file,
181290001Sglebius	int		readonly
182290001Sglebius	)
183290001Sglebius{
184290001Sglebius	/*
185290001Sglebius	 * Max. of 254 characters for hostname, 10 for timestamp, 4 for
186290001Sglebius	 * kisscode, 2 for spaces, 1 for \n, and 1 for \0
187290001Sglebius	 */
188290001Sglebius	char fbuf[254+10+4+2+1+1];
189290001Sglebius	FILE *db_s;
190290001Sglebius	int a, b, sepc, len;
191290001Sglebius	unsigned long long ull;
192290001Sglebius	char *str_ptr;
193290001Sglebius	char error = 0;
194290001Sglebius
195290001Sglebius	TRACE(2, ("Initializing KOD DB...\n"));
196290001Sglebius
197290001Sglebius	kod_db_file = estrdup(db_file);
198290001Sglebius
199290001Sglebius	db_s = fopen(db_file, "r");
200290001Sglebius
201290001Sglebius	if (NULL == db_s) {
202290001Sglebius		msyslog(LOG_WARNING, "kod_init_kod_db(): Cannot open KoD db file %s: %m",
203290001Sglebius			db_file);
204290001Sglebius
205290001Sglebius		return;
206290001Sglebius	}
207290001Sglebius
208290001Sglebius	if (debug)
209290001Sglebius		printf("Starting to read KoD file %s...\n", db_file);
210290001Sglebius	/* First let's see how many entries there are and check for right syntax */
211290001Sglebius
212290001Sglebius	while (!feof(db_s) && NULL != fgets(fbuf, sizeof(fbuf), db_s)) {
213290001Sglebius
214290001Sglebius		/* ignore blank lines */
215290001Sglebius		if ('\n' == fbuf[0])
216290001Sglebius			continue;
217290001Sglebius
218290001Sglebius		sepc = 0;
219290001Sglebius		len = strlen(fbuf);
220290001Sglebius		for (a = 0; a < len; a++) {
221290001Sglebius			if (' ' == fbuf[a])
222290001Sglebius				sepc++;
223290001Sglebius
224290001Sglebius			if ('\n' == fbuf[a]) {
225290001Sglebius				if (sepc != 2) {
226290001Sglebius					if (strcmp(db_file, "/dev/null"))
227290001Sglebius						msyslog(LOG_DEBUG,
228290001Sglebius							"Syntax error in KoD db file %s in line %i (missing space)",
229290001Sglebius							db_file,
230290001Sglebius							kod_db_cnt + 1);
231290001Sglebius					fclose(db_s);
232290001Sglebius					return;
233290001Sglebius				}
234290001Sglebius				sepc = 0;
235290001Sglebius				kod_db_cnt++;
236290001Sglebius			}
237290001Sglebius		}
238290001Sglebius	}
239290001Sglebius
240290001Sglebius	if (0 == kod_db_cnt) {
241290001Sglebius		TRACE(2, ("KoD DB %s empty.\n", db_file));
242290001Sglebius		goto wrapup;
243290001Sglebius	}
244290001Sglebius
245290001Sglebius	TRACE(2, ("KoD DB %s contains %d entries, reading...\n", db_file, kod_db_cnt));
246290001Sglebius
247290001Sglebius	rewind(db_s);
248290001Sglebius
249290001Sglebius	kod_db = eallocarray(kod_db_cnt, sizeof(kod_db[0]));
250290001Sglebius
251290001Sglebius	/* Read contents of file */
252290001Sglebius	for (b = 0;
253290001Sglebius	     !feof(db_s) && !ferror(db_s) && b < kod_db_cnt;
254290001Sglebius	     b++) {
255290001Sglebius
256290001Sglebius		str_ptr = fgets(fbuf, sizeof(fbuf), db_s);
257290001Sglebius		if (NULL == str_ptr) {
258290001Sglebius			error = 1;
259290001Sglebius			break;
260290001Sglebius		}
261290001Sglebius
262290001Sglebius		/* ignore blank lines */
263290001Sglebius		if ('\n' == fbuf[0]) {
264290001Sglebius			b--;
265290001Sglebius			continue;
266290001Sglebius		}
267290001Sglebius
268290001Sglebius		kod_db[b] = emalloc(sizeof(*kod_db[b]));
269290001Sglebius
270290001Sglebius		if (3 != sscanf(fbuf, "%llx %4s %254s", &ull,
271290001Sglebius		    kod_db[b]->type, kod_db[b]->hostname)) {
272290001Sglebius
273290001Sglebius			free(kod_db[b]);
274290001Sglebius			kod_db[b] = NULL;
275290001Sglebius			error = 1;
276290001Sglebius			break;
277290001Sglebius		}
278290001Sglebius
279290001Sglebius		kod_db[b]->timestamp = (time_t)ull;
280290001Sglebius	}
281290001Sglebius
282290001Sglebius	if (ferror(db_s) || error) {
283290001Sglebius		kod_db_cnt = b;
284290001Sglebius		msyslog(LOG_WARNING, "An error occured while parsing the KoD db file %s",
285290001Sglebius			db_file);
286290001Sglebius		fclose(db_s);
287290001Sglebius
288290001Sglebius		return;
289290001Sglebius	}
290290001Sglebius
291290001Sglebius    wrapup:
292290001Sglebius	fclose(db_s);
293290001Sglebius	for (a = 0; a < kod_db_cnt; a++)
294290001Sglebius		TRACE(2, ("KoD entry %d: %s at %llx type %s\n", a,
295290001Sglebius			  kod_db[a]->hostname,
296290001Sglebius			  (unsigned long long)kod_db[a]->timestamp,
297290001Sglebius			  kod_db[a]->type));
298290001Sglebius
299290001Sglebius	if (!readonly && write_kod_db())
300290001Sglebius		atexit(&atexit_write_kod_db);
301290001Sglebius}
302