/*___INFO__MARK_BEGIN__*/ /************************************************************************* * * The Contents of this file are made available subject to the terms of * the Sun Industry Standards Source License Version 1.2 * * Sun Microsystems Inc., March, 2001 * * * Sun Industry Standards Source License Version 1.2 * ================================================= * The contents of this file are subject to the Sun Industry Standards * Source License Version 1.2 (the "License"); You may not use this file * except in compliance with the License. You may obtain a copy of the * License at http://gridengine.sunsource.net/Gridengine_SISSL_license.html * * Software provided under this License is provided on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. * See the License for the specific provisions governing your rights and * obligations concerning the Software. * * The Initial Developer of the Original Code is: Sun Microsystems, Inc. * * Copyright: 2001 by Sun Microsystems, Inc. * * All Rights Reserved. * ************************************************************************/ /*___INFO__MARK_END__*/ #include #include #include #include #include #include "rmon/sgermon.h" #include "uti/sge_stdio.h" #include "uti/sge_log.h" #include "uti/setup_path.h" #include "uti/sge_string.h" #include "uti/sge_uidgid.h" #include "uti/sge_unistd.h" #include "uti/sge_hostname.h" #include "cl_errors.h" #include "sge_host.h" #include "sge_answer.h" #include "sge_job.h" #include "sge_utility.h" #include "sge_path_alias.h" #include "msg_common.h" #include "msg_daemons_common.h" #include "msg_sgeobjlib.h" /****** sgeobj/path_alias/-PathAlias ******************************************* * NAME * PathAlias - Path aliasing mechanism for SGE/EE * * FUNCTION * Sometimes the directory structure on the different submit, * execution hosts is not exactly the same. This makes it * necessary to map certain paths for specific hosts. * * The administrators/users have the possibility to * activate path aliasing by creating one or more of following * files: * * $SGE_ROOT/$CELL/common/sge_aliases * $HOME/.sge_aliases * * The file format is documentied in the ADOC commet for the * function path_alias_read_from_file(). * * The files are interpreted as follows: * - after a submit client (qsub, qmon, ...) has retrieved * the current working directory, the cluster global * path aliasing file is read, if present. The user * path aliasing file is read afterwards, as if it were * appended to the global file. * - as soon as both files are read, the path aliasing * information is passed along with the submitted job. * - On the execution host the aliasing information will be * evaluated. The leading part of the current working * directory will be replaced if the execution host entry of * the path alias matches the executing host. * * SEE ALSO * sgeobj/path_alias/path_alias_read_from_file() * sgeobj/path_alias/path_alias_list_initialize() * sgeobj/path_alias/path_alias_list_get_path() ******************************************************************************/ static int path_alias_read_from_file(lList **path_alias_list, lList **alpp, char *file_name); /****** sgeobj/path_alias/path_alias_read_from_file() ************************* * NAME * path_alias_read_from_file() -- read file content to list * * SYNOPSIS * #include * * static int path_alias_read_from_file(lList **path_alias_list, * lList **alpp, * char *file_name) * * FUNCTION * Read and parse the file with the name "file_name" and append * entries into "path_alias_list". Errors will be logged in "alpp". * * File format: * - Blank lines and lines beginning with a # sign in the first * column are skipped. * - Each line - other than a blank line or a line preceded by # - * must contain four strings separated by any number of blanks * or tabs. * - The first string specifies a source path, the second a submit * host, the third an execution host, and the fourth the source * path replacement. * - Both the submit and the execution host entries may consist * of only a * sign, which matches any host. * * INPUTS * lList **path_alias_list - PA_Type list pointer * lList **alpp - AN_Type list pointer * char *file_name - name of an alias file * * RESULT * static int - error state * -1 - Error * 0 - OK * * NOTES * MT-NOTE: path_alias_read_from_file() is MT safe ******************************************************************************/ static int path_alias_read_from_file(lList **path_alias_list, lList **alpp, char *file_name) { FILE *fp; char buf[10000]; char err[BUFSIZ]; char origin[SGE_PATH_MAX]; char submit_host[SGE_PATH_MAX]; char exec_host[SGE_PATH_MAX]; char translation[SGE_PATH_MAX]; lListElem *pal; SGE_STRUCT_STAT sb; int ret = 0; DENTER(GDI_LAYER, "path_alias_read_from_file"); if (!path_alias_list || !file_name) { CRITICAL((SGE_EVENT, MSG_SGETEXT_NULLPTRPASSED_S, SGE_FUNC)); answer_list_add(alpp, SGE_EVENT, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR); DEXIT; return -1; } if ((SGE_STAT(file_name, &sb) != 0) && (errno == ENOENT)) { DEXIT; return 0; } if (!(fp=(fopen(file_name, "r")))) { DEXIT; return -1; } while (fgets(buf, sizeof(buf), fp)) { char *crp; /* strip \n */ if ((crp = strchr(buf, (int)'\n'))) *crp = '\0'; DPRINTF(("Path Alias: >%s<\n",buf)); /* skip empty lines and comments */ if (!strlen(buf) || (*buf == '#' )) { continue; } /* * reset */ origin[0] = '\0'; submit_host[0] = '\0'; exec_host[0] = '\0'; translation[0] = '\0'; sscanf(buf, "%s %s %s %s", origin, submit_host, exec_host, translation); /* * check for correctness of path alias file */ if (*origin == '\0' || *submit_host == '\0' || *exec_host == '\0' || *translation == '\0') { sprintf(err, MSG_ALIAS_INVALIDSYNTAXOFPATHALIASFILEX_S, file_name); answer_list_add(alpp, err, STATUS_ESYNTAX, ANSWER_QUALITY_ERROR); ret = -1; break; } /* * compress multiple slashes to one slash */ sge_compress_slashes(origin); sge_compress_slashes(translation); pal = lAddElemStr(path_alias_list, PA_origin, origin, PA_Type); if (!pal) { answer_list_add(alpp, MSG_SGETEXT_NOMEM, STATUS_EMALLOC, ANSWER_QUALITY_ERROR); ret = -1; break; } /* * set the values of the element */ lSetHost(pal, PA_submit_host, submit_host); if ( strcmp(submit_host, "*") && (sge_resolve_host(pal, PA_submit_host) != CL_RETVAL_OK)) { SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_SGETEXT_CANTRESOLVEHOST_S, submit_host)); answer_list_add(alpp, SGE_EVENT, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR); ret = -1; break; } lSetHost(pal, PA_exec_host, exec_host); lSetString(pal, PA_translation, translation); } /* while (fgets) */ FCLOSE(fp); DEXIT; return ret; FCLOSE_ERROR: SGE_ADD_MSG_ID(sprintf(SGE_EVENT, MSG_FILE_ERRORCLOSEINGXY_SS, file_name, strerror(errno))); answer_list_add(alpp, SGE_EVENT, STATUS_EUNKNOWN, ANSWER_QUALITY_ERROR); return -1; } /****** sgeobj/path_alias/path_alias_list_initialize() ************************ * NAME * path_alias_list_initialize() -- initialize path_alias_list * * SYNOPSIS * int path_alias_list_initialize(lList **path_alias_list, * lList **alpp, * const char *user, * const char *host) * * FUNCTION * Intitialize "path_alias_list" according to the different * path aliasing files. * * Following files will be used if available: * * $SGE_ROOT/$CELL/common/sge_aliases * $HOME/.sge_aliases * * INPUTS * lList **path_alias_list - PA_Type list pointer * lList **alpp - AN_Type list pointer * const char *user - username * const char *host - hostname * * RESULT * int - return state * -1 - error * 0 - OK * * NOTES * MT-NOTE: path_alias_list_initialize() is MT safe ******************************************************************************/ int path_alias_list_initialize(lList **path_alias_list, lList **alpp, const char *cell_root, const char *user, const char *host) { char filename[2][SGE_PATH_MAX]; char err[BUFSIZ]; DENTER(TOP_LAYER, "path_alias_list_initialize"); /* * find names of different sge_path_alias files: * global * home directory */ { struct passwd *pwd; struct passwd pw_struct; char *buffer; int size; size = get_pw_buffer_size(); buffer = sge_malloc(size); pwd = sge_getpwnam_r(user, &pw_struct, buffer, size); if (!pwd) { sprintf(err, MSG_USER_INVALIDNAMEX_S, user); answer_list_add(alpp, err, STATUS_ENOSUCHUSER, ANSWER_QUALITY_ERROR); FREE(buffer); DRETURN(-1); } if (!pwd->pw_dir) { sprintf(err, MSG_USER_NOHOMEDIRFORUSERX_S, user); answer_list_add(alpp, err, STATUS_EDISK, ANSWER_QUALITY_ERROR); FREE(buffer); DRETURN(-1); } sprintf(filename[0], "%s/%s", cell_root, PATH_ALIAS_COMMON_FILE); sprintf(filename[1], "%s/%s", pwd->pw_dir, PATH_ALIAS_HOME_FILE); FREE(buffer); } /* * read files */ { int i; for (i=0; i<2; i++) { if (path_alias_read_from_file(path_alias_list, alpp, filename[i]) != 0) { sprintf(err, MSG_ALIAS_CANTREAD_SS, filename[i], strerror(errno)); answer_list_add(alpp, err, STATUS_EDISK, ANSWER_QUALITY_ERROR); DEXIT; return -1; } } } /* * remove the unnecessary hosts from the list */ { lCondition *where = NULL; where = lWhere("%T(%I == %s || %I == %s)", PA_Type, PA_submit_host, "*", PA_submit_host, host); *path_alias_list = lSelectDestroy(*path_alias_list, where); lFreeWhere(&where); } DEXIT; return 0; } /****** sgeobj/path_alias/path_alias_list_get_path() ************************** * NAME * path_alias_list_get_path() -- map path according alias table * * SYNOPSIS * int path_alias_list_get_path(const lList *path_aliases, * lList **alpp, * const char *inpath, * const char *myhost, * char *outpath, * int outmax) * * FUNCTION * "path_aliases" is used to map "inpath" for the host "myhost" * into its alias path which will be written into the buffer * "outpath" of size "outmax". * * INPUTS * const lList *path_aliases - alias table (PA_Type) * lList **alpp - AN_Type list pointer * const char *inpath - input path * const char *myhost - hostname * char *outpath - result path * int outmax - size of "outpath" * * RESULT * int - return state * 0 - OK * * NOTES * MT-NOTE: path_alias_list_get_path() is MT safe *******************************************************************************/ int path_alias_list_get_path(const lList *path_aliases, lList **alpp, const char *inpath, const char *myhost, dstring *outpath) { lListElem *pap; const char *origin; const char *translation; const char *exec_host; dstring the_path = DSTRING_INIT; DENTER(TOP_LAYER, "path_alias_list_get_path"); sge_dstring_copy_string(outpath, inpath); sge_dstring_copy_dstring(&the_path, outpath); if (path_aliases && lGetNumberOfElem(path_aliases) > 0) { for_each(pap, path_aliases) { size_t orign_str_len = 0; origin = lGetString(pap, PA_origin); orign_str_len = strlen(origin); exec_host = lGetHost(pap, PA_exec_host); translation = lGetString(pap, PA_translation); if (strncmp(origin, sge_dstring_get_string(&the_path), orign_str_len )) { /* path leaders aren't the same ==> no match */ continue; } /* the paths are ok, what about the exec hosts ? */ /* if exec_host is a '*' we have a match */ if (*exec_host != '*') { /* no '*', so we have to look closer */ /* resolv the exec host from the alias */ if (sge_resolve_host(pap, PA_exec_host) != CL_RETVAL_OK) { ERROR((SGE_EVENT, MSG_SGETEXT_CANTRESOLVEHOST_S, exec_host)); continue; } exec_host = lGetHost(pap, PA_exec_host); /* and compare it to the executing host */ if (sge_hostcmp(exec_host, myhost)) continue; } /* copy the alias as leading part of cwd */ sge_dstring_copy_string(outpath, translation); /* now append the trailer of the original cwd */ { const char *path = sge_dstring_get_string(&the_path); sge_dstring_append(outpath, path + orign_str_len ); } DPRINTF(("Path "SFQ" has been aliased to "SFQ"\n", inpath, sge_dstring_get_string(outpath))); /* and we have to start all over again for subsequent aliases */ sge_dstring_copy_dstring(&the_path, outpath); } } else { DPRINTF(("\"path_aliases\" containes no elements\n")); } sge_dstring_free(&the_path); DEXIT; return 0; } /****** sge_path_alias/path_verify() ******************************************* * NAME * path_verify() -- verify a path string * * SYNOPSIS * bool * path_verify(const char *path, lList **answer_list, const char *name, * bool absolute) * * FUNCTION * Verifies if a path string has valid contents. * * INPUTS * const char *path - the string to verify * lList **answer_list - answer list to pass back error messages * const char *name - name of the path to check, e.g. "prolog" * for error output * bool absolute - does it have to be an absolute path? * * RESULT * bool - true on success, * false on error with error message in answer_list * * NOTES * MT-NOTE: path_verify() is MT safe *******************************************************************************/ bool path_verify(const char *path, lList **answer_list, const char *name, bool absolute) { bool ret = true; if (path == NULL || *path == '\0') { answer_list_add_sprintf(answer_list, STATUS_ESYNTAX, ANSWER_QUALITY_ERROR, MSG_PATH_ALIAS_INVALID_PATH); ret = false; } if (ret) { if (strlen(path) > SGE_PATH_MAX) { answer_list_add_sprintf(answer_list, STATUS_ESYNTAX, ANSWER_QUALITY_ERROR, MSG_PATH_TOOLONG_I, SGE_PATH_MAX); ret = false; } } /* check for absolute path */ if (absolute) { if (path[0] != '/') { answer_list_add_sprintf(answer_list, STATUS_ESYNTAX, ANSWER_QUALITY_ERROR, MSG_CONF_THEPATHGIVENFORXMUSTSTARTWITHANY_S, name); ret = false; } } /* * TODO: we have to do additional checks here * we could use a variant of verify_str_key, using different valid character definition * verify_str_key will be extended to do length check * shall we also check for absolute vs. relative path? * filename vs. directory path? */ return ret; } /****** sge_path_alias/path_alias_verify() ************************************* * NAME * path_alias_verify() -- verify path alias list * * SYNOPSIS * bool * path_alias_verify(const lList *path_aliases, lList **answer_list) * * FUNCTION * Verify a path alias list as it is sent with job or pe task start orders * to sge_execd. * * INPUTS * const lList *path_aliases - path alias list * lList **answer_list - answer list to pass back error messages * * RESULT * bool - true on success, * false on error with error message in answer_list * * NOTES * MT-NOTE: path_alias_verify() is MT safe * * SEE ALSO * sge_path_alias/path_verify() *******************************************************************************/ bool path_alias_verify(const lList *path_aliases, lList **answer_list) { bool ret = true; const lListElem *ep; for_each (ep, path_aliases) { /* * PA_origin and PA_translation may not be NULL or empty string * they have to be valid paths. */ if (ret) { ret = path_verify(lGetString(ep, PA_origin), answer_list, "path_alias: origin", false); } if (ret) { ret = path_verify(lGetString(ep, PA_translation), answer_list, "path_alias: translation", false); } /* * PA_submit_host and PA_exec_host have to be either '*' or a valid * hostname (no need to resolve them). */ if (ret) { ret = verify_host_name(answer_list, lGetHost(ep, PA_submit_host)); } if (ret) { ret = verify_host_name(answer_list, lGetHost(ep, PA_exec_host)); } if (!ret) { break; } } return ret; } /****** sge_path_alias/path_list_verify() ************************************** * NAME * path_list_verify() -- verify a path list * * SYNOPSIS * bool * path_list_verify(const lList *path_list, lList **answer_list, * const char *name) * * FUNCTION * Verify a path list, e.g. the path specification in JB_stdout_path_list, * coming from a qsub -o . * * INPUTS * const lList *path_list - the path list to verify * lList **answer_list - answer list to pass back error messages * const char *name - name of the checked attribute for error output * * RESULT * bool - true: everything ok, else false * * NOTES * MT-NOTE: path_list_verify() is MT safe *******************************************************************************/ bool path_list_verify(const lList *path_list, lList **answer_list, const char *name) { bool ret = true; const lListElem *ep; for_each (ep, path_list) { const char *host; ret = path_verify(lGetString(ep, PN_path), answer_list, name, false); if (!ret) { break; } host = lGetHost(ep, PN_host); if (host != NULL) { ret = verify_host_name(answer_list, host); if (!ret) { break; } } host = lGetHost(ep, PN_file_host); if (host != NULL) { ret = verify_host_name(answer_list, host); if (!ret) { break; } } } return ret; }