cups-lpd with a space limited print spool

Sebastian James seb at esfnet.co.uk
Mon Jul 24 02:28:23 PDT 2006


The cups-lpd.c in cups-1.1.23 makes no checks on file system space when receiving print jobs. Normally admins make sure there is plenty of space on their print spools, but we placed cups on an appliance with limited space for the print spool.

cups-lpd has the following flow (simplified):

1. Receive the job (control and print data) and store print data in a temporary file in the cups "TempDir"
2. Use the cups api to pass the job to cups via a call to cupsDoFileRequest().
3. Clean up the temporary print data file and exit.

This means that for an incoming job with X bytes of print data, 2X bytes are required as a minimum on the storage device on which TempDir is located.

If you have X+1 bytes available (and X>1 for the pedantic), then you can receive the print data, but cups-lpd fails to pass the job on to cups. The sending lpd server thinks the job has printed, but nothing spools out of the cups backend.

This is ok if you have access to the cupsd and work out that your disk is full and clear it out/install a bigger one, but in our application, we need to pause the input if TempDir gets too full, then resume when jobs have spooled out.

Here is a patch to do this. It does it by keeping tabs on available space and pausing the cups-lpd process when there is not enough room. The code will only work reliably if you know a maximum size for your print jobs, so this is not a general purpose fix for everyone, rather a fix for our problem of printing many fixed or similar size print jobs in a production situation.

Next, we're going to look into adding a compile time option to remove the necessity for temporary files from the data flow in cups, placing everything in memory and streaming jobs through the filter(s). Wish us luck!

Seb James

Here's the patch (with apologies for the fact that it has messed up indentation of comments):

--- cups-lpd.c	2006-07-24 10:02:06.000000000 +0100
+++ cups-1.1.23/scheduler/cups-lpd.c	2006-07-24 10:04:40.000000000 +0100
@@ -51,6 +51,8 @@
 #include <netinet/in.h>
 #include <netdb.h>

+#include <sys/stat.h>
+#include <sys/statvfs.h>

 /*
  * LPD "mini-daemon" for CUPS.  This program must be used in conjunction
@@ -86,6 +88,39 @@
 char	*smart_gets(char *s, int len, FILE *fp);


+/* Whether to apply the disk usage checking code for systems with
+   limited file storage */
+#define CHECK_DISK_SPACE_FEATURE 1
+
+#ifdef CHECK_DISK_SPACE_FEATURE
+
+/* BUFFER is the smallest amount of free space allowed on the cups
+   temporary file store before cups-lpd will start to pause while
+   processing jobs. BUFFER should be given in bytes. BUFFER should be
+   twice as big as the largest print job which is expected to be
+   handled by cups-lpd. If BUFFER is set to 8 MB, a print file of 4 MB
+   would be accepted and the data received, but a following job (of
+   any size) would wait.  The accepted job would then be duplicated as
+   it's passed to the cupsd via the print_file() function.
+
+   NB! NB! If a print file of 4MB<size<=8MB is sent to cups-lpd.c, it
+   would be received, but never printed, so this code really does
+   depend on your knowing the size of your print jobs. It's intended
+   for those implementing commercial printing systems with limited
+   storage space for print jobs. */
+
+#define BUFFER      8388608 /* 8 MB */
+#define HALF_BUFFER 4194304 /* half of the above */
+
+#define FORCE_MAX_JOB_SIZE 1 /* Define this if no job of size greater
+				than HALF_BUFFER should be accepted */
+
+/* These two functions are used as checks on allowable free space. */
+int     check_free_space_for_a_duplicate (const char * file);
+int     tempdir_free_space (int bytes_limit);
+
+#endif /* CHECK_DISK_SPACE_FEATURE */
+
 /*
  * 'main()' - Process an incoming LPD request...
  */
@@ -98,33 +133,32 @@
   int		num_defaults;		/* Number of default options */
   cups_option_t	*defaults;		/* Default options */
   char		line[256],		/* Command string */
-		command,		/* Command code */
-		*dest,			/* Pointer to destination */
-		*list,			/* Pointer to list */
-		*agent,			/* Pointer to user */
-		status;			/* Status for client */
+                command,		/* Command code */
+                *dest,			/* Pointer to destination */
+                *list,			/* Pointer to list */
+                *agent,			/* Pointer to user */
+                status;			/* Status for client */
   int		hostlen;		/* Size of client address */
   unsigned	hostip;			/* (32-bit) IP address */
   struct sockaddr_in hostaddr;		/* Address of client */
   struct hostent *hostent;		/* Host entry of client */
   char		hostname[256];		/* Hostname of client */

-
- /*
-  * Don't buffer the output...
-  */
+  /*
+   * Don't buffer the output...
+   */

   setbuf(stdout, NULL);

- /*
-  * Log things using the "cups-lpd" name...
-  */
+  /*
+   * Log things using the "cups-lpd" name...
+   */

   openlog("cups-lpd", LOG_PID, LOG_LPR);

- /*
-  * Get the address of the client...
-  */
+  /*
+   * Get the address of the client...
+   */

   hostlen = sizeof(hostaddr);

@@ -152,9 +186,9 @@
 	   (hostip >> 8) & 255, hostip & 255);
   }

- /*
-  * Scan the command-line for options...
-  */
+  /*
+   * Scan the command-line for options...
+   */

   num_defaults = 0;
   defaults     = NULL;
