/* This file is part of GNUnet. (C) 2010, 2011, 2012, 2013 Christian Grothoff (and other contributing authors) GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file src/setup/gnunet-setup.c * @brief Main function of gnunet-setup * @author Christian Grothoff */ #include "gnunet-setup.h" #include "gnunet-setup-transport.h" #include "gnunet-setup-options.h" #include #include #if ENABLE_NLS #include #endif /** * Main loop handle. */ static struct GNUNET_GTK_MainLoop *ml; /** * Name of the configuration file. */ static const char *cfgName; /** * Our configuration. */ struct GNUNET_CONFIGURATION_Handle *cfg; /** * Global return value (for success/failure of gnunet-setup). */ static int gret; /** * Resolver process handle. */ static struct GNUNET_OS_Process *resolver; /** * Namestore process handle. */ static struct GNUNET_OS_Process *namestore; /** * Identity process handle. */ static struct GNUNET_OS_Process *identity; /** * Run autoconfig-only and then exit immediately. */ static int do_autoconfig; #ifndef MINGW /** * Flag to enable privilege escalation. */ static int do_gksu; #endif /** * Get an object from the main window. * * @param name name of the object * @return NULL on error, otherwise the object */ GObject * GNUNET_SETUP_get_object (const char *name) { if (NULL == ml) return NULL; return GNUNET_GTK_main_loop_get_object (ml, name); } /** * User clicked on some widget, update the help button label and link. * * @param widget widget that was clicked on (unused) * @param event the current event * @param user_data context with the option specification to evaluate * @return FALSE (continue event handling) */ static gboolean help_click_callback (GtkWidget * widget, GdkEventButton * event, gpointer user_data) { const struct GNUNET_SETUP_OptionSpecification *os = user_data; GtkLinkButton *help; if (GDK_BUTTON_PRESS != event->type) return FALSE; help = GTK_LINK_BUTTON (GNUNET_SETUP_get_object ("GNUNET_setup_help_text")); gtk_link_button_set_uri (help, os->help_url); gtk_button_set_label (GTK_BUTTON (help), os->help_text); return FALSE; } /** * The main visible page in our main notebook changed. If the * GNS page is visible, hide the help text, otherwise show it. */ void GNUNET_setup_notebook_switch_page_cb (GtkNotebook *notebook, GtkWidget *page, guint page_num, gpointer user_data) { GtkWidget *help; GtkWidget *gnu; help = GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_help_text")); gnu = GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_vbox")); if (gnu == page) gtk_widget_hide (help); else gtk_widget_show (help); } /** * Change the visibility of widgets according to the * value and visibility specification given. * * @param os option specification * @param value current value for the given option */ static void update_visibility (const struct GNUNET_SETUP_OptionSpecification *os, const char *value) { unsigned int i; const struct GNUNET_SETUP_VisibilitySpecification *vs; GtkWidget *widget; regex_t r; if (NULL == os->visibility) return; i = 0; while (os->visibility[i].widget_name != NULL) { vs = &os->visibility[i]; widget = GTK_WIDGET (GNUNET_SETUP_get_object (vs->widget_name)); if (NULL == widget) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Widget `%s' not found\n"), vs->widget_name); } if (NULL != vs->show_value) { if (0 != regcomp (&r, vs->show_value, REG_EXTENDED | REG_ICASE | REG_NOSUB)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Invalid regular expression `%s'\n"), vs->show_value); i++; continue; } if (0 == regexec (&r, value, 0, NULL, 0)) gtk_widget_show (widget); else gtk_widget_hide (widget); regfree (&r); } if (NULL != vs->hide_value) { if (0 != regcomp (&r, vs->hide_value, REG_ICASE | REG_NOSUB)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Invalid regular expression `%s'\n"), vs->show_value); i++; continue; } if (0 == regexec (&r, value, 0, NULL, 0)) gtk_widget_hide (widget); else gtk_widget_show (widget); regfree (&r); } i++; } } /** * Function called whenever a widget changes its state. * * @param os details about the option */ static void widget_state_change_callback (const struct GNUNET_SETUP_OptionSpecification *os) { GObject *widget; char *value; widget = GNUNET_SETUP_get_object (os->widget_name); GNUNET_assert (NULL != os->save_function); if (GNUNET_OK != os->save_function (os->load_save_cls, os->section, os->option, widget, cfg)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to obtain option value from widget `%s'\n"), os->widget_name); return; } if (NULL != os->input_validation_function) os->input_validation_function (os->input_validation_function_cls, widget); if ((os->section != NULL) && (os->option != NULL)) GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, os->section, os->option, &value)); else return; update_visibility (os, value); GNUNET_free_non_null (value); } /** * Load options into the main dialog. */ static void load_options () { const struct GNUNET_SETUP_OptionSpecification *os; unsigned int i; GObject *widget; char *value; #ifndef LINUX gtk_widget_hide (GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_gns_hijack_checkbutton"))); #endif i = 0; while (NULL != option_specifications[i].widget_name) { os = &option_specifications[i]; widget = GNUNET_SETUP_get_object (os->widget_name); if (NULL == widget) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Widget `%s' not found\n"), os->widget_name); i++; continue; } if (NULL != os->load_function) { if ((NULL == os->section) || (NULL == os->option)) { if (GNUNET_OK != os->load_function (os->load_save_cls, NULL, NULL, NULL, widget, cfg)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to initialize widget `%s'\n"), os->widget_name); } } else { if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, os->section, os->option, &value)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("No default value known for option `%s' in section `%s'\n"), os->option, os->section); } else { if (GNUNET_OK != os->load_function (os->load_save_cls, os->section, os->option, value, widget, cfg)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("Failed to initialize widget `%s' with value `%s'\n"), os->widget_name, value); } else { update_visibility (os, value); } GNUNET_free (value); } } } if (NULL != os->input_validation_function) os->input_validation_function (os->input_validation_function_cls, widget); if (os->help_text != NULL) { g_signal_connect (widget, "button-press-event", G_CALLBACK (&help_click_callback), (void *) os); } if (NULL != os->change_signal) { GNUNET_assert (NULL != os->save_function); g_signal_connect_swapped (widget, os->change_signal, G_CALLBACK (&widget_state_change_callback), (void *) os); } i++; } } /** * Write final configuration to disk. * * @return GNUNET_OK on success */ static int write_configuration () { struct GNUNET_CONFIGURATION_Handle *cfgDefault; int ret; cfgDefault = GNUNET_CONFIGURATION_create (); (void) GNUNET_CONFIGURATION_load (cfgDefault, NULL); /* load defaults only */ ret = GNUNET_CONFIGURATION_write_diffs (cfgDefault, cfg, cfgName); GNUNET_CONFIGURATION_destroy (cfgDefault); return ret; } /** * Method run on shutdown. * * @param cls the main loop handle * @param tc scheduler context */ static void cleanup_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { if (NULL == ml) { GNUNET_break (0); return; } GNUNET_GTK_main_loop_quit (ml); ml = NULL; if (GNUNET_OK != write_configuration ()) gret = 1; cfg = NULL; if (NULL != resolver) { GNUNET_break (0 == GNUNET_OS_process_kill (resolver, SIGTERM)); GNUNET_OS_process_destroy (resolver); resolver = NULL; } if (NULL != namestore) { GNUNET_break (0 == GNUNET_OS_process_kill (namestore, SIGTERM)); GNUNET_OS_process_destroy (namestore); namestore = NULL; } if (NULL != identity) { GNUNET_break (0 == GNUNET_OS_process_kill (identity, SIGTERM)); GNUNET_OS_process_destroy (identity); identity = NULL; } } /** * Write configuration to dis, (re)start the namestore process and * reload the namestore models. * * * @param cls closure (unused) * @param tc scheduler context (unused) */ void GNUNET_SETUP_restart_namestore (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { if (GNUNET_OK != write_configuration ()) return; /* no point in re-starting namestore ... */ if (NULL != namestore) { GNUNET_break (0 == GNUNET_OS_process_kill (namestore, SIGTERM)); GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (namestore)); GNUNET_OS_process_destroy (namestore); namestore = NULL; } namestore = GNUNET_OS_start_process (GNUNET_YES, GNUNET_OS_INHERIT_STD_ALL, NULL, NULL, "gnunet-service-namestore", "gnunet-service-namestore", NULL); } /** * Callback invoked if the application is supposed to exit. */ void GNUNET_SETUP_quit_cb (GObject * object, gpointer user_data) { GNUNET_SCHEDULER_shutdown (); } /** * If the test failed, start the resolver process. * * @param cls closure, NULL * @param result #GNUNET_YES if the service is running */ static void start_resolver (void *cls, int result) { char *binary; if (GNUNET_YES == result) return; binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver"); resolver = GNUNET_OS_start_process (GNUNET_YES, GNUNET_OS_INHERIT_STD_ALL, NULL,NULL, binary, "gnunet-service-resolver", NULL); GNUNET_free (binary); } /** * If the test failed, start the identity process. * * @param cls closure, NULL * @param result #GNUNET_YES if the service is running */ static void start_identity (void *cls, int result) { char *binary; if (GNUNET_YES == result) return; binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-identity"); identity = GNUNET_OS_start_process (GNUNET_YES, GNUNET_OS_INHERIT_STD_ALL, NULL, NULL, binary, "gnunet-service-identity", NULL); GNUNET_free (binary); } /** * If the test failed, start the namestore process. * * @param cls closure, NULL * @param result #GNUNET_YES if the service is running */ static void start_namestore (void *cls, int result) { char *binary; if (GNUNET_YES == result) return; binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-namestore"); namestore = GNUNET_OS_start_process (GNUNET_YES, GNUNET_OS_INHERIT_STD_ALL, NULL, NULL, binary, "gnunet-service-namestore", NULL); GNUNET_free (binary); } #ifndef MINGW /** * Try elevating user priviledges to run as user 'gnunet' or 'root'. * * @param username user gnunet-setup should be run as * @return GNUNET_OK on success */ static int try_gksu (const char *username) { struct GNUNET_OS_Process *proc; proc = GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_ALL, NULL, NULL, "gksu", "-u", username, "-m", _("Enter YOUR password to run gnunet-setup as user 'gnunet' (assuming 'sudo' allows it)"), "-D", _("Enter YOUR password to run gnunet-setup as user 'gnunet' (assuming 'sudo' allows it)"), NULL); if (NULL == proc) return GNUNET_SYSERR; GNUNET_OS_process_wait (proc); GNUNET_OS_process_destroy (proc); return GNUNET_OK; } #endif /** * User clicked on the button to edit the list of friends. * Launch gnunet-peerinfo-gtk. */ void GNUNET_setup_launch_edit_friends_button_clicked_cb (GtkButton *button, gpointer *user_data) { struct GNUNET_OS_Process *proc; proc = GNUNET_OS_start_process (GNUNET_NO, GNUNET_OS_INHERIT_STD_ALL, NULL, NULL, "gnunet-peerinfo-gtk", "-c", cfgName, NULL); if (NULL == proc) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to launch gnunet-peerinfo-gtk\n")); return; } /* simply yield control, let it run */ GNUNET_free (proc); } /** * Called once the autoconfiguration is done. Triggers shutdown. * * @param cls unused */ static void autoconfig_finished_cb (void *cls) { GNUNET_SCHEDULER_shutdown (); } /** * Actual main method that sets up the configuration window. * * @param cls the main loop handle * @param tc scheduler context */ static void run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { GtkWidget *main_window; #ifndef MINGW uid_t my_uid; struct passwd *gnunet_pw; my_uid = getuid (); gnunet_pw = getpwnam ("gnunet"); if ( (0 != do_gksu) && (0 != my_uid) && (NULL != gnunet_pw) && (my_uid != gnunet_pw->pw_uid) && (GNUNET_OK == try_gksu ("gnunet"))) { GNUNET_GTK_main_loop_quit (cls); return; } #endif ml = cls; if (GNUNET_OK != GNUNET_GTK_main_loop_build_window (ml, NULL)) return; cfgName = GNUNET_GTK_main_loop_get_configuration_file (ml); cfg = GNUNET_CONFIGURATION_create (); #ifndef MINGW if ( (0 != my_uid) && (NULL != gnunet_pw) && (my_uid != gnunet_pw->pw_uid) ) { /* load system defaults, system/'gnunet' user's configuration and THEN our configuration file */ if (GNUNET_YES == GNUNET_DISK_file_test ("/etc/gnunet.conf")) (void) GNUNET_CONFIGURATION_load (cfg, "/etc/gnunet.conf"); else { #if HAVE_GETPWNAM struct passwd *pw; pw = getpwnam ("gnunet"); if (NULL != pw) { char *cfgname; GNUNET_asprintf (&cfgname, "%s%s%s", pw->pw_dir, DIR_SEPARATOR_STR, ".gnunet/gnunet.conf"); (void) GNUNET_CONFIGURATION_load (cfg, cfgname); GNUNET_free (cfgname); } #endif } (void) GNUNET_CONFIGURATION_parse (cfg, cfgName); } else #endif { /* only load system defaults and our configuration file */ (void) GNUNET_CONFIGURATION_load (cfg, cfgName); } main_window = GTK_WIDGET (GNUNET_SETUP_get_object ("GNUNET_setup_dialog")); GNUNET_CLIENT_service_test ("resolver", cfg, GNUNET_CONSTANTS_SERVICE_TIMEOUT, &start_resolver, NULL); GNUNET_CLIENT_service_test ("namestore", cfg, GNUNET_CONSTANTS_SERVICE_TIMEOUT, &start_namestore, NULL); GNUNET_CLIENT_service_test ("identity", cfg, GNUNET_CONSTANTS_SERVICE_TIMEOUT, &start_identity, NULL); load_options (); GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task, NULL); if (do_autoconfig) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Attempting fully-automatic, non-interactive network configuration\n")); GNUNET_setup_transport_autoconfig_start (&autoconfig_finished_cb, NULL); return; } gtk_widget_show (main_window); gtk_window_present (GTK_WINDOW (main_window)); } /** * Main function for gnunet-setup. * * @param argc number of arguments * @param argv arguments * @return 0 on success */ int main (int argc, char *const *argv) { struct GNUNET_GETOPT_CommandLineOption options[] = { { 'a', "autoconfig", NULL, gettext_noop ("attempt automatic configuration of the network and instantly exit"), 0, &GNUNET_GETOPT_set_one, &do_autoconfig }, #ifndef MINGW { 'e', "elevate-priviledges", NULL, gettext_noop ("run as user 'gnunet', if necessary by executing gksu to elevate rights"), 0, &GNUNET_GETOPT_set_one, &do_gksu }, #endif GNUNET_GETOPT_OPTION_END }; int ret; if (GNUNET_OK == GNUNET_GTK_main_loop_start ("gnunet-setup", "gnunet-setup", argc, argv, options, "gnunet_setup_main_window.glade", &run)) ret = gret; else ret = 1; return ret; } /* end of gnunet-setup.c */