1/*	$NetBSD: kod_management.c,v 1.9 2020/05/25 20:47:32 christos Exp $	*/
2
3#include <config.h>
4#include <string.h>
5#include <sys/types.h>
6#include <sys/stat.h>
7
8#include "kod_management.h"
9#include "log.h"
10#include "sntp-opts.h"
11#include "ntp_stdlib.h"
12#include "ntp_worker.h"
13#include "ntp_debug.h"
14
15int kod_init = 0, kod_db_cnt = 0;
16const char *kod_db_file;
17struct kod_entry **kod_db;	/* array of pointers to kod_entry */
18
19
20/*
21 * Search for a KOD entry
22 */
23int
24search_entry(
25	const char *hostname,
26	struct kod_entry **dst
27	)
28{
29	register int a, b, resc = 0;
30
31	for (a = 0; a < kod_db_cnt; a++)
32		if (!strcmp(kod_db[a]->hostname, hostname))
33			resc++;
34
35	if (!resc) {
36		*dst = NULL;
37		return 0;
38	}
39
40	*dst = eallocarray(resc, sizeof(**dst));
41
42	b = 0;
43	for (a = 0; a < kod_db_cnt; a++)
44		if (!strcmp(kod_db[a]->hostname, hostname)) {
45			(*dst)[b] = *kod_db[a];
46			b++;
47		}
48
49	return resc;
50}
51
52
53void
54add_entry(
55	const char *	hostname,
56	const char *	type	/* 4 bytes not \0 terminated */
57	)
58{
59	int n;
60	struct kod_entry *pke;
61
62	pke = emalloc_zero(sizeof(*pke));
63	pke->timestamp = time(NULL);
64	memcpy(pke->type, type, 4);
65	pke->type[sizeof(pke->type) - 1] = '\0';
66	strlcpy(pke->hostname, hostname, sizeof(pke->hostname));
67
68	/*
69	 * insert in address ("hostname") order to find duplicates
70	 */
71	for (n = 0; n < kod_db_cnt; n++)
72		if (strcmp(kod_db[n]->hostname, pke->hostname) >= 0)
73			break;
74
75	if (n < kod_db_cnt &&
76	    0 == strcmp(kod_db[n]->hostname, pke->hostname)) {
77		kod_db[n]->timestamp = pke->timestamp;
78		free(pke);
79		return;
80	}
81
82	kod_db_cnt++;
83	kod_db = erealloc(kod_db, kod_db_cnt * sizeof(kod_db[0]));
84	if (n != kod_db_cnt - 1)
85		memmove(&kod_db[n + 1], &kod_db[n],
86			sizeof(kod_db[0]) * ((kod_db_cnt - 1) - n));
87	kod_db[n] = pke;
88}
89
90
91void
92delete_entry(
93	const char *	hostname,
94	const char *	type
95	)
96{
97	int a;
98
99	for (a = 0; a < kod_db_cnt; a++)
100		if (!strcmp(kod_db[a]->hostname, hostname)
101		    && !strcmp(kod_db[a]->type, type))
102			break;
103
104	if (a == kod_db_cnt)
105		return;
106
107	free(kod_db[a]);
108	kod_db_cnt--;
109
110	if (a < kod_db_cnt)
111		memmove(&kod_db[a], &kod_db[a + 1],
112			(kod_db_cnt - a) * sizeof(kod_db[0]));
113}
114
115
116void
117atexit_write_kod_db(void)
118{
119#ifdef WORK_FORK
120	if (worker_process)
121		return;
122#endif
123	write_kod_db();
124}
125
126
127int
128write_kod_db(void)
129{
130	FILE *db_s;
131	char *pch;
132	int dirmode;
133	register int a;
134
135	db_s = fopen(kod_db_file, "w");
136
137	/*
138	 * If opening fails, blindly attempt to create each directory
139	 * in the path first, then retry the open.
140	 */
141	if (NULL == db_s && strlen(kod_db_file)) {
142		dirmode = S_IRUSR | S_IWUSR | S_IXUSR
143			| S_IRGRP | S_IXGRP
144			| S_IROTH | S_IXOTH;
145		pch = strchr(kod_db_file + 1, DIR_SEP);
146		while (NULL != pch) {
147			*pch = '\0';
148			if (-1 == mkdir(kod_db_file, dirmode)
149			    && errno != EEXIST) {
150				msyslog(LOG_ERR, "mkdir(%s) failed: %m",
151					kod_db_file);
152				return FALSE;
153			}
154			*pch = DIR_SEP;
155			pch = strchr(pch + 1, DIR_SEP);
156		}
157		db_s = fopen(kod_db_file, "w");
158	}
159
160	if (NULL == db_s) {
161		msyslog(LOG_WARNING, "Can't open KOD db file %s for writing: %m",
162			kod_db_file);
163
164		return FALSE;
165	}
166
167	for (a = 0; a < kod_db_cnt; a++) {
168		fprintf(db_s, "%16.16llx %s %s\n", (unsigned long long)
169			kod_db[a]->timestamp, kod_db[a]->type,
170			kod_db[a]->hostname);
171	}
172
173	fflush(db_s);
174	fclose(db_s);
175
176	return TRUE;
177}
178
179
180void
181kod_init_kod_db(
182	const char *	db_file,
183	int		readonly
184	)
185{
186	/*
187	 * Max. of 254 characters for hostname, 10 for timestamp, 4 for
188	 * kisscode, 2 for spaces, 1 for \n, and 1 for \0
189	 */
190	char fbuf[254+10+4+2+1+1];
191	FILE *db_s;
192	int a, b, sepc, len;
193	unsigned long long ull;
194	char *str_ptr;
195	char error = 0;
196
197	TRACE(2, ("Initializing KOD DB...\n"));
198
199	kod_db_file = estrdup(db_file);
200
201	db_s = fopen(db_file, "r");
202
203	if (NULL == db_s) {
204		msyslog(LOG_WARNING, "kod_init_kod_db(): Cannot open KoD db file %s: %m",
205			db_file);
206
207		return;
208	}
209
210	if (debug)
211		printf("Starting to read KoD file %s...\n", db_file);
212	/* First let's see how many entries there are and check for right syntax */
213
214	while (!feof(db_s) && NULL != fgets(fbuf, sizeof(fbuf), db_s)) {
215
216		/* ignore blank lines */
217		if ('\n' == fbuf[0])
218			continue;
219
220		sepc = 0;
221		len = strlen(fbuf);
222		for (a = 0; a < len; a++) {
223			if (' ' == fbuf[a])
224				sepc++;
225
226			if ('\n' == fbuf[a]) {
227				if (sepc != 2) {
228					if (strcmp(db_file, "/dev/null"))
229						msyslog(LOG_DEBUG,
230							"Syntax error in KoD db file %s in line %i (missing space)",
231							db_file,
232							kod_db_cnt + 1);
233					fclose(db_s);
234					return;
235				}
236				sepc = 0;
237				kod_db_cnt++;
238			}
239		}
240	}
241
242	if (0 == kod_db_cnt) {
243		TRACE(2, ("KoD DB %s empty.\n", db_file));
244		goto wrapup;
245	}
246
247	TRACE(2, ("KoD DB %s contains %d entries, reading...\n", db_file, kod_db_cnt));
248
249	rewind(db_s);
250
251	/* Allocate the array of pointers to the struct kod_entry items */
252	kod_db = eallocarray(kod_db_cnt, sizeof(kod_db[0]));
253
254	/* Read contents of file */
255	for (b = 0;
256	     !feof(db_s) && !ferror(db_s) && b < kod_db_cnt;
257	     b++) {
258
259		str_ptr = fgets(fbuf, sizeof(fbuf), db_s);
260		if (NULL == str_ptr) {
261			error = 1;
262			break;
263		}
264
265		/* ignore blank lines */
266		if ('\n' == fbuf[0]) {
267			b--;
268			continue;
269		}
270
271		/* Allocate this struct kod_entry item */
272		kod_db[b] = emalloc(sizeof(*kod_db[0]));
273
274		if (3 != sscanf(fbuf, "%llx %4s %254s", &ull,
275		    kod_db[b]->type, kod_db[b]->hostname)) {
276
277			free(kod_db[b]);
278			kod_db[b] = NULL;
279			error = 1;
280			break;
281		}
282
283		kod_db[b]->timestamp = (time_t)ull;
284	}
285
286	if (ferror(db_s) || error) {
287		kod_db_cnt = b;
288		msyslog(LOG_WARNING, "An error occured while parsing the KoD db file %s",
289			db_file);
290		fclose(db_s);
291
292		return;
293	}
294
295    wrapup:
296	fclose(db_s);
297	for (a = 0; a < kod_db_cnt; a++)
298		TRACE(2, ("KoD entry %d: %s at %llx type %s\n", a,
299			  kod_db[a]->hostname,
300			  (unsigned long long)kod_db[a]->timestamp,
301			  kod_db[a]->type));
302
303	if (!readonly && write_kod_db())
304		atexit(&atexit_write_kod_db);
305}
306