1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in 12 * the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include "fastboot.h" 30 31 #include <errno.h> 32 #include <stdarg.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <sys/stat.h> 37 #include <sys/types.h> 38 #include <unistd.h> 39 40 #include <memory> 41 #include <vector> 42 43 #include <android-base/stringprintf.h> 44 45 enum Op { 46 OP_DOWNLOAD, 47 OP_COMMAND, 48 OP_QUERY, 49 OP_NOTICE, 50 OP_DOWNLOAD_SPARSE, 51 OP_WAIT_FOR_DISCONNECT, 52 OP_DOWNLOAD_FD, 53 OP_UPLOAD, 54 }; 55 56 struct Action { 57 Action(Op op, const std::string& cmd) : op(op), cmd(cmd) {} 58 59 Op op; 60 std::string cmd; 61 std::string msg; 62 63 std::string product; 64 65 void* data = nullptr; 66 // The protocol only supports 32-bit sizes, so you'll have to break 67 // anything larger into multiple chunks. 68 uint32_t size = 0; 69 70 int fd = -1; 71 72 int (*func)(Action& a, int status, const char* resp) = nullptr; 73 74 double start = -1; 75 }; 76 77 static std::vector<std::unique_ptr<Action>> action_list; 78 79 bool fb_getvar(Transport* transport, const std::string& key, std::string* value) { 80 std::string cmd = "getvar:" + key; 81 82 char buf[FB_RESPONSE_SZ + 1]; 83 memset(buf, 0, sizeof(buf)); 84 if (fb_command_response(transport, cmd, buf)) { 85 return false; 86 } 87 *value = buf; 88 return true; 89 } 90 91 static int cb_default(Action& a, int status, const char* resp) { 92 if (status) { 93 fprintf(stderr,"FAILED (%s)\n", resp); 94 } else { 95 double split = now(); 96 fprintf(stderr, "OKAY [%7.3fs]\n", (split - a.start)); 97 a.start = split; 98 } 99 return status; 100 } 101 102 static Action& queue_action(Op op, const std::string& cmd) { 103 std::unique_ptr<Action> a{new Action(op, cmd)}; 104 a->func = cb_default; 105 106 action_list.push_back(std::move(a)); 107 return *action_list.back(); 108 } 109 110 void fb_set_active(const std::string& slot) { 111 Action& a = queue_action(OP_COMMAND, "set_active:" + slot); 112 a.msg = "Setting current slot to '" + slot + "'..."; 113 } 114 115 void fb_queue_erase(const std::string& partition) { 116 Action& a = queue_action(OP_COMMAND, "erase:" + partition); 117 a.msg = "Erasing '" + partition + "'..."; 118 } 119 120 void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz) { 121 Action& a = queue_action(OP_DOWNLOAD_FD, ""); 122 a.fd = fd; 123 a.size = sz; 124 a.msg = android::base::StringPrintf("Sending '%s' (%d KB)...", partition.c_str(), sz / 1024); 125 126 Action& b = queue_action(OP_COMMAND, "flash:" + partition); 127 b.msg = "Writing '" + partition + "'..."; 128 } 129 130 void fb_queue_flash(const std::string& partition, void* data, uint32_t sz) { 131 Action& a = queue_action(OP_DOWNLOAD, ""); 132 a.data = data; 133 a.size = sz; 134 a.msg = android::base::StringPrintf("Sending '%s' (%d KB)...", partition.c_str(), sz / 1024); 135 136 Action& b = queue_action(OP_COMMAND, "flash:" + partition); 137 b.msg = "Writing '" + partition + "'..."; 138 } 139 140 void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz, 141 size_t current, size_t total) { 142 Action& a = queue_action(OP_DOWNLOAD_SPARSE, ""); 143 a.data = s; 144 a.size = 0; 145 a.msg = android::base::StringPrintf("Sending sparse '%s' %zu/%zu (%d KB)...", partition.c_str(), 146 current, total, sz / 1024); 147 148 Action& b = queue_action(OP_COMMAND, "flash:" + partition); 149 b.msg = 150 android::base::StringPrintf("Writing '%s' %zu/%zu...", partition.c_str(), current, total); 151 } 152 153 static int match(const char* str, const char** value, unsigned count) { 154 unsigned n; 155 156 for (n = 0; n < count; n++) { 157 const char *val = value[n]; 158 int len = strlen(val); 159 int match; 160 161 if ((len > 1) && (val[len-1] == '*')) { 162 len--; 163 match = !strncmp(val, str, len); 164 } else { 165 match = !strcmp(val, str); 166 } 167 168 if (match) return 1; 169 } 170 171 return 0; 172 } 173 174 static int cb_check(Action& a, int status, const char* resp, int invert) { 175 const char** value = reinterpret_cast<const char**>(a.data); 176 unsigned count = a.size; 177 unsigned n; 178 179 if (status) { 180 fprintf(stderr,"FAILED (%s)\n", resp); 181 return status; 182 } 183 184 if (!a.product.empty()) { 185 if (a.product != cur_product) { 186 double split = now(); 187 fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n", cur_product, 188 a.product.c_str(), (split - a.start)); 189 a.start = split; 190 return 0; 191 } 192 } 193 194 int yes = match(resp, value, count); 195 if (invert) yes = !yes; 196 197 if (yes) { 198 double split = now(); 199 fprintf(stderr, "OKAY [%7.3fs]\n", (split - a.start)); 200 a.start = split; 201 return 0; 202 } 203 204 fprintf(stderr, "FAILED\n\n"); 205 fprintf(stderr, "Device %s is '%s'.\n", a.cmd.c_str() + 7, resp); 206 fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", value[0]); 207 for (n = 1; n < count; n++) { 208 fprintf(stderr, " or '%s'", value[n]); 209 } 210 fprintf(stderr, ".\n\n"); 211 return -1; 212 } 213 214 static int cb_require(Action& a, int status, const char* resp) { 215 return cb_check(a, status, resp, 0); 216 } 217 218 static int cb_reject(Action& a, int status, const char* resp) { 219 return cb_check(a, status, resp, 1); 220 } 221 222 void fb_queue_require(const std::string& product, const std::string& var, bool invert, 223 size_t nvalues, const char** values) { 224 Action& a = queue_action(OP_QUERY, "getvar:" + var); 225 a.product = product; 226 a.data = values; 227 a.size = nvalues; 228 a.msg = "Checking " + var; 229 a.func = invert ? cb_reject : cb_require; 230 if (a.data == nullptr) die("out of memory"); 231 } 232 233 static int cb_display(Action& a, int status, const char* resp) { 234 if (status) { 235 fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp); 236 return status; 237 } 238 fprintf(stderr, "%s: %s\n", static_cast<const char*>(a.data), resp); 239 free(static_cast<char*>(a.data)); 240 return 0; 241 } 242 243 void fb_queue_display(const std::string& label, const std::string& var) { 244 Action& a = queue_action(OP_QUERY, "getvar:" + var); 245 a.data = xstrdup(label.c_str()); 246 a.func = cb_display; 247 } 248 249 static int cb_save(Action& a, int status, const char* resp) { 250 if (status) { 251 fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp); 252 return status; 253 } 254 strncpy(reinterpret_cast<char*>(a.data), resp, a.size); 255 return 0; 256 } 257 258 void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size) { 259 Action& a = queue_action(OP_QUERY, "getvar:" + var); 260 a.data = dest; 261 a.size = dest_size; 262 a.func = cb_save; 263 } 264 265 static int cb_do_nothing(Action&, int, const char*) { 266 fprintf(stderr, "\n"); 267 return 0; 268 } 269 270 void fb_queue_reboot() { 271 Action& a = queue_action(OP_COMMAND, "reboot"); 272 a.func = cb_do_nothing; 273 a.msg = "Rebooting..."; 274 } 275 276 void fb_queue_command(const std::string& cmd, const std::string& msg) { 277 Action& a = queue_action(OP_COMMAND, cmd); 278 a.msg = msg; 279 } 280 281 void fb_queue_download(const std::string& name, void* data, uint32_t size) { 282 Action& a = queue_action(OP_DOWNLOAD, ""); 283 a.data = data; 284 a.size = size; 285 a.msg = "Downloading '" + name + "'"; 286 } 287 288 void fb_queue_download_fd(const std::string& name, int fd, uint32_t sz) { 289 Action& a = queue_action(OP_DOWNLOAD_FD, ""); 290 a.fd = fd; 291 a.size = sz; 292 a.msg = android::base::StringPrintf("Sending '%s' (%d KB)", name.c_str(), sz / 1024); 293 } 294 295 void fb_queue_upload(const std::string& outfile) { 296 Action& a = queue_action(OP_UPLOAD, ""); 297 a.data = xstrdup(outfile.c_str()); 298 a.msg = "Uploading '" + outfile + "'"; 299 } 300 301 void fb_queue_notice(const std::string& notice) { 302 Action& a = queue_action(OP_NOTICE, ""); 303 a.msg = notice; 304 } 305 306 void fb_queue_wait_for_disconnect() { 307 queue_action(OP_WAIT_FOR_DISCONNECT, ""); 308 } 309 310 int64_t fb_execute_queue(Transport* transport) { 311 int64_t status = 0; 312 for (auto& a : action_list) { 313 a->start = now(); 314 if (!a->msg.empty()) { 315 fprintf(stderr, "%s\n", a->msg.c_str()); 316 } 317 if (a->op == OP_DOWNLOAD) { 318 status = fb_download_data(transport, a->data, a->size); 319 status = a->func(*a, status, status ? fb_get_error().c_str() : ""); 320 if (status) break; 321 } else if (a->op == OP_DOWNLOAD_FD) { 322 status = fb_download_data_fd(transport, a->fd, a->size); 323 status = a->func(*a, status, status ? fb_get_error().c_str() : ""); 324 if (status) break; 325 } else if (a->op == OP_COMMAND) { 326 status = fb_command(transport, a->cmd); 327 status = a->func(*a, status, status ? fb_get_error().c_str() : ""); 328 if (status) break; 329 } else if (a->op == OP_QUERY) { 330 char resp[FB_RESPONSE_SZ + 1] = {}; 331 status = fb_command_response(transport, a->cmd, resp); 332 status = a->func(*a, status, status ? fb_get_error().c_str() : resp); 333 if (status) break; 334 } else if (a->op == OP_NOTICE) { 335 // We already showed the notice because it's in `Action::msg`. 336 } else if (a->op == OP_DOWNLOAD_SPARSE) { 337 status = fb_download_data_sparse(transport, reinterpret_cast<sparse_file*>(a->data)); 338 status = a->func(*a, status, status ? fb_get_error().c_str() : ""); 339 if (status) break; 340 } else if (a->op == OP_WAIT_FOR_DISCONNECT) { 341 transport->WaitForDisconnect(); 342 } else if (a->op == OP_UPLOAD) { 343 status = fb_upload_data(transport, reinterpret_cast<char*>(a->data)); 344 status = a->func(*a, status, status ? fb_get_error().c_str() : ""); 345 } else { 346 die("unknown action: %d", a->op); 347 } 348 } 349 action_list.clear(); 350 return status; 351 } 352