From c49b2952d6e3910bb4852aab1112e83736cebfd8 Mon Sep 17 00:00:00 2001
From: Morris Jette <jette@schedmd.com>
Date: Mon, 11 Jul 2016 14:48:36 -0700
Subject: [PATCH] Add support for sbatch --bbf option

bug 2858
---
 NEWS                  |  1 +
 doc/man/man1/sbatch.1 |  8 +++++++
 src/sbatch/opt.c      | 54 +++++++++++++++++++++++++++++++++++++++----
 src/sbatch/opt.h      |  1 +
 src/sbatch/sbatch.c   | 53 +++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 111 insertions(+), 6 deletions(-)

diff --git a/NEWS b/NEWS
index ee8f0286342..aff86374ffd 100644
--- a/NEWS
+++ b/NEWS
@@ -18,6 +18,7 @@ documents those changes that are of interest to users and administrators.
     value.
  -- Add ConstrainKmemSpace to cgroup.conf, defaulting to yes, to allow
     cgroup Kmem enforcement to be disabled while still using ConstrainRAMSpace.
+ -- Add support for sbatch --bbf option.
 
 * Changes in Slurm 16.05.3
 ==========================
diff --git a/doc/man/man1/sbatch.1 b/doc/man/man1/sbatch.1
index 97b5bda9923..329c337a130 100644
--- a/doc/man/man1/sbatch.1
+++ b/doc/man/man1/sbatch.1
@@ -141,6 +141,14 @@ If not specified, the scontrol show job will display 'ReqS:C:T=*:*:*'.
 \fB\-\-bb\fR=<\fIspec\fR>
 Burst buffer specification.
 The form of the specification is system dependent.
+Also see \fB\-\-bbf\fR.
+
+.TP
+\fB\-\-bbf\fR=<\fIfile_name\fR>
+Path of file containing burst buffer specification.
+The form of the specification is system dependent.
+These burst buffer directives will be inserted into the submitted batch script.
+Also see \fB\-\-bb\fR.
 
 .TP
 \fB\-\-begin\fR=<\fItime\fR>
