|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <unistd.h> |
|
#include <string.h> |
|
|
|
// dexec: if you execute this program with -e, docker exec will get called. |
|
// Otherwise, docker compose will be called by default. |
|
// This version also checks that "docker" exists before doing anything. |
|
// compile: gcc -o dexec dexec.c |
|
|
|
void print_usage(const char *prog) { |
|
fprintf(stderr, "Usage:\n"); |
|
fprintf(stderr, " %s [--exec | -e] <container> <command> [args...]\n", prog); |
|
fprintf(stderr, " %s [other docker compose args...]\n", prog); |
|
} |
|
|
|
// Check if a command exists in PATH: here to check whether "docker" is present |
|
int command_exists(const char *cmd) { |
|
char *path_env = getenv("PATH"); |
|
if (path_env == NULL) { |
|
return 0; |
|
} |
|
// duplicate because strtok modifies |
|
char *paths = strdup(path_env); |
|
if (!paths) { |
|
return 0; |
|
} |
|
char *saveptr = NULL; |
|
char *dir = strtok_r(paths, ":", &saveptr); |
|
while (dir != NULL) { |
|
size_t len = strlen(dir) + 1 + strlen(cmd) + 1; |
|
char *full = malloc(len); |
|
if (!full) { |
|
free(paths); |
|
return 0; |
|
} |
|
snprintf(full, len, "%s/%s", dir, cmd); |
|
if (access(full, X_OK) == 0) { |
|
free(full); |
|
free(paths); |
|
return 1; // found |
|
} |
|
free(full); |
|
dir = strtok_r(NULL, ":", &saveptr); |
|
} |
|
free(paths); |
|
return 0; // not found |
|
} |
|
|
|
|
|
int main(int argc, char *argv[]) { |
|
int opt; |
|
int exec_mode = 0; |
|
|
|
// First, check that docker exists |
|
if (!command_exists("docker")) { |
|
fprintf(stderr, "Error: \"docker\" command not found in PATH. Please install Docker or make sure PATH is correct.\n"); |
|
exit(EXIT_FAILURE); |
|
} |
|
|
|
// parse -e / --exec |
|
while ((opt = getopt(argc, argv, "e-:h")) != -1) { |
|
switch (opt) { |
|
case 'e': |
|
exec_mode = 1; |
|
break; |
|
case '-': |
|
// long options parsing for --exec or --help |
|
if (strcmp(optarg, "exec") == 0) { |
|
exec_mode = 1; |
|
} else if (strcmp(optarg, "help") == 0) { |
|
print_usage(argv[0]); |
|
exit(EXIT_SUCCESS); |
|
} else { |
|
fprintf(stderr, "Unknown option --%s\n", optarg); |
|
print_usage(argv[0]); |
|
exit(EXIT_FAILURE); |
|
} |
|
break; |
|
case 'h': |
|
print_usage(argv[0]); |
|
exit(EXIT_SUCCESS); |
|
case '?': |
|
default: |
|
print_usage(argv[0]); |
|
exit(EXIT_FAILURE); |
|
} |
|
} |
|
|
|
if (exec_mode) { |
|
// after -e, expect at least container and command |
|
if (optind + 1 >= argc) { |
|
fprintf(stderr, "Error: -e requires container and command\n"); |
|
print_usage(argv[0]); |
|
exit(EXIT_FAILURE); |
|
} |
|
char *container = argv[optind]; |
|
char *cmd = argv[optind+1]; |
|
char **cmd_args = &argv[optind+1]; // command + its args |
|
|
|
// Build argument list for execvp |
|
// e.g. docker exec -it <container> <cmd> <args...> |
|
// You might want interactivity etc, but start simple: |
|
int num_args = argc - (optind+1) + 4; |
|
char **args = malloc((num_args + 1) * sizeof(char *)); |
|
if (!args) { |
|
perror("malloc"); |
|
exit(EXIT_FAILURE); |
|
} |
|
|
|
args[0] = "docker"; |
|
args[1] = "exec"; |
|
args[2] = "-it"; // optional, only if you want TTY; maybe make this configurable |
|
args[3] = container; |
|
for (int i = 0; i + optind + 1 < argc; i++) { |
|
args[4 + i] = argv[optind + 1 + i]; |
|
} |
|
args[4 + (argc - (optind+1))] = NULL; |
|
|
|
// execute |
|
execvp("docker", args); |
|
// if execvp returns, error |
|
perror("execvp failed"); |
|
free(args); |
|
exit(EXIT_FAILURE); |
|
|
|
} else { |
|
// compose/default mode |
|
// pass all arguments to docker compose |
|
// e.g. docker compose up <args...> or maybe allow arbitrary compose subcommand |
|
|
|
// For example, run `docker compose up` first, or allow specifying subcommand |
|
// Let's assume default is up |
|
// Or check if first non-option arg is a compose subcommand |
|
|
|
// Prepare arguments: "docker", "compose", <args...> |
|
int num_args = (argc - optind) + 2 + 1; |
|
char **args = malloc((num_args + 1) * sizeof(char *)); |
|
if (!args) { |
|
perror("malloc"); |
|
exit(EXIT_FAILURE); |
|
} |
|
args[0] = "docker"; |
|
args[1] = "compose"; |
|
// if no args left (argc==optind), maybe default subcommand |
|
int offset = 2; |
|
if (optind < argc) { |
|
for (int i = 0; i < argc - optind; i++) { |
|
args[offset + i] = argv[optind + i]; |
|
} |
|
offset += argc - optind; |
|
} else { |
|
// no extra args given, default to "up" |
|
args[offset++] = "up"; |
|
} |
|
args[offset] = NULL; |
|
|
|
execvp("docker", args); |
|
perror("execvp failed"); |
|
free(args); |
|
exit(EXIT_FAILURE); |
|
} |
|
} |
|
|