Ejemplo n.º 1
0
void
run_job_grand_child_setup_stderr_stdout(cl_t * line, int *pipe_fd)
    /* setup stderr and stdout correctly so as the mail containing
     * the output of the job can be send at the end of the job.
     * Close the pipe (both ways). */
{

    if (is_mail(line->cl_option)) {
        /* we can't dup2 directly to mailfd, since a "cmd > /dev/stderr" in
         * a script would erase all previously collected message */
        if (dup2(pipe_fd[1], 1) != 1 || dup2(1, 2) != 2)
            die_e("dup2() error");      /* dup2 also clears close-on-exec flag */
        /* we close the pipe_fd[]s : the resources remain, and the pipe will
         * be effectively close when the job stops */
        xclose_check(&(pipe_fd[0]), "pipe_fd[0] in setup_stderr_stdout");
        xclose_check(&(pipe_fd[1]), "pipe_fd[1] in setup_stderr_stdout");
        /* Standard buffering results in unwanted behavior (some messages,
         * at least error from fcron process itself, are lost) */
#ifdef HAVE_SETLINEBUF
        setlinebuf(stdout);
        setlinebuf(stderr);
#else
        setvbuf(stdout, NULL, _IONBF, 0);
        setvbuf(stderr, NULL, _IONBF, 0);
#endif
    }
    else if (foreground) {
        if (freopen("/dev/null", "w", stdout) == NULL)
            error_e("could not freopen /dev/null as stdout");
        if (freopen("/dev/null", "w", stderr) == NULL)
            error_e("could not freopen /dev/null as stderr");
    }

}
Ejemplo n.º 2
0
int
remove_fcrontab(char rm_orig)
    /* remove user's fcrontab and tell daemon to update his conf */
    /* note : the binary fcrontab is removed by fcron */
{
    int return_val = OK;
    int fd;

    if ( rm_orig )
	explain("removing %s's fcrontab", user);

    /* remove source and formated file */
    if ( (rm_orig && remove(buf)) != 0 ) {
	if ( errno == ENOENT )
	    return_val = ENOENT;
	else
	    error_e("could not remove %s", buf);		
    }

#ifdef USE_SETE_ID
    if (seteuid(fcrontab_uid) != 0)
	error_e("seteuid(fcrontab_uid[%d])", fcrontab_uid);
#endif
	    
    /* try to remove the temp file in case he has not
     * been read by fcron daemon */
    snprintf(buf, sizeof(buf), "new.%s", user);
    remove(buf);

    /* finally create a file in order to tell the daemon
     * a file was removed, and launch a signal to daemon */
    snprintf(buf, sizeof(buf), "rm.%s", user);
    fd = open(buf, O_CREAT | O_TRUNC | O_EXCL, S_IRUSR | S_IWUSR);

#ifdef USE_SETE_ID
    if (seteuid(uid) != 0)
	die_e("seteuid(uid[%d])", uid);
#endif

    if ( fd == -1 ) {
	if ( errno != EEXIST )
	    error_e("Can't create file %s", buf);
    }
    else if ( asuid == ROOTUID && fchown(fd, ROOTUID, fcrontab_gid) != 0 )
	error_e("Could not fchown %s to root", buf);
    close(fd);
    
    need_sig = 1;

    return return_val;

}
Ejemplo n.º 3
0
int
install_stdin(void)
    /* install what we get through stdin */
{
    int tmp_fd = 0;
    FILE *tmp_file = NULL;
    char *tmp_str = NULL;
    int c;
    short return_val = EXIT_OK;
	    	    
    tmp_fd = temp_file(&tmp_str);
    
    if( (tmp_file = fdopen(tmp_fd, "w")) == NULL )
	die_e("Could not fdopen file %s", tmp_str);

    while ( (c = getc(stdin)) != EOF )
	putc(c, tmp_file);

    fclose(tmp_file);
    close(tmp_fd);

    if ( make_file(tmp_str) == ERR )
	goto exiterr;
    else
	goto exit;

  exiterr:
	return_val = EXIT_ERR;    
  exit:
    if ( remove(tmp_str) != 0 )
	error_e("Could not remove %s", tmp_str);
    free(tmp_str);
    return return_val;

}
Ejemplo n.º 4
0
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. */
{

#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 /");
    }

}
Ejemplo n.º 5
0
void 
cmd_renice(struct fcrondyn_cl *client, long int *cmd, int fd, int exe_index, int is_root)
/* change nice value of a running job */
{

#ifdef HAVE_SETPRIORITY
    /* check if arguments are valid */
    if ( exe_array[exe_index].e_job_pid <= 0 || ( (int)cmd[1] < 0 && ! is_root )
	|| (int)cmd[1] > 20 || (int)cmd[1] < -20 ) {
	warn("renice: invalid args : pid: %d nice_value: %d user: %s.",
	     exe_array[exe_index].e_job_pid, (int)cmd[1], client->fcl_user);	
	Send_err_msg_end(fd, err_invalid_args_str);
    }

    /* ok, now setpriority() the job */
    if ( setpriority(PRIO_PROCESS, exe_array[exe_index].e_job_pid, (int)cmd[1]) != 0) {
	error_e("could not setpriority(PRIO_PROCESS, %d, %d)",
		exe_array[exe_index].e_job_pid, (int)cmd[1]);
	Send_err_msg_end(fd, err_unknown_str);
    }
    else
	Send_err_msg_end(fd, err_no_err_str);

#else /* HAVE_SETPRIORITY */
    warn("System has no setpriority() : cannot renice. pid: %d nice_value: %d user: %s.",
	 exe_array[exe_index].e_job_pid, (int)cmd[1], client->fcl_user);	
    Send_err_msg_end(fd, err_cmd_unknown_str);    

#endif /* HAVE_SETPRIORITY */
}
Ejemplo n.º 6
0
int
getloadavg(double *result, int n)
/* return the current load average as a floating point number,
 * the number of load averages read, or <0 for error
 */
{
    FILE *fp;
    int i;

    if (n > 3)
	n = 3;

    if ((fp = fopen(PROC "/loadavg", "r")) == NULL) {
	error_e("could not open '"PROC"/loadavg'"
		" (make sure procfs is mounted)");
	i = -1;
    }
    else {
	for (i = 0; i < n; i++) {
	    if (fscanf(fp, "%lf", result) != 1)
		goto end;
	    result++;
	}
    }
  end:
    fclose(fp);
    return (i<0) ? i : i;
}
Ejemplo n.º 7
0
void
run_job_grand_child_setup_nice(cl_t * line)
    /* set the nice value for the job */
{
    if (line->cl_nice != 0) {
        errno = 0;              /* so that it works with any libc and kernel */
        if (nice(line->cl_nice) == -1 && errno != 0)
            error_e("could not set nice value");
    }
}
Ejemplo n.º 8
0
int
read_write_pipe(int fd, void *buf, size_t size, int action)
    /* Read/write data from/to pipe.
     * action can either be PIPE_WRITE or PIPE_READ.
     * Handles signal interruptions, and read in several passes.
     * Returns ERR in case of a closed pipe, the errno from errno
     * for other errors, and OK if everything was read successfully */
{
    int size_processed = 0;
    int ret;
    int num_retry = 0;

    while (size_processed < size) {
        errno = 0;
        if (action == PIPE_READ)
            ret = read(fd, (char *)buf + size_processed, size);
        else if (action == PIPE_WRITE)
            ret = write(fd, (char *)buf + size_processed, size);
        else {
            error("Invalid action parameter for function read_write_pipe():"
                  " %d", action);
            return ERR;
        }
        if (ret > 0)
            /* some data read correctly -- we still may need
             * one or several calls of read() to read the rest */
            size_processed += ret;
        else if (ret < 0 && errno == EINTR)
            /* interrupted by a signal : let's try again */
            continue;
        else {
            /* error */

            if (ret == 0) {
                /* is it really an error when writing ? should we continue
                 * in this case ? */
                if (num_retry < 3) {
                    num_retry++;
                    error_e
                        ("read_write_pipe(): read/write returned 0: retrying... (size: %d, size_processed: %d, num_retry: %d)",
                         size, size_processed, num_retry);
                    sleep(1);
                    continue;
                }
                else
                    return ERR;
            }
            else
                return errno;
        }
    }

    return OK;
}
Ejemplo n.º 9
0
int
save_one_file(cf_t *file, char *filename, uid_t own_uid, gid_t own_gid, time_t save_date)
/* save a given file to disk */
{
    int fd;

    /* open file */
#ifdef CONFIG_FLASK
    if ( is_flask_enabled() )
	fd = open_secure(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR | S_IWUSR, file->cf_file_sid);
    else
#endif
    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR|S_IWUSR);
    if ( fd == -1 ) {
	error_e("Could not open %s", filename);
	return ERR;
    }

    if (fchown(fd, own_uid, own_gid) != 0) {
	error_e("Could not fchown %s to uid:%d gid:%d", filename, own_uid, own_gid);
	close(fd);
	remove(filename);
	return ERR;
    }

    /* save file : */
    if ( write_file_to_disk(fd, file, save_date) == ERR ) {
	close(fd);
	remove(filename);
	return ERR;
    }

    close(fd);

    return OK;
}
Ejemplo n.º 10
0
int
copy(char *orig, char *dest)
    /* copy orig file to dest */
{
    int from;
    int to_fd;
    int nb;
    char *copy_buf[LINE_LEN];

    if ( (from = open(orig, O_RDONLY)) == -1) {
	error_e("copy: open(orig)");
	return ERR;
    }
    /* create it as fcrontab_uid (to avoid problem if user's uid changed)
     * except for root. Root requires filesystem uid root for security
     * reasons */
#ifdef USE_SETE_ID
    if (asuid == ROOTUID) {
	if (seteuid(ROOTUID) != 0)
	    error_e("seteuid(ROOTUID)");
    } 
    else {
    	if (seteuid(fcrontab_uid) != 0)
	    error_e("seteuid(fcrontab_uid[%d])", fcrontab_uid);
    }
#endif
    to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, S_IRUSR|S_IWUSR|S_IRGRP);
    if (to_fd == -1) {
	error_e("copy: dest");
	return ERR;
    }
