Skip to content

g-flame-oss/libdockerexcess

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

16 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Docker-excess (C Docker SDK)

Version: 1.1.59 BETA
A robust, low-level C client for the Docker Engine API.
Provides direct control over Docker via Unix sockets or TCP, using libcurl and json-c.


πŸ“‹ Index

  1. License
  2. Build
  3. Quickstart
  4. Configuration
  5. Containers
  6. File Operations
  7. Execution & Logs
  8. Images
  9. Networks
  10. Volumes
  11. Raw API
  12. Utility Functions
  13. API Coverage
  14. Error Handling
  15. Thread Safety
  16. Memory Management
  17. Notes

πŸ›  Build

Dependencies:

  • libcurl (>= 7.40.0)
  • json-c (>= 0.12)
  • pthreads

Compile:

gcc -o example example.c docker-excess.c -lcurl -ljson-c -lpthread

CMake example:

target_link_libraries(your_target docker-excess curl json-c pthread)

πŸš€ Quickstart

#include "docker-excess.h"
#include <stdio.h>

int main() {
    docker_excess_t *client;
    
    // Create client with default config (Unix socket)
    if (docker_excess_new(&client) != DOCKER_EXCESS_OK) {
        fprintf(stderr, "Failed to create client\n");
        return 1;
    }

    // Test connection
    if (docker_excess_ping(client) != DOCKER_EXCESS_OK) {
        fprintf(stderr, "Docker not reachable: %s\n", docker_excess_get_error(client));
        docker_excess_free(client);
        return 1;
    }

    // Get Docker version
    char *version_json;
    if (docker_excess_version(client, &version_json) == DOCKER_EXCESS_OK) {
        printf("Docker version info: %s\n", version_json);
        free(version_json);
    }

    printf("βœ“ Connected to Docker daemon\n");
    
    docker_excess_free(client);
    return 0;
}

βš™οΈ Configuration

Default Configuration (Unix Socket)

docker_excess_t *client;
docker_excess_new(&client);  // Uses /var/run/docker.sock

Custom Configuration

docker_excess_config_t config = docker_excess_default_config();
config.host = strdup("192.168.1.100");
config.port = 2376;
config.use_tls = true;
config.timeout_s = 60;
config.debug = true;

docker_excess_t *client;
docker_excess_new_with_config(&config, &client);

Environment-based Configuration

// Automatically reads DOCKER_HOST, DOCKER_TLS_VERIFY, DOCKER_CERT_PATH
docker_excess_config_t config = docker_excess_config_from_env();
docker_excess_t *client;
docker_excess_new_with_config(&config, &client);

πŸ“¦ Containers

List Containers

docker_excess_container_t **containers;
size_t count;

if (docker_excess_list_containers(client, true, &containers, &count) == DOCKER_EXCESS_OK) {
    for (size_t i = 0; i < count; i++) {
        printf("ID: %.12s | Name: %s | Status: %s | Image: %s\n",
               containers[i]->id,
               containers[i]->name ? containers[i]->name : "<unnamed>",
               containers[i]->status,
               containers[i]->image);
    }
    docker_excess_free_containers(containers, count);
}

Create Container

docker_excess_container_create_t params = {0};
params.name = "my-test-container";
params.image = "alpine:latest";
params.cmd = (char*[]){"sleep", "30"};
params.cmd_count = 2;
params.env = (char*[]){"ENV_VAR=value", "DEBUG=1"};
params.env_count = 2;
params.ports = (char*[]){"8080:80", "9000:9000"};
params.ports_count = 2;
params.volumes = (char*[]){"/host/path:/container/path:ro"};
params.volumes_count = 1;
params.auto_remove = true;
params.interactive = false;
params.tty = false;

char *container_id;
if (docker_excess_create_container(client, &params, &container_id) == DOCKER_EXCESS_OK) {
    printf("Created container: %s\n", container_id);
    free(container_id);
}

Container Lifecycle

// Start
docker_excess_start_container(client, container_id);

// Stop with timeout
docker_excess_stop_container(client, container_id, 10);

// Restart
docker_excess_restart_container(client, container_id);

