blob: d6839d9c23818922f89f540ffe094289bb2d15a2 [file] [log] [blame]
#!/usr/bin/python
# Copyright 2012 Google Inc. All Rights Reserved.
"""Antirollback clock user space support.
This daemon serves several purposes:
1. Maintain a file containing the minimum time, and periodically
update its value.
2. At startup, write the minimum time to /proc/ar_clock.
The kernel will not allow the time to be set substantially
earlier than this value (there is a small amount of wiggle
room).
"""
__author__ = 'dgentry@google.com (Denton Gentry)'
import os
import pwd
import sys
import tempfile
import time
import options
optspec = """
antirollback [options...]
--
i,interval= seconds between updates [28800]
p,persist= path to persistent file [/config/ar_clock]
u,user= setuid to this user to run
"""
# Unit tests can override these.
BIRTHDAY = 1349064000.0 # 10/1/2012
BUILD_FILENAME = '/etc/softwaredate'
PROC_AR = '/proc/ar_clock'
PROC_UPTIME = '/proc/uptime'
SLEEP = time.sleep
TIMENOW = time.time
def GetPersistTime(ar_filename):
"""Return time stored in ar_filename, or 0.0 if it does not exist."""
try:
with open(ar_filename) as f:
return float(f.read())
except (IOError, ValueError):
return 0.0
def GetBuildDate(build_filename):
"""Return build_date in floating point seconds since epoch."""
try:
with open(build_filename) as f:
return float(f.readline())
except (IOError, ValueError):
return 0.0
def GetMonotime():
"""Return a monotonically increasing count of seconds."""
return float(open(PROC_UPTIME).read().split()[0])
def GetAntirollbackTime(ar_filename):
"""Return the appropriate antirollback time to use at startup."""
now = max(TIMENOW(), GetPersistTime(ar_filename),
GetBuildDate(BUILD_FILENAME), BIRTHDAY)
return now
def StoreAntirollback(now, ar_filename, kern_f):
"""Write time to /proc/ar_clock and the persistent file."""
print 'antirollback time now ' + str(now)
sys.stdout.flush()
kern_f.write(str(now))
kern_f.flush()
tmpdir = os.path.dirname(ar_filename)
with tempfile.NamedTemporaryFile(mode='w', dir=tmpdir, delete=False) as f:
f.write(str(now) + '\n')
f.flush()
os.fsync(f.fileno())
os.rename(f.name, ar_filename)
def LoopIterate(uptime, now, sleeptime, ar_filename, kern_f):
SLEEP(sleeptime)
new_uptime = GetMonotime()
now += (new_uptime - uptime)
uptime = new_uptime
now = max(now, TIMENOW())
StoreAntirollback(now=now, ar_filename=ar_filename, kern_f=kern_f)
return (uptime, now)
def main():
o = options.Options(optspec)
(opt, _, _) = o.parse(sys.argv[1:])
kern_f = open(PROC_AR, 'w')
# Drop privileges
if opt.user:
pd = pwd.getpwnam(opt.user)
os.setuid(pd.pw_uid)
uptime = GetMonotime()
now = GetAntirollbackTime(opt.persist)
StoreAntirollback(now=now, ar_filename=opt.persist, kern_f=kern_f)
while True:
(uptime, now) = LoopIterate(uptime=uptime, now=now,
sleeptime=opt.interval,
ar_filename=opt.persist,
kern_f=kern_f)
if __name__ == '__main__':
main()