1 #include <nm_core.h>
2 #include <nm_utils.h>
3 #include <nm_string.h>
4 #include <nm_vector.h>
5 #include <nm_cfg_file.h>
6 #include <nm_database.h>
7 
8 #include <pthread.h>
9 
10 static const char db_script[] = NM_FULL_DATAROOTDIR "/nemu/scripts/upgrade_db.sh";
11 static pthread_key_t db_conn_key;
12 static pthread_once_t key_once = PTHREAD_ONCE_INIT;
13 
14 static void nm_db_check_version(void);
15 static int nm_db_update(void);
16 static int nm_db_select_cb(void *v, int argc, char **argv,
17                            char **unused NM_UNUSED);
18 
nm_db_init_key()19 static void nm_db_init_key()
20 {
21     if (pthread_key_create(&db_conn_key, NULL) != 0) {
22         nm_bug(_("%s: pthread_key_create error: %s"),
23                 __func__, strerror(errno));
24     }
25 }
26 
nm_db_init(void)27 void nm_db_init(void)
28 {
29     const nm_cfg_t *cfg = nm_cfg_get();
30     int need_create_db = 0;
31     struct stat file_info;
32     char *db_errmsg;
33     db_conn_t *db_conn;
34     int rc;
35 
36     const char *query[] = {
37         "PRAGMA user_version=" NM_DB_VERSION,
38         "CREATE TABLE vms(id integer PRIMARY KEY AUTOINCREMENT, "
39             "name char(31), mem integer, smp char, kvm integer, "
40             "hcpu integer, vnc integer, arch char(32), iso char, "
41             "install integer, usb integer, usbid char, bios char, kernel char, "
42             "mouse_override integer, kernel_append char, tty_path char, "
43             "socket_path char, initrd char, machine char, fs9p_enable integer, "
44             "fs9p_path char, fs9p_name char, usb_type char, spice integer, "
45             "debug_port integer, debug_freeze integer, cmdappend char, team char, display_type char)",
46         "CREATE TABLE ifaces(id integer primary key autoincrement, "
47             "vm_name char, if_name char, mac_addr char, ipv4_addr char, "
48             "if_drv char, vhost integer, macvtap integer, parent_eth char, altname char, "
49             "netuser integer, hostfwd char, smb char)",
50         "CREATE TABLE drives(id integer primary key autoincrement, vm_name char, "
51             "drive_name char, drive_drv char, capacity integer, boot integer, discard integer)",
52         "CREATE TABLE vmsnapshots(id integer primary key autoincrement, "
53             "vm_name char, snap_name char, load integer, timestamp char)",
54         "CREATE TABLE veth(id integer primary key autoincrement, l_name char, r_name char)",
55         "CREATE TABLE usb(id integer primary key autoincrement, "
56             "vm_name char, dev_name char, vendor_id char, product_id char, serial char)"
57     };
58 
59     if (stat(cfg->db_path.data, &file_info) == -1)
60         need_create_db = 1;
61 
62     db_conn = nm_calloc(1, sizeof(*db_conn));
63     db_conn->in_transaction = false;
64 
65     if ((rc = sqlite3_open(cfg->db_path.data, &db_conn->handler)) != SQLITE_OK)
66         nm_bug(_("%s: database error: %s"), __func__, sqlite3_errstr(rc));
67     else
68         nm_debug("%s: db handler %p\n", __func__, (void *) db_conn->handler);
69 
70     if (pthread_once(&key_once, nm_db_init_key) != 0) {
71         nm_bug(_("%s: pthread_once error: %s"), __func__, strerror(errno));
72     }
73 
74     if (pthread_getspecific(db_conn_key) == NULL) {
75         if (pthread_setspecific(db_conn_key, db_conn) != 0) {
76             nm_bug(_("%s: pthread_setspecific error: %s"),
77                     __func__, strerror(errno));
78         }
79     }
80 
81     if (!need_create_db) {
82         nm_db_check_version();
83         return;
84     }
85 
86     for (size_t n = 0; n < nm_arr_len(query); n++) {
87         nm_debug("%s: \"%s\"\n", __func__, query[n]);
88 
89         if (db_conn->in_transaction)
90             nm_bug(_("%s: database in transaction"), __func__);
91 
92         if (sqlite3_exec(db_conn->handler, query[n],
93                     NULL, NULL, &db_errmsg) != SQLITE_OK)
94             nm_bug(_("%s: database error: %s"), __func__, db_errmsg);
95     }
96 }
97 
nm_db_select(const char * query,nm_vect_t * v)98 void nm_db_select(const char *query, nm_vect_t *v)
99 {
100     char *db_errmsg;
101     db_conn_t *db;
102 
103     if ((db = pthread_getspecific(db_conn_key)) == NULL) {
104         nm_bug(_("%s: got NULL db_conn"), __func__);
105     }
106 
107     if (db->in_transaction)
108         nm_bug(_("%s: database in transaction"), __func__);
109 
110     nm_debug("%s: \"%s\"\n", __func__, query);
111 
112     if (sqlite3_exec(db->handler, query, nm_db_select_cb,
113                     (void *) v, &db_errmsg) != SQLITE_OK) {
114         nm_bug(_("%s: database error: %s"), __func__, db_errmsg);
115     }
116 }
117 
nm_db_edit(const char * query)118 void nm_db_edit(const char *query)
119 {
120     char *db_errmsg;
121     db_conn_t *db;
122 
123     if ((db = pthread_getspecific(db_conn_key)) == NULL) {
124         nm_bug(_("%s: got NULL db_conn"), __func__);
125     }
126 
127     if (db->in_transaction)
128         nm_bug(_("%s: database in transaction"), __func__);
129 
130     nm_debug("%s: \"%s\"\n", __func__, query);
131 
132     if (sqlite3_exec(db->handler, query, NULL, NULL, &db_errmsg) != SQLITE_OK)
133         nm_bug(_("%s: database error: %s"), __func__, db_errmsg);
134 }
135 
nm_db_in_transaction()136 bool nm_db_in_transaction()
137 {
138     db_conn_t *db;
139 
140     db = pthread_getspecific(db_conn_key);
141 
142     return (db) ? db->in_transaction : false;
143 }
144 
nm_db_begin_transaction()145 void nm_db_begin_transaction()
146 {
147     char *db_errmsg;
148     db_conn_t *db;
149 
150     if ((db = pthread_getspecific(db_conn_key)) == NULL) {
151         nm_bug(_("%s: got NULL db_conn"), __func__);
152     }
153 
154     if (db->in_transaction)
155         nm_bug(_("%s: database already in transaction"), __func__);
156 
157     nm_debug("%s: BEGIN TRANSACTION\n", __func__);
158 
159     if (sqlite3_exec(db->handler, "BEGIN TRANSACTION", NULL, NULL, &db_errmsg) != SQLITE_OK)
160         nm_bug(_("%s: database error: %s"), __func__, db_errmsg);
161 
162     db->in_transaction = true;
163 }
164 
nm_db_atomic(const char * query)165 void nm_db_atomic(const char *query)
166 {
167     char *db_errmsg;
168     db_conn_t *db;
169 
170     if ((db = pthread_getspecific(db_conn_key)) == NULL) {
171         nm_bug(_("%s: got NULL db_conn"), __func__);
172     }
173 
174     if (!db->in_transaction)
175         nm_bug(_("%s: database not in transaction"), __func__);
176 
177     nm_debug("%s: \"%s\"\n", __func__, query);
178 
179     if (sqlite3_exec(db->handler, query, NULL, NULL, &db_errmsg) != SQLITE_OK)
180         nm_bug(_("%s: database error: %s"), __func__, db_errmsg);
181 }
182 
nm_db_commit()183 void nm_db_commit()
184 {
185     char *db_errmsg;
186     db_conn_t *db;
187 
188     if ((db = pthread_getspecific(db_conn_key)) == NULL) {
189         nm_bug(_("%s: got NULL db_conn"), __func__);
190     }
191 
192     if (!db->in_transaction)
193         nm_bug(_("%s: database not in transaction"), __func__);
194 
195     nm_debug("%s: COMMIT\n", __func__);
196 
197     if (sqlite3_exec(db->handler, "COMMIT", NULL, NULL, &db_errmsg) != SQLITE_OK)
198         nm_bug(_("%s: database error: %s"), __func__, db_errmsg);
199 
200     db->in_transaction = false;
201 }
202 
nm_db_rollback()203 void nm_db_rollback()
204 {
205     char *db_errmsg;
206     db_conn_t *db;
207 
208     if ((db = pthread_getspecific(db_conn_key)) == NULL) {
209         nm_bug(_("%s: got NULL db_conn"), __func__);
210     }
211 
212     if (!db->in_transaction)
213         nm_bug(_("%s: database not in transaction"), __func__);
214 
215     nm_debug("%s: ROLLBACK\n", __func__);
216 
217     if (sqlite3_exec(db->handler, "ROLLBACK", NULL, NULL, &db_errmsg) != SQLITE_OK)
218         nm_bug(_("%s: database error: %s"), __func__, db_errmsg);
219 
220     db->in_transaction = false;
221 }
222 
nm_db_close(void)223 void nm_db_close(void)
224 {
225     db_conn_t *db;
226 
227     db = pthread_getspecific(db_conn_key);
228 
229     if (!db) {
230         return;
231     }
232 
233     sqlite3_close(db->handler);
234     pthread_key_delete(db_conn_key);
235     free(db);
236 }
237 
nm_db_check_version(void)238 static void nm_db_check_version(void)
239 {
240     nm_str_t query = NM_INIT_STR;
241     nm_vect_t res = NM_INIT_VECT;
242 
243     nm_str_alloc_text(&query, NM_GET_DB_VERSION_SQL);
244     nm_db_select(query.data, &res);
245 
246     if (!res.n_memb) {
247         fprintf(stderr, _("%s: cannot get database version"), __func__);
248         exit(NM_ERR);
249     }
250 
251     if (nm_str_cmp_st(nm_vect_at(&res, 0), NM_DB_VERSION) != NM_OK) {
252         printf(_("Database version is not up do date."
253                     " I will try to update it.\n"));
254         if (nm_db_update() != NM_OK) {
255             fprintf(stderr, _("Cannot update database\n"));
256             exit(NM_ERR);
257         }
258     }
259 
260     nm_vect_free(&res, nm_str_vect_free_cb);
261     nm_str_free(&query);
262 }
263 
nm_db_update(void)264 static int nm_db_update(void)
265 {
266     const nm_cfg_t *cfg = nm_cfg_get();
267     nm_str_t backup = NM_INIT_STR;
268     nm_str_t answer = NM_INIT_STR;
269     nm_vect_t argv = NM_INIT_VECT;
270     int rc = NM_OK;
271     db_conn_t *db;
272 
273     if ((db = pthread_getspecific(db_conn_key)) == NULL) {
274         nm_bug(_("%s: got NULL db_conn"), __func__);
275     }
276 
277     if (db->in_transaction)
278         nm_bug(_("%s: database in transaction"), __func__);
279 
280     nm_str_format(&backup, "%s-backup", cfg->db_path.data);
281     nm_copy_file(&cfg->db_path, &backup);
282 
283     nm_vect_insert_cstr(&argv, db_script);
284     nm_vect_insert_cstr(&argv, cfg->db_path.data);
285     nm_vect_insert_cstr(&argv, cfg->qemu_bin_path.data);
286 
287     nm_vect_end_zero(&argv);
288 
289     if (nm_spawn_process(&argv, &answer) != NM_OK) {
290         rc = NM_ERR;
291         unlink(cfg->db_path.data);
292         nm_copy_file(&backup, &cfg->db_path);
293     }
294 
295     if (answer.len)
296         printf("%s\n", answer.data);
297 
298     unlink(backup.data);
299 
300     nm_vect_free(&argv, NULL);
301     nm_str_free(&backup);
302     nm_str_free(&answer);
303 
304     return rc;
305 }
306 
nm_db_select_cb(void * v,int argc,char ** argv,char ** unused NM_UNUSED)307 static int nm_db_select_cb(void *v, int argc, char **argv,
308                            char **unused NM_UNUSED)
309 {
310     nm_vect_t *res = (nm_vect_t *) v;
311     nm_str_t value = NM_INIT_STR;
312 
313     for (int n = 0; n < argc; n++) {
314         nm_str_alloc_text(&value, argv[n] ? argv[n] : "");
315         nm_vect_insert(res, &value, sizeof(nm_str_t), nm_str_vect_ins_cb);
316     }
317 
318     nm_str_free(&value);
319 
320     return 0;
321 }
322 
323 /* vim:set ts=4 sw=4: */
324