1/*	$NetBSD: tickadj.c,v 1.5 2020/05/25 20:47:37 christos Exp $	*/
2
3/*
4 * tickadj - read, and possibly modify, the kernel `tick' and
5 *	     `tickadj' variables, as well as `dosynctodr'.  Note that
6 *	     this operates on the running kernel only.  I'd like to be
7 *	     able to read and write the binary as well, but haven't
8 *	     mastered this yet.
9 *
10 * HMS: The #includes here are different from those in xntpd/ntp_unixclock.c
11 *      These seem "worse".
12 */
13
14#ifdef HAVE_CONFIG_H
15# include <config.h>
16#endif
17
18#include "ntp_types.h"
19#include "l_stdlib.h"
20
21#include <stdio.h>
22#ifdef HAVE_UNISTD_H
23# include <unistd.h>
24#endif /* HAVE_UNISTD_H */
25
26#ifdef HAVE_SYS_TIMEX_H
27# include <sys/timex.h>
28#endif
29
30#ifdef HAVE_ADJTIMEX	/* Linux */
31
32struct timex txc;
33
34#if 0
35int
36main(
37	int argc,
38	char *argv[]
39	)
40{
41	int     c, i;
42	int     quiet = 0;
43	int     errflg = 0;
44	char    *progname;
45	extern int ntp_optind;
46	extern char *ntp_optarg;
47
48	progname = argv[0];
49	if (argc==2 && argv[1][0] != '-') { /* old Linux format, for compatability */
50	    if ((i = atoi(argv[1])) > 0) {
51		    txc.time_tick = i;
52		    txc.modes = ADJ_TIMETICK;
53	    } else {
54		    fprintf(stderr, "Silly value for tick: %s\n", argv[1]);
55		    errflg++;
56	    }
57	} else {
58	    while ((c = ntp_getopt(argc, argv, "a:qt:")) != EOF) {
59		switch (c) {
60		    case 'a':
61			if ((i=atoi(ntp_optarg)) > 0) {
62				txc.tickadj = i;
63				txc.modes |= ADJ_TICKADJ;
64			} else {
65				fprintf(stderr,
66					"%s: unlikely value for tickadj: %s\n",
67					progname, ntp_optarg);
68				errflg++;
69			}
70			break;
71
72		    case 'q':
73			quiet = 1;
74			break;
75
76		    case 't':
77			if ((i=atoi(ntp_optarg)) > 0) {
78				txc.time_tick = i;
79				txc.modes |= ADJ_TIMETICK;
80			} else {
81				(void) fprintf(stderr,
82				       "%s: unlikely value for tick: %s\n",
83				       progname, ntp_optarg);
84				errflg++;
85			}
86			break;
87
88		    default:
89			fprintf(stderr,
90			    "Usage: %s [tick_value]\n-or-   %s [ -q ] [ -t tick ] [ -a tickadj ]\n",
91			    progname, progname);
92			errflg++;
93			break;
94		}
95	    }
96	}
97
98	if (!errflg) {
99		if (adjtimex(&txc) < 0)
100			perror("adjtimex");
101		else if (!quiet)
102			printf("tick     = %ld\ntick_adj = %d\n",
103			    txc.time_tick, txc.tickadj);
104	}
105
106	exit(errflg ? 1 : 0);
107}
108#else
109int
110main(
111	int argc,
112	char *argv[]
113	)
114{
115	if (argc > 2)
116	{
117		fprintf(stderr, "Usage: %s [tick_value]\n", argv[0]);
118		exit(-1);
119	}
120	else if (argc == 2)
121	{
122#ifdef ADJ_TIMETICK
123		if ( (txc.time_tick = atoi(argv[1])) < 1 )
124#else
125		if ( (txc.tick = atoi(argv[1])) < 1 )
126#endif
127		{
128			fprintf(stderr, "Silly value for tick: %s\n", argv[1]);
129			exit(-1);
130		}
131#ifdef ADJ_TIMETICK
132		txc.modes = ADJ_TIMETICK;
133#else
134#ifdef MOD_OFFSET
135		txc.modes = ADJ_TICK;
136#else
137		txc.mode = ADJ_TICK;
138#endif
139#endif
140	}
141	else
142	{
143#ifdef ADJ_TIMETICK
144		txc.modes = 0;
145#else
146#ifdef MOD_OFFSET
147		txc.modes = 0;
148#else
149		txc.mode = 0;
150#endif
151#endif
152	}
153
154	if (adjtimex(&txc) < 0)
155	{
156		perror("adjtimex");
157	}
158	else
159	{
160#ifdef ADJ_TIMETICK
161		printf("tick     = %ld\ntick_adj = %ld\n", txc.time_tick, txc.tickadj);
162#else
163		printf("tick = %ld\n", txc.tick);
164#endif
165	}
166
167	exit(0);
168}
169#endif
170
171#else	/* not Linux... kmem tweaking: */
172
173#ifdef HAVE_SYS_FILE_H
174# include <sys/file.h>
175#endif
176#include <sys/stat.h>
177
178#ifdef HAVE_SYS_PARAM_H
179# include <sys/param.h>
180#endif
181
182#ifdef NLIST_STRUCT
183# include <nlist.h>
184#else /* not NLIST_STRUCT */ /* was defined(SYS_AUX3) || defined(SYS_AUX2) */
185# include <sys/resource.h>
186# include <sys/file.h>
187# include <a.out.h>
188# ifdef HAVE_SYS_VAR_H
189#  include <sys/var.h>
190# endif
191#endif
192
193#include "ntp_stdlib.h"
194#include "ntp_io.h"
195
196#ifdef hz /* Was: RS6000 */
197# undef hz
198#endif /* hz */
199
200#ifdef HAVE_KVM_OPEN
201# include <kvm.h>
202#endif
203
204#ifdef SYS_VXWORKS
205/* vxWorks needs mode flag -casey*/
206#define open(name, flags)   open(name, flags, 0777)
207#endif
208
209#ifndef L_SET	/* Was: defined(SYS_PTX) || defined(SYS_IX86OSF1) */
210# define L_SET SEEK_SET
211#endif
212
213#ifndef HZ
214# define HZ	DEFAULT_HZ
215#endif
216
217#define	KMEM	"/dev/kmem"
218#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
219
220char *progname;
221
222int dokmem = 1;
223int writetickadj = 0;
224int writeopttickadj = 0;
225int unsetdosync = 0;
226int writetick = 0;
227int quiet = 0;
228int setnoprintf = 0;
229
230const char *kmem = KMEM;
231const char *file = NULL;
232int   fd  = -1;
233
234static	void	getoffsets	(off_t *, off_t *, off_t *, off_t *);
235static	int	openfile	(const char *, int);
236static	void	writevar	(int, off_t, int);
237static	void	readvar		(int, off_t, int *);
238
239/*
240 * main - parse arguments and handle options
241 */
242int
243main(
244	int argc,
245	char *argv[]
246	)
247{
248	int c;
249	int errflg = 0;
250	off_t tickadj_offset;
251	off_t tick_offset;
252	off_t dosync_offset;
253	off_t noprintf_offset;
254	int tickadj, ktickadj;	/* HMS: Why isn't this u_long? */
255	int tick, ktick;	/* HMS: Why isn't this u_long? */
256	int dosynctodr;
257	int noprintf;
258	int hz;
259	int hz_int, hz_hundredths;
260	int recommend_tickadj;
261	long tmp;
262
263	init_lib();
264
265	progname = argv[0];
266	while ((c = ntp_getopt(argc, argv, "a:Adkpqst:")) != EOF)
267	{
268		switch (c)
269		{
270		    case 'a':
271			writetickadj = atoi(ntp_optarg);
272			if (writetickadj <= 0)
273			{
274				(void) fprintf(stderr,
275					       "%s: unlikely value for tickadj: %s\n",
276					       progname, ntp_optarg);
277				errflg++;
278			}
279
280#if defined SCO5_CLOCK
281			if (writetickadj % HZ)
282			{
283				writetickadj = (writetickadj / HZ) * HZ;
284				(void) fprintf(stderr,
285					       "tickadj truncated to: %d\n", writetickadj);
286			}
287#endif /* SCO5_CLOCK */
288
289			break;
290		    case 'A':
291			writeopttickadj = 1;
292			break;
293		    case 'd':
294			++debug;
295			break;
296		    case 'k':
297			dokmem = 1;
298			break;
299		    case 'p':
300			setnoprintf = 1;
301			break;
302		    case 'q':
303			quiet = 1;
304			break;
305		    case 's':
306			unsetdosync = 1;
307			break;
308		    case 't':
309			writetick = atoi(ntp_optarg);
310			if (writetick <= 0)
311			{
312				(void) fprintf(stderr,
313					       "%s: unlikely value for tick: %s\n",
314					       progname, ntp_optarg);
315				errflg++;
316			}
317			break;
318		    default:
319			errflg++;
320			break;
321		}
322	}
323	if (errflg || ntp_optind != argc)
324	{
325		(void) fprintf(stderr,
326			       "usage: %s [-Adkpqs] [-a newadj] [-t newtick]\n", progname);
327		exit(2);
328	}
329
330	getoffsets(&tick_offset, &tickadj_offset, &dosync_offset, &noprintf_offset);
331
332	if (debug)
333	{
334		(void) printf("tick offset = %lu\n", (unsigned long)tick_offset);
335		(void) printf("tickadj offset = %lu\n", (unsigned long)tickadj_offset);
336		(void) printf("dosynctodr offset = %lu\n", (unsigned long)dosync_offset);
337		(void) printf("noprintf offset = %lu\n", (unsigned long)noprintf_offset);
338	}
339
340	if (writetick && (tick_offset == 0))
341	{
342		(void) fprintf(stderr,
343			       "No tick kernel variable\n");
344		errflg++;
345	}
346
347	if (writeopttickadj && (tickadj_offset == 0))
348	{
349		(void) fprintf(stderr,
350			       "No tickadj kernel variable\n");
351		errflg++;
352	}
353
354	if (unsetdosync && (dosync_offset == 0))
355	{
356		(void) fprintf(stderr,
357			       "No dosynctodr kernel variable\n");
358		errflg++;
359	}
360
361	if (setnoprintf && (noprintf_offset == 0))
362	{
363		(void) fprintf(stderr,
364			       "No noprintf kernel variable\n");
365		errflg++;
366	}
367
368	if (tick_offset != 0)
369	{
370		readvar(fd, tick_offset, &tick);
371#if defined(TICK_NANO) && defined(K_TICK_NAME)
372		if (!quiet)
373		    (void) printf("KERNEL %s = %d nsec\n", K_TICK_NAME, tick);
374#endif /* TICK_NANO && K_TICK_NAME */
375
376#ifdef TICK_NANO
377		tick /= 1000;
378#endif
379	}
380	else
381	{
382		tick = 0;
383	}
384
385	if (tickadj_offset != 0)
386	{
387		readvar(fd, tickadj_offset, &tickadj);
388
389#ifdef SCO5_CLOCK
390		/* scale from nsec/sec to usec/tick */
391		tickadj /= (1000L * HZ);
392#endif /*SCO5_CLOCK */
393
394#if defined(TICKADJ_NANO) && defined(K_TICKADJ_NAME)
395		if (!quiet)
396		    (void) printf("KERNEL %s = %d nsec\n", K_TICKADJ_NAME, tickadj);
397#endif /* TICKADJ_NANO && K_TICKADJ_NAME */
398
399#ifdef TICKADJ_NANO
400		tickadj += 999;
401		tickadj /= 1000;
402#endif
403	}
404	else
405	{
406		tickadj = 0;
407	}
408
409	if (dosync_offset != 0)
410	{
411		readvar(fd, dosync_offset, &dosynctodr);
412	}
413
414	if (noprintf_offset != 0)
415	{
416		readvar(fd, noprintf_offset, &noprintf);
417	}
418
419	(void) close(fd);
420
421	if (unsetdosync && dosync_offset == 0)
422	{
423		(void) fprintf(stderr,
424			       "%s: can't find %s in namelist\n",
425			       progname,
426#ifdef K_DOSYNCTODR_NAME
427			       K_DOSYNCTODR_NAME
428#else /* not K_DOSYNCTODR_NAME */
429			       "dosynctodr"
430#endif /* not K_DOSYNCTODR_NAME */
431			       );
432		exit(1);
433	}
434
435	hz = HZ;
436#if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)
437	hz = (int) sysconf (_SC_CLK_TCK);
438#endif /* not HAVE_SYSCONF && _SC_CLK_TCK */
439#ifdef OVERRIDE_HZ
440	hz = DEFAULT_HZ;
441#endif
442	ktick = tick;
443#ifdef PRESET_TICK
444	tick = PRESET_TICK;
445#endif /* PRESET_TICK */
446#ifdef TICKADJ_NANO
447	tickadj /= 1000;
448	if (tickadj == 0)
449	    tickadj = 1;
450#endif
451	ktickadj = tickadj;
452#ifdef PRESET_TICKADJ
453	tickadj = (PRESET_TICKADJ) ? PRESET_TICKADJ : 1;
454#endif /* PRESET_TICKADJ */
455
456	if (!quiet)
457	{
458		if (tick_offset != 0)
459		{
460			(void) printf("KERNEL tick = %d usec (from %s kernel variable)\n",
461				      ktick,
462#ifdef K_TICK_NAME
463				      K_TICK_NAME
464#else
465				      "<this can't happen>"
466#endif
467				      );
468		}
469#ifdef PRESET_TICK
470		(void) printf("PRESET tick = %d usec\n", tick);
471#endif /* PRESET_TICK */
472		if (tickadj_offset != 0)
473		{
474			(void) printf("KERNEL tickadj = %d usec (from %s kernel variable)\n",
475				      ktickadj,
476#ifdef K_TICKADJ_NAME
477				      K_TICKADJ_NAME
478#else
479				      "<this can't happen>"
480#endif
481				      );
482		}
483#ifdef PRESET_TICKADJ
484		(void) printf("PRESET tickadj = %d usec\n", tickadj);
485#endif /* PRESET_TICKADJ */
486		if (dosync_offset != 0)
487		{
488			(void) printf("dosynctodr is %s\n", dosynctodr ? "on" : "off");
489		}
490		if (noprintf_offset != 0)
491		{
492			(void) printf("kernel level printf's: %s\n",
493				      noprintf ? "off" : "on");
494		}
495	}
496
497	if (tick <= 0)
498	{
499		(void) fprintf(stderr, "%s: the value of tick is silly!\n",
500			       progname);
501		exit(1);
502	}
503
504	hz_int = (int)(1000000L / (long)tick);
505	hz_hundredths = (int)((100000000L / (long)tick) - ((long)hz_int * 100L));
506	if (!quiet)
507	{
508		(void) printf("KERNEL hz = %d\n", hz);
509		(void) printf("calculated hz = %d.%02d Hz\n", hz_int,
510			      hz_hundredths);
511	}
512
513#if defined SCO5_CLOCK
514	recommend_tickadj = 100;
515#else /* SCO5_CLOCK */
516	tmp = (long) tick * 500L;
517	recommend_tickadj = (int)(tmp / 1000000L);
518	if (tmp % 1000000L > 0)
519	{
520		recommend_tickadj++;
521	}
522
523#ifdef MIN_REC_TICKADJ
524	if (recommend_tickadj < MIN_REC_TICKADJ)
525	{
526		recommend_tickadj = MIN_REC_TICKADJ;
527	}
528#endif /* MIN_REC_TICKADJ */
529#endif /* SCO5_CLOCK */
530
531
532	if ((!quiet) && (tickadj_offset != 0))
533	{
534		(void) printf("recommended value of tickadj = %d us\n",
535			      recommend_tickadj);
536	}
537
538	if (   writetickadj == 0
539	       && !writeopttickadj
540	       && !unsetdosync
541	       && writetick == 0
542	       && !setnoprintf)
543	{
544		exit(errflg ? 1 : 0);
545	}
546
547	if (writetickadj == 0 && writeopttickadj)
548	{
549		writetickadj = recommend_tickadj;
550	}
551
552	fd = openfile(file, O_WRONLY);
553
554	if (setnoprintf && (noprintf_offset != 0))
555	{
556		if (!quiet)
557		{
558			(void) fprintf(stderr, "setting noprintf: ");
559			(void) fflush(stderr);
560		}
561		writevar(fd, noprintf_offset, 1);
562		if (!quiet)
563		{
564			(void) fprintf(stderr, "done!\n");
565		}
566	}
567
568	if ((writetick > 0) && (tick_offset != 0))
569	{
570		if (!quiet)
571		{
572			(void) fprintf(stderr, "writing tick, value %d: ",
573				       writetick);
574			(void) fflush(stderr);
575		}
576		writevar(fd, tick_offset, writetick);
577		if (!quiet)
578		{
579			(void) fprintf(stderr, "done!\n");
580		}
581	}
582
583	if ((writetickadj > 0) && (tickadj_offset != 0))
584	{
585		if (!quiet)
586		{
587			(void) fprintf(stderr, "writing tickadj, value %d: ",
588				       writetickadj);
589			(void) fflush(stderr);
590		}
591
592#ifdef SCO5_CLOCK
593		/* scale from usec/tick to nsec/sec */
594		writetickadj *= (1000L * HZ);
595#endif /* SCO5_CLOCK */
596
597		writevar(fd, tickadj_offset, writetickadj);
598		if (!quiet)
599		{
600			(void) fprintf(stderr, "done!\n");
601		}
602	}
603
604	if (unsetdosync && (dosync_offset != 0))
605	{
606		if (!quiet)
607		{
608			(void) fprintf(stderr, "zeroing dosynctodr: ");
609			(void) fflush(stderr);
610		}
611		writevar(fd, dosync_offset, 0);
612		if (!quiet)
613		{
614			(void) fprintf(stderr, "done!\n");
615		}
616	}
617	(void) close(fd);
618	return(errflg ? 1 : 0);
619}
620
621/*
622 * getoffsets - read the magic offsets from the specified file
623 */
624static void
625getoffsets(
626	off_t *tick_off,
627	off_t *tickadj_off,
628	off_t *dosync_off,
629	off_t *noprintf_off
630	)
631{
632
633#ifndef NOKMEM
634# ifndef HAVE_KVM_OPEN
635	const char **kname;
636# endif
637#endif
638
639#ifndef NOKMEM
640# ifdef NLIST_NAME_UNION
641#  define NL_B {{
642#  define NL_E }}
643# else
644#  define NL_B {
645#  define NL_E }
646# endif
647#endif
648
649#define K_FILLER_NAME "DavidLetterman"
650
651#ifdef NLIST_EXTRA_INDIRECTION
652	int i;
653#endif
654
655#ifndef NOKMEM
656	static struct nlist nl[] =
657	{
658		NL_B
659#ifdef K_TICKADJ_NAME
660#define N_TICKADJ	0
661		K_TICKADJ_NAME
662#else
663		K_FILLER_NAME
664#endif
665		NL_E,
666		NL_B
667#ifdef K_TICK_NAME
668#define N_TICK		1
669		K_TICK_NAME
670#else
671		K_FILLER_NAME
672#endif
673		NL_E,
674		NL_B
675#ifdef K_DOSYNCTODR_NAME
676#define N_DOSYNC	2
677		K_DOSYNCTODR_NAME
678#else
679		K_FILLER_NAME
680#endif
681		NL_E,
682		NL_B
683#ifdef K_NOPRINTF_NAME
684#define N_NOPRINTF	3
685		K_NOPRINTF_NAME
686#else
687		K_FILLER_NAME
688#endif
689		NL_E,
690		NL_B "" NL_E,
691	};
692
693#ifndef HAVE_KVM_OPEN
694	static const char *kernels[] =
695	{
696#ifdef HAVE_GETBOOTFILE
697		NULL,			/* *** SEE BELOW! *** */
698#endif
699		"/kernel/unix",
700		"/kernel",
701		"/vmunix",
702		"/unix",
703		"/mach",
704		"/hp-ux",
705		"/386bsd",
706		"/netbsd",
707		"/stand/vmunix",
708		"/bsd",
709		NULL
710	};
711#endif /* not HAVE_KVM_OPEN */
712
713#ifdef HAVE_KVM_OPEN
714	/*
715	 * Solaris > 2.5 doesn't have a kernel file.  Use the kvm_* interface
716	 * to read the kernel name list. -- stolcke 3/4/96
717	 */
718	kvm_t *kvm_handle = kvm_open(NULL, NULL, NULL, O_RDONLY, progname);
719
720	if (kvm_handle == NULL)
721	{
722		(void) fprintf(stderr,
723			       "%s: kvm_open failed\n",
724			       progname);
725		exit(1);
726	}
727	if (kvm_nlist(kvm_handle, nl) == -1)
728	{
729		(void) fprintf(stderr,
730			       "%s: kvm_nlist failed\n",
731			       progname);
732		exit(1);
733	}
734	kvm_close(kvm_handle);
735#else /* not HAVE_KVM_OPEN */
736#ifdef HAVE_GETBOOTFILE		/* *** SEE HERE! *** */
737	if (kernels[0] == NULL)
738	{
739		char * cp = (char *)getbootfile();
740
741		if (cp)
742		{
743			kernels[0] = cp;
744		}
745		else
746		{
747			kernels[0] = "/Placeholder";
748		}
749	}
750#endif /* HAVE_GETBOOTFILE */
751	for (kname = kernels; *kname != NULL; kname++)
752	{
753		struct stat stbuf;
754
755		if (stat(*kname, &stbuf) == -1)
756		{
757			continue;
758		}
759		if (nlist(*kname, nl) >= 0)
760		{
761			break;
762		}
763		else
764		{
765			(void) fprintf(stderr,
766				       "%s: nlist didn't find needed symbols from <%s>: %s\n",
767				       progname, *kname, strerror(errno));
768		}
769	}
770	if (*kname == NULL)
771	{
772		(void) fprintf(stderr,
773			       "%s: Couldn't find the kernel\n",
774			       progname);
775		exit(1);
776	}
777#endif /* HAVE_KVM_OPEN */
778
779	if (dokmem)
780	{
781		file = kmem;
782
783		fd = openfile(file, O_RDONLY);
784#ifdef NLIST_EXTRA_INDIRECTION
785		/*
786		 * Go one more round of indirection.
787		 */
788		for (i = 0; i < (sizeof(nl) / sizeof(struct nlist)); i++)
789		{
790			if ((nl[i].n_value) && (nl[i].n_sclass == 0x6b))
791			{
792				readvar(fd, nl[i].n_value, &nl[i].n_value);
793			}
794		}
795#endif /* NLIST_EXTRA_INDIRECTION */
796	}
797#endif /* not NOKMEM */
798
799	*tickadj_off  = 0;
800	*tick_off     = 0;
801	*dosync_off   = 0;
802	*noprintf_off = 0;
803
804#if defined(N_TICKADJ)
805	*tickadj_off = nl[N_TICKADJ].n_value;
806#endif
807
808#if defined(N_TICK)
809	*tick_off = nl[N_TICK].n_value;
810#endif
811
812#if defined(N_DOSYNC)
813	*dosync_off = nl[N_DOSYNC].n_value;
814#endif
815
816#if defined(N_NOPRINTF)
817	*noprintf_off = nl[N_NOPRINTF].n_value;
818#endif
819	return;
820}
821
822#undef N_TICKADJ
823#undef N_TICK
824#undef N_DOSYNC
825#undef N_NOPRINTF
826
827
828/*
829 * openfile - open the file, check for errors
830 */
831static int
832openfile(
833	const char *name,
834	int mode
835	)
836{
837	int ifd;
838
839	ifd = open(name, mode);
840	if (ifd < 0)
841	{
842		(void) fprintf(stderr, "%s: open %s: ", progname, name);
843		perror("");
844		exit(1);
845	}
846	return ifd;
847}
848
849
850/*
851 * writevar - write a variable into the file
852 */
853static void
854writevar(
855	int ofd,
856	off_t off,
857	int var
858	)
859{
860
861	if (lseek(ofd, off, L_SET) == -1)
862	{
863		(void) fprintf(stderr, "%s: lseek fails: ", progname);
864		perror("");
865		exit(1);
866	}
867	if (write(ofd, (char *)&var, sizeof(int)) != sizeof(int))
868	{
869		(void) fprintf(stderr, "%s: write fails: ", progname);
870		perror("");
871		exit(1);
872	}
873	return;
874}
875
876
877/*
878 * readvar - read a variable from the file
879 */
880static void
881readvar(
882	int ifd,
883	off_t off,
884	int *var
885	)
886{
887	int i;
888
889	if (lseek(ifd, off, L_SET) == -1)
890	{
891		(void) fprintf(stderr, "%s: lseek fails: ", progname);
892		perror("");
893		exit(1);
894	}
895	i = read(ifd, (char *)var, sizeof(int));
896	if (i < 0)
897	{
898		(void) fprintf(stderr, "%s: read fails: ", progname);
899		perror("");
900		exit(1);
901	}
902	if (i != sizeof(int))
903	{
904		(void) fprintf(stderr, "%s: read expected %d, got %d\n",
905			       progname, (int)sizeof(int), i);
906		exit(1);
907	}
908	return;
909}
910#endif /* not Linux */
911