@@ -167,47 +201,47 @@
     {
       switch (argv[i][1])
       {
-	case 'o' : /* Option */
-	    if (argv[i][2])
-	      num_defaults = cupsParseOptions(argv[i] + 2, num_defaults,
-	                                      &defaults);
-	    else
-	    {
-	      i ++;
-	      if (i < argc)
-		num_defaults = cupsParseOptions(argv[i], num_defaults, &defaults);
-              else
-        	syslog(LOG_WARNING, "Expected option string after -o option!");
-            }
-	    break;
-	default :
-	    syslog(LOG_WARNING, "Unknown option \"%c\" ignored!", argv[i][1]);
-	    break;
+      case 'o' : /* Option */
+	if (argv[i][2])
+	  num_defaults = cupsParseOptions(argv[i] + 2, num_defaults,
+					  &defaults);
+	else
+	{
+	  i ++;
+	  if (i < argc)
+	    num_defaults = cupsParseOptions(argv[i], num_defaults, &defaults);
+	  else
+	    syslog(LOG_WARNING, "Expected option string after -o option!");
+	}
+	break;
+      default :
+	syslog(LOG_WARNING, "Unknown option \"%c\" ignored!", argv[i][1]);
+	break;
       }
     }
     else
       syslog(LOG_WARNING, "Unknown command-line option \"%s\" ignored!", argv[i]);

- /*
-  * RFC1179 specifies that only 1 daemon command can be received for
-  * every connection.
-  */
+  /*
+   * RFC1179 specifies that only 1 daemon command can be received for
+   * every connection.
+   */

   if (smart_gets(line, sizeof(line), stdin) == NULL)
   {
-   /*
-    * Unable to get command from client!  Send an error status and return.
-    */
+    /*
+     * Unable to get command from client!  Send an error status and return.
+     */

     syslog(LOG_ERR, "Unable to get command line from client!");
     putchar(1);
     return (1);
   }

- /*
-  * The first byte is the command byte.  After that will be the queue name,
-  * resource list, and/or user name.
-  */
+  /*
+   * The first byte is the command byte.  After that will be the queue name,
+   * resource list, and/or user name.
+   */

   command = line[0];
   dest    = line + 1;
@@ -217,65 +251,65 @@
   while (isspace(*list & 255))
     *list++ = '\0';

- /*
-  * Do the command...
-  */
+  /*
+   * Do the command...
+   */

   switch (command)
   {
-    default : /* Unknown command */
-        syslog(LOG_ERR, "Unknown LPD command 0x%02X!", command);
-        syslog(LOG_ERR, "Command line = %s", line + 1);
-	putchar(1);
+  default : /* Unknown command */
+    syslog(LOG_ERR, "Unknown LPD command 0x%02X!", command);
+    syslog(LOG_ERR, "Command line = %s", line + 1);
+    putchar(1);

-        status = 1;
-	break;
+    status = 1;
+    break;

-    case 0x01 : /* Print any waiting jobs */
-        syslog(LOG_INFO, "Print waiting jobs (no-op)");
-	putchar(0);
+  case 0x01 : /* Print any waiting jobs */
+    syslog(LOG_INFO, "Print waiting jobs (no-op)");
+    putchar(0);

-        status = 0;
-	break;
+    status = 0;
+    break;

-    case 0x02 : /* Receive a printer job */
-        syslog(LOG_INFO, "Receive print job for %s", dest);
-        /* recv_print_job() sends initial status byte */
+  case 0x02 : /* Receive a printer job */
+    syslog(LOG_INFO, "Receive print job for %s", dest);
+    /* recv_print_job() sends initial status byte */

-        status = recv_print_job(dest, num_defaults, defaults);
-	break;
+    status = recv_print_job(dest, num_defaults, defaults);
+    break;

-    case 0x03 : /* Send queue state (short) */
-        syslog(LOG_INFO, "Send queue state (short) for %s %s", dest, list);
-	/* no status byte for this command */
+  case 0x03 : /* Send queue state (short) */
+    syslog(LOG_INFO, "Send queue state (short) for %s %s", dest, list);
+    /* no status byte for this command */

-        status = send_state(dest, list, 0);
-	break;
+    status = send_state(dest, list, 0);
+    break;

-    case 0x04 : /* Send queue state (long) */
-        syslog(LOG_INFO, "Send queue state (long) for %s %s", dest, list);
-	/* no status byte for this command */
+  case 0x04 : /* Send queue state (long) */
+    syslog(LOG_INFO, "Send queue state (long) for %s %s", dest, list);
+    /* no status byte for this command */

-        status = send_state(dest, list, 1);
-	break;
+    status = send_state(dest, list, 1);
+    break;

-    case 0x05 : /* Remove jobs */
-       /*
-        * Grab the agent and skip to the list of users and/or jobs.
-	*/
+  case 0x05 : /* Remove jobs */
+    /*
+     * Grab the agent and skip to the list of users and/or jobs.
+     */

-        agent = list;
+    agent = list;

-	for (; *list && !isspace(*list & 255); list ++);
-	while (isspace(*list & 255))
-	  *list++ = '\0';
+    for (; *list && !isspace(*list & 255); list ++);
+    while (isspace(*list & 255))
+      *list++ = '\0';

-        syslog(LOG_INFO, "Remove jobs %s on %s by %s", list, dest, agent);
+    syslog(LOG_INFO, "Remove jobs %s on %s by %s", list, dest, agent);

-        status = remove_jobs(dest, agent, list);
+    status = remove_jobs(dest, agent, list);

-	putchar(status);
-	break;
+    putchar(status);
+    break;
   }

   syslog(LOG_INFO, "Closing connection");
