AU: DBus support.
A few changes to support dbus in the Update Engine daemon:
- SConstruct: build marshaller for the dbus signal.
- Update Attempter: respond to dbus calls and broadcast status on dbus
signal.
- Update Engine Client: flag to listen for status updates.
- Also, cleanup outdated code in Omaha Response Handler.
BUG=None
TEST=attached unittests/on device tests
Review URL: http://codereview.chromium.org/2037002
diff --git a/SConstruct b/SConstruct
index 5adf16b..1cf837a 100644
--- a/SConstruct
+++ b/SConstruct
@@ -5,14 +5,14 @@
import os
# Protobuffer compilation
-""" Inputs:
- target: list of targets to compile to
- source: list of sources to compile
- env: the scons environment in which we are compiling
- Outputs:
- target: the list of targets we'll emit
- source: the list of sources we'll compile"""
def ProtocolBufferEmitter(target, source, env):
+ """ Inputs:
+ target: list of targets to compile to
+ source: list of sources to compile
+ env: the scons environment in which we are compiling
+ Outputs:
+ target: the list of targets we'll emit
+ source: the list of sources we'll compile"""
output = str(source[0])
output = output[0:output.rfind('.proto')]
target = [
@@ -21,14 +21,14 @@
]
return target, source
-""" Inputs:
- source: list of sources to process
- target: list of targets to generate
- env: scons environment in which we are working
- for_signature: unused
- Outputs: a list of commands to execute to generate the targets from
- the sources."""
def ProtocolBufferGenerator(source, target, env, for_signature):
+ """ Inputs:
+ source: list of sources to process
+ target: list of targets to generate
+ env: scons environment in which we are working
+ for_signature: unused
+ Outputs: a list of commands to execute to generate the targets from
+ the sources."""
commands = [
'/usr/bin/protoc '
' --proto_path . ${SOURCES} --cpp_out .']
@@ -39,14 +39,14 @@
single_source = 1,
suffix = '.pb.cc')
-""" Inputs:
- target: unused
- source: list containing the source .xml file
- env: the scons environment in which we are compiling
- Outputs:
- target: the list of targets we'll emit
- source: the list of sources we'll process"""
def DbusBindingsEmitter(target, source, env):
+ """ Inputs:
+ target: unused
+ source: list containing the source .xml file
+ env: the scons environment in which we are compiling
+ Outputs:
+ target: the list of targets we'll emit
+ source: the list of sources we'll process"""
output = str(source[0])
output = output[0:output.rfind('.xml')]
target = [
@@ -55,14 +55,14 @@
]
return target, source
-""" Inputs:
- source: list of sources to process
- target: list of targets to generate
- env: scons environment in which we are working
- for_signature: unused
- Outputs: a list of commands to execute to generate the targets from
- the sources."""
def DbusBindingsGenerator(source, target, env, for_signature):
+ """ Inputs:
+ source: list of sources to process
+ target: list of targets to generate
+ env: scons environment in which we are working
+ for_signature: unused
+ Outputs: a list of commands to execute to generate the targets from
+ the sources."""
commands = []
for target_file in target:
if str(target_file).endswith('.dbusserver.h'):
@@ -79,6 +79,46 @@
single_source = 1,
suffix = 'dbusclient.h')
+def GlibMarshalEmitter(target, source, env):
+ """ Inputs:
+ target: unused
+ source: list containing the source .list file
+ env: the scons environment in which we are compiling
+ Outputs:
+ target: the list of targets we'll emit
+ source: the list of sources we'll process"""
+ output = str(source[0])
+ output = output[0:output.rfind('.list')]
+ target = [
+ output + '.glibmarshal.c',
+ output + '.glibmarshal.h'
+ ]
+ return target, source
+
+def GlibMarshalGenerator(source, target, env, for_signature):
+ """ Inputs:
+ source: list of sources to process
+ target: list of targets to generate
+ env: scons environment in which we are working
+ for_signature: unused
+ Outputs: a list of commands to execute to generate the targets from
+ the sources."""
+ commands = []
+ for target_file in target:
+ if str(target_file).endswith('.glibmarshal.h'):
+ mode_flag = '--header '
+ else:
+ mode_flag = '--body '
+ cmd = '/usr/bin/glib-genmarshal %s --prefix=update_engine ' \
+ '%s > %s' % (mode_flag, str(source[0]), str(target_file))
+ commands.append(cmd)
+ return commands
+
+glib_marshal_builder = Builder(generator = GlibMarshalGenerator,
+ emitter = GlibMarshalEmitter,
+ single_source = 1,
+ suffix = 'glibmarshal.c')
+
env = Environment()
for key in Split('CC CXX AR RANLIB LD NM'):
value = os.environ.get(key)
@@ -131,6 +171,7 @@
env['LIBPATH'] = ['../../third_party/chrome']
env['BUILDERS']['ProtocolBuffer'] = proto_builder
env['BUILDERS']['DbusBindings'] = dbus_bindings_builder
+env['BUILDERS']['GlibMarshal'] = glib_marshal_builder
# Fix issue with scons not passing pkg-config vars through the environment.
for key in Split('PKG_CONFIG_LIBDIR PKG_CONFIG_PATH'):
@@ -143,6 +184,8 @@
env.DbusBindings('update_engine.dbusclient.h', 'update_engine.xml')
+env.GlibMarshal('marshal.glibmarshal.c', 'marshal.list')
+
if ARGUMENTS.get('debug', 0):
env['CCFLAGS'] += ' -fprofile-arcs -ftest-coverage'
env['LIBS'] += ['bz2', 'gcov']
@@ -164,6 +207,7 @@
graph_utils.cc
gzip.cc
libcurl_http_fetcher.cc
+ marshal.glibmarshal.c
omaha_hash_calculator.cc
omaha_request_prep_action.cc
omaha_response_handler_action.cc
diff --git a/UpdateEngine.conf b/UpdateEngine.conf
index 88d6dc4..fe332c0 100644
--- a/UpdateEngine.conf
+++ b/UpdateEngine.conf
@@ -16,6 +16,9 @@
<allow send_destination="org.chromium.UpdateEngine"
send_interface="org.chromium.UpdateEngineInterface"
send_member="GetStatus"/>
+ <allow send_destination="org.chromium.UpdateEngine"
+ send_interface="org.chromium.UpdateEngineInterface"
+ send_member="CheckForUpdate"/>
</policy>
<policy context="default">
<deny send_destination="org.chromium.UpdateEngine" />
diff --git a/dbus_service.cc b/dbus_service.cc
index 8dc239f..12398cd 100644
--- a/dbus_service.cc
+++ b/dbus_service.cc
@@ -5,6 +5,7 @@
#include "update_engine/dbus_service.h"
#include <string>
#include "base/logging.h"
+#include "update_engine/marshal.glibmarshal.h"
using std::string;
@@ -14,10 +15,28 @@
G_OBJECT_CLASS(update_engine_service_parent_class)->finalize(object);
}
+static guint status_update_signal = 0;
+
static void update_engine_service_class_init(UpdateEngineServiceClass* klass) {
GObjectClass *object_class;
object_class = G_OBJECT_CLASS(klass);
object_class->finalize = update_engine_service_finalize;
+
+ status_update_signal = g_signal_new(
+ "status_update",
+ G_OBJECT_CLASS_TYPE(klass),
+ G_SIGNAL_RUN_LAST,
+ 0, // 0 == no class method associated
+ NULL, // Accumulator
+ NULL, // Accumulator data
+ update_engine_VOID__INT64_DOUBLE_STRING_STRING_INT64,
+ G_TYPE_NONE, // Return type
+ 5, // param count:
+ G_TYPE_INT64,
+ G_TYPE_DOUBLE,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INT64);
}
static void update_engine_service_init(UpdateEngineService* object) {
@@ -49,3 +68,26 @@
return TRUE;
}
+gboolean update_engine_service_check_for_update(UpdateEngineService* self,
+ GError **error) {
+ self->update_attempter_->CheckForUpdate();
+ return TRUE;
+}
+
+gboolean update_engine_service_emit_status_update(
+ UpdateEngineService* self,
+ gint64 last_checked_time,
+ gdouble progress,
+ const gchar* current_operation,
+ const gchar* new_version,
+ gint64 new_size) {
+ g_signal_emit(self,
+ status_update_signal,
+ 0,
+ last_checked_time,
+ progress,
+ current_operation,
+ new_version,
+ new_size);
+ return TRUE;
+}
diff --git a/dbus_service.h b/dbus_service.h
index 1b2604e..40cb987 100644
--- a/dbus_service.h
+++ b/dbus_service.h
@@ -15,17 +15,17 @@
// Type macros:
#define UPDATE_ENGINE_TYPE_SERVICE (update_engine_service_get_type())
-#define UPDATE_ENGINE_SERVICE(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj), UPDATE_ENGINE_TYPE_SERVICE, \
+#define UPDATE_ENGINE_SERVICE(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), UPDATE_ENGINE_TYPE_SERVICE, \
UpdateEngineService))
-#define UPDATE_ENGINE_IS_SERVICE(obj) \
+#define UPDATE_ENGINE_IS_SERVICE(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE((obj), UPDATE_ENGINE_TYPE_SERVICE))
-#define UPDATE_ENGINE_SERVICE_CLASS(klass) \
+#define UPDATE_ENGINE_SERVICE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST((klass), UPDATE_ENGINE_TYPE_SERVICE, \
UpdateEngineService))
-#define UPDATE_ENGINE_IS_SERVICE_CLASS(klass) \
+#define UPDATE_ENGINE_IS_SERVICE_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass), UPDATE_ENGINE_TYPE_SERVICE))
-#define UPDATE_ENGINE_SERVICE_GET_CLASS(obj) \
+#define UPDATE_ENGINE_SERVICE_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS((obj), UPDATE_ENGINE_TYPE_SERVICE, \
UpdateEngineService))
@@ -47,12 +47,23 @@
// Methods
gboolean update_engine_service_get_status(UpdateEngineService* self,
- int64_t* last_checked_time,
- double* progress,
- gchar** current_operation,
- gchar** new_version,
- int64_t* new_size,
- GError **error);
+ int64_t* last_checked_time,
+ double* progress,
+ gchar** current_operation,
+ gchar** new_version,
+ int64_t* new_size,
+ GError **error);
+
+gboolean update_engine_service_check_for_update(UpdateEngineService* self,
+ GError **error);
+
+gboolean update_engine_service_emit_status_update(
+ UpdateEngineService* self,
+ gint64 last_checked_time,
+ gdouble progress,
+ const gchar* current_operation,
+ const gchar* new_version,
+ gint64 new_size);
G_END_DECLS
diff --git a/download_action.cc b/download_action.cc
index eee25cc..4787ee2 100644
--- a/download_action.cc
+++ b/download_action.cc
@@ -19,7 +19,9 @@
DownloadAction::DownloadAction(HttpFetcher* http_fetcher)
: writer_(NULL),
- http_fetcher_(http_fetcher) {}
+ http_fetcher_(http_fetcher),
+ delegate_(NULL),
+ bytes_received_(0) {}
DownloadAction::~DownloadAction() {}
@@ -29,6 +31,7 @@
// Get the InstallPlan and read it
CHECK(HasInputObject());
install_plan_ = GetInputObject();
+ bytes_received_ = 0;
install_plan_.Dump();
@@ -84,6 +87,9 @@
void DownloadAction::ReceivedBytes(HttpFetcher *fetcher,
const char* bytes,
int length) {
+ bytes_received_ += length;
+ if (delegate_)
+ delegate_->BytesReceived(bytes_received_, install_plan_.size);
int rc = writer_->Write(bytes, length);
TEST_AND_RETURN(rc >= 0);
omaha_hash_calculator_.Update(bytes, length);
@@ -94,9 +100,9 @@
vector<string> command;
command.push_back("/bin/sync");
int rc;
- LOG(INFO) << "FlushLinuxCaches/sync...";
+ LOG(INFO) << "FlushLinuxCaches-sync...";
Subprocess::SynchronousExec(command, &rc);
- LOG(INFO) << "FlushLinuxCaches/drop_caches...";
+ LOG(INFO) << "FlushLinuxCaches-drop_caches...";
const char* const drop_cmd = "3\n";
utils::WriteFile("/proc/sys/vm/drop_caches", drop_cmd, strlen(drop_cmd));
diff --git a/download_action.h b/download_action.h
index 0f375fa..e1e8aeb 100644
--- a/download_action.h
+++ b/download_action.h
@@ -33,6 +33,15 @@
namespace chromeos_update_engine {
+class DownloadActionDelegate {
+ public:
+ // Called before any bytes are received and periodically after
+ // bytes are received.
+ // bytes_received is the number of bytes downloaded thus far.
+ // total is the number of bytes expected.
+ virtual void BytesReceived(uint64_t bytes_received, uint64_t total) = 0;
+};
+
class DownloadAction;
class NoneType;
@@ -66,11 +75,15 @@
static std::string StaticType() { return "DownloadAction"; }
std::string Type() const { return StaticType(); }
- // Delegate methods (see http_fetcher.h)
+ // HttpFetcherDelegate methods (see http_fetcher.h)
virtual void ReceivedBytes(HttpFetcher *fetcher,
const char* bytes, int length);
virtual void TransferComplete(HttpFetcher *fetcher, bool successful);
+ void set_delegate(DownloadActionDelegate* delegate) {
+ delegate_ = delegate;
+ }
+
private:
// The InstallPlan passed in
InstallPlan install_plan_;
@@ -93,6 +106,11 @@
// Used to find the hash of the bytes downloaded
OmahaHashCalculator omaha_hash_calculator_;
+
+ // For reporting status to outsiders
+ DownloadActionDelegate* delegate_;
+ uint64_t bytes_received_;
+
DISALLOW_COPY_AND_ASSIGN(DownloadAction);
};
diff --git a/download_action_unittest.cc b/download_action_unittest.cc
index 6353584..3b04eae 100644
--- a/download_action_unittest.cc
+++ b/download_action_unittest.cc
@@ -75,6 +75,7 @@
// takes ownership of passed in HttpFetcher
InstallPlan install_plan(true,
"",
+ 0,
OmahaHashCalculator::OmahaHashOfData(data),
output_temp_file.GetPath(),
"");
@@ -152,7 +153,7 @@
// takes ownership of passed in HttpFetcher
ObjectFeederAction<InstallPlan> feeder_action;
- InstallPlan install_plan(true, "", "", temp_file.GetPath(), "");
+ InstallPlan install_plan(true, "", 0, "", temp_file.GetPath(), "");
feeder_action.set_obj(install_plan);
DownloadAction download_action(new MockHttpFetcher(&data[0], data.size()));
download_action.SetTestFileWriter(&writer);
@@ -233,6 +234,7 @@
// takes ownership of passed in HttpFetcher
InstallPlan install_plan(true,
"",
+ 0,
OmahaHashCalculator::OmahaHashOfString("x"),
"/dev/null",
"/dev/null");
@@ -268,7 +270,7 @@
DirectFileWriter writer;
// takes ownership of passed in HttpFetcher
- InstallPlan install_plan(true, "", "", path, "");
+ InstallPlan install_plan(true, "", 0, "", path, "");
ObjectFeederAction<InstallPlan> feeder_action;
feeder_action.set_obj(install_plan);
DownloadAction download_action(new MockHttpFetcher("x", 1));
diff --git a/filesystem_copier_action_unittest.cc b/filesystem_copier_action_unittest.cc
index b5cd64d..e2bd42b 100644
--- a/filesystem_copier_action_unittest.cc
+++ b/filesystem_copier_action_unittest.cc
@@ -231,7 +231,7 @@
ObjectFeederAction<InstallPlan> feeder_action;
const char* kUrl = "http://some/url";
- InstallPlan install_plan(true, kUrl, "", "", "");
+ InstallPlan install_plan(true, kUrl, 0, "", "", "");
feeder_action.set_obj(install_plan);
FilesystemCopierAction copier_action(false);
ObjectCollectorAction<InstallPlan> collector_action;
@@ -256,7 +256,7 @@
processor.set_delegate(&delegate);
ObjectFeederAction<InstallPlan> feeder_action;
- InstallPlan install_plan(false, "", "", "/no/such/file", "/no/such/file");
+ InstallPlan install_plan(false, "", 0, "", "/no/such/file", "/no/such/file");
feeder_action.set_obj(install_plan);
FilesystemCopierAction copier_action(false);
ObjectCollectorAction<InstallPlan> collector_action;
diff --git a/install_plan.h b/install_plan.h
index fba89fe..9fc6ead 100644
--- a/install_plan.h
+++ b/install_plan.h
@@ -16,11 +16,13 @@
struct InstallPlan {
InstallPlan(bool is_full,
const std::string& url,
+ uint64_t size,
const std::string& hash,
const std::string& install_path,
const std::string& kernel_install_path)
: is_full_update(is_full),
download_url(url),
+ size(size),
download_hash(hash),
install_path(install_path),
kernel_install_path(kernel_install_path) {}
@@ -28,6 +30,7 @@
bool is_full_update;
std::string download_url; // url to download from
+ uint64_t size; // size of the download url's data
std::string download_hash; // hash of the data at the url
std::string install_path; // path to install device
std::string kernel_install_path; // path to kernel install device
@@ -35,6 +38,7 @@
bool operator==(const InstallPlan& that) const {
return (is_full_update == that.is_full_update) &&
(download_url == that.download_url) &&
+ (size == that.size) &&
(download_hash == that.download_hash) &&
(install_path == that.install_path) &&
(kernel_install_path == that.kernel_install_path);
@@ -45,7 +49,9 @@
void Dump() const {
LOG(INFO) << "InstallPlan: "
<< (is_full_update ? "full_update" : "delta_update")
- << ", url: " << download_url << ", hash: " << download_hash
+ << ", url: " << download_url
+ << ", size: " << size
+ << ", hash: " << download_hash
<< ", install_path: " << install_path
<< ", kernel_install_path: " << kernel_install_path;
}
diff --git a/main.cc b/main.cc
index 4725967..f61ff34 100644
--- a/main.cc
+++ b/main.cc
@@ -26,16 +26,6 @@
namespace chromeos_update_engine {
-gboolean SetupInMainLoop(void* arg) {
- // TODO(adlr): Tell update_attempter to start working.
- // Comment this in for that:
- UpdateAttempter* update_attempter = reinterpret_cast<UpdateAttempter*>(arg);
- LOG(INFO) << "Starting update!";
- update_attempter->Update(false);
-
- return FALSE; // Don't call this callback function again
-}
-
void SetupDbusService(UpdateEngineService* service) {
DBusGConnection *bus;
DBusGProxy *proxy;
@@ -82,18 +72,18 @@
google::ParseCommandLineFlags(&argc, &argv, true);
CommandLine::Init(argc, argv);
logging::InitLogging("logfile.txt",
- FLAGS_logtostderr ?
- logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG :
- logging::LOG_ONLY_TO_FILE,
+ (FLAGS_logtostderr ?
+ logging::LOG_ONLY_TO_SYSTEM_DEBUG_LOG :
+ logging::LOG_ONLY_TO_FILE),
logging::DONT_LOCK_LOG_FILE,
logging::APPEND_TO_OLD_LOG_FILE);
LOG(INFO) << "Chrome OS Update Engine starting";
// Create the single GMainLoop
- GMainLoop *loop = g_main_loop_new(g_main_context_default(), FALSE);
+ GMainLoop* loop = g_main_loop_new(g_main_context_default(), FALSE);
// Create the update attempter:
- chromeos_update_engine::UpdateAttempter update_attempter(loop);
+ chromeos_update_engine::UpdateAttempter update_attempter;
// Create the dbus service object:
dbus_g_object_type_install_info(UPDATE_ENGINE_TYPE_SERVICE,
@@ -101,16 +91,15 @@
UpdateEngineService* service =
UPDATE_ENGINE_SERVICE(g_object_new(UPDATE_ENGINE_TYPE_SERVICE, NULL));
service->update_attempter_ = &update_attempter;
+ update_attempter.set_dbus_service(service);
chromeos_update_engine::SetupDbusService(service);
- // Set up init routine to run within the main loop.
- g_timeout_add(0, &chromeos_update_engine::SetupInMainLoop, &update_attempter);
-
// Run the main loop until exit time:
g_main_loop_run(loop);
// Cleanup:
g_main_loop_unref(loop);
+ update_attempter.set_dbus_service(NULL);
g_object_unref(G_OBJECT(service));
LOG(INFO) << "Chrome OS Update Engine terminating";
diff --git a/marshal.list b/marshal.list
new file mode 100644
index 0000000..9dce1bd
--- /dev/null
+++ b/marshal.list
@@ -0,0 +1 @@
+VOID:INT64,DOUBLE,STRING,STRING,INT64
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index f90255a..486f19b 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -26,34 +26,22 @@
LOG(INFO) << "There are no updates. Aborting.";
return;
}
- InstallPlan install_plan;
- install_plan.download_url = response.codebase;
- install_plan.download_hash = response.hash;
+ install_plan_.download_url = response.codebase;
+ install_plan_.size = response.size;
+ install_plan_.download_hash = response.hash;
TEST_AND_RETURN(GetInstallDev(
(!boot_device_.empty() ? boot_device_ : utils::BootDevice()),
- &install_plan.install_path));
- install_plan.kernel_install_path =
- utils::BootKernelDevice(install_plan.install_path);
+ &install_plan_.install_path));
+ install_plan_.kernel_install_path =
+ utils::BootKernelDevice(install_plan_.install_path);
- // Get the filename part of the url. Assume that if it has kFullUpdateTag
- // in the name, it's a full update.
- string::size_type last_slash = response.codebase.rfind('/');
- string filename;
- if (last_slash == string::npos)
- filename = response.codebase;
- else
- filename = response.codebase.substr(last_slash + 1);
- install_plan.is_full_update = (filename.find(kFullUpdateTag) != string::npos);
+ install_plan_.is_full_update = true; // TODO(adlr): know if update is a delta
- if (filename.size() > 255) {
- // Very long name. Let's shorten it
- filename.resize(255);
- }
TEST_AND_RETURN(HasOutputPipe());
if (HasOutputPipe())
- SetOutputObject(install_plan);
+ SetOutputObject(install_plan_);
LOG(INFO) << "Using this install plan:";
- install_plan.Dump();
+ install_plan_.Dump();
completer.set_success(true);
}
diff --git a/omaha_response_handler_action.h b/omaha_response_handler_action.h
index b56d343..e79e32b 100644
--- a/omaha_response_handler_action.h
+++ b/omaha_response_handler_action.h
@@ -43,6 +43,7 @@
}
bool GotNoUpdateResponse() const { return got_no_update_response_; }
+ const InstallPlan& install_plan() const { return install_plan_; }
// Debugging/logging
static std::string StaticType() { return "OmahaResponseHandlerAction"; }
@@ -59,6 +60,9 @@
// set to non-empty in unit tests
std::string boot_device_;
+ // The install plan, if we have an update.
+ InstallPlan install_plan_;
+
// True only if we got a response and the response said no updates
bool got_no_update_response_;
diff --git a/omaha_response_handler_action_unittest.cc b/omaha_response_handler_action_unittest.cc
index e997ea9..b396a9c 100644
--- a/omaha_response_handler_action_unittest.cc
+++ b/omaha_response_handler_action_unittest.cc
@@ -82,7 +82,7 @@
UpdateCheckResponse in;
in.update_exists = true;
in.display_version = "a.b.c.d";
- in.codebase = "http://foo/the_update_a.b.c.d_FULL_.tgz";
+ in.codebase = "http://foo/the_update_a.b.c.d.tgz";
in.more_info_url = "http://more/info";
in.hash = "HASH+";
in.size = 12;
@@ -99,7 +99,7 @@
UpdateCheckResponse in;
in.update_exists = true;
in.display_version = "a.b.c.d";
- in.codebase = "http://foo/the_update_a.b.c.d_DELTA_.tgz";
+ in.codebase = "http://foo/the_update_a.b.c.d.tgz";
in.more_info_url = "http://more/info";
in.hash = "HASHj+";
in.size = 12;
@@ -107,7 +107,7 @@
in.prompt = true;
InstallPlan install_plan;
EXPECT_TRUE(DoTest(in, "/dev/sda5", &install_plan));
- EXPECT_FALSE(install_plan.is_full_update);
+ EXPECT_TRUE(install_plan.is_full_update);
EXPECT_EQ(in.codebase, install_plan.download_url);
EXPECT_EQ(in.hash, install_plan.download_hash);
EXPECT_EQ("/dev/sda3", install_plan.install_path);
@@ -124,7 +124,7 @@
in.prompt = true;
InstallPlan install_plan;
EXPECT_TRUE(DoTest(in, "/dev/sda3", &install_plan));
- EXPECT_FALSE(install_plan.is_full_update);
+ EXPECT_TRUE(install_plan.is_full_update);
EXPECT_EQ(in.codebase, install_plan.download_url);
EXPECT_EQ(in.hash, install_plan.download_hash);
EXPECT_EQ("/dev/sda5", install_plan.install_path);
diff --git a/update_attempter.cc b/update_attempter.cc
index ad3bd9e..ebfcd1d 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -3,10 +3,18 @@
// found in the LICENSE file.
#include "update_engine/update_attempter.h"
+
+// From 'man clock_gettime': feature test macro: _POSIX_C_SOURCE >= 199309L
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 199309L
+#endif // _POSIX_C_SOURCE
+#include <time.h>
+
#include <tr1/memory>
#include <string>
#include <vector>
#include <glib.h>
+#include "update_engine/dbus_service.h"
#include "update_engine/download_action.h"
#include "update_engine/filesystem_copier_action.h"
#include "update_engine/libcurl_http_fetcher.h"
@@ -22,6 +30,59 @@
namespace chromeos_update_engine {
+namespace {
+// Returns true on success.
+bool GetCPUClockTime(struct timespec* out) {
+ return clock_gettime(CLOCK_REALTIME, out) == 0;
+}
+// Returns stop - start.
+struct timespec CPUClockTimeElapsed(const struct timespec& start,
+ const struct timespec& stop) {
+ CHECK(start.tv_sec >= 0);
+ CHECK(stop.tv_sec >= 0);
+ CHECK(start.tv_nsec >= 0);
+ CHECK(stop.tv_nsec >= 0);
+
+ const int64_t kOneBillion = 1000000000L;
+ const int64_t start64 = start.tv_sec * kOneBillion + start.tv_nsec;
+ const int64_t stop64 = stop.tv_sec * kOneBillion + stop.tv_nsec;
+
+ const int64_t result64 = stop64 - start64;
+
+ struct timespec ret;
+ ret.tv_sec = result64 / kOneBillion;
+ ret.tv_nsec = result64 % kOneBillion;
+
+ return ret;
+}
+bool CPUClockTimeGreaterThanHalfSecond(const struct timespec& spec) {
+ if (spec.tv_sec >= 1)
+ return true;
+ return (spec.tv_nsec > 500000000);
+}
+}
+
+const char* UpdateStatusToString(UpdateStatus status) {
+ switch (status) {
+ case UPDATE_STATUS_IDLE:
+ return "UPDATE_STATUS_IDLE";
+ case UPDATE_STATUS_CHECKING_FOR_UPDATE:
+ return "UPDATE_STATUS_CHECKING_FOR_UPDATE";
+ case UPDATE_STATUS_UPDATE_AVAILABLE:
+ return "UPDATE_STATUS_UPDATE_AVAILABLE";
+ case UPDATE_STATUS_DOWNLOADING:
+ return "UPDATE_STATUS_DOWNLOADING";
+ case UPDATE_STATUS_VERIFYING:
+ return "UPDATE_STATUS_VERIFYING";
+ case UPDATE_STATUS_FINALIZING:
+ return "UPDATE_STATUS_FINALIZING";
+ case UPDATE_STATUS_UPDATED_NEED_REBOOT:
+ return "UPDATE_STATUS_UPDATED_NEED_REBOOT";
+ default:
+ return "unknown status";
+ }
+}
+
void UpdateAttempter::Update(bool force_full_update) {
full_update_ = force_full_update;
CHECK(!processor_.IsRunning());
@@ -46,7 +107,8 @@
new SetBootableFlagAction);
shared_ptr<PostinstallRunnerAction> postinstall_runner_action_postcommit(
new PostinstallRunnerAction(false));
-
+
+ download_action->set_delegate(this);
response_handler_action_ = response_handler_action;
actions_.push_back(shared_ptr<AbstractAction>(request_prep_action));
@@ -87,22 +149,63 @@
BondActions(set_bootable_flag_action.get(),
postinstall_runner_action_postcommit.get());
+ SetStatusAndNotify(UPDATE_STATUS_CHECKING_FOR_UPDATE);
processor_.StartProcessing();
}
-// Delegate method:
+void UpdateAttempter::CheckForUpdate() {
+ if (status_ != UPDATE_STATUS_IDLE) {
+ LOG(INFO) << "Check for update requested, but status is "
+ << UpdateStatusToString(status_) << ", so not checking.";
+ return;
+ }
+ Update(false);
+}
+
+// Delegate methods:
void UpdateAttempter::ProcessingDone(const ActionProcessor* processor,
bool success) {
CHECK(response_handler_action_);
- if (response_handler_action_->GotNoUpdateResponse()) {
- // All done.
- g_main_loop_quit(loop_);
- return;
- }
- if (!success) {
+ LOG(INFO) << "Processing Done.";
+ if (success) {
+ SetStatusAndNotify(UPDATE_STATUS_UPDATED_NEED_REBOOT);
+ } else {
LOG(INFO) << "Update failed.";
+ SetStatusAndNotify(UPDATE_STATUS_IDLE);
}
- g_main_loop_quit(loop_);
+}
+
+void UpdateAttempter::ProcessingStopped(const ActionProcessor* processor) {
+ download_progress_ = 0.0;
+ SetStatusAndNotify(UPDATE_STATUS_IDLE);
+}
+
+// Called whenever an action has finished processing, either successfully
+// or otherwise.
+void UpdateAttempter::ActionCompleted(ActionProcessor* processor,
+ AbstractAction* action,
+ bool success) {
+ // Reset download progress regardless of whether or not the download action
+ // succeeded.
+ const string type = action->Type();
+ if (type == DownloadAction::StaticType())
+ download_progress_ = 0.0;
+ if (!success)
+ return;
+ // Find out which action completed.
+ if (type == OmahaResponseHandlerAction::StaticType()) {
+ SetStatusAndNotify(UPDATE_STATUS_DOWNLOADING);
+ OmahaResponseHandlerAction* omaha_response_handler_action =
+ dynamic_cast<OmahaResponseHandlerAction*>(action);
+ CHECK(omaha_response_handler_action);
+ const InstallPlan& plan = omaha_response_handler_action->install_plan();
+ last_checked_time_ = time(NULL);
+ // TODO(adlr): put version in InstallPlan
+ new_version_ = "0.0.0.0";
+ new_size_ = plan.size;
+ } else if (type == DownloadAction::StaticType()) {
+ SetStatusAndNotify(UPDATE_STATUS_FINALIZING);
+ }
}
// Stop updating. An attempt will be made to record status to the disk
@@ -118,19 +221,49 @@
NOTIMPLEMENTED();
}
+void UpdateAttempter::BytesReceived(uint64_t bytes_received, uint64_t total) {
+ if (status_ != UPDATE_STATUS_DOWNLOADING) {
+ LOG(ERROR) << "BytesReceived called while not downloading.";
+ return;
+ }
+ download_progress_ = static_cast<double>(bytes_received) /
+ static_cast<double>(total);
+ // We self throttle here
+ timespec now;
+ now.tv_sec = 0;
+ now.tv_nsec = 0;
+ if (GetCPUClockTime(&now) &&
+ CPUClockTimeGreaterThanHalfSecond(
+ CPUClockTimeElapsed(last_notify_time_, now))) {
+ SetStatusAndNotify(UPDATE_STATUS_DOWNLOADING);
+ }
+}
+
bool UpdateAttempter::GetStatus(int64_t* last_checked_time,
double* progress,
std::string* current_operation,
std::string* new_version,
int64_t* new_size) {
- // TODO(adlr): Return actual status.
- *last_checked_time = 123;
- *progress = 0.2223;
- *current_operation = "DOWNLOADING";
- *new_version = "0.2.3.8";
- *new_size = 10002;
+ *last_checked_time = last_checked_time_;
+ *progress = download_progress_;
+ *current_operation = UpdateStatusToString(status_);
+ *new_version = new_version_;
+ *new_size = new_size_;
return true;
}
+void UpdateAttempter::SetStatusAndNotify(UpdateStatus status) {
+ status_ = status;
+ if (!dbus_service_)
+ return;
+ GetCPUClockTime(&last_notify_time_);
+ update_engine_service_emit_status_update(
+ dbus_service_,
+ last_checked_time_,
+ download_progress_,
+ UpdateStatusToString(status_),
+ new_version_.c_str(),
+ new_size_);
+}
} // namespace chromeos_update_engine
diff --git a/update_attempter.h b/update_attempter.h
index 83e4737..76fe3fb 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -5,24 +5,52 @@
#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_ATTEMPTER_H__
#define CHROMEOS_PLATFORM_UPDATE_ENGINE_UPDATE_ATTEMPTER_H__
+#include <time.h>
#include <tr1/memory>
#include <string>
#include <vector>
#include <glib.h>
#include "update_engine/action_processor.h"
+#include "update_engine/download_action.h"
#include "update_engine/omaha_response_handler_action.h"
+struct UpdateEngineService;
+
namespace chromeos_update_engine {
-class UpdateAttempter : public ActionProcessorDelegate {
+enum UpdateStatus {
+ UPDATE_STATUS_IDLE = 0,
+ UPDATE_STATUS_CHECKING_FOR_UPDATE,
+ UPDATE_STATUS_UPDATE_AVAILABLE,
+ UPDATE_STATUS_DOWNLOADING,
+ UPDATE_STATUS_VERIFYING,
+ UPDATE_STATUS_FINALIZING,
+ UPDATE_STATUS_UPDATED_NEED_REBOOT
+};
+
+const char* UpdateStatusToString(UpdateStatus status);
+
+class UpdateAttempter : public ActionProcessorDelegate,
+ public DownloadActionDelegate {
public:
- explicit UpdateAttempter(GMainLoop *loop)
- : full_update_(false),
- loop_(loop) {}
+ UpdateAttempter() : full_update_(false),
+ dbus_service_(NULL),
+ status_(UPDATE_STATUS_IDLE),
+ download_progress_(0.0),
+ last_checked_time_(0),
+ new_version_("0.0.0.0"),
+ new_size_(0) {
+ last_notify_time_.tv_sec = 0;
+ last_notify_time_.tv_nsec = 0;
+ }
void Update(bool force_full_update);
- // Delegate method:
+ // ActionProcessorDelegate methods:
void ProcessingDone(const ActionProcessor* processor, bool success);
+ void ProcessingStopped(const ActionProcessor* processor);
+ void ActionCompleted(ActionProcessor* processor,
+ AbstractAction* action,
+ bool success);
// Stop updating. An attempt will be made to record status to the disk
// so that updates can be resumed later.
@@ -38,14 +66,40 @@
std::string* new_version,
int64_t* new_size);
+ void set_dbus_service(struct UpdateEngineService* dbus_service) {
+ dbus_service_ = dbus_service;
+ }
+
+ void CheckForUpdate();
+
+ // DownloadActionDelegate method
+ void BytesReceived(uint64_t bytes_received, uint64_t total);
+
private:
+ // Sets the status to the given status and notifies a status update
+ // over dbus.
+ void SetStatusAndNotify(UpdateStatus status);
+
+ struct timespec last_notify_time_;
+
bool full_update_;
std::vector<std::tr1::shared_ptr<AbstractAction> > actions_;
ActionProcessor processor_;
- GMainLoop *loop_;
+
+ // If non-null, this UpdateAttempter will send status updates over this
+ // dbus service.
+ UpdateEngineService* dbus_service_;
// pointer to the OmahaResponseHandlerAction in the actions_ vector;
std::tr1::shared_ptr<OmahaResponseHandlerAction> response_handler_action_;
+
+ // For status:
+ UpdateStatus status_;
+ double download_progress_;
+ int64_t last_checked_time_;
+ std::string new_version_;
+ int64_t new_size_;
+
DISALLOW_COPY_AND_ASSIGN(UpdateAttempter);
};
diff --git a/update_engine.xml b/update_engine.xml
index 11c804f..f5faaeb 100644
--- a/update_engine.xml
+++ b/update_engine.xml
@@ -14,5 +14,14 @@
<arg type="s" name="new_version" direction="out" />
<arg type="x" name="new_size" direction="out" />
</method>
+ <method name="CheckForUpdate">
+ </method>
+ <signal name="StatusUpdate">
+ <arg type="x" name="last_checked_time" />
+ <arg type="d" name="progress" />
+ <arg type="s" name="current_operation" />
+ <arg type="s" name="new_version" />
+ <arg type="x" name="new_size" />
+ </signal>
</interface>
</node>
diff --git a/update_engine_client.cc b/update_engine_client.cc
index bed7205..cfdbc8a 100644
--- a/update_engine_client.cc
+++ b/update_engine_client.cc
@@ -5,6 +5,7 @@
#include <gflags/gflags.h>
#include <glib.h>
+#include "update_engine/marshal.glibmarshal.h"
#include "update_engine/dbus_constants.h"
#include "update_engine/subprocess.h"
#include "update_engine/utils.h"
@@ -23,13 +24,15 @@
"Force an update, even over an expensive network.");
DEFINE_bool(check_for_update, false,
"Initiate check for updates.");
+DEFINE_bool(watch_for_updates, false,
+ "Listen for status updates and print them to the screen.");
namespace {
-bool GetStatus() {
- DBusGConnection *bus;
- DBusGProxy *proxy;
- GError *error = NULL;
+bool GetProxy(DBusGProxy** out_proxy) {
+ DBusGConnection* bus;
+ DBusGProxy* proxy;
+ GError* error = NULL;
bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
if (!bus) {
@@ -43,6 +46,30 @@
if (!proxy) {
LOG(FATAL) << "Error getting proxy: " << GetGErrorMessage(error);
}
+ *out_proxy = proxy;
+ return true;
+}
+
+static void StatusUpdateSignalHandler(DBusGProxy* proxy,
+ int64_t last_checked_time,
+ double progress,
+ gchar* current_operation,
+ gchar* new_version,
+ int64_t new_size,
+ void* user_data) {
+ LOG(INFO) << "Got status update:";
+ LOG(INFO) << " last_checked_time: " << last_checked_time;
+ LOG(INFO) << " progress: " << progress;
+ LOG(INFO) << " current_operation: " << current_operation;
+ LOG(INFO) << " new_version: " << new_version;
+ LOG(INFO) << " new_size: " << new_size;
+}
+
+bool GetStatus() {
+ DBusGProxy* proxy;
+ GError* error = NULL;
+
+ CHECK(GetProxy(&proxy));
gint64 last_checked_time = 0;
gdouble progress = 0.0;
@@ -71,7 +98,52 @@
return true;
}
+// Should never return.
+void WatchForUpdates() {
+ DBusGProxy* proxy;
+
+ CHECK(GetProxy(&proxy));
+
+ // Register marshaller
+ dbus_g_object_register_marshaller(
+ update_engine_VOID__INT64_DOUBLE_STRING_STRING_INT64,
+ G_TYPE_NONE,
+ G_TYPE_INT64,
+ G_TYPE_DOUBLE,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INT64,
+ G_TYPE_INVALID);
+
+ // TODO(adlr): make StatusUpdate a const string
+ dbus_g_proxy_add_signal(proxy,
+ "StatusUpdate",
+ G_TYPE_INT64,
+ G_TYPE_DOUBLE,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_INT64,
+ G_TYPE_INVALID);
+ GMainLoop* loop = g_main_loop_new (NULL, TRUE);
+ dbus_g_proxy_connect_signal(proxy,
+ "StatusUpdate",
+ G_CALLBACK(StatusUpdateSignalHandler),
+ NULL,
+ NULL);
+ g_main_loop_run(loop);
+ g_main_loop_unref(loop);
+}
+
bool CheckForUpdates(bool force) {
+ DBusGProxy* proxy;
+ GError* error = NULL;
+
+ CHECK(GetProxy(&proxy));
+
+ gboolean rc =
+ org_chromium_UpdateEngineInterface_check_for_update(proxy, &error);
+ CHECK_EQ(rc, TRUE) << "Error checking for update: "
+ << GetGErrorMessage(error);
return true;
}
@@ -101,6 +173,11 @@
<< "Update check/initiate update failed.";
return 0;
}
+ if (FLAGS_watch_for_updates) {
+ LOG(INFO) << "Watching for status updates.";
+ WatchForUpdates(); // Should never return.
+ return 1;
+ }
LOG(INFO) << "No flags specified. Exiting.";
return 0;