-- Copyright: Mario Schweigler (C) 2005-2006
-- Institution: Computing Laboratory, University of Kent, Canterbury, UK
-- Description: pony internal TCP/IP startup code file

-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-- MA 02110-1301, USA.

--{{{  Compiler declarations
#INCLUDE "sock.module"
#INCLUDE "file.module"
#INCLUDE "proc.module"
#INCLUDE "ponylib.inc"
#INCLUDE "ponyint.inc"
#USE "ponyintaux.tce"
#INCLUDE "ponyinttcpip.inc"
#USE "ponyinttcpipsock.tce"
#USE "ponyinttcpip.tce"
--}}}

--{{{  Processes

--{{{  PROC pony.int.startup.linkmgr.tcpip
-- Do TCP/IP-specific initialisations and start link-manager 
-- Parameters: ans.name            | VAL: name of the ANS
--             app.name            | VAL: name of the application
--             node.name           | VAL: name of the node
--             node.type           | VAL: node-type
--                                   `PONYC.NODETYPE.MASTER      | master
--                                   `PONYC.NODETYPE.MASTERRESET | master/reset
--                                   `PONYC.NODETYPE.SLAVE       | slave
--                                   `PONYC.NODETYPE.SLAVEWAIT   | slave/wait
--             ctb.mgr.handle      | CTB-manager-handle (client-end)
--             nct.mgr.handle      | NCT-manager-handle (client-end)
--             kernel.reply.handle | kernel-reply-handle (client-end)
--             output.errs         | VAL: output errors?
--             int.err.handle      | internal error-handle (client-end)
--             msg.type            | VAL: message-type
--             int.msg.handle      | internal message-handle (client-end)
--             own.node.id         | RESULT: own node-ID
--             link.mgr.handle     | RESULT: link-manager-handle
--             result              | RESULT: result
PROC pony.int.startup.linkmgr.tcpip (VAL []BYTE ans.name, app.name, node.name,
                                     VAL INT node.type,
                                     SHARED PONY.INT.CTBMGRHANDLE! ctb.mgr.handle,
                                     SHARED PONY.INT.NCTMGRHANDLE! nct.mgr.handle,
                                     SHARED PONY.INT.KERNELREPLYHANDLE! kernel.reply.handle,
                                     VAL BOOL output.errs,
                                     SHARED PONY.INT.ERRHANDLE! int.err.handle,
                                     VAL INT msg.type,
                                     SHARED PONY.INT.MSGHANDLE! int.msg.handle,
                                     RESULT INT own.node.id,
                                     RESULT SHARED PONY.INT.LINKMGRHANDLE! link.mgr.handle,
                                     RESULT INT result)
  MOBILE []BYTE ans.file.name, node.file.name:
  INT ans.file.fd, ans.file.size,
      node.file.fd, node.file.size,
      ans.ip, ans.port, node.ip, node.port,
      master.ip, master.port, app.index, app.code:
  SOCKET ans.socket, listen.socket, master.socket:
  INITIAL BOOL fixed.node.ip IS FALSE:
  INITIAL BOOL fixed.node.port IS FALSE:
  SEQ
    result := PONYC.RESULT.STARTUP.OK
    --{{{  Check ANS-name
    INITIAL INT i IS 0:
    WHILE (result = PONYC.RESULT.STARTUP.OK) AND (i < (SIZE ans.name))
      INITIAL BOOL found.correct.char IS FALSE:
      SEQ
        CASE ans.name[i]
          '-', '.', '_'
            found.correct.char := TRUE
          ELSE
            IF
              (ans.name[i] >= 'a') AND (ans.name[i] <= 'z')
                found.correct.char := TRUE
              (ans.name[i] >= 'A') AND (ans.name[i] <= 'Z')
                found.correct.char := TRUE
              (ans.name[i] >= '0') AND (ans.name[i] <= '9')
                found.correct.char := TRUE
              TRUE
                SKIP
        IF
          NOT found.correct.char
            result := PONYC.RESULT.STARTUP.TCPIP.ILLEGALANSNAME
          TRUE
            SKIP
        i := i + 1
    --}}}
    --{{{  Check application-name
    IF
      (result = PONYC.RESULT.STARTUP.OK) AND ((SIZE app.name) = 0)
        result := PONYC.RESULT.STARTUP.TCPIP.ILLEGALAPPNAME
      TRUE
        SKIP
    --}}}
    --{{{  Check node-name
    INITIAL INT i IS 0:
    WHILE (result = PONYC.RESULT.STARTUP.OK) AND (i < (SIZE node.name))
      INITIAL BOOL found.correct.char IS FALSE:
      SEQ
        CASE node.name[i]
          '-', '.', '_'
            found.correct.char := TRUE
          ELSE
            IF
              (node.name[i] >= 'a') AND (node.name[i] <= 'z')
                found.correct.char := TRUE
              (node.name[i] >= 'A') AND (node.name[i] <= 'Z')
                found.correct.char := TRUE
              (node.name[i] >= '0') AND (node.name[i] <= '9')
                found.correct.char := TRUE
              TRUE
                SKIP
        IF
          NOT found.correct.char
            result := PONYC.RESULT.STARTUP.TCPIP.ILLEGALNODENAME
          TRUE
            SKIP
        i := i + 1
    --}}}
    --{{{  Check node-type
    IF
      result = PONYC.RESULT.STARTUP.OK
        CASE node.type
          PONYC.NODETYPE.MASTER, PONYC.NODETYPE.MASTERRESET,
          PONYC.NODETYPE.SLAVE, PONYC.NODETYPE.SLAVEWAIT
            SKIP
          ELSE
            result := PONYC.RESULT.STARTUP.TCPIP.ILLEGALNODETYPE
      TRUE
        SKIP
    --}}}
    --{{{  Try to open ANS-file
    IF
      result = PONYC.RESULT.STARTUP.OK
        SEQ
          -- Get name of ANS-file
          ans.file.name := PONYC.INT.TCPIP.FNAME.ANS
          IF
            (SIZE ans.name) > 0
              SEQ
                pony.int.append.str (ans.file.name, ".")
                pony.int.append.str (ans.file.name, ans.name)
            TRUE
              SKIP
          -- Open ANS-file
          file.open (ans.file.name, O.RDONLY, ans.file.fd)
          IF
            -- ANS-file could be opened in current directory
            ans.file.fd <> (-1)
              --{{{  Get size of (open) ANS-file
              STAT stat:
              INT file.result:
              SEQ
                file.fstat (ans.file.fd, stat, file.result)
                IF
                  file.result <> (-1)
                    IF
                      stat[size] > #100000
                        -- Restrict file size to 1MByte
                        ans.file.size := #100000
                      TRUE
                        ans.file.size := INT stat[size]
                  TRUE
                    result := PONYC.RESULT.STARTUP.TCPIP.ANSFILEACCESSERROR
              --}}}
            TRUE
              SEQ
                -- Get size of (non-open) ANS-file
                file.size (ans.file.name, ans.file.size)
                IF
                  -- ANS-file exists in current directory
                  ans.file.size <> (-1)
                    result := PONYC.RESULT.STARTUP.TCPIP.ANSFILEACCESSERROR
                  TRUE
                    ENVIRONMENT env:
                    SEQ
                      -- Get home directory
                      proc.os.getenv (env, PONYC.INT.TCPIP.ENV.HOME)
                      IF
                        env[value.size] = 0
                          result := PONYC.RESULT.STARTUP.TCPIP.HOMEDIRNOTINENV
                        --{{{  Home directory found in environment
                        TRUE
                          SEQ
                            -- Change path of ANS-file
                            pony.int.prepend.str (ans.file.name, "/")
                            pony.int.prepend.str (ans.file.name, [env[value] FOR env[value.size]])
                            -- Open ANS-file
                            file.open (ans.file.name, O.RDONLY, ans.file.fd)
                            IF
                              -- ANS-file could be opened in current directory
                              ans.file.fd <> (-1)
                                --{{{  Get size of (open) ANS-file
                                STAT stat:
                                INT file.result:
                                SEQ
                                  file.fstat (ans.file.fd, stat, file.result)
                                  IF
                                    file.result <> (-1)
                                      IF
                                        stat[size] > #100000
                                          -- Restrict file size to 1MByte
                                          ans.file.size := #100000
                                        TRUE
                                          ans.file.size := INT stat[size]
                                    TRUE
                                      result := PONYC.RESULT.STARTUP.TCPIP.ANSFILEACCESSERROR
                                --}}}
                              TRUE
                                SEQ
                                  -- Get size of (non-open) ANS-file
                                  file.size (ans.file.name, ans.file.size)
                                  IF
                                    -- ANS-file exists in current directory
                                    ans.file.size <> (-1)
                                      result := PONYC.RESULT.STARTUP.TCPIP.ANSFILEACCESSERROR
                                    TRUE
                                      SKIP
                        --}}}
      TRUE
        SKIP
    --}}}
    --{{{  Try to open node-file
    IF
      result = PONYC.RESULT.STARTUP.OK
        SEQ
          -- Get name of node-file
          node.file.name := PONYC.INT.TCPIP.FNAME.NODE
          IF
            (SIZE node.name) > 0
              SEQ
                pony.int.append.str (node.file.name, ".")
                pony.int.append.str (node.file.name, node.name)
            TRUE
              SKIP
          -- Open node-file
          file.open (node.file.name, O.RDONLY, node.file.fd)
          IF
            -- Node-file could be opened in current directory
            node.file.fd <> (-1)
              --{{{  Get size of (open) node-file
              STAT stat:
              INT file.result:
              SEQ
                file.fstat (node.file.fd, stat, file.result)
                IF
                  file.result <> (-1)
                    IF
                      stat[size] > #100000
                        -- Restrict file size to 1MByte
                        node.file.size := #100000
                      TRUE
                        node.file.size := INT stat[size]
                  TRUE
                    result := PONYC.RESULT.STARTUP.TCPIP.NODEFILEACCESSERROR
              --}}}
            TRUE
              SEQ
                -- Get size of (non-open) node-file
                file.size (node.file.name, node.file.size)
                IF
                  -- Node-file exists in current directory
                  node.file.size <> (-1)
                    result := PONYC.RESULT.STARTUP.TCPIP.NODEFILEACCESSERROR
                  TRUE
                    ENVIRONMENT env:
                    SEQ
                      -- Get home directory
                      proc.os.getenv (env, PONYC.INT.TCPIP.ENV.HOME)
                      IF
                        env[value.size] = 0
                          result := PONYC.RESULT.STARTUP.TCPIP.HOMEDIRNOTINENV
                        --{{{  Home directory found in environment
                        TRUE
                          SEQ
                            -- Change path of node-file
                            pony.int.prepend.str (node.file.name, "/")
                            pony.int.prepend.str (node.file.name, [env[value] FOR env[value.size]])
                            -- Open node-file
                            file.open (node.file.name, O.RDONLY, node.file.fd)
                            IF
                              -- Node-file could be opened in current directory
                              node.file.fd <> (-1)
                                --{{{  Get size of (open) node-file
                                STAT stat:
                                INT file.result:
                                SEQ
                                  file.fstat (node.file.fd, stat, file.result)
                                  IF
                                    file.result <> (-1)
                                      IF
                                        stat[size] > #100000
                                          -- Restrict file size to 1MByte
                                          node.file.size := #100000
                                        TRUE
                                          node.file.size := INT stat[size]
                                    TRUE
                                      result := PONYC.RESULT.STARTUP.TCPIP.NODEFILEACCESSERROR
                                --}}}
                              TRUE
                                SEQ
                                  -- Get size of (non-open) node-file
                                  file.size (node.file.name, node.file.size)
                                  IF
                                    -- Node-file exists in current directory
                                    node.file.size <> (-1)
                                      result := PONYC.RESULT.STARTUP.TCPIP.NODEFILEACCESSERROR
                                    TRUE
                                      SKIP
                        --}}}
      TRUE
        SKIP
    --}}}
    --{{{  Get location of ANS
    IF
      result = PONYC.RESULT.STARTUP.OK
        INITIAL BOOL ip.done IS FALSE:
        INITIAL BOOL port.done IS FALSE:
        SEQ
          --{{{  Check ANS-file for IP (from host) and port number
          IF
            ans.file.fd = (-1)
              SKIP
            --{{{  ANS-file is open
            TRUE
              MOBILE []BYTE buffer:
              INT file.result:
              SEQ
                -- Allocate buffer
                buffer := MOBILE [ans.file.size]BYTE
                -- Read buffer from file
                file.read (ans.file.fd, buffer, file.result)
                -- Close file
                INT r:
                file.close (ans.file.fd, r)
                IF
                  file.result <> ans.file.size
                    result := PONYC.RESULT.STARTUP.TCPIP.ANSFILEACCESSERROR
                  --{{{  Buffer successfully read
                  TRUE
                    INITIAL BOOL host.found IS FALSE:
                    INITIAL BOOL port.found IS FALSE:
                    MOBILE []BYTE host.string, port.string:
                    INITIAL INT pos IS 0:
                    SEQ
                      -- Get host and port strings
                      WHILE (NOT (host.found AND port.found)) AND (pos < ans.file.size)
                        INITIAL INT next.pos IS pos:
                        SEQ
                          -- Find next newline or end-of-buffer
                          WHILE (next.pos < ans.file.size) AND (buffer[next.pos] <> '*n')
                            next.pos := next.pos + 1
                          IF
                            (NOT host.found) AND (((next.pos - pos) > (SIZE PONYC.INT.TCPIP.PREF.HOST)) AND
                                pony.int.equal.str([buffer FROM pos FOR (SIZE PONYC.INT.TCPIP.PREF.HOST)],
                                PONYC.INT.TCPIP.PREF.HOST))
                              SEQ
                                host.found := TRUE
                                host.string := [buffer FROM (pos + (SIZE PONYC.INT.TCPIP.PREF.HOST)) FOR
                                  (next.pos - (pos + (SIZE PONYC.INT.TCPIP.PREF.HOST)))]
                            (NOT port.found) AND (((next.pos - pos) > (SIZE PONYC.INT.TCPIP.PREF.PORT)) AND
                                pony.int.equal.str([buffer FROM pos FOR (SIZE PONYC.INT.TCPIP.PREF.PORT)],
                                PONYC.INT.TCPIP.PREF.PORT))
                              SEQ
                                port.found := TRUE
                                port.string := [buffer FROM (pos + (SIZE PONYC.INT.TCPIP.PREF.PORT)) FOR
                                  (next.pos - (pos + (SIZE PONYC.INT.TCPIP.PREF.PORT)))]
                            TRUE
                              SKIP
                          pos := next.pos + 1
                      IF
                        -- Host string was found in ANS-file
                        host.found
                          INT sock.result:
                          SEQ
                            -- Resolve IP address from hostname
                            socket.addr.of.host(host.string, ans.ip, sock.result)
                            IF
                              sock.result = (-1)
                                result := PONYC.RESULT.STARTUP.TCPIP.ANSFILEINVALIDSETTINGS
                              -- IP address could be resolved
                              TRUE
                                ip.done := TRUE
                        TRUE
                          SKIP
                      IF
                        -- Port string was found in ANS-file
                        port.found
                          INITIAL BOOL non.digit.found IS FALSE:
                          SEQ
                            -- Check whether port is a positive integer
                            SEQ i = 0 FOR (SIZE port.string)
                              IF
                                (port.string[i] < '0') OR (port.string[i] > '9')
                                  non.digit.found := TRUE
                                TRUE
                                  SKIP
                            IF
                              non.digit.found
                                result := PONYC.RESULT.STARTUP.TCPIP.ANSFILEINVALIDSETTINGS
                              --{{{  Port string contains only digits
                              TRUE
                                SEQ
                                  -- Get port number
                                  ans.port := 0
                                  SEQ i = 0 FOR (SIZE port.string)
                                    ans.port := (ans.port * 10) + (INT (port.string[i] - '0'))
                                  port.done := TRUE
                              --}}}
                        TRUE
                          SKIP
                  --}}}
            --}}}
          --}}}
          --{{{  Use loopback IP address if host not found in ANS-file
          IF
            (result = PONYC.RESULT.STARTUP.OK) AND (NOT ip.done)
              ans.ip := INADDR.LOOPBACK
            TRUE
              SKIP
          --}}}
          --{{{  Use default port number if port not found in ANS-file
          IF
            (result = PONYC.RESULT.STARTUP.OK) AND (NOT port.done)
              ans.port := PONYC.INT.TCPIP.DEFAULTPORT.ANS
            TRUE
              SKIP
          --}}}
      TRUE
        SKIP
    --}}}
    --{{{  Get location of node
    IF
      result = PONYC.RESULT.STARTUP.OK
        SEQ
          --{{{  Check node-file for IP and port number
          IF
            node.file.fd = (-1)
              SKIP
            --{{{  Node-file is open
            TRUE
              MOBILE []BYTE buffer:
              INT file.result:
              SEQ
                -- Allocate buffer
                buffer := MOBILE [node.file.size]BYTE
                -- Read buffer from file
                file.read (node.file.fd, buffer, file.result)
                -- Close file
                INT r:
                file.close (node.file.fd, r)
                IF
                  file.result <> node.file.size
                    result := PONYC.RESULT.STARTUP.TCPIP.NODEFILEACCESSERROR
                  --{{{  Buffer successfully read
                  TRUE
                    INITIAL BOOL ip.found IS FALSE:
                    INITIAL BOOL port.found IS FALSE:
                    MOBILE []BYTE ip.string, port.string:
                    INITIAL INT pos IS 0:
                    SEQ
                      -- Get IP and port strings
                      WHILE (NOT (ip.found AND port.found)) AND (pos < node.file.size)
                        INITIAL INT next.pos IS pos:
                        SEQ
                          -- Find next newline or end-of-buffer
                          WHILE (next.pos < node.file.size) AND (buffer[next.pos] <> '*n')
                            next.pos := next.pos + 1
                          IF
                            (NOT ip.found) AND (((next.pos - pos) > (SIZE PONYC.INT.TCPIP.PREF.IP)) AND
                                pony.int.equal.str([buffer FROM pos FOR (SIZE PONYC.INT.TCPIP.PREF.IP)],
                                PONYC.INT.TCPIP.PREF.IP))
                              SEQ
                                ip.found := TRUE
                                ip.string := [buffer FROM (pos + (SIZE PONYC.INT.TCPIP.PREF.IP)) FOR
                                  (next.pos - (pos + (SIZE PONYC.INT.TCPIP.PREF.IP)))]
                            (NOT port.found) AND (((next.pos - pos) > (SIZE PONYC.INT.TCPIP.PREF.PORT)) AND
                                pony.int.equal.str([buffer FROM pos FOR (SIZE PONYC.INT.TCPIP.PREF.PORT)],
                                PONYC.INT.TCPIP.PREF.PORT))
                              SEQ
                                port.found := TRUE
                                port.string := [buffer FROM (pos + (SIZE PONYC.INT.TCPIP.PREF.PORT)) FOR
                                  (next.pos - (pos + (SIZE PONYC.INT.TCPIP.PREF.PORT)))]
                            TRUE
                              SKIP
                          pos := next.pos + 1
                      IF
                        -- IP string was found in node-file
                        ip.found
                          -- Check that IP string starts with a digit
                          IF
                            (ip.string[0] < '0') OR (ip.string[0] > '9')
                              result := PONYC.RESULT.STARTUP.TCPIP.NODEFILEINVALIDSETTINGS
                            --{{{  IP string starts with a digit (i.e. cannot be a hostname)
                            TRUE
                              INT sock.result:
                              SEQ
                                -- Resolve IP address from IP string
                                socket.addr.of.host(ip.string, node.ip, sock.result)
                                IF
                                  sock.result = (-1)
                                    result := PONYC.RESULT.STARTUP.TCPIP.NODEFILEINVALIDSETTINGS
                                  -- IP address could be resolved
                                  TRUE
                                    fixed.node.ip := TRUE
                            --}}}
                        TRUE
                          SKIP
                      IF
                        -- Port string was found in node-file
                        port.found
                          INITIAL BOOL non.digit.found IS FALSE:
                          SEQ
                            -- Check whether port is a positive integer
                            SEQ i = 0 FOR (SIZE port.string)
                              IF
                                (port.string[i] < '0') OR (port.string[i] > '9')
                                  non.digit.found := TRUE
                                TRUE
                                  SKIP
                            IF
                              non.digit.found
                                result := PONYC.RESULT.STARTUP.TCPIP.NODEFILEINVALIDSETTINGS
                              --{{{  Port string contains only digits
                              TRUE
                                SEQ
                                  -- Get port number
                                  node.port := 0
                                  SEQ i = 0 FOR (SIZE port.string)
                                    node.port := (node.port * 10) + (INT (port.string[i] - '0'))
                                  fixed.node.port := TRUE
                              --}}}
                        TRUE
                          SKIP
                  --}}}
            --}}}
          --}}}
          --{{{  Use default port number if port not found in node-file
          IF
            (result = PONYC.RESULT.STARTUP.OK) AND (NOT fixed.node.port)
              node.port := PONYC.INT.TCPIP.DEFAULTPORT.NODE
            TRUE
              SKIP
          --}}}
      TRUE
        SKIP
    --}}}
    --{{{  Create listening socket
    IF
      result = PONYC.RESULT.STARTUP.OK
        IF
          --{{{  Port number specified in node-file
          fixed.node.port
            INT sock.result:
            SEQ
              -- Create socket
              pony.int.tcpip.socket.clt.reuse (listen.socket, INADDR.ANY, node.port, sock.result)
              IF
                sock.result = (-1)
                  result := PONYC.RESULT.STARTUP.TCPIP.LISTENSOCKFAILURE
                TRUE
                  SKIP
          --}}}
          --{{{  No port number specified in node-file
          TRUE
            INT eaddrinuse.code, sock.result:
            SEQ
              pony.int.tcpip.getcode.eaddrinuse (eaddrinuse.code)
              INITIAL BOOL not.done IS TRUE:
              WHILE not.done
                SEQ
                  -- Create socket
                  pony.int.tcpip.socket.clt.reuse (listen.socket, INADDR.ANY, node.port, sock.result)
                  IF
                    -- Listening socket was created successfully
                    sock.result <> (-1)
                      not.done := FALSE
                    -- Address already in use
                    listen.socket[error] = eaddrinuse.code
                      -- Try next port
                      node.port := node.port + 1
                    -- Other error
                    TRUE
                      SEQ
                        not.done := FALSE
                        result := PONYC.RESULT.STARTUP.TCPIP.LISTENSOCKFAILURE
          --}}}
      TRUE
        SKIP
    --}}}
    --{{{  Connect to ANS (and to master if slave)
    INITIAL BOOL done IS FALSE:
    WHILE (NOT done) AND (result = PONYC.RESULT.STARTUP.OK)
      INT sock.result, name.result:
      SEQ
        -- Connect to ANS
        socket.create.connect.tcp (ans.socket, ans.ip, ans.port, sock.result)
        IF
          sock.result = (-1)
            result := PONYC.RESULT.STARTUP.TCPIP.ANSNETFAILURE
          --{{{  Could connect to ANS
          TRUE
            INITIAL BOOL close.ans.socket IS TRUE:
            SEQ
              -- Get local address
              socket.getsockname (ans.socket, name.result)
              -- Force immediate socket writes
              socket.setsockopt (ans.socket, SOL.TCP, TCP.NODELAY, OPTION.ON, sock.result)
              IF
                (sock.result = (-1)) OR (name.result = (-1))
                  result := PONYC.RESULT.STARTUP.TCPIP.ANSNETFAILURE
                --{{{  No-delay successfully set, can start operation
                TRUE
                  INITIAL MOBILE []BYTE header IS MOBILE [1 + (4 * BYTESIN (INT))]BYTE:
                  INT app.name.addr, app.name.size:
                  SEQ
                    --{{{  Set node IP address if not found in node-file
                    IF
                      fixed.node.ip
                        SKIP
                      TRUE
                        node.ip := ans.socket[local.addr]
                    --}}}
                    -- Assemble header
                    header[0] := BYTE node.type
                    []BYTE node.ip.array RETYPES node.ip:
                    [header FROM 1 FOR BYTESIN (INT)] := node.ip.array 
                    []BYTE node.port.array RETYPES node.port:
                    [header FROM (1 + BYTESIN (INT)) FOR BYTESIN (INT)] := node.port.array 
                    -- Get address/size of application-name
                    DECODE.DATA (app.name, app.name.addr, app.name.size)
                    -- Send request
                    pony.int.tcpip.socket.fullwrite.multi (ans.socket, header, [app.name.addr], [app.name.size], sock.result)
                    IF
                      sock.result = (-1)
                        result := PONYC.RESULT.STARTUP.TCPIP.ANSNETFAILURE
                      --{{{  Request sent successfully
                      TRUE
                        SEQ
                          -- Allocate correct header size
                          header := MOBILE [1 + (2 * BYTESIN (INT))]BYTE
                          -- Get reply from ANS
                          socket.fullread (ans.socket, header, SIZE header, sock.result)
                          IF
                            sock.result <> (SIZE header)
                              result := PONYC.RESULT.STARTUP.TCPIP.ANSNETFAILURE
                            --{{{  Reply received successfully
                            TRUE
                              CASE header[0]
                                --{{{  Duplicate of master
                                PONYC.INT.TCPIP.ANSHEADER.DUPLICATEMASTER
                                  result := PONYC.RESULT.STARTUP.TCPIP.DUPLICATEMASTER
                                --}}}
                                --{{{  Duplicate of pending slave
                                PONYC.INT.TCPIP.ANSHEADER.DUPLICATESLAVE
                                  result := PONYC.RESULT.STARTUP.TCPIP.DUPLICATESLAVE
                                --}}}
                                --{{{  Not done
                                PONYC.INT.TCPIP.ANSHEADER.NOTDONE
                                  CASE node.type
                                    --{{{  Master
                                    PONYC.NODETYPE.MASTER
                                      result := PONYC.RESULT.STARTUP.TCPIP.OTHERMASTER
                                    --}}}
                                    --{{{  Slave
                                    PONYC.NODETYPE.SLAVE
                                      result := PONYC.RESULT.STARTUP.TCPIP.NOMASTERYET
                                    --}}}
                                    --{{{  Slave/wait
                                    ELSE
                                      SEQ
                                        -- Close ANS-socket
                                        socket.close (ans.socket)
                                        -- Wait until ANS connects to us
                                        socket.accept (listen.socket, ans.socket, sock.result)
                                        IF
                                          sock.result = (-1)
                                            SEQ
                                              result := PONYC.RESULT.STARTUP.TCPIP.ANSNETFAILURE
                                              close.ans.socket := FALSE
                                          --{{{  ANS-socket successfully accepted
                                          TRUE
                                            SEQ
                                              -- Allocate correct header size
                                              header := MOBILE [2 * BYTESIN (INT)]BYTE
                                              -- Get master-location from ANS
                                              socket.fullread (ans.socket, header, SIZE header, sock.result)
                                              IF
                                                sock.result <> (SIZE header)
                                                  result := PONYC.RESULT.STARTUP.TCPIP.ANSNETFAILURE
                                                TRUE
                                                  SEQ
                                                    INITIAL [BYTESIN (INT)]BYTE master.ip.array IS [header FOR BYTESIN (INT)]:
                                                    INT h.master.ip RETYPES master.ip.array:
                                                    master.ip := h.master.ip
                                                    INITIAL [BYTESIN (INT)]BYTE master.port.array IS [header FROM BYTESIN (INT) FOR BYTESIN (INT)]:
                                                    INT h.master.port RETYPES master.port.array:
                                                    master.port := h.master.port
                                          --}}}
                                    --}}}
                                --}}}
                                --{{{  Done
                                ELSE
                                  CASE node.type
                                    --{{{  Master or master/reset
                                    PONYC.NODETYPE.MASTER, PONYC.NODETYPE.MASTERRESET
                                      SEQ
                                        done := TRUE
                                        INITIAL [BYTESIN (INT)]BYTE app.index.array IS [header FROM 1 FOR BYTESIN (INT)]:
                                        INT h.app.index RETYPES app.index.array:
                                        app.index := h.app.index
                                        INITIAL [BYTESIN (INT)]BYTE app.code.array IS [header FROM (1 + BYTESIN (INT)) FOR BYTESIN (INT)]:
                                        INT h.app.code RETYPES app.code.array:
                                        app.code := h.app.code
                                    --}}}
                                    --{{{  Slave or slave/wait
                                    ELSE
                                      SEQ
                                        INITIAL [BYTESIN (INT)]BYTE master.ip.array IS [header FROM 1 FOR BYTESIN (INT)]:
                                        INT h.master.ip RETYPES master.ip.array:
                                        master.ip := h.master.ip
                                        INITIAL [BYTESIN (INT)]BYTE master.port.array IS [header FROM (1 + BYTESIN (INT)) FOR BYTESIN (INT)]:
                                        INT h.master.port RETYPES master.port.array:
                                        master.port := h.master.port
                                    --}}}
                                --}}}
                            --}}}
                      --}}}
                --}}}
              -- Close ANS-socket
              IF
                close.ans.socket
                  socket.close (ans.socket)
                TRUE
                  SKIP
          --}}}
        -- Connect to master if slave
        IF
          ((node.type = PONYC.NODETYPE.SLAVE) OR (node.type = PONYC.NODETYPE.SLAVEWAIT)) AND
              (result = PONYC.RESULT.STARTUP.OK)
            SEQ
              socket.create.connect.tcp (master.socket, master.ip, master.port, sock.result)
              IF
                sock.result = (-1)
                  result := PONYC.RESULT.STARTUP.TCPIP.MASTERNETFAILURE
                --{{{  Could connect to master
                TRUE
                  SEQ
                    -- Force immediate socket writes
                    socket.setsockopt (master.socket, SOL.TCP, TCP.NODELAY, OPTION.ON, sock.result)
                    IF
                      sock.result = (-1)
                        result := PONYC.RESULT.STARTUP.TCPIP.MASTERNETFAILURE
                      --{{{  No-delay successfully set, can start operation
                      TRUE
                        INITIAL MOBILE []BYTE header IS MOBILE [2 * BYTESIN (INT)]BYTE:
                        SEQ
                          -- Assemble header
                          []BYTE node.ip.array RETYPES node.ip:
                          [header FOR BYTESIN (INT)] := node.ip.array
                          []BYTE node.port.array RETYPES node.port:
                          [header FROM BYTESIN (INT) FOR BYTESIN (INT)] := node.port.array
                          -- Send own location to master
                          socket.fullwrite (master.socket, header, sock.result)
                          IF
                            sock.result <> (SIZE header)
                              SEQ
                                result := PONYC.RESULT.STARTUP.TCPIP.MASTERNETFAILURE
                            --{{{  Own location successfully sent to master
                            TRUE
                              SEQ
                                -- Allocate correct header size
                                header := MOBILE [1 + BYTESIN (INT)]BYTE
                                -- Get reply from master
                                socket.fullread (master.socket, header, SIZE header, sock.result)
                                IF
                                  sock.result <> (SIZE header)
                                    result := PONYC.RESULT.STARTUP.TCPIP.MASTERNETFAILURE
                                  --{{{  Reply received successfully
                                  TRUE
                                    CASE header[0]
                                      --{{{  OK
                                      PONYC.INT.TCPIP.MASTERHEADER.OK
                                        SEQ
                                          done := TRUE
                                          INITIAL [BYTESIN (INT)]BYTE own.node.id.array IS [header FROM 1 FOR BYTESIN (INT)]:
                                          INT h.own.node.id RETYPES own.node.id.array:
                                          own.node.id := h.own.node.id
                                      --}}}
                                      --{{{  Duplicate slave
                                      PONYC.INT.TCPIP.MASTERHEADER.DUPLICATESLAVE
                                        result := PONYC.RESULT.STARTUP.TCPIP.DUPLICATESLAVE
                                      --}}}
                                      --{{{  Master shutting down
                                      ELSE
                                        IF
                                          node.type = PONYC.NODETYPE.SLAVE
                                            result := PONYC.RESULT.STARTUP.TCPIP.MASTERSHUTTINGDOWN
                                          TRUE
                                            SKIP
                                      --}}}
                                  --}}}
                            --}}}
                      --}}}
                    -- Close master-socket in case of error
                    IF
                      result <> PONYC.RESULT.STARTUP.OK
                        socket.close (master.socket)
                      TRUE
                        SKIP
                --}}}
          TRUE
            SKIP
    --}}}
    --{{{  Close listening socket in case of error
    IF
      result <> PONYC.RESULT.STARTUP.OK
        socket.close (listen.socket)
      TRUE
        SKIP
    --}}}
    --{{{  Fork off link-manager
    IF
      result = PONYC.RESULT.STARTUP.OK
        PONY.INT.LINKMGRHANDLE? link.mgr.handle.svr:
        SEQ
          CASE node.type
            PONYC.NODETYPE.MASTER, PONYC.NODETYPE.MASTERRESET
              own.node.id := 0
            ELSE
              SKIP
          -- Allocate link-manager-handle
          link.mgr.handle, link.mgr.handle.svr := MOBILE PONY.INT.LINKMGRHANDLE
          -- Fork off link-manager
          FORK pony.int.link.mgr.tcpip (own.node.id, ans.ip, ans.port,
                                        app.index, app.code,
                                        listen.socket, master.socket,
                                        link.mgr.handle.svr, link.mgr.handle, 
                                        ctb.mgr.handle, nct.mgr.handle,
                                        kernel.reply.handle,
                                        output.errs, int.err.handle,
                                        msg.type, int.msg.handle)
      TRUE
        SKIP
    --}}}
:
--}}}

--}}}

