xref: /openbsd/usr.sbin/rdate/rdate.c (revision 893695ce)
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 
55 void rfc868time_client(const char *, int, struct timeval *, struct timeval *, int);
56 void ntp_client(const char *, int, struct timeval *, struct timeval *, int);
57 
58 extern char    *__progname;
59 __dead void	usage(void);
60 
61 struct {
62 	char message[2048];
63 	struct timeval new;
64 	struct timeval adjust;
65 } pdata;
66 
67 __dead void
usage(void)68 usage(void)
69 {
70 	(void) fprintf(stderr, "usage: %s [-46acnopsv] host\n", __progname);
71 	exit(1);
72 }
73 
74 int
main(int argc,char ** argv)75 main(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