gboolean ostree_builtin_checksum (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; gboolean ret = FALSE; g_autoptr(GFile) f = NULL; AsyncChecksumData data = { 0, }; context = g_option_context_new ("PATH - Checksum a file or directory"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NO_REPO, NULL, cancellable, error)) goto out; if (argc > 1) f = g_file_new_for_path (argv[1]); else { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "A filename must be given"); goto out; } data.loop = g_main_loop_new (NULL, FALSE); data.error = error; ostree_checksum_file_async (f, OSTREE_OBJECT_TYPE_FILE, G_PRIORITY_DEFAULT, cancellable, on_checksum_received, &data); g_main_loop_run (data.loop); ret = TRUE; out: if (data.loop) g_main_loop_unref (data.loop); return ret; }
gboolean ot_remote_builtin_delete (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; const char *remote_name; gboolean ret = FALSE; context = g_option_context_new ("NAME - Delete a remote repository"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "NAME must be specified", error); goto out; } remote_name = argv[1]; if (!ostree_repo_remote_change (repo, NULL, opt_if_exists ? OSTREE_REPO_REMOTE_CHANGE_DELETE_IF_EXISTS : OSTREE_REPO_REMOTE_CHANGE_DELETE, remote_name, NULL, NULL, cancellable, error)) goto out; ret = TRUE; out: g_option_context_free (context); return ret; }
gboolean ot_remote_builtin_delete_cookie (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(OstreeRepo) repo = NULL; g_autoptr(GOptionContext) context = g_option_context_new ("NAME DOMAIN PATH COOKIE_NAME- Remote one cookie from remote"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) return FALSE; if (argc < 5) { ot_util_usage_error (context, "NAME, DOMAIN, PATH and COOKIE_NAME must be specified", error); return FALSE; } const char *remote_name = argv[1]; const char *domain = argv[2]; const char *path = argv[3]; const char *cookie_name = argv[4]; g_autofree char *cookie_file = g_strdup_printf ("%s.cookies.txt", remote_name); if (!ot_delete_cookie_at (ostree_repo_get_dfd (repo), cookie_file, domain, path, cookie_name, error)) return FALSE; return TRUE; }
gboolean ot_remote_builtin_show_url (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeRepo) repo = NULL; const char *remote_name; g_autofree char *remote_url = NULL; gboolean ret = FALSE; context = g_option_context_new ("NAME - Show remote repository URL"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "NAME must be specified", error); goto out; } remote_name = argv[1]; if (ostree_repo_remote_get_url (repo, remote_name, &remote_url, error)) { g_print ("%s\n", remote_url); ret = TRUE; } out: return ret; }
gboolean ostree_builtin_summary (int argc, char **argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GOptionContext *context; gs_unref_object OstreeRepo *repo = NULL; context = g_option_context_new ("Manage summary metadata"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (opt_update) { if (!ostree_ensure_repo_writable (repo, error)) goto out; if (!ostree_repo_regenerate_summary (repo, NULL, cancellable, error)) goto out; } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No option specified; use -u to update summary"); goto out; } ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
static gboolean ot_static_delta_builtin_list (int argc, char **argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_autoptr(GPtrArray) delta_names = NULL; guint i; GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; context = g_option_context_new ("LIST - list static delta files"); if (!ostree_option_context_parse (context, list_options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (!ostree_repo_list_static_delta_names (repo, &delta_names, cancellable, error)) goto out; if (delta_names->len == 0) { g_print ("(No static deltas)\n"); } else { for (i = 0; i < delta_names->len; i++) { g_print ("%s\n", (char*)delta_names->pdata[i]); } } ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ostree_builtin_reset (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeRepo) repo = NULL; g_autoptr(GHashTable) known_refs = NULL; gboolean ret = FALSE; const char *ref; const char *target = NULL; g_autofree char *checksum = NULL; /* FIXME: Add support for collection–refs. */ context = g_option_context_new ("REF COMMIT"); if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error)) goto out; if (!ostree_ensure_repo_writable (repo, error)) goto out; if (argc <= 2) { ot_util_usage_error (context, "A REF and COMMIT argument is required", error); goto out; } ref = argv[1]; target = argv[2]; if (!ostree_repo_list_refs (repo, NULL, &known_refs, cancellable, error)) goto out; if (!g_hash_table_contains (known_refs, ref)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR, "Invalid ref '%s'", ref); goto out; } if (!ostree_repo_resolve_rev (repo, target, FALSE, &checksum, error)) goto out; if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) goto out; ostree_repo_transaction_set_ref (repo, NULL, ref, checksum); if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) goto out; ret = TRUE; out: if (repo) ostree_repo_abort_transaction (repo, cancellable, NULL); return ret; }
gboolean ostree_builtin_refs (int argc, char **argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GOptionContext *context; gs_unref_object OstreeRepo *repo = NULL; const char *refspec_prefix = NULL; gs_unref_hashtable GHashTable *refs = NULL; GHashTableIter hashiter; gpointer hashkey, hashvalue; context = g_option_context_new ("[PREFIX] - List refs"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc >= 2) refspec_prefix = argv[1]; if (!ostree_repo_list_refs (repo, refspec_prefix, &refs, cancellable, error)) goto out; if (!opt_delete) { g_hash_table_iter_init (&hashiter, refs); while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue)) { const char *ref = hashkey; g_print ("%s\n", ref); } } else { g_hash_table_iter_init (&hashiter, refs); while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue)) { const char *refspec = hashkey; gs_free char *remote = NULL; gs_free char *ref = NULL; if (!ostree_parse_refspec (refspec, &remote, &ref, error)) goto out; if (!ostree_repo_set_ref_immediate (repo, remote, ref, NULL, cancellable, error)) goto out; } } ret = TRUE; out: if (context) g_option_context_free (context); if (repo) ostree_repo_abort_transaction (repo, cancellable, NULL); return ret; }
gboolean ostree_builtin_checkout (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; gboolean ret = FALSE; const char *commit; const char *destination; g_autofree char *resolved_commit = NULL; context = g_option_context_new ("COMMIT [DESTINATION] - Check out a commit into a filesystem tree"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (opt_disable_fsync) ostree_repo_set_disable_fsync (repo, TRUE); if (argc < 2) { gchar *help = g_option_context_get_help (context, TRUE, NULL); g_printerr ("%s\n", help); g_free (help); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "COMMIT must be specified"); goto out; } if (opt_from_stdin || opt_from_file) { destination = argv[1]; if (!process_many_checkouts (repo, destination, cancellable, error)) goto out; } else { commit = argv[1]; if (argc < 3) destination = commit; else destination = argv[2]; if (!ostree_repo_resolve_rev (repo, commit, FALSE, &resolved_commit, error)) goto out; if (!process_one_checkout (repo, resolved_commit, opt_subpath, destination, cancellable, error)) goto out; } ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ot_remote_builtin_gpg_import (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; g_autoptr(GInputStream) source_stream = NULL; const char *remote_name; const char * const *key_ids; guint imported = 0; gboolean ret = FALSE; context = g_option_context_new ("NAME [KEY-ID...] - Import GPG keys"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "NAME must be specified", error); goto out; } if (opt_stdin && opt_keyrings != NULL) { ot_util_usage_error (context, "--keyring and --stdin are mutually exclusive", error); goto out; } remote_name = argv[1]; key_ids = (argc > 2) ? (const char * const *) argv + 2 : NULL; if (!open_source_stream (&source_stream, cancellable, error)) goto out; if (!ostree_repo_remote_gpg_import (repo, remote_name, source_stream, key_ids, &imported, cancellable, error)) goto out; /* XXX If we ever add internationalization, use ngettext() here. */ g_print ("Imported %u GPG key%s to remote \"%s\"\n", imported, (imported == 1) ? "" : "s", remote_name); ret = TRUE; out: g_option_context_free (context); return ret; }
gboolean ot_remote_builtin_list (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; g_auto(GStrv) remotes = NULL; guint ii, n_remotes = 0; gboolean ret = FALSE; context = g_option_context_new ("- List remote repository names"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; remotes = ostree_repo_remote_list (repo, &n_remotes); if (opt_show_urls) { int max_length = 0; for (ii = 0; ii < n_remotes; ii++) max_length = MAX (max_length, strlen (remotes[ii])); for (ii = 0; ii < n_remotes; ii++) { g_autofree char *remote_url = NULL; if (!ostree_repo_remote_get_url (repo, remotes[ii], &remote_url, error)) goto out; g_print ("%-*s %s\n", max_length, remotes[ii], remote_url); } } else { for (ii = 0; ii < n_remotes; ii++) g_print ("%s\n", remotes[ii]); } ret = TRUE; out: g_option_context_free (context); return ret; }
gboolean ostree_builtin_cat (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; gs_unref_object OstreeRepo *repo = NULL; gboolean ret = FALSE; int i; const char *rev; gs_unref_object GOutputStream *stdout_stream = NULL; gs_unref_object GFile *root = NULL; gs_unref_object GFile *f = NULL; context = g_option_context_new ("COMMIT PATH... - Concatenate contents of files"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc <= 2) { ot_util_usage_error (context, "A COMMIT and at least one PATH argument are required", error); goto out; } rev = argv[1]; if (!ostree_repo_read_commit (repo, rev, &root, NULL, NULL, error)) goto out; stdout_stream = g_unix_output_stream_new (1, FALSE); for (i = 2; i < argc; i++) { g_clear_object (&f); f = g_file_resolve_relative_path (root, argv[i]); if (!cat_one_file (f, stdout_stream, cancellable, error)) goto out; } ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
static gboolean ot_static_delta_builtin_apply_offline (int argc, char **argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; const char *patharg; g_autoptr(GFile) path = NULL; GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; context = g_option_context_new ("DELTA - Apply static delta file"); if (!ostree_option_context_parse (context, apply_offline_options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (!ostree_ensure_repo_writable (repo, error)) goto out; if (argc < 3) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "PATH must be specified"); goto out; } patharg = argv[2]; path = g_file_new_for_path (patharg); if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) goto out; if (!ostree_repo_static_delta_execute_offline (repo, path, TRUE, cancellable, error)) goto out; if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error)) goto out; ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ot_remote_builtin_list_cookies (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { g_autoptr(OstreeRepo) repo = NULL; g_autoptr(GOptionContext) context = g_option_context_new ("NAME"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, invocation, &repo, cancellable, error)) return FALSE; if (argc < 2) { ot_util_usage_error (context, "NAME must be specified", error); return FALSE; } const char *remote_name = argv[1]; g_autofree char *cookie_file = g_strdup_printf ("%s.cookies.txt", remote_name); if (!ot_list_cookies_at (ostree_repo_get_dfd (repo), cookie_file, error)) return FALSE; return TRUE; }
gboolean ostree_builtin_init (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeRepo) repo = NULL; gboolean ret = FALSE; OstreeRepoMode mode; context = g_option_context_new ("- Initialize a new empty repository"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NO_CHECK, &repo, cancellable, error)) goto out; if (!ostree_repo_mode_from_string (opt_mode, &mode, error)) goto out; if (!ostree_repo_create (repo, mode, NULL, error)) goto out; ret = TRUE; out: return ret; }
gboolean ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; gboolean ret = FALSE; gboolean skip_commit = FALSE; g_autoptr(GFile) object_to_commit = NULL; g_autofree char *parent = NULL; g_autofree char *commit_checksum = NULL; g_autoptr(GFile) root = NULL; g_autoptr(GVariant) metadata = NULL; g_autoptr(GVariant) detached_metadata = NULL; glnx_unref_object OstreeMutableTree *mtree = NULL; g_autofree char *tree_type = NULL; g_autoptr(GHashTable) mode_adds = NULL; OstreeRepoCommitModifierFlags flags = 0; OstreeRepoCommitModifier *modifier = NULL; OstreeRepoTransactionStats stats; context = g_option_context_new ("[PATH] - Commit a new revision"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (!ostree_ensure_repo_writable (repo, error)) goto out; if (opt_statoverride_file) { if (!parse_statoverride_file (&mode_adds, cancellable, error)) goto out; } if (opt_metadata_strings) { if (!parse_keyvalue_strings (opt_metadata_strings, &metadata, error)) goto out; } if (opt_detached_metadata_strings) { if (!parse_keyvalue_strings (opt_detached_metadata_strings, &detached_metadata, error)) goto out; } if (!opt_branch) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "A branch must be specified with --branch"); goto out; } if (opt_no_xattrs) flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS; if (opt_generate_sizes) flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES; if (opt_disable_fsync) ostree_repo_set_disable_fsync (repo, TRUE); if (flags != 0 || opt_owner_uid >= 0 || opt_owner_gid >= 0 || opt_statoverride_file != NULL || opt_no_xattrs) { modifier = ostree_repo_commit_modifier_new (flags, commit_filter, mode_adds, NULL); } if (!ostree_repo_resolve_rev (repo, opt_branch, TRUE, &parent, error)) goto out; if (!opt_subject && !opt_body) { if (!commit_editor (repo, opt_branch, &opt_subject, &opt_body, cancellable, error)) goto out; } if (!opt_subject) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "A subject must be specified with --subject"); goto out; } if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) goto out; if (opt_link_checkout_speedup && !ostree_repo_scan_hardlinks (repo, cancellable, error)) goto out; mtree = ostree_mutable_tree_new (); if (argc <= 1 && (opt_trees == NULL || opt_trees[0] == NULL)) { char *current_dir = g_get_current_dir (); object_to_commit = g_file_new_for_path (current_dir); g_free (current_dir); if (!ostree_repo_write_directory_to_mtree (repo, object_to_commit, mtree, modifier, cancellable, error)) goto out; } else if (opt_trees != NULL) { const char *const*tree_iter; const char *tree; const char *eq; for (tree_iter = (const char *const*)opt_trees; *tree_iter; tree_iter++) { tree = *tree_iter; eq = strchr (tree, '='); if (!eq) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing type in tree specification '%s'", tree); goto out; } g_free (tree_type); tree_type = g_strndup (tree, eq - tree); tree = eq + 1; g_clear_object (&object_to_commit); if (strcmp (tree_type, "dir") == 0) { object_to_commit = g_file_new_for_path (tree); if (!ostree_repo_write_directory_to_mtree (repo, object_to_commit, mtree, modifier, cancellable, error)) goto out; } else if (strcmp (tree_type, "tar") == 0) { object_to_commit = g_file_new_for_path (tree); if (!ostree_repo_write_archive_to_mtree (repo, object_to_commit, mtree, modifier, opt_tar_autocreate_parents, cancellable, error)) goto out; } else if (strcmp (tree_type, "ref") == 0) { if (!ostree_repo_read_commit (repo, tree, &object_to_commit, NULL, cancellable, error)) goto out; if (!ostree_repo_write_directory_to_mtree (repo, object_to_commit, mtree, modifier, cancellable, error)) goto out; } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid tree type specification '%s'", tree_type); goto out; } } } else { g_assert (argc > 1); object_to_commit = g_file_new_for_path (argv[1]); if (!ostree_repo_write_directory_to_mtree (repo, object_to_commit, mtree, modifier, cancellable, error)) goto out; } if (mode_adds && g_hash_table_size (mode_adds) > 0) { GHashTableIter hash_iter; gpointer key, value; g_hash_table_iter_init (&hash_iter, mode_adds); while (g_hash_table_iter_next (&hash_iter, &key, &value)) { g_printerr ("Unmatched statoverride path: %s\n", (char*)key); } g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unmatched statoverride paths"); goto out; } if (!ostree_repo_write_mtree (repo, mtree, &root, cancellable, error)) goto out; if (opt_skip_if_unchanged && parent) { g_autoptr(GFile) parent_root; if (!ostree_repo_read_commit (repo, parent, &parent_root, NULL, cancellable, error)) goto out; if (g_file_equal (root, parent_root)) skip_commit = TRUE; } if (!skip_commit) { gboolean update_summary; if (!ostree_repo_write_commit (repo, parent, opt_subject, opt_body, metadata, OSTREE_REPO_FILE (root), &commit_checksum, cancellable, error)) goto out; if (detached_metadata) { if (!ostree_repo_write_commit_detached_metadata (repo, commit_checksum, detached_metadata, cancellable, error)) goto out; } if (opt_key_ids) { char **iter; for (iter = opt_key_ids; iter && *iter; iter++) { const char *keyid = *iter; if (!ostree_repo_sign_commit (repo, commit_checksum, keyid, opt_gpg_homedir, cancellable, error)) goto out; } } ostree_repo_transaction_set_ref (repo, NULL, opt_branch, commit_checksum); if (!ostree_repo_commit_transaction (repo, &stats, cancellable, error)) goto out; /* The default for this option is FALSE, even for archive-z2 repos, * because ostree supports multiple processes committing to the same * repo (but different refs) concurrently, and in fact gnome-continuous * actually does this. In that context it's best to update the summary * explicitly instead of automatically here. */ if (!ot_keyfile_get_boolean_with_default (ostree_repo_get_config (repo), "core", "commit-update-summary", FALSE, &update_summary, error)) goto out; if (update_summary && !ostree_repo_regenerate_summary (repo, NULL, cancellable, error)) goto out; } else { commit_checksum = g_strdup (parent); } if (opt_table_output) { g_print ("Commit: %s\n", commit_checksum); g_print ("Metadata Total: %u\n", stats.metadata_objects_total); g_print ("Metadata Written: %u\n", stats.metadata_objects_written); g_print ("Content Total: %u\n", stats.content_objects_total); g_print ("Content Written: %u\n", stats.content_objects_written); g_print ("Content Bytes Written: %" G_GUINT64_FORMAT "\n", stats.content_bytes_written); } else { g_print ("%s\n", commit_checksum); } ret = TRUE; out: if (repo) ostree_repo_abort_transaction (repo, cancellable, NULL); if (context) g_option_context_free (context); if (modifier) ostree_repo_commit_modifier_unref (modifier); return ret; }
gboolean ostree_builtin_trivial_httpd (int argc, char **argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GOptionContext *context; const char *dirpath; OtTrivialHttpd appstruct = { 0, }; OtTrivialHttpd *app = &appstruct; glnx_unref_object SoupServer *server = NULL; g_autoptr(GFileMonitor) dirmon = NULL; context = g_option_context_new ("[DIR] - Simple webserver"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NO_REPO, NULL, cancellable, error)) goto out; if (argc > 1) dirpath = argv[1]; else dirpath = "."; app->root = g_file_new_for_path (dirpath); if (!(opt_random_500s_percentage >= 0 && opt_random_500s_percentage <= 99)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid --random-500s=%u", opt_random_500s_percentage); goto out; } if (opt_log) { GOutputStream *stream = NULL; if (g_strcmp0 (opt_log, "-") == 0) { if (opt_daemonize) { ot_util_usage_error (context, "Cannot use --log-file=- and --daemonize at the same time", error); goto out; } stream = G_OUTPUT_STREAM (g_unix_output_stream_new (STDOUT_FILENO, FALSE)); } else { g_autoptr(GFile) log_file; GFileOutputStream* log_stream; log_file = g_file_new_for_path (opt_log); log_stream = g_file_create (log_file, G_FILE_CREATE_PRIVATE, cancellable, error); if (!log_stream) goto out; stream = G_OUTPUT_STREAM (log_stream); } app->log = stream; } #if SOUP_CHECK_VERSION(2, 48, 0) server = soup_server_new (SOUP_SERVER_SERVER_HEADER, "ostree-httpd ", NULL); if (!soup_server_listen_all (server, opt_port, 0, error)) goto out; #else server = soup_server_new (SOUP_SERVER_PORT, opt_port, SOUP_SERVER_SERVER_HEADER, "ostree-httpd ", NULL); #endif soup_server_add_handler (server, NULL, httpd_callback, app, NULL); if (opt_port_file) { g_autofree char *portstr = NULL; #if SOUP_CHECK_VERSION(2, 48, 0) GSList *listeners = soup_server_get_listeners (server); g_autoptr(GSocket) listener = NULL; g_autoptr(GSocketAddress) addr = NULL; g_assert (listeners); listener = g_object_ref (listeners->data); g_slist_free (listeners); listeners = NULL; addr = g_socket_get_local_address (listener, error); if (!addr) goto out; g_assert (G_IS_INET_SOCKET_ADDRESS (addr)); portstr = g_strdup_printf ("%u\n", g_inet_socket_address_get_port ((GInetSocketAddress*)addr)); #else portstr = g_strdup_printf ("%u\n", soup_server_get_port (server)); #endif if (g_strcmp0 ("-", opt_port_file) == 0) { fputs (portstr, stdout); // not g_print - this must go to stdout, not a handler fflush (stdout); } else if (!g_file_set_contents (opt_port_file, portstr, strlen (portstr), error)) goto out; } #if !SOUP_CHECK_VERSION(2, 48, 0) soup_server_run_async (server); #endif if (opt_daemonize) { pid_t pid = fork(); if (pid == -1) { int errsv = errno; g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errsv), g_strerror (errsv)); goto out; } else if (pid > 0) { /* Parent */ _exit (0); } /* Child, continue */ /* Daemonising: close stdout/stderr so $() et al work on us */ fclose (stdout); fclose (stdin); } else { /* Since we're used for testing purposes, let's just do this by * default. This ensures we exit when our parent does. */ if (prctl (PR_SET_PDEATHSIG, SIGTERM) != 0) { if (errno != ENOSYS) { glnx_set_error_from_errno (error); goto out; } } } app->running = TRUE; if (opt_autoexit) { gboolean is_symlink = FALSE; g_autoptr(GFileInfo) info = NULL; info = g_file_query_info (app->root, G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error); if (!info) goto out; is_symlink = g_file_info_get_is_symlink (info); if (is_symlink) dirmon = g_file_monitor_file (app->root, 0, cancellable, error); else dirmon = g_file_monitor_directory (app->root, 0, cancellable, error); if (!dirmon) goto out; g_signal_connect (dirmon, "changed", G_CALLBACK (on_dir_changed), app); } { g_autofree gchar *path = g_file_get_path (app->root); httpd_log (app, "serving at root %s\n", path); } while (app->running) g_main_context_iteration (NULL, TRUE); ret = TRUE; out: g_clear_object (&app->root); g_clear_object (&app->log); if (context) g_option_context_free (context); return ret; }
gboolean ot_remote_builtin_add (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; const char *remote_name; const char *remote_url; char **iter; g_autofree char *target_name = NULL; g_autoptr(GFile) target_conf = NULL; g_autoptr(GVariantBuilder) optbuilder = NULL; gboolean ret = FALSE; context = g_option_context_new ("NAME [metalink=|mirrorlist=]URL [BRANCH...] - Add a remote repository"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc < 3) { ot_util_usage_error (context, "NAME and URL must be specified", error); goto out; } remote_name = argv[1]; remote_url = argv[2]; optbuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); if (argc > 3) { g_autoptr(GPtrArray) branchesp = g_ptr_array_new (); int i; for (i = 3; i < argc; i++) g_ptr_array_add (branchesp, argv[i]); g_ptr_array_add (branchesp, NULL); g_variant_builder_add (optbuilder, "{s@v}", "branches", g_variant_new_variant (g_variant_new_strv ((const char*const*)branchesp->pdata, -1))); } /* We could just make users use --set instead for this since it's a string, * but e.g. when mirrorlist support is added, it'll be kinda awkward to type: * --set=contenturl=mirrorlist=... */ if (opt_contenturl != NULL) g_variant_builder_add (optbuilder, "{s@v}", "contenturl", g_variant_new_variant (g_variant_new_string (opt_contenturl))); for (iter = opt_set; iter && *iter; iter++) { const char *keyvalue = *iter; g_autofree char *subkey = NULL; g_autofree char *subvalue = NULL; if (!ot_parse_keyvalue (keyvalue, &subkey, &subvalue, error)) goto out; g_variant_builder_add (optbuilder, "{s@v}", subkey, g_variant_new_variant (g_variant_new_string (subvalue))); } if (opt_no_gpg_verify) g_variant_builder_add (optbuilder, "{s@v}", "gpg-verify", g_variant_new_variant (g_variant_new_boolean (FALSE))); if (!ostree_repo_remote_change (repo, NULL, opt_if_not_exists ? OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS : OSTREE_REPO_REMOTE_CHANGE_ADD, remote_name, remote_url, g_variant_builder_end (optbuilder), cancellable, error)) goto out; /* This is just a convenience option and is not as flexible as the full * "ostree remote gpg-import" command. It imports all keys from a file, * which is likely the most common case. * * XXX Not sure this interacts well with if-not-exists since we don't * know whether the remote already existed. We import regardless. */ if (opt_gpg_import != NULL) { g_autoptr(GFile) file = NULL; g_autoptr(GInputStream) input_stream = NULL; guint imported = 0; file = g_file_new_for_path (opt_gpg_import); input_stream = (GInputStream *) g_file_read (file, cancellable, error); if (input_stream == NULL) goto out; if (!ostree_repo_remote_gpg_import (repo, remote_name, input_stream, NULL, &imported, cancellable, error)) goto out; /* XXX If we ever add internationalization, use ngettext() here. */ g_print ("Imported %u GPG key%s to remote \"%s\"\n", imported, (imported == 1) ? "" : "s", remote_name); } ret = TRUE; out: g_option_context_free (context); return ret; }
gboolean ostree_builtin_commit (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; gs_unref_object OstreeRepo *repo = NULL; gboolean ret = FALSE; gboolean skip_commit = FALSE; gs_unref_object GFile *arg = NULL; gs_free char *parent = NULL; gs_free char *commit_checksum = NULL; gs_unref_object GFile *root = NULL; gs_unref_variant GVariant *metadata = NULL; gs_unref_variant GVariant *detached_metadata = NULL; gs_unref_object OstreeMutableTree *mtree = NULL; gs_free char *tree_type = NULL; gs_unref_hashtable GHashTable *mode_adds = NULL; OstreeRepoCommitModifierFlags flags = 0; OstreeRepoCommitModifier *modifier = NULL; OstreeRepoTransactionStats stats; context = g_option_context_new ("[PATH] - Commit a new revision"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (opt_statoverride_file) { if (!parse_statoverride_file (&mode_adds, cancellable, error)) goto out; } if (opt_metadata_strings) { if (!parse_keyvalue_strings (opt_metadata_strings, &metadata, error)) goto out; } if (opt_detached_metadata_strings) { if (!parse_keyvalue_strings (opt_detached_metadata_strings, &detached_metadata, error)) goto out; } if (!opt_branch) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "A branch must be specified with --branch"); goto out; } if (opt_no_xattrs) flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS; if (opt_generate_sizes) flags |= OSTREE_REPO_COMMIT_MODIFIER_FLAGS_GENERATE_SIZES; if (opt_disable_fsync) ostree_repo_set_disable_fsync (repo, TRUE); if (flags != 0 || opt_owner_uid >= 0 || opt_owner_gid >= 0 || opt_statoverride_file != NULL || opt_no_xattrs) { modifier = ostree_repo_commit_modifier_new (flags, commit_filter, mode_adds, NULL); } if (!ostree_repo_resolve_rev (repo, opt_branch, TRUE, &parent, error)) goto out; if (!opt_subject && !opt_body) { if (!commit_editor (repo, opt_branch, &opt_subject, &opt_body, cancellable, error)) goto out; } if (!opt_subject) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "A subject must be specified with --subject"); goto out; } if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) goto out; if (opt_link_checkout_speedup && !ostree_repo_scan_hardlinks (repo, cancellable, error)) goto out; mtree = ostree_mutable_tree_new (); if (argc <= 1 && (opt_trees == NULL || opt_trees[0] == NULL)) { char *current_dir = g_get_current_dir (); arg = g_file_new_for_path (current_dir); g_free (current_dir); if (!ostree_repo_write_directory_to_mtree (repo, arg, mtree, modifier, cancellable, error)) goto out; } else if (opt_trees != NULL) { const char *const*tree_iter; const char *tree; const char *eq; for (tree_iter = (const char *const*)opt_trees; *tree_iter; tree_iter++) { tree = *tree_iter; eq = strchr (tree, '='); if (!eq) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing type in tree specification '%s'", tree); goto out; } g_free (tree_type); tree_type = g_strndup (tree, eq - tree); tree = eq + 1; g_clear_object (&arg); if (strcmp (tree_type, "dir") == 0) { arg = g_file_new_for_path (tree); if (!ostree_repo_write_directory_to_mtree (repo, arg, mtree, modifier, cancellable, error)) goto out; } else if (strcmp (tree_type, "tar") == 0) { arg = g_file_new_for_path (tree); if (!ostree_repo_write_archive_to_mtree (repo, arg, mtree, modifier, opt_tar_autocreate_parents, cancellable, error)) goto out; } else if (strcmp (tree_type, "ref") == 0) { if (!ostree_repo_read_commit (repo, tree, &arg, NULL, cancellable, error)) goto out; if (!ostree_repo_write_directory_to_mtree (repo, arg, mtree, modifier, cancellable, error)) goto out; } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid tree type specification '%s'", tree_type); goto out; } } } else { g_assert (argc > 1); arg = g_file_new_for_path (argv[1]); if (!ostree_repo_write_directory_to_mtree (repo, arg, mtree, modifier, cancellable, error)) goto out; } if (mode_adds && g_hash_table_size (mode_adds) > 0) { GHashTableIter hash_iter; gpointer key, value; g_hash_table_iter_init (&hash_iter, mode_adds); while (g_hash_table_iter_next (&hash_iter, &key, &value)) { g_printerr ("Unmatched statoverride path: %s\n", (char*)key); } g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unmatched statoverride paths"); goto out; } if (!ostree_repo_write_mtree (repo, mtree, &root, cancellable, error)) goto out; if (opt_skip_if_unchanged && parent) { gs_unref_object GFile *parent_root; if (!ostree_repo_read_commit (repo, parent, &parent_root, NULL, cancellable, error)) goto out; if (g_file_equal (root, parent_root)) skip_commit = TRUE; } if (!skip_commit) { if (!ostree_repo_write_commit (repo, parent, opt_subject, opt_body, metadata, OSTREE_REPO_FILE (root), &commit_checksum, cancellable, error)) goto out; if (detached_metadata) { if (!ostree_repo_write_commit_detached_metadata (repo, commit_checksum, detached_metadata, cancellable, error)) goto out; } #ifdef HAVE_GPGME if (opt_key_ids) { char **iter; for (iter = opt_key_ids; iter && *iter; iter++) { const char *keyid = *iter; if (!ostree_repo_sign_commit (repo, commit_checksum, keyid, opt_gpg_homedir, cancellable, error)) goto out; } } #endif ostree_repo_transaction_set_ref (repo, NULL, opt_branch, commit_checksum); if (!ostree_repo_commit_transaction (repo, &stats, cancellable, error)) goto out; } else { commit_checksum = g_strdup (parent); } if (opt_table_output) { g_print ("Commit: %s\n", commit_checksum); g_print ("Metadata Total: %u\n", stats.metadata_objects_total); g_print ("Metadata Written: %u\n", stats.metadata_objects_written); g_print ("Content Total: %u\n", stats.content_objects_total); g_print ("Content Written: %u\n", stats.content_objects_written); g_print ("Content Bytes Written: %" G_GUINT64_FORMAT "\n", stats.content_bytes_written); } else { g_print ("%s\n", commit_checksum); } ret = TRUE; out: if (repo) ostree_repo_abort_transaction (repo, cancellable, NULL); if (context) g_option_context_free (context); if (modifier) ostree_repo_commit_modifier_unref (modifier); return ret; }
gboolean ostree_builtin_prune (int argc, char **argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; g_autofree char *formatted_freed_size = NULL; OstreeRepoPruneFlags pruneflags = 0; gint n_objects_total; gint n_objects_pruned; guint64 objsize_total; context = g_option_context_new ("- Search for unreachable objects"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (!opt_no_prune && !ostree_ensure_repo_writable (repo, error)) goto out; if (opt_delete_commit) { if (opt_no_prune) { ot_util_usage_error (context, "Cannot specify both --delete-commit and --no-prune", error); goto out; } if (opt_static_deltas_only) { if(!ostree_repo_prune_static_deltas (repo, opt_delete_commit, cancellable, error)) goto out; } else if (!delete_commit (repo, opt_delete_commit, cancellable, error)) goto out; } if (opt_keep_younger_than) { if (opt_no_prune) { ot_util_usage_error (context, "Cannot specify both --keep-younger-than and --no-prune", error); goto out; } if (!prune_commits_keep_younger_than_date (repo, opt_keep_younger_than, cancellable, error)) goto out; } if (opt_refs_only) pruneflags |= OSTREE_REPO_PRUNE_FLAGS_REFS_ONLY; if (opt_no_prune) pruneflags |= OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE; if (!ostree_repo_prune (repo, pruneflags, opt_depth, &n_objects_total, &n_objects_pruned, &objsize_total, cancellable, error)) goto out; formatted_freed_size = g_format_size_full (objsize_total, 0); g_print ("Total objects: %u\n", n_objects_total); if (n_objects_pruned == 0) g_print ("No unreachable objects\n"); else if (pruneflags & OSTREE_REPO_PRUNE_FLAGS_NO_PRUNE) g_print ("Would delete: %u objects, freeing %s\n", n_objects_pruned, formatted_freed_size); else g_print ("Deleted %u objects, %s freed\n", n_objects_pruned, formatted_freed_size); ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ostree_builtin_config (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; glnx_unref_object OstreeRepo *repo = NULL; gboolean ret = FALSE; const char *op; const char *section_key; const char *value; g_autofree char *section = NULL; g_autofree char *key = NULL; GKeyFile *config = NULL; context = g_option_context_new ("- Change configuration settings"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "OPERATION must be specified", error); goto out; } op = argv[1]; if (!strcmp (op, "set")) { if (argc < 4) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "KEY and VALUE must be specified"); goto out; } section_key = argv[2]; value = argv[3]; if (!split_key_string (section_key, §ion, &key, error)) goto out; config = ostree_repo_copy_config (repo); g_key_file_set_string (config, section, key, value); if (!ostree_repo_write_config (repo, config, error)) goto out; } else if (!strcmp (op, "get")) { GKeyFile *readonly_config = NULL; g_autofree char *value = NULL; if (argc < 3) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "KEY must be specified"); goto out; } section_key = argv[2]; if (!split_key_string (section_key, §ion, &key, error)) goto out; readonly_config = ostree_repo_get_config (repo); value = g_key_file_get_string (readonly_config, section, key, error); if (value == NULL) goto out; g_print ("%s\n", value); } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown operation %s", op); goto out; } ret = TRUE; out: if (config) g_key_file_free (config); return ret; }
gboolean ostree_builtin_diff (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeRepo) repo = NULL; const char *src; const char *target; g_autofree char *src_prev = NULL; g_autoptr(GFile) srcf = NULL; g_autoptr(GFile) targetf = NULL; g_autoptr(GPtrArray) modified = NULL; g_autoptr(GPtrArray) removed = NULL; g_autoptr(GPtrArray) added = NULL; context = g_option_context_new ("REV TARGETDIR"); if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error)) goto out; if (argc < 2) { gchar *help = g_option_context_get_help (context, TRUE, NULL); g_printerr ("%s\n", help); g_free (help); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "REV must be specified"); goto out; } if (argc == 2) { src_prev = g_strconcat (argv[1], "^", NULL); src = src_prev; target = argv[1]; } else { src = argv[1]; target = argv[2]; } if (!opt_stats && !opt_fs_diff) opt_fs_diff = TRUE; if (opt_fs_diff) { OstreeDiffFlags diff_flags = OSTREE_DIFF_FLAGS_NONE; if (opt_no_xattrs) diff_flags |= OSTREE_DIFF_FLAGS_IGNORE_XATTRS; if (!parse_file_or_commit (repo, src, &srcf, cancellable, error)) goto out; if (!parse_file_or_commit (repo, target, &targetf, cancellable, error)) goto out; modified = g_ptr_array_new_with_free_func ((GDestroyNotify)ostree_diff_item_unref); removed = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); added = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); OstreeDiffDirsOptions diff_opts = { opt_owner_uid, opt_owner_gid }; if (!ostree_diff_dirs_with_options (diff_flags, srcf, targetf, modified, removed, added, &diff_opts, cancellable, error)) goto out; ostree_diff_print (srcf, targetf, modified, removed, added); } if (opt_stats) { g_autoptr(GHashTable) reachable_a = NULL; g_autoptr(GHashTable) reachable_b = NULL; g_autoptr(GHashTable) reachable_intersection = NULL; g_autofree char *rev_a = NULL; g_autofree char *rev_b = NULL; g_autofree char *size = NULL; guint a_size; guint b_size; guint64 total_common; if (!ostree_repo_resolve_rev (repo, src, FALSE, &rev_a, error)) goto out; if (!ostree_repo_resolve_rev (repo, target, FALSE, &rev_b, error)) goto out; if (!ostree_repo_traverse_commit (repo, rev_a, 0, &reachable_a, cancellable, error)) goto out; if (!ostree_repo_traverse_commit (repo, rev_b, 0, &reachable_b, cancellable, error)) goto out; a_size = g_hash_table_size (reachable_a); b_size = g_hash_table_size (reachable_b); g_print ("[A] Object Count: %u\n", a_size); g_print ("[B] Object Count: %u\n", b_size); reachable_intersection = reachable_set_intersect (reachable_a, reachable_b); g_print ("Common Object Count: %u\n", g_hash_table_size (reachable_intersection)); if (!object_set_total_size (repo, reachable_intersection, &total_common, cancellable, error)) goto out; size = g_format_size_full (total_common, 0); g_print ("Common Object Size: %s\n", size); } ret = TRUE; out: return ret; }
int ostree_run (int argc, char **argv, OstreeCommand *commands, GError **res_error) { OstreeCommand *command; GError *error = NULL; GCancellable *cancellable = NULL; const char *command_name = NULL; gboolean success = FALSE; int in, out; /* avoid gvfs (http://bugzilla.gnome.org/show_bug.cgi?id=526454) */ g_setenv ("GIO_USE_VFS", "local", TRUE); g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, message_handler, NULL); /* * Parse the global options. We rearrange the options as * necessary, in order to pass relevant options through * to the commands, but also have them take effect globally. */ for (in = 1, out = 1; in < argc; in++, out++) { /* The non-option is the command, take it out of the arguments */ if (argv[in][0] != '-') { if (command_name == NULL) { command_name = argv[in]; out--; continue; } } else if (g_str_equal (argv[in], "--")) { break; } argv[out] = argv[in]; } argc = out; command = commands; while (command->name) { if (g_strcmp0 (command_name, command->name) == 0) break; command++; } if (!command->fn) { g_autoptr(GOptionContext) context = NULL; g_autofree char *help; context = ostree_option_context_new_with_commands (commands); /* This will not return for some options (e.g. --version). */ if (ostree_option_context_parse (context, NULL, &argc, &argv, OSTREE_BUILTIN_FLAG_NO_REPO, NULL, cancellable, &error)) { if (command_name == NULL) { g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "No command specified"); } else { g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown command '%s'", command_name); ostree_usage (commands, TRUE); } } help = g_option_context_get_help (context, FALSE, NULL); g_printerr ("%s", help); goto out; } if (!command->fn (argc, argv, cancellable, &error)) goto out; success = TRUE; out: g_assert (success || error); if (error) { g_propagate_error (res_error, error); return 1; } return 0; }
static gboolean ot_static_delta_builtin_generate (int argc, char **argv, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; context = g_option_context_new ("Generate static delta files"); if (!ostree_option_context_parse (context, generate_options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (!ostree_ensure_repo_writable (repo, error)) goto out; if (argc >= 3 && opt_to_rev == NULL) opt_to_rev = argv[2]; if (argc < 3 && opt_to_rev == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "TO revision must be specified"); goto out; } else { const char *from_source; g_autofree char *from_resolved = NULL; g_autofree char *to_resolved = NULL; g_autofree char *from_parent_str = NULL; g_autoptr(GVariantBuilder) parambuilder = NULL; g_assert (opt_to_rev); if (opt_empty) { if (opt_from_rev) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Cannot specify both --empty and --from=REV"); goto out; } from_source = NULL; } else if (opt_from_rev == NULL) { from_parent_str = g_strconcat (opt_to_rev, "^", NULL); from_source = from_parent_str; } else { from_source = opt_from_rev; } if (from_source) { if (!ostree_repo_resolve_rev (repo, from_source, FALSE, &from_resolved, error)) goto out; } if (!ostree_repo_resolve_rev (repo, opt_to_rev, FALSE, &to_resolved, error)) goto out; parambuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); if (opt_min_fallback_size) g_variant_builder_add (parambuilder, "{sv}", "min-fallback-size", g_variant_new_uint32 (g_ascii_strtoull (opt_min_fallback_size, NULL, 10))); if (opt_max_bsdiff_size) g_variant_builder_add (parambuilder, "{sv}", "max-bsdiff-size", g_variant_new_uint32 (g_ascii_strtoull (opt_max_bsdiff_size, NULL, 10))); if (opt_max_chunk_size) g_variant_builder_add (parambuilder, "{sv}", "max-chunk-size", g_variant_new_uint32 (g_ascii_strtoull (opt_max_chunk_size, NULL, 10))); if (opt_disable_bsdiff) g_variant_builder_add (parambuilder, "{sv}", "bsdiff-enabled", g_variant_new_boolean (FALSE)); g_variant_builder_add (parambuilder, "{sv}", "verbose", g_variant_new_boolean (TRUE)); g_print ("Generating static delta:\n"); g_print (" From: %s\n", from_resolved ? from_resolved : "empty"); g_print (" To: %s\n", to_resolved); if (!ostree_repo_static_delta_generate (repo, OSTREE_STATIC_DELTA_GENERATE_OPT_MAJOR, from_resolved, to_resolved, NULL, g_variant_builder_end (parambuilder), cancellable, error)) goto out; } ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ostree_builtin_export (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; glnx_unref_object OstreeRepo *repo = NULL; gboolean ret = FALSE; const char *rev; g_autoptr(GFile) root = NULL; g_autoptr(GFile) subtree = NULL; g_autofree char *commit = NULL; g_autoptr(GVariant) commit_data = NULL; struct archive *a; OstreeRepoExportArchiveOptions opts = { 0, }; context = g_option_context_new ("COMMIT - Stream COMMIT to stdout in tar format"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; #ifdef HAVE_LIBARCHIVE if (argc <= 1) { ot_util_usage_error (context, "A COMMIT argument is required", error); goto out; } rev = argv[1]; a = archive_write_new (); /* Yes, this is hardcoded for now. There is * archive_write_set_format_filter_by_ext() but it's fairly magic. * Many programs have support now for GNU tar, so should be a good * default. I also don't want to lock us into everything libarchive * supports. */ if (archive_write_set_format_gnutar (a) != ARCHIVE_OK) { propagate_libarchive_error (error, a); goto out; } if (archive_write_add_filter_none (a) != ARCHIVE_OK) { propagate_libarchive_error (error, a); goto out; } if (opt_output_path) { if (archive_write_open_filename (a, opt_output_path) != ARCHIVE_OK) { propagate_libarchive_error (error, a); goto out; } } else { if (archive_write_open_FILE (a, stdout) != ARCHIVE_OK) { propagate_libarchive_error (error, a); goto out; } } if (opt_no_xattrs) opts.disable_xattrs = TRUE; if (!ostree_repo_read_commit (repo, rev, &root, &commit, cancellable, error)) goto out; if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, commit, &commit_data, error)) goto out; opts.timestamp_secs = ostree_commit_get_timestamp (commit_data); if (opt_subpath) subtree = g_file_resolve_relative_path (root, opt_subpath); else subtree = g_object_ref (root); opts.path_prefix = opt_prefix; if (!ostree_repo_export_tree_to_archive (repo, &opts, (OstreeRepoFile*)subtree, a, cancellable, error)) goto out; if (archive_write_close (a) != ARCHIVE_OK) { propagate_libarchive_error (error, a); goto out; } #else g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "This version of ostree is not compiled with libarchive support"); goto out; #endif ret = TRUE; out: if (context) g_option_context_free (context); return ret; }
gboolean ot_remote_builtin_summary (int argc, char **argv, GCancellable *cancellable, GError **error) { g_autoptr(GOptionContext) context = NULL; g_autoptr(OstreeRepo) repo = NULL; const char *remote_name; g_autoptr(GBytes) summary_bytes = NULL; g_autoptr(GBytes) signature_bytes = NULL; OstreeDumpFlags flags = OSTREE_DUMP_NONE; gboolean gpg_verify_summary; gboolean ret = FALSE; context = g_option_context_new ("NAME - Show remote summary"); if (!ostree_option_context_parse (context, option_entries, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "NAME must be specified", error); goto out; } remote_name = argv[1]; if (opt_cache_dir) { if (!ostree_repo_set_cache_dir (repo, AT_FDCWD, opt_cache_dir, cancellable, error)) goto out; } if (opt_raw) flags |= OSTREE_DUMP_RAW; if (!ostree_repo_remote_fetch_summary (repo, remote_name, &summary_bytes, &signature_bytes, cancellable, error)) goto out; if (summary_bytes == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Remote server has no summary file"); goto out; } ot_dump_summary_bytes (summary_bytes, flags); if (!ostree_repo_remote_get_gpg_verify_summary (repo, remote_name, &gpg_verify_summary, error)) goto out; if (!gpg_verify_summary) g_clear_pointer (&signature_bytes, g_bytes_unref); /* XXX Note we don't show signatures for "--raw". My intuition is * if someone needs to see or parse raw summary data, including * signatures in the output would probably just interfere. * If there's demand for it I suppose we could introduce a new * option for raw signature data like "--raw-signatures". */ if (signature_bytes != NULL && !opt_raw) { glnx_unref_object OstreeGpgVerifyResult *result = NULL; /* The actual signed summary verification happens above in * ostree_repo_remote_fetch_summary(). Here we just parse * the signatures again for the purpose of printing. */ result = ostree_repo_verify_summary (repo, remote_name, summary_bytes, signature_bytes, cancellable, error); if (result == NULL) goto out; g_print ("\n"); ostree_print_gpg_verify_result (result); } ret = TRUE; out: return ret; }
gboolean ostree_admin_option_context_parse (GOptionContext *context, const GOptionEntry *main_entries, int *argc, char ***argv, OstreeAdminBuiltinFlags flags, OstreeSysroot **out_sysroot, GCancellable *cancellable, GError **error) { g_autoptr(GFile) sysroot_path = NULL; glnx_unref_object OstreeSysroot *sysroot = NULL; gboolean success = FALSE; /* Entries are listed in --help output in the order added. We add the * main entries ourselves so that we can add the --sysroot entry first. */ g_option_context_add_main_entries (context, global_admin_entries, NULL); if (!ostree_option_context_parse (context, main_entries, argc, argv, OSTREE_BUILTIN_FLAG_NO_REPO, NULL, cancellable, error)) goto out; if (opt_sysroot != NULL) sysroot_path = g_file_new_for_path (opt_sysroot); sysroot = ostree_sysroot_new (sysroot_path); if (flags & OSTREE_ADMIN_BUILTIN_FLAG_SUPERUSER) { GFile *path = ostree_sysroot_get_path (sysroot); /* If sysroot path is "/" then user must be root. */ if (!g_file_has_parent (path, NULL) && getuid () != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, "You must be root to perform this command"); goto out; } } if (opt_print_current_dir) { g_autoptr(GPtrArray) deployments = NULL; OstreeDeployment *first_deployment; g_autoptr(GFile) deployment_file = NULL; g_autofree char *deployment_path = NULL; if (!ostree_sysroot_load (sysroot, cancellable, error)) goto out; deployments = ostree_sysroot_get_deployments (sysroot); if (deployments->len == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unable to find a deployment in sysroot"); goto out; } first_deployment = deployments->pdata[0]; deployment_file = ostree_sysroot_get_deployment_directory (sysroot, first_deployment); deployment_path = g_file_get_path (deployment_file); g_print ("%s\n", deployment_path); /* The g_autoptr, g_autofree etc. don't happen when we explicitly * exit, making valgrind complain about leaks */ g_clear_object (&sysroot); g_clear_object (&sysroot_path); g_clear_object (&deployment_file); g_clear_pointer (&deployments, g_ptr_array_unref); g_clear_pointer (&deployment_path, g_free); exit (EXIT_SUCCESS); } if ((flags & OSTREE_ADMIN_BUILTIN_FLAG_UNLOCKED) == 0) { /* Released when sysroot is finalized, or on process exit */ if (!ot_admin_sysroot_lock (sysroot, error)) goto out; } if (out_sysroot) *out_sysroot = g_steal_pointer (&sysroot); success = TRUE; out: return success; }
gboolean ostree_builtin_pull (int argc, char **argv, GCancellable *cancellable, GError **error) { GOptionContext *context; gs_unref_object OstreeRepo *repo = NULL; gboolean ret = FALSE; gs_free char *remote = NULL; OstreeRepoPullFlags pullflags = 0; GSConsole *console = NULL; gs_unref_ptrarray GPtrArray *refs_to_fetch = NULL; gs_unref_object OstreeAsyncProgress *progress = NULL; context = g_option_context_new ("REMOTE [BRANCH...] - Download data from remote repository"); if (!ostree_option_context_parse (context, options, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, &repo, cancellable, error)) goto out; if (argc < 2) { ot_util_usage_error (context, "REMOTE must be specified", error); goto out; } if (opt_disable_fsync) ostree_repo_set_disable_fsync (repo, TRUE); if (opt_mirror) pullflags |= OSTREE_REPO_PULL_FLAGS_MIRROR; if (strchr (argv[1], ':') == NULL) { remote = g_strdup (argv[1]); if (argc > 2) { int i; refs_to_fetch = g_ptr_array_new (); for (i = 2; i < argc; i++) g_ptr_array_add (refs_to_fetch, argv[i]); g_ptr_array_add (refs_to_fetch, NULL); } } else { char *ref_to_fetch; refs_to_fetch = g_ptr_array_new (); if (!ostree_parse_refspec (argv[1], &remote, &ref_to_fetch, error)) goto out; /* Transfer ownership */ g_ptr_array_add (refs_to_fetch, ref_to_fetch); g_ptr_array_add (refs_to_fetch, NULL); } console = gs_console_get (); if (console) { gs_console_begin_status_line (console, "", NULL, NULL); progress = ostree_async_progress_new_and_connect (ostree_repo_pull_default_console_progress_changed, console); } { GVariantBuilder builder; g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}")); if (opt_subpath) g_variant_builder_add (&builder, "{s@v}", "subdir", g_variant_new_variant (g_variant_new_string (opt_subpath))); g_variant_builder_add (&builder, "{s@v}", "flags", g_variant_new_variant (g_variant_new_int32 (pullflags))); if (refs_to_fetch) g_variant_builder_add (&builder, "{s@v}", "refs", g_variant_new_variant (g_variant_new_strv ((const char *const*) refs_to_fetch->pdata, -1))); g_variant_builder_add (&builder, "{s@v}", "depth", g_variant_new_variant (g_variant_new_int32 (opt_depth))); if (!ostree_repo_pull_with_options (repo, remote, g_variant_builder_end (&builder), progress, cancellable, error)) goto out; } if (progress) ostree_async_progress_finish (progress); ret = TRUE; out: if (console) gs_console_end_status_line (console, NULL, NULL); if (context) g_option_context_free (context); return ret; }
gboolean ostree_builtin_fsck (int argc, char **argv, OstreeCommandInvocation *invocation, GCancellable *cancellable, GError **error) { g_autoptr(OstreeRepo) repo = NULL; gboolean found_corruption = FALSE; g_autoptr(GOptionContext) context = g_option_context_new (""); if (!ostree_option_context_parse (context, options, &argc, &argv, invocation, &repo, cancellable, error)) return FALSE; if (!opt_quiet) g_print ("Validating refs...\n"); /* Validate that the commit for each ref is available */ g_autoptr(GHashTable) all_refs = NULL; if (!ostree_repo_list_refs (repo, NULL, &all_refs, cancellable, error)) return FALSE; GHashTableIter hash_iter; gpointer key, value; g_hash_table_iter_init (&hash_iter, all_refs); while (g_hash_table_iter_next (&hash_iter, &key, &value)) { const char *refspec = key; const char *checksum = value; g_autofree char *ref_name = NULL; if (!ostree_parse_refspec (refspec, NULL, &ref_name, error)) return FALSE; if (!fsck_commit_for_ref (repo, checksum, NULL, ref_name, &found_corruption, cancellable, error)) return FALSE; } if (!opt_quiet) g_print ("Validating refs in collections...\n"); g_autoptr(GHashTable) all_collection_refs = NULL; /* (element-type OstreeCollectionRef utf8) */ if (!ostree_repo_list_collection_refs (repo, NULL, &all_collection_refs, OSTREE_REPO_LIST_REFS_EXT_EXCLUDE_REMOTES, cancellable, error)) return FALSE; g_hash_table_iter_init (&hash_iter, all_collection_refs); while (g_hash_table_iter_next (&hash_iter, &key, &value)) { const OstreeCollectionRef *ref = key; if (!fsck_commit_for_ref (repo, value, ref->collection_id, ref->ref_name, &found_corruption, cancellable, error)) return FALSE; } if (!opt_quiet) g_print ("Enumerating objects...\n"); g_autoptr(GHashTable) objects = NULL; if (!ostree_repo_list_objects (repo, OSTREE_REPO_LIST_OBJECTS_ALL, &objects, cancellable, error)) return FALSE; g_autoptr(GHashTable) commits = g_hash_table_new_full (ostree_hash_object_name, g_variant_equal, (GDestroyNotify)g_variant_unref, NULL); g_autoptr(GPtrArray) tombstones = NULL; if (opt_add_tombstones) tombstones = g_ptr_array_new_with_free_func (g_free); if (opt_verify_back_refs) opt_verify_bindings = TRUE; guint n_partial = 0; g_hash_table_iter_init (&hash_iter, objects); while (g_hash_table_iter_next (&hash_iter, &key, &value)) { GVariant *serialized_key = key; const char *checksum; OstreeObjectType objtype; OstreeRepoCommitState commitstate = 0; g_autoptr(GVariant) commit = NULL; ostree_object_name_deserialize (serialized_key, &checksum, &objtype); if (objtype == OSTREE_OBJECT_TYPE_COMMIT) { if (!ostree_repo_load_commit (repo, checksum, &commit, &commitstate, error)) return FALSE; /* If requested, check that all the refs listed in the ref-bindings * for this commit resolve back to this commit. */ if (opt_verify_back_refs) { g_autoptr(GVariant) metadata = g_variant_get_child_value (commit, 0); const char *collection_id = NULL; if (!g_variant_lookup (metadata, OSTREE_COMMIT_META_KEY_COLLECTION_BINDING, "&s", &collection_id)) collection_id = NULL; g_autofree const char **refs = NULL; if (g_variant_lookup (metadata, OSTREE_COMMIT_META_KEY_REF_BINDING, "^a&s", &refs)) { for (const char **iter = refs; *iter != NULL; ++iter) { g_autofree char *checksum_for_ref = NULL; if (collection_id != NULL) { const OstreeCollectionRef collection_ref = { (char *) collection_id, (char *) *iter }; if (!ostree_repo_resolve_collection_ref (repo, &collection_ref, TRUE, OSTREE_REPO_RESOLVE_REV_EXT_NONE, &checksum_for_ref, cancellable, error)) return FALSE; } else { if (!ostree_repo_resolve_rev (repo, *iter, TRUE, &checksum_for_ref, error)) return FALSE; } if (checksum_for_ref == NULL) { if (collection_id != NULL) return glnx_throw (error, "Collection–ref (%s, %s) in bindings for commit %s does not exist", collection_id, *iter, checksum); else return glnx_throw (error, "Ref ‘%s’ in bindings for commit %s does not exist", *iter, checksum); } else if (g_strcmp0 (checksum_for_ref, checksum) != 0) { if (collection_id != NULL) return glnx_throw (error, "Collection–ref (%s, %s) in bindings for commit %s does not resolve to that commit", collection_id, *iter, checksum); else return glnx_throw (error, "Ref ‘%s’ in bindings for commit %s does not resolve to that commit", *iter, checksum); } } } } if (opt_add_tombstones) { GError *local_error = NULL; g_autofree char *parent = ostree_commit_get_parent (commit); if (parent) { g_autoptr(GVariant) parent_commit = NULL; if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT, parent, &parent_commit, &local_error)) { if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_ptr_array_add (tombstones, g_strdup (checksum)); g_clear_error (&local_error); } else { g_propagate_error (error, local_error); return FALSE; } } } } if (commitstate & OSTREE_REPO_COMMIT_STATE_PARTIAL) n_partial++; else g_hash_table_add (commits, g_variant_ref (serialized_key)); } } g_clear_pointer (&objects, (GDestroyNotify) g_hash_table_unref); if (!opt_quiet) g_print ("Verifying content integrity of %u commit objects...\n", (guint)g_hash_table_size (commits)); if (!fsck_reachable_objects_from_commits (repo, commits, &found_corruption, cancellable, error)) return FALSE; if (opt_add_tombstones) { guint i; if (tombstones->len) { if (!ot_enable_tombstone_commits (repo, error)) return FALSE; } for (i = 0; i < tombstones->len; i++) { const char *checksum = tombstones->pdata[i]; g_print ("Adding tombstone for commit %s\n", checksum); if (!ostree_repo_delete_object (repo, OSTREE_OBJECT_TYPE_COMMIT, checksum, cancellable, error)) return FALSE; } } else if (n_partial > 0) { g_print ("%u partial commits not verified\n", n_partial); } if (found_corruption) return glnx_throw (error, "Repository corruption encountered"); return TRUE; }
gboolean ostree_builtin_remote (int argc, char **argv, GCancellable *cancellable, GError **error) { OstreeRemoteCommand *subcommand; const char *subcommand_name = NULL; g_autofree char *prgname = NULL; gboolean ret = FALSE; int in, out; for (in = 1, out = 1; in < argc; in++, out++) { /* The non-option is the command, take it out of the arguments */ if (argv[in][0] != '-') { if (subcommand_name == NULL) { subcommand_name = argv[in]; out--; continue; } } else if (g_str_equal (argv[in], "--")) { break; } argv[out] = argv[in]; } argc = out; subcommand = remote_subcommands; while (subcommand->name) { if (g_strcmp0 (subcommand_name, subcommand->name) == 0) break; subcommand++; } if (!subcommand->name) { g_autoptr(GOptionContext) context = NULL; g_autofree char *help; context = remote_option_context_new_with_commands (); /* This will not return for some options (e.g. --version). */ if (ostree_option_context_parse (context, NULL, &argc, &argv, OSTREE_BUILTIN_FLAG_NONE, NULL, cancellable, error)) { if (subcommand_name == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No \"remote\" subcommand specified"); } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown \"remote\" subcommand '%s'", subcommand_name); } } help = g_option_context_get_help (context, FALSE, NULL); g_printerr ("%s", help); goto out; } prgname = g_strdup_printf ("%s %s", g_get_prgname (), subcommand_name); g_set_prgname (prgname); if (!subcommand->fn (argc, argv, cancellable, error)) goto out; ret = TRUE; out: return ret; }