Google
 

Trailing-Edge - PDP-10 Archives - BB-H138F-BM_1988 - 7-sources/netjob.bli
There are 3 other files named netjob.bli in the archive. Click here to see a list.
%TITLE 'NETJOB - DECnet Server Controller'
MODULE NETJOB (MAIN = NETJOB, VERSION = '1(12)') =
BEGIN
!++
!       COPYRIGHT (C) DIGITAL EQUIPMENT CORPORATION 1986.
!       ALL RIGHTS RESERVED.
!
!       THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY  BE  USED  AND
!       COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH
!       THE INCLUSION OF THE ABOVE COPYRIGHT NOTICE.   THIS  SOFTWARE  OR
!       ANY  OTHER  COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE
!       AVAILABLE TO ANY OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE
!       SOFTWARE IS HEREBY TRANSFERRED.
!
!       THE INFORMATION IN THIS SOFTWARE IS  SUBJECT  TO  CHANGE  WITHOUT
!       NOTICE  AND  SHOULD  NOT  BE CONSTRUED AS A COMMITMENT BY DIGITAL
!       EQUIPMENT CORPORATION.
!
!       DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR  RELIABILITY  OF
!       ITS SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY DIGITAL.
!
!
! FUNCTIONAL DESCRIPTION:
!
!       This stand alone program controls jobs which service DECnet requests. A
!       number of services provided through  DECnet require that the server  be
!       running as  the  user,  and  not as  a  privleged  OPERATOR  job.  This
!       controller makes that possible by starting jobs which are not logged in
!       until they receive a DECnet  connection which includes a valid  user-id
!       and password.
!--
%SBTTL 'Revision History'
!++
! REVISION HISTORY
!
!   12  Don't get confused when monitor reassigns job number we haven't seen 
!       die yet.  Its ugly.
!       Gregory A. Scott  30-Jun-86
!
!   11  Tell me what illegal message type we got if any.
!       Gregory A. Scott  18-Jun-86
!
!   10  Detect connection by blank username and say "<blank username>".
!       Gregory A. Scott  18-Jun-86
!
!    7  Job number wasn't set up right in HANDLE_IPCF.
!       Gregory A. Scott  17-Jun-86
!
!    6  Punt the bad IPCF messages; create routine HANDLE_IPCF.
!       Gregory A. Scott  17-Jun-86
!
!    5  Allow 512 jobs on the system; set file size to something reasonable.
!       Gregory A. Scott  16-Jun-86
!
!    4  Change log file format a little to make it easier to find problems and
!       to make the various log entries follow a similar format.
!       Gregory A. Scott  16-Jun-86
!
!    3  Open and close the log file for each message we write there so that it
!       can be examined when NETJOB is running.  Write a new log file if one 
!       doesn't exist.
!       Gregory A. Scott  16-Jun-86
!
!    2  General cleanup.  And did it ever need it.  Don't use DTRLOG:, use
!       NETJOB-LOG: instead.  Improve error message printer.  Improve the
!       logging routines.
!       Gregory A. Scott  13-Jun-86
!
!    1  Creation for DTRSRV.
!       Peter Mierswa --no date--
!
!--
%SBTTL 'Environment'

LIBRARY 'TOPS20';                       ! TOPS-20 JSYS linkages and symbols

FORWARD ROUTINE NETJOB : NOVALUE,               ! Main routine
                HANDLE_IPCF : NOVALUE,          ! Handle IPCF message
                MOVEAZ : NOVALUE,               ! Move ASCIZ string
                READ_CONTROL : NOVALUE,         ! Read SYSTEM:NETJOB.TXT
                FATAL : NOVALUE,                ! Give fatal error
                LOG_IT : NOVALUE,               ! Log a line to file
                START_JOB : NOVALUE,            ! Start a slave job
                INIT_IPCF : NOVALUE;            ! Init the ipcf system

! Document fatal errors

MACRO $FATAL(TEXT) =
      BEGIN
      FATAL(CH$PTR(UPLIT(%ASCIZ TEXT)));
      END %;

! Make string pointer to an ASCIZ string

MACRO PP(text) = CH$PTR(UPLIT(%ASCIZ %STRING(text, %REMAINING))) %;

! Make a continuation string for the LOG file

MACRO continuation = %CHAR(13,10),'                    '%;

! Literals

