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