1 #include <nm_core.h>
2 #include <nm_form.h>
3 #include <nm_utils.h>
4 #include <nm_string.h>
5 #include <nm_vector.h>
6 #include <nm_window.h>
7 #include <nm_menu.h>
8 #include <nm_machine.h>
9 #include <nm_hw_info.h>
10 #include <nm_network.h>
11 #include <nm_database.h>
12 #include <nm_cfg_file.h>
13 #include <nm_vm_control.h>
14 #include <nm_qmp_control.h>
15 #include <nm_edit_vm.h>
16
17 static const char NM_LC_VM_FORM_CPU[] = "CPU count";
18 static const char NM_LC_VM_FORM_MEM_BEGIN[] = "Memory [4-";
19 static const char NM_LC_VM_FORM_MEM_END[] = "]Mb";
20 static const char NM_LC_VM_FORM_KVM[] = "KVM [yes/no]";
21 static const char NM_LC_VM_FORM_HCPU[] = "Host CPU [yes/no]";
22 static const char NM_LC_VM_FORM_NET_IFS[] = "Network interfaces";
23 static const char NM_LC_VM_FORM_DRV_IF[] = "Disk interface";
24 static const char NM_LC_VM_FORM_DRV_DIS[] = "Discard mode";
25 static const char NM_LC_VM_FORM_USB[] = "USB [yes/no]";
26 static const char NM_LC_VM_FORM_USBT[] = "USB version";
27 static const char NM_LC_VM_FORM_MACH[] = "Machine type";
28 static const char NM_LC_VM_FORM_ARGS[] = "Extra QEMU args";
29 static const char NM_LC_VM_FORM_GROUP[] = "Group";
30
31 static void nm_edit_vm_init_windows(nm_form_t *form);
32 static void nm_edit_vm_fields_setup(const nm_vmctl_data_t *cur);
33 static size_t nm_edit_vm_labels_setup();
34 static int nm_edit_vm_get_data(nm_vm_t *vm, const nm_vmctl_data_t *cur);
35 static void nm_edit_vm_update_db(nm_vm_t *vm, const nm_vmctl_data_t *cur, uint64_t mac);
36
37 enum {
38 NM_LBL_CPUNUM = 0, NM_FLD_CPUNUM,
39 NM_LBL_RAMTOT, NM_FLD_RAMTOT,
40 NM_LBL_KVMFLG, NM_FLD_KVMFLG,
41 NM_LBL_HOSCPU, NM_FLD_HOSCPU,
42 NM_LBL_IFSCNT, NM_FLD_IFSCNT,
43 NM_LBL_DISKIN, NM_FLD_DISKIN,
44 NM_LBL_DISCARD, NM_FLD_DISCARD,
45 NM_LBL_USBUSE, NM_FLD_USBUSE,
46 NM_LBL_USBTYP, NM_FLD_USBTYP,
47 NM_LBL_MACH, NM_FLD_MACH,
48 NM_LBL_ARGS, NM_FLD_ARGS,
49 NM_LBL_GROUP, NM_FLD_GROUP,
50 NM_FLD_COUNT
51 };
52
53 static nm_field_t *fields[NM_FLD_COUNT + 1];
54
nm_edit_vm_init_windows(nm_form_t * form)55 static void nm_edit_vm_init_windows(nm_form_t *form)
56 {
57 if (form) {
58 nm_form_window_init();
59 nm_form_data_t *form_data = (nm_form_data_t *)form_userptr(form);
60 if (form_data)
61 form_data->parent_window = action_window;
62 } else {
63 werase(action_window);
64 werase(help_window);
65 }
66
67 nm_init_side();
68 nm_init_action(_(NM_MSG_EDIT_VM));
69 nm_init_help_edit();
70
71 nm_print_vm_menu(NULL);
72 }
73
nm_edit_vm(const nm_str_t * name)74 void nm_edit_vm(const nm_str_t *name)
75 {
76 nm_form_data_t *form_data = NULL;
77 nm_form_t *form = NULL;
78 nm_vm_t vm = NM_INIT_VM;
79 nm_vmctl_data_t cur_settings = NM_VMCTL_INIT_DATA;
80 uint64_t last_mac;
81 size_t msg_len;
82
83 nm_edit_vm_init_windows(NULL);
84
85 nm_vmctl_get_data(name, &cur_settings);
86
87 msg_len = nm_edit_vm_labels_setup();
88
89 form_data = nm_form_data_new(
90 action_window, nm_edit_vm_init_windows, msg_len, NM_FLD_COUNT / 2, NM_TRUE);
91
92 if (nm_form_data_update(form_data, 0, 0) != NM_OK)
93 goto out;
94
95 for (size_t n = 0; n < NM_FLD_COUNT; n++) {
96 switch (n) {
97 case NM_FLD_CPUNUM:
98 fields[n] = nm_field_regexp_new(
99 n / 2, form_data, "^[0-9]{1}(:[0-9]{1})?(:[0-9]{1})? *$");
100 break;
101 case NM_FLD_RAMTOT:
102 fields[n] = nm_field_integer_new(
103 n / 2, form_data, 0, 4, nm_hw_total_ram());
104 break;
105 case NM_FLD_KVMFLG:
106 fields[n] = nm_field_enum_new(
107 n / 2, form_data, nm_form_yes_no, false, false);
108 break;
109 case NM_FLD_HOSCPU:
110 fields[n] = nm_field_enum_new(
111 n / 2, form_data, nm_form_yes_no, false, false);
112 break;
113 case NM_FLD_IFSCNT:
114 fields[n] = nm_field_integer_new(n / 2, form_data, 1, 0, 64);
115 break;
116 case NM_FLD_DISKIN:
117 fields[n] = nm_field_enum_new(
118 n / 2, form_data, nm_form_drive_drv, false, false);
119 break;
120 case NM_FLD_DISCARD:
121 fields[n] = nm_field_enum_new(
122 n / 2, form_data, nm_form_yes_no, false, false);
123 break;
124 case NM_FLD_USBUSE:
125 fields[n] = nm_field_enum_new(
126 n / 2, form_data, nm_form_yes_no, false, false);
127 break;
128 case NM_FLD_USBTYP:
129 fields[n] = nm_field_enum_new(
130 n / 2, form_data, nm_form_usbtype, false, false);
131 break;
132 case NM_FLD_MACH:
133 fields[n] = nm_field_enum_new(
134 n / 2, form_data,
135 nm_mach_get(nm_vect_str(&cur_settings.main, NM_SQL_ARCH)),
136 false, false
137 );
138 break;
139 case NM_FLD_ARGS:
140 fields[n] = nm_field_default_new(n / 2, form_data);
141 break;
142 case NM_FLD_GROUP:
143 fields[n] = nm_field_default_new(n / 2, form_data);
144 break;
145 default:
146 fields[n] = nm_field_label_new(n / 2, form_data);
147 break;
148 }
149 }
150 fields[NM_FLD_COUNT] = NULL;
151
152 nm_edit_vm_labels_setup();
153 nm_edit_vm_fields_setup(&cur_settings);
154 nm_fields_unset_status(fields);
155
156 form = nm_form_new(form_data, fields);
157 nm_form_post(form);
158
159 if (nm_form_draw(&form) != NM_OK)
160 goto out;
161
162 last_mac = nm_form_get_last_mac();
163
164 if (nm_edit_vm_get_data(&vm, &cur_settings) != NM_OK)
165 goto out;
166
167 nm_edit_vm_update_db(&vm, &cur_settings, last_mac);
168
169 out:
170 NM_FORM_EXIT();
171 nm_vm_free(&vm);
172 nm_form_free(form);
173 nm_form_data_free(form_data);
174 nm_fields_free(fields);
175 nm_vmctl_free_data(&cur_settings);
176 }
177
nm_edit_vm_fields_setup(const nm_vmctl_data_t * cur)178 static void nm_edit_vm_fields_setup(const nm_vmctl_data_t *cur)
179 {
180 nm_str_t buf = NM_INIT_STR;
181
182 field_opts_off(fields[NM_FLD_ARGS], O_STATIC);
183
184 set_field_buffer(fields[NM_FLD_CPUNUM], 0, nm_vect_str_ctx(&cur->main, NM_SQL_SMP));
185 set_field_buffer(fields[NM_FLD_RAMTOT], 0, nm_vect_str_ctx(&cur->main, NM_SQL_MEM));
186
187 if (nm_str_cmp_st(nm_vect_str(&cur->main, NM_SQL_KVM), NM_ENABLE) == NM_OK)
188 set_field_buffer(fields[NM_FLD_KVMFLG], 0, nm_form_yes_no[0]);
189 else
190 set_field_buffer(fields[NM_FLD_KVMFLG], 0, nm_form_yes_no[1]);
191
192 if (nm_str_cmp_st(nm_vect_str(&cur->main, NM_SQL_HCPU), NM_ENABLE) == NM_OK)
193 set_field_buffer(fields[NM_FLD_HOSCPU], 0, nm_form_yes_no[0]);
194 else
195 set_field_buffer(fields[NM_FLD_HOSCPU], 0, nm_form_yes_no[1]);
196
197 nm_str_format(&buf, "%zu", cur->ifs.n_memb / NM_IFS_IDX_COUNT);
198 set_field_buffer(fields[NM_FLD_IFSCNT], 0, buf.data);
199 set_field_buffer(fields[NM_FLD_DISKIN], 0, nm_vect_str_ctx(&cur->drives, NM_SQL_DRV_TYPE));
200 if (nm_str_cmp_st(nm_vect_str(&cur->drives, NM_SQL_DRV_DISC), NM_ENABLE) == NM_OK)
201 set_field_buffer(fields[NM_FLD_DISCARD], 0, nm_form_yes_no[0]);
202 else
203 set_field_buffer(fields[NM_FLD_DISCARD], 0, nm_form_yes_no[1]);
204
205 if (nm_str_cmp_st(nm_vect_str(&cur->main, NM_SQL_USBF), NM_ENABLE) == NM_OK)
206 set_field_buffer(fields[NM_FLD_USBUSE], 0, nm_form_yes_no[0]);
207 else
208 set_field_buffer(fields[NM_FLD_USBUSE], 0, nm_form_yes_no[1]);
209
210 set_field_buffer(fields[NM_FLD_USBTYP], 0, nm_vect_str_ctx(&cur->main, NM_SQL_USBT));
211 set_field_buffer(fields[NM_FLD_MACH], 0, nm_vect_str_ctx(&cur->main, NM_SQL_MACH));
212 set_field_buffer(fields[NM_FLD_ARGS], 0, nm_vect_str_ctx(&cur->main, NM_SQL_ARGS));
213 set_field_buffer(fields[NM_FLD_GROUP], 0, nm_vect_str_ctx(&cur->main, NM_SQL_GROUP));
214
215 #if defined (NM_OS_FREEBSD)
216 field_opts_off(fields[NM_FLD_USBUSE], O_ACTIVE);
217 #endif
218
219 nm_str_free(&buf);
220 }
221
nm_edit_vm_labels_setup()222 static size_t nm_edit_vm_labels_setup()
223 {
224 nm_str_t buf = NM_INIT_STR;
225 size_t max_label_len = 0;
226 size_t msg_len = 0;
227
228 for (size_t n = 0; n < NM_FLD_COUNT; n++) {
229 switch (n) {
230 case NM_LBL_CPUNUM:
231 nm_str_format(&buf, "%s", _(NM_LC_VM_FORM_CPU));
232 break;
233 case NM_LBL_RAMTOT:
234 nm_str_format(&buf, "%s%u%s",
235 _(NM_LC_VM_FORM_MEM_BEGIN), nm_hw_total_ram(), _(NM_LC_VM_FORM_MEM_END));
236 break;
237 case NM_LBL_KVMFLG:
238 nm_str_format(&buf, "%s", _(NM_LC_VM_FORM_KVM));
239 break;
240 case NM_LBL_HOSCPU:
241 nm_str_format(&buf, "%s", _(NM_LC_VM_FORM_HCPU));
242 break;
243 case NM_LBL_IFSCNT:
244 nm_str_format(&buf, "%s", _(NM_LC_VM_FORM_NET_IFS));
245 break;
246 case NM_LBL_DISKIN:
247 nm_str_format(&buf, "%s", _(NM_LC_VM_FORM_DRV_IF));
248 break;
249 case NM_LBL_DISCARD:
250 nm_str_format(&buf, "%s", _(NM_LC_VM_FORM_DRV_DIS));
251 break;
252 case NM_LBL_USBUSE:
253 nm_str_format(&buf, "%s", _(NM_LC_VM_FORM_USB));
254 break;
255 case NM_LBL_USBTYP:
256 nm_str_format(&buf, "%s", _(NM_LC_VM_FORM_USBT));
257 break;
258 case NM_LBL_MACH:
259 nm_str_format(&buf, "%s", _(NM_LC_VM_FORM_MACH));
260 break;
261 case NM_LBL_ARGS:
262 nm_str_format(&buf, "%s", _(NM_LC_VM_FORM_ARGS));
263 break;
264 case NM_LBL_GROUP:
265 nm_str_format(&buf, "%s", _(NM_LC_VM_FORM_GROUP));
266 break;
267 default:
268 continue;
269 }
270
271 msg_len = mbstowcs(NULL, buf.data, buf.len);
272 if (msg_len > max_label_len)
273 max_label_len = msg_len;
274
275 if (fields[n])
276 set_field_buffer(fields[n], 0, buf.data);
277 }
278
279 nm_str_free(&buf);
280 return max_label_len;
281 }
282
nm_edit_vm_get_data(nm_vm_t * vm,const nm_vmctl_data_t * cur)283 static int nm_edit_vm_get_data(nm_vm_t *vm, const nm_vmctl_data_t *cur)
284 {
285 int rc;
286 nm_vect_t err = NM_INIT_VECT;
287
288 nm_str_t ifs = NM_INIT_STR;
289 nm_str_t usb = NM_INIT_STR;
290 nm_str_t kvm = NM_INIT_STR;
291 nm_str_t hcpu = NM_INIT_STR;
292 nm_str_t discard = NM_INIT_STR;
293
294 nm_get_field_buf(fields[NM_FLD_CPUNUM], &vm->cpus);
295 nm_get_field_buf(fields[NM_FLD_RAMTOT], &vm->memo);
296 nm_get_field_buf(fields[NM_FLD_KVMFLG], &kvm);
297 nm_get_field_buf(fields[NM_FLD_HOSCPU], &hcpu);
298 nm_get_field_buf(fields[NM_FLD_IFSCNT], &ifs);
299 nm_get_field_buf(fields[NM_FLD_DISKIN], &vm->drive.driver);
300 nm_get_field_buf(fields[NM_FLD_DISCARD], &discard);
301 nm_get_field_buf(fields[NM_FLD_USBUSE], &usb);
302 nm_get_field_buf(fields[NM_FLD_USBTYP], &vm->usb_type);
303 nm_get_field_buf(fields[NM_FLD_MACH], &vm->mach);
304 nm_get_field_buf(fields[NM_FLD_ARGS], &vm->cmdappend);
305 nm_get_field_buf(fields[NM_FLD_GROUP], &vm->group);
306
307 if (field_status(fields[NM_FLD_CPUNUM]))
308 nm_form_check_data(_("CPU cores"), vm->cpus, err);
309 if (field_status(fields[NM_FLD_RAMTOT]))
310 nm_form_check_data(_("Memory"), vm->memo, err);
311 if (field_status(fields[NM_FLD_KVMFLG]))
312 nm_form_check_data(_("KVM"), kvm, err);
313 if (field_status(fields[NM_FLD_HOSCPU]))
314 nm_form_check_data(_("Host CPU"), kvm, err);
315 if (field_status(fields[NM_FLD_IFSCNT]))
316 nm_form_check_data(_("Network interfaces"), ifs, err);
317 if (field_status(fields[NM_FLD_DISKIN]))
318 nm_form_check_data(_("Disk interface"), vm->drive.driver, err);
319 if (field_status(fields[NM_FLD_DISCARD]))
320 nm_form_check_data(_("Discard mode"), discard, err);
321 if (field_status(fields[NM_FLD_USBUSE]))
322 nm_form_check_data(_("USB"), usb, err);
323 if (field_status(fields[NM_FLD_USBTYP]))
324 nm_form_check_data(_("USB version"), vm->usb_type, err);
325
326 if ((rc = nm_print_empty_fields(&err)) == NM_ERR)
327 goto out;
328
329 if (field_status(fields[NM_FLD_KVMFLG])) {
330 if (nm_str_cmp_st(&kvm, "yes") == NM_OK) {
331 vm->kvm.enable = 1;
332 } else {
333 if (!field_status(fields[NM_FLD_HOSCPU]) &&
334 (nm_str_cmp_st(nm_vect_str(&cur->main, NM_SQL_HCPU), NM_ENABLE) == NM_OK)) {
335 rc = NM_ERR;
336 NM_FORM_RESET();
337 nm_warn(_(NM_MSG_HCPU_KVM));
338 goto out;
339 }
340 }
341 }
342
343 if (field_status(fields[NM_FLD_DISCARD])) {
344 if (nm_str_cmp_st(&discard, "yes") == NM_OK) {
345 vm->drive.discard = 1;
346 }
347 }
348
349 if (field_status(fields[NM_FLD_HOSCPU])) {
350 if (nm_str_cmp_st(&hcpu, "yes") == NM_OK) {
351 if (((!vm->kvm.enable) && (field_status(fields[NM_FLD_KVMFLG]))) ||
352 ((nm_str_cmp_st(nm_vect_str(&cur->main, NM_SQL_KVM), NM_DISABLE) == NM_OK) &&
353 !field_status(fields[NM_FLD_KVMFLG]))) {
354 rc = NM_ERR;
355 NM_FORM_RESET();
356 nm_warn(_(NM_MSG_HCPU_KVM));
357 goto out;
358 }
359 vm->kvm.hostcpu_enable = 1;
360 }
361 }
362
363 if (field_status(fields[NM_FLD_IFSCNT]))
364 vm->ifs.count = nm_str_stoui(&ifs, 10);
365
366 if (field_status(fields[NM_FLD_USBUSE])) {
367 if (nm_str_cmp_st(&usb, "yes") == NM_OK)
368 vm->usb_enable = 1;
369 }
370
371 out:
372 nm_str_free(&ifs);
373 nm_str_free(&usb);
374 nm_str_free(&kvm);
375 nm_str_free(&hcpu);
376 nm_str_free(&discard);
377 nm_vect_free(&err, NULL);
378
379 return rc;
380 }
381
nm_edit_vm_update_db(nm_vm_t * vm,const nm_vmctl_data_t * cur,uint64_t mac)382 static void nm_edit_vm_update_db(nm_vm_t *vm, const nm_vmctl_data_t *cur, uint64_t mac)
383 {
384 nm_str_t query = NM_INIT_STR;
385
386 if (field_status(fields[NM_FLD_CPUNUM])) {
387 nm_str_format(&query, "UPDATE vms SET smp='%s' WHERE name='%s'",
388 vm->cpus.data, nm_vect_str_ctx(&cur->main, NM_SQL_NAME));
389 nm_db_edit(query.data);
390 }
391
392 if (field_status(fields[NM_FLD_RAMTOT])) {
393 nm_str_format(&query, "UPDATE vms SET mem='%s' WHERE name='%s'",
394 vm->memo.data, nm_vect_str_ctx(&cur->main, NM_SQL_NAME));
395 nm_db_edit(query.data);
396 }
397
398 if (field_status(fields[NM_FLD_KVMFLG])) {
399 nm_str_format(&query, "UPDATE vms SET kvm='%s' WHERE name='%s'",
400 vm->kvm.enable ? NM_ENABLE : NM_DISABLE,
401 nm_vect_str_ctx(&cur->main, NM_SQL_NAME));
402 nm_db_edit(query.data);
403 }
404
405 if (field_status(fields[NM_FLD_HOSCPU])) {
406 nm_str_format(&query, "UPDATE vms SET hcpu='%s' WHERE name='%s'",
407 vm->kvm.hostcpu_enable ? NM_ENABLE : NM_DISABLE,
408 nm_vect_str_ctx(&cur->main, NM_SQL_NAME));
409 nm_db_edit(query.data);
410 }
411
412 if (field_status(fields[NM_FLD_IFSCNT])) {
413 size_t cur_count = cur->ifs.n_memb / NM_IFS_IDX_COUNT;
414
415 if (vm->ifs.count < cur_count) {
416 for (; cur_count > vm->ifs.count; cur_count--) {
417 size_t idx_shift = NM_IFS_IDX_COUNT * (cur_count - 1);
418
419 nm_str_format(&query, NM_DEL_IFACE_SQL,
420 nm_vect_str_ctx(&cur->main, NM_SQL_NAME),
421 nm_vect_str_ctx(&cur->ifs, NM_SQL_IF_NAME + idx_shift));
422 nm_db_edit(query.data);
423 }
424 }
425
426 if (vm->ifs.count > cur_count) {
427 for (size_t n = cur_count; n < vm->ifs.count; n++) {
428 int altname;
429 nm_str_t if_name = NM_INIT_STR;
430 nm_str_t if_name_copy = NM_INIT_STR;
431 nm_str_t maddr = NM_INIT_STR;
432 mac++;
433
434 nm_net_mac_n2s(mac, &maddr);
435 nm_str_format(&if_name, "%s_eth%zu",
436 nm_vect_str_ctx(&cur->main, NM_SQL_NAME), n);
437 nm_str_copy(&if_name_copy, &if_name);
438
439 altname = nm_net_fix_tap_name(&if_name, &maddr);
440
441 nm_str_format(&query,
442 "INSERT INTO ifaces(vm_name, if_name, mac_addr, if_drv, vhost, macvtap, altname) "
443 "VALUES('%s', '%s', '%s', '%s', '%s', '%s', '%s')",
444 nm_vect_str_ctx(&cur->main, NM_SQL_NAME),
445 if_name.data,
446 maddr.data,
447 NM_DEFAULT_NETDRV,
448 #if defined (NM_OS_LINUX)
449 NM_ENABLE,
450 #else
451 NM_DISABLE,
452 #endif
453 NM_DISABLE,
454 (altname) ? if_name_copy.data : "");
455 nm_db_edit(query.data);
456
457 nm_str_free(&if_name);
458 nm_str_free(&if_name_copy);
459 nm_str_free(&maddr);
460 }
461 }
462 }
463
464 if (field_status(fields[NM_FLD_DISKIN])) {
465 nm_str_format(&query, "UPDATE drives SET drive_drv='%s' WHERE vm_name='%s'",
466 vm->drive.driver.data, nm_vect_str_ctx(&cur->main, NM_SQL_NAME));
467 nm_db_edit(query.data);
468 }
469
470 if (field_status(fields[NM_FLD_DISCARD])) {
471 nm_str_format(&query, "UPDATE drives SET discard='%s' WHERE vm_name='%s'",
472 vm->drive.discard ? NM_ENABLE : NM_DISABLE,
473 nm_vect_str_ctx(&cur->main, NM_SQL_NAME));
474 nm_db_edit(query.data);
475 }
476
477 if (field_status(fields[NM_FLD_USBUSE])) {
478 nm_str_format(&query, "UPDATE vms SET usb='%s' WHERE name='%s'",
479 vm->usb_enable ? NM_ENABLE : NM_DISABLE,
480 nm_vect_str_ctx(&cur->main, NM_SQL_NAME));
481 nm_db_edit(query.data);
482 }
483
484 if (field_status(fields[NM_FLD_USBTYP])) {
485 nm_str_format(&query, "UPDATE vms SET usb_type='%s' WHERE name='%s'",
486 vm->usb_type.data, nm_vect_str_ctx(&cur->main, NM_SQL_NAME));
487 nm_db_edit(query.data);
488 }
489
490 if (field_status(fields[NM_FLD_MACH])) {
491 nm_str_format(&query, "UPDATE vms SET machine='%s' WHERE name='%s'",
492 vm->mach.data, nm_vect_str_ctx(&cur->main, NM_SQL_NAME));
493 nm_db_edit(query.data);
494 }
495
496 if (field_status(fields[NM_FLD_ARGS])) {
497 nm_str_format(&query, "UPDATE vms SET cmdappend='%s' WHERE name='%s'",
498 vm->cmdappend.data, nm_vect_str_ctx(&cur->main, NM_SQL_NAME));
499 nm_db_edit(query.data);
500 }
501
502 if (field_status(fields[NM_FLD_GROUP])) {
503 nm_str_format(&query, "UPDATE vms SET team='%s' WHERE name='%s'",
504 vm->group.data, nm_vect_str_ctx(&cur->main, NM_SQL_NAME));
505 nm_db_edit(query.data);
506 if (nm_filter.type == NM_FILTER_GROUP) {
507 nm_filter.flags |= NM_FILTER_UPDATE;
508 }
509 }
510
511 nm_str_free(&query);
512 }
513
514 /* vim:set ts=4 sw=4: */
515