/* configs.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/errno.h" #include "hackerlab/os/errno-to-string.h" #include "hackerlab/char/char-class.h" #include "hackerlab/char/str.h" #include "hackerlab/char/pika-escaping-utils.h" #include "hackerlab/fs/file-names.h" #include "hackerlab/fs/cwd.h" #include "hackerlab/mem/talloc.h" #include "hackerlab/vu/safe.h" #include "hackerlab/arrays/ar.h" #include "libfsutils/ensure-dir.h" #include "libfsutils/safety.h" #include "libfsutils/tmp-files.h" #include "libfsutils/read-line.h" #include "libfsutils/rmrf.h" #include "libarch/arch.h" #include "libarch/configs.h" #include "libarch/namespace.h" #include "libarch/pfs.h" #include "libarch/project-tree.h" #include "libarch/patch-logs.h" #include "commands/cmd.h" #include "commands/get.h" #include "po/gettext.h" /* __STDC__ prototypes for static functions */ static rel_table arch_read_config_simple (t_uchar const * const config_path); static t_uchar * arch_config_output_path (t_uchar * tree_root, t_uchar * target_dir); /** * \brief determine the path for a config file or for a config line, taking a potential ree_root * \return t_uchar * the fully qualified path of the file */ t_uchar * arch_config_path (arch_project_tree_t * tree, t_uchar * config_name) { t_uchar * rel = 0; t_uchar * answer = 0; t_uchar * tree_rel = 0; invariant (arch_valid_config_name (config_name)); tree_rel = file_name_in_vicinity (0, tree->root, "configs"); tree_rel = str_replace (tree_rel, file_name_in_vicinity (0, tree_rel, config_name)); rel = file_name_in_vicinity (0, tree->root, config_name); if (! safe_access(tree_rel, F_OK)) /* tree/configs/ */ answer = str_save (0, tree_rel); else if (! safe_access(rel, F_OK)) /* tree/ */ answer = str_save (0, rel); else /* root, config_in[x][0]); safe_chdir (subtree_path_spec); subtree_path = safe_current_working_directory (); safe_fchdir (here_fd); subtree = arch_project_tree_new (NULL, subtree_path); invariant (subtree->root && !str_cmp (subtree->root, subtree_path)); archive = arch_parse_package_name (arch_ret_archive, 0, config_in[x][1]); if (arch_valid_package_name (config_in[x][1], arch_maybe_archive, arch_req_version, 1)) version = arch_parse_package_name (arch_ret_package_version, 0, config_in[x][1]); else { t_uchar * package = 0; package = arch_parse_package_name (arch_ret_package, 0, config_in[x][1]); version = arch_latest_logged_version (subtree->root, archive, package); lim_free (0, package); } level = arch_highest_patch_level (subtree->root, archive, version); if (level) { revision = str_alloc_cat_many (0, version, "--", level, str_end); fqr = arch_fully_qualify (archive, revision); } else fqr = arch_fully_qualify (archive, version); rel_add_records (&answer, rel_make_record (config_in[x][0], fqr, 0), 0); lim_free (0, subtree_path_spec); lim_free (0, subtree_path); lim_free (0, archive); lim_free (0, version); arch_project_tree_delete (subtree); lim_free (0, level); lim_free (0, revision); lim_free (0, fqr); } safe_close (here_fd); return answer; } int arch_begin_new_config (arch_project_tree_t * tree, t_uchar * name, int force) { int ign; t_uchar * config_file = 0; t_uchar * config_dir = 0; t_uchar * name_tail = 0; t_uchar * tmp_tail = 0; t_uchar * config_tmp = 0; int answer; invariant (arch_valid_config_name (name)); config_file = arch_config_path (tree, name); config_dir = file_name_directory_file (0, config_file); name_tail = file_name_tail (0, name); tmp_tail = str_alloc_cat (0, ",,", name_tail); config_tmp = file_name_in_vicinity (0, config_dir, tmp_tail); if (!force && !safe_access (config_file, F_OK)) { safe_printfmt (2, "arch_begin_new_config: config already exists (%s)\n", name); exit (2); } ensure_directory_exists (config_dir); vu_unlink (&ign, config_tmp); answer = safe_open (config_tmp, O_WRONLY | O_CREAT | O_EXCL, 0666); lim_free (0, config_file); lim_free (0, config_dir); lim_free (0, name_tail); lim_free (0, tmp_tail); lim_free (0, config_tmp); return answer; } void arch_finish_new_config (int fd, arch_project_tree_t * tree, t_uchar * name, int force) { t_uchar * config_file = 0; t_uchar * config_dir = 0; t_uchar * name_tail = 0; t_uchar * tmp_tail = 0; t_uchar * config_tmp = 0; invariant (arch_valid_config_name (name)); config_file = arch_config_path (tree, name); config_dir = file_name_directory_file (0, config_file); name_tail = file_name_tail (0, name); tmp_tail = str_alloc_cat (0, ",,", name_tail); config_tmp = file_name_in_vicinity (0, config_dir, tmp_tail); safe_close (fd); if (!force && !safe_access (config_file, F_OK)) { safe_printfmt (2, "arch_begin_new_config: config already exists (%s)\n", name); exit (2); } safe_rename (config_tmp, config_file); lim_free (0, config_file); lim_free (0, config_dir); lim_free (0, name_tail); lim_free (0, tmp_tail); lim_free (0, config_tmp); } /** * \brief get a fully qualified target dir path for a config */ t_uchar * arch_config_output_path (t_uchar * tree_root, t_uchar * target_dir) { /* FIXME: (SECURITY) This allows one to write arbitrary paths from * configs that may be on *remote* filesystems. */ if (tree_root) return file_name_in_vicinity (0, tree_root, target_dir); else return arch_abs_path (target_dir); } void arch_build_config (arch_project_tree_t * tree, t_uchar * config_name, struct arch_build_config_params * params, t_uchar * default_archive) { rel_table config = 0; int x; if (tree->root) config = arch_read_config (tree, config_name); else config = arch_read_config_simple (config_name); /* Ensure a shallowist to deepest sort */ rel_sort_table_by_field (0, config, 0); /* move conflicting dirs and files */ for (x = 0; x < rel_n_records (config); ++x) { int errn; t_uchar * path = 0; t_uchar * path_dir = 0; t_uchar * path_tail = 0; t_uchar * saved_tail = 0; t_uchar * saved_path = 0; path = arch_config_output_path (tree->root, config[x][0]); path_dir = file_name_directory_file (0, path); path_tail = file_name_tail (0, path); saved_tail = str_alloc_cat (0, "++saved.", path_tail); saved_path = talloc_tmp_file_name (talloc_context, path_dir, saved_tail); if (vu_rename (&errn, path, saved_path) && (errn != ENOENT)) { safe_printfmt (2, "build-config: unable to set aside conflicting directory %s\n", path); exit (2); } lim_free (0, path); lim_free (0, path_dir); lim_free (0, path_tail); lim_free (0, saved_tail); talloc_free (saved_path); } /* build desired trees. */ for (x = 0; x < rel_n_records (config); ++x) { t_uchar * path_to_subtree = 0; t_uchar * path_to_subtree_dir = 0; t_uchar * revspec; int status; path_to_subtree = arch_config_output_path (tree->root, config[x][0]); path_to_subtree_dir = file_name_directory_file (0, path_to_subtree); ensure_directory_exists (path_to_subtree_dir); revspec = config[x][1]; { char ** argv = 0; /* call `get' -- build an argv for it */ ar_push_char_star (&argv, "get"); if (default_archive) { ar_push_char_star (&argv, "-A"); ar_push_char_star (&argv, default_archive); } if (params->no_pristines) { ar_push_char_star (&argv, "--no-pristine"); } if (params->hardlinks) { ar_push_char_star (&argv, "--link"); } if (params->library) { ar_push_char_star (&argv, "--library"); } if (params->sparse) { ar_push_char_star (&argv, "--sparse"); } if (params->no_greedy_add) { ar_push_char_star (&argv, "--no-greedy-add"); } ar_push_char_star (&argv, revspec); ar_push_char_star (&argv, path_to_subtree); ar_push_char_star (&argv, 0); status = arch_cmd_get ("get", (ar_size_char_star (argv) - 1), argv); ar_free_char_star (&argv); } if (status) { safe_printfmt (2, "unable to build %s at %s\n", revspec, path_to_subtree); exit (status); } lim_free (0, path_to_subtree); lim_free (0, path_to_subtree_dir); } if (tree->root && params->release_id) { int errn; t_uchar * tree_version = 0; t_uchar * tree_revision = 0; t_uchar * release_id_file = 0; rel_table snapped_config = 0; int out_fd; tree_version = arch_tree_version (tree->root); if (tree_version) { t_uchar * archive = 0; t_uchar * version = 0; t_uchar * level = 0; archive = arch_parse_package_name (arch_ret_archive, 0, tree_version); version = arch_parse_package_name (arch_ret_non_archive, 0, tree_version); level = arch_highest_patch_level (tree->root, archive, version); tree_revision = str_alloc_cat_many (0, tree_version, "--", level, str_end); lim_free (0, archive); lim_free (0, version); lim_free (0, level); } snapped_config = arch_config_from_tree (tree, config); release_id_file = file_name_in_vicinity (0, tree->root, "=RELEASE-ID"); invariant (!vu_unlink (&errn, release_id_file) || (errn == ENOENT)); out_fd = safe_open (release_id_file, O_WRONLY | O_CREAT | O_EXCL, 0666); safe_printfmt (out_fd, "# automatically generated release id (by baz build-config)\n"); safe_printfmt (out_fd, "#\n"); safe_printfmt (out_fd, "\n"); safe_printfmt (out_fd, "%s(%s)\n", (tree_revision ? tree_revision : (t_uchar *)""), config_name); safe_printfmt (out_fd, "\n"); rel_print_pika_escape_iso8859_1_table (out_fd, arch_escape_classes, snapped_config); safe_close (out_fd); lim_free (0, tree_version); lim_free (0, tree_revision); lim_free (0, release_id_file); rel_free_table (snapped_config); } rel_free_table (config); } /* tag: Tom Lord Fri May 30 00:05:24 2003 (configs.c) */