LITERAL CI = 0,                         ! JOB flag, means waiting for connect
        RN = 1,                         ! JOB flag, means link is active

        $NETFAIL = -1,                  ! Failure to LOGIN
        $NETSUCC = -2,                  ! Successful connection
        $NETMESS = -3,                  ! Job message to NETJOB
        $NETCREATE = -4,                ! Job creation

        TRUE = 1,                       ! BLISS style TRUE flag
        FALSE = 0,                      ! Anybody's style FALSE

        MAX_STRING = 299,               ! Maximum length of a string here
        MAX_FILE_SIZE = 160,            ! Maximum characters in file

        MAX_OBJECTS = 25,               ! Entry for each type of service
        MAX_JOB_NUMBER = 513;           ! Entry for each job number possible
! Data structure definitions

FIELD OBJECT_FIELDS =                   ! One entry for each program to run
      SET
      FILE = [0,0,36,0],                                ! ASCIZ file to run
      MIN_JOBS = [(MAX_FILE_SIZE/5),0,18,0],            ! Min jobs allowed
      MAX_JOBS = [(MAX_FILE_SIZE/5),18,18,0],           ! Max jobs allowed
      ACTUAL_JOBS = [(MAX_FILE_SIZE/5)+1,0,18,0],       ! Jobs in state RN+CI
      WAITING_JOBS = [(MAX_FILE_SIZE/5)+1,18,18,0]      ! Jobs in state CI
      TES;

LITERAL OBJECT_SIZE = (MAX_FILE_SIZE/5)+2;              ! Words in object field

FIELD JOB_FIELDS =                      ! One entry for each job I create
      SET
      JOB_STATE = [0,0,18,0],           ! Job's state
      JOB_SERVICE = [0,18,18,0]         ! Service number
      TES;

! Module wide storage

OWN jobs : BLOCKVECTOR [MAX_JOB_NUMBER, 1] FIELD (JOB_FIELDS),
    objects : BLOCKVECTOR [MAX_OBJECTS, OBJECT_SIZE] FIELD (OBJECT_FIELDS),
    pointer,                          ! Pointer to buffer
    buffer : BLOCK [CH$ALLOCATION(MAX_STRING)], ! Buffer for writing
    my_pid,                             ! My PID for IPCF messages from servers
    last_object,                        ! Index of the last used objects entry
    pdesc : VECTOR [9],                 ! The descriptor block for IPCF traffic
    pdata : REF VECTOR[],               ! Address of IPCF data block
    pdblock : BLOCK [2000];             ! Location of IPCF data block
%SBTTL 'Routine NETJOB'

ROUTINE NETJOB : NOVALUE =
!++
! FUNCTIONAL DESCRIPTION
!
!       This is the top functional level of the NETJOB.  It does the following:
!
!       1) Open the log file on NETJOB-LOG: (if that logical exists).
!       2) Read in the file containing all of the programs to run.
!       3) Get a named pid for NETSERVER for IPCF communications.
!       4) Start all detached not logged in jobs.
!       5) Loop reading IPCF messages which are either status messages or
!          logout messages.  If it is a logout message, recreate a job.
!--

BEGIN

! Log our start up.  Try for an old NETJOB.LOG before trying a new one.

LOG_IT(PP('NETJOB started'));           ! Write message to log file

! Fill in the table of servers from SYSTEM:NETJOB.TXT

read_control();

! Establish my identity for IPCF

INIT_IPCF();

! Start all of the jobs

INCR i FROM 1 TO .last_object           ! For each object
DO BEGIN
   objects[.i,ACTUAL_JOBS] = 0;         ! No actual jobs
   objects[.i,WAITING_JOBS] = 0;        ! No waiting jobs
   INCR j FROM 1 TO .objects[.i,MIN_JOBS]       ! For each minimum jobs
   DO START_JOB(.i);                            ! Start all waiting jobs
   END;

! Sit and wait for notification of an event from a job:
!
!       1. Connect attempt failed
!       2. Connect attempt succeeded
!       3. Log message from slave job
!       4. Job logged out

WHILE TRUE                              ! Forever
DO BEGIN                                ! Loop to read IPCF messages

   ! Get an IPCF message.  We will block here at the MRECV most of the time.

   pdesc[$IPCFL] = 0;
   pdesc[$IPCFS] = 0;
   pdesc[$IPCFR] = .my_pid;
   pdesc[$IPCFP] = 512^18 + .pdata;
   IF NOT MRECV(7, pdesc) THEN FATAL(0);

   ! Handle the IPCF message and loop

   HANDLE_IPCF();

   END;                                 ! End of WHILE TRUE
