NET TIME /SET utility I've written - 8K source

Krehbiel, Richard rich at kastle.com
Mon Apr 10 19:05:26 GMT 2000


There are a few Linux application servers around our company that need to be
time-synchronized with the corporate network.  For company inter/intranet
policy reasons I can't use a superior NTP solution, so I've taken the Samba
smbclient source (client.c) and hatcheted it down into a smallish utility that
connects with an SMB server, accquires the date and time, and sets the local
system time to match.

So - I'd like to get some feedback.  Did I waste my time because a better
solution exists?  (Remember, I can't use NTP.)  Am I unaware of some
fundamental problem that makes the concept unworkable?  I'm not a Samba
insider, this is my first experience.

Source is presented below; place it in "source/clients/nettime.c".  I've only
tested this with Red Hat 6.x clients against a Windows NT 4.0 AS server, so
it's likely missing some platform details.  If this passes muster I plan to
daemon-ify it.

/* 
   Unix SMB/Netbios implementation.
   Version 1.9.
   SMB "NET TIME /SET" utility
   Copyright (C) Andrew Tridgell 1994-1998
   
   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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#define NO_SYSLOG

#include "includes.h"

#ifndef REGISTER
#define REGISTER 0
#endif

struct cli_state *cli;
extern BOOL in_client;
static int port = SMB_PORT;
static pstring service;
extern pstring global_myname;
static pstring username;
static pstring workgroup;
extern struct in_addr ipzero;
extern pstring scope;

static int name_type = 0x20;

extern pstring user_socket_options;

extern pstring debugf;
extern int DEBUGLEVEL;

BOOL translation = False;

static BOOL have_ip;

struct in_addr dest_ip;

/****************************************************************************
usage on the program
****************************************************************************/
static void usage(char *pname)
{
  DEBUG(0,("Usage: %s server [options]", pname));

  DEBUG(0,("\nVersion %s\n",VERSION));
  DEBUG(0,("\t-s smb.conf           pathname to smb.conf file\n"));
  DEBUG(0,("\t-O socket_options     socket options to use\n"));
  DEBUG(0,("\t-R name resolve order use these name resolution services
only\n"));
  DEBUG(0,("\t-i scope              use this NetBIOS scope\n"));
  DEBUG(0,("\t-n netbios name.      Use this name as my netbios name\n"));
  DEBUG(0,("\t-d debuglevel         set the debuglevel\n"));
  DEBUG(0,("\t-p port               connect to the specified port\n"));
  DEBUG(0,("\t-l log basename.      Basename for log/debug files\n"));
  DEBUG(0,("\t-h                    Print this help message.\n"));
  DEBUG(0,("\t-I dest IP            use this IP to connect to\n"));
  DEBUG(0,("\t-E                    write messages to stderr instead of
stdout\n"));
  DEBUG(0,("\t-m max protocol       set the max protocol level\n"));
  DEBUG(0,("\t-W workgroup          set the workgroup name\n"));
  DEBUG(0,("\n"));
}

/*
  normalize_tv places tv_usec in the range 0 ... 999999
  when it is in the range -999999 ... 1999999 i.e. after an addition
  or subtraction of normalized values.
*/

static void normalize_tv(struct timeval *tv)
{
	if(tv->tv_usec < 0)
		tv->tv_sec--, tv->tv_usec += 1000000;
	else if(tv->tv_usec >= 1000000)
		tv->tv_sec++, tv->tv_usec -= 1000000;
}

/* Perform tv1 -= tv2 */
void subtract_tv(struct timeval *tv1,
				 struct timeval *tv2)
{
	tv1->tv_sec -= tv2->tv_sec;
	tv1->tv_usec -= tv2->tv_usec;
	normalize_tv(tv1);
}

/***************************************************** 
get the server's time and set ourselves to match.
*******************************************************/
int do_nettime(char *a_server)
{
	struct cli_state *c;
	struct nmb_name called, calling;
	struct in_addr ip;
	extern struct in_addr ipzero;
	struct timeval start, end;
	int ok;
	char *server = strdup(a_server + 2); /* server name without
	
leading double \'es */

	/* Chop off share name from a_server */
	{
		char *sp = strchr(server, '\\');
		if(sp)
			*sp = 0;
	}

	ip = ipzero;

	make_nmb_name(&calling, global_myname, 0x0, "");
	make_nmb_name(&called , server, name_type, "");

 again:
	ip = ipzero;
	if (have_ip) ip = dest_ip;

	/* have to open a new connection */
	if (!(c=cli_initialise(NULL)) || (cli_set_port(c, port) == 0) ||
	    !cli_connect(c, server, &ip)) {
		DEBUG(0,("Connection to %s failed\n", server));
		if(c)
			cli_shutdown(c);
		free(server);
		return 0;				/* Failed to set time.
*/
	}

	if (!cli_session_request(c, &calling, &called)) {
		char *p;
		DEBUG(0,("session request to %s failed (%s)\n", 
			 called.name, cli_errstr(c)));
		cli_shutdown(c);
		if ((p=strchr(called.name, '.'))) {
			*p = 0;
			goto again;
		}
		if (strcmp(called.name, "*SMBSERVER")) {
			make_nmb_name(&called , "*SMBSERVER", 0x20, "");
			goto again;
		}
		free(server);
		return 0;				/* Failed to set time.
*/
	}

	gettimeofday(&start, NULL);	/* Get a precise start time */
	/* measure the time it takes to negotiate - during which time the
	   server returns it's time */

	if (!cli_negprot(c)) {
		DEBUG(0,("protocol negotiation failed\n"));
		cli_shutdown(c);
		free(server);
		return 0;
	}

	gettimeofday(&end, NULL);	/* Get a precise end time */

	ok = 0;						/* assume the time
can't be set */

	subtract_tv(&end, &start);	/* Compute duration in end */
	if(end.tv_sec == 0 &&
	   end.tv_usec <= 500000)	/* Under half a second? */
	{
		end.tv_usec = 0;
		end.tv_sec = c->servertime;
		if(settimeofday(&end, NULL))
			perror("settimeofday");
		else
		{
			time_t servertime = c->servertime;
			struct tm *tm = localtime(&servertime);
			/* Got it!  Set the system time. */
			printf("Current system time set to %s", asctime(tm));
			ok = 1;					/* Okay, system
time is set. */
		}
	}

	cli_shutdown(c);
	free(server);
	return ok;
}

/****************************************************************************
  main program
****************************************************************************/
int main(int argc,char *argv[])
{
	char *pname = argv[0];
	int opt;
	extern FILE *dbf;
	extern char *optarg;
	extern int optind;
	static pstring servicesf = CONFIGFILE;
	pstring new_name_resolve_order;

	*new_name_resolve_order = 0;

	DEBUGLEVEL = 2;

	setup_logging(pname,True);

	TimeInit();
	charset_initialise();

	in_client = True;   /* Make sure that we tell lp_load we are */

	if (!lp_load(servicesf,True,False,False)) {
		fprintf(stderr, "Can't load %s - run testparm to debug it\n",
servicesf);
	}
	
#ifdef WITH_SSL
	sslutil_init(0);
#endif

	pstrcpy(workgroup,lp_workgroup());

	load_interfaces();

	pstrcpy(username,"GUEST");

	if (argc < 2) {
		usage(pname);
		exit(1);
	}
  
	pstrcpy(service,argv[1]);  
	/* Convert any '/' characters in the service name to '\' characters */
	string_replace( service, '/','\\');
	argc--;
	argv++;
		
	if (service[0] != '\\' ||
		service[1] != '\\' ||
		count_chars(service,'\\') != 2)
	{
		usage(pname);
		printf("\n%s: improper server name %s\n", argv[0], service);
		exit(1);
	}

	while ((opt = 
		getopt(argc, argv,"s:O:R:M:i:Nn:d:Pp:l:hI:EU:t:m:W:T:D:c:b:"))
!= EOF) {
		switch (opt) {
		case 's':
			pstrcpy(servicesf, optarg);
			break;
		case 'O':
			pstrcpy(user_socket_options,optarg);
			break;	
		case 'R':
			pstrcpy(new_name_resolve_order, optarg);
			break;
		case 'i':
			pstrcpy(scope,optarg);
			break;
		case 'n':
			pstrcpy(global_myname,optarg);
			break;
		case 'd':
			if (*optarg == 'A')
				DEBUGLEVEL = 10000;
			else
				DEBUGLEVEL = atoi(optarg);
			break;
		case 'p':
			port = atoi(optarg);
			break;
		case 'l':
			slprintf(debugf,sizeof(debugf)-1, "%s.client",optarg);
			break;
		case 'h':
			usage(pname);
			exit(0);
			break;
		case 'I':
			{
				dest_ip = *interpret_addr2(optarg);
				if (zero_ip(dest_ip))
					exit(1);
				have_ip = True;
			}
			break;
		case 'E':
			dbf = stderr;
			break;
		case 'W':
			pstrcpy(workgroup,optarg);
			break;
		default:
			usage(pname);
			exit(1);
		}
	}

	get_myname((*global_myname)?NULL:global_myname);  

	if(*new_name_resolve_order)
		lp_set_name_resolve_order(new_name_resolve_order);

	DEBUG( 3, ( "Client started (version %s).\n", VERSION ) );

	do_nettime(service);
	return 0;
}

==== patch for Makefile.in
*** samba/samba-2.0.6/source/Makefile.in	Wed Nov 10 21:35:55 1999
--- samba-2.0.6/source/Makefile.in	Mon Apr 10 14:57:09 2000
*************** FLAGS  = $(ISA) $(FLAGS5) $(PASSWD_FLAGS
*** 77,83 ****
  FLAGS32  = $(ISA32) $(FLAGS5) $(PASSWD_FLAGS)
  
  SPROGS = bin/smbd bin/nmbd bin/swat
! PROGS1 = bin/smbclient bin/smbspool bin/testparm bin/testprns bin/smbstatus
@RUNPROG@
  PROGS2 = bin/rpcclient bin/smbpasswd bin/make_smbcodepage @WRAP@ @WRAP32@
  MPROGS = @MPROGS@
  PROGS = $(PROGS1) $(PROGS2) $(MPROGS) bin/nmblookup bin/make_printerdef 
--- 77,83 ----
  FLAGS32  = $(ISA32) $(FLAGS5) $(PASSWD_FLAGS)
  
  SPROGS = bin/smbd bin/nmbd bin/swat
! PROGS1 = bin/smbclient bin/nettime bin/smbspool bin/testparm bin/testprns
bin/smbstatus @RUNPROG@
  PROGS2 = bin/rpcclient bin/smbpasswd bin/make_smbcodepage @WRAP@ @WRAP32@
  MPROGS = @MPROGS@
  PROGS = $(PROGS1) $(PROGS2) $(MPROGS) bin/nmblookup bin/make_printerdef 
*************** SMBWRAPPER_OBJ = smbwrapper/smbw.o smbwr
*** 220,225 ****
--- 220,228 ----
  CLIENT_OBJ = client/client.o client/clitar.o \
               $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) $(LIB_OBJ)
  
+ NETTIME_OBJ = client/nettime.o \
+ 	      $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) $(LIB_OBJ)
+ 
  CUPS_OBJ = client/smbspool.o $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ)
$(LIB_OBJ)
  
  MOUNT_OBJ = client/smbmount.o \
*************** SMBFILTER_OBJ = utils/smbfilter.o $(LIBS
*** 256,261 ****
--- 259,265 ----
                   $(UBIQX_OBJ) $(LIB_OBJ)
  
  PROTO_OBJ = $(SMBD_OBJ) $(NMBD_OBJ) $(SWAT_OBJ) $(CLIENT_OBJ) \
+ 	    $(NETTIME_OBJ) \
  	    $(RPCCLIENT_OBJ) $(SMBWRAPPER_OBJ) $(SMBTORTURE_OBJ)
  
  PICOBJS = $(SMBWRAPPER_OBJ:.o=.po)
*************** bin/rpcclient: $(RPCCLIENT_OBJ) bin/.dum
*** 371,376 ****
--- 375,384 ----
  bin/smbclient: $(CLIENT_OBJ) bin/.dummy
  	@echo Linking $@
  	@$(CC) $(FLAGS) -o $@ $(CLIENT_OBJ) $(LDFLAGS) $(LIBS)
+ 
+ bin/nettime: $(NETTIME_OBJ) bin/.dummy
+ 	@echo Linking $@
+ 	@$(CC) $(FLAGS) -o $@ $(NETTIME_OBJ) $(LDFLAGS) $(LIBS)
  
  bin/smbspool: $(CUPS_OBJ) bin/.dummy
  	@echo Linking $@



More information about the samba-technical mailing list