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