#!/usr/bin/env python

"""
This doesn't aim to be a full port of ntpdate to Python.  Instead, it
tries to be a small SNTP utility for setting system time without
having to install the whole NTP package, which is a monstrosity.
Obviously, it must be run with root privileges if you want to set the
time.

Changelog:

0.2a (2006/08/26)
    Jakub Matys, the third registered user of ntpdate.py, corrected
    my omission of ignoring seconds when setting the time.
0.1 (2006/03/22)
    Aggelos Orfanakos, probably the only other user of ntpdate.py,
    added a timeout of 30 seconds for the query socket.
0.1b (2006/01/09)
    Replaced time.gmtime() with time.localtime() that respects local
    time zones after a suggestion by Aggelos Orfanakos.
0.1a (2005/11/03)
    Initial version.
"""

__author__ = "Patroklos Argyroudis"
__version__ = "0.2a"
__rcsid__ = "$Id: ntpdate.py,v 1.13 2006/08/26 17:44:30 argp Exp $"

import os
import sys
import time
import getopt
import socket
import struct

# 48 bytes SNTP message, first byte is 1 to indicate the protocol
# version, and the other 47 bytes are 0s, see RFC 2030
SNTP_MSG = "\010" + "\0" * 47
SNTP_PORT = 123

# NTP time is the number of seconds since 00:00 01/01/1900, Unix
# time is the number of seconds since 00:00 01/01/1970, to convert
# the first to the second subtract NTP_UNIX_TIME
NTP_UNIX_TIME = 2208988800

true = 1
false = 0

def usage(name):
 print "%s [--help (-h)] [--set-time (-s)] <(S)NTP server>" % (name)

def main(argv):
 set_flag = false
 argc = len(argv)

 if argc == 1:
  usage(argv[0])
  sys.exit(1)
 
 try: 
  alist, args = getopt.getopt(argv[1:], "hs", ["help", "set-time"])
 except getopt.GetoptError:
  usage(argv[0]);
  sys.exit(1)
 
 for(field, val) in alist:
  if field in ("-h", "--help"):
   usage(argv[0])
   sys.exit(0)
  if field in ("-s", "--set-time"):
   set_flag = true

 sd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 sd.settimeout(30.0)
 sd.sendto(SNTP_MSG, (argv[argc - 1], SNTP_PORT))
 (data, address) = sd.recvfrom(64)

 if len(data) > 0:
  # 12 unsigned integers in big endian order, the 10th is the time in
  # seconds _not_ taking into consideration network delay
  seconds = struct.unpack(">12I", data)[10]
  seconds -= NTP_UNIX_TIME

  if set_flag == true:
   os.system('date ' + time.strftime('%m%d%H%M.%S',
      time.localtime(seconds)))
  else:
   print time.ctime(seconds)

if __name__ == "__main__":
 main(sys.argv)
 sys.exit(0)

# EOF
