Commit fb2550d6 authored by Vincent Untz's avatar Vincent Untz Committed by Vincent Untz
Browse files

Completely rewrite the way we detect the system timezone. It will work on

2008-04-08  Vincent Untz  <vuntz@gnome.org>

	Completely rewrite the way we detect the system timezone. It will work
	on more distributions this way. Also the code is better and it helps
	improve the world.
	Good thing is that now the clock will always immediately update after
	the timezone is updated.

	* Makefile.am: add new files
	* system-timezone.[ch]: new file, implement a singleton that knows how
	to read the system timezone and that monitors it
	* clock-location.[ch]: (clock_location_new): don't get the TZ
	environment variable here
	(files_are_identical):
	(recursive_guess_zone):
	(guess_zone_from_tree):
	(parse_etc_sysconfig_clock):
	(monitor_etc_sysconfig_clock):
	(zone_from_etc_sysconfig_clock):
	(clock_location_guess_zone): killed
	(clock_location_init): updated to create the SystemTimezone object
	(clock_location_finalize): updated
	(clock_location_unset_tz): use system_timezone_get_env() instead of a
	local variable
	(clock_location_is_current_timezone): use system_timezone_get()
	(clock_location_get_offset): use clock_location_unset_tz() instead of
	duplicating code
	(make_current_cb): updated to remove the current timezone handling
	* clock-zoneinfo.h: move SYSTEM_ZONEINFODIR to system-timezone.h
	* clock-zonetable.c: add new include
	* clock.c: add a SystemTimezone object
	(destroy_clock): unref it
	(clock_timezone_changed): new, handle system timezone change
	(fill_clock_applet): create the SystemTimezone object and connect to
	its changed signal

	* clock-marshallers.list: remove marshallers that already exist in glib
	* clock-location.[ch]: (clock_location_class_init): updated for this

