?? RIGHT := 110 ??
?? NEWTITLE := 'NOS/VE Network Access: Ring 2 Service Routines For Socket Layer' ??
MODULE nlm$sk_service_routines_r2;
?? RIGHT := 110 ??

{ PURPOSE:
{   This module contains procedures needed by the NAM/VE socket layer that execute in ring 2.
{
{ DESIGN:
{   These procedures are called by the socket layer external interface code.
{   These procedures service both the UDP and the TCP portions of the socket layer.
{   This module contains procedures that manipulate socket layer data structures
{   in job paged segment which is writable from ring 2.
{   The access to the list of reusable ports is controlled via the lock to the
{   global list of TCP ports.
{   The XDCL'd procedures have been grouped in alphabetical order
{   followed by the internal procedures. The internal procedures are also in alphabetical order.
{   This module contains code that executes in ring 2. It resides on OSF$JOB_TEMPLATE_223.
{
{ NOTES:
{   The following abbreviations have been used in this module:
{          TCP - Transmission Control Protocol
{          UDP - User Datagram Protocol

?? NEWTITLE := 'Global Declarations Referenced by This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc nae$sk_socket_layer
*copyc nat$connection_id
*copyc nat$sk_job_socket
*copyc nat$sk_job_socket_assignment
*copyc nat$sk_job_socket_list
*copyc nat$sk_socket_identifier
*copyc nat$sk_socket_options
*copyc oss$job_pageable
*copyc ost$status
?? POP ??
*copyc nap$namve_system_error
*copyc osp$append_status_integer
*copyc osp$clear_job_signature_lock
*copyc osp$set_job_signature_lock
*copyc osp$set_status_abnormal
*copyc osp$set_status_condition
*copyc osp$test_signature_lock
*copyc syp$cycle

*copyc osv$job_pageable_heap
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared by This Module', EJECT ??

  VAR
    nav$sk_job_socket_assignment: [XDCL, #GATE, oss$job_pageable]
      nat$sk_job_socket_assignment := [[0], NIL],
    nav$sk_job_socket_list: [XDCL, #GATE, oss$job_pageable] ^nat$sk_job_socket_list := NIL;

?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$sk_add_job_socket', EJECT ??
*copy nlh$sk_add_job_socket

  PROCEDURE [XDCL, #GATE] nlp$sk_add_job_socket
    (    socket_id: nat$sk_socket_identifier;
         job_socket: nat$sk_job_socket);

    VAR
      new_job_socket: ^nat$sk_job_socket;

{ It is assumed that the job socket has been locked by the caller.

    REPEAT
      ALLOCATE new_job_socket IN osv$job_pageable_heap^;
    UNTIL new_job_socket <> NIL;

    new_job_socket^ := job_socket;
    nav$sk_job_socket_list^ [socket_id].job_socket := new_job_socket;

  PROCEND nlp$sk_add_job_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$sk_clear_job_socket_lock', EJECT ??
*copy nlh$sk_clear_job_socket_lock

  PROCEDURE [XDCL, #GATE] nlp$sk_clear_job_socket_lock
    (    socket_id: nat$sk_socket_identifier);

    VAR
      lock_status: ost$signature_lock_status;

    IF nav$sk_job_socket_list <> NIL THEN
      osp$test_signature_lock (nav$sk_job_socket_list^ [socket_id].lock, lock_status);
      IF lock_status = osc$sls_locked_by_current_task THEN
        osp$clear_job_signature_lock (nav$sk_job_socket_list^ [socket_id].lock);
      IFEND;
    IFEND;

  PROCEND nlp$sk_clear_job_socket_lock;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$sk_delete_job_socket', EJECT ??
*copy nlh$sk_delete_job_socket

  PROCEDURE [XDCL, #GATE] nlp$sk_delete_job_socket
    (    socket_id: nat$sk_socket_identifier;
     VAR job_socket: ^nat$sk_job_socket);

{ It is assumed that the job socket has been locked by the caller.

    IF job_socket = nav$sk_job_socket_list^ [socket_id].job_socket THEN
      FREE job_socket IN osv$job_pageable_heap^;
      nav$sk_job_socket_list^ [socket_id].job_socket := NIL;
    ELSE
      nap$namve_system_error ( {Recoverable_error=} TRUE, 'Job socket mismatch during delete.', NIL);
    IFEND;

  PROCEND nlp$sk_delete_job_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$sk_free_socket_id', EJECT ??
*copy nlh$sk_free_socket_id

  PROCEDURE [XDCL, #GATE] nlp$sk_free_socket_id
    (    socket_id: nat$sk_socket_identifier);

    osp$set_job_signature_lock (nav$sk_job_socket_assignment.lock);
    IF nav$sk_job_socket_assignment.assigned_sockets^ [socket_id] THEN
      nav$sk_job_socket_assignment.assigned_sockets^ [socket_id] := FALSE;
    ELSE
      nap$namve_system_error ( {Recoverable_error=} TRUE, 'Free of unassigned socket id.', NIL);
    IFEND;
    osp$clear_job_signature_lock (nav$sk_job_socket_assignment.lock);

  PROCEND nlp$sk_free_socket_id;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$sk_get_socket_id', EJECT ??
*copy nlh$sk_get_socket_id

  PROCEDURE [XDCL, #GATE] nlp$sk_get_socket_id
    (VAR socket_id: nat$sk_socket_identifier;
     VAR status: ost$status);

    VAR
      i: integer,
      job_socket_list: ^nat$sk_job_socket_list,
      socket_assigned: boolean;

    status.normal := TRUE;
    osp$set_job_signature_lock (nav$sk_job_socket_assignment.lock);

    IF nav$sk_job_socket_assignment.assigned_sockets = NIL THEN

{ Initialize job socket assignment.

      REPEAT
        ALLOCATE nav$sk_job_socket_assignment.assigned_sockets
          IN osv$job_pageable_heap^;
        IF nav$sk_job_socket_assignment.assigned_sockets = NIL THEN
          syp$cycle;
        IFEND;
      UNTIL nav$sk_job_socket_assignment.assigned_sockets <> NIL;
      FOR i := 1 TO UPPERBOUND (nav$sk_job_socket_assignment.assigned_sockets^) DO
        nav$sk_job_socket_assignment.assigned_sockets^ [i] := FALSE;
      FOREND;

{ Initialize job socket list.

      REPEAT
        ALLOCATE job_socket_list IN osv$job_pageable_heap^;
        IF job_socket_list = NIL THEN
          syp$cycle;
        IFEND;
      UNTIL job_socket_list <> NIL;
      FOR i := 1 TO UPPERBOUND (job_socket_list^) DO
        job_socket_list^ [i].lock.lock_id := 0;
        job_socket_list^ [i].job_socket := NIL;
      FOREND;
      nav$sk_job_socket_list := job_socket_list;
    IFEND;

{ Assign socket id.

      socket_assigned := FALSE;
      /assign_socket_id/
      FOR i := 1 TO UPPERBOUND (nav$sk_job_socket_assignment.assigned_sockets^) DO
        IF NOT nav$sk_job_socket_assignment.assigned_sockets^ [i] THEN
          nav$sk_job_socket_assignment.assigned_sockets^[i] := TRUE;
          socket_id := i;
          socket_assigned := TRUE;
          EXIT /assign_socket_id/;
        IFEND;
      FOREND /assign_socket_id/;
      osp$clear_job_signature_lock (nav$sk_job_socket_assignment.lock);
      IF NOT socket_assigned THEN
        osp$set_status_condition ( nae$sk_max_sockets_limit,  status);
      IFEND;

  PROCEND nlp$sk_get_socket_id;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$sk_lock_job_socket', EJECT ??
*copy nlh$sk_lock_job_socket

  PROCEDURE [XDCL, #GATE] nlp$sk_lock_job_socket
    (    socket_id: nat$sk_socket_identifier;
     VAR job_socket: ^nat$sk_job_socket);

    IF nav$sk_job_socket_list <> NIL THEN
      osp$set_job_signature_lock (nav$sk_job_socket_list^ [socket_id].lock);
      job_socket := nav$sk_job_socket_list^ [socket_id].job_socket;
    ELSE

{ Get socket not done.

      job_socket := NIL;
    IFEND;

  PROCEND nlp$sk_lock_job_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$sk_process_job_recovery', EJECT ??
*copy nlh$sk_process_job_recovery

  PROCEDURE [XDCL, #GATE] nlp$sk_process_job_recovery;

    VAR
      socket_id: nat$sk_socket_identifier;

{ There is no need to lock the job socket list as no other task should
{ be executing when this procedure is invoked.

    IF nav$sk_job_socket_list <> NIL THEN
      FOR socket_id := 1 TO UPPERBOUND (nav$sk_job_socket_list^) DO
        IF nav$sk_job_socket_list^ [socket_id].job_socket <> NIL THEN
          nav$sk_job_socket_list^ [socket_id].job_socket^.status := nac$sk_job_recovery;
        IFEND;
      FOREND;
    IFEND;

  PROCEND nlp$sk_process_job_recovery;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$sk_unlock_job_socket', EJECT ??
*copy nlh$sk_unlock_job_socket

  PROCEDURE [XDCL, #GATE] nlp$sk_unlock_job_socket
    (    socket_id: nat$sk_socket_identifier);

    IF nav$sk_job_socket_list <> NIL THEN
      osp$clear_job_signature_lock (nav$sk_job_socket_list^ [socket_id].lock);
    IFEND;

  PROCEND nlp$sk_unlock_job_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$sk_update_bound_address', EJECT ??
*copy nlh$sk_update_bound_address

  PROCEDURE [XDCL, #GATE] nlp$sk_update_bound_address
    (    socket_id: nat$sk_socket_identifier;
         bound_address: nat$sk_ip_address);

{ It is assumed that the job socket has been locked by the caller.

    nav$sk_job_socket_list^ [socket_id].job_socket^.bound_address := bound_address;

  PROCEND nlp$sk_update_bound_address;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$sk_update_connect_socket', EJECT ??
*copy nlh$sk_update_connect_socket

  PROCEDURE [XDCL, #GATE] nlp$sk_update_connect_socket
    (    socket_id: nat$sk_socket_identifier;
         connection_id: nat$connection_id;
         local_ip_address: nat$sk_ip_address);

    VAR
      job_socket: ^nat$sk_job_socket;

{ It is assumed that the job socket has been locked by the caller.

    job_socket := nav$sk_job_socket_list^ [socket_id].job_socket;
    job_socket^.connection_id := connection_id;
    job_socket^.tcp_socket_type := nlc$tcp_connect_socket;
    job_socket^.local_ip_address := local_ip_address;

  PROCEND nlp$sk_update_connect_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$sk_update_job_socket', EJECT ??
*copy nlh$sk_update_job_socket

  PROCEDURE [XDCL, #GATE] nlp$sk_update_job_socket
    (    socket_id: nat$sk_socket_identifier;
         port: nat$sk_port_number;
         bound_address: nat$sk_ip_address;
         socket_status: nat$sk_job_socket_status);

    VAR
      job_socket: ^nat$sk_job_socket;

{ It is assumed that the job socket has been locked by the caller.

    job_socket := nav$sk_job_socket_list^ [socket_id].job_socket;
    job_socket^.port := port;
    job_socket^.bound_address := bound_address;
    job_socket^.status := socket_status;

  PROCEND nlp$sk_update_job_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$sk_update_job_socket_status', EJECT ??
*copy nlh$sk_update_job_socket_status

  PROCEDURE [XDCL, #GATE] nlp$sk_update_job_socket_status
    (    socket_id: nat$sk_socket_identifier;
         status: nat$sk_job_socket_status);

    VAR
      job_socket: ^nat$sk_job_socket;

{ It is assumed that the job socket has been locked by the caller.

    job_socket := nav$sk_job_socket_list^ [socket_id].job_socket;
    job_socket^.status := status;

  PROCEND nlp$sk_update_job_socket_status;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$sk_update_listen_socket', EJECT ??
*copy nlh$sk_update_listen_socket

  PROCEDURE [XDCL, #GATE] nlp$sk_update_listen_socket
    (    socket_id: nat$sk_socket_identifier;
         port: nat$sk_port_number);

    VAR
      job_socket: ^nat$sk_job_socket;

{ It is assumed that the job socket has been locked by the caller.

    job_socket := nav$sk_job_socket_list^ [socket_id].job_socket;
    job_socket^.port := port;
    job_socket^.tcp_socket_type := nlc$tcp_listen_socket;

  PROCEND nlp$sk_update_listen_socket;
?? OLDTITLE ??
?? NEWTITLE := '[XDCL, #GATE] nlp$sk_update_socket_options', EJECT ??
*copy nlh$sk_update_socket_options

  PROCEDURE [XDCL, #GATE] nlp$sk_update_socket_options
    (    socket_id: nat$sk_socket_identifier;
         options: nat$sk_socket_options);
    VAR
      i: integer,
      job_socket: ^nat$sk_job_socket,
      option_p: ^nat$sk_socket_option;

{ It is assumed that the job socket has been locked by the caller and the
{ socket options have been verified by the caller.

    job_socket := nav$sk_job_socket_list^ [socket_id].job_socket;
    FOR i := 1 TO UPPERBOUND (options) DO
      option_p := ^options [i];
      CASE option_p^.option_kind OF
      = nac$sk_interface_mode_opt =
        job_socket^.interface_mode := option_p^.interface_mode;

      = nac$sk_interface_timeout_opt =
        job_socket^.interface_timeout := option_p^.interface_timeout;

      = nac$sk_checksum_opt =
        job_socket^.checksum := option_p^.checksum;

      = nac$sk_traffic_pattern_opt =
        job_socket^.traffic_pattern := option_p^.traffic_pattern;

      = nac$sk_graceful_close_opt =
        job_socket^.graceful_close := option_p^.graceful_close;

      = nac$sk_selection_criteria_opt =
        job_socket^.selection_criteria.port := option_p^.port;
        job_socket^.selection_criteria.ip_address := option_p^.ip_address;

      = nac$sk_local_addr_enabled_opt =
        job_socket^.local_ip_address_enabled := option_p^.local_ip_address_enabled;

      = nac$sk_user_cache_enabled_opt =
        job_socket^.user_cache_enabled := option_p^.user_cache_enabled;

      = nac$sk_reuse_address_opt =
        job_socket^.reuse_address := option_p^.reuse_address;

      = nac$sk_broadcast_enabled_opt =
        job_socket^.broadcast_enabled := option_p^.broadcast_enabled;
      ELSE { Invalid socket option
        nap$namve_system_error ( {Recoverable_error=} TRUE, 'Detected invalid socket options.', NIL);
      CASEND;
    FOREND;

  PROCEND nlp$sk_update_socket_options;
?? OLDTITLE ??
MODEND nlm$sk_service_routines_r2;