#ifdef USE_SETE_ID
    if (asuid != ROOTUID && seteuid(uid) != 0)
	die_e("seteuid(uid[%d])", uid);
#endif
    if (asuid == ROOTUID ) {
	if ( fchmod(to_fd, S_IWUSR | S_IRUSR) != 0 )
	    error_e("Could not fchmod %s to 600", dest);
	if ( fchown(to_fd, ROOTUID, fcrontab_gid) != 0 )
	    error_e("Could not fchown %s to root", dest);
    }

    while ( (nb = read(from, copy_buf, sizeof(copy_buf))) != -1 && nb != 0 )
	if ( write(to_fd, copy_buf, nb) != nb ) {
	    error("Error while copying file. Aborting.\n");
	    close(from);
	    close(to_fd);
	    return ERR;
	}

    close(from);
    close(to_fd);
    
    return OK;
}
Ejemplo n.º 11
0
void
cmd_send_signal(struct fcrondyn_cl *client, long int *cmd, int fd, int exe_index)
/* send a signal to a running job */
{
    if ( exe_array[exe_index].e_job_pid <= 0 || (int)cmd[1] <= 0 ) {
	warn("send_signal: invalid args : pid: %d signal: %d user: %s",
	     exe_array[exe_index].e_job_pid, (int)cmd[1], client->fcl_user);	
	Send_err_msg_end(fd, err_invalid_args_str);
    }

    /* ok, now kill() the job */
    if ( kill(exe_array[exe_index].e_job_pid, (int)cmd[1]) != 0) {
	error_e("could not kill(%d, %d)", exe_array[exe_index].e_job_pid, (int)cmd[1]);
	Send_err_msg_end(fd, err_unknown_str);
    }
    else
	Send_err_msg_end(fd, err_no_err_str);
}
Ejemplo n.º 12
0
void
get_lock()
    /* check if another fcron daemon is running with the same config (-c option) :
     * in this case, die. if not, write our pid to /var/run/fcron.pid in order to lock,
     * and to permit fcrontab to read our pid and signal us */
{
    int otherpid = 0;
    FILE *daemon_lockfp = NULL;
    int fd;

    if (((fd = open(pidfile, O_RDWR | O_CREAT, 0644)) == -1)
        || ((daemon_lockfp = fdopen(fd, "r+"))) == NULL)
        die_e("can't open or create %s", pidfile);

#ifdef HAVE_FLOCK
    if (flock(fd, LOCK_EX | LOCK_NB) != 0)
#else                           /* HAVE_FLOCK */
    if (lockf(fileno(daemon_lockfp), F_TLOCK, 0) != 0)
#endif                          /* ! HAVE_FLOCK */
    {
        if (fscanf(daemon_lockfp, "%d", &otherpid) >= 1)
            die_e("can't lock %s, running daemon's pid may be %d", pidfile,
                  otherpid);
        else
            die_e("can't lock %s, and unable to read running"
                  " daemon's pid", pidfile);
    }

    fcntl(fd, F_SETFD, 1);

    rewind(daemon_lockfp);
    fprintf(daemon_lockfp, "%d\n", (int)daemon_pid);
    fflush(daemon_lockfp);
    if (ftruncate(fileno(daemon_lockfp), ftell(daemon_lockfp)) < 0)
        error_e
            ("Unable to ftruncate(fileno(daemon_lockfp), ftell(daemon_lockfp))");

    /* abandon fd and daemon_lockfp even though the file is open. we need to
     * keep it open and locked, but we don't need the handles elsewhere.
     */

}
Ejemplo n.º 13
0
int
is_system_startup(void)
{
    int reboot = 0;

    /* lock exist - skip reboot jobs */
    if (access(REBOOT_LOCK, F_OK) == 0) {
        explain("@reboot jobs will only be run at computer's startup.");
        /* don't run @reboot jobs */
        return 0;
    }
    /* lock doesn't exist - create lock, run reboot jobs */
    if ((reboot = creat(REBOOT_LOCK, S_IRUSR & S_IWUSR)) < 0)
        error_e("Can't create lock for reboot jobs.");
    else
        xclose_check(&reboot, REBOOT_LOCK);

    /* run @reboot jobs */
    return 1;
}
Ejemplo n.º 14
0
int
authenticate_user_password(int fd)
    /* authenticate user */
{
    char *password = NULL;
    char buf[USER_NAME_LEN + 16];
    int len = 0;
    fd_set read_set;            /* needed to use select to check if some data is waiting */
    struct timeval tv;

    snprintf(buf, sizeof(buf), "password for %s :", user_str);
    if ((password = read_string(CONV_ECHO_OFF, buf)) == NULL)
        return ERR;

    len = snprintf(buf, sizeof(buf), "%s", user_str) + 1;
    len += snprintf(buf + len, sizeof(buf) - len, "%s", password) + 1;
    send(fd, buf, len, 0);
    Overwrite(buf);
    Overwrite(password);
    Free_safe(password);

    tv.tv_sec = MAX_WAIT_TIME;
    tv.tv_usec = 0;
    FD_ZERO(&read_set);
    FD_SET(fd, &read_set);
    if (select(fd + 1, &read_set, NULL, NULL, &tv) <= 0) {
        error_e("Couldn't get data from socket during %d seconds.",
                MAX_WAIT_TIME);
        return ERR;
    }
    while (recv(fd, buf, sizeof(buf), 0) < 0 && errno == EINTR)
        if (errno == EINTR && debug_opt)
            fprintf(stderr, "Got EINTR ...");
    if (strncmp(buf, "1", sizeof("1")) != 0)
        return ERR;

    return OK;
}
Ejemplo n.º 15
0
int
write_buf_to_disk(int fd, char *write_buf, int *buf_used)
/* write the buffer to disk */
{
    ssize_t to_write = *buf_used; 
    ssize_t written = 0;
    ssize_t return_val;
    int num_retries = 0;

    while ( written < to_write ) {
	if ( num_retries++ > (int)(to_write / 2) ) {
	    error("too many retries (%d) to write buf to disk : giving up.",num_retries);
	    return ERR;
	}
	return_val = write(fd, (write_buf+written), to_write - written);
	if ( return_val == -1 ) {
	    error_e("could not write() buf to disk");
	    return ERR;
	}
	written += return_val;
    }

    /* */
    debug("write_buf_to_disk() : written %d/%d, %d (re)try(ies)", written, to_write, 
	  num_retries);
    /* */

    if ( written == to_write ) {
	*buf_used = 0;
	return OK;
    }
    else {
	error("write_buf_to_disk() : written %d bytes for %d requested.",
	      written, to_write);
	return ERR;
    }
}
Ejemplo n.º 16
0
void
print_fields(int fd, unsigned char *details)
    /* print a line describing the field types used in print_line() */
{
    char fields[TERM_LEN];
    char field_user[] = " USER  ";
    char field_id[] = "ID   ";
    char field_rq[] = " R&Q ";
    char field_options[] = " OPTIONS  ";
    char field_schedule[] = " SCHEDULE        ";
    char field_until[] = " LAVG 1,5,15 UNTIL       STRICT";
    char field_pid[] = " PID    ";
    char field_index[] = " INDEX";
    char field_cmd[] = " CMD";
    char field_endline[] = "\n";
    int len = 0;

    fields[0] = '\0';

    Add_field(field_id);
    Test_add_field(FIELD_USER, field_user);
    Test_add_field(FIELD_PID, field_pid);
    Test_add_field(FIELD_INDEX, field_index);
    Test_add_field(FIELD_RQ, field_rq);
    Test_add_field(FIELD_OPTIONS, field_options);
    Test_add_field(FIELD_LAVG, field_until);
    Test_add_field(FIELD_SCHEDULE, field_schedule);
    Add_field(field_cmd);
    Add_field(field_endline);

    fields[TERM_LEN-1] = '\0';

    if ( send(fd, fields, (len < sizeof(fields)) ? len : sizeof(fields), 0) < 0 )
	error_e("error in send()");

}
Ejemplo n.º 17
0
int
save_file_safe(cf_t *file, char *final_path, char *prog_name, uid_t own_uid,
	       gid_t own_gid, time_t save_date)