END;
%SBTTL 'Routine Handle_IPCF'
ROUTINE HANDLE_IPCF : NOVALUE =
!++
!
! FUNCTIONAL DESCRIPTION
!
!       Handle IPCF message from slave job.  A log file entry is created.  If
!       the job is accepting a connection, create another waiting job if
!       needed.  If the job is logging out, decrement the number of active jobs
!       if it was running.
!
!--
BEGIN

LOCAL index,                            ! Job index
      job_number;                       ! Slave job number

! Get job number that sent us this IPCF, and service that sent it to us

job_number = .(pdata[1])<0,18,0>;       ! Second word is the job number
IF .pdata[0] EQL -4                     ! Is it function -4?
THEN index = .pdata[2]                          ! Yes, index is in word three
ELSE index = .jobs[.job_number, JOB_SERVICE];   ! No load index from table

! Start constructing a log file entry for this job

pointer = CH$PTR(buffer);                            ! Point to log buffer
MOVEAZ(PP('Job '), pointer);                         ! Start with job text
IF NOT NOUT(.pointer, .job_number, 10; pointer)      ! Output job number
THEN FATAL(0);                                       ! Punt if NOUT fails

! Make sure that we know about this job.  If illegal, give a message.
! Otherwise append program name to the log information
        
IF .index EQL 0                         ! Do we have a service index for this?
THEN MOVEAZ(PP(' unknown to NETJOB '), pointer)  ! Nope
ELSE BEGIN                              ! Job is known to us
     MOVEAZ(PP(' running '), pointer);  ! Label the filename and output it
     MOVEAZ(CH$PTR(objects[.index,FILE]), pointer);
     END;

! Process the message, the first data word will be one of:
!
!  $NETFAIL: Failure to login
!        -2: Logged in OK
!        -3: Error message
!    $IPCLO: Job logged out
! OTHERWISE: Illegal

SELECTONE .pdata[0] OF               ! Select based on message type
SET

[$NETFAIL] : BEGIN                         ! Connect attempt failed

             ! Log this failure, and wait for logout to happen.

             MOVEAZ(PP(continuation, 'Failed connection by '), pointer);
             IF .pdata[2]<29,7> EQL 0
             THEN MOVEAZ(PP('<illegal username>'), pointer)
             ELSE MOVEAZ(CH$PTR(pdata[2]), pointer);
             LOG_IT(CH$PTR(buffer));
             END;

[$NETSUCC] : BEGIN                         ! Connect attempt succeeded

             ! Log the successful login

             MOVEAZ(PP(continuation, 'Successful connection by '), pointer);
             MOVEAZ(CH$PTR(pdata[2]), pointer);
             LOG_IT(CH$PTR(buffer));

             ! Set job to running state and count down waiting jobs.  Start
             ! another waiting job if the max number of jobs hasn't been 
             ! reached

             jobs[.job_number,JOB_STATE] = RN;
             objects[.index,WAITING_JOBS] = .objects[.index,WAITING_JOBS] - 1;

             IF .objects[.index,ACTUAL_JOBS] LSS .objects[.index,MAX_JOBS]
             THEN START_JOB(.index);

             END;

[$NETMESS] : BEGIN                         ! Some other error there, just put
             MOVEAZ(PP(' message', continuation), pointer);
             MOVEAZ(CH$PTR(pdata[2]), pointer);
             LOG_IT(CH$PTR(buffer));
             END;

[$NETCREATE] : BEGIN                            ! Job created
       
               MOVEAZ(PP(' created'), pointer); ! Output what happened
               LOG_IT(CH$PTR(buffer));          ! Send to log file
               jobs[.job_number,JOB_SERVICE] = .index;
               jobs[.job_number,JOB_STATE] = CI;

               END;

