static int update_directory(const char* path, const char* unmount_when_done, int* wipe_cache, Device* device) { ensure_path_mounted(path); const char* MENU_HEADERS[] = { "Choose a package to install:", path, "", NULL }; DIR* d; struct dirent* de; d = opendir(path); if (d == NULL) { LOGE("error opening %s: %s\n", path, strerror(errno)); if (unmount_when_done != NULL) { ensure_path_unmounted(unmount_when_done); } return 0; } const char** headers = prepend_title(MENU_HEADERS); int d_size = 0; int d_alloc = 10; char** dirs = (char**)malloc(d_alloc * sizeof(char*)); int z_size = 1; int z_alloc = 10; char** zips = (char**)malloc(z_alloc * sizeof(char*)); zips[0] = strdup("../"); while ((de = readdir(d)) != NULL) { int name_len = strlen(de->d_name); if (de->d_type == DT_DIR) { // skip "." and ".." entries if (name_len == 1 && de->d_name[0] == '.') continue; if (name_len == 2 && de->d_name[0] == '.' && de->d_name[1] == '.') continue; if (d_size >= d_alloc) { d_alloc *= 2; dirs = (char**)realloc(dirs, d_alloc * sizeof(char*)); } dirs[d_size] = (char*)malloc(name_len + 2); strcpy(dirs[d_size], de->d_name); dirs[d_size][name_len] = '/'; dirs[d_size][name_len+1] = '\0'; ++d_size; } else if (de->d_type == DT_REG && name_len >= 4 && strncasecmp(de->d_name + (name_len-4), ".zip", 4) == 0) { if (z_size >= z_alloc) { z_alloc *= 2; zips = (char**)realloc(zips, z_alloc * sizeof(char*)); } zips[z_size++] = strdup(de->d_name); } } closedir(d); qsort(dirs, d_size, sizeof(char*), compare_string); qsort(zips, z_size, sizeof(char*), compare_string); // append dirs to the zips list if (d_size + z_size + 1 > z_alloc) { z_alloc = d_size + z_size + 1; zips = (char**)realloc(zips, z_alloc * sizeof(char*)); } memcpy(zips + z_size, dirs, d_size * sizeof(char*)); free(dirs); z_size += d_size; zips[z_size] = NULL; int result; int chosen_item = 0; do { chosen_item = get_menu_selection(headers, zips, 1, chosen_item, device); char* item = zips[chosen_item]; int item_len = strlen(item); if (chosen_item == 0) { // item 0 is always "../" // go up but continue browsing (if the caller is update_directory) result = -1; break; } else if (item[item_len-1] == '/') { // recurse down into a subdirectory char new_path[PATH_MAX]; strlcpy(new_path, path, PATH_MAX); strlcat(new_path, "/", PATH_MAX); strlcat(new_path, item, PATH_MAX); new_path[strlen(new_path)-1] = '\0'; // truncate the trailing '/' result = update_directory(new_path, unmount_when_done, wipe_cache, device); if (result >= 0) break; } else { // selected a zip file: attempt to install it, and return // the status to the caller. char new_path[PATH_MAX]; strlcpy(new_path, path, PATH_MAX); strlcat(new_path, "/", PATH_MAX); strlcat(new_path, item, PATH_MAX); ui->Print("\n-- Install %s ...\n", path); set_sdcard_update_bootloader_message(); char* copy = copy_sideloaded_package(new_path); if (unmount_when_done != NULL) { ensure_path_unmounted(unmount_when_done); } if (copy) { result = install_package(copy, wipe_cache, TEMPORARY_INSTALL_FILE); free(copy); } else { result = INSTALL_ERROR; } break; } } while (true); int i; for (i = 0; i < z_size; ++i) free(zips[i]); free(zips); free(headers); if (unmount_when_done != NULL) { ensure_path_unmounted(unmount_when_done); } return result; }
static void prompt_and_wait(Device* device) { const char* const* headers = prepend_title(device->GetMenuHeaders()); for (;;) { finish_recovery(NULL); ui->SetProgressType(RecoveryUI::EMPTY); int chosen_item = get_menu_selection(headers, device->GetMenuItems(), 0, 0, device); // device-specific code may take some action here. It may // return one of the core actions handled in the switch // statement below. chosen_item = device->InvokeMenuItem(chosen_item); int status; int wipe_cache; switch (chosen_item) { case Device::REBOOT: return; case Device::WIPE_DATA: wipe_data(ui->IsTextVisible(), device); if (!ui->IsTextVisible()) return; break; case Device::WIPE_CACHE: ui->Print("\n-- Wiping cache...\n"); erase_volume("/cache"); ui->Print("Cache wipe complete.\n"); if (!ui->IsTextVisible()) return; break; case Device::APPLY_EXT: // Some packages expect /cache to be mounted (eg, // standard incremental packages expect to use /cache // as scratch space). ensure_path_mounted(CACHE_ROOT); status = update_directory(SDCARD_ROOT, SDCARD_ROOT, &wipe_cache, device); if (status == INSTALL_SUCCESS && wipe_cache) { ui->Print("\n-- Wiping cache (at package request)...\n"); if (erase_volume("/cache")) { ui->Print("Cache wipe failed.\n"); } else { ui->Print("Cache wipe complete.\n"); } } if (status >= 0) { if (status != INSTALL_SUCCESS) { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); } else if (!ui->IsTextVisible()) { return; // reboot if logs aren't visible } else { ui->Print("\nInstall from sdcard complete.\n"); } } break; case Device::APPLY_CACHE: // Don't unmount cache at the end of this. status = update_directory(CACHE_ROOT, NULL, &wipe_cache, device); if (status == INSTALL_SUCCESS && wipe_cache) { ui->Print("\n-- Wiping cache (at package request)...\n"); if (erase_volume("/cache")) { ui->Print("Cache wipe failed.\n"); } else { ui->Print("Cache wipe complete.\n"); } } if (status >= 0) { if (status != INSTALL_SUCCESS) { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); } else if (!ui->IsTextVisible()) { return; // reboot if logs aren't visible } else { ui->Print("\nInstall from cache complete.\n"); } } break; case Device::APPLY_ADB_SIDELOAD: ensure_path_mounted(CACHE_ROOT); status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE); if (status >= 0) { if (status != INSTALL_SUCCESS) { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); } else if (!ui->IsTextVisible()) { return; // reboot if logs aren't visible } else { ui->Print("\nInstall from ADB complete.\n"); } } break; } } }
/** * Entry point to demo. Note: this HTTP server will make all * files in the current directory and its subdirectories available * to anyone. Press ENTER to stop the server once it has started. * * @param argc number of arguments in argv * @param argv first and only argument should be the port number * @return 0 on success */ int main (int argc, char *const *argv) { struct MHD_Daemon *d; unsigned int port; if ( (argc != 2) || (1 != sscanf (argv[1], "%u", &port)) || (UINT16_MAX < port) ) { fprintf (stderr, "%s PORT\n", argv[0]); return 1; } #ifndef MINGW ignore_sigpipe (); #endif magic = magic_open (MAGIC_MIME_TYPE); (void) magic_load (magic, NULL); (void) pthread_mutex_init (&mutex, NULL); file_not_found_response = MHD_create_response_from_buffer (strlen (FILE_NOT_FOUND_PAGE), (void *) FILE_NOT_FOUND_PAGE, MHD_RESPMEM_PERSISTENT); mark_as_html (file_not_found_response); request_refused_response = MHD_create_response_from_buffer (strlen (REQUEST_REFUSED_PAGE), (void *) REQUEST_REFUSED_PAGE, MHD_RESPMEM_PERSISTENT); mark_as_html (request_refused_response); internal_error_response = MHD_create_response_from_buffer (strlen (INTERNAL_ERROR_PAGE), (void *) INTERNAL_ERROR_PAGE, MHD_RESPMEM_PERSISTENT); mark_as_html (internal_error_response); update_directory (); d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG #if EPOLL_SUPPORT | MHD_USE_EPOLL_LINUX_ONLY #endif , port, NULL, NULL, &generate_page, NULL, MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (256 * 1024), #if PRODUCTION MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) (64), #endif MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) (120 /* seconds */), MHD_OPTION_THREAD_POOL_SIZE, (unsigned int) NUMBER_OF_THREADS, MHD_OPTION_NOTIFY_COMPLETED, &response_completed_callback, NULL, MHD_OPTION_END); if (NULL == d) return 1; fprintf (stderr, "HTTP server running. Press ENTER to stop the server\n"); (void) getc (stdin); MHD_stop_daemon (d); MHD_destroy_response (file_not_found_response); MHD_destroy_response (request_refused_response); MHD_destroy_response (internal_error_response); update_cached_response (NULL); (void) pthread_mutex_destroy (&mutex); magic_close (magic); return 0; }
static int prompt_and_wait() { char** headers = prepend_title((const char**)MENU_HEADERS); for (;;) { finish_recovery(NULL); ui_reset_progress(); int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0, 0); // device-specific code may take some action here. It may // return one of the core actions handled in the switch // statement below. chosen_item = device_perform_action(chosen_item); int status; int wipe_cache; switch (chosen_item) { case ITEM_REBOOT: return REBOOT_NORMAL; #ifdef RECOVERY_HAS_FACTORY_TEST case ITEM_FACTORY_TEST: return REBOOT_FACTORY_TEST; #endif case ITEM_WIPE_DATA: wipe_data(ui_text_visible()); if (!ui_text_visible()) return REBOOT_NORMAL; break; case ITEM_WIPE_CACHE: ui_print("\n-- Wiping cache...\n"); erase_volume("/cache"); ui_print("Cache wipe complete.\n"); if (!ui_text_visible()) return REBOOT_NORMAL; break; #ifdef RECOVERY_HAS_MEDIA case ITEM_WIPE_MEDIA: wipe_media(ui_text_visible()); if (!ui_text_visible()) return REBOOT_NORMAL; break; #endif /* RECOVERY_HAS_MEDIA */ case ITEM_APPLY_SDCARD: #ifdef RECOVERY_HAS_SDCARD_ONLY status = update_directory(SDCARD_ROOT, SDCARD_ROOT, &wipe_cache); #else status = update_directory("/", "/", &wipe_cache); #endif /* RECOVERY_HAS_SDCARD_ONLY */ if (status == INSTALL_SUCCESS && wipe_cache) { ui_print("\n-- Wiping cache (at package request)...\n"); if (erase_volume("/cache")) { ui_print("Cache wipe failed.\n"); } else { ui_print("Cache wipe complete.\n"); } } if (status >= 0) { if (status != INSTALL_SUCCESS) { ui_set_background(BACKGROUND_ICON_ERROR); ui_print("Installation aborted.\n"); } else if (!ui_text_visible()) { return REBOOT_NORMAL; // reboot if logs aren't visible } else { #ifdef RECOVERY_HAS_SDCARD_ONLY ui_print("\nInstall from sdcard complete.\n"); #else ui_print("\nInstall complete.\n"); #endif /* RECOVERY_HAS_SDCARD_ONLY */ } } break; case ITEM_APPLY_CACHE: // Don't unmount cache at the end of this. status = update_directory(CACHE_ROOT, NULL, &wipe_cache); if (status == INSTALL_SUCCESS && wipe_cache) { ui_print("\n-- Wiping cache (at package request)...\n"); if (erase_volume("/cache")) { ui_print("Cache wipe failed.\n"); } else { ui_print("Cache wipe complete.\n"); } } if (status >= 0) { if (status != INSTALL_SUCCESS) { ui_set_background(BACKGROUND_ICON_ERROR); ui_print("Installation aborted.\n"); } else if (!ui_text_visible()) { return REBOOT_NORMAL; // reboot if logs aren't visible } else { ui_print("\nInstall from cache complete.\n"); } } break; #ifdef RECOVERY_HAS_EFUSE case ITEM_WRITE_EFUSE: recovery_efuse(-1, NULL); if (!ui_text_visible()) return REBOOT_NORMAL; break; #endif /* RECOVERY_HAS_EFUSE */ } } }
/** * Main callback from MHD, used to generate the page. * * @param cls NULL * @param connection connection handle * @param url requested URL * @param method GET, PUT, POST, etc. * @param version HTTP version * @param upload_data data from upload (PUT/POST) * @param upload_data_size number of bytes in "upload_data" * @param ptr our context * @return MHD_YES on success, MHD_NO to drop connection */ static int generate_page (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) { struct MHD_Response *response; int ret; int fd; struct stat buf; if (0 != strcmp (url, "/")) { /* should be file download */ char file_data[MAGIC_HEADER_SIZE]; ssize_t got; const char *mime; if ( (0 != strcmp (method, MHD_HTTP_METHOD_GET)) && (0 != strcmp (method, MHD_HTTP_METHOD_HEAD)) ) return MHD_NO; /* unexpected method (we're not polite...) */ if ( (0 == stat (&url[1], &buf)) && (NULL == strstr (&url[1], "..")) && ('/' != url[1])) fd = open (&url[1], O_RDONLY); else fd = -1; if (-1 == fd) return MHD_queue_response (connection, MHD_HTTP_NOT_FOUND, file_not_found_response); /* read beginning of the file to determine mime type */ got = read (fd, file_data, sizeof (file_data)); if (-1 != got) mime = magic_buffer (magic, file_data, got); else mime = NULL; (void) lseek (fd, 0, SEEK_SET); if (NULL == (response = MHD_create_response_from_fd (buf.st_size, fd))) { /* internal error (i.e. out of memory) */ (void) close (fd); return MHD_NO; } /* add mime type if we had one */ if (NULL != mime) (void) MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_TYPE, mime); ret = MHD_queue_response (connection, MHD_HTTP_OK, response); MHD_destroy_response (response); return ret; } if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) { /* upload! */ struct UploadContext *uc = *ptr; if (NULL == uc) { if (NULL == (uc = malloc (sizeof (struct UploadContext)))) return MHD_NO; /* out of memory, close connection */ memset (uc, 0, sizeof (struct UploadContext)); uc->fd = -1; uc->connection = connection; uc->pp = MHD_create_post_processor (connection, 64 * 1024 /* buffer size */, &process_upload_data, uc); if (NULL == uc->pp) { /* out of memory, close connection */ free (uc); return MHD_NO; } *ptr = uc; return MHD_YES; } if (0 != *upload_data_size) { if (NULL == uc->response) (void) MHD_post_process (uc->pp, upload_data, *upload_data_size); *upload_data_size = 0; return MHD_YES; } /* end of upload, finish it! */ MHD_destroy_post_processor (uc->pp); uc->pp = NULL; if (-1 != uc->fd) { close (uc->fd); uc->fd = -1; } if (NULL != uc->response) { return MHD_queue_response (connection, MHD_HTTP_FORBIDDEN, uc->response); } else { update_directory (); return return_directory_response (connection); } } if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) || (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) ) { return return_directory_response (connection); } /* unexpected request, refuse */ return MHD_queue_response (connection, MHD_HTTP_FORBIDDEN, request_refused_response); }