blob: e869134be1ee52df2b4308b7f256e0baf0e81e45 [file] [log] [blame]
Doug Zongker9931f7f2009-06-10 14:11:53 -07001/*
2 * Copyright (C) 2009 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
Doug Zongkerfbf3c102009-06-24 09:36:20 -070017#include <ctype.h>
Doug Zongker9931f7f2009-06-10 14:11:53 -070018#include <errno.h>
19#include <stdarg.h>
Doug Zongkerfbf3c102009-06-24 09:36:20 -070020#include <stdio.h>
Doug Zongker9931f7f2009-06-10 14:11:53 -070021#include <stdlib.h>
22#include <string.h>
23#include <sys/mount.h>
24#include <sys/stat.h>
25#include <sys/types.h>
Doug Zongkera3f89ea2009-09-10 14:10:48 -070026#include <sys/wait.h>
Doug Zongker9931f7f2009-06-10 14:11:53 -070027#include <unistd.h>
28
Doug Zongker8edb00c2009-06-11 17:21:44 -070029#include "cutils/misc.h"
30#include "cutils/properties.h"
Doug Zongker9931f7f2009-06-10 14:11:53 -070031#include "edify/expr.h"
Doug Zongker512536a2010-02-17 16:11:44 -080032#include "mincrypt/sha.h"
Doug Zongker9931f7f2009-06-10 14:11:53 -070033#include "minzip/DirUtil.h"
34#include "mtdutils/mounts.h"
35#include "mtdutils/mtdutils.h"
36#include "updater.h"
Doug Zongker512536a2010-02-17 16:11:44 -080037#include "applypatch/applypatch.h"
Doug Zongker8edb00c2009-06-11 17:21:44 -070038
Doug Zongker9931f7f2009-06-10 14:11:53 -070039// mount(type, location, mount_point)
40//
41// what: type="MTD" location="<partition>" to mount a yaffs2 filesystem
42// type="vfat" location="/dev/block/<whatever>" to mount a device
Doug Zongker512536a2010-02-17 16:11:44 -080043Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -070044 char* result = NULL;
45 if (argc != 3) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -070046 return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc);
Doug Zongker9931f7f2009-06-10 14:11:53 -070047 }
48 char* type;
49 char* location;
50 char* mount_point;
Doug Zongkerd9c9d102009-06-12 12:24:39 -070051 if (ReadArgs(state, argv, 3, &type, &location, &mount_point) < 0) {
Doug Zongker9931f7f2009-06-10 14:11:53 -070052 return NULL;
53 }
54
55 if (strlen(type) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -070056 ErrorAbort(state, "type argument to %s() can't be empty", name);
Doug Zongker9931f7f2009-06-10 14:11:53 -070057 goto done;
58 }
59 if (strlen(location) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -070060 ErrorAbort(state, "location argument to %s() can't be empty", name);
Doug Zongker9931f7f2009-06-10 14:11:53 -070061 goto done;
62 }
63 if (strlen(mount_point) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -070064 ErrorAbort(state, "mount_point argument to %s() can't be empty", name);
Doug Zongker9931f7f2009-06-10 14:11:53 -070065 goto done;
66 }
67
68 mkdir(mount_point, 0755);
69
70 if (strcmp(type, "MTD") == 0) {
71 mtd_scan_partitions();
72 const MtdPartition* mtd;
73 mtd = mtd_find_partition_by_name(location);
74 if (mtd == NULL) {
75 fprintf(stderr, "%s: no mtd partition named \"%s\"",
76 name, location);
77 result = strdup("");
78 goto done;
79 }
80 if (mtd_mount_partition(mtd, mount_point, "yaffs2", 0 /* rw */) != 0) {
81 fprintf(stderr, "mtd mount of %s failed: %s\n",
82 location, strerror(errno));
83 result = strdup("");
84 goto done;
85 }
86 result = mount_point;
87 } else {
88 if (mount(location, mount_point, type,
89 MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) {
Doug Zongker60babf82009-09-18 15:11:24 -070090 fprintf(stderr, "%s: failed to mount %s at %s: %s\n",
91 name, location, mount_point, strerror(errno));
Doug Zongker9931f7f2009-06-10 14:11:53 -070092 result = strdup("");
93 } else {
94 result = mount_point;
95 }
96 }
97
98done:
99 free(type);
100 free(location);
101 if (result != mount_point) free(mount_point);
Doug Zongker512536a2010-02-17 16:11:44 -0800102 return StringValue(result);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700103}
104
Doug Zongker8edb00c2009-06-11 17:21:44 -0700105
106// is_mounted(mount_point)
Doug Zongker512536a2010-02-17 16:11:44 -0800107Value* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker8edb00c2009-06-11 17:21:44 -0700108 char* result = NULL;
109 if (argc != 1) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700110 return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700111 }
112 char* mount_point;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700113 if (ReadArgs(state, argv, 1, &mount_point) < 0) {
Doug Zongker8edb00c2009-06-11 17:21:44 -0700114 return NULL;
115 }
116 if (strlen(mount_point) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700117 ErrorAbort(state, "mount_point argument to unmount() can't be empty");
Doug Zongker8edb00c2009-06-11 17:21:44 -0700118 goto done;
119 }
120
121 scan_mounted_volumes();
122 const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
123 if (vol == NULL) {
124 result = strdup("");
125 } else {
126 result = mount_point;
127 }
128
129done:
130 if (result != mount_point) free(mount_point);
Doug Zongker512536a2010-02-17 16:11:44 -0800131 return StringValue(result);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700132}
133
134
Doug Zongker512536a2010-02-17 16:11:44 -0800135Value* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700136 char* result = NULL;
137 if (argc != 1) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700138 return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700139 }
140 char* mount_point;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700141 if (ReadArgs(state, argv, 1, &mount_point) < 0) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700142 return NULL;
143 }
144 if (strlen(mount_point) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700145 ErrorAbort(state, "mount_point argument to unmount() can't be empty");
Doug Zongker9931f7f2009-06-10 14:11:53 -0700146 goto done;
147 }
148
149 scan_mounted_volumes();
150 const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
151 if (vol == NULL) {
152 fprintf(stderr, "unmount of %s failed; no such volume\n", mount_point);
153 result = strdup("");
154 } else {
155 unmount_mounted_volume(vol);
156 result = mount_point;
157 }
158
159done:
160 if (result != mount_point) free(mount_point);
Doug Zongker512536a2010-02-17 16:11:44 -0800161 return StringValue(result);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700162}
Doug Zongker8edb00c2009-06-11 17:21:44 -0700163
164
Doug Zongker9931f7f2009-06-10 14:11:53 -0700165// format(type, location)
166//
167// type="MTD" location=partition
Doug Zongker512536a2010-02-17 16:11:44 -0800168Value* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700169 char* result = NULL;
170 if (argc != 2) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700171 return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700172 }
173 char* type;
174 char* location;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700175 if (ReadArgs(state, argv, 2, &type, &location) < 0) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700176 return NULL;
177 }
178
179 if (strlen(type) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700180 ErrorAbort(state, "type argument to %s() can't be empty", name);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700181 goto done;
182 }
183 if (strlen(location) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700184 ErrorAbort(state, "location argument to %s() can't be empty", name);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700185 goto done;
186 }
187
188 if (strcmp(type, "MTD") == 0) {
189 mtd_scan_partitions();
190 const MtdPartition* mtd = mtd_find_partition_by_name(location);
191 if (mtd == NULL) {
192 fprintf(stderr, "%s: no mtd partition named \"%s\"",
193 name, location);
194 result = strdup("");
195 goto done;
196 }
197 MtdWriteContext* ctx = mtd_write_partition(mtd);
198 if (ctx == NULL) {
199 fprintf(stderr, "%s: can't write \"%s\"", name, location);
200 result = strdup("");
201 goto done;
202 }
203 if (mtd_erase_blocks(ctx, -1) == -1) {
204 mtd_write_close(ctx);
205 fprintf(stderr, "%s: failed to erase \"%s\"", name, location);
206 result = strdup("");
207 goto done;
208 }
209 if (mtd_write_close(ctx) != 0) {
210 fprintf(stderr, "%s: failed to close \"%s\"", name, location);
211 result = strdup("");
212 goto done;
213 }
214 result = location;
215 } else {
216 fprintf(stderr, "%s: unsupported type \"%s\"", name, type);
217 }
218
219done:
220 free(type);
221 if (result != location) free(location);
Doug Zongker512536a2010-02-17 16:11:44 -0800222 return StringValue(result);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700223}
224
Doug Zongker8edb00c2009-06-11 17:21:44 -0700225
Doug Zongker512536a2010-02-17 16:11:44 -0800226Value* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700227 char** paths = malloc(argc * sizeof(char*));
228 int i;
229 for (i = 0; i < argc; ++i) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700230 paths[i] = Evaluate(state, argv[i]);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700231 if (paths[i] == NULL) {
232 int j;
233 for (j = 0; j < i; ++i) {
234 free(paths[j]);
235 }
236 free(paths);
237 return NULL;
238 }
239 }
240
241 bool recursive = (strcmp(name, "delete_recursive") == 0);
242
243 int success = 0;
244 for (i = 0; i < argc; ++i) {
245 if ((recursive ? dirUnlinkHierarchy(paths[i]) : unlink(paths[i])) == 0)
246 ++success;
247 free(paths[i]);
248 }
249 free(paths);
250
251 char buffer[10];
252 sprintf(buffer, "%d", success);
Doug Zongker512536a2010-02-17 16:11:44 -0800253 return StringValue(strdup(buffer));
Doug Zongker9931f7f2009-06-10 14:11:53 -0700254}
255
Doug Zongker8edb00c2009-06-11 17:21:44 -0700256
Doug Zongker512536a2010-02-17 16:11:44 -0800257Value* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700258 if (argc != 2) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700259 return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700260 }
261 char* frac_str;
262 char* sec_str;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700263 if (ReadArgs(state, argv, 2, &frac_str, &sec_str) < 0) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700264 return NULL;
265 }
266
267 double frac = strtod(frac_str, NULL);
268 int sec = strtol(sec_str, NULL, 10);
269
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700270 UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700271 fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec);
272
Doug Zongker9931f7f2009-06-10 14:11:53 -0700273 free(sec_str);
Doug Zongker512536a2010-02-17 16:11:44 -0800274 return StringValue(frac_str);
Doug Zongkerfbf3c102009-06-24 09:36:20 -0700275}
276
Doug Zongker512536a2010-02-17 16:11:44 -0800277Value* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongkerfbf3c102009-06-24 09:36:20 -0700278 if (argc != 1) {
279 return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
280 }
281 char* frac_str;
282 if (ReadArgs(state, argv, 1, &frac_str) < 0) {
283 return NULL;
284 }
285
286 double frac = strtod(frac_str, NULL);
287
288 UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
289 fprintf(ui->cmd_pipe, "set_progress %f\n", frac);
290
Doug Zongker512536a2010-02-17 16:11:44 -0800291 return StringValue(frac_str);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700292}
293
Doug Zongker8edb00c2009-06-11 17:21:44 -0700294// package_extract_dir(package_path, destination_path)
Doug Zongker512536a2010-02-17 16:11:44 -0800295Value* PackageExtractDirFn(const char* name, State* state,
Doug Zongker8edb00c2009-06-11 17:21:44 -0700296 int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700297 if (argc != 2) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700298 return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700299 }
300 char* zip_path;
301 char* dest_path;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700302 if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
Doug Zongker9931f7f2009-06-10 14:11:53 -0700303
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700304 ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
Doug Zongker9931f7f2009-06-10 14:11:53 -0700305
306 // To create a consistent system image, never use the clock for timestamps.
307 struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
308
309 bool success = mzExtractRecursive(za, zip_path, dest_path,
310 MZ_EXTRACT_FILES_ONLY, &timestamp,
311 NULL, NULL);
312 free(zip_path);
313 free(dest_path);
Doug Zongker512536a2010-02-17 16:11:44 -0800314 return StringValue(strdup(success ? "t" : ""));
Doug Zongker9931f7f2009-06-10 14:11:53 -0700315}
316
Doug Zongker8edb00c2009-06-11 17:21:44 -0700317
318// package_extract_file(package_path, destination_path)
Doug Zongker6aece332010-02-01 14:40:12 -0800319// or
320// package_extract_file(package_path)
321// to return the entire contents of the file as the result of this
Doug Zongker512536a2010-02-17 16:11:44 -0800322// function (the char* returned is actually a FileContents*).
323Value* PackageExtractFileFn(const char* name, State* state,
Doug Zongker8edb00c2009-06-11 17:21:44 -0700324 int argc, Expr* argv[]) {
Doug Zongker6aece332010-02-01 14:40:12 -0800325 if (argc != 1 && argc != 2) {
326 return ErrorAbort(state, "%s() expects 1 or 2 args, got %d",
327 name, argc);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700328 }
Doug Zongker8edb00c2009-06-11 17:21:44 -0700329 bool success = false;
Doug Zongker6aece332010-02-01 14:40:12 -0800330 if (argc == 2) {
331 // The two-argument version extracts to a file.
Doug Zongker8edb00c2009-06-11 17:21:44 -0700332
Doug Zongker6aece332010-02-01 14:40:12 -0800333 char* zip_path;
334 char* dest_path;
335 if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
336
337 ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
338 const ZipEntry* entry = mzFindZipEntry(za, zip_path);
339 if (entry == NULL) {
340 fprintf(stderr, "%s: no %s in package\n", name, zip_path);
341 goto done2;
342 }
343
344 FILE* f = fopen(dest_path, "wb");
345 if (f == NULL) {
346 fprintf(stderr, "%s: can't open %s for write: %s\n",
347 name, dest_path, strerror(errno));
348 goto done2;
349 }
350 success = mzExtractZipEntryToFile(za, entry, fileno(f));
351 fclose(f);
352
353 done2:
354 free(zip_path);
355 free(dest_path);
Doug Zongker512536a2010-02-17 16:11:44 -0800356 return StringValue(strdup(success ? "t" : ""));
Doug Zongker6aece332010-02-01 14:40:12 -0800357 } else {
358 // The one-argument version returns the contents of the file
359 // as the result.
360
361 char* zip_path;
Doug Zongker512536a2010-02-17 16:11:44 -0800362 Value* v = malloc(sizeof(Value));
363 v->type = VAL_BLOB;
364 v->size = -1;
365 v->data = NULL;
Doug Zongker6aece332010-02-01 14:40:12 -0800366
367 if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL;
368
369 ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
370 const ZipEntry* entry = mzFindZipEntry(za, zip_path);
371 if (entry == NULL) {
372 fprintf(stderr, "%s: no %s in package\n", name, zip_path);
373 goto done1;
374 }
375
Doug Zongker512536a2010-02-17 16:11:44 -0800376 v->size = mzGetZipEntryUncompLen(entry);
377 v->data = malloc(v->size);
378 if (v->data == NULL) {
Doug Zongker6aece332010-02-01 14:40:12 -0800379 fprintf(stderr, "%s: failed to allocate %ld bytes for %s\n",
Doug Zongker512536a2010-02-17 16:11:44 -0800380 name, (long)v->size, zip_path);
Doug Zongker6aece332010-02-01 14:40:12 -0800381 goto done1;
382 }
383
Doug Zongker512536a2010-02-17 16:11:44 -0800384 success = mzExtractZipEntryToBuffer(za, entry,
385 (unsigned char *)v->data);
Doug Zongker6aece332010-02-01 14:40:12 -0800386
387 done1:
388 free(zip_path);
389 if (!success) {
Doug Zongker512536a2010-02-17 16:11:44 -0800390 free(v->data);
391 v->data = NULL;
392 v->size = -1;
Doug Zongker6aece332010-02-01 14:40:12 -0800393 }
Doug Zongker512536a2010-02-17 16:11:44 -0800394 return v;
Doug Zongker8edb00c2009-06-11 17:21:44 -0700395 }
Doug Zongker8edb00c2009-06-11 17:21:44 -0700396}
397
398
Doug Zongker9931f7f2009-06-10 14:11:53 -0700399// symlink target src1 src2 ...
Doug Zongker60babf82009-09-18 15:11:24 -0700400// unlinks any previously existing src1, src2, etc before creating symlinks.
Doug Zongker512536a2010-02-17 16:11:44 -0800401Value* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700402 if (argc == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700403 return ErrorAbort(state, "%s() expects 1+ args, got %d", name, argc);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700404 }
405 char* target;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700406 target = Evaluate(state, argv[0]);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700407 if (target == NULL) return NULL;
408
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700409 char** srcs = ReadVarArgs(state, argc-1, argv+1);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700410 if (srcs == NULL) {
411 free(target);
412 return NULL;
413 }
414
415 int i;
416 for (i = 0; i < argc-1; ++i) {
Doug Zongker60babf82009-09-18 15:11:24 -0700417 if (unlink(srcs[i]) < 0) {
418 if (errno != ENOENT) {
419 fprintf(stderr, "%s: failed to remove %s: %s\n",
420 name, srcs[i], strerror(errno));
421 }
422 }
423 if (symlink(target, srcs[i]) < 0) {
424 fprintf(stderr, "%s: failed to symlink %s to %s: %s\n",
425 name, srcs[i], target, strerror(errno));
426 }
Doug Zongker9931f7f2009-06-10 14:11:53 -0700427 free(srcs[i]);
428 }
429 free(srcs);
Doug Zongker512536a2010-02-17 16:11:44 -0800430 return StringValue(strdup(""));
Doug Zongker9931f7f2009-06-10 14:11:53 -0700431}
432
Doug Zongker8edb00c2009-06-11 17:21:44 -0700433
Doug Zongker512536a2010-02-17 16:11:44 -0800434Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700435 char* result = NULL;
436 bool recursive = (strcmp(name, "set_perm_recursive") == 0);
437
438 int min_args = 4 + (recursive ? 1 : 0);
439 if (argc < min_args) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700440 return ErrorAbort(state, "%s() expects %d+ args, got %d", name, argc);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700441 }
442
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700443 char** args = ReadVarArgs(state, argc, argv);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700444 if (args == NULL) return NULL;
445
446 char* end;
447 int i;
448
449 int uid = strtoul(args[0], &end, 0);
450 if (*end != '\0' || args[0][0] == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700451 ErrorAbort(state, "%s: \"%s\" not a valid uid", name, args[0]);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700452 goto done;
453 }
454
455 int gid = strtoul(args[1], &end, 0);
456 if (*end != '\0' || args[1][0] == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700457 ErrorAbort(state, "%s: \"%s\" not a valid gid", name, args[1]);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700458 goto done;
459 }
460
461 if (recursive) {
462 int dir_mode = strtoul(args[2], &end, 0);
463 if (*end != '\0' || args[2][0] == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700464 ErrorAbort(state, "%s: \"%s\" not a valid dirmode", name, args[2]);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700465 goto done;
466 }
467
468 int file_mode = strtoul(args[3], &end, 0);
469 if (*end != '\0' || args[3][0] == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700470 ErrorAbort(state, "%s: \"%s\" not a valid filemode",
Doug Zongker9931f7f2009-06-10 14:11:53 -0700471 name, args[3]);
472 goto done;
473 }
474
475 for (i = 4; i < argc; ++i) {
476 dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode);
477 }
478 } else {
479 int mode = strtoul(args[2], &end, 0);
480 if (*end != '\0' || args[2][0] == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700481 ErrorAbort(state, "%s: \"%s\" not a valid mode", name, args[2]);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700482 goto done;
483 }
484
Doug Zongker0bbfe3d2009-06-25 13:37:31 -0700485 for (i = 3; i < argc; ++i) {
Doug Zongker60babf82009-09-18 15:11:24 -0700486 if (chown(args[i], uid, gid) < 0) {
487 fprintf(stderr, "%s: chown of %s to %d %d failed: %s\n",
488 name, args[i], uid, gid, strerror(errno));
489 }
490 if (chmod(args[i], mode) < 0) {
491 fprintf(stderr, "%s: chmod of %s to %o failed: %s\n",
492 name, args[i], mode, strerror(errno));
493 }
Doug Zongker9931f7f2009-06-10 14:11:53 -0700494 }
495 }
496 result = strdup("");
497
498done:
499 for (i = 0; i < argc; ++i) {
500 free(args[i]);
501 }
502 free(args);
503
Doug Zongker512536a2010-02-17 16:11:44 -0800504 return StringValue(result);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700505}
506
Doug Zongker8edb00c2009-06-11 17:21:44 -0700507
Doug Zongker512536a2010-02-17 16:11:44 -0800508Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker8edb00c2009-06-11 17:21:44 -0700509 if (argc != 1) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700510 return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700511 }
512 char* key;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700513 key = Evaluate(state, argv[0]);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700514 if (key == NULL) return NULL;
515
516 char value[PROPERTY_VALUE_MAX];
517 property_get(key, value, "");
518 free(key);
519
Doug Zongker512536a2010-02-17 16:11:44 -0800520 return StringValue(strdup(value));
Doug Zongker8edb00c2009-06-11 17:21:44 -0700521}
522
523
Doug Zongker47cace92009-06-18 10:11:50 -0700524// file_getprop(file, key)
525//
526// interprets 'file' as a getprop-style file (key=value pairs, one
527// per line, # comment lines and blank lines okay), and returns the value
528// for 'key' (or "" if it isn't defined).
Doug Zongker512536a2010-02-17 16:11:44 -0800529Value* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker47cace92009-06-18 10:11:50 -0700530 char* result = NULL;
531 char* buffer = NULL;
532 char* filename;
533 char* key;
534 if (ReadArgs(state, argv, 2, &filename, &key) < 0) {
535 return NULL;
536 }
537
538 struct stat st;
539 if (stat(filename, &st) < 0) {
540 ErrorAbort(state, "%s: failed to stat \"%s\": %s",
541 name, filename, strerror(errno));
542 goto done;
543 }
544
545#define MAX_FILE_GETPROP_SIZE 65536
546
547 if (st.st_size > MAX_FILE_GETPROP_SIZE) {
548 ErrorAbort(state, "%s too large for %s (max %d)",
549 filename, name, MAX_FILE_GETPROP_SIZE);
550 goto done;
551 }
552
553 buffer = malloc(st.st_size+1);
554 if (buffer == NULL) {
555 ErrorAbort(state, "%s: failed to alloc %d bytes", name, st.st_size+1);
556 goto done;
557 }
558
559 FILE* f = fopen(filename, "rb");
560 if (f == NULL) {
561 ErrorAbort(state, "%s: failed to open %s: %s",
562 name, filename, strerror(errno));
563 goto done;
564 }
565
566 if (fread(buffer, 1, st.st_size, f) != st.st_size) {
567 ErrorAbort(state, "%s: failed to read %d bytes from %s",
568 name, st.st_size+1, filename);
569 fclose(f);
570 goto done;
571 }
572 buffer[st.st_size] = '\0';
573
574 fclose(f);
575
576 char* line = strtok(buffer, "\n");
577 do {
578 // skip whitespace at start of line
579 while (*line && isspace(*line)) ++line;
580
581 // comment or blank line: skip to next line
582 if (*line == '\0' || *line == '#') continue;
583
584 char* equal = strchr(line, '=');
585 if (equal == NULL) {
586 ErrorAbort(state, "%s: malformed line \"%s\": %s not a prop file?",
587 name, line, filename);
588 goto done;
589 }
590
591 // trim whitespace between key and '='
592 char* key_end = equal-1;
593 while (key_end > line && isspace(*key_end)) --key_end;
594 key_end[1] = '\0';
595
596 // not the key we're looking for
597 if (strcmp(key, line) != 0) continue;
598
599 // skip whitespace after the '=' to the start of the value
600 char* val_start = equal+1;
601 while(*val_start && isspace(*val_start)) ++val_start;
602
603 // trim trailing whitespace
604 char* val_end = val_start + strlen(val_start)-1;
605 while (val_end > val_start && isspace(*val_end)) --val_end;
606 val_end[1] = '\0';
607
608 result = strdup(val_start);
609 break;
610
611 } while ((line = strtok(NULL, "\n")));
612
613 if (result == NULL) result = strdup("");
614
615 done:
616 free(filename);
617 free(key);
618 free(buffer);
Doug Zongker512536a2010-02-17 16:11:44 -0800619 return StringValue(result);
Doug Zongker47cace92009-06-18 10:11:50 -0700620}
621
622
Doug Zongker8edb00c2009-06-11 17:21:44 -0700623static bool write_raw_image_cb(const unsigned char* data,
624 int data_len, void* ctx) {
625 int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len);
626 if (r == data_len) return true;
627 fprintf(stderr, "%s\n", strerror(errno));
628 return false;
629}
630
631// write_raw_image(file, partition)
Doug Zongker512536a2010-02-17 16:11:44 -0800632Value* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker8edb00c2009-06-11 17:21:44 -0700633 char* result = NULL;
634
635 char* partition;
636 char* filename;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700637 if (ReadArgs(state, argv, 2, &filename, &partition) < 0) {
Doug Zongker8edb00c2009-06-11 17:21:44 -0700638 return NULL;
639 }
640
641 if (strlen(partition) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700642 ErrorAbort(state, "partition argument to %s can't be empty", name);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700643 goto done;
644 }
645 if (strlen(filename) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700646 ErrorAbort(state, "file argument to %s can't be empty", name);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700647 goto done;
648 }
649
650 mtd_scan_partitions();
651 const MtdPartition* mtd = mtd_find_partition_by_name(partition);
652 if (mtd == NULL) {
653 fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition);
654 result = strdup("");
655 goto done;
656 }
657
658 MtdWriteContext* ctx = mtd_write_partition(mtd);
659 if (ctx == NULL) {
660 fprintf(stderr, "%s: can't write mtd partition \"%s\"\n",
661 name, partition);
662 result = strdup("");
663 goto done;
664 }
665
666 bool success;
667
668 FILE* f = fopen(filename, "rb");
669 if (f == NULL) {
670 fprintf(stderr, "%s: can't open %s: %s\n",
671 name, filename, strerror(errno));
672 result = strdup("");
673 goto done;
674 }
675
676 success = true;
677 char* buffer = malloc(BUFSIZ);
678 int read;
679 while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
680 int wrote = mtd_write_data(ctx, buffer, read);
681 success = success && (wrote == read);
682 if (!success) {
683 fprintf(stderr, "mtd_write_data to %s failed: %s\n",
684 partition, strerror(errno));
685 }
686 }
687 free(buffer);
688 fclose(f);
689
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700690 if (mtd_erase_blocks(ctx, -1) == -1) {
691 fprintf(stderr, "%s: error erasing blocks of %s\n", name, partition);
692 }
693 if (mtd_write_close(ctx) != 0) {
694 fprintf(stderr, "%s: error closing write of %s\n", name, partition);
695 }
696
Doug Zongker8edb00c2009-06-11 17:21:44 -0700697 printf("%s %s partition from %s\n",
698 success ? "wrote" : "failed to write", partition, filename);
699
700 result = success ? partition : strdup("");
701
702done:
703 if (result != partition) free(partition);
704 free(filename);
Doug Zongker512536a2010-02-17 16:11:44 -0800705 return StringValue(result);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700706}
707
Doug Zongker8edb00c2009-06-11 17:21:44 -0700708// apply_patch_space(bytes)
Doug Zongkerc4351c72010-02-22 14:46:32 -0800709Value* ApplyPatchSpaceFn(const char* name, State* state,
710 int argc, Expr* argv[]) {
711 char* bytes_str;
712 if (ReadArgs(state, argv, 1, &bytes_str) < 0) {
713 return NULL;
714 }
715
716 char* endptr;
717 size_t bytes = strtol(bytes_str, &endptr, 10);
718 if (bytes == 0 && endptr == bytes_str) {
719 ErrorAbort(state, "%s(): can't parse \"%s\" as byte count\n\n",
720 name, bytes_str);
721 free(bytes_str);
722 return NULL;
723 }
724
725 return StringValue(strdup(CacheSizeCheck(bytes) ? "" : "t"));
726}
727
728
729// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1_1, patch_1, ...)
Doug Zongker512536a2010-02-17 16:11:44 -0800730Value* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongkerc4351c72010-02-22 14:46:32 -0800731 if (argc < 6 || (argc % 2) == 1) {
732 return ErrorAbort(state, "%s(): expected at least 6 args and an "
733 "even number, got %d",
734 name, argc);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700735 }
736
Doug Zongkerc4351c72010-02-22 14:46:32 -0800737 char* source_filename;
738 char* target_filename;
739 char* target_sha1;
740 char* target_size_str;
741 if (ReadArgs(state, argv, 4, &source_filename, &target_filename,
742 &target_sha1, &target_size_str) < 0) {
743 return NULL;
Doug Zongker8edb00c2009-06-11 17:21:44 -0700744 }
Doug Zongker8edb00c2009-06-11 17:21:44 -0700745
Doug Zongkerc4351c72010-02-22 14:46:32 -0800746 char* endptr;
747 size_t target_size = strtol(target_size_str, &endptr, 10);
748 if (target_size == 0 && endptr == target_size_str) {
749 ErrorAbort(state, "%s(): can't parse \"%s\" as byte count",
750 name, target_size_str);
751 free(source_filename);
752 free(target_filename);
753 free(target_sha1);
754 free(target_size_str);
755 return NULL;
756 }
757
758 int patchcount = (argc-4) / 2;
759 Value** patches = ReadValueVarArgs(state, argc-4, argv+4);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700760
761 int i;
Doug Zongkerc4351c72010-02-22 14:46:32 -0800762 for (i = 0; i < patchcount; ++i) {
763 if (patches[i*2]->type != VAL_STRING) {
764 ErrorAbort(state, "%s(): sha-1 #%d is not string", name, i);
765 break;
766 }
767 if (patches[i*2+1]->type != VAL_BLOB) {
768 ErrorAbort(state, "%s(): patch #%d is not blob", name, i);
769 break;
770 }
Doug Zongker8edb00c2009-06-11 17:21:44 -0700771 }
Doug Zongkerc4351c72010-02-22 14:46:32 -0800772 if (i != patchcount) {
773 for (i = 0; i < patchcount*2; ++i) {
774 FreeValue(patches[i]);
775 }
776 free(patches);
777 return NULL;
778 }
Doug Zongker8edb00c2009-06-11 17:21:44 -0700779
Doug Zongkerc4351c72010-02-22 14:46:32 -0800780 char** patch_sha_str = malloc(patchcount * sizeof(char*));
781 for (i = 0; i < patchcount; ++i) {
782 patch_sha_str[i] = patches[i*2]->data;
783 patches[i*2]->data = NULL;
784 FreeValue(patches[i*2]);
785 patches[i] = patches[i*2+1];
Doug Zongker8edb00c2009-06-11 17:21:44 -0700786 }
Doug Zongkerc4351c72010-02-22 14:46:32 -0800787
788 int result = applypatch(source_filename, target_filename,
789 target_sha1, target_size,
790 patchcount, patch_sha_str, patches);
791
792 for (i = 0; i < patchcount; ++i) {
793 FreeValue(patches[i]);
794 }
795 free(patch_sha_str);
796 free(patches);
797
798 return StringValue(strdup(result == 0 ? "t" : ""));
799}
800
801// apply_patch_check(file, [sha1_1, ...])
802Value* ApplyPatchCheckFn(const char* name, State* state,
803 int argc, Expr* argv[]) {
804 if (argc < 1) {
805 return ErrorAbort(state, "%s(): expected at least 1 arg, got %d",
806 name, argc);
807 }
808
809 char* filename;
810 if (ReadArgs(state, argv, 1, &filename) < 0) {
811 return NULL;
812 }
813
814 int patchcount = argc-1;
815 char** sha1s = ReadVarArgs(state, argc-1, argv+1);
816
817 int result = applypatch_check(filename, patchcount, sha1s);
818
819 int i;
820 for (i = 0; i < patchcount; ++i) {
821 free(sha1s[i]);
822 }
823 free(sha1s);
824
825 return StringValue(strdup(result == 0 ? "t" : ""));
Doug Zongker8edb00c2009-06-11 17:21:44 -0700826}
827
Doug Zongker512536a2010-02-17 16:11:44 -0800828Value* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700829 char** args = ReadVarArgs(state, argc, argv);
830 if (args == NULL) {
831 return NULL;
832 }
833
834 int size = 0;
835 int i;
836 for (i = 0; i < argc; ++i) {
837 size += strlen(args[i]);
838 }
839 char* buffer = malloc(size+1);
840 size = 0;
841 for (i = 0; i < argc; ++i) {
842 strcpy(buffer+size, args[i]);
843 size += strlen(args[i]);
844 free(args[i]);
845 }
846 free(args);
847 buffer[size] = '\0';
848
849 char* line = strtok(buffer, "\n");
850 while (line) {
851 fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe,
852 "ui_print %s\n", line);
853 line = strtok(NULL, "\n");
854 }
855 fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "ui_print\n");
856
Doug Zongker512536a2010-02-17 16:11:44 -0800857 return StringValue(buffer);
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700858}
859
Doug Zongker512536a2010-02-17 16:11:44 -0800860Value* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongkera3f89ea2009-09-10 14:10:48 -0700861 if (argc < 1) {
862 return ErrorAbort(state, "%s() expects at least 1 arg", name);
863 }
864 char** args = ReadVarArgs(state, argc, argv);
865 if (args == NULL) {
866 return NULL;
867 }
868
869 char** args2 = malloc(sizeof(char*) * (argc+1));
870 memcpy(args2, args, sizeof(char*) * argc);
871 args2[argc] = NULL;
872
873 fprintf(stderr, "about to run program [%s] with %d args\n", args2[0], argc);
874
875 pid_t child = fork();
876 if (child == 0) {
877 execv(args2[0], args2);
878 fprintf(stderr, "run_program: execv failed: %s\n", strerror(errno));
879 _exit(1);
880 }
881 int status;
882 waitpid(child, &status, 0);
883 if (WIFEXITED(status)) {
884 if (WEXITSTATUS(status) != 0) {
885 fprintf(stderr, "run_program: child exited with status %d\n",
886 WEXITSTATUS(status));
887 }
888 } else if (WIFSIGNALED(status)) {
889 fprintf(stderr, "run_program: child terminated by signal %d\n",
890 WTERMSIG(status));
891 }
892
893 int i;
894 for (i = 0; i < argc; ++i) {
895 free(args[i]);
896 }
897 free(args);
898 free(args2);
899
900 char buffer[20];
901 sprintf(buffer, "%d", status);
902
Doug Zongker512536a2010-02-17 16:11:44 -0800903 return StringValue(strdup(buffer));
Doug Zongkera3f89ea2009-09-10 14:10:48 -0700904}
905
Doug Zongker512536a2010-02-17 16:11:44 -0800906// Take a sha-1 digest and return it as a newly-allocated hex string.
907static char* PrintSha1(uint8_t* digest) {
908 char* buffer = malloc(SHA_DIGEST_SIZE*2 + 1);
909 int i;
910 const char* alphabet = "0123456789abcdef";
911 for (i = 0; i < SHA_DIGEST_SIZE; ++i) {
912 buffer[i*2] = alphabet[(digest[i] >> 4) & 0xf];
913 buffer[i*2+1] = alphabet[digest[i] & 0xf];
914 }
915 buffer[i*2] = '\0';
916 return buffer;
917}
918
919// sha1_check(data)
920// to return the sha1 of the data (given in the format returned by
921// read_file).
922//
923// sha1_check(data, sha1_hex, [sha1_hex, ...])
924// returns the sha1 of the file if it matches any of the hex
925// strings passed, or "" if it does not equal any of them.
926//
927Value* Sha1CheckFn(const char* name, State* state, int argc, Expr* argv[]) {
928 if (argc < 1) {
929 return ErrorAbort(state, "%s() expects at least 1 arg", name);
930 }
931
932 Value** args = ReadValueVarArgs(state, argc, argv);
933 if (args == NULL) {
934 return NULL;
935 }
936
937 if (args[0]->size < 0) {
938 fprintf(stderr, "%s(): no file contents received", name);
939 return StringValue(strdup(""));
940 }
941 uint8_t digest[SHA_DIGEST_SIZE];
942 SHA(args[0]->data, args[0]->size, digest);
943 FreeValue(args[0]);
944
945 if (argc == 1) {
946 return StringValue(PrintSha1(digest));
947 }
948
949 int i;
950 uint8_t* arg_digest = malloc(SHA_DIGEST_SIZE);
951 for (i = 1; i < argc; ++i) {
952 if (args[i]->type != VAL_STRING) {
953 fprintf(stderr, "%s(): arg %d is not a string; skipping",
954 name, i);
955 } else if (ParseSha1(args[i]->data, arg_digest) != 0) {
956 // Warn about bad args and skip them.
957 fprintf(stderr, "%s(): error parsing \"%s\" as sha-1; skipping",
958 name, args[i]->data);
959 } else if (memcmp(digest, arg_digest, SHA_DIGEST_SIZE) == 0) {
960 break;
961 }
962 FreeValue(args[i]);
963 }
964 if (i >= argc) {
965 // Didn't match any of the hex strings; return false.
966 return StringValue(strdup(""));
967 }
968 // Found a match; free all the remaining arguments and return the
969 // matched one.
970 int j;
971 for (j = i+1; j < argc; ++j) {
972 FreeValue(args[j]);
973 }
974 return args[i];
975}
976
977// Read a local file and return its contents (the char* returned
978// is actually a FileContents*).
979Value* ReadFileFn(const char* name, State* state, int argc, Expr* argv[]) {
980 if (argc != 1) {
981 return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
982 }
983 char* filename;
984 if (ReadArgs(state, argv, 1, &filename) < 0) return NULL;
985
986 Value* v = malloc(sizeof(Value));
987 v->type = VAL_BLOB;
988
989 FileContents fc;
990 if (LoadFileContents(filename, &fc) != 0) {
991 ErrorAbort(state, "%s() loading \"%s\" failed: %s",
992 name, filename, strerror(errno));
993 free(filename);
994 free(v);
995 free(fc.data);
996 return NULL;
997 }
998
999 v->size = fc.size;
1000 v->data = (char*)fc.data;
1001
1002 free(filename);
1003 return v;
1004}
Doug Zongker8edb00c2009-06-11 17:21:44 -07001005
Doug Zongker9931f7f2009-06-10 14:11:53 -07001006void RegisterInstallFunctions() {
1007 RegisterFunction("mount", MountFn);
Doug Zongker8edb00c2009-06-11 17:21:44 -07001008 RegisterFunction("is_mounted", IsMountedFn);
Doug Zongker9931f7f2009-06-10 14:11:53 -07001009 RegisterFunction("unmount", UnmountFn);
1010 RegisterFunction("format", FormatFn);
1011 RegisterFunction("show_progress", ShowProgressFn);
Doug Zongkerfbf3c102009-06-24 09:36:20 -07001012 RegisterFunction("set_progress", SetProgressFn);
Doug Zongker9931f7f2009-06-10 14:11:53 -07001013 RegisterFunction("delete", DeleteFn);
1014 RegisterFunction("delete_recursive", DeleteFn);
Doug Zongker8edb00c2009-06-11 17:21:44 -07001015 RegisterFunction("package_extract_dir", PackageExtractDirFn);
1016 RegisterFunction("package_extract_file", PackageExtractFileFn);
Doug Zongker9931f7f2009-06-10 14:11:53 -07001017 RegisterFunction("symlink", SymlinkFn);
1018 RegisterFunction("set_perm", SetPermFn);
1019 RegisterFunction("set_perm_recursive", SetPermFn);
Doug Zongker8edb00c2009-06-11 17:21:44 -07001020
1021 RegisterFunction("getprop", GetPropFn);
Doug Zongker47cace92009-06-18 10:11:50 -07001022 RegisterFunction("file_getprop", FileGetPropFn);
Doug Zongker8edb00c2009-06-11 17:21:44 -07001023 RegisterFunction("write_raw_image", WriteRawImageFn);
Doug Zongker8edb00c2009-06-11 17:21:44 -07001024
1025 RegisterFunction("apply_patch", ApplyPatchFn);
Doug Zongkerc4351c72010-02-22 14:46:32 -08001026 RegisterFunction("apply_patch_check", ApplyPatchCheckFn);
1027 RegisterFunction("apply_patch_space", ApplyPatchSpaceFn);
Doug Zongkerd9c9d102009-06-12 12:24:39 -07001028
Doug Zongker512536a2010-02-17 16:11:44 -08001029 RegisterFunction("read_file", ReadFileFn);
1030 RegisterFunction("sha1_check", Sha1CheckFn);
1031
Doug Zongkerd9c9d102009-06-12 12:24:39 -07001032 RegisterFunction("ui_print", UIPrintFn);
Doug Zongkera3f89ea2009-09-10 14:10:48 -07001033
1034 RegisterFunction("run_program", RunProgramFn);
Doug Zongker9931f7f2009-06-10 14:11:53 -07001035}