-
Notifications
You must be signed in to change notification settings - Fork 3
/
yaml_file_descriptor_factory.cc
179 lines (165 loc) · 6.23 KB
/
yaml_file_descriptor_factory.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// Copyright (c) 2017-2018 Nuxi, https://nuxi.nl/
//
// SPDX-License-Identifier: BSD-2-Clause
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include <arpc++/arpc++.h>
#include <flower/protocol/switchboard.ad.h>
#include <yaml-cpp/exceptions.h>
#include <yaml-cpp/mark.h>
#include <argdata.hpp>
#include "yaml_file_descriptor_factory.h"
using arpc::ArgdataParser;
using arpc::Channel;
using arpc::ClientContext;
using arpc::CreateChannel;
using arpc::FileDescriptor;
using arpc::Status;
using cloudabi_run::YAMLFileDescriptorFactory;
using flower::protocol::switchboard::ConstrainRequest;
using flower::protocol::switchboard::ConstrainResponse;
using flower::protocol::switchboard::Switchboard;
const argdata_t *YAMLFileDescriptorFactory::GetNull(const YAML::Mark &mark) {
return fallback_->GetNull(mark);
}
const argdata_t *YAMLFileDescriptorFactory::GetScalar(const YAML::Mark &mark,
std::string_view tag,
std::string_view value) {
if (tag == "tag:nuxi.nl,2015:cloudabi/fd") {
// Insert an already existing file descriptor.
int fd;
if (value == "stdout") {
fd = STDOUT_FILENO;
} else if (value == "stderr") {
fd = STDERR_FILENO;
} else {
// File descriptor number.
std::string value_str(value);
std::size_t len;
try {
fd = std::stoi(value_str, &len);
} catch (std::exception &e) {
throw YAML::ParserException(mark, "Invalid file descriptor number");
}
if (len != value_str.size())
throw YAML::ParserException(mark, "Invalid file descriptor number");
}
return argdatas_.emplace_back(argdata_t::create_fd(fd)).get();
} else if (tag == "tag:nuxi.nl,2015:cloudabi/executable") {
return argdatas_.emplace_back(argdata_t::create_fd(execfd_)).get();
} else {
return fallback_->GetScalar(mark, tag, value);
}
}
const argdata_t *YAMLFileDescriptorFactory::GetSequence(
const YAML::Mark &mark, std::string_view tag,
std::vector<const argdata_t *> elements) {
return fallback_->GetSequence(mark, tag, std::move(elements));
}
const argdata_t *YAMLFileDescriptorFactory::GetMap(
const YAML::Mark &mark, std::string_view tag,
std::vector<const argdata_t *> keys,
std::vector<const argdata_t *> values) {
if (tag == "tag:nuxi.nl,2015:cloudabi/file") {
// Open a file by pathname.
// TODO(ed): Make mode and rights adjustable.
std::optional<std::string_view> path;
for (size_t i = 0; i < keys.size(); ++i) {
std::optional<std::string_view> keystr = keys[i]->get_str();
if (!keystr)
throw YAML::ParserException(mark, "Expected string keys");
if (*keystr == "path") {
path = values[i]->get_str();
} else {
std::ostringstream ss;
ss << "Unsupported attribute: " << *keystr;
throw YAML::ParserException(mark, ss.str());
}
}
if (!path)
throw YAML::ParserException(mark, "Missing path attribute");
int fd = open(std::string(*path).c_str(), O_RDONLY);
if (fd < 0) {
std::ostringstream ss;
ss << "Failed to open \"" << *path << "\": " << std::strerror(errno);
throw YAML::ParserException(mark, ss.str());
}
return argdatas_
.emplace_back(argdata_t::create_fd(
fds_.emplace_back(std::make_unique<FileDescriptor>(fd))->get()))
.get();
} else if (tag == "tag:nuxi.nl,2015:cloudabi/flower_switchboard_handle") {
// Create a Flower switchboard handle for network connectivity.
std::optional<std::string_view> switchboard_path;
ArgdataParser parser;
ConstrainRequest request;
for (size_t i = 0; i < keys.size(); ++i) {
std::optional<std::string_view> keystr = keys[i]->get_str();
if (!keystr)
throw YAML::ParserException(mark, "Expected string keys");
if (*keystr == "switchboard_path") {
switchboard_path = values[i]->get_str();
} else if (*keystr == "constraints") {
request.Parse(*values[i], &parser);
} else {
std::ostringstream ss;
ss << "Unsupported attribute: " << *keystr;
throw YAML::ParserException(mark, ss.str());
}
}
if (!switchboard_path)
throw YAML::ParserException(mark, "Missing switchboard_path attribute");
// Connect to the switchboard.
std::unique_ptr<FileDescriptor> root_handle;
{
int s = socket(AF_UNIX, SOCK_STREAM, 0);
if (s < 0)
throw YAML::ParserException(
mark,
std::string("Failed to create socket: ") + std::strerror(errno));
root_handle = std::make_unique<FileDescriptor>(s);
union {
struct sockaddr_un sun;
struct sockaddr sa;
} address = {};
address.sun.sun_family = AF_UNIX;
if (switchboard_path->size() >= std::size(address.sun.sun_path))
throw YAML::ParserException(mark, "Switchboard path too long");
std::copy(switchboard_path->begin(), switchboard_path->end(),
address.sun.sun_path);
if (connect(root_handle->get(), &address.sa, sizeof(address)) != 0)
throw YAML::ParserException(
mark, std::string("Failed to connect to the switchboard: ") +
std::strerror(errno));
}
// Create a constrained channel.
std::shared_ptr<Channel> channel = CreateChannel(std::move(root_handle));
std::unique_ptr<Switchboard::Stub> stub = Switchboard::NewStub(channel);
ClientContext context;
ConstrainResponse response;
if (Status status = stub->Constrain(&context, request, &response);
!status.ok())
throw YAML::ParserException(
mark, std::string("Failed to constrain switchboard channel: ") +
status.error_message());
if (!response.switchboard())
throw YAML::ParserException(
mark, "Switchboard did not return a file descriptor");
return argdatas_
.emplace_back(argdata_t::create_fd(
fds_.emplace_back(response.switchboard())->get()))
.get();
} else {
return fallback_->GetMap(mark, tag, std::move(keys), std::move(values));
}
}