/* save a file to a temp path, and then rename it (safely) to avoid loss of data
 * if a system crash, hardware failure, etc happens. */
{
    char temp_path[PATH_LEN+4];
    int final_path_len, temp_path_index;
    char *tmp_str = ".tmp";

    final_path_len = strlen(final_path);
    strncpy(temp_path, final_path, sizeof(temp_path)-sizeof(tmp_str));
    temp_path_index = ( final_path_len > sizeof(temp_path)-sizeof(tmp_str) ) ?
	sizeof(temp_path)-sizeof(tmp_str) : final_path_len;
    strcpy(&temp_path[temp_path_index], ".tmp");

    if ( save_one_file(file, temp_path, own_uid, own_gid, save_date) == OK ) {
	if ( rename(temp_path, final_path) != 0 ) {
	    error_e("Cannot rename %s to %s", temp_path, final_path);
	    error("%s will try to save the name to its definitive filename "
		  "directly.", prog_name);
	    error("If there is an error, root may consider to replace %s (which is "
		  "a valid copy) by %s manually.", final_path, temp_path);
	    if ( save_one_file(file, final_path, own_uid, own_gid, save_date) == ERR )
		return ERR;
	}
    }
    else {
	error("Since %s has not been able to save %s's file, it will keep "
	      "the previous version (if any) of %s.", prog_name, final_path, final_path);
	return ERR;
    }

    return OK;

}
Ejemplo n.º 18
0
int
main(int argc, char **argv)
{
    char *codeset = NULL;

    rootuid = get_user_uid_safe(ROOTNAME);
    rootgid = get_group_gid_safe(ROOTGROUP);

    /* we set it to 022 in order to get a pidfile readable by fcrontab
     * (will be set to 066 later) */
    saved_umask = umask(022);

    /* parse options */

    if (strrchr(argv[0], '/') == NULL)
        prog_name = argv[0];
    else
        prog_name = strrchr(argv[0], '/') + 1;

    {
        uid_t daemon_uid;
        if ((daemon_uid = getuid()) != rootuid)
            die("Fcron must be executed as root");
    }

    /* we have to set daemon_pid before the fork because it's
     * used in die() and die_e() functions */
    daemon_pid = getpid();

    /* save the value of the TZ env variable (used for option timezone) */
    orig_tz_envvar = strdup2(getenv("TZ"));

    parseopt(argc, argv);

    /* read fcron.conf and update global parameters */
    read_conf();

    /* initialize the logs before we become a daemon */
    xopenlog();

    /* change directory */

    if (chdir(fcrontabs) != 0)
        die_e("Could not change dir to %s", fcrontabs);

    /* Get the default locale character set for the mail
     * "Content-Type: ...; charset=" header */
    setlocale(LC_ALL, "");      /* set locale to system defaults or to
                                 * that specified by any  LC_* env vars */
    /* Except that "US-ASCII" is preferred to "ANSI_x3.4-1968" in MIME,
     * even though "ANSI_x3.4-1968" is the official charset name. */
    if ((codeset = nl_langinfo(CODESET)) != 0L &&
        strcmp(codeset, "ANSI_x3.4-1968") != 0)
        strncpy(default_mail_charset, codeset, sizeof(default_mail_charset));
    else
        strcpy(default_mail_charset, "US-ASCII");

    if (freopen("/dev/null", "r", stdin) == NULL)
        error_e("Could not open /dev/null as stdin");

    if (foreground == 0) {

        /* close stdout and stderr.
         * close unused descriptors
         * optional detach from controlling terminal */

        int fd;
        pid_t pid;

        switch (pid = fork()) {
        case -1:
            die_e("fork");
            break;
        case 0:
            /* child */
            break;
        default:
            /* parent */
/*  	    printf("%s[%d] " VERSION_QUOTED " : started.\n", */
/*  		   prog_name, pid); */
            exit(0);
        }

        daemon_pid = getpid();

        if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
#ifndef _HPUX_SOURCE
            ioctl(fd, TIOCNOTTY, 0);
#endif
            xclose_check(&fd, "/dev/tty");
        }

        if (freopen("/dev/null", "w", stdout) == NULL)
            error_e("Could not open /dev/null as stdout");
        if (freopen("/dev/null", "w", stderr) == NULL)
            error_e("Could not open /dev/null as stderr");

        /* close most other open fds */
        xcloselog();
        for (fd = 3; fd < 250; fd++)
            /* don't use xclose_check() as we do expect most of them to fail */
            (void)close(fd);

        /* finally, create a new session */
        if (setsid() == -1)
            error("Could not setsid()");

    }

    /* check if another fcron daemon is running, create pid file and lock it */
    get_lock();

    /* this program belongs to root : we set default permission mode
     * to  600 for security reasons, but we reset them to the saved
     * umask just before we run a job */
    umask(066);

    explain("%s[%d] " VERSION_QUOTED " started", prog_name, daemon_pid);

#ifdef HAVE_SIGNAL
    signal(SIGTERM, sigterm_handler);
    signal(SIGHUP, sighup_handler);
    siginterrupt(SIGHUP, 0);
    signal(SIGCHLD, sigchild_handler);
    siginterrupt(SIGCHLD, 0);
    signal(SIGUSR1, sigusr1_handler);
    siginterrupt(SIGUSR1, 0);
    signal(SIGUSR2, sigusr2_handler);
    siginterrupt(SIGUSR2, 0);
    /* we don't want SIGPIPE to kill fcron, and don't need to handle it */
    signal(SIGPIPE, SIG_IGN);
#elif HAVE_SIGSET
    sigset(SIGTERM, sigterm_handler);
    sigset(SIGHUP, sighup_handler);
    sigset(SIGCHLD, sigchild_handler);
    sigset(SIGUSR1, sigusr1_handler);
    sigset(SIGUSR2, sigusr2_handler);
    sigset(SIGPIPE, SIG_IGN);
#endif

    /* initialize job database */
    next_id = 0;

    /* initialize exe_array */
    exe_list = exe_list_init();

    /* initialize serial_array */
    serial_running = 0;
    serial_array_index = 0;
    serial_num = 0;
    serial_array_size = SERIAL_INITIAL_SIZE;
    serial_array =
        alloc_safe(serial_array_size * sizeof(cl_t *), "serial_array");

    /* initialize lavg_array */
    lavg_list = lavg_list_init();
    lavg_list->max_entries = lavg_queue_max;
    lavg_serial_running = 0;

#ifdef FCRONDYN
    /* initialize socket */
    init_socket();
