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