1 /**
2 * Copyright (c) 2014, Timothy Stack
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice, this
10 * list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 * * Neither the name of Timothy Stack nor the names of its contributors
15 * may be used to endorse or promote products derived from this software
16 * without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "config.h"
31
32 #include <assert.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "auto_mem.hh"
37 #include "base/lnav_log.hh"
38 #include "environ_vtab.hh"
39
40 using namespace std;
41
42 extern char **environ;
43
44 const char *ENVIRON_CREATE_STMT = R"(
45 -- Access lnav's environment variables through this table.
46 CREATE TABLE environ (
47 name text PRIMARY KEY,
48 value text
49 );
50 )";
51
52 struct vtab {
53 sqlite3_vtab base;
54 sqlite3 * db;
55 };
56
57 struct vtab_cursor {
58 sqlite3_vtab_cursor base;
59 char **env_cursor;
60 };
61
62 static int vt_destructor(sqlite3_vtab *p_svt);
63
vt_create(sqlite3 * db,void * pAux,int argc,const char * const * argv,sqlite3_vtab ** pp_vt,char ** pzErr)64 static int vt_create(sqlite3 *db,
65 void *pAux,
66 int argc, const char *const *argv,
67 sqlite3_vtab **pp_vt,
68 char **pzErr)
69 {
70 vtab *p_vt;
71
72 /* Allocate the sqlite3_vtab/vtab structure itself */
73 p_vt = (vtab *)sqlite3_malloc(sizeof(*p_vt));
74
75 if (p_vt == NULL) {
76 return SQLITE_NOMEM;
77 }
78
79 memset(&p_vt->base, 0, sizeof(sqlite3_vtab));
80 p_vt->db = db;
81
82 *pp_vt = &p_vt->base;
83
84 int rc = sqlite3_declare_vtab(db, ENVIRON_CREATE_STMT);
85
86 return rc;
87 }
88
89
vt_destructor(sqlite3_vtab * p_svt)90 static int vt_destructor(sqlite3_vtab *p_svt)
91 {
92 vtab *p_vt = (vtab *)p_svt;
93
94 /* Free the SQLite structure */
95 sqlite3_free(p_vt);
96
97 return SQLITE_OK;
98 }
99
vt_connect(sqlite3 * db,void * p_aux,int argc,const char * const * argv,sqlite3_vtab ** pp_vt,char ** pzErr)100 static int vt_connect(sqlite3 *db, void *p_aux,
101 int argc, const char *const *argv,
102 sqlite3_vtab **pp_vt, char **pzErr)
103 {
104 return vt_create(db, p_aux, argc, argv, pp_vt, pzErr);
105 }
106
vt_disconnect(sqlite3_vtab * pVtab)107 static int vt_disconnect(sqlite3_vtab *pVtab)
108 {
109 return vt_destructor(pVtab);
110 }
111
vt_destroy(sqlite3_vtab * p_vt)112 static int vt_destroy(sqlite3_vtab *p_vt)
113 {
114 return vt_destructor(p_vt);
115 }
116
117 static int vt_next(sqlite3_vtab_cursor *cur);
118
vt_open(sqlite3_vtab * p_svt,sqlite3_vtab_cursor ** pp_cursor)119 static int vt_open(sqlite3_vtab *p_svt, sqlite3_vtab_cursor **pp_cursor)
120 {
121 vtab *p_vt = (vtab *)p_svt;
122
123 p_vt->base.zErrMsg = NULL;
124
125 vtab_cursor *p_cur = (vtab_cursor *)new vtab_cursor();
126
127 if (p_cur == NULL) {
128 return SQLITE_NOMEM;
129 } else {
130 *pp_cursor = (sqlite3_vtab_cursor *)p_cur;
131
132 p_cur->base.pVtab = p_svt;
133 p_cur->env_cursor = environ;
134 }
135
136 return SQLITE_OK;
137 }
138
vt_close(sqlite3_vtab_cursor * cur)139 static int vt_close(sqlite3_vtab_cursor *cur)
140 {
141 vtab_cursor *p_cur = (vtab_cursor *)cur;
142
143 /* Free cursor struct. */
144 delete p_cur;
145
146 return SQLITE_OK;
147 }
148
vt_eof(sqlite3_vtab_cursor * cur)149 static int vt_eof(sqlite3_vtab_cursor *cur)
150 {
151 vtab_cursor *vc = (vtab_cursor *)cur;
152
153 return vc->env_cursor[0] == NULL;
154 }
155
vt_next(sqlite3_vtab_cursor * cur)156 static int vt_next(sqlite3_vtab_cursor *cur)
157 {
158 vtab_cursor *vc = (vtab_cursor *)cur;
159
160 if (vc->env_cursor[0] != NULL) {
161 vc->env_cursor += 1;
162 }
163
164 return SQLITE_OK;
165 }
166
vt_column(sqlite3_vtab_cursor * cur,sqlite3_context * ctx,int col)167 static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col)
168 {
169 vtab_cursor *vc = (vtab_cursor *)cur;
170 const char *eq = strchr(vc->env_cursor[0], '=');
171
172 switch (col) {
173 case 0:
174 sqlite3_result_text(ctx,
175 vc->env_cursor[0], eq - vc->env_cursor[0],
176 SQLITE_TRANSIENT);
177 break;
178 case 1:
179 sqlite3_result_text(ctx, eq + 1, -1, SQLITE_TRANSIENT);
180 break;
181 }
182
183 return SQLITE_OK;
184 }
185
vt_rowid(sqlite3_vtab_cursor * cur,sqlite_int64 * p_rowid)186 static int vt_rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *p_rowid)
187 {
188 vtab_cursor *p_cur = (vtab_cursor *)cur;
189
190 *p_rowid = (int64_t)p_cur->env_cursor[0];
191
192 return SQLITE_OK;
193 }
194
vt_best_index(sqlite3_vtab * tab,sqlite3_index_info * p_info)195 static int vt_best_index(sqlite3_vtab *tab, sqlite3_index_info *p_info)
196 {
197 return SQLITE_OK;
198 }
199
vt_filter(sqlite3_vtab_cursor * p_vtc,int idxNum,const char * idxStr,int argc,sqlite3_value ** argv)200 static int vt_filter(sqlite3_vtab_cursor *p_vtc,
201 int idxNum, const char *idxStr,
202 int argc, sqlite3_value **argv)
203 {
204 return SQLITE_OK;
205 }
206
vt_update(sqlite3_vtab * tab,int argc,sqlite3_value ** argv,sqlite_int64 * rowid)207 static int vt_update(sqlite3_vtab *tab,
208 int argc,
209 sqlite3_value **argv,
210 sqlite_int64 *rowid)
211 {
212 const char *name = (
213 argc > 2 ? (const char *)sqlite3_value_text(argv[2]) : nullptr);
214 vtab *p_vt = (vtab *)tab;
215 int retval = SQLITE_ERROR;
216
217 if (argc != 1 &&
218 (argc < 3 ||
219 sqlite3_value_type(argv[2]) == SQLITE_NULL ||
220 sqlite3_value_type(argv[3]) == SQLITE_NULL ||
221 sqlite3_value_text(argv[2])[0] == '\0')) {
222 tab->zErrMsg = sqlite3_mprintf(
223 "A non-empty name and value must be provided when inserting an "
224 "environment variable");
225
226 return SQLITE_ERROR;
227 }
228 if (name != nullptr && strchr(name, '=') != nullptr) {
229 tab->zErrMsg = sqlite3_mprintf(
230 "Environment variable names cannot contain an equals sign (=)");
231
232 return SQLITE_ERROR;
233 }
234
235 if (sqlite3_value_type(argv[0]) != SQLITE_NULL) {
236 int64_t index = sqlite3_value_int64(argv[0]);
237 const char *var = (const char *)index;
238 const char *eq = strchr(var, '=');
239 size_t namelen = eq - var;
240 char name[namelen + 1];
241
242 memcpy(name, var, namelen);
243 name[namelen] = '\0';
244 unsetenv(name);
245
246 retval = SQLITE_OK;
247 } else if (name != nullptr && getenv(name) != nullptr) {
248 #ifdef SQLITE_FAIL
249 int rc;
250
251 rc = sqlite3_vtab_on_conflict(p_vt->db);
252 switch (rc) {
253 case SQLITE_FAIL:
254 case SQLITE_ABORT:
255 tab->zErrMsg = sqlite3_mprintf(
256 "An environment variable with the name '%s' already exists",
257 name);
258 return rc;
259 case SQLITE_IGNORE:
260 return SQLITE_OK;
261 case SQLITE_REPLACE:
262 break;
263 default:
264 return rc;
265 }
266 #endif
267 }
268
269 if (name != nullptr && argc == 4) {
270 const unsigned char *value = sqlite3_value_text(argv[3]);
271
272 setenv((const char *)name, (const char *)value, 1);
273
274 return SQLITE_OK;
275 }
276
277 return retval;
278 }
279
280 static sqlite3_module vtab_module = {
281 0, /* iVersion */
282 vt_create, /* xCreate - create a vtable */
283 vt_connect, /* xConnect - associate a vtable with a connection */
284 vt_best_index, /* xBestIndex - best index */
285 vt_disconnect, /* xDisconnect - disassociate a vtable with a connection */
286 vt_destroy, /* xDestroy - destroy a vtable */
287 vt_open, /* xOpen - open a cursor */
288 vt_close, /* xClose - close a cursor */
289 vt_filter, /* xFilter - configure scan constraints */
290 vt_next, /* xNext - advance a cursor */
291 vt_eof, /* xEof - inidicate end of result set*/
292 vt_column, /* xColumn - read data */
293 vt_rowid, /* xRowid - read data */
294 vt_update, /* xUpdate - write data */
295 NULL, /* xBegin - begin transaction */
296 NULL, /* xSync - sync transaction */
297 NULL, /* xCommit - commit transaction */
298 NULL, /* xRollback - rollback transaction */
299 NULL, /* xFindFunction - function overloading */
300 };
301
register_environ_vtab(sqlite3 * db)302 int register_environ_vtab(sqlite3 *db)
303 {
304 auto_mem<char, sqlite3_free> errmsg;
305 int rc;
306
307 rc = sqlite3_create_module(db, "environ_vtab_impl", &vtab_module, NULL);
308 assert(rc == SQLITE_OK);
309 if ((rc = sqlite3_exec(db,
310 "CREATE VIRTUAL TABLE environ USING environ_vtab_impl()",
311 NULL, NULL, errmsg.out())) != SQLITE_OK) {
312 fprintf(stderr, "unable to create environ table %s\n", errmsg.in());
313 }
314 return rc;
315 }
316