blob: 6b277224b59ead8678f026dcfe0e4fabdb6be8d2 [file] [log] [blame]
Darin Petkovc1a8b422010-07-19 11:34:49 -07001// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
rspangler@google.com49fdf182009-10-10 00:57:34 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
rspangler@google.com49fdf182009-10-10 00:57:34 +00005#include "update_engine/download_action.h"
adlr@google.comc98a7ed2009-12-04 18:54:03 +00006#include <errno.h>
7#include <algorithm>
Andrew de los Reyesf9714432010-05-04 10:21:23 -07008#include <string>
9#include <vector>
adlr@google.comc98a7ed2009-12-04 18:54:03 +000010#include <glib.h>
11#include "update_engine/action_pipe.h"
Andrew de los Reyesf9714432010-05-04 10:21:23 -070012#include "update_engine/subprocess.h"
adlr@google.comc98a7ed2009-12-04 18:54:03 +000013
14using std::min;
Andrew de los Reyesf9714432010-05-04 10:21:23 -070015using std::string;
16using std::vector;
rspangler@google.com49fdf182009-10-10 00:57:34 +000017
18namespace chromeos_update_engine {
19
Darin Petkove971f332010-09-22 16:57:25 -070020// Use a buffer to reduce the number of IOPS on SSD devices.
21const size_t kFileWriterBufferSize = 128 * 1024; // 128 KiB
22
adlr@google.comc98a7ed2009-12-04 18:54:03 +000023DownloadAction::DownloadAction(HttpFetcher* http_fetcher)
Andrew de los Reyesf9185172010-05-03 11:07:05 -070024 : writer_(NULL),
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070025 http_fetcher_(http_fetcher),
26 delegate_(NULL),
27 bytes_received_(0) {}
rspangler@google.com49fdf182009-10-10 00:57:34 +000028
29DownloadAction::~DownloadAction() {}
30
31void DownloadAction::PerformAction() {
32 http_fetcher_->set_delegate(this);
rspangler@google.com49fdf182009-10-10 00:57:34 +000033
adlr@google.comc98a7ed2009-12-04 18:54:03 +000034 // Get the InstallPlan and read it
35 CHECK(HasInputObject());
Andrew de los Reyesf9185172010-05-03 11:07:05 -070036 install_plan_ = GetInputObject();
Andrew de los Reyes63b96d72010-05-10 13:08:54 -070037 bytes_received_ = 0;
adlr@google.comc98a7ed2009-12-04 18:54:03 +000038
Andrew de los Reyesf9185172010-05-03 11:07:05 -070039 install_plan_.Dump();
adlr@google.comc98a7ed2009-12-04 18:54:03 +000040
Andrew de los Reyesf9185172010-05-03 11:07:05 -070041 if (writer_) {
42 LOG(INFO) << "Using writer for test.";
rspangler@google.com49fdf182009-10-10 00:57:34 +000043 } else {
Andrew de los Reyesf9185172010-05-03 11:07:05 -070044 if (install_plan_.is_full_update) {
45 kernel_file_writer_.reset(new DirectFileWriter);
46 rootfs_file_writer_.reset(new DirectFileWriter);
Darin Petkove971f332010-09-22 16:57:25 -070047 kernel_buffered_file_writer_.reset(
48 new BufferedFileWriter(kernel_file_writer_.get(),
49 kFileWriterBufferSize));
50 rootfs_buffered_file_writer_.reset(
51 new BufferedFileWriter(rootfs_file_writer_.get(),
52 kFileWriterBufferSize));
53 split_file_writer_.reset(
54 new SplitFileWriter(kernel_buffered_file_writer_.get(),
55 rootfs_buffered_file_writer_.get()));
Andrew de los Reyesf9185172010-05-03 11:07:05 -070056 split_file_writer_->SetFirstOpenArgs(
57 install_plan_.kernel_install_path.c_str(),
58 O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE,
59 0644);
60 decompressing_file_writer_.reset(
61 new GzipDecompressingFileWriter(split_file_writer_.get()));
62 writer_ = decompressing_file_writer_.get();
63 } else {
64 delta_performer_.reset(new DeltaPerformer);
65 writer_ = delta_performer_.get();
66 }
rspangler@google.com49fdf182009-10-10 00:57:34 +000067 }
Andrew de los Reyesf9185172010-05-03 11:07:05 -070068 int rc = writer_->Open(install_plan_.install_path.c_str(),
69 O_TRUNC | O_WRONLY | O_CREAT | O_LARGEFILE,
70 0644);
rspangler@google.com49fdf182009-10-10 00:57:34 +000071 if (rc < 0) {
Andrew de los Reyesf9185172010-05-03 11:07:05 -070072 LOG(ERROR) << "Unable to open output file " << install_plan_.install_path;
rspangler@google.com49fdf182009-10-10 00:57:34 +000073 // report error to processor
Darin Petkovc97435c2010-07-20 12:37:43 -070074 processor_->ActionComplete(this, kActionCodeInstallDeviceOpenError);
rspangler@google.com49fdf182009-10-10 00:57:34 +000075 return;
76 }
Andrew de los Reyesf9185172010-05-03 11:07:05 -070077 if (!install_plan_.is_full_update) {
78 if (!delta_performer_->OpenKernel(
79 install_plan_.kernel_install_path.c_str())) {
80 LOG(ERROR) << "Unable to open kernel file "
81 << install_plan_.kernel_install_path.c_str();
82 writer_->Close();
Darin Petkovc97435c2010-07-20 12:37:43 -070083 processor_->ActionComplete(this, kActionCodeKernelDeviceOpenError);
Andrew de los Reyesf9185172010-05-03 11:07:05 -070084 return;
85 }
86 }
Darin Petkov9d911fa2010-08-19 09:36:08 -070087 if (delegate_) {
88 delegate_->SetDownloadStatus(true); // Set to active.
89 }
Andrew de los Reyesf9185172010-05-03 11:07:05 -070090 http_fetcher_->BeginTransfer(install_plan_.download_url);
rspangler@google.com49fdf182009-10-10 00:57:34 +000091}
92
93void DownloadAction::TerminateProcessing() {
94 CHECK(writer_);
95 CHECK_EQ(writer_->Close(), 0);
96 writer_ = NULL;
97 http_fetcher_->TerminateTransfer();
Darin Petkov9d911fa2010-08-19 09:36:08 -070098 if (delegate_) {
99 delegate_->SetDownloadStatus(false); // Set to inactive.
100 }
rspangler@google.com49fdf182009-10-10 00:57:34 +0000101}
102
103void DownloadAction::ReceivedBytes(HttpFetcher *fetcher,
104 const char* bytes,
105 int length) {
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700106 bytes_received_ += length;
107 if (delegate_)
108 delegate_->BytesReceived(bytes_received_, install_plan_.size);
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000109 int rc = writer_->Write(bytes, length);
110 TEST_AND_RETURN(rc >= 0);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000111 omaha_hash_calculator_.Update(bytes, length);
112}
113
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700114namespace {
115void FlushLinuxCaches() {
116 vector<string> command;
117 command.push_back("/bin/sync");
118 int rc;
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700119 LOG(INFO) << "FlushLinuxCaches-sync...";
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700120 Subprocess::SynchronousExec(command, &rc);
Andrew de los Reyes63b96d72010-05-10 13:08:54 -0700121 LOG(INFO) << "FlushLinuxCaches-drop_caches...";
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700122
123 const char* const drop_cmd = "3\n";
124 utils::WriteFile("/proc/sys/vm/drop_caches", drop_cmd, strlen(drop_cmd));
125
126 LOG(INFO) << "FlushLinuxCaches done.";
127}
128}
129
rspangler@google.com49fdf182009-10-10 00:57:34 +0000130void DownloadAction::TransferComplete(HttpFetcher *fetcher, bool successful) {
131 if (writer_) {
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000132 CHECK_EQ(writer_->Close(), 0) << errno;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000133 writer_ = NULL;
134 }
Darin Petkov9d911fa2010-08-19 09:36:08 -0700135 if (delegate_) {
136 delegate_->SetDownloadStatus(false); // Set to inactive.
137 }
Darin Petkovc97435c2010-07-20 12:37:43 -0700138 ActionExitCode code =
139 successful ? kActionCodeSuccess : kActionCodeDownloadTransferError;
140 if (code == kActionCodeSuccess) {
Darin Petkov50332f12010-09-24 11:44:47 -0700141 // Makes sure the hash and size are correct.
rspangler@google.com49fdf182009-10-10 00:57:34 +0000142 omaha_hash_calculator_.Finalize();
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700143 if (omaha_hash_calculator_.hash() != install_plan_.download_hash) {
144 LOG(ERROR) << "Download of " << install_plan_.download_url
Darin Petkov50332f12010-09-24 11:44:47 -0700145 << " failed. Expected hash " << install_plan_.download_hash
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700146 << " but got hash " << omaha_hash_calculator_.hash();
Darin Petkovc97435c2010-07-20 12:37:43 -0700147 code = kActionCodeDownloadHashMismatchError;
Darin Petkov50332f12010-09-24 11:44:47 -0700148 } else if (bytes_received_ != install_plan_.size) {
149 LOG(ERROR) << "Download of " << install_plan_.download_url
150 << " failed. Expected size " << install_plan_.size
151 << " but got size " << bytes_received_;
152 code = kActionCodeDownloadSizeMismatchError;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000153 }
154 }
Darin Petkovc1a8b422010-07-19 11:34:49 -0700155
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700156 FlushLinuxCaches();
rspangler@google.com49fdf182009-10-10 00:57:34 +0000157
Darin Petkovc97435c2010-07-20 12:37:43 -0700158 // Write the path to the output pipe if we're successful.
159 if (code == kActionCodeSuccess && HasOutputPipe())
adlr@google.comc98a7ed2009-12-04 18:54:03 +0000160 SetOutputObject(GetInputObject());
Darin Petkovc97435c2010-07-20 12:37:43 -0700161 processor_->ActionComplete(this, code);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000162}
163
164}; // namespace {}