blob: ad895569e655daf0267356ff3c01c3dd8b0035bf [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"
32#include "minzip/DirUtil.h"
33#include "mtdutils/mounts.h"
34#include "mtdutils/mtdutils.h"
35#include "updater.h"
36
Doug Zongker8edb00c2009-06-11 17:21:44 -070037
Doug Zongker9931f7f2009-06-10 14:11:53 -070038// mount(type, location, mount_point)
39//
40// what: type="MTD" location="<partition>" to mount a yaffs2 filesystem
41// type="vfat" location="/dev/block/<whatever>" to mount a device
Doug Zongkerd9c9d102009-06-12 12:24:39 -070042char* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -070043 char* result = NULL;
44 if (argc != 3) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -070045 return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc);
Doug Zongker9931f7f2009-06-10 14:11:53 -070046 }
47 char* type;
48 char* location;
49 char* mount_point;
Doug Zongkerd9c9d102009-06-12 12:24:39 -070050 if (ReadArgs(state, argv, 3, &type, &location, &mount_point) < 0) {
Doug Zongker9931f7f2009-06-10 14:11:53 -070051 return NULL;
52 }
53
54 if (strlen(type) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -070055 ErrorAbort(state, "type argument to %s() can't be empty", name);
Doug Zongker9931f7f2009-06-10 14:11:53 -070056 goto done;
57 }
58 if (strlen(location) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -070059 ErrorAbort(state, "location argument to %s() can't be empty", name);
Doug Zongker9931f7f2009-06-10 14:11:53 -070060 goto done;
61 }
62 if (strlen(mount_point) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -070063 ErrorAbort(state, "mount_point argument to %s() can't be empty", name);
Doug Zongker9931f7f2009-06-10 14:11:53 -070064 goto done;
65 }
66
67 mkdir(mount_point, 0755);
68
69 if (strcmp(type, "MTD") == 0) {
70 mtd_scan_partitions();
71 const MtdPartition* mtd;
72 mtd = mtd_find_partition_by_name(location);
73 if (mtd == NULL) {
74 fprintf(stderr, "%s: no mtd partition named \"%s\"",
75 name, location);
76 result = strdup("");
77 goto done;
78 }
79 if (mtd_mount_partition(mtd, mount_point, "yaffs2", 0 /* rw */) != 0) {
80 fprintf(stderr, "mtd mount of %s failed: %s\n",
81 location, strerror(errno));
82 result = strdup("");
83 goto done;
84 }
85 result = mount_point;
86 } else {
87 if (mount(location, mount_point, type,
88 MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) {
89 result = strdup("");
90 } else {
91 result = mount_point;
92 }
93 }
94
95done:
96 free(type);
97 free(location);
98 if (result != mount_point) free(mount_point);
99 return result;
100}
101
Doug Zongker8edb00c2009-06-11 17:21:44 -0700102
103// is_mounted(mount_point)
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700104char* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker8edb00c2009-06-11 17:21:44 -0700105 char* result = NULL;
106 if (argc != 1) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700107 return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700108 }
109 char* mount_point;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700110 if (ReadArgs(state, argv, 1, &mount_point) < 0) {
Doug Zongker8edb00c2009-06-11 17:21:44 -0700111 return NULL;
112 }
113 if (strlen(mount_point) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700114 ErrorAbort(state, "mount_point argument to unmount() can't be empty");
Doug Zongker8edb00c2009-06-11 17:21:44 -0700115 goto done;
116 }
117
118 scan_mounted_volumes();
119 const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
120 if (vol == NULL) {
121 result = strdup("");
122 } else {
123 result = mount_point;
124 }
125
126done:
127 if (result != mount_point) free(mount_point);
128 return result;
129}
130
131
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700132char* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700133 char* result = NULL;
134 if (argc != 1) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700135 return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700136 }
137 char* mount_point;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700138 if (ReadArgs(state, argv, 1, &mount_point) < 0) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700139 return NULL;
140 }
141 if (strlen(mount_point) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700142 ErrorAbort(state, "mount_point argument to unmount() can't be empty");
Doug Zongker9931f7f2009-06-10 14:11:53 -0700143 goto done;
144 }
145
146 scan_mounted_volumes();
147 const MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point);
148 if (vol == NULL) {
149 fprintf(stderr, "unmount of %s failed; no such volume\n", mount_point);
150 result = strdup("");
151 } else {
152 unmount_mounted_volume(vol);
153 result = mount_point;
154 }
155
156done:
157 if (result != mount_point) free(mount_point);
158 return result;
159}
Doug Zongker8edb00c2009-06-11 17:21:44 -0700160
161
Doug Zongker9931f7f2009-06-10 14:11:53 -0700162// format(type, location)
163//
164// type="MTD" location=partition
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700165char* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700166 char* result = NULL;
167 if (argc != 2) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700168 return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700169 }
170 char* type;
171 char* location;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700172 if (ReadArgs(state, argv, 2, &type, &location) < 0) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700173 return NULL;
174 }
175
176 if (strlen(type) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700177 ErrorAbort(state, "type argument to %s() can't be empty", name);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700178 goto done;
179 }
180 if (strlen(location) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700181 ErrorAbort(state, "location argument to %s() can't be empty", name);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700182 goto done;
183 }
184
185 if (strcmp(type, "MTD") == 0) {
186 mtd_scan_partitions();
187 const MtdPartition* mtd = mtd_find_partition_by_name(location);
188 if (mtd == NULL) {
189 fprintf(stderr, "%s: no mtd partition named \"%s\"",
190 name, location);
191 result = strdup("");
192 goto done;
193 }
194 MtdWriteContext* ctx = mtd_write_partition(mtd);
195 if (ctx == NULL) {
196 fprintf(stderr, "%s: can't write \"%s\"", name, location);
197 result = strdup("");
198 goto done;
199 }
200 if (mtd_erase_blocks(ctx, -1) == -1) {
201 mtd_write_close(ctx);
202 fprintf(stderr, "%s: failed to erase \"%s\"", name, location);
203 result = strdup("");
204 goto done;
205 }
206 if (mtd_write_close(ctx) != 0) {
207 fprintf(stderr, "%s: failed to close \"%s\"", name, location);
208 result = strdup("");
209 goto done;
210 }
211 result = location;
212 } else {
213 fprintf(stderr, "%s: unsupported type \"%s\"", name, type);
214 }
215
216done:
217 free(type);
218 if (result != location) free(location);
219 return result;
220}
221
Doug Zongker8edb00c2009-06-11 17:21:44 -0700222
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700223char* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700224 char** paths = malloc(argc * sizeof(char*));
225 int i;
226 for (i = 0; i < argc; ++i) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700227 paths[i] = Evaluate(state, argv[i]);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700228 if (paths[i] == NULL) {
229 int j;
230 for (j = 0; j < i; ++i) {
231 free(paths[j]);
232 }
233 free(paths);
234 return NULL;
235 }
236 }
237
238 bool recursive = (strcmp(name, "delete_recursive") == 0);
239
240 int success = 0;
241 for (i = 0; i < argc; ++i) {
242 if ((recursive ? dirUnlinkHierarchy(paths[i]) : unlink(paths[i])) == 0)
243 ++success;
244 free(paths[i]);
245 }
246 free(paths);
247
248 char buffer[10];
249 sprintf(buffer, "%d", success);
250 return strdup(buffer);
251}
252
Doug Zongker8edb00c2009-06-11 17:21:44 -0700253
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700254char* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700255 if (argc != 2) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700256 return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700257 }
258 char* frac_str;
259 char* sec_str;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700260 if (ReadArgs(state, argv, 2, &frac_str, &sec_str) < 0) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700261 return NULL;
262 }
263
264 double frac = strtod(frac_str, NULL);
265 int sec = strtol(sec_str, NULL, 10);
266
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700267 UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700268 fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec);
269
Doug Zongker9931f7f2009-06-10 14:11:53 -0700270 free(sec_str);
Doug Zongkerfbf3c102009-06-24 09:36:20 -0700271 return frac_str;
272}
273
274char* SetProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
275 if (argc != 1) {
276 return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
277 }
278 char* frac_str;
279 if (ReadArgs(state, argv, 1, &frac_str) < 0) {
280 return NULL;
281 }
282
283 double frac = strtod(frac_str, NULL);
284
285 UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
286 fprintf(ui->cmd_pipe, "set_progress %f\n", frac);
287
288 return frac_str;
Doug Zongker9931f7f2009-06-10 14:11:53 -0700289}
290
Doug Zongker8edb00c2009-06-11 17:21:44 -0700291// package_extract_dir(package_path, destination_path)
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700292char* PackageExtractDirFn(const char* name, State* state,
Doug Zongker8edb00c2009-06-11 17:21:44 -0700293 int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700294 if (argc != 2) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700295 return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700296 }
297 char* zip_path;
298 char* dest_path;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700299 if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
Doug Zongker9931f7f2009-06-10 14:11:53 -0700300
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700301 ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
Doug Zongker9931f7f2009-06-10 14:11:53 -0700302
303 // To create a consistent system image, never use the clock for timestamps.
304 struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default
305
306 bool success = mzExtractRecursive(za, zip_path, dest_path,
307 MZ_EXTRACT_FILES_ONLY, &timestamp,
308 NULL, NULL);
309 free(zip_path);
310 free(dest_path);
311 return strdup(success ? "t" : "");
312}
313
Doug Zongker8edb00c2009-06-11 17:21:44 -0700314
315// package_extract_file(package_path, destination_path)
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700316char* PackageExtractFileFn(const char* name, State* state,
Doug Zongker8edb00c2009-06-11 17:21:44 -0700317 int argc, Expr* argv[]) {
318 if (argc != 2) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700319 return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700320 }
321 char* zip_path;
322 char* dest_path;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700323 if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
Doug Zongker8edb00c2009-06-11 17:21:44 -0700324
325 bool success = false;
326
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700327 ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
Doug Zongker8edb00c2009-06-11 17:21:44 -0700328 const ZipEntry* entry = mzFindZipEntry(za, zip_path);
329 if (entry == NULL) {
330 fprintf(stderr, "%s: no %s in package\n", name, zip_path);
331 goto done;
332 }
333
334 FILE* f = fopen(dest_path, "wb");
335 if (f == NULL) {
336 fprintf(stderr, "%s: can't open %s for write: %s\n",
337 name, dest_path, strerror(errno));
338 goto done;
339 }
340 success = mzExtractZipEntryToFile(za, entry, fileno(f));
341 fclose(f);
342
343 done:
344 free(zip_path);
345 free(dest_path);
346 return strdup(success ? "t" : "");
347}
348
349
Doug Zongker9931f7f2009-06-10 14:11:53 -0700350// symlink target src1 src2 ...
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700351char* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700352 if (argc == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700353 return ErrorAbort(state, "%s() expects 1+ args, got %d", name, argc);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700354 }
355 char* target;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700356 target = Evaluate(state, argv[0]);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700357 if (target == NULL) return NULL;
358
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700359 char** srcs = ReadVarArgs(state, argc-1, argv+1);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700360 if (srcs == NULL) {
361 free(target);
362 return NULL;
363 }
364
365 int i;
366 for (i = 0; i < argc-1; ++i) {
367 symlink(target, srcs[i]);
368 free(srcs[i]);
369 }
370 free(srcs);
371 return strdup("");
372}
373
Doug Zongker8edb00c2009-06-11 17:21:44 -0700374
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700375char* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700376 char* result = NULL;
377 bool recursive = (strcmp(name, "set_perm_recursive") == 0);
378
379 int min_args = 4 + (recursive ? 1 : 0);
380 if (argc < min_args) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700381 return ErrorAbort(state, "%s() expects %d+ args, got %d", name, argc);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700382 }
383
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700384 char** args = ReadVarArgs(state, argc, argv);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700385 if (args == NULL) return NULL;
386
387 char* end;
388 int i;
389
390 int uid = strtoul(args[0], &end, 0);
391 if (*end != '\0' || args[0][0] == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700392 ErrorAbort(state, "%s: \"%s\" not a valid uid", name, args[0]);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700393 goto done;
394 }
395
396 int gid = strtoul(args[1], &end, 0);
397 if (*end != '\0' || args[1][0] == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700398 ErrorAbort(state, "%s: \"%s\" not a valid gid", name, args[1]);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700399 goto done;
400 }
401
402 if (recursive) {
403 int dir_mode = strtoul(args[2], &end, 0);
404 if (*end != '\0' || args[2][0] == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700405 ErrorAbort(state, "%s: \"%s\" not a valid dirmode", name, args[2]);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700406 goto done;
407 }
408
409 int file_mode = strtoul(args[3], &end, 0);
410 if (*end != '\0' || args[3][0] == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700411 ErrorAbort(state, "%s: \"%s\" not a valid filemode",
Doug Zongker9931f7f2009-06-10 14:11:53 -0700412 name, args[3]);
413 goto done;
414 }
415
416 for (i = 4; i < argc; ++i) {
417 dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode);
418 }
419 } else {
420 int mode = strtoul(args[2], &end, 0);
421 if (*end != '\0' || args[2][0] == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700422 ErrorAbort(state, "%s: \"%s\" not a valid mode", name, args[2]);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700423 goto done;
424 }
425
Doug Zongker0bbfe3d2009-06-25 13:37:31 -0700426 for (i = 3; i < argc; ++i) {
Doug Zongker9931f7f2009-06-10 14:11:53 -0700427 chown(args[i], uid, gid);
428 chmod(args[i], mode);
429 }
430 }
431 result = strdup("");
432
433done:
434 for (i = 0; i < argc; ++i) {
435 free(args[i]);
436 }
437 free(args);
438
439 return result;
440}
441
Doug Zongker8edb00c2009-06-11 17:21:44 -0700442
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700443char* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker8edb00c2009-06-11 17:21:44 -0700444 if (argc != 1) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700445 return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700446 }
447 char* key;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700448 key = Evaluate(state, argv[0]);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700449 if (key == NULL) return NULL;
450
451 char value[PROPERTY_VALUE_MAX];
452 property_get(key, value, "");
453 free(key);
454
455 return strdup(value);
456}
457
458
Doug Zongker47cace92009-06-18 10:11:50 -0700459// file_getprop(file, key)
460//
461// interprets 'file' as a getprop-style file (key=value pairs, one
462// per line, # comment lines and blank lines okay), and returns the value
463// for 'key' (or "" if it isn't defined).
464char* FileGetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
465 char* result = NULL;
466 char* buffer = NULL;
467 char* filename;
468 char* key;
469 if (ReadArgs(state, argv, 2, &filename, &key) < 0) {
470 return NULL;
471 }
472
473 struct stat st;
474 if (stat(filename, &st) < 0) {
475 ErrorAbort(state, "%s: failed to stat \"%s\": %s",
476 name, filename, strerror(errno));
477 goto done;
478 }
479
480#define MAX_FILE_GETPROP_SIZE 65536
481
482 if (st.st_size > MAX_FILE_GETPROP_SIZE) {
483 ErrorAbort(state, "%s too large for %s (max %d)",
484 filename, name, MAX_FILE_GETPROP_SIZE);
485 goto done;
486 }
487
488 buffer = malloc(st.st_size+1);
489 if (buffer == NULL) {
490 ErrorAbort(state, "%s: failed to alloc %d bytes", name, st.st_size+1);
491 goto done;
492 }
493
494 FILE* f = fopen(filename, "rb");
495 if (f == NULL) {
496 ErrorAbort(state, "%s: failed to open %s: %s",
497 name, filename, strerror(errno));
498 goto done;
499 }
500
501 if (fread(buffer, 1, st.st_size, f) != st.st_size) {
502 ErrorAbort(state, "%s: failed to read %d bytes from %s",
503 name, st.st_size+1, filename);
504 fclose(f);
505 goto done;
506 }
507 buffer[st.st_size] = '\0';
508
509 fclose(f);
510
511 char* line = strtok(buffer, "\n");
512 do {
513 // skip whitespace at start of line
514 while (*line && isspace(*line)) ++line;
515
516 // comment or blank line: skip to next line
517 if (*line == '\0' || *line == '#') continue;
518
519 char* equal = strchr(line, '=');
520 if (equal == NULL) {
521 ErrorAbort(state, "%s: malformed line \"%s\": %s not a prop file?",
522 name, line, filename);
523 goto done;
524 }
525
526 // trim whitespace between key and '='
527 char* key_end = equal-1;
528 while (key_end > line && isspace(*key_end)) --key_end;
529 key_end[1] = '\0';
530
531 // not the key we're looking for
532 if (strcmp(key, line) != 0) continue;
533
534 // skip whitespace after the '=' to the start of the value
535 char* val_start = equal+1;
536 while(*val_start && isspace(*val_start)) ++val_start;
537
538 // trim trailing whitespace
539 char* val_end = val_start + strlen(val_start)-1;
540 while (val_end > val_start && isspace(*val_end)) --val_end;
541 val_end[1] = '\0';
542
543 result = strdup(val_start);
544 break;
545
546 } while ((line = strtok(NULL, "\n")));
547
548 if (result == NULL) result = strdup("");
549
550 done:
551 free(filename);
552 free(key);
553 free(buffer);
554 return result;
555}
556
557
Doug Zongker8edb00c2009-06-11 17:21:44 -0700558static bool write_raw_image_cb(const unsigned char* data,
559 int data_len, void* ctx) {
560 int r = mtd_write_data((MtdWriteContext*)ctx, (const char *)data, data_len);
561 if (r == data_len) return true;
562 fprintf(stderr, "%s\n", strerror(errno));
563 return false;
564}
565
566// write_raw_image(file, partition)
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700567char* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker8edb00c2009-06-11 17:21:44 -0700568 char* result = NULL;
569
570 char* partition;
571 char* filename;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700572 if (ReadArgs(state, argv, 2, &filename, &partition) < 0) {
Doug Zongker8edb00c2009-06-11 17:21:44 -0700573 return NULL;
574 }
575
576 if (strlen(partition) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700577 ErrorAbort(state, "partition argument to %s can't be empty", name);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700578 goto done;
579 }
580 if (strlen(filename) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700581 ErrorAbort(state, "file argument to %s can't be empty", name);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700582 goto done;
583 }
584
585 mtd_scan_partitions();
586 const MtdPartition* mtd = mtd_find_partition_by_name(partition);
587 if (mtd == NULL) {
588 fprintf(stderr, "%s: no mtd partition named \"%s\"\n", name, partition);
589 result = strdup("");
590 goto done;
591 }
592
593 MtdWriteContext* ctx = mtd_write_partition(mtd);
594 if (ctx == NULL) {
595 fprintf(stderr, "%s: can't write mtd partition \"%s\"\n",
596 name, partition);
597 result = strdup("");
598 goto done;
599 }
600
601 bool success;
602
603 FILE* f = fopen(filename, "rb");
604 if (f == NULL) {
605 fprintf(stderr, "%s: can't open %s: %s\n",
606 name, filename, strerror(errno));
607 result = strdup("");
608 goto done;
609 }
610
611 success = true;
612 char* buffer = malloc(BUFSIZ);
613 int read;
614 while (success && (read = fread(buffer, 1, BUFSIZ, f)) > 0) {
615 int wrote = mtd_write_data(ctx, buffer, read);
616 success = success && (wrote == read);
617 if (!success) {
618 fprintf(stderr, "mtd_write_data to %s failed: %s\n",
619 partition, strerror(errno));
620 }
621 }
622 free(buffer);
623 fclose(f);
624
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700625 if (mtd_erase_blocks(ctx, -1) == -1) {
626 fprintf(stderr, "%s: error erasing blocks of %s\n", name, partition);
627 }
628 if (mtd_write_close(ctx) != 0) {
629 fprintf(stderr, "%s: error closing write of %s\n", name, partition);
630 }
631
Doug Zongker8edb00c2009-06-11 17:21:44 -0700632 printf("%s %s partition from %s\n",
633 success ? "wrote" : "failed to write", partition, filename);
634
635 result = success ? partition : strdup("");
636
637done:
638 if (result != partition) free(partition);
639 free(filename);
640 return result;
641}
642
643// write_firmware_image(file, partition)
644//
645// partition is "radio" or "hboot"
646// file is not used until after updater exits
647//
648// TODO: this should live in some HTC-specific library
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700649char* WriteFirmwareImageFn(const char* name, State* state,
Doug Zongker8edb00c2009-06-11 17:21:44 -0700650 int argc, Expr* argv[]) {
651 char* result = NULL;
652
653 char* partition;
654 char* filename;
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700655 if (ReadArgs(state, argv, 2, &filename, &partition) < 0) {
Doug Zongker8edb00c2009-06-11 17:21:44 -0700656 return NULL;
657 }
658
659 if (strlen(partition) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700660 ErrorAbort(state, "partition argument to %s can't be empty", name);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700661 goto done;
662 }
663 if (strlen(filename) == 0) {
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700664 ErrorAbort(state, "file argument to %s can't be empty", name);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700665 goto done;
666 }
667
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700668 FILE* cmd = ((UpdaterInfo*)(state->cookie))->cmd_pipe;
Doug Zongker8edb00c2009-06-11 17:21:44 -0700669 fprintf(cmd, "firmware %s %s\n", partition, filename);
670
671 printf("will write %s firmware from %s\n", partition, filename);
672 result = partition;
673
674done:
675 if (result != partition) free(partition);
676 free(filename);
677 return result;
678}
679
680
681extern int applypatch(int argc, char** argv);
682
683// apply_patch(srcfile, tgtfile, tgtsha1, tgtsize, sha1:patch, ...)
684// apply_patch_check(file, sha1, ...)
685// apply_patch_space(bytes)
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700686char* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
Doug Zongker8edb00c2009-06-11 17:21:44 -0700687 printf("in applypatchfn (%s)\n", name);
688
689 char* prepend = NULL;
690 if (strstr(name, "check") != NULL) {
691 prepend = "-c";
692 } else if (strstr(name, "space") != NULL) {
693 prepend = "-s";
694 }
695
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700696 char** args = ReadVarArgs(state, argc, argv);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700697 if (args == NULL) return NULL;
698
699 // insert the "program name" argv[0] and a copy of the "prepend"
700 // string (if any) at the start of the args.
701
702 int extra = 1 + (prepend != NULL ? 1 : 0);
703 char** temp = malloc((argc+extra) * sizeof(char*));
704 memcpy(temp+extra, args, argc * sizeof(char*));
705 temp[0] = strdup("updater");
706 if (prepend) {
707 temp[1] = strdup(prepend);
708 }
709 free(args);
710 args = temp;
711 argc += extra;
712
713 printf("calling applypatch\n");
714 fflush(stdout);
715 int result = applypatch(argc, args);
716 printf("applypatch returned %d\n", result);
717
718 int i;
719 for (i = 0; i < argc; ++i) {
720 free(args[i]);
721 }
722 free(args);
723
724 switch (result) {
725 case 0: return strdup("t");
726 case 1: return strdup("");
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700727 default: return ErrorAbort(state, "applypatch couldn't parse args");
Doug Zongker8edb00c2009-06-11 17:21:44 -0700728 }
729}
730
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700731char* UIPrintFn(const char* name, State* state, int argc, Expr* argv[]) {
732 char** args = ReadVarArgs(state, argc, argv);
733 if (args == NULL) {
734 return NULL;
735 }
736
737 int size = 0;
738 int i;
739 for (i = 0; i < argc; ++i) {
740 size += strlen(args[i]);
741 }
742 char* buffer = malloc(size+1);
743 size = 0;
744 for (i = 0; i < argc; ++i) {
745 strcpy(buffer+size, args[i]);
746 size += strlen(args[i]);
747 free(args[i]);
748 }
749 free(args);
750 buffer[size] = '\0';
751
752 char* line = strtok(buffer, "\n");
753 while (line) {
754 fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe,
755 "ui_print %s\n", line);
756 line = strtok(NULL, "\n");
757 }
758 fprintf(((UpdaterInfo*)(state->cookie))->cmd_pipe, "ui_print\n");
759
760 return buffer;
761}
762
Doug Zongkera3f89ea2009-09-10 14:10:48 -0700763char* RunProgramFn(const char* name, State* state, int argc, Expr* argv[]) {
764 if (argc < 1) {
765 return ErrorAbort(state, "%s() expects at least 1 arg", name);
766 }
767 char** args = ReadVarArgs(state, argc, argv);
768 if (args == NULL) {
769 return NULL;
770 }
771
772 char** args2 = malloc(sizeof(char*) * (argc+1));
773 memcpy(args2, args, sizeof(char*) * argc);
774 args2[argc] = NULL;
775
776 fprintf(stderr, "about to run program [%s] with %d args\n", args2[0], argc);
777
778 pid_t child = fork();
779 if (child == 0) {
780 execv(args2[0], args2);
781 fprintf(stderr, "run_program: execv failed: %s\n", strerror(errno));
782 _exit(1);
783 }
784 int status;
785 waitpid(child, &status, 0);
786 if (WIFEXITED(status)) {
787 if (WEXITSTATUS(status) != 0) {
788 fprintf(stderr, "run_program: child exited with status %d\n",
789 WEXITSTATUS(status));
790 }
791 } else if (WIFSIGNALED(status)) {
792 fprintf(stderr, "run_program: child terminated by signal %d\n",
793 WTERMSIG(status));
794 }
795
796 int i;
797 for (i = 0; i < argc; ++i) {
798 free(args[i]);
799 }
800 free(args);
801 free(args2);
802
803 char buffer[20];
804 sprintf(buffer, "%d", status);
805
806 return strdup(buffer);
807}
808
Doug Zongker8edb00c2009-06-11 17:21:44 -0700809
Doug Zongker9931f7f2009-06-10 14:11:53 -0700810void RegisterInstallFunctions() {
811 RegisterFunction("mount", MountFn);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700812 RegisterFunction("is_mounted", IsMountedFn);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700813 RegisterFunction("unmount", UnmountFn);
814 RegisterFunction("format", FormatFn);
815 RegisterFunction("show_progress", ShowProgressFn);
Doug Zongkerfbf3c102009-06-24 09:36:20 -0700816 RegisterFunction("set_progress", SetProgressFn);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700817 RegisterFunction("delete", DeleteFn);
818 RegisterFunction("delete_recursive", DeleteFn);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700819 RegisterFunction("package_extract_dir", PackageExtractDirFn);
820 RegisterFunction("package_extract_file", PackageExtractFileFn);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700821 RegisterFunction("symlink", SymlinkFn);
822 RegisterFunction("set_perm", SetPermFn);
823 RegisterFunction("set_perm_recursive", SetPermFn);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700824
825 RegisterFunction("getprop", GetPropFn);
Doug Zongker47cace92009-06-18 10:11:50 -0700826 RegisterFunction("file_getprop", FileGetPropFn);
Doug Zongker8edb00c2009-06-11 17:21:44 -0700827 RegisterFunction("write_raw_image", WriteRawImageFn);
828 RegisterFunction("write_firmware_image", WriteFirmwareImageFn);
829
830 RegisterFunction("apply_patch", ApplyPatchFn);
831 RegisterFunction("apply_patch_check", ApplyPatchFn);
832 RegisterFunction("apply_patch_space", ApplyPatchFn);
Doug Zongkerd9c9d102009-06-12 12:24:39 -0700833
834 RegisterFunction("ui_print", UIPrintFn);
Doug Zongkera3f89ea2009-09-10 14:10:48 -0700835
836 RegisterFunction("run_program", RunProgramFn);
Doug Zongker9931f7f2009-06-10 14:11:53 -0700837}