| #include <new> |
| #include <ios> |
| #include <iostream> |
| #include <stdexcept> |
| #include <cstdlib> |
| #include <cstdio> |
| |
| extern "C" |
| { |
| #include <errno.h> |
| #include <config.h> |
| #include <drbd_buildtag.h> |
| } |
| |
| #include <map_types.h> |
| // https://github.com/raltnoeder/cppdsaext |
| #include <dsaext.h> |
| #include <DrbdMon.h> |
| #include <utils.h> |
| #include <CompactDisplay.h> |
| #include <ConfigOption.h> |
| #include <Args.h> |
| |
| const std::string DrbdMon::PROGRAM_NAME = "DRBD DrbdMon"; |
| const std::string DrbdMon::VERSION = PACKAGE_VERSION; |
| |
| const std::string DrbdMon::OPT_HELP_KEY = "help"; |
| const std::string DrbdMon::OPT_VERSION_KEY = "version"; |
| const ConfigOption DrbdMon::OPT_HELP(true, OPT_HELP_KEY); |
| const ConfigOption DrbdMon::OPT_VERSION(true, OPT_VERSION_KEY); |
| |
| const std::string DrbdMon::TOKEN_DELIMITER = " "; |
| |
| const std::string DrbdMon::MODE_EXISTS = "exists"; |
| const std::string DrbdMon::MODE_CREATE = "create"; |
| const std::string DrbdMon::MODE_CHANGE = "change"; |
| const std::string DrbdMon::MODE_DESTROY = "destroy"; |
| |
| const std::string DrbdMon::TYPE_RESOURCE = "resource"; |
| const std::string DrbdMon::TYPE_CONNECTION = "connection"; |
| const std::string DrbdMon::TYPE_DEVICE = "device"; |
| const std::string DrbdMon::TYPE_PEER_DEVICE = "peer-device"; |
| const std::string DrbdMon::TYPE_SEPARATOR = "-"; |
| |
| const char DrbdMon::HOTKEY_QUIT = 'q'; |
| const char DrbdMon::HOTKEY_REPAINT = 'r'; |
| const char DrbdMon::HOTKEY_CLEAR_MSG = 'c'; |
| const char DrbdMon::HOTKEY_REINIT = 'R'; |
| const char DrbdMon::HOTKEY_VERSION = 'V'; |
| |
| const std::string DrbdMon::DESC_QUIT = "Quit"; |
| const std::string DrbdMon::DESC_REPAINT = "Repaint"; |
| const std::string DrbdMon::DESC_CLEAR_MSG = "Clear messages"; |
| const std::string DrbdMon::DESC_REINIT = "Reinitialize"; |
| |
| |
| // @throws std::bad_alloc |
| DrbdMon::DrbdMon( |
| int argc, |
| char* argv[], |
| MessageLog& log_ref, |
| fail_info& fail_data_ref, |
| const std::string* const node_name_ref |
| ): |
| arg_count(argc), |
| arg_values(argv), |
| resources_map(new ResourcesMap(&comparators::compare_string)), |
| hotkeys_info(new HotkeysMap(&comparators::compare_char)), |
| fail_data(fail_data_ref), |
| log(log_ref), |
| node_name(node_name_ref) |
| { |
| } |
| |
| DrbdMon::~DrbdMon() noexcept |
| { |
| // Cleanup resources map |
| { |
| ResourcesMap::NodesIterator dtor_iter(*resources_map); |
| // Free all DrbdResource mappings |
| while (dtor_iter.has_next()) |
| { |
| ResourcesMap::Node* node = dtor_iter.next(); |
| delete node->get_key(); |
| delete node->get_value(); |
| } |
| resources_map->clear(); |
| } |
| |
| // Cleanup hotkeys map |
| // Keys/values in this map are static members of the DrbdMon class |
| hotkeys_info->clear(); |
| } |
| |
| // @throws std::bad_alloc |
| void DrbdMon::run() |
| { |
| std::unique_ptr<PropsMap> event_props; |
| std::unique_ptr<TermSize> term_size; |
| std::unique_ptr<EventsSourceSpawner> events_source; |
| std::unique_ptr<EventsIo> events_io; |
| |
| try |
| { |
| setup_hotkeys_info(); |
| try |
| { |
| event_props = std::unique_ptr<PropsMap>(new PropsMap(&comparators::compare_string)); |
| term_size = std::unique_ptr<TermSize>(new TermSize()); |
| |
| // Create the display and keep a pointer of the implementation's type for later use |
| // as a configurable object |
| // Do not add anything that might throw between display_impl allocation and |
| // display interface unique_ptr initialization to ensure deallocation of the display |
| // object if the current scope is left |
| CompactDisplay* display_impl = new CompactDisplay( |
| *resources_map, log, *hotkeys_info, node_name |
| ); |
| display = std::unique_ptr<GenericDisplay>(dynamic_cast<GenericDisplay*> (display_impl)); |
| |
| configurables = std::unique_ptr<Configurable*[]>(new Configurable*[3]); |
| configurables[0] = dynamic_cast<Configurable*> (this); |
| configurables[1] = dynamic_cast<Configurable*> (display_impl); |
| configurables[2] = nullptr; |
| configure_options(); |
| |
| events_source = std::unique_ptr<EventsSourceSpawner>(new EventsSourceSpawner(log)); |
| |
| if (term_size->probe_terminal_size()) |
| { |
| display->set_terminal_size(term_size->get_size_x(), term_size->get_size_y()); |
| } |
| |
| events_source->spawn_source(); |
| events_io = std::unique_ptr<EventsIo>(new EventsIo(events_source->get_events_source_fd())); |
| |
| // Cleanup any zombies that might not have been collected, |
| // because SIGCHLD is blocked during reinitialization |
| events_source->cleanup_child_processes(); |
| |
| try |
| { |
| events_io->adjust_terminal(); |
| } |
| catch (EventsIoException&) |
| { |
| log.add_entry(MessageLog::log_level::WARN, "Adjusting the terminal mode failed"); |
| } |
| |
| // Show an initial display while reading the initial DRBD status |
| display->initial_display(); |
| |
| while (!shutdown) |
| { |
| EventsIo::event event_id = events_io->wait_event(); |
| switch (event_id) |
| { |
| case EventsIo::event::EVENT_LINE: |
| { |
| std::string* event_line = events_io->get_event_line(); |
| if (event_line != nullptr) |
| { |
| uint32_t update_counter {0}; |
| while (event_line != nullptr) |
| { |
| tokenize_event_message(*event_line, *event_props); |
| events_io->free_event_line(); |
| clear_event_props(*event_props); |
| event_line = events_io->get_event_line(); |
| if (have_initial_state) |
| { |
| if (update_counter >= MAX_EVENT_BUNDLE) |
| { |
| display->status_display(); |
| update_counter = 0; |
| } |
| else |
| { |
| ++update_counter; |
| } |
| } |
| } |
| } |
| else |
| { |
| throw EventMessageException(); |
| } |
| if (have_initial_state) |
| { |
| display->status_display(); |
| } |
| } |
| break; |
| case EventsIo::event::SIGNAL: |
| { |
| int signal_id = events_io->get_signal(); |
| switch (signal_id) |
| { |
| case SIGHUP: |
| // fall-through |
| case SIGINT: |
| // fall-through |
| case SIGTERM: |
| // terminate main loop |
| fin_action = DrbdMon::finish_action::TERMINATE; |
| shutdown = true; |
| break; |
| case SIGWINCH: |
| if (term_size->probe_terminal_size()) |
| { |
| display->set_terminal_size(term_size->get_size_x(), term_size->get_size_y()); |
| display->status_display(); |
| } |
| break; |
| case SIGCHLD: |
| { |
| // Throws an EventsSourceException if the currently tracked events source |
| // process exits, thereby triggering reinitialization |
| events_source->cleanup_child_processes(); |
| } |
| break; |
| default: |
| // Unexpected signals ignored |
| break; |
| } |
| break; |
| } |
| case EventsIo::event::STDIN: |
| { |
| // Consume input from stdin |
| char c = 0; |
| int read_count = 0; |
| do |
| { |
| errno = 0; |
| read_count = read(STDIN_FILENO, &c, 1); |
| } |
| while (read_count == -1 && errno == EINTR); |
| |
| if (read_count == 1) |
| { |
| if (c == HOTKEY_REPAINT) |
| { |
| display->status_display(); |
| } |
| else |
| if (c == HOTKEY_REINIT) |
| { |
| fin_action = DrbdMon::finish_action::RESTART_IMMED; |
| shutdown = true; |
| } |
| else |
| if (c == HOTKEY_CLEAR_MSG) |
| { |
| log.clear(); |
| display->status_display(); |
| } |
| else |
| if (c == HOTKEY_QUIT) |
| { |
| fin_action = DrbdMon::finish_action::TERMINATE; |
| shutdown = true; |
| } |
| else |
| if (c == HOTKEY_VERSION) |
| { |
| std::string version_info = PROGRAM_NAME + " v" + VERSION + |
| " (" + GITHASH + ")"; |
| log.add_entry(MessageLog::log_level::INFO, version_info); |
| display->status_display(); |
| } |
| else |
| { |
| display->key_pressed(c); |
| } |
| } |
| } |
| break; |
| case EventsIo::event::NONE: |
| // Not supposed to happen |
| // fall-through |
| default: |
| log.add_entry( |
| MessageLog::log_level::ALERT, |
| "DrbdMon: Internal error: Unexpected event type returned by EventsIo" |
| ); |
| break; |
| } |
| } |
| } |
| catch (std::ios_base::failure&) |
| { |
| log.add_entry( |
| MessageLog::log_level::ALERT, |
| "DRBD events source I/O error" |
| ); |
| fin_action = DrbdMon::finish_action::RESTART_DELAYED; |
| fail_data = DrbdMon::fail_info::EVENTS_IO; |
| } |
| catch (EventsSourceException&) |
| { |
| log.add_entry( |
| MessageLog::log_level::ALERT, |
| "DRBD events source failed" |
| ); |
| fin_action = DrbdMon::finish_action::RESTART_DELAYED; |
| fail_data = DrbdMon::fail_info::EVENTS_SOURCE; |
| } |
| catch (EventsIoException&) |
| { |
| log.add_entry( |
| MessageLog::log_level::ALERT, |
| "DRBD events source I/O error" |
| ); |
| fin_action = DrbdMon::finish_action::RESTART_DELAYED; |
| fail_data = DrbdMon::fail_info::EVENTS_IO; |
| } |
| catch (EventException&) |
| { |
| log.add_entry( |
| MessageLog::log_level::ALERT, |
| "Received invalid DRBD events source data" |
| ); |
| fin_action = DrbdMon::finish_action::RESTART_DELAYED; |
| fail_data = DrbdMon::fail_info::GENERIC; |
| } |
| catch (ConfigurationException&) |
| { |
| // A ConfigurationException is thrown to abort and exit |
| // while configuring options |
| // (--help uses this too) |
| fin_action = DrbdMon::finish_action::TERMINATE_NO_CLEAR; |
| } |
| } |
| catch (std::bad_alloc&) |
| { |
| cleanup(event_props.get(), events_io.get()); |
| throw; |
| } |
| |
| cleanup(event_props.get(), events_io.get()); |
| } |
| |
| // @throws std::bad_alloc, EventsMessageException, EventObjectException |
| void DrbdMon::tokenize_event_message(std::string& event_line, PropsMap& event_props) |
| { |
| try |
| { |
| StringTokenizer tokens(event_line, TOKEN_DELIMITER); |
| if (tokens.has_next()) |
| { |
| std::string event_mode = tokens.next(); |
| if (tokens.has_next()) |
| { |
| std::string event_type = tokens.next(); |
| parse_event_props(tokens, event_props); |
| |
| process_event_message(event_mode, event_type, event_props); |
| } |
| } |
| } |
| catch (EventMessageException& msg_exc) |
| { |
| log.add_entry( |
| MessageLog::log_level::ALERT, |
| "Received event message was malformed" |
| ); |
| throw msg_exc; |
| } |
| catch (EventObjectException& obj_exc) |
| { |
| log.add_entry( |
| MessageLog::log_level::ALERT, |
| "Received event referenced a nonexistent object" |
| ); |
| throw obj_exc; |
| } |
| } |
| |
| // @throws std::bad_alloc, EventMessageException, EventObjectException |
| void DrbdMon::process_event_message( |
| std::string& event_mode, |
| std::string& event_type, |
| PropsMap& event_props |
| ) |
| { |
| if (event_mode == MODE_EXISTS || event_mode == MODE_CREATE) |
| { |
| if (event_type == TYPE_CONNECTION) |
| { |
| create_connection(event_props); |
| } |
| else |
| if (event_type == TYPE_DEVICE) |
| { |
| create_device(event_props); |
| } |
| else |
| if (event_type == TYPE_PEER_DEVICE) |
| { |
| create_peer_device(event_props); |
| } |
| else |
| if (event_type == TYPE_RESOURCE) |
| { |
| create_resource(event_props); |
| } |
| else |
| if (event_type == TYPE_SEPARATOR && event_mode == MODE_EXISTS) |
| { |
| // "exists -" line from drbdsetup |
| // Report recovering from errors that triggered reinitialization |
| // of the DrbdMon instance |
| |
| switch (fail_data) |
| { |
| case DrbdMon::fail_info::NONE: |
| // no-op |
| break; |
| case DrbdMon::fail_info::OUT_OF_MEMORY: |
| log.add_entry( |
| MessageLog::log_level::INFO, |
| "Status tracking reestablished after out-of-memory condition" |
| ); |
| break; |
| case DrbdMon::fail_info::EVENTS_IO: |
| log.add_entry( |
| MessageLog::log_level::INFO, |
| "Events source I/O reestablished" |
| ); |
| break; |
| case DrbdMon::fail_info::EVENTS_SOURCE: |
| log.add_entry( |
| MessageLog::log_level::INFO, |
| "Events source process respawned" |
| ); |
| break; |
| case DrbdMon::fail_info::GENERIC: |
| // fall-through |
| default: |
| log.add_entry( |
| MessageLog::log_level::INFO, |
| "Status tracking reestablished" |
| ); |
| break; |
| } |
| |
| // Indicate that the initial state is available now |
| // (This can be used to disable display updates until an initial state is available) |
| have_initial_state = true; |
| display->status_display(); |
| |
| // In case that multiple "exists -" lines are received, |
| // which is actually not supposed to happen, avoid spamming |
| // the message log |
| fail_data = fail_info::NONE; |
| } |
| } |
| else |
| if (event_mode == MODE_CHANGE) |
| { |
| if (event_type == TYPE_CONNECTION) |
| { |
| update_connection(event_props); |
| } |
| else |
| if (event_type == TYPE_DEVICE) |
| { |
| update_device(event_props); |
| } |
| else |
| if (event_type == TYPE_PEER_DEVICE) |
| { |
| update_peer_device(event_props); |
| } |
| else |
| if (event_type == TYPE_RESOURCE) |
| { |
| update_resource(event_props); |
| } |
| // unknown object types are skipped |
| } |
| else |
| if (event_mode == MODE_DESTROY) |
| { |
| if (event_type == TYPE_CONNECTION) |
| { |
| destroy_connection(event_props); |
| } |
| else |
| if (event_type == TYPE_DEVICE) |
| { |
| destroy_device(event_props); |
| } |
| else |
| if (event_type == TYPE_PEER_DEVICE) |
| { |
| destroy_peer_device(event_props); |
| } |
| else |
| if (event_type == TYPE_RESOURCE) |
| { |
| destroy_resource(event_props); |
| } |
| // unknown object types are skipped |
| } |
| // unknown message modes are skipped |
| } |
| |
| /** |
| * Returns the action requested to be taken upon return from this class' run() method. |
| * This method should be called only after run() has returned. |
| */ |
| DrbdMon::finish_action DrbdMon::get_fin_action() const |
| { |
| return fin_action; |
| } |
| |
| /** |
| * Loads the event_props map with the properties contained in tokens |
| * |
| * @param: tokens StringTokenizer that returns the 'key:value' tokens from a 'drbdsetup event2' line |
| * @param: event_props Property map to load the key == value mappings into |
| */ |
| // @throws std::bad_alloc |
| void DrbdMon::parse_event_props(StringTokenizer& tokens, PropsMap& event_props) |
| { |
| while (tokens.has_next()) |
| { |
| std::string prop_entry = tokens.next(); |
| |
| size_t split_index = prop_entry.find(":"); |
| if (split_index != std::string::npos) |
| { |
| std::unique_ptr<std::string> key(new std::string(prop_entry.substr(0, split_index))); |
| ++split_index; |
| std::unique_ptr<std::string> value( |
| new std::string(prop_entry.substr(split_index, prop_entry.length() - split_index)) |
| ); |
| try |
| { |
| event_props.insert(key.get(), value.get()); |
| static_cast<void> (key.release()); |
| static_cast<void> (value.release()); |
| } |
| catch (dsaext::DuplicateInsertException& dup_exc) |
| { |
| // DEBUG: duplicate key, malformed event line |
| // Encountering this problem should possibly restart everything from scratch |
| // TODO: FIXME: Issue a message log warning |
| log.add_entry( |
| MessageLog::log_level::WARN, |
| "Duplicate key detected on drbdsetup events line" |
| ); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Clears the event_props map and frees all its entries |
| */ |
| void DrbdMon::clear_event_props(PropsMap& event_props) |
| { |
| PropsMap::NodesIterator clear_iter(event_props); |
| while (clear_iter.has_next()) |
| { |
| PropsMap::Node* node = clear_iter.next(); |
| delete node->get_key(); |
| delete node->get_value(); |
| } |
| event_props.clear(); |
| } |
| |
| // @throws std::bad_alloc, EventMessageException |
| void DrbdMon::create_connection(PropsMap& event_props) |
| { |
| try |
| { |
| DrbdResource& res = get_resource(event_props); |
| |
| try |
| { |
| std::unique_ptr<DrbdConnection> conn(DrbdConnection::new_from_props(event_props)); |
| conn->update(event_props); |
| res.add_connection(conn.get()); |
| static_cast<void> (conn.release()); |
| } |
| catch (dsaext::DuplicateInsertException& dup_exc) |
| { |
| log.add_entry( |
| MessageLog::log_level::ALERT, |
| "Duplicate DRBD connection creation reported by the DRBD events source" |
| ); |
| } |
| } |
| catch (EventObjectException& nonexistent_object_exc) |
| { |
| // ignored |
| } |
| } |
| |
| // @throws std::bad_alloc, EventMessageException |
| void DrbdMon::create_device(PropsMap& event_props) |
| { |
| try |
| { |
| DrbdResource& res = get_resource(event_props); |
| |
| std::unique_ptr<DrbdVolume> vol(DrbdVolume::new_from_props(event_props)); |
| vol->update(event_props); |
| res.add_volume(vol.get()); |
| static_cast<void> (vol.release()); |
| } |
| catch (EventObjectException& nonexistent_object_exc) |
| { |
| // ignored |
| } |
| } |
| |
| // @throws std::bad_alloc, EventMessageException |
| void DrbdMon::create_peer_device(PropsMap& event_props) |
| { |
| try |
| { |
| DrbdResource& res = get_resource(event_props); |
| DrbdConnection& conn = get_connection(res, event_props); |
| |
| std::unique_ptr<DrbdVolume> vol(DrbdVolume::new_from_props(event_props)); |
| vol->update(event_props); |
| vol->set_connection(&conn); |
| conn.add_volume(vol.get()); |
| static_cast<void> (vol.release()); |
| } |
| catch (EventObjectException& nonexistent_object_exc) |
| { |
| // ignored |
| } |
| } |
| |
| // @throws std::bad_alloc, EventMessageException |
| void DrbdMon::create_resource(PropsMap& event_props) |
| { |
| try |
| { |
| std::unique_ptr<DrbdResource> res(DrbdResource::new_from_props(event_props)); |
| res->update(event_props); |
| std::unique_ptr<std::string> res_name(new std::string(res->get_name())); |
| resources_map->insert(res_name.get(), res.get()); |
| static_cast<void> (res.release()); |
| static_cast<void> (res_name.release()); |
| } |
| catch (dsaext::DuplicateInsertException& dup_exc) |
| { |
| log.add_entry( |
| MessageLog::log_level::ALERT, |
| "Duplicate DRBD resource creation reported by the DRBD events source" |
| ); |
| } |
| } |
| |
| // @throws std::bad_alloc, EventMessageException, EventObjectException |
| void DrbdMon::update_connection(PropsMap& event_props) |
| { |
| DrbdResource& res = get_resource(event_props); |
| DrbdConnection& conn = get_connection(res, event_props); |
| conn.update(event_props); |
| } |
| |
| // @throws std::bad_alloc, EventMessageException, EventObjectException |
| void DrbdMon::update_device(PropsMap& event_props) |
| { |
| DrbdResource& res = get_resource(event_props); |
| DrbdVolume& vol = get_device(dynamic_cast<VolumesContainer&> (res), event_props); |
| vol.update(event_props); |
| } |
| |
| // @throws std::bad_alloc, EventMessageException, EventObjectException |
| void DrbdMon::update_peer_device(PropsMap& event_props) |
| { |
| DrbdResource& res = get_resource(event_props); |
| DrbdConnection& conn = get_connection(res, event_props); |
| DrbdVolume& vol = get_device(dynamic_cast<VolumesContainer&> (conn), event_props); |
| vol.update(event_props); |
| } |
| |
| // @throws std::bad_alloc, EventMessageException, EventObjectException |
| void DrbdMon::update_resource(PropsMap& event_props) |
| { |
| DrbdResource& res = get_resource(event_props); |
| res.update(event_props); |
| } |
| |
| // @throws std::bad_alloc, EventMessageException |
| void DrbdMon::destroy_connection(PropsMap& event_props) |
| { |
| try |
| { |
| DrbdResource& res = get_resource(event_props); |
| DrbdConnection& conn = get_connection(res, event_props); |
| res.remove_connection(conn.get_name()); |
| } |
| catch (EventObjectException& nonexistent_object_exc) |
| { |
| // ignored |
| } |
| } |
| |
| // @throws std::bad_alloc, EventMessageException |
| void DrbdMon::destroy_device(PropsMap& event_props) |
| { |
| try |
| { |
| DrbdResource& res = get_resource(event_props); |
| DrbdVolume& vol = get_device(dynamic_cast<VolumesContainer&> (res), event_props); |
| res.remove_volume(vol.get_volume_nr()); |
| } |
| catch (EventObjectException& nonexistent_object_exc) |
| { |
| // ignored |
| } |
| } |
| |
| // @throws std::bad_alloc, EventMessageException |
| void DrbdMon::destroy_peer_device(PropsMap& event_props) |
| { |
| try |
| { |
| DrbdResource& res = get_resource(event_props); |
| DrbdConnection& conn = get_connection(res, event_props); |
| // May report non-existing volume if required |
| static_cast<void> (get_device(dynamic_cast<VolumesContainer&> (conn), event_props)); |
| |
| std::string* vol_nr_str = event_props.get(&DrbdVolume::PROP_KEY_VOL_NR); |
| if (vol_nr_str != nullptr) |
| { |
| try |
| { |
| uint16_t vol_nr = DrbdVolume::parse_volume_nr(*vol_nr_str); |
| conn.remove_volume(vol_nr); |
| } |
| catch (NumberFormatException& nf_exc) |
| { |
| throw EventMessageException(); |
| } |
| } |
| else |
| { |
| throw EventMessageException(); |
| } |
| } |
| catch (EventObjectException& nonexistent_object_exc) |
| { |
| // ignored |
| } |
| } |
| |
| // @throws EventMessageException |
| void DrbdMon::destroy_resource(PropsMap& event_props) |
| { |
| std::string* res_name = event_props.get(&DrbdResource::PROP_KEY_RES_NAME); |
| if (res_name != nullptr) |
| { |
| ResourcesMap::Node* node = resources_map->get_node(res_name); |
| if (node != nullptr) |
| { |
| delete node->get_key(); |
| delete node->get_value(); |
| resources_map->remove_node(node); |
| } |
| } |
| else |
| { |
| throw EventMessageException(); |
| } |
| } |
| |
| // @throws std::bad_alloc, EventMessageException, EventObjectException |
| DrbdConnection& DrbdMon::get_connection(DrbdResource& res, PropsMap& event_props) |
| { |
| DrbdConnection* conn {nullptr}; |
| std::string* conn_name = event_props.get(&DrbdConnection::PROP_KEY_CONN_NAME); |
| if (conn_name != nullptr) |
| { |
| conn = res.get_connection(*conn_name); |
| } |
| else |
| { |
| throw EventMessageException(); |
| } |
| if (conn == nullptr) |
| { |
| std::string error_message = "Non-existent connection "; |
| error_message += *conn_name; |
| error_message += " referenced by the DRBD events source"; |
| log.add_entry( |
| MessageLog::log_level::ALERT, |
| error_message |
| ); |
| throw EventObjectException(); |
| } |
| return *conn; |
| } |
| |
| // @throws EventMessageException, EventObjectException |
| DrbdVolume& DrbdMon::get_device(VolumesContainer& vol_con, PropsMap& event_props) |
| { |
| DrbdVolume* vol {nullptr}; |
| std::string* vol_nr_str = event_props.get(&DrbdVolume::PROP_KEY_VOL_NR); |
| if (vol_nr_str != nullptr) |
| { |
| try |
| { |
| uint16_t vol_nr = DrbdVolume::parse_volume_nr(*vol_nr_str); |
| vol = vol_con.get_volume(vol_nr); |
| } |
| catch (NumberFormatException& nf_exc) |
| { |
| throw EventMessageException(); |
| } |
| } |
| else |
| { |
| throw EventMessageException(); |
| } |
| if (vol == nullptr) |
| { |
| log.add_entry( |
| MessageLog::log_level::ALERT, |
| "Non-existent volume id referenced by the DRBD events source" |
| ); |
| throw EventObjectException(); |
| } |
| return *vol; |
| } |
| |
| // @throws std::bad_alloc, EventMessageException, EventObjectException |
| DrbdResource& DrbdMon::get_resource(PropsMap& event_props) |
| { |
| DrbdResource* res {nullptr}; |
| std::string* res_name = event_props.get(&DrbdResource::PROP_KEY_RES_NAME); |
| if (res_name != nullptr) |
| { |
| res = resources_map->get(res_name); |
| } |
| else |
| { |
| throw EventMessageException(); |
| } |
| if (res == nullptr) |
| { |
| std::string error_message = "Non-existent resource "; |
| error_message += *res_name; |
| error_message += " referenced by the DRBD events source"; |
| log.add_entry( |
| MessageLog::log_level::ALERT, |
| error_message |
| ); |
| throw EventObjectException(); |
| } |
| return *res; |
| } |
| |
| // @throws std::bad_alloc |
| void DrbdMon::setup_hotkeys_info() |
| { |
| hotkeys_info->append(&HOTKEY_QUIT, &DESC_QUIT); |
| hotkeys_info->append(&HOTKEY_REPAINT, &DESC_REPAINT); |
| hotkeys_info->append(&HOTKEY_CLEAR_MSG, &DESC_CLEAR_MSG); |
| } |
| |
| // Frees resources |
| // @throws std::bad_alloc |
| void DrbdMon::cleanup( |
| PropsMap* event_props, |
| EventsIo* events_io |
| ) |
| { |
| if (event_props != nullptr) |
| { |
| clear_event_props(*event_props); |
| } |
| |
| if (events_io != nullptr) |
| { |
| try |
| { |
| events_io->restore_terminal(); |
| } |
| catch (EventsIoException&) |
| { |
| log.add_entry( |
| MessageLog::log_level::WARN, |
| "Restoring the terminal mode failed" |
| ); |
| } |
| } |
| } |
| |
| // @throws std::bad_alloc |
| void DrbdMon::configure_options() |
| { |
| options = std::unique_ptr<OptionsMap>(new OptionsMap(&comparators::compare_string)); |
| |
| try |
| { |
| Configurator& collector = dynamic_cast<Configurator&> (*this); |
| size_t slot = 0; |
| while (configurables[slot] != nullptr) |
| { |
| configurables[slot]->announce_options(collector); |
| ++slot; |
| } |
| |
| { |
| std::unique_ptr<Args> arg_list(new Args(arg_count, arg_values)); |
| while (arg_list->has_next()) |
| { |
| std::string option_key(arg_list->next()); |
| if (option_key.find("--") == 0) |
| { |
| option_key = option_key.substr(2, std::string::npos); |
| option_entry* entry = options->get(&option_key); |
| if (entry != nullptr) |
| { |
| if (entry->option.is_flag) |
| { |
| entry->owner.set_flag(option_key); |
| } |
| else |
| { |
| char* arg = arg_list->next(); |
| if (arg != nullptr) |
| { |
| std::string option_value(arg); |
| entry->owner.set_option(option_key, option_value); |
| } |
| else |
| { |
| std::string error_message = "Missing value for argument key '--"; |
| error_message += option_key; |
| error_message += "'"; |
| log.add_entry(MessageLog::log_level::ALERT, error_message); |
| } |
| } |
| } |
| else |
| { |
| std::string error_message = "Unknown argument key '--"; |
| error_message += option_key; |
| error_message += "'"; |
| log.add_entry(MessageLog::log_level::ALERT, error_message); |
| } |
| } |
| else |
| { |
| std::string error_message = "Malformed argument '"; |
| error_message += option_key; |
| error_message += "' ignored"; |
| log.add_entry(MessageLog::log_level::ALERT, error_message); |
| } |
| } |
| } |
| } |
| catch (ConfigurationException&) |
| { |
| options_cleanup(); |
| throw; |
| } |
| catch (std::bad_alloc&) |
| { |
| options_cleanup(); |
| throw; |
| } |
| |
| options_cleanup(); |
| } |
| |
| void DrbdMon::options_cleanup() noexcept |
| { |
| if (options != nullptr) |
| { |
| OptionsMap::ValuesIterator cleanup_iter(*options); |
| while (cleanup_iter.has_next()) |
| { |
| delete cleanup_iter.next(); |
| } |
| } |
| } |
| |
| // @throws std::bad_alloc |
| void DrbdMon::add_config_option(Configurable& owner, const ConfigOption& conf_option) |
| { |
| if (options != nullptr) |
| { |
| try |
| { |
| std::unique_ptr<DrbdMon::option_entry> entry = std::unique_ptr<DrbdMon::option_entry>( |
| new DrbdMon::option_entry { owner, conf_option } |
| ); |
| options->insert(&conf_option.key, entry.get()); |
| static_cast<void> (entry.release()); |
| } |
| catch (dsaext::DuplicateInsertException&) |
| { |
| std::string error_message = "Duplicate configuration option '--"; |
| error_message += conf_option.key; |
| error_message += "'"; |
| log.add_entry(MessageLog::log_level::ALERT, error_message); |
| } |
| } |
| } |
| |
| // @throws std::bad_alloc |
| void DrbdMon::announce_options(Configurator& collector) |
| { |
| Configurable& owner = dynamic_cast<Configurable&> (*this); |
| collector.add_config_option(owner, OPT_HELP); |
| collector.add_config_option(owner, OPT_VERSION); |
| } |
| |
| void DrbdMon::options_help() noexcept |
| { |
| std::fputs("DrbdMon configuration options:\n", stderr); |
| std::fputs(" --version Display version information\n", stderr); |
| std::fputs(" --help Display help\n", stderr); |
| std::fputc('\n', stderr); |
| std::fflush(stderr); |
| } |
| |
| // @throws std::bad_alloc |
| void DrbdMon::set_flag(std::string& key) |
| { |
| if (key == OPT_HELP.key) |
| { |
| size_t slot = 0; |
| while (configurables[slot] != nullptr) |
| { |
| configurables[slot]->options_help(); |
| ++slot; |
| } |
| throw ConfigurationException(); |
| } |
| else |
| if (key == OPT_VERSION.key) |
| { |
| std::fprintf(stdout, "%s v%s (%s)\n", PROGRAM_NAME.c_str(), VERSION.c_str(), GITHASH); |
| throw ConfigurationException(); |
| } |
| } |
| |
| // @throws std::bad_alloc |
| void DrbdMon::set_option(std::string& key, std::string& value) |
| { |
| // no-op; the DrbdMon instance does not have any configurable options at this time |
| } |