int request_snapshot(struct rmonitor_file_watch_info *f) { const char *label = construct_label(f); if(!label) { //no snapshot to request return 0; } struct rmonitor_msg msg; bzero(&msg, sizeof(msg)); msg.type = SNAPSHOT; msg.error = 0; msg.origin = -1; strncpy(msg.data.s, label, sizeof(msg.data.s) - 1); int status = send_monitor_msg(&msg); return status; }
static void __check_for_updates(struct world *mzx_world, struct config_info *conf) { int cur_host; char *update_host; bool try_next_host = true; bool ret = false; set_context(CTX_UPDATER); if(conf->update_host_count < 1) { error("No updater hosts defined! Aborting.", 1, 8, 0); goto err_out; } if(!swivel_current_dir(true)) goto err_out; for(cur_host = 0; (cur_host < conf->update_host_count) && try_next_host; cur_host++) { char **list_entries, buffer[LINE_BUF_LEN], *url_base, *value; struct manifest_entry *removed, *replaced, *added, *e; int i = 0, entries = 0, buf_len, result; char update_branch[LINE_BUF_LEN]; const char *version = VERSION; int list_entry_width = 0; enum host_status status; struct host *h = NULL; unsigned int retries; FILE *f; // Acid test: Can we write to this directory? f = fopen_unsafe(UPDATES_TXT, "w+b"); if(!f) { error("Failed to create \"" UPDATES_TXT "\". Check permissions.", 1, 8, 0); goto err_chdir; } update_host = conf->update_hosts[cur_host]; if(!reissue_connection(conf, &h, update_host)) goto err_host_destroy; for(retries = 0; retries < MAX_RETRIES; retries++) { // Grab the file containing the names of the current Stable and Unstable status = host_recv_file(h, "/" UPDATES_TXT, f, "text/plain"); rewind(f); if(status == HOST_SUCCESS) break; if(!reissue_connection(conf, &h, update_host)) goto err_host_destroy; } if(retries == MAX_RETRIES) { snprintf(widget_buf, WIDGET_BUF_LEN, "Failed to download \"" UPDATES_TXT "\" (err=%d).\n", status); widget_buf[WIDGET_BUF_LEN - 1] = 0; error(widget_buf, 1, 8, 0); goto err_host_destroy; } snprintf(update_branch, LINE_BUF_LEN, "Current-%s", conf->update_branch_pin); // Walk this list (of two, hopefully) while(true) { char *m = buffer, *key; value = NULL; // Grab a single line from the manifest if(!fgets(buffer, LINE_BUF_LEN, f)) break; key = strsep(&m, ":\n"); if(!key) break; value = strsep(&m, ":\n"); if(!value) break; if(strcmp(key, update_branch) == 0) break; } fclose(f); unlink(UPDATES_TXT); /* There was no "Current-XXX: Version" found; we cannot proceed with the * update because we cannot compute an update URL below. */ if(!value) { error("Failed to identify applicable update version.", 1, 8, 0); goto err_host_destroy; } /* There's likely to be a space prepended to the version number. * Skip it here. */ if(value[0] == ' ') value++; /* We found the latest update version, but we should check to see if that * matches the version we're already using. The user may choose to receive * "stability" updates for their current major version, or upgrade to the * newest one. */ if(strcmp(value, version) != 0) { struct element *elements[6]; struct dialog di; buf_len = snprintf(widget_buf, WIDGET_BUF_LEN, "A new major version is available (%s)", value); widget_buf[WIDGET_BUF_LEN - 1] = 0; elements[0] = construct_label((55 - buf_len) >> 1, 2, widget_buf); elements[1] = construct_label(2, 4, "You can continue to receive updates for the version\n" "installed (if available), or you can upgrade to the\n" "newest major version (recommended)."); elements[2] = construct_label(2, 8, "If you do not upgrade, this question will be asked\n" "again the next time you run the updater.\n"); elements[3] = construct_button(9, 11, "Upgrade", 0); elements[4] = construct_button(21, 11, "Update Old", 1); elements[5] = construct_button(36, 11, "Cancel", 2); construct_dialog(&di, "New Major Version", 11, 6, 55, 14, elements, 6, 3); result = run_dialog(mzx_world, &di); destruct_dialog(&di); // User pressed Escape, abort all updates if(result < 0 || result == 2) { try_next_host = false; goto err_host_destroy; } // User pressed Upgrade, use new major if(result == 0) version = value; } /* We can now compute a unique URL base for the updater. This will * be composed of a user-selected version and a static platform-archicture * name. */ url_base = cmalloc(LINE_BUF_LEN); snprintf(url_base, LINE_BUF_LEN, "/%s/" PLATFORM, version); debug("Update base URL: %s\n", url_base); /* The call to manifest_get_updates() destroys any existing manifest * file in this directory. Since we still allow user to abort after * this call, and downloading the updates may fail, we copy the * old manifest to a backup location and optionally restore it later. */ if(!backup_original_manifest()) { error("Failed to back up manifest. Check permissions.", 1, 8, 0); try_next_host = false; goto err_free_url_base; } for(retries = 0; retries < MAX_RETRIES; retries++) { bool m_ret; m_hide(); draw_window_box(3, 11, 76, 13, DI_MAIN, DI_DARK, DI_CORNER, 1, 1); write_string("Computing manifest deltas (added, replaced, deleted)..", 13, 12, DI_TEXT, 0); update_screen(); m_ret = manifest_get_updates(h, url_base, &removed, &replaced, &added); clear_screen(32, 7); m_show(); update_screen(); if(m_ret) break; if(!reissue_connection(conf, &h, update_host)) goto err_roll_back_manifest; } if(retries == MAX_RETRIES) { error("Failed to compute update manifests", 1, 8, 0); goto err_roll_back_manifest; } // At this point, we have a successful manifest, so we won't need another host try_next_host = false; if(!removed && !replaced && !added) { struct element *elements[3]; struct dialog di; elements[0] = construct_label(2, 2, "This client is already current."); elements[1] = construct_button(7, 4, "OK", 0); elements[2] = construct_button(13, 4, "Try next host", 1); construct_dialog(&di, "No Updates", 22, 9, 35, 6, elements, 3, 1); result = run_dialog(mzx_world, &di); destruct_dialog(&di); if((result == 1) && (cur_host < conf->update_host_count)) try_next_host = true; goto err_free_update_manifests; } for(e = removed; e; e = e->next, entries++) list_entry_width = MAX(list_entry_width, 2 + (int)strlen(e->name)+1+1); for(e = replaced; e; e = e->next, entries++) list_entry_width = MAX(list_entry_width, 2 + (int)strlen(e->name)+1+1); for(e = added; e; e = e->next, entries++) list_entry_width = MAX(list_entry_width, 2 + (int)strlen(e->name)+1+1); // We don't want the listbox to be too wide list_entry_width = MIN(list_entry_width, 60); list_entries = cmalloc(entries * sizeof(char *)); for(e = removed; e; e = e->next, i++) { list_entries[i] = cmalloc(list_entry_width); snprintf(list_entries[i], list_entry_width, "- %s", e->name); list_entries[i][list_entry_width - 1] = 0; } for(e = replaced; e; e = e->next, i++) { list_entries[i] = cmalloc(list_entry_width); snprintf(list_entries[i], list_entry_width, "* %s", e->name); list_entries[i][list_entry_width - 1] = 0; } for(e = added; e; e = e->next, i++) { list_entries[i] = cmalloc(list_entry_width); snprintf(list_entries[i], list_entry_width, "+ %s", e->name); list_entries[i][list_entry_width - 1] = 0; } draw_window_box(19, 1, 59, 4, DI_MAIN, DI_DARK, DI_CORNER, 1, 1); write_string(" Task Summary ", 33, 1, DI_TITLE, 0); write_string("ESC - Cancel [+] Add [-] Delete", 21, 2, DI_TEXT, 0); write_string("ENTER - Proceed [*] Replace ", 21, 3, DI_TEXT, 0); result = list_menu((const char **)list_entries, list_entry_width, NULL, 0, entries, ((80 - (list_entry_width + 9)) >> 1) + 1, 4); for(i = 0; i < entries; i++) free(list_entries[i]); free(list_entries); clear_screen(32, 7); update_screen(); if(result < 0) goto err_free_update_manifests; /* Defer deletions until we restart; any of these files may still be * in use by this (old) process. Reduce the number of entries by the * number of removed items for the progress meter below. */ for(e = removed; e; e = e->next, entries--) delete_hook(e->name); /* Since the operations for adding and replacing a file are identical, * we modify the replaced list and tack on the added list to the end. * * Either list may be NULL; in the case that `replaced' is NULL, simply * re-assign the `added' pointer. `added' being NULL has no effect. * * Later, we need only free the replaced list (see below). */ if(replaced) { for(e = replaced; e->next; e = e->next) ; e->next = added; } else replaced = added; cancel_update = false; host_set_callbacks(h, NULL, recv_cb, cancel_cb); i = 1; for(e = replaced; e; e = e->next, i++) { for(retries = 0; retries < MAX_RETRIES; retries++) { char name[72]; bool m_ret; if(!check_create_basedir(e->name)) goto err_free_delete_list; final_size = (long)e->size; m_hide(); snprintf(name, 72, "%s (%ldb) [%u/%u]", e->name, final_size, i, entries); meter(name, 0, final_size); update_screen(); m_ret = manifest_entry_download_replace(h, url_base, e, delete_hook); clear_screen(32, 7); m_show(); update_screen(); if(m_ret) break; if(cancel_update) { error("Download was cancelled; update aborted.", 1, 8, 0); goto err_free_delete_list; } if(!reissue_connection(conf, &h, update_host)) goto err_free_delete_list; host_set_callbacks(h, NULL, recv_cb, cancel_cb); } if(retries == MAX_RETRIES) { snprintf(widget_buf, WIDGET_BUF_LEN, "Failed to download \"%s\" (after %d attempts).", e->name, retries); widget_buf[WIDGET_BUF_LEN - 1] = 0; error(widget_buf, 1, 8, 0); goto err_free_delete_list; } } if(delete_list) { f = fopen_unsafe(DELETE_TXT, "wb"); if(!f) { error("Failed to create \"" DELETE_TXT "\". Check permissions.", 1, 8, 0); goto err_free_delete_list; } for(e = delete_list; e; e = e->next) { fprintf(f, "%08x%08x%08x%08x%08x%08x%08x%08x %lu %s\n", e->sha256[0], e->sha256[1], e->sha256[2], e->sha256[3], e->sha256[4], e->sha256[5], e->sha256[6], e->sha256[7], e->size, e->name); } fclose(f); } try_next_host = false; ret = true; err_free_delete_list: manifest_list_free(&delete_list); delete_list = delete_p = NULL; err_free_update_manifests: manifest_list_free(&removed); manifest_list_free(&replaced); err_roll_back_manifest: restore_original_manifest(ret); err_free_url_base: free(url_base); err_host_destroy: host_destroy(h); pop_context(); } //end host for loop err_chdir: swivel_current_dir_back(true); err_out: /* At this point we found updates and we successfully updated * to them. Reload the program with the original argv. */ if(ret) { const void *argv = process_argv; struct element *elements[2]; struct dialog di; elements[0] = construct_label(2, 2, "This client will now attempt to restart itself."); elements[1] = construct_button(23, 4, "OK", 0); construct_dialog(&di, "Update Successful", 14, 9, 51, 6, elements, 2, 1); run_dialog(mzx_world, &di); destruct_dialog(&di); execv(process_argv[0], argv); perror("execv"); error("Attempt to invoke self failed!", 1, 8, 0); return; } }