首页 > 系统 > Android > 正文

android源码分析1--updater(l上)

2019-11-06 09:47:15
字体:
来源:转载
供稿:网友
一install.cpp中调用updater:const char* binary = "/tmp/update_binary";const char** args = (const char**)malloc(sizeof(char*) * 5);args[0] = binary;args[1] = EXPAND(RECOVERY_API_VERSION); // defined in Android.mkchar* temp = (char*)malloc(10);sPRintf(temp, "%d", pipefd[1]);args[2] = temp;args[3] = (char*)path;args[4] = NULL;pid_t pid = fork();if (pid == 0) {umask(022);close(pipefd[0]);execv(binary, (char* const*)args);fprintf(stdout, "E:Can't run %s (%s)/n", binary, strerror(errno)); //updater执行正确永远不会被调用_exit(-1);}close(pipefd[1]);    #include <unistd.h>        int execv(const char *progname, char *const argv[]);    1.2 用法介绍        execv会停止执行当前的进程,并且以progname应用进程替换被停止执行的进程,进程ID没有改变。        progname: 被执行的应用程序。         argv: 传递给应用程序的参数列表, 注意这个数组的第一个参数应该是应用程序名字本身(即argv[0] = progname),并且最后一个参数应该为NULL,不能将多个参数合并为一个参数放入数组。   1.3 返回值       如果应用程序正常执行完毕,那么execv是永远不会返回的;当execv在调用进程中返回时,那么这个应用程序应该出错了(可能是程序本身没找到,权限不够等), 此时它的返回值应该是-1,具体的错误代码可以通过全局变量errno查看,还可以通过stderr得到具体的错误描述字符串。调用updater的3个参数:1 recovery API: the version number for this interface2 一个管道的fd,updater向这个管道写,用于更新进度条 an fd to which the program can write in order to update the progress barint pipefd[2];pipe(pipefd);char* temp = (char*)malloc(10);sprintf(temp, "%d", pipefd[1]); //把pipefd[1]代表的管道的fd,作为字符串参数传给updaterargs[2] = temp;args[3] = (char*)path;args[4] = NULL;pid_t pid = fork();if (pid == 0) {umask(022);close(pipefd[0]); //updater中关闭pipefd[0]execv(binary, (char* const*)args);fprintf(stdout, "E:Can't run %s (%s)/n", binary, strerror(errno));_exit(-1);}close(pipefd[1]); //recovery中关闭pipefd[1]FILE* from_child = fdopen(pipefd[0], "r"); //recovery从管道接收来自updater的信息while (fgets(buffer, sizeof(buffer), from_child) != NULL) {3 升级包路径 the name of the package zip file二 recovery/updater.c中:int main(int argc, char** argv) {    // Various things log information to stdout or stderr more or less    // at random (though we've tried to standardize on stdout).  The    // log file makes more sense if buffering is turned off so things    // appear in the right order.    setbuf(stdout, NULL);// 创建子进程时,父进程的缓冲区也被复制到子进程了。所以子进程在printf时,就一起printf出来了,因为recovery中已经将 stdout stderr重定向到了文件中,所以这里把输出缓冲区设置为无缓冲,直接从流输出数据//setbuf函数的第二个参数取值可以为null,此时标准输出不需要进行缓冲。这种情况下,程序仍然能够工作,只不过速度较慢而已    setbuf(stderr, NULL);     if (argc != 4) {        printf("unexpected number of arguments (%d)/n", argc);        return 1;    }     char* version = argv[1];    if ((version[0] != '1' && version[0] != '2' && version[0] != '3') ||        version[1] != '/0') {        // We support version 1, 2, or 3.        printf("wrong updater binary API; expected 1, 2, or 3; "                        "got %s/n",                argv[1]);        return 2;    }     // Set up the pipe for sending commands back to the parent process.  // 将recovery传递的字符串格式的pipe[1]的fd,转换为fd,再打开这个管道    int fd = atoi(argv[2]);    FILE* cmd_pipe = fdopen(fd, "wb");    setlinebuf(cmd_pipe);     // Extract the script from the package.     const char* package_filename = argv[3]; //argv[3]就是升级包完成路径    MemMapping map;    if (sysMapFile(package_filename, &map) != 0) { //将升级包映射到内存中        printf("failed to map package %s/n", argv[3]);        return 3;    }    ZipArchive za;    int err;    err = mzOpenZipArchive(map.addr, map.length, &za); //根据内存中的起始地址和长度,打开这个文件    if (err != 0) {        printf("failed to open package %s: %s/n",               argv[3], strerror(err));        return 3;    }     const ZipEntry* script_entry = mzFindZipEntry(&za, SCRIPT_NAME); //在文件中查找升级脚本这个entry    if (script_entry == NULL) {        printf("failed to find %s in %s/n", SCRIPT_NAME, package_filename);        return 4;    }     char* script = malloc(script_entry->uncompLen+1);// 根据升级脚本的实际大小分配一段内存,将升级脚本所有内容读到script中    if (!mzReadZipEntry(&za, script_entry, script, script_entry->uncompLen)) {        printf("failed to read script from package/n");        return 5;    }    script[script_entry->uncompLen] = '/0';     // Configure edify's functions.     RegisterBuiltins();    RegisterInstallFunctions();    RegisterBlockImageFunctions();    RegisterDeviceExtensions();    FinishRegistration();     // Parse the script.     Expr* root;struct Expr {Function fn;char* name;int argc;Expr** argv;int start, end;};    int error_count = 0;    int error = parse_string(script, &root, &error_count); //解析脚本    if (error != 0 || error_count > 0) {        printf("%d parse errors/n", error_count);        return 6;    }     struct selinux_opt SEOpts[] = {      { SELABEL_OPT_PATH, "/file_contexts" }    };     sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);     if (!sehandle) {        fprintf(cmd_pipe, "ui_print Warning: No file_contexts/n");    }     // Evaluate the parsed script.     UpdaterInfo updater_info;typedef struct {FILE* cmd_pipe;ZipArchive* package_zip;int version;uint8_t* package_zip_addr;size_t package_zip_len;} UpdaterInfo;    updater_info.cmd_pipe = cmd_pipe; //updater_info.cmd_pipe取得了updater打开的管道    updater_info.package_zip = &za; //updater_info.package_zip 内存中的zip升级包    updater_info.version = atoi(version); //updater_info.version recovery api版本    updater_info.package_zip_addr = map.addr; //updater_info.package_zip_addr zip升级包在内存中的起始地址    updater_info.package_zip_len = map.length; // updater_info.package_zip_len zip升级包在内存中的长度     State state;typedef struct {// Optional pointer to app-specific data; the core of edify never// uses this value.void* cookie;// The source of the original script. Must be NULL-terminated,// and in writable memory (Evaluate may make temporary changes to// it but will restore it when done).char* script;// The error message (if any) returned if the evaluation aborts.// Should be NULL initially, will be either NULL or a malloc'd// pointer after Evaluate() returns.char* errmsg;} State;    state.cookie = &updater_info;    state.script = script; //现在state.script指向的就是脚本内容    state.errmsg = NULL;     char* result = Evaluate(&state, root); //执行脚本    if (result == NULL) {        if (state.errmsg == NULL) {            printf("script aborted (no error message)/n");            fprintf(cmd_pipe, "ui_print script aborted (no error message)/n");        } else {            printf("script aborted: %s/n", state.errmsg);            char* line = strtok(state.errmsg, "/n");            while (line) {                fprintf(cmd_pipe, "ui_print %s/n", line);                line = strtok(NULL, "/n");            }            fprintf(cmd_pipe, "ui_print/n");        }        free(state.errmsg);        return 7;    } else {        fprintf(cmd_pipe, "ui_print script succeeded: result was [%s]/n", result);        free(result);    }     if (updater_info.package_zip) {        mzCloseZipArchive(updater_info.package_zip);    }    sysReleaseMap(&map);    free(script);     return 0;}三 recovery/edify/expr.ctypedef struct {int type;ssize_t size;char* data;} Value;struct Expr {Function fn;char* name;int argc;Expr** argv;int start, end;};typedef struct {void* cookie;char* script;char* errmsg;} State;char* Evaluate(State* state, Expr* expr) {    Value* v = expr->fn(expr->name, state, expr->argc, expr->argv);typedef Value* (*Function)(const char* name, State* state, int argc, Expr* argv[]);    if (v == NULL) return NULL;    if (v->type != VAL_STRING) {        ErrorAbort(state, "expecting string, got value type %d", v->type);        FreeValue(v);        return NULL;    }    char* result = v->data;    free(v);    return result;}Evaluate()函数主要是调用了expr的fn()函数,参数expr的类型是Expr,定义如下:[java] view plain copystruct Expr {      Function fn;      char* name;      int argc;      Expr** argv;      int start, end;  };  从Expr的定义中可以看到它有一个字段argv,这个字段是Expr指针的指针类型,它实际上会指向一个Expr指针的数组对象,表示Expr对象的所有下一级对象。通过这个字段,脚本解析后得到的所有命令都串接在一起,而且命令的执行函数还会调用Ecaluate()来继续执行argv中的Expr对象,因此,虽然Evaluate()中只调用了root对象的fn()函数,但是实际上会执行脚本中的所有命令。// args:// - block device (or file) to modify in-place// - transfer list (blob)// - new data stream (filename within package.zip)// - patch stream (filename within package.zip, must be uncompressed)Value* BlockImageUpdateFn(const char* name, State* state, int argc, Expr* argv[]) {Value* blockdev_filename;Value* transfer_list_value;char* transfer_list = NULL;Value* new_data_fn;Value* patch_data_fn;bool success = false;if (ReadValueArgs(state, argv, 4, &blockdev_filename, &transfer_list_value,&new_data_fn, &patch_data_fn) < 0) {return NULL;}脚本中调用block_image_update("/dev/block/bootdevice/by-name/system", package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat");就会执行BlockImageUpdateFn函数1 ReadValueArgs取得脚本中的/dev/block/bootdevice/by-name/system,package_extract_file("system.transfer.list"),system.new.dat", "system.patch.dat"这四个参数,赋值给blockdev_filename ,transfer_list_value, new_data_fn,patch_data_fntypedef struct {int type;ssize_t size;char* data;} Value;2
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表