/* import.c: * **************************************************************** * Copyright (C) 2003 Tom Lord * * See the file "COPYING" for further information about * the copyright and warranty status of this work. */ #include "hackerlab/bugs/exception.h" #include "hackerlab/bugs/panic.h" #include "hackerlab/os/time.h" #include "hackerlab/char/str.h" #include "hackerlab/fs/file-names.h" #include "hackerlab/vu/safe.h" #include "libdate/date-string.h" #include "libfsutils/rmrf.h" #include "libfsutils/string-files.h" #include "libfsutils/copy-file.h" #include "libfsutils/tmp-files.h" #include "libfsutils/rmrf.h" #include "libarch/ancestry.h" #include "libarch/patch-logs.h" #include "libarch/invent.h" #include "libarch/my.h" #include "libarch/hooks.h" #include "libarch/namespace.h" #include "libarch/pristines.h" #include "libarch/project-tree.h" #include "libarch/changelogs.h" #include "libarch/local-cache.h" #include "libarch/import.h" /* __STDC__ prototypes for static functions */ static t_uchar * arch_prepare_import_pristine (t_uchar ** changelog_loc_ret, t_uchar ** cooked_log_ret, arch_project_tree_t * tree, t_uchar * raw_log, t_uchar * archive, t_uchar * version); static void arch_finish_import (arch_project_tree_t * tree, t_uchar * archive, t_uchar * version, t_uchar * pristine, t_uchar * changelog_loc); static void arch_import_failed (arch_project_tree_t * tree, t_uchar * archive, t_uchar * version, t_uchar * pristine); static rel_table all_patches (t_uchar * tree_root); void arch_import (struct arch_archive * arch, t_uchar * version, arch_project_tree_t * tree, t_uchar * raw_log) { t_uchar * changelog_loc = 0; t_uchar * errstr; t_uchar * pristine_path = 0; t_uchar * cooked_log = 0; t_uchar * my_uid = 0; t_uchar * txn_id = 0; t_uchar * revision; revision = str_alloc_cat (0, version, "--base-0"); pristine_path = arch_prepare_import_pristine (&changelog_loc, &cooked_log, tree, raw_log, arch->official_name, version); my_uid = arch_my_id_uid (); txn_id = arch_generate_txn_id (); if (arch_revision_exists (arch, revision)) { safe_printfmt (2, "arch_import: the revision already exists\n revision: %s/%s\n", arch->official_name, revision); exit (2); } if (arch_archive_lock_revision (&errstr, arch, version, 0, my_uid, txn_id, "base-0")) { safe_printfmt (2, "arch_import: unable to acquire revision lock (%s)\n tree: %s\n revision: %s/%s--base-0\n url: %s\n", errstr, tree->root, arch->official_name, version, arch->location); exit (2); } if (arch_archive_put_log (&errstr, arch, version, 0, my_uid, txn_id, cooked_log)) { safe_printfmt (2, "arch_import: unable to send log message to archive (%s)\n tree: %s\n revision: %s/%s\n url: %s\n", errstr, tree->root, arch->official_name, version, arch->location); exit (2); } if (arch_archive_put_import (&errstr, arch, version, 0, my_uid, txn_id, "base-0", pristine_path)) { safe_printfmt (2, "arch_import: unable to send import tree to archive (%s)\n tree: %s\n revision: %s/%s\n url: %s\n", errstr, tree->root, arch->official_name, version, arch->location); exit (2); } if (arch_revision_ready (&errstr, arch, version, 0, my_uid, txn_id, "base-0")) { safe_printfmt (2, "arch_import: error sending tree to archive (%s)\n tree: %s\n revision: %s/%s\n url: %s\n", errstr, tree->root, arch->official_name, version, arch->location); exit (2); } arch_start_tree_commit (tree, cooked_log); if (arch_archive_finish_revision (&errstr, arch, version, 0, my_uid, txn_id, "base-0")) { arch_import_failed (tree, arch->official_name, version, pristine_path); safe_printfmt (2, "arch_import: unable to complete import transaction (%s)\n tree: %s\n revision: %s/%s\n url: %s\n", errstr, tree->root, arch->official_name, version, arch->location); exit (2); } { arch_patch_id * patch_id = arch_patch_id_new_archive (arch->official_name, revision); /* FIXME upload limited history and update a current dir in the archive */ ancestry_upload_patch (arch, patch_id, -1); talloc_free (patch_id); } arch_finish_import (tree, arch->official_name, version, pristine_path, changelog_loc); arch_run_hook ("import", "ARCH_ARCHIVE", arch->official_name, "ARCH_REVISION", revision, "ARCH_TREE_ROOT", tree->root, 0); lim_free (0, revision); lim_free (0, changelog_loc); lim_free (0, pristine_path); lim_free (0, cooked_log); lim_free (0, my_uid); lim_free (0, txn_id); } static t_uchar * arch_prepare_import_pristine (t_uchar ** changelog_loc_ret, t_uchar ** cooked_log_ret, arch_project_tree_t * tree, t_uchar * raw_log, t_uchar * archive, t_uchar * version) { t_uchar * tmp_stem = 0; t_uchar * tmp_path = 0; t_uchar * revision = 0; t_uchar * pristine_path = 0; rel_table inventory = 0; rel_table new_files_list = 0; t_uchar * log_message = 0; t_uchar * pristine_log_path = 0; arch_project_tree_t * pristine; int pristine_log_fd; /**************************************************************** * double check that we were handed a valid log message, if any */ if (raw_log) invariant (arch_valid_log_file (raw_log)); /**************************************************************** * make a temp dir for the pristine copy of the import */ tmp_stem = str_alloc_cat_many (0, ",,import.", version, "--base-0--", archive, str_end); tmp_path = talloc_tmp_file_name (talloc_context, tree->root, tmp_stem); rmrf_file (tmp_path); safe_mkdir (tmp_path, 0777); revision = str_alloc_cat (0, version, "--base-0"); pristine_path = file_name_in_vicinity (0, tmp_path, revision); inventory = arch_source_inventory (tree, 1, 0, 0); safe_mkdir (pristine_path, 0777); copy_file_list (pristine_path, tree->root, inventory); pristine = arch_project_tree_new (tmp_path, pristine_path); /**************************************************************** * As long as the cache is hot with directories and inodes, compute * a list of files for the New-files: header in the log message. * FIXME: this is wasteful, post process the inventory instead. */ { rel_table temp_list = arch_source_files_inventory (tree, 1, 0); new_files_list = pick_non_control (temp_list); rel_free_table (temp_list); } /**************************************************************** * Create a log message for the newly committed base-0. * * Although the message is empty at this point, it's important for * it to appear in directory listings of the patch-log, so that it * shows up in the New-patches: header. */ pristine_log_path = arch_log_file (pristine_path, archive, revision); pristine_log_fd = safe_open (pristine_log_path, O_WRONLY | O_CREAT | O_EXCL, 0666); /**************************************************************** * Generate the actual log message, adding automatically generated * headers either to the supplied raw-log or to a default raw-log. */ { t_uchar * my_id = 0; time_t now; t_uchar * std_date = 0; t_uchar * human_date = 0; int log_fd; rel_table patches = 0; t_uchar * eoh = NULL; /* gcc doesn't see the link from raw_log to eoh */ my_id = arch_my_id (); now = time(0); std_date = standard_date (now); human_date = pretty_date (now); log_fd = make_output_to_string_fd (); safe_printfmt (log_fd, "Revision: %s--base-0\n", version); safe_printfmt (log_fd, "Archive: %s\n", archive); safe_printfmt (log_fd, "Creator: %s\n", my_id); safe_printfmt (log_fd, "Date: %s\n", human_date); safe_printfmt (log_fd, "Standard-date: %s\n", std_date); /******************************** * Copy headers from the raw log */ if (raw_log) { eoh = raw_log; while (1) { eoh = str_chr_index (eoh, '\n'); if (!eoh || (eoh[1] == '\n') || (!eoh[1])) break; ++eoh; } if (eoh) { eoh = eoh + 1; safe_printfmt (log_fd, "%.*s", (int)(eoh - raw_log), raw_log); } } else { safe_printfmt (log_fd, "Summary: initial import\n"); } /******************************** * automatic headers for New-files: and New-patches: */ arch_print_log_list_header (log_fd, "New-files", new_files_list, 0); patches = all_patches (pristine_path); arch_print_log_list_header (log_fd, "New-patches", patches, 0); /******************************** * copy or generate the log body */ if (!raw_log) safe_printfmt (log_fd, "\n\n(automatically generated log message)\n"); else { if (*eoh) { safe_printfmt (log_fd, "%s", eoh); } else { safe_printfmt (log_fd, "\n\n"); } } /******************************** * oh... did i mention we were writing * the log to a string? */ log_message = string_fd_close (log_fd); lim_free (0, my_id); lim_free (0, std_date); lim_free (0, human_date); rel_free_table (patches); } /**************************************************************** * Write the log into the patch-log of the pristine tree. */ safe_printfmt (pristine_log_fd, "%s", log_message); safe_close (pristine_log_fd); /**************************************************************** * If the import tree has a ChangeLog for this version, * update it in the pristine tree. */ { int x; t_uchar * changelog_id_suffix = 0; t_uchar * changelog_x_id = 0; t_uchar * changelog_i_id = 0; changelog_id_suffix = str_alloc_cat_many (0, "_automatic-ChangeLog--", archive, "/", version, str_end); changelog_x_id = str_alloc_cat (0, "x", changelog_id_suffix); changelog_i_id = str_alloc_cat (0, "i", changelog_id_suffix); for (x = 0; x < rel_n_records (inventory); ++x) { if (!str_cmp (changelog_x_id, inventory[x][1]) || !str_cmp (changelog_i_id, inventory[x][1])) { struct stat clstatb; t_uchar * changelog_path = 0; mode_t clmode; int out_fd = -1; if (changelog_loc_ret) *changelog_loc_ret = str_save (0, inventory[x][0]); changelog_path = file_name_in_vicinity (0, pristine_path, inventory[x][0]); safe_stat (changelog_path, &clstatb); clmode = clstatb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); out_fd = safe_open (changelog_path, O_WRONLY | O_CREAT | O_TRUNC, clmode); safe_fchmod (out_fd, clmode); arch_generate_changelog (out_fd, pristine, 0, 0, 0, 0, archive, version); safe_close (out_fd); lim_free (0, changelog_path); break; } } lim_free (0, changelog_id_suffix); lim_free (0, changelog_x_id); lim_free (0, changelog_i_id); } /**************************************************************** * Give a copy of the log to the caller. */ if (cooked_log_ret) *cooked_log_ret = str_save (0, log_message); lim_free (0, tmp_stem); talloc_free (tmp_path); lim_free (0, revision); rel_free_table (inventory); rel_free_table (new_files_list); lim_free (0, log_message); lim_free (0, pristine_log_path); /**************************************************************** * Give the user the path to the pristine tree for base-0. * It's up tot he caller to stash this in the archive. */ return pristine_path; } static void arch_finish_import (arch_project_tree_t * tree, t_uchar * archive, t_uchar * version, t_uchar * pristine, t_uchar * changelog_loc) { t_uchar * revision = 0; t_uchar * pristine_dir = 0; t_uchar * pristine_dir_tail = 0; arch_patch_id * patch_id; revision = str_alloc_cat (0, version, "--base-0"); patch_id = arch_patch_id_new_archive (archive, revision); /**************************************************************** * txnally install the log file in the patch log */ arch_finish_tree_commit (tree, archive, revision, changelog_loc); /**************************************************************** * Install the pristine tree unless we have a greedy library * that is eager to slurp it up. * * If we don't snarf the library here, it'll be deleted * during "clean up", below. */ if (!arch_greedy_library_wants_revision (patch_id)) arch_install_pristine (tree, archive, revision, pristine); /**************************************************************** * clean up */ pristine_dir = file_name_directory_file (0, pristine); invariant (!!pristine_dir); pristine_dir_tail = file_name_tail (0, pristine_dir); invariant (!str_cmp_prefix (",,import.", pristine_dir_tail)); rmrf_file (pristine_dir); lim_free (0, revision); lim_free (0, pristine_dir); lim_free (0, pristine_dir_tail); talloc_free (patch_id); } static void arch_import_failed (arch_project_tree_t * tree, t_uchar * archive, t_uchar * version, t_uchar * pristine) { t_uchar * revision = 0; t_uchar * pristine_dir = 0; t_uchar * pristine_dir_tail = 0; revision = str_alloc_cat (0, version, "--base-0"); /**************************************************************** * get out of mid-commit state in the project tree */ arch_abort_tree_commit (tree, archive, revision); /**************************************************************** * clean up */ pristine_dir = file_name_directory_file (0, pristine); invariant (!!pristine_dir); pristine_dir_tail = file_name_tail (0, pristine_dir); invariant (str_cmp_prefix (",,import.", pristine_dir_tail)); rmrf_file (pristine_dir); lim_free (0, revision); lim_free (0, pristine_dir); lim_free (0, pristine_dir_tail); } static rel_table all_patches (t_uchar * tree_root) { rel_table log_versions = 0; rel_table answer = 0; int x; log_versions = arch_log_versions (tree_root, 0, 0, 0, 0); for (x = 0; x < rel_n_records (log_versions); ++x) { t_uchar * archive = 0; t_uchar * version = 0; rel_table patch_list = 0; archive = arch_parse_package_name (arch_ret_archive, 0, log_versions[x][0]); version = arch_parse_package_name (arch_ret_non_archive, 0, log_versions[x][0]); patch_list = arch_logs (tree_root, archive, version, 1); rel_append_x (&answer, patch_list); lim_free (0, archive); lim_free (0, version); rel_free_table (patch_list); } rel_free_table (log_versions); return answer; } /* tag: Tom Lord Sat May 24 22:40:45 2003 (import.c) */