diff --git a/rak/timer.h b/rak/timer.h index 91ff75e8..06a3bbac 100644 --- a/rak/timer.h +++ b/rak/timer.h @@ -57,6 +57,7 @@ class timer { timeval tval() const { return (timeval) { m_time / 1000000, m_time % 1000000}; } static timer current(); + static timer from_seconds(uint32_t seconds) { return rak::timer((uint64_t)seconds * 1000000); } bool operator < (const timer& t) const { return m_time < t.m_time; } bool operator > (const timer& t) const { return m_time > t.m_time; } diff --git a/src/command_scheduler.cc b/src/command_scheduler.cc index 969c0444..1f0daba0 100644 --- a/src/command_scheduler.cc +++ b/src/command_scheduler.cc @@ -133,7 +133,7 @@ CommandScheduler::parse(const std::string& arg) { item->set_command(rak::trim(std::string(command))); item->set_interval(interval); - item->enable((cachedTime + rak::timer(absolute) * 1000000).round_seconds()); + item->enable((cachedTime + rak::timer::from_seconds(absolute)).round_seconds()); } uint32_t diff --git a/src/command_scheduler_item.cc b/src/command_scheduler_item.cc index bf33314e..6e46a5db 100644 --- a/src/command_scheduler_item.cc +++ b/src/command_scheduler_item.cc @@ -77,7 +77,7 @@ CommandSchedulerItem::next_time_scheduled() const { // This should be done in a non-looping manner. do { - next += rak::timer(m_interval) * 1000000; + next += rak::timer::from_seconds(m_interval); } while (next <= cachedTime.round_seconds()); return next; diff --git a/src/control.cc b/src/control.cc index df98ec34..e5368099 100644 --- a/src/control.cc +++ b/src/control.cc @@ -128,7 +128,7 @@ Control::handle_shutdown() { m_core->shutdown(false); if (!m_taskShutdown.is_queued()) - priority_queue_insert(&taskScheduler, &m_taskShutdown, cachedTime + 5 * 1000000); + priority_queue_insert(&taskScheduler, &m_taskShutdown, cachedTime + rak::timer::from_seconds(5)); } else { m_core->shutdown(true); diff --git a/src/core/download_store.cc b/src/core/download_store.cc index 0e3ed0d7..47fb92a4 100644 --- a/src/core/download_store.cc +++ b/src/core/download_store.cc @@ -65,7 +65,7 @@ DownloadStore::enable(bool lock) { m_lockfile.set_path(std::string()); if (!m_lockfile.try_lock()) - throw torrent::input_error("Could not lock session directory: \"" + m_path + "\", held by \"" + m_lockfile.locked_by() + "\"."); + throw torrent::input_error("Could not lock session directory: \"" + m_path + "\", held by \"" + m_lockfile.locked_by_as_string() + "\"."); } void diff --git a/src/display/window_download_list.cc b/src/display/window_download_list.cc index 4e5f293e..afcf0a5d 100644 --- a/src/display/window_download_list.cc +++ b/src/display/window_download_list.cc @@ -60,7 +60,7 @@ WindowDownloadList::~WindowDownloadList() { void WindowDownloadList::redraw() { - m_slotSchedule(this, (cachedTime + 1000000).round_seconds()); + m_slotSchedule(this, (cachedTime + rak::timer::from_seconds(1)).round_seconds()); m_canvas->erase(); diff --git a/src/display/window_download_statusbar.cc b/src/display/window_download_statusbar.cc index 2db1db58..866bb99d 100644 --- a/src/display/window_download_statusbar.cc +++ b/src/display/window_download_statusbar.cc @@ -54,7 +54,7 @@ WindowDownloadStatusbar::WindowDownloadStatusbar(core::Download* d) : void WindowDownloadStatusbar::redraw() { - m_slotSchedule(this, (cachedTime + 1000000).round_seconds()); + m_slotSchedule(this, (cachedTime + rak::timer::from_seconds(1)).round_seconds()); m_canvas->erase(); diff --git a/src/display/window_file_list.cc b/src/display/window_file_list.cc index ba7ec3d3..509d2c78 100644 --- a/src/display/window_file_list.cc +++ b/src/display/window_file_list.cc @@ -69,7 +69,7 @@ hack_wstring(const std::string& src) { void WindowFileList::redraw() { - m_slotSchedule(this, (cachedTime + 10 * 1000000).round_seconds()); + m_slotSchedule(this, (cachedTime + rak::timer::from_seconds(10)).round_seconds()); m_canvas->erase(); if (m_download->get_download().size_file_entries() == 0 || diff --git a/src/display/window_http_queue.cc b/src/display/window_http_queue.cc index 58ef04d2..7b36bf71 100644 --- a/src/display/window_http_queue.cc +++ b/src/display/window_http_queue.cc @@ -58,7 +58,7 @@ WindowHttpQueue::WindowHttpQueue(core::HttpQueue* q) : void WindowHttpQueue::redraw() { - m_slotSchedule(this, (cachedTime + 1000000).round_seconds()); + m_slotSchedule(this, (cachedTime + rak::timer::from_seconds(1)).round_seconds()); cleanup_list(); @@ -147,7 +147,7 @@ WindowHttpQueue::receive_erase(core::CurlGet* h) { throw std::logic_error("WindowHttpQueue::receive_erase(...) tried to remove an object we don't have"); itr->m_http = NULL; - itr->m_timer = cachedTime + 10000000; + itr->m_timer = cachedTime + rak::timer::from_seconds(1); mark_dirty(); } diff --git a/src/display/window_log.cc b/src/display/window_log.cc index 562ecea3..e8cd96a6 100644 --- a/src/display/window_log.cc +++ b/src/display/window_log.cc @@ -60,7 +60,7 @@ WindowLog::~WindowLog() { WindowLog::iterator WindowLog::find_older() { - return m_log->find_older(cachedTime - 60*1000000); + return m_log->find_older(cachedTime - rak::timer::from_seconds(60)); } void diff --git a/src/display/window_log_complete.cc b/src/display/window_log_complete.cc index 2ffb64a4..02087154 100644 --- a/src/display/window_log_complete.cc +++ b/src/display/window_log_complete.cc @@ -58,7 +58,7 @@ WindowLogComplete::~WindowLogComplete() { WindowLogComplete::iterator WindowLogComplete::find_older() { - return m_log->find_older(cachedTime - 60*1000000); + return m_log->find_older(cachedTime - rak::timer::from_seconds(60)); } void diff --git a/src/display/window_peer_info.cc b/src/display/window_peer_info.cc index a08ae093..feace23b 100644 --- a/src/display/window_peer_info.cc +++ b/src/display/window_peer_info.cc @@ -57,7 +57,7 @@ WindowPeerInfo::WindowPeerInfo(core::Download* d, PList* l, PList::iterator* f) void WindowPeerInfo::redraw() { - m_slotSchedule(this, (cachedTime + 1000000).round_seconds()); + m_slotSchedule(this, (cachedTime + rak::timer::from_seconds(1)).round_seconds()); m_canvas->erase(); int y = 0; diff --git a/src/display/window_peer_list.cc b/src/display/window_peer_list.cc index c48d90cb..29e5789f 100644 --- a/src/display/window_peer_list.cc +++ b/src/display/window_peer_list.cc @@ -56,7 +56,7 @@ WindowPeerList::WindowPeerList(core::Download* d, PList* l, PList::iterator* f) void WindowPeerList::redraw() { - m_slotSchedule(this, (cachedTime + 1000000).round_seconds()); + m_slotSchedule(this, (cachedTime + rak::timer::from_seconds(1)).round_seconds()); m_canvas->erase(); int x = 2; diff --git a/src/display/window_statusbar.cc b/src/display/window_statusbar.cc index 50bceacc..824de71d 100644 --- a/src/display/window_statusbar.cc +++ b/src/display/window_statusbar.cc @@ -54,7 +54,7 @@ WindowStatusbar::WindowStatusbar(Control* c) : void WindowStatusbar::redraw() { - m_slotSchedule(this, (cachedTime + 1000000).round_seconds()); + m_slotSchedule(this, (cachedTime + rak::timer::from_seconds(1)).round_seconds()); m_canvas->erase(); diff --git a/src/display/window_title.cc b/src/display/window_title.cc index 4984f768..f7334dd4 100644 --- a/src/display/window_title.cc +++ b/src/display/window_title.cc @@ -48,7 +48,7 @@ WindowTitle::WindowTitle(const std::string& s) : void WindowTitle::redraw() { - m_slotSchedule(this, (cachedTime + 1000000).round_seconds()); + m_slotSchedule(this, (cachedTime + rak::timer::from_seconds(1)).round_seconds()); m_canvas->erase(); m_canvas->print(std::max(0, (m_canvas->get_width() - (int)m_title.size()) / 2 - 4), 0, diff --git a/src/display/window_tracker_list.cc b/src/display/window_tracker_list.cc index 7f60eede..23af1b2f 100644 --- a/src/display/window_tracker_list.cc +++ b/src/display/window_tracker_list.cc @@ -55,7 +55,7 @@ WindowTrackerList::WindowTrackerList(core::Download* d, unsigned int* focus) : void WindowTrackerList::redraw() { // TODO: Make this depend on tracker signal. - m_slotSchedule(this, (cachedTime + 10 * 1000000).round_seconds()); + m_slotSchedule(this, (cachedTime + rak::timer::from_seconds(10)).round_seconds()); m_canvas->erase(); int pos = 0; diff --git a/src/main.cc b/src/main.cc index a5f24516..9d4b7e32 100644 --- a/src/main.cc +++ b/src/main.cc @@ -133,7 +133,7 @@ load_arg_torrents(Control* c, char** first, char** last) { rak::timer client_next_timeout() { if (taskScheduler.empty()) - return 60 * 1000000; + return rak::timer::from_seconds(60); else if (taskScheduler.top()->time() <= cachedTime) return 0; else diff --git a/src/option_handler_rules.cc b/src/option_handler_rules.cc index 40e7f827..8276d411 100644 --- a/src/option_handler_rules.cc +++ b/src/option_handler_rules.cc @@ -160,7 +160,7 @@ initialize_option_handler(Control* c) { variables->insert("use_udp_trackers", new utils::VariableAny("yes")); variables->insert("port_random", new utils::VariableAny("yes")); variables->insert("session", new utils::VariableSlotString<>(NULL, rak::mem_fn(&control->core()->download_store(), &core::DownloadStore::set_path))); - variables->insert("session_lock", new utils::VariableAny("no")); + variables->insert("session_lock", new utils::VariableAny("yes")); variables->insert("session_on_completion", new utils::VariableAny("yes")); variables->insert("connection_leech", new utils::VariableAny("leech")); diff --git a/src/ui/download_list.cc b/src/ui/download_list.cc index 7d2b7b08..7f5ed04f 100644 --- a/src/ui/download_list.cc +++ b/src/ui/download_list.cc @@ -359,7 +359,7 @@ void DownloadList::task_update() { m_windowLog->receive_update(); - priority_queue_insert(&taskScheduler, &m_taskUpdate, (cachedTime + 1000000).round_seconds()); + priority_queue_insert(&taskScheduler, &m_taskUpdate, (cachedTime + rak::timer::from_seconds(1)).round_seconds()); } void diff --git a/src/utils/lockfile.cc b/src/utils/lockfile.cc index 42428b71..69516ca6 100644 --- a/src/utils/lockfile.cc +++ b/src/utils/lockfile.cc @@ -37,8 +37,11 @@ #include "config.h" #include +#include #include +#include #include +#include #include #include #include @@ -47,6 +50,31 @@ namespace utils { +struct lockfile_valid_char { + bool operator () (char c) { + return !std::isgraph(c); + } +}; + +struct lockfile_valid_hostname { + bool operator () (char c) { + return !std::isgraph(c) || c == ':'; + } +}; + +bool +Lockfile::is_stale() { + process_type process = locked_by(); + + char buf[256]; + + if (process.second <= 0 || + ::gethostname(buf, 255) != 0 || buf != process.first) + return false; + + return ::kill(process.second, 0) != 0 && errno != EPERM; +} + bool Lockfile::try_lock() { if (m_path.empty()) { @@ -54,6 +82,9 @@ Lockfile::try_lock() { return true; } + if (is_stale()) + ::unlink(m_path.c_str()); + // Just do a simple locking for now that isn't safe for network // devices. int fd = ::open(m_path.c_str(), O_RDWR | O_CREAT | O_EXCL, 0444); @@ -85,25 +116,46 @@ Lockfile::unlock() { return ::unlink(m_path.c_str()) != -1; } -struct lockfile_valid_char { - bool operator () (char c) { - return !std::isgraph(c); - } -}; - -std::string +Lockfile::process_type Lockfile::locked_by() const { int fd = ::open(m_path.c_str(), O_RDONLY); if (fd < 0) - return ""; + return process_type(std::string(), 0); - char buf[256]; - int pos = read(fd, buf, 255); + char first[256]; + char* last = first + std::max(read(fd, first, 255), 0); + *last = '\0'; ::close(fd); - return std::string(buf, std::find_if(buf, buf + std::max(pos, 0), lockfile_valid_char())); + char* endHostname = std::find_if(first, last, lockfile_valid_hostname()); + char* beginPid = endHostname; + char* endPid; + + long long int pid; + + if (beginPid + 2 >= last || + *(beginPid++) != ':' || + *(beginPid++) != '+' || + (pid = strtoll(beginPid, &endPid, 10)) == 0 || + endPid == NULL) + return process_type(std::string(), 0); + + return process_type(std::string(first, endHostname), pid); +} + +std::string +Lockfile::locked_by_as_string() const { + process_type p = locked_by(); + + if (p.first.empty()) + return ""; + + std::stringstream str; + str << p.first << ":+" << p.second; + + return str.str(); } } diff --git a/src/utils/lockfile.h b/src/utils/lockfile.h index d0218ff5..087e1ad9 100644 --- a/src/utils/lockfile.h +++ b/src/utils/lockfile.h @@ -44,14 +44,18 @@ #define RTORRENT_UTILS_LOCKFILE_H #include +#include namespace utils { class Lockfile { public: + typedef std::pair process_type; + Lockfile() : m_locked(false) {} bool is_locked() const { return m_locked; } + bool is_stale(); // If the path is empty no lock file will be created, although // is_locked() will return true. @@ -61,7 +65,8 @@ public: const std::string& path() const { return m_path; } void set_path(const std::string& path) { m_path = path; } - std::string locked_by() const; + std::string locked_by_as_string() const; + process_type locked_by() const; private: std::string m_path;