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