]> granicus.if.org Git - fcron/commitdiff
- Rewrote the env var management code with Vixie cron compatibility in mind
authorThibault Godouet <thib@wind.(none)>
Sat, 9 Jan 2010 15:28:31 +0000 (15:28 +0000)
committerThibault Godouet <thib@wind.(none)>
Sat, 9 Jan 2010 15:28:31 +0000 (15:28 +0000)
 * 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/...)

27 files changed:
Makefile.in
conf.c
config.h.in
convert-fcrontab.c
database.c
doc/en/todo.sgml
env_list.c
env_list.h
fcron.c
fcron.h
fcrondyn.c
fcrondyn.h
fcronsighup.c
fcrontab.c
fcrontab.h
fileconf.c
global.h
job.c
job.h
log.c
log.h
read_string.c
save.c
subs.c
subs.h
temp_file.c
u_list.c

index 40997919219c43b0e4d69d509a22a82a295aaa51..fea8fc61066ba89f3fb9589ee8644266f32bb413 100644 (file)
@@ -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 84575e6cc8592da0da0f4f4c5b6aba825508c18a..9dce1dbb22b57c43b64c233a99b7e73ac1e59c3f 100644 (file)
--- 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);
index fe99202444b578fdcc6d7d94e3d71ee429394c89..16a0d0532ceb5dd7320d63a5fa63cf263619e373 100644 (file)
@@ -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 *** */
                              * 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 */
index e306e17119d5be6fe34f33e609d53474b3e6ebe4..18cbeddbfd880f533c5f639f7cdc69d72877d60c 100644 (file)
@@ -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);
index 65d8afd129a1a5e56949cc719d92fa51b934055d..01f77a7e8b5127c64523c07ede4eb3b8a83df73e 100644 (file)
@@ -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");
index 452eb32ce941fc0db9ca49d357c18f77d22fc2fc..f8f7a6264df1c632164bb6989830430f2cf41a37 100644 (file)
@@ -24,6 +24,12 @@ A copy of the license is included in gfdl.sgml.
       <sect2>
         <title>High priority</title>
         <itemizedlist>
+           <listitem>
+              <para>replace all free() by free_safe()</para>
+           </listitem>
+           <listitem>
+              <para>check fcron for memory leaks using library</para>
+           </listitem>
            <listitem>
               <para>Test (and use ?) docbook2x-man -- xlstproc ? cf http://antoine.ginies.free.fr/docbook/ch09.html</para>
            </listitem>
@@ -129,4 +135,4 @@ mode: sgml
 sgml-parent-document:("fcron-doc.sgml" "book" "chapter" "sect1" "")
 End:
 -->
-   
\ No newline at end of file
+
index 50fe3df740ad83f665f4bfbd26eb6079227c8bd0..3ed3d236ff74d2f44f69838daa99b2805ab82531 100644 (file)
@@ -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;
 }
index 7f9e2c7c5610da5dee031613ff3a8805710acd13..4df3070a150de5adf5f69926c67a650c89334c97 100644 (file)
 
 #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 3b4e1de6d0a8d13333131173e8dc80c9532277bc..0395dd709d96cd5c77c95d104364aed601f5d24c 100644 (file)
--- 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 5a4c47288614685b2d2ffa2c2c920fa64e604c00..04a8c6696c3cee521ec6d7b2784e1d2127d682e6 100644 (file)
--- 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;
index a3cbedbf169568d356027097a66e7d1b86a13afa..fc87e98e5ee518bbd40e6a1b9e3ebf35a68795c1 100644 (file)
@@ -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 : */
index d246e186fb658ec61be5f80ac26eab6dddc6505b..51c4bf71ae176d9862f9157b542873d60fb6cce9 100644 (file)
@@ -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;
index ffb2318353116abd7a344566fcd6f2737968c68b..016627ea3e511e2420d5f1cbe9c57c69cdcabc39 100644 (file)
@@ -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 */
index 09eae272701bede9d54f5ca4764a613c6af2b52a..3b29c049161572762f9c334d36ce50c280800d2c 100644 (file)
@@ -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) */
index 9723741e1db18a2c8c27fc865d816dfe24d7c072..c5b0bc926401a5d26774a558ecf6079b37d48334 100644 (file)
@@ -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;
index f1489a61864ccd3649e8f6665c33fd4ec6efbdf6..76d7348decade8c7dac36fda29e937ade21ca8e3 100644 (file)
@@ -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);
index fba2b01bd4f533b699e9329ca7d377db7f54fc95..41acd6d6e8dbfc4209bb71d8c0c428eb43a41cfc 100644 (file)
--- a/global.h
+++ b/global.h
 
 #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           
 
 #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 426e52b93bf4bd3c0cf5ad0b06ef2463fabb3119..16d6e4fd05456b0142792aa90b083551b5a8f282 100644 (file)
--- 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 2cd733424e57ed33ea5499f0a8366a3c2f590b6e..cd76661d08c55f769493fdd42df7856a8d564304 100644 (file)
--- a/job.h
+++ b/job.h
 #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 96290c28db8787cdeda741b733c3684a6c067f9f..d0619d3952563dc984be2a47ce64975b9566764d 100644 (file)
--- a/log.c
+++ b/log.c
 #include <sys/types.h>
 #include <sys/socket.h>
 
+#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 d66af6e26204f659b9d1760507cf05b850ba8eaf..3377de8810730a08375d9153f6abec22799d9143 100644 (file)
--- 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, ...);
index e2bca961ac6cfede840f0a64091492adb1db0c07..ad33356eded74be8f5b380f78b10b86e52e16e43 100644 (file)
@@ -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 ef2d28ef859a17d3b16e8c2ac8c4f63268e505c0..671e840336b332e8c0e267deac70fc7119252644 100644 (file)
--- 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 69f92eefdd3c92f0bf987329773e76dbe32ee974..851b6d5993c816492fc274d6c206d4a7d323a0b5 100644 (file)
--- 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 2f47c98dc1c047c3075c8ce4a9b6f2bd23080bb3..48aad239ad0f289074d1d0cd6ed9a9666e596a25 100644 (file)
--- 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__ */
index 90f2ed62ac886c7ad3fb21aeb2a22e373901e000..f7a82cd02ea8af1665ae7a3f5d02e469e2be3539 100644 (file)
@@ -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
index da404af05f32adcf29c3ec9b9b4830beb9dce843..52a3a51116b21c4096e616885fd87043e3b0d964 100644 (file)
--- 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 );
 }