From b7f5f88d0f162b66c7d184286d9eeb5f2b1ba9fe Mon Sep 17 00:00:00 2001 From: Thibault Godouet Date: Sat, 9 Jan 2010 15:28:31 +0000 Subject: [PATCH] - Rewrote the env var management code with Vixie cron compatibility in mind * new env_list_t type with associated functions * build some char **envp arrays and pass them to execle() * cleaner management of env var defined several times * don't inherit fcron's environment when running a job but build the environment from scratch * list the env var as X-Cron-Env headers in the job output emails * add Auto-Submitted headers to the job output emails (as cronie, i.e. cron on RedHat) * added a default path for jobs: /usr/bin:/bin (as Vixie cron) * ignore not only USER but LOGNAME assigments as well * added my_unsetenv() and my_setenv_overwrite() to make the direct manipulation (without env_list_t) of env var easier - no longer try to run a job with execlp("sh", ...) if execl(curshell, ...) fails (as vixie cron) - improved u_list: realloc() instead of free() followed by a calloc() - debug_opt is now defined by log.c (instead of fcron.c/fcrontab.c/...) --- Makefile.in | 6 +- conf.c | 39 ++-- config.h.in | 13 +- convert-fcrontab.c | 22 +- database.c | 55 ++--- doc/en/todo.sgml | 8 +- env_list.c | 107 +++++++--- env_list.h | 12 +- fcron.c | 9 +- fcron.h | 1 - fcrondyn.c | 5 - fcrondyn.h | 1 - fcronsighup.c | 8 +- fcrontab.c | 6 - fcrontab.h | 1 - fileconf.c | 32 +-- global.h | 8 +- job.c | 510 ++++++++++++++++++++++++--------------------- job.h | 7 +- log.c | 7 + log.h | 2 + read_string.c | 2 +- save.c | 7 +- subs.c | 81 ++++++- subs.h | 3 + temp_file.c | 4 +- u_list.c | 21 +- 27 files changed, 539 insertions(+), 438 deletions(-) diff --git a/Makefile.in b/Makefile.in index 4099791..fea8fc6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -73,10 +73,10 @@ CFLAGS += $(OPTIM) $(OPTION) $(DEFS) $(CPPFLAGS) ifeq ($(FCRONDYN), 1) LIBOBJS := socket.o $(LIBOBJS) endif -OBJSD := fcron.o subs.o save.o temp_file.o log.o database.o job.o conf.o u_list.o exe_list.o lavg_list.o $(LIBOBJS) -OBJSTAB := fcrontab.o subs.o save.o temp_file.o log.o fileconf.o allow.o read_string.o +OBJSD := fcron.o subs.o save.o temp_file.o log.o database.o job.o conf.o u_list.o exe_list.o lavg_list.o env_list.o $(LIBOBJS) +OBJSTAB := fcrontab.o subs.o save.o temp_file.o log.o fileconf.o allow.o read_string.o u_list.o env_list.o OBJSDYN := fcrondyn.o subs.o log.o allow.o read_string.o -OBJCONV := convert-fcrontab.o subs.o save.o log.o +OBJCONV := convert-fcrontab.o subs.o save.o log.o u_list.o env_list.o OBJSIG := fcronsighup.o subs.o log.o allow.o HEADERSALL := config.h $(SRCDIR)/global.h $(SRCDIR)/log.h $(SRCDIR)/subs.h $(SRCDIR)/save.h $(SRCDIR)/option.h $(SRCDIR)/dyncom.h diff --git a/conf.c b/conf.c index 84575e6..9dce1db 100644 --- a/conf.c +++ b/conf.c @@ -430,8 +430,6 @@ read_file(const char *file_name, cf_t *cf) { FILE *ff = NULL; cl_t *cl = NULL; - env_t *env = NULL; - char buf[LINE_LEN]; long int bufi = 0; time_t t_save = 0; uid_t runas = 0; @@ -531,7 +529,6 @@ read_file(const char *file_name, cf_t *cf) #endif debug("User %s Entry", file_name); - bzero(buf, sizeof(buf)); /* get version of fcrontab file: it permits to daemon not to load * a file which he won't understand the syntax, for example @@ -573,6 +570,9 @@ read_file(const char *file_name, cf_t *cf) goto err; } + if ( cf->cf_env_list == NULL ) + cf->cf_env_list = env_list_init(); + Alloc(cl, cl_t); /* main loop : read env variables, and lines */ while ( read_type(fileno(ff), &type, &size) == OK ) { @@ -581,12 +581,24 @@ read_file(const char *file_name, cf_t *cf) case S_ENVVAR_T: /* read a env variable and add it to the env var list */ - Alloc(env, env_t); - Read_strn(env->e_val, size, "Error while reading env var"); - /* Read_strn go to "err" on error */ - debug(" Env: \"%s\"", env->e_val ); - env->e_next = cf->cf_env_base; - cf->cf_env_base = env; + { + char *envvar = NULL; + + /* Read_strn go to "err" on error */ + Read_strn(envvar, size, "Error while reading env var"); + debug(" Env: \"%s\"", envvar ); + /* we do not allow USER or LOGNAME assignment. + * this was already checked by fcrontab, but we check again + * just in case... */ + if ( strcmp_until(envvar, "USER", '=') == 0 + || strcmp_until(envvar, "LOGNAME", '=') == 0 ) { + error("USER or LOGNAME assignement is not allowed: ignored."); + } + else { + env_list_putenv(cf->cf_env_list, envvar, 1); + } + free(envvar); + } break; case S_TZDIFF_T: @@ -904,8 +916,6 @@ delete_file(const char *user_name) cf_t *prev_file = NULL; cl_t *line; cl_t *cur_line; - env_t *env = NULL; - env_t *cur_env = NULL; struct job_t *j = NULL; struct job_t *prev_j; int i, k; @@ -1021,12 +1031,7 @@ delete_file(const char *user_name) prev_file->cf_next = file->cf_next; /* free env variables */ - cur_env = file->cf_env_base; - while ( (env = cur_env) != NULL ) { - cur_env = env->e_next; - free(env->e_val); - free(env); - } + env_list_destroy(file->cf_env_list); /* finally free file itself */ free(file->cf_user); diff --git a/config.h.in b/config.h.in index fe99202..16a0d05 100644 --- a/config.h.in +++ b/config.h.in @@ -34,6 +34,8 @@ /* beginning of configurable stuff ********************************** */ +#define DEFAULT_JOB_PATH "/usr/bin:/bin" /* Default PATH to use when running jobs*/ + #define SENDMAIL_ARGS "-Ffcron", "-odi" /* args of mail command */ /* *** time *** */ @@ -57,9 +59,16 @@ * to prevent infinite loop on corrupted lines */ /* *** memory *** */ +#define ENVVAR_INITIAL_SIZE 10 /* initial number of possible env var for a job + * if more env var are required, + * fcron will have to realloc() more memory */ +#define ENVVAR_GROW_SIZE 5 /* this is the number of entries that will be + * added to env_list each time it has to grow + * up */ + #define EXE_INITIAL_SIZE 6 /* initial number of possible running job * if more jobs have to be run simultaneously, - * fcron will have to calloc() more memory */ + * fcron will have to realloc() more memory */ #define EXE_GROW_SIZE 5 /* this is the number of entries that will be * added to exe_array each time it has to grow * up */ @@ -69,7 +78,7 @@ * non-serially each time a serial job is added */ #define SERIAL_INITIAL_SIZE 10 /* initial number of possible serial job. If * more jobs have to be in queue simultaneously, - * fcron will have to calloc() more memory */ + * fcron will have to realloc() more memory */ #define SERIAL_GROW_SIZE 10 /* this is the number of entries that will be * added to serial queue each time it has to grow * up */ diff --git a/convert-fcrontab.c b/convert-fcrontab.c index e306e17..18cbedd 100644 --- a/convert-fcrontab.c +++ b/convert-fcrontab.c @@ -42,7 +42,6 @@ char foreground = 1; pid_t daemon_pid = 0; uid_t rootuid = 0; gid_t rootgid = 0; -char debug_opt = 0; char dosyslog = 1; void @@ -114,8 +113,6 @@ delete_file(cf_t *file) { cl_t *line = NULL; cl_t *cur_line = NULL; - env_t *env = NULL; - env_t *cur_env = NULL; /* free lines */ @@ -129,12 +126,7 @@ delete_file(cf_t *file) } /* free env variables */ - cur_env = file->cf_env_base; - while ( (env = cur_env) != NULL ) { - cur_env = env->e_next; - free(env->e_val); - free(env); - } + env_list_destroy(file->cf_env_list); /* finally free file itself */ free(file->cf_user); @@ -150,7 +142,7 @@ convert_file(char *file_name) { cf_t *file = NULL; cl_t *line = NULL; - env_t *env = NULL; + char *env = NULL; FILE *f = NULL; char buf[LINE_LEN]; time_t t_save = 0; @@ -188,14 +180,10 @@ convert_file(char *file_name) error("could not get time and date of saving"); /* read env variables */ - Alloc(env, env_t); - while( (env->e_val = read_str(f, buf, sizeof(buf))) != NULL ) { - env->e_next = file->cf_env_base; - file->cf_env_base = env; - Alloc(env, env_t); + while( (env = read_str(f, buf, sizeof(buf))) != NULL ) { + env_list_putenv(file->cf_env_list, env, 1); + free_safe(env); } - /* free last alloc : unused */ - free(env); /* read lines */ Alloc(line, cl_t); diff --git a/database.c b/database.c index 65d8afd..01f77a7 100644 --- a/database.c +++ b/database.c @@ -41,11 +41,6 @@ void run_serial_job(void); void run_lavg_job(lavg_t *l); void run_queue_job(cl_t *line); - -#if ! defined(HAVE_SETENV) || ! defined(HAVE_UNSETENV) -char env_tz[PATH_LEN]; -#endif - void test_jobs(void) /* determine which jobs need to be run, and run them. */ @@ -85,21 +80,14 @@ switch_timezone(const char *orig_tz, const char* dest_tz) /* Returns 1 if this function has switched the timezone, 0 otherwise */ { char *current_tz = getenv("TZ"); - if ( dest_tz != NULL && - (current_tz == NULL || strcmp(dest_tz, current_tz) != 0)) { -#ifdef HAVE_SETENV - if ( setenv("TZ", dest_tz, 1) < 0 ) - error_e("could not set env var TZ to %s", dest_tz); -#else - snprintf(env_tz, sizeof(env_tz) - 1, "TZ=%s", dest_tz); - env_tz[sizeof(env_tz)-1] = '\0'; - if ( putenv(env_tz) < 0 ) - error_e("could not set env var TZ to %s", dest_tz); -#endif - return 1; - } - else - return 0; + + if ( dest_tz != NULL && + (current_tz == NULL || strcmp(dest_tz, current_tz) != 0)) { + my_setenv_overwrite("TZ", dest_tz); + return 1; + } + else + return 0; } void @@ -108,25 +96,10 @@ switch_back_timezone(const char *orig_tz) * otherwise, sets TZ to orig_tz */ { if ( orig_tz == NULL) { -#ifdef HAVE_UNSETENV - unsetenv("TZ"); -#else - snprintf(env_tz, sizeof(env_tz) - 1, "TZ="); - env_tz[sizeof(env_tz)-1] = '\0'; - if ( putenv(env_tz) < 0 ) - error_e("could not flush env var TZ"); -#endif + my_unsetenv("TZ"); } else { -#ifdef HAVE_SETENV - if ( setenv("TZ", orig_tz, 1) < 0 ) - error_e("could not set env var TZ to %s", orig_tz); -#else - snprintf(env_tz, sizeof(env_tz) - 1, "TZ=%s", orig_tz); - env_tz[sizeof(env_tz)-1] = '\0'; - if ( putenv(env_tz) < 0 ) - error_e("could not set env var TZ to %s", orig_tz); -#endif + my_setenv_overwrite("TZ", orig_tz); } } @@ -1285,6 +1258,7 @@ mail_notrun(cl_t *line, char context, struct tm *since) FILE *mailf = 0; struct tm *time2 = NULL, time; int tz_changed = 0; + char **sendmailenv = NULL; switch ( pid = fork() ) { case -1: @@ -1322,7 +1296,7 @@ mail_notrun(cl_t *line, char context, struct tm *since) memcpy(&time, time2, sizeof(time)); /* create a temp file, and write in it the message to send */ - mailf = create_mail(line, "Non-execution of fcron job"); + mailf = create_mail(line, "Non-execution of fcron job", NULL); switch ( context ) { case SYSDOWN: @@ -1362,11 +1336,10 @@ mail_notrun(cl_t *line, char context, struct tm *since) } /* become user (for security reasons) */ - if (change_user(line) < 0) - return ; + change_user_setup_env(line, &sendmailenv, NULL, NULL, NULL); /* then, send mail */ - launch_mailer(line, mailf); + launch_mailer(line, mailf, sendmailenv); /* we should not come here : launch_mailer does not return */ die("mail_notrun : launch_mailer failed"); diff --git a/doc/en/todo.sgml b/doc/en/todo.sgml index 452eb32..f8f7a62 100644 --- a/doc/en/todo.sgml +++ b/doc/en/todo.sgml @@ -24,6 +24,12 @@ A copy of the license is included in gfdl.sgml. High priority + + replace all free() by free_safe() + + + check fcron for memory leaks using library + Test (and use ?) docbook2x-man -- xlstproc ? cf http://antoine.ginies.free.fr/docbook/ch09.html @@ -129,4 +135,4 @@ mode: sgml sgml-parent-document:("fcron-doc.sgml" "book" "chapter" "sect1" "") End: --> - \ No newline at end of file + diff --git a/env_list.c b/env_list.c index 50fe3df..3ed3d23 100644 --- a/env_list.c +++ b/env_list.c @@ -34,7 +34,8 @@ env_list_t *env_list_init(void) { - return (env_list_t *) u_list_init(sizeof(env_t), 20, 10); + return (env_list_t *) u_list_init(sizeof(env_t), + ENVVAR_INITIAL_SIZE, ENVVAR_GROW_SIZE); } env_list_t * @@ -44,12 +45,11 @@ env_list_copy(env_list_t *list) env_t *e = NULL; /* copy the list structure */ - new_list = (env_list_t *) u_list_copy(u_list_t *list); + new_list = (env_list_t *) u_list_copy( (u_list_t *) list); /* for now the new list points to the same data strings - duplicate them */ - for ( e = env_list_first(new_list) ; e != NULL ; e = env_list_next(e) ) { - e->e_name = strdup2(e->e_name); - e->e_value = strdup2(e->e_value); + for ( e = env_list_first(new_list) ; e != NULL ; e = env_list_next(new_list) ) { + e->e_envvar = strdup2(e->e_envvar); } return new_list; @@ -59,35 +59,89 @@ env_list_copy(env_list_t *list) env_t * env_list_setenv(env_list_t *list, char *name, char *value, int overwrite) { - env_t e = { NULL, NULL }; + env_t e = { NULL }; env_t *c = NULL; + size_t len = strlen(name)+1+strlen(value)+1; /* 1 for '=', 1 for '\0' */ + + /* sanity check */ + if ( name == NULL || name[0] == '\0' ) + return NULL; /* check if a var 'name' already exists */ - for ( c = env_list_first(list) ; c != NULL ; c = env_list_next(c) ) { - if ( strcmp(name, c->e_name) == 0 ) { + for ( c = env_list_first(list) ; c != NULL ; c = env_list_next(list) ) { + if ( strcmp_until(name, c->e_envvar, '=') == 0 ) { /* variable already set: overwrite the value if asked - * and return that entry */ + * and return that entry */ if ( overwrite == 1 ) { - free_safe(c->e_value); - c->e_value = strdup2(value); - env_list_end_iteration(list); + c->e_envvar = realloc(c->e_envvar, len); + if ( c->e_envvar == NULL ) + die_e("Could not allocate memory to modify env var"); + snprintf(c->e_envvar, len, "%s=%s", name, value); } + env_list_end_iteration(list); return c; - } + } } /* if we're here we didn't find a var called 'name': add it */ - e.e_name = strdup2(name); - e.e_value = strdup2(value); + e.e_envvar = calloc(1, len); + snprintf(e.e_envvar, len, "%s=%s", name, value); return (env_t *) u_list_add( (u_list_t *) list, (u_list_entry_t *) &e); } env_t * -env_list_add(env_list_t *list, env_t *e) +env_list_putenv(env_list_t *list, char *envvar, int overwrite) { + env_t e = { NULL }; + env_t *c = NULL; + size_t len = strlen(envvar) + 1; /* +1 for the terminating '\0' */ + + /* sanity check */ + if ( envvar == NULL || envvar[0] == '\0' ) + return NULL; + + /* check if a var 'name' already exists */ + for ( c = env_list_first(list) ; c != NULL ; c = env_list_next(list) ) { + if ( strcmp_until(envvar, c->e_envvar, '=') == 0 ) { + /* variable already set: overwrite the value if asked + * and return that entry */ + if ( overwrite == 1 ) { + c->e_envvar = realloc(c->e_envvar, len); + if ( c->e_envvar == NULL ) + die_e("Could not allocate memory to modify env var"); + memcpy(c->e_envvar, envvar, len); /* includes the final '\0' */ + } + env_list_end_iteration(list); + return c; + } + } + + /* if we're here we didn't find a var called 'name': add it */ + e.e_envvar = strdup2(envvar); return (env_t *) u_list_add( (u_list_t *) list, (u_list_entry_t *) &e); } +char * +env_list_getenv(env_list_t *list, char *name) +{ + env_t *c = NULL; + + /* sanity check */ + if ( name == NULL || name[0] == '\0' ) + return NULL; + + for ( c = env_list_first(list) ; c != NULL ; c = env_list_next(list) ) { + if ( strcmp_until(name, c->e_envvar, '=') == 0 ) { + /* found the var: return the pointer to the value */ + env_list_end_iteration(list); + return (c->e_envvar+strlen(name)+1); /* +1 for '=' */ + } + } + + /* var 'name' not found */ + return NULL; +} + env_t * env_list_first(env_list_t *list) { @@ -115,9 +169,8 @@ env_list_destroy(env_list_t *list) /* make sure the iteration below won't fail in case one was already iterating */ env_list_end_iteration(list); /* free the data in the env_t entries */ - for ( c = env_list_first(list) ; c != NULL ; c = env_list_next(c) ) { - free_safe(c->e_name); - free_safe(c->e_value); + for ( c = env_list_first(list) ; c != NULL ; c = env_list_next(list) ) { + free_safe(c->e_envvar); } /* free the actual list structure */ return (env_list_t *) u_list_destroy((u_list_t *) list); @@ -130,21 +183,19 @@ env_list_export_envp(env_list_t *list) env_t *c = NULL; int i = 0; char **envp = NULL; - size_t len = 0; - envp = calloc(l->num_entries + 1); /* +1 for the end-of-list NULL */ + envp = calloc(list->num_entries + 1, sizeof(char *)); /* +1 for the end-of-list NULL */ if ( envp == NULL ) die_e("Could not allocate memory for the environment"); - for ( c=env_list_first(list), i=0 ; c != NULL && i < l->num_entries ; c=env_list_next(c), i++ ) { - len = strlen(c->e_name) + strlen(c->e_value) + 2; /* +1 for =, +1 for \0 */ - envp[i] = calloc(1, len); - if ( envp[i] == NULL ) - die_e("Could not allocaed memory for an environment entry"); - snprintf(envp[i], len, "%s=%s", c->e_name, c->e_value); + for ( c=env_list_first(list), i=0 ; c != NULL && i < list->num_entries ; + c=env_list_next(list), i++ ) { + envp[i] = strdup2(c->e_envvar); + if ( envp[i] == NULL ) + die_e("Could not allocate memory for an environment entry"); } /* add a NULL as a end-of-list marker */ - envp[ (l->num_entries + 1 ) - 1] = NULL; + envp[ (list->num_entries + 1 ) - 1] = NULL; return envp; } diff --git a/env_list.h b/env_list.h index 7f9e2c7..4df3070 100644 --- a/env_list.h +++ b/env_list.h @@ -33,10 +33,9 @@ #include "u_list.h" -/* Entry to describe one job being executed */ +/* One environment variable assignation */ typedef struct env_t { - char *e_name; - char *e_value; + char *e_envvar; } env_t; typedef struct u_list_t env_list_t; @@ -44,13 +43,14 @@ typedef struct u_list_t env_list_t; /* functions prototypes */ extern env_list_t *env_list_init(void); extern env_list_t *env_list_copy(env_list_t *list); -extern env_t *env_list_setenv(env_list_t *list, char *name, char *value, int overwrite); -extern env_t *env_list_add(env_list_t *list, env_t *e); /* WARNING: - These functions are NOT re-entrant. * i.e. there should always be a unique iteration loop based on * u_list_first()/u_list_next() running at any one time in the code - * - add_replace() and export_envp() use an iteration internally + * - setenv, putenv, getenv use an iteration internally * so they cannot be called when already iterating */ +extern env_t *env_list_setenv(env_list_t *list, char *name, char *value, int overwrite); +extern env_t *env_list_putenv(env_list_t *list, char *envvar, int overwrite); +extern char *env_list_getenv(env_list_t *list, char *name); extern env_t *env_list_first(env_list_t *list); extern env_t *env_list_next(env_list_t *list); extern void env_list_end_iteration(env_list_t *list); diff --git a/fcron.c b/fcron.c index 3b4e1de..0395dd7 100644 --- a/fcron.c +++ b/fcron.c @@ -49,14 +49,7 @@ int parseopt(int argc, char *argv[]); void get_lock(void); void create_spooldir(char *dir); - - /* command line options */ -#ifdef DEBUG -char debug_opt = 1; /* set to 1 if we are in debug mode */ -#else -char debug_opt = 0; /* set to 1 if we are in debug mode */ -#endif #ifdef FOREGROUND char foreground = 1; /* set to 1 when we are on foreground, else 0 */ @@ -225,6 +218,8 @@ xexit(int exit_value) remove(pidfile); + exe_list_destroy(exe_list); + lavg_list_destroy(lavg_list); free_conf(); free(orig_tz_envvar); diff --git a/fcron.h b/fcron.h index 5a4c472..04a8c66 100644 --- a/fcron.h +++ b/fcron.h @@ -62,7 +62,6 @@ /* global variables */ extern time_t now; -extern char debug_opt; extern char foreground; extern char dosyslog; extern time_t first_sleep; diff --git a/fcrondyn.c b/fcrondyn.c index a3cbedb..fc87e98 100644 --- a/fcrondyn.c +++ b/fcrondyn.c @@ -53,11 +53,6 @@ int connect_fcron(void); int authenticate_user_password(int fd); /* command line options */ -#ifdef DEBUG -char debug_opt = 1; /* set to 1 if we are in debug mode */ -#else -char debug_opt = 0; /* set to 1 if we are in debug mode */ -#endif char *cmd_str = NULL; /* needed by log part : */ diff --git a/fcrondyn.h b/fcrondyn.h index d246e18..51c4bf7 100644 --- a/fcrondyn.h +++ b/fcrondyn.h @@ -30,7 +30,6 @@ #include "dyncom.h" /* global variables */ -extern char debug_opt; extern char dosyslog; extern pid_t daemon_pid; extern uid_t rootuid; diff --git a/fcronsighup.c b/fcronsighup.c index ffb2318..016627e 100644 --- a/fcronsighup.c +++ b/fcronsighup.c @@ -38,12 +38,6 @@ uid_t fcrontab_uid = 0; uid_t rootuid = 0; gid_t rootgid = 0; -#ifdef DEBUG -char debug_opt = 1; /* set to 1 if we are in debug mode */ -#else -char debug_opt = 0; /* set to 1 if we are in debug mode */ -#endif - /* needed by log part : */ char *prog_name = NULL; char foreground = 1; @@ -250,7 +244,7 @@ main(int argc, char **argv) /* check if user is allowed to use this program */ if ( ! (pass = getpwuid(uid)) ) die("user \"%s\" is not in passwd file. Aborting.", USERNAME); - cur_user = strdup(pass->pw_name); + cur_user = strdup2(pass->pw_name); if ( is_allowed(cur_user) ) { /* check if daemon is running */ diff --git a/fcrontab.c b/fcrontab.c index 09eae27..3b29c04 100644 --- a/fcrontab.c +++ b/fcrontab.c @@ -63,12 +63,6 @@ char reinstall_opt = 0; char ignore_prev = 0; int file_opt = 0; -#ifdef DEBUG -char debug_opt = 1; /* set to 1 if we are in debug mode */ -#else -char debug_opt = 0; /* set to 1 if we are in debug mode */ -#endif - /* uid/gid of users/groups * (we don't use the static UID or GID as we ask for user and group names * in the configure script) */ diff --git a/fcrontab.h b/fcrontab.h index 9723741..c5b0bc9 100644 --- a/fcrontab.h +++ b/fcrontab.h @@ -29,7 +29,6 @@ #include "global.h" /* global variables */ -extern char debug_opt; extern pid_t daemon_pid; extern char dosyslog; extern struct cf_t *file_base; diff --git a/fileconf.c b/fileconf.c index f1489a6..76d7348 100644 --- a/fileconf.c +++ b/fileconf.c @@ -178,6 +178,7 @@ read_file(char *filename) } Alloc(cf, cf_t); + cf->cf_env_list = env_list_init(); cf->cf_user = strdup2(user); default_line.cl_file = cf; default_line.cl_runas = strdup2(runas); @@ -283,7 +284,6 @@ read_env(char *ptr, cf_t *cf) * (remove blanks) */ { char name[LINE_LEN]; - env_t *env = NULL; int j=0; char *val = NULL; @@ -297,7 +297,7 @@ read_env(char *ptr, cf_t *cf) } name[j] = '\0'; - if ( name == '\0' ) + if ( name[0] == '\0' ) goto error; /* skip '=' and spaces around */ @@ -322,9 +322,9 @@ read_env(char *ptr, cf_t *cf) if (debug_opt) fprintf(stderr, " Env : '%s=%s'\n", name, val); - /* we ignore USER's assignment */ - if ( strcmp(name, "USER") == 0 ) { - fprintf(stderr, "%s:%d: USER assignement is not allowed: ignored.\n", + /* we ignore USER/LOGNAME's assignment */ + if ( strcmp(name, "USER") == 0 || strcmp(name, "LOGNAME") == 0 ) { + fprintf(stderr, "%s:%d: USER or LOGNAME assignement is not allowed: ignored.\n", file_name, line); return; } @@ -342,18 +342,11 @@ read_env(char *ptr, cf_t *cf) } else { - - Alloc(env, env_t); - - strncat(name, "=", sizeof(name) - strlen(name) - 1); - name[sizeof(name)-1]='\0'; - strncat(name,val,sizeof(name)-strlen(name)-1); - name[sizeof(name)-1]='\0'; - env->e_val = strdup2( name ); - env->e_next = cf->cf_env_base; - cf->cf_env_base = env; + env_list_setenv(cf->cf_env_list, name, val, 1); } + free_safe(val); + return; error: @@ -1659,8 +1652,6 @@ delete_file(const char *user_name) cf_t *prev_file = NULL; cl_t *line = NULL; cl_t *cur_line = NULL; - env_t *env = NULL; - env_t *cur_env = NULL; file = file_base; while ( file != NULL) { @@ -1695,12 +1686,7 @@ delete_file(const char *user_name) prev_file->cf_next = file->cf_next; /* free env variables */ - cur_env = file->cf_env_base; - while ( (env = cur_env) != NULL ) { - cur_env = env->e_next; - free(env->e_val); - free(env); - } + env_list_destroy(file->cf_env_list); /* finally free file itself */ free(file->cf_user); diff --git a/global.h b/global.h index fba2b01..41acd6d 100644 --- a/global.h +++ b/global.h @@ -131,6 +131,7 @@ #include "bitstring.h" /* bit arrays */ #include "option.h" /* manage fcrontab's options */ +#include "env_list.h" /* manage fcrontab's environment variable lists */ /* you should not change this (nor need to do it) */ #define ERR -1 @@ -178,16 +179,11 @@ #define debug if(debug_opt) Debug -typedef struct env_t { - char *e_val; /* env value */ - struct env_t *e_next; -} env_t ; - typedef struct cf_t { struct cf_t *cf_next; struct cl_t *cf_line_base; char *cf_user; /* user-name */ - struct env_t *cf_env_base; /* list of all env variables to set */ + env_list_t *cf_env_list; /* list of all parsed env var */ int cf_running; /* number of jobs running */ signed char cf_tzdiff; /* time diff between system and local hour */ #ifdef WITH_SELINUX diff --git a/job.c b/job.c index 426e52b..16d6e4f 100644 --- a/job.c +++ b/job.c @@ -28,7 +28,7 @@ #include "job.h" void sig_dfl(void); -void end_job(cl_t *line, int status, FILE *mailf, short mailpos); +void end_job(cl_t *line, int status, FILE *mailf, short mailpos, char **sendmailenv); void end_mailer(cl_t *line, int status); #ifdef HAVE_LIBPAM void die_mail_pame(cl_t *cl, int pamerrno, struct passwd *pas, char *str); @@ -39,31 +39,22 @@ int read_write_pipe(int fd, void *buf, size_t size, int action); int read_pipe(int fd, void *to, size_t size); int write_pipe(int fd, void *buf, size_t size); -#ifndef HAVE_SETENV -char env_user[PATH_LEN]; -char env_logname[PATH_LEN]; -char env_home[PATH_LEN]; -char env_shell[PATH_LEN]; -char env_tz[PATH_LEN]; -#endif - #ifdef WITH_SELINUX extern char **environ; #endif #ifdef HAVE_LIBPAM void -die_mail_pame(cl_t *cl, int pamerrno, struct passwd *pas, char *str) +die_mail_pame(cl_t *cl, int pamerrno, struct passwd *pas, char *str, env_list_t *env) /* log an error in syslog, mail user if necessary, and die */ { char buf[MAX_MSG]; - strncpy(buf, str, sizeof(buf)-1); - strncat(buf, " for '%s'", sizeof(buf)-strlen(buf)-1); - buf[sizeof(buf)-1]='\0'; + snprintf(buf, sizeof(buf), "%s for user '%s'", str, pas->pw_name); if (is_mail(cl->cl_option)) { - FILE *mailf = create_mail(cl, "Could not run fcron job"); + char **envp = env_list_export_envp(env); + FILE *mailf = create_mail(cl, "Could not run fcron job", NULL, envp); /* print the error in both syslog and a file, in order to mail it to user */ if (dup2(fileno(mailf), 1) != 1 || dup2(1, 2) != 2) @@ -76,16 +67,9 @@ die_mail_pame(cl_t *cl, int pamerrno, struct passwd *pas, char *str) pam_end(pamh, pamerrno); - /* Change running state to the user in question : it's safer to run the mail - * as user, not root */ - if (initgroups(pas->pw_name, pas->pw_gid) < 0) - die_e("initgroups failed: %s", pas->pw_name); - if (setgid(pas->pw_gid) < 0) - die("setgid failed: %s %d", pas->pw_name, pas->pw_gid); - if (setuid(pas->pw_uid) < 0) - die("setuid failed: %s %d", pas->pw_name, pas->pw_uid); + become_user(cl, pas, "/") - launch_mailer(cl, mailf); + launch_mailer(cl, mailf, envp); /* launch_mailer() does not return : we never get here */ } else @@ -93,74 +77,78 @@ die_mail_pame(cl_t *cl, int pamerrno, struct passwd *pas, char *str) } #endif -int -change_user(struct cl_t *cl) +void +become_user(struct cl_t *cl, struct passwd *pas, char *home) +/* Become the user who owns the job: change privileges, check PAM authorization, + * and change dir to HOME. */ { - struct passwd *pas; + +#ifndef RUN_NON_PRIVILEGED + if (pas == NULL) + die("become_user() called with a NULL struct passwd"); + + /* Change running state to the user in question */ + if (initgroups(pas->pw_name, pas->pw_gid) < 0) + die_e("initgroups failed: %s", pas->pw_name); + + if (setgid(pas->pw_gid) < 0) + die("setgid failed: %s %d", pas->pw_name, pas->pw_gid); + + if (setuid(pas->pw_uid) < 0) + die("setuid failed: %s %d", pas->pw_name, pas->pw_uid); +#endif /* not RUN_NON_PRIVILEGED */ + + /* make sure HOME is defined and change dir to it */ + if (chdir(home) != 0) { + error_e("Could not chdir to HOME dir '%s'. Trying to chdir to '/'.", home); + if (chdir("/") < 0) + die_e("Could not chdir to HOME dir /"); + } + +} + +void +setup_user_and_env(struct cl_t *cl, struct passwd *pas, + char ***sendmailenv, char ***jobenv, char **curshell, char **curhome) +/* Check PAM authorization, and setup the environment variables + * to run sendmail and to run the job itself. Change dir to HOME and check if SHELL is ok */ +/* (*curshell) and (*curhome) will be allocated and should thus be freed + * if curshell and curhome are not NULL. */ +/* Return the the two env var sets, the shell to use to execle() commands and the home dir */ + +{ + env_list_t *env_list = env_list_init(); + env_t *e = NULL; + char *path = NULL; + char *myshell = NULL; #ifdef HAVE_LIBPAM int retcode = 0; const char * const * env; #endif - /* Obtain password entry and change privileges */ + if (pas == NULL) + die("setup_user_and_env() called with a NULL struct passwd"); + + env_list_setenv(env_list, "USER", pas->pw_name, 1); + env_list_setenv(env_list, "LOGNAME", pas->pw_name, 1); + env_list_setenv(env_list, "HOME", pas->pw_dir, 1); + /* inherit fcron's PATH for sendmail. We will later change it to DEFAULT_JOB_PATH + * or a user defined PATH for the job itself */ + path = getenv("PATH"); + env_list_setenv(env_list, "PATH", ( path != NULL ) ? path : DEFAULT_JOB_PATH, 1); - errno = 0; - if ((pas = getpwnam(cl->cl_runas)) == NULL) - die_e("failed to get passwd fields for user \"%s\"", cl->cl_runas); - -#ifdef HAVE_SETENV - setenv("USER", pas->pw_name, 1); - setenv("LOGNAME", pas->pw_name, 1); - setenv("HOME", pas->pw_dir, 1); if (cl->cl_tz != NULL) - setenv("TZ", cl->cl_tz, 1); + env_list_setenv(env_list, "TZ", cl->cl_tz, 1); /* To ensure compatibility with Vixie cron, we don't use the shell defined * in /etc/passwd by default, but the default value from fcron.conf instead: */ - if ( *shell == '\0' ) - /* shell is empty, ie. not defined: use value from /etc/passwd */ - setenv("SHELL", pas->pw_shell, 1); + if ( shell != NULL && shell[0] != '\0' ) + /* default: use value from fcron.conf */ + env_list_setenv(env_list, "SHELL", shell, 1); else - /* default: use value from fcron.conf */ - setenv("SHELL", shell, 1); -#else - { - strcpy(env_user, "USER="); - strncat(env_user, pas->pw_name, sizeof(env_user)-5-1); - env_user[sizeof(env_user)-1]='\0'; - putenv( env_user ); - - strcpy(env_logname, "LOGNAME="); - strncat(env_logname, pas->pw_name, sizeof(env_logname)-8-1); - env_logname[sizeof(env_logname)-1]='\0'; - putenv( env_logname ); - - strcpy(env_home, "HOME="); - strncat(env_home, pas->pw_dir, sizeof(env_home)-5-1); - env_home[sizeof(env_home)-1]='\0'; - putenv( env_home ); - - if (cl->cl_tz != NULL) { - strcpy(env_tz, "TZ="); - strncat(env_tz, pas->pw_dir, sizeof(env_tz)-3-1); - env_tz[sizeof(env_tz)-1]='\0'; - putenv( env_tz ); - } + /* shell is empty, ie. not defined: fail back to /etc/passwd's value */ + env_list_setenv(env_list, "SHELL", pas->pw_shell, 1); - strcpy(env_shell, "SHELL="); - /* To ensure compatibility with Vixie cron, we don't use the shell defined - * in /etc/passwd by default, but the default value from fcron.conf instead: */ - if ( *shell == '\0' ) - /* shell is empty, ie. not defined: use value from /etc/passwd */ - strncat(env_shell, pas->pw_shell, sizeof(env_shell)-6-1); - else - /* default: use value from fcron.conf */ - strncat(env_shell, shell, sizeof(env_shell)-6-1); - env_shell[sizeof(env_shell)-1]='\0'; - putenv( env_shell ); - } -#endif /* HAVE_SETENV */ - -#ifdef HAVE_LIBPAM +#if ( ! defined(RUN_NON_PRIVILEGED)) && defined(HAVE_LIBPAM) /* Open PAM session for the user and obtain any security credentials we might need */ @@ -172,41 +160,94 @@ change_user(struct cl_t *cl) * we must set auth to pam_permit. */ retcode = pam_authenticate(pamh, PAM_SILENT); if (retcode != PAM_SUCCESS) die_mail_pame(cl, retcode, pas, - "Could not authenticate PAM user"); + "Could not authenticate PAM user", env_list); retcode = pam_acct_mgmt(pamh, PAM_SILENT); /* permitted access? */ if (retcode != PAM_SUCCESS) die_mail_pame(cl, retcode, pas, - "Could not init PAM account management"); + "Could not init PAM account management", env_list); retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED | PAM_SILENT); if (retcode != PAM_SUCCESS) die_mail_pame(cl, retcode, pas, - "Could not set PAM credentials"); + "Could not set PAM credentials", env_list); retcode = pam_open_session(pamh, PAM_SILENT); if (retcode != PAM_SUCCESS) die_mail_pame(cl, retcode, pas, - "Could not open PAM session"); + "Could not open PAM session", env_list); - env = (const char * const *) pam_getenvlist(pamh); - while (env && *env) { - if (putenv((char*) *env)) die_e("Could not copy PAM environment"); - env++; + for (env = (const char * const *)pam_getenvlist(pamh); env && *env; env++) { + env_list_putenv(env_list, *env, 1); } /* Close the log here, because PAM calls openlog(3) and our log messages could go to the wrong facility */ xcloselog(); -#endif /* USE_PAM */ - - /* Change running state to the user in question */ - if (initgroups(pas->pw_name, pas->pw_gid) < 0) - die_e("initgroups failed: %s", pas->pw_name); +#endif /* ( ! defined(RUN_NON_PRIVILEGED)) && defined(HAVE_LIBPAM) */ + + /* export the environment for sendmail before we apply user customization */ + if (sendmailenv != NULL) + *sendmailenv = env_list_export_envp(env_list); + + /* Now add user customizations to the environment to form jobenv */ + + if (jobenv != NULL) { + + /* Make sure we don't keep fcron daemon's PATH (which we used for sendmail) */ + env_list_setenv(env_list, "PATH", DEFAULT_JOB_PATH, 1); + + for ( e = env_list_first(cl->cl_file->cf_env_list); e != NULL; + e = env_list_next(cl->cl_file->cf_env_list) ) { + env_list_putenv(env_list, e->e_envvar, 1); + } + + /* make sure HOME is defined */ + env_list_putenv(env_list, "HOME=/", 0); /* don't overwrite if already defined */ + if ( curhome != NULL ) { + (*curhome) = strdup2(env_list_getenv(env_list, "HOME")); + } + + /* check that SHELL is valid */ + myshell = env_list_getenv(env_list, "SHELL"); + if ( myshell == NULL || myshell[0] == '\0' ) { + myshell = shell; + } + else if ( access(myshell, X_OK) != 0 ) { + if (errno == ENOENT) + error("shell \"%s\" : no file or directory. SHELL set to %s", + myshell, shell); + else + error_e("shell \"%s\" not valid : SHELL set to %s", + myshell, shell); + + myshell = shell; + } + env_list_setenv(env_list, "SHELL", myshell, 1); + if ( curshell != NULL ) + *curshell = strdup2(myshell); + + if (jobenv != NULL) + *jobenv = env_list_export_envp(env_list); + + env_list_destroy(env_list); - if (setgid(pas->pw_gid) < 0) - die("setgid failed: %s %d", pas->pw_name, pas->pw_gid); - - if (setuid(pas->pw_uid) < 0) - die("setuid failed: %s %d", pas->pw_name, pas->pw_uid); + } - return(pas->pw_uid); } +void +change_user_setup_env(struct cl_t *cl, + char ***sendmailenv, char ***jobenv, char **curshell, char **curhome) +/* call setup_user_and_env() and become_user(). + * As a result, *curshell and *curhome will be allocated and should thus be freed + * if curshell and curhome are not NULL. */ +{ + struct passwd *pas; + + errno = 0; + pas = getpwnam(cl->cl_runas); + if ( pas == NULL ) + die_e("failed to get passwd fields for user \"%s\"", cl->cl_runas); + + setup_user_and_env(cl, pas, sendmailenv, jobenv, curshell, curhome); + become_user(cl, pas, *curhome); + free(*curhome); +} void sig_dfl(void) @@ -222,7 +263,7 @@ sig_dfl(void) FILE * -create_mail(cl_t *line, char *subject) +create_mail(cl_t *line, char *subject, char **env) /* create a temp file and write in it a mail header */ { /* create temporary file for stdout and stderr of the job */ @@ -230,15 +271,12 @@ create_mail(cl_t *line, char *subject) FILE *mailf = fdopen(mailfd, "r+"); char hostname[USER_NAME_LEN]; /* is this a complete mail address ? (ie. with a "@", not only a username) */ - char complete_adr = 0; - int i; + char add_hostname = 0; + int i = 0; if ( mailf == NULL ) die_e("Could not fdopen() mailfd"); - /* write mail header */ - fprintf(mailf, "To: %s", line->cl_mailto); - #ifdef HAVE_GETHOSTNAME if (gethostname(hostname, sizeof(hostname)) != 0) { error_e("Could not get hostname"); @@ -249,26 +287,44 @@ create_mail(cl_t *line, char *subject) hostname[USER_NAME_LEN-1] = '\0'; /* check if mailto is a complete mail address */ - for ( i = 0 ; line->cl_mailto[i] != '\0' ; i++ ) { - if ( line->cl_mailto[i] == '@' ) { - complete_adr = 1; - break; - } - } - if ( ! complete_adr ) - fprintf(mailf, "@%s", hostname); + add_hostname = ( strchr(line->cl_mailto, '@') == NULL ) ? 1 : 0; } -#else +#else /* HAVE_GETHOSTNAME */ hostname[0] = '\0'; #endif /* HAVE_GETHOSTNAME */ + /* write mail header */ + if ( add_hostname ) + fprintf(mailf, "To: %s@%s\n", line->cl_mailto, hostname); + else + fprintf(mailf, "To: %s\n", line->cl_mailto); + if (subject) - fprintf(mailf, "\nSubject: fcron <%s@%s> %s: %s\n\n", line->cl_file->cf_user, + fprintf(mailf, "Subject: fcron <%s@%s> %s: %s\n", line->cl_file->cf_user, ( hostname[0] != '\0')? hostname:"?" , subject, line->cl_shell); else - fprintf(mailf, "\nSubject: fcron <%s@%s> %s\n\n", line->cl_file->cf_user, + fprintf(mailf, "Subject: fcron <%s@%s> %s\n", line->cl_file->cf_user, ( hostname[0] != '\0')? hostname:"?" , line->cl_shell); + /* Add headers so as automated systems can identify that this message + * is an automated one sent by fcron. + * That's useful for example for vacation auto-reply systems: no need + * to send such an automated response to fcron! */ + + /* The Auto-Submitted header is + * defined (and suggested by) RFC3834. */ + fprintf(mailf, "Auto-Submitted: auto-generated\n"); + + /* See environ(7) and execle(3) to get documentation on environ: + * it is an array of NULL-terminated strings, whose last entry is NULL */ + if ( env != NULL ) { + for ( i = 0 ; env[i] != NULL ; i++ ) { + fprintf(mailf, "X-Cron-Env: <%s>\n", env[i]); + } + } + + /* Final line return to end the header section: */ + fprintf(mailf, "\n"); return mailf; } @@ -393,41 +449,6 @@ run_job_grand_child_setup_nice(cl_t *line) } } - -void -run_job_grand_child_setup_env_var(cl_t *line, char **curshell) -/* set the env var from the fcrontab, change dir to HOME and check that SHELL is ok - * Return the final value of SHELL in curshell. */ -{ - env_t *env; - char *home; - - for ( env = line->cl_file->cf_env_base; env; env = env->e_next) - if ( putenv(env->e_val) != 0 ) - error("could not putenv()"); - - /* change dir to HOME */ - if ( (home = getenv("HOME")) != NULL ) - if (chdir(home) != 0) { - error_e("Could not chdir to HOME dir \"%s\"", home); - if (chdir("/") < 0) - die_e("Could not chdir to HOME dir /"); - } - - /* check that SHELL is valid */ - if ( (*curshell = getenv("SHELL")) == NULL ) - *curshell = shell; - else if ( access(*curshell, X_OK) != 0 ) { - if (errno == ENOENT) - error("shell \"%s\" : no file or directory. SHELL set to %s", - *curshell, shell); - else - error_e("shell \"%s\" not valid : SHELL set to %s", - *curshell, shell); - *curshell = shell; - } -} - int run_job(struct exe_t *exeent) /* fork(), redirect outputs to a temp file, and execl() the task. @@ -459,16 +480,20 @@ run_job(struct exe_t *exeent) case 0: /* child */ { - char *curshell; - FILE *mailf = NULL; - int status = 0; - int to_stdout = foreground && is_stdout(line->cl_option); - int pipe_fd[2]; - short int mailpos = 0; /* 'empty mail file' size */ + struct passwd *pas = NULL; + char **jobenv = NULL; + char **sendmailenv = NULL; + char *curshell = NULL; + char *curhome = NULL; + FILE *mailf = NULL; + int status = 0; + int to_stdout = foreground && is_stdout(line->cl_option); + int pipe_fd[2]; + short int mailpos = 0; /* 'empty mail file' size */ #ifdef WITH_SELINUX - int flask_enabled = is_selinux_enabled(); + int flask_enabled = is_selinux_enabled(); #endif - + /* // */ debug("run_job(): child: %s, output to %s, %s, %s\n", is_mail(line->cl_option) || is_mailzerolength(line->cl_option) ? @@ -478,6 +503,13 @@ run_job(struct exe_t *exeent) is_stdout(line->cl_option) ? "stdout" : "normal" ); /* // */ + errno = 0; + pas = getpwnam(line->cl_runas); + if ( pas == NULL ) + die_e("failed to get passwd fields for user \"%s\"", line->cl_runas); + + setup_user_and_env(line, pas, &sendmailenv, &jobenv, &curshell, &curhome); + /* close unneeded READ fd */ if ( close(pipe_pid_fd[0]) < 0 ) error_e("child: could not close(pipe_pid_fd[0])"); @@ -486,22 +518,20 @@ run_job(struct exe_t *exeent) if ( ! to_stdout && ( is_mail(line->cl_option) || is_mailzerolength(line->cl_option))){ /* we create the temp file (if needed) before change_user(), - * as temp_file() needs the root privileges */ + * as temp_file() needs root privileges */ /* if we run in foreground, stdout and stderr point to the console. * Otherwise, stdout and stderr point to /dev/null . */ - mailf = create_mail(line, NULL); + mailf = create_mail(line, NULL, jobenv); mailpos = ftell(mailf); if (pipe(pipe_fd) != 0) die_e("could not pipe() (job not executed)"); } - /* First, restore umask to default */ - umask (saved_umask); + become_user(line, pas, curhome); + free_safe(curhome); -#ifndef RUN_NON_PRIVILEGED - if (change_user(line) < 0) - exit(EXIT_ERR); -#endif + /* restore umask to default */ + umask (saved_umask); sig_dfl(); @@ -528,12 +558,12 @@ run_job(struct exe_t *exeent) /* grand child (child of the 2nd fork) */ /* the grand child does not use this pipe: close remaining fd */ - if ( close(pipe_pid_fd[1]) < 0 ) - error_e("grand child: could not close(pipe_pid_fd[1])"); + if ( close(pipe_pid_fd[1]) < 0 ) + error_e("grand child: could not close(pipe_pid_fd[1])"); - if ( ! to_stdout ) - /* note : the following closes the pipe */ - run_job_grand_child_setup_stderr_stdout(line, pipe_fd); + if ( ! to_stdout ) + /* note : the following closes the pipe */ + run_job_grand_child_setup_stderr_stdout(line, pipe_fd); foreground = 1; /* now, errors will be mailed to the user (or to /dev/null) */ @@ -542,9 +572,6 @@ run_job(struct exe_t *exeent) xcloselog(); - /* set env variables */ - run_job_grand_child_setup_env_var(line, &curshell); - #if defined(CHECKJOBS) || defined(CHECKRUNJOB) /* this will force to mail a message containing at least the exact * and complete command executed for each execution of all jobs */ @@ -560,11 +587,9 @@ run_job(struct exe_t *exeent) die_e("setsid(): errno %d", errno); } #endif - execl(curshell, curshell, "-c", line->cl_shell, NULL); - /* execl returns only on error */ - error_e("Can't find \"%s\". Trying a execlp(\"sh\",...)",curshell); - execlp("sh", "sh", "-c", line->cl_shell, NULL); - die_e("execl() \"%s -c %s\" error", curshell, line->cl_shell); + execle(curshell, curshell, "-c", line->cl_shell, NULL, jobenv); + /* execle returns only on error */ + die_e("Couldn't exec shell '%s'",curshell); /* execution never gets here */ @@ -582,75 +607,73 @@ run_job(struct exe_t *exeent) /* give the pid of the child to the parent (main) fcron process */ ret = write_pipe(pipe_pid_fd[1], &pid, sizeof(pid)); - if ( ret != OK ) { - if ( ret == ERR ) - error("run_job(): child: Could not write job pid" - " to pipe"); - else { - errno = ret; - error_e("run_job(): child: Could not write job pid" - " to pipe"); - } - } - + if ( ret != OK ) { + if ( ret == ERR ) + error("run_job(): child: Could not write job pid to pipe"); + else { + errno = ret; + error_e("run_job(): child: Could not write job pid to pipe"); + } + } + #ifdef CHECKRUNJOB debug("run_job(): child: grand-child pid written to pipe"); #endif /* CHECKRUNJOB */ if ( ! is_nolog(line->cl_option) ) - explain("Job %s started for user %s (pid %d)", line->cl_shell, - line->cl_file->cf_user, pid); - - if ( ! to_stdout && is_mail(line->cl_option ) ) { - /* user wants a mail : we use the pipe */ - char mailbuf[TERM_LEN]; - FILE *pipef = fdopen(pipe_fd[0], "r"); - - if ( pipef == NULL ) - die_e("Could not fdopen() pipe_fd[0]"); - - mailbuf[sizeof(mailbuf)-1] = '\0'; - while ( fgets(mailbuf, sizeof(mailbuf), pipef) != NULL ) - if ( fputs(mailbuf, mailf) < 0 ) - warn("fputs() failed to write to mail file for job %s (pid %d)", - line->cl_shell, pid); - /* (closes also pipe_fd[0]): */ - if ( fclose(pipef) != 0 ) - error_e("child: Could not fclose(pipef)"); - } - - /* FIXME : FOLLOWING HACK USELESS ? */ - /* FIXME : HACK - * this is a try to fix the bug on sorcerer linux (no jobs - * exectued at all, and - * "Could not read job pid : setting it to -1: No child processes" - * error messages) */ - /* use a select() or similar to know when parent has read - * the pid (with a timeout !) */ - /* // */ - sleep(2); - /* // */ + explain("Job %s started for user %s (pid %d)", line->cl_shell, + line->cl_file->cf_user, pid); + + if ( ! to_stdout && is_mail(line->cl_option ) ) { + /* user wants a mail : we use the pipe */ + char mailbuf[TERM_LEN]; + FILE *pipef = fdopen(pipe_fd[0], "r"); + + if ( pipef == NULL ) + die_e("Could not fdopen() pipe_fd[0]"); + + mailbuf[sizeof(mailbuf)-1] = '\0'; + while ( fgets(mailbuf, sizeof(mailbuf), pipef) != NULL ) + if ( fputs(mailbuf, mailf) < 0 ) + warn("fputs() failed to write to mail file for job %s (pid %d)", + line->cl_shell, pid); + /* (closes also pipe_fd[0]): */ + if ( fclose(pipef) != 0 ) + error_e("child: Could not fclose(pipef)"); + } + + /* FIXME : FOLLOWING HACK USELESS ? */ + /* FIXME : HACK + * this is a try to fix the bug on sorcerer linux (no jobs + * exectued at all, and + * "Could not read job pid : setting it to -1: No child processes" + * error messages) */ + /* use a select() or similar to know when parent has read + * the pid (with a timeout !) */ + /* // */ + sleep(2); + /* // */ #ifdef CHECKRUNJOB - debug("run_job(): child: closing pipe with parent"); + debug("run_job(): child: closing pipe with parent"); #endif /* CHECKRUNJOB */ - if ( close(pipe_pid_fd[1]) < 0 ) - error_e("child: could not close(pipe_pid_fd[1])"); + if ( close(pipe_pid_fd[1]) < 0 ) + error_e("child: could not close(pipe_pid_fd[1])"); - /* we use a while because of a possible interruption by a signal */ - while ( (pid = wait3(&status, 0, NULL)) > 0) - { + /* we use a while because of a possible interruption by a signal */ + while ( (pid = wait3(&status, 0, NULL)) > 0) + { #ifdef CHECKRUNJOB - debug("run_job(): child: ending job pid %d", pid); + debug("run_job(): child: ending job pid %d", pid); #endif /* CHECKRUNJOB */ - end_job(line, status, mailf, mailpos); - } + end_job(line, status, mailf, mailpos, sendmailenv); + } - /* execution never gets here */ - - } + /* execution never gets here */ + + } - /* execution should never gets here, but if it happened we exit with an error */ - exit(EXIT_ERR); + /* execution should never gets here, but if it happened we exit with an error */ + exit(EXIT_ERR); } default: @@ -694,7 +717,7 @@ run_job(struct exe_t *exeent) } void -end_job(cl_t *line, int status, FILE *mailf, short mailpos) +end_job(cl_t *line, int status, FILE *mailf, short mailpos, char **sendmailenv) /* if task have made some output, mail it to user */ { @@ -767,7 +790,7 @@ end_job(cl_t *line, int status, FILE *mailf, short mailpos) #endif if (mail_output == 1) { - launch_mailer(line, mailf); + launch_mailer(line, mailf, sendmailenv); /* never reached */ die_e("Internal error: launch_mailer returned"); } @@ -781,7 +804,7 @@ end_job(cl_t *line, int status, FILE *mailf, short mailpos) } void -launch_mailer(cl_t *line, FILE *mailf) +launch_mailer(cl_t *line, FILE *mailf, char **sendmailenv) /* mail the output of a job to user */ { #ifdef USE_SENDMAIL @@ -804,10 +827,11 @@ launch_mailer(cl_t *line, FILE *mailf) die_e("Could not chdir to /"); /* run sendmail with mail file as standard input */ - execl(sendmail, sendmail, SENDMAIL_ARGS, line->cl_mailto, NULL); - error_e("Can't find \"%s\". Trying a execlp(\"sendmail\")", sendmail); - execlp("sendmail", "sendmail", SENDMAIL_ARGS, line->cl_mailto, NULL); - die_e("Can't exec " SENDMAIL); + /* // */ + debug("execle(%s, %s, %s, %s, NULL, sendmailenv)", sendmail, sendmail, SENDMAIL_ARGS, line->cl_mailto); + /* // */ + execle(sendmail, sendmail, SENDMAIL_ARGS, line->cl_mailto, NULL, sendmailenv); + die_e("Couldn't exec '%s'", sendmail); #else /* defined(USE_SENDMAIL) */ exit(EXIT_OK); #endif diff --git a/job.h b/job.h index 2cd7334..cd76661 100644 --- a/job.h +++ b/job.h @@ -27,9 +27,10 @@ #define __JOB_H__ /* functions prototypes */ -extern int change_user(struct cl_t *cl); +extern void change_user_setup_env(struct cl_t *cl, char ***sendmailenv, + char ***jobenv, char **curshell, char **curhome); extern int run_job(struct exe_t *exeent); -extern FILE *create_mail(struct cl_t *line, char *subject); -extern void launch_mailer(struct cl_t *line, FILE *mailf); +extern FILE *create_mail(struct cl_t *line, char *subject, char **env); +extern void launch_mailer(struct cl_t *line, FILE *mailf, char **env); #endif /* __JOB_H__ */ diff --git a/log.c b/log.c index 96290c2..d0619d3 100644 --- a/log.c +++ b/log.c @@ -34,6 +34,13 @@ #include #include +#ifdef DEBUG +char debug_opt = 1; /* set to 1 if we are in debug mode */ +#else +char debug_opt = 0; /* set to 1 if we are in debug mode */ +#endif + + static void xopenlog(void); char* make_msg(const char *append, char *fmt, va_list args); void log_syslog_str(int priority, char *msg); diff --git a/log.h b/log.h index d66af6e..3377de8 100644 --- a/log.h +++ b/log.h @@ -26,6 +26,8 @@ #ifndef __LOG_H__ #define __LOG_H__ +extern char debug_opt; + /* functions prototypes */ extern void xcloselog(void); extern void explain(char *fmt, ...); diff --git a/read_string.c b/read_string.c index e2bca96..ad33356 100644 --- a/read_string.c +++ b/read_string.c @@ -86,7 +86,7 @@ char *read_string(int echo, const char *prompt) } else { line[nc] = '\0'; } - input = ( (line) ? strdup(line):NULL ); + input = strdup2(line); Overwrite(line); return input; /* return malloc()ed string */ diff --git a/save.c b/save.c index ef2d28e..671e840 100644 --- a/save.c +++ b/save.c @@ -228,8 +228,11 @@ write_file_to_disk(int fd, struct cf_t *file, time_t time_date) Save_lint(fd, S_TZDIFF_T, file->cf_tzdiff, write_buf, &write_buf_used); /* env variables, */ - for (env = file->cf_env_base; env; env = env->e_next) - Save_str(fd, S_ENVVAR_T, env->e_val, write_buf, &write_buf_used); + for (env = env_list_first(file->cf_env_list); + env != NULL; + env = env_list_next(file->cf_env_list)) { + Save_str(fd, S_ENVVAR_T, env->e_envvar, write_buf, &write_buf_used); + } /* then, lines. */ for (line = file->cf_line_base; line; line = line->cl_next) { diff --git a/subs.c b/subs.c index 69f92ee..851b6d5 100644 --- a/subs.c +++ b/subs.c @@ -105,6 +105,26 @@ remove_blanks(char *str) } +int +strcmp_until(const char *left, const char *right, char until) +/* compare two strings up to a given char (copied from Vixie cron) */ +/* Copyright 1988,1990,1993,1994 by Paul Vixie */ +/* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. */ + +{ + while (*left != '\0' && *left != until && *left == *right) { + left++; + right++; + } + + if ((*left == '\0' || *left == until) + && (*right == '\0' || *right == until)) { + return (0); + } + return (*left - *right); +} + char * strdup2(const char *str) @@ -114,12 +134,11 @@ strdup2(const char *str) if ( str == NULL ) return NULL; - ptr = malloc(strlen(str) + 1); + ptr = strdup(str); if ( ! ptr) - die_e("Could not calloc"); + die_e("Could not strdup()"); - strcpy(ptr, str); return(ptr); } @@ -296,3 +315,59 @@ read_conf(void) fclose(f); } + +void +my_unsetenv(const char *name) +/* call unsetenv() if available, otherwise call putenv("var="). + * Check for errors and log them. */ +{ + +#ifdef HAVE_UNSETENV + if ( unsetenv(name) < 0 ) + error_e("could not flush env var %s with unsetenv()", name); +#else + char buf[PATH_LEN]; + snprintf(buf, sizeof(buf) - 1, "%s=", name); + buf[sizeof(buf)-1] = '\0'; + if ( putenv(buf) < 0 ) + error_e("could not flush env var %s with putenv()", name); +#endif + +} + +void +my_setenv_overwrite(const char *name, const char *value) +/* call setenv(x, x, 1) if available, otherwise call putenv() with the appropriate + * constructed string. + * Check for errors and log them. */ + +{ + +#ifdef HAVE_SETENV + + /* // */ + debug("Calling setenv(%s, %s, 1)", name, value); + /* // */ + if ( setenv(name, value, 1) != 0 ) + error_e("setenv(%s, %s, 1) failed", name, value); + +#else + char buf[PATH_LEN]; + + snprintf(buf, sizeof(buf) - 1, "%s=%s", name, value) + + /* The final \0 may not have been copied because of lack of space: + * add it to make sure */ + buf[sizeof(buf)-1]='\0'; + + /* // */ + debug("Calling putenv(%s)", buf); + /* // */ + if ( putenv(buf) != 0 ) + error_e("putenv(%s) failed", buf); + +#endif + +} + + diff --git a/subs.h b/subs.h index 2f47c98..48aad23 100644 --- a/subs.h +++ b/subs.h @@ -45,11 +45,14 @@ extern char *sendmail; extern uid_t get_user_uid_safe(char *username); extern gid_t get_group_gid_safe(char *groupname); extern int remove_blanks(char *str); +extern int strcmp_until(const char *left, const char *right, char until); extern char *strdup2(const char *); extern void free_safe(void *ptr); extern int get_word(char **str); extern int temp_file(char **name); extern void read_conf(void); extern void free_conf(void); +extern void my_unsetenv(const char *name); +extern void my_setenv_overwrite(const char *name, const char *value); #endif /* __SUBS_H__ */ diff --git a/temp_file.c b/temp_file.c index 90f2ed6..f7a82cd 100644 --- a/temp_file.c +++ b/temp_file.c @@ -39,9 +39,7 @@ temp_file(char **name) int fd; #ifdef HAVE_MKSTEMP char name_local[PATH_LEN] = ""; - strncpy(name_local, tmp_path, sizeof(name_local) - 1); - name_local[sizeof(name_local)-1] = '\0'; - strcat(name_local, "fcr-XXXXXX"); + snprintf(name_local, sizeof(name_local), "%sfcr-XXXXXX", tmp_path); if ( (fd = mkstemp(name_local)) == -1 ) die_e("Can't find a unique temporary filename"); /* we must set the file mode to 600 (some versions of mkstemp may set it diff --git a/u_list.c b/u_list.c index da404af..52a3a51 100644 --- a/u_list.c +++ b/u_list.c @@ -47,14 +47,14 @@ u_list_init(size_t entry_size, int init_size, int grow_size) /* sanity check */ if ( entry_size < 1 || init_size < 1 || grow_size < 1 ) - die("Invalid arguments for u_list_init(): entry_size=%d, init_size=%d, " - "grow_size=%d", entry_size, init_size, grow_size); + die("Invalid arguments for u_list_init(): entry_size=%d, init_size=%d, " + "grow_size=%d", entry_size, init_size, grow_size); /* Allocate the list structure: */ l = calloc(1, sizeof(struct u_list_t)); if ( l == NULL ) - die_e("Failed creating a new unordered list: could not calloc() u_list_t " - "(entry_size: %d)", entry_size); + die_e("Failed creating a new unordered list: could not calloc() u_list_t " + "(entry_size: %d)", entry_size); /* Initialize the structure and allocate the array: */ l->array_size = init_size; @@ -64,8 +64,8 @@ u_list_init(size_t entry_size, int init_size, int grow_size) l->cur_removed = 0; l->entries_array = calloc(init_size, entry_size); if ( l->entries_array == NULL ) - die_e("Failed creating a new unordered list: could not calloc array" - "(entry_size: %d, init_size: %d)", entry_size, init_size); + die_e("Failed creating a new unordered list: could not calloc array" + "(entry_size: %d, init_size: %d)", entry_size, init_size); return l; } @@ -78,10 +78,9 @@ u_list_copy(u_list_t *list) if ( list == NULL ) return NULL; - new_list = (1, sizeof(struct u_list_t)); - if ( l == NULL ) - die_e("Failed copying unordered list: could not calloc() u_list_t " - "(entry_size: %d)", entry_size); + new_list = calloc(1, sizeof(struct u_list_t)); + if ( new_list == NULL ) + die_e("Failed copying unordered list: could not calloc() u_list_t"); memcpy(new_list, list, sizeof(struct u_list_t)); new_list->cur_entry = NULL; @@ -176,7 +175,7 @@ u_list_is_iterating(u_list_t *l) { /* sanity check */ if ( l == NULL ) - die("Invalid argument for u_list_first(): list=%d", l); + die("Invalid argument for u_list_iterating(): list=%d", l); return ( l->cur_entry != NULL ); } -- 2.40.0