rpms/vixie-cron/devel vixie-cron-4.1-60_seconds.patch,NONE,1.1

fedora-cvs-commits at redhat.com fedora-cvs-commits at redhat.com
Thu Jan 12 18:14:40 UTC 2006


Author: jvdias

Update of /cvs/dist/rpms/vixie-cron/devel
In directory cvs.devel.redhat.com:/tmp/cvs-serv7341

Added Files:
	vixie-cron-4.1-60_seconds.patch 
Log Message:
DO NOT APPLY THIS PATCH!
This is checked in just to record the work done to ensure that at least
60 seconds elapse between job runs, which it turns out is not required -
the important thing is that the same job cannot be run twice in the same
minute, not that 60 seconds must elapse between job runs.
The mechanism to communicate the actual job run time to the crond main
process may be of some use in the future...


vixie-cron-4.1-60_seconds.patch:
 cron.c       |   43 ++++++++++++++++++++++++++++++++-
 do_command.c |   75 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 115 insertions(+), 3 deletions(-)

--- NEW FILE vixie-cron-4.1-60_seconds.patch ---
--- vixie-cron-4.1/cron.c.60_seconds	2006-01-11 15:16:47.000000000 -0500
+++ vixie-cron-4.1/cron.c	2006-01-11 20:02:08.000000000 -0500
@@ -37,12 +37,14 @@
 		sigchld_handler(int),
 		sighup_handler(int),
 		sigchld_reaper(void),
+                sigusr1_handler(int),
 		quit(int),
 		parse_args(int c, char *v[]);
 
 static	volatile sig_atomic_t	got_sighup, got_sigchld;
-static	int			timeRunning, virtualTime, clockTime;
+static	int			timeRunning, virtualTime, clockTime, drift;
 static	long			GMToff;
