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
main(int argc,char ** argv)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
init_file(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
doit(const char * zname,const char * zroot,int get)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
counter_get(const char * zname,int fd)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
counter_set(int fd)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
walk_zones(int get)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
send_cron_msg(const char * zname,const char * zroot)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