=== squeezeplay_baby/share/applets/SqueezeboxBaby/SqueezeboxBabyApplet.lua ================================================================== --- squeezeplay_baby/share/applets/SqueezeboxBaby/SqueezeboxBabyApplet.lua (revision 7836) +++ squeezeplay_baby/share/applets/SqueezeboxBaby/SqueezeboxBabyApplet.lua (local) @@ -10,6 +10,7 @@ local string = require("jive.utils.string") local table = require("jive.utils.table") local math = require("math") +local squeezeos = require("squeezeos"); local Applet = require("jive.Applet") local Decode = require("squeezeplay.decode") @@ -513,9 +514,9 @@ log:warn(err) return end - log:debug('date sync: local: ', chunk.data.date, ' utc: ', chunk.data.date_utc) - if chunk.data.date_utc then - self:setDate(chunk.data.date_utc) + log:debug('date sync epoch: ', chunk.data.date_epoch) + if chunk.data.date_epoch then + self:setDate(chunk.data.date_epoch) end end @@ -540,18 +541,12 @@ end -function setDate(self, date) - -- matches date format 2007-09-08T20:40:42+00:00, expects UTC - local CCYY, MM, DD, hh, mm, ss, TZ = string.match(date, "(%d%d%d%d)%-(%d%d)%-(%d%d)T(%d%d):(%d%d):(%d%d)([-+]%d%d:%d%d)") - - log:debug("CCYY=", CCYY, " MM=", MM, " DD=", DD, " hh=", hh, " mm=", mm, " ss=", ss, " TZ=", TZ) - - -- set system date - os.execute("/bin/date -u " .. MM..DD..hh..mm..CCYY.."."..ss) - - -- set RTC to system time - os.execute("hwclock -w -u") - +function setDate(self, epoch) + squeezeos.swclock_set_epoch(epoch); + local success,err = squeezeos.hwclock_sys2hc() + if not success + log:warn("hwclock_sys2hc() failed: %s", err) + end iconbar:update() end === squeezeplay_fab4/share/applets/SqueezeboxFab4/SqueezeboxFab4Applet.lua ================================================================== --- squeezeplay_fab4/share/applets/SqueezeboxFab4/SqueezeboxFab4Applet.lua (revision 7836) +++ squeezeplay_fab4/share/applets/SqueezeboxFab4/SqueezeboxFab4Applet.lua (local) @@ -9,6 +9,7 @@ local io = require("io") local string = require("string") local math = require("math") +local squeezeos = require("squeezeos") local Applet = require("jive.Applet") local Decode = require("squeezeplay.decode") @@ -264,9 +265,9 @@ log:warn(err) return end - log:debug('date sync: local: ', chunk.data.date, ' utc: ', chunk.data.date_utc) - if chunk.data.date_utc then - self:setDate(chunk.data.date_utc) + log:debug('date sync epoch: ', chunk.data.date_epoch) + if chunk.data.date_epoch then + self:setDate(chunk.data.date_epoch) end end @@ -291,15 +292,8 @@ end -function setDate(self, date) - -- matches date format 2007-09-08T20:40:42+00:00, expects UTC time - local CCYY, MM, DD, hh, mm, ss, TZ = string.match(date, "(%d%d%d%d)%-(%d%d)%-(%d%d)T(%d%d):(%d%d):(%d%d)([-+]%d%d:%d%d)") - - log:debug("CCYY=", CCYY, " MM=", MM, " DD=", DD, " hh=", hh, " mm=", mm, " ss=", ss, " TZ=", TZ) - - -- set system date - os.execute("/bin/date -u " .. MM..DD..hh..mm..CCYY.."."..ss) - +function setDate(self, epoch) + squeezeos.swclock_set_epoch(epoch); iconbar:update() end === squeezeplay_jive/share/applets/SqueezeboxJive/SqueezeboxJiveApplet.lua ================================================================== --- squeezeplay_jive/share/applets/SqueezeboxJive/SqueezeboxJiveApplet.lua (revision 7836) +++ squeezeplay_jive/share/applets/SqueezeboxJive/SqueezeboxJiveApplet.lua (local) @@ -7,6 +7,7 @@ local math = require("math") local os = require("os") local io = require("io") +local squeezeos = require("squeezeos") local jiveBSP = require("jiveBSP") local Networking = require("jive.net.Networking") @@ -98,7 +99,10 @@ function() local systemTime = os.date() log:info('syncing system clock to hw clock: ', systemTime) - os.execute("hwclock -s -u") + local success,err = squeezeos.hwclock_hc2sys() + if not success + log:warn("hwclock_hc2sys() failed: %s", err) + end systemTime = os.date() log:info('system clock now synced to hw clock: ', systemTime) end) @@ -255,25 +259,24 @@ self.player = player local sink = function(chunk, err) - if err then - log:warn(err) - return - end - log:debug('date sync: local: ', chunk.data.date, ' utc: ', chunk.data.date_utc) - if chunk.data.date_utc then - self:setDate(chunk.data.date_utc) - end - end + if err then + log:warn(err) + return + end + log:debug('date sync epoch: ', chunk.data.date_epoch) + if chunk.data.date_epoch then + self:setDate(chunk.data.date_epoch) + end + end -- setup a once/hour - player:subscribe( - '/slim/datestatus/' .. self.player:getId(), - sink, - self.player:getId(), - { 'date', 'subscribe:3600' } + player:subscribe( + '/slim/datestatus/' .. self.player:getId(), + sink, + self.player:getId(), + { 'date', 'subscribe:3600' } ) - end function notify_playerDelete(self, player) @@ -287,18 +290,12 @@ end -function setDate(self, date) - -- matches date format 2007-09-08T20:40:42+00:00, expects UTC - local CCYY, MM, DD, hh, mm, ss, TZ = string.match(date, "(%d%d%d%d)%-(%d%d)%-(%d%d)T(%d%d):(%d%d):(%d%d)([-+]%d%d:%d%d)") - - log:debug("CCYY=", CCYY, " MM=", MM, " DD=", DD, " hh=", hh, " mm=", mm, " ss=", ss, " TZ=", TZ) - - -- set system date - os.execute("/bin/date -u " .. MM..DD..hh..mm..CCYY.."."..ss) - - -- set RTC to system time - os.execute("hwclock -w -u") - +function setDate(self, epoch) + squeezeos.swclock_set_epoch(epoch) + local success,err = squeezeos.hwclock_sys2hc() + if not success + log:warn("hwclock_sys2hc() failed: %s", err) + end iconbar:update() end @@ -599,7 +596,10 @@ else -- the system clock drifts in sleep mode, reset it if self.powerState == "sleep" or self.powerState == "suspend" then - os.execute("hwclock -s -u") + local success,err = squeezeos.hwclock_hc2sys() + if not success + log:warn("hwclock_hc2sys() failed: %s", err) + end end self:setPowerState("active") === squeezeplay_squeezeos/share/applets/SetupTZ/SetupTZApplet.lua ================================================================== --- squeezeplay_squeezeos/share/applets/SetupTZ/SetupTZApplet.lua (revision 7836) +++ squeezeplay_squeezeos/share/applets/SetupTZ/SetupTZApplet.lua (local) @@ -1,12 +1,10 @@ -- stuff we use -local pairs, tonumber = pairs, tonumber +local pairs = pairs local oo = require("loop.simple") -local io = require("io") -local os = require("os") local string = require("string") -local lfs = require("lfs") +local squeezeos = require("squeezeos") local Applet = require("jive.Applet") local System = require("jive.System") @@ -104,43 +102,8 @@ { strid = "TZ_PACIFIC_TONGATAPU", olson = "Pacific/Tongatapu" }, } --- Returns the current timezone as a path string --- underneath zonedir, for example: "America/Chicago". --- Note that the return value may not be an available --- selection from the menu of region names, such as --- when it is "Factory". - -function getTimezone(self) - -- Lua and Busybox both have no readlink(), and - -- apparently lfs.symlinkattributes doesn't exist either, - -- so we just assume it's a link and use busybox /bin/ls to find - -- the target... - local cmd = io.popen("/bin/ls --color=never -dl /etc/localtime") - local line - for _line in cmd:lines() do line = _line end - cmd:close() - - if not line then return nil end - local _,_,tz = string.find(line, "/usr/share/zoneinfo/([^%s]+)%s*$") - - return tz -end - --- Sets the timezone for the machine -function setTimezone(self, tz) - local mode = lfs.attributes("/usr/share/zoneinfo/" .. tz).mode - if mode == "file" then - log:info("Setting timezone to: " .. tz) - os.execute("/bin/ln -fs /usr/share/zoneinfo/" .. tz .. " /etc/localtime") - return true - else - log:warn("Attempted to set non-existent timezone: " .. tz) - return false - end -end - function settingsShow(self, menuItem) - local current_tz = self:getTimezone() + local current_tz = squeezeos.get_timezone() local radio_group = RadioGroup() local menu_list = {} local tz_selected_index @@ -157,7 +120,12 @@ check = RadioButton( "radio", radio_group, - function() self:setTimezone(tzdata.olson) end, + function() + local success,err = squeezeos.set_timezone(tzdata.olson) + if not success + log:warn("set_timezone() failed: %s", err) + end + end enableme ) } @@ -165,7 +133,7 @@ self.window = Window("help_list", menuItem.text, "text") - self.menu = SimpleMenu("menu", menu_list); + self.menu = SimpleMenu("menu", menu_list) if tz_selected_index then self.menu:setSelectedIndex(tz_selected_index) end === squeezeplay_squeezeos/share/applets/SetupTZ/SetupTZMeta.lua ================================================================== --- squeezeplay_squeezeos/share/applets/SetupTZ/SetupTZMeta.lua (revision 7836) +++ squeezeplay_squeezeos/share/applets/SetupTZ/SetupTZMeta.lua (local) @@ -6,6 +6,7 @@ local appletManager = appletManager local jiveMain = jiveMain local jnt = jnt +local squeezeos = require("squeezeos") local RequestHttp = require("jive.net.RequestHttp") local SocketHttp = require("jive.net.SocketHttp") @@ -18,30 +19,28 @@ function registerApplet(meta) jiveMain:addItem(meta:menuItem('appletSetupTZ', 'advancedSettings', "TZ_TIMEZONE", function(applet, ...) applet:settingsShow(...) end)) - meta:registerService("setTimezone") - meta:registerService("getTimezone") - -- At register time, don't bother with all the subscription - -- magic unless the TZ is unset - local current_tz = appletManager:callService("getTimezone") - if not current_tz or current_tz == "Factory" then + -- At register time, subscribe to network events + -- if the timezone hasn't been set + if not squeezeos.get_timezone() then jnt:subscribe(meta) end end function notify_serverConnected(meta) - -- But the TZ could have been set by the user between then - -- and now, so recheck again anyways, and unsubcribe if - -- either it has become validly set somehow, or we manage - -- to get a setting from SN - local current_tz = appletManager:callService("getTimezone") - if not current_tz or current_tz == "Factory" then + -- On server connect, if the timezone still hasn't + -- been set, set it from SN's guess over http + -- and unsubscribe if this succeeds + if not squeezeos.get_timezone() then local socket = SocketHttp(jnt, jnt:getSNHostname(), 80, "tzguess") local req = RequestHttp( function(data) if data then log:debug("Got http data for TZ >>" .. data .. "<<") - if appletManager:callService("setTimezone", data) then + local success,err = squeezeos.set_timezone(data) + if success jnt:unsubscribe(meta) + else + log:warn("set_timezone() failed: %s", err) end end end, 'GET', '/public/tz' === squeezeplay_squeezeos/src/squeezeos.c ================================================================== --- squeezeplay_squeezeos/src/squeezeos.c (revision 7836) +++ squeezeplay_squeezeos/src/squeezeos.c (local) @@ -7,9 +7,15 @@ #include "common.h" +#include +#include #include +#include +#include +#include +#include +#include - static int squeezeos_reboot(lua_State *L) { sync(); @@ -25,14 +31,195 @@ return 0; } +/* swclock_set_epoch - Sets the kernel's software + * wallclock from the first argument, which is + * standard integer *nix time (seconds since epoch in UTC) + */ +static int squeezeos_swclock_set_epoch(lua_State *L) +{ + const unsigned int epoch_secs = lua_tointeger(L, 1); + const struct timeval tv = { epoch_secs, 0 }; + settimeofday(&tv, NULL); + return 0; +} +/* private to the hwclock_ funcs below */ +static int _open_rtc(const int flags) +{ + int rtc_fd = open("/dev/rtc", flags); + if(rtc_fd < 0) + rtc_fd = open("/dev/rtc0", flags); + if(rtc_fd < 0) + rtc_fd = open("/dev/misc/rtc", flags); + + return rtc_fd; +} + +/* hwclock_sys2hc - Sets the hardware RTC from + * the kernel's software wallclock time. + * No arguments + * returns true on success + * returns false, errmsg on failure + */ +static int squeezeos_hwclock_sys2hc(lua_State *L) +{ + int rtc_fd; + struct timeval tv; + struct tm tm; + + /* open the rtc device for writing */ + rtc_fd = _open_rtc(O_WRONLY); + if(rtc_fd < 0) { + lua_pushnil(L); + lua_pushstring("Failed to open RTC device for writing"); + return 2; + } + + /* Get system wallclock as a struct tm in UTC */ + gettimeofday(&tv, NULL); + gmtime_r(&(tv.sec), &tm); + + /* use ioctl on the rtc device to set the hwclock */ + if(ioctl(rtc_fd, RTC_SET_TIME, &tm)) { + lua_pushnil(L); + lua_pushstring("ioctl(RTC_SET_TIME) failed"); + } + else { + lua_pushboolean(L, 1); + lua_pushnil(L); + } + + close(rtc_fd); + return 2; +} + +/* hwclock_hc2sys - Sets the kernel's software + * wallclock time from the hardware RTC. + * No arguments + * returns true on success + * returns false, errmsg on failure + */ +static int squeezeos_hwclock_hc2sys(lua_State *L) +{ + int rtc_fd; + struct timeval tv; + struct tm tm; + + /* open rtc device for reading */ + rtc_fd = _open_rtc(O_RDONLY); + if(rtc_fd < 0) { + lua_pushnil(L); + lua_pushstring("Failed to open RTC device for reading"); + return 2; + } + + /* use ioctl on the rtc device to read the hwclock */ + if(ioctl(rtc_fd, RTC_RD_TIME, &tm)) { + lua_pushnil(L); + lua_pushstring("ioctl(RTC_GET_TIME) failed"); + close(rtc_fd); + return 2; + } + + tm.tm_isdst = 0; + tm.tm_gmtoff = 0; + + /* convert tm to epoch seconds and set time */ + tv.sec = timegm(&tm); + tv.usec = 0; + settimeofday(&tv, NULL); + + /* Success */ + close(rtc_fd); + lua_pushboolean(L, 1); + lua_pushnil(L); + return 2; +} + + +/* get_timezone - Returns the current Olson + * timezone, e.g. "America/Chicago" + * No input arguments. + * Return value can be nil in case of error or + * if the timezone has never validly been set before. + */ +static int squeezeos_get_timezone(lua_State *L) +{ + const char* path = lua_tostring(L, 1); + char* buf = malloc(PATH_MAX); + char* tzptr; + + ssize_t len = readlink(path, buf, PATH_MAX - 1); + if(len >= 0) { + buf[len] = '\0'; + tzptr = strstr(buf, "zoneinfo/"); + if(tzptr) { + tzptr += 9; + if(strcmp(tzptr, "Factory")) { + free(buf); + lua_pushstring(L, tzptr); + return 1; + } + } + } + + free(buf); + lua_pushnil(L); + return 1; +} + + +/* set_timezone - Sets the current timezone + * The only argument is the Olson-format timezone + * string, e.g. "America/Chicago". + * Returns true on success + * Returns false, errmsg on failure + */ +static int squeezeos_set_timezone(lua_State *L) +{ + char* tz; + char* tzfn; + + if(unlink("/etc/localtime") && errno != ENOENT) { + lua_pushnil(L); + lua_pushstring("Cannot unlink(/etc/localtime)"); + return 2; + } + + tz = lua_tostring(L, 1); + tzfn = malloc(21 + strlen(tz)); + if(!tzfn) { + lua_pushnil(L); + lua_pushstring("malloc() failed"); + return 2; + } + strcpy(tzfn, "/usr/share/zoneinfo/"); + strcat(tzfn, tz); + + if(symlink(tzfn, "/etc/localtime")) { + free(tzfn); + lua_pushnil(L); + lua_pushstring("symlink() to /etc/localtime failed"); + return 2; + } + + free(tzfn); + lua_pushboolean(L, 1); + return 1; +} + + static const struct luaL_Reg squeezeos_bsp_lib[] = { { "reboot", squeezeos_reboot }, { "poweroff", squeezeos_poweroff }, + { "swclock_set_epoch", squeezeos_swclock_set_epoch }, + { "hwclock_sys2hc", squeezeos_hwclock_sys2hc }, + { "hwclock_hc2sys", squeezeos_hwclock_hc2sys }, + { "get_timezone", squeezeos_get_timezone }, + { "set_timezone", squeezeos_set_timezone }, { NULL, NULL } }; - int luaopen_squeezeos_bsp(lua_State *L) { luaL_register(L, "squeezeos.bsp", squeezeos_bsp_lib); return 1;