ad8210f0ac1ec109102e076e9626b24866390172
[petitboot] / ui / ncurses / nc-plugin.c
1 /*
2  *  Copyright (C) 2017 IBM Corporation
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; version 2 of the License.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17
18 #if defined(HAVE_CONFIG_H)
19 #include "config.h"
20 #endif
21
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <talloc/talloc.h>
27 #include <types/types.h>
28 #include <i18n/i18n.h>
29 #include <log/log.h>
30
31 #include "nc-cui.h"
32 #include "nc-plugin.h"
33 #include "nc-widgets.h"
34 #include "nc-menu.h"
35 #include "ui/common/discover-client.h"
36 #include "process/process.h"
37 #include "system/system.h"
38
39 #define N_FIELDS        15
40
41 extern const struct help_text plugin_help_text;
42
43 struct plugin_screen {
44         struct nc_scr           scr;
45         struct cui              *cui;
46         struct nc_widgetset     *widgetset;
47         WINDOW                  *pad;
48
49         bool                    exit;
50         bool                    show_help;
51         bool                    need_redraw;
52         void                    (*on_exit)(struct cui *);
53
54         int                     label_x;
55         int                     field_x;
56         int                     scroll_y;
57
58         const struct plugin_option      *opt;
59
60         struct {
61                 struct nc_widget_label          *id_l;
62                 struct nc_widget_label          *id_f;
63                 struct nc_widget_label          *name_l;
64                 struct nc_widget_label          *name_f;
65                 struct nc_widget_label          *vendor_l;
66                 struct nc_widget_label          *vendor_f;
67                 struct nc_widget_label          *vendor_id_l;
68                 struct nc_widget_label          *vendor_id_f;
69                 struct nc_widget_label          *version_l;
70                 struct nc_widget_label          *version_f;
71                 struct nc_widget_label          *date_l;
72                 struct nc_widget_label          *date_f;
73
74                 struct nc_widget_label          *commands_l;
75                 struct nc_widget_select         *commands_f;
76
77                 struct nc_widget_button         *run_b;
78         } widgets;
79 };
80
81 static void plugin_screen_draw(struct plugin_screen *screen,
82                 struct pmenu_item *item);
83
84 static struct plugin_screen *plugin_screen_from_scr(struct nc_scr *scr)
85 {
86         struct plugin_screen *plugin_screen;
87
88         assert(scr->sig == pb_plugin_screen_sig);
89         plugin_screen = (struct plugin_screen *)
90                 ((char *)scr - (size_t)&((struct plugin_screen *)0)->scr);
91         assert(plugin_screen->scr.sig == pb_plugin_screen_sig);
92         return plugin_screen;
93 }
94
95 struct nc_scr *plugin_screen_scr(struct plugin_screen *screen)
96 {
97         return &screen->scr;
98 }
99
100 static void pad_refresh(struct plugin_screen *screen)
101 {
102         int y, x, rows, cols;
103
104         getmaxyx(screen->scr.sub_ncw, rows, cols);
105         getbegyx(screen->scr.sub_ncw, y, x);
106
107         prefresh(screen->pad, screen->scroll_y, 0, y, x, rows, cols);
108 }
109
110 static void plugin_screen_widget_focus(struct nc_widget *widget, void *arg)
111 {
112         struct plugin_screen *screen = arg;
113         int w_y, w_height, w_focus, s_max, adjust;
114
115         w_height = widget_height(widget);
116         w_focus = widget_focus_y(widget);
117         w_y = widget_y(widget) + w_focus;
118         s_max = getmaxy(screen->scr.sub_ncw) - 1;
119
120         if (w_y < screen->scroll_y)
121                 screen->scroll_y = w_y;
122
123         else if (w_y + screen->scroll_y + 1 > s_max) {
124                 /* Fit as much of the widget into the screen as possible */
125                 adjust = min(s_max - 1, w_height - w_focus);
126                 if (w_y + adjust >= screen->scroll_y + s_max)
127                         screen->scroll_y = max(0, 1 + w_y + adjust - s_max);
128         } else
129                 return;
130
131         pad_refresh(screen);
132 }
133
134 static void plugin_screen_process_key(struct nc_scr *scr, int key)
135 {
136         struct plugin_screen *screen = plugin_screen_from_scr(scr);
137         bool handled;
138
139         handled = widgetset_process_key(screen->widgetset, key);
140
141         if (!handled) {
142                 pb_log("Not handled by widgetset\n");
143                 switch (key) {
144                 case 'x':
145                 case 27: /* esc */
146                         screen->exit = true;
147                         break;
148                 case 'h':
149                         screen->show_help = true;
150                         break;
151                 }
152         }
153
154         if (screen->exit) {
155                 screen->on_exit(screen->cui);
156
157         } else if (screen->show_help) {
158                 screen->show_help = false;
159                 screen->need_redraw = true;
160                 cui_show_help(screen->cui, _("Petitboot Plugin"),
161                                 &plugin_help_text);
162
163         } else if (handled) {
164                 pad_refresh(screen);
165         }
166 }
167
168 static int plugin_screen_post(struct nc_scr *scr)
169 {
170         struct plugin_screen *screen = plugin_screen_from_scr(scr);
171
172         widgetset_post(screen->widgetset);
173
174         nc_scr_frame_draw(scr);
175         if (screen->need_redraw) {
176                 redrawwin(scr->main_ncw);
177                 screen->need_redraw = false;
178         }
179         wrefresh(screen->scr.main_ncw);
180         pad_refresh(screen);
181         return 0;
182 }
183
184 static int plugin_screen_unpost(struct nc_scr *scr)
185 {
186         widgetset_unpost(plugin_screen_from_scr(scr)->widgetset);
187         return 0;
188 }
189
190 static void plugin_screen_resize(struct nc_scr *scr)
191 {
192         /* FIXME: forms can't be resized, need to recreate here */
193         plugin_screen_unpost(scr);
194         plugin_screen_post(scr);
195 }
196
197 static void plugin_run_command(void *arg)
198 {
199         struct plugin_screen *screen = arg;
200         char *cmd;
201         int i, result;
202
203         i = widget_select_get_value(screen->widgets.commands_f);
204         /* pb-plugin copies all executables to the wrapper directory */
205         cmd = talloc_asprintf(screen, "%s/%s", "/var/lib/pb-plugins/bin",
206                         basename(screen->opt->executables[i]));
207
208         if (!cmd) {
209                 pb_log("nc-plugin: plugin option has missing command %d\n", i);
210                 return;
211         }
212
213         const char *argv[] = {
214                 pb_system_apps.pb_exec,
215                 cmd,
216                 NULL
217         };
218
219         /* Drop our pad before running plugin */
220         delwin(screen->pad);
221         screen->pad = NULL;
222
223         result = cui_run_cmd(screen->cui, argv);
224
225         if (result)
226                 pb_log("Failed to run plugin command %s\n", cmd);
227         else
228                 nc_scr_status_printf(screen->cui->current, _("Finished: %s"), cmd);
229
230         plugin_screen_draw(screen, NULL);
231
232         talloc_free(cmd);
233 }
234
235 /* Call pb-plugin to install a plugin specified by plugin_file */
236 int plugin_install_plugin(struct pmenu_item *item)
237 {
238         struct cui *cui = cui_from_item(item);
239         struct cui_opt_data *cod = cod_from_item(item);
240         int rc;
241
242         assert(cui->current == &cui->plugin_menu->scr);
243
244         nc_scr_status_printf(cui->current, _("Installing plugin %s"),
245                         cod->pd->plugin_file);
246
247         rc = cui_send_plugin_install(cui, cod->pd->plugin_file);
248
249         if (rc) {
250                 pb_log("cui_send_plugin_install failed!\n");
251                 nc_scr_status_printf(cui->current,
252                                 _("Failed to send install request"));
253         } else
254                 pb_debug("cui_send_plugin_install sent!\n");
255
256         return rc;
257 }
258
259 static void plugin_screen_setup_widgets(struct plugin_screen *screen)
260 {
261         const struct plugin_option *opt = screen->opt;
262         struct nc_widgetset *set = screen->widgetset;
263         unsigned int i;
264
265         build_assert(sizeof(screen->widgets) / sizeof(struct widget *)
266                         == N_FIELDS);
267
268         screen->widgets.id_l = widget_new_label(set, 0, 0, _("ID:"));
269         screen->widgets.id_f = widget_new_label(set, 0, 0, opt->id);
270         screen->widgets.name_l = widget_new_label(set, 0, 0, _("Name:"));
271         screen->widgets.name_f = widget_new_label(set, 0, 0, opt->name);
272         screen->widgets.vendor_l = widget_new_label(set, 0, 0, _("Vendor:"));
273         screen->widgets.vendor_f = widget_new_label(set, 0, 0, opt->vendor);
274         screen->widgets.vendor_id_l = widget_new_label(set, 0, 0,
275                                                        _("Vendor ID:"));
276         screen->widgets.vendor_id_f = widget_new_label(set, 0, 0,
277                                                         opt->vendor_id);
278         screen->widgets.version_l = widget_new_label(set, 0, 0, _("Version:"));
279         screen->widgets.version_f = widget_new_label(set, 0, 0,
280                                                         opt->version);
281         screen->widgets.date_l = widget_new_label(set, 0, 0, _("Date"));
282         screen->widgets.date_f = widget_new_label(set, 0, 0, opt->date);
283
284         screen->widgets.commands_l = widget_new_label(set, 0, 0,
285                                                          _("Commands:"));
286         screen->widgets.commands_f = widget_new_select(set, 0, 0,
287                         COLS - screen->field_x - 1);
288         for (i = 0; i < opt->n_executables; i++) {
289                 widget_select_add_option(screen->widgets.commands_f, i,
290                                 basename(opt->executables[i]), i == 0);
291         }
292
293         screen->widgets.run_b = widget_new_button(set, 0, 0, 30,
294                         _("Run selected command"), plugin_run_command, screen);
295 }
296
297 static int layout_pair(struct plugin_screen *screen, int y,
298                 struct nc_widget_label *label,
299                 struct nc_widget *field)
300 {
301         struct nc_widget *label_w = widget_label_base(label);
302         widget_move(label_w, y, screen->label_x);
303         widget_move(field, y, screen->field_x);
304         return max(widget_height(label_w), widget_height(field));
305 }
306
307 static void plugin_screen_layout_widgets(struct plugin_screen *screen)
308 {
309         unsigned int y;
310
311         /* list of details (static) */
312
313         y = 1;
314
315         layout_pair(screen, y++, screen->widgets.id_l,
316                         widget_label_base(screen->widgets.id_f));
317         layout_pair(screen, y++, screen->widgets.name_l,
318                         widget_label_base(screen->widgets.name_f));
319         layout_pair(screen, y++, screen->widgets.vendor_l,
320                         widget_label_base(screen->widgets.vendor_f));
321         layout_pair(screen, y++, screen->widgets.vendor_id_l,
322                         widget_label_base(screen->widgets.vendor_id_f));
323         layout_pair(screen, y++, screen->widgets.version_l,
324                         widget_label_base(screen->widgets.version_f));
325         layout_pair(screen, y++, screen->widgets.date_l,
326                         widget_label_base(screen->widgets.date_f));
327
328         y += 1;
329
330         /* available commands */
331         widget_move(widget_label_base(screen->widgets.commands_l), y,
332                     screen->label_x);
333         widget_move(widget_select_base(screen->widgets.commands_f), y,
334                         screen->field_x);
335
336         y += 2;
337
338         widget_move(widget_button_base(screen->widgets.run_b), y++, screen->field_x);
339
340 }
341
342 static void plugin_screen_draw(struct plugin_screen *screen,
343                 struct pmenu_item *item)
344 {
345         struct cui_opt_data *cod;
346         int height = N_FIELDS * 2;
347         bool repost = false;
348
349         if (item) {
350                 /* First init or update */
351                 cod = cod_from_item(item);
352                 screen->opt = cod->pd->opt;
353         }
354
355         if (!screen->pad || getmaxy(screen->pad) < height) {
356                 if (screen->pad)
357                         delwin(screen->pad);
358                 screen->pad = newpad(height, COLS);
359         }
360
361         if (screen->widgetset) {
362                 widgetset_unpost(screen->widgetset);
363                 talloc_free(screen->widgetset);
364                 repost = true;
365         }
366
367         screen->widgetset = widgetset_create(screen, screen->scr.main_ncw,
368                         screen->pad);
369         widgetset_set_widget_focus(screen->widgetset,
370                         plugin_screen_widget_focus, screen);
371
372         plugin_screen_setup_widgets(screen);
373         plugin_screen_layout_widgets(screen);
374
375         if (repost)
376                 widgetset_post(screen->widgetset);
377 }
378
379 static int plugin_screen_destroy(void *arg)
380 {
381         struct plugin_screen *screen = arg;
382         if (screen->pad)
383                 delwin(screen->pad);
384         return 0;
385 }
386
387 struct plugin_screen *plugin_screen_init(struct cui *cui,
388                 struct pmenu_item *item,
389                 void (*on_exit)(struct cui *))
390 {
391         struct plugin_screen *screen = talloc_zero(cui, struct plugin_screen);
392         talloc_set_destructor(screen, plugin_screen_destroy);
393
394         nc_scr_init(&screen->scr, pb_plugin_screen_sig, 0,
395                         cui, plugin_screen_process_key,
396                         plugin_screen_post, plugin_screen_unpost,
397                         plugin_screen_resize);
398
399
400         screen->cui = cui;
401         screen->on_exit = on_exit;
402         screen->label_x = 2;
403         screen->field_x = 25;
404
405         screen->scr.frame.ltitle = talloc_strdup(screen,
406                         _("Petitboot Plugin"));
407         screen->scr.frame.rtitle = NULL;
408         screen->scr.frame.help = talloc_strdup(screen,
409                         _("tab=next, shift+tab=previous, x=exit, h=help"));
410         nc_scr_frame_draw(&screen->scr);
411
412         scrollok(screen->scr.sub_ncw, true);
413
414         plugin_screen_draw(screen, item);
415
416         return screen;
417 }
418