/*++

Copyright (c) 1991-1992  Microsoft Corporation

Module Name:

    SvcStub.c (was scstub.c)

Abstract:

    These are the Service Controller API RPC client stubs.

    These stubs contain RPC work-around code due to the fact that RPC
    does not support unions yet.  Therefore, a remote entry point must be
    created for every info-level.  This results in messy looking switch
    statements that are used to determine which entry point to call.

    These switch statements include default paths that can currently cause
    an error.  When unions are available, the case statements will be
    removed, and this side of the API will not make any assumptions as to
    what a valid info level is for a given API.

Author:

    Dan Lafferty    (danl)  06-Feb-1991

Environment:

    User Mode - Win32

Revision History:

    06-Feb-1991     Danl
        Created
    12-Sep-1991 JohnRo
        Downlevel NetService APIs.
    06-Nov-1991 JohnRo
        RAID 4186: Fix assert in RxNetShareAdd and other MIPS problems.
        Use NetpRpcStatusToApiStatus, not NetpNtStatusToApiStatus.
        Make sure API name is in every debug message.
    08-Nov-1991 JohnRo
        RAID 4186: assert in RxNetShareAdd and other DLL stub problems.
    30-Mar-1992 JohnRo
        Extracted DanL's code from /nt/private project back to NET project.
    29-Apr-1992 JohnRo
        Use FORMAT_ equates where possible.
    08-May-1992 JohnRo
        Translate service names on the fly.
    14-May-1992 JohnRo
        winsvc.h and related file cleanup.
    06-Aug-1992 JohnRo
        RAID 3021: NetService APIs don't always translate svc names.
    09-Sep-1992 JohnRo
        RAID 1090: net start/stop "" causes assertion.
        Oops, NetServiceControl forgot to translate one more svc name.
    05-Nov-1992 JohnRo
        RAID 7780: Corrected error code for invalid level.
        Also fixed overactive assert in NetServiceEnum with no services.
        Also fixed rare memory leaks.

--*/

//
// INCLUDES
//

#define NOSERVICE       // Avoid <winsvc.h> vs. <lmsvc.h> conflicts.
#include <windows.h>    // DWORD, etc.

#include <lmcons.h>     // NET_API_STATUS
#include <rpcutil.h>    // NetRpc utils, GENERIC_ENUM_STRUC, etc.

#include <netdebug.h>   // Needed by NetRpc.h; FORMAT_ equates.
#include <lmapibuf.h>   // NetApiBufferAllocate(), etc.
#include <lmerr.h>      // NetError codes
#include <lmremutl.h>   // NetRemoteComputerSupports(), SUPPORTS_RPC
#include <lmsvc.h>
#include <rxsvc.h>      // RxNetService routines.

#include <netlib.h>     // NetpTranslateServiceName().
#include <svcdebug.h>   // SCC_LOG
#include <svcmap.h>     // MapService() routines.

//
// Globals
//
#ifdef SC_DEBUG
    DWORD   SvcctrlDebugLevel = DEBUG_ALL;
#else
    DWORD   SvcctrlDebugLevel = DEBUG_ERROR;
#endif


DBGSTATIC BOOL
MachineSupportsNt(
    IN LPWSTR UncServerName OPTIONAL
    );