// Pause/Unpause
docker_excess_pause_container(client, container_id);
docker_excess_unpause_container(client, container_id);

// Remove (force if running)
docker_excess_remove_container(client, container_id, true);

Wait for Container

int exit_code;
if (docker_excess_wait_container(client, container_id, &exit_code) == DOCKER_EXCESS_OK) {
    printf("Container exited with code: %d\n", exit_code);
}

πŸ“ File Operations

Read File

char *content;
size_t size;
if (docker_excess_read_file(client, container_id, "/etc/hostname", &content, &size) == DOCKER_EXCESS_OK) {
    printf("File content (%zu bytes): %.*s\n", size, (int)size, content);
    free(content);
}

Write File (Binary-Safe)

const char data[] = "Hello, Docker!\nThis is a test file.";
if (docker_excess_write_file(client, container_id, "/tmp/test.txt", data, sizeof(data)-1) == DOCKER_EXCESS_OK) {
    printf("File written successfully\n");
}

List Directory

docker_excess_file_t **files;
size_t count;
if (docker_excess_list_files(client, container_id, "/etc", &files, &count) == DOCKER_EXCESS_OK) {
    for (size_t i = 0; i < count; i++) {
        printf("%c %8ld %s\n", 
               files[i]->is_dir ? 'd' : '-',
               files[i]->size,
               files[i]->name);
    }
    docker_excess_free_files(files, count);
}

File Operations

// Create directory
docker_excess_mkdir(client, container_id, "/tmp/new_dir", 0755);

// Remove file/directory
docker_excess_remove_file(client, container_id, "/tmp/test.txt", false);

// Remove directory recursively  
docker_excess_remove_file(client, container_id, "/tmp/old_dir", true);

// Copy from container to host
docker_excess_copy_from_container(client, container_id, "/etc/hosts", "./hosts");

// Copy from host to container
docker_excess_copy_to_container(client, container_id, "./config.txt", "/app/config.txt");

⚑ Execution & Logs

Simple Command Execution

char *stdout_data = NULL;
char *stderr_data = NULL;
int exit_code;

if (docker_excess_exec_simple(client, container_id, "ls -la /", &stdout_data, &stderr_data, &exit_code) == DOCKER_EXCESS_OK) {
    printf("Command output:\n%s\n", stdout_data);
    printf("Exit code: %d\n", exit_code);
    free(stdout_data);
    free(stderr_data);
}

Callback-based Execution

void exec_callback(const char *stdout_data, const char *stderr_data, void *userdata) {
    if (stdout_data) printf("STDOUT: %s\n", stdout_data);
    if (stderr_data) printf("STDERR: %s\n", stderr_data);
}

const char *cmd[] = {"ps", "aux"};
docker_excess_exec(client, container_id, cmd, 2, exec_callback, NULL);

Container Logs

void log_callback(const char *line, void *userdata) {
    printf("LOG: %s\n", line);
}

// Get last 100 lines, with timestamps, don't follow
docker_excess_get_logs(client, container_id, false, true, 100, log_callback, NULL);

// Follow logs in real-time
docker_excess_get_logs(client, container_id, true, false, 0, log_callback, NULL);

πŸ–Ό Images

List Images

docker_excess_image_t **images;
size_t count;

if (docker_excess_list_images(client, false, &images, &count) == DOCKER_EXCESS_OK) {
    for (size_t i = 0; i < count; i++) {
        printf("Image ID: %.12s\n", images[i]->id);
        printf("  Created: %ld\n", images[i]->created);
        
        char size_str[64];
        docker_excess_format_bytes(images[i]->size, size_str, sizeof(size_str));
        printf("  Size: %s\n", size_str);
        
        if (images[i]->repo_tags) {
            printf("  Tags: ");
            for (size_t j = 0; j < images[i]->repo_tags_count; j++) {
                printf("%s ", images[i]->repo_tags[j]);
            }
            printf("\n");
        }
    }
    docker_excess_free_images(images, count);
}

Pull Image

// Pull latest
docker_excess_pull_image(client, "nginx", NULL);

