root/usr/src/cmd/lp/model/lp.tell.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*        All Rights Reserved   */

#include "signal.h"
#include "stdio.h"
#include "errno.h"

#include "lp.h"
#include "msgs.h"
#include "string.h"

void                    startup(),
                        cleanup(),
                        done();

extern char             *getenv(),
                        *malloc(),
                        *realloc();

extern long             atol();

extern int              atoi();

static void             wakeup();
extern char    *optarg;
extern int     optind, opterr, optopt;
int optsw;

#define PREFIX_STRING "%%["
#define SUFFIX_STRING "]%%"
#define PRINTER_ERROR_STRING "PrinterError:"
#define STATUS_STRING "status:"
#define JOB_STRING "job:"
#define STATUS_OK_STRING "ready and printing"
#define PAPER_CHANGED_STRING "paper changed:"

/*
 * Some common postscript printer fault messages.
 * These strings are here so that they get l10ned and then lpstat will
 * be able to display them in the users language.
 * This seemed like a good place for them, since lp.tell knows about
 * postscript msgs.
 */

char *ps_m1 = "unable to print: out of media (paper)";
char *ps_m2 = "out of media (paper)";
char *ps_m3 = "unable to print: tray not (properly) installed";
char *ps_m4 = "tray not (properly) installed";
char *ps_m5 = "unable to print: paper out for the selected tray";
char *ps_m6 = "paper out for the selected tray";
char *ps_m7 = "unable to print: cartridge life expiring";
char *ps_m8 = "cartridge life expiring";
char *ps_m9 = "unable to print: printer cover not locked";
char *ps_m10 = "printer cover not locked";
char *ps_m11 = "unable to print: media (paper) jam in exit path";
char *ps_m12 = "media (paper) jam in exit path";
char *ps_m13 = "unable to print: media (paper) jam in feed path";
char *ps_m14 = "media (paper) jam in feed path";
char *ps_m15 = "unable to print: drum assembly almost expended";
char *ps_m16 = "drum assembly almost expended";
char *ps_m17 = "unable to print: toner cartridge almost expended";
char *ps_m18 = "toner cartridge almost expended";
char *ps_m19 = "unable to print: drum assembly not (properly) installed";
char *ps_m20 = "drum assembly not (properly) installed";
char *ps_m21 = "unable to print: toner cartridge not (properly) installed";
char *ps_m22 = "toner cartridge not (properly) installed";
char *ps_m23 = "unable to print: drum assembly requires replacement";
char *ps_m24 = "drum assembly requires replacement";
char *ps_m25 = "unable to print: toner cartridge requires replacement";
char *ps_m26 = "toner cartridge requires replacement";
char *ps_m27 = "unable to print: fuser warming up";
char *ps_m28 = "fuser warming up";
char *ps_m29 = "unable to print: printer not responding";
char *ps_m30 = "printer not responding";
char *ps_m31 = "unable to print: fuser pausing";
char *ps_m32 = "fuser pausing";
char *ps_m33 = "unable to print: printer turned off";
char *ps_m34 = "printer turned off";
char *ps_m35 = "unable to print: printer warming up";
char *ps_m36 = "printer warming up";
char *ps_m37 = "unable to print: interlock open";
char *ps_m38 = "interlock open";
char *ps_m39 = "unable to print: selected tray out";
char *ps_m40 = "selected tray out";
char *ps_m41 = "unable to print: paper out for the manual tray";
char *ps_m42 = "paper out for the manual tray";
char *ps_m43 = "unable to print: paper exit jam";
char *ps_m44 = "paper exit jam";
char *ps_m45 = "unable to print: paper misfeed jam";
char *ps_m46 = "paper misfeed jam";
char *ps_m47 = "unable to print: paper jam between registration & heat rollers";
char *ps_m48 = "paper jam between registration & heat rollers";
char *ps_m49 = "unable to print: paper jam at registration roller";
char *ps_m50 = "paper jam at registration roller";
char *ps_m51 = "unable to print: no cartridge";
char *ps_m52 = "no cartridge";
char *ps_m53 = "unable to print: cartridge out";
char *ps_m54 = "cartridge out";

/**
 ** main()
 **/