NET_API_STATUS NET_API_FUNCTION
NetServiceControl (
    IN  LPCWSTR servername OPTIONAL,
    IN  LPCWSTR service,
    IN  DWORD   opcode,
    IN  DWORD   arg,
    OUT LPBYTE  *bufptr
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetServiceControl.

Arguments:

    servername - Pointer to a string containing the name of the computer
        that is to execute the API function.

    service - Pointer to a string containing the name of the service
        that is to receive the control request.

    opcode - The control request code.

    arg - An additional (user defined) code that will be passed to the
        service.

    bufptr - pointer to a location where the service status is to
        be returned.  If this pointer is invalid, it will be set to NULL
        upon return.

Return Value:

    The returned InfoStruct2 structure is valid as long as the returned
    error is NOT NERR_ServiceNotInstalled or NERR_ServiceBadServiceName.

    NERR_Success - The operation was successful.

    NERR_InternalError - LocalAlloc or TransactNamedPipe failed, or
        TransactNamedPipe returned fewer bytes than expected.

    NERR_ServiceNotInstalled - The service record was not found in the
        installed list.

    NERR_BadServiceName - The service name pointer was NULL.

    NERR_ServiceCtlTimeout - The service did not respond with a status
        message within the fixed timeout limit (RESPONSE_WAIT_TIMEOUT).

    NERR_ServiceKillProcess - The service process had to be killed because
        it wouldn't terminate when requested.

    NERR_ServiceNotCtrl - The service cannot accept control messages.
        The install state indicates that start-up or shut-down is pending.

    NERR_ServiceCtrlNotValid - The request is not valid for this service.
        For instance, a PAUSE request is not valid for a service that
        lists itself as NOT_PAUSABLE.

    ERROR_ACCESS_DENIED - This is a status response from the service
        security check.


--*/
{
    NET_API_STATUS          apiStatus;
    LPWSTR                  translatedServiceName;
    LPBYTE                  untranslatedBuffer = NULL;

    if (MachineSupportsNt( (LPWSTR) servername )) {

        apiStatus = NetpTranslateServiceName(
                (LPWSTR) service,   // untranslated.
                TRUE,               // yes, we want new style name
                & translatedServiceName );
        NetpAssert( apiStatus == NO_ERROR );

        apiStatus = MapServiceControl (
                (LPWSTR) servername,
                (LPWSTR) service,
                opcode,
                arg,
                & untranslatedBuffer);

    } else {

        apiStatus = NetpTranslateServiceName(
                (LPWSTR) service,  // untranslated.
                FALSE,             // no, we don't want new style name
                & translatedServiceName );
        NetpAssert( apiStatus == NO_ERROR );

        //
        // Call downlevel...
        //
        apiStatus = RxNetServiceControl(
                (LPWSTR) servername,
                translatedServiceName,
                opcode,
                arg,
                & untranslatedBuffer);

    }

    //
    // Translate service name in returned buffer.
    //
    if (apiStatus == NO_ERROR) {

        NetpAssert( untranslatedBuffer != NULL );
        apiStatus = NetpTranslateNamesInServiceArray(
                2,  // level 2 by definition
                untranslatedBuffer,
                1,     // only one entry
                TRUE,  // yes, caller wants new style names
                (LPVOID *) (LPVOID) bufptr);

    }

    if (untranslatedBuffer != NULL) {
        (VOID) NetApiBufferFree( untranslatedBuffer );
    }

    return(apiStatus);
}


NET_API_STATUS NET_API_FUNCTION
NetServiceEnum (
    IN  LPCWSTR     servername OPTIONAL,
    IN  DWORD       level,
    OUT LPBYTE      *bufptr,
    IN  DWORD       prefmaxlen,
    OUT LPDWORD     entriesread,
    OUT LPDWORD     totalentries,
    IN OUT LPDWORD  resume_handle OPTIONAL
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetSeviceEnum.

Arguments:

    servername - Pointer to a string containing the name of the computer
        that is to execute the API function.

    level - This indicates the level of information that is desired.

    bufptr - A pointer to the location where the pointer to the returned
        array of info structures is to be placed.

    prefmaxlen - Indicates a maximum size limit that the caller will allow
        for the return buffer.

    entriesread - A pointer to the location where the number of entries
        (data structures)read is to be returned.

    totalentries - A pointer to the location which upon return indicates
        the total number of entries in the "active" database.

    resumehandle - Pointer to a value that indicates where to resume
        enumerating data.

Return Value:

    Nerr_Success - The operation was successful.

    ERROR_MORE_DATA - Not all of the data in the active database could be
        returned.

    ERROR_INVALID_LEVEL - An illegal info Level was passed in.

Note:


--*/
{
    NET_API_STATUS          apiStatus;
    LPBYTE                  untranslatedBuffer = NULL;


    if (MachineSupportsNt( (LPWSTR) servername )) {

        apiStatus = MapServiceEnum (
                (LPWSTR) servername,
                level,
                & untranslatedBuffer,
                prefmaxlen,
                entriesread,
                totalentries,
                resume_handle);

    } else {

        //
        // Call downlevel...
        //
        apiStatus = RxNetServiceEnum(
                (LPWSTR) servername,
                level,
                & untranslatedBuffer,
                prefmaxlen,
                entriesread,
                totalentries,
                resume_handle);

    }

    //
    // Translate service names in returned buffer.
    //
    if ( (apiStatus == NO_ERROR) || (apiStatus == ERROR_MORE_DATA) ) {

        if ( (*entriesread) > 0 ) {    // One or more services returned.
            NetpAssert( untranslatedBuffer != NULL );
            NetpAssert( (*totalentries) > 0 );

            apiStatus = NetpTranslateNamesInServiceArray(
                    level,
                    untranslatedBuffer,
                    *entriesread,
                    TRUE,  // yes, caller wants new style names
                    (LPVOID *) (LPVOID) bufptr);

        } else {          // Zero services returned.
            NetpAssert( untranslatedBuffer == NULL );
            // Note: total entries may be > 0, if this is ERROR_MORE_DATA...
            *bufptr = NULL;
        }
    }

    if (untranslatedBuffer != NULL) {
        (VOID) NetApiBufferFree( untranslatedBuffer );
    }

    return(apiStatus);

} // NetServiceEnum



NET_API_STATUS NET_API_FUNCTION
NetServiceGetInfo (
    IN  LPCWSTR servername OPTIONAL,
    IN  LPCWSTR service,
    IN  DWORD   level,
    OUT LPBYTE  *bufptr
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetServiceGetInfo.

Arguments:

    servername - Pointer to a string containing the name of the computer
        that is to execute the API function.  Since this function is
        executing on that computer, this information is not useful
        by the time it gets here.  It is really only useful on the RPC
        client side.

    service - Pointer to a string containing the name of the service
        for which information is desired.

    level - This indicates the level of information that is desired.

    bufptr - Pointer to a Location where the pointer to the returned
        information structure is to be placed.

Return Value:

    NERR_Success - The operation was successful.

    NERR_ServiceNotInstalled - if the service record was not found in
        either the installed or uninstalled lists.

    NERR_BadServiceName - The service name pointer was NULL.

    ERROR_INVALID_LEVEL - An illegal info level was passed in.

    ERROR_NOT_ENOUGH_MEMORY - The memory allocation for the returned
        Info Record failed.

    other - Any error returned by the following base API:
                RPC Runtime API


--*/

{
    NET_API_STATUS          apiStatus;
    LPWSTR                  translatedServiceName;
    LPBYTE                  untranslatedBuffer = NULL;

    if (MachineSupportsNt( (LPWSTR) servername )) {

        apiStatus = NetpTranslateServiceName(
                (LPWSTR) service,  // untranslated.
                TRUE,              // yes, we want new style name
                & translatedServiceName );
        NetpAssert( apiStatus == NO_ERROR );

        apiStatus = MapServiceGetInfo (
                (LPWSTR) servername,
                (LPWSTR) service,
                level,
                & untranslatedBuffer);

    } else {

        apiStatus = NetpTranslateServiceName(
                (LPWSTR) service,  // untranslated.
                FALSE,             // no, we don't want new style name
                & translatedServiceName );
        NetpAssert( apiStatus == NO_ERROR );

        //
        // Call downlevel...
        //
        apiStatus = RxNetServiceGetInfo(
                (LPWSTR) servername,
                translatedServiceName,
                level,
                & untranslatedBuffer);

    }

    //
    // Translate service name in returned buffer.
    //
    if (apiStatus == NO_ERROR) {

        NetpAssert( untranslatedBuffer != NULL );
        apiStatus = NetpTranslateNamesInServiceArray(
                level,
                untranslatedBuffer,
                1,     // only one entry
                TRUE,  // yes, caller wants new style names
                (LPVOID *) (LPVOID) bufptr);

    }

    if (untranslatedBuffer != NULL) {
        (VOID) NetApiBufferFree( untranslatedBuffer );
    }

    return(apiStatus);
}


NET_API_STATUS NET_API_FUNCTION
NetServiceInstall (
    IN  LPCWSTR servername OPTIONAL,
    IN  LPCWSTR service,
    IN  DWORD   argc,
    IN  LPCWSTR argv[],
    OUT LPBYTE  *bufptr
    )
/*++

Routine Description:

    This is the DLL entrypoint for NetServiceInstall.

Arguments:

    servername - Points to a string containing the name of the computer
        that is to execute the API function.

    service- Points to a string containing the name of the service
        that is to be started.

    argc - Indicates the number or argument vectors in argv.

    argv - A pointer to an array of pointers to strings.  These
        are command line arguments that are to be passed to the service.

    bufptr - This is the address where a pointer to the service's
        information buffer (SERVICE_INFO_2) is to be placed.

Return Value:

    NERR_Success - The operation was successful

    NERR_InternalError - There is a bug in this program somewhere.

    NERR_ServiceInstalled - The service is already running - we do not
        yet allow multiple instances of the same service.

    NERR_CfgCompNotFound - The configuration component could not be found.
        The Image File could not be found for this service.

    NERR_ServiceTableFull - The maximum number of running services has
        already been reached.

    NERR_ServiceCtlTimeout - The service program did not respond to the
        start-up request within the timeout period.  If this was the
        only service in the service process, the service process was
        killed.

    ERROR_NOT_ENOUGH_MEMORY - If this error occurs early in the
        start-up procedure, the start-up will fail.  If it occurs at the
        end (allocating the return status buffer), the service will still
        be started and allowed to run.

    other - Any error returned by the following base API:
                CreateNamedPipe
                ConnectNamedPipe
                CreateProcess
                TransactNamedPipe
                RPC Runtime API


--*/
{
    NET_API_STATUS          apiStatus;
    LPWSTR                  translatedServiceName;
    LPBYTE                  untranslatedBuffer = NULL;


    if (MachineSupportsNt( (LPWSTR) servername )) {

        apiStatus = NetpTranslateServiceName(
                (LPWSTR) service,  // untranslated.
                TRUE,              // yes, we want new style name
                & translatedServiceName );
        NetpAssert( apiStatus == NO_ERROR );

        apiStatus = MapServiceInstall (
                (LPWSTR) servername,
                (LPWSTR) service,
                argc,
                (LPWSTR *) argv,
                & untranslatedBuffer);

    } else {

        apiStatus = NetpTranslateServiceName(
                (LPWSTR) service,  // untranslated.
                FALSE,             // no, we don't want new style name
                & translatedServiceName );
        NetpAssert( apiStatus == NO_ERROR );

        //
        // Call downlevel....
        //
        apiStatus = RxNetServiceInstall(
                (LPWSTR) servername,
                translatedServiceName,
                argc,
                (LPWSTR *) argv,
                & untranslatedBuffer);

    }

    //
    // Translate service name in returned buffer.
    //
    if (apiStatus == NO_ERROR) {

        NetpAssert( untranslatedBuffer != NULL );
        apiStatus = NetpTranslateNamesInServiceArray(
                2,  // level 2 by definition
                untranslatedBuffer,
                1,     // only one entry
                TRUE,  // yes, caller wants new style names
                (LPVOID *) (LPVOID) bufptr);

    }

    if (untranslatedBuffer != NULL) {
        (VOID) NetApiBufferFree( untranslatedBuffer );
    }

    return(apiStatus);
}


DBGSTATIC BOOL
MachineSupportsNt(
    IN LPWSTR UncServerName OPTIONAL
    )
{
    NET_API_STATUS ApiStatus;
    DWORD ActualSupports;

    ApiStatus = NetRemoteComputerSupports(
            UncServerName,
            SUPPORTS_RPC,                        // Set SUPPORT_ bits wanted.
            & ActualSupports );

    if (ApiStatus != NO_ERROR) {
        return (FALSE);   // Error; say it doesn't support NT, and someone else
                          // will set the correct error code.
    }
    if (ActualSupports & SUPPORTS_RPC) {
        return (TRUE);
    }
    return (FALSE);

} // MachineSupportsNt