?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Statistics Facility: Program Interfaces' ??
MODULE sfm$statistic_interfaces;

{ PURPOSE:
{   This module contains the external interfaces used to manipulate the system and job routing control tables,
{   emit statistics, and retrieve information from the system and job routing control tables.
{
{ DESIGN:
{   The interfaces in this module protect the information in the system and job routing control tables from
{   update or retrieval by unprivileged users.
{
{   The interfaces that update the system or job routing control tables call internal interfaces in the
{   statistics facility that execute in lower rings and are capable of locking the routing control tables.
{   Interfaces that retrieve information from the routing control tables do not call internal interfaces in
{   the statistics facility and do not interlock the routing control tables.

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc ofe$error_codes
*copyc osc$max_status_condition_number
*copyc ost$caller_identifier
*copyc ost$name
*copyc ost$status
*copyc ost$status_condition_number
*copyc ost$status_identifier
*copyc ost$string
*copyc sfc$statistic_version
*copyc sfe$call_again_job_recovered
*copyc sfe$counter_array_size_range
*copyc sfe$descriptive_data_size
*copyc sfe$incorrect_statistic_code
*copyc sfe$insufficient_privilege
*copyc sfe$invalid_statistic_name
*copyc sfe$limit_condition_codes
*copyc sfe$security_audit_not_enabled
*copyc sfe$statistics_not_available
*copyc sfe$too_much_data_for_statistic
*copyc sfe$unknown_routing_ctl_access
*copyc sfe$work_area_full
*copyc sft$audited_operation_set
*copyc sft$audit_selection_criteria
*copyc sft$binary_logs
*copyc sft$counters
*copyc sft$descriptive_data
*copyc sft$global_binary_logset
*copyc sft$routing_control_access
*copyc sft$routing_control_table_id
*copyc sft$statistic_buffer
*copyc sft$statistic_code
*copyc sft$statistic_group
*copyc sft$statistic_header
*copyc sft$statistic_identifier
?? POP ??
*copyc avp$accounting_administrator
*copyc avp$configuration_administrator
*copyc avp$security_option_active
*copyc avp$system_administrator
*copyc avp$system_displays
*copyc avp$system_operator
*copyc clp$convert_integer_to_string
*copyc clp$convert_string_to_integer
*copyc clp$get_processing_phase
*copyc clp$only_validate_name
*copyc clp$validate_name
*copyc lgp$add_entry_global_binary_log
*copyc lgp$add_entry_local_binary_log
*copyc osp$append_status_integer
*copyc osp$append_status_parameter
*copyc osp$disestablish_cond_handler
*copyc osp$establish_condition_handler
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc osp$status_condition_code
*copyc osp$unpack_status_condition
*copyc pmp$continue_to_cause
*copyc pmp$get_compact_date_time
*copyc pmp$get_executing_task_gtid
*copyc pmp$get_job_names
*copyc sfp$add_job_audit_control
*copyc sfp$add_job_routing_control
*copyc sfp$add_system_audit_control
*copyc sfp$add_system_routing_control
*copyc sfp$delete_job_audit_control
*copyc sfp$delete_job_routing_control
*copyc sfp$delete_sys_audit_control
*copyc sfp$delete_sys_routing_control
*copyc sfp$lock_job_routing_control
*copyc sfp$lock_system_routing_control
*copyc sfp$routing_control
*copyc sfp$update_job_limit_accum
*copyc lgv$log_names
*copyc sfv$job_routing_control_table
*copyc sfv$sys_routing_control_table
?? OLDTITLE ??
?? NEWTITLE := 'check_job_routing_ctl_privilege', EJECT ??
{ PURPOSE
{   Verifies that the caller has the appropriate privileges to access the job routing control table.

  PROCEDURE check_job_routing_ctl_privilege
    (    logs: sft$binary_logset;
         caller_id: ost$caller_identifier;
         access: sft$routing_control_access;
     VAR status: ost$status);

    VAR
      ignored_status: ost$status,
      processing_phase: clt$processing_phase;

    status.normal := TRUE;

{ If the request is only accessing the job statistics log, no special privileges are required.

    IF logs = $sft$binary_logset [pmc$job_statistic_log] THEN
      RETURN;
    IFEND;

{ Calls made from ring 3 have complete access to the job routing control table.

    IF caller_id.ring <= osc$tsrv_ring THEN
      RETURN;
    IFEND;

{ Calls made during the system prolog or epilog have complete access to the job routing control tables

    clp$get_processing_phase (processing_phase, ignored_status);
    IF (processing_phase = clc$system_prolog_phase) OR (processing_phase = clc$system_epilog_phase) THEN
      RETURN;
    IFEND;

{ Calls with read access and system_display access are permitted access to the logs

    IF ( access = sfc$read_routing_controls ) AND avp$system_displays () THEN
      RETURN;
    IFEND;

{ Check for proper intersection of logs/validation
{     Account_log requires accounting administration validation
{     Job_account_log requirs accounting administration validation
{     Engineering_log requires configuration administration validation
{     Statistics_log requires configuration administration validation
{     History_log requires system_operation validation

    IF ( pmc$account_log IN logs )    AND  NOT ( avp$accounting_administrator () )    OR

      ( pmc$job_account_log IN logs ) AND  NOT ( avp$accounting_administrator () )    OR

      ( pmc$engineering_log IN logs ) AND  NOT ( avp$configuration_administrator () ) OR

      ( pmc$security_log IN logs )   AND  NOT ( avp$system_administrator () ) OR

      ( pmc$statistic_log IN logs )   AND  NOT ( avp$configuration_administrator () ) OR

      ( pmc$history_log IN logs )     AND  NOT ( avp$system_operator () ) THEN

{ The caller does not have sufficient privilege for the requested access to the job routing control table.

      CASE access OF
      = sfc$read_routing_controls =
        osp$set_status_abnormal ('SF', sfe$insufficient_privilege, 'read', status);
        osp$append_status_parameter (osc$status_parameter_delimiter, 'job', status);
      = sfc$update_routing_controls =
        osp$set_status_abnormal ('SF', sfe$insufficient_privilege, 'update', status);
        osp$append_status_parameter (osc$status_parameter_delimiter, 'job', status);
      ELSE
        osp$set_status_abnormal ('SF', sfe$unknown_routing_ctl_access, 'check_job_routing_ctl_privilege',
              status);
      CASEND;

    IFEND;

  PROCEND check_job_routing_ctl_privilege;
?? OLDTITLE ??
?? NEWTITLE := 'check_sys_routing_ctl_privilege', EJECT ??
{ PURPOSE
{   Verifies that the caller has the appropriate privileges to access the system routing control table.

  PROCEDURE check_sys_routing_ctl_privilege
    (    logs: sft$binary_logset;
         caller_id: ost$caller_identifier;
         access: sft$routing_control_access;
     VAR status: ost$status);

    status.normal := TRUE;

{ If the request is only reading information about the job statistics log, no special privileges are required.

    IF (logs = $sft$binary_logset [pmc$job_statistic_log]) AND (access = sfc$read_routing_controls) THEN
      RETURN;
    IFEND;

{ Calls made from ring 3 and below have complete access to the system routing control table.

    IF caller_id.ring <= osc$tsrv_ring THEN
      RETURN;
    IFEND;

{ OK if access = read and system display validation

    IF ( access = sfc$read_routing_controls) AND ( avp$system_displays () ) THEN
      RETURN;
    IFEND;

{ Check for proper intersection of logs/validation
{     Account_log requires accounting administration validation
{     Job_account_log requirs accounting administration validation
{     Engineering_log requires configuration administration validation
{     Statistics_log requires configuration administration validation
{     History_log requires system_operation validation
{     Job_statistics_log with update access requires configuration administration validation

    IF ( pmc$account_log IN logs )    AND NOT ( avp$accounting_administrator () ) OR

      ( pmc$job_account_log IN logs ) AND NOT ( avp$accounting_administrator () ) OR

      ( pmc$engineering_log IN logs ) AND NOT ( avp$configuration_administrator () ) OR

      ( pmc$security_log IN logs ) AND NOT ( avp$system_administrator () ) OR

      ( pmc$statistic_log IN logs )   AND NOT ( avp$configuration_administrator () ) OR

      ( pmc$history_log IN logs )     AND NOT ( avp$system_operator () ) OR

      ( pmc$job_statistic_log IN logs ) AND ( access = sfc$update_routing_controls) AND
        NOT avp$configuration_administrator () THEN

{ The caller does not have sufficient privilege for the requested access to the system routing control table.

      CASE access OF
      = sfc$read_routing_controls =
        osp$set_status_abnormal ('SF', sfe$insufficient_privilege, 'read', status);
        osp$append_status_parameter (osc$status_parameter_delimiter, 'system', status);
      = sfc$update_routing_controls =
        osp$set_status_abnormal ('SF', sfe$insufficient_privilege, 'update', status);
        osp$append_status_parameter (osc$status_parameter_delimiter, 'system', status);
      ELSE
        osp$set_status_abnormal ('SF', sfe$unknown_routing_ctl_access, 'check_sys_routing_ctl_privilege',
              status);
      CASEND;

    IFEND;

  PROCEND check_sys_routing_ctl_privilege;
?? OLDTITLE ??
?? NEWTITLE := 'copy_routing_control', EJECT ??
{ PURPOSE:
{   Make a copy of a routing control entry in the provided work area.

  PROCEDURE copy_routing_control
    (    routing_control: ^sft$routing_control;
         logs: sft$binary_logset;
     VAR head: ^sft$routing_control;
     VAR work_area: ^SEQ ( * );
     VAR routing_control_copy: ^sft$routing_control;
     VAR status: ost$status);

    VAR
      current_routing_control: ^sft$routing_control,
      previous_routing_control: ^sft$routing_control;

    status.normal := TRUE;

    NEXT routing_control_copy IN work_area;
    IF routing_control_copy = NIL THEN
      osp$set_status_condition (sfe$work_area_full, status);
      RETURN;
    IFEND;
    routing_control_copy^ := routing_control^;
    routing_control_copy^.activated_logs := logs * routing_control^.activated_logs;
    IF routing_control_copy^.audit_control_p <> NIL THEN
      routing_control_copy^.audit_control_p := NIL;
      routing_control_copy^.activated_logs := routing_control_copy^.activated_logs +
            $sft$binary_logset [pmc$security_log];
    IFEND;

    IF head = NIL THEN
      head := routing_control_copy;
      head^.forward := NIL;
    ELSE
      current_routing_control := head;
      previous_routing_control := NIL;

    /find_insertion_position/
      WHILE (current_routing_control <> NIL) AND
            (current_routing_control^.statistic_code < routing_control_copy^.statistic_code) DO
        previous_routing_control := current_routing_control;
        current_routing_control := current_routing_control^.forward;
      WHILEND /find_insertion_position/;
      routing_control_copy^.forward := current_routing_control;
      IF previous_routing_control = NIL THEN
        head := routing_control_copy;
      ELSE
        previous_routing_control^.forward := routing_control_copy;
      IFEND;
    IFEND;

  PROCEND copy_routing_control;
?? OLDTITLE ??
?? NEWTITLE := 'get_active_statistics', EJECT ??
{ PURPOSE
{   Returns a list of routing control information for the specified statistics and logs.

  PROCEDURE get_active_statistics
    (    routing_control_table: sft$routing_control_table;
         logs: sft$binary_logset;
     VAR head: ^sft$routing_control;
     VAR work_area: ^SEQ ( * );
     VAR status: ost$status);

    VAR
      current_routing_control: ^sft$routing_control,
      routing_control_copy: ^sft$routing_control,
      routing_ctl_table_index: 0 .. sfc$routing_control_table_size;

    status.normal := TRUE;

    head := NIL;

    IF routing_control_table = NIL THEN
      RETURN;
    IFEND;

  /process_routing_ctl_table/
    FOR routing_ctl_table_index := 0 TO sfc$routing_control_table_size DO
      current_routing_control := routing_control_table^ [routing_ctl_table_index];

    /process_routing_ctl_linked_list/
      WHILE current_routing_control <> NIL DO
        IF ((logs * current_routing_control^.activated_logs) <> $sft$binary_logset []) OR
              ((pmc$security_log IN logs) AND (current_routing_control^.audit_control_p <> NIL)) THEN
          copy_routing_control (current_routing_control, logs, head, work_area, routing_control_copy, status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;
        IFEND;
        current_routing_control := current_routing_control^.forward;
      WHILEND /process_routing_ctl_linked_list/;

    FOREND /process_routing_ctl_table/;

  PROCEND get_active_statistics;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$activate_audit', EJECT ??
*copy sfh$activate_audit

  PROCEDURE [XDCL, #GATE] sfp$activate_audit
    (    operation_set: sft$audited_operation_set;
         selection_criteria: sft$audit_selection_criteria;
         routing_control_table_id: sft$routing_control_table_id;
         lock: boolean;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier;

    status.normal := TRUE;

{ Determine the callers ring.

    #CALLER_ID (caller_id);

{ Verify that the user has the authority to update the specified routing control table.

    IF routing_control_table_id = sfc$sys_routing_control_table THEN
      check_sys_routing_ctl_privilege ($sft$binary_logset[pmc$security_log], caller_id,
            sfc$update_routing_controls, status);
    ELSE
      check_job_routing_ctl_privilege ($sft$binary_logset[pmc$security_log], caller_id,
            sfc$update_routing_controls, status);
    IFEND;
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Make sure that the security audit security option has been enabled.

    IF NOT avp$security_option_active (avc$vso_security_audit) THEN
      osp$set_status_condition (sfe$security_audit_not_enabled, status);
      RETURN;
    IFEND;

{ Update the routing control table.

    IF routing_control_table_id = sfc$sys_routing_control_table THEN
      sfp$add_system_audit_control (operation_set, selection_criteria, lock, status);
    ELSE
      sfp$add_job_audit_control (operation_set, selection_criteria, lock, status);
    IFEND;

  PROCEND sfp$activate_audit;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$activate_job_statistic', EJECT ??
*copy sfh$activate_job_statistic

  PROCEDURE [XDCL, #GATE] sfp$activate_job_statistic
    (    statistic_code: sft$statistic_code;
         logs: sft$binary_logset;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier;

    #CALLER_ID (caller_id);

    status.normal := TRUE;

{ Verify that the user is allowed to update the routing control table.

    check_job_routing_ctl_privilege (logs, caller_id, sfc$update_routing_controls, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Update the job routing control table.

    sfp$add_job_routing_control (statistic_code, logs, osc$null_name, status);

  PROCEND sfp$activate_job_statistic;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$activate_system_statistic', EJECT ??
*copy sfh$activate_system_statistic

  PROCEDURE [XDCL, #GATE] sfp$activate_system_statistic
    (    statistic_code: sft$statistic_code;
         logs: sft$binary_logset;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier;

    #CALLER_ID (caller_id);

    status.normal := TRUE;

{ Verify that the user is allowed to update the routing control table.

    check_sys_routing_ctl_privilege (logs, caller_id, sfc$update_routing_controls, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Update the system routing control table.

    sfp$add_system_routing_control (statistic_code, logs, status);

  PROCEND sfp$activate_system_statistic;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] sfp$build_statistic', EJECT ??
{ PURPOSE
{   Constructs a statistic that will be recorded in a binary log.

  PROCEDURE [XDCL] sfp$build_statistic
    (    statistic_code: sft$statistic_code;
         descriptive_data: sft$descriptive_data;
         counters: sft$counters;
         global_task_id: ost$global_task_id;
     VAR statistic: ^SEQ ( * );
     VAR status: ost$status);

    VAR
      statistic_counters: sft$counters,
      statistic_descriptive_data: ^sft$descriptive_data,
      statistic_header: ^sft$statistic_header,
      user_supplied_name: jmt$user_supplied_name;

    status.normal := TRUE;

    RESET statistic;

    NEXT statistic_header IN statistic;
    IF statistic_header = NIL THEN
      osp$set_status_abnormal ('SF', sfe$work_area_full, 'BUILD_STATISTIC', status);
      RETURN;
    IFEND;

    statistic_header^.version := sfc$statistic_version;

    pmp$get_compact_date_time (statistic_header^.date_time, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    statistic_header^.statistic_code := statistic_code;

    pmp$get_job_names (user_supplied_name, statistic_header^.job_name, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    statistic_header^.task_id := global_task_id;

    IF counters <> NIL THEN
      statistic_header^.number_of_counters := UPPERBOUND (counters^);
      NEXT statistic_counters: [1 .. UPPERBOUND (counters^)] IN statistic;
      IF statistic_counters = NIL THEN
        osp$set_status_abnormal ('SF', sfe$work_area_full, 'BUILD_STATISTIC', status);
        RETURN;
      IFEND;
      statistic_counters^ := counters^;
    ELSE
      statistic_header^.number_of_counters := 0;
    IFEND;

    IF STRLENGTH (descriptive_data) <> 0 THEN
      statistic_header^.descriptive_data_size := STRLENGTH (descriptive_data);
      NEXT statistic_descriptive_data: [STRLENGTH (descriptive_data)] IN statistic;
      IF statistic_descriptive_data = NIL THEN
        osp$set_status_abnormal ('SF', sfe$work_area_full, 'BUILD_STATISTIC', status);
        RETURN;
      IFEND;
      statistic_descriptive_data^ := descriptive_data;
    ELSE
      statistic_header^.descriptive_data_size := 0;
    IFEND;

  PROCEND sfp$build_statistic;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$convert_stat_code_to_name', EJECT ??
*copy sfh$convert_stat_code_to_name

  PROCEDURE [XDCL, #GATE] sfp$convert_stat_code_to_name
    (    statistic_code: sft$statistic_code;
     VAR statistic_name: ost$name;
     VAR status: ost$status);

    VAR
      numeric_string: ost$string,
      statistic_id: ost$status_identifier,
      statistic_number: ost$status_condition_number,
      string_size: integer,
      valid_name: boolean;

    status.normal := TRUE;

    statistic_name := osc$null_name;

    osp$unpack_status_condition (statistic_code, statistic_id, statistic_number);

    clp$convert_integer_to_string (statistic_number, 10, FALSE, numeric_string, status);
    IF NOT status.normal THEN
      osp$set_status_condition (sfe$incorrect_statistic_code, status);
      osp$append_status_integer (osc$status_parameter_delimiter, statistic_code, 10, FALSE, status);
      RETURN;
    IFEND;

    statistic_name := statistic_id;
    statistic_name (#SIZE(statistic_id)+1, *) := numeric_string.value (1, numeric_string.size);

    clp$only_validate_name (statistic_name, valid_name);
    IF NOT valid_name THEN
      osp$set_status_condition (sfe$incorrect_statistic_code, status);
      osp$append_status_integer (osc$status_parameter_delimiter, statistic_code, 10, FALSE, status);
    IFEND;

  PROCEND sfp$convert_stat_code_to_name;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$convert_stat_name_to_code', EJECT ??
*copy sfh$convert_stat_name_to_code

  PROCEDURE [XDCL, #GATE] sfp$convert_stat_name_to_code
    (    statistic_name: ost$name;
     VAR statistic_code: sft$statistic_code;
     VAR status: ost$status);

    VAR
      converted_integer: clt$integer,
      statistic_id: ost$status_identifier,
      statistic_number: ost$status_condition_number,
      valid_name: boolean,
      validated_name: ost$name;

    status.normal := TRUE;

    clp$validate_name (statistic_name, validated_name, valid_name);
    IF NOT valid_name THEN
      osp$set_status_abnormal ('SF', sfe$invalid_statistic_name, statistic_name, status);
      RETURN;
    IFEND;

    statistic_id := validated_name (1, 2);

    clp$convert_string_to_integer (validated_name (3, * ), converted_integer, status);
    IF NOT status.normal THEN
      osp$set_status_abnormal ('SF', sfe$invalid_statistic_name, validated_name, status);
      RETURN;
    IFEND;
    IF converted_integer.value <= osc$max_status_condition_number THEN
      statistic_number := converted_integer.value;
      statistic_code := osp$status_condition_code (statistic_id, statistic_number);
    ELSE
      osp$set_status_abnormal ('SF', sfe$invalid_statistic_name, validated_name, status);
    IFEND;

  PROCEND sfp$convert_stat_name_to_code;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$deactivate_audit', EJECT ??
*copy sfh$deactivate_audit

  PROCEDURE [XDCL, #GATE] sfp$deactivate_audit
    (    operation_set: sft$audited_operation_set;
         routing_control_table_id: sft$routing_control_table_id;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier;

    status.normal := TRUE;

{ Determine the callers ring.

    #CALLER_ID (caller_id);

{ Verify that the user has the authority to update the specified routing control table.

    IF routing_control_table_id = sfc$sys_routing_control_table THEN
      check_sys_routing_ctl_privilege ($sft$binary_logset[pmc$security_log], caller_id,
            sfc$update_routing_controls, status);
    ELSE
      check_job_routing_ctl_privilege ($sft$binary_logset[pmc$security_log], caller_id,
            sfc$update_routing_controls, status);
    IFEND;
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Make sure that the security audit security option has been enabled.

    IF NOT avp$security_option_active (avc$vso_security_audit) THEN
      osp$set_status_condition (sfe$security_audit_not_enabled, status);
      RETURN;
    IFEND;

{ Update the routing control table.

    IF routing_control_table_id = sfc$sys_routing_control_table THEN
      sfp$delete_sys_audit_control (operation_set, status);
    ELSE
      sfp$delete_job_audit_control (operation_set, status);
    IFEND;

  PROCEND sfp$deactivate_audit;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$deactivate_job_statistic', EJECT ??
*copy sfh$deactivate_job_statistic

  PROCEDURE [XDCL, #GATE] sfp$deactivate_job_statistic
    (    statistic_code: sft$statistic_code;
         logs: sft$binary_logset;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier;

    #CALLER_ID (caller_id);

    status.normal := TRUE;

    check_job_routing_ctl_privilege (logs, caller_id, sfc$update_routing_controls, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    sfp$delete_job_routing_control (statistic_code, logs, status);

  PROCEND sfp$deactivate_job_statistic;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$deactivate_system_statistic', EJECT ??
*copy sfh$deactivate_system_statistic

  PROCEDURE [XDCL, #GATE] sfp$deactivate_system_statistic
    (    statistic_code: sft$statistic_code;
         logs: sft$binary_logset;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier;

    #CALLER_ID (caller_id);

    status.normal := TRUE;

    check_sys_routing_ctl_privilege (logs, caller_id, sfc$update_routing_controls, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    sfp$delete_sys_routing_control (statistic_code, logs, status);

  PROCEND sfp$deactivate_system_statistic;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$emit_statistic', EJECT ??
*copy sfh$emit_statistic

  PROCEDURE [XDCL, #GATE] sfp$emit_statistic
    (    statistic_code: sft$statistic_code;
         descriptive_data: sft$descriptive_data;
         counters: sft$counters;
     VAR status: ost$status);

    VAR
      global_task_id: ost$global_task_id;

    status.normal := TRUE;

    pmp$get_executing_task_gtid (global_task_id);

    sfp$internal_emit_statistic (statistic_code, descriptive_data, counters, global_task_id, status);

  PROCEND sfp$emit_statistic;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$emit_system_statistic', EJECT ??
*copy sfh$emit_system_statistic

  PROCEDURE [XDCL, #GATE] sfp$emit_system_statistic
    (    identifier: sft$statistic_identifier;
         statistic_code: sft$statistic_code;
         descriptive_data: sft$descriptive_data;
         counters: sft$counters;
     VAR status: ost$status);

    VAR
      composite_statistic_code: sft$statistic_code,
      global_task_id: ost$global_task_id;

    status.normal := TRUE;

    composite_statistic_code := osp$status_condition_code (identifier, statistic_code);

    pmp$get_executing_task_gtid (global_task_id);

    sfp$internal_emit_statistic (composite_statistic_code, descriptive_data, counters, global_task_id,
          status);

  PROCEND sfp$emit_system_statistic;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$get_active_job_statistics', EJECT ??
*copy sfh$get_active_job_statistics

  PROCEDURE [XDCL, #GATE] sfp$get_active_job_statistics
    (    logs: sft$binary_logset;
     VAR head: ^sft$routing_control;
     VAR work_area: ^SEQ ( * );
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier;

    #CALLER_ID (caller_id);

    status.normal := TRUE;

    head := NIL;

    check_job_routing_ctl_privilege (logs, caller_id, sfc$read_routing_controls, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    IF work_area = NIL THEN
      osp$set_status_abnormal ('SF', sfe$work_area_full, 'SFP$GET_ACTIVE_JOB_STATISTICS', status);
      RETURN;
    IFEND;

    IF sfv$sys_routing_control_table = NIL THEN
      osp$set_status_abnormal ('SF', sfe$statistics_not_available, 'Job', status);
      osp$append_status_parameter (osc$status_parameter_delimiter, 'sfp$get_active_job_statistics', status);
      RETURN;
    IFEND;

    get_active_statistics (sfv$job_routing_control_table, logs, head, work_area, status);
    IF (NOT status.normal) AND (status.condition = sfe$work_area_full) THEN
      osp$append_status_parameter (osc$status_parameter_delimiter, 'SFP$GET_ACTIVE_JOB_STATISTICS', status);
    IFEND;

  PROCEND sfp$get_active_job_statistics;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$get_active_system_stats', EJECT ??
*copy sfh$get_active_system_stats

  PROCEDURE [XDCL, #GATE] sfp$get_active_system_stats
    (    logs: sft$binary_logset;
     VAR head: ^sft$routing_control;
     VAR work_area: ^SEQ ( * );
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier;

?? NEWTITLE := 'job_recovery_condition_handler', EJECT ??
{ PURPOSE:
{   This condition handler is used around the code that is reading the system routing information to handle
{   the case of job recovery.  The system routing control table is kept in mainframe pageable and is moved if
{   the system is recovered.  If the job is recovered while it is reading the system routing control table,
{   the condition handler will perform a non-local exit with an abnormal status of
{   SFE$JOB_RECOVERED_CALL_AGAIN.


    PROCEDURE job_recovery_condition_handler
      (    condition: pmt$condition;
           condition_information: ^pmt$condition_information;
           save_area: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      pmp$continue_to_cause (pmc$execute_standard_procedure, handler_status);
      IF NOT handler_status.normal THEN
        RETURN;
      IFEND;

      IF (condition.selector = pmc$user_defined_condition) AND (condition.user_condition_name =
            'OSC$JOB_RECOVERY') THEN
        osp$set_status_abnormal ('SF', sfe$call_again_job_recovered, 'SFP$GET_ACTIVE_SYSTEM_STATS', status);
        EXIT sfp$get_active_system_stats;
      IFEND;

    PROCEND job_recovery_condition_handler;

?? OLDTITLE, EJECT ??

    #CALLER_ID (caller_id);

    status.normal := TRUE;

    head := NIL;

    IF work_area = NIL THEN
      osp$set_status_abnormal ('SF', sfe$work_area_full, 'SFP$GET_ACTIVE_SYSTEM_STATS', status);
      RETURN;
    IFEND;

    check_sys_routing_ctl_privilege (logs, caller_id, sfc$read_routing_controls, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    osp$establish_condition_handler (^job_recovery_condition_handler, FALSE);

    IF sfv$sys_routing_control_table = NIL THEN
      osp$set_status_abnormal ('SF', sfe$statistics_not_available, 'System', status);
      osp$append_status_parameter (osc$status_parameter_delimiter, 'sfp$get_active_system_stats', status);
      RETURN;
    IFEND;

    REPEAT
      get_active_statistics (sfv$sys_routing_control_table, logs, head, work_area, status);
    UNTIL (status.normal) OR (status.condition <> sfe$call_again_job_recovered);
    IF (NOT status.normal) AND (status.condition = sfe$work_area_full) THEN
      osp$append_status_parameter (osc$status_parameter_delimiter, 'SFP$GET_ACTIVE_SYSTEM_STATS', status);
    IFEND;

    osp$disestablish_cond_handler;

  PROCEND sfp$get_active_system_stats;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$get_audited_operations', EJECT ??
*copy sfh$get_audited_operations

  PROCEDURE [XDCL, #GATE] sfp$get_audited_operations
    (    routing_control_table_id: sft$routing_control_table_id;
     VAR work_area: ^SEQ ( * );
     VAR first_routing_control_entry: ^sft$routing_control;
     VAR status: ost$status);

    VAR
      audit_control_copy_p: ^sft$audit_control,
      caller_id: ost$caller_identifier,
      current_audit_control: ^sft$audit_control,
      current_routing_control: ^sft$routing_control,
      routing_control_copy: ^sft$routing_control,
      routing_control_table: sft$routing_control_table,
      routing_ctl_table_index: 0 .. sfc$routing_control_table_size;

?? NEWTITLE := 'job_recovery_condition_handler', EJECT ??
{ PURPOSE:
{   This condition handler is used around the code that is reading the system routing information to handle
{   the case of job recovery.  The system routing control table is kept in mainframe pageable and is moved if
{   the system is recovered.  If the job is recovered while it is reading the system routing control table,
{   the condition handler will perform a non-local exit with an abnormal status of
{   SFE$JOB_RECOVERED_CALL_AGAIN.


    PROCEDURE job_recovery_condition_handler
      (    condition: pmt$condition;
           condition_information: ^pmt$condition_information;
           save_area: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      pmp$continue_to_cause (pmc$execute_standard_procedure, handler_status);
      IF NOT handler_status.normal THEN
        RETURN;
      IFEND;

      IF (condition.selector = pmc$user_defined_condition) AND (condition.user_condition_name =
            'OSC$JOB_RECOVERY') THEN
        osp$set_status_abnormal ('SF', sfe$call_again_job_recovered, 'SFP$GET_AUDITED_OPERATIONS', status);
        EXIT sfp$get_audited_operations;
      IFEND;

    PROCEND job_recovery_condition_handler;

?? OLDTITLE, EJECT ??
    #CALLER_ID (caller_id);

    status.normal := TRUE;

    IF work_area = NIL THEN
      osp$set_status_abnormal ('SF', sfe$work_area_full, 'SFP$GET_AUDITED_OPERATIONS', status);
      RETURN;
    IFEND;

    first_routing_control_entry := NIL;

{ Verify that the user has the authority to read the specified routing control table.

    IF routing_control_table_id = sfc$sys_routing_control_table THEN
      check_sys_routing_ctl_privilege ($sft$binary_logset [pmc$security_log], caller_id,
            sfc$read_routing_controls, status);
      routing_control_table := sfv$sys_routing_control_table;
    ELSE
      check_job_routing_ctl_privilege ($sft$binary_logset [pmc$security_log], caller_id,
            sfc$read_routing_controls, status);
      routing_control_table := sfv$job_routing_control_table;
    IFEND;
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Make sure the routing control table has been initialized.

    IF routing_control_table = NIL THEN
      IF routing_control_table_id = sfc$sys_routing_control_table THEN
        osp$set_status_abnormal ('SF', sfe$statistics_not_available, 'Job', status);
      ELSE
        osp$set_status_abnormal ('SF', sfe$statistics_not_available, 'System', status);
      IFEND;
      osp$append_status_parameter (osc$status_parameter_delimiter, 'sfp$get_audited_operations', status);
      RETURN;
    IFEND;

{ Make sure that the security audit security option has been enabled.

    IF NOT avp$security_option_active (avc$vso_security_audit) THEN
      osp$set_status_condition (sfe$security_audit_not_enabled, status);
      RETURN;
    IFEND;

{ Establish a condition handler to deal with job recovery when reading the system routing control table.

    IF routing_control_table_id = sfc$sys_routing_control_table THEN
      osp$establish_condition_handler (^job_recovery_condition_handler, FALSE);
    IFEND;

{ Go through the routing control table, copying every routing control entry that affects audit.

    FOR routing_ctl_table_index := 0 TO sfc$routing_control_table_size DO
      current_routing_control := routing_control_table^ [routing_ctl_table_index];
      WHILE current_routing_control <> NIL DO
        IF (pmc$security_log IN current_routing_control^.activated_logs) OR
              (current_routing_control^.audit_control_p <> NIL) THEN
          copy_routing_control (current_routing_control, $sft$binary_logset[pmc$security_log],
                first_routing_control_entry, work_area, routing_control_copy, status);
          IF NOT status.normal THEN
            RETURN;
          IFEND;

{ Copy the audit control information (if any).

          current_audit_control := current_routing_control^.audit_control_p;
          WHILE current_audit_control <> NIL DO
            NEXT audit_control_copy_p: [1 .. UPPERBOUND(current_audit_control^.selection_criteria)] IN
                  work_area;
            IF audit_control_copy_p = NIL THEN
              osp$set_status_abnormal ('SF', sfe$work_area_full, 'SFP$GET_AUDITED_OPERATIONS', status);
              RETURN;
            IFEND;
            audit_control_copy_p^ := current_audit_control^;
            audit_control_copy_p^.forward := routing_control_copy^.audit_control_p;
            routing_control_copy^.audit_control_p := audit_control_copy_p;
            current_audit_control := current_audit_control^.forward;
          WHILEND;
        IFEND;
        current_routing_control := current_routing_control^.forward;
      WHILEND;
    FOREND;

{ Disestablish the condition handler that deals with job recovery when reading the system routing control
{ table.

    IF routing_control_table_id = sfc$sys_routing_control_table THEN
      osp$disestablish_cond_handler;
    IFEND;

  PROCEND sfp$get_audited_operations;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$get_log_name', EJECT ??
*copy sfh$get_log_name

  PROCEDURE [XDCL, #GATE] sfp$get_log_name
    (    log: sft$binary_logs;
     VAR log_name: ost$name;
     VAR status: ost$status);

    status.normal := TRUE;

    log_name := lgv$log_names [log];

  PROCEND sfp$get_log_name;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL] sfp$get_routing_controls', EJECT ??
{ PURPOSE:
{   Returns the routing control information for the specified statistic.

  PROCEDURE [XDCL] sfp$get_routing_controls
    (    statistic_code: sft$statistic_code;
     VAR activated_logs: sft$binary_logset;
     VAR activated_limit_name: ost$name;
     VAR status: ost$status);

    VAR
      routing_control: ^sft$routing_control;

?? NEWTITLE := 'job_recovery_condition_handler', EJECT ??
{ PURPOSE:
{   This condition handler is used around the code that is reading the system routing information to handle
{   the case of job recovery.  The system routing control table is kept in mainframe pageable and is moved if
{   the system is recovered.  If the job is recovered while it is reading the system routing control table,
{   the condition handler will perform a non-local exit with an abnormal status of
{   SFE$JOB_RECOVERED_CALL_AGAIN.

    PROCEDURE job_recovery_condition_handler
      (    condition: pmt$condition;
           condition_information: ^pmt$condition_information;
           save_area: ^ost$stack_frame_save_area;
       VAR handler_status: ost$status);

      pmp$continue_to_cause (pmc$execute_standard_procedure, handler_status);
      IF NOT handler_status.normal THEN
        RETURN;
      IFEND;

      IF (condition.selector = pmc$user_defined_condition) AND (condition.user_condition_name =
            'OSC$JOB_RECOVERY') THEN
        osp$set_status_abnormal ('SF', sfe$call_again_job_recovered, 'GET_ROUTING_CONTROLS', status);
        EXIT sfp$get_routing_controls;
      IFEND;

    PROCEND job_recovery_condition_handler;
?? OLDTITLE, EJECT ??

    status.normal := TRUE;

    activated_logs := $sft$binary_logset [];
    activated_limit_name := osc$null_name;

    IF sfv$job_routing_control_table <> NIL THEN
      routing_control := sfp$routing_control (statistic_code, sfv$job_routing_control_table);
      IF routing_control <> NIL THEN
        activated_logs := activated_logs + routing_control^.activated_logs;
        activated_limit_name := routing_control^.limit_name;
      IFEND;
    IFEND;

    osp$establish_condition_handler (^job_recovery_condition_handler, FALSE);

    IF sfv$sys_routing_control_table <> NIL THEN
      routing_control := sfp$routing_control (statistic_code, sfv$sys_routing_control_table);
      IF routing_control <> NIL THEN
        activated_logs := activated_logs + routing_control^.activated_logs;
      IFEND;
    IFEND;

    osp$disestablish_cond_handler;

  PROCEND sfp$get_routing_controls;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, INLINE] sfp$internal_emit_statistic', EJECT ??
*copy sfh$internal_emit_statistic

  PROCEDURE [XDCL, INLINE] sfp$internal_emit_statistic
    (    statistic_code: sft$statistic_code;
         descriptive_data: sft$descriptive_data;
         counters: sft$counters;
         global_task_id: ost$global_task_id;
     VAR status: ost$status);

    VAR
      activated_limit_name: ost$name,
      activated_logs: sft$binary_logset,
      ignored_status: ost$status,
      log: sft$binary_logs,
      statistic: ^SEQ ( * ),
      statistic_name: ost$name,
      statistic_size: integer;

    status.normal := TRUE;

{ Verify the size of the descriptive data.

    IF STRLENGTH (descriptive_data) > sfc$max_descriptive_data_size THEN
      sfp$convert_stat_code_to_name (statistic_code, statistic_name, ignored_status);
      osp$set_status_abnormal ('SF', sfe$descriptive_data_size, statistic_name, status);
      osp$append_status_integer (osc$status_parameter_delimiter, sfc$max_descriptive_data_size, 10, FALSE,
            status);
      RETURN;
    IFEND;

{ Verify the number of counters.

    IF counters <> NIL THEN
      IF UPPERBOUND (counters^) > sfc$max_number_of_counters THEN
        sfp$convert_stat_code_to_name (statistic_code, statistic_name, ignored_status);
        osp$set_status_abnormal ('SF', sfe$counter_array_size_range, statistic_name, status);
        osp$append_status_integer (osc$status_parameter_delimiter, sfc$max_number_of_counters, 10, FALSE,
              status);
        RETURN;
      IFEND;
    IFEND;

{ Find out which logs the statistic has been activated to.

    REPEAT
      sfp$get_routing_controls (statistic_code, activated_logs, activated_limit_name, status);
    UNTIL (status.normal) OR (status.condition <> sfe$call_again_job_recovered);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ If the statistic is activated to any logs build the statistic and record it.

    IF activated_logs <> $sft$binary_logset [] THEN

{ Allocate the space to build the statistic in.

      statistic_size := #SIZE (sft$statistic_header);
      IF counters <> NIL THEN
        statistic_size := statistic_size + #SIZE (counters^);
      IFEND;
      statistic_size := statistic_size + #SIZE (descriptive_data);
      IF statistic_size > sfc$max_statistic_record_size THEN
        sfp$convert_stat_code_to_name (statistic_code, statistic_name, {ignore} status);
        osp$set_status_abnormal ('SF', sfe$too_much_data_for_statistic, statistic_name, status);
        RETURN;
      IFEND;

      PUSH statistic: [[REP statistic_size OF cell]];

      sfp$build_statistic (statistic_code, descriptive_data, counters, global_task_id, statistic, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

{ Record the statistic in activated logs.

      FOR log := LOWERVALUE (sft$binary_logs) TO UPPERVALUE (sft$binary_logs) DO
        IF log IN activated_logs THEN
          IF log IN -$sft$global_binary_logset [] THEN
            lgp$add_entry_global_binary_log (log, statistic, status);
          ELSE
            lgp$add_entry_local_binary_log (log, statistic, status);
          IFEND;
          IF NOT status.normal THEN
            RETURN;
          IFEND;
        IFEND;
      FOREND;

    IFEND;

    IF (activated_limit_name <> osc$null_name) AND (counters <> NIL) THEN
      sfp$update_job_limit_accum (activated_limit_name, counters^ [1], sfc$incremental_update, status);
    IFEND;

  PROCEND sfp$internal_emit_statistic;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] sfp$lock_statistic', EJECT ??
*copy sfh$lock_statistic

  PROCEDURE [XDCL, #GATE] sfp$lock_statistic
    (    statistic_code: sft$statistic_code;
         logs: sft$binary_logset;
         routing_control_table_id: sft$routing_control_table_id;
     VAR status: ost$status);

    VAR
      caller_id: ost$caller_identifier;

    status.normal := TRUE;

{ Determine the callers ring.

    #CALLER_ID (caller_id);

{ Verify that the user has the authority to update the specified routing control table.

    IF routing_control_table_id = sfc$sys_routing_control_table THEN
      check_sys_routing_ctl_privilege ($sft$binary_logset[pmc$security_log], caller_id,
            sfc$update_routing_controls, status);
    ELSE
      check_job_routing_ctl_privilege ($sft$binary_logset[pmc$security_log], caller_id,
            sfc$update_routing_controls, status);
    IFEND;
    IF NOT status.normal THEN
      RETURN;
    IFEND;

{ Update the routing control table.

    IF routing_control_table_id = sfc$sys_routing_control_table THEN
      sfp$lock_system_routing_control (statistic_code, logs, status);
    ELSE
      sfp$lock_job_routing_control (statistic_code, logs, status);
    IFEND;

  PROCEND sfp$lock_statistic;
?? OLDTITLE ??
MODEND sfm$statistic_interfaces;