#endif

    /* initialize random number generator :
     * WARNING : easy to guess !!! */
    /* we use the hostname and tv_usec in order to get different seeds
     * on two different machines starting fcron at the same moment */
    {
        char hostname[50];
        int i;
        unsigned int seed;
#ifdef HAVE_GETTIMEOFDAY
        struct timeval tv;      /* we use usec field to get more precision */
        gettimeofday(&tv, NULL);
        seed = ((unsigned int)tv.tv_usec) ^ ((unsigned int)tv.tv_sec);
#else
        seed = (unsigned int)time(NULL);
#endif
        gethostname(hostname, sizeof(hostname));

        for (i = 0; i < sizeof(hostname) - sizeof(seed); i += sizeof(seed))
            seed ^= (unsigned int)*(hostname + i);

        srand(seed);
    }

    main_loop();

    /* never reached */
    return EXIT_OK;
}
Ejemplo n.º 19
0
void
print_line(int fd, struct cl_t *line,  unsigned char *details, pid_t pid, int index,
	   time_t until)
    /* print some basic fields of a line, and some more if details == 1 */
{
    char buf[TERM_LEN];
    int len = 0;
    struct tm *ftime;


    len = snprintf(buf, sizeof(buf), "%-5ld", line->cl_id);
    if ( bit_test(details, FIELD_USER) )
	len += snprintf(buf+len, sizeof(buf)-len, " %-6s", line->cl_file->cf_user);
    if ( bit_test(details, FIELD_PID) )
	len += snprintf(buf+len, sizeof(buf)-len, " %-7d", pid);
    if ( bit_test(details, FIELD_INDEX) )
	len += snprintf(buf+len, sizeof(buf)-len, " %-5d", index);
    if ( bit_test(details, FIELD_RQ) )
	len += snprintf(buf+len, sizeof(buf)-len, " %-4d", line->cl_numexe);
    if ( bit_test(details, FIELD_OPTIONS) ) {
	char opt[9];
	int i = 0;
	opt[0] = '\0';
	if ( is_lavg(line->cl_option) )
	    i += snprintf(opt+i, sizeof(opt)-i, "L%.*s",
			  (is_lavg_sev(line->cl_option)) ? 0:1, "O");
	if ( is_serial(line->cl_option) )
	    i += snprintf(opt+i, sizeof(opt)-i, "%.*sS%.*s", i, ",",
			  (is_serial_sev(line->cl_option)) ? 0:1, "O");
	if ( is_exe_sev(line->cl_option) )
	    i += snprintf(opt+i, sizeof(opt)-i, "%.*sES", i, ",");

	len += snprintf(buf+len, sizeof(buf)-len, " %-9s", opt);
    }
    if ( bit_test(details, FIELD_LAVG) ) {
	len += snprintf(buf+len, sizeof(buf)-len, " %.1f,%.1f,%.1f",
			((double)((line->cl_lavg)[0]))/10,
			((double)((line->cl_lavg)[1]))/10,
			((double)((line->cl_lavg)[2]))/10);
	if ( until > 0 ) {
	    ftime = localtime( &until );
	    len += snprintf(buf+len, sizeof(buf)-len, " %02d/%02d/%d %02d:%02d %s",
			    (ftime->tm_mon + 1), ftime->tm_mday, (ftime->tm_year + 1900),
			    ftime->tm_hour, ftime->tm_min,
			    (is_strict(line->cl_option)) ? "Y":"N");
	}
	else
	    len += snprintf(buf+len, sizeof(buf)-len, " %18s", " (no until set) ");
    }
    if ( bit_test(details, FIELD_SCHEDULE) ) {
	ftime = localtime( &(line->cl_nextexe) );
	len += snprintf(buf+len, sizeof(buf)-len, " %02d/%02d/%d %02d:%02d",
			(ftime->tm_mon + 1), ftime->tm_mday, (ftime->tm_year + 1900),
			ftime->tm_hour, ftime->tm_min );
    }
    len += snprintf(buf+len, sizeof(buf)-len, " %s\n", line->cl_shell);

    if ( send(fd, buf, (len < sizeof(buf)) ? len : sizeof(buf), 0) < 0 )
	error_e("error in send()");
    
}
Ejemplo n.º 20
0
void
auth_client(struct fcrondyn_cl *client)
    /* check client identity */
{
    char *pass_cry = NULL;
    char *pass_sys = NULL;
    char *pass_str = NULL;

#ifdef HAVE_LIBSHADOW
    struct spwd *pass_sp = NULL;
    if ( (pass_sp = getspnam((char *) client->fcl_cmd )) == NULL ) {
	error_e("could not getspnam %s", (char *) client->fcl_cmd);
	send(client->fcl_sock_fd, "0", sizeof("0"), 0);
	return;
    }
    pass_sys = pass_sp->sp_pwdp;
#else
    struct passwd *pass = NULL;
    if ( (pass = getpwnam((char *) client->fcl_cmd )) == NULL ) {
	error_e("could not getpwnam %s", (char *) client->fcl_cmd);
	send(client->fcl_sock_fd, "0", sizeof("0"), 0);
	return;
    }
    pass_sys = pass->pw_passwd;
#endif

    /* */
    debug("auth_client() : socket : %d", client->fcl_sock_fd);
    /* */

    /* we need to limit auth failures : otherwise fcron may be used to "read"
     * shadow password !!! (or to crack it using a test-all-possible-password attack) */
    if (auth_fail > 0 && auth_nofail_since + AUTH_WAIT <= now )
	/* no auth time exceeded : set counter to 0 */
	auth_fail = 0;
    if (auth_fail >= MAX_AUTH_FAIL) {
	error("Too many authentication failures : try to connect later.");
	send(client->fcl_sock_fd, "0", sizeof("0"), 0);	    
	auth_fail = auth_nofail_since = 0;
	return;    
    }

    /* password is stored after user name */
    pass_str = &( (char *)client->fcl_cmd ) [ strlen( (char*)client->fcl_cmd ) + 1 ];
    if ( (pass_cry = crypt(pass_str, pass_sys)) == NULL ) {
	error_e("could not crypt()");
	send(client->fcl_sock_fd, "0", sizeof("0"), 0);
	return;
    }

/*      debug("pass_sp->sp_pwdp : %s", pass_sp->sp_pwdp); */
/*      debug("pass_cry : %s", pass_cry); */
    if (strcmp(pass_cry, pass_sys) == 0) {
	client->fcl_user = strdup2( (char *) client->fcl_cmd );
	send(client->fcl_sock_fd, "1", sizeof("1"), 0);
    }
    else {
	auth_fail++;
	auth_nofail_since = now;
	error("Invalid passwd for %s from socket %d",
	      (char *) client->fcl_cmd, client->fcl_sock_fd);
	send(client->fcl_sock_fd, "0", sizeof("0"), 0);
    }
}
Ejemplo n.º 21
0
void
sig_daemon(void)
    /* send SIGHUP to daemon to tell him configuration has changed */
    /* SIGHUP is sent once 10s before the next minute to avoid
     * some bad users to block daemon by sending it SIGHUP all the time */
{
    /* we don't need to make root wait */
    if (uid != rootuid) {
        time_t t = 0;
        int sl = 0;
        FILE *fp = NULL;
        int fd = 0;
        struct tm *tm = NULL;
        char sigfile[PATH_LEN];
        char buf[PATH_LEN];

        sigfile[0] = '\0';
        t = time(NULL);
        tm = localtime(&t);

        if ((sl = 60 - (t % 60) - 10) < 0) {
            if ((tm->tm_min = tm->tm_min + 2) >= 60) {
                tm->tm_hour++;
                tm->tm_min -= 60;
            }
            snprintf(buf, sizeof(buf), "%02d:%02d", tm->tm_hour, tm->tm_min);
            sl = 60 - (t % 60) + 50;
        }
        else {
            if (++tm->tm_min >= 60) {
                tm->tm_hour++;
                tm->tm_min -= 60;
            }
            snprintf(buf, sizeof(buf), "%02d:%02d", tm->tm_hour, tm->tm_min);
        }
        fprintf(stderr, "Modifications will be taken into account"
                " at %s.\n", buf);

        /* if fcrontabs is too long, snprintf will not be able to add "/fcrontab.sig"
         * string at the end of sigfile */
        if (strlen(fcrontabs) > (sizeof(sigfile) - sizeof("/fcrontab.sig")))
            die("fcrontabs string too long (more than %d characters)",
                (sizeof(sigfile) - sizeof("/fcrontab.sig")));
        snprintf(sigfile, sizeof(sigfile), "%s/fcrontab.sig", fcrontabs);

        switch (fork()) {
        case -1:
            remove(sigfile);
            die_e("could not fork : daemon has not been signaled");
            break;
        case 0:
            /* child */
            break;
        default:
            /* parent */
            return;
        }

        foreground = 0;

        /* try to create a lock file */
        /* // */
        debug("uid: %d, euid: %d, gid: %d, egid: %d", getuid(), geteuid(),
              getgid(), getegid());
        /* // */
        fd = open(sigfile, O_RDWR | O_CREAT, 0644);
        if (fd == -1)
            die_e("can't open or create %s", sigfile);
        fp = fdopen(fd, "r+");
        if (fp == NULL)
            die_e("can't fdopen %s", sigfile);


#ifdef HAVE_FLOCK
        if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
            debug("fcrontab is already waiting for signalling the daemon :"
                  " exiting.");
            return;
        }
#else                           /* HAVE_FLOCK */
        if (lockf(fd, F_TLOCK, 0) != 0) {
            debug("fcrontab is already waiting for signalling the daemon :"
                  " exiting.");
            return;
        }
#endif                          /* ! HAVE_FLOCK */

        sleep(sl);

        /* also closes the underlying file descriptor fd: */
        xfclose_check(&fp, sigfile);
        /* also reset fd, now closed by xfclose_check(), to make it clear it is closed */
        fd = -1;

        if (remove(sigfile) < 0)
            error_e("Could not remove %s");
    }
    else
        /* we are root */
        fprintf(stderr,
                "Modifications will be taken into account" " right now.\n");

    if ((daemon_pid = read_pid()) == 0)
        /* daemon is not running any longer : we exit */
        return;

    foreground = 1;