svn path=/trunk/; revision=11016
parent ec880045
2008-04-08 Vincent Untz <vuntz@gnome.org>
Completely rewrite the way we detect the system timezone. It will work
on more distributions this way. Also the code is better and it helps
improve the world.
Good thing is that now the clock will always immediately update after
the timezone is updated.
* Makefile.am: add new files
* system-timezone.[ch]: new file, implement a singleton that knows how
to read the system timezone and that monitors it
* clock-location.[ch]: (clock_location_new): don't get the TZ
environment variable here
(files_are_identical):
(recursive_guess_zone):
(guess_zone_from_tree):
(parse_etc_sysconfig_clock):
(monitor_etc_sysconfig_clock):
(zone_from_etc_sysconfig_clock):
(clock_location_guess_zone): killed
(clock_location_init): updated to create the SystemTimezone object
(clock_location_finalize): updated
(clock_location_unset_tz): use system_timezone_get_env() instead of a
local variable
(clock_location_is_current_timezone): use system_timezone_get()
(clock_location_get_offset): use clock_location_unset_tz() instead of
duplicating code
(make_current_cb): updated to remove the current timezone handling
* clock-zoneinfo.h: move SYSTEM_ZONEINFODIR to system-timezone.h
* clock-zonetable.c: add new include
* clock.c: add a SystemTimezone object
(destroy_clock): unref it
(clock_timezone_changed): new, handle system timezone change
(fill_clock_applet): create the SystemTimezone object and connect to
its changed signal
* clock-marshallers.list: remove marshallers that already exist in glib
* clock-location.[ch]: (clock_location_class_init): updated for this
2008-04-08 Vincent Untz <vuntz@gnome.org>
* clock-zoneinfo.[ch]: add some const to function declarations
......
......@@ -65,6 +65,8 @@ CLOCK_SOURCES = \
clock-zoneinfo.h \
clock-zonetable.c \
clock-zonetable.h \
system-timezone.c \
system-timzone.h \
$(BUILT_SOURCES) \
$(CALENDAR_SOURCES) \
$(SETTIME_SOURCES)
......
......@@ -26,13 +26,15 @@
#include "clock-location.h"
#include "clock-marshallers.h"
#include "set-timezone.h"
#include "system-timezone.h"
G_DEFINE_TYPE (ClockLocation, clock_location, G_TYPE_OBJECT)
typedef struct {
gchar *name;
gchar *sys_timezone;
SystemTimezone *systz;
gchar *timezone;
gchar *tzname;
......@@ -79,11 +81,6 @@ clock_location_new (const gchar *name, const gchar *timezone,
priv->name = g_strdup (name);
priv->timezone = g_strdup (timezone);
priv->sys_timezone = getenv ("TZ");
if (priv->sys_timezone) {
priv->sys_timezone = g_strdup (priv->sys_timezone);
}
/* initialize priv->tzname */
clock_location_set_tz (this);
clock_location_unset_tz (this);
......@@ -103,244 +100,7 @@ clock_location_new (const gchar *name, const gchar *timezone,
return this;
}
static gboolean
files_are_identical (const char *localtime, struct stat *localtime_s,
const char *localtime_data, const gsize localtime_len,
char *file, struct stat *file_s)
{
gsize file_len = -1;
gchar *file_data = NULL;
if (localtime_s->st_size != file_s->st_size) {
return FALSE;
}
if (!g_file_get_contents (file, &file_data, &file_len, NULL)) {
return FALSE;
}
if (localtime_len != file_len) {
g_free (file_data);
return FALSE;
}
if (memcmp (localtime_data, file_data, localtime_len) == 0) {
g_free (file_data);
return TRUE;
}
g_free (file_data);
return FALSE;
}
static gchar *
recursive_guess_zone (const char *localtime, struct stat *localtime_s,
const char *localtime_data, const gsize localtime_len,
char *file, struct stat *file_s, ClockZoneTable *zones)
{
if (S_ISREG (file_s->st_mode)) {
gchar *zone = file + strlen (SYSTEM_ZONEINFODIR) + 1;
/* ignore files that aren't in the Olson database */
if (!clock_zonetable_get_zone (zones, zone)) {
return NULL;
}
if (files_are_identical (localtime, localtime_s,
localtime_data, localtime_len,
file, file_s)) {
return g_strdup (file + strlen (SYSTEM_ZONEINFODIR) + 1);
} else {
return NULL;
}
} else if (S_ISDIR (file_s->st_mode)) {
GDir *dir = NULL;
gchar *ret = NULL;
const gchar *subfile = NULL;
gchar *subpath = NULL;
struct stat subpath_s;
dir = g_dir_open (file, 0, NULL);
if (dir == NULL) {
return NULL;
}
while ((subfile = g_dir_read_name (dir)) != NULL) {
subpath = g_build_filename (file, subfile, NULL);
if (stat (subpath, &subpath_s) == -1) {
continue;
}
ret = recursive_guess_zone (localtime, localtime_s,
localtime_data, localtime_len,
subpath, &subpath_s,
zones);
g_free (subpath);
if (ret != NULL) {
break;
}
}
g_dir_close (dir);
return ret;
}
return NULL;
}
static gchar *
guess_zone_from_tree (const gchar *localtime, ClockZoneTable *zones)
{
int i;
struct stat s;
struct stat dir_s;
char *ret = NULL;
char *localtime_data = NULL;
gsize localtime_len = -1;
/* walk the zoneinfo tree and compare with
/etc/localtime to try to find the current zone */
i = stat (localtime, &s);
if (i == -1 || !S_ISREG (s.st_mode)) {
return NULL;
}
i = stat (SYSTEM_ZONEINFODIR, &dir_s);
if (i == -1 || !S_ISDIR (dir_s.st_mode)) {
return NULL;
}
if (!g_file_get_contents (localtime, &localtime_data,
&localtime_len, NULL)) {
return NULL;
}
ret = recursive_guess_zone (localtime, &s,
localtime_data, localtime_len,
SYSTEM_ZONEINFODIR, &dir_s, zones);
g_free (localtime_data);
return ret;
}
static gchar *current_zone = NULL;
static ClockLocation *current_location = NULL;
static GFileMonitor *monitor = NULL;
static void
parse_etc_sysconfig_clock (void)
{
gchar *data;
gsize len;
gchar **lines;
gchar *res;
gint i;
gchar *p, *q;
lines = NULL;
res = NULL;
if (g_file_test ("/etc/sysconfig/clock",
G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
if (!g_file_get_contents ("/etc/sysconfig/clock",
&data, &len, NULL))
goto out;
lines = g_strsplit (data, "\n", 0);
g_free (data);
for (i = 0; lines[i] && !res; i++) {
/* If you are Fedora, uncomment these and comment out the other version
if (g_str_has_prefix (lines[i], "ZONE=")) {
p = lines[i] + strlen ("ZONE=");
*/
if (g_str_has_prefix (lines[i], "TIMEZONE=")) {
p = lines[i] + strlen ("TIMEZONE=");
if (p[0] != '\"')
goto out;
p++;
q = strchr (p, '\"');
q[0] = '\0';
res = g_strdup (p);
}
}
}
out:
if (lines)
g_strfreev (lines);
g_free (current_zone);
current_zone = res;
}
static void
monitor_etc_sysconfig_clock (GFileMonitor *handle,
GFile *file,
GFile *other_file,
GFileMonitorEvent event,
gpointer user_data)
{
parse_etc_sysconfig_clock ();
}
static const gchar *
zone_from_etc_sysconfig_clock (void)
{
if (monitor == NULL) {
GFile *file;
parse_etc_sysconfig_clock ();
file = g_file_new_for_path ("/etc/sysconfig/clock");
monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE,
NULL, NULL);
g_object_unref (file);
if (monitor)
g_signal_connect (G_OBJECT (monitor), "changed",
G_CALLBACK (monitor_etc_sysconfig_clock),
NULL);
}
return current_zone;
}
static gchar *
clock_location_guess_zone (ClockZoneTable *zones)
{
const char *localtime = "/etc/localtime";
gchar *linkfile = NULL;
GError *err = NULL;
const gchar *zone;
/* look for /etc/sysconfig/clock */
if ((zone = zone_from_etc_sysconfig_clock ())) {
return g_strdup (zone);
}
/* guess the current time zone by readlink() on /etc/localtime */
linkfile = g_file_read_link (localtime, &err);
if (err) {
return guess_zone_from_tree (localtime, zones);
}
if (strncmp (linkfile, SYSTEM_ZONEINFODIR,
strlen (SYSTEM_ZONEINFODIR)) == 0) {
return g_strdup (linkfile + strlen (SYSTEM_ZONEINFODIR) + 1);
}
return NULL;
}
static void
clock_location_class_init (ClockLocationClass *this_class)
......@@ -355,7 +115,7 @@ clock_location_class_init (ClockLocationClass *this_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (ClockLocationClass, weather_updated),
NULL, NULL,
_clock_marshal_VOID__POINTER,
g_cclosure_marshal_VOID__POINTER,
G_TYPE_NONE, 1, G_TYPE_POINTER);
location_signals[SET_CURRENT] =
......@@ -364,7 +124,7 @@ clock_location_class_init (ClockLocationClass *this_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (ClockLocationClass, set_current),
NULL, NULL,
_clock_marshal_VOID__VOID,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
g_type_class_add_private (this_class, sizeof (ClockLocationPrivate));
......@@ -377,7 +137,8 @@ clock_location_init (ClockLocation *this)
priv->name = NULL;
priv->sys_timezone = NULL;
priv->systz = system_timezone_new ();
priv->timezone = NULL;
priv->tzname = NULL;
......@@ -401,16 +162,16 @@ clock_location_finalize (GObject *g_obj)
priv->name = NULL;
}
if (priv->systz) {
g_object_unref (priv->systz);
priv->systz = NULL;
}
if (priv->timezone) {
g_free (priv->timezone);
priv->timezone = NULL;
}
if (priv->sys_timezone) {
g_free (priv->sys_timezone);
priv->sys_timezone = NULL;
}
if (priv->tzname) {
g_free (priv->tzname);
priv->tzname = NULL;
......@@ -431,12 +192,6 @@ clock_location_finalize (GObject *g_obj)
priv->weather_timeout = 0;
}
if (monitor) {
g_file_monitor_cancel (monitor);
g_object_unref (monitor);
monitor = NULL;
}
G_OBJECT_CLASS (clock_location_parent_class)->finalize (g_obj);
}
......@@ -560,13 +315,16 @@ static void
clock_location_unset_tz (ClockLocation *this)
{
ClockLocationPrivate *priv = PRIVATE (this);
const char *env_timezone;
if (priv->timezone == NULL) {
return;
}
if (priv->sys_timezone) {
setenv ("TZ", priv->sys_timezone, 1);
env_timezone = system_timezone_get_env (priv->systz);
if (env_timezone) {
setenv ("TZ", env_timezone, 1);
} else {
unsetenv ("TZ");
}
......@@ -592,7 +350,9 @@ clock_location_is_current_timezone (ClockLocation *loc)
ClockLocationPrivate *priv = PRIVATE (loc);
const char *zone;
if ((zone = zone_from_etc_sysconfig_clock ()))
zone = system_timezone_get (priv->systz);
if (zone)
return strcmp (zone, priv->timezone) == 0;
else
return clock_location_get_offset (loc) == 0;
......@@ -652,12 +412,7 @@ clock_location_get_offset (ClockLocation *loc)
offset = local_timezone - sys_timezone;
if (priv->sys_timezone) {
setenv ("TZ", priv->sys_timezone, 1);
} else {
unsetenv ("TZ");
}
tzset();
clock_location_unset_tz (loc);
return offset;
}
......@@ -673,16 +428,8 @@ static void
make_current_cb (gpointer data, GError *error)
{
MakeCurrentData *mcdata = data;
ClockLocationPrivate *priv = PRIVATE (mcdata->location);
if (error == NULL) {
/* FIXME this ugly shortcut is necessary until we move the
* current timezone tracking to clock.c and emit the
* signal from there
*/
g_free (current_zone);
current_zone = g_strdup (priv->timezone);
if (current_location)
g_object_remove_weak_pointer (G_OBJECT (current_location),
(gpointer *)&current_location);
......
VOID:VOID
VOID:OBJECT
VOID:POINTER
POINTER:VOID
VOID:OBJECT,STRING
INT:VOID
......@@ -6,12 +6,6 @@
G_BEGIN_DECLS
#ifdef HAVE_SOLARIS
#define SYSTEM_ZONEINFODIR "/usr/share/lib/zoneinfo/tab"
#else
#define SYSTEM_ZONEINFODIR "/usr/share/zoneinfo"
#endif
#define CLOCK_ZONEINFO_TYPE (clock_zoneinfo_get_type ())
#define CLOCK_ZONEINFO(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CLOCK_ZONEINFO_TYPE, ClockZoneInfo))
#define CLOCK_ZONEINFO_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), CLOCK_ZONEINFO_TYPE, ClockZoneInfoClass))
......
......@@ -11,6 +11,7 @@
#include "clock-country.h"
#include "clock-zoneinfo.h"
#include "system-timezone.h"
#ifdef HAVE_SOLARIS
#define ZONETAB_FILE SYSTEM_ZONEINFODIR"/zone_sun.tab"
......@@ -95,7 +96,6 @@ clock_zonetable_constructor (GType type,
n_construct_properties,
construct_properties);
clock_zonetable_load_zonetab (CLOCK_ZONETABLE (obj));
clock_zonetable_load_iso3166 (CLOCK_ZONETABLE (obj));
/* FIXME: add some file monitoring here to reload the files? */
......
......@@ -62,6 +62,7 @@
#include "clock-zonetable.h"
#include "obox.h"
#include "set-timezone.h"
#include "system-timezone.h"
#define INTERNETSECOND (864)
#define INTERNETBEAT (86400)
......@@ -191,6 +192,7 @@ struct _ClockData {
int size;
GtkAllocation old_allocation;
SystemTimezone *systz;
ClockZoneTable *zones;
int fixed_width;
......@@ -758,6 +760,11 @@ destroy_clock (GtkWidget * widget, ClockData *cd)
g_list_free (cd->location_tiles);
cd->location_tiles = NULL;
if (cd->systz) {
g_object_unref (cd->systz);
cd->systz = NULL;
}
if (cd->zones) {
g_object_unref (cd->zones);
cd->zones = NULL;
......@@ -2330,6 +2337,17 @@ clock_migrate_to_26 (ClockData *clock)
NULL);
}
static void
clock_timezone_changed (SystemTimezone *systz,
const char *new_tz,
ClockData *cd)
{
/* This will refresh the current location */
save_cities_store (cd);
refresh_clock_timeout (cd);
}
static void
parse_and_set_temperature_string (const char *str, ClockData *cd)
{
......@@ -2747,6 +2765,10 @@ fill_clock_applet (PanelApplet *applet)
}
}
cd->systz = system_timezone_new ();
g_signal_connect (cd->systz, "changed",
G_CALLBACK (clock_timezone_changed), cd);
cd->zones = clock_zonetable_new ();
bonobo_ui_component_set_prop (popup_component,
......
/* To compile a test program, do:
* gcc -DSYSTZ_TEST -Wall -o system-timezone `pkg-config --cflags --libs glib-2.0` system-timezone.c
*/
/* System timezone handling
*
* Copyright (C) 2008 Novell, Inc.
*
* Authors: Vincent Untz <vuntz@gnome.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* Some code is based on previous code in clock-location.c and on code from
* tz.c (shipped with version <= 2.22.0). Those files were under the same
* license, with those authors and copyrights:
*
* clock-location.c:
* ================
* No header, but most of the work was done (AFAIK) by
* Federico Mena Quintero <federico@novell.com>
* Matthias Clasen <mclasen@redhat.com>
*
* tz.c:
* ====
* Copyright (C) 2000-2001 Ximian, Inc.
* Copyright (C) 2004 Sun Microsystems, Inc.
*
* Authors: Hans Petter Jansson <hpj@ximian.com>
* additional functions by Erwann Chenede <erwann.chenede@sun.com>
* reworked by Vincent Untz <vuntz@gnome.org>
*
* Largely based on Michael Fulbright's work on Anaconda.
*/
#include <string.h>
#include <glib.h>
#include <glib/gstdio.h>
/* Files that we look at and that should be monitored */
#define CHECK_NB 5
#define ETC_TIMEZONE "/etc/timezone"
#define ETC_TIMEZONE_MAJ "/etc/TIMEZONE"
#define ETC_SYSCONFIG_CLOCK "/etc/sysconfig/clock"
#define ETC_CONF_D_CLOCK "/etc/conf.d/clock"
#define ETC_LOCALTIME "/etc/localtime"
#ifndef SYSTZ_TEST
#include <gio/gio.h>
#include "system-timezone.h"
static char *files_to_check[CHECK_NB] = {
ETC_TIMEZONE,
ETC_TIMEZONE_MAJ,
ETC_SYSCONFIG_CLOCK,
ETC_CONF_D_CLOCK,
ETC_LOCALTIME
};
G_DEFINE_TYPE (SystemTimezone, system_timezone, G_TYPE_OBJECT)
typedef struct {
char *tz;
char *env_tz;
GFileMonitor *monitors[CHECK_NB];
} SystemTimezonePrivate;
enum {
CHANGED,
LAST_SIGNAL
};
static guint system_timezone_signals[LAST_SIGNAL] = { 0 };
static GObject *system_timezone_constructor (GType type,