master uint/parados / server / config.c
  1#include <limits.h>
  2#include <stdio.h>
  3#include <stdlib.h>
  4#include <string.h>
  5#include <strings.h>
  6
  7#include "config.h"
  8#include "log.h"
  9#include "users.h"
 10
 11static int load_path(const char* path);
 12static int parse_bool(const char* s, bool* out);
 13static int set_kv(const char* k, const char* v);
 14static char* trim(char* s);
 15
 16/* defaults */
 17char media_dir[4096] = "/mnt/sto/Media/";
 18char server_addr[64] = "127.0.0.1";
 19int  server_port = 8088;
 20bool verbose_log = true;
 21char cors_origin[1024] = "";
 22int http_io_timeout = 5;
 23int max_clients = 64;
 24int auth_delay = 250;
 25
 26/**
 27 * @brief Load configuration from a file path
 28 *
 29 * @param path Path to config file
 30 *
 31 * @return 0=Success, -1=Failure
 32 */
 33static int load_path(const char* path)
 34{
 35	FILE* f = fopen(path, "r");
 36	if (!f)
 37		return -1;
 38
 39	char* line = NULL;
 40	size_t cap = 0;
 41	ssize_t n;
 42
 43	while ((n = getline(&line, &cap, f)) >= 0) {
 44		(void)n;
 45
 46		char* s = trim(line);
 47		if (s[0] == '\0')
 48			continue;
 49		if (s[0] == '#')
 50			continue;
 51
 52		char* eq = strchr(s, '=');
 53		if (!eq)
 54			continue;
 55
 56		*eq = '\0';
 57		char* k = trim(s);
 58		char* v = trim(eq + 1);
 59
 60		if (k[0] == '\0')
 61			continue;
 62
 63		if (set_kv(k, v) < 0)
 64			LOG(true, "CONF", "Config Error       %s=%s", k, v);
 65	}
 66
 67	free(line);
 68	fclose(f);
 69	return 0;
 70}
 71
 72/**
 73 * @brief Parse bool string value
 74 *
 75 * @param s Input string
 76 * @param out Output bool
 77 *
 78 * @return 0=Success, -1=Failure
 79 */
 80static int parse_bool(const char* s, bool* out)
 81{
 82	if (!s || !out)
 83		return -1;
 84
 85	if (strcmp(s, "1") == 0 || strcasecmp(s, "true") == 0 || strcasecmp(s, "yes") == 0)
 86		*out = true;
 87	else if (strcmp(s, "0") == 0 || strcasecmp(s, "false") == 0 || strcasecmp(s, "no") == 0)
 88		*out = false;
 89	else
 90		return -1;
 91
 92	return 0;
 93}
 94
 95/**
 96 * @brief Apply a config key/value pair
 97 *
 98 * @param k Config key
 99 * @param v Config value
100 *
101 * @return 0=Success, -1=Failure
102 */
103static int set_kv(const char* k, const char* v)
104{
105	/* regular */
106	if (strcmp(k, "media_dir") == 0) {
107		snprintf(media_dir, sizeof(media_dir), "%s", v);
108		return 0;
109	}
110
111	if (strcmp(k, "server_addr") == 0) {
112		snprintf(server_addr, sizeof(server_addr), "%s", v);
113		return 0;
114	}
115
116	if (strcmp(k, "server_port") == 0) {
117		char* end = NULL;
118		long p = strtol(v, &end, 10);
119
120		if (end == v || *end != '\0') {
121			LOG(true, "CONF", "Config Error       %s invalid integer '%s'", k, v);
122			return -1;
123		}
124		if (p < 1 || p > 65535) {
125			LOG(true, "CONF", "Config Error       server_port out of bounds (1..65535)");
126			return -1;
127		}
128
129		server_port = (int)p;
130		return 0;
131	}
132
133	if (strcmp(k, "verbose_log") == 0) {
134		bool b;
135		if (parse_bool(v, &b) < 0){
136			LOG(true, "CONF", "Config Error       verbose_log invalid bool \"%s\"", v);
137			return -1;
138		}
139
140		verbose_log = b;
141		return 0;
142	}
143
144	if (strcmp(k, "cors_origin") == 0) {
145		snprintf(cors_origin, sizeof(cors_origin), "%s", v);
146		return 0;
147	}
148
149	if (strcmp(k, "http_io_timeout") == 0) {
150		char* end = NULL;
151		long t = strtol(v, &end, 10);
152
153		if (end == v || *end != '\0') {
154			LOG(true, "CONF", "Config Error       %s invalid integer '%s'", k, v);
155			return -1;
156		}
157		if (t < 1 || t > 300) {
158			LOG(true, "CONF", "Config Error       http_io_timeout too %s (1..300)", t > 300 ? "high" : "low");
159			return -1;
160		}
161
162		http_io_timeout = (int)t;
163		return 0;
164	}
165
166	if (strcmp(k, "max_clients") == 0) {
167		char* end = NULL;
168		long n = strtol(v, &end, 10);
169
170		if (end == v || *end != '\0') {
171			LOG(true, "CONF", "Config Error       %s invalid integer '%s'", k, v);
172			return -1;
173		}
174		if (n < 1 || n > 1024) {
175			LOG(true, "CONF", "Config Error       max_clients too %s (1..1024)", n > 1024 ? "high" : "low");
176			return -1;
177		}
178
179		max_clients = (int)n;
180		return 0;
181	}
182
183	if (strcmp(k, "auth_delay") == 0) {
184		char* end = NULL;
185		long n = strtol(v, &end, 10);
186
187		if (end == v || *end != '\0') {
188			LOG(true, "CONF", "Config Error       %s invalid integer '%s'", k, v);
189			return -1;
190		}
191		if (n < 0 || n > INT_MAX) {
192			LOG(true, "CONF", "Config Error       auth_delay out of bounds (0..%d)", INT_MAX);
193			return -1;
194		}
195
196		auth_delay = (int)n;
197		return 0;
198	}
199
200	/* auth */
201	if (strcmp(k, "user") == 0) {
202		int r = users_push(v);
203		if (r < 0)
204			LOG(true, "CONF", "Config Error       user invalid '%s'", v);
205		return r;
206	}
207
208	if (strcmp(k, "pass") == 0) {
209		int r = users_set_pass(v);
210		if (r < 0)
211			LOG(true, "CONF", "Config Error       pass invalid '%s'", v);
212		return r;
213	}
214
215	if (strcmp(k, "allow") == 0) {
216		int r = users_add_allow(v);
217		if (r < 0)
218			LOG(true, "CONF", "Config Error       allow invalid '%s'", v);
219		return r;
220	}
221
222	/* unknown key */
223	LOG(true, "CONF", "Unknown Key Value  %s=%s", k, v);
224	return 0;
225}
226
227/**
228 * @brief Trim leading and trailing whitespace
229 *
230 * @param s Input string
231 *
232 * @return Ptr to trimmed string
233 */
234static char* trim(char* s)
235{
236	while (*s && (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n'))
237		s++;
238
239	char* e = s + strlen(s);
240	while (e > s) {
241		char c = e[-1];
242		if (c != ' ' && c != '\t' && c != '\r' && c != '\n')
243			break;
244		e--;
245	}
246	*e = '\0';
247	return s;
248}
249
250void config_load(void)
251{
252	const char* e = getenv("PARADOS_CONFIG");
253	const char* path = "";
254	if (e && e[0]) {
255		(void)load_path(e);
256		path = e;
257		goto log;
258	}
259
260	if (load_path("/etc/parados.conf") == 0) {
261		path = "/etc/parados.conf";
262		goto log;
263	}
264
265	if (load_path("./parados.conf") == 0) {
266		path = "./parados.conf";
267		goto log;
268	}
269
270	LOG(true, "CONF", "Unable to load configuration... using defaults");
271	return;
272
273log:
274	(void)0;
275
276	char port[8];
277	char tmo[16];
278	char maxc[16];
279	char adly[16];
280
281	LOG(true, "CONF", "Config File        %s", path);
282	LOG(true, "CONF", "Media Directory    %s", media_dir);
283	LOG(true, "CONF", "Server Address     %s", server_addr);
284	snprintf(port, sizeof(port), "%d", server_port);
285	LOG(true, "CONF", "Server Port        %s", port);
286	LOG(true, "CONF", "Verbose Logging    %s", (verbose_log) ? "true" : "false");
287	LOG(true, "CONF", "CORS origins       %s", cors_origin);
288	snprintf(tmo, sizeof(tmo), "%d", http_io_timeout);
289	LOG(true, "CONF", "HTTP IO Timeout    %s", tmo);
290	snprintf(maxc, sizeof(maxc), "%d", max_clients);
291	LOG(true, "CONF", "Max Clients        %s", maxc);
292	snprintf(adly, sizeof(adly), "%d", auth_delay);
293	LOG(true, "CONF", "Auth Delay         %s (ms)", adly);
294
295	return;
296}
297