1/*	$OpenBSD: rdate.c,v 1.37 2023/01/04 13:00:11 jsg Exp $	*/
2/*	$NetBSD: rdate.c,v 1.4 1996/03/16 12:37:45 pk Exp $	*/
3
4/*
5 * Copyright (c) 1994 Christos Zoulas
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * rdate.c: Set the date from the specified host
31 *
32 *	Time is returned as the number of seconds since
33 *	midnight January 1st 1900.
34 */
35
36#include <sys/socket.h>
37#include <sys/time.h>
38#include <sys/wait.h>
39
40#include <stdio.h>
41#include <stdlib.h>
42#include <ctype.h>
43#include <err.h>
44#include <string.h>
45#include <unistd.h>
46#include <time.h>
47
48/* there are systems without libutil; for portability */
49#ifndef NO_UTIL
50#include <util.h>
51#else
52#define logwtmp(a,b,c)
53#endif
54
55void rfc868time_client(const char *, int, struct timeval *, struct timeval *, int);
56void ntp_client(const char *, int, struct timeval *, struct timeval *, int);
57
58extern char    *__progname;
59__dead void	usage(void);
60
61struct {
62	char message[2048];
63	struct timeval new;
64	struct timeval adjust;
65} pdata;
66
67__dead void
68usage(void)
69{
70	(void) fprintf(stderr, "usage: %s [-46acnopsv] host\n", __progname);
71	exit(1);
72}
73
74int
75main(int argc, char **argv)
76{
77	int             pr = 0, silent = 0, ntp = 1, verbose = 0;
78	int		slidetime = 0, corrleaps = 0;
79	char           *hname;
80	int             c, p[2], pid;
81	int		family = PF_UNSPEC;
82
83	while ((c = getopt(argc, argv, "46psanocv")) != -1) {
84		switch (c) {
85		case '4':
86			family = PF_INET;
87			break;
88
89		case '6':
90			family = PF_INET6;
91			break;
92
93		case 'p':
94			pr = 1;
95			break;
96
97		case 's':
98			silent = 1;
99			break;
100
101		case 'a':
102			slidetime = 1;
103			break;
104
105		case 'n':
106			ntp = 1;
107			break;
108
109		case 'o':
110			ntp = 0;
111			break;
112
113		case 'c':
114			corrleaps = 1;
115			break;
116
117		case 'v':
118			verbose = 1;
119			break;
120
121		default:
122			usage();
123		}
124	}
125	if (argc - 1 != optind)
126		usage();
127	hname = argv[optind];
128
129	/*
130	 * Privilege separation increases safety, with a slight reduction
131	 * in precision because the time values have to return over a pipe.
132	 */
133	if (pipe(p) == -1)
134		err(1, "pipe");
135	switch ((pid = fork())) {
136	case -1:
137		err(1, "fork");
138		break;
139	case 0:
140		if (pledge("stdio inet dns", NULL) == -1)
141			err(1, "pledge");
142
143		close(p[0]);	/* read side of pipe */
144		dup2(p[1], STDIN_FILENO);
145		if (p[1] != STDIN_FILENO)
146			close(p[1]);
147		dup2(STDIN_FILENO, STDOUT_FILENO);
148		dup2(STDOUT_FILENO, STDERR_FILENO);
149		setvbuf(stdout, NULL, _IOFBF, 0);
150		setvbuf(stderr, NULL, _IOFBF, 0);
151
152		if (ntp)
153			ntp_client(hname, family, &pdata.new,
154			    &pdata.adjust, corrleaps);
155		else
156			rfc868time_client(hname, family, &pdata.new,
157			    &pdata.adjust, corrleaps);
158
159		if (write(STDOUT_FILENO, &pdata, sizeof pdata) != sizeof pdata)
160			exit(1);
161		exit(0);
162	}
163
164	if (pledge("stdio rpath wpath settime", NULL) == -1)
165		err(1, "pledge");
166
167	close(p[1]);	/* write side of pipe */
168	if (read(p[0], &pdata, sizeof pdata) < 1)
169		err(1, "child did not collect time");
170	if (waitpid(pid, NULL, 0) == -1)
171		err(1, "waitpid");
172
173	/*
174	 * A viable timestamp from the child contains no message.
175	 */
176	if (pdata.message[0]) {
177		pdata.message[sizeof(pdata.message)- 1] = '\0';
178		write(STDERR_FILENO, pdata.message, strlen(pdata.message));
179		exit(1);
180	}
181
182	if (!pr) {
183		if (!slidetime) {
184			logwtmp("|", "date", "");
185			if (settimeofday(&pdata.new, NULL) == -1)
186				err(1, "Could not set time of day");
187			logwtmp("{", "date", "");
188		} else {
189			if (adjtime(&pdata.adjust, NULL) == -1)
190				err(1, "Could not adjust time of day");
191		}
192	}
193
194	if (pledge("stdio rpath", NULL) == -1)
195		err(1, "pledge");
196
197	if (!silent) {
198		struct tm      *ltm;
199		char		buf[80];
200		time_t		tim = pdata.new.tv_sec;
201		double		adjsec;
202
203		ltm = localtime(&tim);
204		(void) strftime(buf, sizeof buf, "%a %b %e %H:%M:%S %Z %Y\n", ltm);
205		(void) fputs(buf, stdout);
206
207		adjsec  = pdata.adjust.tv_sec + pdata.adjust.tv_usec / 1.0e6;
208
209		if (slidetime || verbose) {
210			if (ntp)
211				(void) fprintf(stdout,
212				   "%s: adjust local clock by %.6f seconds\n",
213				   __progname, adjsec);
214			else
215				(void) fprintf(stdout,
216				   "%s: adjust local clock by %lld seconds\n",
217				   __progname, (long long)pdata.adjust.tv_sec);
218		}
219	}
220
221	return 0;
222}
223