xref: /illumos-gate/usr/src/cmd/tzreload/tzreload.c (revision 2f172c55ef76964744bc62b4500ece87f3089b4d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	All Rights Reserved	*/
29 
30 #include <atomic.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <limits.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <sys/mman.h>
39 #include <sys/stat.h>
40 #include <locale.h>
41 #include <libintl.h>
42 #include <zone.h>
43 #include <libzonecfg.h>
44 #include <sys/brand.h>
45 #include <dlfcn.h>
46 
47 #define	TZSYNC_FILE	"/var/run/tzsync"
48 
49 static void	init_file(void);
50 static void	doit(const char *zname, const char *zroot, int get);
51 static void	counter_get(const char *zname, int fd);
52 static void	counter_set(int fd);
53 static void	walk_zones(int get);
54 static void	send_cron_msg(const char *zname, const char *zroot);
55 
56 /*
57  * There are undocumeted command line options:
58  * -l	list the value of semaphore.
59  * -I	initialize the semaphore file (ie /var/run/tzsync)
60  */
61 
62 int
63 main(int argc, char **argv)
64 {
65 	int	arg;
66 	int	all = 0, get = 0, init = 0;
67 
68 	(void) setlocale(LC_ALL, "");
69 	(void) textdomain(TEXT_DOMAIN);
70 
71 	while ((arg = getopt(argc, argv, "alI")) != EOF) {
72 		switch (arg) {
73 		case 'a':
74 			all = 1;
75 			break;
76 		case 'l':
77 			get = 1;
78 			break;
79 		case 'I':
80 			init = 1;
81 			break;
82 		default:
83 			(void) fprintf(stderr,
84 			    gettext("Usage: tzreload [-a]\n"));
85 			exit(1);
86 		}
87 	}
88 
89 	if (init) {
90 		init_file();
91 		return (0);
92 	}
93 
94 	if (all)
95 		walk_zones(get);
96 	else
97 		doit(NULL, "", get);
98 
99 	return (0);
100 }
101 
102 /*
103  * Create /var/run/tzsync atomically.
104  *
105  * While creating the /var/run/tzsync initially, there is a timing window
106  * that the file is created but no disk block is allocated (empty file).
107  * If apps mmap'ed the file at the very moment, it succeeds but accessing
108  * the memory page causes a segfault since disk block isn't yet allocated.
109  * To avoid this situation, we create a temp file which has pagesize block
110  * assigned, and then rename it to tzsync.
111  */
112 static void
113 init_file(void)
114 {
115 	char	path[sizeof (TZSYNC_FILE) + 16];
116 	char	*buf;
117 	int	fd, pgsz;
118 	struct stat st;
119 
120 	/* We don't allow to re-create the file */
121 	if (stat(TZSYNC_FILE, &st) == 0) {
122 		(void) fprintf(stderr, gettext("%s already exists.\n"),
123 		    TZSYNC_FILE);
124 		exit(1);
125 	}
126 
127 	pgsz = sysconf(_SC_PAGESIZE);
128 
129 	(void) strcpy(path, TZSYNC_FILE "XXXXXX");
130 	if ((fd = mkstemp(path)) == -1) {
131 		(void) fprintf(stderr,
132 		    gettext("failed to create a temporary file.\n"));
133 		exit(1);
134 	}
135 
136 	if ((buf = calloc(1, pgsz)) == NULL) {
137 		(void) fprintf(stderr, gettext("Insufficient memory.\n"));
138 errout:
139 		(void) close(fd);
140 		(void) unlink(path);
141 		exit(1);
142 	}
143 
144 	if (write(fd, buf, pgsz) != pgsz) {
145 		(void) fprintf(stderr,
146 		    gettext("failed to create tzsync file, %s\n"),
147 		    strerror(errno));
148 		goto errout;
149 	}
150 	(void) close(fd);
151 
152 	/* link it */
153 	if (link(path, TZSYNC_FILE) != 0) {
154 		if (errno == EEXIST) {
155 			(void) fprintf(stderr, gettext("%s already exists.\n"),
156 			    TZSYNC_FILE);
157 		} else {
158 			(void) fprintf(stderr, gettext("failed to create %s\n"),
159 			    TZSYNC_FILE);
160 		}
161 		(void) unlink(path);
162 		exit(1);
163 	}
164 	(void) unlink(path);
165 
166 	/*
167 	 * Unplivileged apps may fail to open the file until the chmod
168 	 * below succeeds. However, it's okay as long as open() fails;
169 	 * ctime() won't cache zoneinfo until file is opened and mmap'd.
170 	 */
171 
172 	/* /var/run/tzsync has been made. Adjust permission */
173 	if (chmod(TZSYNC_FILE, 0644) != 0) {
174 		(void) fprintf(stderr,
175 		    gettext("failed to change permission of %s\n"),
176 		    TZSYNC_FILE);
177 		(void) unlink(TZSYNC_FILE);
178 		exit(1);
179 	}
180 }
181 
182 /*
183  * Open the /var/run/tzsync, then set or get the semaphore.
184  *
185  * zname	name of zone (NULL if no need to consider zones)
186  * zroot	zone's root path
187  * get		get/set semaphore
188  */
189 static void
190 doit(const char *zname, const char *zroot, int get)
191 {
192 	int	fd;
193 	char	file[PATH_MAX + 1];
194 
195 	if (strlcpy(file, zroot, sizeof (file)) >= sizeof (file) ||
196 	    strlcat(file, TZSYNC_FILE, sizeof (file)) >= sizeof (file)) {
197 		(void) fprintf(stderr, gettext("zonepath too long\n"));
198 		exit(1);
199 	}
200 
201 	if ((fd = open(file, get ? O_RDONLY : O_RDWR)) < 0) {
202 		(void) fprintf(stderr,
203 		    gettext("Can't open file %s, %s\n"),
204 		    file, strerror(errno));
205 		exit(1);
206 	}
207 
208 	if (get) {
209 		counter_get(zname, fd);
210 	} else {
211 		counter_set(fd);
212 		/* let cron reschedule events */
213 		send_cron_msg(zname, zroot);
214 	}
215 
216 	(void) close(fd);
217 }
218 
219 /*
220  * Get semaphore value and print.
221  */
222 static void
223 counter_get(const char *zname, int fd)
224 {
225 	uint32_t counter;
226 	caddr_t	addr;
227 
228 	addr = mmap(NULL, sizeof (uint32_t), PROT_READ, MAP_SHARED, fd, 0);
229 	if (addr == MAP_FAILED) {
230 		(void) fprintf(stderr,
231 		    gettext("Error mapping semaphore: %s\n"),
232 		    strerror(errno));
233 		exit(1);
234 	}
235 	counter = *(uint32_t *)(uintptr_t)addr;
236 
237 	(void) munmap(addr, sizeof (uint32_t));
238 
239 	if (zname == NULL)
240 		(void) printf("%u\n", counter);
241 	else
242 		(void) printf("%-20s %u\n", zname, counter);
243 
244 }
245 
246 /*
247  * Increment semaphore value.
248  */
249 static void
250 counter_set(int fd)
251 {
252 	caddr_t	addr;
253 
254 	addr = mmap(NULL, sizeof (uint32_t), PROT_READ|PROT_WRITE,
255 	    MAP_SHARED, fd, 0);
256 	if (addr == MAP_FAILED) {
257 		(void) fprintf(stderr,
258 		    gettext("Error mapping semaphore: %s\n"),
259 		    strerror(errno));
260 		exit(1);
261 	}
262 
263 	/*LINTED*/
264 	atomic_add_32((uint32_t *)addr, 1);
265 
266 	(void) munmap(addr, sizeof (uint32_t));
267 }
268 
269 /*
270  * Walk through running zones and call doit() for each zones.
271  *
272  * Note: we call zone_get_rootpath() indirectly using dlopen().
273  * This is because tzreload resides under /sbin and needs to run
274  * without /usr (ie /usr/lib/libzonecfg.so.1). The reason tzreload
275  * being in /sbin is that tzreload -I may be called to create
276  * /var/run/tzsync before /usr is mounted. To do that zone_get_rootpath()
277  * isn't necessary. Therefore, libzonecfg is dlopen'd when required
278  * rather than having static linkage to it which would make tzreload
279  * unable to run without /usr.
280  */
281 static void
282 walk_zones(int get)
283 {
284 	zoneid_t *zids;
285 	uint_t	ui, nzents, onzents;
286 	char	zroot[PATH_MAX + 1];
287 	char	zname[ZONENAME_MAX];
288 	char	zbrand[MAXNAMELEN];
289 	static int (*get_zroot)(char *, char *, size_t);
290 
291 	if (getzoneid() != GLOBAL_ZONEID) {
292 		(void) fprintf(stderr, gettext("not in the global zone.\n"));
293 		exit(1);
294 	}
295 
296 	if (get_zroot == NULL) {
297 		void	*hdl;
298 
299 		if ((hdl = dlopen("libzonecfg.so.1", RTLD_NOW)) == NULL) {
300 			(void) fprintf(stderr,
301 			    gettext("unable to get zone configuration.\n"));
302 			exit(1);
303 		}
304 		get_zroot = (int (*)(char *, char *, size_t))
305 		    dlsym(hdl, "zone_get_rootpath");
306 		if (get_zroot == NULL) {
307 			(void) fprintf(stderr,
308 			    gettext("unable to get zone configuration.\n"));
309 			exit(1);
310 		}
311 	}
312 
313 	nzents = 0;
314 	if (zone_list(NULL, &nzents) != 0) {
315 		(void) fprintf(stderr,
316 		    gettext("failed to get zoneid list\n"));
317 		exit(1);
318 	}
319 
320 again:
321 	if (nzents == 0)
322 		return;
323 
324 	if ((zids = malloc(nzents * sizeof (zoneid_t))) == NULL) {
325 		(void) fprintf(stderr, gettext("Insufficient memory.\n"));
326 		exit(1);
327 	}
328 
329 	onzents = nzents;
330 	if (zone_list(zids, &nzents) != 0) {
331 		(void) fprintf(stderr,
332 		    gettext("failed to get zoneid list\n"));
333 		exit(1);
334 	}
335 
336 	if (nzents != onzents) {
337 		/* zone increased while doing zone_list() */
338 		free(zids);
339 		goto again;
340 	}
341 
342 	for (ui = 0; ui < nzents; ui++) {
343 		if (zone_getattr(zids[ui], ZONE_ATTR_BRAND, zbrand,
344 		    sizeof (zbrand)) < 0) {
345 			(void) fprintf(stderr,
346 			    gettext("failed to get zone attribute\n"));
347 			exit(1);
348 		}
349 		/* We only take care of native zones */
350 		if (strcmp(zbrand, NATIVE_BRAND_NAME) != 0)
351 			continue;
352 		if (getzonenamebyid(zids[ui], zname, sizeof (zname)) < 0) {
353 			(void) fprintf(stderr,
354 			    gettext("failed to get zone name\n"));
355 			exit(1);
356 		}
357 
358 		if (zids[ui] == GLOBAL_ZONEID) {
359 			zroot[0] = '\0';
360 		} else {
361 			if ((*get_zroot)(zname, zroot,
362 			    sizeof (zroot)) != Z_OK) {
363 				(void) fprintf(stderr,
364 				    gettext("failed to get zone's root\n"));
365 				exit(1);
366 			}
367 		}
368 		doit(zname, zroot, get);
369 	}
370 }
371 
372 #include "cron.h"
373 
374 /*
375  * Send REFRESH event to cron.
376  */
377 static void
378 send_cron_msg(const char *zname, const char *zroot)
379 {
380 	struct message msg;
381 	int	msgfd;
382 	char	fifo[PATH_MAX + 1];
383 
384 	if (strlcpy(fifo, zroot, sizeof (fifo)) >= sizeof (fifo) ||
385 	    strlcat(fifo, FIFO, sizeof (fifo)) >= sizeof (fifo)) {
386 		(void) fprintf(stderr, gettext("zonepath too long\n"));
387 		exit(1);
388 	}
389 
390 	(void) memset(&msg, 0, sizeof (msg));
391 	msg.etype = REFRESH;
392 
393 	if ((msgfd = open(fifo, O_WRONLY|O_NDELAY)) < 0) {
394 		if (errno == ENXIO || errno == ENOENT) {
395 			if (zname != NULL) {
396 				(void) fprintf(stderr, gettext(
397 				    "cron isn't running in %s zone.\n"), zname);
398 			} else {
399 				(void) fprintf(stderr,
400 				    gettext("cron isn't running.\n"));
401 			}
402 		} else {
403 			if (zname != NULL) {
404 				(void) fprintf(stderr, gettext(
405 				    "failed to send message to cron "
406 				    "in %s zone.\n"), zname);
407 			} else {
408 				(void) fprintf(stderr, gettext(
409 				    "failed to send message to cron.\n"));
410 			}
411 		}
412 		return;
413 	}
414 
415 	if (write(msgfd, &msg, sizeof (msg)) != sizeof (msg)) {
416 		(void) fprintf(stderr, gettext("failed to send message.\n"));
417 	}
418 
419 	(void) close(msgfd);
420 }
421