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.