1/*	$NetBSD: sht.c,v 1.6 2020/05/25 20:47:37 christos Exp $	*/
2
3/*
4 * sht.c - Testprogram for shared memory refclock
5 * read/write shared memory segment; see usage
6 */
7#include "config.h"
8
9#ifndef SYS_WINNT
10#include <sys/types.h>
11#include <sys/ipc.h>
12#include <sys/shm.h>
13#include <stdio.h>
14#include <time.h>
15#include <unistd.h>
16#include <stdlib.h>
17#else
18#include <windows.h>
19#include <time.h>
20#include <stdlib.h>
21#include <stdio.h>
22#include <iostream.h>
23#define sleep(x) Sleep(x*1000)
24#endif
25#include <assert.h>
26
27struct shmTime {
28	int    mode; /* 0 - if valid set
29		      *       use values,
30		      *       clear valid
31		      * 1 - if valid set
32		      *       if count before and after read of values is equal,
33		      *         use values
34		      *       clear valid
35		      */
36	volatile int	count;
37	time_t		clockTimeStampSec;
38	int		clockTimeStampUSec;
39	time_t		receiveTimeStampSec;
40	int		receiveTimeStampUSec;
41	int		leap;
42	int		precision;
43	int		nsamples;
44	volatile int	valid;
45	unsigned	clockTimeStampNSec;	/* Unsigned ns timestamps */
46	unsigned	receiveTimeStampNSec;	/* Unsigned ns timestamps */
47};
48
49static struct shmTime *
50getShmTime (
51	int unit
52	)
53{
54#ifndef SYS_WINNT
55	int shmid=shmget (0x4e545030+unit, sizeof (struct shmTime), IPC_CREAT|0777);
56	if (shmid==-1) {
57		perror ("shmget");
58		exit (1);
59	}
60	else {
61		struct shmTime *p=(struct shmTime *)shmat (shmid, 0, 0);
62		if ((int)(long)p==-1) {
63			perror ("shmat");
64			p=0;
65		}
66		assert (p!=0);
67		return p;
68	}
69#else
70	char buf[10];
71	LPSECURITY_ATTRIBUTES psec=0;
72	snprintf (buf, sizeof(buf), "NTP%d", unit);
73	SECURITY_DESCRIPTOR sd;
74	SECURITY_ATTRIBUTES sa;
75	HANDLE shmid;
76
77	assert (InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION));
78	assert (SetSecurityDescriptorDacl(&sd,1,0,0));
79	sa.nLength=sizeof (SECURITY_ATTRIBUTES);
80	sa.lpSecurityDescriptor=&sd;
81	sa.bInheritHandle=0;
82	shmid=CreateFileMapping ((HANDLE)0xffffffff, 0, PAGE_READWRITE,
83				 psec, sizeof (struct shmTime),buf);
84	if (!shmid) {
85		shmid=CreateFileMapping ((HANDLE)0xffffffff, 0, PAGE_READWRITE,
86					 0, sizeof (struct shmTime),buf);
87		cout <<"CreateFileMapping with psec!=0 failed"<<endl;
88	}
89
90	if (!shmid) {
91		char mbuf[1000];
92		FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
93			       0, GetLastError (), 0, mbuf, sizeof (mbuf), 0);
94		int x=GetLastError ();
95		cout <<"CreateFileMapping "<<buf<<":"<<mbuf<<endl;
96		exit (1);
97	}
98	else {
99		struct shmTime *p=(struct shmTime *) MapViewOfFile (shmid,
100								    FILE_MAP_WRITE, 0, 0, sizeof (struct shmTime));
101		if (p==0) {
102			char mbuf[1000];
103			FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
104				       0, GetLastError (), 0, mbuf, sizeof (mbuf), 0);
105			cout <<"MapViewOfFile "<<buf<<":"<<mbuf<<endl;
106			exit (1);
107		}
108		return p;
109	}
110	return 0;
111#endif
112}
113
114
115int
116main (
117	int argc,
118	char *argv[]
119	)
120{
121	volatile struct shmTime *p;
122	int unit;
123	char *argp;
124
125	if (argc<=1) {
126	  usage:
127		printf ("usage: %s [uu:]{r[c][l]|w|snnn}\n",argv[0]);
128		printf ("       uu use clock unit uu (default: 2)\n");
129		printf ("       r read shared memory\n");
130		printf ("       c clear valid-flag\n");
131		printf ("       l loop (so, rcl will read and clear in a loop\n");
132		printf ("       w write shared memory with current time\n");
133		printf ("       snnnn set nsamples to nnn\n");
134		printf ("       lnnnn set leap to nnn\n");
135		printf ("       pnnnn set precision to -nnn\n");
136		exit (0);
137	}
138
139	srand(time(NULL));
140
141	unit = strtoul(argv[1], &argp, 10);
142	if (argp == argv[1])
143		unit = 2;
144	else if (*argp == ':')
145		argp++;
146	else
147		goto usage;
148
149	p=getShmTime(unit);
150	switch (*argp) {
151	case 's':
152		p->nsamples=atoi(argp+1);
153		break;
154
155	case 'l':
156		p->leap=atoi(argp+1);
157		break;
158
159	case 'p':
160		p->precision=-atoi(argp+1);
161		break;
162
163	case 'r': {
164		int clear=0;
165		int loop=0;
166		printf ("reader\n");
167		while (*++argp) {
168			switch (*argp) {
169			case 'l': loop=1; break;
170			case 'c': clear=1; break;
171			default : goto usage;
172			}
173		}
174again:
175		printf ("mode=%d, count=%d, clock=%ld.%09u, rec=%ld.%09u,\n",
176			p->mode,p->count,
177			(long)p->clockTimeStampSec,p->clockTimeStampNSec,
178			(long)p->receiveTimeStampSec,p->receiveTimeStampNSec);
179		printf ("  leap=%d, precision=%d, nsamples=%d, valid=%d\n",
180			p->leap, p->precision, p->nsamples, p->valid);
181		if (!p->valid)
182			printf ("***\n");
183		if (clear) {
184			p->valid=0;
185			printf ("cleared\n");
186		}
187		if (loop) {
188			sleep (1);
189			goto again;
190		}
191		break;
192	}
193
194	case 'w': {
195		/* To show some life action, we read the system
196		 * clock and use a bit of fuzz from 'random()' to get a
197		 * bit of wobbling into the values (so we can observe a
198		 * certain jitter!)
199		 */
200		time_t clk_sec, rcv_sec;
201		u_int  clk_frc, rcv_frc;
202
203#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_REALTIME)
204
205		/* Here we have a high-resolution system clock, and
206		 * we're not afraid to use it!
207		 */
208		struct timespec tmptime;
209		if (0 == clock_gettime(CLOCK_REALTIME, &tmptime)) {
210			rcv_sec = tmptime.tv_sec;
211			rcv_frc = (u_int)tmptime.tv_nsec;
212		}
213		else
214#endif
215		{
216			time(&rcv_sec);
217			rcv_frc = (u_int)random() % 1000000000u;
218		}
219		/* add a wobble of ~3.5msec to the clock time */
220		clk_sec = rcv_sec;
221		clk_frc = rcv_frc + (u_int)(random()%7094713 - 3547356);
222		/* normalise result -- the SHM driver is picky! */
223		while ((int)clk_frc < 0) {
224			clk_frc += 1000000000;
225			clk_sec -= 1;
226		}
227		while ((int)clk_frc >= 1000000000) {
228			clk_frc -= 1000000000;
229			clk_sec += 1;
230		}
231
232		/* Most 'real' time sources would create a clock
233		 * (reference) time stamp where the fraction is zero,
234		 * but that's not an actual requirement. So we show how
235		 * to deal with the time stamps in general; changing the
236		 * behaviour for cases where the fraction of the
237		 * clock time is zero should be trivial.
238		 */
239		printf ("writer\n");
240		p->mode=0;
241		if (!p->valid) {
242			p->clockTimeStampSec    = clk_sec;
243			p->clockTimeStampUSec   = clk_frc / 1000; /* truncate! */
244			p->clockTimeStampNSec   = clk_frc;
245			p->receiveTimeStampSec  = rcv_sec;
246			p->receiveTimeStampUSec = rcv_frc / 1000; /* truncate! */
247			p->receiveTimeStampNSec = rcv_frc;
248			printf ("%ld.%09u %ld.%09u\n",
249				(long)p->clockTimeStampSec  , p->clockTimeStampNSec  ,
250				(long)p->receiveTimeStampSec, p->receiveTimeStampNSec);
251			p->valid=1;
252		}
253		else {
254			printf ("p->valid still set\n"); /* not an error! */
255		}
256		break;
257	}
258	default:
259		break;
260	}
261	return 0;
262}
263