cURL / Mailing Lists / curl-library / Single Mail

curl-library

multi Error Code 23 (Failed writing body) corner case

From: Dan Donahue <ddonahuex_at_gmail.com>
Date: Thu, 02 Oct 2014 14:42:58 -0400

Hi,

This is my first post to the libcurl mailing list. My hope is this mail
is "etiquettely" correct according to your mailing list rules, which I
read in their entirety.

Please review the info below and provide feedback that will help me
isolate the issue to either my application or libcurl.

Info

----
Embedded PowerPC CPU
libcurl 7.29.0
linux kernel 3.0.34
libssh2 1.4.3
openssl 1.0.1e
All multi sessions contained herein use sftp employing username and
password for authentication.
Issue
-----
A second failed upload attempt ("Login denied (67)") during a download
in process causes said download to fail with: "Failed writing received
data to disk/application (23)". This only occurs if an upload was
attempted and failed prior to initiating the download. 
Note, the 67 error is expected because the wrong username is used. 
Download progress reports received before the second upload attempt
indicate download is proceeding as expected. The 23 error occurs
immediately after the second 67 error.
The 23 error occurs because the fwrite in the write function did not
write out all the data given (e.g. 209 != 2000). ferror for the failed
fwrite reports "Bad file descriptor". This occurs whether I use the
libcurl default write function or a registered callback.
Subsequent download attempts complete successfully.
Debug Notes
-----------
"client" refers to an easy session associated w/a multi intfc
* Issue only seen for error upload 1->download start->error upload 2
* Issue not seen for upload error 1->upload error 2->download start
* Issue not seen for good upload 1->download start->good upload 2
* Issue not seen for download start->upload error 1
* Issue seen when upload/download use same or different servers
* Issue seen when upload/download use same or different credentials
* Issue seen whether or not client cleanup is done during active
download or pended until all sessions complete.
* Verified correct easy client/file streams associations (e.g. upload
not closing download file stream)
* Verified no errant/duplicate fcloses
* The file being written during download persists after failure
* Output from CURLOPT_VERBOSE for both download/upload is below
CURLOPT_VERBOSE for download:
* About to connect() to 10.40.10.211 port 22 (#1)
*   Trying 10.40.10.211...
* Connected to 10.40.10.211 (10.40.10.211) port 22 (#1)
* SSH MD5 fingerprint: <removed>
* SSH authentication methods available:
publickey,gssapi-keyex,gssapi-with-mic,password
* Using ssh public key file /home/root/.ssh/id_dsa.pub
* Using ssh private key file /home/root/.ssh/id_dsa
* SSH public key authentication failed: Unable to open public key file
* Initialized password authentication
* Authentication complete
* Failed writing body (288 != 2000)
* Closing connection 1
CURLOPT_VERBOSE for failed upload:
* About to connect() to 10.40.10.211 port 22 (#0)
*   Trying 10.40.10.211...
* Connected to 10.40.10.211 (10.40.10.211) port 22 (#0)
* SSH MD5 fingerprint: <removed>
* SSH authentication methods available:
publickey,gssapi-keyex,gssapi-with-mic,password
* Using ssh public key file /home/root/.ssh/id_dsa.pub
* Using ssh private key file /home/root/.ssh/id_dsa
* SSH public key authentication failed: Unable to open public key file
* Failure connecting to agent
* Authentication failure
* Closing connection 0
SW Design
---------
My process is responsible for handling asynchronous file transfer
requests as well as periodically monitoring a directory and uploading
any files contained therein.
Code
----
A 10ms timer (different from "dir check timer" above) is used to call
curlMultiPeform (below) when there are active sessions. Bumped timer to
1000ms just for test, but issue still occurs.
These are stripped down (NULL checks, esoteric code, etc) versions of my
curl functions. I did not include my write callback used during debug
because the design uses the libcurl default. Testing proved no
difference.
// derived from multi-app.c on your site
bool curlMultiPerform(void)
{
    struct timeval timeout;
    int rc;
    CURLMcode mcode;
    int activeTransfers;
    fd_set fdread;
    fd_set fdwrite;
    fd_set fdexcep;
    int maxfd = -1;
    long curl_timeo = -1;
    FD_ZERO(&fdread);
    FD_ZERO(&fdwrite);
    FD_ZERO(&fdexcep);
    timeout.tv_sec = 0;
    timeout.tv_usec = 1000;
    curl_multi_timeout(multiHandle, &curl_timeo);
    if(curl_timeo >= 0)
    {
        timeout.tv_sec = curl_timeo / 1000;
        if(timeout.tv_sec > 1)
            timeout.tv_sec = 1;
        else
            timeout.tv_usec = (curl_timeo % 1000) * 1000;
    }
    mcode = curl_multi_fdset(multiHandle, &fdread, &fdwrite,
                             &fdexcep, &maxfd);
    if (mcode != CURLM_OK)
    {
        curlPurgeClients();
        return false;
    }
    rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
    if (rc < 0)
        curlPurgeClients();
        return false;
    }
    mcode = curl_multi_perform(multiHandle, &activeTransfers);
    if (mcode != CURLM_OK)
    {
        curlPurgeClients();
        return false;
    }
    if (activeTransfers < activeTransfersLast)
       curlProcessMessages(); // somebody completed
    activeTransfersLast = activeTransfers;
    if (curlClients.size() > 0)
        curlTimerStart(); // somebody still active
   return true;
}                                                                                                                                                                                                                     
void curlProcessMessages(void)
{
    CURLMsg *msg = NULL;
    CurlClient* client = NULL;
    int msgsLeft = 0;
    while ((msg = curl_multi_info_read(multiHandle, &msgsLeft)))
    {
        client = curlClientGet(msg->easy_handle);
        if (msg->msg == CURLMSG_DONE)
        {
            if (msg->data.result == CURLE_OK)
                status = OK;
            else
                status = NOT_OK;
            curlClientComplete(client);
        }
    }
}
void curlClientComplete(CurlClient *client)
{
    if (client->curlHandle)
    {
        curl_multi_remove_handle(multiHandle, client->curlHandle);
        curl_easy_cleanup(client->curlHandle);
    }
    if (client->fd)
        fclose(client->fd);
    if (curlClients.size() == 0)
    {
        curl_multi_cleanup(multiHandle); // all sessions complete
        multiHandle = NULL;
    }
}
CurlClient * curlClientCreate(FtpReq *req)
{
    CurlClient *client = NULL;
    if (multiHandle == NULL)
        multiHandle = curl_multi_init();
    client = new CurlClient;
    memcpy(&(client->reqInfo), req, sizeof(FtpReq));
    client->reqHandle = req->handle;
    client->curlHandle = NULL;
    client->fd = NULL;
    client->reqInfo.download = req->download;
    if (req->download)
    {
        snprintf(&(client->reqInfo.fileName[0]), FTP_MAX_STRLEN,
                 %s/%d.download", DOWNLOAD_DIR, client->reqHandle);
    }
    else
    {
        strncpy(&(client->reqInfo.fileName[0]),                   
                &(req->fileName[0]), FTP_MAX_STRLEN);
    }
    client->fd = fopen(client->reqInfo.fileName,
                       (req->download?"w+b":"r"));
 
    client->curlHandle = curl_easy_init();
    curl_multi_add_handle(multiHandle, client->curlHandle);
    curlClients.push_back(client);
    return client;
}
Thank you for taking the time.
-- 
Thanks,
Dan
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html
Received on 2014-10-02