+static  struct timeval          time_last_job_run_tv={0,0};
 
 static void
 usage(void) {
@@ -86,6 +88,8 @@
 	sact.sa_handler = quit;
 	(void) sigaction(SIGINT, &sact, NULL);
 	(void) sigaction(SIGTERM, &sact, NULL);
+	sact.sa_handler = sigusr1_handler;
+	(void) sigaction(SIGUSR1, &sact, NULL);
 
 	acquire_daemonlock(0);
 	set_cron_uid();
@@ -137,6 +141,7 @@
 	set_time(TRUE);
 	run_reboot_jobs(&database);
 	timeRunning = virtualTime = clockTime;
+	drift = 0;
 
 	/*
 	 * Too many clocks, not enough time (Al. Einstein)
@@ -151,6 +156,9 @@
 	while (TRUE) {
 		int timeDiff;
 		enum timejump wakeupKind;
+		struct timeval tv;
+		struct timezone tz={0,0};
+		struct timespec ts;
 
 		/* ... wait for the time (in minutes) to change ... */
 		do {
@@ -159,6 +167,31 @@
 		} while (clockTime == timeRunning);
 		timeRunning = clockTime;
 
+		/* Ensure at least 60 seconds have elapsed between successive job runs.
+                 */
+		drift = 0;
+		if ( time_last_job_run_tv.tv_sec > 0 )
+		{
+		    gettimeofday(&tv,&tz);
+		    double time_since_last_job = 
+			( (((double)tv.tv_sec) + (((double)tv.tv_usec)/1000000.0))
+			 -( ((double)time_last_job_run_tv.tv_sec)
+			  +(((double)time_last_job_run_tv.tv_usec)/1000000.0)
+			  )
+			);
+		    memset(&time_last_job_run_tv,0,sizeof(struct timeval));
+		    if( time_since_last_job < 60.0 )
+		    {
+			double delay = (60.0 - time_since_last_job) 
+			             + (tv.tv_usec ? (((double)(1000000 - tv.tv_usec))/1000000.0) : 0);
+			ts.tv_sec = delay ;
+			drift = ts.tv_sec ;
+			ts.tv_nsec = ( delay - (double)ts.tv_sec ) * 1000000000;
+			Debug(DSCH,("DELAY: %lu %lu\n",ts.tv_sec,ts.tv_nsec));
+			nanosleep(&ts,0L);
+		    }
+		}
+ 
 		/*
 		 * Calculate how the current time differs from our virtual
 		 * clock.  Classify the change into one of 4 cases.
@@ -362,7 +395,7 @@
 	int seconds_to_wait;
 
 	t1 = time(NULL) + GMToff;
-	seconds_to_wait = (int)(target * SECONDS_PER_MINUTE - t1) + 1;
+	seconds_to_wait = (int)(target * SECONDS_PER_MINUTE - t1) + 1 + drift;
 	Debug(DSCH, ("[%ld] Target time=%ld, sec-to-wait=%d\n",
 	    (long)getpid(), (long)target*SECONDS_PER_MINUTE, seconds_to_wait))
 
@@ -399,6 +432,12 @@
 }
 
 static void
+sigusr1_handler(int x) {
+    struct timezone tz={0,0}; /* UTC */
+    gettimeofday(&time_last_job_run_tv,&tz);
+}
+
+static void
 quit(int x) {
 	(void) unlink(_PATH_CRON_PID);
 	_exit(0);
--- vixie-cron-4.1/do_command.c.60_seconds	2006-01-11 15:16:47.000000000 -0500
+++ vixie-cron-4.1/do_command.c	2006-01-11 18:57:44.000000000 -0500
@@ -25,15 +25,38 @@
 
 #include "cron.h"
 
+/* Allow the job user process the 'CAP_KILL' capability to signal the time of
+   job execution to the main cron process - need capability includes:
+*/
+#include <sys/syscall.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#ifndef SYS_capset
+#ifndef __NR_capset
+#error  __NR_capset and SYS_capset undefined
+#else
+#define SYS_capset __NR_capset
+#endif
+#endif
+#ifndef SYS_capget
+#ifndef __NR_capget
+#error  __NR_capget and SYS_capget undefined
+#else
+#define SYS_capset __NR_capget
+#endif
+#endif
+
 static void		child_process(entry *, user *);
 static int		safe_p(const char *, const char *);
+static pid_t  crond_pid;
 
 void
 do_command(entry *e, user *u) {
 	Debug(DPROC, ("[%ld] do_command(%s, (%s,%ld,%ld))\n",
 		      (long)getpid(), e->cmd, u->name,
 		      (long)e->pwd->pw_uid, (long)e->pwd->pw_gid))
-
+	    
+	crond_pid = getpid();
 	/* fork to become asynchronous -- parent process is done immediately,
 	 * and continues to run the normal cron code, which means return to
 	 * tick().  the child and grandchild don't leave this function, alive.
@@ -67,6 +90,28 @@
 	int children = 0; 
         char **jobenv=0L;
 
+	struct __user_cap_header_struct caphead;
+	struct __user_cap_data_struct cap;
+
+	/* Before we set the security context, allow the job user to inherit the CAP_KILL capability: */    
+
+	memset(&caphead, 0, sizeof(caphead));
+	memset(&cap, 0, sizeof(cap));
+	caphead.version = _LINUX_CAPABILITY_VERSION;
+	caphead.pid = 0;
+
+	if ( syscall(SYS_capget, &caphead, &cap) < 0)
+	    syslog(LOG_INFO, "CRON (%s) ERROR: cannot get process capabilities:", strerror(errno));
+
+	cap.inheritable |= CAP_KILL ;
+	
+	if ( syscall(SYS_capset, &caphead, &cap) < 0) 
+	    syslog(LOG_INFO, "CRON (%s) ERROR: cannot set process capabilities:", strerror(errno));
+	
+	if ( prctl(PR_SET_KEEPCAPS,1,0,0,0) < 0 )
+	    syslog(LOG_INFO, "CRON (%s) ERROR: cannot prctl(PR_SET_KEEPCAPS..):", strerror(errno));
+
+
 	/* Set up the Red Hat security context for both mail/minder and job processes:
          */
 	if ( cron_set_job_security_context( e, u, &jobenv ) != 0 )
@@ -75,6 +120,17 @@
 	    exit(ERROR_EXIT);
 	}
 
+	/* Now we are the job user, running in user context with non-root capabilities.
+	 * Add the CAP_KILL capability to our effective capability set:
+	 */
+	if ( syscall(SYS_capget, &caphead, &cap) < 0)
+	    syslog(LOG_INFO, "CRON (%s) ERROR: cannot get process capabilities:", strerror(errno));
+	
+	cap.effective = cap.permitted ;
+
+	if ( syscall(SYS_capset, &caphead, &cap) < 0) 
+	    syslog(LOG_INFO, "CRON (%s) ERROR: cannot set process capabilities:", strerror(errno));
+
 	Debug(DPROC, ("[%ld] child_process('%s')\n", (long)getpid(), e->cmd))
 
 #ifdef CAPITALIZE_FOR_PS
@@ -230,8 +286,25 @@
 				_exit(OK_EXIT);
 			}
 # endif /*DEBUGGING*/
+			/* tell crond main process we are running the job */
+			if( kill(crond_pid, SIGUSR1) == -1 )
+			    syslog(LOG_INFO, "CRON (%s) ERROR: kill failed: %s", e->pwd->pw_name, strerror(errno));
+
+			/* drop capabilities */
+			cap.permitted = 0;
+			cap.effective = 0;
+			cap.inheritable=0;
+			if( syscall(SYS_capset, &caphead, &cap) < 0 )
+			    syslog(LOG_INFO, "CRON (%s) ERROR: capset (DROP) failed: %s", e->pwd->pw_name,
+				   strerror(errno));
+			if( prctl(PR_SET_KEEPCAPS,0,0,0,0) < 0 )
+			    syslog(LOG_INFO, "CRON (%s) ERROR: prctl clear KEEPCAPS failed: %s", e->pwd->pw_name,
+				   strerror(errno));
 
+			/*** RUN THE COMMAND ***/
+			
 			execle(shell, shell, "-c", e->cmd, (char *)0, jobenv);
+			
 			fprintf(stderr, "execl: couldn't exec `%s'\n", shell);
 			perror("execl");
 			_exit(ERROR_EXIT);




More information about the fedora-cvs-commits mailing list