Value* 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 StringValue(buffer); }
// symlink target src1 src2 ... // unlinks any previously existing src1, src2, etc before creating symlinks. Value* 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) { if (unlink(srcs[i]) < 0) { if (errno != ENOENT) { fprintf(stderr, "%s: failed to remove %s: %s\n", name, srcs[i], strerror(errno)); } } if (symlink(target, srcs[i]) < 0) { fprintf(stderr, "%s: failed to symlink %s to %s: %s\n", name, srcs[i], target, strerror(errno)); } free(srcs[i]); } free(srcs); return StringValue(strdup("")); }
// apply_patch_check(file, [sha1_1, ...]) Value* ApplyPatchCheckFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc < 1) { return ErrorAbort(state, "%s(): expected at least 1 arg, got %d", name, argc); } char* filename; if (ReadArgs(state, argv, 1, &filename) < 0) { return NULL; } int patchcount = argc-1; char** sha1s = ReadVarArgs(state, argc-1, argv+1); int result = applypatch_check(filename, patchcount, sha1s); int i; for (i = 0; i < patchcount; ++i) { free(sha1s[i]); } free(sha1s); return StringValue(strdup(result == 0 ? "t" : "")); }
Value *CommandFunction(int (*fun) (int, char **), const char *name, State * state, int argc, Expr * argv[]) { Value *ret = NULL; char *argv_str[argc + 1]; int i; char **argv_read = ReadVarArgs(state, argc, argv); if (argv_read == NULL) { ErrorAbort(state, "%s parameter parsing failed.", name); goto done; } argv_str[0] = (char *)name; for (i = 0; i < argc; i++) argv_str[i + 1] = argv_read[i]; if (fun(sizeof(argv_str) / sizeof(char *), argv_str) != EXIT_SUCCESS) { ErrorAbort(state, "%s failed.", name); goto done; } for (i = 0; i < argc; i++) free(argv_read[i]); free(argv_read); ret = StringValue(strdup("t")); done: return ret; }
/* verify_trustzone("TZ_VERSION", "TZ_VERSION", ...) */ Value * VerifyTrustZoneFn(const char *name, State *state, int argc, Expr *argv[]) { char current_tz_version[TZ_VER_BUF_LEN]; int i, ret; ret = get_tz_version(current_tz_version, TZ_VER_BUF_LEN); if (ret) { return ErrorAbort(state, "%s() failed to read current TZ version: %d", name, ret); } char** tz_version = ReadVarArgs(state, argc, argv); if (tz_version == NULL) { return ErrorAbort(state, "%s() error parsing arguments", name); } ret = 0; for (i = 0; i < argc; i++) { uiPrintf(state, "Comparing TZ version %s to %s", tz_version[i], current_tz_version); if (strncmp(tz_version[i], current_tz_version, strlen(tz_version[i])) == 0) { ret = 1; break; } } for (i = 0; i < argc; i++) { free(tz_version[i]); } free(tz_version); return StringValue(strdup(ret ? "1" : "0")); }
Value* RestoreFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc < 1) { return ErrorAbort(state, "%s() expects at least 1 arg", name); } char** args = ReadVarArgs(state, argc, argv); if (args == NULL) { return NULL; } char** args2 = malloc(sizeof(char*) * (argc+1)); memcpy(args2, args, sizeof(char*) * argc); args2[argc] = NULL; char* path = strdup(args2[0]); int restoreboot = 1; int restoresystem = 1; int restoredata = 1; int restorecache = 1; int restoresdext = 1; int restorewebtop = 1; int i; for (i = 1; i < argc; i++) { if (args2[i] == NULL) continue; if (strcmp(args2[i], "noboot") == 0) restoreboot = 0; else if (strcmp(args2[i], "nosystem") == 0) restoresystem = 0; else if (strcmp(args2[i], "nodata") == 0) restoredata = 0; else if (strcmp(args2[i], "nocache") == 0) restorecache = 0; else if (strcmp(args2[i], "nosd-ext") == 0) restoresdext = 0; else if (strcmp(args2[i], "nowebtop") == 0) restorewebtop = 0; } for (i = 0; i < argc; ++i) { free(args[i]); } free(args); free(args2); if (0 != nandroid_restore(path, restoreboot, restoresystem, restoredata, restorecache, restoresdext, 0, restorewebtop, 0)) { free(path); return StringValue(strdup("")); } return StringValue(path); }
Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) { if (argc < 1) { return ErrorAbort(state, "%s() expects at least 1 arg", name); } char** args = ReadVarArgs(state, argc, argv); if (args == NULL) { return NULL; } char** args2 = malloc(sizeof(char*) * (argc+1)); memcpy(args2, args, sizeof(char*) * argc); args2[argc] = NULL; fprintf(stderr, "about to run program [%s] with %d args\n", args2[0], argc); pid_t child = fork(); if (child == 0) { execv(args2[0], args2); fprintf(stderr, "run_program: execv failed: %s\n", strerror(errno)); _exit(1); } int status; waitpid(child, &status, 0); if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) { fprintf(stderr, "run_program: child exited with status %d\n", WEXITSTATUS(status)); } } else if (WIFSIGNALED(status)) { fprintf(stderr, "run_program: child terminated by signal %d\n", WTERMSIG(status)); } int i; for (i = 0; i < argc; ++i) { free(args[i]); } free(args); free(args2); char buffer[20]; sprintf(buffer, "%d", status); return StringValue(strdup(buffer)); }
// undo_retouch_binaries(lib1, lib2, ...) Value* UndoRetouchBinariesFn(const char* name, State* state, int argc, Expr* argv[]) { UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); char **retouch_entries = ReadVarArgs(state, argc, argv); if (retouch_entries == NULL) { return StringValue(strdup("t")); } int i = 0; bool success = true; int32_t override_base; while (i < (argc-1)) { success = success && retouch_one_library(retouch_entries[i], retouch_entries[i+1], 0 /* undo => offset==0 */, NULL); if (!success) ErrorAbort(state, "Failed to unretouch '%s'.", retouch_entries[i]); free(retouch_entries[i]); free(retouch_entries[i+1]); i += 2; } if (i < argc) { free(retouch_entries[i]); success = false; } free(retouch_entries); if (!success) { Value* v = malloc(sizeof(Value)); v->type = VAL_STRING; v->data = NULL; v->size = -1; return v; } return StringValue(strdup("t")); }
// apply_patch_check(file, [sha1_1, ...]) Value* ApplyPatchCheckFn(const char* name, State* state, int argc, Expr* argv[]) { int result = 0; if (argc < 1) { return ErrorAbort(state, "%s(): expected at least 1 arg, got %d", name, argc); } char* filename; if (ReadArgs(state, argv, 1, &filename) < 0) { return NULL; } /* * Some of the symbolic links to shared libraries are created at runtime * based on hw being used as these are created at runtime sha1 would be * different compared to the one generated by updater-script. As we anyway * update the actual file the sym link points to, we can skip ahead sym links. */ if (!CheckSymLink(filename)) { result = 0; goto ret; } int patchcount = argc-1; char** sha1s = ReadVarArgs(state, argc-1, argv+1); result = applypatch_check(filename, patchcount, sha1s); int i; for (i = 0; i < patchcount; ++i) { free(sha1s[i]); } free(sha1s); ret: return StringValue(strdup(result == 0 ? "t" : "")); }
Value* 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 bad = 0; 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) { if (chown(args[i], uid, gid) < 0) { fprintf(stderr, "%s: chown of %s to %d %d failed: %s\n", name, args[i], uid, gid, strerror(errno)); ++bad; } if (chmod(args[i], mode) < 0) { fprintf(stderr, "%s: chmod of %s to %o failed: %s\n", name, args[i], mode, strerror(errno)); ++bad; } } } result = strdup(""); done: for (i = 0; i < argc; ++i) { free(args[i]); } free(args); if (bad) { free(result); return ErrorAbort(state, "%s: some changes failed", name); } return StringValue(result); }
// retouch_binaries(lib1, lib2, ...) Value* RetouchBinariesFn(const char* name, State* state, int argc, Expr* argv[]) { UpdaterInfo* ui = (UpdaterInfo*)(state->cookie); char **retouch_entries = ReadVarArgs(state, argc, argv); if (retouch_entries == NULL) { return StringValue(strdup("t")); } // some randomness from the clock int32_t override_base; bool override_set = false; int32_t random_base = time(NULL) % 1024; // some more randomness from /dev/random FILE *f_random = fopen("/dev/urandom", "rb"); uint16_t random_bits = 0; if (f_random != NULL) { fread(&random_bits, 2, 1, f_random); random_bits = random_bits % 1024; fclose(f_random); } random_base = (random_base + random_bits) % 1024; fprintf(ui->cmd_pipe, "ui_print Random offset: 0x%x\n", random_base); fprintf(ui->cmd_pipe, "ui_print\n"); // make sure we never randomize to zero; this let's us look at a file // and know for sure whether it has been processed; important in the // crash recovery process if (random_base == 0) random_base = 1; // make sure our randomization is page-aligned random_base *= -0x1000; override_base = random_base; int i = 0; bool success = true; while (i < (argc - 1)) { success = success && retouch_one_library(retouch_entries[i], retouch_entries[i+1], random_base, override_set ? NULL : &override_base); if (!success) ErrorAbort(state, "Failed to retouch '%s'.", retouch_entries[i]); free(retouch_entries[i]); free(retouch_entries[i+1]); i += 2; if (success && override_base != 0) { random_base = override_base; override_set = true; } } if (i < argc) { free(retouch_entries[i]); success = false; } free(retouch_entries); if (!success) { Value* v = malloc(sizeof(Value)); v->type = VAL_STRING; v->data = NULL; v->size = -1; return v; } return StringValue(strdup("t")); }