#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);
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);
}
static void
init_file(void)
{
char path[sizeof (TZSYNC_FILE) + 16];
char *buf;
int fd, pgsz;
struct stat st;
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);
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);
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);
}
}
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);
send_cron_msg(zname, zroot);
}
(void) close(fd);
}
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);
}
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);
}
atomic_add_32((uint32_t *)addr, 1);
(void) munmap(addr, sizeof (uint32_t));
}
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) {
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);
}
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"
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);
}