Skip to content
Snippets Groups Projects
supervisord.py 33.5 KiB
Newer Older
jfp's avatar
jfp committed
#
# Required  privileges: ALTPRI, SYSNAM, PRMMBX, IMPERSONATE, SETPRV, WORLD
jfp's avatar
jfp committed
#
jfp's avatar
jfp committed
import configparser
import ctypes
import datetime
import json
import logging
import logging.handlers
import queue
from dataclasses import dataclass, field
from enum import IntEnum
from typing import Any, Dict, List, Literal, Tuple, Type, NewType
jfp's avatar
jfp committed

from ovms import (
    accdef,
    cmbdef,
    crtl,
    dvidef,
    iodef,
    iosbdef,
    jpidef,
    lnmdef,
    mbxqio,
jfp's avatar
jfp committed
    prvdef,
    psldef,
    ssdef,
    starlet,
    stsdef,
)
from ovms import ast as vmsast
from ovms.rtl import lib

logger: logging.Logger
logger = logging.getLogger('supervisord')

supervisord_table_name = b'SUPERVISORD_TABLE'
current_tick: int = 0
timer_queue: queue.PriorityQueue['PrioritizedItem'] = queue.PriorityQueue()

jfp's avatar
jfp committed

TRUTHY_STRINGS = ('yes', 'true', 'on', '1')
FALSY_STRINGS = ('no', 'false', 'off', '0')

jfp's avatar
jfp committed
timer_delay = starlet.bintim('0 0:00:01.00')[1]
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,
}


jfp's avatar
jfp committed
def start_timer():
    global timer_delay, timer_astctx
    timer_astctx.reset()
    starlet.setimr(daytim=timer_delay, reqidt=timer_astctx)


class RestartWhenExitUnexpected:
    pass


class RestartUnconditionally:
    pass


def auto_restart(
    value: str,
) -> (
    Type[RestartUnconditionally]
    | Type[RestartWhenExitUnexpected]
    | Literal[False]
):
    value = str(value.lower())
    computed_value = value
    if value in TRUTHY_STRINGS:
        computed_value = RestartUnconditionally
    elif value in FALSY_STRINGS:
        computed_value = False
    elif value == 'unexpected':
        computed_value = RestartWhenExitUnexpected
    if computed_value not in (
        RestartWhenExitUnexpected,
        RestartUnconditionally,
        False,
    ):
        raise ValueError("invalid 'autorestart' value %r" % value)
    return computed_value


jfp's avatar
jfp committed
class TimerItemType(IntEnum):
    UNDEF = 0
    PROC_STARTING = 1
    PROC_BACKOFF = 2
jfp's avatar
jfp committed


class TimerItem(object):
    typ: TimerItemType
    value: Any
    cancel: bool
    done: bool

    def __init__(
        self,
        typ: TimerItemType = TimerItemType.UNDEF,
        value: Any = None,
    ) -> None:
        self.typ = typ
        self.value = value
        self.cancel = False
        self.done = False


@dataclass(order=True)
class PrioritizedItem:
    priority: int
    item: TimerItem = field(compare=False)


class AstParamType(IntEnum):
    PROCTERM = 1
    CMD = 2
    TIMER = 3


# http://supervisord.org/subprocess.html#process-states
class ProcessStates(IntEnum):
    STOPPED = 0
    STARTING = 10
    RUNNING = 20
    BACKOFF = 30
    STOPPING = 40
Loading
Loading full blame...