install.c revision a3f89eabb7ddcf44add8ce3b321ceab6d35289cb
1/*
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
17#include <ctype.h>
18#include <errno.h>
19#include <stdarg.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <sys/mount.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26#include <sys/wait.h>
27#include <unistd.h>
28
29#include "cutils/misc.h"
30#include "cutils/properties.h"
31#include "edify/expr.h"
32#include "minzip/DirUtil.h"
33#include "mtdutils/mounts.h"
34#include "mtdutils/mtdutils.h"
35#include "updater.h"
36
37
38// 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
42char* MountFn(const char* name, State* state, int argc, Expr* argv[]) {
43    char* result = NULL;
44    if (argc != 3) {
45        return ErrorAbort(state, "%s() expects 3 args, got %d", name, argc);
46    }
47    char* type;
48    char* location;
49    char* mount_point;
50    if (ReadArgs(state, argv, 3, &type, &location, &mount_point) < 0) {
51        return NULL;
52    }
53
54    if (strlen(type) == 0) {
55        ErrorAbort(state, "type argument to %s() can't be empty", name);
56        goto done;
57    }
58    if (strlen(location) == 0) {
59        ErrorAbort(state, "location argument to %s() can't be empty", name);
60        goto done;
61    }
62    if (strlen(mount_point) == 0) {
63        ErrorAbort(state, "mount_point argument to %s() can't be empty", name);
64        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
102
103// is_mounted(mount_point)
104char* IsMountedFn(const char* name, State* state, int argc, Expr* argv[]) {
105    char* result = NULL;
106    if (argc != 1) {
107        return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
108    }
109    char* mount_point;
110    if (ReadArgs(state, argv, 1, &mount_point) < 0) {
111        return NULL;
112    }
113    if (strlen(mount_point) == 0) {
114        ErrorAbort(state, "mount_point argument to unmount() can't be empty");
115        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
132char* UnmountFn(const char* name, State* state, int argc, Expr* argv[]) {
133    char* result = NULL;
134    if (argc != 1) {
135        return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
136    }
137    char* mount_point;
138    if (ReadArgs(state, argv, 1, &mount_point) < 0) {
139        return NULL;
140    }
141    if (strlen(mount_point) == 0) {
142        ErrorAbort(state, "mount_point argument to unmount() can't be empty");
143        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}
160
161
162// format(type, location)
163//
164//    type="MTD"  location=partition
165char* FormatFn(const char* name, State* state, int argc, Expr* argv[]) {
166    char* result = NULL;
167    if (argc != 2) {
168        return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
169    }
170    char* type;
171    char* location;
172    if (ReadArgs(state, argv, 2, &type, &location) < 0) {
173        return NULL;
174    }
175
176    if (strlen(type) == 0) {
177        ErrorAbort(state, "type argument to %s() can't be empty", name);
178        goto done;
179    }
180    if (strlen(location) == 0) {
181        ErrorAbort(state, "location argument to %s() can't be empty", name);
182        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
222
223char* DeleteFn(const char* name, State* state, int argc, Expr* argv[]) {
224    char** paths = malloc(argc * sizeof(char*));
225    int i;
226    for (i = 0; i < argc; ++i) {
227        paths[i] = Evaluate(state, argv[i]);
228        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
253
254char* ShowProgressFn(const char* name, State* state, int argc, Expr* argv[]) {
255    if (argc != 2) {
256        return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
257    }
258    char* frac_str;
259    char* sec_str;
260    if (ReadArgs(state, argv, 2, &frac_str, &sec_str) < 0) {
261        return NULL;
262    }
263
264    double frac = strtod(frac_str, NULL);
265    int sec = strtol(sec_str, NULL, 10);
266
267    UpdaterInfo* ui = (UpdaterInfo*)(state->cookie);
268    fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec);
269
270    free(sec_str);
271    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;
289}
290
291// package_extract_dir(package_path, destination_path)
292char* PackageExtractDirFn(const char* name, State* state,
293                          int argc, Expr* argv[]) {
294    if (argc != 2) {
295        return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
296    }
297    char* zip_path;
298    char* dest_path;
299    if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
300
301    ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
302
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
314
315// package_extract_file(package_path, destination_path)
316char* PackageExtractFileFn(const char* name, State* state,
317                           int argc, Expr* argv[]) {
318    if (argc != 2) {
319        return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc);
320    }
321    char* zip_path;
322    char* dest_path;
323    if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL;
324
325    bool success = false;
326
327    ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip;
328    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
350// symlink target src1 src2 ...
351char* SymlinkFn(const char* name, State* state, int argc, Expr* argv[]) {
352    if (argc == 0) {
353        return ErrorAbort(state, "%s() expects 1+ args, got %d", name, argc);
354    }
355    char* target;
356    target = Evaluate(state, argv[0]);
357    if (target == NULL) return NULL;
358
359    char** srcs = ReadVarArgs(state, argc-1, argv+1);
360    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
374
375char* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) {
376    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) {
381        return ErrorAbort(state, "%s() expects %d+ args, got %d", name, argc);
382    }
383
384    char** args = ReadVarArgs(state, argc, argv);
385    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) {
392        ErrorAbort(state, "%s: \"%s\" not a valid uid", name, args[0]);
393        goto done;
394    }
395
396    int gid = strtoul(args[1], &end, 0);
397    if (*end != '\0' || args[1][0] == 0) {
398        ErrorAbort(state, "%s: \"%s\" not a valid gid", name, args[1]);
399        goto done;
400    }
401
402    if (recursive) {
403        int dir_mode = strtoul(args[2], &end, 0);
404        if (*end != '\0' || args[2][0] == 0) {
405            ErrorAbort(state, "%s: \"%s\" not a valid dirmode", name, args[2]);
406            goto done;
407        }
408
409        int file_mode = strtoul(args[3], &end, 0);
410        if (*end != '\0' || args[3][0] == 0) {
411            ErrorAbort(state, "%s: \"%s\" not a valid filemode",
412                       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) {
422            ErrorAbort(state, "%s: \"%s\" not a valid mode", name, args[2]);
423            goto done;
424        }
425
426        for (i = 3; i < argc; ++i) {
427            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
442
443char* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
444    if (argc != 1) {
445        return ErrorAbort(state, "%s() expects 1 arg, got %d", name, argc);
446    }
447    char* key;
448    key = Evaluate(state, argv[0]);
449    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
459// 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
558static 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)
567char* WriteRawImageFn(const char* name, State* state, int argc, Expr* argv[]) {
568    char* result = NULL;
569
570    char* partition;
571    char* filename;
572    if (ReadArgs(state, argv, 2, &filename, &partition) < 0) {
573        return NULL;
574    }
575
576    if (strlen(partition) == 0) {
577        ErrorAbort(state, "partition argument to %s can't be empty", name);
578        goto done;
579    }
580    if (strlen(filename) == 0) {
581        ErrorAbort(state, "file argument to %s can't be empty", name);
582        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
625    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
632    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
649char* WriteFirmwareImageFn(const char* name, State* state,
650                           int argc, Expr* argv[]) {
651    char* result = NULL;
652
653    char* partition;
654    char* filename;
655    if (ReadArgs(state, argv, 2, &filename, &partition) < 0) {
656        return NULL;
657    }
658
659    if (strlen(partition) == 0) {
660        ErrorAbort(state, "partition argument to %s can't be empty", name);
661        goto done;
662    }
663    if (strlen(filename) == 0) {
664        ErrorAbort(state, "file argument to %s can't be empty", name);
665        goto done;
666    }
667
668    FILE* cmd = ((UpdaterInfo*)(state->cookie))->cmd_pipe;
669    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)
686char* ApplyPatchFn(const char* name, State* state, int argc, Expr* argv[]) {
687    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
696    char** args = ReadVarArgs(state, argc, argv);
697    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("");
727        default:  return ErrorAbort(state, "applypatch couldn't parse args");
728    }
729}
730
731char* 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
763char* 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
809
810void RegisterInstallFunctions() {
811    RegisterFunction("mount", MountFn);
812    RegisterFunction("is_mounted", IsMountedFn);
813    RegisterFunction("unmount", UnmountFn);
814    RegisterFunction("format", FormatFn);
815    RegisterFunction("show_progress", ShowProgressFn);
816    RegisterFunction("set_progress", SetProgressFn);
817    RegisterFunction("delete", DeleteFn);
818    RegisterFunction("delete_recursive", DeleteFn);
819    RegisterFunction("package_extract_dir", PackageExtractDirFn);
820    RegisterFunction("package_extract_file", PackageExtractFileFn);
821    RegisterFunction("symlink", SymlinkFn);
822    RegisterFunction("set_perm", SetPermFn);
823    RegisterFunction("set_perm_recursive", SetPermFn);
824
825    RegisterFunction("getprop", GetPropFn);
826    RegisterFunction("file_getprop", FileGetPropFn);
827    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);
833
834    RegisterFunction("ui_print", UIPrintFn);
835
836    RegisterFunction("run_program", RunProgramFn);
837}
838