@@ -301,9 +335,9 @@
   int		accepting;		/* printer-is-accepting-jobs value */


- /*
-  * Setup a connection and request data...
-  */
+  /*
+   * Setup a connection and request data...
+   */

   if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
                                  cupsEncryption())) == NULL)
@@ -313,10 +347,10 @@
     return (0);
   }

- /*
-  * Build a standard CUPS URI for the printer and fill the standard IPP
-  * attributes...
-  */
+  /*
+   * Build a standard CUPS URI for the printer and fill the standard IPP
+   * attributes...
+   */

   if ((request = ippNew()) == NULL)
   {
@@ -345,9 +379,9 @@
   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requested-attributes",
                NULL, "printer-is-accepting-jobs");

- /*
-  * Do the request...
-  */
+  /*
+   * Do the request...
+   */

   response = cupsDoRequest(http, request, "/");

@@ -402,11 +436,12 @@
   char		uri[HTTP_MAX_URI];	/* Printer URI */
   cups_lang_t	*language;		/* Language to use */
   int		jobid;			/* New job ID */
-
-
- /*
-  * Setup a connection and request data...
-  */
+#ifdef CHECK_DISK_SPACE_FEATURE
+  int           rtn;                    /* return value of check_free_space... */
+#endif
+  /*
+   * Setup a connection and request data...
+   */

   if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
                                  cupsEncryption())) == NULL)
@@ -416,10 +451,10 @@
     return (0);
   }

- /*
-  * Build a standard CUPS URI for the printer and fill the standard IPP
-  * attributes...
-  */
+  /*
+   * Build a standard CUPS URI for the printer and fill the standard IPP
+   * attributes...
+   */

   if ((request = ippNew()) == NULL)
   {
@@ -453,29 +488,49 @@
   if (docname)
     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name", NULL, docname);

- /*
-  * Then add all options on the command-line...
-  */
+  /*
+   * Then add all options on the command-line...
+   */

   cupsEncodeOptions(request, num_options, options);

- /*
-  * Do the request...
-  */
+  /*
+   * Do the request...
+   */

   snprintf(uri, sizeof(uri), "/printers/%s", name);

+#ifdef CHECK_DISK_SPACE_FEATURE
+  /*
+   * Find the size of the file and make sure we have 1 times that amount
+   * of free space prior to processing it, as the cupsDoFileRequest will copy
+   * the file prior to spooling it to the printer.
+   */
+  while (!(rtn = check_free_space_for_a_duplicate (file)))
+  {
+    syslog (LOG_INFO,
+	    "ramdisk is full, wait for space to become "
+	    "available before passing job on to cupsd");
+    usleep (5000000); /* 5 sec */
+  }
+
+  if (rtn == -1)
+    syslog (LOG_ERR, "check_free_space_for_a_duplicate() returned an error.");
+
+#endif /* CHECK_DISK_SPACE_FEATURE */
+
   response = cupsDoFileRequest(http, request, uri, file);

   if (response == NULL)
   {
-    syslog(LOG_ERR, "Unable to print file - %s",
+    syslog(LOG_ERR, "Null response - Unable to print file - %s",
            ippErrorString(cupsLastError()));
     jobid = 0;
   }
   else if (response->request.status.status_code > IPP_OK_CONFLICT)
   {
-    syslog(LOG_ERR, "Unable to print file - %s",
+    syslog(LOG_ERR, "response->request.status.status_code = %d - Unable to print file - %s",
+	   response->request.status.status_code,
            ippErrorString(response->request.status.status_code));
     jobid = 0;
   }
@@ -488,7 +543,7 @@
   {
     jobid = attr->values[0].integer;

-    syslog(LOG_INFO, "Print file - job ID = %d", jobid);
+    syslog(LOG_INFO, "Printed file - job ID = %d", jobid);
   }

   if (response != NULL)
@@ -508,7 +563,7 @@
 int					/* O - Command status */
 recv_print_job(const char    *dest,	/* I - Destination */
                int           num_defaults,
-					/* I - Number of default options */
+	                                /* I - Number of default options */
 	       cups_option_t *defaults)	/* I - Default options */
 {
   int		i;			/* Looping var */
@@ -518,25 +573,27 @@
   char		filename[1024];		/* Temporary filename */
   int		bytes;			/* Bytes received */
   char		line[256],		/* Line from file/stdin */
-		command,		/* Command from line */
-		*count,			/* Number of bytes */
-		*name;			/* Name of file */
+                command,		/* Command from line */
+                *count,			/* Number of bytes */
+                *name;			/* Name of file */
   int		num_data;		/* Number of data files */
   char		control[1024],		/* Control filename */
-		data[32][256],		/* Data files */
-		temp[32][1024];		/* Temporary files */
+                data[32][256],		/* Data files */
+                temp[32][1024];		/* Temporary files */
   char		user[1024],		/* User name */
-		title[1024],		/* Job title */
-		docname[1024],		/* Document name */
-		queue[256],		/* Printer/class queue */
-		*instance;		/* Printer/class instance */
+                title[1024],		/* Job title */
+                docname[1024],		/* Document name */
+                queue[256],		/* Printer/class queue */
+                *instance;		/* Printer/class instance */
   int		num_dests;		/* Number of destinations */
   cups_dest_t	*dests,			/* Destinations */
-		*destptr;		/* Current destination */
+                *destptr;		/* Current destination */
   int		num_options;		/* Number of options */
   cups_option_t	*options;		/* Options */
   int		banner;			/* Print banner? */
-
+#ifdef FORCE_MAX_JOB_SIZE
+  int           bytes_total = 0;        /* Total bytes received */
+#endif

   status   = 0;
   num_data = 0;
@@ -552,9 +609,9 @@
   num_dests = cupsGetDests(&dests);
   if ((destptr = cupsGetDest(queue, instance, num_dests, dests)) == NULL)
   {
-   /*
-    * If the queue name is blank or "lp" then use the default queue.
-    */
+    /*
+     * If the queue name is blank or "lp" then use the default queue.
+     */

     if (!queue[0] || !strcmp(queue, "lp"))
       if ((destptr = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL)
@@ -578,9 +635,9 @@
   if (!check_printer(queue))
   {
     cupsFreeDests(num_dests, dests);
-
+
     putchar(1);
-
+
     return (1);
   }

@@ -603,91 +660,91 @@

     switch (command)
     {
-      default :
-      case 0x01 : /* Abort */
-          status = 1;
-	  break;
+    default :
+    case 0x01 : /* Abort */
+      status = 1;
+      break;

-      case 0x02 : /* Receive control file */
-          if (strlen(name) < 2)
-	  {
-	    syslog(LOG_ERR, "Bad control file name \"%s\"", name);
-	    putchar(1);
-	    status = 1;
-	    break;
-	  }
+    case 0x02 : /* Receive control file */
+      if (strlen(name) < 2)
+      {
+	syslog(LOG_ERR, "Bad control file name \"%s\"", name);
+	putchar(1);
+	status = 1;
+	break;
+      }

-          if (control[0])
-	  {
-	   /*
-	    * Append to the existing control file - the LPD spec is
-	    * not entirely clear, but at least the OS/2 LPD code sends
-	    * multiple control files per connection...
-	    */
-
-	    if ((fd = open(control, O_WRONLY)) < 0)
-	    {
-	      syslog(LOG_ERR,
-	             "Unable to append to temporary control file \"%s\" - %s",
-        	     control, strerror(errno));
-	      putchar(1);
-	      status = 1;
-	      break;
-	    }
+      if (control[0])
+      {
+	/*
+	 * Append to the existing control file - the LPD spec is
+	 * not entirely clear, but at least the OS/2 LPD code sends
+	 * multiple control files per connection...
+	 */

-	    lseek(fd, 0, SEEK_END);
-          }
-	  else
-	  {
-	    if ((fd = cupsTempFd(control, sizeof(control))) < 0)
-	    {
-	      syslog(LOG_ERR, "Unable to open temporary control file \"%s\" - %s",
-        	     control, strerror(errno));
-	      putchar(1);
-	      status = 1;
-	      break;
-	    }
+	if ((fd = open(control, O_WRONLY)) < 0)
+	{
+	  syslog(LOG_ERR,
+		 "Unable to append to temporary control file \"%s\" - %s",
+		 control, strerror(errno));
+	  putchar(1);
+	  status = 1;
+	  break;
+	}

-	    strcpy(filename, control);
-	  }
+	lseek(fd, 0, SEEK_END);
+      }
+      else
+      {
+	if ((fd = cupsTempFd(control, sizeof(control))) < 0)
+	{
+	  syslog(LOG_ERR, "Unable to open temporary control file \"%s\" - %s",
+		 control, strerror(errno));
+	  putchar(1);
+	  status = 1;
 	  break;
+	}

-      case 0x03 : /* Receive data file */
-          if (strlen(name) < 2)
-	  {
-	    syslog(LOG_ERR, "Bad data file name \"%s\"", name);
-	    putchar(1);
-	    status = 1;
-	    break;
-	  }
+	strcpy(filename, control);
+      }
+      break;

-          if (num_data >= (sizeof(data) / sizeof(data[0])))
-	  {
-	   /*
-	    * Too many data files...
-	    */
+    case 0x03 : /* Receive data file */
+      if (strlen(name) < 2)
+      {
+	syslog(LOG_ERR, "Bad data file name \"%s\"", name);
+	putchar(1);
+	status = 1;
+	break;
+      }

-	    syslog(LOG_ERR, "Too many data files (%d)", num_data);
-	    putchar(1);
-	    status = 1;
-	    break;
-	  }
+      if (num_data >= (sizeof(data) / sizeof(data[0])))
+      {
+	/*
+	 * Too many data files...
+	 */

-	  strlcpy(data[num_data], name, sizeof(data[0]));
+	syslog(LOG_ERR, "Too many data files (%d)", num_data);
+	putchar(1);
+	status = 1;
+	break;
+      }

-          if ((fd = cupsTempFd(temp[num_data], sizeof(temp[0]))) < 0)
-	  {
-	    syslog(LOG_ERR, "Unable to open temporary data file \"%s\" - %s",
-        	   temp[num_data], strerror(errno));
-	    putchar(1);
-	    status = 1;
-	    break;
-	  }
+      strlcpy(data[num_data], name, sizeof(data[0]));
+
+      if ((fd = cupsTempFd(temp[num_data], sizeof(temp[0]))) < 0)
+      {
+	syslog(LOG_ERR, "Unable to open temporary data file \"%s\" - %s",
+	       temp[num_data], strerror(errno));
+	putchar(1);
+	status = 1;
+	break;
+      }

-	  strcpy(filename, temp[num_data]);
+      strcpy(filename, temp[num_data]);

-          num_data ++;
-	  break;
+      num_data ++;
+      break;
     }

     putchar(status);
@@ -695,32 +752,58 @@
     if (status)
       break;

-   /*
-    * Copy the data or control file from the client...
-    */
+    /*
+     * Copy the data or control file from the client...
+     */
+
+#ifdef CHECK_DISK_SPACE_FEATURE
+    /* Wait till there is BUFFER Mbytes free before
+       starting to receive the data */
+    while (tempdir_free_space (BUFFER) != 1)
+    {
+      syslog (LOG_ERR, "tempdir_free_space() said 'not enough space', pause 5s.");
+      usleep (5000000);
+    }
+#endif /* CHECK_DISK_SPACE_FEATURE */

     for (i = atoi(count); i > 0; i -= bytes)
     {
       if (i > sizeof(line))
-        bytes = sizeof(line);
+	bytes = sizeof(line);
       else
-        bytes = i;
+	bytes = i;

       if ((bytes = fread(line, 1, bytes, stdin)) > 0)
-        bytes = write(fd, line, bytes);
+	bytes = write(fd, line, bytes);

       if (bytes < 1)
       {
+	/* This case occurs when the input file can't be
+	   stored due to space issues */
 	syslog(LOG_ERR, "Error while reading file - %s",
-               strerror(errno));
-        status = 1;
+	       strerror(errno));
+	status = 1;
 	break;
+      }
+#ifdef FORCE_MAX_JOB_SIZE
+      else
+      {
+	bytes_total += bytes;
+	if (bytes_total > HALF_BUFFER)
+	{
+	  /* if FORCE_MAX_JOB_SIZE is set, we don't allow a job in
+	   which is greater than half the size of our "minimum
+	   available free print file space" value */
+	  status = 1;
+	  break;
+	}
       }
+#endif /* FORCE_MAX_JOB_SIZE */
     }

-   /*
-    * Read trailing nul...
-    */
+    /*
+     * Read trailing nul...
+     */

     if (!status)
     {
@@ -738,9 +821,9 @@
       }
     }

-   /*
-    * Close the file and send an acknowledgement...
-    */
+    /*
+     * Close the file and send an acknowledgement...
+     */

     close(fd);

@@ -752,17 +835,17 @@

   if (!status)
   {
-   /*
-    * Process the control file and print stuff...
-    */
+    /*
+     * Process the control file and print stuff...
+     */

     if ((fp = fopen(control, "rb")) == NULL)
       status = 1;
     else
     {
-     /*
-      * Grab the job information first...
-      */
+      /*
+       * Grab the job information first...
+       */

       title[0]   = '\0';
       user[0]    = '\0';
@@ -771,126 +854,128 @@

       while (smart_gets(line, sizeof(line), fp) != NULL)
       {
-       /*
-        * Process control lines...
-	*/
+	/*
+	 * Process control lines...
+	 */

 	switch (line[0])
 	{
-	  case 'J' : /* Job name */
-	      strlcpy(title, line + 1, sizeof(title));
-	      break;
+	case 'J' : /* Job name */
+	  strlcpy(title, line + 1, sizeof(title));
+	  break;

-	  case 'N' : /* Document name */
-	      strlcpy(docname, line + 1, sizeof(docname));
-	      break;
+	case 'N' : /* Document name */
+	  strlcpy(docname, line + 1, sizeof(docname));
+	  break;

-	  case 'P' : /* User identification */
-	      strlcpy(user, line + 1, sizeof(user));
-	      break;
+	case 'P' : /* User identification */
+	  strlcpy(user, line + 1, sizeof(user));
+	  break;

-	  case 'L' : /* Print banner page */
-	      banner = 1;
-	      break;
+	case 'L' : /* Print banner page */
+	  banner = 1;
+	  break;
 	}

 	if (status)
 	  break;
       }

-     /*
-      * Then print the jobs...
-      */
+      /*
+       * Then print the jobs...
+       */

       rewind(fp);

       while (smart_gets(line, sizeof(line), fp) != NULL)
       {
-       /*
-        * Process control lines...
-	*/
+	/*
+	 * Process control lines...
+	 */

 	switch (line[0])
 	{
-	  case 'c' : /* Plot CIF file */
-	  case 'd' : /* Print DVI file */
-	  case 'f' : /* Print formatted file */
-	  case 'g' : /* Plot file */
-	  case 'l' : /* Print file leaving control characters (raw) */
-	  case 'n' : /* Print ditroff output file */
-	  case 'o' : /* Print PostScript output file */
-	  case 'p' : /* Print file with 'pr' format (prettyprint) */
-	  case 'r' : /* File to print with FORTRAN carriage control */
-	  case 't' : /* Print troff output file */
-	  case 'v' : /* Print raster file */
-	     /*
-	      * Check that we have a username...
-	      */
-
-	      if (!user[0])
-	      {
-		syslog(LOG_WARNING, "No username specified by client! "
-		                    "Using \"anonymous\"...");
-		strcpy(user, "anonymous");
-	      }
-
-             /*
-	      * Copy the default options...
-	      */
-
-              num_options = 0;
-	      options     = NULL;
-
-	      for (i = 0; i < num_defaults; i ++)
-	        num_options = cupsAddOption(defaults[i].name,
-		                            defaults[i].value,
-		                            num_options, &options);
-	      for (i = 0; i < destptr->num_options; i ++)
-	        num_options = cupsAddOption(destptr->options[i].name,
-		                            destptr->options[i].value,
-		                            num_options, &options);
-
-             /*
-	      * Add additional options as needed...
-	      */
-
-              if (!banner)
-	        num_options = cupsAddOption("job-sheets", "none",
-		                            num_options, &options);
-
-	      if (line[0] == 'l')
-	        num_options = cupsAddOption("raw", "", num_options, &options);
-
-              if (line[0] == 'p')
-	        num_options = cupsAddOption("prettyprint", "", num_options,
-		                            &options);
-
-             /*
-	      * Figure out which file we are printing...
-	      */
-
-	      for (i = 0; i < num_data; i ++)
-	        if (strcmp(data[i], line + 1) == 0)
-		  break;
-
-              if (i >= num_data)
-	      {
-	        status = 1;
-		break;
-	      }
-
-             /*
-	      * Send the print request...
-	      */
-
-              if (print_file(queue, temp[i], title, docname, user, num_options,
-	                     options) == 0)
-                status = 1;
-	      else
-	        status = 0;
+	case 'c' : /* Plot CIF file */
+	case 'd' : /* Print DVI file */
+	case 'f' : /* Print formatted file */
+	case 'g' : /* Plot file */
+	case 'l' : /* Print file leaving control characters (raw) */
+	case 'n' : /* Print ditroff output file */
+	case 'o' : /* Print PostScript output file */
+	case 'p' : /* Print file with 'pr' format (prettyprint) */
+	case 'r' : /* File to print with FORTRAN carriage control */
+	case 't' : /* Print troff output file */
+	case 'v' : /* Print raster file */
+	  /*
+	   * Check that we have a username...
+	   */
+
+	  if (!user[0])
+	  {
+	    syslog(LOG_WARNING, "No username specified by client! "
+		   "Using \"anonymous\"...");
+	    strcpy(user, "anonymous");
+	  }
+
+	  /*
+	   * Copy the default options...
+	   */
+
+	  num_options = 0;
+	  options     = NULL;
+
+	  for (i = 0; i < num_defaults; i ++)
+	    num_options = cupsAddOption(defaults[i].name,
+					defaults[i].value,
+					num_options, &options);
+	  for (i = 0; i < destptr->num_options; i ++)
+	    num_options = cupsAddOption(destptr->options[i].name,
+					destptr->options[i].value,
+					num_options, &options);
+
+	  /*
+	   * Add additional options as needed...
+	   */
+
+	  if (!banner)
+	    num_options = cupsAddOption("job-sheets", "none",
+					num_options, &options);
+
+	  if (line[0] == 'l')
+	    num_options = cupsAddOption("raw", "", num_options, &options);
+
+	  if (line[0] == 'p')
+	    num_options = cupsAddOption("prettyprint", "", num_options,
+					&options);
+
+	  /*
+	   * Figure out which file we are printing...
+	   */

-              cupsFreeOptions(num_options, options);
+	  for (i = 0; i < num_data; i ++)
+	    if (strcmp(data[i], line + 1) == 0)
 	      break;
+
+	  if (i >= num_data)
+	  {
+	    status = 1;
+	    break;
+	  }
+
+	  /*
+	   * Send the print request...
+	   */
+
+	  /* This print_file() request will fail if we don't have at least
+	     1x or 2x the size of the file spare */
+	  if (print_file(queue, temp[i], title, docname, user, num_options,
+			 options) == 0)
+	    status = 1;
+	  else
+	    status = 0;
+
+	  cupsFreeOptions(num_options, options);
+	  break;
 	}

 	if (status)
@@ -901,9 +986,9 @@
     }
   }

- /*
-  * Clean up all temporary files and return...
-  */
+  /*
+   * Clean up all temporary files and return...
+   */

   unlink(control);

@@ -928,16 +1013,16 @@
   int		id;			/* Job ID */
   http_t	*http;			/* HTTP server connection */
   ipp_t		*request,		/* IPP Request */
-		*response;		/* IPP Response */
+                *response;		/* IPP Response */
   cups_lang_t	*language;		/* Default language */
   char		uri[HTTP_MAX_URI];	/* Job URI */


   (void)dest;	/* Suppress compiler warnings... */

- /*
-  * Try connecting to the local server...
-  */
+  /*
+   * Try connecting to the local server...
+   */

   if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
                                  cupsEncryption())) == NULL)
@@ -949,30 +1034,30 @@

   language = cupsLangDefault();

- /*
-  * Loop for each job...
-  */
+  /*
+   * Loop for each job...
+   */

   while ((id = atoi(list)) > 0)
   {
-   /*
-    * Skip job ID in list...
-    */
+    /*
+     * Skip job ID in list...
+     */

     while (isdigit(*list & 255))
       list ++;
     while (isspace(*list & 255))
       list ++;

-   /*
-    * Build an IPP_CANCEL_JOB request, which requires the following
-    * attributes:
-    *
-    *    attributes-charset
-    *    attributes-natural-language
-    *    job-uri
-    *    requesting-user-name
-    */
+    /*
+     * Build an IPP_CANCEL_JOB request, which requires the following
+     * attributes:
+     *
+     *    attributes-charset
+     *    attributes-natural-language
+     *    job-uri
+     *    requesting-user-name
+     */

     request = ippNew();

@@ -991,9 +1076,9 @@
     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
                  "requesting-user-name", NULL, agent);

-   /*
-    * Do the request and get back a response...
-    */
+    /*
+     * Do the request and get back a response...
+     */

     if ((response = cupsDoRequest(http, request, "/jobs")) != NULL)
     {
@@ -1040,61 +1125,61 @@
   int		id;			/* Job ID from list */
   http_t	*http;			/* HTTP server connection */
   ipp_t		*request,		/* IPP Request */
-		*response;		/* IPP Response */
+                *response;		/* IPP Response */
   ipp_attribute_t *attr;		/* Current attribute */
   cups_lang_t	*language;		/* Default language */
   ipp_pstate_t	state;			/* Printer state */
   const char	*jobdest,		/* Pointer into job-printer-uri */
-		*jobuser,		/* Pointer to job-originating-user-name */
-		*jobname;		/* Pointer to job-name */
+                *jobuser,		/* Pointer to job-originating-user-name */
+                *jobname;		/* Pointer to job-name */
   ipp_jstate_t	jobstate;		/* job-state */
   int		jobid,			/* job-id */
-		jobsize,		/* job-k-octets */
-		jobcount,		/* Number of jobs */
-		jobcopies,		/* Number of copies */
-		rank;			/* Rank of job */
+                jobsize,		/* job-k-octets */
+                jobcount,		/* Number of jobs */
+                jobcopies,		/* Number of copies */
+                rank;			/* Rank of job */
   char		rankstr[255];		/* Rank string */
   char		namestr[1024];		/* Job name string */
   char		uri[HTTP_MAX_URI];	/* Printer URI */
   char		queue[256],		/* Printer/class queue */
-		*instance;		/* Printer/class instance */
+    *instance;		/* Printer/class instance */
   static const char * const ranks[10] =	/* Ranking strings */
-		{
-		  "th",
-		  "st",
-		  "nd",
-		  "rd",
-		  "th",
-		  "th",
-		  "th",
-		  "th",
-		  "th",
-		  "th"
-		};
+    {
+      "th",
+      "st",
+      "nd",
+      "rd",
+      "th",
+      "th",
+      "th",
+      "th",
+      "th",
+      "th"
+    };
   static const char * const requested[] =
-		{			/* Requested attributes */
-		  "job-id",
-		  "job-k-octets",
-		  "job-state",
-		  "job-printer-uri",
-		  "job-originating-user-name",
-		  "job-name",
-		  "copies"
-		};
+    {			/* Requested attributes */
+      "job-id",
+      "job-k-octets",
+      "job-state",
+      "job-printer-uri",
+      "job-originating-user-name",
+      "job-name",
+      "copies"
+    };


- /*
-  * Remove instance from destination, if any...
-  */
+  /*
+   * Remove instance from destination, if any...
+   */

   strlcpy(queue, dest, sizeof(queue));

   if ((instance = strrchr(queue, '/')) != NULL)
     *instance++ = '\0';

- /*
-  * Try connecting to the local server...
-  */
+  /*
+   * Try connecting to the local server...
+   */

   if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
                                  cupsEncryption())) == NULL)
