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 return vte_terminal_fork_command(VTE_TERMINAL(vte
), args
[0], args
, NULL
,
104 NULL
, FALSE
, FALSE
, FALSE
);
109 gtk_main_iteration_do(FALSE
);
113 static void start_guest()
117 page
= get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook
)));
118 if (page
&& page
->guest
->get_state(page
->guest
) == GUEST_STOPPED
)
120 vte_terminal_feed(VTE_TERMINAL(page
->vte
),
121 "--- starting guest ---\n\r", -1);
122 page
->guest
->start(page
->guest
, invoke
, VTE_TERMINAL(page
->vte
), idle
);
126 static void start_all_guests()
128 enumerator_t
*enumerator
;
131 enumerator
= pages
->create_enumerator(pages
);
132 while (enumerator
->enumerate(enumerator
, (void**)&page
))
134 if (page
->guest
->get_state(page
->guest
) == GUEST_STOPPED
)
136 vte_terminal_feed(VTE_TERMINAL(page
->vte
),
137 "--- starting all guests ---\n\r", -1);
138 page
->guest
->start(page
->guest
, invoke
,
139 VTE_TERMINAL(page
->vte
), idle
);
142 enumerator
->destroy(enumerator
);
145 static void stop_guest()
149 page
= get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook
)));
150 if (page
&& page
->guest
->get_state(page
->guest
) == GUEST_RUNNING
)
152 page
->guest
->stop(page
->guest
, idle
);
157 * quit signal handler
161 enumerator_t
*enumerator
;
164 dumm
->load_template(dumm
, NULL
);
166 enumerator
= pages
->create_enumerator(pages
);
167 while (enumerator
->enumerate(enumerator
, &page
))
169 if (page
->guest
->get_state(page
->guest
) != GUEST_STOPPED
)
171 page
->guest
->stop(page
->guest
, idle
);
174 enumerator
->destroy(enumerator
);
178 static void error_dialog(char *msg
)
182 error
= gtk_message_dialog_new(GTK_WINDOW(window
),
183 GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_MESSAGE_ERROR
,
184 GTK_BUTTONS_CLOSE
, msg
);
185 gtk_dialog_run(GTK_DIALOG(error
));
186 gtk_widget_destroy(error
);
189 static void create_switch()
191 GtkWidget
*dialog
, *table
, *label
, *name
;
194 dialog
= gtk_dialog_new_with_buttons("Create new switch", GTK_WINDOW(window
),
195 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
196 GTK_STOCK_CANCEL
, GTK_RESPONSE_REJECT
,
197 GTK_STOCK_NEW
, GTK_RESPONSE_ACCEPT
, NULL
);
199 table
= gtk_table_new(1, 2, TRUE
);
200 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), table
);
202 label
= gtk_label_new("Switch name");
203 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 0, 1, 0, 0, 0, 0);
204 gtk_widget_show(label
);
206 name
= gtk_entry_new();
207 gtk_table_attach(GTK_TABLE(table
), name
, 1, 2, 0, 1,
208 GTK_FILL
| GTK_EXPAND
| GTK_SHRINK
, 0, 0, 0);
209 gtk_widget_show(name
);
211 gtk_widget_show(table
);
215 switch (gtk_dialog_run(GTK_DIALOG(dialog
)))
217 case GTK_RESPONSE_ACCEPT
:
219 if (streq(gtk_entry_get_text(GTK_ENTRY(name
)), ""))
223 bridge
= dumm
->create_bridge(dumm
,
224 (char*)gtk_entry_get_text(GTK_ENTRY(name
)));
227 error_dialog("creating bridge failed!");
237 gtk_widget_destroy(dialog
);
240 static void delete_switch()
245 static void connect_guest()
248 GtkWidget
*dialog
, *table
, *label
, *name
, *box
;
251 enumerator_t
*enumerator
;
253 page
= get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook
)));
254 if (!page
|| page
->guest
->get_state(page
->guest
) != GUEST_RUNNING
)
259 dialog
= gtk_dialog_new_with_buttons("Connect guest", GTK_WINDOW(window
),
260 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
261 GTK_STOCK_CANCEL
, GTK_RESPONSE_REJECT
,
262 GTK_STOCK_NEW
, GTK_RESPONSE_ACCEPT
, NULL
);
264 table
= gtk_table_new(2, 2, TRUE
);
265 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), table
);
267 label
= gtk_label_new("Interface name");
268 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 0, 1, 0, 0, 0, 0);
269 gtk_widget_show(label
);
271 name
= gtk_entry_new();
272 gtk_table_attach(GTK_TABLE(table
), name
, 1, 2, 0, 1,
273 GTK_FILL
| GTK_EXPAND
| GTK_SHRINK
, 0, 0, 0);
274 gtk_widget_show(name
);
276 label
= gtk_label_new("Connected switch");
277 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 1, 2, 0, 0, 0, 0);
278 gtk_widget_show(label
);
280 box
= gtk_combo_box_new_text();
281 gtk_table_attach(GTK_TABLE(table
), box
, 1, 2, 1, 2,
282 GTK_FILL
| GTK_EXPAND
| GTK_SHRINK
, 0, 0, 0);
283 enumerator
= dumm
->create_bridge_enumerator(dumm
);
284 while (enumerator
->enumerate(enumerator
, &bridge
))
286 gtk_combo_box_append_text(GTK_COMBO_BOX(box
), bridge
->get_name(bridge
));
288 enumerator
->destroy(enumerator
);
289 gtk_widget_show(box
);
291 gtk_widget_show(table
);
295 switch (gtk_dialog_run(GTK_DIALOG(dialog
)))
297 case GTK_RESPONSE_ACCEPT
:
299 if (streq(gtk_entry_get_text(GTK_ENTRY(name
)), ""))
304 iface
= page
->guest
->create_iface(page
->guest
,
305 (char*)gtk_entry_get_text(GTK_ENTRY(name
)));
308 error_dialog("creating interface failed!");
311 enumerator
= dumm
->create_bridge_enumerator(dumm
);
312 while (enumerator
->enumerate(enumerator
, &bridge
))
314 if (!bridge
->connect_iface(bridge
, iface
))
316 error_dialog("connecting interface failed!");
320 enumerator
->destroy(enumerator
);
328 gtk_widget_destroy(dialog
);
331 static void disconnect_guest()
336 static void delete_guest()
340 page
= get_page(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook
)));
343 page
->guest
->stop(page
->guest
, idle
);
344 dumm
->delete_guest(dumm
, page
->guest
);
345 gtk_notebook_remove_page(GTK_NOTEBOOK(notebook
), page
->num
);
346 pages
->remove(pages
, page
, NULL
);
352 * create a new page for a guest
354 static page_t
* create_page(guest_t
*guest
)
359 page
= g_new(page_t
, 1);
361 page
->vte
= vte_terminal_new();
362 label
= gtk_label_new(guest
->get_name(guest
));
363 page
->num
= gtk_notebook_append_page(GTK_NOTEBOOK(notebook
),
365 gtk_widget_show(page
->vte
);
366 pages
->insert_last(pages
, page
);
373 static void create_guest()
376 GtkWidget
*dialog
, *table
, *label
, *name
, *kernel
, *master
, *args
;
378 dialog
= gtk_dialog_new_with_buttons("Create new guest", GTK_WINDOW(window
),
379 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
380 GTK_STOCK_CANCEL
, GTK_RESPONSE_REJECT
,
381 GTK_STOCK_NEW
, GTK_RESPONSE_ACCEPT
, NULL
);
383 table
= gtk_table_new(4, 2, TRUE
);
384 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog
)->vbox
), table
);
386 label
= gtk_label_new("Guest name");
387 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 0, 1, 0, 0, 0, 0);
388 gtk_widget_show(label
);
390 label
= gtk_label_new("UML kernel");
391 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 1, 2, 0, 0, 0, 0);
392 gtk_widget_show(label
);
394 label
= gtk_label_new("Master filesystem");
395 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 2, 3, 0, 0, 0, 0);
396 gtk_widget_show(label
);
398 label
= gtk_label_new("Kernel arguments");
399 gtk_table_attach(GTK_TABLE(table
), label
, 0, 1, 3, 4, 0, 0, 0, 0);
400 gtk_widget_show(label
);
402 name
= gtk_entry_new();
403 gtk_table_attach(GTK_TABLE(table
), name
, 1, 2, 0, 1,
404 GTK_FILL
| GTK_EXPAND
| GTK_SHRINK
, 0, 0, 0);
405 gtk_widget_show(name
);
407 kernel
= gtk_file_chooser_button_new("Select UML kernel image",
408 GTK_FILE_CHOOSER_ACTION_OPEN
);
409 gtk_table_attach(GTK_TABLE(table
), kernel
, 1, 2, 1, 2,
410 GTK_FILL
| GTK_EXPAND
| GTK_SHRINK
, 0, 0, 0);
411 gtk_widget_show(kernel
);
413 master
= gtk_file_chooser_button_new("Select master filesystem",
414 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
);
415 gtk_table_attach(GTK_TABLE(table
), master
, 1, 2, 2, 3,
416 GTK_FILL
| GTK_EXPAND
| GTK_SHRINK
, 0, 0, 0);
417 gtk_widget_show(master
);
419 args
= gtk_entry_new();
420 gtk_table_attach(GTK_TABLE(table
), args
, 1, 2, 3, 4,
421 GTK_FILL
| GTK_EXPAND
| GTK_SHRINK
, 0, 0, 0);
422 gtk_widget_show(args
);
424 gtk_widget_show(table
);
428 switch (gtk_dialog_run(GTK_DIALOG(dialog
)))
430 case GTK_RESPONSE_ACCEPT
:
432 char *sname
, *skernel
, *smaster
, *sargs
;
435 sname
= (char*)gtk_entry_get_text(GTK_ENTRY(name
));
436 skernel
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(kernel
));
437 smaster
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(master
));
438 sargs
= (char*)gtk_entry_get_text(GTK_ENTRY(args
));
440 if (!sname
[0] || !skernel
|| !smaster
)
444 guest
= dumm
->create_guest(dumm
, sname
, skernel
, smaster
, sargs
);
447 error_dialog("creating guest failed!");
450 page
= create_page(guest
);
451 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook
), page
->num
);
459 gtk_widget_destroy(dialog
);
463 * main routine, parses args and reads from console
465 int main(int argc
, char *argv
[])
467 GtkWidget
*menubar
, *menu
, *menuitem
, *vbox
;
468 GtkWidget
*dummMenu
, *guestMenu
, *switchMenu
;
469 enumerator_t
*enumerator
;
473 gtk_init(&argc
, &argv
);
475 pages
= linked_list_create();
476 dumm
= dumm_create(NULL
);
479 window
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
480 g_signal_connect(G_OBJECT(window
), "destroy", G_CALLBACK(quit
), NULL
);
481 gtk_window_set_title(GTK_WINDOW (window
), "Dumm");
482 gtk_window_set_default_size(GTK_WINDOW (window
), 1000, 500);
483 g_signal_connect(G_OBJECT(vte_reaper_get()), "child-exited",
484 G_CALLBACK(child_exited
), NULL
);
486 /* add vbox with menubar, notebook */
487 vbox
= gtk_vbox_new(FALSE
, 0);
488 gtk_container_add(GTK_CONTAINER(window
), vbox
);
489 menubar
= gtk_menu_bar_new();
490 gtk_box_pack_start(GTK_BOX(vbox
), menubar
, FALSE
, TRUE
, 0);
491 notebook
= gtk_notebook_new();
492 g_object_set(G_OBJECT(notebook
), "homogeneous", TRUE
, NULL
);
493 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook
), GTK_POS_BOTTOM
);
494 gtk_container_add(GTK_CONTAINER(vbox
), notebook
);
497 menu
= gtk_menu_new();
498 dummMenu
= gtk_menu_item_new_with_mnemonic("_Dumm");
499 gtk_menu_bar_append(GTK_MENU_BAR(menubar
), dummMenu
);
500 gtk_widget_show(dummMenu
);
501 gtk_menu_item_set_submenu(GTK_MENU_ITEM(dummMenu
), menu
);
504 menuitem
= gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT
, NULL
);
505 g_signal_connect(G_OBJECT(menuitem
), "activate",
506 G_CALLBACK(quit
), NULL
);
507 gtk_menu_append(GTK_MENU(menu
), menuitem
);
508 gtk_widget_show(menuitem
);
511 menu
= gtk_menu_new();
512 guestMenu
= gtk_menu_item_new_with_mnemonic("_Guest");
513 gtk_menu_bar_append(GTK_MENU_BAR(menubar
), guestMenu
);
514 gtk_widget_show(guestMenu
);
515 gtk_menu_item_set_submenu(GTK_MENU_ITEM(guestMenu
), menu
);
518 menuitem
= gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW
, NULL
);
519 g_signal_connect(G_OBJECT(menuitem
), "activate",
520 G_CALLBACK(create_guest
), NULL
);
521 gtk_menu_append(GTK_MENU(menu
), menuitem
);
522 gtk_widget_show(menuitem
);
524 /* Guest -> delete */
525 menuitem
= gtk_image_menu_item_new_from_stock(GTK_STOCK_DELETE
, NULL
);
526 g_signal_connect(G_OBJECT(menuitem
), "activate",
527 G_CALLBACK(delete_guest
), NULL
);
528 gtk_menu_append(GTK_MENU(menu
), menuitem
);
529 gtk_widget_show(menuitem
);
531 menuitem
= gtk_separator_menu_item_new();
532 gtk_menu_append(GTK_MENU(menu
), menuitem
);
533 gtk_widget_show(menuitem
);
536 menuitem
= gtk_menu_item_new_with_mnemonic("_Start");
537 g_signal_connect(G_OBJECT(menuitem
), "activate",
538 G_CALLBACK(start_guest
), NULL
);
539 gtk_menu_append(GTK_MENU(menu
), menuitem
);
540 gtk_widget_show(menuitem
);
542 /* Guest -> startall */
543 menuitem
= gtk_menu_item_new_with_mnemonic("Start _all");
544 g_signal_connect(G_OBJECT(menuitem
), "activate",
545 G_CALLBACK(start_all_guests
), NULL
);
546 gtk_menu_append(GTK_MENU(menu
), menuitem
);
547 gtk_widget_show(menuitem
);
550 menuitem
= gtk_image_menu_item_new_from_stock(GTK_STOCK_STOP
, NULL
);
551 g_signal_connect(G_OBJECT(menuitem
), "activate",
552 G_CALLBACK(stop_guest
), NULL
);
553 gtk_menu_append(GTK_MENU(menu
), menuitem
);
554 gtk_widget_show(menuitem
);
556 menuitem
= gtk_separator_menu_item_new();
557 gtk_menu_append(GTK_MENU(menu
), menuitem
);
558 gtk_widget_show(menuitem
);
560 /* Guest -> connect */
561 menuitem
= gtk_image_menu_item_new_from_stock(GTK_STOCK_CONNECT
, NULL
);
562 g_signal_connect(G_OBJECT(menuitem
), "activate",
563 G_CALLBACK(connect_guest
), NULL
);
564 gtk_menu_append(GTK_MENU(menu
), menuitem
);
565 gtk_widget_show(menuitem
);
567 /* Guest -> disconnect */
568 menuitem
= gtk_image_menu_item_new_from_stock(GTK_STOCK_DISCONNECT
, NULL
);
569 g_signal_connect(G_OBJECT(menuitem
), "activate",
570 G_CALLBACK(disconnect_guest
), NULL
);
571 gtk_menu_append(GTK_MENU(menu
), menuitem
);
572 gtk_widget_set_sensitive(menuitem
, FALSE
);
573 gtk_widget_show(menuitem
);
576 menu
= gtk_menu_new();
577 switchMenu
= gtk_menu_item_new_with_mnemonic("_Switch");
578 gtk_menu_bar_append(GTK_MENU_BAR(menubar
), switchMenu
);
579 gtk_widget_show(switchMenu
);
580 gtk_menu_item_set_submenu(GTK_MENU_ITEM(switchMenu
), menu
);
583 menuitem
= gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW
, NULL
);
584 g_signal_connect(G_OBJECT(menuitem
), "activate",
585 G_CALLBACK(create_switch
), NULL
);
586 gtk_menu_append(GTK_MENU(menu
), menuitem
);
587 gtk_widget_show(menuitem
);
589 /* Switch -> delete */
590 menuitem
= gtk_image_menu_item_new_from_stock(GTK_STOCK_DELETE
, NULL
);
591 g_signal_connect(G_OBJECT(menuitem
), "activate",
592 G_CALLBACK(delete_switch
), NULL
);
593 gtk_menu_append(GTK_MENU(menu
), menuitem
);
594 gtk_widget_set_sensitive(menuitem
, FALSE
);
595 gtk_widget_show(menuitem
);
598 gtk_widget_show(menubar
);
599 gtk_widget_show(notebook
);
600 gtk_widget_show(vbox
);
601 gtk_widget_show(window
);
603 /* fill notebook with guests */
604 enumerator
= dumm
->create_guest_enumerator(dumm
);
605 while (enumerator
->enumerate(enumerator
, (void**)&guest
))
609 enumerator
->destroy(enumerator
);
614 pages
->destroy_function(pages
, g_free
);