1 /*****************************************************************************\
2  *  proc_args.c - helper functions for command argument processing
3  *****************************************************************************
4  *  Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
5  *  Portions Copyright (C) 2010-2015 SchedMD LLC <https://www.schedmd.com>.
6  *  Written by Christopher Holmes <cholmes@hp.com>, who borrowed heavily
7  *  from existing Slurm source code, particularly src/srun/opt.c
8  *
9  *  This file is part of Slurm, a resource management program.
10  *  For details, see <https://slurm.schedmd.com/>.
11  *  Please also read the included file: DISCLAIMER.
12  *
13  *  Slurm is free software; you can redistribute it and/or modify it under
14  *  the terms of the GNU General Public License as published by the Free
15  *  Software Foundation; either version 2 of the License, or (at your option)
16  *  any later version.
17  *
18  *  In addition, as a special exception, the copyright holders give permission
19  *  to link the code of portions of this program with the OpenSSL library under
20  *  certain conditions as described in each individual source file, and
21  *  distribute linked combinations including the two. You must obey the GNU
22  *  General Public License in all respects for all of the code used other than
23  *  OpenSSL. If you modify file(s) with this exception, you may extend this
24  *  exception to your version of the file(s), but you are not obligated to do
25  *  so. If you do not wish to do so, delete this exception statement from your
26  *  version.  If you delete this exception statement from all source files in
27  *  the program, then also delete it here.
28  *
29  *  Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
30  *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
31  *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
32  *  details.
33  *
34  *  You should have received a copy of the GNU General Public License along
35  *  with Slurm; if not, write to the Free Software Foundation, Inc.,
36  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA.
37 \*****************************************************************************/
38 
39 #include "config.h"
40 
41 #define _GNU_SOURCE
42 
43 #include <ctype.h>		/* isdigit    */
44 #include <fcntl.h>
45 #include <limits.h>
46 #include <pwd.h>		/* getpwuid   */
47 #include <stdarg.h>		/* va_start   */
48 #include <stdio.h>
49 #include <stdlib.h>		/* getenv, strtoll */
50 #include <string.h>		/* strcpy */
51 #include <sys/param.h>		/* MAXPATHLEN */
52 #include <sys/stat.h>
53 #include <sys/types.h>
54 #include <sys/utsname.h>
55 #include <unistd.h>
56 
57 #include "src/common/gres.h"
58 #include "src/common/list.h"
59 #include "src/common/log.h"
60 #include "src/common/proc_args.h"
61 #include "src/common/parse_time.h"
62 #include "src/common/slurm_protocol_api.h"
63 #include "src/common/slurm_acct_gather_profile.h"
64 #include "src/common/xmalloc.h"
65 #include "src/common/xstring.h"
66 
67 enum {
68 	RESV_NEW, /* It is a new reservation */
69 	RESV_ADD, /* It is a reservation update with += */
70 	RESV_REM, /* It is an reservation  update with -= */
71 };
72 
73 /* print this version of Slurm */
print_slurm_version(void)74 void print_slurm_version(void)
75 {
76 	printf("%s %s\n", PACKAGE_NAME, SLURM_VERSION_STRING);
77 }
78 
79 /* print the available gres options */
print_gres_help(void)80 void print_gres_help(void)
81 {
82 	char *msg = gres_plugin_help_msg();
83 	printf("%s", msg);
84 	xfree(msg);
85 }
86 
set_distribution(task_dist_states_t distribution,char ** dist,char ** lllp_dist)87 void set_distribution(task_dist_states_t distribution,
88 		      char **dist, char **lllp_dist)
89 {
90 	if (((int)distribution >= 0)
91 	    && ((distribution & SLURM_DIST_STATE_BASE) != SLURM_DIST_UNKNOWN)) {
92 		switch (distribution & SLURM_DIST_STATE_BASE) {
93 		case SLURM_DIST_CYCLIC:
94 			*dist      = "cyclic";
95 			break;
96 		case SLURM_DIST_BLOCK:
97 			*dist      = "block";
98 			break;
99 		case SLURM_DIST_PLANE:
100 			*dist      = "plane";
101 			*lllp_dist = "plane";
102 			break;
103 		case SLURM_DIST_ARBITRARY:
104 			*dist      = "arbitrary";
105 			break;
106 		case SLURM_DIST_CYCLIC_CYCLIC:
107 			*dist      = "cyclic:cyclic";
108 			*lllp_dist = "cyclic";
109 			break;
110 		case SLURM_DIST_CYCLIC_BLOCK:
111 			*dist      = "cyclic:block";
112 			*lllp_dist = "block";
113 			break;
114 		case SLURM_DIST_BLOCK_CYCLIC:
115 			*dist      = "block:cyclic";
116 			*lllp_dist = "cyclic";
117 			break;
118 		case SLURM_DIST_BLOCK_BLOCK:
119 			*dist      = "block:block";
120 			*lllp_dist = "block";
121 			break;
122 		case SLURM_DIST_CYCLIC_CFULL:
123 			*dist      = "cyclic:fcyclic";
124 			*lllp_dist = "fcyclic";
125 			break;
126 		case SLURM_DIST_BLOCK_CFULL:
127 			*dist      = "block:fcyclic";
128 			*lllp_dist = "cyclic";
129 			break;
130 		case SLURM_DIST_CYCLIC_CYCLIC_CYCLIC:
131 			*dist      = "cyclic:cyclic:cyclic";
132 			*lllp_dist = "cyclic:cyclic";
133 			break;
134 		case SLURM_DIST_CYCLIC_CYCLIC_BLOCK:
135 			*dist      = "cyclic:cyclic:block";
136 			*lllp_dist = "cyclic:block";
137 			break;
138 		case SLURM_DIST_CYCLIC_CYCLIC_CFULL:
139 			*dist      = "cyclic:cyclic:fcyclic";
140 			*lllp_dist = "cyclic:fcyclic";
141 			break;
142 		case SLURM_DIST_CYCLIC_BLOCK_CYCLIC:
143 			*dist      = "cyclic:block:cyclic";
144 			*lllp_dist = "block:cyclic";
145 			break;
146 		case SLURM_DIST_CYCLIC_BLOCK_BLOCK:
147 			*dist      = "cyclic:block:block";
148 			*lllp_dist = "block:block";
149 			break;
150 		case SLURM_DIST_CYCLIC_BLOCK_CFULL:
151 			*dist      = "cyclic:cylic:cyclic";
152 			*lllp_dist = "cyclic:cyclic";
153 			break;
154 		case SLURM_DIST_CYCLIC_CFULL_CYCLIC:
155 			*dist      = "cyclic:cylic:cyclic";
156 			*lllp_dist = "cyclic:cyclic";
157 			break;
158 		case SLURM_DIST_CYCLIC_CFULL_BLOCK:
159 			*dist      = "cyclic:fcyclic:block";
160 			*lllp_dist = "fcyclic:block";
161 			break;
162 		case SLURM_DIST_CYCLIC_CFULL_CFULL:
163 			*dist      = "cyclic:fcyclic:fcyclic";
164 			*lllp_dist = "fcyclic:fcyclic";
165 			break;
166 		case SLURM_DIST_BLOCK_CYCLIC_CYCLIC:
167 			*dist      = "block:cyclic:cyclic";
168 			*lllp_dist = "cyclic:cyclic";
169 			break;
170 		case SLURM_DIST_BLOCK_CYCLIC_BLOCK:
171 			*dist      = "block:cyclic:block";
172 			*lllp_dist = "cyclic:block";
173 			break;
174 		case SLURM_DIST_BLOCK_CYCLIC_CFULL:
175 			*dist      = "block:cyclic:fcyclic";
176 			*lllp_dist = "cyclic:fcyclic";
177 			break;
178 		case SLURM_DIST_BLOCK_BLOCK_CYCLIC:
179 			*dist      = "block:block:cyclic";
180 			*lllp_dist = "block:cyclic";
181 			break;
182 		case SLURM_DIST_BLOCK_BLOCK_BLOCK:
183 			*dist      = "block:block:block";
184 			*lllp_dist = "block:block";
185 			break;
186 		case SLURM_DIST_BLOCK_BLOCK_CFULL:
187 			*dist      = "block:block:fcyclic";
188 			*lllp_dist = "block:fcyclic";
189 			break;
190 		case SLURM_DIST_BLOCK_CFULL_CYCLIC:
191 			*dist      = "block:fcyclic:cyclic";
192 			*lllp_dist = "fcyclic:cyclic";
193 			break;
194 		case SLURM_DIST_BLOCK_CFULL_BLOCK:
195 			*dist      = "block:fcyclic:block";
196 			*lllp_dist = "fcyclic:block";
197 			break;
198 		case SLURM_DIST_BLOCK_CFULL_CFULL:
199 			*dist      = "block:fcyclic:fcyclic";
200 			*lllp_dist = "fcyclic:fcyclic";
201 			break;
202 		default:
203 			error("unknown dist, type 0x%X", distribution);
204 			break;
205 		}
206 	}
207 }
208 
209 /*
210  * Get the size of the plane distribution and put it in plane_size.
211  *
212  * An invalid plane size is zero, negative, or larger than INT_MAX.
213  *
214  * Return SLURM_DIST_PLANE for a valid plane size, SLURM_DIST_UNKNOWN otherwise.
215  */
_parse_plane_dist(const char * tok,uint32_t * plane_size)216 static task_dist_states_t _parse_plane_dist(const char *tok,
217 					    uint32_t *plane_size)
218 {
219 	long tmp_long;
220 	char *endptr, *plane_size_str;
221 
222 	/*
223 	 * Check for plane size given after '=' sign or in SLURM_DIST_PLANESIZE
224 	 * environment variable.
225 	 */
226 	if ((plane_size_str = strchr(tok, '=')))
227 		plane_size_str++;
228 	else if (!(plane_size_str = getenv("SLURM_DIST_PLANESIZE")))
229 		return SLURM_DIST_UNKNOWN; /* No plane size given */
230 	else if (*plane_size_str == '\0')
231 		return SLURM_DIST_UNKNOWN; /* No plane size given */
232 
233 	tmp_long = strtol(plane_size_str, &endptr, 10);
234 	if ((plane_size_str == endptr) || (*endptr != '\0')) {
235 		/* No valid digits or there are characters after plane_size */
236 		return SLURM_DIST_UNKNOWN;
237 	} else if ((tmp_long > INT_MAX) || (tmp_long <= 0) ||
238 		   ((errno == ERANGE) && (tmp_long == LONG_MAX)))
239 		return SLURM_DIST_UNKNOWN; /* Number is too high/low */
240 	*plane_size = (uint32_t)tmp_long;
241 	return SLURM_DIST_PLANE;
242 }
243 
244 /*
245  * verify that a distribution type in arg is of a known form
246  * returns the task_dist_states, or -1 if state is unknown
247  */
verify_dist_type(const char * arg,uint32_t * plane_size)248 task_dist_states_t verify_dist_type(const char *arg, uint32_t *plane_size)
249 {
250 	int len;
251 	char *dist_str = NULL;
252 	task_dist_states_t result = SLURM_DIST_UNKNOWN;
253 	bool pack_nodes = false, no_pack_nodes = false;
254 	char *tok, *tmp, *save_ptr = NULL;
255 	int i, j;
256 	char *cur_ptr;
257 	char buf[3][25];
258 	buf[0][0] = '\0';
259 	buf[1][0] = '\0';
260 	buf[2][0] = '\0';
261 	char outstr[100];
262 	outstr[0]='\0';
263 
264 	if (!arg)
265 		return result;
266 
267 	if (!xstrncasecmp(arg, "plane", 5)) {
268 		/*
269 		 * plane distribution can't be with any other type,
270 		 * so just parse plane distribution and then break
271 		 */
272 		return _parse_plane_dist(arg, plane_size);
273 	}
274 
275 	tmp = xstrdup(arg);
276 	tok = strtok_r(tmp, ",", &save_ptr);
277 	while (tok) {
278 		bool lllp_dist = false;
279 		len = strlen(tok);
280 		dist_str = strchr(tok, ':');
281 		if (dist_str != NULL) {
282 			/* -m cyclic|block:cyclic|block */
283 			lllp_dist = true;
284 		}
285 
286 		cur_ptr = tok;
287 	 	for (j = 0; j < 3; j++) {
288 			for (i = 0; i < 24; i++) {
289 				if (*cur_ptr == '\0' || *cur_ptr ==':')
290 					break;
291 				buf[j][i] = *cur_ptr++;
292 			}
293 			buf[j][i] = '\0';
294 			if (*cur_ptr == '\0')
295 				break;
296 			buf[j][i] = '\0';
297 			cur_ptr++;
298 		}
299 		if (xstrcmp(buf[0], "*") == 0)
300 			/* default node distribution is block */
301 			strcpy(buf[0], "block");
302 		strcat(outstr, buf[0]);
303 		if (xstrcmp(buf[1], "\0") != 0) {
304 			strcat(outstr, ":");
305 			if (!xstrcmp(buf[1], "*") || !xstrcmp(buf[1], "\0")) {
306 				/* default socket distribution is cyclic */
307 				strcpy(buf[1], "cyclic");
308 			}
309 			strcat(outstr, buf[1]);
310 		}
311 		if (xstrcmp(buf[2], "\0") != 0) {
312 			strcat(outstr, ":");
313 			if (!xstrcmp(buf[2], "*") || !xstrcmp(buf[2], "\0")) {
314 				/* default core dist is inherited socket dist */
315 				strcpy(buf[2], buf[1]);
316 			}
317 			strcat(outstr, buf[2]);
318 		}
319 
320 		if (lllp_dist) {
321 			if (xstrcasecmp(outstr, "cyclic:cyclic") == 0) {
322 				result = SLURM_DIST_CYCLIC_CYCLIC;
323 			} else if (xstrcasecmp(outstr, "cyclic:block") == 0) {
324 				result = SLURM_DIST_CYCLIC_BLOCK;
325 			} else if (xstrcasecmp(outstr, "block:block") == 0) {
326 				result = SLURM_DIST_BLOCK_BLOCK;
327 			} else if (xstrcasecmp(outstr, "block:cyclic") == 0) {
328 				result = SLURM_DIST_BLOCK_CYCLIC;
329 			} else if (xstrcasecmp(outstr, "block:fcyclic") == 0) {
330 				result = SLURM_DIST_BLOCK_CFULL;
331 			} else if (xstrcasecmp(outstr, "cyclic:fcyclic") == 0) {
332 				result = SLURM_DIST_CYCLIC_CFULL;
333 			} else if (xstrcasecmp(outstr, "cyclic:cyclic:cyclic")
334 				   == 0) {
335 				result = SLURM_DIST_CYCLIC_CYCLIC_CYCLIC;
336 			} else if (xstrcasecmp(outstr, "cyclic:cyclic:block")
337 				   == 0) {
338 				result = SLURM_DIST_CYCLIC_CYCLIC_BLOCK;
339 			} else if (xstrcasecmp(outstr, "cyclic:cyclic:fcyclic")
340 				== 0) {
341 				result = SLURM_DIST_CYCLIC_CYCLIC_CFULL;
342 			} else if (xstrcasecmp(outstr, "cyclic:block:cyclic")
343 				== 0) {
344 				result = SLURM_DIST_CYCLIC_BLOCK_CYCLIC;
345 			} else if (xstrcasecmp(outstr, "cyclic:block:block")
346 				== 0) {
347 				result = SLURM_DIST_CYCLIC_BLOCK_BLOCK;
348 			} else if (xstrcasecmp(outstr, "cyclic:block:fcyclic")
349 				== 0) {
350 				result = SLURM_DIST_CYCLIC_BLOCK_CFULL;
351 			} else if (xstrcasecmp(outstr, "cyclic:fcyclic:cyclic")
352 				== 0) {
353 				result = SLURM_DIST_CYCLIC_CFULL_CYCLIC;
354 			} else if (xstrcasecmp(outstr, "cyclic:fcyclic:block")
355 				== 0) {
356 				result = SLURM_DIST_CYCLIC_CFULL_BLOCK;
357 			} else if (xstrcasecmp(outstr, "cyclic:fcyclic:fcyclic")
358 				== 0) {
359 				result = SLURM_DIST_CYCLIC_CFULL_CFULL;
360 			} else if (xstrcasecmp(outstr, "block:cyclic:cyclic")
361 				== 0) {
362 				result = SLURM_DIST_BLOCK_CYCLIC_CYCLIC;
363 			} else if (xstrcasecmp(outstr, "block:cyclic:block")
364 				== 0) {
365 				result = SLURM_DIST_BLOCK_CYCLIC_BLOCK;
366 			} else if (xstrcasecmp(outstr, "block:cyclic:fcyclic")
367 				== 0) {
368 				result = SLURM_DIST_BLOCK_CYCLIC_CFULL;
369 			} else if (xstrcasecmp(outstr, "block:block:cyclic")
370 				== 0) {
371 				result = SLURM_DIST_BLOCK_BLOCK_CYCLIC;
372 			} else if (xstrcasecmp(outstr, "block:block:block")
373 				== 0) {
374 				result = SLURM_DIST_BLOCK_BLOCK_BLOCK;
375 			} else if (xstrcasecmp(outstr, "block:block:fcyclic")
376 				== 0) {
377 				result = SLURM_DIST_BLOCK_BLOCK_CFULL;
378 			} else if (xstrcasecmp(outstr, "block:fcyclic:cyclic")
379 				== 0) {
380 				result = SLURM_DIST_BLOCK_CFULL_CYCLIC;
381 			} else if (xstrcasecmp(outstr, "block:fcyclic:block")
382 				== 0) {
383 				result = SLURM_DIST_BLOCK_CFULL_BLOCK;
384 			} else if (xstrcasecmp(outstr, "block:fcyclic:fcyclic")
385 				== 0) {
386 				result = SLURM_DIST_BLOCK_CFULL_CFULL;
387 			}
388 		} else {
389 			if (xstrncasecmp(tok, "cyclic", len) == 0) {
390 				result = SLURM_DIST_CYCLIC;
391 			} else if ((xstrncasecmp(tok, "block", len) == 0) ||
392 				   (xstrncasecmp(tok, "*", len) == 0)) {
393 				/*
394 				 * We can get here with syntax like this:
395 				 * -m *,pack
396 				 * '*' means get default (block for node dist).
397 				 */
398 				result = SLURM_DIST_BLOCK;
399 			} else if ((xstrncasecmp(tok, "arbitrary", len) == 0) ||
400 				   (xstrncasecmp(tok, "hostfile", len) == 0)) {
401 				result = SLURM_DIST_ARBITRARY;
402 			} else if (xstrncasecmp(tok, "nopack", len) == 0) {
403 				no_pack_nodes = true;
404 			} else if (xstrncasecmp(tok, "pack", len) == 0) {
405 				pack_nodes = true;
406 			}
407 		}
408 		tok = strtok_r(NULL, ",", &save_ptr);
409 	}
410 	xfree(tmp);
411 
412 	if (pack_nodes)
413 		result |= SLURM_DIST_PACK_NODES;
414 	else if (no_pack_nodes)
415 		result |= SLURM_DIST_NO_PACK_NODES;
416 
417 	return result;
418 }
419 
format_task_dist_states(task_dist_states_t t)420 extern char *format_task_dist_states(task_dist_states_t t)
421 {
422 	switch (t & SLURM_DIST_STATE_BASE) {
423 	case SLURM_DIST_BLOCK:
424 		return "block";
425 	case SLURM_DIST_CYCLIC:
426 		return "cyclic";
427 	case SLURM_DIST_PLANE:
428 		return "plane";
429 	case SLURM_DIST_ARBITRARY:
430 		return "arbitrary";
431 	case SLURM_DIST_CYCLIC_CYCLIC:
432 		return "cyclic:cyclic";
433 	case SLURM_DIST_CYCLIC_BLOCK:
434 		return "cyclic:block";
435 	case SLURM_DIST_CYCLIC_CFULL:
436 		return "cyclic:fcyclic";
437 	case SLURM_DIST_BLOCK_CYCLIC:
438 		return "block:cyclic";
439 	case SLURM_DIST_BLOCK_BLOCK:
440 		return "block:block";
441 	case SLURM_DIST_BLOCK_CFULL:
442 		return "block:fcyclic";
443 	case SLURM_DIST_CYCLIC_CYCLIC_CYCLIC:
444 		return "cyclic:cyclic:cyclic";
445 	case SLURM_DIST_CYCLIC_CYCLIC_BLOCK:
446 		return "cyclic:cyclic:block";
447 	case SLURM_DIST_CYCLIC_CYCLIC_CFULL:
448 		return "cyclic:cyclic:fcyclic";
449 	case SLURM_DIST_CYCLIC_BLOCK_CYCLIC:
450 		return "cyclic:block:cyclic";
451 	case SLURM_DIST_CYCLIC_BLOCK_BLOCK:
452 		return "cyclic:block:block";
453 	case SLURM_DIST_CYCLIC_BLOCK_CFULL:
454 		return "cyclic:block:fcyclic";
455 	case SLURM_DIST_CYCLIC_CFULL_CYCLIC:
456 		return "cyclic:fcyclic:cyclic" ;
457 	case SLURM_DIST_CYCLIC_CFULL_BLOCK:
458 		return "cyclic:fcyclic:block";
459 	case SLURM_DIST_CYCLIC_CFULL_CFULL:
460 		return "cyclic:fcyclic:fcyclic";
461 	case SLURM_DIST_BLOCK_CYCLIC_CYCLIC:
462 		return "block:cyclic:cyclic";
463 	case SLURM_DIST_BLOCK_CYCLIC_BLOCK:
464 		return "block:cyclic:block";
465 	case SLURM_DIST_BLOCK_CYCLIC_CFULL:
466 		return "block:cyclic:fcyclic";
467 	case SLURM_DIST_BLOCK_BLOCK_CYCLIC:
468 		return "block:block:cyclic";
469 	case SLURM_DIST_BLOCK_BLOCK_BLOCK:
470 		return "block:block:block";
471 	case SLURM_DIST_BLOCK_BLOCK_CFULL:
472 		return "block:block:fcyclic";
473 	case SLURM_DIST_BLOCK_CFULL_CYCLIC:
474 		return "block:fcyclic:cyclic";
475 	case SLURM_DIST_BLOCK_CFULL_BLOCK:
476 		return "block:fcyclic:block";
477 	case SLURM_DIST_BLOCK_CFULL_CFULL:
478 		return "block:fcyclic:fcyclic";
479 	default:
480 		return "unknown";
481 	}
482 }
483 
484 /* return command name from its full path name */
base_name(const char * command)485 char *base_name(const char *command)
486 {
487 	const char *char_ptr;
488 
489 	if (command == NULL)
490 		return NULL;
491 
492 	char_ptr = strrchr(command, (int)'/');
493 	if (char_ptr == NULL)
494 		char_ptr = command;
495 	else
496 		char_ptr++;
497 
498 	return xstrdup(char_ptr);
499 }
500 
_str_to_mbytes(const char * arg,int use_gbytes)501 static uint64_t _str_to_mbytes(const char *arg, int use_gbytes)
502 {
503 	long long result;
504 	char *endptr;
505 
506 	errno = 0;
507 	result = strtoll(arg, &endptr, 10);
508 	if ((errno != 0) && ((result == LLONG_MIN) || (result == LLONG_MAX)))
509 		return NO_VAL64;
510 	if (result < 0)
511 		return NO_VAL64;
512 
513 	else if ((endptr[0] == '\0') && (use_gbytes == 1))  /* GB default */
514 		result *= 1024;
515 	else if (endptr[0] == '\0')	/* MB default */
516 		;
517 	else if ((endptr[0] == 'k') || (endptr[0] == 'K'))
518 		result = (result + 1023) / 1024;	/* round up */
519 	else if ((endptr[0] == 'm') || (endptr[0] == 'M'))
520 		;
521 	else if ((endptr[0] == 'g') || (endptr[0] == 'G'))
522 		result *= 1024;
523 	else if ((endptr[0] == 't') || (endptr[0] == 'T'))
524 		result *= (1024 * 1024);
525 	else
526 		return NO_VAL64;
527 
528 	return (uint64_t) result;
529 }
530 
531 /*
532  * str_to_mbytes(): verify that arg is numeric with optional "K", "M", "G"
533  * or "T" at end and return the number in mega-bytes. Default units are MB.
534  */
str_to_mbytes(const char * arg)535 uint64_t str_to_mbytes(const char *arg)
536 {
537 	return _str_to_mbytes(arg, 0);
538 }
539 
540 /*
541  * str_to_mbytes2(): verify that arg is numeric with optional "K", "M", "G"
542  * or "T" at end and return the number in mega-bytes. Default units are GB
543  * if "SchedulerParameters=default_gbytes" is configured, otherwise MB.
544  */
str_to_mbytes2(const char * arg)545 uint64_t str_to_mbytes2(const char *arg)
546 {
547 	static int use_gbytes = -1;
548 
549 	if (use_gbytes == -1) {
550 		char *sched_params = slurm_get_sched_params();
551 		if (xstrcasestr(sched_params, "default_gbytes"))
552 			use_gbytes = 1;
553 		else
554 			use_gbytes = 0;
555 		xfree(sched_params);
556 	}
557 
558 	return _str_to_mbytes(arg, use_gbytes);
559 }
560 
mbytes2_to_str(uint64_t mbytes)561 extern char *mbytes2_to_str(uint64_t mbytes)
562 {
563 	int i = 0;
564 	char *unit = "MGTP?";
565 	static int use_gbytes = -1;
566 
567 	if (mbytes == NO_VAL64)
568 		return NULL;
569 
570 	if (use_gbytes == -1) {
571 		char *sched_params = slurm_get_sched_params();
572 		if (xstrcasestr(sched_params, "default_gbytes"))
573 			use_gbytes = 1;
574 		else
575 			use_gbytes = 0;
576 		xfree(sched_params);
577 	}
578 
579 	for (i = 0; unit[i] != '?'; i++) {
580 		if (mbytes && (mbytes % 1024))
581 			break;
582 		mbytes /= 1024;
583 	}
584 
585 	/* no need to display the default unit */
586 	if ((unit[i] == 'G' && use_gbytes) || (unit[i] == 'M' && !use_gbytes))
587 		return xstrdup_printf("%"PRIu64, mbytes);
588 
589 	return xstrdup_printf("%"PRIu64"%c", mbytes, unit[i]);
590 }
591 
592 /* Convert a string into a node count */
593 static int
_str_to_nodes(const char * num_str,char ** leftover)594 _str_to_nodes(const char *num_str, char **leftover)
595 {
596 	long int num;
597 	char *endptr;
598 
599 	num = strtol(num_str, &endptr, 10);
600 	if (endptr == num_str) { /* no valid digits */
601 		*leftover = (char *)num_str;
602 		return -1;
603 	}
604 	if (*endptr != '\0' && (*endptr == 'k' || *endptr == 'K')) {
605 		num *= 1024;
606 		endptr++;
607 	}
608 	if (*endptr != '\0' && (*endptr == 'm' || *endptr == 'M')) {
609 		num *= (1024 * 1024);
610 		endptr++;
611 	}
612 	*leftover = endptr;
613 
614 	return (int)num;
615 }
616 
617 /*
618  * verify that a node count in arg is of a known form (count or min-max)
619  * OUT min, max specified minimum and maximum node counts
620  * RET true if valid
621  */
verify_node_count(const char * arg,int * min_nodes,int * max_nodes)622 bool verify_node_count(const char *arg, int *min_nodes, int *max_nodes)
623 {
624 	char *ptr, *min_str, *max_str;
625 	char *leftover;
626 
627 	/*
628 	 * Does the string contain a "-" character?  If so, treat as a range.
629 	 * otherwise treat as an absolute node count.
630 	 */
631 	if ((ptr = xstrchr(arg, '-')) != NULL) {
632 		min_str = xstrndup(arg, ptr-arg);
633 		*min_nodes = _str_to_nodes(min_str, &leftover);
634 		if (!xstring_is_whitespace(leftover)) {
635 			error("\"%s\" is not a valid node count", min_str);
636 			xfree(min_str);
637 			return false;
638 		}
639 		xfree(min_str);
640 		if (*min_nodes < 0)
641 			*min_nodes = 1;
642 
643 		max_str = xstrndup(ptr+1, strlen(arg)-((ptr+1)-arg));
644 		*max_nodes = _str_to_nodes(max_str, &leftover);
645 		if (!xstring_is_whitespace(leftover)) {
646 			error("\"%s\" is not a valid node count", max_str);
647 			xfree(max_str);
648 			return false;
649 		}
650 		xfree(max_str);
651 	} else {
652 		*min_nodes = *max_nodes = _str_to_nodes(arg, &leftover);
653 		if (!xstring_is_whitespace(leftover)) {
654 			error("\"%s\" is not a valid node count", arg);
655 			return false;
656 		}
657 		if (*min_nodes < 0) {
658 			error("\"%s\" is not a valid node count", arg);
659 			return false;
660 		}
661 	}
662 
663 	if ((*max_nodes != 0) && (*max_nodes < *min_nodes)) {
664 		error("Maximum node count %d is less than minimum node count %d",
665 		      *max_nodes, *min_nodes);
666 		return false;
667 	}
668 
669 	return true;
670 }
671 
672 /*
673  * If the node list supplied is a file name, translate that into
674  *	a list of nodes, we orphan the data pointed to
675  * RET true if the node list is a valid one
676  */
verify_node_list(char ** node_list_pptr,enum task_dist_states dist,int task_count)677 bool verify_node_list(char **node_list_pptr, enum task_dist_states dist,
678 		      int task_count)
679 {
680 	char *nodelist = NULL;
681 
682 	xassert (node_list_pptr);
683 	xassert (*node_list_pptr);
684 
685 	if (strchr(*node_list_pptr, '/') == NULL)
686 		return true;	/* not a file name */
687 
688 	/* If we are using Arbitrary grab count out of the hostfile
689 	   using them exactly the way we read it in since we are
690 	   saying, lay it out this way! */
691 	if ((dist & SLURM_DIST_STATE_BASE) == SLURM_DIST_ARBITRARY)
692 		nodelist = slurm_read_hostfile(*node_list_pptr, task_count);
693 	else
694 		nodelist = slurm_read_hostfile(*node_list_pptr, NO_VAL);
695 
696 	if (!nodelist)
697 		return false;
698 
699 	xfree(*node_list_pptr);
700 	*node_list_pptr = xstrdup(nodelist);
701 	free(nodelist);
702 
703 	return true;
704 }
705 
706 /*
707  * get either 1 or 2 integers for a resource count in the form of either
708  * (count, min-max, or '*')
709  * A partial error message is passed in via the 'what' param.
710  * IN arg - argument
711  * IN what - variable name (for errors)
712  * OUT min - first number
713  * OUT max - maximum value if specified, NULL if don't care
714  * IN isFatal - if set, exit on error
715  * RET true if valid
716  */
get_resource_arg_range(const char * arg,const char * what,int * min,int * max,bool isFatal)717 bool get_resource_arg_range(const char *arg, const char *what, int* min,
718 			    int *max, bool isFatal)
719 {
720 	char *p;
721 	long int result;
722 
723 	/* wildcard meaning every possible value in range */
724 	if ((*arg == '\0') || (*arg == '*' )) {
725 		*min = 1;
726 		if (max)
727 			*max = INT_MAX;
728 		return true;
729 	}
730 
731 	result = strtol(arg, &p, 10);
732 	if (*p == 'k' || *p == 'K') {
733 		result *= 1024;
734 		p++;
735 	} else if (*p == 'm' || *p == 'M') {
736 		result *= 1048576;
737 		p++;
738 	}
739 
740 	if (((*p != '\0') && (*p != '-')) || (result < 0L)) {
741 		error ("Invalid numeric value \"%s\" for %s.", arg, what);
742 		if (isFatal)
743 			exit(1);
744 		return false;
745 	} else if (result > INT_MAX) {
746 		error ("Numeric argument (%ld) to big for %s.", result, what);
747 		if (isFatal)
748 			exit(1);
749 		return false;
750 	}
751 
752 	*min = (int) result;
753 
754 	if (*p == '\0')
755 		return true;
756 	if (*p == '-')
757 		p++;
758 
759 	result = strtol(p, &p, 10);
760 	if ((*p == 'k') || (*p == 'K')) {
761 		result *= 1024;
762 		p++;
763 	} else if (*p == 'm' || *p == 'M') {
764 		result *= 1048576;
765 		p++;
766 	}
767 
768 	if (((*p != '\0') && (*p != '-')) || (result <= 0L)) {
769 		error ("Invalid numeric value \"%s\" for %s.", arg, what);
770 		if (isFatal)
771 			exit(1);
772 		return false;
773 	} else if (result > INT_MAX) {
774 		error ("Numeric argument (%ld) to big for %s.", result, what);
775 		if (isFatal)
776 			exit(1);
777 		return false;
778 	}
779 
780 	if (max)
781 		*max = (int) result;
782 
783 	return true;
784 }
785 
786 /*
787  * verify that a resource counts in arg are of a known form X, X:X, X:X:X, or
788  * X:X:X:X, where X is defined as either (count, min-max, or '*')
789  * RET true if valid
790  */
verify_socket_core_thread_count(const char * arg,int * min_sockets,int * min_cores,int * min_threads,cpu_bind_type_t * cpu_bind_type)791 bool verify_socket_core_thread_count(const char *arg, int *min_sockets,
792 				     int *min_cores, int *min_threads,
793 				     cpu_bind_type_t *cpu_bind_type)
794 {
795 	bool tmp_val, ret_val;
796 	int i, j;
797 	int max_sockets = 0, max_cores = 0, max_threads = 0;
798 	const char *cur_ptr = arg;
799 	char buf[3][48]; /* each can hold INT64_MAX - INT64_MAX */
800 
801 	if (!arg) {
802 		error("%s: argument is NULL", __func__);
803 		return false;
804 	}
805 	memset(buf, 0, sizeof(buf));
806 	for (j = 0; j < 3; j++) {
807 		for (i = 0; i < 47; i++) {
808 			if (*cur_ptr == '\0' || *cur_ptr ==':')
809 				break;
810 			buf[j][i] = *cur_ptr++;
811 		}
812 		if (*cur_ptr == '\0')
813 			break;
814 		xassert(*cur_ptr == ':');
815 		cur_ptr++;
816 	}
817 	/* if cpu_bind_type doesn't already have a auto preference, choose
818 	 * the level based on the level of the -E specification
819 	 */
820 	if (cpu_bind_type &&
821 	    !(*cpu_bind_type & (CPU_BIND_TO_SOCKETS |
822 				CPU_BIND_TO_CORES |
823 				CPU_BIND_TO_THREADS))) {
824 		if (j == 0) {
825 			*cpu_bind_type |= CPU_BIND_TO_SOCKETS;
826 		} else if (j == 1) {
827 			*cpu_bind_type |= CPU_BIND_TO_CORES;
828 		} else if (j == 2) {
829 			*cpu_bind_type |= CPU_BIND_TO_THREADS;
830 		}
831 	}
832 
833 	ret_val = true;
834 	tmp_val = get_resource_arg_range(&buf[0][0], "first arg of -B",
835 					 min_sockets, &max_sockets, true);
836 	if ((*min_sockets == 1) && (max_sockets == INT_MAX))
837 		*min_sockets = NO_VAL;	/* Use full range of values */
838 	ret_val = ret_val && tmp_val;
839 
840 
841 	tmp_val = get_resource_arg_range(&buf[1][0], "second arg of -B",
842 					 min_cores, &max_cores, true);
843 	if ((*min_cores == 1) && (max_cores == INT_MAX))
844 		*min_cores = NO_VAL;	/* Use full range of values */
845 	ret_val = ret_val && tmp_val;
846 
847 	tmp_val = get_resource_arg_range(&buf[2][0], "third arg of -B",
848 					 min_threads, &max_threads, true);
849 	if ((*min_threads == 1) && (max_threads == INT_MAX))
850 		*min_threads = NO_VAL;	/* Use full range of values */
851 	ret_val = ret_val && tmp_val;
852 
853 	return ret_val;
854 }
855 
856 /*
857  * verify that a hint is valid and convert it into the implied settings
858  * RET true if valid
859  */
verify_hint(const char * arg,int * min_sockets,int * min_cores,int * min_threads,int * ntasks_per_core,cpu_bind_type_t * cpu_bind_type)860 bool verify_hint(const char *arg, int *min_sockets, int *min_cores,
861 		 int *min_threads, int *ntasks_per_core,
862 		 cpu_bind_type_t *cpu_bind_type)
863 {
864 	char *buf, *p, *tok;
865 
866 	if (!arg)
867 		return true;
868 
869 	buf = xstrdup(arg);
870 	p = buf;
871 	/* change all ',' delimiters not followed by a digit to ';'  */
872 	/* simplifies parsing tokens while keeping map/mask together */
873 	while (p[0] != '\0') {
874 		if ((p[0] == ',') && (!isdigit((int)p[1])))
875 			p[0] = ';';
876 		p++;
877 	}
878 
879 	p = buf;
880 	while ((tok = strsep(&p, ";"))) {
881 		if (xstrcasecmp(tok, "help") == 0) {
882 			printf(
883 "Application hint options:\n"
884 "    --hint=             Bind tasks according to application hints\n"
885 "        compute_bound   use all cores in each socket\n"
886 "        memory_bound    use only one core in each socket\n"
887 "        [no]multithread [don't] use extra threads with in-core multi-threading\n"
888 "        help            show this help message\n");
889 			xfree(buf);
890 			return 1;
891 		} else if (xstrcasecmp(tok, "compute_bound") == 0) {
892 			*min_sockets = NO_VAL;
893 			*min_cores   = NO_VAL;
894 			*min_threads = 1;
895 			if (cpu_bind_type)
896 				*cpu_bind_type |= CPU_BIND_TO_CORES;
897 		} else if (xstrcasecmp(tok, "memory_bound") == 0) {
898 			*min_cores   = 1;
899 			*min_threads = 1;
900 			if (cpu_bind_type)
901 				*cpu_bind_type |= CPU_BIND_TO_CORES;
902 		} else if (xstrcasecmp(tok, "multithread") == 0) {
903 			*min_threads = NO_VAL;
904 			if (cpu_bind_type) {
905 				*cpu_bind_type |= CPU_BIND_TO_THREADS;
906 				*cpu_bind_type &=
907 					(~CPU_BIND_ONE_THREAD_PER_CORE);
908 			}
909 			if (*ntasks_per_core == NO_VAL)
910 				*ntasks_per_core = INFINITE16;
911 		} else if (xstrcasecmp(tok, "nomultithread") == 0) {
912 			*min_threads = 1;
913 			if (cpu_bind_type) {
914 				*cpu_bind_type |= CPU_BIND_TO_THREADS;
915 				*cpu_bind_type |= CPU_BIND_ONE_THREAD_PER_CORE;
916 			}
917 		} else {
918 			error("unrecognized --hint argument \"%s\", "
919 			      "see --hint=help", tok);
920 			xfree(buf);
921 			return 1;
922 		}
923 	}
924 
925 	if (!cpu_bind_type)
926 		setenvf(NULL, "SLURM_HINT", "%s", arg);
927 
928 	xfree(buf);
929 	return 0;
930 }
931 
parse_mail_type(const char * arg)932 uint16_t parse_mail_type(const char *arg)
933 {
934 	char *buf, *tok, *save_ptr = NULL;
935 	uint16_t rc = 0;
936 	bool none_set = false;
937 
938 	if (!arg)
939 		return INFINITE16;
940 
941 	buf = xstrdup(arg);
942 	tok = strtok_r(buf, ",", &save_ptr);
943 	while (tok) {
944 		if (xstrcasecmp(tok, "NONE") == 0) {
945 			rc = 0;
946 			none_set = true;
947 			break;
948 		}
949 		else if (xstrcasecmp(tok, "ARRAY_TASKS") == 0)
950 			rc |= MAIL_ARRAY_TASKS;
951 		else if (xstrcasecmp(tok, "BEGIN") == 0)
952 			rc |= MAIL_JOB_BEGIN;
953 		else if  (xstrcasecmp(tok, "END") == 0)
954 			rc |= MAIL_JOB_END;
955 		else if (xstrcasecmp(tok, "FAIL") == 0)
956 			rc |= MAIL_JOB_FAIL;
957 		else if (xstrcasecmp(tok, "REQUEUE") == 0)
958 			rc |= MAIL_JOB_REQUEUE;
959 		else if (xstrcasecmp(tok, "ALL") == 0)
960 			rc |= MAIL_JOB_BEGIN |  MAIL_JOB_END |  MAIL_JOB_FAIL |
961 			      MAIL_JOB_REQUEUE | MAIL_JOB_STAGE_OUT;
962 		else if (!xstrcasecmp(tok, "STAGE_OUT"))
963 			rc |= MAIL_JOB_STAGE_OUT;
964 		else if (xstrcasecmp(tok, "TIME_LIMIT") == 0)
965 			rc |= MAIL_JOB_TIME100;
966 		else if (xstrcasecmp(tok, "TIME_LIMIT_90") == 0)
967 			rc |= MAIL_JOB_TIME90;
968 		else if (xstrcasecmp(tok, "TIME_LIMIT_80") == 0)
969 			rc |= MAIL_JOB_TIME80;
970 		else if (xstrcasecmp(tok, "TIME_LIMIT_50") == 0)
971 			rc |= MAIL_JOB_TIME50;
972 		tok = strtok_r(NULL, ",", &save_ptr);
973 	}
974 	xfree(buf);
975 	if (!rc && !none_set)
976 		rc = INFINITE16;
977 
978 	return rc;
979 }
print_mail_type(const uint16_t type)980 char *print_mail_type(const uint16_t type)
981 {
982 	static char buf[256];
983 
984 	buf[0] = '\0';
985 
986 	if (type == 0)
987 		return "NONE";
988 
989 	if (type & MAIL_ARRAY_TASKS) {
990 		if (buf[0])
991 			strcat(buf, ",");
992 		strcat(buf, "ARRAY_TASKS");
993 	}
994 	if (type & MAIL_JOB_BEGIN) {
995 		if (buf[0])
996 			strcat(buf, ",");
997 		strcat(buf, "BEGIN");
998 	}
999 	if (type & MAIL_JOB_END) {
1000 		if (buf[0])
1001 			strcat(buf, ",");
1002 		strcat(buf, "END");
1003 	}
1004 	if (type & MAIL_JOB_FAIL) {
1005 		if (buf[0])
1006 			strcat(buf, ",");
1007 		strcat(buf, "FAIL");
1008 	}
1009 	if (type & MAIL_JOB_REQUEUE) {
1010 		if (buf[0])
1011 			strcat(buf, ",");
1012 		strcat(buf, "REQUEUE");
1013 	}
1014 	if (type & MAIL_JOB_STAGE_OUT) {
1015 		if (buf[0])
1016 			strcat(buf, ",");
1017 		strcat(buf, "STAGE_OUT");
1018 	}
1019 	if (type & MAIL_JOB_TIME50) {
1020 		if (buf[0])
1021 			strcat(buf, ",");
1022 		strcat(buf, "TIME_LIMIT_50");
1023 	}
1024 	if (type & MAIL_JOB_TIME80) {
1025 		if (buf[0])
1026 			strcat(buf, ",");
1027 		strcat(buf, "TIME_LIMIT_80");
1028 	}
1029 	if (type & MAIL_JOB_TIME90) {
1030 		if (buf[0])
1031 			strcat(buf, ",");
1032 		strcat(buf, "TIME_LIMIT_90");
1033 	}
1034 	if (type & MAIL_JOB_TIME100) {
1035 		if (buf[0])
1036 			strcat(buf, ",");
1037 		strcat(buf, "TIME_LIMIT");
1038 	}
1039 
1040 	return buf;
1041 }
1042 
1043 static List
_create_path_list(void)1044 _create_path_list(void)
1045 {
1046 	List l = list_create(xfree_ptr);
1047 	char *path;
1048 	char *c, *lc;
1049 
1050 	c = getenv("PATH");
1051 	if (!c) {
1052 		error("No PATH environment variable");
1053 		return l;
1054 	}
1055 	path = xstrdup(c);
1056 	c = lc = path;
1057 
1058 	while (*c != '\0') {
1059 		if (*c == ':') {
1060 			/* nullify and push token onto list */
1061 			*c = '\0';
1062 			if (lc != NULL && strlen(lc) > 0)
1063 				list_append(l, xstrdup(lc));
1064 			lc = ++c;
1065 		} else
1066 			c++;
1067 	}
1068 
1069 	if (strlen(lc) > 0)
1070 		list_append(l, xstrdup(lc));
1071 
1072 	xfree(path);
1073 
1074 	return l;
1075 }
1076 
1077 /*
1078  * Check a specific path to see if the executable exists and is not a directory
1079  * IN path - path of executable to check
1080  * RET true if path exists and is not a directory; false otherwise
1081  */
_exists(const char * path)1082 static bool _exists(const char *path)
1083 {
1084 	struct stat st;
1085         if (stat(path, &st)) {
1086 		debug2("_check_exec: failed to stat path %s", path);
1087 		return false;
1088 	}
1089 	if (S_ISDIR(st.st_mode)) {
1090 		debug2("_check_exec: path %s is a directory", path);
1091 		return false;
1092 	}
1093 	return true;
1094 }
1095 
1096 /*
1097  * Check a specific path to see if the executable is accessible
1098  * IN path - path of executable to check
1099  * IN access_mode - determine if executable is accessible to caller with
1100  *		    specified mode
1101  * RET true if path is accessible according to access mode, false otherwise
1102  */
_accessible(const char * path,int access_mode)1103 static bool _accessible(const char *path, int access_mode)
1104 {
1105 	if (access(path, access_mode)) {
1106 		debug2("_check_exec: path %s is not accessible", path);
1107 		return false;
1108 	}
1109 	return true;
1110 }
1111 
1112 /*
1113  * search PATH to confirm the location and access mode of the given command
1114  * IN cwd - current working directory
1115  * IN cmd - command to execute
1116  * IN check_cwd_last - if true, search cwd after PATH is checked
1117  *                   - if false, search cwd for the command first
1118  * IN access_mode - required access rights of cmd
1119  * IN test_exec - if false, do not confirm access mode of cmd if full path
1120  * RET full path of cmd or NULL if not found
1121  */
search_path(char * cwd,char * cmd,bool check_cwd_last,int access_mode,bool test_exec)1122 char *search_path(char *cwd, char *cmd, bool check_cwd_last, int access_mode,
1123 		  bool test_exec)
1124 {
1125 	List         l        = NULL;
1126 	ListIterator i        = NULL;
1127 	char *path, *fullpath = NULL;
1128 
1129 	/* Relative path */
1130 	if (cmd[0] == '.') {
1131 		if (test_exec) {
1132 			char *cmd1 = xstrdup_printf("%s/%s", cwd, cmd);
1133 			if (_exists(cmd1) && _accessible(cmd1, access_mode)) {
1134 				fullpath = xstrdup(cmd1);
1135 				debug5("%s: relative path found %s -> %s",
1136 					__func__, cmd, cmd1);
1137 			} else {
1138 				debug5("%s: relative path not found %s -> %s",
1139 					__func__, cmd, cmd1);
1140 			}
1141 			xfree(cmd1);
1142 		}
1143 		return fullpath;
1144 	}
1145 	/* Absolute path */
1146 	if (cmd[0] == '/') {
1147 		if (test_exec && _exists(cmd) && _accessible(cmd, access_mode)) {
1148 			fullpath = xstrdup(cmd);
1149 			debug5("%s: absolute path found %s",
1150 			       __func__, cmd);
1151 		} else {
1152 			debug5("%s: absolute path not found %s",
1153 			       __func__, cmd);
1154 		}
1155 		return fullpath;
1156 	}
1157 	/* Otherwise search in PATH */
1158 	l = _create_path_list();
1159 	if (l == NULL) {
1160 		debug5("%s: empty PATH environment",
1161 			__func__);
1162 		return NULL;
1163 	}
1164 
1165 	if (check_cwd_last)
1166 		list_append(l, xstrdup(cwd));
1167 	else
1168 		list_prepend(l, xstrdup(cwd));
1169 
1170 	i = list_iterator_create(l);
1171 	while ((path = list_next(i))) {
1172 		if (path[0] == '.')
1173 			xstrfmtcat(fullpath, "%s/%s/%s", cwd, path, cmd);
1174 		else
1175 			xstrfmtcat(fullpath, "%s/%s", path, cmd);
1176 		/* Use first executable found in PATH */
1177 		if (_exists(fullpath)) {
1178 			if (!test_exec) {
1179 				debug5("%s: env PATH found: %s",
1180 					__func__, fullpath);
1181 				break;
1182 			}
1183 			if (_accessible(path, access_mode)) {
1184 				debug5("%s: env PATH found: %s",
1185 					__func__, fullpath);
1186 				break;
1187 			}
1188 		}
1189 
1190 		debug5("%s: env PATH not found: %s",
1191 			__func__, fullpath);
1192 
1193 		xfree(fullpath);
1194 	}
1195 	list_iterator_destroy(i);
1196 	FREE_NULL_LIST(l);
1197 	return fullpath;
1198 }
1199 
print_commandline(const int script_argc,char ** script_argv)1200 char *print_commandline(const int script_argc, char **script_argv)
1201 {
1202 	int i;
1203 	char *out_buf = NULL, *prefix = "";
1204 
1205 	for (i = 0; i < script_argc; i++) {
1206 		xstrfmtcat(out_buf,  "%s%s", prefix, script_argv[i]);
1207 		prefix = " ";
1208 	}
1209 	return out_buf;
1210 }
1211 
1212 /* Translate a signal option string "--signal=<int>[@<time>]" into
1213  * it's warn_signal and warn_time components.
1214  * RET 0 on success, -1 on failure */
get_signal_opts(char * optarg,uint16_t * warn_signal,uint16_t * warn_time,uint16_t * warn_flags)1215 int get_signal_opts(char *optarg, uint16_t *warn_signal, uint16_t *warn_time,
1216 		    uint16_t *warn_flags)
1217 {
1218 	static bool daemon_run = false, daemon_set = false;
1219 	char *endptr;
1220 	long num;
1221 
1222 	if (optarg == NULL)
1223 		return -1;
1224 
1225 	if (!xstrncasecmp(optarg, "R", 1)) {
1226 		*warn_flags |= KILL_JOB_RESV;
1227 		optarg++;
1228 	}
1229 
1230 	if (run_in_daemon(&daemon_run, &daemon_set, "sbatch")) {
1231 		if (!xstrncasecmp(optarg, "B", 1)) {
1232 			*warn_flags |= KILL_JOB_BATCH;
1233 			optarg++;
1234 		}
1235 
1236 		/* easiest way to handle BR and RB */
1237 		if (!xstrncasecmp(optarg, "R", 1)) {
1238 			*warn_flags |= KILL_JOB_RESV;
1239 			optarg++;
1240 		}
1241 	}
1242 
1243 	if (*optarg == ':')
1244 		optarg++;
1245 
1246 	endptr = strchr(optarg, '@');
1247 	if (endptr)
1248 		endptr[0] = '\0';
1249 	num = (uint16_t) sig_name2num(optarg);
1250 	if (endptr)
1251 		endptr[0] = '@';
1252 	if ((num < 1) || (num > 0x0ffff))
1253 		return -1;
1254 	*warn_signal = (uint16_t) num;
1255 
1256 	if (!endptr) {
1257 		*warn_time = 60;
1258 		return 0;
1259 	}
1260 
1261 	num = strtol(endptr+1, &endptr, 10);
1262 	if ((num < 0) || (num > 0x0ffff))
1263 		return -1;
1264 	*warn_time = (uint16_t) num;
1265 	if (endptr[0] == '\0')
1266 		return 0;
1267 	return -1;
1268 }
1269 
signal_opts_to_cmdline(uint16_t warn_signal,uint16_t warn_time,uint16_t warn_flags)1270 extern char *signal_opts_to_cmdline(uint16_t warn_signal, uint16_t warn_time,
1271 				    uint16_t warn_flags)
1272 {
1273 	char *cmdline = NULL, *sig_name;
1274 
1275 	if (warn_flags & KILL_JOB_RESV)
1276 		xstrcat(cmdline, "R");
1277 	if (warn_flags & KILL_JOB_BATCH)
1278 		xstrcat(cmdline, "B");
1279 
1280 	if ((warn_flags & KILL_JOB_RESV) || (warn_flags & KILL_JOB_BATCH))
1281 		xstrcat(cmdline, ":");
1282 
1283 	sig_name = sig_num2name(warn_signal);
1284 	xstrcat(cmdline, sig_name);
1285 	xfree(sig_name);
1286 
1287 	if (warn_time != 60) /* default value above, don't print */
1288 		xstrfmtcat(cmdline, "@%u", warn_time);
1289 
1290 	return cmdline;
1291 }
1292 
1293 static struct {
1294 	char *name;
1295 	uint16_t val;
1296 } signals_mapping[] = {
1297 	{ "HUP",	SIGHUP	},
1298 	{ "INT",	SIGINT	},
1299 	{ "QUIT",	SIGQUIT	},
1300 	{ "ABRT",	SIGABRT	},
1301 	{ "KILL",	SIGKILL	},
1302 	{ "ALRM",	SIGALRM	},
1303 	{ "TERM",	SIGTERM	},
1304 	{ "USR1",	SIGUSR1	},
1305 	{ "USR2",	SIGUSR2	},
1306 	{ "URG",	SIGURG	},
1307 	{ "CONT",	SIGCONT	},
1308 	{ "STOP",	SIGSTOP	},
1309 	{ "TSTP",	SIGTSTP	},
1310 	{ "TTIN",	SIGTTIN	},
1311 	{ "TTOU",	SIGTTOU	},
1312 	{ NULL,		0	}	/* terminate array */
1313 };
1314 
1315 /* Convert a signal name to it's numeric equivalent.
1316  * Return 0 on failure */
sig_name2num(const char * signal_name)1317 int sig_name2num(const char *signal_name)
1318 {
1319 	char *ptr;
1320 	long tmp;
1321 	int i;
1322 
1323 	tmp = strtol(signal_name, &ptr, 10);
1324 	if (ptr != signal_name) { /* found a number */
1325 		if (xstring_is_whitespace(ptr))
1326 			return (int)tmp;
1327 		else
1328 			return 0;
1329 	}
1330 
1331 	/* search the array */
1332 	ptr = (char *) signal_name;
1333 	while (isspace((int)*ptr))
1334 		ptr++;
1335 	if (xstrncasecmp(ptr, "SIG", 3) == 0)
1336 		ptr += 3;
1337 	for (i = 0; ; i++) {
1338 		int siglen;
1339 		if (signals_mapping[i].name == NULL)
1340 			return 0;
1341 		siglen = strlen(signals_mapping[i].name);
1342 		if ((!xstrncasecmp(ptr, signals_mapping[i].name, siglen)
1343 		    && xstring_is_whitespace(ptr + siglen))) {
1344 			/* found the signal name */
1345 			return signals_mapping[i].val;
1346 		}
1347 	}
1348 
1349 	return 0;	/* not found */
1350 }
1351 
sig_num2name(int signal)1352 extern char *sig_num2name(int signal)
1353 {
1354 	for (int i = 0; signals_mapping[i].name; i++) {
1355 		if (signal == signals_mapping[i].val)
1356 			return xstrdup(signals_mapping[i].name);
1357 	}
1358 
1359 	return xstrdup_printf("%d", signal);
1360 }
1361 
1362 /*
1363  * parse_uint16 - Convert ascii string to a 16 bit unsigned int.
1364  * IN      aval - ascii string.
1365  * IN/OUT  ival - 16 bit pointer.
1366  * RET     0 if no error, 1 otherwise.
1367  */
parse_uint16(char * aval,uint16_t * ival)1368 extern int parse_uint16(char *aval, uint16_t *ival)
1369 {
1370 	/*
1371 	 * First,  convert the ascii value it to a
1372 	 * long long int. If the result is greater then
1373 	 * or equal to 0 and less than NO_VAL16
1374 	 * set the value and return. Otherwise
1375 	 * return an error.
1376 	 */
1377 	uint16_t max16uint = NO_VAL16;
1378 	long long tval;
1379 	char *p;
1380 
1381 	/*
1382 	 * Return error for invalid value.
1383 	 */
1384 	tval = strtoll(aval, &p, 10);
1385 	if (p[0] || (tval == LLONG_MIN) || (tval == LLONG_MAX) ||
1386 	    (tval < 0) || (tval >= max16uint))
1387 		return 1;
1388 
1389 	*ival = (uint16_t) tval;
1390 
1391 	return 0;
1392 }
1393 
1394 /*
1395  * parse_uint32 - Convert ascii string to a 32 bit unsigned int.
1396  * IN      aval - ascii string.
1397  * IN/OUT  ival - 32 bit pointer.
1398  * RET     0 if no error, 1 otherwise.
1399  */
parse_uint32(char * aval,uint32_t * ival)1400 extern int parse_uint32(char *aval, uint32_t *ival)
1401 {
1402 	/*
1403 	 * First,  convert the ascii value it to a
1404 	 * long long int. If the result is greater
1405 	 * than or equal to 0 and less than NO_VAL
1406 	 * set the value and return. Otherwise return
1407 	 * an error.
1408 	 */
1409 	uint32_t max32uint = NO_VAL;
1410 	long long tval;
1411 	char *p;
1412 
1413 	/*
1414 	 * Return error for invalid value.
1415 	 */
1416 	tval = strtoll(aval, &p, 10);
1417 	if (p[0] || (tval == LLONG_MIN) || (tval == LLONG_MAX) ||
1418 	    (tval < 0) || (tval >= max32uint))
1419 		return 1;
1420 
1421 	*ival = (uint32_t) tval;
1422 
1423 	return 0;
1424 }
1425 
1426 /*
1427  * parse_uint64 - Convert ascii string to a 64 bit unsigned int.
1428  * IN      aval - ascii string.
1429  * IN/OUT  ival - 64 bit pointer.
1430  * RET     0 if no error, 1 otherwise.
1431  */
parse_uint64(char * aval,uint64_t * ival)1432 extern int parse_uint64(char *aval, uint64_t *ival)
1433 {
1434 	/*
1435 	 * First,  convert the ascii value it to an
1436 	 * unsigned long long. If the result is greater
1437 	 * than or equal to 0 and less than NO_VAL
1438 	 * set the value and return. Otherwise return
1439 	 * an error.
1440 	 */
1441 	uint64_t max64uint = NO_VAL64;
1442 	long long tval;
1443 	char *p;
1444 
1445 	/*
1446  	 * Return error for invalid value.
1447 	 */
1448 	tval = strtoll(aval, &p, 10);
1449 	if (p[0] || (tval == LLONG_MIN) || (tval == LLONG_MAX) ||
1450 	    (tval < 0) || (tval >= max64uint))
1451 		return 1;
1452 
1453 	*ival = (uint64_t) tval;
1454 
1455 	return 0;
1456 }
1457 
1458 /*
1459  *  Get a decimal integer from arg.
1460  *
1461  *  Returns the integer on success, exits program on failure.
1462  */
parse_int(const char * name,const char * val,bool positive)1463 extern int parse_int(const char *name, const char *val, bool positive)
1464 {
1465 	char *p = NULL;
1466 	long int result = 0;
1467 
1468 	if (val)
1469 		result = strtol(val, &p, 10);
1470 
1471 	if ((p == NULL) || (p[0] != '\0') || (result < 0L) ||
1472 	    (positive && (result <= 0L))) {
1473 		error ("Invalid numeric value \"%s\" for %s.", val, name);
1474 		exit(1);
1475 	} else if (result == LONG_MAX) {
1476 		error ("Numeric argument (%ld) to big for %s.", result, name);
1477 		exit(1);
1478 	} else if (result == LONG_MIN) {
1479 		error ("Numeric argument (%ld) to small for %s.", result, name);
1480 		exit(1);
1481 	}
1482 
1483 	return (int) result;
1484 }
1485 
1486 /* print_db_notok() - Print an error message about slurmdbd
1487  *                    is unreachable or wrong cluster name.
1488  * IN  cname - char * cluster name
1489  * IN  isenv - bool  cluster name from env or from command line option.
1490  */
print_db_notok(const char * cname,bool isenv)1491 void print_db_notok(const char *cname, bool isenv)
1492 {
1493 	if (errno)
1494 		error("There is a problem talking to the database: %m.  "
1495 		      "Only local cluster communication is available, remove "
1496 		      "%s or contact your admin to resolve the problem.",
1497 		      isenv ? "SLURM_CLUSTERS from your environment" :
1498 		      "--cluster from your command line");
1499 	else if (!xstrcasecmp("all", cname))
1500 		error("No clusters can be reached now. "
1501 		      "Contact your admin to resolve the problem.");
1502 	else
1503 		error("'%s' can't be reached now, "
1504 		      "or it is an invalid entry for %s.  "
1505 		      "Use 'sacctmgr list clusters' to see available clusters.",
1506 		      cname, isenv ? "SLURM_CLUSTERS" : "--cluster");
1507 }
1508 
1509 /*
1510  * parse_resv_flags() used to parse the Flags= option.  It handles
1511  * daily, weekly, static_alloc, part_nodes, and maint, optionally
1512  * preceded by + or -, separated by a comma but no spaces.
1513  *
1514  * flagstr IN - reservation flag string
1515  * msg IN - string to append to error message (e.g. function name)
1516  * resv_msg_ptr IN/OUT - sets flags and times in ptr.
1517  * RET equivalent reservation flag bits
1518  */
parse_resv_flags(const char * flagstr,const char * msg,resv_desc_msg_t * resv_msg_ptr)1519 extern uint64_t parse_resv_flags(const char *flagstr, const char *msg,
1520 				 resv_desc_msg_t  *resv_msg_ptr)
1521 {
1522 	int op = RESV_NEW;
1523 	uint64_t outflags = 0;
1524 	char *curr = xstrdup(flagstr), *start = curr;
1525 	int taglen = 0;
1526 
1527 	while (*curr != '\0') {
1528 		if (*curr == '+') {
1529 			op = RESV_ADD;
1530 			curr++;
1531 		} else if (*curr == '-') {
1532 			op = RESV_REM;
1533 			curr++;
1534 		}
1535 		taglen = 0;
1536 		while (curr[taglen] != ',' && curr[taglen] != '\0'
1537 		       && curr[taglen] != '=')
1538 			taglen++;
1539 
1540 		if (xstrncasecmp(curr, "Maintenance", MAX(taglen,3)) == 0) {
1541 			curr += taglen;
1542 			if (op == RESV_REM)
1543 				outflags |= RESERVE_FLAG_NO_MAINT;
1544 			else
1545 				outflags |= RESERVE_FLAG_MAINT;
1546 		} else if ((xstrncasecmp(curr, "Overlap", MAX(taglen,1))
1547 			    == 0) && (op != RESV_REM)) {
1548 			curr += taglen;
1549 			outflags |= RESERVE_FLAG_OVERLAP;
1550 			/*
1551 			 * "-OVERLAP" is not supported since that's the
1552 			 * default behavior and the option only applies
1553 			 * for reservation creation, not updates
1554 			 */
1555 		} else if (xstrncasecmp(curr, "Flex", MAX(taglen,1)) == 0) {
1556 			curr += taglen;
1557 			if (op == RESV_REM)
1558 				outflags |= RESERVE_FLAG_NO_FLEX;
1559 			else
1560 				outflags |= RESERVE_FLAG_FLEX;
1561 		} else if (xstrncasecmp(curr, "Ignore_Jobs", MAX(taglen,1))
1562 			   == 0) {
1563 			curr += taglen;
1564 			if (op == RESV_REM)
1565 				outflags |= RESERVE_FLAG_NO_IGN_JOB;
1566 			else
1567 				outflags |= RESERVE_FLAG_IGN_JOBS;
1568 		} else if (xstrncasecmp(curr, "Daily", MAX(taglen,1)) == 0) {
1569 			curr += taglen;
1570 			if (op == RESV_REM)
1571 				outflags |= RESERVE_FLAG_NO_DAILY;
1572 			else
1573 				outflags |= RESERVE_FLAG_DAILY;
1574 		} else if (xstrncasecmp(curr, "Weekday", MAX(taglen,1)) == 0) {
1575 			curr += taglen;
1576 			if (op == RESV_REM)
1577 				outflags |= RESERVE_FLAG_NO_WEEKDAY;
1578 			else
1579 				outflags |= RESERVE_FLAG_WEEKDAY;
1580 		} else if (xstrncasecmp(curr, "Weekend", MAX(taglen,1)) == 0) {
1581 			curr += taglen;
1582 			if (op == RESV_REM)
1583 				outflags |= RESERVE_FLAG_NO_WEEKEND;
1584 			else
1585 				outflags |= RESERVE_FLAG_WEEKEND;
1586 		} else if (xstrncasecmp(curr, "Weekly", MAX(taglen,1)) == 0) {
1587 			curr += taglen;
1588 			if (op == RESV_REM)
1589 				outflags |= RESERVE_FLAG_NO_WEEKLY;
1590 			else
1591 				outflags |= RESERVE_FLAG_WEEKLY;
1592 		} else if (!xstrncasecmp(curr, "Any_Nodes", MAX(taglen,1)) ||
1593 			   !xstrncasecmp(curr, "License_Only", MAX(taglen,1))) {
1594 			curr += taglen;
1595 			if (op == RESV_REM)
1596 				outflags |= RESERVE_FLAG_NO_ANY_NODES;
1597 			else
1598 				outflags |= RESERVE_FLAG_ANY_NODES;
1599 		} else if (xstrncasecmp(curr, "Static_Alloc", MAX(taglen,1))
1600 			   == 0) {
1601 			curr += taglen;
1602 			if (op == RESV_REM)
1603 				outflags |= RESERVE_FLAG_NO_STATIC;
1604 			else
1605 				outflags |= RESERVE_FLAG_STATIC;
1606 		} else if (xstrncasecmp(curr, "Part_Nodes", MAX(taglen, 2))
1607 			   == 0) {
1608 			curr += taglen;
1609 			if (op == RESV_REM)
1610 				outflags |= RESERVE_FLAG_NO_PART_NODES;
1611 			else
1612 				outflags |= RESERVE_FLAG_PART_NODES;
1613 		} else if (!xstrncasecmp(curr, "magnetic", MAX(taglen, 3)) ||
1614 			   !xstrncasecmp(curr, "promiscuous", MAX(taglen, 2))) {
1615 			curr += taglen;
1616 			if (op == RESV_REM)
1617 				outflags |= RESERVE_FLAG_NO_PROM;
1618 			else
1619 				outflags |= RESERVE_FLAG_PROM;
1620 		} else if (!xstrncasecmp(curr, "PURGE_COMP", MAX(taglen, 2))) {
1621 			if (curr[taglen] == '=') {
1622 				int num_end;
1623 				taglen++;
1624 
1625 				num_end = taglen;
1626 				while (curr[num_end] != ',' &&
1627 				       curr[num_end] != '\0')
1628 					num_end++;
1629 				if (curr[num_end] == ',') {
1630 					curr[num_end] = '\0';
1631 					num_end++;
1632 				}
1633 				if (resv_msg_ptr)
1634 					resv_msg_ptr->purge_comp_time =
1635 						time_str2secs(curr+taglen);
1636 				taglen = num_end;
1637 			}
1638 			curr += taglen;
1639 			if (op == RESV_REM)
1640 				outflags |= RESERVE_FLAG_NO_PURGE_COMP;
1641 			else
1642 				outflags |= RESERVE_FLAG_PURGE_COMP;
1643 		} else if (!xstrncasecmp(curr, "First_Cores", MAX(taglen,1)) &&
1644 			   op != RESV_REM) {
1645 			curr += taglen;
1646 			outflags |= RESERVE_FLAG_FIRST_CORES;
1647 		} else if (!xstrncasecmp(curr, "Time_Float", MAX(taglen,1)) &&
1648 			   op == RESV_NEW) {
1649 			curr += taglen;
1650 			outflags |= RESERVE_FLAG_TIME_FLOAT;
1651 		} else if (!xstrncasecmp(curr, "Replace", MAX(taglen, 1)) &&
1652 			   op != RESV_REM) {
1653 			curr += taglen;
1654 			outflags |= RESERVE_FLAG_REPLACE;
1655 		} else if (!xstrncasecmp(curr, "Replace_Down", MAX(taglen, 8))
1656 			   && op != RESV_REM) {
1657 			curr += taglen;
1658 			outflags |= RESERVE_FLAG_REPLACE_DOWN;
1659 		} else if (!xstrncasecmp(curr, "NO_HOLD_JOBS_AFTER_END",
1660 					 MAX(taglen, 1)) && op != RESV_REM) {
1661 			curr += taglen;
1662 			outflags |= RESERVE_FLAG_NO_HOLD_JOBS;
1663 		} else {
1664 			error("Error parsing flags %s.  %s", flagstr, msg);
1665 			return INFINITE64;
1666 		}
1667 
1668 		if (*curr == ',') {
1669 			curr++;
1670 		}
1671 	}
1672 
1673 	if (resv_msg_ptr && (outflags != INFINITE64)) {
1674 		if (resv_msg_ptr->flags == NO_VAL64)
1675 			resv_msg_ptr->flags = outflags;
1676 		else
1677 			resv_msg_ptr->flags |= outflags;
1678 	}
1679 	xfree(start);
1680 	return outflags;
1681 }
1682 
1683 /* parse --compress for a compression type, set to default type if not found */
parse_compress_type(const char * arg)1684 uint16_t parse_compress_type(const char *arg)
1685 {
1686 	/* if called with null string return default compression type */
1687 	if (!arg) {
1688 #if HAVE_LZ4
1689 		return COMPRESS_LZ4;
1690 #elif HAVE_LIBZ
1691 		return COMPRESS_ZLIB;
1692 #else
1693 		error("No compression library available,"
1694 		      " compression disabled.");
1695 		return COMPRESS_OFF;
1696 #endif
1697 	}
1698 
1699 	if (!strcasecmp(arg, "zlib"))
1700 		return COMPRESS_ZLIB;
1701 	else if (!strcasecmp(arg, "lz4"))
1702 		return COMPRESS_LZ4;
1703 	else if (!strcasecmp(arg, "none"))
1704 		return COMPRESS_OFF;
1705 
1706 	error("Compression type '%s' unknown, disabling compression support.",
1707 	      arg);
1708 	return COMPRESS_OFF;
1709 }
1710 
validate_acctg_freq(char * acctg_freq)1711 extern int validate_acctg_freq(char *acctg_freq)
1712 {
1713 	int i;
1714 	char *save_ptr = NULL, *tok, *tmp;
1715 	bool valid;
1716 	int rc = SLURM_SUCCESS;
1717 
1718 	if (!acctg_freq)
1719 		return rc;
1720 
1721 	tmp = xstrdup(acctg_freq);
1722 	tok = strtok_r(tmp, ",", &save_ptr);
1723 	while (tok) {
1724 		valid = false;
1725 		for (i = 0; i < PROFILE_CNT; i++)
1726 			if (acct_gather_parse_freq(i, tok) != -1) {
1727 				valid = true;
1728 				break;
1729 			}
1730 
1731 		if (!valid) {
1732 			error("Invalid --acctg-freq specification: %s", tok);
1733 			rc = SLURM_ERROR;
1734 		}
1735 		tok = strtok_r(NULL, ",", &save_ptr);
1736 	}
1737 	xfree(tmp);
1738 
1739 	return rc;
1740 }
1741 
1742 /*
1743  * Format a tres_per_* argument
1744  * dest OUT - resulting string
1745  * prefix IN - TRES type (e.g. "gpu")
1746  * src IN - user input, can include multiple comma-separated specifications
1747  */
xfmt_tres(char ** dest,char * prefix,char * src)1748 extern void xfmt_tres(char **dest, char *prefix, char *src)
1749 {
1750 	char *result = NULL, *save_ptr = NULL, *sep = "", *tmp, *tok;
1751 
1752 	if (!src || (src[0] == '\0'))
1753 		return;
1754 	if (*dest) {
1755 		result = xstrdup(*dest);
1756 		sep = ",";
1757 	}
1758 	tmp = xstrdup(src);
1759 	tok = strtok_r(tmp, ",", &save_ptr);
1760 	while (tok) {
1761 		xstrfmtcat(result, "%s%s:%s", sep, prefix, tok);
1762 		sep = ",";
1763 		tok = strtok_r(NULL, ",", &save_ptr);
1764 	}
1765 	xfree(tmp);
1766 	*dest = result;
1767 }
1768 
1769 /*
1770  * Format a tres_freq argument
1771  * dest OUT - resulting string
1772  * prefix IN - TRES type (e.g. "gpu")
1773  * src IN - user input
1774  */
xfmt_tres_freq(char ** dest,char * prefix,char * src)1775 extern void xfmt_tres_freq(char **dest, char *prefix, char *src)
1776 {
1777 	char *result = NULL, *sep = "";
1778 
1779 	if (!src || (src[0] == '\0'))
1780 		return;
1781 	if (*dest) {
1782 		result = xstrdup(*dest);
1783 		sep = ";";
1784 	}
1785 	xstrfmtcat(result, "%s%s:%s", sep, prefix, src);
1786 	*dest = result;
1787 }
1788