]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * amtterm -- Intel AMT serial-over-lan client, gtk version. | |
3 | * | |
4 | * Copyright (C) 2007 Gerd Hoffmann <kraxel@redhat.com | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along | |
17 | * with this program; if not, write to the Free Software Foundation, Inc., | |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
19 | */ | |
20 | ||
21 | #include <stdio.h> | |
22 | #include <stdlib.h> | |
23 | #include <unistd.h> | |
24 | #include <string.h> | |
25 | #include <errno.h> | |
26 | #include <fcntl.h> | |
27 | #include <locale.h> | |
28 | #include <signal.h> | |
29 | #include <ctype.h> | |
30 | ||
31 | #include <gdk/gdk.h> | |
32 | #include <gdk/gdkx.h> | |
33 | #include <gtk/gtk.h> | |
34 | #include <vte/vte.h> | |
35 | ||
36 | #include "parseconfig.h" | |
37 | #include "redir.h" | |
38 | ||
39 | #define APPNAME "gamt" | |
40 | ||
41 | struct gamt_window { | |
42 | /* gtk stuff */ | |
43 | GtkActionGroup *ag; | |
44 | GtkUIManager *ui; | |
45 | GtkWidget *win; | |
46 | GtkWidget *vte; | |
47 | GtkWidget *status; | |
48 | GtkWidget *icon; | |
49 | ||
50 | GtkActionGroup *hosts_ag; | |
51 | guint hosts_id; | |
52 | ||
53 | /* sol stuff */ | |
54 | struct redir redir; | |
55 | GIOChannel *ch; | |
56 | guint id; | |
57 | }; | |
58 | ||
59 | static const char *state_stock[] = { | |
60 | [ REDIR_NONE ] = GTK_STOCK_DISCONNECT, | |
61 | #if 0 | |
62 | [ REDIR_CONNECT ] = GTK_STOCK_, | |
63 | [ REDIR_INIT ] = GTK_STOCK_, | |
64 | [ REDIR_AUTH ] = GTK_STOCK_, | |
65 | [ REDIR_INIT_SOL ] = GTK_STOCK_, | |
66 | #endif | |
67 | [ REDIR_RUN_SOL ] = GTK_STOCK_CONNECT, | |
68 | #if 0 | |
69 | [ REDIR_INIT_IDER ] = GTK_STOCK_, | |
70 | [ REDIR_RUN_IDER ] = GTK_STOCK_, | |
71 | [ REDIR_CLOSING ] = GTK_STOCK_, | |
72 | #endif | |
73 | [ REDIR_CLOSED ] = GTK_STOCK_DISCONNECT, | |
74 | [ REDIR_ERROR ] = GTK_STOCK_DISCONNECT, | |
75 | }; | |
76 | ||
77 | static char amt_host[64]; | |
78 | static char amt_port[16]; | |
79 | static char amt_user[32] = "admin"; | |
80 | static char amt_pass[32]; | |
81 | static int amt_trace; | |
82 | static int amt_debug; | |
83 | ||
84 | static int gamt_getstring(GtkWidget *window, char *title, char *message, | |
85 | char *dest, int dlen, int hide); | |
86 | static int gamt_connect(struct gamt_window *gamt); | |
87 | static void gamt_rebuild_hosts(struct gamt_window *gamt); | |
88 | ||
89 | /* ------------------------------------------------------------------ */ | |
90 | ||
91 | #define CFG_SECTION "config", "config" | |
92 | #define CFG_FONT CFG_SECTION, "font" | |
93 | #define CFG_FOREGROUND CFG_SECTION, "foreground" | |
94 | #define CFG_BACKGROUND CFG_SECTION, "background" | |
95 | #define CFG_BLINK CFG_SECTION, "blink-cursor" | |
96 | ||
97 | /* ------------------------------------------------------------------ */ | |
98 | ||
99 | static void menu_cb_connect(GtkAction *action, void *data) | |
100 | { | |
101 | struct gamt_window *gamt = data; | |
102 | int rc; | |
103 | ||
104 | if (gamt->redir.state != REDIR_NONE && | |
105 | gamt->redir.state != REDIR_CLOSED && | |
106 | gamt->redir.state != REDIR_ERROR) | |
107 | /* already have an active connection */ | |
108 | return; | |
109 | ||
110 | rc = gamt_getstring(gamt->win, "Connecting", | |
111 | "Connect to host ?", | |
112 | amt_host, sizeof(amt_host), 0); | |
113 | if (0 != rc) | |
114 | return; | |
115 | ||
116 | gamt_connect(gamt); | |
117 | } | |
118 | ||
119 | static void menu_cb_connect_to(GtkAction *action, void *data) | |
120 | { | |
121 | struct gamt_window *gamt = data; | |
122 | ||
123 | if (gamt->redir.state != REDIR_NONE && | |
124 | gamt->redir.state != REDIR_CLOSED && | |
125 | gamt->redir.state != REDIR_ERROR) | |
126 | /* already have an active connection */ | |
127 | return; | |
128 | ||
129 | if (1 != sscanf(gtk_action_get_name(action), "ConnectTo_%s", amt_host)) | |
130 | return; | |
131 | gamt_connect(gamt); | |
132 | } | |
133 | ||
134 | static void menu_cb_disconnect(GtkAction *action, void *data) | |
135 | { | |
136 | struct gamt_window *gamt = data; | |
137 | ||
138 | if (gamt->redir.state != REDIR_RUN_SOL) | |
139 | return; | |
140 | redir_sol_stop(&gamt->redir); | |
141 | } | |
142 | ||
143 | static void menu_cb_config_font(GtkAction *action, void *data) | |
144 | { | |
145 | struct gamt_window *gamt = data; | |
146 | GtkWidget *dialog; | |
147 | char *fontname; | |
148 | ||
149 | dialog = gtk_font_selection_dialog_new("Terminal font"); | |
150 | fontname = cfg_get_str(CFG_FONT); | |
151 | gtk_font_selection_dialog_set_font_name | |
152 | (GTK_FONT_SELECTION_DIALOG(dialog), fontname); | |
153 | ||
154 | gtk_widget_show_all(dialog); | |
155 | switch (gtk_dialog_run(GTK_DIALOG(dialog))) { | |
156 | case GTK_RESPONSE_OK: | |
157 | fontname = gtk_font_selection_dialog_get_font_name | |
158 | (GTK_FONT_SELECTION_DIALOG(dialog)); | |
159 | vte_terminal_set_font_from_string(VTE_TERMINAL(gamt->vte), fontname); | |
160 | cfg_set_str(CFG_FONT, fontname); | |
161 | break; | |
162 | } | |
163 | gtk_widget_destroy(dialog); | |
164 | } | |
165 | ||
166 | static int pickcolor(char *title, GdkColor *color) | |
167 | { | |
168 | GtkWidget *dialog; | |
169 | GtkColorSelection *csel; | |
170 | int rc = -1; | |
171 | ||
172 | dialog = gtk_color_selection_dialog_new(title); | |
173 | csel = GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(dialog)->colorsel); | |
174 | gtk_color_selection_set_has_opacity_control(csel, FALSE); | |
175 | gtk_color_selection_set_current_color(csel, color); | |
176 | ||
177 | gtk_widget_show_all(dialog); | |
178 | switch (gtk_dialog_run(GTK_DIALOG(dialog))) { | |
179 | case GTK_RESPONSE_OK: | |
180 | gtk_color_selection_get_current_color(csel, color); | |
181 | rc = 0; | |
182 | } | |
183 | gtk_widget_destroy(dialog); | |
184 | return rc; | |
185 | } | |
186 | ||
187 | static void menu_cb_config_fg(GtkAction *action, void *data) | |
188 | { | |
189 | struct gamt_window *gamt = data; | |
190 | GdkColor color = {0,0,0,0}; | |
191 | char name[16]; | |
192 | ||
193 | gdk_color_parse(cfg_get_str(CFG_FOREGROUND), &color); | |
194 | if (0 != pickcolor("Text color", &color)) | |
195 | return; | |
196 | vte_terminal_set_color_foreground(VTE_TERMINAL(gamt->vte), &color); | |
197 | snprintf(name, sizeof(name), "#%04x%04x%04x", | |
198 | color.red, color.green, color.blue); | |
199 | cfg_set_str(CFG_FOREGROUND, name); | |
200 | } | |
201 | ||
202 | static void menu_cb_config_bg(GtkAction *action, void *data) | |
203 | { | |
204 | struct gamt_window *gamt = data; | |
205 | GdkColor color = {0,0,0,0}; | |
206 | char name[16]; | |
207 | ||
208 | gdk_color_parse(cfg_get_str(CFG_BACKGROUND), &color); | |
209 | if (0 != pickcolor("Background color", &color)) | |
210 | return; | |
211 | vte_terminal_set_color_background(VTE_TERMINAL(gamt->vte), &color); | |
212 | snprintf(name, sizeof(name), "#%04x%04x%04x", | |
213 | color.red, color.green, color.blue); | |
214 | cfg_set_str(CFG_BACKGROUND, name); | |
215 | } | |
216 | ||
217 | static void menu_cb_blink_cursor(GtkToggleAction *action, gpointer user_data) | |
218 | { | |
219 | struct gamt_window *gamt = user_data; | |
220 | gboolean state = gtk_toggle_action_get_active(action); | |
221 | ||
222 | if (amt_debug) | |
223 | fprintf(stderr, "%s: %s\n", __FUNCTION__, state ? "on" : "off"); | |
224 | cfg_set_bool(CFG_BLINK, state); | |
225 | vte_terminal_set_cursor_blinks(VTE_TERMINAL(gamt->vte), state); | |
226 | } | |
227 | ||
228 | static void menu_cb_quit(GtkAction *action, void *data) | |
229 | { | |
230 | struct gamt_window *gamt = data; | |
231 | ||
232 | gtk_widget_destroy(gamt->win); | |
233 | } | |
234 | ||
235 | static void show_manpage(char *page, char *section) | |
236 | { | |
237 | char buf[64]; | |
238 | ||
239 | ||
240 | switch(fork()) { | |
241 | case -1: | |
242 | perror("fork"); | |
243 | break; | |
244 | case 0: | |
245 | /* child: try xdg-open first ... */ | |
246 | snprintf(buf, sizeof(buf), "man:%s(%s)", page, section); | |
247 | execlp("xdg-open", "xdg-open", buf, NULL); | |
248 | perror("execlp(xdg-open)"); | |
249 | /* ... fallback is an xterm with man */ | |
250 | snprintf(buf, sizeof(buf), "manual page: %s(%s)", page, section); | |
251 | execlp("xterm", "xterm", | |
252 | "-title", buf, | |
253 | "-e", "man", section, page, | |
254 | NULL); | |
255 | perror("execlp(xterm)"); | |
256 | exit(1); | |
257 | break; | |
258 | default: | |
259 | /* parent */ | |
260 | break; | |
261 | } | |
262 | } | |
263 | ||
264 | static void menu_cb_man_gamt(GtkAction *action, void *data) | |
265 | { | |
266 | show_manpage("gamt", "1"); | |
267 | } | |
268 | ||
269 | static void menu_cb_man_amt_howto(GtkAction *action, void *data) | |
270 | { | |
271 | show_manpage("amt-howto", "7"); | |
272 | } | |
273 | ||
274 | static void menu_cb_about(GtkAction *action, void *data) | |
275 | { | |
276 | static char *comments = "Intel AMT serial-over-lan client"; | |
277 | static char *copyright = "(c) 2007 Gerd Hoffmann"; | |
278 | static char *website = "http://dl.bytesex.org/releases/amtterm/"; | |
279 | static char *authors[] = { "Gerd Hoffmann <kraxel@redhat.com>", NULL }; | |
280 | struct gamt_window *gamt = data; | |
281 | ||
282 | gtk_show_about_dialog(GTK_WINDOW(gamt->win), | |
283 | "authors", authors, | |
284 | "comments", comments, | |
285 | "copyright", copyright, | |
286 | "logo-icon-name", GTK_STOCK_ABOUT, | |
287 | "version", VERSION, | |
288 | "website", website, | |
289 | // "license", "GPLv2+", | |
290 | NULL); | |
291 | } | |
292 | ||
293 | static void destroy_cb(GtkWidget *widget, gpointer data) | |
294 | { | |
295 | struct gamt_window *gamt = data; | |
296 | ||
297 | gtk_main_quit(); | |
298 | free(gamt); | |
299 | } | |
300 | ||
301 | /* ------------------------------------------------------------------ */ | |
302 | ||
303 | static int recv_gtk(void *cb_data, unsigned char *buf, int len) | |
304 | { | |
305 | struct gamt_window *gamt = cb_data; | |
306 | vte_terminal_feed(VTE_TERMINAL(gamt->vte), buf, len); | |
307 | return 0; | |
308 | } | |
309 | ||
310 | static void state_gtk(void *cb_data, enum redir_state old, enum redir_state new) | |
311 | { | |
312 | struct gamt_window *gamt = cb_data; | |
313 | unsigned char buf[128]; | |
314 | int last; | |
315 | ||
316 | switch (new) { | |
317 | case REDIR_ERROR: | |
318 | #if 0 | |
319 | snprintf(buf, sizeof(buf), "%s: %s FAILED (%s)", gamt->redir.host, | |
320 | redir_state_desc(old), gamt->redir.err); | |
321 | #else | |
322 | snprintf(buf, sizeof(buf), "%s: ERROR: %s", gamt->redir.host, | |
323 | gamt->redir.err); | |
324 | #endif | |
325 | if (old == REDIR_AUTH) { | |
326 | /* ask for a new password next time ... */ | |
327 | strcpy(amt_pass, ""); | |
328 | } | |
329 | break; | |
330 | case REDIR_RUN_SOL: | |
331 | last = cfg_get_int("config", "hosts", gamt->redir.host, 0); | |
332 | cfg_set_int("config", "hosts", gamt->redir.host, time(NULL)); | |
333 | if (!last) | |
334 | gamt_rebuild_hosts(gamt); | |
335 | /* fall through */ | |
336 | default: | |
337 | snprintf(buf, sizeof(buf), "%s: %s", gamt->redir.host, | |
338 | redir_state_desc(new)); | |
339 | break; | |
340 | } | |
341 | if (state_stock[new]) | |
342 | gtk_image_set_from_stock(GTK_IMAGE(gamt->icon), state_stock[new], | |
343 | GTK_ICON_SIZE_SMALL_TOOLBAR); | |
344 | gtk_label_set_text(GTK_LABEL(gamt->status), buf); | |
345 | } | |
346 | ||
347 | static void user_input(VteTerminal *vte, gchar *buf, guint len, | |
348 | gpointer data) | |
349 | { | |
350 | struct gamt_window *gamt = data; | |
351 | ||
352 | if (gamt->redir.state == REDIR_RUN_SOL) | |
353 | redir_sol_send(&gamt->redir, buf, len); | |
354 | } | |
355 | ||
356 | /* ------------------------------------------------------------------ */ | |
357 | ||
358 | static const GtkActionEntry entries[] = { | |
359 | { | |
360 | .name = "FileMenu", | |
361 | .label = "_File", | |
362 | },{ | |
363 | .name = "ConfigMenu", | |
364 | .label = "_Options", | |
365 | },{ | |
366 | .name = "HostMenu", | |
367 | .label = "Ho_sts", | |
368 | },{ | |
369 | .name = "HelpMenu", | |
370 | .label = "_Help", | |
371 | },{ | |
372 | ||
373 | /* File menu */ | |
374 | .name = "Connect", | |
375 | .stock_id = GTK_STOCK_CONNECT, | |
376 | .label = "_Connect ...", | |
377 | .callback = G_CALLBACK(menu_cb_connect), | |
378 | },{ | |
379 | .name = "Disconnect", | |
380 | .stock_id = GTK_STOCK_DISCONNECT, | |
381 | .label = "_Disconnect", | |
382 | .callback = G_CALLBACK(menu_cb_disconnect), | |
383 | },{ | |
384 | .name = "Quit", | |
385 | .stock_id = GTK_STOCK_QUIT, | |
386 | .label = "_Quit", | |
387 | .callback = G_CALLBACK(menu_cb_quit), | |
388 | },{ | |
389 | ||
390 | /* Config menu */ | |
391 | .name = "VteFont", | |
392 | .stock_id = GTK_STOCK_SELECT_FONT, | |
393 | .label = "Terminal _font ...", | |
394 | .callback = G_CALLBACK(menu_cb_config_font), | |
395 | },{ | |
396 | .name = "VteForeground", | |
397 | // .stock_id = GTK_STOCK_SELECT_COLOR, | |
398 | .label = "_Text Color ...", | |
399 | .callback = G_CALLBACK(menu_cb_config_fg), | |
400 | },{ | |
401 | .name = "VteBackground", | |
402 | .label = "_Background Color ...", | |
403 | .callback = G_CALLBACK(menu_cb_config_bg), | |
404 | },{ | |
405 | ||
406 | /* Help menu */ | |
407 | .name = "ManGamt1", | |
408 | .stock_id = GTK_STOCK_HELP, | |
409 | .label = "Manual Page", | |
410 | .callback = G_CALLBACK(menu_cb_man_gamt), | |
411 | },{ | |
412 | .name = "ManAmtHowto7", | |
413 | .stock_id = GTK_STOCK_INFO, | |
414 | .label = "AMT HowTo", | |
415 | .callback = G_CALLBACK(menu_cb_man_amt_howto), | |
416 | },{ | |
417 | .name = "About", | |
418 | .stock_id = GTK_STOCK_ABOUT, | |
419 | .label = "_About ...", | |
420 | .callback = G_CALLBACK(menu_cb_about), | |
421 | } | |
422 | }; | |
423 | ||
424 | static const GtkToggleActionEntry tentries[] = { | |
425 | { | |
426 | .name = "BlinkCursor", | |
427 | .label = "Blinking cursor", | |
428 | .callback = G_CALLBACK(menu_cb_blink_cursor), | |
429 | } | |
430 | }; | |
431 | ||
432 | static char ui_xml[] = | |
433 | "<ui>\n" | |
434 | " <menubar action='MainMenu'>\n" | |
435 | " <menu action='FileMenu'>\n" | |
436 | " <menuitem action='Connect'/>\n" | |
437 | " <menuitem action='Disconnect'/>\n" | |
438 | " <separator/>\n" | |
439 | " <menuitem action='Quit'/>\n" | |
440 | " </menu>\n" | |
441 | " <menu action='HostMenu'>\n" | |
442 | " </menu>\n" | |
443 | " <menu action='ConfigMenu'>\n" | |
444 | " <menuitem action='VteFont'/>\n" | |
445 | " <menuitem action='VteForeground'/>\n" | |
446 | " <menuitem action='VteBackground'/>\n" | |
447 | " <separator/>\n" | |
448 | " <menuitem action='BlinkCursor'/>\n" | |
449 | " </menu>\n" | |
450 | " <menu action='HelpMenu'>\n" | |
451 | " <menuitem action='ManGamt1'/>\n" | |
452 | " <menuitem action='ManAmtHowto7'/>\n" | |
453 | " <separator/>\n" | |
454 | " <menuitem action='About'/>\n" | |
455 | " </menu>\n" | |
456 | " </menubar>\n" | |
457 | " <toolbar action='ToolBar'>\n" | |
458 | " <toolitem action='Quit'/>\n" | |
459 | " <toolitem action='Connect'/>\n" | |
460 | " <toolitem action='Disconnect'/>\n" | |
461 | " </toolbar>\n" | |
462 | "</ui>\n"; | |
463 | ||
464 | static char hosts_xml_start[] = | |
465 | "<ui>\n" | |
466 | " <menubar name='MainMenu'>\n" | |
467 | " <menu action='HostMenu'>\n"; | |
468 | ||
469 | static char hosts_xml_end[] = | |
470 | " </menu>\n" | |
471 | " </menubar>\n" | |
472 | "</ui>\n"; | |
473 | ||
474 | /* ------------------------------------------------------------------ */ | |
475 | ||
476 | static int gamt_getstring(GtkWidget *window, char *title, char *message, | |
477 | char *dest, int dlen, int hide) | |
478 | { | |
479 | GtkWidget *dialog, *label, *entry; | |
480 | const char *txt; | |
481 | int retval; | |
482 | ||
483 | /* Create the widgets */ | |
484 | dialog = gtk_dialog_new_with_buttons(title, | |
485 | GTK_WINDOW(window), | |
486 | GTK_DIALOG_DESTROY_WITH_PARENT, | |
487 | GTK_STOCK_OK, | |
488 | GTK_RESPONSE_ACCEPT, | |
489 | GTK_STOCK_CANCEL, | |
490 | GTK_RESPONSE_REJECT, | |
491 | NULL); | |
492 | gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); | |
493 | ||
494 | label = gtk_label_new(message); | |
495 | gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
496 | ||
497 | entry = gtk_entry_new(); | |
498 | gtk_entry_set_text(GTK_ENTRY(entry), dest); | |
499 | gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE); | |
500 | if (hide) | |
501 | gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); | |
502 | ||
503 | gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); | |
504 | gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), entry); | |
505 | gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), 10); | |
506 | #if 0 /* FIXME: doesn't work ... */ | |
507 | gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 10); | |
508 | #endif | |
509 | ||
510 | /* show and wait for response */ | |
511 | gtk_widget_show_all(dialog); | |
512 | switch (gtk_dialog_run(GTK_DIALOG(dialog))) { | |
513 | case GTK_RESPONSE_ACCEPT: | |
514 | txt = gtk_entry_get_text(GTK_ENTRY(entry)); | |
515 | snprintf(dest, dlen, "%s", txt); | |
516 | retval = 0; | |
517 | break; | |
518 | default: | |
519 | retval = -1; | |
520 | break; | |
521 | } | |
522 | gtk_widget_destroy(dialog); | |
523 | return retval; | |
524 | } | |
525 | ||
526 | static gboolean gamt_data(GIOChannel *source, GIOCondition condition, | |
527 | gpointer data) | |
528 | { | |
529 | struct gamt_window *gamt = data; | |
530 | ||
531 | redir_data(&gamt->redir); | |
532 | ||
533 | if (gamt->redir.state == REDIR_CLOSED || | |
534 | gamt->redir.state == REDIR_ERROR) { | |
535 | g_source_destroy(g_main_context_find_source_by_id | |
536 | (g_main_context_default(), gamt->id)); | |
537 | gamt->id = 0; | |
538 | gamt->ch = NULL; | |
539 | } | |
540 | return TRUE; | |
541 | } | |
542 | ||
543 | static void gamt_rebuild_hosts(struct gamt_window *gamt) | |
544 | { | |
545 | int count, size, pos; | |
546 | char *hosts_xml, *host, action[128]; | |
547 | GtkActionEntry entry; | |
548 | GError *err = NULL; | |
549 | ||
550 | /* remove */ | |
551 | if (gamt->hosts_id) { | |
552 | gtk_ui_manager_remove_ui(gamt->ui, gamt->hosts_id); | |
553 | gamt->hosts_id = 0; | |
554 | } | |
555 | if (gamt->hosts_ag) { | |
556 | gtk_ui_manager_remove_action_group(gamt->ui, gamt->hosts_ag); | |
557 | g_object_unref(gamt->hosts_ag); | |
558 | gamt->hosts_ag = NULL; | |
559 | } | |
560 | ||
561 | /* build */ | |
562 | memset(&entry, 0, sizeof(entry)); | |
563 | entry.callback = G_CALLBACK(menu_cb_connect_to); | |
564 | gamt->hosts_ag = gtk_action_group_new("HostActions"); | |
565 | count = cfg_entries_count("config", "hosts"); | |
566 | size = 128 * count + sizeof(hosts_xml_start) + sizeof(hosts_xml_end); | |
567 | hosts_xml = malloc(size); pos = 0; | |
568 | pos += sprintf(hosts_xml+pos, "%s", hosts_xml_start); | |
569 | for (host = cfg_entries_first("config", "hosts"); | |
570 | NULL != host; | |
571 | host = cfg_entries_next("config", "hosts", host)) { | |
572 | snprintf(action, sizeof(action), "ConnectTo_%s", host); | |
573 | pos += snprintf(hosts_xml+pos, 128, | |
574 | " <menuitem action='%s'/>\n", | |
575 | action); | |
576 | entry.name = action; | |
577 | entry.label = host; | |
578 | gtk_action_group_add_actions(gamt->hosts_ag, &entry, 1, gamt); | |
579 | } | |
580 | pos += sprintf(hosts_xml+pos, "%s", hosts_xml_end); | |
581 | ||
582 | /* add */ | |
583 | gtk_ui_manager_insert_action_group(gamt->ui, gamt->hosts_ag, 1); | |
584 | gamt->hosts_id = gtk_ui_manager_add_ui_from_string(gamt->ui, hosts_xml, -1, &err); | |
585 | if (!gamt->hosts_id) { | |
586 | g_message("building host menu failed: %s", err->message); | |
587 | g_error_free(err); | |
588 | } | |
589 | } | |
590 | ||
591 | static int gamt_connect(struct gamt_window *gamt) | |
592 | { | |
593 | int rc; | |
594 | ||
595 | if (0 == strlen(amt_pass)) { | |
596 | char msg[128]; | |
597 | ||
598 | snprintf(msg, sizeof(msg), "AMT password for %s@%s ?", | |
599 | amt_user, amt_host); | |
600 | rc = gamt_getstring(gamt->win, "Authentication", msg, | |
601 | amt_pass, sizeof(amt_pass), 1); | |
602 | if (0 != rc) | |
603 | return -1; | |
604 | } | |
605 | ||
606 | memset(&gamt->redir, 0, sizeof(gamt->redir)); | |
607 | memcpy(&gamt->redir.type, "SOL ", 4); | |
608 | ||
609 | snprintf(gamt->redir.host, sizeof(gamt->redir.host), "%s", amt_host); | |
610 | snprintf(gamt->redir.port, sizeof(gamt->redir.port), "%s", amt_port); | |
611 | snprintf(gamt->redir.user, sizeof(gamt->redir.user), "%s", amt_user); | |
612 | snprintf(gamt->redir.pass, sizeof(gamt->redir.pass), "%s", amt_pass); | |
613 | ||
614 | gamt->redir.verbose = 1; | |
615 | gamt->redir.trace = amt_trace; | |
616 | gamt->redir.cb_data = gamt; | |
617 | gamt->redir.cb_recv = recv_gtk; | |
618 | gamt->redir.cb_state = state_gtk; | |
619 | ||
620 | if (-1 == redir_connect(&gamt->redir)) | |
621 | return -1; | |
622 | ||
623 | fcntl(gamt->redir.sock, F_SETFD, FD_CLOEXEC); | |
624 | vte_terminal_reset(VTE_TERMINAL(gamt->vte), TRUE, TRUE); | |
625 | gamt->ch = g_io_channel_unix_new(gamt->redir.sock); | |
626 | gamt->id = g_io_add_watch(gamt->ch, G_IO_IN, gamt_data, gamt); | |
627 | redir_start(&gamt->redir); | |
628 | return 0; | |
629 | } | |
630 | ||
631 | static struct gamt_window *gamt_window() | |
632 | { | |
633 | GtkWidget *vbox, *hbox, *frame, *item; | |
634 | GdkColor color; | |
635 | GError *err; | |
636 | gboolean state; | |
637 | struct gamt_window *gamt; | |
638 | char *str; | |
639 | ||
640 | gamt = malloc(sizeof(*gamt)); | |
641 | if (NULL == gamt) | |
642 | return NULL; | |
643 | memset(gamt,0,sizeof(*gamt)); | |
644 | ||
645 | /* gtk toplevel */ | |
646 | gamt->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
647 | g_signal_connect(G_OBJECT(gamt->win), "destroy", | |
648 | G_CALLBACK(destroy_cb), gamt); | |
649 | ||
650 | /* menu + toolbar */ | |
651 | gamt->ui = gtk_ui_manager_new(); | |
652 | gamt->ag = gtk_action_group_new("MenuActions"); | |
653 | gtk_action_group_add_actions(gamt->ag, entries, G_N_ELEMENTS(entries), gamt); | |
654 | gtk_action_group_add_toggle_actions(gamt->ag, tentries, | |
655 | G_N_ELEMENTS(tentries), gamt); | |
656 | gtk_ui_manager_insert_action_group(gamt->ui, gamt->ag, 0); | |
657 | #if 0 | |
658 | GtkAccelGroup *accel = gtk_ui_manager_get_accel_group(gamt->ui); | |
659 | gtk_window_add_accel_group(GTK_WINDOW(gamt->win), accel); | |
660 | #endif | |
661 | ||
662 | err = NULL; | |
663 | if (!gtk_ui_manager_add_ui_from_string(gamt->ui, ui_xml, -1, &err)) { | |
664 | g_message("building menus failed: %s", err->message); | |
665 | g_error_free(err); | |
666 | exit(1); | |
667 | } | |
668 | gamt_rebuild_hosts(gamt); | |
669 | ||
670 | /* vte terminal */ | |
671 | gamt->vte = vte_terminal_new(); | |
672 | g_signal_connect(gamt->vte, "commit", G_CALLBACK(user_input), gamt); | |
673 | vte_terminal_set_scrollback_lines(VTE_TERMINAL(gamt->vte), 4096); | |
674 | str = cfg_get_str(CFG_FONT); | |
675 | vte_terminal_set_font_from_string(VTE_TERMINAL(gamt->vte), str); | |
676 | ||
677 | /* FIXME: make configurable */ | |
678 | vte_terminal_set_backspace_binding(VTE_TERMINAL(gamt->vte), | |
679 | VTE_ERASE_ASCII_BACKSPACE); | |
680 | vte_terminal_set_delete_binding(VTE_TERMINAL(gamt->vte), | |
681 | VTE_ERASE_AUTO); | |
682 | ||
683 | item = gtk_ui_manager_get_widget(gamt->ui, "/MainMenu/ConfigMenu/BlinkCursor"); | |
684 | state = cfg_get_bool(CFG_BLINK, 0); | |
685 | gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), state); | |
686 | vte_terminal_set_cursor_blinks(VTE_TERMINAL(gamt->vte), state); | |
687 | ||
688 | /* other widgets */ | |
689 | gamt->status = gtk_label_new("idle"); | |
690 | gtk_misc_set_alignment(GTK_MISC(gamt->status), 0, 0.5); | |
691 | gtk_misc_set_padding(GTK_MISC(gamt->status), 3, 1); | |
692 | gamt->icon = gtk_image_new_from_stock(GTK_STOCK_DISCONNECT, | |
693 | GTK_ICON_SIZE_SMALL_TOOLBAR); | |
694 | ||
695 | /* Make a vbox and put stuff in */ | |
696 | vbox = gtk_vbox_new(FALSE, 1); | |
697 | hbox = gtk_hbox_new(FALSE, 1); | |
698 | gtk_container_set_border_width(GTK_CONTAINER(vbox), 1); | |
699 | gtk_container_add(GTK_CONTAINER(gamt->win), vbox); | |
700 | item = gtk_ui_manager_get_widget(gamt->ui, "/MainMenu"); | |
701 | gtk_box_pack_start(GTK_BOX(vbox), item, FALSE, FALSE, 0); | |
702 | #if 0 | |
703 | item = gtk_ui_manager_get_widget(gamt->ui, "/ToolBar"); | |
704 | gtk_box_pack_start(GTK_BOX(vbox), item, FALSE, FALSE, 0); | |
705 | #endif | |
706 | gtk_box_pack_start(GTK_BOX(vbox), gamt->vte, TRUE, TRUE, 0); | |
707 | gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); | |
708 | ||
709 | frame = gtk_frame_new(NULL); | |
710 | gtk_container_add(GTK_CONTAINER(frame), gamt->status); | |
711 | gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); | |
712 | ||
713 | frame = gtk_frame_new(NULL); | |
714 | gtk_container_add(GTK_CONTAINER(frame), gamt->icon); | |
715 | gtk_box_pack_end(GTK_BOX(hbox), frame, FALSE, TRUE, 0); | |
716 | ||
717 | /* display window */ | |
718 | gtk_widget_show_all(gamt->win); | |
719 | ||
720 | str = cfg_get_str(CFG_FOREGROUND); | |
721 | if (str) { | |
722 | gdk_color_parse(str, &color); | |
723 | vte_terminal_set_color_foreground(VTE_TERMINAL(gamt->vte), &color); | |
724 | } | |
725 | str = cfg_get_str(CFG_BACKGROUND); | |
726 | if (str) { | |
727 | gdk_color_parse(str, &color); | |
728 | vte_terminal_set_color_background(VTE_TERMINAL(gamt->vte), &color); | |
729 | } | |
730 | ||
731 | return gamt; | |
732 | } | |
733 | ||
734 | /* ------------------------------------------------------------------ */ | |
735 | ||
736 | static void usage(FILE *fp) | |
737 | { | |
738 | fprintf(fp, | |
739 | "\n" | |
740 | "This is " APPNAME ", release " VERSION ", I'll establish\n" | |
741 | "serial-over-lan (sol) connections to your Intel AMT boxes.\n" | |
742 | "\n" | |
743 | "usage: " APPNAME " [options] host\n" | |
744 | "options:\n" | |
745 | " -h print this text\n" | |
746 | " -u user username (default: admin)\n" | |
747 | " -p pass password (default: $AMT_PASSWORD)\n" | |
748 | " -f font terminal font\n" | |
749 | " -c color text color\n" | |
750 | " -b color backgrounf color\n" | |
751 | "\n" | |
752 | "By default port 16994 is used.\n" | |
753 | "If no password is given " APPNAME " will ask for one.\n" | |
754 | "\n" | |
755 | "-- \n" | |
756 | "(c) 2007 Gerd Hoffmann <kraxel@redhat.com>\n" | |
757 | "\n"); | |
758 | } | |
759 | ||
760 | int | |
761 | main(int argc, char *argv[]) | |
762 | { | |
763 | Display *dpy; | |
764 | struct gamt_window *gamt; | |
765 | char configfile[256]; | |
766 | char *h; | |
767 | int c; | |
768 | ||
769 | if (NULL != (h = getenv("AMT_PASSWORD"))) | |
770 | snprintf(amt_pass, sizeof(amt_pass), "%s", h); | |
771 | ||
772 | /* read config, make sure we have sane defaults */ | |
773 | snprintf(configfile, sizeof(configfile), "%s/.gamtrc", getenv("HOME")); | |
774 | cfg_parse_file("config", configfile); | |
775 | if (!cfg_get_str(CFG_FONT)) | |
776 | cfg_set_str(CFG_FONT, "monospace 12"); | |
777 | if (!cfg_get_str(CFG_FOREGROUND)) | |
778 | cfg_set_str(CFG_FOREGROUND, "gray"); | |
779 | if (!cfg_get_str(CFG_BACKGROUND)) | |
780 | cfg_set_str(CFG_BACKGROUND, "black"); | |
781 | ||
782 | gtk_init(&argc, &argv); | |
783 | dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default()); | |
784 | fcntl(ConnectionNumber(dpy),F_SETFD,FD_CLOEXEC); | |
785 | ||
786 | for (;;) { | |
787 | if (-1 == (c = getopt(argc, argv, "hdtu:p:f:c:b:"))) | |
788 | break; | |
789 | switch (c) { | |
790 | case 'd': | |
791 | amt_debug++; | |
792 | break; | |
793 | case 't': | |
794 | amt_trace++; | |
795 | break; | |
796 | case 'u': | |
797 | snprintf(amt_user, sizeof(amt_user), "%s", optarg); | |
798 | break; | |
799 | case 'p': | |
800 | snprintf(amt_pass, sizeof(amt_pass), "%s", optarg); | |
801 | memset(optarg,'*',strlen(optarg)); /* rm passwd from ps list */ | |
802 | break; | |
803 | case 'f': | |
804 | cfg_set_str(CFG_FONT, optarg); | |
805 | break; | |
806 | case 'c': | |
807 | cfg_set_str(CFG_FOREGROUND, optarg); | |
808 | break; | |
809 | case 'b': | |
810 | cfg_set_str(CFG_BACKGROUND, optarg); | |
811 | break; | |
812 | ||
813 | case 'h': | |
814 | usage(stdout); | |
815 | exit(0); | |
816 | default: | |
817 | usage(stderr); | |
818 | exit(1); | |
819 | } | |
820 | } | |
821 | ||
822 | gamt = gamt_window(); | |
823 | if (NULL == gamt) | |
824 | exit(1); | |
825 | ||
826 | if (optind+1 <= argc) { | |
827 | snprintf(amt_host, sizeof(amt_host), "%s", argv[optind]); | |
828 | gamt_connect(gamt); | |
829 | } | |
830 | ||
831 | gtk_main(); | |
832 | cfg_write_file("config", configfile); | |
833 | exit(0); | |
834 | } |