2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
18 #include <utils/linked_list.h>
20 #include <sys/types.h>
26 #include <vte/reaper.h>
29 * notebook page with vte and guest
43 * notebook with guests, vtes
53 * pages in notebook, page_t
58 * handle guest termination, SIGCHILD
60 static void child_exited(VteReaper
*vtereaper
, gint pid
, gint status
)
62 enumerator_t
*enumerator
;
65 enumerator
= pages
->create_enumerator(pages
);
66 while (enumerator
->enumerate(enumerator
, (void**)&page
))
68 if (page
->guest
->get_pid(page
->guest
) == pid
)
70 page
->guest
->sigchild(page
->guest
);
71 vte_terminal_feed(VTE_TERMINAL(page
->vte
),
72 "\n\r--- guest terminated ---\n\r", -1);
76 enumerator
->destroy(enumerator
);
79 static page_t
* get_page(int num
)
81 enumerator_t
*enumerator
;
82 page_t
*page
, *found
= NULL
;
84 enumerator
= pages
->create_enumerator(pages
);
85 while (enumerator
->enumerate(enumerator
, (void**)&page
))
93 enumerator
->destroy(enumerator
);
98 * Guest invocation callback
100 static pid_t
invoke(void *vte
, guest_t
*guest
,
101 char *args
[], int argc
)
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
);
110 gtk_main_iteration_do(FALSE
);
114 static void start_guest()
118 page
= get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook
)));
119 if (page
&& page
->guest
->get_state(page
->guest
) == GUEST_STOPPED
)
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
);
127 static void start_all_guests()
129 enumerator_t
*enumerator
;
132 enumerator
= pages
->create_enumerator(pages
);
133 while (enumerator
->enumerate(enumerator
, (void**)&page
))
135 if (page
->guest
->get_state(page
->guest
) == GUEST_STOPPED
)
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
);
143 enumerator
->destroy(enumerator
);
146 static void stop_guest()
150 page
= get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook
)));
151 if (page
&& page
->guest
->get_state(page
->guest
) == GUEST_RUNNING
)
153 page
->guest
->stop(page
->guest
, idle
);
158 * quit signal handler
162 enumerator_t
*enumerator
;
165 dumm
->load_template(dumm
, NULL
);
167 enumerator
= pages
->create_enumerator(pages
);
168 while (enumerator
->enumerate(enumerator
, &page
))
170 if (page
->guest
->get_state(page
->guest
) != GUEST_STOPPED
)
172 page
->guest
->stop(page
->guest
, idle
);
175 enumerator
->destroy(enumerator
);
179 static void error_dialog(char *msg
)
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
);
190 static void create_switch()
192 GtkWidget
*dialog
, *table
, *label
, *name
;
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
);
200 table
= gtk_table_new(1, 2, TRUE
);
201 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), table
);
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
);
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
);
212 gtk_widget_show(table
);
216 switch (gtk_dialog_run(GTK_DIALOG(dialog
)))
218 case GTK_RESPONSE_ACCEPT
:
220 if (streq(gtk_entry_get_text(GTK_ENTRY(name
)), ""))
224 bridge
= dumm
->create_bridge(dumm
,
225 (char*)gtk_entry_get_text(GTK_ENTRY(name
)));
228 error_dialog("creating bridge failed!");
238 gtk_widget_destroy(dialog
);
241 static void delete_switch()
246 static void connect_guest()
249 GtkWidget
*dialog
, *table
, *label
, *name
, *box
;
252 enumerator_t
*enumerator
;
254 page
= get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook
)));
255 if (!page
|| page
->guest
->get_state(page
->guest
) != GUEST_RUNNING
)
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
);
265 table
= gtk_table_new(2, 2, TRUE
);
266 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), table
);
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
);
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
);
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
);
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
))
287 gtk_combo_box_append_text(GTK_COMBO_BOX(box
), bridge
->get_name(bridge
));
289 enumerator
->destroy(enumerator
);
290 gtk_widget_show(box
);
292 gtk_widget_show(table
);
296 switch (gtk_dialog_run(GTK_DIALOG(dialog
)))
298 case GTK_RESPONSE_ACCEPT
:
300 if (streq(gtk_entry_get_text(GTK_ENTRY(name
)), ""))
305 iface
= page
->guest
->create_iface(page
->guest
,
306 (char*)gtk_entry_get_text(GTK_ENTRY(name
)));
309 error_dialog("creating interface failed!");
312 enumerator
= dumm
->create_bridge_enumerator(dumm
);
313 while (enumerator
->enumerate(enumerator
, &bridge
))
315 if (!bridge
->connect_iface(bridge
, iface
))
317 error_dialog("connecting interface failed!");
321 enumerator
->destroy(enumerator
);
329 gtk_widget_destroy(dialog
);
332 static void disconnect_guest()
337 static void delete_guest()
341 page
= get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook
)));
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
);
353 * create a new page for a guest
355 static page_t
* create_page(guest_t
*guest
)
360 page
= g_new(page_t
, 1);
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
),
366 gtk_widget_show(page
->vte
);
367 pages
->insert_last(pages
, page
);
374 static void create_guest()
377 GtkWidget
*dialog
, *table
, *label
, *name
, *kernel
, *master
, *memory
;
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
);
384 table
= gtk_table_new(4, 2, TRUE
);
385 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), table
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
426 gtk_widget_show(table
);
430 switch (gtk_dialog_run(GTK_DIALOG(dialog
)))
432 case GTK_RESPONSE_ACCEPT
:
434 char *sname
, *skernel
, *smaster
;
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
));
441 if (!sname
[0] || !skernel
|| !smaster
)
445 guest
= dumm
->create_guest(dumm
, sname
, skernel
, smaster
,
446 gtk_spin_button_get_value(GTK_SPIN_BUTTON(memory
)));
449 error_dialog("creating guest failed!");
452 page
= create_page(guest
);
453 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook
), page
->num
);
461 gtk_widget_destroy(dialog
);
465 * main routine, parses args and reads from console
467 int main(int argc
, char *argv
[])
469 GtkWidget
*menubar
, *menu
, *menuitem
, *vbox
;
470 GtkWidget
*dummMenu
, *guestMenu
, *switchMenu
;
471 enumerator_t
*enumerator
;
475 gtk_init(&argc
, &argv
);
477 pages
= linked_list_create();
478 dumm
= dumm_create(NULL
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
533 menuitem
= gtk_separator_menu_item_new();
534 gtk_menu_append(GTK_MENU(menu
), menuitem
);
535 gtk_widget_show(menuitem
);
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
);
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
);
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
);
558 menuitem
= gtk_separator_menu_item_new();
559 gtk_menu_append(GTK_MENU(menu
), menuitem
);
560 gtk_widget_show(menuitem
);
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
);
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
);
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
);
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
);
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
);
600 gtk_widget_show(menubar
);
601 gtk_widget_show(notebook
);
602 gtk_widget_show(vbox
);
603 gtk_widget_show(window
);
605 /* fill notebook with guests */
606 enumerator
= dumm
->create_guest_enumerator(dumm
);
607 while (enumerator
->enumerate(enumerator
, (void**)&guest
))
611 enumerator
->destroy(enumerator
);
616 pages
->destroy_function(pages
, g_free
);