Exemple #1
0
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;
}