#ifdef USE_SETE_ID
    if (seteuid(rootuid) != 0)
        error_e("seteuid(rootuid)");
#endif                          /* USE_SETE_ID */

    if (kill(daemon_pid, SIGHUP) != 0)
        die_e("could not send SIGHUP to daemon (pid %d)", daemon_pid);

#ifdef USE_SETE_ID
    /* get user's permissions */
    if (seteuid(fcrontab_uid) != 0)
        die_e("Could not change euid to " USERNAME "[%d]", uid);
#endif                          /* USE_SETE_ID */

}
Ejemplo n.º 22
0
void
edit_file(char *buf)
    /* copy file to a temp file, edit that file, and install it
       if necessary */
{
    char *cureditor = NULL;
    char editorcmd[PATH_LEN];
    pid_t pid;
    int status;
    struct stat st;
    time_t mtime = 0;
    char *tmp_str;
    FILE *f, *fi;
    int file = 0;
    int c;
    char correction = 0;
    short return_val = EXIT_OK;

    explain("fcrontab : editing %s's fcrontab", user);	

    if ((cureditor=getenv("VISUAL")) == NULL || strcmp(cureditor, "\0") == 0 )
	if((cureditor=getenv("EDITOR"))==NULL || strcmp(cureditor, "\0") == 0 )
	    cureditor = editor;
	
    file = temp_file(&tmp_str);
    if ( (fi = fdopen(file, "w")) == NULL ) {
	error_e("could not fdopen");
	goto exiterr;
    }
#ifndef USE_SETE_ID
    if (fchown(file, asuid, asgid) != 0) {
	error_e("Could not fchown %s to asuid and asgid", tmp_str);
	goto exiterr;
    }
#endif
    /* copy user's fcrontab (if any) to a temp file */
    if ( (f = fopen(buf, "r")) == NULL ) {
	if ( errno != ENOENT ) {
	    error_e("could not open file %s", buf);
	    goto exiterr;
	}
	else
	    fprintf(stderr, "no fcrontab for %s - using an empty one\n",
		    user);
    }
    else { 
	/* copy original file to temp file */
	while ( (c=getc(f)) != EOF )
	    putc(c, fi);
	fclose(f);
    }

    fclose(fi);
    close(file);

    do {

	if ( stat(tmp_str, &st) == 0 )
	    mtime = st.st_mtime;
	else {
	    error_e("could not stat \"%s\"", buf);
	    goto exiterr;
	}

	switch ( pid = fork() ) {
	case 0:
	    /* child */
	    if ( uid != ROOTUID ) {
		if (setgid(asgid) < 0) {
		    error_e("setgid(asgid)");
		    goto exiterr;
		}
		if (setuid(asuid) < 0) {
		    error_e("setuid(asuid)");
		    goto exiterr;
		}
	    }
	    else {
		/* Some programs, like perl, require gid=egid : */
		if ( setgid(getgid()) < 0 ) {
		    error_e("setgid(getgid())");
		    goto exiterr;
		}
	    }
	    snprintf(editorcmd, sizeof(editorcmd), "%s %s", cureditor, tmp_str);
	    execlp(shell, shell, "-c", editorcmd, tmp_str, NULL);
	    error_e("Error while running \"%s\"", cureditor);
	    goto exiterr;

	case -1:
	    error_e("fork");
	    goto exiterr;

	default:
	    /* parent */
	    break ;
	}
	    
	/* only reached by parent */
	waitpid(pid, &status, 0);
	if ( ! WIFEXITED(status) ) {
	    fprintf(stderr, "Editor exited abnormally:"
		    " fcrontab is unchanged.\n");
	    goto exiterr;
	}

#ifndef USE_SETE_ID
	/* we have chown the tmp file to user's name : user may have
	 * linked the tmp file to a file owned by root. In that case, as
	 * fcrontab is setuid root, user may read some informations he is not
	 * allowed to read :
	 * we *must* check that the tmp file is not a link. */

	/* open the tmp file, chown it to root and chmod it to avoid race
	 * conditions */
	/* make sure that the tmp file is not a link */
	{
	    int fd = 0;
	    if ( (fd = open(tmp_str, O_RDONLY)) <= 0 ||
		 fstat(fd, &st) != 0 || ! S_ISREG(st.st_mode) ||
		 S_ISLNK(st.st_mode) || st.st_uid != asuid || st.st_nlink > 1){
		fprintf(stderr, "%s is not a valid regular file.\n", tmp_str);
		close(fd);
		goto exiterr;
	    }
	    if ( fchown(fd, ROOTUID, ROOTGID) != 0 || fchmod(fd, S_IRUSR|S_IWUSR) != 0 ){
		fprintf(stderr, "Can't chown or chmod %s.\n", tmp_str);
		close(fd);
		goto exiterr;
	    }
	    close(fd);
	}
#endif
	
	/* check if file has been modified */
	if ( stat(tmp_str, &st) != 0 ) {
	    error_e("could not stat %s", tmp_str);
	    goto exiterr;
	}    

	else if ( st.st_mtime > mtime || correction == 1) {

	    correction = 0;

	    switch ( read_file(tmp_str) ) {
	    case ERR:
		goto exiterr;
	    case 2:
		fprintf(stderr, "\nFile contains some errors. "
			"Ignore [i] or Correct [c] ? ");
		/* the 2nd getchar() is for the newline char (\n) */
		while ( (c = getchar())	&& c != 'i' && c != 'c' ) {
		    fprintf(stderr, "Please press c to correct, "
			    "or i to ignore: ");
		    while (c != '\n')
			c = getchar();
		}
		if ( c == 'c' ) {
		    /* free memory used to store the list */
		    delete_file(user);
		    correction = 1;
		}
		break;
	    default:
		break;
	    }

	}
	else {
	    fprintf(stderr, "Fcrontab is unchanged :"
		    " no need to install it.\n"); 
	    goto end;
	}

    } while ( correction == 1);

    if ( write_file(tmp_str) != OK )
	return_val = EXIT_ERR;
    
    /* free memory used to store the list */
    delete_file(user);
    
    /* tell daemon to update the conf */
    need_sig = 1;
    
  end:
    if ( remove(tmp_str) != 0 )
	error_e("could not remove %s", tmp_str);
    free(tmp_str);
    xexit (return_val);

  exiterr:
    if ( remove(tmp_str) != 0 )
	error_e("could not remove %s", tmp_str);
    free(tmp_str);
    xexit (EXIT_ERR);

}
Ejemplo n.º 23
0
int
run_job(struct exe_t *exeent)
    /* fork(), redirect outputs to a temp file, and execl() the task.
     * Return ERR if it could not fork() the first time, OK otherwise. */
{

    pid_t pid;
    cl_t *line = exeent->e_line;
    int pipe_pid_fd[2];
    int ret = 0;

    /* prepare the job execution */
    if (pipe(pipe_pid_fd) != 0) {
        error_e("pipe(pipe_pid_fd) : setting job_pid to -1");
        exeent->e_job_pid = -1;
        pipe_pid_fd[0] = pipe_pid_fd[1] = -1;
    }

#ifdef CHECKRUNJOB
    debug
        ("run_job(): first pipe created successfully : about to do first fork()");
#endif                          /* CHECKRUNJOB */

    switch (pid = fork()) {
    case -1:
        error_e("Fork error : could not exec '%s'", line->cl_shell);
        return ERR;
        break;

    case 0:
        /* child */
        {
            struct passwd *pas = NULL;
            char **jobenv = NULL;
            char **sendmailenv = NULL;
            char *curshell = NULL;
            char *curhome = NULL;
            char *content_type = NULL;
            char *encoding = 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();
#endif

            /* // */
            debug("run_job(): child: %s, output to %s, %s, %s\n",
                  is_mail(line->cl_option) ? "mail" : "no mail",
                  to_stdout ? "stdout" : "file",
                  foreground ? "running in foreground" :
                  "running in background",
                  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, &content_type, &encoding);

            /* close unneeded READ fd */
            xclose_check(&(pipe_pid_fd[0]), "child's pipe_pid_fd[0]");

            pipe_fd[0] = pipe_fd[1] = -1;
            if (!to_stdout && is_mail(line->cl_option)) {
                /* we create the temp file (if needed) before change_user(),
                 * 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, content_type, encoding, jobenv);
                mailpos = ftell(mailf);
                if (pipe(pipe_fd) != 0)
                    die_e("could not pipe() (job not executed)");
            }

            become_user(line, pas, curhome);
            Free_safe(curhome);

            /* restore umask to default */
            umask(saved_umask);

            sig_dfl();

#ifdef CHECKRUNJOB
            debug
                ("run_job(): child: change_user() done -- about to do 2nd fork()");
#endif                          /* CHECKRUNJOB */

            /* now, run the job */
            switch (pid = fork()) {
            case -1:
                error_e("Fork error : could not exec '%s'", line->cl_shell);
                if (write(pipe_pid_fd[1], &pid, sizeof(pid)) < 0)
                    error_e("could not write child pid to pipe_pid_fd[1]");
                xclose_check(&(pipe_fd[0]), "child's pipe_fd[0]");
                xclose_check(&(pipe_fd[1]), "child's pipe_fd[1]");
                xclose_check(&(pipe_pid_fd[1]), "child's pipe_pid_fd[1]");
                exit(EXIT_ERR);
                break;

            case 0:
                /* grand child (child of the 2nd fork) */

                /* the grand child does not use this pipe: close remaining fd */
                xclose_check(&(pipe_pid_fd[1]), "grand child's pipe_pid_fd[1]");

                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) */

                run_job_grand_child_setup_nice(line);

                xcloselog();

#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 */
                debug("run_job(): grand-child: Executing \"%s -c %s\"",
                      curshell, line->cl_shell);
#endif                          /* CHECKJOBS OR CHECKRUNJOB */

#ifdef WITH_SELINUX
                if (flask_enabled
                    && setexeccon(line->cl_file->cf_user_context) < 0)
                    die_e("Can't set execute context '%s' for user '%s'.",
                          line->cl_file->cf_user_context, line->cl_runas);
#else
                if (setsid() == -1) {
                    die_e("setsid(): errno %d", errno);
                }
#endif
                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 */

            default:
                /* child (parent of the 2nd fork) */

                /* close unneeded WRITE pipe and READ pipe */
                xclose_check(&(pipe_fd[1]), "child's pipe_fd[1]");

#ifdef CHECKRUNJOB
                debug("run_job(): child: pipe_fd[1] and pipe_pid_fd[0] closed"
                      " -- about to write grand-child pid to pipe");
#endif                          /* CHECKRUNJOB */

                /* 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");
                    }
                }

#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]): */
                    xfclose_check(&pipef, "child's 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");
#endif                          /* CHECKRUNJOB */
                xclose_check(&(pipe_pid_fd[1]), "child's pipe_pid_fd[1]");

                /* 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);
#endif                          /* CHECKRUNJOB */
                    end_job(line, status, mailf, mailpos, sendmailenv);
                }

                /* execution never gets here */

            }

            /* execution should never gets here, but if it happened we exit with an error */
            exit(EXIT_ERR);
        }

    default:
        /* parent */

        /* close unneeded WRITE fd */
        xclose_check(&(pipe_pid_fd[1]), "parent's pipe_pid_fd[1]");

        exeent->e_ctrl_pid = pid;

#ifdef CHECKRUNJOB
        debug("run_job(): about to read grand-child pid...");
#endif                          /* CHECKRUNJOB */

        /* read the pid of the job */
        ret = read_pipe(pipe_pid_fd[0], &(exeent->e_job_pid), sizeof(pid_t));
        if (ret != OK) {
            if (ret == ERR) {
                error("Could not read job pid because of closed pipe:"
                      " setting it to -1");
            }
            else {
                errno = ret;
                error_e("Could not read job pid : setting it to -1");
            }

            exeent->e_job_pid = -1;
        }
        xclose_check(&(pipe_pid_fd[0]), "parent's pipe_pid_fd[0]");

#ifdef CHECKRUNJOB
        debug
            ("run_job(): finished reading pid of the job -- end of run_job().");
#endif                          /* CHECKRUNJOB */

    }

    return OK;

}
Ejemplo n.º 24
0
int
main(int argc, char **argv)
{

#ifdef HAVE_LIBPAM
    int    retcode = 0;
    const char * const * env;
#endif
#ifdef USE_SETE_ID
    struct passwd *pass;
#endif

    memset(buf, 0, sizeof(buf));
    memset(file, 0, sizeof(file));

    if (strrchr(argv[0],'/')==NULL) prog_name = argv[0];
    else prog_name = strrchr(argv[0],'/')+1;
    
    uid = getuid();

    /* get current dir */
    if ( getcwd(orig_dir, sizeof(orig_dir)) == NULL )
	die_e("getcwd");

    /* interpret command line options */
    parseopt(argc, argv);

#ifdef USE_SETE_ID
    if ( ! (pass = getpwnam(USERNAME)) )
	die("user \"%s\" is not in passwd file. Aborting.", USERNAME);
    fcrontab_uid = pass->pw_uid;
    fcrontab_gid = pass->pw_gid;

#ifdef HAVE_LIBPAM
    /* Open PAM session for the user and obtain any security
       credentials we might need */

    debug("username: %s", user);
    retcode = pam_start("fcrontab", user, &apamconv, &pamh);
    if (retcode != PAM_SUCCESS) die_pame(pamh, retcode, "Could not start PAM");
    retcode = pam_authenticate(pamh, 0);    /* is user really user? */
    if (retcode != PAM_SUCCESS)
	die_pame(pamh, retcode, "Could not authenticate user using PAM (%d)", retcode);
    retcode = pam_acct_mgmt(pamh, 0); /* permitted access? */
    if (retcode != PAM_SUCCESS)
	die_pame(pamh, retcode, "Could not init PAM account management (%d)", retcode);
    retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
    if (retcode != PAM_SUCCESS) die_pame(pamh, retcode, "Could not set PAM credentials");
    retcode = pam_open_session(pamh, 0);
    if (retcode != PAM_SUCCESS) die_pame(pamh, retcode, "Could not open PAM session");

    env = (const char * const *) pam_getenvlist(pamh);
    while (env && *env) {
	if (putenv((char*) *env)) die_e("Could not copy PAM environment");
	env++;
    }

    /* Close the log here, because PAM calls openlog(3) and
       our log messages could go to the wrong facility */
    xcloselog();
#endif /* USE_PAM */

    if (uid != fcrontab_uid)
	if (seteuid(fcrontab_uid) != 0) 
	    die_e("Couldn't change euid to fcrontab_uid[%d]",fcrontab_uid);
    /* change directory */
    if (chdir(fcrontabs) != 0) {
	error_e("Could not chdir to %s", fcrontabs);
	xexit (EXIT_ERR);
    }
    /* get user's permissions */
    if (seteuid(uid) != 0) 
	die_e("Could not change euid to %d", uid); 
    if (setegid(fcrontab_gid) != 0) 
	die_e("Could not change egid to " GROUPNAME "[%d]", fcrontab_gid); 

#else /* USE_SETE_ID */

    if (setuid(ROOTUID) != 0 ) 
	die_e("Could not change uid to ROOTUID"); 
    if (setgid(ROOTGID) != 0)
    	die_e("Could not change gid to ROOTGID");
    /* change directory */
    if (chdir(fcrontabs) != 0) {
	error_e("Could not chdir to %s", fcrontabs);
	xexit (EXIT_ERR);
    }
#endif /* USE_SETE_ID */
    
    /* this program is seteuid : we set default permission mode
     * to 640 for a normal user, 600 for root, for security reasons */
    if ( asuid == ROOTUID )
	umask(066);  /* octal : '0' + number in octal notation */
    else
	umask(026);

    snprintf(buf, sizeof(buf), "%s.orig", user);

    /* determine what action should be taken */
    if ( file_opt ) {

	if ( strcmp(argv[file_opt], "-") == 0 )

	    xexit(install_stdin());

	else {

	    if ( *argv[file_opt] != '/' )
		/* this is just the file name, not the path : complete it */
		snprintf(file, sizeof(file), "%s/%s", orig_dir, argv[file_opt]);
	    else {
		strncpy(file, argv[file_opt], sizeof(file) - 1);
		file[sizeof(file)-1] = '\0';
	    }

	    if (make_file(file) == OK)
		xexit(EXIT_OK);
	    else
		xexit(EXIT_ERR);

	}

    } 

    /* remove user's entries */
    if ( rm_opt == 1 ) {
	if ( remove_fcrontab(1) == ENOENT )
	    fprintf(stderr, "no fcrontab for %s\n", user);
	xexit (EXIT_OK);
    }

    /* list user's entries */
    if ( list_opt == 1 ) {
	list_file(buf);
	xexit(EXIT_OK);
    }


    /* edit user's entries */
    if ( edit_opt == 1 ) {
	edit_file(buf);
	xexit(EXIT_OK);
    }

    /* reinstall user's entries */
    if ( reinstall_opt == 1 ) {
	reinstall(buf);
	xexit(EXIT_OK);
    }

    /* never reached */
    return EXIT_OK;
}
Ejemplo n.º 25
0
FILE *
create_mail(cl_t * line, char *subject, char *content_type, char *encoding,
            char **env)
    /* create a temp file and write in it a mail header */
{
    /* create temporary file for stdout and stderr of the job */
    int mailfd = temp_file(NULL);
    FILE *mailf = fdopen(mailfd, "r+");
    char hostname[USER_NAME_LEN];
    /* is this a complete mail address ? (ie. with a "@", not only a username) */
    char add_hostname = 0;
    int i = 0;

    if (mailf == NULL)
        die_e("Could not fdopen() mailfd");

#ifdef HAVE_GETHOSTNAME
    if (gethostname(hostname, sizeof(hostname)) != 0) {
        error_e("Could not get hostname");
        hostname[0] = '\0';
    }
    else {
        /* it is unspecified whether a truncated hostname is NUL-terminated */
        hostname[USER_NAME_LEN - 1] = '\0';

        /* check if mailto is a complete mail address */
        add_hostname = (strchr(line->cl_mailto, '@') == NULL) ? 1 : 0;
    }
#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, "Subject: fcron <%s@%s> %s: %s\n",
                line->cl_file->cf_user, (hostname[0] != '\0') ? hostname : "?",
                subject, line->cl_shell);
    else
        fprintf(mailf, "Subject: fcron <%s@%s> %s\n", line->cl_file->cf_user,
                (hostname[0] != '\0') ? hostname : "?", line->cl_shell);

    if (content_type == NULL) {
        fprintf(mailf, "Content-Type: text/plain; charset=%s\n",
                default_mail_charset);
    }
    else {
        /* user specified Content-Type header. */
        char *c = NULL;

        /* Remove new-lines or users could specify arbitrary mail headers!
         * (fcrontab should already prevent that, but better safe than sorry) */
        for (c = content_type; *c != '\0'; c++) {
            if (*c == '\n')
                *c = ' ';
        }
        fprintf(mailf, "Content-Type: %s\n", content_type);
    }

    if (encoding != NULL) {
        char *c = NULL;

        /* Remove new-lines or users could specify arbitrary mail headers!
         * (fcrontab should already prevent that, but better safe than sorry) */
        for (c = encoding; *c != '\0'; c++) {
            if (*c == '\n')
                *c = ' ';
        }
        fprintf(mailf, "Content-Transfer-Encoding: %s\n", encoding);
    }

    /* 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;
}
Ejemplo n.º 26
0
void
check_socket(int num)
    /* check for new connection, command, connection closed */
{
    int fd = -1, avoid_fd = -1, addr_len = sizeof(struct sockaddr_un);
    struct sockaddr_un client_addr;
    long int buf_int[SOCKET_MSG_LEN];
    int read_len = 0;
    struct fcrondyn_cl *client = NULL, *prev_client = NULL;

    if ( num <= 0 )
	/* no socket to check : go directly to the end of that function */
	goto final_settings;

    debug("Checking socket ...");

    if ( FD_ISSET(listen_fd, &read_set) ) {
	debug("got new connection ...");
	if ((fd = accept(listen_fd, (struct sockaddr *)&client_addr, &addr_len)) == -1) {
	    error_e("could not accept new connection : isset(listen_fd = %d) = %d",
		    listen_fd, FD_ISSET(listen_fd, &read_set));
	}
	else {
	    fcntl(fd, F_SETFD, 1);
	    /* set fd to O_NONBLOCK : we do not want fcron to be stopped on error, etc */
	    if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) == -1) {
		error_e("Could not set fd attribute O_NONBLOCK : connection rejected.");
		shutdown(fd, SHUT_RDWR);
		close(fd);
	    }
	    else {
		Alloc(client, fcrondyn_cl);
		client->fcl_sock_fd = fd;
		/* means : not authenticated yet : */
		client->fcl_user = NULL;
		client->fcl_cmd = NULL;

		/* include new entry in client list */
		client->fcl_next = fcrondyn_cl_base;
		fcrondyn_cl_base = client;
		client->fcl_idle_since = now;
		/* to avoid trying to read from it in this call */
		avoid_fd = fd;
		
		FD_SET(fd, &master_set);
		if ( fd > set_max_fd )
		    set_max_fd = fd;
		fcrondyn_cl_num += 1;
		
		debug("Added connection fd : %d - %d connections", fd, fcrondyn_cl_num);
	    }
	}
    }

    client = fcrondyn_cl_base;
    while ( client != NULL ) {
	if (! FD_ISSET(client->fcl_sock_fd, &read_set) || client->fcl_sock_fd==avoid_fd){
	    /* check if the connection has not been idle for too long ... */
	    if (client->fcl_user==NULL && now - client->fcl_idle_since > MAX_AUTH_TIME ){
		warn("Connection with no auth for more than %ds : closing it.",
		     MAX_AUTH_TIME);
		remove_connection(&client, prev_client);
	    }
	    else if ( now - client->fcl_idle_since > MAX_IDLE_TIME ) {
		warn("Connection of %s is idle for more than %ds : closing it.",
		     client->fcl_user, MAX_IDLE_TIME);
		remove_connection(&client, prev_client);
	    }
	    else {
		/* nothing to do on this one ... check the next one */
		prev_client = client;
		client = client->fcl_next;
	    }
	    continue;
	}

	if ( (read_len = recv(client->fcl_sock_fd, buf_int, sizeof(buf_int), 0)) <= 0 ) {
	    if (read_len == 0) {
		/* connection closed by client */
		remove_connection(&client, prev_client);
	    }
	    else {
		error_e("error recv() from sock fd %d", client->fcl_sock_fd);
		prev_client = client;
		client = client->fcl_next;
	    }
	}
	else {
	    client->fcl_cmd_len = read_len;
	    client->fcl_cmd = buf_int;
	    if ( client->fcl_user == NULL )
		/* not authenticated yet */
		auth_client(client);
	    else {
		/* we've just read a command ... */
		client->fcl_idle_since = now;
		exe_cmd(client);
	    }
	    prev_client = client;
	    client = client->fcl_next;
	}
    }

  final_settings:
    /* copy master_set in read_set, because read_set is modified by select() */
    read_set = master_set;
}
Ejemplo n.º 27
0
int
talk_fcron(char *cmd_str, int fd)
    /* read a string command, check if it is valid and translate it,
     * send it to fcron, and print its answer */
{
    long int *cmd = NULL;
    int cmd_len = 0;
    char buf[LINE_LEN];
    size_t read_len = 0;
    char existing_connection = (fd < 0) ? 0 : 1;
    fd_set read_set;            /* needed to use select to check if some data is waiting */
    struct timeval tv;

    switch (parse_cmd(cmd_str, &cmd, &cmd_len)) {
    case OK:
        break;
    case HELP_CMD:
        {
            int i, j, len;
            printf("Command recognized by fcrondyn :\n");
            printf("------------------------------\n");
            for (i = 0; i < cmd_list_len; i++) {
                len = printf("%s ", cmd_list[i].cmd_name);

                /* print args : */
                for (j = 0; j < cmd_list[i].cmd_numopt; j++) {
                    if (cmd_list[i].cmd_default[j] != ARG_REQUIRED)
                        len += printf("[");
                    switch (cmd_list[i].cmd_opt[j]) {
                    case USER:
                        len += printf("user");
                        break;
                    case JOBID:
                        len += printf("jobid");
                        break;
                    case TIME_AND_DATE:
                        len += printf("time");
                        break;
                    case NICE_VALUE:
                        len += printf("niceval");
                        break;
                    case SIGNAL:
                        len += printf("sig");
                        break;
                    case BOOLEAN:
                        len += printf("bool");
                        break;
                    default:
                        len += printf("unknown_arg!");
                    }
                    if (cmd_list[i].cmd_default[j] != ARG_REQUIRED)
                        len += printf("]");
                    len += printf(" ");
                }
                /* Align correctly the descriptions : */
                printf("%*s%s", 24 - len, "", cmd_list[i].cmd_desc);

                /* print alias list (if any) */
                if (cmd_list[i].cmd_alias[0] != NULL) {
                    printf(" (aliases:");
                    for (j = 0;
                         j < MAX_NUM_ALIAS && cmd_list[i].cmd_alias[j] != NULL;
                         j++) {
                        printf(" %s", cmd_list[i].cmd_alias[j]);
                    }
                    printf(")");
                }

                printf("\n");
            }
        }
        return HELP_CMD;
    case QUIT_CMD:
        return QUIT_CMD;
    case CMD_NOT_FOUND:
        return CMD_NOT_FOUND;
    case INVALID_ARG:
        return INVALID_ARG;
    case ZEROLEN_CMD:
        return ZEROLEN_CMD;
    default:
        return ERR;
    }

    /* This is a valid command (so we'll have to free() it) ... */

    if (!existing_connection && (fd = connect_fcron()) == ERR)
        return ERR;

    send(fd, cmd, cmd_len * sizeof(long int), 0);
    Free_safe(cmd);
    cmd_len = 0;

    tv.tv_sec = MAX_WAIT_TIME;
    tv.tv_usec = 0;
    FD_ZERO(&read_set);
    FD_SET(fd, &read_set);
    if (select(fd + 1, &read_set, NULL, NULL, &tv) <= 0) {
        error_e("Couldn't get data from socket during %d seconds.",
                MAX_WAIT_TIME);
        return ERR;
    }


    while ((read_len = (size_t) recv(fd, buf, sizeof(buf) - 1, 0)) >= 0
           || errno == EINTR) {

        if (errno == EINTR && debug_opt)
            fprintf(stderr, "got EINTR ...\n");
        else if (read_len > sizeof(buf)) {
            /* weird ... no data yet ? */
            if (debug_opt)
                fprintf(stderr, "no data yet ?");
        }
        else if (read_len <= 0) {
            if (debug_opt)
                fprintf(stderr, "read_len = %d\n", (int)read_len);
            fprintf(stderr, "connection closed by fcron\n");
            shutdown(fd, SHUT_RDWR);
            return ERR;
        }
        else {
            /* ensure the string is terminated by a '\0' for when we'll printf() it */
            buf[read_len] = '\0';
            printf("%s", buf);

            /* check for the end of command output marker */
            if (read_len >= sizeof(END_STR) &&
                strncmp(&buf[read_len - sizeof(END_STR)], END_STR,
                        sizeof(END_STR)) == 0)
                break;
        }
    }
    if (read_len < 0) {
        error_e("error in recv()");
    }

    if (!existing_connection)
        xclose_check(&fd, "unix socket");

    return OK;
}
Ejemplo n.º 28
0
void
init_socket(void)
    /* do everything needed to get a working listening socket */
{
    struct sockaddr_un addr;
    int len = 0;

    /* used in fcron.c:main_loop():select() */
    FD_ZERO(&read_set);
    FD_ZERO(&master_set);

    if ( (listen_fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1 ) {
	error_e("Could not create socket : fcrondyn won't work");
	return;
    }

    addr.sun_family = AF_UNIX;
    if ( (len = strlen(fifofile)) > sizeof(addr.sun_path) ) {
	error("Error : fifo file path too long (max is %d)", sizeof(addr.sun_path));
	goto err;
    }
    strncpy(addr.sun_path, fifofile, sizeof(addr.sun_path) - 1);
    addr.sun_path[sizeof(addr.sun_path) -1 ] = '\0';

    unlink(fifofile);
    if (bind(listen_fd, (struct sockaddr *) &addr,  sizeof(addr.sun_family)+len) != 0) {
	error_e("Cannot bind socket to '%s'", fifofile);
	goto err;
    }

    if ( listen(listen_fd, MAX_CONNECTION) != 0 ) {
	error_e("Cannot set socket in listen mode");
	goto err;
    }

    /* */
    if ( chmod(fifofile, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) != 0 )
	error_e("Cannot chmod() socket file");
    /* */
	 
    fcntl(listen_fd, F_SETFD, 1);
    /* set listen_fd to O_NONBLOCK : we do not want fcron to be stopped on error, etc */
    if ( fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFL) | O_NONBLOCK) == -1 ) {
	error_e("Could not set listen_fd attribute O_NONBLOCK : no fcrondyn support");
	goto err;
    }

    /* no error */
    FD_SET(listen_fd, &master_set);
    if ( listen_fd > set_max_fd )
	set_max_fd = listen_fd;
    
    /* copy master in read_fs, because read_fs will be modified by select() */
    read_set = master_set;
    debug("Socket initialized : listen_fd : %d set_max_fd : %d ", listen_fd, set_max_fd);
    return;

  err:
    close_socket();

}
Ejemplo n.º 29
0
void
setup_user_and_env(struct cl_t *cl, struct passwd *pas,
                   char ***sendmailenv, char ***jobenv, char **curshell,
                   char **curhome, char **content_type, char **encoding)
