如何通过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
相关产品推荐
相关产品推荐

