+bool parse_udpstream(const ConfigLine &line, Config *config)
+{
+ if (line.arguments.size() != 1) {
+ log(ERROR, "'udpstream' takes exactly one argument");
+ return false;
+ }
+
+ UDPStreamConfig udpstream;
+
+ string hostport = line.arguments[0];
+ if (!parse_hostport(hostport, &udpstream.dst)) {
+ return false;
+ }
+
+ const auto src_it = line.parameters.find("src");
+ if (src_it == line.parameters.end()) {
+ // This is pretty meaningless, but OK, consistency is good.
+ log(WARNING, "udpstream to %s has no src= attribute, clients will not get any data.",
+ hostport.c_str());
+ } else {
+ udpstream.src = src_it->second;
+ // TODO: Verify that the URL is parseable?
+ }
+
+ // Parse the pacing rate, converting from kilobits to bytes as needed.
+ const auto pacing_rate_it = line.parameters.find("pacing_rate_kbit");
+ if (pacing_rate_it == line.parameters.end()) {
+ udpstream.pacing_rate = ~0U;
+ } else {
+ udpstream.pacing_rate = stoi(pacing_rate_it->second) * 1024 / 8;
+ }
+
+ // Parse the TTL. The same value is used for unicast and multicast.
+ const auto ttl_it = line.parameters.find("ttl");
+ if (ttl_it == line.parameters.end()) {
+ udpstream.ttl = -1;
+ } else {
+ udpstream.ttl = stoi(ttl_it->second);
+ }
+
+ // Parse the multicast interface index.
+ const auto multicast_iface_it = line.parameters.find("multicast_output_interface");
+ if (multicast_iface_it == line.parameters.end()) {
+ udpstream.multicast_iface_index = -1;
+ } else {
+ udpstream.multicast_iface_index = if_nametoindex(multicast_iface_it->second.c_str());
+ if (udpstream.multicast_iface_index == 0) {
+ log(ERROR, "Interface '%s' does not exist", multicast_iface_it->second.c_str());
+ return false;
+ }
+ }
+
+ config->udpstreams.push_back(udpstream);
+ return true;
+}
+
+bool parse_gen204(const ConfigLine &line, Config *config)
+{
+ if (line.arguments.size() != 1) {
+ log(ERROR, "'gen204' takes exactly one argument");
+ return false;
+ }
+
+ Gen204Config gen204;
+ gen204.url = line.arguments[0];
+
+ // Parse the CORS origin, if it exists.
+ const auto allow_origin_it = line.parameters.find("allow_origin");
+ if (allow_origin_it != line.parameters.end()) {
+ gen204.allow_origin = allow_origin_it->second;
+ }
+
+ config->pings.push_back(gen204);
+ return true;
+}
+
+bool parse_error_log(const ConfigLine &line, Config *config)
+{
+ if (line.arguments.size() != 0) {
+ log(ERROR, "'error_log' takes no arguments (only parameters type= and filename=)");
+ return false;
+ }
+
+ LogConfig log_config;
+ const auto type_it = line.parameters.find("type");
+ if (type_it == line.parameters.end()) {
+ log(ERROR, "'error_log' has no type= parameter");
+ return false;
+ }
+
+ string type = type_it->second;
+ if (type == "file") {
+ log_config.type = LogConfig::LOG_TYPE_FILE;
+ } else if (type == "syslog") {
+ log_config.type = LogConfig::LOG_TYPE_SYSLOG;
+ } else if (type == "console") {
+ log_config.type = LogConfig::LOG_TYPE_CONSOLE;
+ } else {
+ log(ERROR, "Unknown log type '%s'", type.c_str());
+ return false;
+ }
+
+ if (log_config.type == LogConfig::LOG_TYPE_FILE) {
+ const auto filename_it = line.parameters.find("filename");
+ if (filename_it == line.parameters.end()) {
+ log(ERROR, "error_log type 'file' with no filename= parameter");
+ return false;
+ }
+ log_config.filename = filename_it->second;
+ }
+
+ config->log_destinations.push_back(log_config);
+ return true;
+}
+
+} // namespace
+