/* 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;
    char **env;
#endif

    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);

    if (cl->cl_tz != NULL)
        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 != NULL && shell[0] != '\0')
        /* default: use value from fcron.conf */
        env_list_setenv(env_list, "SHELL", shell, 1);
    else
        /* shell is empty, ie. not defined: fail back to /etc/passwd's value */
        env_list_setenv(env_list, "SHELL", pas->pw_shell, 1);

#if ( ! defined(RUN_NON_PRIVILEGED)) && defined(HAVE_LIBPAM)
    /* Open PAM session for the user and obtain any security
     * credentials we might need */

    retcode = pam_start("fcron", pas->pw_name, &apamconv, &pamh);
    if (retcode != PAM_SUCCESS)
        die_pame(pamh, retcode, "Could not start PAM for '%s'", cl->cl_shell);
    /* Some system seem to need that pam_authenticate() call.
     * Anyway, we have no way to authentificate the user :
     * 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", 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", 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",
                      env_list);
    retcode = pam_open_session(pamh, PAM_SILENT);
    if (retcode != PAM_SUCCESS)
        die_mail_pame(cl, retcode, pas, "Could not open PAM session", env_list);

    for (env = 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                          /* ( ! 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);

        *jobenv = env_list_export_envp(env_list);

    }

    if (content_type != NULL) {
        (*content_type) = strdup2(env_list_getenv(env_list, "CONTENT_TYPE"));
    }
    if (encoding != NULL) {
        (*encoding) =
            strdup2(env_list_getenv(env_list, "CONTENT_TRANSFER_ENCODING"));
    }

    env_list_destroy(env_list);

}