[$IPCLO] : BEGIN                     ! Job ended
           pointer = CH$PTR(buffer);
           MOVEAZ(PP('Job '), pointer);
           IF NOT NOUT(.pointer, .job_number, 10; pointer) 
           THEN FATAL(0);
           MOVEAZ(PP(' running '), pointer);
           MOVEAZ(CH$PTR(objects[.index,FILE]), pointer);
           MOVEAZ(PP(' logged out'), pointer);
           LOG_IT(CH$PTR(buffer));
              
           ! Count down the number of actual jobs, and count down the number
           ! of waiting jobs if the state was CI.  If the number of waiting
           ! jobs is less then the minimum and the number of running jobs is
           ! less then the minimum, then make another job.

           objects[.index,ACTUAL_JOBS] = .objects[.index,ACTUAL_JOBS] - 1;

           IF .jobs[.job_number,JOB_STATE] EQL CI 
           THEN objects[.index,WAITING_JOBS] = 
                                             .objects[.index,WAITING_JOBS] - 1;
           jobs[.job_number,JOB_SERVICE] = 0;

           IF .objects[.index,WAITING_JOBS] LSS .objects[.index,MIN_JOBS]
              AND .objects[.index,ACTUAL_JOBS] LSS .objects[.index,MAX_JOBS]
           THEN START_JOB(.index);

           END;

[OTHERWISE] : BEGIN                     ! Log this strange message
              MOVEAZ(PP(continuation, 'illegal message type '), pointer);
              IF NOT NOUT(.pointer, .pdata[0], 10; pointer) 
              THEN FATAL(0);
              LOG_IT(CH$PTR(buffer));
              END;
TES;                                    ! End of selectone set
END;                                    ! HANDLE_IPCF
%SBTTL 'Routine MOVEAZ'
ROUTINE MOVEAZ (source, destination) : NOVALUE =
BEGIN
!++
!
! FUNCTIONAL DESCRIPTION
!
!       This routine appends an ASCIZ string to the specified target.
!       The destination string pointer is updated.
!
! FORMAL PARAMETERS
!
!       source: pointer to ASCIZ string source
!       destination: pointer to ASCIZ string to be appended to
!--

LOCAL char,                             ! Character we are copying
      spointer;                         ! Source pointer

spointer = .source;                     ! Copy the source pointer down here

! Loop through the string getting each character copied.  Don't do over 299.

INCR i FROM 1 TO MAX_STRING
DO BEGIN
   char = CH$RCHAR_A(spointer);
   IF .char EQL 0 THEN EXITLOOP;
   CH$WCHAR_A(.char, .destination);
   END;

! Leave the destination byte pointer ready to append more characters but also
! insure that the destination is an ASCIZ string.

CH$WCHAR(0, ..destination);

END;
%SBTTL 'Routine Read_Control'
ROUTINE read_control : NOVALUE =
!++
! FUNCTIONAL DESCRIPTION
!
!       This routine fills in the program tables by reading SYSTEM:NETJOB.TXT
!--

BEGIN

LOCAL eof,                              ! Flag for EOF
      jfn,                              ! JFN of SYSTEM:NETJOB.TXT
      exe_jfn;                          ! EXEC's JFN

! Get a JFN for the file that tells us what to do

IF NOT GTJFN(GJ_SHT+GJ_OLD, PP('SYSTEM:NETJOB.TXT'); jfn)
THEN $FATAL('Failed to find SYSTEM:NETJOB.TXT');

! Open the control file

IF NOT OPENF(.jfn, OF_RD+7^30)          ! Read access, 7 bit bytes
THEN $FATAL('Failed to open SYSTEM:NETJOB.TXT');

! Read its entries until EOF seen

eof = FALSE;                            ! Not (yet) at EOF

INCR i FROM 1 TO MAX_OBJECTS-1          ! For each program possible
DO BEGIN

   ! Read a line of text from the file, exit if EOF

   IF NOT SIN(.jfn, CH$PTR(buffer), MAX_FILE_SIZE-1, %O'12')
   THEN BEGIN
        eof = TRUE;
        EXITLOOP;
        END;

   ! Get complete name of program to run, store it in a good place

   pointer = CH$PTR(buffer);
   IF NOT GTJFN(GJ_SHT+GJ_OLD, .pointer; exe_jfn, pointer)
   THEN $FATAL('Cannot find file specified in SYSTEM:NETJOB.TXT');
   IF NOT JFNS(CH$PTR(objects[.i,file]), .exe_jfn, %O'111100000001', 0)
   THEN FATAL(0);
   IF NOT RLJFN(.exe_jfn) THEN FATAL(0);

   ! Minimum tasks ready to accept connections

   IF NOT NIN(.pointer, 10; pointer, objects[.i, MIN_JOBS])
   THEN $FATAL('Failed to read minimum number of jobs from NETJOB.TXT');

   ! Maximum jobs started

   IF NOT NIN(.pointer, 10; pointer, objects[.i, MAX_JOBS])
   THEN $FATAL('Failed to read maximum number of jobs from NETJOB.TXT');

   ! Remember the last one used for later

   last_object = .i;

   END;                                  ! End of INCR DO loop