int
main(int argc, char *argv[])
{
        char                    *alert_text,
                                buf[BUFSIZ],
                                msgbuf[MSGMAX],
                                *bufPtr,
                                *printer,
                                *s_key;

        char *printerErrorString = NULL;
        char *statusString = NULL;
        char *paperChangedString = NULL;
        char *suffixString = NULL;
        char *jobString = NULL;
        char *prefixString = NULL;
        char *statusOkString = NULL;
        int                     mtype,
               doStdOut,
                                        doDebug,
                                        first,
                                oldalarm;


        short                   status;

        long                    key,clearKey;
        char *ptr1,*ptr2,*ptr3,*ptr4,*ptr5;
        int trayNum = 0;
        int mode = 0;
        int pagesPrinted = 0;
        char *paperType = NULL;
        short mesgRetType;
        int useLaserWriterMessages;
        int pLen,sLen,peLen,jLen,pcLen ;

        void                    (*oldsignal)();


        /*
         * Run immune from typical interruptions, so that
         * we stand a chance to get the fault message.
         * EOF (or startup error) is the only way out.
         */
        signal (SIGHUP, SIG_IGN);
        signal (SIGINT, SIG_IGN);
        signal (SIGQUIT, SIG_IGN);
        signal (SIGTERM, SIG_IGN);

        /*
         *  Do we have a key?
         */
        if (
                argc < 2
             || !(s_key = getenv("SPOOLER_KEY"))
             || !*s_key
             || (key = atol(s_key)) <= 0
        ) {
                printf( "Usage: lptell [-lodk] [-X String]  printer\n");
                printf("Options (where X is P,S,e,s, O or c )\n");
                printf("   environment variable SPOOLER_KEY: must be defined and > 0\n");
                printf("   printer: name of printer to give status for.\n");
                printf("   -l: expect laser writer type messages (NeWSprint does)\n");
                printf("   -o: send input to stdout\n");
                printf("   -d: send additional debugging output to stdout\n");
                printf("   -P String: string for prefix, default: '%%%%['\n");
                printf("   -S String: string for suffix, default: ']%%%%'\n");
                printf("   -e String: string to detect printer error,\n");
                printf("       default: 'PrinterError:', send S_SEND_FAULT to lpsched\n");
                printf(
                "   -c String: string to detect paper change in context of printer error,\n");
                printf("       default: 'paper changed:', send S_PAPER_CHANGED to lpsched\n");
                printf("   -s String: string to detect printer ok status, \n");
                printf("       default: 'status:', send S_CLEAR_FAULT to lpsched\n");
                printf("       -k: do not use the key for making status ok\n");
                printf("   -O String: string sent as status message to lpsched,\n");
                printf("                  default: 'ready and printing:'\n");
                exit (90);
                  }


        doStdOut = 0;
        doDebug = 0;
        useLaserWriterMessages = 0;
        clearKey = key;

        prefixString = PREFIX_STRING; pLen = strlen(prefixString);
        suffixString = SUFFIX_STRING;
        printerErrorString = PRINTER_ERROR_STRING;
          peLen = strlen(printerErrorString);
        statusString = STATUS_STRING; sLen = strlen(statusString);
        jobString = JOB_STRING; jLen = strlen(jobString);
        paperChangedString = PAPER_CHANGED_STRING;
          pcLen = strlen(paperChangedString);
        statusOkString = STATUS_OK_STRING;

   while ((optsw = getopt(argc, argv, "le:s:c:okdO:S:P:")) != EOF) {
                switch ( optsw ) {
                        case 'l':
                                useLaserWriterMessages = 1;
                                break;
                        case 'P':
                                prefixString = (optarg  ? strdup(optarg) : NULL);
                                pLen = strlen(prefixString );
                                break;
                        case 'S':
                                suffixString = (optarg  ? strdup(optarg) : NULL);
                                break;
                        case 'e':
                                printerErrorString = (optarg  ? strdup(optarg) : NULL);
                                peLen = strlen(printerErrorString);
                                break;
                        case 's':
                                statusString = (optarg  ? strdup(optarg) : NULL);
                                sLen = strlen(statusString);
                                break;
                        case 'O':
                                statusOkString = (optarg  ? strdup(optarg) : NULL);
                                break;
                        case 'c':
                                paperChangedString = (optarg  ? strdup(optarg) : NULL);
                                pcLen = strlen(paperChangedString );
                                break;
                        case 'k':
                                clearKey = -1;
                                break;
                        case 'o':
                                doStdOut = 1;
                                break;
                        case 'd':
                                doDebug = 1;
                                break;
                        }
                }
        /*
         * Which printer is this? Do we have a key?
         */
        if (
             !(printer = argv[optind])
             || !*printer
        ) {
                exit (90);
                  }
        if (doDebug) {
                printf( "start lp.tell for %s key %d mode %s %s\n",
                                printer,key,(useLaserWriterMessages ? "LW" : "standard"),
                                (doStdOut ? "doStdOut" : "no output"));
                printf( "prefix (%s) suffix (%s) printerError (%s)\n",
                          prefixString,suffixString,printerErrorString);
                printf( "paper_changed (%s) status (%s) key %d \n",
                        paperChangedString,statusString , clearKey);
                fflush(stdout);
           }
        /*
         * Wait for a message on the standard input. When a single line
         * comes in, take a couple of more seconds to get any other lines
         * that may be ready, then send them to the Spooler.
         */
        while (fgets(buf, BUFSIZ, stdin)) {
                if (useLaserWriterMessages) {
                        /* NeWSprint style processing (which simulates the LaserWriter
                         *There are four types of messages:
                         *      1) fault messages: printer error message from handler
                         *      2) clear fault messages: printer ok messages from handler
                         *      3) paper changed messages: printer handler detected paper change
                         *      4) server messages: xnews problems
                         */
                        bufPtr = buf;
                        if (strncmp(prefixString, bufPtr, pLen) == 0) {
                                bufPtr += pLen;
                                while (*bufPtr == ' ')
                                        bufPtr++;

                                if (strncmp(printerErrorString, bufPtr,
                                                peLen) == 0) {
                                        bufPtr += peLen;
                                        while (*bufPtr == ' ')
                                                bufPtr++;

                                        if ((strncmp(bufPtr,paperChangedString,pcLen) == 0) &&
                                                         (ptr1 = bufPtr +pcLen) &&
                                                         (ptr2 = strchr(ptr1+1,':')) &&
                                                         (ptr3 = strchr(ptr2+1,':')) &&
                                                         (ptr4 = strchr(ptr3+1,':')) &&
                                                         (ptr5 = strchr(ptr4+1,'\n'))) {
                                                if (doStdOut) printf("%s",buf);
                                                *ptr2 =0;
                                                *ptr3= 0;
                                                *ptr4= 0;
                                                *ptr5= 0;
                                                trayNum = atoi(ptr1+1);
                                                paperType = ptr2+1;
                                                mode = atoi(ptr3+1);
                                                pagesPrinted = atoi(ptr4+1);
                                                if (doDebug) {
                                                         printf("Paper changed: %s tray %d paper %s md %d pages %d\n",
                                                                printer,trayNum,paperType,mode,pagesPrinted);
                                                        }
                                                startup ();
                                                mesgRetType = R_PAPER_CHANGED;
                                                (void)putmessage ( msgbuf, S_PAPER_CHANGED, printer, trayNum,
                                                        paperType, mode, pagesPrinted);
                                        } else {
                                                if (doStdOut)  printf("%s",buf);
                                                if (ptr1 = strstr(bufPtr,suffixString))  *ptr1 = 0;
                                                if ( doDebug ) {
                                                        printf("Send fault: %s key %d (%s)\n",printer,key,bufPtr);
                                                }
                                                mesgRetType = R_SEND_FAULT;
                                                startup ();
                                                (void)putmessage (msgbuf,S_SEND_FAULT,printer,key,bufPtr);
                                        }
                                } else if ((first = (strncmp(statusString,bufPtr,sLen) == 0)) ||
                                                         (strncmp(jobString,bufPtr,jLen) == 0)) {
                                        bufPtr += (first ? sLen : jLen);
                                        if (doStdOut)  printf("%s",buf);
                                        if (ptr1 = strstr(bufPtr,suffixString))  *ptr1 = 0;
                                        if ( doDebug ) {
                                                printf("Clear fault: %s key %d (%s)\n",printer, clearKey,
                                                          bufPtr);
                                                }
                                        mesgRetType = R_CLEAR_FAULT;
                                        startup ();
                                        (void)putmessage( msgbuf,S_CLEAR_FAULT,printer,clearKey,
                                                statusOkString);
                                } else {
                                        if (doStdOut)  printf("%s",buf);
                                        if (ptr1 = strstr(bufPtr,suffixString))  *ptr1 = 0;
                                        if ( doDebug ) {
                                                printf("Server error: %s key %d (%s)\n",printer,key,
                                                         buf);
                                        }
                                        mesgRetType = 0;
                                }
                        } else {
                                if (doStdOut) printf("%s",buf);
                                if (ptr1 = strstr(bufPtr,suffixString))
                                        *ptr1 = 0;
                                if (doDebug) {
                                        printf("Server error: %s key %d (%s)\n",
                                                printer, key, buf);
                                }
                                mesgRetType = 0;
                        }
                } else {        /* not generic PostScript style messages */
                        oldsignal = signal(SIGALRM, wakeup);
                        oldalarm = alarm(2);

                        alert_text = 0;
                        do {
                                if (alert_text)
                                        alert_text = realloc(alert_text,
                                                strlen(alert_text)+strlen(buf)+1
                                        );
                                else {
                                        alert_text = malloc(strlen(buf) + 1);
                                        alert_text[0] = 0;
                                }
                                strcat (alert_text, buf);

                        } while (fgets(buf, BUFSIZ, stdin));

                        alarm (oldalarm);
                        signal (SIGALRM, oldsignal);

                        if (doStdOut) {
                                if ( doDebug ) {
                                        printf("Send generic fault: %s key %d (%s)\n",printer,key,
                                                alert_text);
                                        }
                                else {
                                        printf("%s\n",alert_text);
                                        }
                                }
                        if (strcmp(alert_text, "printer ok\n") == 0) {
                                mesgRetType = R_CLEAR_FAULT;
                                startup ();
                                (void)putmessage(msgbuf, S_CLEAR_FAULT, printer,
                                                clearKey, statusOkString);
                        } else {
                                mesgRetType = R_SEND_FAULT;
                                startup ();
                                (void)putmessage(msgbuf, S_SEND_FAULT, printer,
                                                key, alert_text);
                        }
                }

                if (mesgRetType) {
                        if (msend(msgbuf) == -1)
                                done (91);
                        if (mrecv(msgbuf, sizeof(msgbuf)) == -1)
                                done (92);
                        mtype = getmessage(msgbuf, mesgRetType, &status);
                        /*
                         * check for R_CLEAR_FAULT here and 3 lines below
                         * because older lpsched doesn't pass S_CLEAR_FAULT
                         */
                        if ((mtype != mesgRetType) &&
                            (mesgRetType != R_CLEAR_FAULT))
                                done (93);

                        if ((status != MOK) && (mesgRetType != R_CLEAR_FAULT))
                                done (94);
                }

        }
        done (0);

        return (0);
}

/**
 ** startup() - OPEN MESSAGE QUEUE TO SPOOLER
 ** cleanup() - CLOSE THE MESSAGE QUEUE TO THE SPOOLER
 **/

static int              have_contacted_spooler  = 0;

void                    startup ()
{
        void                    catch();

        /*
         * Open a message queue to the Spooler.
         * An error is deadly.
         */
        if (!have_contacted_spooler) {
                if (mopen() == -1) {

                        switch (errno) {
                        case ENOMEM:
                        case ENOSPC:
                                break;
                        default:
                                break;
                        }

                        exit (1);
                }
                have_contacted_spooler = 1;
        }
        return;
}

void                    cleanup ()
{
        if (have_contacted_spooler)
                mclose ();
        return;
}

/**
 ** wakeup() - TRAP ALARM
 **/

static void             wakeup ()
{
        return;
}

/**
 ** done() - CLEANUP AND EXIT
 **/

void                    done (ec)
        int                     ec;
{
        cleanup ();
        exit (ec);
}