int mgr_modify_node( struct pbsnode **ppnode, /* do some kind of "set" action on this node */ attribute_def *pdef, /* pbsnode doesn't have attributes but in the future it might so, make code similar to existing */ int limit, /* length of the *pdef array */ svrattrl *plist, /* batch request's svrattrl list begins here */ int privil, /* requester's privilege */ int *bad, /* if there is a "bad pbs_attribute" pass back position via this loc */ int mode) /*passed to attrib's action func not used by this func at this time*/ { int index; int rc = PBSE_NONE; pbs_attribute *new_attr; pbs_attribute *unused = NULL; pbs_attribute *pnew; if (plist == NULL) { return(0); /* nothing to do, return success */ } /* Get heap space for a temporary node-pbs_attribute array and use the * various "node-attribute action" functions defined in the file * "node_attr_def.c" to set the current values of each entry's * structure members */ new_attr = (pbs_attribute *)calloc((unsigned int)limit, sizeof(pbs_attribute)); if (new_attr == NULL) { return(PBSE_SYSTEM); } /* * The function "attr_atomic_node_set" does the following: * successively decodes the new pbs_attribute modifications carried in the * the request list, "plist", into a temporary pbs_attribute (on stack). * Updates each pbs_attribute in the derived node-attribute array using the * decoded values. If a failure of some sort occurs, as evidenced by * a non-zero return code (rc), call attr_atomic_kill() to undo * everything-- calls upon the "at_free" function for each pbs_attribute * to free hanging structures, then frees up the "new" array * return code (rc) shapes caller's reply */ if ((rc = attr_atomic_node_set(plist, unused, new_attr, pdef, limit, -1, privil, bad)) != 0) { attr_atomic_kill(new_attr, pdef, limit); return(rc); } for(index = ND_ATR_state;index < ND_ATR_LAST;index++) { pnew = new_attr + index; if (pnew->at_flags & ATR_VFLAG_MODIFY) { switch(index) { case ND_ATR_power_state: { rc = set_node_power_state(ppnode,pnew->at_val.at_short); } break; case ND_ATR_ttl: { snprintf((*ppnode)->nd_ttl, sizeof((*ppnode)->nd_ttl), "%s", pnew->at_val.at_str); rc = PBSE_NONE; } break; case ND_ATR_acl: { (*ppnode)->nd_acl = pnew->at_val.at_arst; rc = PBSE_NONE; } break; case ND_ATR_requestid: { *(*ppnode)->nd_requestid = pnew->at_val.at_str; rc = PBSE_NONE; } break; case ND_ATR_state: case ND_ATR_np: case ND_ATR_properties: case ND_ATR_ntype: case ND_ATR_jobs: case ND_ATR_status: case ND_ATR_note: case ND_ATR_mom_port: case ND_ATR_mom_rm_port: case ND_ATR_num_node_boards: case ND_ATR_numa_str: case ND_ATR_gpus: case ND_ATR_gpus_str: case ND_ATR_gpustatus: case ND_ATR_mics: case ND_ATR_micstatus: default: rc = PBSE_IVALREQ; break; } } if((rc != PBSE_NONE)||(*ppnode == NULL)) { break; } } free(new_attr); return(rc); } /* END mgr_modify_node() */
/** * @brief * modify_job_attr - modify the attributes of a job automatically * Used by req_modifyjob() to alter the job attributes and by * stat_update() [see req_stat.c] to update with latest from MOM * * @param[in,out] pjob - job structure * @param[in,out] plist - Pointer to list of attributes * @param[in] perm - Permissions of the caller requesting the operation * @param[out] bad - Pointer to the attribute index in case of a failed */ int modify_job_attr(job *pjob, svrattrl *plist, int perm, int *bad) { int changed_resc; int allow_unkn; long i; int modified = 0; attribute *newattr; attribute *pre_copy; attribute *attr_save; attribute *pattr; resource *prc; int rc; int newstate = -1; int newsubstate = -1; long newaccruetype = -1; if (pjob->ji_qhdr->qu_qs.qu_type == QTYPE_Execution) allow_unkn = -1; else allow_unkn = (int)JOB_ATR_UNKN; pattr = pjob->ji_wattr; /* call attr_atomic_set to decode and set a copy of the attributes. * We need 2 copies: 1 for copying to pattr and 1 for calling the action functions * We can't use the same copy for the action functions because copying to pattr * is a shallow copy and array pointers will be cleared during the copy. */ newattr = calloc(JOB_ATR_LAST, sizeof(attribute)); if (newattr == NULL) return PBSE_SYSTEM; rc = attr_atomic_set(plist, pattr, newattr, job_attr_def, JOB_ATR_LAST, allow_unkn, perm, bad); if (rc) { attr_atomic_kill(newattr, job_attr_def, JOB_ATR_LAST); return rc; } pre_copy = calloc(JOB_ATR_LAST, sizeof(attribute)); if(pre_copy == NULL) { attr_atomic_kill(newattr, job_attr_def, JOB_ATR_LAST); return PBSE_SYSTEM; } attr_atomic_copy(pre_copy, newattr, job_attr_def, JOB_ATR_LAST); attr_save = calloc(JOB_ATR_LAST, sizeof(attribute)); if (attr_save == NULL) { attr_atomic_kill(newattr, job_attr_def, JOB_ATR_LAST); attr_atomic_kill(pre_copy, job_attr_def, JOB_ATR_LAST); return PBSE_SYSTEM; } attr_atomic_copy(attr_save, pattr, job_attr_def, JOB_ATR_LAST); /* If resource limits are being changed ... */ changed_resc = newattr[(int)JOB_ATR_resource].at_flags & ATR_VFLAG_SET; if ((rc == 0) && (changed_resc != 0)) { /* first, remove ATR_VFLAG_DEFLT from any value which was set */ /* it can no longer be a "default" as it explicitly changed */ prc = (resource *)GET_NEXT(newattr[(int)JOB_ATR_resource].at_val.at_list); while (prc) { if ((prc->rs_value.at_flags & (ATR_VFLAG_MODIFY|ATR_VFLAG_DEFLT)) == (ATR_VFLAG_MODIFY|ATR_VFLAG_DEFLT)) prc->rs_value.at_flags &= ~ATR_VFLAG_DEFLT; if ((prc->rs_value.at_flags & (ATR_VFLAG_MODIFY|ATR_VFLAG_SET)) == (ATR_VFLAG_MODIFY|ATR_VFLAG_SET)) { /* if being changed at all, see if "select" */ if (prc->rs_defin == pseldef) { /* select is modified, recalc chunk sums */ rc = set_chunk_sum(&prc->rs_value, &newattr[(int)JOB_ATR_resource]); if (rc) break; } } prc = (resource *)GET_NEXT(prc->rs_link); } /* Manager/Operator can modify job just about any old way */ /* So, the following checks are made only if not the Op/Admin */ if ((perm & (ATR_DFLAG_MGWR | ATR_DFLAG_OPWR)) == 0) { if (pjob->ji_qs.ji_state == JOB_STATE_RUNNING) { /* regular user cannot raise the limits of a running job */ if ((comp_resc(&pjob->ji_wattr[(int)JOB_ATR_resource], &newattr[(int)JOB_ATR_resource]) == -1) || comp_resc_lt) rc = PBSE_PERM; } /* Also check against queue, system and entity limits */ if (rc == 0) { rc = chk_resc_limits(&newattr[(int)JOB_ATR_resource], pjob->ji_qhdr); } if (rc == 0) { rc = check_entity_resc_limit_max(pjob, pjob->ji_qhdr, &newattr[(int)JOB_ATR_resource]); if (rc == 0) { rc = check_entity_resc_limit_queued(pjob, pjob->ji_qhdr, &newattr[(int)JOB_ATR_resource]); if (rc == 0) { rc = check_entity_resc_limit_max(pjob, NULL, &newattr[(int)JOB_ATR_resource]); if (rc == 0) rc = check_entity_resc_limit_queued(pjob, NULL, &newattr[(int)JOB_ATR_resource]); } } } } } /* special check on permissions for hold */ if ((rc == 0) && (newattr[(int)JOB_ATR_hold].at_flags & ATR_VFLAG_MODIFY)) { svrattrl *hold_e = find_name_in_svrattrl(plist, ATTR_h); /* don't perform permission check if Hold_Types attribute */ /* was set in a hook script (special privilege) */ if ((hold_e == NULL) || ((hold_e->al_flags & ATR_VFLAG_HOOK) == 0)) { i = newattr[(int)JOB_ATR_hold].at_val.at_long ^ (pattr+(int)JOB_ATR_hold)->at_val.at_long; rc = chk_hold_priv(i, perm); } } if ((rc == 0) && ((newattr[(int)JOB_ATR_userlst].at_flags & ATR_VFLAG_MODIFY) || (newattr[(int)JOB_ATR_grouplst].at_flags & ATR_VFLAG_MODIFY))) { /* Need to reset execution uid and gid */ rc = set_objexid((void *)pjob, JOB_OBJECT, newattr); } if (rc) { attr_atomic_kill(newattr, job_attr_def, JOB_ATR_LAST); attr_atomic_kill(attr_save, job_attr_def, JOB_ATR_LAST); attr_atomic_kill(pre_copy, job_attr_def, JOB_ATR_LAST); return (rc); } /* OK, if resources changed, reset entity sums */ if (changed_resc) { account_entity_limit_usages(pjob, NULL, &newattr[(int)JOB_ATR_resource], INCR, ETLIM_ACC_ALL_RES); account_entity_limit_usages(pjob, pjob->ji_qhdr, &newattr[(int)JOB_ATR_resource], INCR, ETLIM_ACC_ALL_RES); } /* Now copy the new values into the job attribute array for the purposes of running the action functions */ for (i = 0; i < JOB_ATR_LAST; i++) { if (newattr[i].at_flags & ATR_VFLAG_MODIFY) { /* * The function update_eligible_time() expects it is the only one setting accrue_type. * If we set it here, it will get confused. There is no action function for accrue_type, * so pre-setting it for the action function calls isn't required. */ if (i == JOB_ATR_accrue_type) continue; job_attr_def[i].at_free(&pattr[i]); if ((pre_copy[i].at_type == ATR_TYPE_LIST) || (pre_copy[i].at_type == ATR_TYPE_RESC)) { list_move(&pre_copy[i].at_val.at_list, &pattr[i].at_val.at_list); } else { pattr[i] = pre_copy[i]; } /* ATR_VFLAG_MODCACHE will be included if set */ pattr[i].at_flags = pre_copy[i].at_flags; } } for (i = 0; i < JOB_ATR_LAST; i++) { /* Check newattr instead of pattr for modify. It is possible that * the attribute already has the modify flag before we added the new attributes to it. * We only want to call the action functions for attributes which are being modified by this function. */ if (newattr[i].at_flags & ATR_VFLAG_MODIFY) { if ((job_attr_def[i].at_flags & ATR_DFLAG_NOSAVM) == 0) modified = 1; /* full save to disk for job */ if (job_attr_def[i].at_action) { rc = job_attr_def[i].at_action(&newattr[i], pjob, ATR_ACTION_ALTER); if (rc) { *bad = i; break; } } } } if (rc) { attr_atomic_copy(pjob->ji_wattr, attr_save, job_attr_def, JOB_ATR_LAST); free(pre_copy); attr_atomic_kill(newattr, job_attr_def, JOB_ATR_LAST); attr_atomic_kill(attr_save, job_attr_def, JOB_ATR_LAST); return (rc); } /* The action functions may have modified the attributes, need to set them to newattr2 */ for (i = 0; i < JOB_ATR_LAST; i++) { if (newattr[i].at_flags & ATR_VFLAG_MODIFY) { job_attr_def[i].at_free(&pattr[i]); switch (i) { case JOB_ATR_state: newstate = state_char2int(newattr[i].at_val.at_char); break; case JOB_ATR_substate: newsubstate = newattr[i].at_val.at_long; break; case JOB_ATR_accrue_type: newaccruetype = newattr[i].at_val.at_long; break; default: if ((newattr[i].at_type == ATR_TYPE_LIST) || (newattr[i].at_type == ATR_TYPE_RESC)) { list_move(&newattr[i].at_val.at_list, &pattr[i].at_val.at_list); } else { pattr[i] = newattr[i]; } } /* ATR_VFLAG_MODCACHE will be included if set */ pattr[i].at_flags = newattr[i].at_flags; } } if (newstate != -1 && newsubstate != -1) { svr_setjobstate(pjob, newstate, newsubstate); } if (newaccruetype != -1) update_eligible_time(newaccruetype, pjob); if (modified) pjob->ji_modified = 1; /* an attr was modified, do full save */ free(newattr); free(pre_copy); attr_atomic_kill(attr_save, job_attr_def, JOB_ATR_LAST); return (0); }