@@ -1105,14 +1190,14 @@
     return (1);
   }

- /*
-  * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
-  * attributes:
-  *
-  *    attributes-charset
-  *    attributes-natural-language
-  *    printer-uri
-  */
+  /*
+   * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
+   * attributes:
+   *
+   *    attributes-charset
+   *    attributes-natural-language
+   *    printer-uri
+   */

   request = ippNew();

@@ -1134,9 +1219,9 @@
   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
                "requested-attributes", NULL, "printer-state");

- /*
-  * Do the request and get back a response...
-  */
+  /*
+   * Do the request and get back a response...
+   */

   if ((response = cupsDoRequest(http, request, "/")) != NULL)
   {
@@ -1157,15 +1242,15 @@

     switch (state)
     {
-      case IPP_PRINTER_IDLE :
-          printf("%s is ready\n", dest);
-	  break;
-      case IPP_PRINTER_PROCESSING :
-          printf("%s is ready and printing\n", dest);
-	  break;
-      case IPP_PRINTER_STOPPED :
-          printf("%s is not ready\n", dest);
-	  break;
+    case IPP_PRINTER_IDLE :
+      printf("%s is ready\n", dest);
+      break;
+    case IPP_PRINTER_PROCESSING :
+      printf("%s is ready and printing\n", dest);
+      break;
+    case IPP_PRINTER_STOPPED :
+      printf("%s is not ready\n", dest);
+      break;
     }

     ippDelete(response);
@@ -1179,14 +1264,14 @@
     return (1);
   }

