char *timestampdir;
char *timestampfile;
{
- time_t now = time(NULL);
-
if (timestamp_uid != 0)
set_perms(PERM_TIMESTAMP);
- if (touch(-1, timestampfile ? timestampfile : timestampdir, now, 0) == -1) {
+ if (touch(-1, timestampfile ? timestampfile : timestampdir, NULL) == -1) {
if (timestampfile) {
int fd = open(timestampfile, O_WRONLY|O_CREAT|O_TRUNC, 0600);
if (def_timestamp_timeout < 0 && sb.st_mtime != 0)
status = TS_CURRENT;
else {
+ /* XXX - should use timespec here */
now = time(NULL);
if (def_timestamp_timeout &&
now - sb.st_mtime < 60 * def_timestamp_timeout) {
remove_timestamp(remove)
int remove;
{
- char *timestampdir;
- char *timestampfile;
- char *ts;
+ struct timespec ts;
+ char *timestampdir, *timestampfile, *path;
int status;
build_timestamp(×tampdir, ×tampfile);
status = timestamp_status(timestampdir, timestampfile, user_name, FALSE);
if (status == TS_OLD || status == TS_CURRENT) {
- ts = timestampfile ? timestampfile : timestampdir;
+ path = timestampfile ? timestampfile : timestampdir;
if (remove) {
if (timestampfile)
status = unlink(timestampfile);
status = rmdir(timestampdir);
if (status == -1 && errno != ENOENT) {
log_error(NO_EXIT, "can't remove %s (%s), will reset to Epoch",
- ts, strerror(errno));
+ path, strerror(errno));
remove = FALSE;
}
+ } else {
+ timespecclear(&ts);
+ if (touch(-1, path, &ts) == -1)
+ err(1, "can't reset %s to Epoch", path);
}
- if (!remove && touch(-1, ts, 0, 0) == -1)
- err(1, "can't reset %s to Epoch", ts);
}
free(timestampdir);
};
#endif /* !HAVE_TIMESPEC */
+#ifndef timespecclear
+# define timespecclear(ts) (ts)->tv_sec = (ts)->tv_nsec = 0
+#endif
+#ifndef timespecisset
+# define timespecisset(ts) ((ts)->tv_sec || (ts)->tv_nsec)
+#endif
+#ifndef timespecsub
+# define timespecsub(minuend, subrahend, difference) \
+ do { \
+ (difference)->tv_sec = (minuend)->tv_sec - (subrahend)->tv_sec; \
+ (difference)->tv_nsec = (minuend)->tv_nsec - (subrahend)->tv_nsec; \
+ if ((difference)->tv_nsec < 0) { \
+ (difference)->tv_nsec += 1000000000L; \
+ (difference)->tv_sec--; \
+ } \
+ } while (0)
+#endif
+
#endif /* _SUDO_COMPAT_H */
* Update the access and modify times on an fd or file.
*/
int
-touch(fd, path, sec, nsec)
+touch(fd, path, tsp)
int fd;
char *path;
- time_t sec;
- long nsec;
+ struct timespec *tsp;
{
struct timeval times[2];
- times[0].tv_sec = times[1].tv_sec = sec;
- times[0].tv_usec = times[1].tv_usec = nsec;
+ if (tsp != NULL) {
+ times[0].tv_sec = times[1].tv_sec = tsp->tv_sec;
+ times[0].tv_usec = times[1].tv_usec = tsp->tv_nsec / 1000;
+ }
#if defined(HAVE_FUTIME) || defined(HAVE_FUTIMES)
if (fd != -1)
- return(futimes(fd, times));
+ return(futimes(fd, tsp ? times : NULL));
else
#endif
if (path != NULL)
- return(utimes(path, times));
+ return(utimes(path, tsp ? times : NULL));
else
return(-1);
}
void dump_auth_methods __P((void));
void init_envtables __P((void));
int lock_file __P((int, int));
-int touch __P((int, char *, time_t, long));
+int touch __P((int, char *, struct timespec *));
int user_is_exempt __P((void));
void set_fqdn __P((void));
int set_runaspw __P((char *));
char *sudo_getepw __P((const struct passwd *));
int pam_prep_user __P((struct passwd *));
void zero_bytes __P((volatile VOID *, size_t));
+int gettime __P((struct timespec *));
YY_DECL;
/* Only provide extern declarations outside of sudo.c. */
int i, ac, ofd, nargc, rval;
sigaction_t sa;
struct stat sb;
+ struct timespec ts1, ts2;
struct tempfile {
char *tfile;
char *ofile;
* file's mtime. It is better than nothing and we only use the info
* to determine whether or not a file has been modified.
*/
- if (touch(tf[i].tfd, NULL, tf[i].ots.tv_sec, tf[i].ots.tv_nsec) == -1) {
+ if (touch(tf[i].tfd, NULL, &tf[i].ots) == -1) {
if (fstat(tf[i].tfd, &sb) == 0) {
tf[i].ots.tv_sec = mtim_getsec(sb);
tf[i].ots.tv_nsec = mtim_getnsec(sb);
}
+ /* XXX - else error? */
}
#endif
}
(void) sigaction(SIGTSTP, &saved_sa_tstp, NULL);
/*
- * Fork and exec the editor as with the invoking user's creds.
+ * Fork and exec the editor as with the invoking user's creds,
+ * keeping track of the time spent in the editor.
*/
+ gettime(&ts1);
kidpid = fork();
if (kidpid == -1) {
warn("fork");
break;
}
} while (pid != -1 || errno == EINTR);
+ gettime(&ts2);
if (pid == -1 || !WIFEXITED(i))
rval = 1;
else
if (tf[i].osize == sb.st_size &&
tf[i].ots.tv_sec == mtim_getsec(sb) &&
tf[i].ots.tv_nsec == mtim_getnsec(sb)) {
- warnx("%s unchanged", tf[i].ofile);
- unlink(tf[i].tfile);
- close(tf[i].tfd);
- continue;
+ /*
+ * If mtime and size match but the user spent no measurable
+ * time in the editor we can't tell if the file was changed.
+ */
+ timespecsub(&ts1, &ts2, &ts2);
+ if (timespecisset(&ts2)) {
+ warnx("%s unchanged", tf[i].ofile);
+ unlink(tf[i].tfile);
+ close(tf[i].tfd);
+ continue;
+ }
}
}
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
+#include <sys/time.h>
#ifndef __TANDEM
# include <sys/file.h>
#endif
int stmp_fd; /* stmp file descriptor */
int n; /* length parameter */
int ch; /* getopt char */
- time_t now; /* time now */
- struct stat stmp_sb, sudoers_sb; /* to check for changes */
+ struct timespec ts1, ts2; /* time before and after edit */
+ struct timespec sudoers_mtim; /* starting mtime of sudoers file */
+ off_t sudoers_size; /* starting size of sudoers file */
+ struct stat sb; /* stat buffer */
/* Warn about aliases that are used before being defined. */
pedantic = 1;
if (!lock_file(sudoers_fd, SUDO_TLOCK))
errx(1, "sudoers file busy, try again later");
#ifdef HAVE_FSTAT
- if (fstat(sudoers_fd, &sudoers_sb) == -1)
+ if (fstat(sudoers_fd, &sb) == -1)
#else
- if (stat(sudoers, &sudoers_sb) == -1)
+ if (stat(sudoers, &sb) == -1)
#endif
err(1, "can't stat %s", sudoers);
+ sudoers_size = sb.st_size;
+ sudoers_mtim.tv_sec = mtim_getsec(sb);
+ sudoers_mtim.tv_nsec = mtim_getnsec(sb);
/*
* Open sudoers temp file.
setup_signals();
/* Copy sudoers -> stmp and reset the mtime */
- if (sudoers_sb.st_size) {
+ if (sudoers_size) {
while ((n = read(sudoers_fd, buf, sizeof(buf))) > 0)
if (write(stmp_fd, buf, n) != n)
err(1, "write error");
write(stmp_fd, buf, 1);
}
- (void) touch(stmp_fd, stmp, sudoers_sb.st_mtime, 0);
+ (void) touch(stmp_fd, stmp, &sudoers_mtim);
(void) close(stmp_fd);
/* Parse sudoers to pull in editor and env_editor conf values. */
* XPG4 specifies that vi's exit value is a function of the
* number of errors during editing (?!?!).
*/
- now = time(NULL);
+ gettime(&ts1);
if (run_command(Editor, av) != -1) {
+ gettime(&ts2);
/*
* Sanity checks.
*/
- if (stat(stmp, &stmp_sb) < 0) {
+ if (stat(stmp, &sb) < 0) {
warnx("cannot stat temporary file (%s), %s unchanged",
stmp, sudoers);
Exit(-1);
}
- if (stmp_sb.st_size == 0) {
+ if (sb.st_size == 0) {
warnx("zero length temporary file (%s), %s unchanged",
stmp, sudoers);
Exit(-1);
switch (whatnow()) {
case 'Q' : parse_error = FALSE; /* ignore parse error */
break;
- case 'x' : if (sudoers_sb.st_size == 0)
+ case 'x' : if (sudoers_size == 0)
unlink(sudoers);
Exit(0);
break;
/*
* If the user didn't change the temp file, just unlink it.
*/
- if (sudoers_sb.st_mtime != now && sudoers_sb.st_mtime == stmp_sb.st_mtime &&
- sudoers_sb.st_size == stmp_sb.st_size) {
- warnx("sudoers file unchanged");
- Exit(0);
+ if (sudoers_size == sb.st_size &&
+ sudoers_mtim.tv_sec == mtim_getsec(sb) &&
+ sudoers_mtim.tv_nsec == mtim_getnsec(sb)) {
+ /*
+ * If mtime and size match but the user spent no measurable
+ * time in the editor we can't tell if the file was changed.
+ */
+ timespecsub(&ts1, &ts2, &ts2);
+ if (timespecisset(&ts2)) {
+ warnx("sudoers file unchanged");
+ Exit(0);
+ }
}
/*