手っ取り早くUNIXドメインソケットを利用したプログラムを作るためのサンプルです。
ローカルPCにDockerがインストールされている前提で話を進めます。ターミナルからdocker -v
を実行するとバージョンが表示されることと思います。筆者の環境では次のようになりました。
$ docker -v
Docker version 18.09.2, build 6247962
このバージョン番号を取得するプログラムをC++で作成します。
dockerが動作しているマシンでは、/var/run/docker.sock
というソケットが存在しており、これに接続すると、HTTPと同じ要領でdockerの機能にアクセスすることができます。詳細は公式のAPIドキュメントを参照していただくとして、最初の一歩となるプログラムを作成してみました。
GET /v1.20/version HTTP/1.1
というリクエストを行うと、次のようなJSONデータが返ります。
.json
{
"ApiVersion": "1.39",
"Arch": "amd64",
"BuildTime": "2019-02-12T22:47:29.000000000+00:00",
"Components": [
{
"Details": {
"ApiVersion": "1.39",
"Arch": "amd64",
"BuildTime": "2019-02-12T22:47:29.000000000+00:00",
"Experimental": "false",
"GitCommit": "6247962",
"GoVersion": "go1.10.4",
"KernelVersion": "4.15.0-47-generic",
"MinAPIVersion": "1.12",
"Os": "linux"
},
"Name": "Engine",
"Version": "18.09.2"
}
],
"GitCommit": "6247962",
"GoVersion": "go1.10.4",
"KernelVersion": "4.15.0-47-generic",
"MinAPIVersion": "1.12",
"Os": "linux",
"Platform": {
"Name": ""
},
"Version": "18.09.2"
}
この中からVersion
の値を取り出します。
JSONパーサはpicojsonを使用しました。
.cpp
#include "picojson.h"
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
int write_string(int fd, char const *p)
{
return ::write(fd, p, strlen(p));
}
void parse_response(char const *begin, char const *end, std::vector<std::string> *headers, std::vector<char> *contents)
{
headers->clear();
contents->clear();
char const *head = begin;
char const *next = begin;
while (1) {
int c = 0;
if (next < end) {
c = (unsigned char)*next;
}
if (c == '\r' || c == '\n' || c == 0) {
bool empty = false;
if (head == next) {
empty = true;
} else {
std::string line(head, next);
headers->push_back(line);
}
if (c == 0) break;
next++;
if (c == '\r' && next < end && *next == '\n') {
next++;
}
if (empty) {
contents->insert(contents->end(), next, end);
return;
}
head = next;
} else {
next++;
}
}
}
int main()
{
std::string result;
struct sockaddr_un sa = {};
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
sa.sun_family = AF_UNIX;
memset(sa.sun_path, 0, sizeof(sa.sun_path));
strcpy(sa.sun_path, "/var/run/docker.sock");
if (::connect(sock, (struct sockaddr *)&sa, sizeof(struct sockaddr_un)) != -1) {
write_string(sock, "GET /v1.20/version HTTP/1.1\r\n");
write_string(sock, "Host: localhost\r\n");
write_string(sock, "\r\n");
char tmp[10000];
int n = ::read(sock, tmp, sizeof(tmp));
if (n > 0) {
bool ok = false;
std::vector<char> contents;
{
std::vector<std::string> headers;
char const *begin = tmp;
char const *end = tmp + n;
parse_response(begin, end, &headers, &contents);
if (headers.size() > 0) {
int a, b, c;
if (sscanf(headers[0].c_str(), "HTTP/%u.%u %u OK", &a, &b, &c) == 3 && c == 200) {
ok = true;
}
}
}
std::string err;
picojson::value v;
picojson::parse(v, contents.begin(), contents.end(), &err);
if (v.is<picojson::object>()) {
picojson::object &obj = v.get<picojson::object>();
result = obj["Version"].to_str();
}
}
}
::close(sock);
puts(result.c_str());
return 0;
}
実行結果は次のようになります。
$ g++ main.cpp
$ ./a.out
18.09.2