OpenSolaris_b135/cmd/tzreload/tzreload.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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);
}