/* Test Case:
 * Submit a job that generates output after a short time.
 * Before that output is generated, send a STDIO_UPDATE signal, to
 * direct the output to a different port.
 * Verify that the output arrives at the new port
 */
int
test_stdio_update(void)
{
    char                                *old_listener_url, *new_listener_url;
    char                                *old_job_contact;
    int                                 rc;
    char                                *callback_contact;
    char                                *old_rsl, *new_rsl;
    test_monitor_t                      monitor;
    const char                          rsl_spec[] =
            "&(executable=/bin/sh)"
            "(arguments=-c 'sleep 30; echo hello;')"
            "(rsl_substitution = (TEST_GASS_URL %s))"
            "(stdout = $(TEST_GASS_URL)/out)";
    const char                          stdio_update_rsl_spec[] =
            "&(stdout = %s/out)";

    globus_mutex_init(&monitor.mutex, NULL);
    globus_cond_init(&monitor.cond, NULL);

    memset(monitor.old_output, 0, sizeof(monitor.old_output));
    memset(monitor.new_output, 0, sizeof(monitor.new_output));
    monitor.old_request = GLOBUS_NULL_HANDLE;
    monitor.new_request = GLOBUS_NULL_HANDLE;
    monitor.status = GLOBUS_GRAM_PROTOCOL_JOB_STATE_UNSUBMITTED;
    monitor.failure_code = 0;

    /* Create a pair of listeners and get their base URLs. The job will be
     * submitted with stdout directed to the first, then redirected to the
     * second via a stdio update signal
     */
    rc = globus_gass_transfer_create_listener(
            &monitor.old_listener,
            NULL,
            "https");
    test_assert_gram_rc_equals(rc, GLOBUS_SUCCESS);
    test_assert(monitor.old_listener != GLOBUS_NULL_HANDLE);

    old_listener_url = globus_gass_transfer_listener_get_base_url(
            monitor.old_listener);
    test_assert(old_listener_url != NULL);

    rc = globus_gass_transfer_register_listen(
            monitor.old_listener,
            test_l_old_listener_callback,
            &monitor);
    test_assert_gram_rc_equals(rc, GLOBUS_SUCCESS);

    rc = globus_gass_transfer_create_listener(
            &monitor.new_listener,
            NULL,
            "https");
    test_assert_gram_rc_equals(rc, GLOBUS_SUCCESS);
    test_assert(monitor.new_listener != GLOBUS_NULL_HANDLE);

    new_listener_url = globus_gass_transfer_listener_get_base_url(
            monitor.new_listener);
    test_assert(new_listener_url != NULL);

    rc = globus_gass_transfer_register_listen(
            monitor.new_listener,
            test_l_new_listener_callback,
            &monitor);
    test_assert(rc == GLOBUS_SUCCESS);

    old_rsl = globus_common_create_string(rsl_spec, old_listener_url);
    test_assert(old_rsl != NULL);

    /* Submit the job, do the two-phase commit, then submit a restart
     * request with the new stdout destination
     */
    rc = globus_gram_client_callback_allow(
            test_l_gram_callback,
            &monitor,
            &callback_contact);
    test_assert_gram_rc_equals(rc, GLOBUS_SUCCESS);
    test_assert(callback_contact != NULL);

    rc = globus_gram_client_job_request(
            contact_string,
            old_rsl,
            GLOBUS_GRAM_PROTOCOL_JOB_STATE_UNSUBMITTED|
            GLOBUS_GRAM_PROTOCOL_JOB_STATE_STAGE_IN|
            GLOBUS_GRAM_PROTOCOL_JOB_STATE_PENDING|
            GLOBUS_GRAM_PROTOCOL_JOB_STATE_ACTIVE|
            GLOBUS_GRAM_PROTOCOL_JOB_STATE_STAGE_OUT|
            GLOBUS_GRAM_PROTOCOL_JOB_STATE_DONE|
            GLOBUS_GRAM_PROTOCOL_JOB_STATE_FAILED,
            callback_contact,
            &old_job_contact);
    test_assert_gram_rc_equals(rc, GLOBUS_SUCCESS);
    test_assert(old_job_contact != NULL);

    globus_mutex_lock(&monitor.mutex);
    while (monitor.status == GLOBUS_GRAM_PROTOCOL_JOB_STATE_UNSUBMITTED)
    {
        globus_cond_wait(&monitor.cond, &monitor.mutex);
    }

    new_rsl = globus_common_create_string(
            stdio_update_rsl_spec, new_listener_url);
    test_assert(new_rsl != NULL);

    rc = globus_gram_client_job_signal(
            old_job_contact,
            GLOBUS_GRAM_PROTOCOL_JOB_SIGNAL_STDIO_UPDATE,
            new_rsl,
            &monitor.status,
            &monitor.failure_code);
    test_assert_gram_rc_equals(rc, GLOBUS_SUCCESS);

    /* Wait for job to complete. After it's done, check to see which
     * destination got stdout
     */
    while (monitor.status != GLOBUS_GRAM_PROTOCOL_JOB_STATE_DONE &&
           monitor.status != GLOBUS_GRAM_PROTOCOL_JOB_STATE_FAILED)
    {
        globus_cond_wait(&monitor.cond, &monitor.mutex);
    }
    if (monitor.old_request)
    {
        globus_gass_transfer_fail(monitor.old_request, test_l_gass_fail, NULL);
    }

    globus_mutex_unlock(&monitor.mutex);

    if (monitor.new_output[0] == 0)
    {
        fprintf(stderr, "Didn't get expected output to new handle\n");
        test_assert(strcmp((char *) monitor.new_output, "hello\n") == 0);
    }
    if (monitor.old_output[0] != 0)
    {
        fprintf(stderr, "Unexpected output to old handle: %s",
                monitor.old_output);
        test_assert(monitor.old_output[0] == 0);
    }

    return rc;
}
int main(int argc, char *argv[])
{
    int rc = 1;
    monitor_t monitor;
    char * rsl;

    if (argc < 4 || (!globus_l_get_mode(&monitor, argv[2]))
            || ((monitor.timeout = atoi(argv[3])) < 0))
    {
        globus_libc_fprintf(stderr,
                "Usage: %s RM-CONTACT MODE SAVE_STATE TIMEOUT\n"
                "    RM-CONTACT: resource manager contact\n"
                "    MODE: no-commit|no-commit-end|commit|late-commit-end\n"
                "    TIMEOUT: two-phase timeout in seconds\n",
                argv[0]);
        goto error_exit;
    }

    rc = globus_module_activate(GLOBUS_GRAM_CLIENT_MODULE);

    if (rc != GLOBUS_SUCCESS)
    {
        globus_libc_fprintf(stderr,
                "failure activating GLOBUS_GRAM_CLIENT_MODULE: %s\n",
                globus_gram_client_error_string(rc));
        goto error_exit;
    }

    rsl = globus_common_create_string(format, monitor.timeout);
    if (rsl == NULL)
    {
        globus_libc_fprintf(stderr, "failure allocating rsl string\n");
        goto deactivate_exit;
    }

    globus_mutex_init(&monitor.mutex, NULL);
    globus_cond_init(&monitor.cond, NULL);
    monitor.job_contact = NULL;
    monitor.callback_contact = NULL;

    rc = globus_gram_client_callback_allow(
            globus_l_state_callback,
            &monitor,
            &monitor.callback_contact);

    if (rc != GLOBUS_SUCCESS || monitor.callback_contact == NULL)
    {
        globus_libc_fprintf(stderr,
                "failure allowing callbacks\n");
        rc = -1;
        goto destroy_monitor_exit;
    }

    globus_mutex_lock(&monitor.mutex);
    rc = globus_gram_client_job_request(
            argv[1],
            rsl,
            GLOBUS_GRAM_PROTOCOL_JOB_STATE_ALL,
            monitor.callback_contact,
            &monitor.job_contact);
    if (monitor.job_contact != NULL)
    {
        globus_libc_printf("%s\n", monitor.job_contact);
    }

    if (rc != GLOBUS_GRAM_PROTOCOL_ERROR_WAITING_FOR_COMMIT)
    {
        if (rc == GLOBUS_SUCCESS)
        {
            globus_libc_fprintf(stderr,
                    "job manager did not return "
                    "GLOBUS_GRAM_PROTOCOL_ERROR_COMMIT_TIMED_OUT\n");
            rc = -1;
        }
        else
        {
            globus_libc_fprintf(stderr,
                    "failure submitting job request [%d]: %s\n",
                    rc,
                    globus_gram_client_error_string(rc));
        }

        goto disallow_exit;
    }
    rc = 0;

    if (monitor.mode == no_commit)
    {
        goto disallow_exit;
    }

    rc = globus_gram_client_job_signal(
            monitor.job_contact,
            GLOBUS_GRAM_PROTOCOL_JOB_SIGNAL_COMMIT_REQUEST,
            NULL,
            &monitor.job_status,
            &monitor.failure_code);

    if (rc != GLOBUS_SUCCESS)
    {
        globus_libc_fprintf(stderr,
                "failure sending commit signal: %s\n",
                globus_gram_client_error_string(rc));
        goto disallow_exit;
    }

    while (monitor.job_status != GLOBUS_GRAM_PROTOCOL_JOB_STATE_DONE &&
           monitor.job_status != GLOBUS_GRAM_PROTOCOL_JOB_STATE_FAILED)
    {
        globus_cond_wait(&monitor.cond, &monitor.mutex);
    }

    if (monitor.mode == no_commit_end)
    {
        rc = 0;
        goto disallow_exit;
    }
    else if (monitor.mode == late_commit_end)
    {
        rc = 0;
        sleep(monitor.timeout + 1);
    }

    rc = globus_gram_client_job_signal(
            monitor.job_contact,
            GLOBUS_GRAM_PROTOCOL_JOB_SIGNAL_COMMIT_END,
            NULL,
            &monitor.job_status,
            &monitor.failure_code);

    if (rc != GLOBUS_SUCCESS)
    {
        globus_libc_fprintf(stderr,
                "failure sending commit end signal: %s\n",
                globus_gram_client_error_string(rc));
        goto disallow_exit;
    }

disallow_exit:
    if (monitor.job_contact != NULL)
    {
        globus_gram_client_job_contact_free(monitor.job_contact);
    }
    globus_mutex_unlock(&monitor.mutex);
    globus_gram_client_callback_disallow(monitor.callback_contact);
destroy_monitor_exit:
    if (monitor.callback_contact != NULL)
    {
        free(monitor.callback_contact);
    }
    globus_mutex_destroy(&monitor.mutex);
    globus_cond_destroy(&monitor.cond);
    free(rsl);
deactivate_exit:
    globus_module_deactivate_all();
error_exit:
    return rc;
}