System and Subsystem classes
The System and Subsystem classes are collections of shared methods that operate on entities (globals, strings, objects) made available to all users in an Online system or all users in a subsystem, or that operate on environmental entities such as the current INCLUDE arguments, or the current call stack that are essentially part of the "system."
There are no System or Subsystem objects, per se. That is, you cannot instantiate an instance of a System or Subsystem object, nor assign references from one variable to another, nor are there any instance-specific methods in these classes. You can, however, declare objects of these classes. The main use of such objects is to provide a short-hand way of referencing the classes:
%sys is object system %subsys is object subsystem ... %sys:setGlobal('GOOSE', 'SAUCE') %subsys:setGlobal('GANDER', 'SAUCE')
Even though you cannot instantiate or invoke instance-specific methods against System or Subsystem objects, you may find it helpful to think of there being an implicit System object that contains system-wide and thread-specific context for the system, and a SubSystem object for every valid subsystem context. Still, all System and Subsystem class methods are shared methods, so they are invokable via their class names:
%(system):setString('STATUS', %status) print %(subsystem):string('INFO')
Subsystem context
Subsystem class methods operate on entities associated with the current subsystem context. This context is, by default, the current subsystem in which a request is running. It is, possible, however, to run with a subsystem context different from the current subsystem. This subsystem context can be a subsystem other than the one being run, or it can even be a context that isn't associated with a real subsystem — the context is simply a name, and it is not required to be a started or even defined subsystem. It is also possible to set a subsystem context for requests that are not even inside a subsystem.
When a subsystem is entered, the subsystem context is automatically set to the name of the one being entered. When the subsystem is exited, the context is set back to the subsystem context in effect at entry to the subsystem, whether or not the subsystem was changed inside the subsystem.
Once the subsystem context is set, all Subsystem class methods apply to that subsystem context. For example, if subsystem ORDER is entered, the following statement sets a subsystem string for that subsystem:
%(subsystem):setString('DEBUG', 'ON')
However, if the following statement is run before it, the SetString method actually applies to the DISORDER subsystem context:
%(subsystem):context = 'DISORDER'
Since setting the subsystem context allows a request to modify entities associated with another subsystem, the ability to set subsystem context is protected. Ordinarily, only a system manager can set the subsystem context, though the ability to set subsystem context can be controlled with SIRMETH rules.
Using SIRMETH to specify security rules
The ability to modify system or subsystem entities gives a user the ability to cause problems in threads other than their own. As such, this capability must be protected to prevent malicious or unintentional modification of system-wide or subsystem-wide entities.
The default protection for system-wide and subsystem-wide entities is:
- Only system managers can modify system-wide entities (system globals and strings, for example).
- Only pre-compiled procedures in a subsystem can modify subsystem-wide entities.
- Only system managers can set their subsystem context.
While these rules prevent intentional or unintentional tampering with system or subsystem entities, they can also be onerous:
- There might be several trusted subsystems that one would want to allow to set System globals.
- One might want to set SubSystem globals in non-precompiled procedures.
- One might want several subsystems to operate under the same subsystem context, that is, share subsystem entities.
- For development and debugging, one might want very loose restrictions on what programmers can and cannot do.
While most of these problems can be worked around by giving programmers system manager privileges or by having subsystems run with system manager privileges, this might be further than one wants to go to provide these capabilities.
To ameliorate these problems, the SIRMETH command is provided. The SIRMETH command provides a way of giving specific subsystems or all users access to certain System or Subsystem class capabilities. Of course, the SIRMETH command itself requires system manager privileges.
SIRMETH command syntax
SIRMETH {ALLOW | DISALLOW} - {SYSTEMSET | SUBSYSTEMSET | - SUBSYSTEMCONTEXT csubsys | ALL} - [SUBSYSTEM subsys [NONPRE] ]
Where:
ALLOW | Indicates that requests matching the rule will be allowed to perform the indicated action. |
---|---|
DISALLOW | Indicates that requests matching the rule will not be allowed to perform the indicated action. |
SYSTEMSET | Indicates the ability to set system strings or globals. This applies to the SetGlobal and SetString methods in the System class, as well as the $Setg_Sys function. |
SUBSYSTEMSET | Indicates the ability to set subsytem strings or globals. This applies to the SetGlobal and SetString methods in the SubSystem class, as well as the $Setg_Subsys function (when the third argument, the subsystem name, is not specified). |
SUBSYSTEMCONTEXT | Indicates the ability to change subsytem context to the subsystem indicated by csubsys. csubsys can be a specific subsystem name such as SIRPRO , or it can be a wild-card such as SIR* or ???PRO . |
ALL | Only allowed on a DISALLOW rule, this indicates that no extra capabilities are to be provided to requests matching the rule. |
SUBSYSTEM | Indicates that the action is to be allowed to methods in the subsystem indicated by subsys.
subsys can be a specific subsystem name such as Note that if SUBSYSTEM is specified, the rule applies to the real subsystem being run, regardless of the current subsystem context. Note also that unless followed by the keyword NONPRE, the rule only applies to pre-compiled procedures in the subsystem. |
NONPRE | Indicates that non-pre-compiled procedures in the subsystem are also to be given the capabilities indicated by the SIRMETH rule. NONPRE must immediately follow the subsystem name. |
You can specify as many SIRMETH rules as you want. A SIRMETH rule that totally encompasses a previous rule preempts that rule. For example, in the following two rules, the second SIRMETH rule completely encompasses the first, so the first rule is discarded:
SIRMETH ALLOW SUBSYSTEMSET SUBSYS FOOBAR SIRMETH ALLOW SUBSYSTEMSET SUBSYS FOO*
A SIRMETH DISALLOW ALL
preempts all previous rules,
so it is a way of starting from a clean slate.
If you place a SIRMETH DISALLOW ALL
at the start of a
sequence of SIRMETH rules (most likely in a procedure), those rules
can be run again and again, perhaps after some of the rules have been modified.
Of course, in the interval between the SIRMETH DISALLOW ALL
and the
subsequent rules, some ordinarily legal requests might be disallowed,
so it is probably not a good idea to reapply SIRMETH rules in a production region.
Generally, SIRMETH rules are placed in the CCAIN stream or in a procedure INCLUDE'd in the CCAIN stream.
The reason for the NONPRE keyword is that many debugging and development subsystems allow users (programmers, typically) to run arbitrary code from inside the subsystem. Typically, this arbitrary code is run from a non-pre-compiled procedure that includes the programmer's code. To prevent such code from picking up the subsystem's capabilities, System and Subsystem class security does not apply SIRMETH rules to non-pre-compiled procedures, unless the NONPRE keyword is specified on the rule.
Since most subsystems don't allow users to run arbitrary code inside them, and since it is quite likely that you might want to set subsystem globals or switch subsystem contexts inside a non-pre-compiled procedure, it is probably a good idea to specify NONPRE for those subsystems.
In a development environment, to avoid the effort of modifying multiple individual SIRMETH rules, you may be willing to risk letting programmers modify any system or subsystem entity. If so, the following rules allow everyone to do everything with system and subsystem entities:
SIRMETH ALLOW SUBSYSTEMSET SIRMETH ALLOW SUBSYSTEMSET SIRMETH ALLOW SUBSYSTEMCONTEXT
It is worth reiterating that while the System and Subsystem classes are the preferred interface for setting system and subsystem globals, the SIRMETH rules also apply to $Setg_sys and $Setg_subsys. If the third parameter of $Setg_subsys (subsystem name) is specified, no SIRMETH rules apply, and the user must be a system manager. You can get the same effect as a $Setg_subsys with a subsystem name by saving the subsystem context, setting it, and then restoring it:
%oldContext = %(subsystem):context %(subsystem):context = 'CUSTOMER' %(subsystem):setGlobal('CUSTFILE', 'GROUP CUST2004') %(subsystem):context = %oldContext
System and subsystem globals and strings
Typically, a $Getg function or a dummy string compile-time string (that is, something that begins with ?&
) is resolved from a thread-level
global variable (that is, a variable set via $Setg).
Often, however, all requests in an Online or subsystem will use the same value for some of these globals, so it seems appealing for these
users to share a copy of those globals rather than each wasting GTBL space to hold a copy for each thread.
System and subsystem globals make this feasible.
By default, if a global reference (either a $Getg or a dummy string) cannot be resolved with thread-level globals, subsystem and then system globals will be searched. If one of these matches the requested global, that global will be returned for the $Getg or the dummy string.
For example, if the following statement exists in a subsystem initialization procedure:
%(subsystem):setGlobal('INVFILE', 'PERM GROUP INV2004')
the following line
%inventoryRecords is object recordSet in ?&INVFILE
is resolved as
%inventoryRecords is object recordSet in PERM GROUP INV2004
$SirParm has settings to indicate that $Get or dummy string lookup should use subsystem or system globals ahead of thread-specific globals. For example, the following statement indicates that subsystem or system globals are to be used ahead of thread-specific globals for dummy strings:
%rc = $sirparm('DUMMYSYS', 1)
And the following call indicates that subsystem or system globals are to be used ahead of thread-specific globals for $Getg calls:
%rc = $sirparm('GETGSYS', 1)
Note that system and subsystem globals are different from system and subsystem strings. The latter are never returned for $Getg or dummy string requests, and they can only be retrieved via the String function. Also, system and subsystem global values can never be longer than 255 bytes, whereas system and subsystem strings are Longstrings, which can be up to 2³¹-1 bytes long.
System-wide objects
The Janus System and Subsystem classes have methods that make designated objects available to all users in an Online or of a subsystem. Such objects, referred to as system-wide objects, are made available via deep copy, so only deep-copyable (see Copying objects) objects are eligible.
An object becomes system-wide if and only if it is saved by the System class SetObject method or by the Subsystem class SetObject method. If by the System class SetObject method, the "system" in system-wide refers to all users in the Online; if by the Subsystem SetObject method, all users in the current or a specified subsystem.
System-wide objects belong to a namespace that is not the same as that for System and Subsystem longstrings. That is, both a System longstring and a system-wide object may be called by the same name at the same time.
The methods that work with system-wide objects are one System and one Subsystem class version of each of the following:
Method | Summary | System | SubSystem |
---|---|---|---|
SetObject | Makes an object available system-wide with a specified name. | SetObject | SetObject |
GetObject | Retrieves a copy of the specified system-wide object. | GetObject | GetObject |
DeleteObject | Deletes the specified system-wide object. | DeleteObject | DeleteObject |
ListOfObjects | Returns a Stringlist containing information about system-wide objects | ListOfObjects | ListOfObjects |
Lists of System and Subsystem methods
The individual System methods are summarized in "List of System methods".
The individual Subsystem methods are summarized in "List of Subsystem methods".