Fix content type header for error pages
[MonkeyD.git] / src / config.c
blob406e5dcdc978588f8dd1974dfae9e31721616723
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /* Monkey HTTP Daemon
4 * ------------------
5 * Copyright (C) 2001-2010, Eduardo Silva P. <edsiper@gmail.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Library General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #include <dirent.h>
23 #include <stdio.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <stdlib.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <fcntl.h>
33 #include "monkey.h"
34 #include "config.h"
35 #include "str.h"
36 #include "utils.h"
37 #include "mimetype.h"
38 #include "info.h"
39 #include "logfile.h"
40 #include "memory.h"
41 #include "plugin.h"
43 struct mk_config *mk_config_create(char *path)
45 FILE *f;
46 int len;
47 char buf[255];
48 char *key = 0, *val = 0, *last = 0;
49 struct mk_config *cnf = 0, *new, *p;
51 if ((f = fopen(path, "r")) == NULL) {
52 fprintf(stderr, "\nConfig Error: I can't open %s file\n\n", path);
53 exit(1);
56 /* looking for configuration directives */
57 while (fgets(buf, 255, f)) {
58 len = strlen(buf);
59 if (buf[len - 1] == '\n') {
60 buf[--len] = 0;
61 if (len && buf[len - 1] == '\r')
62 buf[--len] = 0;
65 if (!buf[0] || buf[0] == '#')
66 continue;
68 key = strtok_r(buf, "\"\t ", &last);
69 val = strtok_r(NULL, "\"\t ", &last);
71 if (!key || !val) {
72 continue;
75 /* Allow new entry found */
76 new = mk_mem_malloc(sizeof(struct mk_config));
77 new->key = mk_string_dup(key);
79 new->val = mk_string_dup(val);
80 new->next = NULL;
82 /* Link to main list */
83 if (!cnf) {
84 cnf = new;
86 else {
87 p = cnf;
88 while (p->next) {
89 p = p->next;
91 p->next = new;
95 fclose(f);
96 return cnf;
99 void mk_config_free(struct mk_config *cnf)
101 struct mk_config *prev = 0, *target;
103 target = cnf;
104 while (target) {
105 while (target->next) {
106 prev = target;
107 target = target->next;
110 mk_mem_free(target);
112 if (target == cnf) {
113 return;
115 prev->next = NULL;
116 target = cnf;
120 void *mk_config_getval(struct mk_config *cnf, char *key, int mode)
122 int on, off;
123 struct mk_config *p;
125 p = cnf;
126 while (p) {
127 if (strcasecmp(p->key, key) == 0) {
128 switch (mode) {
129 case MK_CONFIG_VAL_STR:
130 return (void *) p->val;
131 case MK_CONFIG_VAL_NUM:
132 return (void *) atoi(p->val);
133 case MK_CONFIG_VAL_BOOL:
134 on = strcasecmp(p->val, VALUE_ON);
135 off = strcasecmp(p->val, VALUE_OFF);
137 if (on != 0 && off != 0) {
138 return (void *) -1;
140 else if (on >= 0) {
141 return (void *) VAR_ON;
143 else {
144 return (void *) VAR_OFF;
146 case MK_CONFIG_VAL_LIST:
147 return mk_string_split_line(p->val);
150 else {
151 p = p->next;
154 return NULL;
157 /* Read configuration files */
158 void mk_config_read_files(char *path_conf, char *file_conf)
160 unsigned long len;
161 char *path = 0;
162 struct stat checkdir;
163 struct mk_config *cnf;
164 struct mk_string_line *line, *line_val;
166 config->serverconf = mk_string_dup(path_conf);
167 config->workers = MK_WORKERS_DEFAULT;
169 if (stat(config->serverconf, &checkdir) == -1) {
170 fprintf(stderr, "ERROR: Invalid path to configuration files.");
171 exit(1);
174 m_build_buffer(&path, &len, "%s/%s", path_conf, file_conf);
176 cnf = mk_config_create(path);
178 /* Listen */
179 config->listen_addr = mk_config_getval(cnf, "Listen", MK_CONFIG_VAL_STR);
180 if (!config->listen_addr) {
181 config->listen_addr = MK_DEFAULT_LISTEN_ADDR;
184 /* Connection port */
185 config->serverport = (int) mk_config_getval(cnf,
186 "Port", MK_CONFIG_VAL_NUM);
187 if (!config->serverport >= 1 && !config->serverport <= 65535) {
188 mk_config_print_error_msg("Port", path);
191 /* Number of thread workers */
192 config->workers = (int) mk_config_getval(cnf,
193 "Workers", MK_CONFIG_VAL_NUM);
194 if (config->maxclients < 1) {
195 mk_config_print_error_msg("Workers", path);
198 /* Timeout */
199 config->timeout = (int) mk_config_getval(cnf,
200 "Timeout", MK_CONFIG_VAL_NUM);
201 if (config->timeout < 1) {
202 mk_config_print_error_msg("Timeout", path);
205 /* KeepAlive */
206 config->keep_alive = (int) mk_config_getval(cnf,
207 "KeepAlive",
208 MK_CONFIG_VAL_BOOL);
209 if (config->keep_alive == VAR_ERR) {
210 mk_config_print_error_msg("KeepAlive", path);
213 /* MaxKeepAliveRequest */
214 config->max_keep_alive_request = (int) mk_config_getval(cnf,
215 "MaxKeepAliveRequest",
216 MK_CONFIG_VAL_NUM);
217 if (config->max_keep_alive_request == 0) {
218 mk_config_print_error_msg("MaxKeepAliveRequest", path);
221 /* KeepAliveTimeout */
222 config->keep_alive_timeout = (int) mk_config_getval(cnf,
223 "KeepAliveTimeout",
224 MK_CONFIG_VAL_NUM);
225 if (config->keep_alive_timeout == 0) {
226 mk_config_print_error_msg("KeepAliveTimeout", path);
229 /* Pid File */
230 config->pid_file_path = mk_config_getval(cnf,
231 "PidFile", MK_CONFIG_VAL_STR);
233 /* Home user's directory /~ */
234 config->user_dir = mk_config_getval(cnf, "UserDir", MK_CONFIG_VAL_STR);
236 /* Index files */
237 line_val = line = mk_config_getval(cnf, "Indexfile", MK_CONFIG_VAL_LIST);
238 while (line_val != NULL) {
239 mk_config_add_index(line_val->val);
240 line_val = line_val->next;
243 /* HideVersion Variable */
244 config->hideversion = (int) mk_config_getval(cnf,
245 "HideVersion",
246 MK_CONFIG_VAL_BOOL);
247 if (config->hideversion == VAR_ERR) {
248 mk_config_print_error_msg("HideVersion", path);
251 /* User Variable */
252 config->user = mk_config_getval(cnf, "User", MK_CONFIG_VAL_STR);
254 /* Resume */
255 config->resume = (int) mk_config_getval(cnf,
256 "Resume", MK_CONFIG_VAL_BOOL);
257 if (config->resume == VAR_ERR) {
258 mk_config_print_error_msg("Resume", path);
261 /* Symbolic Links */
262 config->symlink = (int) mk_config_getval(cnf,
263 "SymLink", MK_CONFIG_VAL_BOOL);
264 if (config->symlink == VAR_ERR) {
265 mk_config_print_error_msg("SymLink", path);
268 mk_mem_free(path);
269 mk_config_free(cnf);
270 mk_config_read_hosts(path_conf);
273 void mk_config_read_hosts(char *path)
275 DIR *dir;
276 unsigned long len;
277 char *buf = 0;
278 char *file;
279 struct host *p_host, *new_host; /* debug */
280 struct dirent *ent;
282 m_build_buffer(&buf, &len, "%s/sites/default", path);
283 config->hosts = mk_config_get_host(buf);
284 config->nhosts++;
285 mk_mem_free(buf);
287 if (!config->hosts) {
288 printf("\nError parsing main configuration file 'default'\n");
289 exit(1);
292 m_build_buffer(&buf, &len, "%s/sites/", path);
293 if (!(dir = opendir(buf)))
294 exit(1);
297 p_host = config->hosts;
299 /* Reading content */
300 while ((ent = readdir(dir)) != NULL) {
301 if (strcmp((char *) ent->d_name, ".") == 0)
302 continue;
303 if (strcmp((char *) ent->d_name, "..") == 0)
304 continue;
305 if (strcasecmp((char *) ent->d_name, "default") == 0)
306 continue;
308 m_build_buffer(&file, &len, "%s/sites/%s", path, ent->d_name);
310 new_host = (struct host *) mk_config_get_host(file);
311 mk_mem_free(file);
312 if (!new_host) {
313 continue;
315 else {
316 p_host->next = new_host;
317 p_host = new_host;
318 config->nhosts++;
321 closedir(dir);
323 h = config->hosts;
324 while(h)
326 printf("*** HOST ***\n");
327 printf(" [servername]\t\t%s\n", h->servername);
328 printf(" [documentroot]\t\t%s\n", h->documentroot);
329 printf(" [conf file]\t\t%s\n", h->file);
330 printf(" [access log]\t\t%s\n", h->access_log_path);
331 printf(" [error log]\t\t%s\n", h->error_log_path);
332 printf(" [script alias]\t\t%s %s\n", h->scriptalias[0], h->scriptalias[1]);
333 printf(" [get dir]\t\t%i\n", h->getdir);
334 printf(" [header file]\t\t%s\n", h->header_file);
335 printf(" [footer file]\t\t%s\n\n", h->footer_file);
337 h = h->next;
339 fflush(stdout);
343 struct host *mk_config_get_host(char *path)
345 unsigned long len = 0;
346 struct stat checkdir;
347 struct host *host;
348 struct mk_config *cnf;
350 cnf = mk_config_create(path);
352 host = mk_mem_malloc_z(sizeof(struct host));
353 host->servername = 0;
354 host->file = mk_string_dup(path);
356 host->servername = mk_config_getval(cnf, "Servername", MK_CONFIG_VAL_STR);
357 host->documentroot.data = mk_config_getval(cnf,
358 "DocumentRoot",
359 MK_CONFIG_VAL_STR);
360 host->documentroot.len = strlen(host->documentroot.data);
361 if (stat(host->documentroot.data, &checkdir) == -1) {
362 fprintf(stderr, "ERROR: Invalid path to Server_root in %s\n\n", path);
363 exit(1);
365 else if (!(checkdir.st_mode & S_IFDIR)) {
366 fprintf(stderr,
367 "ERROR: DocumentRoot variable in %s has an invalid directory path\n\n",
368 path);
369 exit(1);
372 /* Access log */
373 host->access_log_path = mk_config_getval(cnf,
374 "AccessLog", MK_CONFIG_VAL_STR);
375 /* Error log */
376 host->error_log_path = mk_config_getval(cnf,
377 "ErrorLog", MK_CONFIG_VAL_STR);
379 /* Get directory */
380 host->getdir = (int) mk_config_getval(cnf, "GetDir", MK_CONFIG_VAL_BOOL);
381 if (host->getdir == VAR_ERR) {
382 mk_config_print_error_msg("GetDir", path);
385 if (!host->servername) {
386 mk_config_free(cnf);
387 return NULL;
390 /* Server Signature */
391 if (config->hideversion == VAR_OFF) {
392 m_build_buffer(&host->host_signature, &len,
393 "Monkey/%s", VERSION);
395 else {
396 m_build_buffer(&host->host_signature, &len, "Monkey");
398 m_build_buffer(&host->header_host_signature.data,
399 &host->header_host_signature.len,
400 "Server: %s", host->host_signature);
402 if( host->access_log_path != NULL ) {
403 if (pipe(host->log_access) < 0) {
404 perror("pipe");
405 } else {
406 fcntl(host->log_access[1], F_SETFL, O_NONBLOCK);
410 if( host->error_log_path != NULL ) {
411 if (pipe(host->log_error) < 0) {
412 perror("pipe");
413 } else {
414 fcntl(host->log_error[1], F_SETFL, O_NONBLOCK);
418 host->next = NULL;
419 mk_config_free(cnf);
420 return host;
423 /* Imprime error de configuracion y cierra */
424 void mk_config_print_error_msg(char *variable, char *path)
426 fprintf(stderr, "\nError: %s variable in %s has an invalid value.\n",
427 variable, path);
428 fflush(stderr);
429 exit(1);
432 /* Agrega distintos index.xxx */
433 void mk_config_add_index(char *indexname)
435 struct indexfile *new_index = 0, *aux_index;
437 new_index = (struct indexfile *) malloc(sizeof(struct indexfile));
438 strncpy(new_index->indexname, indexname, MAX_INDEX_NOMBRE - 1);
439 new_index->indexname[MAX_INDEX_NOMBRE - 1] = '\0';
440 new_index->next = NULL;
442 if (first_index == NULL) {
443 first_index = new_index;
445 else {
446 aux_index = first_index;
447 while (aux_index->next != NULL)
448 aux_index = aux_index->next;
449 aux_index->next = new_index;
453 void mk_config_set_init_values(void)
455 /* Valores iniciales */
456 config->timeout = 15;
457 config->hideversion = VAR_OFF;
458 config->keep_alive = VAR_ON;
459 config->keep_alive_timeout = 15;
460 config->max_keep_alive_request = 50;
461 config->maxclients = 150;
462 config->max_ip = 15;
463 config->resume = VAR_ON;
464 config->standard_port = 80;
465 config->listen_addr = MK_DEFAULT_LISTEN_ADDR;
466 config->serverport = 2001;
467 config->symlink = VAR_OFF;
468 config->nhosts = 0;
469 config->user = NULL;
470 config->open_flags = O_RDONLY | O_NONBLOCK;
472 /* Plugins */
473 config->plugins = mk_mem_malloc_z(sizeof(struct plugin_stages));
476 /* read main configuration from monkey.conf */
477 void mk_config_start_configure(void)
479 unsigned long len;
481 mk_config_set_init_values();
482 mk_config_read_files(config->file_config, M_DEFAULT_CONFIG_FILE);
484 /* if not index names defined, set default */
485 if (first_index == NULL) {
486 mk_config_add_index("index.html");
489 /* Load mimes */
490 mk_mimetype_read_config();
492 /* Basic server information */
493 if (config->hideversion == VAR_OFF) {
494 m_build_buffer(&config->server_software.data,
495 &len, "Monkey/%s (%s)", VERSION, OS);
496 config->server_software.len = len;
498 else {
499 m_build_buffer(&config->server_software.data, &len, "Monkey Server");
500 config->server_software.len = len;
504 struct host *mk_config_host_find(mk_pointer host)
506 struct host *aux_host;
508 aux_host = config->hosts;
510 while (aux_host) {
511 if (strncasecmp(aux_host->servername, host.data, host.len) == 0)
512 break;
513 else
514 aux_host = aux_host->next;
517 return aux_host;
520 void mk_config_sanity_check()
522 /* Check O_NOATIME for current user, flag will just be used
523 * if running user is allowed to.
525 int fd, flags = config->open_flags;
527 flags |= O_NOATIME;
528 fd = open(config->file_config, flags);
530 if (fd > -1) {
531 config->open_flags = flags;
532 close(fd);