// Pull specific tag
docker_excess_pull_image(client, "alpine", "3.18");

Remove Image

// Remove by name:tag
docker_excess_remove_image(client, "nginx:latest", false);

// Force remove (even if containers using it)
docker_excess_remove_image(client, "alpine:3.18", true);

🌐 Networks

List Networks

docker_excess_network_t **networks;
size_t count;

if (docker_excess_list_networks(client, &networks, &count) == DOCKER_EXCESS_OK) {
    for (size_t i = 0; i < count; i++) {
        printf("Network: %s (ID: %.12s, Driver: %s, Scope: %s)\n",
               networks[i]->name,
               networks[i]->id,
               networks[i]->driver,
               networks[i]->scope);
    }
    docker_excess_free_networks(networks, count);
}

Network Management

// Create network
char *network_id;
docker_excess_create_network(client, "my-network", "bridge", &network_id);

// Connect container to network
docker_excess_connect_network(client, network_id, container_id);

// Disconnect container from network  
docker_excess_disconnect_network(client, network_id, container_id);

// Remove network
docker_excess_remove_network(client, network_id);

free(network_id);

πŸ’Ύ Volumes

List Volumes

docker_excess_volume_t **volumes;
size_t count;

if (docker_excess_list_volumes(client, &volumes, &count) == DOCKER_EXCESS_OK) {
    for (size_t i = 0; i < count; i++) {
        printf("Volume: %s\n", volumes[i]->name);
        printf("  Driver: %s\n", volumes[i]->driver);
        printf("  Mountpoint: %s\n", volumes[i]->mountpoint);
    }
    docker_excess_free_volumes(volumes, count);
}

Volume Management

// Create volume
char *volume_name;
docker_excess_create_volume(client, "my-volume", "local", &volume_name);

// Remove volume
docker_excess_remove_volume(client, volume_name, false);

// Force remove (even if in use)
docker_excess_remove_volume(client, "old-volume", true);

free(volume_name);

πŸ”§ Raw API

Make Custom Requests

char *response;
int http_code;

// GET request
if (docker_excess_raw_request(client, "GET", "/info", NULL, &response, &http_code) == DOCKER_EXCESS_OK) {
    printf("HTTP %d: %s\n", http_code, response);
    free(response);
}

// POST with body
const char *body = "{\"Image\":\"alpine\",\"Cmd\":[\"echo\",\"hello\"]}";
docker_excess_raw_request(client, "POST", "/containers/create", body, &response, &http_code);

πŸ›  Utility Functions

Container ID Resolution

// Resolve container name or partial ID to full ID
char *full_id;
if (docker_excess_resolve_container_id(client, "my-container", &full_id) == DOCKER_EXCESS_OK) {
    printf("Full container ID: %s\n", full_id);
    free(full_id);
}

Byte Formatting

char buffer[64];
docker_excess_format_bytes(1073741824, buffer, sizeof(buffer));
printf("Size: %s\n", buffer); // "1.0 GB"

Error Information

docker_excess_error_t err = docker_excess_ping(client);
if (err != DOCKER_EXCESS_OK) {
    printf("Error code: %d\n", err);
    printf("Error string: %s\n", docker_excess_error_string(err));
    printf("Detailed error: %s\n", docker_excess_get_error(client));
}

πŸ“Š API Coverage

Category Function Status
Core docker_excess_new / docker_excess_free βœ…
docker_excess_ping / docker_excess_version βœ…
Container list / create / start / stop / restart βœ…
remove / pause / unpause / wait βœ…
Execution exec_simple / exec / get_logs βœ…
Files read_file / write_file / list_files βœ…
copy_from/to_container / mkdir / remove_file βœ…
Images list_images / pull_image / remove_image βœ…
build_image ⚠️*
Networks list / create / remove / connect / disconnect βœ…
Volumes list_volumes / create_volume / remove_volume βœ…
Raw API raw_request βœ…
Utility resolve_container_id / format_bytes / error_string βœ…

Legend:
βœ… = Fully implemented
⚠️* = Partial implementation (build_image needs tar archive creation)


