prototype of irdumm - interactive ruby shell for dumm
[strongswan.git] / src / dumm / main.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "dumm.h"
17
18 #include <utils/linked_list.h>
19
20 #include <sys/types.h>
21 #include <unistd.h>
22
23 #include <glib.h>
24 #include <gtk/gtk.h>
25 #include <vte/vte.h>
26 #include <vte/reaper.h>
27
28 /**
29 * notebook page with vte and guest
30 */
31 typedef struct {
32 gint num;
33 GtkWidget *vte;
34 guest_t *guest;
35 } page_t;
36
37 /**
38 * Main window
39 */
40 GtkWidget *window;
41
42 /**
43 * notebook with guests, vtes
44 */
45 GtkWidget *notebook;
46
47 /**
48 * dumm context
49 */
50 dumm_t *dumm;
51
52 /**
53 * pages in notebook, page_t
54 */
55 linked_list_t *pages;
56
57 /**
58 * handle guest termination, SIGCHILD
59 */
60 static void child_exited(VteReaper *vtereaper, gint pid, gint status)
61 {
62 enumerator_t *enumerator;
63 page_t *page;
64
65 enumerator = pages->create_enumerator(pages);
66 while (enumerator->enumerate(enumerator, (void**)&page))
67 {
68 if (page->guest->get_pid(page->guest) == pid)
69 {
70 page->guest->sigchild(page->guest);
71 vte_terminal_feed(VTE_TERMINAL(page->vte),
72 "\n\r--- guest terminated ---\n\r", -1);
73 break;
74 }
75 }
76 enumerator->destroy(enumerator);
77 }
78
79 static page_t* get_page(int num)
80 {
81 enumerator_t *enumerator;
82 page_t *page, *found = NULL;
83
84 enumerator = pages->create_enumerator(pages);
85 while (enumerator->enumerate(enumerator, (void**)&page))
86 {
87 if (page->num == num)
88 {
89 found = page;
90 break;
91 }
92 }
93 enumerator->destroy(enumerator);
94 return found;
95 }
96
97 /**
98 * Guest invocation callback
99 */
100 static pid_t invoke(void *vte, guest_t *guest,
101 char *args[], int argc)
102 {
103 args[argc] = "con0=fd:0,fd:1";
104 return vte_terminal_fork_command(VTE_TERMINAL(vte), args[0], args, NULL,
105 NULL, FALSE, FALSE, FALSE);
106 }
107
108 void idle(void)
109 {
110 gtk_main_iteration_do(FALSE);
111 sched_yield();
112 }
113
114 static void start_guest()
115 {
116 page_t *page;
117
118 page = get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)));
119 if (page && page->guest->get_state(page->guest) == GUEST_STOPPED)
120 {
121 vte_terminal_feed(VTE_TERMINAL(page->vte),
122 "--- starting guest ---\n\r", -1);
123 page->guest->start(page->guest, invoke, VTE_TERMINAL(page->vte), idle);
124 }
125 }
126
127 static void start_all_guests()
128 {
129 enumerator_t *enumerator;
130 page_t *page;
131
132 enumerator = pages->create_enumerator(pages);
133 while (enumerator->enumerate(enumerator, (void**)&page))
134 {
135 if (page->guest->get_state(page->guest) == GUEST_STOPPED)
136 {
137 vte_terminal_feed(VTE_TERMINAL(page->vte),
138 "--- starting all guests ---\n\r", -1);
139 page->guest->start(page->guest, invoke,
140 VTE_TERMINAL(page->vte), idle);
141 }
142 }
143 enumerator->destroy(enumerator);
144 }
145
146 static void stop_guest()
147 {
148 page_t *page;
149
150 page = get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)));
151 if (page && page->guest->get_state(page->guest) == GUEST_RUNNING)
152 {
153 page->guest->stop(page->guest, idle);
154 }
155 }
156
157 /**
158 * quit signal handler
159 */
160 static void quit()
161 {
162 enumerator_t *enumerator;
163 page_t *page;
164
165 dumm->load_template(dumm, NULL);
166
167 enumerator = pages->create_enumerator(pages);
168 while (enumerator->enumerate(enumerator, &page))
169 {
170 if (page->guest->get_state(page->guest) != GUEST_STOPPED)
171 {
172 page->guest->stop(page->guest, idle);
173 }
174 }
175 enumerator->destroy(enumerator);
176 gtk_main_quit();
177 }
178
179 static void error_dialog(char *msg)
180 {
181 GtkWidget *error;
182
183 error = gtk_message_dialog_new(GTK_WINDOW(window),
184 GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
185 GTK_BUTTONS_CLOSE, msg);
186 gtk_dialog_run(GTK_DIALOG(error));
187 gtk_widget_destroy(error);
188 }
189
190 static void create_switch()
191 {
192 GtkWidget *dialog, *table, *label, *name;
193 bridge_t *bridge;
194
195 dialog = gtk_dialog_new_with_buttons("Create new switch", GTK_WINDOW(window),
196 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
197 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
198 GTK_STOCK_NEW, GTK_RESPONSE_ACCEPT, NULL);
199
200 table = gtk_table_new(1, 2, TRUE);
201 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), table);
202
203 label = gtk_label_new("Switch name");
204 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, 0, 0, 0, 0);
205 gtk_widget_show(label);
206
207 name = gtk_entry_new();
208 gtk_table_attach(GTK_TABLE(table), name, 1, 2, 0, 1,
209 GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0);
210 gtk_widget_show(name);
211
212 gtk_widget_show(table);
213
214 while (TRUE)
215 {
216 switch (gtk_dialog_run(GTK_DIALOG(dialog)))
217 {
218 case GTK_RESPONSE_ACCEPT:
219 {
220 if (streq(gtk_entry_get_text(GTK_ENTRY(name)), ""))
221 {
222 continue;
223 }
224 bridge = dumm->create_bridge(dumm,
225 (char*)gtk_entry_get_text(GTK_ENTRY(name)));
226 if (!bridge)
227 {
228 error_dialog("creating bridge failed!");
229 continue;
230 }
231 break;
232 }
233 default:
234 break;
235 }
236 break;
237 }
238 gtk_widget_destroy(dialog);
239 }
240
241 static void delete_switch()
242 {
243
244 }
245
246 static void connect_guest()
247 {
248 page_t *page;
249 GtkWidget *dialog, *table, *label, *name, *box;
250 bridge_t *bridge;
251 iface_t *iface;
252 enumerator_t *enumerator;
253
254 page = get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)));
255 if (!page || page->guest->get_state(page->guest) != GUEST_RUNNING)
256 {
257 return;
258 }
259
260 dialog = gtk_dialog_new_with_buttons("Connect guest", GTK_WINDOW(window),
261 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
262 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
263 GTK_STOCK_NEW, GTK_RESPONSE_ACCEPT, NULL);
264
265 table = gtk_table_new(2, 2, TRUE);
266 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), table);
267
268 label = gtk_label_new("Interface name");
269 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, 0, 0, 0, 0);
270 gtk_widget_show(label);
271
272 name = gtk_entry_new();
273 gtk_table_attach(GTK_TABLE(table), name, 1, 2, 0, 1,
274 GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0);
275 gtk_widget_show(name);
276
277 label = gtk_label_new("Connected switch");
278 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, 0, 0, 0, 0);
279 gtk_widget_show(label);
280
281 box = gtk_combo_box_new_text();
282 gtk_table_attach(GTK_TABLE(table), box, 1, 2, 1, 2,
283 GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0);
284 enumerator = dumm->create_bridge_enumerator(dumm);
285 while (enumerator->enumerate(enumerator, &bridge))
286 {
287 gtk_combo_box_append_text(GTK_COMBO_BOX(box), bridge->get_name(bridge));
288 }
289 enumerator->destroy(enumerator);
290 gtk_widget_show(box);
291
292 gtk_widget_show(table);
293
294 while (TRUE)
295 {
296 switch (gtk_dialog_run(GTK_DIALOG(dialog)))
297 {
298 case GTK_RESPONSE_ACCEPT:
299 {
300 if (streq(gtk_entry_get_text(GTK_ENTRY(name)), ""))
301 {
302 continue;
303 }
304
305 iface = page->guest->create_iface(page->guest,
306 (char*)gtk_entry_get_text(GTK_ENTRY(name)));
307 if (!iface)
308 {
309 error_dialog("creating interface failed!");
310 continue;
311 }
312 enumerator = dumm->create_bridge_enumerator(dumm);
313 while (enumerator->enumerate(enumerator, &bridge))
314 {
315 if (!bridge->connect_iface(bridge, iface))
316 {
317 error_dialog("connecting interface failed!");
318 }
319 break;
320 }
321 enumerator->destroy(enumerator);
322 break;
323 }
324 default:
325 break;
326 }
327 break;
328 }
329 gtk_widget_destroy(dialog);
330 }
331
332 static void disconnect_guest()
333 {
334
335 }
336
337 static void delete_guest()
338 {
339 page_t *page;
340
341 page = get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)));
342 if (page)
343 {
344 page->guest->stop(page->guest, idle);
345 dumm->delete_guest(dumm, page->guest);
346 gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), page->num);
347 pages->remove(pages, page, NULL);
348 g_free(page);
349 }
350 }
351
352 /**
353 * create a new page for a guest
354 */
355 static page_t* create_page(guest_t *guest)
356 {
357 GtkWidget *label;
358 page_t *page;
359
360 page = g_new(page_t, 1);
361 page->guest = guest;
362 page->vte = vte_terminal_new();
363 label = gtk_label_new(guest->get_name(guest));
364 page->num = gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
365 page->vte, label);
366 gtk_widget_show(page->vte);
367 pages->insert_last(pages, page);
368 return page;
369 }
370
371 /**
372 * create a new guest
373 */
374 static void create_guest()
375 {
376 guest_t *guest;
377 GtkWidget *dialog, *table, *label, *name, *kernel, *master, *memory;
378
379 dialog = gtk_dialog_new_with_buttons("Create new guest", GTK_WINDOW(window),
380 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
381 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
382 GTK_STOCK_NEW, GTK_RESPONSE_ACCEPT, NULL);
383
384 table = gtk_table_new(4, 2, TRUE);
385 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), table);
386
387 label = gtk_label_new("Guest name");
388 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, 0, 0, 0, 0);
389 gtk_widget_show(label);
390
391 label = gtk_label_new("UML kernel");
392 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, 0, 0, 0, 0);
393 gtk_widget_show(label);
394
395 label = gtk_label_new("Master filesystem");
396 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 2, 3, 0, 0, 0, 0);
397 gtk_widget_show(label);
398
399 label = gtk_label_new("Memory (MB)");
400 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 3, 4, 0, 0, 0, 0);
401 gtk_widget_show(label);
402
403 name = gtk_entry_new();
404 gtk_table_attach(GTK_TABLE(table), name, 1, 2, 0, 1,
405 GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0);
406 gtk_widget_show(name);
407
408 kernel = gtk_file_chooser_button_new("Select UML kernel image",
409 GTK_FILE_CHOOSER_ACTION_OPEN);
410 gtk_table_attach(GTK_TABLE(table), kernel, 1, 2, 1, 2,
411 GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0);
412 gtk_widget_show(kernel);
413
414 master = gtk_file_chooser_button_new("Select master filesystem",
415 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
416 gtk_table_attach(GTK_TABLE(table), master, 1, 2, 2, 3,
417 GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0);
418 gtk_widget_show(master);
419
420 memory = gtk_spin_button_new_with_range(1, 4096, 1);
421 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(memory), 0);
422 gtk_table_attach(GTK_TABLE(table), memory, 1, 2, 3, 4,
423 GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0, 0);
424 gtk_widget_show(memory);
425
426 gtk_widget_show(table);
427
428 while (TRUE)
429 {
430 switch (gtk_dialog_run(GTK_DIALOG(dialog)))
431 {
432 case GTK_RESPONSE_ACCEPT:
433 {
434 char *sname, *skernel, *smaster;
435 page_t *page;
436
437 sname = (char*)gtk_entry_get_text(GTK_ENTRY(name));
438 skernel = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(kernel));
439 smaster = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(master));
440
441 if (!sname[0] || !skernel || !smaster)
442 {
443 continue;
444 }
445 guest = dumm->create_guest(dumm, sname, skernel, smaster,
446 gtk_spin_button_get_value(GTK_SPIN_BUTTON(memory)));
447 if (!guest)
448 {
449 error_dialog("creating guest failed!");
450 continue;
451 }
452 page = create_page(guest);
453 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), page->num);
454 break;
455 }
456 default:
457 break;
458 }
459 break;
460 }
461 gtk_widget_destroy(dialog);
462 }
463
464 /**
465 * main routine, parses args and reads from console
466 */
467 int main(int argc, char *argv[])
468 {
469 GtkWidget *menubar, *menu, *menuitem, *vbox;
470 GtkWidget *dummMenu, *guestMenu, *switchMenu;
471 enumerator_t *enumerator;
472 guest_t *guest;
473
474 library_init(NULL);
475 gtk_init(&argc, &argv);
476
477 pages = linked_list_create();
478 dumm = dumm_create(NULL);
479
480 /* setup window */
481 window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
482 g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(quit), NULL);
483 gtk_window_set_title(GTK_WINDOW (window), "Dumm");
484 gtk_window_set_default_size(GTK_WINDOW (window), 1000, 500);
485 g_signal_connect(G_OBJECT(vte_reaper_get()), "child-exited",
486 G_CALLBACK(child_exited), NULL);
487
488 /* add vbox with menubar, notebook */
489 vbox = gtk_vbox_new(FALSE, 0);
490 gtk_container_add(GTK_CONTAINER(window), vbox);
491 menubar = gtk_menu_bar_new();
492 gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
493 notebook = gtk_notebook_new();
494 g_object_set(G_OBJECT(notebook), "homogeneous", TRUE, NULL);
495 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_BOTTOM);
496 gtk_container_add(GTK_CONTAINER(vbox), notebook);
497
498 /* Dumm menu */
499 menu = gtk_menu_new();
500 dummMenu = gtk_menu_item_new_with_mnemonic("_Dumm");
501 gtk_menu_bar_append(GTK_MENU_BAR(menubar), dummMenu);
502 gtk_widget_show(dummMenu);
503 gtk_menu_item_set_submenu(GTK_MENU_ITEM(dummMenu), menu);
504
505 /* Dumm -> exit */
506 menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
507 g_signal_connect(G_OBJECT(menuitem), "activate",
508 G_CALLBACK(quit), NULL);
509 gtk_menu_append(GTK_MENU(menu), menuitem);
510 gtk_widget_show(menuitem);
511
512 /* Guest menu */
513 menu = gtk_menu_new();
514 guestMenu = gtk_menu_item_new_with_mnemonic("_Guest");
515 gtk_menu_bar_append(GTK_MENU_BAR(menubar), guestMenu);
516 gtk_widget_show(guestMenu);
517 gtk_menu_item_set_submenu(GTK_MENU_ITEM(guestMenu), menu);
518
519 /* Guest -> new */
520 menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
521 g_signal_connect(G_OBJECT(menuitem), "activate",
522 G_CALLBACK(create_guest), NULL);
523 gtk_menu_append(GTK_MENU(menu), menuitem);
524 gtk_widget_show(menuitem);
525
526 /* Guest -> delete */
527 menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_DELETE, NULL);
528 g_signal_connect(G_OBJECT(menuitem), "activate",
529 G_CALLBACK(delete_guest), NULL);
530 gtk_menu_append(GTK_MENU(menu), menuitem);
531 gtk_widget_show(menuitem);
532
533 menuitem = gtk_separator_menu_item_new();
534 gtk_menu_append(GTK_MENU(menu), menuitem);
535 gtk_widget_show(menuitem);
536
537 /* Guest -> start */
538 menuitem = gtk_menu_item_new_with_mnemonic("_Start");
539 g_signal_connect(G_OBJECT(menuitem), "activate",
540 G_CALLBACK(start_guest), NULL);
541 gtk_menu_append(GTK_MENU(menu), menuitem);
542 gtk_widget_show(menuitem);
543
544 /* Guest -> startall */
545 menuitem = gtk_menu_item_new_with_mnemonic("Start _all");
546 g_signal_connect(G_OBJECT(menuitem), "activate",
547 G_CALLBACK(start_all_guests), NULL);
548 gtk_menu_append(GTK_MENU(menu), menuitem);
549 gtk_widget_show(menuitem);
550
551 /* Guest -> stop */
552 menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_STOP, NULL);
553 g_signal_connect(G_OBJECT(menuitem), "activate",
554 G_CALLBACK(stop_guest), NULL);
555 gtk_menu_append(GTK_MENU(menu), menuitem);
556 gtk_widget_show(menuitem);
557
558 menuitem = gtk_separator_menu_item_new();
559 gtk_menu_append(GTK_MENU(menu), menuitem);
560 gtk_widget_show(menuitem);
561
562 /* Guest -> connect */
563 menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_CONNECT, NULL);
564 g_signal_connect(G_OBJECT(menuitem), "activate",
565 G_CALLBACK(connect_guest), NULL);
566 gtk_menu_append(GTK_MENU(menu), menuitem);
567 gtk_widget_show(menuitem);
568
569 /* Guest -> disconnect */
570 menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_DISCONNECT, NULL);
571 g_signal_connect(G_OBJECT(menuitem), "activate",
572 G_CALLBACK(disconnect_guest), NULL);
573 gtk_menu_append(GTK_MENU(menu), menuitem);
574 gtk_widget_set_sensitive(menuitem, FALSE);
575 gtk_widget_show(menuitem);
576
577 /* Switch menu */
578 menu = gtk_menu_new();
579 switchMenu = gtk_menu_item_new_with_mnemonic("_Switch");
580 gtk_menu_bar_append(GTK_MENU_BAR(menubar), switchMenu);
581 gtk_widget_show(switchMenu);
582 gtk_menu_item_set_submenu(GTK_MENU_ITEM(switchMenu), menu);
583
584 /* Switch -> new */
585 menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
586 g_signal_connect(G_OBJECT(menuitem), "activate",
587 G_CALLBACK(create_switch), NULL);
588 gtk_menu_append(GTK_MENU(menu), menuitem);
589 gtk_widget_show(menuitem);
590
591 /* Switch -> delete */
592 menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_DELETE, NULL);
593 g_signal_connect(G_OBJECT(menuitem), "activate",
594 G_CALLBACK(delete_switch), NULL);
595 gtk_menu_append(GTK_MENU(menu), menuitem);
596 gtk_widget_set_sensitive(menuitem, FALSE);
597 gtk_widget_show(menuitem);
598
599 /* show widgets */
600 gtk_widget_show(menubar);
601 gtk_widget_show(notebook);
602 gtk_widget_show(vbox);
603 gtk_widget_show(window);
604
605 /* fill notebook with guests */
606 enumerator = dumm->create_guest_enumerator(dumm);
607 while (enumerator->enumerate(enumerator, (void**)&guest))
608 {
609 create_page(guest);
610 }
611 enumerator->destroy(enumerator);
612
613 gtk_main();
614
615 dumm->destroy(dumm);
616 pages->destroy_function(pages, g_free);
617
618 library_deinit();
619 return 0;
620 }
621