1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

UNIXドメインソケットを使ってDockerのバージョンを取得する

Posted at

手っ取り早く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
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?