/* Network Respooler v 1.0 by Phillip G. Austin The following is a Lan Manager service designed to allow redirection of print jobs to network printers from any system that does not support network connection but that will print to a serial device. The service can run on either a server or an OS/2 workstation, and the number of redirected print devices is limited only by the machine/network capacity through the use of multiple threads. See the file "respool.txt" for installation instructions. Version 1.0 of this software is released as shareware and may be freely used and distributed, however commercial sale of this software or distribution where fees are charged is prohibited unless consent of the author is first obtained. This software has no warranty of any kind, is distributed as-is, and usage is at the risk of the user. This program requires Microsoft C 6.0 and the Lan Manager PTK in order to compile. The file "makeit.bat" illustrates the required compiler and linker command lines. */ #define INCL_KBD #define INCL_VIO #define INCL_DOSSIGNALS #define INCL_DOSPROCESS #define INCL_DOSSEMAPHORES #define INCL_DOSFILEMGR #define INCL_DOSERRORS #define INCL_DOSDEVIOCTL #include // MS OS/2 header files #define INCL_NETSERVICE #define INCL_NETALERT #define INCL_NETERRORS #define INCL_NETERRORLOG #include // LAN Manager header files #include #include #include #include #define DEFAULT_STACK_SIZE (8152+512) // 8K thread stack size #define MYSERVICENAME "RESPOOLER" #define DEFAULTCOMPONENTNAME "RESPOOLER" #define MSGERRCNTRLCSIGNAL "Error in Setting CTL-C Handler %d" #define MSGERRCNTRLBSIGNAL "Error in Setting CTL-BREAK Handler %d" #define MSGERRKILLPSIGNAL "Error in Setting KILLPROCESS Handler %d" #define MSGERRFLAGASIGNAL "Error in Setting Flag A Handler %d" #define MSGERRFLAGBSIGNAL "Error in Setting Flag B Handler %d" #define MSGERRFLAGCSIGNAL "Error in Setting Flag C Handler %d" #define MSGERRSRVSSIGNAL "Error in Setting Service Handler %d" #define MSGERRDOSSEMSET "Error in DosSemSet %d" #define MSGERRDOSSEMWAIT "Error in DosSemWait %d" #define MSGERRBEGINTHREAD "Error in Spawning Thread tid: %d" #define MSGERRSEMWAIT "Error in DosSemWait %d" #define MSGERRGETPID "Error in DosGetPID %d" #define MSGERRDOSOPEN "Error in DosOpen %d" #define MSGERRCREATESEM "Error Creating System Semaphore %d" PFNSIGHANDLER pnPrevHandler; // Previous handle address unsigned short pfPrevAction; // Previous action flag int fSigStarted; // Flag: TRUE if signal handler started struct service_status ssStatus; // Service status structure PBYTE pbStack; // Address of top of stack TID tThreadTid; // TID of Alert handler thread // Application Semaphores unsigned long hSemThreadInit; // RAM semaphore to pause main during thread init unsigned long hSemPause = 0; // RAM semaphore for pausing respooler HSEM hSemThreadBlock; // System Semaphore to block main until threads complete char szErrBuffer[80]; // Buffer used to report errors int Shutdown = FALSE; // Service shutdown flag // Function declarations void far Respool(char *arg); void ErrOut(unsigned short, char far *); void far pascal SignalHandler(unsigned short, unsigned short); void far PopOneUp(char far *); unsigned extern far pascal DosDevIOCtl(void far *, void far *, unsigned, unsigned, unsigned); void main(int argc, char *argv[]) { int iCount; // Index for parsing argv unsigned uReturnCode; // Return code char szErrBuffer[80]; // Buffer used to report errors unsigned short usAction; // Action taken by DosOpen PIDINFO pidInfo; // Get PID of main process HFILE hFileNul; // Handle for NUL device HFILE hNewHandle; // Temporary handle fSigStarted = FALSE; // Signal handler not started // Set up service status for param error ssStatus.svcs_status = SERVICE_UNINSTALLED | SERVICE_UNINSTALLABLE; ssStatus.svcs_code = SERVICE_UIC_M_ERRLOG; // Look at error log /* * Get the process ID of the main process to be * used in subsequest NetServiceStatus calls. */ uReturnCode = DosGetPID( &pidInfo); if (uReturnCode != NERR_Success) { sprintf(szErrBuffer, MSGERRGETPID, uReturnCode); ErrOut(uReturnCode, szErrBuffer); } ssStatus.svcs_pid = pidInfo.pid; /* * Notify LAN Manager that the service is installing. At this time, * the service cannot be paused or uninstalled because signal handler * has not been installed. */ ssStatus.svcs_status = SERVICE_INSTALL_PENDING | SERVICE_NOT_UNINSTALLABLE | SERVICE_NOT_PAUSABLE; ssStatus.svcs_code = SERVICE_UIC_NORMAL; uReturnCode = NetServiceStatus((char far *) &ssStatus, sizeof(ssStatus)); /* * Install the signal handler. Ignore CTRL-C, BREAK, and KILL * signals and let LAN Manager take care of them. The signal * handler will be raised when SERVICE_RCV_SIG_FLAG is raised * (SERVICE_RCV_SIG_FLAG is the same as SIG_PFLG_A). */ uReturnCode = DosSetSigHandler(SignalHandler, &pnPrevHandler, &pfPrevAction, SIGA_ACCEPT, SERVICE_RCV_SIG_FLAG); if (uReturnCode != NERR_Success) { sprintf(szErrBuffer, MSGERRCNTRLCSIGNAL, uReturnCode); ErrOut(uReturnCode, szErrBuffer); } // PopOneUp("Signal Handler Installed"); /* * Signal handler has been installed. Service can now be * paused or uninstalled. */ fSigStarted = TRUE; ssStatus.svcs_status = SERVICE_INSTALL_PENDING | SERVICE_UNINSTALLABLE | SERVICE_PAUSABLE; ssStatus.svcs_code = SERVICE_UIC_NORMAL; uReturnCode = NetServiceStatus((char far *)&ssStatus, sizeof(ssStatus)); uReturnCode = DosSetSigHandler(NULL, // Ignore CTRL-C &pnPrevHandler, &pfPrevAction, SIGA_IGNORE, SIG_CTRLC); if (uReturnCode != NERR_Success) { sprintf(szErrBuffer, MSGERRCNTRLCSIGNAL, uReturnCode); ErrOut(uReturnCode, szErrBuffer); } uReturnCode = DosSetSigHandler(NULL, // Ignore BREAK signal &pnPrevHandler, &pfPrevAction, SIGA_IGNORE, SIG_CTRLBREAK); if (uReturnCode != NERR_Success) { sprintf(szErrBuffer, MSGERRCNTRLBSIGNAL, uReturnCode); ErrOut(uReturnCode, szErrBuffer); } uReturnCode = DosSetSigHandler(NULL, // Ignore KILL signal &pnPrevHandler, &pfPrevAction, SIGA_IGNORE, SIG_KILLPROCESS); if (uReturnCode != NERR_Success) { sprintf(szErrBuffer, MSGERRKILLPSIGNAL, uReturnCode); ErrOut(uReturnCode, szErrBuffer); } uReturnCode = DosSetSigHandler(NULL, // Flag B should cause error &pnPrevHandler, &pfPrevAction, SIGA_ERROR, SIG_PFLG_B); if (uReturnCode != NERR_Success) { sprintf(szErrBuffer, MSGERRFLAGBSIGNAL, uReturnCode); ErrOut(uReturnCode, szErrBuffer); } uReturnCode = DosSetSigHandler(NULL, // Flag C should cause error &pnPrevHandler, &pfPrevAction, SIGA_ERROR, SIG_PFLG_C); if (uReturnCode != NERR_Success) { sprintf(szErrBuffer, MSGERRFLAGCSIGNAL, uReturnCode); ErrOut(uReturnCode, szErrBuffer); } /* * Close stdin, stdout, and stderr handles so that * they cannot be used from this service. Redirect stdin, stdout, * stderr to NULL device. */ // Open the null device uReturnCode = DosOpen("NUL", // Name of device &hFileNul, // Handle &usAction, // Action taken 0L, // File size FILE_NORMAL, // File attribute FILE_OPEN, // Open action // Open mode OPEN_ACCESS_READWRITE|OPEN_SHARE_DENYNONE, 0L); // Reserved if (uReturnCode != NERR_Success) { sprintf(szErrBuffer, MSGERRDOSOPEN, uReturnCode); ErrOut(uReturnCode, szErrBuffer); } // Reroute stdin, stderr, and stderr to NUL for (hNewHandle=0; hNewHandle < 3; hNewHandle++) { // Skip the duplicating operation if the // handle already points to NUL if (hFileNul != hNewHandle) DosDupHandle(hFileNul, &hNewHandle); } // Extra handle to NUL not needed anymore if (hFileNul > 2) DosClose(hFileNul); /* Create ThreadBlock system semaphore which is recursively owned by * each thread. */ uReturnCode = DosCreateSem(0,&hSemThreadBlock,"\\SEM\\RESPOOL\\TBLOCK.SEM"); if (uReturnCode != 0) { sprintf(szErrBuffer, MSGERRCREATESEM, uReturnCode); ErrOut(0, szErrBuffer); } // Notify LAN Manager that the service is installed and active. ssStatus.svcs_status = SERVICE_INSTALLED | SERVICE_ACTIVE | SERVICE_UNINSTALLABLE| SERVICE_PAUSABLE; ssStatus.svcs_code = SERVICE_UIC_NORMAL; uReturnCode = NetServiceStatus((char far *) &ssStatus, sizeof(ssStatus)); /* * Parse the service parameters passed by LAN Manager. * The parameters passed in argv are from the LANMAN.INI * [Respooler] section and follow the convention: * * Input Port=Printer Name,Timeout (seconds) * * Start a thread to service each port listed. */ for (iCount = 1; iCount < argc; iCount++) { DosSemSet(&hSemThreadInit); tThreadTid = _beginthread(Respool, NULL, DEFAULT_STACK_SIZE, argv[iCount]); if(tThreadTid == -1) { sprintf(szErrBuffer, MSGERRBEGINTHREAD,tThreadTid); PopOneUp(szErrBuffer); ErrOut(0, szErrBuffer); } else { DosSemWait(&hSemThreadInit,SEM_INDEFINITE_WAIT); } } /* * The main thread waits on the system semaphore, but it is * interrupted whenever a signal comes. Loop over DosSemWait * if interrupted by a signal. */ do { uReturnCode = DosSemWait(hSemThreadBlock, SEM_INDEFINITE_WAIT); } while (uReturnCode == ERROR_INTERRUPT); DosCloseSem(hSemThreadBlock); //Notify LAN Manager that the service will uninstall. ssStatus.svcs_status = SERVICE_UNINSTALL_PENDING | SERVICE_UNINSTALLABLE | SERVICE_NOT_PAUSABLE; ssStatus.svcs_code = SERVICE_UIC_NORMAL; uReturnCode = NetServiceStatus((char far *) &ssStatus, sizeof(ssStatus)); // Notify LAN Manager that the service is uninstalled. ssStatus.svcs_status = SERVICE_UNINSTALLED | SERVICE_UNINSTALLABLE | SERVICE_NOT_PAUSABLE; ssStatus.svcs_code = SERVICE_UIC_NORMAL; NetServiceStatus((char far *) &ssStatus, sizeof(ssStatus)); return; } //======================================================================= // ErrOut // // This routine is called whenever an error that needs to // be reported occurs. It writes a text message in the error log // and then calls the ExitHandler routine to stop the service. //======================================================================= void ErrOut(unsigned short usErrCode, char far * psErrStr) { NetErrorLogWrite(NULL, // Reserved; must be NULL usErrCode, // Error code MYSERVICENAME, // Component name NULL, // Pointer to raw data buffer 0, // Length of raw data buffer psErrStr, // String data 1, // Number of error strings NULL); // Reserved; Must be NULL /* * If signal handler IS installed, notify LAN Manager * that the service is exiting. */ DosExit(EXIT_PROCESS, 0); } //======================================================================= // SignalHandler // // The signal handling routine gets control when the system sends a // status-changing request to the service. The routine should always // expect these arguments: // usSigArg = Opcode that describes the request // usSigNum = Number that identifies the process flag // the signal was raised on //======================================================================= void far pascal SignalHandler(unsigned short usSigArg, unsigned short usSigNum) { unsigned uReturnCode; unsigned char fOpCode; // Compute the function code fOpCode = (unsigned char) (usSigArg & 0xFF); switch (fOpCode) // Do the function { case SERVICE_CTRL_UNINSTALL: // Uninstall; end the service Shutdown = TRUE; return; break; case SERVICE_CTRL_PAUSE: // Pause service DosSemSet(&hSemPause); // Pause AlertHandler ssStatus.svcs_status = SERVICE_INSTALLED | SERVICE_PAUSED | SERVICE_UNINSTALLABLE| SERVICE_PAUSABLE; ssStatus.svcs_code = SERVICE_UIC_NORMAL; uReturnCode = NetServiceStatus((char far *) &ssStatus, sizeof(ssStatus)); break; case SERVICE_CTRL_CONTINUE: // Continue service DosSemClear(&hSemPause); // Continue AlertHandler ssStatus.svcs_status = SERVICE_INSTALLED | SERVICE_ACTIVE | SERVICE_UNINSTALLABLE| SERVICE_PAUSABLE; ssStatus.svcs_code = SERVICE_UIC_NORMAL; uReturnCode = NetServiceStatus((char far *) &ssStatus, sizeof(ssStatus)); break; case SERVICE_CTRL_INTERROGATE: // Give service status default: uReturnCode = NetServiceStatus((char far *) &ssStatus, sizeof(ssStatus)); break; } /* * Reenable signal handling. This signal handler accepts the next * signal raised on usSigNum. */ uReturnCode = DosSetSigHandler(0, 0, 0, SIGA_ACKNOWLEDGE, usSigNum); return; } void far Respool(char *Parms) { char CommBuffer[1024]; USHORT BytesIn; USHORT BytesOut; char Port[6]; char PrintDev[30]; char cReadTimeout[20]; unsigned hCommPort; unsigned hPrinterPort; unsigned Action; int PrinterOpen = FALSE; DCBINFO PortDCB; // Set ThreadBlock Semaphore DosSemRequest( hSemThreadBlock, 0); // Parse Parms _fstrcpy(Port,_fstrtok(Parms,"=")); _fstrcpy(PrintDev,_fstrtok(NULL,",")); _fstrcpy(cReadTimeout,_fstrtok(NULL,",")); // Open and initialize ports if(DosOpen(Port,&hCommPort,&Action,0L,0,0x01,0x12, 0L)) PopOneUp("Can't Open Port"); DosDevIOCtl(&PortDCB,NULL,0x73,1,hCommPort); PortDCB.usReadTimeout = (USHORT) atoi(cReadTimeout)*100; DosDevIOCtl(NULL,&PortDCB,0x53,1,hCommPort); // Clear ThreadInit Semaphore DosSemClear( &hSemThreadInit ); // loop until Shutdown = TRUE while ( Shutdown == FALSE ) { DosSemWait( &hSemPause, -1 ); DosRead(hCommPort, CommBuffer,1024,&BytesIn); if (BytesIn > 0) { if ( PrinterOpen != TRUE ) { DosOpen(PrintDev,&hPrinterPort,&Action,0L,0,0x01,0x41,0); PrinterOpen = TRUE; } DosWrite(hPrinterPort,CommBuffer,BytesIn,&BytesOut); } else { if (PrinterOpen == TRUE) { DosClose(hPrinterPort); PrinterOpen = FALSE; } } // DosSleep(10000); // DosSemWait( &hSemPause, -1 ); // PopOneUp(PopBuffer); } // Close ports and clean up DosClose(hCommPort); if (PrinterOpen == TRUE) DosClose(hPrinterPort); // Clear ThreadBlock Semaphore DosSemClear( hSemThreadBlock ); return; } void far PopOneUp(char *message) { unsigned short fPopUp = VP_WAIT | VP_OPAQUE; unsigned char bAttr = 0x17; char sVioString[80]; KBDKEYINFO kbci; VioPopUp(&fPopUp, 0); VioWrtNAttr(&bAttr, 25 *80,0,0,0); sprintf(sVioString,"%s", message); VioWrtCharStrAtt(sVioString, strlen(sVioString),5,5,&bAttr,0); KbdCharIn(&kbci, IO_WAIT,0); VioEndPopUp(0); return; }