int main(int argc, char **argv) { abrt_init(argv); /* I18n */ setlocale(LC_ALL, ""); #if ENABLE_NLS bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif const char *dump_dir_name = "."; const char *conf_file = CONF_DIR"/plugins/upload.conf"; const char *url = NULL; const char *ssh_public_key = NULL; const char *ssh_private_key = NULL; /* Can't keep these strings/structs static: _() doesn't support that */ const char *program_usage_string = _( "& [-v] -d DIR [-c CONFFILE] [-u URL] [-b FILE] [-r FILE]\n" "\n" "Uploads compressed tarball of problem directory DIR to URL.\n" "If URL is not specified, creates tarball in "LARGE_DATA_TMP_DIR" and exits.\n" "\n" "URL should have form 'protocol://[user[:pass]@]host/dir/[file.tar.gz]'\n" "where protocol can be http(s), ftp, scp, or file.\n" "File protocol can't have user and host parts: 'file:///dir/[file.tar.gz].'\n" "If URL ends with a slash, the archive name will be generated and appended\n" "to URL; otherwise, URL will be used as full file name.\n" "\n" "Files with names listed in $EXCLUDE_FROM_REPORT are not included\n" "into the tarball.\n" "\n" "\n""If not specified, CONFFILE defaults to "CONF_DIR"/plugins/upload.conf" "\n""Its lines should have 'PARAM = VALUE' format." "Recognized string parameter: URL.\n" "Parameter can be overridden via $Upload_URL." ); enum { OPT_v = 1 << 0, OPT_d = 1 << 1, OPT_c = 1 << 2, OPT_u = 1 << 3, OPT_b = 1 << 4, OPT_r = 1 << 5, }; /* Keep enum above and order of options below in sync! */ struct options program_options[] = { OPT__VERBOSE(&g_verbose), OPT_STRING('d', NULL, &dump_dir_name, "DIR" , _("Problem directory")), OPT_STRING('c', NULL, &conf_file , "CONFFILE", _("Config file")), OPT_STRING('u', NULL, &url , "URL" , _("Base URL to upload to")), OPT_STRING('b', "pubkey", &ssh_public_key , "FILE" , _("SSH public key file")), OPT_STRING('r', "key", &ssh_private_key, "FILE" , _("SSH private key file")), OPT_END() }; /*unsigned opts =*/ parse_opts(argc, argv, program_options, program_usage_string); export_abrt_envvars(0); // 2015-10-16 (jfilak): // It looks like there is no demand for encryption and other archive // types. Configurable ExcludeFiles sounds reasonable to me, I am // not sure about globbing though. // //Encrypt = yes //ArchiveType = .tar.bz2 // //TODO: //ExcludeFiles = foo,bar*,b*z map_string_t *settings = new_map_string(); if (conf_file) load_conf_file(conf_file, settings, /*skip key w/o values:*/ false); char *input_url = NULL; const char *conf_url = getenv("Upload_URL"); if (!conf_url || conf_url[0] == '\0') conf_url = url; if (!conf_url || conf_url[0] == '\0') conf_url = get_map_string_item_or_empty(settings, "URL"); if (!conf_url || conf_url[0] == '\0') conf_url = input_url = ask_url(_("Please enter a URL (scp, ftp, etc.) where the problem data is to be exported:")); set_map_string_item_from_string(settings, "UploadUsername", getenv("Upload_Username")); set_map_string_item_from_string(settings, "UploadPassword", getenv("Upload_Password")); /* set SSH keys */ if (ssh_public_key) set_map_string_item_from_string(settings, "SSHPublicKey", ssh_public_key); else if (getenv("Upload_SSHPublicKey") != NULL) set_map_string_item_from_string(settings, "SSHPublicKey", getenv("Upload_SSHPublicKey")); if (ssh_private_key) set_map_string_item_from_string(settings, "SSHPrivateKey", ssh_private_key); else if (getenv("Upload_SSHPrivateKey") != NULL) set_map_string_item_from_string(settings, "SSHPrivateKey", getenv("Upload_SSHPrivateKey")); char *remote_name = NULL; const int result = create_and_upload_archive(dump_dir_name, conf_url, settings, &remote_name); if (result != 0) goto finito; struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (dd) { report_result_t *result; result = report_result_new_with_label_from_env("upload"); report_result_set_url(result, remote_name); add_reported_to_entry(dd, result); report_result_free(result); dd_close(dd); } free(remote_name); finito: free(input_url); free_map_string(settings); return result; }
static int create_and_upload_archive( const char *dump_dir_name, map_string_t *settings) { int result = 1; /* error */ pid_t child; TAR* tar = NULL; const char* errmsg = NULL; char* tempfile = NULL; struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) xfunc_die(); /* error msg is already logged by dd_opendir */ /* Gzipping e.g. 0.5gig coredump takes a while. Let client know what we are doing */ log(_("Compressing data")); //TODO: //Encrypt = yes //ArchiveType = .tar.bz2 //ExcludeFiles = foo,bar*,b*z const char* opt = getenv("Upload_URL"); if (!opt) opt = get_map_string_item_or_empty(settings, "URL"); char *url = opt[0] != '\0' ? xstrdup(opt) : ask_url(_("Please enter a URL (scp, ftp, etc.) where the problem data is to be exported:")); /* Create a child gzip which will compress the data */ /* SELinux guys are not happy with /tmp, using /var/run/abrt */ /* Reverted back to /tmp for ABRT2 */ /* Changed again to /var/tmp because of Fedora feature tmp-on-tmpfs */ tempfile = concat_path_basename(LARGE_DATA_TMP_DIR, dump_dir_name); tempfile = append_to_malloced_string(tempfile, ".tar.gz"); int pipe_from_parent_to_child[2]; xpipe(pipe_from_parent_to_child); child = vfork(); if (child == 0) { /* child */ close(pipe_from_parent_to_child[1]); xmove_fd(pipe_from_parent_to_child[0], 0); xmove_fd(xopen3(tempfile, O_WRONLY | O_CREAT | O_EXCL, 0600), 1); execlp("gzip", "gzip", NULL); perror_msg_and_die("Can't execute '%s'", "gzip"); } close(pipe_from_parent_to_child[0]); /* If child died (say, in xopen), then parent might get SIGPIPE. * We want to properly unlock dd, therefore we must not die on SIGPIPE: */ signal(SIGPIPE, SIG_IGN); /* Create tar writer object */ if (tar_fdopen(&tar, pipe_from_parent_to_child[1], tempfile, /*fileops:(standard)*/ NULL, O_WRONLY | O_CREAT, 0644, TAR_GNU) != 0) { errmsg = "Can't create temporary file in "LARGE_DATA_TMP_DIR; goto ret; } /* Write data to the tarball */ { string_vector_ptr_t exclude_from_report = get_global_always_excluded_elements(); dd_init_next_file(dd); char *short_name, *full_name; while (dd_get_next_file(dd, &short_name, &full_name)) { if (exclude_from_report && is_in_string_list(short_name, (const_string_vector_const_ptr_t)exclude_from_report)) goto next; // dd_get_next_file guarantees that it's a REG: //struct stat stbuf; //if (stat(full_name, &stbuf) != 0) // || !S_ISREG(stbuf.st_mode) //) { // goto next; //} if (tar_append_file(tar, full_name, short_name) != 0) { errmsg = "Can't create temporary file in "LARGE_DATA_TMP_DIR; free(short_name); free(full_name); goto ret; } next: free(short_name); free(full_name); } } dd_close(dd); dd = NULL; /* Close tar writer... */ if (tar_append_eof(tar) != 0 || tar_close(tar) != 0) { errmsg = "Can't create temporary file in "LARGE_DATA_TMP_DIR; goto ret; } tar = NULL; /* ...and check that gzip child finished successfully */ int status; safe_waitpid(child, &status, 0); child = -1; if (status != 0) { /* We assume the error was out-of-disk-space or out-of-quota */ errmsg = "Can't create temporary file in "LARGE_DATA_TMP_DIR; goto ret; } /* Upload the tarball */ /* Upload from /tmp to /tmp + deletion -> BAD, exclude this possibility */ if (url && url[0] && strcmp(url, "file://"LARGE_DATA_TMP_DIR"/") != 0) { post_state_t *state = new_post_state(POST_WANT_ERROR_MSG); state->username = getenv("Upload_Username"); char *password_inp = NULL; if (state->username != NULL && state->username[0] != '\0') { /* Load Password only if Username is configured, it doesn't make */ /* much sense to load Password without Username. */ state->password = getenv("Upload_Password"); if (state->password == NULL) { /* Be permissive and nice, ask only once and don't check */ /* the result. User can dismiss this prompt but the upload */ /* may work somehow??? */ char *msg = xasprintf(_("Please enter password for uploading:"), state->username); state->password = password_inp = ask_password(msg); free(msg); } } char *remote_name = upload_file_ext(state, url, tempfile, UPLOAD_FILE_HANDLE_ACCESS_DENIALS); result = (remote_name == NULL); /* error if NULL */ free(remote_name); free(password_inp); free_post_state(state); /* cleanup code will delete tempfile */ } else { result = 0; /* success */ log(_("Archive is created: '%s'"), tempfile); free(tempfile); tempfile = NULL; } ret: free(url); dd_close(dd); if (tar) tar_close(tar); /* close(pipe_from_parent_to_child[1]); - tar_close() does it itself */ if (child > 0) safe_waitpid(child, NULL, 0); if (tempfile) { unlink(tempfile); free(tempfile); } if (errmsg) error_msg_and_die("%s", errmsg); return result; }