diff --git a/src/sbatch/opt.c b/src/sbatch/opt.c
index 267d3dc2b79..49c817a43a8 100644
--- a/src/sbatch/opt.c
+++ b/src/sbatch/opt.c
@@ -143,7 +143,6 @@ enum wrappers {
 #define LONG_OPT_WRAP        0x118
 #define LONG_OPT_REQUEUE     0x119
 #define LONG_OPT_NETWORK     0x120
-#define LONG_OPT_BURST_BUFFER    0x126
 #define LONG_OPT_QOS             0x127
 #define LONG_OPT_SOCKETSPERNODE  0x130
 #define LONG_OPT_CORESPERSOCKET  0x131
@@ -184,6 +183,8 @@ enum wrappers {
 #define LONG_OPT_SPREAD_JOB      0x162
 #define LONG_OPT_MCS_LABEL       0x165
 #define LONG_OPT_DEADLINE        0x166
+#define LONG_OPT_BURST_BUFFER_SPEC 0x167
+#define LONG_OPT_BURST_BUFFER_FILE 0x168
 
 /*---- global variables, defined in opt.h ----*/
 opt_t opt;
@@ -226,6 +227,7 @@ static uint16_t _parse_pbs_mail_type(const char *arg);
 
 static void  _usage(void);
 static void _fullpath(char **filename, const char *cwd);
+static char *_read_file(char *fname);
 static void _set_options(int argc, char **argv);
 static void _parse_pbs_resource_list(char *rl);
 
@@ -282,7 +284,7 @@ static bool _valid_node_list(char **node_list_pptr)
 /*
  * _opt_default(): used by initialize_and_process_args to set defaults
  */
-static void _opt_default()
+static void _opt_default(void)
 {
 	char buf[MAXPATHLEN + 1];
 	int i;
@@ -412,6 +414,40 @@ static void _opt_default()
 	opt.mcs_label		= NULL;
 }
 
+/* Read specified file's contents into a buffer.
+ * Caller must xfree the buffer's contents */
+static char *_read_file(char *fname)
+{
+	int fd, i, offset = 0;
+	struct stat stat_buf;
+	char *file_buf;
+
+	fd = open(fname, O_RDONLY);
+	if (fd < 0) {
+		fatal("Could not open burst buffer specification file %s: %m",
+		      fname);
+	}
+	if (fstat(fd, &stat_buf) < 0) {
+		fatal("Could not stat burst buffer specification file %s: %m",
+		      fname);
+	}
+	file_buf = xmalloc(stat_buf.st_size);
+	while (stat_buf.st_size > offset) {
+		i = read(fd, file_buf + offset, stat_buf.st_size - offset);
+		if (i < 0) {
+			if (errno == EAGAIN)
+				continue;
+			fatal("Could not read burst buffer specification "
+			      "file %s: %m", fname);
+		}
+		if (i == 0)
+			break;	/* EOF */
+		offset += i;
+	}
+	close(fd);
+	return file_buf;
+}
+
 /*---[ env var processing ]-----------------------------------------------*/
 
 /*
@@ -744,7 +780,8 @@ static struct option long_options[] = {
 	{"nodelist",      required_argument, 0, 'w'},
 	{"exclude",       required_argument, 0, 'x'},
 	{"acctg-freq",    required_argument, 0, LONG_OPT_ACCTG_FREQ},
-	{"bb",            required_argument, 0, LONG_OPT_BURST_BUFFER},
+	{"bb",            required_argument, 0, LONG_OPT_BURST_BUFFER_SPEC},
+	{"bbf",           required_argument, 0, LONG_OPT_BURST_BUFFER_FILE},
 	{"begin",         required_argument, 0, LONG_OPT_BEGIN},
 	{"blrts-image",   required_argument, 0, LONG_OPT_BLRTS_IMAGE},
 	{"checkpoint",    required_argument, 0, LONG_OPT_CHECKPOINT},
@@ -1588,10 +1625,14 @@ static void _set_options(int argc, char **argv)
 			opt.mcs_label = xstrdup(optarg);
 			break;
 		}
-		case LONG_OPT_BURST_BUFFER:
+		case LONG_OPT_BURST_BUFFER_SPEC:
 			xfree(opt.burst_buffer);
 			opt.burst_buffer = xstrdup(optarg);
 			break;
+		case LONG_OPT_BURST_BUFFER_FILE:
+			xfree(opt.burst_buffer_file);
+			opt.burst_buffer_file = _read_file(optarg);
+			break;
 		case LONG_OPT_NICE:
 			if (optarg)
 				opt.nice = strtol(optarg, NULL, 10);
@@ -3180,6 +3221,7 @@ static void _opt_list(void)
 	} else
 		info("core-spec         : %d", opt.core_spec);
 	info("burst_buffer      : `%s'", opt.burst_buffer);
+	info("burst_buffer_file : `%s'", opt.burst_buffer_file);
 	info("remote command    : `%s'", str);
 	info("power             : %s", power_flags_str(opt.power_flags));
 	info("wait              : %s", opt.wait ? "no" : "yes");
@@ -3223,7 +3265,8 @@ static void _usage(void)
 "              [--mem_bind=...] [--reservation=name] [--mcs-label=mcs]\n"
 "              [--cpu-freq=min[-max[:gov]] [--power=flags] [--gres-flags=opts]\n"
 "              [--switches=max-switches{@max-time-to-wait}] [--reboot]\n"
-"              [--core-spec=cores] [--thread-spec=threads] [--bb=burst_buffer_spec]\n"
+"              [--core-spec=cores] [--thread-spec=threads]\n"
+"              [--bb=burst_buffer_spec] [--bbf=burst_buffer_file]\n"
 "              [--array=index_values] [--profile=...] [--ignore-pbs] [--spread-job]\n"
 "              [--export[=names]] [--export-file=file|fd] executable [args...]\n");
 }
@@ -3239,6 +3282,7 @@ static void _help(void)
 "  -a, --array=indexes         job array index values\n"
 "  -A, --account=name          charge job to specified account\n"
 "      --bb=<spec>             burst buffer specifications\n"
+"      --bbf=<file_name>       burst buffer specification file\n"
 "      --begin=time            defer job until HH:MM MM/DD/YY\n"
 "  -M, --clusters=names        Comma separated list of clusters to issue\n"
 "                              commands to.  Default is current cluster.\n"
diff --git a/src/sbatch/opt.h b/src/sbatch/opt.h
index 398b2890543..15883a7bb47 100644
--- a/src/sbatch/opt.h
+++ b/src/sbatch/opt.h
@@ -184,6 +184,7 @@ typedef struct sbatch_options {
 	uint32_t cpu_freq_gov;  /* cpu frequency governor */
 	bool test_only;		/* --test-only			*/
 	char *burst_buffer;	/* -bb				*/
+	char *burst_buffer_file;/* -bbf				*/
 	uint8_t power_flags;	/* Power management options	*/
 	char *mcs_label;	/* mcs label if mcs plugin in use */
 	time_t deadline;	/* ---deadline                  */
diff --git a/src/sbatch/sbatch.c b/src/sbatch/sbatch.c
index a464dedf944..067c0f3dd18 100644
--- a/src/sbatch/sbatch.c
+++ b/src/sbatch/sbatch.c
@@ -62,6 +62,7 @@
 
 #define MAX_RETRIES 15
 
+static void  _add_bb_to_script(char **script_body, char *burst_buffer_file);
 static void  _env_merge_filter(job_desc_msg_t *desc);
 static int   _fill_job_desc_from_opts(job_desc_msg_t *desc);
 static int   _check_cluster_specific_settings(job_desc_msg_t *desc);
@@ -81,7 +82,7 @@ int main(int argc, char *argv[])
 	job_desc_msg_t desc;
 	submit_response_msg_t *resp;
 	char *script_name;
-	void *script_body;
+	char *script_body;
 	int script_size = 0;
 	int rc = 0, retries = 0;
 
@@ -125,6 +126,10 @@ int main(int argc, char *argv[])
 		exit(error_exit);
 	}
 
+	if (opt.burst_buffer_file)
+		_add_bb_to_script(&script_body, opt.burst_buffer_file);
+		
+
 	if (spank_init_post_opt() < 0) {
 		error("Plugin stack post-option processing failed");
 		exit(error_exit);
@@ -224,6 +229,52 @@ int main(int argc, char *argv[])
 	return rc;
 }
 
+/* Insert the contents of "burst_buffer_file" into "script_body" */
+static void  _add_bb_to_script(char **script_body, char *burst_buffer_file)
+{
+	char *orig_script = *script_body;
+	char *new_script, *sep, save_char;
+	int i;
+
+	if (!burst_buffer_file || (burst_buffer_file[0] == '\0'))
+		return;	/* No burst buffer file or empty file */
+
+	if (!orig_script) {
+		*script_body = xstrdup(burst_buffer_file);
+		return;
+	}
+
+	i = strlen(burst_buffer_file) - 1;
+	if (burst_buffer_file[i] != '\n')	/* Append new line as needed */
+		xstrcat(burst_buffer_file, "\n");
+
+	if (orig_script[0] != '#') {
+		/* Prepend burst buffer file */
+		new_script = xstrdup(burst_buffer_file);
+		xstrcat(new_script, orig_script);
+		*script_body = new_script;
+		return;
+	}
+
+	sep = strchr(orig_script, '\n');
+	if (sep) {
+		save_char = sep[1];
+		sep[1] = '\0';
+		new_script = xstrdup(orig_script);
+		xstrcat(new_script, burst_buffer_file);
+		sep[1] = save_char;
+		xstrcat(new_script, sep + 1);
+		*script_body = new_script;
+		return;
+	} else {
+		new_script = xstrdup(orig_script);
+		xstrcat(new_script, "\n");
+		xstrcat(new_script, burst_buffer_file);
+		*script_body = new_script;
+		return;
+	}
+}
+
 /* Wait for specified job ID to terminate, return it's exit code */
 static int _job_wait(uint32_t job_id)
 {
-- 
GitLab