blob: 5024c083e332b383c8a1e06f69128d1b7f91f132 [file] [log] [blame]
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -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
42#include "extendedcommands.h"
43
44static int gDidShowProgress = 0;
45
46#define UNUSED(p) ((void)(p))
47
48#define CHECK_BOOL() \
49 do { \
50 assert(argv == NULL); \
51 if (argv != NULL) return -1; \
52 assert(argc == true || argc == false); \
53 if (argc != true && argc != false) return -1; \
54 } while (false)
55
56#define CHECK_WORDS() \
57 do { \
58 assert(argc >= 0); \
59 if (argc < 0) return -1; \
60 assert(argc == 0 || argv != NULL); \
61 if (argc != 0 && argv == NULL) return -1; \
62 if (permissions != NULL) { \
63 int CW_I_; \
64 for (CW_I_ = 0; CW_I_ < argc; CW_I_++) { \
65 assert(argv[CW_I_] != NULL); \
66 if (argv[CW_I_] == NULL) return -1; \
67 } \
68 } \
69 } while (false)
70
71#define CHECK_FN() \
72 do { \
73 CHECK_WORDS(); \
74 if (permissions != NULL) { \
75 assert(result == NULL); \
76 if (result != NULL) return -1; \
77 } else { \
78 assert(result != NULL); \
79 if (result == NULL) return -1; \
80 } \
81 } while (false)
82
83#define NO_PERMS(perms) \
84 do { \
85 PermissionRequestList *NP_PRL_ = (perms); \
86 if (NP_PRL_ != NULL) { \
87 int NP_RET_ = addPermissionRequestToList(NP_PRL_, \
88 "", false, PERM_NONE); \
89 if (NP_RET_ < 0) { \
90 /* Returns from the calling function. \
91 */ \
92 return NP_RET_; \
93 } \
94 } \
95 } while (false)
96
97/*
98 * Command definitions
99 */
100
101/* assert <boolexpr>
102 */
103static int
104cmd_assert(const char *name, void *cookie, int argc, const char *argv[],
105 PermissionRequestList *permissions)
106{
107 UNUSED(name);
108 UNUSED(cookie);
109 CHECK_BOOL();
110 NO_PERMS(permissions);
111
112 if (!script_assert_enabled) {
113 return 0;
114 }
115
116 /* If our argument is false, return non-zero (failure)
117 * If our argument is true, return zero (success)
118 */
119 if (argc) {
120 return 0;
121 } else {
122 return 1;
123 }
124}
125
126/* format <root>
127 */
128static int
129cmd_format(const char *name, void *cookie, int argc, const char *argv[],
130 PermissionRequestList *permissions)
131{
132 UNUSED(name);
133 UNUSED(cookie);
134 CHECK_WORDS();
135
136 if (argc != 1) {
137 LOGE("Command %s requires exactly one argument\n", name);
138 return 1;
139 }
140 const char *root = argv[0];
141 ui_print("Formatting %s...\n", root);
142
143 int ret = format_root_device(root);
144 if (ret != 0) {
145 LOGE("Can't format %s\n", root);
146 return 1;
147 }
148
149 return 0;
150}
151
152/* delete <file1> [<fileN> ...]
153 * delete_recursive <file-or-dir1> [<file-or-dirN> ...]
154 *
155 * Like "rm -f", will try to delete every named file/dir, even if
156 * earlier ones fail. Recursive deletes that fail halfway through
157 * give up early.
158 */
159static int
160cmd_delete(const char *name, void *cookie, int argc, const char *argv[],
161 PermissionRequestList *permissions)
162{
163 UNUSED(cookie);
164 CHECK_WORDS();
165 int nerr = 0;
166 bool recurse;
167
168 if (argc < 1) {
169 LOGE("Command %s requires at least one argument\n", name);
170 return 1;
171 }
172
173 recurse = (strcmp(name, "delete_recursive") == 0);
174 ui_print("Deleting files...\n");
175//xxx permissions
176
177 int i;
178 for (i = 0; i < argc; i++) {
179 const char *root_path = argv[i];
180 char pathbuf[PATH_MAX];
181 const char *path;
182
183 /* This guarantees that all paths use "SYSTEM:"-style roots;
184 * plain paths won't make it through translate_root_path().
185 */
186 path = translate_root_path(root_path, pathbuf, sizeof(pathbuf));
187 if (path != NULL) {
188 int ret = ensure_root_path_mounted(root_path);
189 if (ret < 0) {
190 LOGW("Can't mount volume to delete \"%s\"\n", root_path);
191 nerr++;
192 continue;
193 }
194 if (recurse) {
195 ret = dirUnlinkHierarchy(path);
196 } else {
197 ret = unlink(path);
198 }
199 if (ret != 0 && errno != ENOENT) {
200 LOGW("Can't delete %s\n(%s)\n", path, strerror(errno));
201 nerr++;
202 }
203 } else {
204 nerr++;
205 }
206 }
207//TODO: add a way to fail if a delete didn't work
208
209 return 0;
210}
211
212typedef struct {
213 int num_done;
214 int num_total;
215} ExtractContext;
216
217static void extract_count_cb(const char *fn, void *cookie)
218{
219 ++((ExtractContext*) cookie)->num_total;
220}
221
222static void extract_cb(const char *fn, void *cookie)
223{
224 // minzip writes the filename to the log, so we don't need to
225 ExtractContext *ctx = (ExtractContext*) cookie;
226 ui_set_progress((float) ++ctx->num_done / ctx->num_total);
227}
228
229/* copy_dir <src-dir> <dst-dir> [<timestamp>]
230 *
231 * The contents of <src-dir> will become the contents of <dst-dir>.
232 * The original contents of <dst-dir> are preserved unless something
233 * in <src-dir> overwrote them.
234 *
235 * e.g., for "copy_dir PKG:system SYSTEM:", the file "PKG:system/a"
236 * would be copied to "SYSTEM:a".
237 *
238 * The specified timestamp (in decimal seconds since 1970) will be used,
239 * or a fixed default timestamp will be supplied otherwise.
240 */
241static int
242cmd_copy_dir(const char *name, void *cookie, int argc, const char *argv[],
243 PermissionRequestList *permissions)
244{
245 UNUSED(name);
246 UNUSED(cookie);
247 CHECK_WORDS();
248//xxx permissions
249
250 // To create a consistent system image, never use the clock for timestamps.
251 struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
252 if (argc == 3) {
253 char *end;
254 time_t value = strtoul(argv[2], &end, 0);
255 if (value == 0 || end[0] != '\0') {
256 LOGE("Command %s: invalid timestamp \"%s\"\n", name, argv[2]);
257 return 1;
258 } else if (value < timestamp.modtime) {
259 LOGE("Command %s: timestamp \"%s\" too early\n", name, argv[2]);
260 return 1;
261 }
262 timestamp.modtime = timestamp.actime = value;
263 } else if (argc != 2) {
264 LOGE("Command %s requires exactly two arguments\n", name);
265 return 1;
266 }
267
268 // Use 40% of the progress bar (80% post-verification) by default
269 ui_print("Copying files...\n");
270 if (!gDidShowProgress) ui_show_progress(DEFAULT_FILES_PROGRESS_FRACTION, 0);
271
272 /* Mount the destination volume if it isn't already.
273 */
274 const char *dst_root_path = argv[1];
275 int ret = ensure_root_path_mounted(dst_root_path);
276 if (ret < 0) {
277 LOGE("Can't mount %s\n", dst_root_path);
278 return 1;
279 }
280
281 /* Get the real target path.
282 */
283 char dstpathbuf[PATH_MAX];
284 const char *dst_path;
285 dst_path = translate_root_path(dst_root_path,
286 dstpathbuf, sizeof(dstpathbuf));
287 if (dst_path == NULL) {
288 LOGE("Command %s: bad destination path \"%s\"\n", name, dst_root_path);
289 return 1;
290 }
291
292 /* Try to copy the directory. The source may be inside a package.
293 */
294 const char *src_root_path = argv[0];
295 char srcpathbuf[PATH_MAX];
296 const char *src_path;
297 if (is_package_root_path(src_root_path)) {
298 const ZipArchive *package;
299 src_path = translate_package_root_path(src_root_path,
300 srcpathbuf, sizeof(srcpathbuf), &package);
301 if (src_path == NULL) {
302 LOGE("Command %s: bad source path \"%s\"\n", name, src_root_path);
303 return 1;
304 }
305
306 /* Extract the files. Set MZ_EXTRACT_FILES_ONLY, because only files
307 * are validated by the signature. Do a dry run first to count how
308 * many there are (and find some errors early).
309 */
310 ExtractContext ctx;
311 ctx.num_done = 0;
312 ctx.num_total = 0;
313
314 if (!mzExtractRecursive(package, src_path, dst_path,
315 MZ_EXTRACT_FILES_ONLY | MZ_EXTRACT_DRY_RUN,
316 &timestamp, extract_count_cb, (void *) &ctx) ||
317 !mzExtractRecursive(package, src_path, dst_path,
318 MZ_EXTRACT_FILES_ONLY,
319 &timestamp, extract_cb, (void *) &ctx)) {
320 LOGW("Command %s: couldn't extract \"%s\" to \"%s\"\n",
321 name, src_root_path, dst_root_path);
322 return 1;
323 }
324 } else {
325 LOGE("Command %s: non-package source path \"%s\" not yet supported\n",
326 name, src_root_path);
327//xxx mount the src volume
328//xxx
329 return 255;
330 }
331
332 return 0;
333}
334
335/* run_program <program-file> [<args> ...]
336 *
337 * Run an external program included in the update package.
338 */
339static int
340cmd_run_program(const char *name, void *cookie, int argc, const char *argv[],
341 PermissionRequestList *permissions)
342{
343 UNUSED(cookie);
344 CHECK_WORDS();
345
346 if (argc < 1) {
347 LOGE("Command %s requires at least one argument\n", name);
348 return 1;
349 }
350
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800351 static const char *binary = "/tmp/run_program_binary";
Koushik K. Duttaa958ac42010-03-29 23:10:32 -0700352 char path[PATH_MAX];
353 if (!is_package_root_path(argv[0])) {
354 // Copy the program file to temporary storage.
355 binary = argv[0];
356 strcpy(path, binary);
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800357 }
Koushik K. Duttaa958ac42010-03-29 23:10:32 -0700358 else
359 {
360 const ZipArchive *package;
361 if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
362 LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
363 return 1;
364 }
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800365
Koushik K. Duttaa958ac42010-03-29 23:10:32 -0700366 const ZipEntry *entry = mzFindZipEntry(package, path);
367 if (entry == NULL) {
368 LOGE("Can't find %s\n", path);
369 return 1;
370 }
371
372 unlink(binary); // just to be sure
373 int fd = creat(binary, 0755);
374 if (fd < 0) {
375 LOGE("Can't make %s\n", binary);
376 return 1;
377 }
378 bool ok = mzExtractZipEntryToFile(package, entry, fd);
379 close(fd);
380
381 if (!ok) {
382 LOGE("Can't copy %s\n", path);
383 return 1;
384 }
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800385 }
386
387 // Create a copy of argv to NULL-terminate it, as execv requires
388 char **args = (char **) malloc(sizeof(char*) * (argc + 1));
389 memcpy(args, argv, sizeof(char*) * argc);
390 args[argc] = NULL;
391
392 pid_t pid = fork();
393 if (pid == 0) {
394 execv(binary, args);
395 fprintf(stderr, "E:Can't run %s\n(%s)\n", binary, strerror(errno));
396 _exit(-1);
397 }
398
399 int status;
400 waitpid(pid, &status, 0);
Koushik K. Dutta69b75412010-02-23 18:07:31 -0800401 if (WIFEXITED(status) && WEXITSTATUS(status) == 0 || !script_assert_enabled) {
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800402 return 0;
403 } else {
404 LOGE("Error in %s\n(Status %d)\n", path, status);
405 return 1;
406 }
407}
408
409/* set_perm <uid> <gid> <mode> <path> [... <pathN>]
410 * set_perm_recursive <uid> <gid> <dir-mode> <file-mode> <path> [... <pathN>]
411 *
412 * Like "chmod", "chown" and "chgrp" all in one, set ownership and permissions
413 * of single files or entire directory trees. Any error causes failure.
414 * User, group, and modes must all be integer values (hex or octal OK).
415 */
416static int
417cmd_set_perm(const char *name, void *cookie, int argc, const char *argv[],
418 PermissionRequestList *permissions)
419{
420 UNUSED(cookie);
421 CHECK_WORDS();
422 bool recurse = !strcmp(name, "set_perm_recursive");
423
424 int min_args = 4 + (recurse ? 1 : 0);
425 if (argc < min_args) {
426 LOGE("Command %s requires at least %d args\n", name, min_args);
427 return 1;
428 }
429
430 // All the arguments except the path(s) are numeric.
431 int i, n[min_args - 1];
432 for (i = 0; i < min_args - 1; ++i) {
433 char *end;
434 n[i] = strtoul(argv[i], &end, 0);
435 if (end[0] != '\0' || argv[i][0] == '\0') {
436 LOGE("Command %s: invalid argument \"%s\"\n", name, argv[i]);
437 return 1;
438 }
439 }
440
441 for (i = min_args - 1; i < min_args; ++i) {
442 char path[PATH_MAX];
443 if (translate_root_path(argv[i], path, sizeof(path)) == NULL) {
444 LOGE("Command %s: bad path \"%s\"\n", name, argv[i]);
445 return 1;
446 }
447
448 if (ensure_root_path_mounted(argv[i])) {
449 LOGE("Can't mount %s\n", argv[i]);
450 return 1;
451 }
452
453 if (recurse
454 ? dirSetHierarchyPermissions(path, n[0], n[1], n[2], n[3])
455 : (chown(path, n[0], n[1]) || chmod(path, n[2]))) {
456 LOGE("Can't chown/mod %s\n(%s)\n", path, strerror(errno));
457 return 1;
458 }
459 }
460
461 return 0;
462}
463
464/* show_progress <fraction> <duration>
465 *
466 * Use <fraction> of the on-screen progress meter for the next operation,
467 * automatically advancing the meter over <duration> seconds (or more rapidly
468 * if the actual rate of progress can be determined).
469 */
470static int
471cmd_show_progress(const char *name, void *cookie, int argc, const char *argv[],
472 PermissionRequestList *permissions)
473{
474 UNUSED(cookie);
475 CHECK_WORDS();
476
477 if (argc != 2) {
478 LOGE("Command %s requires exactly two arguments\n", name);
479 return 1;
480 }
481
482 char *end;
483 double fraction = strtod(argv[0], &end);
484 if (end[0] != '\0' || argv[0][0] == '\0' || fraction < 0 || fraction > 1) {
485 LOGE("Command %s: invalid fraction \"%s\"\n", name, argv[0]);
486 return 1;
487 }
488
489 int duration = strtoul(argv[1], &end, 0);
490 if (end[0] != '\0' || argv[0][0] == '\0') {
491 LOGE("Command %s: invalid duration \"%s\"\n", name, argv[1]);
492 return 1;
493 }
494
495 // Half of the progress bar is taken by verification,
496 // so everything that happens during installation is scaled.
497 ui_show_progress(fraction * (1 - VERIFICATION_PROGRESS_FRACTION), duration);
498 gDidShowProgress = 1;
499 return 0;
500}
501
502/* symlink <link-target> <link-path>
503 *
504 * Create a symlink, like "ln -s". The link path must not exist already.
505 * Note that <link-path> is in root:path format, but <link-target> is
506 * for the target filesystem (and may be relative).
507 */
508static int
509cmd_symlink(const char *name, void *cookie, int argc, const char *argv[],
510 PermissionRequestList *permissions)
511{
512 UNUSED(cookie);
513 CHECK_WORDS();
514
515 if (argc != 2) {
516 LOGE("Command %s requires exactly two arguments\n", name);
517 return 1;
518 }
519
520 char path[PATH_MAX];
521 if (translate_root_path(argv[1], path, sizeof(path)) == NULL) {
522 LOGE("Command %s: bad path \"%s\"\n", name, argv[1]);
523 return 1;
524 }
525
526 if (ensure_root_path_mounted(argv[1])) {
527 LOGE("Can't mount %s\n", argv[1]);
528 return 1;
529 }
530
531 if (symlink(argv[0], path)) {
532 LOGE("Can't symlink %s\n", path);
533 return 1;
534 }
535
536 return 0;
537}
538
539struct FirmwareContext {
540 size_t total_bytes, done_bytes;
541 char *data;
542};
543
544static bool firmware_fn(const unsigned char *data, int data_len, void *cookie)
545{
546 struct FirmwareContext *context = (struct FirmwareContext*) cookie;
547 if (context->done_bytes + data_len > context->total_bytes) {
548 LOGE("Data overrun in firmware\n");
549 return false; // Should not happen, but let's be safe.
550 }
551
552 memcpy(context->data + context->done_bytes, data, data_len);
553 context->done_bytes += data_len;
554 ui_set_progress(context->done_bytes * 1.0 / context->total_bytes);
555 return true;
556}
557
558/* write_radio_image <src-image>
559 * write_hboot_image <src-image>
560 * Doesn't actually take effect until the rest of installation finishes.
561 */
562static int
563cmd_write_firmware_image(const char *name, void *cookie,
564 int argc, const char *argv[], PermissionRequestList *permissions)
565{
566 UNUSED(cookie);
567 CHECK_WORDS();
568
569 if (argc != 1) {
570 LOGE("Command %s requires exactly one argument\n", name);
571 return 1;
572 }
573
574 const char *type;
575 if (!strcmp(name, "write_radio_image")) {
576 type = "radio";
577 } else if (!strcmp(name, "write_hboot_image")) {
578 type = "hboot";
579 } else {
580 LOGE("Unknown firmware update command %s\n", name);
581 return 1;
582 }
583
584 if (!is_package_root_path(argv[0])) {
585 LOGE("Command %s: non-package image file \"%s\" not supported\n",
586 name, argv[0]);
587 return 1;
588 }
589
590 ui_print("Extracting %s image...\n", type);
591 char path[PATH_MAX];
592 const ZipArchive *package;
593 if (!translate_package_root_path(argv[0], path, sizeof(path), &package)) {
594 LOGE("Command %s: bad source path \"%s\"\n", name, argv[0]);
595 return 1;
596 }
597
598 const ZipEntry *entry = mzFindZipEntry(package, path);
599 if (entry == NULL) {
600 LOGE("Can't find %s\n", path);
601 return 1;
602 }
603
604 // Load the update image into RAM.
605 struct FirmwareContext context;
606 context.total_bytes = mzGetZipEntryUncompLen(entry);
607 context.done_bytes = 0;
608 context.data = malloc(context.total_bytes);
609 if (context.data == NULL) {
610 LOGE("Can't allocate %d bytes for %s\n", context.total_bytes, argv[0]);
611 return 1;
612 }
613
614 if (!mzProcessZipEntryContents(package, entry, firmware_fn, &context) ||
615 context.done_bytes != context.total_bytes) {
616 LOGE("Can't read %s\n", argv[0]);
617 free(context.data);
618 return 1;
619 }
620
621 if (remember_firmware_update(type, context.data, context.total_bytes)) {
622 LOGE("Can't store %s image\n", type);
623 free(context.data);
624 return 1;
625 }
626
627 return 0;
628}
629
630static bool write_raw_image_process_fn(
631 const unsigned char *data,
632 int data_len, void *ctx)
633{
634 int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len);
635 if (r == data_len) return true;
636 LOGE("%s\n", strerror(errno));
637 return false;
638}
639
640/* write_raw_image <src-image> <dest-root>
641 */
642static int
643cmd_write_raw_image(const char *name, void *cookie,
644 int argc, const char *argv[], PermissionRequestList *permissions)
645{
646 UNUSED(cookie);
647 CHECK_WORDS();
648//xxx permissions
649
650 if (argc != 2) {
651 LOGE("Command %s requires exactly two arguments\n", name);
652 return 1;
653 }
654
655 // Use 10% of the progress bar (20% post-verification) by default
656 const char *src_root_path = argv[0];
657 const char *dst_root_path = argv[1];
658 ui_print("Writing %s...\n", dst_root_path);
659 if (!gDidShowProgress) ui_show_progress(DEFAULT_IMAGE_PROGRESS_FRACTION, 0);
660
661 /* Find the source image, which is probably in a package.
662 */
663 if (!is_package_root_path(src_root_path)) {
664 LOGE("Command %s: non-package source path \"%s\" not yet supported\n",
665 name, src_root_path);
666 return 255;
667 }
668
669 /* Get the package.
670 */
671 char srcpathbuf[PATH_MAX];
672 const char *src_path;
673 const ZipArchive *package;
674 src_path = translate_package_root_path(src_root_path,
675 srcpathbuf, sizeof(srcpathbuf), &package);
676 if (src_path == NULL) {
677 LOGE("Command %s: bad source path \"%s\"\n", name, src_root_path);
678 return 1;
679 }
680
681 /* Get the entry.
682 */
683 const ZipEntry *entry = mzFindZipEntry(package, src_path);
684 if (entry == NULL) {
685 LOGE("Missing file %s\n", src_path);
686 return 1;
687 }
688
689 /* Unmount the destination root if it isn't already.
690 */
691 int ret = ensure_root_path_unmounted(dst_root_path);
692 if (ret < 0) {
693 LOGE("Can't unmount %s\n", dst_root_path);
694 return 1;
695 }
696
697 /* Open the partition for writing.
698 */
699 const MtdPartition *partition = get_root_mtd_partition(dst_root_path);
700 if (partition == NULL) {
701 LOGE("Can't find %s\n", dst_root_path);
702 return 1;
703 }
704 MtdWriteContext *context = mtd_write_partition(partition);
705 if (context == NULL) {
706 LOGE("Can't open %s\n", dst_root_path);
707 return 1;
708 }
709
710 /* Extract and write the image.
711 */
712 bool ok = mzProcessZipEntryContents(package, entry,
713 write_raw_image_process_fn, context);
714 if (!ok) {
715 LOGE("Error writing %s\n", dst_root_path);
716 mtd_write_close(context);
717 return 1;
718 }
719
720 if (mtd_erase_blocks(context, -1) == (off_t) -1) {
721 LOGE("Error finishing %s\n", dst_root_path);
722 mtd_write_close(context);
723 return -1;
724 }
725
726 if (mtd_write_close(context)) {
727 LOGE("Error closing %s\n", dst_root_path);
728 return -1;
729 }
730 return 0;
731}
732
733/* mark <resource> dirty|clean
734 */
735static int
736cmd_mark(const char *name, void *cookie, int argc, const char *argv[],
737 PermissionRequestList *permissions)
738{
739 UNUSED(name);
740 UNUSED(cookie);
741 CHECK_WORDS();
742//xxx when marking, save the top-level hash at the mark point
743// so we can retry on failure. Otherwise the hashes won't match,
744// or someone could intentionally dirty the FS to force a downgrade
745//xxx
746 return -1;
747}
748
749/* done
750 */
751static int
752cmd_done(const char *name, void *cookie, int argc, const char *argv[],
753 PermissionRequestList *permissions)
754{
755 UNUSED(name);
756 UNUSED(cookie);
757 CHECK_WORDS();
758//xxx
759 return -1;
760}
761
762
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800763static int
764cmd_backup_rom(const char *name, void *cookie, int argc, const char *argv[],
765 PermissionRequestList *permissions)
766{
767 UNUSED(cookie);
768 CHECK_WORDS();
769
770 char* backup_name = NULL;
Koushik K. Dutta7e0a72c2010-03-15 00:54:34 -0700771 char backup_path[PATH_MAX];
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800772 switch(argc)
773 {
774 case 0:
Koushik K. Dutta7e0a72c2010-03-15 00:54:34 -0700775 {
Koushik K. Dutta6a26e7c2010-03-27 15:26:11 -0700776 char backup_path[PATH_MAX];
777 time_t t = time(NULL);
778 struct tm *tmp = localtime(&t);
779 if (tmp == NULL)
780 {
781 struct timeval tp;
782 gettimeofday(&tp, NULL);
783 sprintf(backup_path, "/sdcard/clockworkmod/backup/%d", tp.tv_sec);
784 }
785 else
786 {
787 strftime(backup_path, sizeof(backup_path), "/sdcard/clockworkmod/backup/%F.%H.%M.%S", tmp);
788 }
Koushik K. Dutta7e0a72c2010-03-15 00:54:34 -0700789 backup_name = backup_path;
790 }
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800791 break;
792 case 1:
793 backup_name = argv[0];
794 break;
795 default:
796 LOGE("Command %s requires zero or one argument\n", name);
797 return 1;
798 }
799
Koushik K. Duttaee57bbc2010-03-12 23:21:12 -0800800 return nandroid_backup(backup_name);
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800801}
802
803static int
804cmd_restore_rom(const char *name, void *cookie, int argc, const char *argv[],
805 PermissionRequestList *permissions)
806{
807 UNUSED(cookie);
808 CHECK_WORDS();
809
Koushik K. Dutta6923cc32010-03-29 14:46:00 -0700810 int restoreboot = 1;
811 int restoresystem = 1;
812 int restoredata = 1;
813 int restorecache = 1;
Koushik K. Dutta68b01902010-04-01 12:20:39 -0700814 int restoresdext = 1;
Koushik K. Dutta6923cc32010-03-29 14:46:00 -0700815 int i;
816 for (i = 0; i < argc; i++)
817 {
818 if (strcmp(argv[i], "noboot") == 0)
819 restoreboot = 0;
820 else if (strcmp(argv[i], "nosystem") == 0)
821 restoresystem = 0;
822 else if (strcmp(argv[i], "nodata") == 0)
823 restoredata = 0;
824 else if (strcmp(argv[i], "nocache") == 0)
825 restorecache = 0;
Koushik K. Dutta68b01902010-04-01 12:20:39 -0700826 else if (strcmp(argv[i], "nosd-ext") == 0)
827 restorecache = 0;
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800828 }
829
Koushik K. Dutta68b01902010-04-01 12:20:39 -0700830 return nandroid_restore(argv[0], restoreboot, restoresystem, restoredata, restorecache, restoresdext);
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800831}
832
Koushik K. Dutta60d7ee02010-03-07 22:16:55 -0800833static int
834cmd_sleep(const char *name, void *cookie, int argc, const char *argv[],
835 PermissionRequestList *permissions)
836{
837 UNUSED(cookie);
838 CHECK_WORDS();
839
840 if (argc != 1) {
841 LOGE("Command %s requires exactly one argument\n", name);
842 return 1;
843 }
844
845 int seconds = atoi(argv[0]);
846 sleep(seconds);
847
848 return 0;
849}
850
851static int
852cmd_print(const char *name, void *cookie, int argc, const char *argv[],
853 PermissionRequestList *permissions)
854{
855 UNUSED(cookie);
856 CHECK_WORDS();
857
858 char message[1024];
859 message[0] = NULL;
860 int i;
861 for (i = 0; i < argc; i++)
862 {
863 strcat(message, argv[i]);
864 strcat(message, " ");
865 }
866 strcat(message, "\n");
867
868 ui_print(message);
869 return 0;
870}
Koushik K. Duttaa9483082010-03-07 21:47:41 -0800871
Koushik K. Duttaea46fe22010-03-08 02:58:04 -0800872static int
873cmd_install_zip(const char *name, void *cookie, int argc, const char *argv[],
874 PermissionRequestList *permissions)
875{
876 UNUSED(cookie);
877 CHECK_WORDS();
878
879 if (argc != 1) {
880 LOGE("Command %s requires exactly one argument\n", name);
881 return 1;
882 }
883
884 return install_zip(argv[0]);
885}
886
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -0800887/*
888 * Function definitions
889 */
890
891/* compatible_with(<version>)
892 *
893 * Returns "true" if this version of the script parser and command
894 * set supports the named version.
895 */
896static int
897fn_compatible_with(const char *name, void *cookie, int argc, const char *argv[],
898 char **result, size_t *resultLen,
899 PermissionRequestList *permissions)
900{
901 UNUSED(name);
902 UNUSED(cookie);
903 CHECK_FN();
904 NO_PERMS(permissions);
905
906 if (argc != 1) {
907 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
908 name, argc);
909 return 1;
910 }
911
912 if (!strcmp(argv[0], "0.1") || !strcmp(argv[0], "0.2")) {
913 *result = strdup("true");
914 } else {
915 *result = strdup("");
916 }
917 if (resultLen != NULL) {
918 *resultLen = strlen(*result);
919 }
920 return 0;
921}
922
923/* update_forced()
924 *
925 * Returns "true" if some system setting has determined that
926 * the update should happen no matter what.
927 */
928static int
929fn_update_forced(const char *name, void *cookie, int argc, const char *argv[],
930 char **result, size_t *resultLen,
931 PermissionRequestList *permissions)
932{
933 UNUSED(name);
934 UNUSED(cookie);
935 CHECK_FN();
936 NO_PERMS(permissions);
937
938 if (argc != 0) {
939 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
940 name, argc);
941 return 1;
942 }
943
944 //xxx check some global or property
945 bool force = true;
946 if (force) {
947 *result = strdup("true");
948 } else {
949 *result = strdup("");
950 }
951 if (resultLen != NULL) {
952 *resultLen = strlen(*result);
953 }
954
955 return 0;
956}
957
958/* get_mark(<resource>)
959 *
960 * Returns the current mark associated with the provided resource.
961 */
962static int
963fn_get_mark(const char *name, void *cookie, int argc, const char *argv[],
964 char **result, size_t *resultLen,
965 PermissionRequestList *permissions)
966{
967 UNUSED(name);
968 UNUSED(cookie);
969 CHECK_FN();
970 NO_PERMS(permissions);
971
972 if (argc != 1) {
973 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
974 name, argc);
975 return 1;
976 }
977
978 //xxx look up the value
979 *result = strdup("");
980 if (resultLen != NULL) {
981 *resultLen = strlen(*result);
982 }
983
984 return 0;
985}
986
987/* hash_dir(<path-to-directory>)
988 */
989static int
990fn_hash_dir(const char *name, void *cookie, int argc, const char *argv[],
991 char **result, size_t *resultLen,
992 PermissionRequestList *permissions)
993{
994 int ret = -1;
995
996 UNUSED(name);
997 UNUSED(cookie);
998 CHECK_FN();
999
1000 const char *dir;
1001 if (argc != 1) {
1002 fprintf(stderr, "%s: wrong number of arguments (%d)\n",
1003 name, argc);
1004 return 1;
1005 } else {
1006 dir = argv[0];
1007 }
1008
1009 if (permissions != NULL) {
1010 if (dir == NULL) {
1011 /* The argument is the result of another function.
1012 * Assume the worst case, where the function returns
1013 * the root.
1014 */
1015 dir = "/";
1016 }
1017 ret = addPermissionRequestToList(permissions, dir, true, PERM_READ);
1018 } else {
1019//xxx build and return the string
1020 *result = strdup("hashvalue");
1021 if (resultLen != NULL) {
1022 *resultLen = strlen(*result);
1023 }
1024 ret = 0;
1025 }
1026
1027 return ret;
1028}
1029
1030/* matches(<str>, <str1> [, <strN>...])
1031 * If <str> matches (strcmp) any of <str1>...<strN>, returns <str>,
1032 * otherwise returns "".
1033 *
1034 * E.g., assert matches(hash_dir("/path"), "hash1", "hash2")
1035 */
1036static int
1037fn_matches(const char *name, void *cookie, int argc, const char *argv[],
1038 char **result, size_t *resultLen,
1039 PermissionRequestList *permissions)
1040{
1041 UNUSED(name);
1042 UNUSED(cookie);
1043 CHECK_FN();
1044 NO_PERMS(permissions);
1045
1046 if (argc < 2) {
1047 fprintf(stderr, "%s: not enough arguments (%d < 2)\n",
1048 name, argc);
1049 return 1;
1050 }
1051
1052 int i;
1053 for (i = 1; i < argc; i++) {
1054 if (strcmp(argv[0], argv[i]) == 0) {
1055 *result = strdup(argv[0]);
1056 if (resultLen != NULL) {
1057 *resultLen = strlen(*result);
1058 }
1059 return 0;
1060 }
1061 }
1062
1063 *result = strdup("");
1064 if (resultLen != NULL) {
1065 *resultLen = 1;
1066 }
1067 return 0;
1068}
1069
1070/* concat(<str>, <str1> [, <strN>...])
1071 * Returns the concatenation of all strings.
1072 */
1073static int
1074fn_concat(const char *name, void *cookie, int argc, const char *argv[],
1075 char **result, size_t *resultLen,
1076 PermissionRequestList *permissions)
1077{
1078 UNUSED(name);
1079 UNUSED(cookie);
1080 CHECK_FN();
1081 NO_PERMS(permissions);
1082
1083 size_t totalLen = 0;
1084 int i;
1085 for (i = 0; i < argc; i++) {
1086 totalLen += strlen(argv[i]);
1087 }
1088
1089 char *s = (char *)malloc(totalLen + 1);
1090 if (s == NULL) {
1091 return -1;
1092 }
1093 s[totalLen] = '\0';
1094 for (i = 0; i < argc; i++) {
1095 //TODO: keep track of the end to avoid walking the string each time
1096 strcat(s, argv[i]);
1097 }
1098 *result = s;
1099 if (resultLen != NULL) {
1100 *resultLen = strlen(s);
1101 }
1102
1103 return 0;
1104}
1105
1106/* getprop(<property>)
1107 * Returns the named Android system property value, or "" if not set.
1108 */
1109static int
1110fn_getprop(const char *name, void *cookie, int argc, const char *argv[],
1111 char **result, size_t *resultLen,
1112 PermissionRequestList *permissions)
1113{
1114 UNUSED(cookie);
1115 CHECK_FN();
1116 NO_PERMS(permissions);
1117
1118 if (argc != 1) {
1119 LOGE("Command %s requires exactly one argument\n", name);
1120 return 1;
1121 }
1122
1123 char value[PROPERTY_VALUE_MAX];
1124 property_get(argv[0], value, "");
1125
1126 *result = strdup(value);
1127 if (resultLen != NULL) {
1128 *resultLen = strlen(*result);
1129 }
1130
1131 return 0;
1132}
1133
1134/* file_contains(<filename>, <substring>)
1135 * Returns "true" if the file exists and contains the specified substring.
1136 */
1137static int
1138fn_file_contains(const char *name, void *cookie, int argc, const char *argv[],
1139 char **result, size_t *resultLen,
1140 PermissionRequestList *permissions)
1141{
1142 UNUSED(cookie);
1143 CHECK_FN();
1144 NO_PERMS(permissions);
1145
1146 if (argc != 2) {
1147 LOGE("Command %s requires exactly two arguments\n", name);
1148 return 1;
1149 }
1150
1151 char pathbuf[PATH_MAX];
1152 const char *root_path = argv[0];
1153 const char *path = translate_root_path(root_path, pathbuf, sizeof(pathbuf));
1154 if (path == NULL) {
1155 LOGE("Command %s: bad path \"%s\"\n", name, root_path);
1156 return 1;
1157 }
1158
1159 if (ensure_root_path_mounted(root_path)) {
1160 LOGE("Can't mount %s\n", root_path);
1161 return 1;
1162 }
1163
1164 const char *needle = argv[1];
1165 char *haystack = (char*) load_file(path, NULL);
1166 if (haystack == NULL) {
1167 LOGI("%s: Can't read \"%s\" (%s)\n", name, path, strerror(errno));
1168 *result = ""; /* File not found is not an error. */
1169 } else if (strstr(haystack, needle) == NULL) {
1170 LOGI("%s: Can't find \"%s\" in \"%s\"\n", name, needle, path);
1171 *result = strdup("");
1172 free(haystack);
1173 } else {
1174 *result = strdup("true");
1175 free(haystack);
1176 }
1177
1178 if (resultLen != NULL) {
1179 *resultLen = strlen(*result);
1180 }
1181 return 0;
1182}
1183
1184int
1185register_update_commands(RecoveryCommandContext *ctx)
1186{
1187 int ret;
1188
1189 ret = commandInit();
1190 if (ret < 0) return ret;
1191
1192 /*
1193 * Commands
1194 */
1195
1196 ret = registerCommand("assert", CMD_ARGS_BOOLEAN, cmd_assert, (void *)ctx);
1197 if (ret < 0) return ret;
1198
1199 ret = registerCommand("delete", CMD_ARGS_WORDS, cmd_delete, (void *)ctx);
1200 if (ret < 0) return ret;
1201
1202 ret = registerCommand("delete_recursive", CMD_ARGS_WORDS, cmd_delete,
1203 (void *)ctx);
1204 if (ret < 0) return ret;
1205
1206 ret = registerCommand("copy_dir", CMD_ARGS_WORDS,
1207 cmd_copy_dir, (void *)ctx);
1208 if (ret < 0) return ret;
1209
1210 ret = registerCommand("run_program", CMD_ARGS_WORDS,
1211 cmd_run_program, (void *)ctx);
1212 if (ret < 0) return ret;
1213
1214 ret = registerCommand("set_perm", CMD_ARGS_WORDS,
1215 cmd_set_perm, (void *)ctx);
1216 if (ret < 0) return ret;
1217
1218 ret = registerCommand("set_perm_recursive", CMD_ARGS_WORDS,
1219 cmd_set_perm, (void *)ctx);
1220 if (ret < 0) return ret;
1221
1222 ret = registerCommand("show_progress", CMD_ARGS_WORDS,
1223 cmd_show_progress, (void *)ctx);
1224 if (ret < 0) return ret;
1225
1226 ret = registerCommand("symlink", CMD_ARGS_WORDS, cmd_symlink, (void *)ctx);
1227 if (ret < 0) return ret;
1228
1229 ret = registerCommand("format", CMD_ARGS_WORDS, cmd_format, (void *)ctx);
1230 if (ret < 0) return ret;
1231
1232 ret = registerCommand("write_radio_image", CMD_ARGS_WORDS,
1233 cmd_write_firmware_image, (void *)ctx);
1234 if (ret < 0) return ret;
1235
1236 ret = registerCommand("write_hboot_image", CMD_ARGS_WORDS,
1237 cmd_write_firmware_image, (void *)ctx);
1238 if (ret < 0) return ret;
1239
1240 ret = registerCommand("write_raw_image", CMD_ARGS_WORDS,
1241 cmd_write_raw_image, (void *)ctx);
1242 if (ret < 0) return ret;
1243
1244 ret = registerCommand("mark", CMD_ARGS_WORDS, cmd_mark, (void *)ctx);
1245 if (ret < 0) return ret;
1246
1247 ret = registerCommand("done", CMD_ARGS_WORDS, cmd_done, (void *)ctx);
1248 if (ret < 0) return ret;
1249
Koushik K. Duttaa9483082010-03-07 21:47:41 -08001250 ret = registerCommand("backup_rom", CMD_ARGS_WORDS, cmd_backup_rom, (void *)ctx);
1251 if (ret < 0) return ret;
1252
1253 ret = registerCommand("restore_rom", CMD_ARGS_WORDS, cmd_restore_rom, (void *)ctx);
1254 if (ret < 0) return ret;
1255
Koushik K. Dutta60d7ee02010-03-07 22:16:55 -08001256 ret = registerCommand("sleep", CMD_ARGS_WORDS, cmd_sleep, (void *)ctx);
1257 if (ret < 0) return ret;
1258
1259 ret = registerCommand("print", CMD_ARGS_WORDS, cmd_print, (void *)ctx);
1260 if (ret < 0) return ret;
1261
Koushik K. Duttaea46fe22010-03-08 02:58:04 -08001262 ret = registerCommand("install_zip", CMD_ARGS_WORDS, cmd_install_zip, (void *)ctx);
1263 if (ret < 0) return ret;
1264
Koushik K. Dutta6060e5c2010-02-11 22:27:06 -08001265 /*
1266 * Functions
1267 */
1268
1269 ret = registerFunction("compatible_with", fn_compatible_with, (void *)ctx);
1270 if (ret < 0) return ret;
1271
1272 ret = registerFunction("update_forced", fn_update_forced, (void *)ctx);
1273 if (ret < 0) return ret;
1274
1275 ret = registerFunction("get_mark", fn_get_mark, (void *)ctx);
1276 if (ret < 0) return ret;
1277
1278 ret = registerFunction("hash_dir", fn_hash_dir, (void *)ctx);
1279 if (ret < 0) return ret;
1280
1281 ret = registerFunction("matches", fn_matches, (void *)ctx);
1282 if (ret < 0) return ret;
1283
1284 ret = registerFunction("concat", fn_concat, (void *)ctx);
1285 if (ret < 0) return ret;
1286
1287 ret = registerFunction("getprop", fn_getprop, (void *)ctx);
1288 if (ret < 0) return ret;
1289
1290 ret = registerFunction("file_contains", fn_file_contains, (void *)ctx);
1291 if (ret < 0) return ret;
1292
1293 return 0;
1294}