cmogstored 1.8.1 - use default system stack size
[cmogstored.git] / cfg.c
blobcdd137404c6864edfd2e0087b22a3b20c69b8270
1 /*
2 * Copyright (C) 2012-2020 all contributors <cmogstored-public@yhbt.net>
3 * License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4 */
5 #include "cmogstored.h"
6 #include "cfg.h"
8 static Hash_table *all_cfg; /* we support multiple configs -> svcs */
9 struct mog_cfg mog_cli;
10 bool mog_cfg_multi;
12 static void cfg_free_internal(struct mog_cfg *cfg)
14 mog_free(cfg->docroot);
15 mog_free(cfg->pidfile);
16 mog_free(cfg->config);
17 mog_free(cfg->configfile);
18 mog_free(cfg->server);
19 mog_addrinfo_free(&cfg->mgmtlisten);
20 mog_addrinfo_free(&cfg->httplisten);
21 mog_addrinfo_free(&cfg->httpgetlisten);
22 /* let svc.c deal with cfg->svc for now */
25 static void cfg_free(void *ptr)
27 struct mog_cfg *cfg = ptr;
28 cfg_free_internal(cfg);
29 free(cfg);
32 static size_t cfg_hash(const void *x, size_t tablesize)
34 const struct mog_cfg *cfg = x;
36 return hash_string(cfg->configfile, tablesize);
39 static bool cfg_cmp(const void *a, const void *b)
41 const struct mog_cfg *cfg_a = a;
42 const struct mog_cfg *cfg_b = b;
44 return strcmp(cfg_a->configfile, cfg_b->configfile) == 0;
47 static void cfg_atexit(void)
49 hash_free(all_cfg);
50 cfg_free_internal(&mog_cli);
53 __attribute__((constructor)) static void cfg_init(void)
55 all_cfg = hash_initialize(7, NULL, cfg_hash, cfg_cmp, cfg_free);
56 mog_oom_if_null(all_cfg);
58 atexit(cfg_atexit);
61 struct mog_cfg * mog_cfg_new(const char *configfile)
63 struct mog_cfg *cfg = xzalloc(sizeof(struct mog_cfg));
65 cfg->configfile = mog_canonpath_die(configfile, CAN_EXISTING);
66 cfg->config = xstrdup(configfile);
68 switch (hash_insert_if_absent(all_cfg, cfg, NULL)) {
69 case 0:
70 cfg_free(cfg);
71 cfg = NULL;
72 case 1: break;
73 default: mog_oom();
76 return cfg;
79 int mog_cfg_load(struct mog_cfg *cfg)
81 struct stat sb;
82 char *buf = NULL;
83 ssize_t r;
84 int rc = -1;
85 int fd = open(cfg->configfile, O_RDONLY);
87 if (fd < 0) goto out;
88 if (fstat(fd, &sb) < 0) goto out;
90 buf = xmalloc(sb.st_size + strlen("\n"));
92 errno = 0;
93 r = read(fd, buf, sb.st_size);
94 if (r != sb.st_size)
95 die("read(..., %ld) failed on %s: %s",
96 (long)sb.st_size, cfg->configfile,
97 errno ? strerror(errno) : "EOF");
99 buf[r] = '\n'; /* parser _needs_ a trailing newline */
100 rc = mog_cfg_parse(cfg, buf, r + 1);
101 out:
102 PRESERVE_ERRNO(do {
103 if (buf) free(buf);
104 if (fd >= 0) mog_close(fd);
105 } while(0));
107 return rc;
110 static size_t nr_config(void)
112 return all_cfg ? hash_get_n_entries(all_cfg) : 0;
115 #define RELPATH_ERR \
116 "relative paths are incompatible with --daemonize and SIGUSR2 upgrades"
117 static void validate_daemonize(struct mog_cfg *cli)
119 size_t nerr = 0;
120 const char *path = getenv("PATH");
121 const char *p;
123 hash_do_for_each(all_cfg, mog_cfg_validate_daemon, &nerr);
125 /* cli may have merged identical settings */
126 if (!nerr)
127 mog_cfg_validate_daemon(cli, &nerr);
129 /* we may use confstr(_CS_PATH) in the future, currently we do not */
130 if (!path)
131 die("PATH environment must be set");
133 p = path;
135 /* trailing ':' in PATH is identical to trailing ":." (cwd) */
136 if (p[strlen(p) - 1] == ':')
137 goto err;
139 while (*p) {
140 if (*p == '/') {
141 p = strchr(p, ':');
142 if (!p)
143 break;
144 p++;
145 continue;
147 err:
148 warn("PATH environment contains relative path: %s", p);
149 nerr++;
150 break;
153 if (nerr)
154 die(RELPATH_ERR);
157 #define MULTI_CFG_ERR \
158 "--multi must be set if using multiple --config/-c switches"
160 void mog_cfg_validate_or_die(struct mog_cfg *cli)
162 switch (nr_config()) {
163 case 0:
164 mog_cfg_merge_defaults(cli);
165 assert(cli->configfile == NULL &&
166 "BUG: --config was set but not detected");
167 break; /* CLI-only */
168 case 1:
169 hash_do_for_each(all_cfg, mog_cfg_validate_one, cli);
170 mog_cfg_merge_defaults(cli);
171 break;
172 default: /* multiple config files */
173 mog_cfg_die_if_cli_set(cli);
174 hash_do_for_each(all_cfg, mog_cfg_validate_multi, cli);
175 if (!mog_cfg_multi)
176 die(MULTI_CFG_ERR);
178 if (cli->daemonize)
179 validate_daemonize(cli);
180 mog_set_maxconns(cli->maxconns);
183 static struct mog_fd *
184 bind_or_die(struct mog_addrinfo *a, struct mog_svc *svc, mog_post_accept_fn fn)
186 int fd;
188 if (a == NULL) return NULL;
189 fd = mog_bind_listen(a->addr);
190 if (fd < 0)
191 die_errno("addr=%s failed to bind+listen", a->orig);
193 return mog_accept_init(fd, svc, a, fn);
196 static bool svc_from_cfg(void *cfg_ptr, void *ignored)
198 struct mog_cfg *cfg = cfg_ptr;
199 struct mog_svc *svc;
201 assert(cfg->docroot && "no docroot specified");
202 svc = mog_svc_new(cfg->docroot);
203 if (!svc)
204 die("failed to load svc from docroot=%s", cfg->docroot);
206 svc->mgmt_mfd = bind_or_die(cfg->mgmtlisten, svc, mog_mgmt_post_accept);
208 if (cfg->server && strcmp(cfg->server, "none") == 0)
209 return true;
211 svc->http_mfd = bind_or_die(cfg->httplisten, svc, mog_http_post_accept);
212 svc->httpget_mfd = bind_or_die(cfg->httpgetlisten, svc,
213 mog_httpget_post_accept);
215 return true;
218 void mog_cfg_svc_start_or_die(struct mog_cfg *cli)
220 switch (nr_config()) {
221 case 0:
222 case 1:
223 svc_from_cfg(cli, NULL);
224 break;
225 default:
226 hash_do_for_each(all_cfg, svc_from_cfg, NULL);