2018-02-18 18:16:37 -08:00
|
|
|
# vim: set et nosta sw=4 ts=4 :
|
2018-02-12 12:26:16 -08:00
|
|
|
#
|
2020-07-25 15:05:26 -07:00
|
|
|
# Copyright (c) 2018-2020, Mahlon E. Smith <mahlon@martini.nu>
|
2018-02-12 12:26:16 -08:00
|
|
|
# All rights reserved.
|
|
|
|
|
# Redistribution and use in source and binary forms, with or without
|
|
|
|
|
# modification, are permitted provided that the following conditions are met:
|
|
|
|
|
#
|
|
|
|
|
# * Redistributions of source code must retain the above copyright
|
|
|
|
|
# notice, this list of conditions and the following disclaimer.
|
|
|
|
|
#
|
|
|
|
|
# * Redistributions in binary form must reproduce the above copyright
|
|
|
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
|
|
|
# documentation and/or other materials provided with the distribution.
|
|
|
|
|
#
|
|
|
|
|
# * Neither the name of Mahlon E. Smith nor the names of his
|
|
|
|
|
# contributors may be used to endorse or promote products derived
|
|
|
|
|
# from this software without specific prior written permission.
|
|
|
|
|
#
|
|
|
|
|
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
|
|
|
|
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
|
# DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
|
|
|
|
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
|
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import
|
|
|
|
|
db_postgres,
|
|
|
|
|
json,
|
|
|
|
|
nativesockets,
|
|
|
|
|
net,
|
2018-11-06 15:54:05 -08:00
|
|
|
parseopt,
|
2018-02-18 18:16:37 -08:00
|
|
|
posix,
|
2021-10-20 11:41:30 -07:00
|
|
|
std/exitprocs,
|
2018-02-12 12:26:16 -08:00
|
|
|
strutils,
|
2020-07-25 15:05:26 -07:00
|
|
|
strformat,
|
2018-02-12 12:26:16 -08:00
|
|
|
tables,
|
2018-02-14 15:41:11 -08:00
|
|
|
terminal,
|
2018-02-18 18:16:37 -08:00
|
|
|
times
|
2018-02-12 12:26:16 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
const
|
2020-07-25 15:05:26 -07:00
|
|
|
VERSION = "v0.3.0"
|
2018-02-12 12:26:16 -08:00
|
|
|
USAGE = """
|
2020-07-25 15:05:26 -07:00
|
|
|
./netdata_tsrelay [-adDhopqtTv]
|
2018-02-14 15:41:11 -08:00
|
|
|
|
2020-07-25 15:05:26 -07:00
|
|
|
-a --listen-addr:
|
|
|
|
|
The outbound IP address to listen for netdata streams.
|
|
|
|
|
|
|
|
|
|
-d --debug:
|
|
|
|
|
Debug: Show incoming and parsed data.
|
|
|
|
|
|
|
|
|
|
-D --dropconn:
|
|
|
|
|
Drop the persistent socket to netdata between samples to conserve
|
|
|
|
|
local resources. This may be helpful with a large number of clients.
|
|
|
|
|
Defaults to false.
|
|
|
|
|
|
|
|
|
|
-h --help:
|
|
|
|
|
Help. You're lookin' at it.
|
|
|
|
|
|
|
|
|
|
-o --dbopts:
|
|
|
|
|
The PostgreSQL connection string parameters.
|
|
|
|
|
The default connection string is:
|
|
|
|
|
"host=localhost dbname=netdata application_name=netdata-tsrelay"
|
|
|
|
|
|
|
|
|
|
-p --listen-port:
|
|
|
|
|
Change the listening port from the default (14866).
|
|
|
|
|
|
|
|
|
|
-P --persistent:
|
|
|
|
|
Don't disconnect from the database between samples. This may be
|
|
|
|
|
more efficient with a small number of clients, when not using a
|
|
|
|
|
pooler, or with a very high sample size/rate. Defaults to false.
|
|
|
|
|
|
|
|
|
|
-q --quiet:
|
|
|
|
|
Quiet mode. No output at all. Ignored if -d is supplied.
|
|
|
|
|
|
|
|
|
|
-T --dbtable:
|
|
|
|
|
Change the destination table name from the default (netdata).
|
|
|
|
|
|
|
|
|
|
-t --timeout:
|
|
|
|
|
Alter the maximum time (in ms) an open socket waits for data
|
|
|
|
|
before processing the sample. Default: 500ms.
|
|
|
|
|
|
2020-12-10 17:20:37 -08:00
|
|
|
-v --version:
|
2020-07-25 15:05:26 -07:00
|
|
|
Display version number.
|
2018-02-12 12:26:16 -08:00
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
INSERT_SQL = """
|
2018-02-19 18:22:25 -08:00
|
|
|
INSERT INTO $1
|
2018-02-12 12:26:16 -08:00
|
|
|
( time, host, metrics )
|
|
|
|
|
VALUES
|
|
|
|
|
( 'epoch'::timestamptz + ? * '1 second'::interval, ?, ? )
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
2018-02-14 15:41:11 -08:00
|
|
|
type
|
|
|
|
|
Config = object of RootObj
|
|
|
|
|
dbopts: string # The postgresql connection parameters. (See https://www.postgresql.org/docs/current/static/libpq-connect.html)
|
2018-02-19 18:22:25 -08:00
|
|
|
dbtable: string # The name of the table to write to.
|
2020-07-25 15:05:26 -07:00
|
|
|
dropconn: bool # Close the TCP connection between samples.
|
|
|
|
|
persistent: bool # Don't close the database handle between samples.
|
2018-02-19 18:22:25 -08:00
|
|
|
listen_port: int # The port to listen for incoming connections.
|
2018-02-14 15:41:11 -08:00
|
|
|
listen_addr: string # The IP address listen for incoming connections. Defaults to inaddr_any.
|
|
|
|
|
verbose: bool # Be informative
|
|
|
|
|
debug: bool # Spew out raw data
|
2018-02-19 18:22:25 -08:00
|
|
|
insertsql: string # The SQL insert string after interpolating the table name.
|
|
|
|
|
timeout: int # How long to block, waiting on connection data.
|
2018-02-12 12:26:16 -08:00
|
|
|
|
2020-07-25 15:05:26 -07:00
|
|
|
|
|
|
|
|
type
|
|
|
|
|
NetdataClient = ref object
|
|
|
|
|
sock: Socket # The raw socket fd
|
|
|
|
|
address: string # The remote IP address
|
|
|
|
|
db: DbConn # An optionally persistent database handle
|
|
|
|
|
|
|
|
|
|
|
2018-02-18 18:16:37 -08:00
|
|
|
# Global configuration
|
|
|
|
|
var conf: Config
|
2018-02-12 12:26:16 -08:00
|
|
|
|
2018-02-18 22:18:44 -08:00
|
|
|
|
2018-02-14 15:41:11 -08:00
|
|
|
proc hl( msg: string, fg: ForegroundColor, bright=false ): string =
|
|
|
|
|
## Quick wrapper for color formatting a string, since the 'terminal'
|
|
|
|
|
## module only deals with stdout directly.
|
2018-02-18 18:16:37 -08:00
|
|
|
if not isatty(stdout): return msg
|
2018-02-14 15:41:11 -08:00
|
|
|
|
|
|
|
|
var color: BiggestInt = ord( fg )
|
|
|
|
|
if bright: inc( color, 60 )
|
|
|
|
|
result = "\e[" & $color & 'm' & msg & "\e[0m"
|
|
|
|
|
|
|
|
|
|
|
2020-07-25 15:05:26 -07:00
|
|
|
proc fetch_data( client: NetdataClient ): string =
|
|
|
|
|
## Netdata JSON backend doesn't send a length nor a separator
|
|
|
|
|
## between samples, so we read line by line and wait for stream
|
|
|
|
|
## timeout to determine what constitutes a sample.
|
2018-11-19 12:05:47 -08:00
|
|
|
var buf = ""
|
2020-07-25 15:05:26 -07:00
|
|
|
while true:
|
|
|
|
|
try:
|
|
|
|
|
client.sock.readline( buf, timeout=conf.timeout )
|
|
|
|
|
if buf == "":
|
|
|
|
|
if conf.debug: echo "Client {client.address} closed socket.".fmt.hl( fgRed, bright=true )
|
|
|
|
|
quit( 1 )
|
|
|
|
|
|
|
|
|
|
result = result & buf & "\n"
|
|
|
|
|
|
|
|
|
|
except OSError:
|
|
|
|
|
quit( 1 )
|
|
|
|
|
except TimeoutError:
|
|
|
|
|
if result == "": continue
|
|
|
|
|
return
|
2018-02-12 12:26:16 -08:00
|
|
|
|
|
|
|
|
|
2018-02-18 18:16:37 -08:00
|
|
|
proc parse_data( data: string ): seq[ JsonNode ] =
|
|
|
|
|
## Given a raw +data+ string, parse JSON and return a sequence
|
|
|
|
|
## of JSON samples. Netdata can buffer multiple samples in one batch.
|
|
|
|
|
result = @[]
|
2018-11-06 15:54:05 -08:00
|
|
|
if data == "": return
|
2018-02-12 12:26:16 -08:00
|
|
|
|
|
|
|
|
# Hash of sample timeperiods to pivoted json data
|
2018-02-18 18:16:37 -08:00
|
|
|
var pivoted_data = init_table[ BiggestInt, JsonNode ]()
|
2018-02-12 12:26:16 -08:00
|
|
|
|
|
|
|
|
for sample in split_lines( data ):
|
2018-11-06 15:54:05 -08:00
|
|
|
if sample == "": continue
|
2018-02-19 18:22:25 -08:00
|
|
|
if conf.debug: echo sample.hl( fgBlack, bright=true )
|
2018-02-12 12:26:16 -08:00
|
|
|
|
|
|
|
|
var parsed: JsonNode
|
|
|
|
|
try:
|
|
|
|
|
parsed = sample.parse_json
|
|
|
|
|
except JsonParsingError:
|
2018-02-14 15:41:11 -08:00
|
|
|
if conf.debug: echo hl( "Unable to parse sample line: " & sample.hl(fgRed, bright=true), fgRed )
|
2018-02-18 18:16:37 -08:00
|
|
|
continue
|
|
|
|
|
if parsed.kind != JObject: return
|
2018-02-12 12:26:16 -08:00
|
|
|
|
|
|
|
|
# Create or use existing Json object for modded data.
|
|
|
|
|
#
|
|
|
|
|
var pivot: JsonNode
|
2018-02-18 18:16:37 -08:00
|
|
|
try:
|
2018-11-06 15:54:05 -08:00
|
|
|
let key = parsed[ "timestamp" ].get_int
|
2018-02-18 18:16:37 -08:00
|
|
|
|
|
|
|
|
if pivoted_data.has_key( key ):
|
|
|
|
|
pivot = pivoted_data[ key ]
|
|
|
|
|
else:
|
|
|
|
|
pivot = newJObject()
|
|
|
|
|
pivoted_data[ key ] = pivot
|
2018-02-12 12:26:16 -08:00
|
|
|
|
2018-02-18 18:16:37 -08:00
|
|
|
var name = parsed[ "chart_id" ].get_str & "." & parsed[ "id" ].get_str
|
|
|
|
|
pivot[ "hostname" ] = parsed[ "hostname" ]
|
|
|
|
|
pivot[ "timestamp" ] = parsed[ "timestamp" ]
|
|
|
|
|
pivot[ name ] = parsed[ "value" ]
|
2020-12-12 21:46:39 -08:00
|
|
|
|
|
|
|
|
if parsed.has_key( "labels" ):
|
|
|
|
|
pivot[ "labels" ] = parsed[ "labels" ]
|
2018-02-18 18:16:37 -08:00
|
|
|
except:
|
|
|
|
|
continue
|
2018-02-12 12:26:16 -08:00
|
|
|
|
2018-02-18 18:16:37 -08:00
|
|
|
for timestamp, sample in pivoted_data:
|
|
|
|
|
result.add( sample )
|
2018-02-12 12:26:16 -08:00
|
|
|
|
|
|
|
|
|
2020-07-25 15:05:26 -07:00
|
|
|
proc write_to_database( client: NetdataClient, samples: seq[ JsonNode ] ): void =
|
2018-02-18 18:16:37 -08:00
|
|
|
## Given a sequence of json samples, write them to database.
|
|
|
|
|
if samples.len == 0: return
|
2018-02-12 12:26:16 -08:00
|
|
|
|
2020-07-25 15:05:26 -07:00
|
|
|
if client.db.isNil:
|
|
|
|
|
client.db = open( "", "", "", conf.dbopts )
|
2018-02-18 18:16:37 -08:00
|
|
|
|
|
|
|
|
try:
|
2020-07-25 15:05:26 -07:00
|
|
|
client.db.exec sql( "BEGIN" )
|
2018-02-18 18:16:37 -08:00
|
|
|
for sample in samples:
|
|
|
|
|
var
|
2018-11-06 15:54:05 -08:00
|
|
|
timestamp = sample[ "timestamp" ].get_int
|
2018-06-26 09:47:23 -07:00
|
|
|
host = sample[ "hostname" ].get_str.to_lowerascii
|
2018-02-18 18:16:37 -08:00
|
|
|
sample.delete( "timestamp" )
|
|
|
|
|
sample.delete( "hostname" )
|
2020-07-25 15:05:26 -07:00
|
|
|
client.db.exec sql( conf.insertsql ), timestamp, host, sample
|
|
|
|
|
client.db.exec sql( "COMMIT" )
|
2018-02-18 18:16:37 -08:00
|
|
|
except:
|
|
|
|
|
let
|
|
|
|
|
e = getCurrentException()
|
|
|
|
|
msg = getCurrentExceptionMsg()
|
|
|
|
|
echo "Got exception ", repr(e), " while writing to DB: ", msg
|
|
|
|
|
discard
|
|
|
|
|
|
2020-07-25 15:05:26 -07:00
|
|
|
if not conf.persistent:
|
|
|
|
|
client.db.close
|
|
|
|
|
client.db = nil
|
2018-02-18 18:16:37 -08:00
|
|
|
|
|
|
|
|
|
2020-07-25 15:05:26 -07:00
|
|
|
proc process( client: NetdataClient ): void =
|
2018-02-18 22:18:44 -08:00
|
|
|
## Do the work for a connected client within child process.
|
2018-02-18 18:16:37 -08:00
|
|
|
let t0 = cpu_time()
|
2018-02-12 12:26:16 -08:00
|
|
|
var raw_data = client.fetch_data
|
|
|
|
|
|
2018-02-14 15:41:11 -08:00
|
|
|
# Done with the socket, netdata will automatically
|
|
|
|
|
# reconnect. Save local resources/file descriptors
|
|
|
|
|
# by closing after the send is considered complete.
|
|
|
|
|
#
|
2020-07-25 15:05:26 -07:00
|
|
|
if conf.dropconn:
|
|
|
|
|
try:
|
|
|
|
|
client.sock.close
|
|
|
|
|
except OSError:
|
|
|
|
|
return
|
2018-02-12 12:26:16 -08:00
|
|
|
|
2018-02-18 18:16:37 -08:00
|
|
|
# Pivot the parsed data to a single JSON blob per sample time.
|
|
|
|
|
var samples = parse_data( raw_data )
|
2020-07-25 15:05:26 -07:00
|
|
|
client.write_to_database( samples )
|
2018-02-14 15:41:11 -08:00
|
|
|
|
|
|
|
|
if conf.verbose:
|
2020-07-25 15:05:26 -07:00
|
|
|
let cputime = cpu_time() - t0
|
2018-02-14 15:41:11 -08:00
|
|
|
echo(
|
2018-02-18 18:16:37 -08:00
|
|
|
hl( $(epochTime().to_int), fgMagenta, bright=true ),
|
|
|
|
|
" ",
|
|
|
|
|
hl( $(samples.len), fgWhite, bright=true ),
|
2018-02-14 15:41:11 -08:00
|
|
|
" sample(s) parsed from ",
|
2020-07-25 15:05:26 -07:00
|
|
|
client.address.hl( fgYellow, bright=true ),
|
|
|
|
|
" in ", hl( "{cputime:<2.3f}".fmt, fgWhite, bright=true), " seconds."
|
2018-02-14 15:41:11 -08:00
|
|
|
)
|
2018-02-12 13:25:26 -08:00
|
|
|
|
2018-02-12 12:26:16 -08:00
|
|
|
|
2018-02-18 18:16:37 -08:00
|
|
|
proc serverloop( conf: Config ): void =
|
2018-02-12 12:26:16 -08:00
|
|
|
## Open a database connection, bind to the listening socket,
|
|
|
|
|
## and start serving incoming netdata streams.
|
|
|
|
|
let db = open( "", "", "", conf.dbopts )
|
2018-02-18 18:16:37 -08:00
|
|
|
db.close
|
2018-02-18 22:18:44 -08:00
|
|
|
if conf.verbose: echo( "Successfully tested connection to the backend database.".hl( fgGreen ) )
|
|
|
|
|
|
|
|
|
|
# Ensure children are properly reaped.
|
|
|
|
|
#
|
|
|
|
|
var sa: Sigaction
|
|
|
|
|
sa.sa_handler = SIG_IGN
|
|
|
|
|
discard sigaction( SIGCHLD, sa )
|
2018-02-14 15:41:11 -08:00
|
|
|
|
2018-02-18 18:16:37 -08:00
|
|
|
# Setup listening socket.
|
|
|
|
|
#
|
|
|
|
|
var server = newSocket()
|
2018-02-12 12:26:16 -08:00
|
|
|
server.set_sock_opt( OptReuseAddr, true )
|
2018-02-14 15:41:11 -08:00
|
|
|
server.bind_addr( Port(conf.listen_port), conf.listen_addr )
|
2018-02-12 12:26:16 -08:00
|
|
|
server.listen()
|
|
|
|
|
|
2018-02-14 15:41:11 -08:00
|
|
|
if conf.verbose:
|
|
|
|
|
echo(
|
|
|
|
|
"Listening for incoming connections on ".hl( fgGreen, bright=true ),
|
|
|
|
|
hl( (if conf.listen_addr == "0.0.0.0": "*" else: conf.listen_addr) , fgBlue, bright=true ),
|
|
|
|
|
":",
|
|
|
|
|
hl( $conf.listen_port, fgBlue, bright=true ),
|
|
|
|
|
)
|
|
|
|
|
echo ""
|
|
|
|
|
|
2018-02-18 18:16:37 -08:00
|
|
|
# Wait for incoming connections, fork for each client.
|
|
|
|
|
#
|
2018-02-12 12:26:16 -08:00
|
|
|
while true:
|
2020-07-25 15:05:26 -07:00
|
|
|
let client = NetdataClient.new
|
|
|
|
|
client.sock = Socket.new
|
2018-02-14 17:27:29 -08:00
|
|
|
|
2018-02-18 22:18:44 -08:00
|
|
|
# Block, waiting for new connections.
|
2020-07-25 15:05:26 -07:00
|
|
|
server.acceptAddr( client.sock, client.address )
|
2018-02-18 18:16:37 -08:00
|
|
|
|
|
|
|
|
if fork() == 0:
|
|
|
|
|
server.close
|
2020-07-25 15:05:26 -07:00
|
|
|
if conf.dropconn:
|
|
|
|
|
# "one shot" mode.
|
|
|
|
|
client.process
|
|
|
|
|
quit( 0 )
|
|
|
|
|
else:
|
|
|
|
|
# Keep the connection to netdata open.
|
|
|
|
|
while true: client.process
|
2018-02-15 10:29:37 -08:00
|
|
|
|
2020-07-25 15:05:26 -07:00
|
|
|
client.sock.close
|
2018-02-15 10:29:37 -08:00
|
|
|
when defined( testing ): dumpNumberOfInstances()
|
2018-02-12 12:26:16 -08:00
|
|
|
|
|
|
|
|
|
2018-02-18 18:16:37 -08:00
|
|
|
proc parse_cmdline: Config =
|
2018-02-12 12:26:16 -08:00
|
|
|
## Populate the config object with the user's preferences.
|
2018-02-14 15:41:11 -08:00
|
|
|
|
2018-02-18 18:16:37 -08:00
|
|
|
# Config object defaults.
|
|
|
|
|
#
|
|
|
|
|
result = Config(
|
2018-02-20 10:59:26 -08:00
|
|
|
dbopts: "host=localhost dbname=netdata application_name=netdata-tsrelay",
|
2018-02-19 18:22:25 -08:00
|
|
|
dbtable: "netdata",
|
2020-07-25 15:05:26 -07:00
|
|
|
dropconn: false,
|
2018-02-18 18:16:37 -08:00
|
|
|
listen_port: 14866,
|
|
|
|
|
listen_addr: "0.0.0.0",
|
|
|
|
|
verbose: true,
|
2018-02-19 18:22:25 -08:00
|
|
|
debug: false,
|
|
|
|
|
timeout: 500,
|
2020-07-25 15:05:26 -07:00
|
|
|
persistent: false,
|
2018-02-19 18:22:25 -08:00
|
|
|
insertsql: INSERT_SQL % [ "netdata" ]
|
2018-02-18 18:16:37 -08:00
|
|
|
)
|
|
|
|
|
|
2018-02-14 15:41:11 -08:00
|
|
|
# always set debug mode if development build.
|
2018-02-18 18:16:37 -08:00
|
|
|
result.debug = defined( testing )
|
2018-02-14 15:41:11 -08:00
|
|
|
|
2018-02-12 12:26:16 -08:00
|
|
|
for kind, key, val in getopt():
|
|
|
|
|
case kind
|
|
|
|
|
|
|
|
|
|
of cmdArgument:
|
|
|
|
|
discard
|
|
|
|
|
|
|
|
|
|
of cmdLongOption, cmdShortOption:
|
|
|
|
|
case key
|
2018-02-14 15:41:11 -08:00
|
|
|
of "debug", "d":
|
2018-02-18 18:16:37 -08:00
|
|
|
result.debug = true
|
2018-02-14 15:41:11 -08:00
|
|
|
|
2020-07-25 15:05:26 -07:00
|
|
|
of "dropconn", "D":
|
|
|
|
|
if result.persistent:
|
|
|
|
|
echo "Dropping TCP sockets are incompatible with persistent database connections."
|
|
|
|
|
quit( 1 )
|
|
|
|
|
result.dropconn = true
|
|
|
|
|
|
2018-02-12 12:26:16 -08:00
|
|
|
of "help", "h":
|
|
|
|
|
echo USAGE
|
|
|
|
|
quit( 0 )
|
2018-02-14 15:41:11 -08:00
|
|
|
|
|
|
|
|
of "quiet", "q":
|
2018-02-18 18:16:37 -08:00
|
|
|
result.verbose = false
|
2018-02-19 18:22:25 -08:00
|
|
|
|
2018-02-12 12:26:16 -08:00
|
|
|
of "version", "v":
|
2018-02-14 15:41:11 -08:00
|
|
|
echo hl( "netdata_tsrelay " & VERSION, fgWhite, bright=true )
|
2018-02-12 12:26:16 -08:00
|
|
|
quit( 0 )
|
2018-02-19 18:22:25 -08:00
|
|
|
|
|
|
|
|
of "timeout", "t": result.timeout = val.parse_int
|
|
|
|
|
|
|
|
|
|
of "dbtable", "T":
|
|
|
|
|
result.insertsql = INSERT_SQL % [ val ]
|
2020-07-25 15:05:26 -07:00
|
|
|
of "dbopts", "o": result.dbopts = val
|
2018-02-19 18:22:25 -08:00
|
|
|
|
2018-02-18 18:16:37 -08:00
|
|
|
of "listen-addr", "a": result.listen_addr = val
|
|
|
|
|
of "listen-port", "p": result.listen_port = val.parse_int
|
2018-02-12 12:26:16 -08:00
|
|
|
|
2020-07-25 15:05:26 -07:00
|
|
|
of "persistent", "P":
|
|
|
|
|
if result.dropconn:
|
|
|
|
|
echo "Persistent database connections are incompatible with dropping TCP sockets."
|
|
|
|
|
quit( 1 )
|
|
|
|
|
result.persistent = true
|
|
|
|
|
|
2018-02-12 12:26:16 -08:00
|
|
|
else: discard
|
|
|
|
|
|
|
|
|
|
of cmdEnd: assert( false ) # shouldn't reach here ever
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
when isMainModule:
|
2021-10-20 11:41:30 -07:00
|
|
|
addExitProc( resetAttributes )
|
2018-02-18 18:16:37 -08:00
|
|
|
conf = parse_cmdline()
|
2018-02-14 15:41:11 -08:00
|
|
|
if conf.debug: echo hl( $conf, fgYellow )
|
2018-02-18 18:16:37 -08:00
|
|
|
serverloop( conf )
|
2018-02-12 12:26:16 -08:00
|
|
|
|