Skip to content
Snippets Groups Projects
Commit 58e996ba25f8 authored by jfp's avatar jfp
Browse files

Add supprt to kt_limit, quotas, privileges, nouaf

parent b3bf2ad04df4
No related branches found
No related tags found
No related merge requests found
[supervisord]
nodaemon = false
user = system
command = dev:[dir]supervisord.com
[program:firstpgm] [program:firstpgm]
command=dev:[dir]PGM1.COM command=dev:[dir]PGM1.COM
process_name=PGM1D process_name=PGM1D
...@@ -8,6 +12,15 @@ ...@@ -8,6 +12,15 @@
startretries=3 startretries=3
stopwaitsecs=10 stopwaitsecs=10
user=vmsuser user=vmsuser
kt_limit=1
# if privileges or quotas are specified nouaf is implicitly define to true
# else default false
# nouaf=true
# cpuplm express in seconds
# quotas= astlm=300, diolm=250,, cpulm=60,
# biolm=20, pgflquota=1000000
# privileges= SYSNAM, PRMMBX, IMPERSONATE,
# WORLD
autorestart=unexpected autorestart=unexpected
# SS$_NORMAL, SS$_FORCEX, SS$_EXITFORCED # SS$_NORMAL, SS$_FORCEX, SS$_EXITFORCED
exitcodes=1,11228,11220 exitcodes=1,11228,11220
# #
# Required privileges: SYSNAM, PRMMBX, IMPERSONATE, WORLD # Required privileges: ALTPRI, SYSNAM, PRMMBX, IMPERSONATE, SETPRV, WORLD
# #
import argparse import argparse
import configparser import configparser
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
import queue import queue
from dataclasses import dataclass, field from dataclasses import dataclass, field
from enum import IntEnum from enum import IntEnum
from typing import Any, Dict, List, Literal, Tuple, Type from typing import Any, Dict, List, Literal, Tuple, Type, NewType
from ovms import ( from ovms import (
accdef, accdef,
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
jpidef, jpidef,
lnmdef, lnmdef,
mbxqio, mbxqio,
pqldef,
prcdef,
prvdef, prvdef,
psldef, psldef,
ssdef, ssdef,
...@@ -48,6 +50,73 @@ ...@@ -48,6 +50,73 @@
timer_astctx = vmsast.AstContext(vmsast.M_WAKE | vmsast.M_QUEUE) timer_astctx = vmsast.AstContext(vmsast.M_WAKE | vmsast.M_QUEUE)
# Define a new type called "PrvMask", which is internally an `int`
PrvMask = NewType('PrvMask', int)
# Ditto for "PidType"
PidType = NewType('PidType', int)
privileges: Dict[str, PrvMask] = {
'NOSAME': PrvMask(0),
'ACNT': PrvMask(prvdef.PRV_M_ACNT),
'ALLSPOOL': PrvMask(prvdef.PRV_M_ALLSPOOL),
'ALTPRI': PrvMask(prvdef.PRV_M_ALTPRI),
'AUDIT': PrvMask(prvdef.PRV_M_AUDIT),
'BUGCHK': PrvMask(prvdef.PRV_M_BUGCHK),
'BYPASS': PrvMask(prvdef.PRV_M_BYPASS),
'CMEXEC': PrvMask(prvdef.PRV_M_CMEXEC),
'CMKRNL': PrvMask(prvdef.PRV_M_CMKRNL),
'DIAGNOSE': PrvMask(prvdef.PRV_M_DIAGNOSE),
'DOWNGRADE': PrvMask(prvdef.PRV_M_DOWNGRADE),
'EXQUOTA': PrvMask(prvdef.PRV_M_EXQUOTA),
'GROUP': PrvMask(prvdef.PRV_M_GROUP),
'GRPNAM': PrvMask(prvdef.PRV_M_GRPNAM),
'GRPPRV': PrvMask(prvdef.PRV_M_GRPPRV),
'IMPERSONATE': PrvMask(prvdef.PRV_M_IMPERSONATE),
'IMPORT': PrvMask(prvdef.PRV_M_IMPORT),
'LOG_IO': PrvMask(prvdef.PRV_M_LOG_IO),
'MOUNT': PrvMask(prvdef.PRV_M_MOUNT),
'NETMBX': PrvMask(prvdef.PRV_M_NETMBX),
'OPER': PrvMask(prvdef.PRV_M_OPER),
'PFNMAP': PrvMask(prvdef.PRV_M_PFNMAP),
'PHY_IO': PrvMask(prvdef.PRV_M_PHY_IO),
'PRMCEB': PrvMask(prvdef.PRV_M_PRMCEB),
'PRMGBL': PrvMask(prvdef.PRV_M_SYSGBL),
'PRMMBX': PrvMask(prvdef.PRV_M_PRMMBX),
'PSWAPM': PrvMask(prvdef.PRV_M_PSWAPM),
'READALL': PrvMask(prvdef.PRV_M_READALL),
'SECURITY': PrvMask(prvdef.PRV_M_SECURITY),
'SETPRV': PrvMask(prvdef.PRV_M_SETPRV),
'SHARE': PrvMask(prvdef.PRV_M_SHARE),
'SHMEM': PrvMask(prvdef.PRV_M_SHMEM),
'SYSGBL': PrvMask(prvdef.PRV_M_SYSGBL),
'SYSLCK': PrvMask(prvdef.PRV_M_SYSLCK),
'SYSNAM': PrvMask(prvdef.PRV_M_SYSNAM),
'SYSPRV': PrvMask(prvdef.PRV_M_SYSPRV),
'TMPMBX': PrvMask(prvdef.PRV_M_TMPMBX),
'UPGRADE': PrvMask(prvdef.PRV_M_UPGRADE),
'VOLPRO': PrvMask(prvdef.PRV_M_VOLPRO),
'WORLD': PrvMask(prvdef.PRV_M_WORLD),
}
quotas: Dict[str, int] = {
'ASTLM': pqldef.PQL__ASTLM,
'BIOLM': pqldef.PQL__BIOLM,
'BYTLM': pqldef.PQL__BYTLM,
'CPULM': pqldef.PQL__CPULM,
'DIOLM': pqldef.PQL__DIOLM,
'ENQLM': pqldef.PQL__ENQLM,
'FILLM': pqldef.PQL__FILLM,
'JTQUOTA': pqldef.PQL__JTQUOTA,
'PGFLQUOTA': pqldef.PQL__PGFLQUOTA,
'PRCLM': pqldef.PQL__PRCLM,
'TQELM': pqldef.PQL__TQELM,
'WSDEFAULT': pqldef.PQL__WSDEFAULT,
'WSEXTENT': pqldef.PQL__WSEXTENT,
'WSQUOTA': pqldef.PQL__WSQUOTA,
}
def start_timer(): def start_timer():
global timer_delay, timer_astctx global timer_delay, timer_astctx
timer_astctx.reset() timer_astctx.reset()
...@@ -135,7 +204,7 @@ ...@@ -135,7 +204,7 @@
class ProcessInfo(object): class ProcessInfo(object):
pid: int pid: PidType
state: ProcessStates state: ProcessStates
finalsts: int | None finalsts: int | None
start_time: datetime.datetime | None start_time: datetime.datetime | None
...@@ -143,7 +212,7 @@ ...@@ -143,7 +212,7 @@
def __init__( def __init__(
self, self,
pid: int = 0, pid: PidType = PidType(0),
start_time: datetime.datetime | None = None, start_time: datetime.datetime | None = None,
state: ProcessStates = ProcessStates.STOPPED, state: ProcessStates = ProcessStates.STOPPED,
) -> None: ) -> None:
...@@ -166,5 +235,9 @@ ...@@ -166,5 +235,9 @@
startsecs: int startsecs: int
startretries: int startretries: int
stopwaitsecs: int stopwaitsecs: int
kt_limit: int
nouaf: bool
quotas: List[Tuple[int, int]] | None
prv: PrvMask | None
user: bytes user: bytes
process: ProcessInfo process: ProcessInfo
...@@ -169,6 +242,6 @@ ...@@ -169,6 +242,6 @@
user: bytes user: bytes
process: ProcessInfo process: ProcessInfo
running_processes: Dict[int, 'Program'] running_processes: Dict[PidType, 'Program']
running_processes = dict() running_processes = dict()
programs: Dict[str, 'Program'] programs: Dict[str, 'Program']
programs = dict() programs = dict()
...@@ -195,6 +268,10 @@ ...@@ -195,6 +268,10 @@
startsecs: int, startsecs: int,
startretries: int, startretries: int,
stopwaitsecs: int, stopwaitsecs: int,
nouaf: bool,
quotas: List[Tuple[int, int]] | None,
prv: PrvMask | None,
kt_limit: int,
autorestart: ( autorestart: (
Type[RestartUnconditionally] Type[RestartUnconditionally]
| Type[RestartWhenExitUnexpected] | Type[RestartWhenExitUnexpected]
...@@ -217,6 +294,10 @@ ...@@ -217,6 +294,10 @@
self.timer_item = None self.timer_item = None
self.kill_request = False self.kill_request = False
self.autorestart = autorestart self.autorestart = autorestart
self.nouaf = nouaf
self.quotas = quotas
self.prv = prv
self.kt_limit = kt_limit
self.exitcodes = exitcodes self.exitcodes = exitcodes
self.process = ProcessInfo() self.process = ProcessInfo()
...@@ -266,7 +347,7 @@ ...@@ -266,7 +347,7 @@
self.process_name + b'_PID', self.process_name + b'_PID',
supervisord_table_name, supervisord_table_name,
)[1] )[1]
pid = int(v, 16) pid = PidType(int(v, 16))
while True: while True:
try: try:
itime, stime = lib.getjpi(jpidef.JPI__LOGINTIM, pid)[2:] itime, stime = lib.getjpi(jpidef.JPI__LOGINTIM, pid)[2:]
...@@ -276,7 +357,7 @@ ...@@ -276,7 +357,7 @@
raise raise
itime = crtl.fix_time(itime) itime = crtl.fix_time(itime)
assert itime is not None assert itime is not None
self.process.pid = pid self.process.pid = PidType(pid)
self.process.start_time = datetime.datetime.fromtimestamp(itime) self.process.start_time = datetime.datetime.fromtimestamp(itime)
self.set_running() self.set_running()
...@@ -305,6 +386,10 @@ ...@@ -305,6 +386,10 @@
self.timer_item = None self.timer_item = None
self.process.finalsts = None self.process.finalsts = None
# If the image argument specifies the SYS$SYSTEM:LOGINOUT.EXE,
# the UIC of the created process will be the UIC of the caller of $CREPRC,
# and the UIC parameter is ignored.
# So, we need to use persona
userpro = starlet.create_user_profile(usrnam=self.user)[1] userpro = starlet.create_user_profile(usrnam=self.user)[1]
persona_id = starlet.persona_create(usrpro=userpro)[1] persona_id = starlet.persona_create(usrpro=userpro)[1]
persona_previous_id = starlet.persona_assume(persona_id)[1] persona_previous_id = starlet.persona_assume(persona_id)[1]
...@@ -308,15 +393,38 @@ ...@@ -308,15 +393,38 @@
userpro = starlet.create_user_profile(usrnam=self.user)[1] userpro = starlet.create_user_profile(usrnam=self.user)[1]
persona_id = starlet.persona_create(usrpro=userpro)[1] persona_id = starlet.persona_create(usrpro=userpro)[1]
persona_previous_id = starlet.persona_assume(persona_id)[1] persona_previous_id = starlet.persona_assume(persona_id)[1]
starlet.persona_assume(persona_previous_id) # Create a detach process (stsflg=prvdef.PRV_M_IMPERSONATE)
uic = lib.getjpi(jpidef.JPI__UIC)[2] # From John Gillings:
starlet.persona_delete(persona_id) # (https://community.hpe.com/t5/operating-system-openvms/run-uic-vs-sys-creprc/td-p/5171510)
# Create a detach process (uic is specified) # General comment about PRC$M_NOUAF ...
s, pid, d = starlet.creprc( # When you run LOGINOUT in a detached process *WITHOUT* specifying PRC$M_NOUAF
uic=uic, # for $CREPRC, or *WITH* /AUTHORIZE on the DCL RUN command, (note that the
# default is reversed between the two), many of the process attributes you have
# specified with qualifiers or parameters may be overridden in the resulting
# process.
# That includes UIC, process name, quotas and privileges. Behind the scenes,
# the process is created with whatever you specified, but LOGINOUT replaces
# them with values from the UAF.
#
# From https://docs.vmssoftware.com/vsi-openvms-programming-concepts-manual-volume-i/
# The SYS$CREPRC system service also does not provide default equivalence names
# for the logical names SYS$LOGIN, SYS$LOGIN_DEVICE, and SYS$SCRATCH.
# These logical names are available to the created process only when the
# specified image is LOGINOUT, and when the PRC$M_NOUAF flag is not set.
try:
stsflg = (
prcdef.PRC_M_IMPERSONATE
| prcdef.PRC_M_PARSE_EXTENDED
| prcdef.PRC_M_KT_LIMIT
)
if self.nouaf:
stsflg |= prcdef.PRC_M_NOUAF
pid = PidType(
starlet.creprc(
image=b'SYS$SYSTEM:LOGINOUT.EXE', image=b'SYS$SYSTEM:LOGINOUT.EXE',
input=self.command, input=self.command,
output=self.stdout_file, output=self.stdout_file,
error=self.stderr_file, error=self.stderr_file,
prcnam=self.process_name, prcnam=self.process_name,
mbxunt=Program.mbxunt, mbxunt=Program.mbxunt,
...@@ -317,7 +425,13 @@ ...@@ -317,7 +425,13 @@
image=b'SYS$SYSTEM:LOGINOUT.EXE', image=b'SYS$SYSTEM:LOGINOUT.EXE',
input=self.command, input=self.command,
output=self.stdout_file, output=self.stdout_file,
error=self.stderr_file, error=self.stderr_file,
prcnam=self.process_name, prcnam=self.process_name,
mbxunt=Program.mbxunt, mbxunt=Program.mbxunt,
quota=self.quotas,
prv=self.prv,
kt_limit=self.kt_limit,
# prcdef.PRC_M_DETACH and prcdef.PRC_M_IMPERSONATE are synonyms
stsflg=stsflg,
)[1]
) )
...@@ -323,5 +437,13 @@ ...@@ -323,5 +437,13 @@
) )
except OSError as e:
logger.warning(
f"Can' create process {self.process_name}, error {e}"
)
self.process.state = ProcessStates.FATAL
return
finally:
starlet.persona_assume(persona_previous_id)
starlet.persona_delete(persona_id)
lib.set_logical( lib.set_logical(
self.process_name + b'_PID', self.process_name + b'_PID',
hex(pid)[2:].upper(), hex(pid)[2:].upper(),
...@@ -333,7 +455,7 @@ ...@@ -333,7 +455,7 @@
supervisord_table_name, supervisord_table_name,
) )
self.process.pid = pid self.process.pid = PidType(pid)
self.process.start_time = datetime.datetime.now() self.process.start_time = datetime.datetime.now()
if self.startsecs == 0: if self.startsecs == 0:
self.set_running() self.set_running()
...@@ -343,7 +465,7 @@ ...@@ -343,7 +465,7 @@
timer_queue.put( timer_queue.put(
PrioritizedItem(current_tick + self.startsecs, self.timer_item) PrioritizedItem(current_tick + self.startsecs, self.timer_item)
) )
Program.running_processes[pid] = self Program.running_processes[PidType(pid)] = self
lib.set_logical( lib.set_logical(
self.process_name + b'_BEG', self.process_name + b'_BEG',
str(self.process.start_time)[:19], str(self.process.start_time)[:19],
...@@ -659,7 +781,9 @@ ...@@ -659,7 +781,9 @@
t_astctxt = qio_procterm(fterm) # noqa: F841 t_astctxt = qio_procterm(fterm) # noqa: F841
c_astctxt = qio_cmd(fcmd) # noqa: F841 c_astctxt = qio_cmd(fcmd) # noqa: F841
for pgm in sorted(Program.programs.values(), key=lambda pgm: pgm.priority): for pgm in sorted(
Program.programs.values(), key=lambda pgm: pgm.priority
):
if pgm.autostart: if pgm.autostart:
pgm.create_process(False) pgm.create_process(False)
...@@ -710,7 +834,7 @@ ...@@ -710,7 +834,7 @@
handler.setFormatter(formatter) handler.setFormatter(formatter)
main_log.addHandler(handler) main_log.addHandler(handler)
# Required privileges: SYSNAM, PRMMBX, IMPERSONATE, WORLD # Required privileges: ALTPRI, SYSNAM, PRMMBX, IMPERSONATE, SETPRV, WORLD
starlet.setprv( starlet.setprv(
1, 1,
prvdef.PRV_M_SYSNAM prvdef.PRV_M_SYSNAM
...@@ -714,4 +838,5 @@ ...@@ -714,4 +838,5 @@
starlet.setprv( starlet.setprv(
1, 1,
prvdef.PRV_M_SYSNAM prvdef.PRV_M_SYSNAM
| prvdef.PRV_M_ALTPRI
| prvdef.PRV_M_PRMMBX | prvdef.PRV_M_PRMMBX
...@@ -717,4 +842,5 @@ ...@@ -717,4 +842,5 @@
| prvdef.PRV_M_PRMMBX | prvdef.PRV_M_PRMMBX
| prvdef.PRV_M_SETPRV
| prvdef.PRV_M_IMPERSONATE | prvdef.PRV_M_IMPERSONATE
| prvdef.PRV_M_WORLD, | prvdef.PRV_M_WORLD,
) )
...@@ -744,6 +870,40 @@ ...@@ -744,6 +870,40 @@
for sn in config.sections(): for sn in config.sections():
if sn.startswith('program:'): if sn.startswith('program:'):
quotaslst = [
quota.strip().upper().split('=')
for quota in config[sn].get('quotas', '').split(',')
if quota != ''
]
prcquotas = []
for quotan, quotav in quotaslst:
if quotan not in quotas:
print(f'{sn}: {quotan}={quotav} not a valid quota')
crtl.vms_exit(ssdef.SS__INVARG)
if quotan == 'CPULM':
print('>>>', repr(quotav))
quotav = int(quotav) * 100
prcquotas.append((quotas[quotan], int(quotav)))
if prcquotas == []:
prcquotas = None
prvnames = [
name.strip().upper()
for name in config[sn].get('privileges', '').split(',')
if name != ''
]
prv: PrvMask | None
prv = None
for prvnam in prvnames:
if prvnam not in privileges:
print(f'{sn}: {prvnam} not a valid privilege')
crtl.vms_exit(ssdef.SS__INVARG)
prv = (
privileges[prvnam]
if prv is None
else PrvMask(prv | privileges[prvnam])
)
name = sn.split(':')[-1].upper() name = sn.split(':')[-1].upper()
process_name = config[sn]['process_name'] process_name = config[sn]['process_name']
autostart = config[sn].getboolean('autostart', False) autostart = config[sn].getboolean('autostart', False)
...@@ -751,6 +911,8 @@ ...@@ -751,6 +911,8 @@
stdout_file = config[sn].get('stdout_file', 'NLA0:') stdout_file = config[sn].get('stdout_file', 'NLA0:')
stderr_file = config[sn].get('stderr_file', 'NLA0:') stderr_file = config[sn].get('stderr_file', 'NLA0:')
priority = config[sn].getint('priority', 999) priority = config[sn].getint('priority', 999)
nouaf = config[sn].getboolean('nouaf', False)
kt_limit = config[sn].getint('kt_limit', 0)
startsecs = config[sn].getint('startsecs', 10) startsecs = config[sn].getint('startsecs', 10)
startretries = config[sn].getint('startretries', 3) startretries = config[sn].getint('startretries', 3)
stopwaitsecs = config[sn].getint('stopwaitsescs', 10) stopwaitsecs = config[sn].getint('stopwaitsescs', 10)
...@@ -759,6 +921,8 @@ ...@@ -759,6 +921,8 @@
) )
exitcodes = config[sn].get('exitcodes', '1').split(',') exitcodes = config[sn].get('exitcodes', '1').split(',')
user = config[sn]['user'] user = config[sn]['user']
if prv is not None or quotaslst is not None:
nouaf = True
p = Program( p = Program(
name=name, name=name,
user=user, user=user,
...@@ -772,6 +936,10 @@ ...@@ -772,6 +936,10 @@
startretries=startretries, startretries=startretries,
stopwaitsecs=stopwaitsecs, stopwaitsecs=stopwaitsecs,
autorestart=autorestart, autorestart=autorestart,
nouaf=nouaf,
quotas=prcquotas,
prv=prv,
kt_limit=kt_limit,
exitcodes=[ exitcodes=[
int(exitcode) for exitcode in exitcodes if exitcode != '' int(exitcode) for exitcode in exitcodes if exitcode != ''
], ],
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment