| 
									
										
										
										
											2017-12-01 08:44:45 -03:00
										 |  |  | // Copyright (c) 2017 GitHub, Inc.
 | 
					
						
							|  |  |  | // Use of this source code is governed by the MIT license that can be
 | 
					
						
							|  |  |  | // found in the LICENSE file.
 | 
					
						
							|  |  |  | #include "atom/browser/lib/power_observer_linux.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <unistd.h>
 | 
					
						
							|  |  |  | #include <uv.h>
 | 
					
						
							|  |  |  | #include <iostream>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "base/bind.h"
 | 
					
						
							|  |  |  | #include "device/bluetooth/dbus/dbus_thread_manager_linux.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const char kLogindServiceName[] = "org.freedesktop.login1"; | 
					
						
							|  |  |  | const char kLogindObjectPath[] = "/org/freedesktop/login1"; | 
					
						
							|  |  |  | const char kLogindManagerInterface[] = "org.freedesktop.login1.Manager"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::string get_executable_basename() { | 
					
						
							|  |  |  |   char buf[4096]; | 
					
						
							|  |  |  |   size_t buf_size = sizeof(buf); | 
					
						
							|  |  |  |   std::string rv("electron"); | 
					
						
							|  |  |  |   if (!uv_exepath(buf, &buf_size)) { | 
					
						
							|  |  |  |     rv = strrchr(static_cast<const char*>(buf), '/') + 1; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return std::move(rv); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace atom { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PowerObserverLinux::PowerObserverLinux() | 
					
						
							|  |  |  |     : lock_owner_name_(get_executable_basename()), weak_ptr_factory_(this) { | 
					
						
							|  |  |  |   auto dbus_thread_manager = bluez::DBusThreadManagerLinux::Get(); | 
					
						
							|  |  |  |   if (dbus_thread_manager) { | 
					
						
							|  |  |  |     bus_ = dbus_thread_manager->GetSystemBus(); | 
					
						
							|  |  |  |     if (bus_) { | 
					
						
							|  |  |  |       logind_ = bus_->GetObjectProxy(kLogindServiceName, | 
					
						
							|  |  |  |                                      dbus::ObjectPath(kLogindObjectPath)); | 
					
						
							|  |  |  |       logind_->WaitForServiceToBeAvailable( | 
					
						
							|  |  |  |           base::Bind(&PowerObserverLinux::OnLoginServiceAvailable, | 
					
						
							|  |  |  |                      weak_ptr_factory_.GetWeakPtr())); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       LOG(WARNING) << "Failed to get system bus connection"; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     LOG(WARNING) << "DBusThreadManagerLinux instance isn't available"; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PowerObserverLinux::OnLoginServiceAvailable(bool service_available) { | 
					
						
							|  |  |  |   if (!service_available) { | 
					
						
							|  |  |  |     LOG(WARNING) << kLogindServiceName << " not available"; | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-12-07 10:10:40 -03:00
										 |  |  |   // Connect to PrepareForShutdown/PrepareForSleep signals
 | 
					
						
							|  |  |  |   logind_->ConnectToSignal(kLogindManagerInterface, "PrepareForShutdown", | 
					
						
							|  |  |  |                            base::Bind(&PowerObserverLinux::OnPrepareForShutdown, | 
					
						
							|  |  |  |                                       weak_ptr_factory_.GetWeakPtr()), | 
					
						
							|  |  |  |                            base::Bind(&PowerObserverLinux::OnSignalConnected, | 
					
						
							|  |  |  |                                       weak_ptr_factory_.GetWeakPtr())); | 
					
						
							| 
									
										
										
										
											2017-12-01 08:44:45 -03:00
										 |  |  |   logind_->ConnectToSignal(kLogindManagerInterface, "PrepareForSleep", | 
					
						
							|  |  |  |                            base::Bind(&PowerObserverLinux::OnPrepareForSleep, | 
					
						
							|  |  |  |                                       weak_ptr_factory_.GetWeakPtr()), | 
					
						
							|  |  |  |                            base::Bind(&PowerObserverLinux::OnSignalConnected, | 
					
						
							|  |  |  |                                       weak_ptr_factory_.GetWeakPtr())); | 
					
						
							| 
									
										
										
										
											2017-12-07 10:10:40 -03:00
										 |  |  |   // Take sleep inhibit lock
 | 
					
						
							| 
									
										
										
										
											2017-12-21 07:49:54 -03:00
										 |  |  |   BlockSleep(); | 
					
						
							| 
									
										
										
										
											2017-12-01 08:44:45 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-21 07:49:54 -03:00
										 |  |  | void PowerObserverLinux::BlockSleep() { | 
					
						
							| 
									
										
										
										
											2017-12-01 08:44:45 -03:00
										 |  |  |   dbus::MethodCall sleep_inhibit_call(kLogindManagerInterface, "Inhibit"); | 
					
						
							|  |  |  |   dbus::MessageWriter inhibit_writer(&sleep_inhibit_call); | 
					
						
							|  |  |  |   inhibit_writer.AppendString("sleep");                               // what
 | 
					
						
							|  |  |  |   // Use the executable name as the lock owner, which will list rebrands of the
 | 
					
						
							|  |  |  |   // electron executable as separate entities.
 | 
					
						
							|  |  |  |   inhibit_writer.AppendString(lock_owner_name_);                      // who
 | 
					
						
							|  |  |  |   inhibit_writer.AppendString("Application cleanup before suspend");  // why
 | 
					
						
							|  |  |  |   inhibit_writer.AppendString("delay");                               // mode
 | 
					
						
							|  |  |  |   logind_->CallMethod(&sleep_inhibit_call, | 
					
						
							|  |  |  |                       dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, | 
					
						
							|  |  |  |                       base::Bind(&PowerObserverLinux::OnInhibitResponse, | 
					
						
							|  |  |  |                                  weak_ptr_factory_.GetWeakPtr(), &sleep_lock_)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-21 07:49:54 -03:00
										 |  |  | void PowerObserverLinux::UnblockSleep() { | 
					
						
							|  |  |  |   sleep_lock_.reset(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-07 10:10:40 -03:00
										 |  |  | void PowerObserverLinux::BlockShutdown() { | 
					
						
							| 
									
										
										
										
											2017-12-21 08:12:25 -03:00
										 |  |  |   if (shutdown_lock_.is_valid()) { | 
					
						
							| 
									
										
										
										
											2017-12-07 10:10:40 -03:00
										 |  |  |     LOG(WARNING) << "Trying to subscribe to shutdown multiple times"; | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   dbus::MethodCall shutdown_inhibit_call(kLogindManagerInterface, "Inhibit"); | 
					
						
							|  |  |  |   dbus::MessageWriter inhibit_writer(&shutdown_inhibit_call); | 
					
						
							|  |  |  |   inhibit_writer.AppendString("shutdown");                 // what
 | 
					
						
							|  |  |  |   inhibit_writer.AppendString(lock_owner_name_);           // who
 | 
					
						
							|  |  |  |   inhibit_writer.AppendString("Ensure a clean shutdown");  // why
 | 
					
						
							|  |  |  |   inhibit_writer.AppendString("delay");                    // mode
 | 
					
						
							|  |  |  |   logind_->CallMethod( | 
					
						
							|  |  |  |       &shutdown_inhibit_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, | 
					
						
							|  |  |  |       base::Bind(&PowerObserverLinux::OnInhibitResponse, | 
					
						
							| 
									
										
										
										
											2017-12-21 08:12:25 -03:00
										 |  |  |                  weak_ptr_factory_.GetWeakPtr(), &shutdown_lock_)); | 
					
						
							| 
									
										
										
										
											2017-12-07 10:10:40 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PowerObserverLinux::UnblockShutdown() { | 
					
						
							| 
									
										
										
										
											2017-12-21 08:12:25 -03:00
										 |  |  |   if (!shutdown_lock_.is_valid()) { | 
					
						
							| 
									
										
										
										
											2017-12-07 10:10:40 -03:00
										 |  |  |     LOG(WARNING) | 
					
						
							|  |  |  |         << "Trying to unsubscribe to shutdown without being subscribed"; | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-12-21 08:12:25 -03:00
										 |  |  |   shutdown_lock_.reset(); | 
					
						
							| 
									
										
										
										
											2017-12-07 10:10:40 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-05 15:49:43 +09:00
										 |  |  | void PowerObserverLinux::SetShutdownHandler(base::Callback<bool()> handler) { | 
					
						
							|  |  |  |   should_shutdown_ = std::move(handler); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-01 08:44:45 -03:00
										 |  |  | void PowerObserverLinux::OnInhibitResponse(base::ScopedFD* scoped_fd, | 
					
						
							|  |  |  |                                            dbus::Response* response) { | 
					
						
							|  |  |  |   dbus::MessageReader reader(response); | 
					
						
							|  |  |  |   reader.PopFileDescriptor(scoped_fd); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PowerObserverLinux::OnPrepareForSleep(dbus::Signal* signal) { | 
					
						
							|  |  |  |   dbus::MessageReader reader(signal); | 
					
						
							| 
									
										
										
										
											2017-12-14 08:39:00 -03:00
										 |  |  |   bool suspending; | 
					
						
							|  |  |  |   if (!reader.PopBool(&suspending)) { | 
					
						
							| 
									
										
										
										
											2017-12-01 08:44:45 -03:00
										 |  |  |     LOG(ERROR) << "Invalid signal: " << signal->ToString(); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-12-14 08:39:00 -03:00
										 |  |  |   if (suspending) { | 
					
						
							| 
									
										
										
										
											2017-12-01 08:44:45 -03:00
										 |  |  |     OnSuspend(); | 
					
						
							| 
									
										
										
										
											2017-12-21 07:49:54 -03:00
										 |  |  |     UnblockSleep(); | 
					
						
							| 
									
										
										
										
											2017-12-01 08:44:45 -03:00
										 |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2017-12-21 07:49:54 -03:00
										 |  |  |     BlockSleep(); | 
					
						
							| 
									
										
										
										
											2017-12-01 08:44:45 -03:00
										 |  |  |     OnResume(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-07 10:10:40 -03:00
										 |  |  | void PowerObserverLinux::OnPrepareForShutdown(dbus::Signal* signal) { | 
					
						
							|  |  |  |   dbus::MessageReader reader(signal); | 
					
						
							| 
									
										
										
										
											2017-12-14 08:39:00 -03:00
										 |  |  |   bool shutting_down; | 
					
						
							|  |  |  |   if (!reader.PopBool(&shutting_down)) { | 
					
						
							| 
									
										
										
										
											2017-12-07 10:10:40 -03:00
										 |  |  |     LOG(ERROR) << "Invalid signal: " << signal->ToString(); | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2017-12-14 08:39:00 -03:00
										 |  |  |   if (shutting_down) { | 
					
						
							| 
									
										
										
										
											2018-02-05 16:13:35 +09:00
										 |  |  |     if (!should_shutdown_ || should_shutdown_.Run()) { | 
					
						
							| 
									
										
										
										
											2017-12-07 10:10:40 -03:00
										 |  |  |       // The user didn't try to prevent shutdown. Release the lock and allow the
 | 
					
						
							|  |  |  |       // shutdown to continue normally.
 | 
					
						
							| 
									
										
										
										
											2017-12-21 08:12:25 -03:00
										 |  |  |       shutdown_lock_.reset(); | 
					
						
							| 
									
										
										
										
											2017-12-07 10:10:40 -03:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-01 08:44:45 -03:00
										 |  |  | void PowerObserverLinux::OnSignalConnected(const std::string& interface, | 
					
						
							|  |  |  |                                            const std::string& signal, | 
					
						
							|  |  |  |                                            bool success) { | 
					
						
							|  |  |  |   LOG_IF(WARNING, !success) << "Failed to connect to " << signal; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }  // namespace atom
 |