/* cmd-grab.c * **************************************************************** * Copyright (C) 2003 Tom Lord, Mark Thomas * * See the file "COPYING" for further information about * the copyright and warranty status of this work. */ #include "config-options.h" #include "libawk/trim.h" #include "po/gettext.h" #include "hackerlab/bugs/exception.h" #include "hackerlab/bugs/panic.h" #include "hackerlab/char/str.h" #include "hackerlab/cmd/main.h" #include "hackerlab/fs/file-names.h" #include "hackerlab/fs/cwd.h" #include "hackerlab/vu/safe.h" #include "libarch/archive.h" #include "libarch/archives.h" #include "libarch/configs.h" #include "libarch/build-revision.h" #include "libarch/namespace.h" #include "libarch/pfs-dav.h" #include "libarch/project-tree.h" #include "libfsutils/file-contents.h" #include "libfsutils/rmrf.h" #include "libfsutils/tmp-files.h" #include "commands/cmd.h" #include "commands/build-config.h" #include "commands/grab.h" #include "commands/version.h" /* __STDC__ prototypes for static functions */ static void arch_grab (int chatter_fd, t_uchar *location, struct arch_build_config_params*); static t_uchar * find_latest_revision (struct arch_archive * archive, t_uchar *revision); static size_t remote_path_off (t_uchar * location); static t_uchar * usage = N_("[options] location"); #define OPTS(OP) \ OP (opt_help_msg, "h", "help", 0, \ N_("Display a help message and exit.")) \ OP (opt_long_help, "H", 0, 0, \ N_("Display a verbose help message and exit.")) \ OP (opt_version, "V", "version", 0, \ N_("Display a release identifier string\n" \ "and exit.")) \ OP (opt_no_pristines, 0, "no-pristines", 0, \ N_("don't create pristine copies")) \ OP (opt_hardlinks, 0, "link", 0, \ N_("hardlink files to revision library instead of copying")) \ OP (opt_library, 0, "library", 0, \ N_("ensure revisions are in the revision library")) \ OP (opt_sparse, 0, "sparse", 0, \ N_("add library entries sparsely (--link, --library)")) \ OP (opt_release_id, "r", "release-id", 0, \ N_("overwrite ./=RELEASE-ID for this config")) t_uchar arch_cmd_grab_help[] = N_("grab a published revision\n" "Grabs a published revision from LOCATION.\n\n" "A grab file has the following syntax:\n\n" "Archive-Name: [the name of the archive]\n" "Archive-Location: [the location of the archive]\n" "Target-Revision: [PACKAGE|VERSION|REVISION to get]\n" "Target-Directory: [optional][The suggested directory to get into]\n" "Target-Config: [optional][A configuration to build upon grabbing]\n" ); enum options { OPTS (OPT_ENUM) }; static struct opt_desc opts[] = { OPTS (OPT_DESC) {-1, 0, 0, 0, 0} }; int arch_cmd_grab (t_uchar * program_name, int argc, char * argv[]) { int o; struct opt_parsed * option; t_uchar * errname; t_uchar * location; struct arch_build_config_params config_params = {0, }; errname = "grab"; safe_buffer_fd (1, 0, O_WRONLY, 0); option = 0; while (1) { o = opt_standard (lim_use_must_malloc, &option, opts, &argc, argv, program_name, usage, libarch_version_string, arch_cmd_grab_help, opt_help_msg, opt_long_help, opt_version); if (o == opt_none) break; switch (o) { default: safe_printfmt (2, "unhandled option `%s'\n", option->opt_string); panic ("internal error parsing arguments"); usage_error: opt_usage (2, argv[0], program_name, usage, 1); exit (1); /* bogus_arg: */ safe_printfmt (2, "ill-formed argument for `%s' (`%s')\n", option->opt_string, option->arg_string); goto usage_error; case opt_no_pristines: { config_params.no_pristines = 1; break; } case opt_hardlinks: { config_params.hardlinks = 1; break; } case opt_library: { config_params.library = 1; break; } case opt_sparse: { config_params.sparse = 1; break; } case opt_release_id: { config_params.release_id = 1; break; } } } if (argc != 2) goto usage_error; location = str_save (0, argv[1]); arch_grab (1, location, &config_params); lim_free (0, location); return 0; } void arch_grab (int chatter_fd, t_uchar * location, struct arch_build_config_params* config_params) { t_uchar * publication = NULL; t_uchar * archive_name = NULL; t_uchar * archive_location = NULL; t_uchar * target_revision = NULL; t_uchar * target_directory = NULL; t_uchar * target_config = NULL; t_uchar * current_directory = NULL; t_uchar * target_full_directory = NULL; t_uchar * tmp_tail; t_uchar * tmp_directory = NULL; t_uchar * line = NULL; t_uchar * colon = NULL; t_uchar * eol = NULL; t_uchar * name = NULL; t_uchar * value = NULL; t_uchar * final_revision; t_uchar * uri = NULL; t_uchar * path = NULL; struct arch_pfs_session * session; path = location + remote_path_off (location); if (path) { /************************************************************ * grab from http location */ path = str_chr_index(path, '/'); if (path) { uri = str_save_n (0, location, path - location); path = str_save (0, path); } else { path = str_save (0, "/"); uri = str_save (0, location); } session = arch_pfs_connect ( uri, 0); publication = arch_pfs_file_contents(session, path, 0); } else { /************************************************************ * try local file system */ publication = file_contents (location); } if (publication == NULL) { safe_printfmt (2, "could not obtain publication data from %s.\n", location); return; } /**************************************************************** * parse the publication */ line = publication; while (*line) { while (*line == '\n') ++line; /************************************************************ * split the line up */ colon = str_chr_index (line, ':'); eol = str_chr_index (line, '\n'); if (!eol) eol = line + str_length (line); if (!colon || colon > eol) { line = eol; continue; } name = str_save_n (0, line, colon - line); value = str_save_n (0, colon + 1, eol - colon - 1); name = trim_surrounding_ws (name); value = trim_surrounding_ws (value); /************************************************************ * save any useful values */ if (str_cmp (name, "Archive-Name") == 0 && !archive_name) archive_name = value; else if (str_cmp (name, "Archive-Location") == 0 && !archive_location) archive_location = value; else if (str_cmp (name, "Target-Revision") == 0 && !target_revision) target_revision = value; else if (str_cmp (name, "Target-Directory") == 0 && !target_directory) target_directory = value; else if (str_cmp (name, "Target-Config") == 0 && !target_config) target_config = value; else lim_free (0, value); lim_free (0, name); line = eol; } lim_free (0, publication); if (!archive_name || !archive_location || !target_revision) { safe_printfmt (2, "Invalid publication file at %s.\n", location); return; } else { struct arch_archive *archive; if (target_directory) { /******************************************************** * perform sanity check on target dir * we don't want to allow any old target dir (to avoid * "/etc" or "../../../etc" exploits), so we only take * the tail of the specified target directory. */ t_uchar * target_directory_file = file_name_from_directory (0, target_directory); t_uchar * target_directory_tail = file_name_tail (0, target_directory_file); lim_free (0, target_directory); lim_free (0, target_directory_file); if (str_length (target_directory_tail) == 0) { lim_free (0, target_directory_tail); target_directory_tail = NULL; } target_directory = target_directory_tail; } if (target_directory == NULL) { target_directory = str_save (0, target_revision); } current_directory = safe_current_working_directory (); tmp_tail = str_alloc_cat_many (0, ",,grab.", target_directory, str_end); target_full_directory = file_name_in_vicinity (0, current_directory, target_directory); tmp_directory = talloc_tmp_file_name (talloc_context, current_directory, tmp_tail); rmrf_file (tmp_directory); safe_mkdir (tmp_directory, 0777); safe_printfmt(1, "Grabbing: %s/%s\n", archive_name, archive_location); safe_printfmt(1, "Source: %s, Dest: %s\n", archive_location, target_directory); if (target_config) safe_printfmt(1, "Config: %s\n", target_config); /******************** * Time to grab the archive */ archive = arch_archive_connect_branch (archive_location, NULL); final_revision = find_latest_revision (archive, target_revision); arch_build_revision (tmp_directory, archive, archive->official_name, final_revision, NULL); arch_archive_close (archive); safe_rename (tmp_directory, target_full_directory); /**************** * Time to config, if we should */ if (target_config) { arch_project_tree_t * tree; t_uchar *tree_version; t_uchar *def_archive = NULL; if (!arch_valid_config_name (target_config)) { safe_printfmt(2, "grab: given invalid config name '%s'\n", target_config); exit(1); } tree = arch_project_tree_new (talloc_context, target_full_directory); if (! tree->root) panic("grab: Not in a valid tree root"); tree_version = arch_tree_version (tree->root); if (tree_version) { def_archive = arch_parse_package_name (arch_ret_archive, 0, tree_version); lim_free(0, tree_version); } arch_build_config (tree, target_config, config_params , def_archive); arch_project_tree_delete (tree); lim_free (0, def_archive); } } lim_free (0, archive_name); lim_free (0, archive_location); lim_free (0, target_revision); lim_free (0, target_directory); lim_free (0, current_directory); lim_free (0, target_full_directory); talloc_free (tmp_directory); lim_free (0, tmp_tail); } static t_uchar * find_latest_revision (struct arch_archive *archive, t_uchar *revision) { t_uchar *oldrevision; t_uchar *workrevision; if (! arch_valid_package_name (revision, arch_no_archive, arch_req_package, 1)) { panic("Invalid Package"); } workrevision = str_save(0, revision); if (! arch_valid_package_name (workrevision, arch_no_archive, arch_req_version, 1)) { rel_table versions = 0; versions = arch_archive_versions (archive, revision); if (!versions) { panic("No versions for that package exist in the archive"); } arch_sort_table_by_name_field (1, versions, 0); oldrevision = workrevision; workrevision = str_save(0, versions[0][0]); lim_free(0, oldrevision); rel_free_table (versions); } if (! arch_valid_package_name (workrevision, arch_no_archive, arch_req_patch_level, 1)) { oldrevision = workrevision; workrevision = str_alloc_cat_many (0, workrevision, "--", arch_archive_latest_revision(archive, workrevision, 0), str_end); lim_free(0, oldrevision); } return workrevision; } static size_t remote_path_off (t_uchar * location) { if (!str_casecmp_prefix ("http:/", location)) return sizeof ("http:/"); return 0; } /* tag: Mark Thomas Tue Jul 29 22:32:52 BST 2003 (cmd-grab.c) */