You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过libdocker调用Docker API捕获容器Exec命令的输出?

问题解决:Docker Exec API捕获容器命令输出

核心问题分析

Docker Exec的start接口返回的是Chunked编码的流式响应,你使用的libdocker默认缓冲区可能没正确处理这种流式输出,加上自定义回调可能被libdocker内部设置覆盖,导致缓冲区始终为空。

修复步骤

1. 替换静态缓冲区为动态可扩容结构

固定大小的缓冲区无法适配不确定长度的命令输出,需要自定义动态缓冲区:

typedef struct {
    char *data;
    size_t len;
    size_t capacity;
} DynamicBuffer;

DynamicBuffer* dyn_buf_init() {
    DynamicBuffer *buf = malloc(sizeof(DynamicBuffer));
    buf->capacity = 1024;
    buf->data = malloc(buf->capacity);
    buf->len = 0;
    return buf;
}

void dyn_buf_free(DynamicBuffer *buf) {
    free(buf->data);
    free(buf);
}

static size_t write_chunk_callback(char *contents, size_t size, size_t nmemb, void *userp) {
    DynamicBuffer *buf = (DynamicBuffer*)userp;
    size_t total = size * nmemb;
    
    // 扩容缓冲区
    if (buf->len + total >= buf->capacity) {
        buf->capacity *= 2;
        buf->data = realloc(buf->data, buf->capacity);
    }
    
    // 复制数据到缓冲区并添加终止符
    memcpy(buf->data + buf->len, contents, total);
    buf->len += total;
    buf->data[buf->len] = '\0';
    
    return total;
}

2. 绕过libdocker封装,直接使用curl调用exec start接口

如果libdocker的docker_post_with_http_status内部覆盖了curl的写入回调,直接用原生curl操作更可控:

char * docker_exec(const char * name, const char * cmd) {
    json_error_t error;
    DOCKER *docker = docker_init(DOCKER_API_VERSION);
    char url[256];
    long http_status = 0;

    // 1. 创建exec实例,用libjson-c构建请求体避免格式错误
    json_t *exec_req = json_object();
    json_object_set_new(exec_req, "AttachStdout", json_true());
    json_object_set_new(exec_req, "AttachStderr", json_true());
    json_t *cmd_arr = json_array();
    json_array_append_new(cmd_arr, json_string(cmd));
    json_object_set_new(exec_req, "Cmd", cmd_arr);
    char *post_json = json_dumps(exec_req, JSON_COMPACT);
    
    sprintf(url, "http://%s/containers/%s/exec", DOCKER_API_VERSION, name);
    CURLcode response = docker_post_with_http_status(docker, url, post_json, &http_status);
    free(post_json);
    json_decref(exec_req);

    if (http_status != 201) {
        docker_free(docker);
        return NULL;
    }

    // 获取exec ID
    json_t *j_id = json_loads(docker_buffer(docker), JSON_COMPACT, &error);
    const char *exec_id = json_string_value(json_object_get(j_id, "Id"));
    docker_free(docker);

    // 2. 直接用curl启动exec并捕获输出
    CURL *curl = curl_easy_init();
    if (!curl) {
        json_decref(j_id);
        return NULL;
    }

    DynamicBuffer *output_buf = dyn_buf_init();
    char start_post[] = "{\"Detach\":false,\"Tty\":false}";
    
    sprintf(url, "http://%s/exec/%s/start", DOCKER_API_VERSION, exec_id);
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_POST, 1L);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, start_post);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_slist_append(NULL, "Content-Type: application/json"));
    // 设置自定义回调和缓冲区
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_chunk_callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, output_buf);
    // 显式开启分块编码处理
    curl_easy_setopt(curl, CURLOPT_HTTP_TRANSFER_DECODING, 1L);

    response = curl_easy_perform(curl);
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status);
    curl_easy_cleanup(curl);
    json_decref(j_id);

    if (response != CURLE_OK || http_status != 200) {
        dyn_buf_free(output_buf);
        return NULL;
    }

    // 复制输出到可返回的字符串
    char *result = malloc(output_buf->len + 1);
    memcpy(result, output_buf->data, output_buf->len + 1);
    dyn_buf_free(output_buf);
    return result;
}

3. 关键注意事项

  • 禁止手动拼接JSON:用libjson-c构建请求体,防止命令含特殊字符导致JSON格式错误。
  • 强制分块编码处理:显式开启CURLOPT_HTTP_TRANSFER_DECODING,确保curl自动处理分块响应。
  • 动态缓冲区扩容:根据输出长度自动扩容缓冲区,避免数据截断。
  • 资源全释放:所有malloc的结构、curl句柄、json对象都要正确释放,防止内存泄漏。

内容的提问来源于stack exchange,提问作者Seraph

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 23:44:56