! Close the control file

IF NOT CLOSF(.jfn)                      ! Try and close the file
THEN $FATAL('Failed to close SYSTEM:NETJOB.TXT');       ! Oops

! Was the table too small?

IF NOT .eof                             ! Did we get to EOF?
THEN $FATAL('SYSTEM:NETJOB.TXT is too large');  ! Nope, punt

END;
%SBTTL 'Routine START_JOB'
ROUTINE START_JOB (index) : NOVALUE =
!++
!
! FUNCTIONAL DESCRIPTION
!
!       Start up a job to receive connections for the specified object in the
!       object table.  Logs this fact in the log file.
!
! FORMAL PARAMETERS
!
!       INDEX: Index into the object table.
!
!--

BEGIN

LOCAL pointer,
      result,
      argblk : VECTOR [12];

BIND filename = objects[.INDEX,FILE];

! Log this attempt


! Initialize the argument block

ARGBLK[0] = 0;                          ! User name string
ARGBLK[1] = 0;                          ! Password string
ARGBLK[2] = 0;                          ! Account number
ARGBLK[3] = CH$PTR(objects[.INDEX,FILE]);       ! Program to run
ARGBLK[4] = 0;                          ! Entry vec offset
ARGBLK[5] = $NULIO;                     ! Controlling tty
ARGBLK[6] = 0;                          ! Not used
ARGBLK[7] = 0;                          ! AC block
ARGBLK[8] = 0;                          ! EXEC flag bits
ARGBLK[9] = 0;                          ! IO designators
ARGBLK[10] = 0;                         ! Runtime limit
ARGBLK[11] = 0;                         ! Capability mask
ARGBLK[12] = .my_pid;                   ! PID for logout message

! Start the job

IF NOT CRJOB(CJ_SLO+CJ_CAP+CJ_FIL+CJ_ACS, argblk, 0; result) 
THEN BEGIN
     pointer = CH$PTR(buffer);                          ! Point to buffer
     MOVEAZ(PP('Can''t create job for '), pointer);     ! Output label
     MOVEAZ(CH$PTR(objects[.index,FILE]), pointer);     ! Output filename
     LOG_IT(CH$PTR(buffer));                            ! Send to log file
     IF .result NEQ CRJBX6
     THEN $FATAL('Failed to start a new job');
     RETURN;
     END;

! Send me an IPCF stating that I created a job here just now.  This needed
! because we can have an IPCF in the queue for job deletion when when we create
! a job with the same number - to avoid confusion, we just send an IPCF to
! ourselves when this happens so we aren't confused.

pdesc[$IPCFL] = 0;                      ! Zero flags
pdesc[$IPCFS] = .my_pid;                ! Sender is me
pdesc[$IPCFR] = .my_pid;                ! Reciever is me
pdesc[$IPCFP] = 4^18 + .pdata;          ! Point to the data
pdata[0] = $NETCREATE;                  ! NETJOB function code
pdata[1] = .result;                     ! Job number 
pdata[2] = .index;                      ! Service that is for this job

IF NOT MSEND(4,pdesc) THEN $FATAL('Can''t send IPCF to self');

! Increment the count of running jobs and jobs waiting for connects

objects[.index,ACTUAL_JOBS] = .objects[.index,ACTUAL_JOBS] + 1;
objects[.index,WAITING_JOBS] = .objects[.index,WAITING_JOBS] + 1;

END;                                    ! START_JOB
%SBTTL 'Routine LOG_IT'
ROUTINE LOG_IT (p_pointer) : NOVALUE =
!++
!  FUNCTIONAL DESCRIPTION
!
!       Send a line of ASCIZ text to the log file
!
!  FORMAL PARAMETERS
!
!       p_pointer: Pointer to an asciz string
!
!--

BEGIN

LOCAL log_jfn,
      lpointer,
      lbuffer : VECTOR[CH$ALLOCATION(MAX_STRING)];

! Open up the LOG file

IF NOT GTJFN(GJ_SHT+GJ_OLD,             ! Try to open an old log file
             PP('NETJOB-LOG:NETJOB.LOG');
             log_jfn) 
