/* * Copyright (c) 2015 Carsten Larsen * Copyright (c) 2014 Poul-Henning Kamp * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. * */ #include "ntimed_platform.h" #include "ntimed.h" #include "ntp.h" #include "udp.h" #define ARGSFORMAT "P=PARAM/K,B=BUMP/K,T=TRACEFILE/K,SIMFILE/A,QUIET/S" const char *vers = "\0$VER: ntimed-sim" AMIGA_VERSION; #define PARAM_CLIENT PARAM_INSTANCE #define PARAM_TABLE_NAME client_param_table #include "param_instance.h" #undef PARAM_TABLE_NAME #undef PARAM_CLIENT /**********************************************************************/ struct sim_file { unsigned magic; #define SIM_FILE_MAGIC 0x7f847bd0 char *filename; FILE *input; unsigned n_peer; struct ntp_peerset *npl; struct timestamp when; unsigned t0; }; static void simfile_poll(struct ocx *ocx, const struct sim_file *sf, char *buf); static enum todo_e simfile_readline(struct ocx *ocx, struct todolist *tdl, void *priv); static struct sim_file * SimFile_Open(struct ocx *ocx, const char *fn, struct todolist *tdl, struct ntp_peerset *npl); /**********************************************************************/ struct RDArgs *rdargs = NULL; struct todolist *tdl = NULL; struct ntimedargs { char *params; char *bump; char *tracefile; char *simfile; long quiet; }; static void init_logic(struct ntimedargs *args); static void set_params(char *params); static void clean_exit(); int main(int argc, char **argv) { int result = 0; struct ntimedargs args = { NULL, NULL, NULL, NULL, FALSE }; atexit(clean_exit); if((result = amiga_open_libs()) != 0) { exit(result); } rdargs = ReadArgs((STRPTR)ARGSFORMAT, (APTR)&args, NULL); if (!rdargs) { PrintFault(IoErr(), (STRPTR)argv[0]); exit(5); } if (!args.quiet) { EnableDebug(); } if (args.tracefile) { ArgTracefile(args.tracefile); } init_logic(&args); (void)TODO_Run(NULL, tdl); return result; } static void init_logic(struct ntimedargs *args) { int ch; struct sim_file *sf; struct ntp_peerset *npl; struct ntp_peer *np; struct combine_delta *cd; double a, b, c; tdl = TODO_NewList(); Time_Sim(tdl); PLL_Init(); npl = NTP_PeerSet_New(NULL); Param_Register(client_param_table); NF_Init(); set_params(args->params); if (args->bump) { ch = sscanf(args->bump, "%lg,%lg,%lg", &a, &b, &c); if (ch != 3) { Put(NULL, OCX_DEBUG, "bad BUMP argument \"when,freq,phase\"\n"); exit(1); } } Time_Sim_Bump(tdl, a, b, c); Param_Report(NULL, OCX_TRACE); sf = SimFile_Open(NULL, args->simfile, tdl, npl); AN(sf); cd = CD_New(); NTP_PeerSet_Foreach(np, npl) { NF_New(np); np->combiner = CD_AddSource(cd, np->hostname, np->ip); } amiga_init_offset(); } /**********************************************************************/ static void clean_exit() { ArgTracefile(NULL); if (rdargs) { FreeArgs(rdargs); rdargs = NULL; } amiga_close_libs(); freeall(); } /**********************************************************************/ static void set_params(char *params) { char *token; const char d[2] = ","; if (params == NULL) return; token = strtok(params, d); while (token != NULL) { Param_Tweak(NULL, token); token = strtok(NULL, d); } } /**********************************************************************/ static void simfile_poll(struct ocx *ocx, const struct sim_file *sf, char *buf) { char *hostname; char *ip; char *pkt; struct ntp_peer *np; struct ntp_packet *rxp; struct ntp_packet *txp; CHECK_OBJ_NOTNULL(sf, SIM_FILE_MAGIC); AN(buf); if (memcmp(buf, "Poll ", 5)) Fail(ocx, 0, "Bad 'Poll' line (%s)\n", buf); hostname = buf + 5; ip = strchr(hostname, ' '); if (ip == NULL) Fail(ocx, 0, "Bad 'Poll' line (%s)\n", buf); pkt = strchr(ip + 1, ' '); if (pkt == NULL) Fail(ocx, 0, "Bad 'Poll' line (%s)\n", buf); *ip++ = '\0'; *pkt++ = '\0'; NTP_PeerSet_Foreach(np, sf->npl) if (!strcmp(np->hostname, hostname) && !strcmp(np->ip, ip)) break; if (np == NULL) Fail(ocx, 0, "Peer not found (%s, %s)\n", hostname, ip); CHECK_OBJ_NOTNULL(np, NTP_PEER_MAGIC); txp = np->tx_pkt; INIT_OBJ(txp, NTP_PACKET_MAGIC); rxp = np->rx_pkt; if (NTP_Tool_Scan(rxp, pkt)) Fail(ocx, 0, "Cannot parse packet (%s, %s, %s)\n", hostname, ip, pkt); TS_Add(&rxp->ntp_origin, Time_Sim_delta); TS_Add(&rxp->ts_rx, Time_Sim_delta); txp->ntp_transmit = rxp->ntp_origin; if (np->filter_func != NULL) np->filter_func(ocx, np); } static enum todo_e simfile_readline(struct ocx *ocx, struct todolist *tdl, void *priv) { struct sim_file *sf; char buf[BUFSIZ], *p; struct timestamp t0; unsigned u1, u2; double dt; AN(tdl); CAST_OBJ_NOTNULL(sf, priv, SIM_FILE_MAGIC); TB_Now(&t0); while (1) { if (fgets(buf, sizeof buf, sf->input) == NULL) { Debug(ocx, "EOF on SIMFILE (%s)\n", sf->filename); exit(0); } p = strchr(buf, '\r'); if (p != NULL) *p = '\0'; p = strchr(buf, '\n'); if (p != NULL) *p = '\0'; if (!strncmp(buf, "Now ", 4)) { if (sscanf(buf, "Now %u.%u", &u1, &u2) != 2) Fail(ocx, 0, "Bad 'Now' line (%s)", buf); if (sf->t0 == 0) sf->t0 = u1 - t0.sec; u1 -= sf->t0; TS_Nanosec(&sf->when, u1, u2); dt = TS_Diff(&sf->when, &t0); if (dt >= 1e-3) { TODO_ScheduleAbs(tdl, simfile_readline, priv, &sf->when, 0.0, "Readline"); return (TODO_OK); } } else if (!strncmp(buf, "Poll ", 5)) { simfile_poll(ocx, sf, buf); } /* We ignore things we don't understand */ } } static struct sim_file * SimFile_Open(struct ocx *ocx, const char *fn, struct todolist *tdl, struct ntp_peerset *npl) { struct sim_file *sf; char buf[BUFSIZ]; char buf2[BUFSIZ]; char buf3[BUFSIZ]; char *e; int s; unsigned fpeer = 0; AN(fn); AN(tdl); AN(npl); ALLOC_OBJ(sf, SIM_FILE_MAGIC); AN(sf); sf->input = fopen(fn, "r"); if (sf->input == NULL) Fail(ocx, 1, "Could not open SIMFILE (%s)", fn); sf->filename = strdup(fn); AN(sf->filename); sf->npl = npl; for (s = 0; s < 3; ) { if (fgets(buf, sizeof buf, sf->input) == NULL) Fail(ocx, 1, "Premature EOF on SIMFILE (%s)", fn); e = strchr(buf, '\0'); AN(e); if (e == buf) continue; if (e[-1] == '\n') *--e = '\0'; Debug(ocx, ">>> %s\n", buf); switch(s) { case 0: if (strcmp(buf, "# NTIMED Format poll-server 1.0")) Fail(ocx, 0, "Wrong fileformat in SIMFILE (%s)", fn); s++; break; case 1: if (sscanf(buf, "# Found %u peers", &sf->n_peer) != 1) Fail(ocx, 0, "Expected '# Found ... peers' line"); s++; break; case 2: if (sscanf(buf, "# Peer %s %s", buf2, buf3) != 2) Fail(ocx, 0, "Expected '# Peer' line"); NTP_PeerSet_AddSim(ocx, npl, buf2, buf3); if (++fpeer == sf->n_peer) s++; break; default: Debug(ocx, "<%s>\n", buf); Fail(ocx, 0, "XXX: Wrong state (%d) in open_sim_file", s); } } (void)simfile_readline(NULL, tdl, sf); return (sf); }