spice-gtk-0.35原始碼解析之spicy解析
spice-gtk介紹:
spice-gtk是基於紅帽的spice遠端連線協議的一套客戶端核心原始碼,上層有一套virt-viewer也是客戶端原始碼,只不過spice-gtk屬於底層直接對話協議的專案,而virt-viewer是基於spice-gtk的更加偏向於介面的專案。
spicy:
spicy是基於spice-gtk專案的一個測試可執行檔案(virt-viewer的可執行檔案remote-viewer是更加前端的連線客戶端),此命令可以直接連線遠端桌面,首先給一下spicy的使用方法:
spicy -h 192.168.112.32 -p 61000 -w 123
其中ip埠和密碼都是服務端建立虛擬機器時已經設定好的(在此不說服務端),spicy原始碼在spice-gtk/tools/spicy.c下面,我們可以從這裡作為頭,開始慢慢滲透spice-gtk專案。下面是spicy的主函式入口:
int main(int argc, char *argv[]) { GError *error = NULL; GOptionContext *context; spice_connection *conn; gchar *conf_file, *conf; char *host = NULL, *port = NULL, *tls_port = NULL, *unix_path = NULL; keyfile = g_key_file_new();//keyfile是一個全域性變數,我們將配置檔案讀到keyfile裡面,再在程式裡面讀取儲存的配置檔案 int mode = S_IRWXU; conf_file = g_build_filename(g_get_user_config_dir(), "spicy", NULL);//g_get_user_config_dir()這個在linux下是/root/.config if (g_mkdir_with_parents(conf_file, mode) == -1) SPICE_DEBUG("failed to create config directory"); g_free(conf_file); conf_file = g_build_filename(g_get_user_config_dir(), "spicy", "settings", NULL); if (!g_key_file_load_from_file(keyfile, conf_file, G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, &error)) { SPICE_DEBUG("Couldn't load configuration: %s", error->message); g_clear_error(&error); } /* parse opts */ gtk_init(&argc, &argv); #if HAVE_GSTAUDIO || HAVE_GSTVIDEO gst_init(&argc, &argv); #endif context = g_option_context_new("- spice client test application"); g_option_context_set_summary(context, "Gtk+ test client to connect to Spice servers."); g_option_context_set_description(context, "Report bugs to " PACKAGE_BUGREPORT "."); g_option_context_add_group(context, spice_get_option_group()); g_option_context_set_main_group(context, spice_cmdline_get_option_group()); g_option_context_add_main_entries(context, cmd_entries, NULL); g_option_context_add_group(context, gtk_get_option_group(TRUE)); #if HAVE_GSTAUDIO || HAVE_GSTVIDEO g_option_context_add_group(context, gst_init_get_option_group()); #endif if (!g_option_context_parse (context, &argc, &argv, &error)) { g_print("option parsing failed: %s\n", error->message); exit(1); } g_option_context_free(context); if (version) { g_print("spicy " PACKAGE_VERSION "\n"); exit(0); } /*從這裡開始上面其實都不用看,上面keyfile是讀配置檔案的能記住上次連線的ip和埠什麼的,option_context是spicy的引數列表,執行前命令列敲打spicy --help顯示的東西,下面才是真正的開始*/ mainloop = g_main_loop_new(NULL, false);//mainloop是個全域性變數,先建立變數但是先不阻塞直到g_main_loop_run的時候程式阻塞 conn = connection_new(); spice_set_session_option(conn->session); spice_cmdline_session_setup(conn->session); g_object_get(conn->session, "unix-path", &unix_path, "host", &host, "port", &port, "tls-port", &tls_port, NULL); /* If user doesn't provide hostname and port, show the dialog window instead of connecting to server automatically */ if ((host == NULL || (port == NULL && tls_port == NULL)) && unix_path == NULL) { if (!spicy_connect_dialog(conn->session)) { exit(0); } } g_free(host); g_free(port); g_free(tls_port); g_free(unix_path); connection_connect(conn); if (connections > 0) g_main_loop_run(mainloop);//程式阻塞,在connection_destroy函式執行的時候會解開,程式就會往下走 g_main_loop_unref(mainloop); /*寫配置檔案,從keyfile到conf_file*/ if ((conf = g_key_file_to_data(keyfile, NULL, &error)) == NULL || !g_file_set_contents(conf_file, conf, -1, &error)) { SPICE_DEBUG("Couldn't save configuration: %s", error->message); g_error_free(error); error = NULL; } g_free(conf_file); g_free(conf); g_key_file_free(keyfile); g_free(spicy_title); setup_terminal(true); #if HAVE_GSTAUDIO || HAVE_GSTVIDEO gst_deinit(); #endif return 0; }
從程式碼中間開始上面其實都不用看,上面keyfile是讀配置檔案的能記住上次連線的ip和埠什麼的,option_context是spicy的引數列表,執行前命令列敲打spicy --help顯示的東西,下面才是真正的開始,程式碼註釋有解釋
mainloop = g_main_loop_new(NULL, false); 這裡是建立一個迴圈物件,false表示未開始迴圈,一旦執行g_main_loop_run(mainloop);,程式就會進入睡眠狀態,所有的介面程式都會有這樣的操作,主程式睡眠,等待觸發的是介面上的訊號和相應的方法。
conn = connection_new();
spice_set_session_option(conn->session);//設定一些session中的額外的引數
spice_cmdline_session_setup(conn->session);//設定session中的最主要的幾個引數ip埠密碼等
如果spicy命令列沒設定引數,下面程式碼會去判斷session物件中到底有沒有沒初始化引數,沒有的話會呼叫一個介面,然後卡在那邊讓你寫引數,spicy_connect_dialog實現了一個gtk的介面。
g_object_get(conn->session,
"unix-path", &unix_path,
"host", &host,
"port", &port,
"tls-port", &tls_port,
NULL);
/* If user doesn't provide hostname and port, show the dialog window
instead of connecting to server automatically */
if ((host == NULL || (port == NULL && tls_port == NULL)) && unix_path == NULL) {
if (!spicy_connect_dialog(conn->session)) {
exit(0);
}
}
spicy不加引數呼叫spicy_connect_dialog,下面是介面。
![](https://img-blog.csdnimg.cn/20181106095740397.png)
點選connect會以後會設定引數之後spicy_connect_dialog正常返回,一切引數準備就緒以後,執行connection_connect(conn)連線遠端桌面。g_main_loop_run(mainloop);主程式處於睡眠狀態。
connection_connect(conn);//連線遠端桌面
if (connections > 0)
g_main_loop_run(mainloop);//主函式睡眠,直到呼叫connection_destroy中的g_main_loop_quit才會往下走
g_main_loop_unref(mainloop);
這裡是spicy的大概的流程,關於connection_connect(conn);以後再分析。