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