summaryrefslogtreecommitdiff
path: root/usr/src/cmd/tzreload
diff options
context:
space:
mode:
authorNobutomo Nakano <Nobutomo.Nakano@Sun.COM>2009-11-19 13:59:09 -0800
committerNobutomo Nakano <Nobutomo.Nakano@Sun.COM>2009-11-19 13:59:09 -0800
commitd1419d5a02eaedd520f925cd465fd62f3950ae43 (patch)
tree70b607534608fd5705f0880922159aac8a9a4f67 /usr/src/cmd/tzreload
parent5fbacb6048c95ef1fe242a6a164168d2624111da (diff)
downloadillumos-joyent-d1419d5a02eaedd520f925cd465fd62f3950ae43.tar.gz
PSARC/2009/516 Timezone cache renewal
6751272 RFE: Solaris timezone patches should not require reboot
Diffstat (limited to 'usr/src/cmd/tzreload')
-rw-r--r--usr/src/cmd/tzreload/Makefile43
-rw-r--r--usr/src/cmd/tzreload/tzreload.c420
2 files changed, 463 insertions, 0 deletions
diff --git a/usr/src/cmd/tzreload/Makefile b/usr/src/cmd/tzreload/Makefile
new file mode 100644
index 0000000000..21325d3937
--- /dev/null
+++ b/usr/src/cmd/tzreload/Makefile
@@ -0,0 +1,43 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=tzreload
+
+include ../Makefile.cmd
+
+CPPFLAGS += -I../cron
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTSBINPROG)
+ $(RM) $(ROOTUSRSBIN)/$(PROG)
+ $(SYMLINK) ../../sbin/$(PROG) $(ROOTUSRSBIN)/$(PROG)
+
+clean:
+
+lint: lint_PROG
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/tzreload/tzreload.c b/usr/src/cmd/tzreload/tzreload.c
new file mode 100644
index 0000000000..30d4a7600a
--- /dev/null
+++ b/usr/src/cmd/tzreload/tzreload.c
@@ -0,0 +1,420 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+#include <atomic.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <locale.h>
+#include <libintl.h>
+#include <zone.h>
+#include <libzonecfg.h>
+#include <sys/brand.h>
+#include <dlfcn.h>
+
+#define TZSYNC_FILE "/var/run/tzsync"
+
+static void init_file(void);
+static void doit(const char *zname, const char *zroot, int get);
+static void counter_get(const char *zname, int fd);
+static void counter_set(int fd);
+static void walk_zones(int get);
+static void send_cron_msg(const char *zname, const char *zroot);
+
+/*
+ * There are undocumeted command line options:
+ * -l list the value of semaphore.
+ * -I initialize the semaphore file (ie /var/run/tzsync)
+ */
+
+int
+main(int argc, char **argv)
+{
+ int arg;
+ int all = 0, get = 0, init = 0;
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ while ((arg = getopt(argc, argv, "alI")) != EOF) {
+ switch (arg) {
+ case 'a':
+ all = 1;
+ break;
+ case 'l':
+ get = 1;
+ break;
+ case 'I':
+ init = 1;
+ break;
+ default:
+ (void) fprintf(stderr,
+ gettext("Usage: tzreload [-a]\n"));
+ exit(1);
+ }
+ }
+
+ if (init) {
+ init_file();
+ return (0);
+ }
+
+ if (all)
+ walk_zones(get);
+ else
+ doit(NULL, "", get);
+
+ return (0);
+}
+
+/*
+ * Create /var/run/tzsync atomically.
+ *
+ * While creating the /var/run/tzsync initially, there is a timing window
+ * that the file is created but no disk block is allocated (empty file).
+ * If apps mmap'ed the file at the very moment, it succeeds but accessing
+ * the memory page causes a segfault since disk block isn't yet allocated.
+ * To avoid this situation, we create a temp file which has pagesize block
+ * assigned, and then rename it to tzsync.
+ */
+static void
+init_file(void)
+{
+ char path[sizeof (TZSYNC_FILE) + 16];
+ char *buf;
+ int fd, pgsz;
+ struct stat st;
+
+ /* We don't allow to re-create the file */
+ if (stat(TZSYNC_FILE, &st) == 0) {
+ (void) fprintf(stderr, gettext("%s already exists.\n"),
+ TZSYNC_FILE);
+ exit(1);
+ }
+
+ pgsz = sysconf(_SC_PAGESIZE);
+
+ (void) strcpy(path, TZSYNC_FILE "XXXXXX");
+ if ((fd = mkstemp(path)) == -1) {
+ (void) fprintf(stderr,
+ gettext("failed to create a temporary file.\n"));
+ exit(1);
+ }
+
+ if ((buf = calloc(1, pgsz)) == NULL) {
+ (void) fprintf(stderr, gettext("Insufficient memory.\n"));
+errout:
+ (void) close(fd);
+ (void) unlink(path);
+ exit(1);
+ }
+
+ if (write(fd, buf, pgsz) != pgsz) {
+ (void) fprintf(stderr,
+ gettext("failed to create tzsync file, %s\n"),
+ strerror(errno));
+ goto errout;
+ }
+ (void) close(fd);
+
+ /* link it */
+ if (link(path, TZSYNC_FILE) != 0) {
+ if (errno == EEXIST) {
+ (void) fprintf(stderr, gettext("%s already exists.\n"),
+ TZSYNC_FILE);
+ } else {
+ (void) fprintf(stderr, gettext("failed to create %s\n"),
+ TZSYNC_FILE);
+ }
+ (void) unlink(path);
+ exit(1);
+ }
+ (void) unlink(path);
+
+ /*
+ * Unplivileged apps may fail to open the file until the chmod
+ * below succeeds. However, it's okay as long as open() fails;
+ * ctime() won't cache zoneinfo until file is opened and mmap'd.
+ */
+
+ /* /var/run/tzsync has been made. Adjust permission */
+ if (chmod(TZSYNC_FILE, 0644) != 0) {
+ (void) fprintf(stderr,
+ gettext("failed to change permission of %s\n"),
+ TZSYNC_FILE);
+ (void) unlink(TZSYNC_FILE);
+ exit(1);
+ }
+}
+
+/*
+ * Open the /var/run/tzsync, then set or get the semaphore.
+ *
+ * zname name of zone (NULL if no need to consider zones)
+ * zroot zone's root path
+ * get get/set semaphore
+ */
+static void
+doit(const char *zname, const char *zroot, int get)
+{
+ int fd;
+ char file[PATH_MAX + 1];
+
+ if (strlcpy(file, zroot, sizeof (file)) >= sizeof (file) ||
+ strlcat(file, TZSYNC_FILE, sizeof (file)) >= sizeof (file)) {
+ (void) fprintf(stderr, gettext("zonepath too long\n"));
+ exit(1);
+ }
+
+ if ((fd = open(file, get ? O_RDONLY : O_RDWR)) < 0) {
+ (void) fprintf(stderr,
+ gettext("Can't open file %s, %s\n"),
+ file, strerror(errno));
+ exit(1);
+ }
+
+ if (get) {
+ counter_get(zname, fd);
+ } else {
+ counter_set(fd);
+ /* let cron reschedule events */
+ send_cron_msg(zname, zroot);
+ }
+
+ (void) close(fd);
+}
+
+/*
+ * Get semaphore value and print.
+ */
+static void
+counter_get(const char *zname, int fd)
+{
+ uint32_t counter;
+ caddr_t addr;
+
+ addr = mmap(NULL, sizeof (uint32_t), PROT_READ, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ (void) fprintf(stderr,
+ gettext("Error mapping semaphore: %s\n"),
+ strerror(errno));
+ exit(1);
+ }
+ counter = *(uint32_t *)(uintptr_t)addr;
+
+ (void) munmap(addr, sizeof (uint32_t));
+
+ if (zname == NULL)
+ (void) printf("%u\n", counter);
+ else
+ (void) printf("%-20s %u\n", zname, counter);
+
+}
+
+/*
+ * Increment semaphore value.
+ */
+static void
+counter_set(int fd)
+{
+ caddr_t addr;
+
+ addr = mmap(NULL, sizeof (uint32_t), PROT_READ|PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ (void) fprintf(stderr,
+ gettext("Error mapping semaphore: %s\n"),
+ strerror(errno));
+ exit(1);
+ }
+
+ /*LINTED*/
+ atomic_add_32((uint32_t *)addr, 1);
+
+ (void) munmap(addr, sizeof (uint32_t));
+}
+
+/*
+ * Walk through running zones and call doit() for each zones.
+ *
+ * Note: we call zone_get_rootpath() indirectly using dlopen().
+ * This is because tzreload resides under /sbin and needs to run
+ * without /usr (ie /usr/lib/libzonecfg.so.1). The reason tzreload
+ * being in /sbin is that tzreload -I may be called to create
+ * /var/run/tzsync before /usr is mounted. To do that zone_get_rootpath()
+ * isn't necessary. Therefore, libzonecfg is dlopen'd when required
+ * rather than having static linkage to it which would make tzreload
+ * unable to run without /usr.
+ */
+static void
+walk_zones(int get)
+{
+ zoneid_t *zids;
+ uint_t ui, nzents, onzents;
+ char zroot[PATH_MAX + 1];
+ char zname[ZONENAME_MAX];
+ char zbrand[MAXNAMELEN];
+ static int (*get_zroot)(char *, char *, size_t);
+
+ if (getzoneid() != GLOBAL_ZONEID) {
+ (void) fprintf(stderr, gettext("not in the global zone.\n"));
+ exit(1);
+ }
+
+ if (get_zroot == NULL) {
+ void *hdl;
+
+ if ((hdl = dlopen("libzonecfg.so.1", RTLD_NOW)) == NULL) {
+ (void) fprintf(stderr,
+ gettext("unable to get zone configuration.\n"));
+ exit(1);
+ }
+ get_zroot = (int (*)(char *, char *, size_t))
+ dlsym(hdl, "zone_get_rootpath");
+ if (get_zroot == NULL) {
+ (void) fprintf(stderr,
+ gettext("unable to get zone configuration.\n"));
+ exit(1);
+ }
+ }
+
+ nzents = 0;
+ if (zone_list(NULL, &nzents) != 0) {
+ (void) fprintf(stderr,
+ gettext("failed to get zoneid list\n"));
+ exit(1);
+ }
+
+again:
+ if (nzents == 0)
+ return;
+
+ if ((zids = malloc(nzents * sizeof (zoneid_t))) == NULL) {
+ (void) fprintf(stderr, gettext("Insufficient memory.\n"));
+ exit(1);
+ }
+
+ onzents = nzents;
+ if (zone_list(zids, &nzents) != 0) {
+ (void) fprintf(stderr,
+ gettext("failed to get zoneid list\n"));
+ exit(1);
+ }
+
+ if (nzents != onzents) {
+ /* zone increased while doing zone_list() */
+ free(zids);
+ goto again;
+ }
+
+ for (ui = 0; ui < nzents; ui++) {
+ if (zone_getattr(zids[ui], ZONE_ATTR_BRAND, zbrand,
+ sizeof (zbrand)) < 0) {
+ (void) fprintf(stderr,
+ gettext("failed to get zone attribute\n"));
+ exit(1);
+ }
+ /* We only take care of native zones */
+ if (strcmp(zbrand, NATIVE_BRAND_NAME) != 0)
+ continue;
+ if (getzonenamebyid(zids[ui], zname, sizeof (zname)) < 0) {
+ (void) fprintf(stderr,
+ gettext("failed to get zone name\n"));
+ exit(1);
+ }
+
+ if (zids[ui] == GLOBAL_ZONEID) {
+ zroot[0] = '\0';
+ } else {
+ if ((*get_zroot)(zname, zroot,
+ sizeof (zroot)) != Z_OK) {
+ (void) fprintf(stderr,
+ gettext("failed to get zone's root\n"));
+ exit(1);
+ }
+ }
+ doit(zname, zroot, get);
+ }
+}
+
+#include "cron.h"
+
+/*
+ * Send REFRESH event to cron.
+ */
+static void
+send_cron_msg(const char *zname, const char *zroot)
+{
+ struct message msg;
+ int msgfd;
+ char fifo[PATH_MAX + 1];
+
+ if (strlcpy(fifo, zroot, sizeof (fifo)) >= sizeof (fifo) ||
+ strlcat(fifo, FIFO, sizeof (fifo)) >= sizeof (fifo)) {
+ (void) fprintf(stderr, gettext("zonepath too long\n"));
+ exit(1);
+ }
+
+ (void) memset(&msg, 0, sizeof (msg));
+ msg.etype = REFRESH;
+
+ if ((msgfd = open(fifo, O_WRONLY|O_NDELAY)) < 0) {
+ if (errno == ENXIO || errno == ENOENT) {
+ if (zname != NULL) {
+ (void) fprintf(stderr, gettext(
+ "cron isn't running in %s zone.\n"), zname);
+ } else {
+ (void) fprintf(stderr,
+ gettext("cron isn't running.\n"));
+ }
+ } else {
+ if (zname != NULL) {
+ (void) fprintf(stderr, gettext(
+ "failed to send message to cron "
+ "in %s zone.\n"), zname);
+ } else {
+ (void) fprintf(stderr, gettext(
+ "failed to send message to cron.\n"));
+ }
+ }
+ return;
+ }
+
+ if (write(msgfd, &msg, sizeof (msg)) != sizeof (msg)) {
+ (void) fprintf(stderr, gettext("failed to send message.\n"));
+ }
+
+ (void) close(msgfd);
+}