From 6207bfc0fc5b8060dec89311eb3e8944b8895f8d Mon Sep 17 00:00:00 2001 From: Danny Auble <da@llnl.gov> Date: Tue, 24 Nov 2009 00:44:38 +0000 Subject: [PATCH] Patch from Aaron Knister and Mark Grondona, to parse correctly quoted #SBATCH options in a batch script. --- NEWS | 2 ++ src/sbatch/opt.c | 82 +++++++++++++++++++++++++-------------------- src/sbatch/opt.h | 2 +- src/sbatch/sbatch.c | 7 ++-- 4 files changed, 53 insertions(+), 40 deletions(-) diff --git a/NEWS b/NEWS index a8579470425..a231dab97ab 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,8 @@ documents those changes that are of interest to users and admins. -- Accounting - Slurmctld and slurmdbd will now set uids of users which were created after the start of the daemons on reconfig. Slurmdbd will attempt to set previously non-existant uids every hour. + -- Patch from Aaron Knister and Mark Grondona, to parse correctly quoted + #SBATCH options in a batch script. * Changes in SLURM 2.1.0-pre7 ============================= diff --git a/src/sbatch/opt.c b/src/sbatch/opt.c index c6eafb93729..70d9377e8d7 100644 --- a/src/sbatch/opt.c +++ b/src/sbatch/opt.c @@ -180,10 +180,10 @@ static void _help(void); static void _opt_default(void); /* set options from batch script */ -static void _opt_batch_script(const void *body, int size); +static void _opt_batch_script(const char *file, const void *body, int size); /* set options from pbs batch script */ -static void _opt_pbs_batch_script(const void *body, int size); +static void _opt_pbs_batch_script(const char *file, const void *body, int size); /* set options based upon env vars */ static void _opt_env(void); @@ -817,14 +817,14 @@ char *process_options_first_pass(int argc, char **argv) * 3. update options with commandline args * 4. perform some verification that options are reasonable */ -int process_options_second_pass(int argc, char *argv[], +int process_options_second_pass(int argc, char *argv[], const char *file, const void *script_body, int script_size) { /* set options from batch script */ - _opt_batch_script(script_body, script_size); + _opt_batch_script(file, script_body, script_size); /* set options from pbs batch script */ - _opt_pbs_batch_script(script_body, script_size); + _opt_pbs_batch_script(file, script_body, script_size); /* set options from env vars */ _opt_env(); @@ -870,12 +870,13 @@ static char *_next_line(const void *buf, int size, void **state) ptr = current = (char *)*state; while ((*ptr != '\n') && (ptr < ((char *)buf+size))) ptr++; - if (*ptr == '\n') - ptr++; line = xstrndup(current, (ptr-current)); - *state = (void *)ptr; + /* + * Advance state past newline + */ + *state = (ptr < ((char *) buf + size)) ? ptr+1 : ptr; return line; } @@ -891,15 +892,17 @@ static char *_next_line(const void *buf, int size, void **state) * RET - xmalloc'ed argument string (may be shorter than "skipped") * or NULL if no arguments remaining */ -static char *_get_argument(const char *line, int *skipped) +static char * +_get_argument(const char *file, int lineno, const char *line, int *skipped) { - char *ptr; + const char *ptr; char argument[BUFSIZ]; + char q_char = '\0'; bool escape_flag = false; - bool no_isspace_check = false; + bool quoted = false; int i; - ptr = (char *)line; + ptr = line; *skipped = 0; /* skip whitespace */ @@ -909,41 +912,43 @@ static char *_get_argument(const char *line, int *skipped) if (*ptr == '\0') return NULL; - + /* copy argument into "argument" buffer, */ i = 0; - while ((no_isspace_check || !isspace(*ptr)) - && *ptr != '\n' - && *ptr != '\0') { + while ((quoted || !isspace(*ptr)) && *ptr != '\n' && *ptr != '\0') { if (escape_flag) { escape_flag = false; - argument[i] = *ptr; - ptr++; - i++; } else if (*ptr == '\\') { escape_flag = true; ptr++; - } else if (*ptr == '"') { - /* toggle the no_isspace_check flag */ - no_isspace_check = no_isspace_check? false : true; - ptr++; + continue; + } else if (quoted) { + if (*ptr == q_char) { + quoted = false; + ptr++; + continue; + } + } else if (*ptr == '"' || *ptr == '\'') { + quoted = true; + q_char = *(ptr++); + continue; } else if (*ptr == '#') { /* found an un-escaped #, rest of line is a comment */ break; - } else { - argument[i] = *ptr; - ptr++; - i++; } + + argument[i++] = *(ptr++); } + argument[i] = '\0'; + + if (quoted) /* Unmatched quote */ + fatal ("%s: line %d: Unmatched `%c` in [%s]\n", + file, lineno, q_char, line); *skipped = ptr - line; - if (i > 0) { - return xstrndup(argument, i); - } else { - return NULL; - } + + return (i > 0 ? xstrdup (argument) : NULL); } /* @@ -952,7 +957,7 @@ static char *_get_argument(const char *line, int *skipped) * Build an argv-style array of options from the script "body", * then pass the array to _set_options for() further parsing. */ -static void _opt_batch_script(const void *body, int size) +static void _opt_batch_script(const char * file, const void *body, int size) { char *magic_word1 = "#SBATCH"; char *magic_word2 = "#SLURM"; @@ -963,7 +968,7 @@ static void _opt_batch_script(const void *body, int size) char *line; char *option; char *ptr; - int skipped = 0, warned = 0; + int skipped = 0, warned = 0, lineno = 0; int i; magic_word_len1 = strlen(magic_word1); @@ -975,6 +980,7 @@ static void _opt_batch_script(const void *body, int size) argv[0] = "sbatch"; while((line = _next_line(body, size, &state)) != NULL) { + lineno++; if (!strncmp(line, magic_word1, magic_word_len1)) ptr = line + magic_word_len1; else if (!strncmp(line, magic_word2, magic_word_len2)) { @@ -991,7 +997,7 @@ static void _opt_batch_script(const void *body, int size) } /* this line starts with the magic word */ - while ((option = _get_argument(ptr, &skipped)) != NULL) { + while ((option = _get_argument(file, lineno, ptr, &skipped))) { debug2("Found in script, argument \"%s\"", option); argc += 1; xrealloc(argv, sizeof(char*) * argc); @@ -1015,7 +1021,7 @@ static void _opt_batch_script(const void *body, int size) * Build an argv-style array of options from the script "body", * then pass the array to _set_options for() further parsing. */ -static void _opt_pbs_batch_script(const void *body, int size) +static void _opt_pbs_batch_script(const char *file, const void *body, int size) { char *magic_word = "#PBS"; int magic_word_len; @@ -1026,6 +1032,7 @@ static void _opt_pbs_batch_script(const void *body, int size) char *option; char *ptr; int skipped = 0; + int lineno = 0; int i; magic_word_len = strlen(magic_word); @@ -1035,6 +1042,7 @@ static void _opt_pbs_batch_script(const void *body, int size) argv[0] = "sbatch"; while((line = _next_line(body, size, &state)) != NULL) { + lineno++; if (strncmp(line, magic_word, magic_word_len) != 0) { xfree(line); continue; @@ -1042,7 +1050,7 @@ static void _opt_pbs_batch_script(const void *body, int size) /* this line starts with the magic word */ ptr = line + magic_word_len; - while ((option = _get_argument(ptr, &skipped)) != NULL) { + while ((option = _get_argument(file, lineno, ptr, &skipped))) { debug2("Found in script, argument \"%s\"", option); argc += 1; xrealloc(argv, sizeof(char*) * argc); diff --git a/src/sbatch/opt.h b/src/sbatch/opt.h index aa1e5207a1a..12a818318c8 100644 --- a/src/sbatch/opt.h +++ b/src/sbatch/opt.h @@ -194,7 +194,7 @@ char *process_options_first_pass(int argc, char **argv); * 3. update options with commandline args * 4. perform some verification that options are reasonable */ -int process_options_second_pass(int argc, char *argv[], +int process_options_second_pass(int argc, char *argv[], const char *file, const void *script_body, int script_size); /* external functions available for SPANK plugins to modify the environment diff --git a/src/sbatch/sbatch.c b/src/sbatch/sbatch.c index ecc3f9d84d9..a9a42e80b92 100644 --- a/src/sbatch/sbatch.c +++ b/src/sbatch/sbatch.c @@ -112,8 +112,11 @@ int main(int argc, char *argv[]) if (script_body == NULL) exit(error_exit); - if (process_options_second_pass((argc - opt.script_argc), argv, - script_body, script_size) < 0) { + if (process_options_second_pass( + (argc - opt.script_argc), + argv, + xbasename (script_name), + script_body, script_size) < 0) { error("sbatch parameter parsing"); exit(error_exit); } -- GitLab