ftp.nice.ch/pub/next/tools/hack/TimeShift.I.bs.tar.gz#/TimeShift/timeshift.c

This is timeshift.c in view mode; [Download] [Up]

/* timeshift
** This program is linked to 3 different names.
**	timeshift
**	halt
**	reboot
** When executed via the names halt or reboot, the clock is set to
** what it would be if the local time zone was GMT.  After munging
** the clock, the real halt or reboot executable is then exec'ed.
**
** When executed via the name timeshift, the clock is shifted back
** to the normal time within the local time zone.
**
** In order to prevent shifting the clock an odd number of times
** and to prevent screwing up when shutdown and boot span a DST
** cusp, the amount of clock shift is stored in a lock file
** /usr/etc/shifted.time.
**
** This software is provided as shareware.
** If you find this software useful, please send me $10
**
** email feedback to jq@quick.com
** For current snail address refer to address returned from internic
** via 'whois quick.com'
**
** $Header: /usr/users/jq/c/util/timeshift/RCS/timeshift.c,v 1.1 95/12/03 16:26:57 jq Exp $
*/
#include <sys/param.h>
#include <syslog.h>
#include <libc.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <sys/file.h>

#define	TIME_LOCK	"/usr/etc/shifted.time"

extern int errno;

enum shift_mode {
	UP = -1,
	NOCHANGE = 0,
	DOWN = 1
};

/* Return the basename of a pathname
*/
char *
basename(char *path)
{
	char	*base = rindex(path, '/');

	if (base) {
		base++;
	} else {
		base = path;
	}

	return base;
}

/* Shift the clock by the amount required to go from local time in time zone
** to local time in GMT when shutting down the computer.  Reverse the
** process on the way back up.
** N.B. The reason for saving the time shift to a file and using it on the
** way back up, is to prevent dropping or gaining an hour if the system
** is brought down under daylight savings time and then brought up in
** standard time, or vice versa.
*/
void
shift_time(enum shift_mode mode)
{
	struct timeval tp;
	struct timezone tzone;
	long	shift = 0;
	int	err;
	FILE	*stamp = 0;

	/* Let someone know why we did nothing.
	*/
	if (mode == NOCHANGE) {
		syslog(LOG_ALERT, "sync option prevented time change.");
		return;
	}

	/* Make sure that we do not set clock multiple timed in DOWN mode.
	*/
	if (mode == DOWN && access(TIME_LOCK, F_OK) == 0) {
		syslog(LOG_ALERT, "%s already exists. No change made.",
			TIME_LOCK);
		return;
	}

	/* Open the TIME_LOCK file in read mode on the way up and in
	** write mode on the way down.
	*/
	stamp = fopen(TIME_LOCK, (mode == UP)?"r":"w");
	if (stamp == 0) {
		syslog(LOG_ALERT, "Cannot fopen %s - %s",
			TIME_LOCK,
			strerror(errno));
		return;
	}

	/* On the way down, set shift amount and save it in TIME_LOCK.
	** On the way up, read the previous shift amount to undo it.
	*/
	if (mode == DOWN) {
		time_t	clock = 0;
		struct tm *now;

		time(&clock);
		now = localtime(&clock);
		fprintf(stamp, "%ld\n", now->tm_gmtoff);
		shift = mode * (now->tm_gmtoff);
	} else if (mode == UP) {
		fscanf(stamp, "%ld", &shift);
		shift *= mode;
	}
	fclose(stamp);

	/* Now, get the current time, adjust for shift, and reset time.
	** Note that we are not accounting for the amount of time it 
	** will take to set the clock or performe the addition.
	** Though we could probably perform the operation and get a
	** rough guess of how many microseconds to fudge, I don't think
	** it's worth the effort.
	*/
	gettimeofday(&tp, &tzone);
	tp.tv_sec += shift;
	err = settimeofday(&tp, &tzone);

	if (err == -1) {
		/* If we failed to change the clock on the way down,
		** then unlink the TIME_LOCK so that we don't make
		** things worse on the way up.
		*/
		if (mode == DOWN) {
			unlink(TIME_LOCK);
		}
	} else {
		/* If we did set the clock, get rid of our old stamp file
		** since we've undone the change it represents.
		*/
		if (mode == UP) {
			unlink(TIME_LOCK);
		}
		syslog(LOG_ALERT, "Clock adjusted by %d seconds\n", shift);
	}
}

/* Important safety checks!
** If 'reboot -n' or 'halt -n' are used it would be *extremely* uncool
** to do anything with the file system.  Since a sync is not performed
** you can end up allocating a block which will remain linked to a 
** directory entry, but actually be still on the free list.  Ouch!
**
** I do not know if 'reboot -q' suffers the same problems but I'm
** erring on false positive here and bailing on any command which
** has either 'n' or 'q' as any part of a hyphenated argument.
*/
enum shift_mode
safety_check(int argc, char **argv)
{
	char	*arg;
	int	i;

	for (i = 0; i < argc; i++) {
		arg = argv[i];
		if (*arg == '-' && (index(arg, 'n') || index(arg, 'q'))) {
			return NOCHANGE; /* whew */
		}
	}

	return DOWN;
}

int
main(int argc, char **argv)
{
	char	*base = basename(argv[0]);
	enum shift_mode mode = NOCHANGE;
	char	realcmd[MAXPATHLEN + 1];
	char	*exec_cmd = 0;
	char	ishmael[128]; /* What to call me. */


	/* First determine whether we are coming down or booting up.
	*/
	if (strcmp(base,"timeshift") == 0) {
		mode = UP;
		strcpy(ishmael, base);
	} else if (strcmp(base, "halt") == 0 || strcmp(base, "reboot") == 0) {
		char	*prefix = "/usr/etc/";

		mode = safety_check(argc, argv);

		sprintf(ishmael, "%s (%s)", "timeshift", base);
		if (index(argv[0], '/') != 0) {
			prefix = "";
		}
		sprintf(realcmd, "%s%s.real", prefix, argv[0]);

		exec_cmd = realcmd;
	} else {
		syslog(LOG_ALERT, "%s: is not named reboot halt or shiftup\n", argv[0]);
		exit(1);
	}
	openlog(ishmael, LOG_CONS, LOG_DAEMON);

	/* Next make sure we are root.
	** It's kind of gilding the lily a bit, but better safe than sorry.
	*/
	if (geteuid() != 0) {
		syslog(LOG_ALERT, "%s: not run as root.\n", argv[0]);
		exit(1);
	}

	/* Cover our asses in case we should just not do anything.
	*/
	shift_time(mode);

	/* If we were called instead of the real halt or reboot
	** we now need to exec the real mcCoy.
	*/
	if (exec_cmd) {
		execvp(exec_cmd, argv);
		syslog(LOG_ALERT, "Fatal error! Could't exec '%s'", realcmd);
	}

	return 0;
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.