1 #include <nm_core.h>
2 #include <nm_form.h>
3 #include <nm_utils.h>
4 #include <nm_string.h>
5 #include <nm_window.h>
6 #include <nm_ncurses.h>
7 #include <nm_database.h>
8 #include <nm_cfg_file.h>
9 #include <nm_usb_plug.h>
10 #include <nm_stat_usage.h>
11 
12 static float nm_window_scale = 0.7;
13 
14 static void nm_init_window__(nm_window_t *w, const char *msg);
15 static void nm_print_help_lines(const char **msg, size_t objs, int err);
16 static void nm_print_help__(const char **keys, const char **values,
17                             size_t hotkey_num, size_t maxlen);
18 
19 #if defined (NM_OS_LINUX)
20   #if defined (NM_WITH_OVF_SUPPORT)
21     #define NM_HELP_MSG \
22       "q:Quit", "I:Install VM", "O:Import OVA", "A:Import image", "N:Network", "?:Help"
23   #else
24     #define NM_HELP_MSG \
25       "q:Quit", "I:Install VM", "A:Import image", "N:Network", "?:Help"
26   #endif
27 #else
28   #if defined (NM_WITH_OVF_SUPPORT)
29     #define NM_HELP_MSG \
30       "q:Quit", "I:Install VM", "O:Import OVA", "A:Import image", "?:Help"
31   #else
32     #define NM_HELP_MSG \
33       "q:Quit", "I:Install VM", "A:Import image", "?:Help"
34   #endif
35 #endif
36 
37 #if defined (NM_WITH_NETWORK_MAP)
38   #define NM_LAN_MSG \
39     "q:Back", "e:Export SVG map", "?:Help"
40 #else
41   #define NM_LAN_MSG \
42     "q:Back", "?:Help"
43 #endif
44 
45 #define X_NM_HELP_GEN                         \
46     X(main, NM_HELP_MSG)                      \
47     X(lan, NM_LAN_MSG)                        \
48     X(edit, "esc:Cancel", "enter:Save")       \
49     X(import, "esc:Cancel", "enter:Import")   \
50     X(install, "esc:Cancel", "enter:Install") \
51     X(iface, "q:Back", "enter:Edit")          \
52     X(clone, "esc:Cancel", "enter:Clone")     \
53     X(export, "esc:Cancel", "enter:Export")   \
54     X(delete, "q:Back", "enter:Delete")
55 
56 #define X(name, ...)                                         \
57     void nm_init_help_ ## name(void) {                       \
58         const char *msg[] = { __VA_ARGS__ };                 \
59         nm_print_help_lines(msg, nm_arr_len(msg), NM_FALSE); \
60     }
61 X_NM_HELP_GEN
62 #undef X
63 
nm_create_windows(void)64 void nm_create_windows(void)
65 {
66     int action_cols, screen_x, screen_y;
67     nm_cord_t help_size;
68     nm_cord_t side_size;
69     nm_cord_t action_size;
70 
71     getmaxyx(stdscr, screen_y, screen_x);
72     action_cols = screen_x * nm_window_scale;
73 
74     help_size = NM_SET_POS(1, screen_x, 0, 0);
75     side_size = NM_SET_POS(screen_y - 1, screen_x - action_cols, 0, 1);
76     action_size = NM_SET_POS(screen_y - 1, action_cols, screen_x - action_cols, 1);
77 
78     help_window = nm_init_window(&help_size);
79     side_window = nm_init_window(&side_size);
80     action_window = nm_init_window(&action_size);
81 
82     help_panel = new_panel(help_window);
83     side_panel = new_panel(side_window);
84     action_panel = new_panel(action_window);
85 }
86 
nm_init_help(const char * msg,int err)87 void nm_init_help(const char *msg, int err)
88 {
89     nm_print_help_lines(&msg, 1, err);
90 }
91 
nm_print_help_lines(const char ** msg,size_t objs,int err)92 static void nm_print_help_lines(const char **msg, size_t objs, int err)
93 {
94     if (!msg)
95         return;
96 
97     int x = 1, y = 0;
98     int use_glyph = nm_cfg_get()->glyphs.separator;
99 
100     wbkgd(help_window, COLOR_PAIR(err ? NM_COLOR_RED : NM_COLOR_BLACK));
101 
102     for (size_t n = 0; n < objs; n++) {
103         if (n > 0) {
104             x++;
105             if (use_glyph) {
106                 mvwprintw(help_window, y, x, NM_GLYPH_SEP);
107             } else {
108                 mvwaddch(help_window, y, x, ACS_VLINE);
109             }
110             x+=2;
111         }
112 
113         mvwprintw(help_window, y, x, _(msg[n]));
114         x += mbstowcs(NULL, _(msg[n]), strlen(_(msg[n])));
115 
116         if (use_glyph && (n == objs - 1)) {
117             x++;
118             mvwprintw(help_window, y, x, NM_GLYPH_SEP);
119         }
120     }
121 
122     wrefresh(help_window);
123 }
124 
nm_init_side(void)125 void nm_init_side(void)
126 {
127     nm_str_t title = NM_INIT_STR;
128 
129     wattroff(side_window, COLOR_PAIR(NM_COLOR_HIGHLIGHT));
130 
131     if (nm_filter.type == NM_FILTER_GROUP) {
132         nm_str_format(&title, "VM list [%s]", nm_filter.query.data);
133         nm_init_window__(side_window, _(title.data));
134         nm_str_free(&title);
135     } else {
136         nm_init_window__(side_window, _("VM list"));
137     }
138 
139     wtimeout(side_window, 500);
140 }
141 
nm_init_side_lan(void)142 void nm_init_side_lan(void)
143 {
144     wattroff(side_window, COLOR_PAIR(NM_COLOR_HIGHLIGHT));
145     nm_init_window__(side_window, _("veth list"));
146     wtimeout(side_window, -1);
147 }
148 
nm_init_side_if_list(void)149 void nm_init_side_if_list(void)
150 {
151     wattroff(side_window, COLOR_PAIR(NM_COLOR_HIGHLIGHT));
152     nm_init_window__(side_window, _("Iface list"));
153     wtimeout(side_window, -1);
154 }
155 
nm_init_side_drives(void)156 void nm_init_side_drives(void)
157 {
158     wattroff(side_window, COLOR_PAIR(NM_COLOR_HIGHLIGHT));
159     nm_init_window__(side_window, _("Drive list"));
160     wtimeout(side_window, -1);
161 }
162 
nm_init_action(const char * msg)163 void nm_init_action(const char *msg)
164 {
165     nm_init_window__(action_window, msg ? msg : _("Properties"));
166 }
167 
nm_init_window__(nm_window_t * w,const char * msg)168 static void nm_init_window__(nm_window_t *w, const char *msg)
169 {
170     int cols = getmaxx(w);
171     size_t mb_len = mbstowcs(NULL, msg, strlen(msg));
172 
173     box(w, 0, 0);
174     mvwprintw(w, 1, (cols - mb_len) / 2, msg);
175     mvwaddch(w, 2, 0, ACS_LTEE);
176     mvwhline(w, 2, 1, ACS_HLINE, cols - 2);
177     mvwaddch(w, 2, cols - 1, ACS_RTEE);
178     wrefresh(w);
179 }
180 
nm_destroy_windows(void)181 void nm_destroy_windows(void)
182 {
183     del_panel(help_panel);
184     del_panel(side_panel);
185     del_panel(action_panel);
186 
187     delwin(help_window);
188     delwin(side_window);
189     delwin(action_window);
190 
191     help_window = NULL;
192     side_window = NULL;
193     action_window = NULL;
194 }
195 
nm_print_cmd(const nm_str_t * name)196 void nm_print_cmd(const nm_str_t *name)
197 {
198     nm_str_t buf = NM_INIT_STR;
199     nm_vect_t argv = NM_INIT_VECT;
200     nm_vect_t res = NM_INIT_VECT;
201     nm_vmctl_data_t vm = NM_VMCTL_INIT_DATA;
202     int off, start = 3, flags = 0;
203 
204     int col = getmaxx(stdscr);
205     flags |= NM_VMCTL_INFO;
206 
207     nm_vmctl_get_data(name, &vm);
208 
209     nm_vmctl_gen_cmd(&argv, &vm, name, &flags, NULL, NULL);
210 
211     /* pre checking */
212     for (size_t n = 0; n < argv.n_memb; n++) {
213         off = strlen((char *) nm_vect_at(&argv, n));
214         off += 2; /* count ' \' */
215 
216         if (off >= col) {
217             nm_str_format(&buf, "%s", _("window to small"));
218             nm_clear_screen();
219             mvprintw(1, (col - name->len) / 2, "%s", name->data);
220             mvprintw(3, 0, "%s", buf.data);
221             goto out;
222         }
223     }
224 
225     for (size_t n = 0; n < argv.n_memb; n++) {
226         const char *arg = nm_vect_at(&argv, n);
227         off = buf.len;
228         off += strlen(arg);
229         off += 2;
230 
231         if (off > col) {
232             nm_str_t tmp = NM_INIT_STR;
233             nm_str_append_format(&tmp, "%s\\", buf.data);
234             nm_vect_insert_cstr(&res, tmp.data);
235             nm_str_trunc(&buf, 0);
236             if (n + 1 != argv.n_memb) {
237                 nm_str_append_format(&buf, "%s ", arg);
238             } else {
239                 nm_vect_insert_cstr(&res, arg);
240             }
241             nm_str_free(&tmp);
242         } else if (off <= col && n + 1 < argv.n_memb) {
243             nm_str_append_format(&buf, "%s ", arg);
244         } else {
245             nm_str_append_format(&buf, "%s ", arg);
246             nm_vect_insert_cstr(&res, buf.data);
247         }
248     }
249 
250     nm_clear_screen();
251     mvprintw(1, (col - name->len) / 2, "%s", name->data);
252 
253     for (size_t n = 0; n < res.n_memb; n++) {
254         mvprintw(start + n, 0, "%s", (char *) nm_vect_at(&res, n));
255     }
256 
257 out:
258     nm_str_free(&buf);
259     nm_vect_free(&argv, NULL);
260     nm_vect_free(&res, NULL);
261     nm_vmctl_free_data(&vm);
262 
263     refresh();
264     getch();
265 }
266 
nm_print_snapshots(const nm_vect_t * v)267 void nm_print_snapshots(const nm_vect_t *v)
268 {
269     nm_str_t buf = NM_INIT_STR;
270     size_t count = v->n_memb / 5;
271     size_t y = 7, x = 2;
272     size_t cols, rows;
273     chtype ch1, ch2;
274     ch1 = ch2 = 0;
275 
276     getmaxyx(action_window, rows, cols);
277 
278     enum {
279         NM_SQL_VMSNAP_NAME = 2,
280         NM_SQL_VMSNAP_TIME = 4
281     };
282 
283     for (size_t n = 0; n < count; n++) {
284         size_t idx_shift = 5 * n;
285 
286         if (n && n < count) {
287             ch1 = (n != (count - 1)) ? ACS_LTEE : ACS_LLCORNER;
288             ch2 = ACS_HLINE;
289         }
290 
291         nm_str_format(&buf, "%s (%s)",
292                 nm_vect_str_ctx(v, NM_SQL_VMSNAP_NAME + idx_shift),
293                 nm_vect_str_ctx(v, NM_SQL_VMSNAP_TIME + idx_shift));
294         NM_PR_VM_INFO();
295     }
296 
297     nm_str_free(&buf);
298 }
299 
nm_print_drive_info(const nm_vect_t * v,size_t idx)300 void nm_print_drive_info(const nm_vect_t *v, size_t idx)
301 {
302     if (!idx)
303         return;
304 
305     nm_str_t buf = NM_INIT_STR;
306     size_t y = 3, x = 2;
307     size_t cols, rows;
308     size_t idx_shift;
309     chtype ch1, ch2;
310     ch1 = ch2 = 0;
311 
312     idx_shift = 2 * (--idx);
313 
314     getmaxyx(action_window, rows, cols);
315 
316     nm_str_format(&buf, "%-12s%sGb", "capacity: ",
317             nm_vect_str_ctx(v, 1 + idx_shift));
318     NM_PR_VM_INFO();
319 
320     nm_str_free(&buf);
321 }
322 
nm_print_iface_info(const nm_vmctl_data_t * vm,size_t idx)323 void nm_print_iface_info(const nm_vmctl_data_t *vm, size_t idx)
324 {
325     if (!idx)
326         return;
327 
328     nm_str_t buf = NM_INIT_STR;
329     size_t y = 3, x = 2;
330     size_t cols, rows;
331     size_t idx_shift, mvtap_idx = 0;
332     chtype ch1, ch2;
333     ch1 = ch2 = 0;
334 
335     idx_shift = NM_IFS_IDX_COUNT * (--idx);
336 
337     getmaxyx(action_window, rows, cols);
338 
339     if (nm_str_cmp_st(nm_vect_str(&vm->ifs, NM_SQL_IF_USR + idx_shift),
340                 NM_ENABLE) == NM_OK) {
341         nm_str_format(&buf, "%-12s%s", "User mode: ", "enabled");
342         NM_PR_VM_INFO();
343 
344         if (nm_vect_str_len(&vm->ifs, NM_SQL_IF_FWD + idx_shift) != 0) {
345             nm_str_format(&buf, "%-12s%s", "hostfwd: ",
346                     nm_vect_str_ctx(&vm->ifs, NM_SQL_IF_FWD + idx_shift));
347             NM_PR_VM_INFO();
348         }
349         if (nm_vect_str_len(&vm->ifs, NM_SQL_IF_SMB + idx_shift) != 0) {
350             nm_str_format(&buf, "%-12s%s", "smb: ",
351                     nm_vect_str_ctx(&vm->ifs, NM_SQL_IF_SMB + idx_shift));
352             NM_PR_VM_INFO();
353         }
354 
355         goto out;
356     }
357 
358     nm_str_format(&buf, "%-12s%s", "hwaddr: ",
359             nm_vect_str_ctx(&vm->ifs, NM_SQL_IF_MAC + idx_shift));
360     NM_PR_VM_INFO();
361 
362     nm_str_format(&buf, "%-12s%s", "driver: ",
363             nm_vect_str_ctx(&vm->ifs, NM_SQL_IF_DRV + idx_shift));
364     NM_PR_VM_INFO();
365 
366     if (nm_vect_str_len(&vm->ifs, NM_SQL_IF_IP4 + idx_shift) > 0) {
367         nm_str_format(&buf, "%-12s%s", "host addr: ",
368                 nm_vect_str_ctx(&vm->ifs, NM_SQL_IF_IP4 + idx_shift));
369         NM_PR_VM_INFO();
370     }
371 
372     nm_str_format(&buf, "%-12s%s", "vhost: ",
373             (nm_str_cmp_st(nm_vect_str(&vm->ifs, NM_SQL_IF_VHO + idx_shift),
374                            NM_ENABLE) == NM_OK) ? "yes" : "no");
375     NM_PR_VM_INFO();
376 
377     mvtap_idx = nm_str_stoui(nm_vect_str(&vm->ifs, NM_SQL_IF_MVT + idx_shift), 10);
378     if (!mvtap_idx)
379         nm_str_format(&buf, "%-12s%s", "MacVTap: ", nm_form_macvtap[mvtap_idx]);
380     else
381         nm_str_format(&buf, "%-12s%s [iface: %s]", "MacVTap: ", nm_form_macvtap[mvtap_idx],
382             nm_vect_str_ctx(&vm->ifs, NM_SQL_IF_PET + idx_shift));
383     NM_PR_VM_INFO();
384 out:
385     nm_str_free(&buf);
386 }
387 
nm_print_vm_info(const nm_str_t * name,const nm_vmctl_data_t * vm,int status)388 void nm_print_vm_info(const nm_str_t *name, const nm_vmctl_data_t *vm, int status)
389 {
390     nm_str_t buf = NM_INIT_STR;
391     size_t y = 3, x = 2;
392     size_t cols, rows;
393     size_t ifs_count, drives_count;
394     chtype ch1, ch2;
395     nm_cpu_t cpu = NM_INIT_CPU;
396     ch1 = ch2 = 0;
397 
398     static const nm_str_t *name_ = NULL;
399     static const nm_vmctl_data_t *vm_ = NULL;
400     static int status_ = 0;
401 
402     if (name && vm) {
403         name_ = name;
404         vm_ = vm;
405         status_ = status;
406     }
407 
408     if (!name_ || !vm_)
409         return;
410 
411     getmaxyx(action_window, rows, cols);
412 
413     nm_str_format(&buf, "%-12s%s", "arch: ",
414         nm_vect_str_ctx(&vm_->main, NM_SQL_ARCH));
415     NM_PR_VM_INFO();
416 
417     nm_parse_smp(&cpu, nm_vect_str_ctx(&vm_->main, NM_SQL_SMP));
418     nm_str_format(&buf, "%-12s%zu %s (%zu %s), threads %zu", "cpu: ",
419             (cpu.sockets) ? cpu.sockets : cpu.smp,
420             (cpu.sockets > 1) ? "cpus" : "cpu",
421             (cpu.cores) ? cpu.cores : 1,
422             (cpu.cores > 1) ? "cores" : "core",
423             cpu.smp);
424     NM_PR_VM_INFO();
425 
426     nm_str_format(&buf, "%-12s%s %s", "memory: ",
427         nm_vect_str_ctx(&vm_->main, NM_SQL_MEM), "Mb");
428     NM_PR_VM_INFO();
429 
430     if (nm_str_cmp_st(nm_vect_str(&vm_->main, NM_SQL_KVM), NM_ENABLE) == NM_OK) {
431         if (nm_str_cmp_st(nm_vect_str(&vm_->main, NM_SQL_HCPU), NM_ENABLE) == NM_OK)
432             nm_str_format(&buf, "%-12s%s", "kvm: ", "enabled [+hostcpu]");
433         else
434             nm_str_format(&buf, "%-12s%s", "kvm: ", "enabled");
435     } else {
436         nm_str_format(&buf, "%-12s%s", "kvm: ", "disabled");
437     }
438     NM_PR_VM_INFO();
439 
440     if (nm_str_cmp_st(nm_vect_str(&vm_->main, NM_SQL_USBF), "1") == NM_OK) {
441         nm_str_format(&buf, "%-12s%s [%s]", "usb: ", "enabled",
442                 nm_vect_str_ctx(&vm_->main, NM_SQL_USBT));
443     } else {
444         nm_str_format(&buf, "%-12s%s", "usb: ", "disabled");
445     }
446     NM_PR_VM_INFO();
447 
448     {
449         nm_vect_t usb_names = NM_INIT_VECT;
450 
451         nm_usb_unplug_list(&vm_->usb, &usb_names, false);
452 
453         for (size_t n = 0; n < usb_names.n_memb; n++) {
454             ch1 = (n != (usb_names.n_memb - 1)) ? ACS_LTEE : ACS_LLCORNER;
455             ch2 = ACS_HLINE;
456             nm_str_format(&buf, "%s", (char *) usb_names.data[n]);
457             NM_PR_VM_INFO();
458         }
459 
460         nm_vect_free(&usb_names, NULL);
461         ch1 = ch2 = 0;
462     }
463 
464     nm_str_format(&buf, "%-12s%s [%u]", "vnc port: ",
465              nm_vect_str_ctx(&vm_->main, NM_SQL_VNC),
466              nm_str_stoui(nm_vect_str(&vm_->main, NM_SQL_VNC), 10) + NM_STARTING_VNC_PORT);
467     NM_PR_VM_INFO();
468 
469     /* print network interfaces info */
470     ifs_count = vm_->ifs.n_memb / NM_IFS_IDX_COUNT;
471 
472     for (size_t n = 0; n < ifs_count; n++) {
473         size_t idx_shift = NM_IFS_IDX_COUNT * n;
474         if (nm_str_cmp_st(nm_vect_str(&vm_->ifs, NM_SQL_IF_USR + idx_shift),
475                     NM_ENABLE) == NM_OK) {
476             nm_str_format(&buf, "eth%zu%-8s%s [user mode]",
477                     n, ":",
478                     nm_vect_str_ctx(&vm_->ifs, NM_SQL_IF_NAME + idx_shift));
479         } else {
480             nm_str_format(&buf, "eth%zu%-8s%s [%s %s%s]",
481                     n, ":",
482                     nm_vect_str_ctx(&vm_->ifs, NM_SQL_IF_NAME + idx_shift),
483                     nm_vect_str_ctx(&vm_->ifs, NM_SQL_IF_MAC + idx_shift),
484                     nm_vect_str_ctx(&vm_->ifs, NM_SQL_IF_DRV + idx_shift),
485                     (nm_str_cmp_st(nm_vect_str(&vm_->ifs, NM_SQL_IF_VHO + idx_shift),
486                                    NM_ENABLE) == NM_OK) ? "+vhost" : "");
487         }
488 
489         NM_PR_VM_INFO();
490     }
491 
492     /* print drives info */
493     drives_count = vm_->drives.n_memb / NM_DRV_IDX_COUNT;
494 
495     for (size_t n = 0; n < drives_count; n++) {
496         size_t idx_shift = NM_DRV_IDX_COUNT * n;
497         int boot = 0;
498 
499         if (nm_str_cmp_st(nm_vect_str(&vm_->drives, NM_SQL_DRV_BOOT + idx_shift),
500                 NM_ENABLE) == NM_OK) {
501             boot = 1;
502         }
503 
504         nm_str_format(&buf, "disk%zu%-7s%s [%sGb %s discard=%s] %s", n, ":",
505                  nm_vect_str_ctx(&vm_->drives, NM_SQL_DRV_NAME + idx_shift),
506                  nm_vect_str_ctx(&vm_->drives, NM_SQL_DRV_SIZE + idx_shift),
507                  nm_vect_str_ctx(&vm_->drives, NM_SQL_DRV_TYPE + idx_shift),
508                  (nm_str_cmp_st(nm_vect_str(&vm_->drives, NM_SQL_DRV_DISC + idx_shift),
509                                 NM_ENABLE) == NM_OK) ? "on" : "off",
510                  boot ? "*" : "");
511         NM_PR_VM_INFO();
512     }
513 
514     /* print 9pfs info */
515     if (nm_str_cmp_st(nm_vect_str(&vm_->main, NM_SQL_9FLG), "1") == NM_OK) {
516         nm_str_format(&buf, "%-12s%s [%s]", "9pfs: ",
517                  nm_vect_str_ctx(&vm_->main, NM_SQL_9PTH),
518                  nm_vect_str_ctx(&vm_->main, NM_SQL_9ID));
519         NM_PR_VM_INFO();
520     }
521 
522     /* generate guest boot settings info */
523     if (nm_vect_str_len(&vm_->main, NM_SQL_MACH)) {
524         nm_str_format(&buf, "%-12s%s", "machine: ",
525                 nm_vect_str_ctx(&vm_->main, NM_SQL_MACH));
526         NM_PR_VM_INFO();
527     }
528     if (nm_vect_str_len(&vm_->main, NM_SQL_BIOS)) {
529         nm_str_format(&buf,"%-12s%s", "bios: ",
530                 nm_vect_str_ctx(&vm_->main, NM_SQL_BIOS));
531         NM_PR_VM_INFO();
532     }
533     if (nm_vect_str_len(&vm_->main, NM_SQL_KERN)) {
534         nm_str_format(&buf,"%-12s%s", "kernel: ",
535                 nm_vect_str_ctx(&vm_->main, NM_SQL_KERN));
536         NM_PR_VM_INFO();
537     }
538     if (nm_vect_str_len(&vm_->main, NM_SQL_KAPP)) {
539         nm_str_format(&buf, "%-12s%s", "cmdline: ",
540                 nm_vect_str_ctx(&vm_->main, NM_SQL_KAPP));
541         NM_PR_VM_INFO();
542     }
543     if (nm_vect_str_len(&vm_->main, NM_SQL_INIT)) {
544         nm_str_format(&buf, "%-12s%s", "initrd: ",
545                 nm_vect_str_ctx(&vm_->main, NM_SQL_INIT));
546         NM_PR_VM_INFO();
547     }
548     if (nm_vect_str_len(&vm_->main, NM_SQL_TTY)) {
549         nm_str_format(&buf, "%-12s%s", "tty: ",
550                 nm_vect_str_ctx(&vm_->main, NM_SQL_TTY));
551         NM_PR_VM_INFO();
552     }
553     if (nm_vect_str_len(&vm_->main, NM_SQL_SOCK)) {
554         nm_str_format(&buf, "%-12s%s", "socket: ",
555                 nm_vect_str_ctx(&vm_->main, NM_SQL_SOCK));
556         NM_PR_VM_INFO();
557     }
558     if (nm_vect_str_len(&vm_->main, NM_SQL_DEBP)) {
559         nm_str_format(&buf, "%-12s%s", "gdb port: ",
560                 nm_vect_str_ctx(&vm_->main, NM_SQL_DEBP));
561         NM_PR_VM_INFO();
562     }
563     if (nm_str_cmp_st(nm_vect_str(&vm_->main, NM_SQL_DEBF), NM_ENABLE) == NM_OK) {
564         nm_str_format(&buf, "%-12s", "freeze cpu: yes");
565         NM_PR_VM_INFO();
566     }
567     if (nm_vect_str_len(&vm_->main, NM_SQL_ARGS)) {
568         nm_str_format(&buf,"%-12s%s", "extra args: ",
569                 nm_vect_str_ctx(&vm_->main, NM_SQL_ARGS));
570         NM_PR_VM_INFO();
571     }
572     if (nm_vect_str_len(&vm_->main, NM_SQL_GROUP)) {
573         nm_str_format(&buf,"%-12s%s", "group: ",
574                 nm_vect_str_ctx(&vm_->main, NM_SQL_GROUP));
575         NM_PR_VM_INFO();
576     }
577 
578     /* print host IP addresses for TAP ints */
579     for (size_t n = 0; n < ifs_count; n++) {
580         size_t idx_shift = NM_IFS_IDX_COUNT * n;
581 
582         if (!nm_vect_str_len(&vm_->ifs, NM_SQL_IF_IP4 + idx_shift))
583             continue;
584 
585         nm_str_format(&buf, "%-12s%s [%s]", "host IP: ",
586             nm_vect_str_ctx(&vm_->ifs, NM_SQL_IF_NAME + idx_shift),
587             nm_vect_str_ctx(&vm_->ifs, NM_SQL_IF_IP4 + idx_shift));
588         NM_PR_VM_INFO();
589 
590     }
591 
592     /* print PID */
593     {
594         int fd;
595         nm_str_t pid_path = NM_INIT_STR;
596 
597         nm_str_format(&pid_path, "%s/%s/%s",
598             nm_cfg_get()->vm_dir.data, name_->data, NM_VM_PID_FILE);
599 
600         if ((status_ && (fd = open(pid_path.data, O_RDONLY)) != -1)) {
601             char pid[10];
602             ssize_t nread;
603             int pid_num = 0;
604 
605             if ((nread = read(fd, pid, sizeof(pid))) > 0) {
606                 pid[nread - 1] = '\0';
607                 pid_num = atoi(pid);
608 
609                 nm_str_format(&buf, "%-12s%d", "pid: ", pid_num);
610                 NM_PR_VM_INFO();
611             }
612             close(fd);
613 
614 #if defined (NM_OS_LINUX)
615             if (pid_num) {
616                 double usage = nm_stat_get_usage(pid_num);
617                 nm_str_format(&buf, "%-12s%0.1f%%", "cpu usage: ", usage);
618                 mvwhline(action_window, y, 1, ' ', cols - 4);
619                 NM_PR_VM_INFO();
620             }
621 #else
622             (void) pid_num;
623 #endif
624         } else { /* clear PID file info and cpu usage data */
625             if (y < (rows - 2)) {
626                 mvwhline(action_window, y, 1, ' ', cols - 4);
627                 mvwhline(action_window, y + 1, 1, ' ', cols - 4);
628             }
629             NM_STAT_CLEAN();
630         }
631 
632         nm_str_free(&pid_path);
633     }
634 
635     nm_str_free(&buf);
636 }
637 
nm_lan_help(void)638 void nm_lan_help(void)
639 {
640     const char *keys[] = {
641         "a", "r", "u", "d"
642     };
643 
644     const char *values[] = {
645         "add veth interface",
646         "remove veth interface",
647         "up veth interface",
648         "down veth interface",
649         NULL
650     };
651 
652     size_t hotkey_num = nm_arr_len(keys);
653     size_t maxlen = nm_max_msg_len(values);
654 
655     nm_print_help__(keys, values, hotkey_num, maxlen);
656 }
657 
nm_print_help(void)658 void nm_print_help(void)
659 {
660     const char *keys[] = {
661         "r", "t",
662 #if defined(NM_WITH_VNC_CLIENT) || defined(NM_WITH_SPICE)
663         "c",
664 #endif
665         "p", "z", "f", "d", "y", "e",
666         "i", "C", "a", "l", "b", "h",
667         "m", "v", "u", "P", "R", "S",
668         "X", "D",
669 #if defined (NM_OS_LINUX)
670         "+", "-",
671 #endif
672         "k", "/"
673 };
674 
675     const char *values[] = {
676         "start vm",
677         "start vm in temporary mode",
678 #if defined(NM_WITH_VNC_CLIENT) || defined(NM_WITH_SPICE)
679         "connect to vm",
680 #endif
681         "powerdown vm",
682         "reset vm",
683         "force stop vm",
684         "delete vm",
685         "rename vm",
686         "edit vm settings",
687         "edit network settings",
688         "edit viewer settings",
689         "add virtual disk",
690         "clone vm",
691         "edit boot settings",
692         "share host filesystem",
693         "show command",
694         "delete virtual disk",
695         "delete unused tap interfaces",
696         "pause vm",
697         "resume vm",
698         "take vm snapshot",
699         "revert vm snapshot",
700         "delete vm snapshot",
701 #if defined (NM_OS_LINUX)
702         "attach usb device",
703         "detach usb device",
704 #endif
705         "kill vm process",
706         "search vm, filters",
707         NULL
708     };
709 
710     size_t hotkey_num = nm_arr_len(keys);
711     size_t maxlen = nm_max_msg_len(values);
712 
713     nm_print_help__(keys, values, hotkey_num, maxlen);
714 }
715 
716 static void
nm_print_help__(const char ** keys,const char ** values,size_t hotkey_num,size_t maxlen)717 nm_print_help__(const char **keys, const char **values,
718                 size_t hotkey_num, size_t maxlen)
719 {
720     size_t cols, rows;
721     size_t n = 0;
722     int perc;
723     nm_str_t help_title = NM_INIT_STR;
724 
725     getmaxyx(action_window, rows, cols);
726     rows -= 4;
727     cols -= 2;
728 
729     if (maxlen + 10 > cols) {
730         nm_warn(_(NM_MSG_SMALL_WIN));
731         return;
732     }
733 
734     perc = (hotkey_num > rows) ? ((rows * 100) / hotkey_num) : 100;
735     if (perc == 100)
736         nm_str_format(&help_title, _("nEMU %s - help [all]"), NM_VERSION);
737     else
738         nm_str_format(&help_title, _("nEMU %s - help [%d%%]") , NM_VERSION, perc);
739 
740     werase(action_window);
741     nm_init_action(help_title.data);
742 
743     for (size_t y = 3; n < rows && n < hotkey_num; n++, y++)
744         mvwprintw(action_window, y, 2, "%-10s%s", keys[n], values[n]);
745 
746     if (perc != 100) {
747         size_t shift = 0;
748 
749         for (;;) {
750             int ch = wgetch(action_window);
751 
752             if (ch == NM_KEY_ENTER) {
753                 size_t last = 0;
754 
755                 shift++;
756                 n = shift;
757                 werase(action_window);
758 
759                 for (size_t y = 3, l = 0; l < rows && n < hotkey_num; n++, y++, l++)
760                     mvwprintw(action_window, y, 2, "%-10s%s", keys[n], values[n]);
761 
762                 last = n;
763                 if (last < hotkey_num) {
764                     perc = 100 * last / hotkey_num;
765                     nm_str_format(&help_title, _("nEMU %s - help [%d%%]") , NM_VERSION, perc);
766                 } else {
767                     nm_str_format(&help_title, _("nEMU %s - help [end]"), NM_VERSION);
768                 }
769 
770                 nm_init_action(help_title.data);
771 
772                 if (last >= hotkey_num) {
773                     wgetch(action_window);
774                     break;
775                 }
776             } else {
777                 break;
778             }
779         }
780     } else {
781         wgetch(action_window);
782     }
783 
784     wrefresh(action_window);
785     nm_init_action(NULL);
786     nm_str_free(&help_title);
787 }
788 
nm_align2line(nm_str_t * str,size_t line_len)789 void nm_align2line(nm_str_t *str, size_t line_len)
790 {
791     if (line_len <= 4)
792         return;
793 
794     if (str->len > (line_len - 4)) {
795         nm_str_trunc(str, line_len - 7);
796         nm_str_add_text(str, "...");
797     }
798 }
799 
nm_max_msg_len(const char ** msg)800 size_t nm_max_msg_len(const char **msg)
801 {
802     if (!msg)
803         return 0;
804 
805     size_t len = 0;
806 
807     while (*msg) {
808         size_t mb_len = mbstowcs(NULL, _(*msg), strlen(_(*msg)));
809         len = (mb_len > len) ? mb_len : len;
810         msg++;
811     }
812 
813     return len;
814 }
815 
nm_warn__(const char * msg,int red)816 static int nm_warn__(const char *msg, int red)
817 {
818     if (!msg)
819         return ERR;
820 
821     int ch;
822 
823     werase(help_window);
824     nm_init_help(msg, red);
825     while ((ch = wgetch(help_window)) == KEY_RESIZE);
826     werase(help_window);
827     nm_init_help_main();
828 
829     return ch;
830 }
831 
nm_warn(const char * msg)832 int nm_warn(const char *msg)
833 {
834     return nm_warn__(msg, NM_TRUE);
835 }
836 
nm_notify(const char * msg)837 int nm_notify(const char *msg)
838 {
839     return nm_warn__(msg, NM_FALSE);
840 }
841 
nm_window_scale_inc(void)842 int nm_window_scale_inc(void)
843 {
844     if (nm_window_scale > 0.8)
845         return NM_ERR;
846 
847     nm_window_scale += 0.02;
848 
849     return NM_OK;
850 }
851 
nm_window_scale_dec(void)852 int nm_window_scale_dec(void)
853 {
854     if (nm_window_scale < 0.2)
855         return NM_ERR;
856 
857     nm_window_scale -= 0.02;
858 
859     return NM_OK;
860 }
861 
862 /* vim:set ts=4 sw=4: */
863