❌ Error Handling

Error Types

typedef enum {
    DOCKER_EXCESS_OK = 0,
    DOCKER_EXCESS_ERR_INVALID_PARAM = -1,
    DOCKER_EXCESS_ERR_MEMORY = -2,
    DOCKER_EXCESS_ERR_NETWORK = -3,
    DOCKER_EXCESS_ERR_HTTP = -4,
    DOCKER_EXCESS_ERR_JSON = -5,
    DOCKER_EXCESS_ERR_NOT_FOUND = -6,
    DOCKER_EXCESS_ERR_TIMEOUT = -7,
    DOCKER_EXCESS_ERR_INTERNAL = -8
} docker_excess_error_t;

Comprehensive Error Handling

docker_excess_error_t err = docker_excess_start_container(client, "nonexistent");
switch (err) {
    case DOCKER_EXCESS_OK:
        printf("Success!\n");
        break;
    case DOCKER_EXCESS_ERR_NOT_FOUND:
        printf("Container not found\n");
        break;
    case DOCKER_EXCESS_ERR_NETWORK:
        printf("Network error: %s\n", docker_excess_get_error(client));
        break;
    case DOCKER_EXCESS_ERR_TIMEOUT:
        printf("Operation timed out\n");
        break;
    default:
        printf("Error: %s\n", docker_excess_error_string(err));
        printf("Details: %s\n", docker_excess_get_error(client));
}

πŸ”’ Thread Safety

All public functions are thread-safe. Internal operations use mutex locking:

// Safe to call from multiple threads
#pragma omp parallel for
for (int i = 0; i < 10; i++) {
    docker_excess_ping(client);  // Thread-safe
}

Note: Each thread should ideally have its own client instance for optimal performance.


🧠 Memory Management

Critical Rules

  1. Always free returned arrays and strings:
// βœ… Correct
docker_excess_container_t **containers;
size_t count;
docker_excess_list_containers(client, true, &containers, &count);
docker_excess_free_containers(containers, count);  // Required!

// ❌ Wrong - memory leak
docker_excess_list_containers(client, true, &containers, &count);
// Missing docker_excess_free_containers() call
  1. Free single strings from functions:
// βœ… Correct  
char *version;
docker_excess_version(client, &version);
free(version);  // Required!
  1. Always free the client:
docker_excess_t *client;
docker_excess_new(&client);
// ... use client ...
docker_excess_free(client);  // Required!

Memory Safety Checklist

  • βœ… Use provided docker_excess_free_* functions for arrays
  • βœ… Use standard free() for single returned strings
  • βœ… Check return values before using output parameters
  • βœ… Free client with docker_excess_free()
  • ❌ Never free strings inside structures (handled by docker_excess_free_*)

πŸ“ Notes

Connection Details

  • Default socket: /var/run/docker.sock
  • API Version: 1.41 (Docker Engine >= 18.06)
  • Timeout: 30 seconds (configurable)

Platform Support

  • Linux: Full support (Unix socket + TCP)
  • macOS: Full support (Unix socket + TCP)
  • Windows: TCP only (no Unix socket support)

Performance Tips

  • Reuse client instances when possible
  • Use docker_excess_ping() to verify connectivity before operations
  • Set appropriate timeouts for long-running operations
  • Consider separate client instances for concurrent operations

Limitations

  • build_image requires manual tar archive creation
  • Log streaming doesn't handle all Docker multiplexed format edge cases
  • No support for Docker Swarm-specific APIs
  • Limited to Docker Engine API v1.41 features

Debugging

docker_excess_config_t config = docker_excess_default_config();
config.debug = true;  // Enable cURL verbose output
docker_excess_new_with_config(&config, &client);

πŸ“„ License

MIT License - See LICENSE for details.


🀝 Contributing

Contributions welcome! Areas for improvement:

  • Complete build_image tar archive creation
  • Enhanced log parsing for edge cases
  • Docker Compose API support
  • Additional utility functions
  • Performance optimizations

--

About

A docker SDK for C it handles low level and high level task

Topics

Resources

License

Stars

Watchers

Forks

Languages