|  | /*****************************************************************************\ | 
|  | *  fname.c - IO filename type implementation (srun specific) | 
|  | ***************************************************************************** | 
|  | *  Copyright (C) 2002 The Regents of the University of California. | 
|  | *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). | 
|  | *  Written by Mark Grondona <mgrondona@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 <ctype.h> | 
|  | #include <stdarg.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include "fname.h" | 
|  | #include "opt.h" | 
|  |  | 
|  | #include "src/common/xmalloc.h" | 
|  | #include "src/common/xstring.h" | 
|  | #include "src/common/xassert.h" | 
|  |  | 
|  | /* | 
|  | * Max zero-padding width allowed | 
|  | */ | 
|  | #define MAX_WIDTH 10 | 
|  |  | 
|  | static char *_remove_path_slashes(char *); | 
|  |  | 
|  | /* | 
|  | * Fill in as much of filename as possible from srun, update | 
|  | * filename type to one of the io types ALL, NONE, PER_TASK, ONE | 
|  | * These options should mirror those used with "sbatch" (parsed in | 
|  | * _batch_path_check found in src/slurmd/common/fname.c) | 
|  | */ | 
|  | extern fname_t *fname_create(srun_job_t *job, char *format, int task_count) | 
|  | { | 
|  | unsigned int wid     = 0; | 
|  | unsigned long int taskid  = 0; | 
|  | fname_t *fname = NULL; | 
|  | char *p, *q, *name, *tmp_env, *tmp_perc; | 
|  | uint32_t array_job_id  = job->step_id.job_id; | 
|  | uint32_t array_task_id = NO_VAL; | 
|  | char *esc; | 
|  | char *end; | 
|  | bool double_p = false; | 
|  |  | 
|  | fname = xmalloc(sizeof(*fname)); | 
|  | fname->type = IO_ALL; | 
|  | fname->name = NULL; | 
|  | fname->taskid = -1; | 
|  |  | 
|  | /* Handle special  cases | 
|  | */ | 
|  |  | 
|  | if ((format == NULL) | 
|  | || (xstrncasecmp(format, "all", (size_t) 3) == 0) | 
|  | || (xstrncmp(format, "-", (size_t) 1) == 0)       ) { | 
|  | /* "all" explicitly sets IO_ALL and is the default */ | 
|  | return fname; | 
|  | } | 
|  |  | 
|  | if (xstrcasecmp(format, "none") == 0) { | 
|  | /* | 
|  | * Set type to IO_PER_TASK so that /dev/null is opened | 
|  | *  on every node, which should be more efficient | 
|  | */ | 
|  | fname->type = IO_PER_TASK; | 
|  | fname->name = xstrdup ("/dev/null"); | 
|  | return fname; | 
|  | } | 
|  |  | 
|  | taskid = strtoul(format, &p, 10); | 
|  | if ((*p == '\0') && ((int) taskid < task_count)) { | 
|  | fname->type   = IO_ONE; | 
|  | fname->taskid = (uint32_t) taskid; | 
|  | /* Set the name string to pass to slurmd | 
|  | *  to the taskid requested, so that tasks with | 
|  | *  no IO can open /dev/null. | 
|  | */ | 
|  | fname->name   = xstrdup (format); | 
|  | return fname; | 
|  | } | 
|  |  | 
|  | /* Check if path has escaped characters | 
|  | * in it and prevent them to be expanded. | 
|  | */ | 
|  | esc = _remove_path_slashes(format); | 
|  | if (esc) { | 
|  | fname->name = esc; | 
|  | return fname; | 
|  | } | 
|  |  | 
|  | tmp_perc = NULL; | 
|  | name = NULL; | 
|  | q = p = format; | 
|  | while (*p != '\0') { | 
|  | if (*p == '%') { | 
|  | if (*(p+1) == '%') { | 
|  | p++; | 
|  | double_p = true; | 
|  | xmemcat(name, q, p); | 
|  | /* Save the removed % just in case */ | 
|  | xstrcat(tmp_perc, "%"); | 
|  | q = ++p; | 
|  | continue; | 
|  | } | 
|  | if (isdigit(*(++p))) { | 
|  | unsigned long in_width = 0; | 
|  | xmemcat(name, q, p - 1); | 
|  | if ((in_width = strtoul(p, &p, 10)) > MAX_WIDTH) | 
|  | wid = MAX_WIDTH; | 
|  | else | 
|  | wid = in_width; | 
|  | q = p - 1; | 
|  | if (*p == '\0') | 
|  | break; | 
|  | } | 
|  |  | 
|  | switch (*p) { | 
|  | case 'a':  /* '%a' => array task id   */ | 
|  | tmp_env = getenv("SLURM_ARRAY_TASK_ID"); | 
|  | if (tmp_env) | 
|  | array_task_id = strtoul(tmp_env, &end, | 
|  | 10); | 
|  | xmemcat(name, q, p - 1); | 
|  | xstrfmtcat(name, "%0*u", wid, | 
|  | array_task_id); | 
|  | xfree(tmp_perc); | 
|  | tmp_perc = NULL; | 
|  | q = ++p; | 
|  | break; | 
|  | case 'b':  /* '%b' => array task id modulo 10  */ | 
|  | tmp_env = getenv("SLURM_ARRAY_TASK_ID"); | 
|  | if (tmp_env) | 
|  | array_task_id = strtoul(tmp_env, &end, | 
|  | 10); | 
|  | xmemcat(name, q, p - 1); | 
|  | xstrfmtcat(name, "%0*u", wid, | 
|  | array_task_id % 10); | 
|  | xfree(tmp_perc); | 
|  | tmp_perc = NULL; | 
|  | q = ++p; | 
|  | break; | 
|  | case 'A':  /* '%A' => array master job id */ | 
|  | tmp_env = getenv("SLURM_ARRAY_JOB_ID"); | 
|  | if (tmp_env) | 
|  | array_job_id = strtoul(tmp_env, &end, | 
|  | 10); | 
|  | xmemcat(name, q, p - 1); | 
|  | xstrfmtcat(name, "%0*u", wid, | 
|  | array_job_id); | 
|  | xfree(tmp_perc); | 
|  | tmp_perc = NULL; | 
|  | q = ++p; | 
|  | break; | 
|  | case 'J':  /* '%J' => "jobid.stepid" */ | 
|  | case 'j':  /* '%j' => jobid          */ | 
|  | xmemcat(name, q, p - 1); | 
|  | xstrfmtcat(name, "%0*d", wid, | 
|  | job->step_id.job_id); | 
|  |  | 
|  | if ((*p == 'J') && | 
|  | (job->step_id.step_id != | 
|  | SLURM_BATCH_SCRIPT)) | 
|  | xstrfmtcat(name, ".%d", | 
|  | job->step_id.step_id); | 
|  | xfree(tmp_perc); | 
|  | tmp_perc = NULL; | 
|  | q = ++p; | 
|  | break; | 
|  | case 's':  /* '%s' => step_id.step_id         */ | 
|  | xmemcat(name, q, p - 1); | 
|  | xstrfmtcat(name, "%0*d", wid, | 
|  | job->step_id.step_id); | 
|  | xfree(tmp_perc); | 
|  | tmp_perc = NULL; | 
|  | q = ++p; | 
|  | break; | 
|  | case 'u':  /* '%u' => username       */ | 
|  | case 't':  /* '%t' => taskid         */ | 
|  | case 'n':  /* '%n' => nodeid         */ | 
|  | case 'N':  /* '%N' => node name      */ | 
|  | fname->type = IO_PER_TASK; | 
|  | if (double_p) | 
|  | xstrcat(name, tmp_perc); | 
|  |  | 
|  | if (wid) { /* Put the width back and | 
|  | * the removed %, so the | 
|  | * slurmstepd can remove it */ | 
|  | xstrcat(name, | 
|  | xstrdup_printf("%%%u", wid)); | 
|  |  | 
|  | } else { | 
|  | xmemcat(name, q, p); | 
|  | } | 
|  | xfree(tmp_perc); | 
|  | tmp_perc = NULL; | 
|  | q = p; | 
|  | p++; | 
|  | break; | 
|  | case 'x': | 
|  | xmemcat(name, q, p - 1); | 
|  | xstrfmtcat(name, "%s", getenv("SLURM_JOB_NAME")); | 
|  | q = ++p; | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | double_p = false; | 
|  | } else if (double_p) { | 
|  | /* special case when there is a | 
|  | * double percent and a t, n, or N | 
|  | */ | 
|  | double_p = false; | 
|  |  | 
|  | if (isdigit(*p)) | 
|  | wid = strtoul(p, &p, 10); | 
|  |  | 
|  | switch (*p) { | 
|  | case 'u':  /* '%u' => username       */ | 
|  | case 't':  /* '%t' => taskid         */ | 
|  | case 'n':  /* '%n' => nodeid         */ | 
|  | case 'N':  /* '%N' => node name      */ | 
|  | fname->type = IO_PER_TASK; | 
|  | /* Put back all the removed % */ | 
|  | xstrcat(name, tmp_perc); | 
|  | if (wid) { /* Put the width back and | 
|  | * the removed %, so the | 
|  | * step can remove it | 
|  | */ | 
|  | xstrcat(name, | 
|  | xstrdup_printf("%u", wid)); | 
|  | q = p; | 
|  | } | 
|  | xfree(tmp_perc); | 
|  | tmp_perc = NULL; | 
|  | p++; | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  |  | 
|  | } else | 
|  | p++; | 
|  | wid = 0; | 
|  | } | 
|  |  | 
|  | if (q != p) | 
|  | xmemcat(name, q, p); | 
|  |  | 
|  | xfree(tmp_perc); | 
|  | fname->name = name; | 
|  | return fname; | 
|  | } | 
|  |  | 
|  | void | 
|  | fname_destroy(fname_t *f) | 
|  | { | 
|  | if (f->name) | 
|  | xfree(f->name); | 
|  | xfree(f); | 
|  | } | 
|  |  | 
|  | char * | 
|  | fname_remote_string (fname_t *f) | 
|  | { | 
|  | if ((f->type == IO_PER_TASK) || (f->type == IO_ONE)) | 
|  | return (xstrdup (f->name)); | 
|  |  | 
|  | return (NULL); | 
|  | } | 
|  |  | 
|  | /* remove_path_slashes() | 
|  | * | 
|  | * If there are \ chars in the path strip them. | 
|  | * The new path will tell the caller not to | 
|  | * translate escaped characters. | 
|  | */ | 
|  | static char * | 
|  | _remove_path_slashes(char *p) | 
|  | { | 
|  | char *buf; | 
|  | bool t; | 
|  | int i; | 
|  |  | 
|  | if (p == NULL) | 
|  | return NULL; | 
|  |  | 
|  | buf = xmalloc(strlen(p) + 1); | 
|  | t = false; | 
|  | i = 0; | 
|  |  | 
|  | while (*p) { | 
|  | if (*p == '\\') { | 
|  | t = true; | 
|  | ++p; | 
|  | continue; | 
|  | } | 
|  | buf[i] = *p; | 
|  | ++i; | 
|  | ++p; | 
|  | } | 
|  |  | 
|  | if (t == false) { | 
|  | xfree(buf); | 
|  | return NULL; | 
|  | } | 
|  | return buf; | 
|  | } |