- /*
-  * Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires
-  * the following attributes:
-  *
-  *    attributes-charset
-  *    attributes-natural-language
-  *    job-uri or printer-uri
-  */
+  /*
+   * Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires
+   * the following attributes:
+   *
+   *    attributes-charset
+   *    attributes-natural-language
+   *    job-uri or printer-uri
+   */

   id = atoi(list);

@@ -1219,9 +1304,9 @@
                 "requested-attributes", sizeof(requested) / sizeof(requested[0]),
 		NULL, requested);

- /*
-  * Do the request and get back a response...
-  */
+  /*
+   * Do the request and get back a response...
+   */

   jobcount = 0;

@@ -1237,15 +1322,15 @@

     rank = 1;

-   /*
-    * Loop through the job list and display them...
-    */
+    /*
+     * Loop through the job list and display them...
+     */

     for (attr = response->attrs; attr != NULL; attr = attr->next)
     {
-     /*
-      * Skip leading attributes until we hit a job...
-      */
+      /*
+       * Skip leading attributes until we hit a job...
+       */

       while (attr != NULL &&
              (attr->group_tag != IPP_TAG_JOB || attr->name == NULL))
@@ -1254,9 +1339,9 @@
       if (attr == NULL)
         break;

-     /*
-      * Pull the needed attributes from this job...
-      */
+      /*
+       * Pull the needed attributes from this job...
+       */

       jobid       = 0;
       jobsize     = 0;
@@ -1300,9 +1385,9 @@
         attr = attr->next;
       }

