blob: b4678ba6f2257f9b3f5339f7652f6a095bef289e [file] [log] [blame]
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#undef NDEBUG
18
19#include <assert.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <limits.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <sys/stat.h>
27#include <sys/types.h>
28#include <sys/wait.h>
29#include <unistd.h>
30#include <unistd.h>
31
32#include "amend/commands.h"
33#include "commands.h"
34#include "common.h"
35#include "cutils/misc.h"
36#include "cutils/properties.h"
37#include "firmware.h"
38#include "minzip/DirUtil.h"
39#include "minzip/Zip.h"
40#include "roots.h"
41
42static int gDidShowProgress = 0;
43
44#define UNUSED(p) ((void)(p))
45
46#define CHECK_BOOL() \
47 do { \
48 assert(argv == NULL); \
49 if (argv != NULL) return -1; \
50 assert(argc == true || argc == false); \
51 if (argc != true && argc != false) return -1; \
52 } while (false)
53
54#define CHECK_WORDS() \
55 do { \
56 assert(argc >= 0); \
57 if (argc < 0) return -1; \
58 assert(argc == 0 || argv != NULL); \
59 if (argc != 0 && argv == NULL) return -1; \
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080060 } while (false)
61
62#define CHECK_FN() \
63 do { \
64 CHECK_WORDS(); \
Doug Zongkerf28c9162009-06-02 15:30:11 -070065 assert(result != NULL); \
66 if (result == NULL) return -1; \
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080067 } while (false)
68
69/*
70 * Command definitions
71 */
72
73/* assert <boolexpr>
74 */
75static int
Doug Zongkerf28c9162009-06-02 15:30:11 -070076cmd_assert(const char *name, void *cookie, int argc, const char *argv[])
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080077{
78 UNUSED(name);
79 UNUSED(cookie);
80 CHECK_BOOL();
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080081
82 /* If our argument is false, return non-zero (failure)
83 * If our argument is true, return zero (success)
84 */
85 if (argc) {
86 return 0;
87 } else {
88 return 1;
89 }
90}
91
92/* format <root>
93 */
94static int
Doug Zongkerf28c9162009-06-02 15:30:11 -070095cmd_format(const char *name, void *cookie, int argc, const char *argv[])
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -080096{
97 UNUSED(name);
98 UNUSED(cookie);
99 CHECK_WORDS();
100
101 if (argc != 1) {
102 LOGE("Command %s requires exactly one argument\n", name);
103 return 1;
104 }
105 const char *root = argv[0];
106 ui_print("Formatting %s...\n", root);
107
108 int ret = format_root_device(root);
109 if (ret != 0) {
110 LOGE("Can't format %s\n", root);
111 return 1;
112 }
113
114 return 0;
115}
116
117/* delete <file1> [<fileN> ...]
118 * delete_recursive <file-or-dir1> [<file-or-dirN> ...]
119 *
120 * Like "rm -f", will try to delete every named file/dir, even if
121 * earlier ones fail. Recursive deletes that fail halfway through
122 * give up early.
123 */
124static int
Doug Zongkerf28c9162009-06-02 15:30:11 -0700125cmd_delete(const char *name, void *cookie, int argc, const char *argv[])
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800126{
127 UNUSED(cookie);
128 CHECK_WORDS();
129 int nerr = 0;
130 bool recurse;
131
132 if (argc < 1) {
133 LOGE("Command %s requires at least one argument\n", name);
134 return 1;
135 }
136
137 recurse = (strcmp(name, "delete_recursive") == 0);
138 ui_print("Deleting files...\n");
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800139
140 int i;
141 for (i = 0; i < argc; i++) {
142 const char *root_path = argv[i];
143 char pathbuf[PATH_MAX];
144 const char *path;
145
146 /* This guarantees that all paths use "SYSTEM:"-style roots;
147 * plain paths won't make it through translate_root_path().
148 */
149 path = translate_root_path(root_path, pathbuf, sizeof(pathbuf));
150 if (path != NULL) {
151 int ret = ensure_root_path_mounted(root_path);
152 if (ret < 0) {
153 LOGW("Can't mount volume to delete \"%s\"\n", root_path);
154 nerr++;
155 continue;
156 }
157 if (recurse) {
158 ret = dirUnlinkHierarchy(path);
159 } else {
160 ret = unlink(path);
161 }
162 if (ret != 0 && errno != ENOENT) {
163 LOGW("Can't delete %s\n(%s)\n", path, strerror(errno));
164 nerr++;
165 }
166 } else {
167 nerr++;
168 }
169 }
170//TODO: add a way to fail if a delete didn't work
171
172 return 0;
173}
174
175typedef struct {
176 int num_done;
177 int num_total;
178} ExtractContext;
179
180static void extract_count_cb(const char *fn, void *cookie)
181{
182 ++((ExtractContext*) cookie)->num_total;
183}
184
185static void extract_cb(const char *fn, void *cookie)
186{
187 // minzip writes the filename to the log, so we don't need to
188 ExtractContext *ctx = (ExtractContext*) cookie;
189 ui_set_progress((float) ++ctx->num_done / ctx->num_total);
190}
191
192/* copy_dir <src-dir> <dst-dir> [<timestamp>]
193 *
194 * The contents of <src-dir> will become the contents of <dst-dir>.
195 * The original contents of <dst-dir> are preserved unless something
196 * in <src-dir> overwrote them.
197 *
198 * e.g., for "copy_dir PKG:system SYSTEM:", the file "PKG:system/a"
199 * would be copied to "SYSTEM:a".
200 *
201 * The specified timestamp (in decimal seconds since 1970) will be used,
202 * or a fixed default timestamp will be supplied otherwise.
203 */
204static int
Doug Zongkerf28c9162009-06-02 15:30:11 -0700205cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[])
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800206{
207 UNUSED(name);
208 UNUSED(cookie);
209 CHECK_WORDS();
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800210
211 // To create a consistent system image, never use the clock for timestamps.
212 struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
213 if (argc == 3) {
214 char *end;
215 time_t value = strtoul(argv[2], &end, 0);
216 if (value == 0 || end[0] != '\0') {
217 LOGE("Command %s: invalid timestamp \"%s\"\n", name, argv[2]);
218 return 1;
219 } else if (value < timestamp.modtime) {
220 LOGE("Command %s: timestamp \"%s\" too early\n", name, argv[2]);
221 return 1;
222 }
223 timestamp.modtime = timestamp.actime = value;
224 } else if (argc != 2) {
225 LOGE("Command %s requires exactly two arguments\n", name);
226 return 1;
227 }
228
229 // Use 40% of the progress bar (80% post-verification) by default
230 ui_print("Copying files...\n");
231 if (!gDidShowProgress) ui_show_progress(DEFAULT_FILES_PROGRESS_FRACTION, 0);
232
233 /* Mount the destination volume if it isn't already.
234 */
235 const char *dst_root_path = argv[1];
236 int ret = ensure_root_path_mounted(dst_root_path);
237 if (ret < 0) {
238 LOGE("Can't mount %s\n", dst_root_path);
239 return 1;
240 }
241
242 /* Get the real target path.
243 */
244 char dstpathbuf[PATH_MAX];
245 const char *dst_path;
246 dst_path = translate_root_path(dst_root_path,
247 dstpathbuf, sizeof(dstpathbuf));
248 if (dst_path == NULL) {
249 LOGE("Command %s: bad destination path \"%s\"\n", name, dst_root_path);
250 return 1;
251 }
252
253 /* Try to copy the directory. The source may be inside a package.
254 */
255 const char *src_root_path = argv[0];
256 char srcpathbuf[PATH_MAX];
257 const char *src_path;
258 if (is_package_root_path(src_root_path)) {
259 const ZipArchive *package;
260 src_path = translate_package_root_path(src_root_path,
261 srcpathbuf, sizeof(srcpathbuf), &package);
262 if (src_path == NULL) {
263 LOGE("Command %s: bad source path \"%s\"\n", name, src_root_path);
264 return 1;
265 }
266
267 /* Extract the files. Set MZ_EXTRACT_FILES_ONLY, because only files
268 * are validated by the signature. Do a dry run first to count how
269 * many there are (and find some errors early).
270 */
271 ExtractContext ctx;
272 ctx.num_done = 0;
273 ctx.num_total = 0;
274
275 if (!mzExtractRecursive(package, src_path, dst_path,
276 MZ_EXTRACT_FILES_ONLY | MZ_EXTRACT_DRY_RUN,
277 &timestamp, extract_count_cb, (void *) &ctx) ||
278 !mzExtractRecursive(package, src_path, dst_path,
279 MZ_EXTRACT_FILES_ONLY,
280 &timestamp, extract_cb, (void *) &ctx)) {
281 LOGW("Command %s: couldn't extract \"%s\" to \"%s\"\n",
282 name, src_root_path, dst_root_path);
283 return 1;
284 }
285 } else {
286 LOGE("Command %s: non-package source path \"%s\" not yet supported\n",
287 name, src_root_path);
288//xxx mount the src volume
289//xxx
290 return 255;
291 }
292
293 return 0;
294}
295
296/* run_program <program-file> [<args> ...]
297 *
298 * Run an external program included in the update package.
299 */
300static int
Doug Zongkerf28c9162009-06-02 15:30:11 -0700301cmd_run_program(const char *name, void *cookie, int argc, const char *argv[])
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800302{
303 UNUSED(cookie);
304 CHECK_WORDS();
305
306 if (argc < 1) {
307 LOGE("Command %s requires at least one argument\n", name);
308 return 1;
309 }
310
311 // Copy the program file to temporary storage.
312 if (!is_package_root_path(argv[0])) {
313 LOGE("Command %s: non-package program file \"%s\" not supported\n",
314 name, argv[0]);
315 return 1;
316 }
317
318 char path[PATH_MAX];
319 const ZipArchive *package;
320 if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
321 LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
322 return 1;
323 }
324
325 const ZipEntry *entry = mzFindZipEntry(package, path);
326 if (entry == NULL) {
327 LOGE("Can't find %s\n", path);
328 return 1;
329 }
330
331 static const char *binary = "/tmp/run_program_binary";
332 unlink(binary); // just to be sure
333 int fd = creat(binary, 0755);
334 if (fd < 0) {
335 LOGE("Can't make %s\n", binary);
336 return 1;
337 }
338 bool ok = mzExtractZipEntryToFile(package, entry, fd);
339 close(fd);
340
341 if (!ok) {
342 LOGE("Can't copy %s\n", path);
343 return 1;
344 }
345
346 // Create a copy of argv to NULL-terminate it, as execv requires
347 char **args = (char **) malloc(sizeof(char*) * (argc + 1));
348 memcpy(args, argv, sizeof(char*) * argc);
349 args[argc] = NULL;
350
351 pid_t pid = fork();
352 if (pid == 0) {
353 execv(binary, args);
354 fprintf(stderr, "E:Can't run %s\n(%s)\n", binary, strerror(errno));
355 _exit(-1);
356 }
357
358 int status;
359 waitpid(pid, &status, 0);
360 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
361 return 0;
362 } else {
363 LOGE("Error in %s\n(Status %d)\n", path, status);
364 return 1;
365 }
366}
367
368/* set_perm <uid> <gid> <mode> <path> [... <pathN>]
369 * set_perm_recursive <uid> <gid> <dir-mode> <file-mode> <path> [... <pathN>]
370 *
371 * Like "chmod", "chown" and "chgrp" all in one, set ownership and permissions
372 * of single files or entire directory trees. Any error causes failure.
373 * User, group, and modes must all be integer values (hex or octal OK).
374 */
375static int
Doug Zongkerf28c9162009-06-02 15:30:11 -0700376cmd_set_perm(const char *name, void *cookie, int argc, const char *argv[])
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800377{
378 UNUSED(cookie);
379 CHECK_WORDS();
380 bool recurse = !strcmp(name, "set_perm_recursive");
381
382 int min_args = 4 + (recurse ? 1 : 0);
383 if (argc < min_args) {
384 LOGE("Command %s requires at least %d args\n", name, min_args);
385 return 1;
386 }
387
388 // All the arguments except the path(s) are numeric.
389 int i, n[min_args - 1];
390 for (i = 0; i < min_args - 1; ++i) {
391 char *end;
392 n[i] = strtoul(argv[i], &end, 0);
393 if (end[0] != '\0' || argv[i][0] == '\0') {
394 LOGE("Command %s: invalid argument \"%s\"\n", name, argv[i]);
395 return 1;
396 }
397 }
398
399 for (i = min_args - 1; i < min_args; ++i) {
400 char path[PATH_MAX];
401 if (translate_root_path(argv[i], path, sizeof(path)) == NULL) {
402 LOGE("Command %s: bad path \"%s\"\n", name, argv[i]);
403 return 1;
404 }
405
406 if (ensure_root_path_mounted(argv[i])) {
407 LOGE("Can't mount %s\n", argv[i]);
408 return 1;
409 }
410
411 if (recurse
412 ? dirSetHierarchyPermissions(path, n[0], n[1], n[2], n[3])
413 : (chown(path, n[0], n[1]) || chmod(path, n[2]))) {
414 LOGE("Can't chown/mod %s\n(%s)\n", path, strerror(errno));
415 return 1;
416 }
417 }
418
419 return 0;
420}
421
422/* show_progress <fraction> <duration>
423 *
424 * Use <fraction> of the on-screen progress meter for the next operation,
425 * automatically advancing the meter over <duration> seconds (or more rapidly
426 * if the actual rate of progress can be determined).
427 */
428static int
Doug Zongkerf28c9162009-06-02 15:30:11 -0700429cmd_show_progress(const char *name, void *cookie, int argc, const char *argv[])
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800430{
431 UNUSED(cookie);
432 CHECK_WORDS();
433
434 if (argc != 2) {
435 LOGE("Command %s requires exactly two arguments\n", name);
436 return 1;
437 }
438
439 char *end;
440 double fraction = strtod(argv[0], &end);
441 if (end[0] != '\0' || argv[0][0] == '\0' || fraction < 0 || fraction > 1) {
442 LOGE("Command %s: invalid fraction \"%s\"\n", name, argv[0]);
443 return 1;
444 }
445
446 int duration = strtoul(argv[1], &end, 0);
447 if (end[0] != '\0' || argv[0][0] == '\0') {
448 LOGE("Command %s: invalid duration \"%s\"\n", name, argv[1]);
449 return 1;
450 }
451
452 // Half of the progress bar is taken by verification,
453 // so everything that happens during installation is scaled.
454 ui_show_progress(fraction * (1 - VERIFICATION_PROGRESS_FRACTION), duration);
455 gDidShowProgress = 1;
456 return 0;
457}
458
459/* symlink <link-target> <link-path>
460 *
461 * Create a symlink, like "ln -s". The link path must not exist already.
462 * Note that <link-path> is in root:path format, but <link-target> is
463 * for the target filesystem (and may be relative).
464 */
465static int
Doug Zongkerf28c9162009-06-02 15:30:11 -0700466cmd_symlink(const char *name, void *cookie, int argc, const char *argv[])
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800467{
468 UNUSED(cookie);
469 CHECK_WORDS();
470
471 if (argc != 2) {
472 LOGE("Command %s requires exactly two arguments\n", name);
473 return 1;
474 }
475
476 char path[PATH_MAX];
477 if (translate_root_path(argv[1], path, sizeof(path)) == NULL) {
478 LOGE("Command %s: bad path \"%s\"\n", name, argv[1]);
479 return 1;
480 }
481
482 if (ensure_root_path_mounted(argv[1])) {
483 LOGE("Can't mount %s\n", argv[1]);
484 return 1;
485 }
486
487 if (symlink(argv[0], path)) {
488 LOGE("Can't symlink %s\n", path);
489 return 1;
490 }
491
492 return 0;
493}
494
495struct FirmwareContext {
496 size_t total_bytes, done_bytes;
497 char *data;
498};
499
500static bool firmware_fn(const unsigned char *data, int data_len, void *cookie)
501{
502 struct FirmwareContext *context = (struct FirmwareContext*) cookie;
503 if (context->done_bytes + data_len > context->total_bytes) {
504 LOGE("Data overrun in firmware\n");
505 return false; // Should not happen, but let's be safe.
506 }
507
508 memcpy(context->data + context->done_bytes, data, data_len);
509 context->done_bytes += data_len;
510 ui_set_progress(context->done_bytes * 1.0 / context->total_bytes);
511 return true;
512}
513
514/* write_radio_image <src-image>
515 * write_hboot_image <src-image>
516 * Doesn't actually take effect until the rest of installation finishes.
517 */
518static int
519cmd_write_firmware_image(const char *name, void *cookie,
Doug Zongkerf28c9162009-06-02 15:30:11 -0700520 int argc, const char *argv[])
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800521{
522 UNUSED(cookie);
523 CHECK_WORDS();
524
525 if (argc != 1) {
526 LOGE("Command %s requires exactly one argument\n", name);
527 return 1;
528 }
529
530 const char *type;
531 if (!strcmp(name, "write_radio_image")) {
532 type = "radio";
533 } else if (!strcmp(name, "write_hboot_image")) {
534 type = "hboot";
535 } else {
536 LOGE("Unknown firmware update command %s\n", name);
537 return 1;
538 }
539
540 if (!is_package_root_path(argv[0])) {
541 LOGE("Command %s: non-package image file \"%s\" not supported\n",
542 name, argv[0]);
543 return 1;
544 }
545
546 ui_print("Extracting %s image...\n", type);
547 char path[PATH_MAX];
548 const ZipArchive *package;
549 if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
550 LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
551 return 1;
552 }
553
554 const ZipEntry *entry = mzFindZipEntry(package, path);
555 if (entry == NULL) {
556 LOGE("Can't find %s\n", path);
557 return 1;
558 }
559
560 // Load the update image into RAM.
561 struct FirmwareContext context;
562 context.total_bytes = mzGetZipEntryUncompLen(entry);
563 context.done_bytes = 0;
564 context.data = malloc(context.total_bytes);
565 if (context.data == NULL) {
566 LOGE("Can't allocate %d bytes for %s\n", context.total_bytes, argv[0]);
567 return 1;
568 }
569
570 if (!mzProcessZipEntryContents(package, entry, firmware_fn, &context) ||
571 context.done_bytes != context.total_bytes) {
572 LOGE("Can't read %s\n", argv[0]);
573 free(context.data);
574 return 1;
575 }
576
577 if (remember_firmware_update(type, context.data, context.total_bytes)) {
578 LOGE("Can't store %s image\n", type);
579 free(context.data);
580 return 1;
581 }
582
583 return 0;
584}
585
586static bool write_raw_image_process_fn(
587 const unsigned char *data,
588 int data_len, void *ctx)
589{
590 int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len);
591 if (r == data_len) return true;
592 LOGE("%s\n", strerror(errno));
593 return false;
594}
595
596/* write_raw_image <src-image> <dest-root>
597 */
598static int
599cmd_write_raw_image(const char *name, void *cookie,
Doug Zongkerf28c9162009-06-02 15:30:11 -0700600 int argc, const char *argv[])
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800601{
602 UNUSED(cookie);
603 CHECK_WORDS();
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800604
605 if (argc != 2) {
606 LOGE("Command %s requires exactly two arguments\n", name);
607 return 1;
608 }
609
610 // Use 10% of the progress bar (20% post-verification) by default
611 const char *src_root_path = argv[0];
612 const char *dst_root_path = argv[1];
613 ui_print("Writing %s...\n", dst_root_path);
614 if (!gDidShowProgress) ui_show_progress(DEFAULT_IMAGE_PROGRESS_FRACTION, 0);
615
616 /* Find the source image, which is probably in a package.
617 */
618 if (!is_package_root_path(src_root_path)) {
619 LOGE("Command %s: non-package source path \"%s\" not yet supported\n",
620 name, src_root_path);
621 return 255;
622 }
623
624 /* Get the package.
625 */
626 char srcpathbuf[PATH_MAX];
627 const char *src_path;
628 const ZipArchive *package;
629 src_path = translate_package_root_path(src_root_path,
630 srcpathbuf, sizeof(srcpathbuf), &package);
631 if (src_path == NULL) {
632 LOGE("Command %s: bad source path \"%s\"\n", name, src_root_path);
633 return 1;
634 }
635
636 /* Get the entry.
637 */
638 const ZipEntry *entry = mzFindZipEntry(package, src_path);
639 if (entry == NULL) {
640 LOGE("Missing file %s\n", src_path);
641 return 1;
642 }
643
644 /* Unmount the destination root if it isn't already.
645 */
646 int ret = ensure_root_path_unmounted(dst_root_path);
647 if (ret < 0) {
648 LOGE("Can't unmount %s\n", dst_root_path);
649 return 1;
650 }
651
652 /* Open the partition for writing.
653 */
654 const MtdPartition *partition = get_root_mtd_partition(dst_root_path);
655 if (partition == NULL) {
656 LOGE("Can't find %s\n", dst_root_path);
657 return 1;
658 }
659 MtdWriteContext *context = mtd_write_partition(partition);
660 if (context == NULL) {
661 LOGE("Can't open %s\n", dst_root_path);
662 return 1;
663 }
664
665 /* Extract and write the image.
666 */
667 bool ok = mzProcessZipEntryContents(package, entry,
668 write_raw_image_process_fn, context);
669 if (!ok) {
670 LOGE("Error writing %s\n", dst_root_path);
671 mtd_write_close(context);
672 return 1;
673 }
674
675 if (mtd_erase_blocks(context, -1) == (off_t) -1) {
676 LOGE("Error finishing %s\n", dst_root_path);
677 mtd_write_close(context);
678 return -1;
679 }
680
681 if (mtd_write_close(context)) {
682 LOGE("Error closing %s\n", dst_root_path);
683 return -1;
684 }
685 return 0;
686}
687
688/* mark <resource> dirty|clean
689 */
690static int
Doug Zongkerf28c9162009-06-02 15:30:11 -0700691cmd_mark(const char *name, void *cookie, int argc, const char *argv[])
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800692{
693 UNUSED(name);
694 UNUSED(cookie);
695 CHECK_WORDS();
696//xxx when marking, save the top-level hash at the mark point
697// so we can retry on failure. Otherwise the hashes won't match,
698// or someone could intentionally dirty the FS to force a downgrade
699//xxx
700 return -1;
701}
702
703/* done
704 */
705static int
Doug Zongkerf28c9162009-06-02 15:30:11 -0700706cmd_done(const char *name, void *cookie, int argc, const char *argv[])
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800707{
708 UNUSED(name);
709 UNUSED(cookie);
710 CHECK_WORDS();
711//xxx
712 return -1;
713}
714
715
716/*
717 * Function definitions
718 */
719
720/* compatible_with(<version>)
721 *
722 * Returns "true" if this version of the script parser and command
723 * set supports the named version.
724 */
725static int
726fn_compatible_with(const char *name, void *cookie, int argc, const char *argv[],
Doug Zongkerf28c9162009-06-02 15:30:11 -0700727 char **result, size_t *resultLen)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800728{
729 UNUSED(name);
730 UNUSED(cookie);
731 CHECK_FN();
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800732
733 if (argc != 1) {
734 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
735 name, argc);
736 return 1;
737 }
738
739 if (!strcmp(argv[0], "0.1") || !strcmp(argv[0], "0.2")) {
740 *result = strdup("true");
741 } else {
742 *result = strdup("");
743 }
744 if (resultLen != NULL) {
745 *resultLen = strlen(*result);
746 }
747 return 0;
748}
749
750/* update_forced()
751 *
752 * Returns "true" if some system setting has determined that
753 * the update should happen no matter what.
754 */
755static int
756fn_update_forced(const char *name, void *cookie, int argc, const char *argv[],
Doug Zongkerf28c9162009-06-02 15:30:11 -0700757 char **result, size_t *resultLen)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800758{
759 UNUSED(name);
760 UNUSED(cookie);
761 CHECK_FN();
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800762
763 if (argc != 0) {
764 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
765 name, argc);
766 return 1;
767 }
768
769 //xxx check some global or property
770 bool force = true;
771 if (force) {
772 *result = strdup("true");
773 } else {
774 *result = strdup("");
775 }
776 if (resultLen != NULL) {
777 *resultLen = strlen(*result);
778 }
779
780 return 0;
781}
782
783/* get_mark(<resource>)
784 *
785 * Returns the current mark associated with the provided resource.
786 */
787static int
788fn_get_mark(const char *name, void *cookie, int argc, const char *argv[],
Doug Zongkerf28c9162009-06-02 15:30:11 -0700789 char **result, size_t *resultLen)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800790{
791 UNUSED(name);
792 UNUSED(cookie);
793 CHECK_FN();
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800794
795 if (argc != 1) {
796 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
797 name, argc);
798 return 1;
799 }
800
801 //xxx look up the value
802 *result = strdup("");
803 if (resultLen != NULL) {
804 *resultLen = strlen(*result);
805 }
806
807 return 0;
808}
809
810/* hash_dir(<path-to-directory>)
811 */
812static int
813fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[],
Doug Zongkerf28c9162009-06-02 15:30:11 -0700814 char **result, size_t *resultLen)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800815{
816 int ret = -1;
817
818 UNUSED(name);
819 UNUSED(cookie);
820 CHECK_FN();
821
822 const char *dir;
823 if (argc != 1) {
824 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
825 name, argc);
826 return 1;
827 } else {
828 dir = argv[0];
829 }
830
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800831 return ret;
832}
833
834/* matches(<str>, <str1> [, <strN>...])
835 * If <str> matches (strcmp) any of <str1>...<strN>, returns <str>,
836 * otherwise returns "".
837 *
838 * E.g., assert matches(hash_dir("/path"), "hash1", "hash2")
839 */
840static int
841fn_matches(const char *name, void *cookie, int argc, const char *argv[],
Doug Zongkerf28c9162009-06-02 15:30:11 -0700842 char **result, size_t *resultLen)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800843{
844 UNUSED(name);
845 UNUSED(cookie);
846 CHECK_FN();
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800847
848 if (argc < 2) {
849 fprintf(stderr, "%s: not enough arguments (%d < 2)\n",
850 name, argc);
851 return 1;
852 }
853
854 int i;
855 for (i = 1; i < argc; i++) {
856 if (strcmp(argv[0], argv[i]) == 0) {
857 *result = strdup(argv[0]);
858 if (resultLen != NULL) {
859 *resultLen = strlen(*result);
860 }
861 return 0;
862 }
863 }
864
865 *result = strdup("");
866 if (resultLen != NULL) {
867 *resultLen = 1;
868 }
869 return 0;
870}
871
872/* concat(<str>, <str1> [, <strN>...])
873 * Returns the concatenation of all strings.
874 */
875static int
876fn_concat(const char *name, void *cookie, int argc, const char *argv[],
Doug Zongkerf28c9162009-06-02 15:30:11 -0700877 char **result, size_t *resultLen)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800878{
879 UNUSED(name);
880 UNUSED(cookie);
881 CHECK_FN();
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800882
883 size_t totalLen = 0;
884 int i;
885 for (i = 0; i < argc; i++) {
886 totalLen += strlen(argv[i]);
887 }
888
889 char *s = (char *)malloc(totalLen + 1);
890 if (s == NULL) {
891 return -1;
892 }
893 s[totalLen] = '\0';
894 for (i = 0; i < argc; i++) {
895 //TODO: keep track of the end to avoid walking the string each time
896 strcat(s, argv[i]);
897 }
898 *result = s;
899 if (resultLen != NULL) {
900 *resultLen = strlen(s);
901 }
902
903 return 0;
904}
905
906/* getprop(<property>)
907 * Returns the named Android system property value, or "" if not set.
908 */
909static int
910fn_getprop(const char *name, void *cookie, int argc, const char *argv[],
Doug Zongkerf28c9162009-06-02 15:30:11 -0700911 char **result, size_t *resultLen)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800912{
913 UNUSED(cookie);
914 CHECK_FN();
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800915
916 if (argc != 1) {
917 LOGE("Command %s requires exactly one argument\n", name);
918 return 1;
919 }
920
921 char value[PROPERTY_VALUE_MAX];
922 property_get(argv[0], value, "");
923
924 *result = strdup(value);
925 if (resultLen != NULL) {
926 *resultLen = strlen(*result);
927 }
928
929 return 0;
930}
931
932/* file_contains(<filename>, <substring>)
933 * Returns "true" if the file exists and contains the specified substring.
934 */
935static int
936fn_file_contains(const char *name, void *cookie, int argc, const char *argv[],
Doug Zongkerf28c9162009-06-02 15:30:11 -0700937 char **result, size_t *resultLen)
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800938{
939 UNUSED(cookie);
940 CHECK_FN();
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800941
942 if (argc != 2) {
943 LOGE("Command %s requires exactly two arguments\n", name);
944 return 1;
945 }
946
947 char pathbuf[PATH_MAX];
948 const char *root_path = argv[0];
949 const char *path = translate_root_path(root_path, pathbuf, sizeof(pathbuf));
950 if (path == NULL) {
951 LOGE("Command %s: bad path \"%s\"\n", name, root_path);
952 return 1;
953 }
954
955 if (ensure_root_path_mounted(root_path)) {
956 LOGE("Can't mount %s\n", root_path);
957 return 1;
958 }
959
960 const char *needle = argv[1];
961 char *haystack = (char*) load_file(path, NULL);
962 if (haystack == NULL) {
963 LOGI("%s: Can't read \"%s\" (%s)\n", name, path, strerror(errno));
964 *result = ""; /* File not found is not an error. */
965 } else if (strstr(haystack, needle) == NULL) {
966 LOGI("%s: Can't find \"%s\" in \"%s\"\n", name, needle, path);
967 *result = strdup("");
968 free(haystack);
969 } else {
970 *result = strdup("true");
971 free(haystack);
972 }
973
974 if (resultLen != NULL) {
975 *resultLen = strlen(*result);
976 }
977 return 0;
978}
979
980int
981register_update_commands(RecoveryCommandContext *ctx)
982{
983 int ret;
984
985 ret = commandInit();
986 if (ret < 0) return ret;
987
988 /*
989 * Commands
990 */
991
992 ret = registerCommand("assert", CMD_ARGS_BOOLEAN, cmd_assert, (void *)ctx);
993 if (ret < 0) return ret;
994
995 ret = registerCommand("delete", CMD_ARGS_WORDS, cmd_delete, (void *)ctx);
996 if (ret < 0) return ret;
997
998 ret = registerCommand("delete_recursive", CMD_ARGS_WORDS, cmd_delete,
999 (void *)ctx);
1000 if (ret < 0) return ret;
1001
1002 ret = registerCommand("copy_dir", CMD_ARGS_WORDS,
1003 cmd_copy_dir, (void *)ctx);
1004 if (ret < 0) return ret;
1005
1006 ret = registerCommand("run_program", CMD_ARGS_WORDS,
1007 cmd_run_program, (void *)ctx);
1008 if (ret < 0) return ret;
1009
1010 ret = registerCommand("set_perm", CMD_ARGS_WORDS,
1011 cmd_set_perm, (void *)ctx);
1012 if (ret < 0) return ret;
1013
1014 ret = registerCommand("set_perm_recursive", CMD_ARGS_WORDS,
1015 cmd_set_perm, (void *)ctx);
1016 if (ret < 0) return ret;
1017
1018 ret = registerCommand("show_progress", CMD_ARGS_WORDS,
1019 cmd_show_progress, (void *)ctx);
1020 if (ret < 0) return ret;
1021
1022 ret = registerCommand("symlink", CMD_ARGS_WORDS, cmd_symlink, (void *)ctx);
1023 if (ret < 0) return ret;
1024
1025 ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, (void *)ctx);
1026 if (ret < 0) return ret;
1027
1028 ret = registerCommand("write_radio_image", CMD_ARGS_WORDS,
1029 cmd_write_firmware_image, (void *)ctx);
1030 if (ret < 0) return ret;
1031
1032 ret = registerCommand("write_hboot_image", CMD_ARGS_WORDS,
1033 cmd_write_firmware_image, (void *)ctx);
1034 if (ret < 0) return ret;
1035
1036 ret = registerCommand("write_raw_image", CMD_ARGS_WORDS,
1037 cmd_write_raw_image, (void *)ctx);
1038 if (ret < 0) return ret;
1039
1040 ret = registerCommand("mark", CMD_ARGS_WORDS, cmd_mark, (void *)ctx);
1041 if (ret < 0) return ret;
1042
1043 ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, (void *)ctx);
1044 if (ret < 0) return ret;
1045
1046 /*
1047 * Functions
1048 */
1049
1050 ret = registerFunction("compatible_with", fn_compatible_with, (void *)ctx);
1051 if (ret < 0) return ret;
1052
1053 ret = registerFunction("update_forced", fn_update_forced, (void *)ctx);
1054 if (ret < 0) return ret;
1055
1056 ret = registerFunction("get_mark", fn_get_mark, (void *)ctx);
1057 if (ret < 0) return ret;
1058
1059 ret = registerFunction("hash_dir", fn_hash_dir, (void *)ctx);
1060 if (ret < 0) return ret;
1061
1062 ret = registerFunction("matches", fn_matches, (void *)ctx);
1063 if (ret < 0) return ret;
1064
1065 ret = registerFunction("concat", fn_concat, (void *)ctx);
1066 if (ret < 0) return ret;
1067
1068 ret = registerFunction("getprop", fn_getprop, (void *)ctx);
1069 if (ret < 0) return ret;
1070
1071 ret = registerFunction("file_contains", fn_file_contains, (void *)ctx);
1072 if (ret < 0) return ret;
1073
1074 return 0;
1075}