diff options
Diffstat (limited to 'updater')
-rw-r--r-- | updater/Android.mk | 30 | ||||
-rw-r--r-- | updater/install.c | 788 | ||||
-rw-r--r-- | updater/install.h | 22 | ||||
-rw-r--r-- | updater/updater.c | 128 | ||||
-rw-r--r-- | updater/updater.h | 28 |
5 files changed, 996 insertions, 0 deletions
diff --git a/updater/Android.mk b/updater/Android.mk new file mode 100644 index 000000000..897b9d74c --- /dev/null +++ b/updater/Android.mk @@ -0,0 +1,30 @@ +# Copyright 2009 The Android Open Source Project + +LOCAL_PATH := $(call my-dir) + +updater_src_files := \ + install.c \ + updater.c + +# +# Build a statically-linked binary to include in OTA packages +# +include $(CLEAR_VARS) + +# Build only in eng, so we don't end up with a copy of this in /system +# on user builds. (TODO: find a better way to build device binaries +# needed only for OTA packages.) +LOCAL_MODULE_TAGS := eng + +LOCAL_SRC_FILES := $(updater_src_files) + +LOCAL_STATIC_LIBRARIES := libapplypatch libedify libmtdutils libminzip libz +LOCAL_STATIC_LIBRARIES += libmincrypt libbz +LOCAL_STATIC_LIBRARIES += libcutils libstdc++ libc +LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. + +LOCAL_MODULE := updater + +LOCAL_FORCE_STATIC_EXECUTABLE := true + +include $(BUILD_EXECUTABLE) diff --git a/updater/install.c b/updater/install.c new file mode 100644 index 000000000..c4f5e0341 --- /dev/null +++ b/updater/install.c @@ -0,0 +1,788 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ctype.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "cutils/misc.h" +#include "cutils/properties.h" +#include "edify/expr.h" +#include "minzip/DirUtil.h" +#include "mtdutils/mounts.h" +#include "mtdutils/mtdutils.h" +#include "updater.h" + + +// mount(type, location, mount_point) +// +// what: type="MTD" location="<partition>" to mount a yaffs2 filesystem +// type="vfat" location="/dev/block/<whatever>" to mount a device +char* MountFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + if (argc != 3) { + return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc); + } + char* type; + char* location; + char* mount_point; + if (ReadArgs(state, argv, 3, &type, &location, &mount_point) < 0) { + return NULL; + } + + if (strlen(type) == 0) { + ErrorAbort(state, "type argument to %s() can't be empty", name); + goto done; + } + if (strlen(location) == 0) { + ErrorAbort(state, "location argument to %s() can't be empty", name); + goto done; + } + if (strlen(mount_point) == 0) { + ErrorAbort(state, "mount_point argument to %s() can't be empty", name); + goto done; + } + + mkdir(mount_point, 0755); + + if (strcmp(type, "MTD") == 0) { + mtd_scan_partitions(); + const MtdPartition* mtd; + mtd = mtd_find_partition_by_name(location); + if (mtd == NULL) { + fprintf(stderr, "%s: no mtd partition named \"%s\"", + name, location); + result = strdup(""); + goto done; + } + if (mtd_mount_partition(mtd, mount_point, "yaffs2", 0 /* rw */) != 0) { + fprintf(stderr, "mtd mount of %s failed: %s\n", + location, strerror(errno)); + result = strdup(""); + goto done; + } + result = mount_point; + } else { + if (mount(location, mount_point, type, + MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) { + result = strdup(""); + } else { + result = mount_point; + } + } + +done: + free(type); + free(location); + if (result != mount_point) free(mount_point); + return result; +} + + +// is_mounted(mount_point) +char* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + if (argc != 1) { + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + } + char* mount_point; + if (ReadArgs(state, argv, 1, &mount_point) < 0) { + return NULL; + } + if (strlen(mount_point) == 0) { + ErrorAbort(state, "mount_point argument to unmount() can't be empty"); + goto done; + } + + scan_mounted_volumes(); + const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point); + if (vol == NULL) { + result = strdup(""); + } else { + result = mount_point; + } + +done: + if (result != mount_point) free(mount_point); + return result; +} + + +char* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + if (argc != 1) { + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + } + char* mount_point; + if (ReadArgs(state, argv, 1, &mount_point) < 0) { + return NULL; + } + if (strlen(mount_point) == 0) { + ErrorAbort(state, "mount_point argument to unmount() can't be empty"); + goto done; + } + + scan_mounted_volumes(); + const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point); + if (vol == NULL) { + fprintf(stderr, "unmount of %s failed; no such volume\n", mount_point); + result = strdup(""); + } else { + unmount_mounted_volume(vol); + result = mount_point; + } + +done: + if (result != mount_point) free(mount_point); + return result; +} + + +// format(type, location) +// +// type="MTD" location=partition +char* FormatFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + char* type; + char* location; + if (ReadArgs(state, argv, 2, &type, &location) < 0) { + return NULL; + } + + if (strlen(type) == 0) { + ErrorAbort(state, "type argument to %s() can't be empty", name); + goto done; + } + if (strlen(location) == 0) { + ErrorAbort(state, "location argument to %s() can't be empty", name); + goto done; + } + + if (strcmp(type, "MTD") == 0) { + mtd_scan_partitions(); + const MtdPartition* mtd = mtd_find_partition_by_name(location); + if (mtd == NULL) { + fprintf(stderr, "%s: no mtd partition named \"%s\"", + name, location); + result = strdup(""); + goto done; + } + MtdWriteContext* ctx = mtd_write_partition(mtd); + if (ctx == NULL) { + fprintf(stderr, "%s: can't write \"%s\"", name, location); + result = strdup(""); + goto done; + } + if (mtd_erase_blocks(ctx, -1) == -1) { + mtd_write_close(ctx); + fprintf(stderr, "%s: failed to erase \"%s\"", name, location); + result = strdup(""); + goto done; + } + if (mtd_write_close(ctx) != 0) { + fprintf(stderr, "%s: failed to close \"%s\"", name, location); + result = strdup(""); + goto done; + } + result = location; + } else { + fprintf(stderr, "%s: unsupported type \"%s\"", name, type); + } + +done: + free(type); + if (result != location) free(location); + return result; +} + + +char* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) { + char** paths = malloc(argc * sizeof(char*)); + int i; + for (i = 0; i < argc; ++i) { + paths[i] = Evaluate(state, argv[i]); + if (paths[i] == NULL) { + int j; + for (j = 0; j < i; ++i) { + free(paths[j]); + } + free(paths); + return NULL; + } + } + + bool recursive = (strcmp(name, "delete_recursive") == 0); + + int success = 0; + for (i = 0; i < argc; ++i) { + if ((recursive ? dirUnlinkHierarchy(paths[i]) : unlink(paths[i])) == 0) + ++success; + free(paths[i]); + } + free(paths); + + char buffer[10]; + sprintf(buffer, "%d", success); + return strdup(buffer); +} + + +char* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + char* frac_str; + char* sec_str; + if (ReadArgs(state, argv, 2, &frac_str, &sec_str) < 0) { + return NULL; + } + + double frac = strtod(frac_str, NULL); + int sec = strtol(sec_str, NULL, 10); + + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec); + + free(sec_str); + return frac_str; +} + +char* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 1) { + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + } + char* frac_str; + if (ReadArgs(state, argv, 1, &frac_str) < 0) { + return NULL; + } + + double frac = strtod(frac_str, NULL); + + UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); + fprintf(ui->cmd_pipe, "set_progress %f\n", frac); + + return frac_str; +} + +// package_extract_dir(package_path, destination_path) +char* PackageExtractDirFn(const char* name, State* state, + int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + char* zip_path; + char* dest_path; + if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; + + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + + // To create a consistent system image, never use the clock for timestamps. + struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default + + bool success = mzExtractRecursive(za, zip_path, dest_path, + MZ_EXTRACT_FILES_ONLY, ×tamp, + NULL, NULL); + free(zip_path); + free(dest_path); + return strdup(success ? "t" : ""); +} + + +// package_extract_file(package_path, destination_path) +char* PackageExtractFileFn(const char* name, State* state, + int argc, Expr* argv[]) { + if (argc != 2) { + return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + } + char* zip_path; + char* dest_path; + if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; + + bool success = false; + + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + const ZipEntry* entry = mzFindZipEntry(za, zip_path); + if (entry == NULL) { + fprintf(stderr, "%s: no %s in package\n", name, zip_path); + goto done; + } + + FILE* f = fopen(dest_path, "wb"); + if (f == NULL) { + fprintf(stderr, "%s: can't open %s for write: %s\n", + name, dest_path, strerror(errno)); + goto done; + } + success = mzExtractZipEntryToFile(za, entry, fileno(f)); + fclose(f); + + done: + free(zip_path); + free(dest_path); + return strdup(success ? "t" : ""); +} + + +// symlink target src1 src2 ... +char* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc == 0) { + return ErrorAbort(state, "%s() expects 1+ args, got %d", name, argc); + } + char* target; + target = Evaluate(state, argv[0]); + if (target == NULL) return NULL; + + char** srcs = ReadVarArgs(state, argc-1, argv+1); + if (srcs == NULL) { + free(target); + return NULL; + } + + int i; + for (i = 0; i < argc-1; ++i) { + symlink(target, srcs[i]); + free(srcs[i]); + } + free(srcs); + return strdup(""); +} + + +char* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + bool recursive = (strcmp(name, "set_perm_recursive") == 0); + + int min_args = 4 + (recursive ? 1 : 0); + if (argc < min_args) { + return ErrorAbort(state, "%s() expects %d+ args, got %d", name, argc); + } + + char** args = ReadVarArgs(state, argc, argv); + if (args == NULL) return NULL; + + char* end; + int i; + + int uid = strtoul(args[0], &end, 0); + if (*end != '\0' || args[0][0] == 0) { + ErrorAbort(state, "%s: \"%s\" not a valid uid", name, args[0]); + goto done; + } + + int gid = strtoul(args[1], &end, 0); + if (*end != '\0' || args[1][0] == 0) { + ErrorAbort(state, "%s: \"%s\" not a valid gid", name, args[1]); + goto done; + } + + if (recursive) { + int dir_mode = strtoul(args[2], &end, 0); + if (*end != '\0' || args[2][0] == 0) { + ErrorAbort(state, "%s: \"%s\" not a valid dirmode", name, args[2]); + goto done; + } + + int file_mode = strtoul(args[3], &end, 0); + if (*end != '\0' || args[3][0] == 0) { + ErrorAbort(state, "%s: \"%s\" not a valid filemode", + name, args[3]); + goto done; + } + + for (i = 4; i < argc; ++i) { + dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode); + } + } else { + int mode = strtoul(args[2], &end, 0); + if (*end != '\0' || args[2][0] == 0) { + ErrorAbort(state, "%s: \"%s\" not a valid mode", name, args[2]); + goto done; + } + + for (i = 3; i < argc; ++i) { + chown(args[i], uid, gid); + chmod(args[i], mode); + } + } + result = strdup(""); + +done: + for (i = 0; i < argc; ++i) { + free(args[i]); + } + free(args); + + return result; +} + + +char* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) { + if (argc != 1) { + return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc); + } + char* key; + key = Evaluate(state, argv[0]); + if (key == NULL) return NULL; + + char value[PROPERTY_VALUE_MAX]; + property_get(key, value, ""); + free(key); + + return strdup(value); +} + + +// file_getprop(file, key) +// +// interprets 'file' as a getprop-style file (key=value pairs, one +// per line, # comment lines and blank lines okay), and returns the value +// for 'key' (or "" if it isn't defined). +char* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + char* buffer = NULL; + char* filename; + char* key; + if (ReadArgs(state, argv, 2, &filename, &key) < 0) { + return NULL; + } + + struct stat st; + if (stat(filename, &st) < 0) { + ErrorAbort(state, "%s: failed to stat \"%s\": %s", + name, filename, strerror(errno)); + goto done; + } + +#define MAX_FILE_GETPROP_SIZE 65536 + + if (st.st_size > MAX_FILE_GETPROP_SIZE) { + ErrorAbort(state, "%s too large for %s (max %d)", + filename, name, MAX_FILE_GETPROP_SIZE); + goto done; + } + + buffer = malloc(st.st_size+1); + if (buffer == NULL) { + ErrorAbort(state, "%s: failed to alloc %d bytes", name, st.st_size+1); + goto done; + } + + FILE* f = fopen(filename, "rb"); + if (f == NULL) { + ErrorAbort(state, "%s: failed to open %s: %s", + name, filename, strerror(errno)); + goto done; + } + + if (fread(buffer, 1, st.st_size, f) != st.st_size) { + ErrorAbort(state, "%s: failed to read %d bytes from %s", + name, st.st_size+1, filename); + fclose(f); + goto done; + } + buffer[st.st_size] = '\0'; + + fclose(f); + + char* line = strtok(buffer, "\n"); + do { + // skip whitespace at start of line + while (*line && isspace(*line)) ++line; + + // comment or blank line: skip to next line + if (*line == '\0' || *line == '#') continue; + + char* equal = strchr(line, '='); + if (equal == NULL) { + ErrorAbort(state, "%s: malformed line \"%s\": %s not a prop file?", + name, line, filename); + goto done; + } + + // trim whitespace between key and '=' + char* key_end = equal-1; + while (key_end > line && isspace(*key_end)) --key_end; + key_end[1] = '\0'; + + // not the key we're looking for + if (strcmp(key, line) != 0) continue; + + // skip whitespace after the '=' to the start of the value + char* val_start = equal+1; + while(*val_start && isspace(*val_start)) ++val_start; + + // trim trailing whitespace + char* val_end = val_start + strlen(val_start)-1; + while (val_end > val_start && isspace(*val_end)) --val_end; + val_end[1] = '\0'; + + result = strdup(val_start); + break; + + } while ((line = strtok(NULL, "\n"))); + + if (result == NULL) result = strdup(""); + + done: + free(filename); + free(key); + free(buffer); + return result; +} + + +static bool write_raw_image_cb(const unsigned char* data, + int data_len, void* ctx) { + int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len); + if (r == data_len) return true; + fprintf(stderr, "%s\n", strerror(errno)); + return false; +} + +// write_raw_image(file, partition) +char* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) { + char* result = NULL; + + char* partition; + char* filename; + if (ReadArgs(state, argv, 2, &filename, &partition) < 0) { + return NULL; + } + + if (strlen(partition) == 0) { + ErrorAbort(state, "partition argument to %s can't be empty", name); + goto done; + } + if (strlen(filename) == 0) { + ErrorAbort(state, "file argument to %s can't be empty", name); + goto done; + } + + mtd_scan_partitions(); + const MtdPartition* mtd = mtd_find_partition_by_name(partition); + if (mtd == NULL) { + fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition); + result = strdup(""); + goto done; + } + + MtdWriteContext* ctx = mtd_write_partition(mtd); + if (ctx == NULL) { + fprintf(stderr, "%s: can't write mtd partition \"%s\"\n", + name, partition); + result = strdup(""); + goto done; + } + + bool success; + + FILE* f = fopen(filename, "rb"); + if (f == NULL) { + fprintf(stderr, "%s: can't open %s: %s\n", + name, filename, strerror(errno)); + result = strdup(""); + goto done; + } + + success = true; + char* buffer = malloc(BUFSIZ); + int read; + while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) { + int wrote = mtd_write_data(ctx, buffer, read); + success = success && (wrote == read); + if (!success) { + fprintf(stderr, "mtd_write_data to %s failed: %s\n", + partition, strerror(errno)); + } + } + free(buffer); + fclose(f); + + if (mtd_erase_blocks(ctx, -1) == -1) { + fprintf(stderr, "%s: error erasing blocks of %s\n", name, partition); + } + if (mtd_write_close(ctx) != 0) { + fprintf(stderr, "%s: error closing write of %s\n", name, partition); + } + + printf("%s %s partition from %s\n", + success ? "wrote" : "failed to write", partition, filename); + + result = success ? partition : strdup(""); + +done: + if (result != partition) free(partition); + free(filename); + return result; +} + +// write_firmware_image(file, partition) +// +// partition is "radio" or "hboot" +// file is not used until after updater exits +// +// TODO: this should live in some HTC-specific library +char* WriteFirmwareImageFn(const char* name, State* state, + int argc, Expr* argv[]) { + char* result = NULL; + + char* partition; + char* filename; + if (ReadArgs(state, argv, 2, &filename, &partition) < 0) { + return NULL; + } + + if (strlen(partition) == 0) { + ErrorAbort(state, "partition argument to %s can't be empty", name); + goto done; + } + if (strlen(filename) == 0) { + ErrorAbort(state, "file argument to %s can't be empty", name); + goto done; + } + + FILE* cmd = ((UpdaterInfo*)(state->cookie))->cmd_pipe; + fprintf(cmd, "firmware %s %s\n", partition, filename); + + printf("will write %s firmware from %s\n", partition, filename); + result = partition; + +done: + if (result != partition) free(partition); + free(filename); + return result; +} + + +extern int applypatch(int argc, char** argv); + +// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1:patch, ...) +// apply_patch_check(file, sha1, ...) +// apply_patch_space(bytes) +char* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) { + printf("in applypatchfn (%s)\n", name); + + char* prepend = NULL; + if (strstr(name, "check") != NULL) { + prepend = "-c"; + } else if (strstr(name, "space") != NULL) { + prepend = "-s"; + } + + char** args = ReadVarArgs(state, argc, argv); + if (args == NULL) return NULL; + + // insert the "program name" argv[0] and a copy of the "prepend" + // string (if any) at the start of the args. + + int extra = 1 + (prepend != NULL ? 1 : 0); + char** temp = malloc((argc+extra) * sizeof(char*)); + memcpy(temp+extra, args, argc * sizeof(char*)); + temp[0] = strdup("updater"); + if (prepend) { + temp[1] = strdup(prepend); + } + free(args); + args = temp; + argc += extra; + + printf("calling applypatch\n"); + fflush(stdout); + int result = applypatch(argc, args); + printf("applypatch returned %d\n", result); + + int i; + for (i = 0; i < argc; ++i) { + free(args[i]); + } + free(args); + + switch (result) { + case 0: return strdup("t"); + case 1: return strdup(""); + default: return ErrorAbort(state, "applypatch couldn't parse args"); + } +} + +char* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) { + char** args = ReadVarArgs(state, argc, argv); + if (args == NULL) { + return NULL; + } + + int size = 0; + int i; + for (i = 0; i < argc; ++i) { + size += strlen(args[i]); + } + char* buffer = malloc(size+1); + size = 0; + for (i = 0; i < argc; ++i) { + strcpy(buffer+size, args[i]); + size += strlen(args[i]); + free(args[i]); + } + free(args); + buffer[size] = '\0'; + + char* line = strtok(buffer, "\n"); + while (line) { + fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, + "ui_print %s\n", line); + line = strtok(NULL, "\n"); + } + fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "ui_print\n"); + + return buffer; +} + + +void RegisterInstallFunctions() { + RegisterFunction("mount", MountFn); + RegisterFunction("is_mounted", IsMountedFn); + RegisterFunction("unmount", UnmountFn); + RegisterFunction("format", FormatFn); + RegisterFunction("show_progress", ShowProgressFn); + RegisterFunction("set_progress", SetProgressFn); + RegisterFunction("delete", DeleteFn); + RegisterFunction("delete_recursive", DeleteFn); + RegisterFunction("package_extract_dir", PackageExtractDirFn); + RegisterFunction("package_extract_file", PackageExtractFileFn); + RegisterFunction("symlink", SymlinkFn); + RegisterFunction("set_perm", SetPermFn); + RegisterFunction("set_perm_recursive", SetPermFn); + + RegisterFunction("getprop", GetPropFn); + RegisterFunction("file_getprop", FileGetPropFn); + RegisterFunction("write_raw_image", WriteRawImageFn); + RegisterFunction("write_firmware_image", WriteFirmwareImageFn); + + RegisterFunction("apply_patch", ApplyPatchFn); + RegisterFunction("apply_patch_check", ApplyPatchFn); + RegisterFunction("apply_patch_space", ApplyPatchFn); + + RegisterFunction("ui_print", UIPrintFn); +} diff --git a/updater/install.h b/updater/install.h new file mode 100644 index 000000000..94f344f8e --- /dev/null +++ b/updater/install.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UPDATER_INSTALL_H_ +#define _UPDATER_INSTALL_H_ + +void RegisterInstallFunctions(); + +#endif diff --git a/updater/updater.c b/updater/updater.c new file mode 100644 index 000000000..31d93ae96 --- /dev/null +++ b/updater/updater.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> + +#include "edify/expr.h" +#include "updater.h" +#include "install.h" +#include "minzip/Zip.h" + +// Where in the package we expect to find the edify script to execute. +// (Note it's "updateR-script", not the older "update-script".) +#define SCRIPT_NAME "META-INF/com/google/android/updater-script" + +int main(int argc, char** argv) { + if (argc != 4) { + fprintf(stderr, "unexpected number of arguments (%d)\n", argc); + return 1; + } + + char* version = argv[1]; + if ((version[0] != '1' && version[0] != '2') || version[1] != '\0') { + // We support version "1" or "2". + fprintf(stderr, "wrong updater binary API; expected 1 or 2, got %s\n", + argv[1]); + return 2; + } + + // Set up the pipe for sending commands back to the parent process. + + int fd = atoi(argv[2]); + FILE* cmd_pipe = fdopen(fd, "wb"); + setlinebuf(cmd_pipe); + + // Extract the script from the package. + + char* package_data = argv[3]; + ZipArchive za; + int err; + err = mzOpenZipArchive(package_data, &za); + if (err != 0) { + fprintf(stderr, "failed to open package %s: %s\n", + package_data, strerror(err)); + return 3; + } + + const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME); + if (script_entry == NULL) { + fprintf(stderr, "failed to find %s in %s\n", SCRIPT_NAME, package_data); + return 4; + } + + char* script = malloc(script_entry->uncompLen+1); + if (!mzReadZipEntry(&za, script_entry, script, script_entry->uncompLen)) { + fprintf(stderr, "failed to read script from package\n"); + return 5; + } + script[script_entry->uncompLen] = '\0'; + + // Configure edify's functions. + + RegisterBuiltins(); + RegisterInstallFunctions(); + FinishRegistration(); + + // Parse the script. + + Expr* root; + int error_count = 0; + yy_scan_string(script); + int error = yyparse(&root, &error_count); + if (error != 0 || error_count > 0) { + fprintf(stderr, "%d parse errors\n", error_count); + return 6; + } + + // Evaluate the parsed script. + + UpdaterInfo updater_info; + updater_info.cmd_pipe = cmd_pipe; + updater_info.package_zip = &za; + + State state; + state.cookie = &updater_info; + state.script = script; + state.errmsg = NULL; + + char* result = Evaluate(&state, root); + if (result == NULL) { + if (state.errmsg == NULL) { + fprintf(stderr, "script aborted (no error message)\n"); + fprintf(cmd_pipe, "ui_print script aborted (no error message)\n"); + } else { + fprintf(stderr, "script aborted: %s\n", state.errmsg); + char* line = strtok(state.errmsg, "\n"); + while (line) { + fprintf(cmd_pipe, "ui_print %s\n", line); + line = strtok(NULL, "\n"); + } + fprintf(cmd_pipe, "ui_print\n"); + } + free(state.errmsg); + return 7; + } else { + fprintf(stderr, "script result was [%s]\n", result); + free(result); + } + + mzCloseZipArchive(&za); + free(script); + + return 0; +} diff --git a/updater/updater.h b/updater/updater.h new file mode 100644 index 000000000..22fbfd285 --- /dev/null +++ b/updater/updater.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UPDATER_UPDATER_H_ +#define _UPDATER_UPDATER_H_ + +#include <stdio.h> +#include "minzip/Zip.h" + +typedef struct { + FILE* cmd_pipe; + ZipArchive* package_zip; +} UpdaterInfo; + +#endif |