/* Helper function for getSystemTimeZoneID() that compares the zoneinfo_file_path (regular) file with files in the zoneinfo_dir_path until it finds a match. The matching file's name is used to determine the time zone ID. */ static QString findZoneinfoFile(QString zoneinfo_file_path, QString zoneinfo_dir_path) { QString zone_id("UNDEF"); QDir zoneinfo_dir(zoneinfo_dir_path); QFileInfoList dirlist = zoneinfo_dir.entryInfoList(); QFileInfo info; QString basename; QFileInfo zoneinfo_file_info(zoneinfo_file_path); for (QFileInfoList::const_iterator it = dirlist.begin(); it != dirlist.end(); ++it) { info = *it; // Skip '.' and '..' and other files starting with "." and // skip localtime (which is often a link to zoneinfo_file_path) basename = info.baseName(); if (basename.isEmpty() || (basename == "localtime")) { continue; } if (info.isDir()) { zone_id = findZoneinfoFile(zoneinfo_file_path, info.absoluteFilePath()); if (zone_id != "UNDEF") return zone_id; } else if (compare_zone_files(zoneinfo_file_info, info)) { zone_id = info.absoluteFilePath(); break; } } return zone_id; }
/* * Returns a zone ID of Solaris when the TZ value is "localtime". * First, it tries scf. If scf fails, it looks for the same file as * /usr/share/lib/zoneinfo/localtime under /usr/share/lib/zoneinfo/. */ static char * getSolarisDefaultZoneID() { char *tz = NULL; struct stat statbuf; size_t size; char *buf; int fd; /* scf specific variables */ scf_handle_t *h = NULL; scf_snapshot_t *snap = NULL; scf_instance_t *inst = NULL; scf_propertygroup_t *pg = NULL; scf_property_t *prop = NULL; scf_value_t *val = NULL; if ((h = scf_handle_create(SCF_VERSION)) != NULL && scf_handle_bind(h) == 0 && (inst = scf_instance_create(h)) != NULL && (snap = scf_snapshot_create(h)) != NULL && (pg = scf_pg_create(h)) != NULL && (prop = scf_property_create(h)) != NULL && (val = scf_value_create(h)) != NULL && scf_handle_decode_fmri(h, TIMEZONE_FMRI, NULL, NULL, inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) == 0 && scf_instance_get_snapshot(inst, "running", snap) == 0 && scf_instance_get_pg_composed(inst, snap, TIMEZONE_PG, pg) == 0 && scf_pg_get_property(pg, LOCALTIME_PROP, prop) == 0 && scf_property_get_value(prop, val) == 0) { ssize_t len; /* Gets the length of the zone ID string */ len = scf_value_get_astring(val, NULL, 0); if (len != -1) { tz = malloc(++len); /* +1 for a null byte */ if (tz != NULL && scf_value_get_astring(val, tz, len) != -1) { cleanupScf(h, snap, inst, pg, prop, val, NULL); return tz; } } } cleanupScf(h, snap, inst, pg, prop, val, tz); if (stat(DEFAULT_ZONEINFO_FILE, &statbuf) == -1) { return NULL; } size = (size_t) statbuf.st_size; buf = malloc(size); if (buf == NULL) { return NULL; } if ((fd = open(DEFAULT_ZONEINFO_FILE, O_RDONLY)) == -1) { free((void *) buf); return NULL; } if (read(fd, buf, size) != (ssize_t) size) { (void) close(fd); free((void *) buf); return NULL; } (void) close(fd); tz = findZoneinfoFile(buf, size, ZONEINFO_DIR); free((void *) buf); return tz; }
/* * Performs Linux specific mapping and returns a zone ID * if found. Otherwise, NULL is returned. */ static char * getPlatformTimeZoneID() { struct stat statbuf; char *tz = NULL; FILE *fp; int fd; char *buf; size_t size; #if defined(__linux__) /* * Try reading the /etc/timezone file for Debian distros. There's * no spec of the file format available. This parsing assumes that * there's one line of an Olson tzid followed by a '\n', no * leading or trailing spaces, no comments. */ if ((fp = fopen(ETC_TIMEZONE_FILE, "r")) != NULL) { char line[256]; if (fgets(line, sizeof(line), fp) != NULL) { char *p = strchr(line, '\n'); if (p != NULL) { *p = '\0'; } if (strlen(line) > 0) { tz = strdup(line); } } (void) fclose(fp); if (tz != NULL) { return tz; } } #endif /* defined(__linux__) */ /* * Next, try /etc/localtime to find the zone ID. */ if (lstat(DEFAULT_ZONEINFO_FILE, &statbuf) == -1) { return NULL; } /* * If it's a symlink, get the link name and its zone ID part. (The * older versions of timeconfig created a symlink as described in * the Red Hat man page. It was changed in 1999 to create a copy * of a zoneinfo file. It's no longer possible to get the zone ID * from /etc/localtime.) */ if (S_ISLNK(statbuf.st_mode)) { char linkbuf[PATH_MAX+1]; int len; if ((len = readlink(DEFAULT_ZONEINFO_FILE, linkbuf, sizeof(linkbuf)-1)) == -1) { jio_fprintf(stderr, (const char *) "can't get a symlink of %s\n", DEFAULT_ZONEINFO_FILE); return NULL; } linkbuf[len] = '\0'; tz = getZoneName(linkbuf); if (tz != NULL) { tz = strdup(tz); return tz; } } /* * If it's a regular file, we need to find out the same zoneinfo file * that has been copied as /etc/localtime. * If initial symbolic link resolution failed, we should treat target * file as a regular file. */ if ((fd = open(DEFAULT_ZONEINFO_FILE, O_RDONLY)) == -1) { return NULL; } if (fstat(fd, &statbuf) == -1) { (void) close(fd); return NULL; } size = (size_t) statbuf.st_size; buf = (char *) malloc(size); if (buf == NULL) { (void) close(fd); return NULL; } if (read(fd, buf, size) != (ssize_t) size) { (void) close(fd); free((void *) buf); return NULL; } (void) close(fd); tz = findZoneinfoFile(buf, size, ZONEINFO_DIR); free((void *) buf); return tz; }
/* * Scans the specified directory and its subdirectories to find a * zoneinfo file which has the same content as /etc/localtime on Linux * or /usr/share/lib/zoneinfo/localtime on Solaris given in 'buf'. * If file is symbolic link, then the contents it points to are in buf. * Returns a zone ID if found, otherwise, NULL is returned. */ static char * findZoneinfoFile(char *buf, size_t size, const char *dir) { DIR *dirp = NULL; struct stat statbuf; struct dirent64 *dp = NULL; struct dirent64 *entry = NULL; char *pathname = NULL; int fd = -1; char *dbuf = NULL; char *tz = NULL; dirp = opendir(dir); if (dirp == NULL) { return NULL; } entry = (struct dirent64 *) malloc((size_t) pathconf(dir, _PC_NAME_MAX)); if (entry == NULL) { (void) closedir(dirp); return NULL; } while (readdir64_r(dirp, entry, &dp) == 0 && dp != NULL) { /* * Skip '.' and '..' (and possibly other .* files) */ if (dp->d_name[0] == '.') { continue; } /* * Skip "ROC", "posixrules", and "localtime". */ if ((strcmp(dp->d_name, "ROC") == 0) || (strcmp(dp->d_name, "posixrules") == 0) #if defined(__solaris__) /* * Skip the "src" and "tab" directories on Solaris. */ || (strcmp(dp->d_name, "src") == 0) || (strcmp(dp->d_name, "tab") == 0) #endif || (strcmp(dp->d_name, "localtime") == 0)) { continue; } pathname = getPathName(dir, dp->d_name); if (pathname == NULL) { break; } if (stat(pathname, &statbuf) == -1) { break; } if (S_ISDIR(statbuf.st_mode)) { tz = findZoneinfoFile(buf, size, pathname); if (tz != NULL) { break; } } else if (S_ISREG(statbuf.st_mode) && (size_t)statbuf.st_size == size) { dbuf = (char *) malloc(size); if (dbuf == NULL) { break; } if ((fd = open(pathname, O_RDONLY)) == -1) { break; } if (read(fd, dbuf, size) != (ssize_t) size) { break; } if (memcmp(buf, dbuf, size) == 0) { tz = getZoneName(pathname); if (tz != NULL) { tz = strdup(tz); } break; } free((void *) dbuf); dbuf = NULL; (void) close(fd); fd = -1; } free((void *) pathname); pathname = NULL; } if (entry != NULL) { free((void *) entry); } if (dirp != NULL) { (void) closedir(dirp); } if (pathname != NULL) { free((void *) pathname); } if (fd != -1) { (void) close(fd); } if (dbuf != NULL) { free((void *) dbuf); } return tz; }
/* * Scans the specified directory and its subdirectories to find a * zoneinfo file which has the same content as /etc/localtime on Linux * or /usr/share/lib/zoneinfo/localtime on Solaris given in 'buf'. * If file is symbolic link, then the contents it points to are in buf. * Returns a zone ID if found, otherwise, NULL is returned. */ static char * findZoneinfoFile(char *buf, size_t size, const char *dir) { DIR *dirp = NULL; struct stat statbuf; struct dirent *dp = NULL; struct dirent *entry = NULL; char *pathname = NULL; int fd = -1; char *dbuf = NULL; char *tz = NULL; dirp = opendir(dir); if (dirp == NULL) { return NULL; } entry = (struct dirent *) malloc((size_t) pathconf(dir, _PC_NAME_MAX)); if (entry == NULL) { (void) closedir(dirp); return NULL; } #if defined(__linux__) || defined(MACOSX) || (defined(__solaris__) \ && (defined(_POSIX_PTHREAD_SEMANTICS) || defined(_LP64))) while (readdir_r(dirp, entry, &dp) == 0 && dp != NULL) { #else while ((dp = readdir_r(dirp, entry)) != NULL) { #endif /* * Skip '.' and '..' (and possibly other .* files) */ if (dp->d_name[0] == '.') { continue; } /* * Skip "ROC", "posixrules", and "localtime". */ if ((strcmp(dp->d_name, "ROC") == 0) || (strcmp(dp->d_name, "posixrules") == 0) #ifdef __solaris__ /* * Skip the "src" and "tab" directories on Solaris. */ || (strcmp(dp->d_name, "src") == 0) || (strcmp(dp->d_name, "tab") == 0) #endif || (strcmp(dp->d_name, "localtime") == 0)) { continue; } pathname = getPathName(dir, dp->d_name); if (pathname == NULL) { break; } if (stat(pathname, &statbuf) == -1) { break; } if (S_ISDIR(statbuf.st_mode)) { tz = findZoneinfoFile(buf, size, pathname); if (tz != NULL) { break; } } else if (S_ISREG(statbuf.st_mode) && (size_t)statbuf.st_size == size) { dbuf = (char *) malloc(size); if (dbuf == NULL) { break; } if ((fd = open(pathname, O_RDONLY)) == -1) { fd = 0; break; } if (read(fd, dbuf, size) != (ssize_t) size) { break; } if (memcmp(buf, dbuf, size) == 0) { tz = getZoneName(pathname); if (tz != NULL) { tz = strdup(tz); } break; } free((void *) dbuf); dbuf = NULL; (void) close(fd); fd = 0; } free((void *) pathname); pathname = NULL; } if (entry != NULL) { free((void *) entry); } if (dirp != NULL) { (void) closedir(dirp); } if (pathname != NULL) { free((void *) pathname); } if (fd != 0) { (void) close(fd); } if (dbuf != NULL) { free((void *) dbuf); } return tz; } #if defined(__linux__) || defined(MACOSX) /* * Performs Linux specific mapping and returns a zone ID * if found. Otherwise, NULL is returned. */ static char * getPlatformTimeZoneID() { struct stat statbuf; char *tz = NULL; FILE *fp; int fd; char *buf; size_t size; #ifdef __linux__ /* * Try reading the /etc/timezone file for Debian distros. There's * no spec of the file format available. This parsing assumes that * there's one line of an Olson tzid followed by a '\n', no * leading or trailing spaces, no comments. */ if ((fp = fopen(ETC_TIMEZONE_FILE, "r")) != NULL) { char line[256]; if (fgets(line, sizeof(line), fp) != NULL) { char *p = strchr(line, '\n'); if (p != NULL) { *p = '\0'; } if (strlen(line) > 0) { tz = strdup(line); } } (void) fclose(fp); if (tz != NULL) { return tz; } } #endif /* __linux__ */ /* * Next, try /etc/localtime to find the zone ID. */ if (lstat(DEFAULT_ZONEINFO_FILE, &statbuf) == -1) { return NULL; } /* * If it's a symlink, get the link name and its zone ID part. (The * older versions of timeconfig created a symlink as described in * the Red Hat man page. It was changed in 1999 to create a copy * of a zoneinfo file. It's no longer possible to get the zone ID * from /etc/localtime.) */ if (S_ISLNK(statbuf.st_mode)) { char linkbuf[PATH_MAX+1]; int len; if ((len = readlink(DEFAULT_ZONEINFO_FILE, linkbuf, sizeof(linkbuf)-1)) == -1) { jio_fprintf(stderr, (const char *) "can't get a symlink of %s\n", DEFAULT_ZONEINFO_FILE); return NULL; } linkbuf[len] = '\0'; tz = getZoneName(linkbuf); if (tz != NULL) { tz = strdup(tz); return tz; } } /* * If it's a regular file, we need to find out the same zoneinfo file * that has been copied as /etc/localtime. * If initial symbolic link resolution failed, we should treat target * file as a regular file. */ if ((fd = open(DEFAULT_ZONEINFO_FILE, O_RDONLY)) == -1) { return NULL; } if (fstat(fd, &statbuf) == -1) { (void) close(fd); return NULL; } size = (size_t) statbuf.st_size; buf = (char *) malloc(size); if (buf == NULL) { (void) close(fd); return NULL; } if (read(fd, buf, size) != (ssize_t) size) { (void) close(fd); free((void *) buf); return NULL; } (void) close(fd); tz = findZoneinfoFile(buf, size, ZONEINFO_DIR); free((void *) buf); return tz; } #else #ifdef __solaris__ #if !defined(__sparcv9) && !defined(amd64) /* * Those file* functions mimic the UNIX stream io functions. This is * because of the limitation of the number of open files on Solaris * (32-bit mode only) due to the System V ABI. */ #define BUFFER_SIZE 4096 static struct iobuffer { int magic; /* -1 to distinguish from the real FILE */ int fd; /* file descriptor */ char *buffer; /* pointer to buffer */ char *ptr; /* current read pointer */ char *endptr; /* end pointer */ }; static int fileclose(FILE *stream) { struct iobuffer *iop = (struct iobuffer *) stream; if (iop->magic != -1) { return fclose(stream); } if (iop == NULL) { return 0; } close(iop->fd); free((void *)iop->buffer); free((void *)iop); return 0; } static FILE * fileopen(const char *fname, const char *fmode) { FILE *fp; int fd; struct iobuffer *iop; if ((fp = fopen(fname, fmode)) != NULL) { return fp; } /* * It assumes read open. */ if ((fd = open(fname, O_RDONLY)) == -1) { return NULL; } /* * Allocate struct iobuffer and its buffer */ iop = malloc(sizeof(struct iobuffer)); if (iop == NULL) { (void) close(fd); errno = ENOMEM; return NULL; } iop->magic = -1; iop->fd = fd; iop->buffer = malloc(BUFFER_SIZE); if (iop->buffer == NULL) { (void) close(fd); free((void *) iop); errno = ENOMEM; return NULL; } iop->ptr = iop->buffer; iop->endptr = iop->buffer; return (FILE *)iop; }
/* * Scans the specified directory and its subdirectories to find a * zoneinfo file which has the same content as /etc/localtime given in * 'buf'. Returns a zone ID if found, otherwise, NULL is returned. */ static char * findZoneinfoFile(char *buf, size_t size, const char *dir) { DIR *dirp = NULL; struct stat statbuf; struct dirent *dp; char *pathname = NULL; int fd = -1; char *dbuf = NULL; char *tz = NULL; dirp = opendir(dir); if (dirp == NULL) { return NULL; } while ((dp = readdir(dirp)) != NULL) { /* * Skip '.' and '..' (and possibly other .* files) */ if (dp->d_name[0] == '.') { continue; } pathname = getPathName(dir, dp->d_name); if (pathname == NULL) { break; } if (stat(pathname, &statbuf) == -1) { break; } if (S_ISDIR(statbuf.st_mode)) { tz = findZoneinfoFile(buf, size, pathname); if (tz != NULL) { break; } } else if (S_ISREG(statbuf.st_mode) && (size_t)statbuf.st_size == size) { dbuf = (char *) malloc(size); if (dbuf == NULL) { break; } if ((fd = open(pathname, O_RDONLY)) == -1) { fd = 0; break; } if (read(fd, dbuf, size) != (ssize_t) size) { break; } if (memcmp(buf, dbuf, size) == 0) { tz = getZoneName(pathname); if (tz != NULL) { tz = strdup(tz); } break; } free((void *) dbuf); dbuf = NULL; (void) close(fd); fd = 0; } free((void *) pathname); pathname = NULL; } if (dirp != NULL) { (void) closedir(dirp); } if (pathname != NULL) { free((void *) pathname); } if (fd != 0) { (void) close(fd); } if (dbuf != NULL) { free((void *) dbuf); } return tz; }
/* * Performs libc implementation specific mapping and returns a zone ID * if found. Otherwise, NULL is returned. */ static char * getPlatformTimeZoneID() { struct stat statbuf; char *tz = NULL; FILE *fp; int fd; char *buf; size_t size; /* * First, try the ZONE entry in /etc/sysconfig/clock. However, the * ZONE entry is not set up after initial Red Hat Linux * installation. In case that /etc/localtime is set up without * using timeconfig, there might be inconsistency between * /etc/localtime and the ZONE entry. The inconsistency between * timeconfig and linuxconf is reported as a bug in the Red Hat * web page as of May 1, 2000. */ if ((fp = fopen(sysconfig_clock_file, "r")) != NULL) { char line[256]; while (fgets(line, sizeof(line), fp) != NULL) { char *p = line; char *s; SKIP_SPACE(p); if (*p != 'Z') { continue; } if (strncmp(p, "ZONE=\"", 6) == 0) { p += 6; } else { /* * In case we need to parse it token by token. */ if (strncmp(p, "ZONE", 4) != 0) { continue; } p += 4; SKIP_SPACE(p); if (*p++ != '=') { break; } SKIP_SPACE(p); if (*p++ != '"') { break; } } for (s = p; *s && *s != '"'; s++) ; if (*s != '"') { /* this ZONE entry is broken. */ break; } *s = '\0'; tz = strdup(p); break; } (void) fclose(fp); if (tz != NULL) { return tz; } } /* * Next, try /etc/localtime to find the zone ID. */ if (lstat(defailt_zoneinfo_file, &statbuf) == -1) { return NULL; } /* * If it's a symlink, get the link name and its zone ID part. (The * older versions of timeconfig created a symlink as described in * the Red Hat man page. It was changed in 1999 to create a copy * of a zoneinfo file. It's no longer possible to get the zone ID * from /etc/localtime.) */ if (S_ISLNK(statbuf.st_mode)) { char linkbuf[PATH_MAX+1]; int len; if ((len = readlink(defailt_zoneinfo_file, linkbuf, sizeof(linkbuf)-1)) == -1) { jio_fprintf(stderr, (const char *) "can't get a symlink of %s\n", defailt_zoneinfo_file); return NULL; } linkbuf[len] = '\0'; tz = getZoneName(linkbuf); if (tz != NULL) { tz = strdup(tz); } return tz; } /* * If it's a regular file, we need to find out the same zoneinfo file * that has been copied as /etc/localtime. */ size = (size_t) statbuf.st_size; buf = (char *) malloc(size); if (buf == NULL) { return NULL; } if ((fd = open(defailt_zoneinfo_file, O_RDONLY)) == -1) { free((void *) buf); return NULL; } if (read(fd, buf, size) != (ssize_t) size) { (void) close(fd); free((void *) buf); return NULL; } (void) close(fd); tz = findZoneinfoFile(buf, size, zoneinfo_dir); free((void *) buf); return tz; }
/* Helper function for getTimeZoneID() that provides an unprocessed time zone id obtained using system-dependent means of identifying the system's time zone. */ static QString getSystemTimeZoneID(void) { QString zone_id("UNDEF"); #ifdef _WIN32 // typedef struct _TIME_ZONE_INFORMATION { ... // GetTimeZoneInformation(); // ... // Sadly, Windows zone names are different to the (probably Unix) // backend's names - "AUS Eastern Standard Time" vs "Australia/Sydney". // Translation is not worthwhile. Leave it as UNDEF to check the offset. #else // Try to determine the time zone information by inspecting the system // configuration QString time_zone_file_path("/etc/timezone"); QString clock_file_path("/etc/sysconfig/clock"); QString zoneinfo_file_path("/etc/localtime"); QString zoneinfo_dir_path("/usr/share/zoneinfo"); // First, check time_zone_file_path (used by Debian-based systems) if (read_time_zone_id(time_zone_file_path, zone_id)) return zone_id; // Next, look for the ZONE entry in clock_file_path (used by Red Hat-based // systems) if (read_time_zone_id(clock_file_path, zone_id)) return zone_id; // Next check zoneinfo_file_path QFile zoneinfo_file(zoneinfo_file_path); QFileInfo info(zoneinfo_file); if (info.exists() && info.isFile()) { QString tz; if (info.isSymLink()) { // The symlink refers to a file whose name contains the zone ID tz = info.symLinkTarget(); } else { // The zoneinfo_file is a copy of the file in the // zoneinfo_dir_path, so search for the same file in // zoneinfo_dir_path tz = findZoneinfoFile(zoneinfo_file_path, zoneinfo_dir_path); } if (tz != "UNDEF") { int pos = 0; // Get the zone ID from the filename // Look for the basename of zoneinfo_dir_path in case it's a // relative link QString zoneinfo_dirname = zoneinfo_dir_path.section('/', -1); if ((pos = tz.indexOf(zoneinfo_dirname)) != -1) { zone_id = tz.right(tz.size() - (pos + 1) - zoneinfo_dirname.size()); } } else { // If we still haven't found a time zone, try localtime_r() to at // least get the zone name/abbreviation (as opposed to the // identifier for the set of rules governing the zone) char name[64]; time_t t; struct tm *result = (struct tm *)malloc(sizeof(*result)); if (result != NULL) { t = time(NULL); localtime_r(&t, result); if (result != NULL) { if (strftime(name, sizeof(name), "%Z", result) > 0) zone_id = name; free(result); } } } } #endif return zone_id; }