cmogstored.git  about / heads / tags
alternative mogstored implementation for MogileFS
blob cdd137404c6864edfd2e0087b22a3b20c69b8270 5020 bytes (raw)
$ git show HEAD:cfg.c	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
 
/*
 * Copyright (C) 2012-2020 all contributors <cmogstored-public@yhbt.net>
 * License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
 */
#include "cmogstored.h"
#include "cfg.h"

static Hash_table *all_cfg; /* we support multiple configs -> svcs */
struct mog_cfg mog_cli;
bool mog_cfg_multi;

static void cfg_free_internal(struct mog_cfg *cfg)
{
	mog_free(cfg->docroot);
	mog_free(cfg->pidfile);
	mog_free(cfg->config);
	mog_free(cfg->configfile);
	mog_free(cfg->server);
	mog_addrinfo_free(&cfg->mgmtlisten);
	mog_addrinfo_free(&cfg->httplisten);
	mog_addrinfo_free(&cfg->httpgetlisten);
	/* let svc.c deal with cfg->svc for now */
}

static void cfg_free(void *ptr)
{
	struct mog_cfg *cfg = ptr;
	cfg_free_internal(cfg);
	free(cfg);
}

static size_t cfg_hash(const void *x, size_t tablesize)
{
	const struct mog_cfg *cfg = x;

	return hash_string(cfg->configfile, tablesize);
}

static bool cfg_cmp(const void *a, const void *b)
{
	const struct mog_cfg *cfg_a = a;
	const struct mog_cfg *cfg_b = b;

	return strcmp(cfg_a->configfile, cfg_b->configfile) == 0;
}

static void cfg_atexit(void)
{
	hash_free(all_cfg);
	cfg_free_internal(&mog_cli);
}

__attribute__((constructor)) static void cfg_init(void)
{
	all_cfg = hash_initialize(7, NULL, cfg_hash, cfg_cmp, cfg_free);
	mog_oom_if_null(all_cfg);

	atexit(cfg_atexit);
}

struct mog_cfg * mog_cfg_new(const char *configfile)
{
	struct mog_cfg *cfg = xzalloc(sizeof(struct mog_cfg));

	cfg->configfile = mog_canonpath_die(configfile, CAN_EXISTING);
	cfg->config = xstrdup(configfile);

	switch (hash_insert_if_absent(all_cfg, cfg, NULL)) {
	case 0:
		cfg_free(cfg);
		cfg = NULL;
	case 1: break;
	default: mog_oom();
	}

	return cfg;
}

int mog_cfg_load(struct mog_cfg *cfg)
{
	struct stat sb;
	char *buf = NULL;
	ssize_t r;
	int rc = -1;
	int fd = open(cfg->configfile, O_RDONLY);

	if (fd < 0) goto out;
	if (fstat(fd, &sb) < 0) goto out;

	buf = xmalloc(sb.st_size + strlen("\n"));

	errno = 0;
	r = read(fd, buf, sb.st_size);
	if (r != sb.st_size)
		die("read(..., %ld) failed on %s: %s",
		    (long)sb.st_size, cfg->configfile,
		    errno ? strerror(errno) : "EOF");

	buf[r] = '\n'; /* parser _needs_ a trailing newline */
	rc = mog_cfg_parse(cfg, buf, r + 1);
out:
	PRESERVE_ERRNO(do {
		if (buf) free(buf);
		if (fd >= 0) mog_close(fd);
	} while(0));

	return rc;
}

static size_t nr_config(void)
{
	return all_cfg ? hash_get_n_entries(all_cfg) : 0;
}

#define RELPATH_ERR \
"relative paths are incompatible with --daemonize and SIGUSR2 upgrades"
static void validate_daemonize(struct mog_cfg *cli)
{
	size_t nerr = 0;
	const char *path = getenv("PATH");
	const char *p;

	hash_do_for_each(all_cfg, mog_cfg_validate_daemon, &nerr);

	/* cli may have merged identical settings */
	if (!nerr)
		mog_cfg_validate_daemon(cli, &nerr);

	/* we may use confstr(_CS_PATH) in the future, currently we do not */
	if (!path)
		die("PATH environment must be set");

	p = path;

	/* trailing ':' in PATH is identical to trailing ":." (cwd) */
	if (p[strlen(p) - 1] == ':')
		goto err;

	while (*p) {
		if (*p == '/') {
			p = strchr(p, ':');
			if (!p)
				break;
			p++;
			continue;
		}
err:
		warn("PATH environment contains relative path: %s", p);
		nerr++;
		break;
	}

	if (nerr)
		die(RELPATH_ERR);
}

#define MULTI_CFG_ERR \
"--multi must be set if using multiple --config/-c switches"

void mog_cfg_validate_or_die(struct mog_cfg *cli)
{
	switch (nr_config()) {
	case 0:
		mog_cfg_merge_defaults(cli);
		assert(cli->configfile == NULL &&
		       "BUG: --config was set but not detected");
		break; /* CLI-only */
	case 1:
		hash_do_for_each(all_cfg, mog_cfg_validate_one, cli);
		mog_cfg_merge_defaults(cli);
		break;
	default: /* multiple config files */
		mog_cfg_die_if_cli_set(cli);
		hash_do_for_each(all_cfg, mog_cfg_validate_multi, cli);
		if (!mog_cfg_multi)
			die(MULTI_CFG_ERR);
	}
	if (cli->daemonize)
		validate_daemonize(cli);
	mog_set_maxconns(cli->maxconns);
}

static struct mog_fd *
bind_or_die(struct mog_addrinfo *a, struct mog_svc *svc, mog_post_accept_fn fn)
{
	int fd;

	if (a == NULL) return NULL;
	fd = mog_bind_listen(a->addr);
	if (fd < 0)
		die_errno("addr=%s failed to bind+listen", a->orig);

	return mog_accept_init(fd, svc, a, fn);
}

static bool svc_from_cfg(void *cfg_ptr, void *ignored)
{
	struct mog_cfg *cfg = cfg_ptr;
	struct mog_svc *svc;

	assert(cfg->docroot && "no docroot specified");
	svc = mog_svc_new(cfg->docroot);
	if (!svc)
		die("failed to load svc from docroot=%s", cfg->docroot);

	svc->mgmt_mfd = bind_or_die(cfg->mgmtlisten, svc, mog_mgmt_post_accept);

	if (cfg->server && strcmp(cfg->server, "none") == 0)
		return true;

	svc->http_mfd = bind_or_die(cfg->httplisten, svc, mog_http_post_accept);
	svc->httpget_mfd = bind_or_die(cfg->httpgetlisten, svc,
					mog_httpget_post_accept);

	return true;
}

void mog_cfg_svc_start_or_die(struct mog_cfg *cli)
{
	switch (nr_config()) {
	case 0:
	case 1:
		svc_from_cfg(cli, NULL);
		break;
	default:
		hash_do_for_each(all_cfg, svc_from_cfg, NULL);
	}
}

git clone https://yhbt.net/cmogstored.git