Jump to content











Photo
- - - - -

Delaying service startup on multi-homed server

service startup tftp

  • Please log in to reply
1 reply to this topic

#1 AHenry

AHenry
  • Members
  • 3 posts
  •  
    United States

Posted 15 July 2015 - 07:10 PM

I've run into an odd issue with the Tftpd32 service (4.52 service edition on Windows 7) - maybe someone can offer a suggestion.

 

When the Tftpd32 service starts up, the TftpBindLocalInterface socket bind call fails with WSAEADDRNOTAVAIL.  It appears that the second network adapter on our server is not quite ready for use yet.  The service doesn't recover on its own, and needs to be restarted.

 

I've added dependencies on the Tftpd32 service for LanmanagerWorkstation and LanmangerServer, even DHCP, but this doesn't delay the startup quite enough - it still needs a few seconds more delay.  Setting the service to Delayed Start would slow down the startup of our whole system by a lot, and the server and the connected client get restarted fairly often.

 

There are a few ways that I can hack in a fixed delay of a couple seconds, or add a retry to TftpBindLocalInterface, but it feels like there should be a better way.  Any thoughts?



#2 AHenry

AHenry
  • Members
  • 3 posts
  •  
    United States

Posted 17 July 2015 - 08:19 PM

For what it's worth, throwing a dumb retry around the bind call worked for me (along with adding the LanmanServer dependency in the Windows service manager). I don't know that it's suitable for general use, but here's what I did:

in tftpd_main.c:

#define TFTP_MAX_BIND_RETRIES 5

static SOCKET TftpBindLocalInterface (void)
{
    int retryBind = 0;
    int retries = 0;
.
.
.
    // It's possible for the service to start before all network interfaces
    // are available, even if it has dependencies on LanmanServer and LanmanWorkstation.
    // So retry WSAEADDRNOTAVAIL bind errors a few times.  
    do
    {
        // bind the socket to the active interface
        Rc = bind(sListenSocket, res->ai_addr, res->ai_addrlen);

        if (Rc == INVALID_SOCKET)
        {
            char szAddr[MAXLEN_IPv6]="unknown", szServ[NI_MAXSERV]="unknown";
            int KeepLastError = GetLastError();
            // retrieve localhost and port
            getnameinfo (  res->ai_addr, res->ai_addrlen,
                szAddr, sizeof szAddr,
                szServ, sizeof szServ,
                NI_NUMERICHOST | AI_NUMERICSERV );
            SetLastError (KeepLastError); // getnameinfo has reset LastError !
            // 3 causes : access violation, socket already bound, bind on an address
            switch (GetLastError ())
            {
            case WSAEADDRNOTAVAIL :   // 10049
                SVC_ERROR ("Error %d\n%s\n\n"
                    "Tftpd32 tried to bind the %s port\n"
                    "to the interface %s\nwhich is not available for this host\n"
                    "Either remove the %s service or suppress %s interface assignation",
                    GetLastError (), LastErrorText (),
                    "tftp", sSettings.szTftpLocalIP, "tftp", sSettings.szTftpLocalIP);
                retries++;
                if ( retries<= TFTP_MAX_BIND_RETRIES )
                {
                    Sleep(1000);
                    retryBind = 1;
                }
                break;
            case WSAEINVAL :
            case WSAEADDRINUSE :
                SVC_ERROR ("Error %d\n%s\n\n"
                    "Tftpd32 can not bind the %s port\n"
                    "an application is already listening on this port",
                    GetLastError (), LastErrorText (),
                    "tftp" );
                break;
            default :
                SVC_ERROR ("Bind error %d\n%s",
                    GetLastError (), LastErrorText () );
                break;
            } // switch error type
            if (!retryBind)
            {
                closesocket (sListenSocket);
                LogToMonitor ("bind port to %s port %s failed\n", szAddr, szServ);
            }
        }
    } while (retryBind);

and in service_stuff.c, I bumped up the wait hint value for good measure:

    if (!ReportStatusToSCMgr(
        SERVICE_START_PENDING, // service state
        NO_ERROR,              // exit code
        8000))                 // wait hint

Edited by AHenry, 17 July 2015 - 08:23 PM.





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users