1 /*-
2  * Copyright (c) 2007-2011 Varnish Software AS
3  * All rights reserved.
4  *
5  * Author: Dag-Erling Smørgav <des@des.no>
6  *
7  * SPDX-License-Identifier: BSD-2-Clause
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * STEVEDORE: one who works at or is responsible for loading and
31  * unloading ships in port.  Example: "on the wharves, stevedores were
32  * unloading cargo from the far corners of the world." Origin: Spanish
33  * estibador, from estibar to pack.  First Known Use: 1788
34  */
35 
36 #include "config.h"
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 
43 #include "mgt/mgt.h"
44 #include "common/heritage.h"
45 #include "vcli_serve.h"
46 
47 #include "storage/storage.h"
48 
49 static VTAILQ_HEAD(, stevedore) stevedores =
50     VTAILQ_HEAD_INITIALIZER(stevedores);
51 
52 /* Name of transient storage */
53 #define TRANSIENT_STORAGE	"Transient"
54 
55 struct stevedore *stv_transient;
56 
57 /*--------------------------------------------------------------------*/
58 
59 int
STV__iter(struct stevedore ** const pp)60 STV__iter(struct stevedore ** const pp)
61 {
62 
63 	AN(pp);
64 	CHECK_OBJ_ORNULL(*pp, STEVEDORE_MAGIC);
65 	if (*pp != NULL)
66 		*pp = VTAILQ_NEXT(*pp, list);
67 	else
68 		*pp = VTAILQ_FIRST(&stevedores);
69 	return (*pp != NULL);
70 }
71 
72 /*--------------------------------------------------------------------*/
73 
v_matchproto_(cli_func_t)74 static void v_matchproto_(cli_func_t)
75 stv_cli_list(struct cli *cli, const char * const *av, void *priv)
76 {
77 	struct stevedore *stv;
78 
79 	ASSERT_MGT();
80 	(void)av;
81 	(void)priv;
82 	VCLI_Out(cli, "Storage devices:\n");
83 	STV_Foreach(stv)
84 		VCLI_Out(cli, "\tstorage.%s = %s\n", stv->ident, stv->name);
85 }
86 
v_matchproto_(cli_func_t)87 static void v_matchproto_(cli_func_t)
88 stv_cli_list_json(struct cli *cli, const char * const *av, void *priv)
89 {
90 	struct stevedore *stv;
91 	int n = 0;
92 
93 	(void)priv;
94 	ASSERT_MGT();
95 	VCLI_JSON_begin(cli, 2, av);
96 	VCLI_Out(cli, ",\n");
97 	STV_Foreach(stv) {
98 		VCLI_Out(cli, "%s", n ? ",\n" : "");
99 		n++;
100 		VCLI_Out(cli, "{\n");
101 		VSB_indent(cli->sb, 2);
102 		VCLI_Out(cli, "\"name\": ");
103 		VCLI_JSON_str(cli, stv->ident);
104 		VCLI_Out(cli, ",\n");
105 		VCLI_Out(cli, "\"storage\": ");
106 		VCLI_JSON_str(cli, stv->name);
107 		VSB_indent(cli->sb, -2);
108 		VCLI_Out(cli, "\n}");
109 	}
110 	VCLI_JSON_end(cli);
111 }
112 
113 /*--------------------------------------------------------------------*/
114 
115 static struct cli_proto cli_stv[] = {
116 	{ CLICMD_STORAGE_LIST,		"", stv_cli_list, stv_cli_list_json },
117 	{ NULL}
118 };
119 
120 /*--------------------------------------------------------------------
121  */
122 
123 #ifdef WITH_PERSISTENT_STORAGE
v_matchproto_(storage_init_f)124 static void v_noreturn_ v_matchproto_(storage_init_f)
125 smp_fake_init(struct stevedore *parent, int ac, char * const *av)
126 {
127 
128 	(void)parent;
129 	(void)ac;
130 	(void)av;
131 	ARGV_ERR(
132 	    "-spersistent has been deprecated, please see:\n"
133 	    "  https://www.varnish-cache.org/docs/trunk/phk/persistent.html\n"
134 	    "for details.\n"
135 	);
136 }
137 
138 static const struct stevedore smp_fake_stevedore = {
139 	.magic = STEVEDORE_MAGIC,
140 	.name = "deprecated_persistent",
141 	.init = smp_fake_init,
142 };
143 #endif
144 
145 /*--------------------------------------------------------------------
146  * Parse a stevedore argument on the form:
147  *	[ name '=' ] strategy [ ',' arg ] *
148  */
149 
150 static const struct choice STV_choice[] = {
151 	{ "file",			&smf_stevedore },
152 	{ "malloc",			&sma_stevedore },
153 #ifdef WITH_PERSISTENT_STORAGE
154 	{ "deprecated_persistent",	&smp_stevedore },
155 	{ "persistent",			&smp_fake_stevedore },
156 #endif
157 #if defined(HAVE_UMEM_H)
158 	{ "umem",			&smu_stevedore },
159 	{ "default",			&smu_stevedore },
160 #else
161 	{ "default",			&sma_stevedore },
162 #endif
163 	{ NULL,		NULL }
164 };
165 
166 static void
stv_check_ident(const char * spec,const char * ident)167 stv_check_ident(const char *spec, const char *ident)
168 {
169 	struct stevedore *stv;
170 	unsigned found = 0;
171 
172 	if (!strcmp(ident, TRANSIENT_STORAGE))
173 		found = (stv_transient != NULL);
174 	else {
175 		STV_Foreach(stv)
176 			if (!strcmp(stv->ident, ident)) {
177 				found = 1;
178 				break;
179 			}
180 	}
181 
182 	if (found)
183 		ARGV_ERR("(-s %s) '%s' is already defined\n", spec, ident);
184 }
185 
186 void
STV_Config(const char * spec)187 STV_Config(const char *spec)
188 {
189 	char **av, buf[8];
190 	const char *ident;
191 	struct stevedore *stv;
192 	const struct stevedore *stv2;
193 	int ac;
194 	static unsigned seq = 0;
195 
196 	av = MGT_NamedArg(spec, &ident, "-s");
197 	AN(av);
198 
199 	if (av[1] == NULL)
200 		ARGV_ERR("-s argument lacks strategy {malloc, file, ...}\n");
201 
202 	for (ac = 0; av[ac + 2] != NULL; ac++)
203 		continue;
204 
205 	stv2 = MGT_Pick(STV_choice, av[1], "storage");
206 	AN(stv2);
207 
208 	/* Append strategy to ident string */
209 	VSB_printf(vident, ",-s%s", av[1]);
210 
211 	av += 2;
212 
213 	CHECK_OBJ_NOTNULL(stv2, STEVEDORE_MAGIC);
214 	ALLOC_OBJ(stv, STEVEDORE_MAGIC);
215 	AN(stv);
216 
217 	*stv = *stv2;
218 	AN(stv->name);
219 
220 	if (ident) {
221 		stv->ident = ident;
222 	} else {
223 		bprintf(buf, "s%u", seq++);
224 		stv->ident = strdup(buf);
225 	}
226 	AN(stv->ident);
227 	stv_check_ident(spec, stv->ident);
228 
229 	if (stv->init != NULL)
230 		stv->init(stv, ac, av);
231 	else if (ac != 0)
232 		ARGV_ERR("(-s %s) too many arguments\n", stv->name);
233 
234 	AN(stv->allocobj);
235 	AN(stv->methods);
236 
237 	if (!strcmp(stv->ident, TRANSIENT_STORAGE)) {
238 		AZ(stv_transient);
239 		stv_transient = stv;
240 	} else
241 		VTAILQ_INSERT_TAIL(&stevedores, stv, list);
242 	/* NB: Do not free av, stevedore gets to keep it */
243 }
244 
245 /*--------------------------------------------------------------------*/
246 
247 void
STV_Config_Transient(void)248 STV_Config_Transient(void)
249 {
250 
251 	ASSERT_MGT();
252 
253 	VCLS_AddFunc(mgt_cls, MCF_AUTH, cli_stv);
254 	if (stv_transient == NULL)
255 		STV_Config(TRANSIENT_STORAGE "=default");
256 	AN(stv_transient);
257 	VTAILQ_INSERT_TAIL(&stevedores, stv_transient, list);
258 }
259