Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 13 Aug 1995 18:17:21 +0200
From:      Wolfram Schneider <wosch@cs.tu-berlin.de>
To:        FreeBSD-gnats-submit@freebsd.org
Cc:        Paul Vixie <paul@vix.com>, wosch@cs.tu-berlin.de
Subject:   bin/683: cron(8)
Message-ID:  <199508131617.SAA13412@localhost>
Resent-Message-ID: <199508140810.BAA04270@freefall.FreeBSD.org>

next in thread | raw e-mail | index | archive | help

>Number:         683
>Category:       bin
>Synopsis:       cron(8)
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Aug 14 01:10:01 PDT 1995
>Last-Modified:
>Originator:     Wolfram Schneider
>Organization:
>Release:        FreeBSD 2.0-ALPHA i386
>Environment:
>Description:

	- cron(8) does not show or explain debug flags
	- debug flag 'test' cause endless loop
	- cron start ever a shell. Use exec if there are no meta chars.

>How-To-Repeat:
>Fix:
	
--- 1.1	1995/08/13 14:32:09
+++ cron.c	1995/08/13 14:32:21
@@ -46,7 +46,16 @@
 
 static void
 usage() {
+    char **dflags;
+
 	fprintf(stderr, "usage:  %s [-x debugflag[,...]]\n", ProgramName);
+	fprintf(stderr, "\ndebugflags: ");
+
+    for(dflags = DebugFlagNames; *dflags; dflags++) {
+	fprintf(stderr, "%s ", *dflags);
+    }
+    fprintf(stderr, "\n");
+
 	exit(ERROR_EXIT);
 }
 
