crond: implement support for setting PATH in crontab files

It's very inconvenient for a cron user not to be able to set a
"personal" PATH for their cron jobs, as is possible with other crons

function                                             old     new   delta
load_crontab                                         868     942     +74
.rodata                                           104878  104884      +6
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 80/0)               Total: 80 bytes

Signed-off-by: Paul Fox <pgf@foxharp.boston.ma.us>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Paul Fox 2022-03-07 11:35:28 -05:00 committed by Denys Vlasenko
parent 9b6f44e040
commit 52a7bf6fa6

View File

@ -125,6 +125,7 @@ typedef struct CronLine {
char *cl_mailto; /* whom to mail results, may be NULL */ char *cl_mailto; /* whom to mail results, may be NULL */
#endif #endif
char *cl_shell; char *cl_shell;
char *cl_path;
/* ordered by size, not in natural order. makes code smaller: */ /* ordered by size, not in natural order. makes code smaller: */
char cl_Dow[7]; /* 0-6, beginning sunday */ char cl_Dow[7]; /* 0-6, beginning sunday */
char cl_Mons[12]; /* 0-11 */ char cl_Mons[12]; /* 0-11 */
@ -421,6 +422,7 @@ static void load_crontab(const char *fileName)
char *mailTo = NULL; char *mailTo = NULL;
#endif #endif
char *shell = NULL; char *shell = NULL;
char *path = NULL;
delete_cronfile(fileName); delete_cronfile(fileName);
@ -470,7 +472,12 @@ static void load_crontab(const char *fileName)
shell = xstrdup(&tokens[0][6]); shell = xstrdup(&tokens[0][6]);
continue; continue;
} }
//TODO: handle HOME= too? "man crontab" says: if (is_prefixed_with(tokens[0], "PATH=")) {
free(path);
path = xstrdup(&tokens[0][5]);
continue;
}
//TODO: handle HOME= too? Better yet, handle arbitrary ENVVARs? "man crontab" says:
//name = value //name = value
// //
//where the spaces around the equal-sign (=) are optional, and any subsequent //where the spaces around the equal-sign (=) are optional, and any subsequent
@ -480,8 +487,8 @@ static void load_crontab(const char *fileName)
// //
//Several environment variables are set up automatically by the cron(8) daemon. //Several environment variables are set up automatically by the cron(8) daemon.
//SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd //SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd
//line of the crontab's owner. HOME and SHELL may be overridden by settings //line of the crontab's owner. HOME, SHELL, and PATH may be overridden by
//in the crontab; LOGNAME may not. //settings in the crontab; LOGNAME may not.
#if ENABLE_FEATURE_CROND_SPECIAL_TIMES #if ENABLE_FEATURE_CROND_SPECIAL_TIMES
if (tokens[0][0] == '@') { if (tokens[0][0] == '@') {
@ -567,6 +574,7 @@ static void load_crontab(const char *fileName)
line->cl_mailto = xstrdup(mailTo); line->cl_mailto = xstrdup(mailTo);
#endif #endif
line->cl_shell = xstrdup(shell); line->cl_shell = xstrdup(shell);
line->cl_path = xstrdup(path);
/* copy command */ /* copy command */
line->cl_cmd = xstrdup(tokens[5]); line->cl_cmd = xstrdup(tokens[5]);
pline = &line->cl_next; pline = &line->cl_next;
@ -653,21 +661,22 @@ static void safe_setenv(char **pvar_val, const char *var, const char *val)
} }
#endif #endif
static void set_env_vars(struct passwd *pas, const char *shell) static void set_env_vars(struct passwd *pas, const char *shell, const char *path)
{ {
/* POSIX requires crond to set up at least HOME, LOGNAME, PATH, SHELL. /* POSIX requires crond to set up at least HOME, LOGNAME, PATH, SHELL.
* We assume crond inherited suitable PATH.
*/ */
#if SETENV_LEAKS #if SETENV_LEAKS
safe_setenv(&G.env_var_logname, "LOGNAME", pas->pw_name); safe_setenv(&G.env_var_logname, "LOGNAME", pas->pw_name);
safe_setenv(&G.env_var_user, "USER", pas->pw_name); safe_setenv(&G.env_var_user, "USER", pas->pw_name);
safe_setenv(&G.env_var_home, "HOME", pas->pw_dir); safe_setenv(&G.env_var_home, "HOME", pas->pw_dir);
safe_setenv(&G.env_var_shell, "SHELL", shell); safe_setenv(&G.env_var_shell, "SHELL", shell);
if (path) safe_setenv(&G.env_var_shell, "PATH", path);
#else #else
xsetenv("LOGNAME", pas->pw_name); xsetenv("LOGNAME", pas->pw_name);
xsetenv("USER", pas->pw_name); xsetenv("USER", pas->pw_name);
xsetenv("HOME", pas->pw_dir); xsetenv("HOME", pas->pw_dir);
xsetenv("SHELL", shell); xsetenv("SHELL", shell);
if (path) xsetenv("PATH", path);
#endif #endif
} }
@ -701,7 +710,7 @@ fork_job(const char *user, int mailFd, CronLine *line, bool run_sendmail)
shell = line->cl_shell ? line->cl_shell : G.default_shell; shell = line->cl_shell ? line->cl_shell : G.default_shell;
prog = run_sendmail ? SENDMAIL : shell; prog = run_sendmail ? SENDMAIL : shell;
set_env_vars(pas, shell); set_env_vars(pas, shell, NULL); /* don't use crontab's PATH for sendmail */
sv_logmode = logmode; sv_logmode = logmode;
pid = vfork(); pid = vfork();
@ -845,7 +854,7 @@ static pid_t start_one_job(const char *user, CronLine *line)
/* Prepare things before vfork */ /* Prepare things before vfork */
shell = line->cl_shell ? line->cl_shell : G.default_shell; shell = line->cl_shell ? line->cl_shell : G.default_shell;
set_env_vars(pas, shell); set_env_vars(pas, shell, line->cl_path);
/* Fork as the user in question and run program */ /* Fork as the user in question and run program */
pid = vfork(); pid = vfork();