annotate vamp-server/simple-server.cpp @ 155:7d58d25f687f

Ensure example plugins are available
author Chris Cannam <cannam@all-day-breakfast.com>
date Fri, 20 Jan 2017 18:41:25 +0000
parents 80d85794d8cd
children 0876b5e67afe
rev   line source
c@125 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
c@125 2 /*
c@125 3 Piper C++
c@125 4
c@125 5 An API for audio analysis and feature extraction plugins.
c@125 6
c@125 7 Centre for Digital Music, Queen Mary, University of London.
c@125 8 Copyright 2006-2016 Chris Cannam and QMUL.
c@125 9
c@125 10 Permission is hereby granted, free of charge, to any person
c@125 11 obtaining a copy of this software and associated documentation
c@125 12 files (the "Software"), to deal in the Software without
c@125 13 restriction, including without limitation the rights to use, copy,
c@125 14 modify, merge, publish, distribute, sublicense, and/or sell copies
c@125 15 of the Software, and to permit persons to whom the Software is
c@125 16 furnished to do so, subject to the following conditions:
c@125 17
c@125 18 The above copyright notice and this permission notice shall be
c@125 19 included in all copies or substantial portions of the Software.
c@125 20
c@125 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
c@125 22 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
c@125 23 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
c@125 24 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
c@125 25 ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
c@125 26 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
c@125 27 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
c@125 28
c@125 29 Except as contained in this notice, the names of the Centre for
c@125 30 Digital Music; Queen Mary, University of London; and Chris Cannam
c@125 31 shall not be used in advertising or otherwise to promote the sale,
c@125 32 use or other dealings in this Software without prior written
c@125 33 authorization.
c@125 34 */
c@125 35
c@125 36 #include "vamp-json/VampJson.h"
c@125 37 #include "vamp-capnp/VampnProto.h"
c@125 38 #include "vamp-support/RequestOrResponse.h"
c@125 39 #include "vamp-support/CountingPluginHandleMapper.h"
c@125 40 #include "vamp-support/LoaderRequests.h"
c@125 41
c@125 42 #include <iostream>
c@125 43 #include <sstream>
c@125 44 #include <stdexcept>
c@125 45
c@125 46 #include <capnp/serialize.h>
c@125 47
c@125 48 #include <map>
c@125 49 #include <set>
c@125 50
c@125 51 // pid for logging
c@125 52 #ifdef _WIN32
c@125 53 #include <process.h>
c@125 54 static int pid = _getpid();
c@125 55 #else
c@125 56 #include <unistd.h>
c@125 57 static int pid = getpid();
c@125 58 #endif
c@125 59
c@138 60 // for _setmode stuff and _dup
c@125 61 #ifdef _WIN32
c@125 62 #include <io.h>
c@125 63 #include <fcntl.h>
c@125 64 #endif
c@125 65
c@138 66 // for dup, open etc
c@138 67 #ifndef _WIN32
c@138 68 #include <fcntl.h>
c@138 69 #include <unistd.h>
c@138 70 #endif
c@138 71
c@125 72 using namespace std;
c@125 73 using namespace json11;
c@125 74 using namespace piper_vamp;
c@125 75 using namespace Vamp;
c@125 76
c@125 77 static string myname = "piper-vamp-simple-server";
c@125 78
c@125 79 static void version()
c@125 80 {
c@125 81 cout << "1.0" << endl;
c@125 82 exit(0);
c@125 83 }
c@125 84
c@125 85 static void usage(bool successful = false)
c@125 86 {
c@125 87 cerr << "\n" << myname <<
c@125 88 ": Load & run Vamp plugins in response to Piper messages\n\n"
c@125 89 " Usage: " << myname << " [-d] <format>\n"
c@125 90 " " << myname << " -v\n"
c@125 91 " " << myname << " -h\n\n"
c@125 92 " where\n"
c@125 93 " <format>: the format to read and write messages in (\"json\" or \"capnp\")\n"
c@125 94 " -d: also print debug information to stderr\n"
c@125 95 " -v: print version number to stdout and exit\n"
c@125 96 " -h: print this text to stderr and exit\n\n"
c@125 97 "Expects Piper request messages in either Cap'n Proto or JSON format on stdin,\n"
c@125 98 "and writes response messages in the same format to stdout.\n\n"
c@125 99 "This server is intended for simple process separation. It's only suitable for\n"
c@125 100 "use with a single trusted client per server invocation.\n\n"
c@125 101 "The two formats behave differently in case of parser errors. JSON messages are\n"
c@125 102 "expected one per input line; because the JSON support is really intended for\n"
c@125 103 "interactive troubleshooting, any unparseable message is reported and discarded\n"
c@125 104 "and the server waits for another message. In contrast, because of the assumption\n"
c@125 105 "that the client is trusted and coupled to the server instance, a mangled\n"
c@125 106 "Cap'n Proto message causes the server to exit.\n\n";
c@125 107 if (successful) exit(0);
c@125 108 else exit(2);
c@125 109 }
c@125 110
c@125 111 static CountingPluginHandleMapper mapper;
c@125 112
c@138 113 // We write our output to stdout, but want to ensure that the plugin
c@138 114 // doesn't write anything itself. To do this we open a null file
c@138 115 // descriptor and dup2() it into place of stdout in the gaps between
c@138 116 // our own output activity.
c@138 117
c@138 118 static int normalFd = -1;
c@138 119 static int suspendedFd = -1;
c@138 120
c@138 121 static void initFds(bool binary)
c@138 122 {
c@138 123 #ifdef _WIN32
c@138 124 if (binary) {
c@138 125 int result = _setmode(0, _O_BINARY);
c@138 126 if (result == -1) {
c@138 127 throw runtime_error("Failed to set binary mode on stdin");
c@138 128 }
c@138 129 result = _setmode(1, _O_BINARY);
c@138 130 if (result == -1) {
c@138 131 throw runtime_error("Failed to set binary mode on stdout");
c@138 132 }
c@138 133 }
c@138 134 normalFd = _dup(1);
c@138 135 suspendedFd = _open("NUL", _O_WRONLY);
c@138 136 #else
c@141 137 (void)binary;
c@138 138 normalFd = dup(1);
c@138 139 suspendedFd = open("/dev/null", O_WRONLY);
c@138 140 #endif
c@138 141
c@138 142 if (normalFd < 0 || suspendedFd < 0) {
c@138 143 throw runtime_error("Failed to initialise fds for stdio suspend/resume");
c@138 144 }
c@138 145 }
c@138 146
c@138 147 static void suspendOutput()
c@138 148 {
c@138 149 #ifdef _WIN32
c@138 150 _dup2(suspendedFd, 1);
c@138 151 #else
c@138 152 dup2(suspendedFd, 1);
c@138 153 #endif
c@138 154 }
c@138 155
c@138 156 static void resumeOutput()
c@138 157 {
c@138 158 #ifdef _WIN32
c@138 159 _dup2(normalFd, 1);
c@138 160 #else
c@138 161 dup2(normalFd, 1);
c@138 162 #endif
c@138 163 }
c@138 164
c@125 165 static RequestOrResponse::RpcId
c@125 166 readId(const piper::RpcRequest::Reader &r)
c@125 167 {
c@125 168 int number;
c@125 169 string tag;
c@125 170 switch (r.getId().which()) {
c@125 171 case piper::RpcRequest::Id::Which::NUMBER:
c@125 172 number = r.getId().getNumber();
c@125 173 return { RequestOrResponse::RpcId::Number, number, "" };
c@125 174 case piper::RpcRequest::Id::Which::TAG:
c@125 175 tag = r.getId().getTag();
c@125 176 return { RequestOrResponse::RpcId::Tag, 0, tag };
c@125 177 case piper::RpcRequest::Id::Which::NONE:
c@125 178 return { RequestOrResponse::RpcId::Absent, 0, "" };
c@125 179 }
cannam@154 180 return { RequestOrResponse::RpcId::Absent, 0, "" };
c@125 181 }
c@125 182
c@125 183 static void
c@125 184 buildId(piper::RpcResponse::Builder &b, const RequestOrResponse::RpcId &id)
c@125 185 {
c@125 186 switch (id.type) {
c@125 187 case RequestOrResponse::RpcId::Number:
c@125 188 b.getId().setNumber(id.number);
c@125 189 break;
c@125 190 case RequestOrResponse::RpcId::Tag:
c@125 191 b.getId().setTag(id.tag);
c@125 192 break;
c@125 193 case RequestOrResponse::RpcId::Absent:
c@125 194 b.getId().setNone();
c@125 195 break;
c@125 196 }
c@125 197 }
c@125 198
c@125 199 static RequestOrResponse::RpcId
c@125 200 readJsonId(const Json &j)
c@125 201 {
c@125 202 RequestOrResponse::RpcId id;
c@125 203
c@125 204 if (j["id"].is_number()) {
c@125 205 id.type = RequestOrResponse::RpcId::Number;
c@140 206 id.number = int(round(j["id"].number_value()));
c@125 207 } else if (j["id"].is_string()) {
c@125 208 id.type = RequestOrResponse::RpcId::Tag;
c@125 209 id.tag = j["id"].string_value();
c@125 210 } else {
c@125 211 id.type = RequestOrResponse::RpcId::Absent;
c@125 212 }
c@125 213
c@125 214 return id;
c@125 215 }
c@125 216
c@125 217 static Json
c@125 218 writeJsonId(const RequestOrResponse::RpcId &id)
c@125 219 {
c@125 220 if (id.type == RequestOrResponse::RpcId::Number) {
c@125 221 return id.number;
c@125 222 } else if (id.type == RequestOrResponse::RpcId::Tag) {
c@125 223 return id.tag;
c@125 224 } else {
c@125 225 return Json();
c@125 226 }
c@125 227 }
c@125 228
c@125 229 static Json
c@125 230 convertRequestJson(string input, string &err)
c@125 231 {
c@125 232 Json j = Json::parse(input, err);
c@125 233 if (err != "") {
c@125 234 err = "invalid json: " + err;
c@125 235 return {};
c@125 236 }
c@125 237 if (!j.is_object()) {
c@125 238 err = "object expected at top level";
c@125 239 } else if (!j["method"].is_string()) {
c@125 240 err = "string expected for method field";
c@125 241 } else if (!j["params"].is_null() && !j["params"].is_object()) {
c@125 242 err = "object expected for params field";
c@125 243 }
c@125 244 return j;
c@125 245 }
c@125 246
c@125 247 RequestOrResponse
c@125 248 readRequestJson(string &err)
c@125 249 {
c@125 250 RequestOrResponse rr;
c@125 251 rr.direction = RequestOrResponse::Request;
c@125 252
c@125 253 string input;
c@125 254 if (!getline(cin, input)) {
c@125 255 // the EOF case, not actually an error
c@125 256 rr.type = RRType::NotValid;
c@125 257 return rr;
c@125 258 }
c@125 259
c@125 260 Json j = convertRequestJson(input, err);
c@125 261 if (err != "") return {};
c@125 262
c@125 263 rr.type = VampJson::getRequestResponseType(j, err);
c@125 264 if (err != "") return {};
c@125 265
c@125 266 rr.id = readJsonId(j);
c@125 267
c@125 268 VampJson::BufferSerialisation serialisation =
c@125 269 VampJson::BufferSerialisation::Array;
c@125 270
c@125 271 switch (rr.type) {
c@125 272
c@125 273 case RRType::List:
c@130 274 rr.listRequest = VampJson::toRpcRequest_List(j, err);
c@125 275 break;
c@125 276 case RRType::Load:
c@125 277 rr.loadRequest = VampJson::toRpcRequest_Load(j, err);
c@125 278 break;
c@125 279 case RRType::Configure:
c@125 280 rr.configurationRequest = VampJson::toRpcRequest_Configure(j, mapper, err);
c@125 281 break;
c@125 282 case RRType::Process:
c@125 283 rr.processRequest = VampJson::toRpcRequest_Process(j, mapper, serialisation, err);
c@125 284 break;
c@125 285 case RRType::Finish:
c@125 286 rr.finishRequest = VampJson::toRpcRequest_Finish(j, mapper, err);
c@125 287 break;
c@125 288 case RRType::NotValid:
c@125 289 break;
c@125 290 }
c@125 291
c@125 292 return rr;
c@125 293 }
c@125 294
c@125 295 void
c@125 296 writeResponseJson(RequestOrResponse &rr, bool useBase64)
c@125 297 {
c@125 298 Json j;
c@125 299
c@125 300 VampJson::BufferSerialisation serialisation =
c@125 301 (useBase64 ?
c@125 302 VampJson::BufferSerialisation::Base64 :
c@125 303 VampJson::BufferSerialisation::Array);
c@125 304
c@125 305 Json id = writeJsonId(rr.id);
c@125 306
c@125 307 if (!rr.success) {
c@125 308
c@125 309 j = VampJson::fromError(rr.errorText, rr.type, id);
c@125 310
c@125 311 } else {
c@125 312
c@125 313 switch (rr.type) {
c@125 314
c@125 315 case RRType::List:
c@125 316 j = VampJson::fromRpcResponse_List(rr.listResponse, id);
c@125 317 break;
c@125 318 case RRType::Load:
c@125 319 j = VampJson::fromRpcResponse_Load(rr.loadResponse, mapper, id);
c@125 320 break;
c@125 321 case RRType::Configure:
c@125 322 j = VampJson::fromRpcResponse_Configure(rr.configurationResponse,
c@125 323 mapper, id);
c@125 324 break;
c@125 325 case RRType::Process:
c@125 326 j = VampJson::fromRpcResponse_Process
c@125 327 (rr.processResponse, mapper, serialisation, id);
c@125 328 break;
c@125 329 case RRType::Finish:
c@125 330 j = VampJson::fromRpcResponse_Finish
c@125 331 (rr.finishResponse, mapper, serialisation, id);
c@125 332 break;
c@125 333 case RRType::NotValid:
c@125 334 break;
c@125 335 }
c@125 336 }
c@138 337
c@125 338 cout << j.dump() << endl;
c@125 339 }
c@125 340
c@125 341 void
c@138 342 writeExceptionJson(const exception &e, RRType type)
c@125 343 {
c@125 344 Json j = VampJson::fromError(e.what(), type, Json());
c@125 345 cout << j.dump() << endl;
c@125 346 }
c@125 347
c@125 348 RequestOrResponse
c@125 349 readRequestCapnp()
c@125 350 {
c@125 351 RequestOrResponse rr;
c@125 352 rr.direction = RequestOrResponse::Request;
c@125 353
c@125 354 static kj::FdInputStream stream(0); // stdin
c@125 355 static kj::BufferedInputStreamWrapper buffered(stream);
c@125 356
c@125 357 if (buffered.tryGetReadBuffer() == nullptr) {
c@125 358 rr.type = RRType::NotValid;
c@125 359 return rr;
c@125 360 }
c@125 361
c@125 362 capnp::InputStreamMessageReader message(buffered);
c@125 363 piper::RpcRequest::Reader reader = message.getRoot<piper::RpcRequest>();
c@125 364
c@125 365 rr.type = VampnProto::getRequestResponseType(reader);
c@125 366 rr.id = readId(reader);
c@125 367
c@125 368 switch (rr.type) {
c@125 369
c@125 370 case RRType::List:
c@127 371 VampnProto::readRpcRequest_List(rr.listRequest, reader);
c@125 372 break;
c@125 373 case RRType::Load:
c@125 374 VampnProto::readRpcRequest_Load(rr.loadRequest, reader);
c@125 375 break;
c@125 376 case RRType::Configure:
c@125 377 VampnProto::readRpcRequest_Configure(rr.configurationRequest,
c@125 378 reader, mapper);
c@125 379 break;
c@125 380 case RRType::Process:
c@125 381 VampnProto::readRpcRequest_Process(rr.processRequest, reader, mapper);
c@125 382 break;
c@125 383 case RRType::Finish:
c@125 384 VampnProto::readRpcRequest_Finish(rr.finishRequest, reader, mapper);
c@125 385 break;
c@125 386 case RRType::NotValid:
c@125 387 break;
c@125 388 }
c@125 389
c@125 390 return rr;
c@125 391 }
c@125 392
c@125 393 void
c@125 394 writeResponseCapnp(RequestOrResponse &rr)
c@125 395 {
c@125 396 capnp::MallocMessageBuilder message;
c@125 397 piper::RpcResponse::Builder builder = message.initRoot<piper::RpcResponse>();
c@125 398
c@125 399 buildId(builder, rr.id);
c@125 400
c@125 401 if (!rr.success) {
c@125 402
c@125 403 VampnProto::buildRpcResponse_Error(builder, rr.errorText, rr.type);
c@125 404
c@125 405 } else {
c@125 406
c@125 407 switch (rr.type) {
c@125 408
c@125 409 case RRType::List:
c@125 410 VampnProto::buildRpcResponse_List(builder, rr.listResponse);
c@125 411 break;
c@125 412 case RRType::Load:
c@125 413 VampnProto::buildRpcResponse_Load(builder, rr.loadResponse, mapper);
c@125 414 break;
c@125 415 case RRType::Configure:
c@125 416 VampnProto::buildRpcResponse_Configure(builder, rr.configurationResponse, mapper);
c@125 417 break;
c@125 418 case RRType::Process:
c@125 419 VampnProto::buildRpcResponse_Process(builder, rr.processResponse, mapper);
c@125 420 break;
c@125 421 case RRType::Finish:
c@125 422 VampnProto::buildRpcResponse_Finish(builder, rr.finishResponse, mapper);
c@125 423 break;
c@125 424 case RRType::NotValid:
c@125 425 break;
c@125 426 }
c@125 427 }
c@125 428
c@125 429 writeMessageToFd(1, message);
c@125 430 }
c@125 431
c@125 432 void
c@138 433 writeExceptionCapnp(const exception &e, RRType type)
c@125 434 {
c@125 435 capnp::MallocMessageBuilder message;
c@125 436 piper::RpcResponse::Builder builder = message.initRoot<piper::RpcResponse>();
c@125 437 VampnProto::buildRpcResponse_Exception(builder, e, type);
c@125 438
c@125 439 writeMessageToFd(1, message);
c@125 440 }
c@125 441
c@125 442 RequestOrResponse
c@125 443 handleRequest(const RequestOrResponse &request, bool debug)
c@125 444 {
c@125 445 RequestOrResponse response;
c@125 446 response.direction = RequestOrResponse::Response;
c@125 447 response.type = request.type;
c@125 448
c@125 449 switch (request.type) {
c@125 450
c@125 451 case RRType::List:
c@127 452 response.listResponse =
c@127 453 LoaderRequests().listPluginData(request.listRequest);
c@125 454 response.success = true;
c@125 455 break;
c@125 456
c@125 457 case RRType::Load:
c@127 458 response.loadResponse =
c@127 459 LoaderRequests().loadPlugin(request.loadRequest);
c@125 460 if (response.loadResponse.plugin != nullptr) {
c@125 461 mapper.addPlugin(response.loadResponse.plugin);
c@125 462 if (debug) {
c@127 463 cerr << "piper-vamp-server " << pid
c@127 464 << ": loaded plugin, handle = "
c@127 465 << mapper.pluginToHandle(response.loadResponse.plugin)
c@127 466 << endl;
c@125 467 }
c@125 468 response.success = true;
c@125 469 }
c@125 470 break;
c@125 471
c@125 472 case RRType::Configure:
c@125 473 {
c@125 474 auto &creq = request.configurationRequest;
c@125 475 auto h = mapper.pluginToHandle(creq.plugin);
c@125 476 if (mapper.isConfigured(h)) {
c@125 477 throw runtime_error("plugin has already been configured");
c@125 478 }
c@125 479
c@125 480 response.configurationResponse = LoaderRequests().configurePlugin(creq);
c@125 481
c@125 482 if (!response.configurationResponse.outputs.empty()) {
c@125 483 mapper.markConfigured
c@125 484 (h, creq.configuration.channelCount, creq.configuration.blockSize);
c@125 485 response.success = true;
c@125 486 }
c@125 487 break;
c@125 488 }
c@125 489
c@125 490 case RRType::Process:
c@125 491 {
c@125 492 auto &preq = request.processRequest;
c@125 493 auto h = mapper.pluginToHandle(preq.plugin);
c@125 494 if (!mapper.isConfigured(h)) {
c@125 495 throw runtime_error("plugin has not been configured");
c@125 496 }
c@125 497
c@125 498 int channels = int(preq.inputBuffers.size());
c@125 499 if (channels != mapper.getChannelCount(h)) {
c@125 500 throw runtime_error("wrong number of channels supplied to process");
c@125 501 }
c@125 502
c@125 503 const float **fbuffers = new const float *[channels];
c@125 504 for (int i = 0; i < channels; ++i) {
c@125 505 if (int(preq.inputBuffers[i].size()) != mapper.getBlockSize(h)) {
c@125 506 delete[] fbuffers;
c@125 507 throw runtime_error("wrong block size supplied to process");
c@125 508 }
c@125 509 fbuffers[i] = preq.inputBuffers[i].data();
c@125 510 }
c@125 511
c@125 512 response.processResponse.plugin = preq.plugin;
c@125 513 response.processResponse.features =
c@125 514 preq.plugin->process(fbuffers, preq.timestamp);
c@125 515 response.success = true;
c@125 516
c@125 517 delete[] fbuffers;
c@125 518 break;
c@125 519 }
c@125 520
c@125 521 case RRType::Finish:
c@125 522 {
c@125 523 auto &freq = request.finishRequest;
c@125 524 response.finishResponse.plugin = freq.plugin;
c@125 525
c@125 526 auto h = mapper.pluginToHandle(freq.plugin);
c@125 527 // Finish can be called (to unload the plugin) even if the
c@125 528 // plugin has never been configured or used. But we want to
c@125 529 // make sure we call getRemainingFeatures only if we have
c@125 530 // actually configured the plugin.
c@125 531 if (mapper.isConfigured(h)) {
c@125 532 response.finishResponse.features = freq.plugin->getRemainingFeatures();
c@125 533 }
c@125 534
c@125 535 // We do not delete the plugin here -- we need it in the
c@125 536 // mapper when converting the features. It gets deleted in the
c@125 537 // calling function.
c@125 538 response.success = true;
c@125 539 break;
c@125 540 }
c@125 541
c@125 542 case RRType::NotValid:
c@125 543 break;
c@125 544 }
c@125 545
c@125 546 return response;
c@125 547 }
c@125 548
c@125 549 RequestOrResponse
c@125 550 readRequest(string format)
c@125 551 {
c@125 552 if (format == "capnp") {
c@125 553 return readRequestCapnp();
c@125 554 } else if (format == "json") {
c@125 555 string err;
c@125 556 auto result = readRequestJson(err);
c@125 557 if (err != "") throw runtime_error(err);
c@125 558 else return result;
c@125 559 } else {
c@125 560 throw runtime_error("unknown input format \"" + format + "\"");
c@125 561 }
c@125 562 }
c@125 563
c@125 564 void
c@125 565 writeResponse(string format, RequestOrResponse &rr)
c@125 566 {
c@138 567 resumeOutput();
c@125 568 if (format == "capnp") {
c@125 569 writeResponseCapnp(rr);
c@125 570 } else if (format == "json") {
c@125 571 writeResponseJson(rr, false);
c@125 572 } else {
c@125 573 throw runtime_error("unknown output format \"" + format + "\"");
c@125 574 }
c@138 575 suspendOutput();
c@125 576 }
c@125 577
c@125 578 void
c@138 579 writeException(string format, const exception &e, RRType type)
c@125 580 {
c@138 581 resumeOutput();
c@125 582 if (format == "capnp") {
c@125 583 writeExceptionCapnp(e, type);
c@125 584 } else if (format == "json") {
c@125 585 writeExceptionJson(e, type);
c@125 586 } else {
c@125 587 throw runtime_error("unknown output format \"" + format + "\"");
c@125 588 }
c@138 589 suspendOutput();
c@125 590 }
c@125 591
c@125 592 int main(int argc, char **argv)
c@125 593 {
c@125 594 if (argc != 2 && argc != 3) {
c@125 595 usage();
c@125 596 }
c@125 597
c@125 598 bool debug = false;
c@125 599
c@125 600 string arg = argv[1];
c@125 601 if (arg == "-h") {
c@125 602 if (argc == 2) {
c@125 603 usage(true);
c@125 604 } else {
c@125 605 usage();
c@125 606 }
c@125 607 } else if (arg == "-v") {
c@125 608 if (argc == 2) {
c@125 609 version();
c@125 610 } else {
c@125 611 usage();
c@125 612 }
c@125 613 } else if (arg == "-d") {
c@125 614 if (argc == 2) {
c@125 615 usage();
c@125 616 } else {
c@125 617 debug = true;
c@125 618 arg = argv[2];
c@125 619 }
c@125 620 }
c@125 621
c@125 622 string format = arg;
c@125 623
c@125 624 if (format != "capnp" && format != "json") {
c@125 625 usage();
c@125 626 }
c@125 627
c@138 628 try {
c@138 629 initFds(format == "capnp");
c@138 630 } catch (exception &e) {
c@138 631 cerr << "ERROR: " << e.what() << endl;
c@138 632 exit(1);
c@125 633 }
c@138 634
c@138 635 suspendOutput();
c@125 636
c@125 637 if (debug) {
c@125 638 cerr << myname << " " << pid << ": waiting for format: " << format << endl;
c@125 639 }
c@138 640
c@125 641 while (true) {
c@125 642
c@125 643 RequestOrResponse request;
c@125 644
c@125 645 try {
c@125 646
c@125 647 request = readRequest(format);
c@125 648
c@125 649 // NotValid without an exception indicates EOF:
c@125 650 if (request.type == RRType::NotValid) {
c@125 651 if (debug) {
c@125 652 cerr << myname << " " << pid << ": eof reached, exiting" << endl;
c@125 653 }
c@125 654 break;
c@125 655 }
c@125 656
c@125 657 if (debug) {
c@125 658 cerr << myname << " " << pid << ": request received, of type "
c@125 659 << int(request.type)
c@125 660 << endl;
c@125 661 }
c@125 662
c@125 663 RequestOrResponse response = handleRequest(request, debug);
c@125 664 response.id = request.id;
c@125 665
c@125 666 if (debug) {
c@125 667 cerr << myname << " " << pid << ": request handled, writing response"
c@125 668 << endl;
c@125 669 }
c@125 670
c@125 671 writeResponse(format, response);
c@125 672
c@125 673 if (debug) {
c@125 674 cerr << myname << " " << pid << ": response written" << endl;
c@125 675 }
c@125 676
c@125 677 if (request.type == RRType::Finish) {
c@125 678 auto h = mapper.pluginToHandle(request.finishRequest.plugin);
c@125 679 if (debug) {
c@125 680 cerr << myname << " " << pid << ": deleting the plugin with handle " << h << endl;
c@125 681 }
c@125 682 mapper.removePlugin(h);
c@125 683 delete request.finishRequest.plugin;
c@125 684 }
c@125 685
c@138 686 } catch (exception &e) {
c@125 687
c@125 688 if (debug) {
c@125 689 cerr << myname << " " << pid << ": error: " << e.what() << endl;
c@125 690 }
c@125 691
c@125 692 writeException(format, e, request.type);
c@125 693
c@125 694 if (format == "capnp") {
c@125 695 // Don't try to continue; we can't recover from a
c@125 696 // mangled input stream. However, we can return a
c@125 697 // successful error code because we are reporting the
c@125 698 // status in our Capnp output stream instead
c@125 699 if (debug) {
c@125 700 cerr << myname << " " << pid << ": not attempting to recover from capnp parse problems, exiting" << endl;
c@125 701 }
c@125 702 exit(0);
c@125 703 }
c@125 704 }
c@125 705 }
c@125 706
c@125 707 exit(0);
c@125 708 }