@@ -116,7 +125,7 @@
 	cron_sync();
 	while (TRUE) {
 # if DEBUGGING
-		if (!(DebugFlags & DTEST))
+	    /* if (!(DebugFlags & DTEST)) */
 # endif /*DEBUGGING*/
 			cron_sleep();
 
@@ -288,8 +297,10 @@
 {
 	int	argch;
 
-	while (EOF != (argch = getopt(argc, argv, "x:"))) {
+	while (EOF != (argch = getopt(argc, argv, "h?x:"))) {
 		switch (argch) {
+		case '?':
+		case 'h':
 		default:
 			usage();
 		case 'x':
--- 1.1	1995/08/13 10:08:46
+++ do_command.c	1995/08/13 15:23:53
@@ -33,6 +33,22 @@
 static void		child_process __P((entry *, user *)),
 			do_univ __P((user *));
 
+void do_exec __P((entry *));
+
+#if !defined(__FreeBSD__) && !defined(EXEC_SHELL)
+# warning "Only tested for FreeBSD."
+# warning "Use #define EXEC_SHELL if you have some"
+# warning "trouble with theses changes."
+#endif
+
+# if !defined(EXEC_SHELL)
+char **brk_string(char *, int *);
+char *emalloc(u_int);
+void enomem();
+int exectp(const char *file, char *const argv[], char *envp[]);
+
+#include <sys/errno.h>
+# endif /* EXEC_SHELL */
 
 void
 do_command(e, u)
@@ -215,23 +231,9 @@
 
 		/* exec the command.
 		 */
-		{
-			char	*shell = env_get("SHELL", e->envp);
 
-# if DEBUGGING
-			if (DebugFlags & DTEST) {
-				fprintf(stderr,
-				"debug DTEST is on, not exec'ing command.\n");
-				fprintf(stderr,
-				"\tcmd='%s' shell='%s'\n", e->cmd, shell);
-				_exit(OK_EXIT);
-			}
-# endif /*DEBUGGING*/
-			execle(shell, shell, "-c", e->cmd, (char *)0, e->envp);
-			fprintf(stderr, "execl: couldn't exec `%s'\n", shell);
-			perror("execl");
-			_exit(ERROR_EXIT);
-		}
+		do_exec(e);
+		
 		break;
 	default:
 		/* parent process */
@@ -499,3 +501,381 @@
 	(void) universe(U_ATT);
 #endif
 }
+
+
+/* exec the command.
+ */
+
+void
+do_exec(e)
+    entry *e;
+{
+
+    char	*shell = env_get("SHELL", e->envp);
+
+
+    /* use ever shell -c for executing programs */
+# if defined(EXEC_SHELL) 
+
+# if DEBUGGING
+    if (DebugFlags & DTEST) {
+	fprintf(stderr,
+		"debug DTEST is on, not exec'ing command.\n");
+	fprintf(stderr,
+		"\tcmd='%s' shell='%s'\n", e->cmd, shell);
+	_exit(OK_EXIT);
+    }
+# endif /*DEBUGGING*/
+    execle(shell, shell, "-c", e->cmd, (char *)0, e->envp);
+    fprintf(stderr, "execl: couldn't exec `%s'\n", shell);
+    perror("execl");
+    _exit(ERROR_EXIT);
+
+
+
+# else
+    /* try exec if there are no meta chars,
+     * otherweise use sh -c for executing programs 
+     *
+     * code borrowed from 4.4BSD make(1)
+     */
+
+    static char meta[256];
+    char  *cp;      /* Pointer to string of shell meta-characters */
+    char  **av;     /* Argument vector for thing to exec */
+    int   argc;     /* Number of arguments in av or 0 if not
+                     * dynamically allocated */
+    int i;
+    char **a;
+
+    for(i = 0; i < 256; i++)
+	meta[i] = 0;
+    
+    for (cp = "#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; 
+	 cp++) {
+	meta[(unsigned char) *cp] = 1;
+    }
+
+    /*
+     * The null character serves as a sentinel in the string.
+     */
+    meta[0] = 1;
+
+    /*
+     * Search for meta characters in the command. If there are no meta
+     * characters, there's no need to execute a shell to execute the
+     * command. 
+     */
+    for (cp = e->cmd; !meta[(unsigned char)*cp]; cp++) {
+	continue;
+    }
+
+    /*
+     * If *cp isn't the null character, we hit a "meta" character 
+     * or command ist not a absolute path and need to pass the 
+     * command off to the shell.
+     */
+
+    if (*cp != '\0') {
+
+# if DEBUGGING	
+	if (DebugFlags & DTEST) {
+	    fprintf(stderr,
+		    "debug DTEST is on, not exec'ing command.\n");
+	    fprintf(stderr, "Meta char: `%s' `%s'\n", cp, e->cmd); 
+	    _exit(OK_EXIT);
+	}
+# endif /*DEBUGGING*/
+	execle(shell, shell, "-c", e->cmd, (char *)0, e->envp);
+	fprintf(stderr, "execle: couldn't exec `%s'\n", shell);
+	perror("execle");
+	_exit(ERROR_EXIT);
+    } 
+
+    else {
+        /*
+         * No meta-characters, so no need to exec a shell. Break the command
+         * into words to form an argument vector we can execute.
+         * brk_string sticks our name in av[0], so we have to
+         * skip over it...
+         */
+
+        av = brk_string(e->cmd, &argc);
+	a = av;
+
+# if DEBUGGING	
+	if (DebugFlags & DTEST) {
+	    fprintf(stderr,
+		    "debug DTEST is on, not exec'ing command.\n");
+
+	    fprintf(stderr, "No meta chars: `%s'\n", e->cmd); 
+	    fprintf(stderr, "exect(\"%s\"", *av);
+	    
+	    for(a = av; *a; a++) {
+		fprintf(stderr, ", \"%s\"", *a);
+	    }
+	    fprintf(stderr, ", e->envp);\nargc: %d\n", argc);
+
+	    _exit(OK_EXIT);
+	}
+# endif /*DEBUGGING*/
+
+	/* exectp
+	 *
+	 * searching for an executable file if the specified file 
+	 * name does not contain a slash ``/'' character.  
+	 * The search path is the path specified in the environment 
+	 * by ``PATH'' variable.  If this variable isn't
+	 * specified, the default path ``/bin:/usr/bin:'' is used
+	 */
+
+	(void)exectp(av[0], av, e->envp);
+
+	fprintf(stderr, "exectp: couldn't exec `%s'\n", av[0]);
+	perror("exectp");
+	_exit(ERROR_EXIT);
+
+    }
+}
+#endif /* EXEC_SHELL */
+
+# if !defined(EXEC_SHELL)
+/* following code borrowed from 4.4BSD make(1) */
+
+/*-
+ * brk_string --
+ *	Fracture a string into an array of words (as delineated by tabs or
+ *	spaces) taking quotation marks into account.  Leading tabs/spaces
+ *	are ignored.
+ *
+ * returns --
+ *	Pointer to the array of pointers to the words.  To make life easier,
+ *	the first word is always the value of the .MAKE variable.
+ */
+char **
+brk_string(str, store_argc)
+	register char *str;
+	int *store_argc;
+{
+	static int argmax, curlen;
+	static char **argv, *buf;
+	register int argc, ch;
+	register char inquote, *p, *start, *t;
+	int len;
+
+	if (!argv) {
+		argv = (char **)emalloc((argmax = 50) * sizeof(char *));
+	}
+
+	/* skip leading space chars. */
+	for (; *str == ' ' || *str == '\t'; ++str)
+		continue;
+
+	/* allocate room for a copy of the string */
+	if ((len = strlen(str) + 1) > curlen)
+		buf = emalloc(curlen = len);
+
+	/*
+	 * copy the string; at the same time, parse backslashes,
+	 * quotes and build the argument list.
+	 */
+	argc = 0;
+	inquote = '\0';
+
+	for (p = str, start = t = buf;; ++p) {
+		switch(ch = *p) {
+		case '"':
+		case '\'':
+			if (inquote) {
+				if (inquote == ch)
+					inquote = '\0';
+				else
+					break;
+			} else {
+				inquote = (char) ch;
+				start = t;
+				continue;
+			}
+			/* FALLTHROUGH */
+		case ' ':
+		case '\t':
+			if (inquote)
+				break;
+			if (!start)
+				continue;
+			/* FALLTHROUGH */
+		case '\n':
+		case '\0':
+			/*
+			 * end of a token -- make sure there's enough argv
+			 * space and save off a pointer.
+			 */
+			*t++ = '\0';
+			if (argc == argmax) {
+				argmax *= 2;		/* ramp up fast */
+				if (!(argv = (char **)realloc(argv,
+				    argmax * sizeof(char *))))
+				enomem();
+			}
+			argv[argc++] = start;
+			start = (char *)NULL;
+			if (ch == '\n' || ch == '\0')
+				goto done;
+			continue;
+		case '\\':
+			switch (ch = *++p) {
+			case '\0':
+			case '\n':
+				/* hmmm; fix it up as best we can */
+				ch = '\\';
+				--p;
+				break;
+			case 'b':
+				ch = '\b';
+				break;
+			case 'f':
+				ch = '\f';
+				break;
+			case 'n':
+				ch = '\n';
+				break;
+			case 'r':
+				ch = '\r';
+				break;
+			case 't':
+				ch = '\t';
+				break;
+			}
+			break;
+		}
+		if (!start)
+			start = t;
+		*t++ = (char) ch;
+	}
+done:	argv[argc] = (char *)NULL;
+	*store_argc = argc;
+	return(argv);
+}
+
+/*
+ * emalloc --
+ *	malloc, but die on error.
+ */
+char *
+emalloc(len)
+	u_int len;
+{
+	char *p;
+
+	if (!(p = malloc(len)))
+		enomem();
+	return(p);
+}
+
+/*
+ * enomem --
+ *	die when out of memory.
+ */
+void
+enomem()
+{
+	(void)fprintf(stderr, "cron: %s.\n", strerror(errno));
+	exit(2);
+}
+
+
+/* code borrowed from 4.4BSD libc exec(3) */
+int
+exectp(name, argv, envp)
+	const char *name;
+	char * const *argv;
+        char **envp;
+{
+	static int memsize;
+	static char **memp;
+	register int cnt, lp, ln;
+	register char *p;
+	int eacces, etxtbsy;
+	char *bp, *cur, *path, buf[MAXPATHLEN];
+
+	/* If it's an absolute or relative path name, it's easy. */
+	if (index(name, '/')) {
+		bp = (char *)name;
+		cur = path = NULL;
+		goto retry;
+	}
+	bp = buf;
+
+	/* Get the path we're searching. */
+	if (!(path = env_get("PATH", envp)))
+		path = _PATH_DEFPATH;
+	cur = path = strdup(path);
+
+	eacces = etxtbsy = 0;
+	while (p = strsep(&cur, ":")) {
+		/*
+		 * It's a SHELL path -- double, leading and trailing colons
+		 * mean the current directory.
+		 */
+		if (!*p) {
+			p = ".";
+			lp = 1;
+		} else
+			lp = strlen(p);
+		ln = strlen(name);
+
+		/*
+		 * If the path is too long complain.  This is a possible
+		 * security issue; given a way to make the path too long
+		 * the user may execute the wrong program.
+		 */
+		if (lp + ln + 2 > sizeof(buf)) {
+			(void)write(STDERR_FILENO, "exectp: ", 8);
+			(void)write(STDERR_FILENO, p, lp);
+			(void)write(STDERR_FILENO, ": path too long\n", 16);
+			continue;
+		}
+		bcopy(p, buf, lp);
+		buf[lp] = '/';
+		bcopy(name, buf + lp + 1, ln);
+		buf[lp + ln + 1] = '\0';
+
+retry:		(void)execve(bp, argv, envp);
+		switch(errno) {
+		case EACCES:
+			eacces = 1;
+			break;
+		case ENOENT:
+			break;
+		case ENOEXEC:
+			for (cnt = 0; argv[cnt]; ++cnt);
+			if ((cnt + 2) * sizeof(char *) > memsize) {
+				memsize = (cnt + 2) * sizeof(char *);
+				if ((memp = realloc(memp, memsize)) == NULL) {
+					memsize = 0;
+					goto done;
+				}
+			}
+			memp[0] = "sh";
+			memp[1] = bp;
+			bcopy(argv + 1, memp + 2, cnt * sizeof(char *));
+			(void)execve(_PATH_BSHELL, memp, envp);
+			goto done;
+		case ETXTBSY:
+			if (etxtbsy < 3)
+				(void)sleep(++etxtbsy);
+			goto retry;
+		default:
+			goto done;
+		}
+	}
+	if (eacces)
+		errno = EACCES;
+	else if (!errno)
+		errno = ENOENT;
+done:	if (path)
+		free(path);
+	return (-1);
+}
+
+#endif


>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199508131617.SAA13412>