diff --git a/NEWS b/NEWS index 5fc78bbdbd14b8378eb305ccd0884f8098b5dddf..85336dab451c7a9a170441cd9e95f259be461ea7 100644 --- a/NEWS +++ b/NEWS @@ -130,6 +130,7 @@ documents those changes that are of interest to users and admins. -- Avoid printing negative job run time in squeue due to clock skew. -- In sched/wiki and sched/wiki2, add support for wiki.conf option HidePartitionJobs (see man pages for details). + -- Update to srun/sbatch --get-user-env option logic (needed by Moab). * Changes in SLURM 1.2.21 ========================= diff --git a/contribs/env_cache_builder.c b/contribs/env_cache_builder.c index 5bcd9149cadc79a6fbac431c26f2e98dfccc5e0d..f13d7f8e799a3e1e9c121125f8a42b7c26b782ed 100644 --- a/contribs/env_cache_builder.c +++ b/contribs/env_cache_builder.c @@ -14,7 +14,7 @@ * * This program must execute as user root. ***************************************************************************** - * Copyright (C) 2007 The Regents of the University of California. + * Copyright (C) 2007-2008 The Regents of the University of California. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). * Written by Morris Jette <jette1@llnl.gov>. * UCRL-CODE-226842. @@ -53,16 +53,18 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <strings.h> #include <unistd.h> +#include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> -#define DEBUG 0 +#define _DEBUG 0 #define SU_WAIT_MSEC 8000 static long int _build_cache(char *user_name, char *cache_dir); static int _get_cache_dir(char *buffer, int buf_size); -static void _parse_line(char *in_line, char **user_name, int *user_id); +static int _parse_line(char *in_line, char **user_name, int *user_id); main (int argc, char **argv) { @@ -80,19 +82,17 @@ main (int argc, char **argv) exit(1); strncat(cache_dir, "/env_cache", sizeof(cache_dir)); if (mkdir(cache_dir, 0500) && (errno != EEXIST)) { - printf("Could not create cache directory %s: %s", cache_dir, + printf("Could not create cache directory %s: %s\n", cache_dir, strerror(errno)); exit(1); } -#if DEBUG printf("cache_dir=%s\n", cache_dir); -#endif for (i=1; i<argc; i++) { delta_t = _build_cache(argv[i], cache_dir); -#if DEBUG - printf("user %-8s time %ld usec\n", argv[i], delta_t); -#endif + if (delta_t < ((SU_WAIT_MSEC * 0.8) * 1000)) + continue; + printf("WARNING: user %-8s time %ld usec\n", argv[i], delta_t); } if (i > 1) exit(0); @@ -104,52 +104,76 @@ main (int argc, char **argv) } while (fgets(in_line, sizeof(in_line), passwd_fd)) { - _parse_line(in_line, &user_name, &user_id); + if (_parse_line(in_line, &user_name, &user_id) < 0) + continue; if (user_id <= 100) continue; delta_t = _build_cache(user_name, cache_dir); -#if DEBUG if (delta_t < ((SU_WAIT_MSEC * 0.8) * 1000)) continue; - printf("user %-8s time %ld usec\n", user_name, delta_t); -#endif + printf("WARNING: user %-8s time %ld usec\n", user_name, delta_t); } fclose(passwd_fd); } -/* Given a line from /etc/passwd, return the user_name and user_id */ -static void _parse_line(char *in_line, char **user_name, int *user_id) +/* Given a line from /etc/passwd, sets the user_name and user_id + * RET -1 if user can't login, 0 otherwise */ +static int _parse_line(char *in_line, char **user_name, int *user_id) { - char *tok; - + char *tok, *shell; + + /* user name */ *user_name = strtok(in_line, ":"); + (void) strtok(NULL, ":"); + + /* uid */ tok = strtok(NULL, ":"); if (tok) *user_id = atoi(tok); else { - printf("error parsing /etc/passwd: %s\n", in_line); + printf("ERROR: parsing /etc/passwd: %s\n", in_line); *user_id = 0; } + + (void) strtok(NULL, ":"); /* gid */ + (void) strtok(NULL, ":"); /* name */ + (void) strtok(NULL, ":"); /* home */ + + shell = strtok(NULL, ":"); + if (shell) { + tok = strchr(shell, '\n'); + if (tok) + tok[0] = '\0'; + if ((strcmp(shell, "/sbin/nologin") == 0) || + (strcmp(shell, "/bin/false") == 0)) + return -1; + } + + return 0; + } /* For a given user_name, get his environment variable by executing * "su - <user_name> -c env" and store the result in * cache_dir/env_<user_name> - * Returns time to perform the operation in usec + * Returns time to perform the operation in usec or -1 on error */ static long int _build_cache(char *user_name, char *cache_dir) { - FILE *su, *cache; - char line[BUFSIZ], name[BUFSIZ], value[BUFSIZ], out_file[BUFSIZ]; + FILE *cache; + char *line, *last, out_file[BUFSIZ], buffer[64 * 1024]; char *starttoken = "XXXXSLURMSTARTPARSINGHEREXXXX"; char *stoptoken = "XXXXSLURMSTOPPARSINGHEREXXXXX"; int fildes[2], found, fval, len, rc, timeleft; + int buf_read, buf_rem; pid_t child; struct timeval begin, now; struct pollfd ufds; long int delta_t; + gettimeofday(&begin, NULL); + if (pipe(fildes) < 0) { perror("pipe"); return -1; @@ -166,13 +190,14 @@ static long int _build_cache(char *user_name, char *cache_dir) dup2(fildes[1], 1); close(2); open("/dev/null", O_WRONLY); - snprintf(line, sizeof(line), - "echo; echo; echo; echo %s; env; echo %s", + snprintf(buffer, sizeof(buffer), + "/bin/echo; /bin/echo; /bin/echo; " + "/bin/echo %s; /bin/env; /bin/echo %s", starttoken, stoptoken); #ifdef LOAD_ENV_NO_LOGIN - execl("/bin/su", "su", user_name, "-c", line, NULL); + execl("/bin/su", "su", user_name, "-c", buffer, NULL); #else - execl("/bin/su", "su", "-", user_name, "-c", line, NULL); + execl("/bin/su", "su", "-", user_name, "-c", buffer, NULL); #endif exit(1); } @@ -180,30 +205,30 @@ static long int _build_cache(char *user_name, char *cache_dir) close(fildes[1]); if ((fval = fcntl(fildes[0], F_GETFL, 0)) >= 0) fcntl(fildes[0], F_SETFL, fval | O_NONBLOCK); - su = fdopen(fildes[0], "r"); - gettimeofday(&begin, NULL); ufds.fd = fildes[0]; ufds.events = POLLIN; + ufds.revents = 0; - /* First look for the start token in the output */ - len = strlen(starttoken); + /* Read all of the output from /bin/su into buffer */ found = 0; - while (!found) { + buf_read = 0; + bzero(buffer, sizeof(buffer)); + while (1) { gettimeofday(&now, NULL); timeleft = SU_WAIT_MSEC * 10; timeleft -= (now.tv_sec - begin.tv_sec) * 1000; timeleft -= (now.tv_usec - begin.tv_usec) / 1000; if (timeleft <= 0) { -#if DEBUG - printf("timeout1\n"); +#if _DEBUG + printf("timeout1 for %s\n", user_name); #endif break; } if ((rc = poll(&ufds, 1, timeleft)) <= 0) { if (rc == 0) { -#if DEBUG - printf("timeout2\n"); +#if _DEBUG + printf("timeout2 for %s\n, user_name"); #endif break; } @@ -213,88 +238,97 @@ static long int _build_cache(char *user_name, char *cache_dir) break; } if (!(ufds.revents & POLLIN)) { - perror("POLLERR|POLLHUP"); + if (ufds.revents & POLLHUP) { /* EOF */ +#if _DEBUG + printf("POLLHUP for %s\n", user_name); +#endif + found = 1; /* success */ + } else if (ufds.revents & POLLERR) { + printf("ERROR: POLLERR for %s\n", user_name); + } else { + printf("ERROR: poll() revents=%d for %s\n", + ufds.revents, user_name); + } break; } - while (fgets(line, BUFSIZ, su)) { - if (!strncmp(line, starttoken, len)) { - found = 1; - break; - } + buf_rem = sizeof(buffer) - buf_read; + if (buf_rem == 0) { + printf("ERROR: buffer overflow for %s\n", user_name); + break; + } + rc = read(fildes[0], &buffer[buf_read], buf_rem); + if (rc > 0) + buf_read += rc; + else if (rc == 0) { /* EOF */ +#if _DEBUG + printf("EOF for %s\n", user_name); +#endif + found = 1; /* success */ + break; + } else { /* error */ + perror("read"); + break; } } + close(fildes[0]); if (!found) { - printf("Failed to get current user environment variables " - "for %s\n", user_name); - close(fildes[0]); - gettimeofday(&now, NULL); - delta_t = now.tv_sec - begin.tv_sec * 1000000; - delta_t += now.tv_usec - begin.tv_usec; - if (delta_t < (SU_WAIT_MSEC * 1000)) - return (SU_WAIT_MSEC * 1000); - return delta_t; + printf("ERROR: Failed to load current user environment " + "variables for %s\n", user_name); + return -1; + } + + /* First look for the start token in the output */ + len = strlen(starttoken); + found = 0; + line = strtok_r(buffer, "\n", &last); + while (!found && line) { + if (!strncmp(line, starttoken, len)) { + found = 1; + break; + } + line = strtok_r(NULL, "\n", &last); + } + if (!found) { + printf("ERROR: Failed to get current user environment " + "variables for %s\n", user_name); + return -1; } snprintf(out_file, sizeof(out_file), "%s/%s", cache_dir, user_name); cache = fopen(out_file, "w"); if (!cache) { - printf("Could not create cache file %s: %s\n", out_file, - strerror(errno)); + printf("ERROR: Could not create cache file %s for %s: %s\n", + out_file, user_name, strerror(errno)); + return -1; } + chmod(out_file, 0600); + /* Process environment variables until we find the stop token */ len = strlen(stoptoken); found = 0; - while (!found && cache) { - gettimeofday(&now, NULL); - timeleft = SU_WAIT_MSEC * 10; - timeleft -= (now.tv_sec - begin.tv_sec) * 1000; - timeleft -= (now.tv_usec - begin.tv_usec) / 1000; - if (timeleft <= 0) { -#if DEBUG - printf("timeout3\n"); -#endif - break; - } - if ((rc = poll(&ufds, 1, timeleft)) <= 0) { - if (rc == 0) { -#if DEBUG - printf("timeout4\n"); -#endif - break; - } - if ((errno == EINTR) || (errno == EAGAIN)) - continue; - perror("poll"); - break; - } - if (!(ufds.revents & POLLIN)) { - perror("POLLERR|POLLHUP"); - break; - } - /* stop at the line containing the stoptoken string */ - if ((fgets(line, BUFSIZ, su) == 0) || - (!strncmp(line, stoptoken, len))) { + line = strtok_r(NULL, "\n", &last); + while (!found && line) { + if (!strncmp(line, stoptoken, len)) { found = 1; break; } - - if (fputs(line, cache) == EOF) { - printf("Could not write cache file %s: %s\n", - out_file, strerror(errno)); + if (fprintf(cache, "%s\n",line) < 0) { + printf("ERROR: Could not write cache file %s " + "for %s: %s\n", + out_file, user_name, strerror(errno)); found = 1; /* quit now */ } + line = strtok_r(NULL, "\n", &last); } - close(fildes[0]); - if (cache) - fclose(cache); + fclose(cache); waitpid(-1, NULL, WNOHANG); gettimeofday(&now, NULL); delta_t = (now.tv_sec - begin.tv_sec) * 1000000; delta_t += now.tv_usec - begin.tv_usec; if (!found) { - printf("Failed to get current user environment variables " - "for %s\n", user_name); + printf("ERROR: Failed to write all user environment " + "variables for %s\n", user_name); if (delta_t < (SU_WAIT_MSEC * 1000)) return (SU_WAIT_MSEC * 1000); } @@ -349,7 +383,7 @@ static int _get_cache_dir(char *buffer, int buf_size) } close(fildes[0]); if (!buffer[0]) { - printf("Failed to get StateSaveLocation\n"); + printf("ERROR: Failed to get StateSaveLocation\n"); close(fildes[0]); return -1; } diff --git a/src/common/env.c b/src/common/env.c index 8f71b4c9e0f37498f9a68f4eebe570a29959f42d..0b7ad731dfeebc850038e4a5ebd05d1da7363989 100644 --- a/src/common/env.c +++ b/src/common/env.c @@ -1300,15 +1300,14 @@ char **_load_env_cache(const char *username) */ char **env_array_user_default(const char *username, int timeout, int mode) { - FILE *su; - char line[ENV_BUFSIZE]; - char name[128]; - char value[ENV_BUFSIZE]; + char *line, *last, name[128], value[ENV_BUFSIZE]; + char buffer[ENV_BUFSIZE]; char **env = NULL; char *starttoken = "XXXXSLURMSTARTPARSINGHEREXXXX"; char *stoptoken = "XXXXSLURMSTOPPARSINGHEREXXXXX"; char cmdstr[256]; int fildes[2], found, fval, len, rc, timeleft; + int buf_read, buf_rem; pid_t child; struct timeval begin, now; struct pollfd ufds; @@ -1335,7 +1334,8 @@ char **env_array_user_default(const char *username, int timeout, int mode) close(2); open("/dev/null", O_WRONLY); snprintf(cmdstr, sizeof(cmdstr), - "/bin/echo; /bin/echo; /bin/echo; /bin/echo %s; /bin/env; /bin/echo %s", + "/bin/echo; /bin/echo; /bin/echo; " + "/bin/echo %s; /bin/env; /bin/echo %s", starttoken, stoptoken); if (mode == 1) execl("/bin/su", "su", username, "-c", cmdstr, NULL); @@ -1354,25 +1354,24 @@ char **env_array_user_default(const char *username, int timeout, int mode) close(fildes[1]); if ((fval = fcntl(fildes[0], F_GETFL, 0)) >= 0) fcntl(fildes[0], F_SETFL, fval | O_NONBLOCK); - su= fdopen(fildes[0], "r"); gettimeofday(&begin, NULL); ufds.fd = fildes[0]; ufds.events = POLLIN; - /* First look for the start token in the output */ - len = strlen(starttoken); + /* Read all of the output from /bin/su into buffer */ found = 0; - while (!found) { + buf_read = 0; + bzero(buffer, sizeof(buffer)); + while (1) { gettimeofday(&now, NULL); - if (timeout > 0) - timeleft = timeout * 1000; - else - timeleft = SU_WAIT_MSEC; + timeleft = SU_WAIT_MSEC; timeleft -= (now.tv_sec - begin.tv_sec) * 1000; timeleft -= (now.tv_usec - begin.tv_usec) / 1000; - if (timeleft <= 0) + if (timeleft <= 0) { + verbose("timeout waiting for /bin/su to complete"); break; + } if ((rc = poll(&ufds, 1, timeleft)) <= 0) { if (rc == 0) { verbose("timeout waiting for /bin/su to complete"); @@ -1380,64 +1379,76 @@ char **env_array_user_default(const char *username, int timeout, int mode) } if ((errno == EINTR) || (errno == EAGAIN)) continue; - error("poll: %m"); + error("poll(): %m"); break; } - if (!(ufds.revents & POLLIN)) - break; - while (fgets(line, sizeof(line), su)) { - if (!strncmp(line, starttoken, len)) { - found = 1; - break; + if (!(ufds.revents & POLLIN)) { + if (ufds.revents & POLLHUP) { /* EOF */ + found = 1; /* success */ + } else if (ufds.revents & POLLERR) { + error("POLLERR"); + } else { + error("poll() revents=%d", ufds.revents); } + break; + } + buf_rem = sizeof(buffer) - buf_read; + if (buf_rem == 0) { + error("buffer overflow loading env vars"); + break; + } + rc = read(fildes[0], &buffer[buf_read], buf_rem); + if (rc > 0) + buf_read += rc; + else if (rc == 0) { /* EOF */ + found = 1; /* success */ + break; + } else { /* error */ + error("read(env pipe): %m"); + break; + } + } + close(fildes[0]); + if (!found) { + error("Failed to load current user environment variables"); + _load_env_cache(username); + } + + /* First look for the start token in the output */ + len = strlen(starttoken); + found = 0; + line = strtok_r(buffer, "\n", &last); + while (!found && line) { + if (!strncmp(line, starttoken, len)) { + found = 1; + break; } + line = strtok_r(NULL, "\n", &last); } if (!found) { error("Failed to get current user environment variables"); - close(fildes[0]); return _load_env_cache(username); } - /* Now read in the environment variable strings. */ - env = env_array_create(); + /* Process environment variables until we find the stop token */ len = strlen(stoptoken); found = 0; - while (!found) { - gettimeofday(&now, NULL); - if (timeout > 0) - timeleft = timeout * 1000; - else - timeleft = SU_WAIT_MSEC; - timeleft -= (now.tv_sec - begin.tv_sec) * 1000; - timeleft -= (now.tv_usec - begin.tv_usec) / 1000; - if (timeleft <= 0) - break; - if ((rc = poll(&ufds, 1, timeleft)) <= 0) { - if (rc == 0) { - verbose("timeout waiting for /bin/su to complete"); - break; - } - if ((errno == EINTR) || (errno == EAGAIN)) - continue; - error("poll: %m"); - break; - } - /* stop at the line containing the stoptoken string */ - if (!(ufds.revents & POLLIN)) - break; - if ((fgets(line, sizeof(line), su) == 0) || - (!strncmp(line, stoptoken, len))) { + env = env_array_create(); + line = strtok_r(NULL, "\n", &last); + while (!found && line) { + if (!strncmp(line, stoptoken, len)) { found = 1; break; } - - _strip_cr_nl(line); - if (_env_array_entry_splitter(line, name, sizeof(name), + if (_env_array_entry_splitter(line, name, sizeof(name), value, sizeof(value))) env_array_overwrite(&env, name, value); + line = strtok_r(NULL, "\n", &last); + } + if (!found) { + error("Failed to get all user environment variables"); + return _load_env_cache(username); } - close(fildes[0]); return env; } -