THEN IF NOT GTJFN(GJ_SHT+GJ_FOU,            ! Try to open a new log file
              PP('NETJOB-LOG:NETJOB.LOG;P777700');
              log_jfn) 
     THEN RETURN;                       ! Return now if no LOG file available

IF NOT OPENF(.log_jfn, OF_APP+7^30)     ! Open the LOG file
THEN BEGIN                              ! We couldn't open the file
     RLJFN(.log_jfn);                   ! Dump the JFN
     RETURN;                            ! Just return now
     END;

! Now there is a log file.  Make a buffer full of data to send.

lpointer = CH$PTR(lbuffer);             ! Point to the buffer please
ODTIM(.lpointer, -1, 0; lpointer);      ! Output time to log file
MOVEAZ(PP(' '), lpointer);              ! and a space
MOVEAZ(.p_pointer, lpointer);           ! and the line to write
MOVEAZ(CH$PTR(UPLIT(%CHAR(13,10,0))), lpointer);        ! and a CRLF

! Have a buffer of data; append it to the log file.

SOUT(.log_jfn, CH$PTR(lbuffer), 0);     ! Quickly send that to the log file

! Close the log file for readers.

CLOSF(.log_jfn);                        ! Close it to make it appear

END;
%SBTTL 'Routine FATAL'
ROUTINE FATAL (message) : NOVALUE =
!++
!  FUNCTIONAL DESCRIPTION
!
!       Give a message when a fatal error occurs.  NETJOB then halts.
!--

BEGIN

LOCAL lpointer,                         ! Pointer to string to log
      pointer,                          ! Pointer to buffer to make string
      lbuffer : VECTOR[CH$ALLOCATION(MAX_STRING)];      ! string buffer

! Point to our error buffer

pointer = CH$PTR(lbuffer);

! First get the time and the error string output

CH$WCHAR_A(%C'?', pointer);             ! Output question mark
ODTIM(.pointer, -1, 0; pointer);        ! Output time
CH$WCHAR_A(%C' ', pointer);             ! Output space
lpointer = .pointer;                    ! Save pointer to current text

! Copy the furnished message or zero to the string to print

MOVEAZ(PP('Fatal NETJOB error: '), pointer);    ! Start the error
IF .message NEQ 0                       ! Is there a message to print?
THEN MOVEAZ(.message, pointer);         ! Output error text

! Give the last error in this process

MOVEAZ(PP(continuation, 'Last JSYS error: '), pointer);
ERSTR(.pointer, $FHSLF ^ 18 + %O'777777',0);

! Display message on terminal and in the LOG file

PSOUT(CH$PTR(lbuffer));                 ! Output to terminal always
LOG_IT(.lpointer);                      ! Log error (less time)

! Exit and never allow continue

WHILE TRUE DO HALTF();

END;
%SBTTL 'Routine INIT_IPCF'
ROUTINE INIT_IPCF : NOVALUE =
!++
!  FUNCTIONAL DESCRIPTION
!
!       Get our PID, and assign us a name (NETSERVER)
!--

BEGIN

LOCAL argblk : VECTOR[10];

! Save the PDB address (Pick a page-aligned 512 words out of 2 pages)

pdata = (((pdblock AND %O'777777')+777) ^ -9) * 512;

! Get my PID

argblk[0] = $MUCRE;
argblk[1] = $FHSLF;
argblk[2] = 0;
IF NOT MUTIL(3,argblk) THEN $FATAL('Can''t get a PID for myself');
my_pid = .argblk[2];

! Give my PID a name

pdesc[$IPCFL] = 0;
pdesc[$IPCFS] = .MY_PID;
pdesc[$IPCFR] = 0;
pdesc[$IPCFP] = 4^18 + .pdata;
pdata[0] = $IPCII;
pdata[1] = 0;
pdata[2] = %ASCII 'NETSE';
pdata[3] = %ASCIZ 'RVER';

IF NOT MSEND(4,pdesc) THEN $FATAL('Can''t get named NETSERVER PID');

! Get response from INFO

pdesc[$IPCFL] = 0;
pdesc[$IPCFS] = 0;
pdesc[$IPCFR] = .my_pid;
pdesc[$IPCFP] = 512^18 + .pdata;
IF NOT MRECV(7,pdesc) THEN FATAL(0);

END;
END
ELUDOM