blob: 8130d52944cfbc63da7d1a24592fc2135d84afd6 [file] [log] [blame]
/*****************************************************************************\
* print.c - definitions for all printing functions.
*****************************************************************************
* Copyright (C) 2008 Lawrence Livermore National Security.
* Copyright (C) 2002-2007 The Regents of the University of California.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Danny Auble <da@llnl.gov>
* CODE-OCEC-09-009. All rights reserved.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#include "src/common/print_fields.h"
#include "src/common/parse_time.h"
#include "src/common/read_config.h"
#include "src/common/sluid.h"
int print_fields_parsable_print = 0;
int print_fields_have_header = 1;
char *fields_delimiter = NULL;
typedef enum {
STATE_INIT,
STATE_EXPAND,
STATE_DONE,
STATE_ESCAPE,
STATE_ERROR
} parser_state;
extern void destroy_print_field(void *object)
{
print_field_t *field = (print_field_t *)object;
if (field) {
xfree(field->name);
xfree(field);
}
}
extern void print_fields_header(list_t *print_fields_list)
{
list_itr_t *itr = NULL;
print_field_t *field = NULL;
int curr_inx = 1;
int field_count = 0;
if (!print_fields_list || !print_fields_have_header)
return;
field_count = list_count(print_fields_list);
itr = list_iterator_create(print_fields_list);
while ((field = list_next(itr))) {
if (print_fields_parsable_print
== PRINT_FIELDS_PARSABLE_NO_ENDING
&& (curr_inx == field_count))
printf("%s", field->name);
else if (print_fields_parsable_print
&& fields_delimiter) {
printf("%s%s", field->name, fields_delimiter);
} else if (print_fields_parsable_print
&& !fields_delimiter) {
printf("%s|", field->name);
} else {
int abs_len = abs(field->len);
printf("%*.*s ", field->len, abs_len, field->name);
}
curr_inx++;
}
list_iterator_reset(itr);
printf("\n");
if (print_fields_parsable_print) {
list_iterator_destroy(itr);
return;
}
while ((field = list_next(itr))) {
int abs_len = abs(field->len);
for (int i = 0; i < abs_len; i++)
putchar('-');
putchar(' ');
}
list_iterator_destroy(itr);
printf("\n");
}
extern void print_fields_date(print_field_t *field, void *input, int last)
{
int abs_len = print_fields_parsable_print ? 256 : abs(field->len);
char temp_char[abs_len+1];
time_t value = 0;
if (input)
value = *(time_t *) input;
slurm_make_time_str(&value, (char *)temp_char, sizeof(temp_char));
if (print_fields_parsable_print == PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
printf("%s", temp_char);
else if (print_fields_parsable_print && !fields_delimiter)
printf("%s|", temp_char);
else if (print_fields_parsable_print && fields_delimiter)
printf("%s%s", temp_char, fields_delimiter);
else if (field->len == abs_len)
printf("%*.*s ", abs_len, abs_len, temp_char);
else
printf("%-*.*s ", abs_len, abs_len, temp_char);
}
extern void print_fields_str(print_field_t *field, void *input, int last)
{
int abs_len = abs(field->len);
char temp_char[abs_len+1];
char *print_this = NULL;
char *value = NULL;
if (input)
value = input;
if (!value) {
if (print_fields_parsable_print)
print_this = "";
else
print_this = " ";
} else
print_this = value;
if (print_fields_parsable_print == PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
printf("%s", print_this);
else if (print_fields_parsable_print && !fields_delimiter)
printf("%s|", print_this);
else if (print_fields_parsable_print && fields_delimiter)
printf("%s%s", print_this, fields_delimiter);
else {
if (value) {
int len = strlen(value);
memcpy(&temp_char, value, MIN(len, abs_len) + 1);
if (len > abs_len)
temp_char[abs_len-1] = '+';
print_this = temp_char;
}
if (field->len == abs_len)
printf("%*.*s ", abs_len, abs_len, print_this);
else
printf("%-*.*s ", abs_len, abs_len, print_this);
}
}
extern void print_fields_uint16(print_field_t *field, void *input, int last)
{
int abs_len = abs(field->len);
uint16_t value = NO_VAL16;
if (input)
value = *(uint16_t *) input;
/* (value == unset) || (value == cleared) */
if (((uint16_t)value == NO_VAL16)
|| ((uint16_t)value == INFINITE16)) {
if (print_fields_parsable_print
== PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
;
else if (print_fields_parsable_print && !fields_delimiter)
printf("|");
else if (print_fields_parsable_print && fields_delimiter)
printf("%s", fields_delimiter);
else
printf("%*s ", field->len, " ");
} else {
if (print_fields_parsable_print
== PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
printf("%u", value);
else if (print_fields_parsable_print && !fields_delimiter)
printf("%u|", value);
else if (print_fields_parsable_print && fields_delimiter)
printf("%u%s", value, fields_delimiter);
else if (field->len == abs_len)
printf("%*u ", abs_len, value);
else
printf("%-*u ", abs_len, value);
}
}
extern void print_fields_uint32(print_field_t *field, void *input, int last)
{
int abs_len = abs(field->len);
uint32_t value = NO_VAL;
if (input)
value = *(uint32_t *) input;
/* (value == unset) || (value == cleared) */
if ((value == NO_VAL) || (value == INFINITE)) {
if (print_fields_parsable_print
== PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
;
else if (print_fields_parsable_print && !fields_delimiter)
printf("|");
else if (print_fields_parsable_print && fields_delimiter)
printf("%s", fields_delimiter);
else
printf("%*s ", field->len, " ");
} else {
if (print_fields_parsable_print
== PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
printf("%u", value);
else if (print_fields_parsable_print && !fields_delimiter)
printf("%u|", value);
else if (print_fields_parsable_print && fields_delimiter)
printf("%u%s", value, fields_delimiter);
else if (field->len == abs_len)
printf("%*u ", abs_len, value);
else
printf("%-*u ", abs_len, value);
}
}
extern void print_fields_uint64(print_field_t *field, void *input, int last)
{
int abs_len = abs(field->len);
uint64_t value = NO_VAL64;
if (input)
value = *(uint64_t *) input;
/* (value == unset) || (value == cleared) */
if ((value == NO_VAL64) || (value == INFINITE64)) {
if (print_fields_parsable_print
== PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
;
else if (print_fields_parsable_print && !fields_delimiter)
printf("|");
else if (print_fields_parsable_print && fields_delimiter)
printf("%s", fields_delimiter);
else
printf("%*s ", field->len, " ");
} else {
if (print_fields_parsable_print
== PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
printf("%llu", (long long unsigned) value);
else if (print_fields_parsable_print && !fields_delimiter)
printf("%llu|", (long long unsigned) value);
else if (print_fields_parsable_print && fields_delimiter)
printf("%llu%s", (long long unsigned) value,
fields_delimiter);
else if (field->len == abs_len)
printf("%*llu ", abs_len, (long long unsigned) value);
else
printf("%-*llu ", abs_len, (long long unsigned) value);
}
}
extern void print_fields_double(print_field_t *field, void *input, int last)
{
int abs_len = abs(field->len);
double value = (double) NO_VAL64;
if (input)
value = *(double *) input;
/* (value == unset) || (value == cleared) */
if ((value == (double) NO_VAL64) || (value == (double) INFINITE64) ||
(value == (double) NO_VAL) || (value == (double) INFINITE)) {
if (print_fields_parsable_print
== PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
;
else if (print_fields_parsable_print && !fields_delimiter)
printf("|");
else if (print_fields_parsable_print && fields_delimiter)
printf("%s", fields_delimiter);
else
printf("%*s ", field->len, " ");
} else {
if (print_fields_parsable_print
== PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
printf("%f", value);
else if (print_fields_parsable_print && !fields_delimiter)
printf("%f|", value);
else if (print_fields_parsable_print && fields_delimiter)
printf("%f%s", value, fields_delimiter);
else {
int length, width = abs_len;
int new_length;
char *tmp = NULL;
xstrfmtcat(tmp, "%*f", abs_len, value);
length = strlen(tmp);
if (length > width) {
xstrfmtcat(tmp, "%*.*e", width, width, value);
new_length = strlen(tmp) - length;
if (new_length > width)
width -= new_length - width;
if (field->len == abs_len)
printf("%*.*e ", width, width, value);
else
printf("%-*.*e ", width, width, value);
} else {
if (field->len == abs_len)
printf("%*f ", width, value);
else
printf("%-*f ", width, value);
}
xfree(tmp);
}
}
}
extern void print_fields_time(print_field_t *field, void *input, int last)
{
int abs_len = abs(field->len);
uint32_t value = NO_VAL;
if (input)
value = *(uint32_t *) input;
/* (value == unset) || (value == cleared) */
if ((value == NO_VAL) || (value == INFINITE)) {
if (print_fields_parsable_print
== PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
;
else if (print_fields_parsable_print && !fields_delimiter)
printf("|");
else if (print_fields_parsable_print && fields_delimiter)
printf("%s", fields_delimiter);
else
printf("%*s ", field->len, " ");
} else {
char time_buf[32];
mins2time_str((time_t) value, time_buf, sizeof(time_buf));
if (print_fields_parsable_print
== PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
printf("%s", time_buf);
else if (print_fields_parsable_print && !fields_delimiter)
printf("%s|", time_buf);
else if (print_fields_parsable_print && fields_delimiter)
printf("%s%s", time_buf, fields_delimiter);
else if (field->len == abs_len)
printf("%*s ", abs_len, time_buf);
else
printf("%-*s ", abs_len, time_buf);
}
}
extern void print_fields_time_from_secs(print_field_t *field,
void *input, int last)
{
int abs_len = abs(field->len);
uint64_t value = NO_VAL64;
if (input)
value = *(uint64_t *) input;
/* (value == unset) || (value == cleared) */
if ((value == NO_VAL64) || (value == INFINITE64)) {
if (print_fields_parsable_print
== PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
;
else if (print_fields_parsable_print && !fields_delimiter)
printf("|");
else if (print_fields_parsable_print && fields_delimiter)
printf("%s", fields_delimiter);
else
printf("%*s ", field->len, " ");
} else {
char time_buf[32];
secs2time_str((time_t) value, time_buf, sizeof(time_buf));
if (print_fields_parsable_print
== PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
printf("%s", time_buf);
else if (print_fields_parsable_print && !fields_delimiter)
printf("%s|", time_buf);
else if (print_fields_parsable_print && fields_delimiter)
printf("%s%s", time_buf, fields_delimiter);
else if (field->len == abs_len)
printf("%*s ", abs_len, time_buf);
else
printf("%-*s ", abs_len, time_buf);
}
}
extern void print_fields_sluid(print_field_t *field, void *input, int last)
{
int abs_len = abs(field->len);
sluid_t sluid = 0;
char *sluid_str = NULL;
if (input)
sluid = *(sluid_t *) input;
sluid_str = sluid2str(sluid);
if ((print_fields_parsable_print == PRINT_FIELDS_PARSABLE_NO_ENDING)
&& last)
printf("%s", sluid_str);
else if (print_fields_parsable_print && !fields_delimiter)
printf("%s|", sluid_str);
else if (print_fields_parsable_print && fields_delimiter)
printf("%s%s", sluid_str, fields_delimiter);
else if (field->len == abs_len)
printf("%*s ", abs_len, sluid_str);
else
printf("%-*s ", abs_len, sluid_str);
xfree(sluid_str);
}
extern void print_fields_char_list(print_field_t *field, void *input, int last)
{
int abs_len = abs(field->len);
char *print_this = NULL;
list_t *value = NULL;
if (input)
value = *(list_t **) input;
if (!value || !list_count(value)) {
if (print_fields_parsable_print)
print_this = xstrdup("");
else
print_this = xstrdup(" ");
} else {
print_this = slurm_char_list_to_xstr(value);
}
if (print_fields_parsable_print == PRINT_FIELDS_PARSABLE_NO_ENDING
&& last)
printf("%s", print_this);
else if (print_fields_parsable_print && !fields_delimiter)
printf("%s|", print_this);
else if (print_fields_parsable_print && fields_delimiter)
printf("%s%s", print_this, fields_delimiter);
else if (print_this) {
if (strlen(print_this) > abs_len)
print_this[abs_len-1] = '+';
if (field->len == abs_len)
printf("%*.*s ", abs_len, abs_len, print_this);
else
printf("%-*.*s ", abs_len, abs_len, print_this);
}
xfree(print_this);
}
static bool _is_wildcard(char *ptr)
{
switch (*ptr) {
case 'A': /* Array job ID */
case 'a': /* Array task ID */
case 'b': /* Array task ID modulo 10 */
case 'J': /* Jobid.stepid */
case 'j': /* Job ID */
case 'N': /* Short hostname */
case 'n': /* Node id relative to current job */
case 's': /* Stepid of the running job */
case 't': /* Task id (rank) relative to current job */
case 'u': /* User name */
case 'x':
return true;
break;
default:
break;
}
return false;
}
static void _expand_wildcard(char **expanded, char **pos, char *ptr,
uint32_t padding, job_std_pattern_t *job)
{
switch (*ptr) {
case 'A': /* Array job ID */
if (job->array_job_id) {
xstrfmtcatat(*expanded, pos, "%0*u", padding,
job->array_job_id);
} else {
xstrfmtcatat(*expanded, pos, "%0*u", padding,
job->jobid);
}
break;
case 'J': /* Jobid.stepid */
case 'j': /* Job ID */
xstrfmtcatat(*expanded, pos, "%0*u", padding, job->jobid);
if ((*ptr == 'J') && (job->first_step_id != SLURM_BATCH_SCRIPT))
xstrfmtcatat(*expanded, pos, ".%d", job->first_step_id);
break;
case 'a': /* Array task ID */
xstrfmtcatat(*expanded, pos, "%0*u", padding,
job->array_task_id);
break;
case 'b': /* Array task ID modulo 10 */
xstrfmtcatat(*expanded, pos, "%0*u", padding,
job->array_task_id % 10);
break;
case 'N': /* Short hostname */
xstrfmtcatat(*expanded, pos, "%s", job->first_step_node);
break;
case 's': /* Stepid of the running job */
if (job->first_step_id == SLURM_BATCH_SCRIPT)
xstrcatat(*expanded, pos, "batch");
else
xstrfmtcatat(*expanded, pos, "%0*u", padding,
job->first_step_id);
break;
case 'n': /* Node id relative to current job */
case 't': /* Task id (rank) relative to current job */
xstrfmtcatat(*expanded, pos, "0");
break;
case 'u': /* User name */
xstrfmtcatat(*expanded, pos, "%s", job->user);
break;
case 'x':
xstrfmtcatat(*expanded, pos, "%s", job->jobname);
break;
default:
break;
}
}
/*
* Special expansion function for stdin/stdout/stderr filename patterns.
* Fields that can potentially map to a range of values will use the first in
* that range (e.g %t is replaced by 0).
*
* \ If we found this symbol, don't replace anything.
* %% The character "%".
* %A Job array's master job allocation number.
* %a Job array ID (index) number.
* %J jobid.stepid of the running job. (e.g. "128.0" or "batch")
* %j jobid of the running job.
* %N short hostname. This will create a separate IO file per node.
* %n Node identifier relative to current job.
* %s stepid of the running job.
* %t task identifier (rank) relative to current job.
* %u User name.
* %x Job name.
*
* A number placed between the percent character and format specifier may be
* used to zero-pad the result. Ignored if the format specifier corresponds to
* non-numeric data (%N for example). The maximum padding is 10.
* job%J.out job128.0.out
* job%4j.out job0128.out
* job%2j-%2t.out job128-00.out, job128-01.out, ...
*
* OUT - Expanded path as xstring. Must xfree.
* IN stdio_path - stdin/stdout/stderr job filepath
* IN job - job record in the database
*/
extern char *expand_stdio_fields(char *stdio_path, job_std_pattern_t *job)
{
parser_state curr_state = STATE_INIT;
char *ptr, *end, *expanded = NULL, *pos = NULL;
uint32_t padding = 0;
if (!stdio_path || !*stdio_path || !job)
return NULL;
if (job->work_dir && (stdio_path[0] != '/')) {
size_t len = strlen(job->work_dir);
if (job->work_dir[len - 1] == '/')
xstrcatat(expanded, &pos, job->work_dir);
else
xstrfmtcatat(expanded, &pos, "%s/", job->work_dir);
}
/*
* Special case, if we find a \ it means the file has not been
* expanded in any case, so skip any replacement from now on.
*/
if (xstrstr(stdio_path, "\\"))
curr_state = STATE_ESCAPE;
ptr = stdio_path;
while (*ptr) {
switch (curr_state) {
case STATE_ESCAPE:
if (*ptr != '\\')
xstrfmtcatat(expanded, &pos, "%c", *ptr);
break;
case STATE_INIT:
if (*ptr == '%')
curr_state = STATE_EXPAND;
else
xstrfmtcatat(expanded, &pos, "%c", *ptr);
break;
case STATE_EXPAND:
/* Double %% is escape, so print one %. */
if (*ptr == '%') {
xstrfmtcatat(expanded, &pos, "%c", *ptr);
curr_state = STATE_INIT;
break;
}
if (isdigit(*ptr)) {
char *tmp_ptr = ptr;
if ((padding = strtoul(ptr, &end, 10)) > 9) {
/* Remove % and double digit 10 */
ptr = end;
padding = 10;
} else {
ptr++;
}
/*
* weird behavior fix: we remove all the digits
* except the last one to match with the current
* fname creation.
*/
if (!_is_wildcard(ptr)) {
ptr = tmp_ptr;
/* seek until the last digit */
while (isdigit(*(ptr + 1))) {
ptr++;
}
xstrfmtcatat(expanded, &pos,
"%c", *ptr);
padding = 0;
curr_state = STATE_INIT;
break;
}
}
if (!_is_wildcard(ptr)) {
/* If not a wildcard print also the %. */
xstrfmtcatat(expanded, &pos, "%%%c", *ptr);
} else {
_expand_wildcard(&expanded, &pos, ptr, padding,
job);
padding = 0;
}
curr_state = STATE_INIT;
break;
default:
break;
}
ptr++;
}
return expanded;
}