-     /*
-      * See if we have everything needed...
-      */
+      /*
+       * See if we have everything needed...
+       */

       if (jobdest == NULL || jobid == 0)
       {
@@ -1317,9 +1402,9 @@

       jobcount ++;

-     /*
-      * Display the job...
-      */
+      /*
+       * Display the job...
+       */

       if (jobstate == IPP_JOB_PROCESSING)
 	strcpy(rankstr, "active");
@@ -1378,16 +1463,16 @@
 	   FILE *fp)			/* I - File to read from */
 {
   char	*ptr,				/* Pointer into line */
-	*end;				/* End of line */
+        *end;				/* End of line */
   int	ch;				/* Character from file */


- /*
-  * Read the line; unlike fgets(), we read the entire line but dump
-  * characters that go past the end of the buffer.  Also, we accept
-  * CR, LF, or CR LF for the line endings to be "safe", although
-  * RFC 1179 specifically says "just use LF".
-  */
+  /*
+   * Read the line; unlike fgets(), we read the entire line but dump
+   * characters that go past the end of the buffer.  Also, we accept
+   * CR, LF, or CR LF for the line endings to be "safe", although
+   * RFC 1179 specifically says "just use LF".
+   */

   ptr = s;
   end = s + len - 1;
@@ -1398,9 +1483,9 @@
       break;
     else if (ch == '\r')
     {
-     /*
-      * See if a LF follows...
-      */
+      /*
+       * See if a LF follows...
+       */

       ch = getc(fp);

@@ -1422,6 +1507,125 @@
 }


+#ifdef CHECK_DISK_SPACE_FEATURE
+
+/*
+ * 'check_free_space_for_a_duplicate()' - Check we have enough free space
+ * to create a duplicate of "file" on the filesystem containing "file".
+ *
+ * NB: This assumes the cups-lpd temporary file is created in the same
+ * directory as the cups temporary file. Is that true? It is for my
+ * application, but may not be for all?
+ *
+ * This check is important on cups systems where the print spool filesystem
+ * is limited in size.
+ *
+ */
+
+int
+check_free_space_for_a_duplicate (const char * file)
+{
+  struct statvfs    *tmpstat;        /* Used by statvfs() function */
+  struct stat       *buf;            /* Used by stat() function */
+  int               statret;         /* To hold the rtn value of stat() and statvfs() */
+  unsigned long     file_size = 0;   /* The size of the file to be printed */
+  unsigned long     free_space = 0;  /* The free space available on the filesystem holding file */
+
+  /* Find disk usage of file */
+  buf = malloc (sizeof (struct stat));
+  memset (buf, 0, sizeof(struct stat));
+
+  if ((statret = stat (file, buf)) != 0) {
+    syslog (LOG_ERR, "%s: stat() returned error %d\n",
+	    __FUNCTION__, statret);
+    free (buf);
+    return -1;
+  }
+
+  if (S_ISREG(buf->st_mode))
+  {
+    file_size = buf->st_size;
+    free (buf);
+  }
+  else
+  {
+    /* Error reading file */
+    syslog (LOG_ERR, "%s: Error reading file", __FUNCTION__);
+    free (buf);
+    return -1;
+  }
+
+  /* Find free space available */
+  tmpstat = malloc (sizeof (struct statvfs));
+
+  /* Here we have a hardcoded directory - how to get this from cups? */
+  if ((statret = statvfs (file, tmpstat)))
+  {
+    syslog (LOG_ERR, "%s: statvfs() returned error %d\n",
+	    __FUNCTION__, statret);
+    free (tmpstat);
+    return -1;
+  }
+
+  /* 512 blocks is 2 MB on /tmp on the vortex */
+  free_space = tmpstat->f_bfree * tmpstat->f_bsize;
+
+  syslog (LOG_INFO, "file_size = %ld (%2f MB), free_space = %ld (%2f MB)",
+	  file_size, (float)file_size/1048576,
+	  free_space, (float)free_space/1048576);
+
+  if (free_space > file_size)
+  {
+    free (tmpstat);
+    return 1;
+  }
+  else
+  {
+    free (tmpstat);
+    return 0;
+  }
+
+}
+
+
+/*
+ * Seb's hacky "how much free space on /tmp" fn.  The hackyness is
+ * that it's specific to my system where print files are stored in
+ * /tmp/ - that is, in cupsd.conf: TempDir=/tmp
+ *
+ * Is there a cups API function which returns the value of TempDir
+ * from cupsd.conf?
+ */
+int tempdir_free_space (int bytes_limit)
+{
+  struct statvfs   *tmpstat;
+  int              statret;
+
+  tmpstat = malloc (sizeof (struct statvfs));
+
+  /* Before generating each file, test that there is
+     bytes_limit bytes free on the /tmp filesystem */
+  if ((statret = statvfs ("/tmp", tmpstat)))
+  {
+    syslog (LOG_ERR, "statvfs returned error %d", statret);
+    return -1;
+  }
+
+  if ((tmpstat->f_bfree * tmpstat->f_bsize) > bytes_limit)
+  {
+    free (tmpstat);
+    return 1;
+  }
+  else
+  {
+    free (tmpstat);
+    return 0;
+  }
+}
+
+#endif /* CHECK_DISK_SPACE_FEATURE */
+
+
 /*
  * End of "$Id: cups-lpd.c,v 1.49 2005/01/03 19:29:59 mike Exp $".
  */







More information about the cups mailing list