ui/ncurses: Add nc-auth and authenticate when required.
[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 static void plugin_run_command(void *arg);
44
45 struct plugin_screen {
46         struct nc_scr           scr;
47         struct cui              *cui;
48         struct nc_widgetset     *widgetset;
49         WINDOW                  *pad;
50
51         bool                    exit;
52         bool                    show_help;
53         bool                    show_auth_run;
54         bool                    need_redraw;
55         void                    (*on_exit)(struct cui *);
56
57         int                     label_x;
58         int                     field_x;
59         int                     scroll_y;
60
61         const struct plugin_option      *opt;
62
63         struct {
64                 struct nc_widget_label          *id_l;
65                 struct nc_widget_label          *id_f;
66                 struct nc_widget_label          *name_l;
67                 struct nc_widget_label          *name_f;
68                 struct nc_widget_label          *vendor_l;
69                 struct nc_widget_label          *vendor_f;
70                 struct nc_widget_label          *vendor_id_l;
71                 struct nc_widget_label          *vendor_id_f;
72                 struct nc_widget_label          *version_l;
73                 struct nc_widget_label          *version_f;
74                 struct nc_widget_label          *date_l;
75                 struct nc_widget_label          *date_f;
76
77                 struct nc_widget_label          *commands_l;
78                 struct nc_widget_select         *commands_f;
79
80                 struct nc_widget_button         *run_b;
81         } widgets;
82 };
83
84 static void plugin_screen_draw(struct plugin_screen *screen,
85                 struct pmenu_item *item);
86
87 static struct plugin_screen *plugin_screen_from_scr(struct nc_scr *scr)
88 {
89         struct plugin_screen *plugin_screen;
90
91         assert(scr->sig == pb_plugin_screen_sig);
92         plugin_screen = (struct plugin_screen *)
93                 ((char *)scr - (size_t)&((struct plugin_screen *)0)->scr);
94         assert(plugin_screen->scr.sig == pb_plugin_screen_sig);
95         return plugin_screen;
96 }
97
98 struct nc_scr *plugin_screen_scr(struct plugin_screen *screen)
99 {
100         return &screen->scr;
101 }
102
103 static void pad_refresh(struct plugin_screen *screen)
104 {
105         int y, x, rows, cols;
106
107         getmaxyx(screen->scr.sub_ncw, rows, cols);
108         getbegyx(screen->scr.sub_ncw, y, x);
109
110         prefresh(screen->pad, screen->scroll_y, 0, y, x, rows, cols);
111 }
112
113 static void plugin_screen_widget_focus(struct nc_widget *widget, void *arg)
114 {
115         struct plugin_screen *screen = arg;
116         int w_y, w_height, w_focus, s_max, adjust;
117
118         w_height = widget_height(widget);
119         w_focus = widget_focus_y(widget);
120         w_y = widget_y(widget) + w_focus;
121         s_max = getmaxy(screen->scr.sub_ncw) - 1;
122
123         if (w_y < screen->scroll_y)
124                 screen->scroll_y = w_y;
125
126         else if (w_y + screen->scroll_y + 1 > s_max) {
127                 /* Fit as much of the widget into the screen as possible */
128                 adjust = min(s_max - 1, w_height - w_focus);
129                 if (w_y + adjust >= screen->scroll_y + s_max)
130                         screen->scroll_y = max(0, 1 + w_y + adjust - s_max);
131         } else
132                 return;
133
134         pad_refresh(screen);
135 }
136
137 static void plugin_screen_process_key(struct nc_scr *scr, int key)
138 {
139         struct plugin_screen *screen = plugin_screen_from_scr(scr);
140         bool handled;
141
142         handled = widgetset_process_key(screen->widgetset, key);
143
144         if (!handled) {
145                 pb_log("Not handled by widgetset\n");
146                 switch (key) {
147                 case 'x':
148                 case 27: /* esc */
149                         screen->exit = true;
150                         break;
151                 case 'h':
152                         screen->show_help = true;
153                         break;
154                 }
155         }
156
157         if (screen->exit) {
158                 screen->on_exit(screen->cui);
159
160         } else if (screen->show_help) {
161                 screen->show_help = false;
162                 screen->need_redraw = true;
163                 cui_show_help(screen->cui, _("Petitboot Plugin"),
164                                 &plugin_help_text);
165
166         } else if (handled && (screen->cui->current == scr)) {
167                 pad_refresh(screen);
168         }
169 }
170
171 static int plugin_screen_post(struct nc_scr *scr)
172 {
173         struct plugin_screen *screen = plugin_screen_from_scr(scr);
174
175         widgetset_post(screen->widgetset);
176
177         nc_scr_frame_draw(scr);
178         if (screen->need_redraw) {
179                 redrawwin(scr->main_ncw);
180                 screen->need_redraw = false;
181         }
182         wrefresh(screen->scr.main_ncw);
183         pad_refresh(screen);
184
185         if (screen->show_auth_run) {
186                 screen->show_auth_run = false;
187                 plugin_run_command(screen);
188         }
189
190         return 0;
191 }
192
193 static int plugin_screen_unpost(struct nc_scr *scr)
194 {
195         widgetset_unpost(plugin_screen_from_scr(scr)->widgetset);
196         return 0;
197 }
198
199 static void plugin_screen_resize(struct nc_scr *scr)
200 {
201         /* FIXME: forms can't be resized, need to recreate here */
202         plugin_screen_unpost(scr);
203         plugin_screen_post(scr);
204 }
205
206 static void plugin_run_command(void *arg)
207 {
208         struct plugin_screen *screen = arg;
209         char *cmd;
210         int i, result;
211
212         i = widget_select_get_value(screen->widgets.commands_f);
213         /* pb-plugin copies all executables to the wrapper directory */
214         cmd = talloc_asprintf(screen, "%s/%s", "/var/lib/pb-plugins/bin",
215                         basename(screen->opt->executables[i]));
216
217         if (!cmd) {
218                 pb_log("nc-plugin: plugin option has missing command %d\n", i);
219                 return;
220         }
221
222         const char *argv[] = {
223                 pb_system_apps.pb_exec,
224                 cmd,
225                 NULL
226         };
227
228         /* Drop our pad before running plugin */
229         delwin(screen->pad);
230         screen->pad = NULL;
231
232         result = cui_run_cmd(screen->cui, argv);
233
234         if (result)
235                 pb_log("Failed to run plugin command %s\n", cmd);
236         else
237                 nc_scr_status_printf(screen->cui->current, _("Finished: %s"), cmd);
238
239         plugin_screen_draw(screen, NULL);
240
241         talloc_free(cmd);
242 }
243
244 static void plugin_run_command_check(void *arg)
245 {
246         struct plugin_screen *screen = arg;
247
248         if (discover_client_authenticated(screen->cui->client)) {
249                 plugin_run_command(screen);
250                 return;
251         }
252
253         /*
254          * Don't supply a callback as we want to handle running the command
255          * from the plugin screen.
256          */
257         screen->show_auth_run = true;
258         cui_show_auth(screen->cui, screen->scr.main_ncw, false, NULL);
259 }
260
261 static void plugin_screen_setup_widgets(struct plugin_screen *screen)
262 {
263         const struct plugin_option *opt = screen->opt;
264         struct nc_widgetset *set = screen->widgetset;
265         unsigned int i;
266
267         build_assert(sizeof(screen->widgets) / sizeof(struct widget *)
268                         == N_FIELDS);
269
270         screen->widgets.id_l = widget_new_label(set, 0, 0, _("ID:"));
271         screen->widgets.id_f = widget_new_label(set, 0, 0, opt->id);
272         screen->widgets.name_l = widget_new_label(set, 0, 0, _("Name:"));
273         screen->widgets.name_f = widget_new_label(set, 0, 0, opt->name);
274         screen->widgets.vendor_l = widget_new_label(set, 0, 0, _("Vendor:"));
275         screen->widgets.vendor_f = widget_new_label(set, 0, 0, opt->vendor);
276         screen->widgets.vendor_id_l = widget_new_label(set, 0, 0,
277                                                        _("Vendor ID:"));
278         screen->widgets.vendor_id_f = widget_new_label(set, 0, 0,
279                                                         opt->vendor_id);
280         screen->widgets.version_l = widget_new_label(set, 0, 0, _("Version:"));
281         screen->widgets.version_f = widget_new_label(set, 0, 0,
282                                                         opt->version);
283         screen->widgets.date_l = widget_new_label(set, 0, 0, _("Date"));
284         screen->widgets.date_f = widget_new_label(set, 0, 0, opt->date);
285
286         screen->widgets.commands_l = widget_new_label(set, 0, 0,
287                                                          _("Commands:"));
288         screen->widgets.commands_f = widget_new_select(set, 0, 0,
289                         COLS - screen->field_x - 1);
290         for (i = 0; i < opt->n_executables; i++) {
291                 widget_select_add_option(screen->widgets.commands_f, i,
292                                 basename(opt->executables[i]), i == 0);
293         }
294
295         screen->widgets.run_b = widget_new_button(set, 0, 0, 30,
296                         _("Run selected command"), plugin_run_command_check, screen);
297 }
298
299 static int layout_pair(struct plugin_screen *screen, int y,
300                 struct nc_widget_label *label,
301                 struct nc_widget *field)
302 {
303         struct nc_widget *label_w = widget_label_base(label);
304         widget_move(label_w, y, screen->label_x);
305         widget_move(field, y, screen->field_x);
306         return max(widget_height(label_w), widget_height(field));
307 }
308
309 static void plugin_screen_layout_widgets(struct plugin_screen *screen)
310 {
311         unsigned int y;
312
313         /* list of details (static) */
314
315         y = 1;
316
317         layout_pair(screen, y++, screen->widgets.id_l,
318                         widget_label_base(screen->widgets.id_f));
319         layout_pair(screen, y++, screen->widgets.name_l,
320                         widget_label_base(screen->widgets.name_f));
321         layout_pair(screen, y++, screen->widgets.vendor_l,
322                         widget_label_base(screen->widgets.vendor_f));
323         layout_pair(screen, y++, screen->widgets.vendor_id_l,
324                         widget_label_base(screen->widgets.vendor_id_f));
325         layout_pair(screen, y++, screen->widgets.version_l,
326                         widget_label_base(screen->widgets.version_f));
327         layout_pair(screen, y++, screen->widgets.date_l,
328                         widget_label_base(screen->widgets.date_f));
329
330         y += 1;
331
332         /* available commands */
333         widget_move(widget_label_base(screen->widgets.commands_l), y,
334                     screen->label_x);
335         widget_move(widget_select_base(screen->widgets.commands_f), y,
336                         screen->field_x);
337
338         y += 2;
339
340         widget_move(widget_button_base(screen->widgets.run_b), y++, screen->field_x);
341
342 }
343
344 static void plugin_screen_draw(struct plugin_screen *screen,
345                 struct pmenu_item *item)
346 {
347         struct cui_opt_data *cod;
348         int height = N_FIELDS * 2;
349         bool repost = false;
350
351         if (item) {
352                 /* First init or update */
353                 cod = cod_from_item(item);
354                 screen->opt = cod->pd->opt;
355         }
356
357         if (!screen->pad || getmaxy(screen->pad) < height) {
358                 if (screen->pad)
359                         delwin(screen->pad);
360                 screen->pad = newpad(height, COLS);
361         }
362
363         if (screen->widgetset) {
364                 widgetset_unpost(screen->widgetset);
365                 talloc_free(screen->widgetset);
366                 repost = true;
367         }
368
369         screen->widgetset = widgetset_create(screen, screen->scr.main_ncw,
370                         screen->pad);
371         widgetset_set_widget_focus(screen->widgetset,
372                         plugin_screen_widget_focus, screen);
373
374         plugin_screen_setup_widgets(screen);
375         plugin_screen_layout_widgets(screen);
376
377         if (repost)
378                 widgetset_post(screen->widgetset);
379 }
380
381 static int plugin_screen_destroy(void *arg)
382 {
383         struct plugin_screen *screen = arg;
384         if (screen->pad)
385                 delwin(screen->pad);
386         return 0;
387 }
388
389 struct plugin_screen *plugin_screen_init(struct cui *cui,
390                 struct pmenu_item *item,
391                 void (*on_exit)(struct cui *))
392 {
393         struct plugin_screen *screen = talloc_zero(cui, struct plugin_screen);
394         talloc_set_destructor(screen, plugin_screen_destroy);
395
396         nc_scr_init(&screen->scr, pb_plugin_screen_sig, 0,
397                         cui, plugin_screen_process_key,
398                         plugin_screen_post, plugin_screen_unpost,
399                         plugin_screen_resize);
400
401
402         screen->cui = cui;
403         screen->on_exit = on_exit;
404         screen->label_x = 2;
405         screen->field_x = 25;
406
407         screen->scr.frame.ltitle = talloc_strdup(screen,
408                         _("Petitboot Plugin"));
409         screen->scr.frame.rtitle = NULL;
410         screen->scr.frame.help = talloc_strdup(screen,
411                         _("tab=next, shift+tab=previous, x=exit, h=help"));
412         nc_scr_frame_draw(&screen->scr);
413
414         scrollok(screen->scr.sub_ncw, true);
415
416         plugin_screen_draw(screen, item);
417
418         return screen;
419 }
420