Basic AIX Printer Backend Program Example


Contents

About this document
    Related documentation
Goal and overview
Include files
Getting command line arguments
ENQ flags - libqb routines
Requeueing the data
Logging the information
Creating the queue
Example program

About this document

This document describes how to write a simple AIX print backend routine that calls the libq library routines to obtain job and queue information from the AIX qdaemon. The backend supplied allows you to create your own custom AIX backends. The functions supplied in this sample backend are flag handling, obtaining queue information, logging information, and requeing to a new AIX queue.

These instructions apply to all levels of AIX.

Related documentation

Printer Backend Overview for Programming
AIX Guide to Printers and Printing SC23-2783-01

Goal and overview

This backend was written as a starting point for other backends. The backend receives print jobs, logs information about the job, and requeues the job to another queue. A few of the qprt flags are supported as a starting example.


Include files

You must include at least:

You can also include the following:

/* Example include statements                                  */
	#include <stdio.h>
	#include <IN/backend.h>
	#include <IN/standard.h>
	#include <sys/types.h>
	#include <sys/stat.h>
	#include <string.h>
	#include <errno.h>
	#include <signal.h>
	#include <fcntl.h>
	#include <stdlib.h>

Getting command line arguments

This program is designed to expect the first argument passed to be the new queue to which the program will requeue the data. This queue will be passed to the program from the /etc/qconfig line that specifies the backend. For example:

	backend = /usr/local/bin/baseback newqueue

The queue daemon passes other flags to AIX as command line arguments. Flags that are specific to enq are received with special libqb backend function calls.

Because there will be command line arguments, use the main function with argc and argv variables common to C programmers. This should be nothing new. For example:

main(argc,argv)
unsigned argc;
char **argv;
{
/* Pluck off the command line the new queue name               */
/* The /etc/qconfig line should be                             */
/* backend = /full_path/baseback new_queue                     */
    strcpy(newq,argv[1]);

The variable declarations are left out. See the Example Program for details.

Next, call a subroutine to parse a few of the other non-enq flags for an example.

/* Parse the print command arguments                            */
  flgarg = ++argv;	/* I need this to get rid of queue name */
/* Call routine flag_opts to parse arguments                    */
  cmrc = flag_opts(argc-1, flgarg);
...
/* Excerpt from the flag_opts routine				*/
/* This routine is designed to take the command line arguments
   and turn them into flags for the enq print command           */
/* Current version really only supports the -p qprt flag and    */
/* the -z qprt flag, but the general structure applies          */
/* This routine builds the enq output flags that will be	*/
/* needed when enq'ing to the next queue.			*/
int flag_opts(argc, argv)
int     argc;
char    **argv;
{
int  c,i;
char tflags[40];
int     ndex;
        strcpy(flagout,"");
        while ((c = getopt(argc, argv,
                "A:p:z:")
               ) !=EOF)
        {
                switch (c)
                {
                  case 'A':
                        printf("debug opt");
                        break;
                  case 'p':
                        sprintf(tflags," -o -p -o %s",optarg);
                        strcat(flagout,tflags);
                        break;
                  case 'z':
                        sprintf(tflags," -o -z -o %s",optarg);
                        strcat(flagout,tflags);
                  default:
                        ndex = optind;
   printf ("unknown opt. ch:%c opt:%s ind:%d\n",c,optarg,optind);
                        break;
                }
        }
        strcpy(filelst,"");            /* Blank out file names */
        filecnt = 0;
/* Now generate a list of files since we fell through getopt	*/
/* The rest of the arguments should be files			*/
        for (i=optind; i<argc; i++)
        {
                filecnt++;
                strcat(filelst," ");
                strcat(filelst,argv[i]);
                if (filecnt == 1)
                        pfile = argv[i];
        }
}	
/* End of function flag_opts	                               */

The printf statements are for debug purposes, and will write to the file specified in the /etc/qconfig line. The file must exist before using the queue. touch /dev/lpx. In /etc/qconfig:


ENQ flags - libqb routines

The flags that are valid for enq (man enq) do not show up as command line arguments, but are available as functions. For more details, see info - queue library backend routines. To compile with these routines requires that you link with -lqb. This provides all the information that you would need to write your own header page routines. They can also be used for flags needed when requeuing the jobs to the new queue.

If the code is to be tested from the command line (without a queue), you may want to call a routine to find if this is a backend. The routine that is useful is get_backend(). Example:

The lib backend routines are illustrated by example. Remember that you can use printf to write these to stdout, and they will go to the file = /dev/lpx file for debugging. You should recognize most of these without explanation as either values from man enq, or from the header page information.

/* See if this is being run as a backend program                */
  if (isbackend)
  {
/*  Some of the standard backend subroutine calls are           */
/*  used here.  These require link with libqb (-lqb)            */
/*  during the compile of this program.                         */
    qdate     = get_qdate();	        /* When job was queued	*/
    to        = get_to();	        /* Output goes to whom	*/
    strcpy(touser,to);		        /* This contains user	*/
				        /* name and system	*/
    strcpy(tosys,"");
/* It is useful to have the system of the user to send 	        */
/* mail about job completion or error messages		        */
    to1 = strchr(touser,'@');
    if (to1 != 0)
    {
      to1 = strtok(touser,"@");
      acnt = strlen(touser);
      sysuser = to + acnt + 1;
      strcpy (tosys,sysuser);
    }
/* enq -Ttitle option 					*/
    title     = get_title();	/* Usually the print file name	*/
    qname     = get_queue_name();	/* This queues name	*/
    qdname    = get_device_name();	/* queue device name	*/
    from      = get_from();		/* Submitting user	*/
/* If you don't want those messages to the screen you can	*/
/* resubmit the new job as mail only.  This tells how this	*/
/* job was submitted.  enq -C option used?		*/
    mail_only = get_mail_only();	/* Mail only or not	*/
/* Job number could be useful for logging			*/
    jobnum    = get_job_number();
/* This will give /etc/qconfig header =, unless -Bxx flag used	*/
    heder     = get_header();
    if (heder == 0)
        strcpy(hedstr,"nn");
    else
        strcpy(hedstr,"an");
/*  This cmdline actually contains hostname and user who
    sent the job.  You should parse this                        */
    cmdline   = get_cmd_line();
   }    /* End of backend only routines                         */
/* Just a little code so this will run without a queue	        */
   else /* When run from the command line for testing           */
   {
        strcpy(hedstr,"nn");
        strcpy(ctitle, pfile);
        strcpy(cfrom,"only_you");
        printf("hed: %s %s %s\n",hedstr, ctitle, cfrom);
   }

Requeueing the data

This job requeues the data, but it could actually submit the job to any other program to run.

From the main routine, call the do_data subroutine as follows:

The subroutine to send the data is as follows:

/* The do_data routine creates the command string that is sent  */
/* to the shell with the pipeit routine.  Some basic ENQ flags  */
/* are supported in this section from the libqb input           */
int do_data (dtype)
  int dtype;
{
  int w, land, port, irc;
  char tmptst[300];
  char *str1;
  strcpy(queue,newq);
  if (dtype)
/* from a queue use */
/* The basic enq flags used are -T Title, -Bnn Header, -Z touser*/
/* The qprt flags -z and -p are sent with flagout		*/
/* The job names are sent with filelst				*/
  sprintf(cmd,"enq -B%2s -c -P%s -T%s -Z%s %s %s\n",hedstr,queue,
        title, from, flagout, filelst);
  else
/* If not a backend program then use */
  sprintf(cmd,"enq -B%2s -c -P%s -T%s -Z%s %s %s\n",hedstr,queue,
        ctitle, cfrom, flagout, filelst);
/* Pipeit routine actually forks and execs the print job	*/
  irc = pipeit(cmd);
  if (irc != 0) errcode = errcode + 2;
}
/* Finally this routine actually forks a process to run the     */
/* print command.  If you uncomment the printf line, it will    */
/* also send the command line to the file in 'file = ' of       */
/* /etc/qconfig for debugging purposes                          */
char    trapit[20]="trap \"\" 15;\n";
int
pipeit(s)
char   *s;
{
   int prc, pid, w;
   int loopture;
   void (*istat)(), (*qstat)();
   char outstr[300];
   strcpy(outstr,trapit);
   strcat(outstr,s);
/* Uncomment this for debugging
   printf("sending to queue with\n %s\n",outstr);
*/
   if((pid = fork()) == 0)
   {
       setpgrp();       /* set group id */
       execl("/bin/ksh", "ksh", "-c", outstr, 0);
/*
       ERREXIT(0, MSG_EXECFAIL, NULL, NULL, errno);
*/
   }
   sleep(5);    /* Let the files get copied                     */
}

Logging the information

The next step is to show how to add a simple queue logging routine that will log all activity through this queue.

An environment variable is used to pass the log file to the backend. This could just as easily be an argument passed on the backend = line of /etc/qconfig, but this illustrates another method of passing data.

The environment variable can be set up in /etc/environment, or in a users .profile, or simply from the command line with:

The program only logs data if the AQLOG environment variable is set. The following code shows how logging is implemented in a simple case. The file size is logged, as opposed to counting pages or lines.

/*This gives us the capability to log files to user defined file */
/*If the user defines a value for AQLOG, then logging will occur */
  aqlog = getenv("AQLOG");      /* Get user log file             */
 if (strlen(aqlog) == 0)
        log = 0;
 else
  {
        log = 1;
        strcpy(aqlogf,aqlog);
  }
        if (log == 1) /* If logging turned on, write to file     */
        {
/* This statement is to get the file size of the file            */
          lstat(pfile, &attrs);
/* Open the log file based on the name passed in AQLOG	 	 */
/* This open should append to the file, not overwrite		 */
          fd = fopen(aqlog,"a");
/* Treat this different if not acting as a backend		 */
          if (isbackend)
          {
          fprintf(fd,"%3d, %20s, %7s, %7s,"
                ,jobnum,qdate,qname,queue);
          fprintf(fd," %12s,",to);
          fprintf(fd," %6d ",attrs.st_size);
          if (dt == 3)
            fprintf(fd,", %s, %d\n",title,heder);
          if (errcode != 0)
            fprintf(fd,",  ERROR = %d\n",errcode);
          }
          else
            fprintf(fd,"queuename: %s file: %s size: %6d\n",newq,
                pfile,attrs.st_size);
/* Close the file						 */
          fclose(fd);

Creating the queue

The following smit page is from AIX 4, but a similar screen appears at AIX 3.2 when you are adding a queue with smitty spooler.

  1. touch /dev/testl
  2. smitty mkpq
  3. Choose:
    other       User Defined Backend
  4. From the "Add a print queue" screen, add the following and press Enter or DO.
* Name of QUEUE to add                               [testq]
* Name of QUEUE DEVICE to add                        [testd]
* BACKEND PROGRAM pathname     [/usr/local/bin/subque q.log]
  ACTIVATE the queue?                                 yes
  Should this become the DEFAULT queue?               no
  Queuing DISCIPLINE                   first come first serve
  ACCOUNTING FILE pathname                           []
  HOSTNAME of remote server                          []
  Name of QUEUE on remote server                     []
  Pathname of the SHORT FORM FILTER for queue        []
   status output
  Pathname of the LONG FORM FILTER for queue         []
   status output
  BACKEND OUTPUT FILE pathname             [/dev/testl]

Example program

/* This is baseback   backend 					*/
/* Copyright IBM Corporation Inc, 1996				*/
/* Author:  John Tesch, IBM AIX Systems Center			*/
/* Code may be used and distributed, but must maintain original	*/
/* Copyright and references.					*/
/* Compile with cc -o baseback -lqb baseback.c  		*/
/* This program is designed to show the basics of writing a	*/
/* Simple AIX Print Backend - Stage one				*/
/*								*/
/* This program simply re-queues a job to another queue		*/
/* 								*/
#include <stdio.h>
#include <IN/backend.h>
#include <IN/standard.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <stdlib.h>
/* I'm over generous with the use of global variables - sorry	*/
char *qdate;
char* pfile;
char *cmdline;
char *to;
char *devname;
char *title;
char *aqlog;
char newq[14];
char *qname;
char queue[24];
char *qdname;
char *from;
char ctitle[90];
char cfrom[15];
char  errmsg[80];
char  filelst[256];
int   filecnt;
char  cmd[255];
char  o_flags[80];
char  flagout[80];
char  touser[40];
char  tosys[40];
char  aqlogf[40];
char  hedstr[6];
int   mail_only;
int   jobnum,log;
int   linecnt, pagecnt, widecnt;
int   rflag;  /* Remove file flag - make copy? */
int   errcode;
int   isbackend;
FILE *fd;
FILE  *fn;
/* Define libq backend routines			*/
extern char *get_qdate();
extern char *get_to();
extern char *get_from();
extern char *get_title();
extern char *get_queue_name();
extern char *get_device_name();
extern char *get_cmd_line();
main(argc,argv)
unsigned argc;
char **argv;
{
  int 	rc, i, c, w, dt, cmrc, acnt;
  int   itmp;
  unsigned int heder;
  char* myuser;
  char* sysuser;
  char* str1;
  char  myout[80];
  char  test[80];
  char* to1;
  FILE *pf;
  struct stat attrs;
  char	**flgarg;
/* Pluck off the command line the new queue name */
/* The /etc/qconfig line should be 		*/
/* backend = /full_path/baseback new_queue	*/
    strcpy(newq,argv[1]);
/* Parse the print command arguments		*/
  flgarg = ++argv;
  cmrc = flag_opts(argc-1, flgarg);
  
/* Test to see if this is being run as queue backend or from cmd line	*/
  isbackend = get_backend();	/* This will be set to 0 if not run as queue */
  errcode = 0;
/* This gives us the capability to log files to user defined file	*/
/* If the user defines a value for AQLOG, then logging will occur	*/
  aqlog = getenv("AQLOG");	/* Get user log file			*/
 if (strlen(aqlog) == 0)
  {
        log = 0;
  }
        else
  {
        log = 1;
        strcpy(aqlogf,aqlog);
  }
  rc = 0;
  if (isbackend) 	/* Only call log_init if this is a backend */
/*  This is an obligatory routine for getting access to 
    all the other routines - log_init 		*/
  rc = log_init();
  if (rc == -1) 
  {
	exit(1);
  }
  if (isbackend)
  {
/*  Some of the standard backend subroutine calls are 	*/
/*  used here.  These require link with libqb (-lqb)  	*/
/*  during the compile of this program.		 	*/
    qdate     = get_qdate();
    to        = get_to();
    strcpy(touser,to);
    strcpy(tosys,"");
    to1 = strchr(touser,'@');
    if (to1 != 0)
    {
      to1 = strtok(touser,"@");
      acnt = strlen(touser);
      sysuser = to + acnt + 1;
      strcpy (tosys,sysuser);
    }
    title     = get_title();
    qname     = get_queue_name();
    qdname    = get_device_name();
    from      = get_from();
    mail_only = get_mail_only();
    jobnum    = get_job_number();
    heder     = get_header();
    if (heder == 0)
	strcpy(hedstr,"nn");
    else
	strcpy(hedstr,"an");
/*  This cmdline actually contains hostname and user who
    sent the job.  I'll leave it for you to parse this 	*/
    cmdline   = get_cmd_line();
   }	/* End of backend only routines			*/
   else /* When run from the command line for testing   */
   {
	strcpy(hedstr,"nn");
	strcpy(ctitle, pfile);
	strcpy(cfrom,"only_you");
	printf("hed: %s %s %s\n",hedstr, ctitle, cfrom);
   }
/*  Now to get the data to print 			*/
/*  And pass it on to the printer - FILE = is stdout 	*/
/*  The values here are non valid enq flags	     	*/
/*
    strcpy(o_flags,"");
    if (argc > 1)
	  for (acnt = 1; acnt < argc-1; acnt++)
          {
		strcat(o_flags," -o ");
		strcat(o_flags,argv[acnt]);
          }
    printf("O_FLAGS: %s\n");
*/
/* This calls routine that also prints the file			*/
	rc = do_data(isbackend);
	if (log == 1) /* If logging turned on, write to file	*/
	{
/* This statement is to get the file size of the file for our log	*/
  	  lstat(pfile, &attrs);
	  fd = fopen(aqlog,"a");
	  if (isbackend)
	  {
          fprintf(fd,"%3d, %20s, %7s, %7s,"
		,jobnum,qdate,qname,queue);
	  fprintf(fd," %12s,",to);
	  fprintf(fd," %6d ",attrs.st_size);
          if (dt == 3) 
	    fprintf(fd,", %s, %d\n",title,heder);
	  if (errcode != 0)
	    fprintf(fd,",  ERROR = %d\n",errcode);
	  }
	  else
	  fprintf(fd,"queuename: %s file: %s size: %6d\n",newq,pfile,attrs.st_size);
	  fclose(fd);
  exit(0);
}
}
/* The do_data routine creates the command string that is sent	*/
/* To the shell with the pipeit routine.  Some basic ENQ flags	*/
/* are supported in this section from the libqb input		*/
int do_data (dtype)
  int dtype;
{
  int w, land, port, irc;
  char tmptst[300];
  char *str1;
  strcpy(queue,newq);
  if (dtype)
  sprintf(cmd,"enq -B%2s -c -P%s -T%s -Z%s %s %s\n",hedstr,queue,
	title, from, flagout, filelst);
  else
  sprintf(cmd,"enq -B%2s -c -P%s -T%s -Z%s %s %s\n",hedstr,queue,
	ctitle, cfrom, flagout, filelst);
  irc = pipeit(cmd);
  if (irc != 0) errcode = errcode + 2;
}
/* This routine is designed to take the command line arguments
   and turn them into flags for the enq print command		*/
/* Current version really only supports the -p qprt flag and	*/
/* The -z qprt flag, but the general structure applies		*/
int flag_opts(argc, argv)
int	argc;
char	**argv;
{
int  c,i;
char tflags[40];
int	ndex;
        strcpy(flagout,"");
        while ((c = getopt(argc, argv,
		"A:p:z:")
               ) !=EOF)
        {
		switch (c)
		{
		  case 'A':
			printf("debug opt");
			break;
		  case 'p':
			sprintf(tflags," -o -p -o %s",optarg);
			strcat(flagout,tflags);
			break;
		  case 'z':
			sprintf(tflags," -o -z -o %s",optarg);
			strcat(flagout,tflags);
		  case '+':
			printf ("+ arg: %s\n",optarg);
			break;
		  default:
			ndex = optind;
			printf ("unknown opt. ch:%c opt:%s ind:%d\n",c,optarg,optind);
			break;
		}
	}
	strcpy(filelst,"");	/* Blank out file names */
	filecnt = 0;
	for (i=optind; i<argc; i++)
	{
		filecnt++;
		strcat(filelst," ");
		strcat(filelst,argv[i]);
		if (filecnt == 1)
			pfile = argv[i];
	}
}
 
/* Finally this routine actually forks a process to run the	*/
/* print command.  If you uncomment the printf line, it will	*/
/* also send the command line to the file in 'file = ' of	*/
/* /etc/qconfig for debugging purposes				*/
char	trapit[20]="trap \"\" 15;\n";
int
pipeit(s)
char   *s;
{
   int prc, pid, w;
   int loopture;
   void (*istat)(), (*qstat)();
   char outstr[300];
   strcpy(outstr,trapit);
   strcat(outstr,s);
/* Uncomment this for debugging					
   printf("sending to queue with\n %s\n",outstr);
*/
   if((pid = fork()) == 0)
   {
       setpgrp();	/* set group id */
       execl("/bin/ksh", "ksh", "-c", outstr, 0);
/*
       ERREXIT(0, MSG_EXECFAIL, NULL, NULL, errno);
*/
   }
   sleep(5);	/* Let the files get copied			*/
}



[ Doc Ref: 91763041913324     Publish Date: Apr. 07, 2000     4FAX Ref: 6503 ]