+static int c_grpc_write(__attribute__((unused)) data_set_t const *ds,
+ value_list_t const *vl, user_data_t *ud) {
+ CollectdClient *c = (CollectdClient *)ud->data;
+ return c->PutValues(vl);
+}
+
+static int c_grpc_config_listen(oconfig_item_t *ci) {
+ if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_STRING) ||
+ (ci->values[1].type != OCONFIG_TYPE_STRING)) {
+ ERROR("grpc: The `%s` config option needs exactly "
+ "two string argument (address and port).",
+ ci->key);
+ return -1;
+ }
+
+ auto listener = Listener();
+ listener.addr = grpc::string(ci->values[0].value.string);
+ listener.port = grpc::string(ci->values[1].value.string);
+ listener.ssl = nullptr;
+
+ auto ssl_opts = new (grpc::SslServerCredentialsOptions);
+ grpc::SslServerCredentialsOptions::PemKeyCertPair pkcp = {};
+ bool use_ssl = false;
+
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (!strcasecmp("EnableSSL", child->key)) {
+ if (cf_util_get_boolean(child, &use_ssl)) {
+ ERROR("grpc: Option `%s` expects a boolean value", child->key);
+ return -1;
+ }
+ } else if (!strcasecmp("SSLCACertificateFile", child->key)) {
+ char *certs = NULL;
+ if (cf_util_get_string(child, &certs)) {
+ ERROR("grpc: Option `%s` expects a string value", child->key);
+ return -1;
+ }
+ ssl_opts->pem_root_certs = read_file(certs);
+ } else if (!strcasecmp("SSLCertificateKeyFile", child->key)) {
+ char *key = NULL;
+ if (cf_util_get_string(child, &key)) {
+ ERROR("grpc: Option `%s` expects a string value", child->key);
+ return -1;
+ }
+ pkcp.private_key = read_file(key);
+ } else if (!strcasecmp("SSLCertificateFile", child->key)) {
+ char *cert = NULL;
+ if (cf_util_get_string(child, &cert)) {
+ ERROR("grpc: Option `%s` expects a string value", child->key);
+ return -1;
+ }
+ pkcp.cert_chain = read_file(cert);
+ } else {
+ WARNING("grpc: Option `%s` not allowed in <%s> block.", child->key,
+ ci->key);
+ }
+ }
+
+ ssl_opts->pem_key_cert_pairs.push_back(pkcp);
+ if (use_ssl)
+ listener.ssl = ssl_opts;
+ else
+ delete (ssl_opts);
+
+ listeners.push_back(listener);
+ return 0;
+} /* c_grpc_config_listen() */
+
+static int c_grpc_config_server(oconfig_item_t *ci) {
+ if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_STRING) ||
+ (ci->values[1].type != OCONFIG_TYPE_STRING)) {
+ ERROR("grpc: The `%s` config option needs exactly "
+ "two string argument (address and port).",
+ ci->key);
+ return -1;
+ }
+
+ grpc::SslCredentialsOptions ssl_opts;
+ bool use_ssl = false;
+
+ for (int i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (!strcasecmp("EnableSSL", child->key)) {
+ if (cf_util_get_boolean(child, &use_ssl)) {
+ return -1;
+ }
+ } else if (!strcasecmp("SSLCACertificateFile", child->key)) {
+ char *certs = NULL;
+ if (cf_util_get_string(child, &certs)) {
+ return -1;
+ }
+ ssl_opts.pem_root_certs = read_file(certs);
+ } else if (!strcasecmp("SSLCertificateKeyFile", child->key)) {
+ char *key = NULL;
+ if (cf_util_get_string(child, &key)) {
+ return -1;
+ }
+ ssl_opts.pem_private_key = read_file(key);
+ } else if (!strcasecmp("SSLCertificateFile", child->key)) {
+ char *cert = NULL;
+ if (cf_util_get_string(child, &cert)) {
+ return -1;
+ }
+ ssl_opts.pem_cert_chain = read_file(cert);
+ } else {
+ WARNING("grpc: Option `%s` not allowed in <%s> block.", child->key,
+ ci->key);
+ }
+ }
+
+ auto node = grpc::string(ci->values[0].value.string);
+ auto service = grpc::string(ci->values[1].value.string);
+ auto addr = node + ":" + service;
+
+ CollectdClient *client;
+ if (use_ssl) {
+ auto channel_creds = grpc::SslCredentials(ssl_opts);
+ auto channel = grpc::CreateChannel(addr, channel_creds);
+ client = new CollectdClient(channel);
+ } else {
+ auto channel =
+ grpc::CreateChannel(addr, grpc::InsecureChannelCredentials());
+ client = new CollectdClient(channel);
+ }
+
+ auto callback_name = grpc::string("grpc/") + addr;
+ user_data_t ud = {
+ .data = client, .free_func = c_grpc_destroy_write_callback,
+ };
+
+ plugin_register_write(callback_name.c_str(), c_grpc_write, &ud);
+ return 0;
+} /* c_grpc_config_server() */
+
+static int c_grpc_config(oconfig_item_t *ci) {
+ int i;
+
+ for (i = 0; i < ci->children_num; i++) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (!strcasecmp("Listen", child->key)) {
+ if (c_grpc_config_listen(child))
+ return -1;
+ } else if (!strcasecmp("Server", child->key)) {
+ if (c_grpc_config_server(child))
+ return -1;
+ }
+
+ else {
+ WARNING("grpc: Option `%s` not allowed here.", child->key);
+ }
+ }
+
+ return 0;
+} /* c_grpc_config() */
+
+static int c_grpc_init(void) {
+ server = new CollectdServer();
+ if (!server) {
+ ERROR("grpc: Failed to create server");
+ return -1;
+ }
+
+ server->Start();
+ return 0;
+} /* c_grpc_init() */
+
+static int c_grpc_shutdown(void) {
+ if (!server)
+ return 0;
+
+ server->Shutdown();
+
+ delete server;
+ server = nullptr;
+
+ return 0;
+} /* c_grpc_shutdown() */
+
+void module_register(void) {
+ plugin_register_complex_config("grpc", c_grpc_config);
+ plugin_register_init("grpc", c_grpc_init);
+ plugin_register_shutdown("grpc", c_grpc_shutdown);
+} /* module_register() */
+} /* extern "C" */