Blame SOURCES/cups-failover-backend.patch

87cf9a
diff -up cups-1.6.3/backend/failover.c.failover-backend cups-1.6.3/backend/failover.c
87cf9a
--- cups-1.6.3/backend/failover.c.failover-backend	2017-10-25 13:57:43.032337219 +0200
87cf9a
+++ cups-1.6.3/backend/failover.c	2017-10-25 16:15:31.746984025 +0200
87cf9a
@@ -0,0 +1,837 @@
87cf9a
+/*
87cf9a
+ * Failover Backend for the Common UNIX Printing System (CUPS).
87cf9a
+ *
87cf9a
+ * Copyright (c) 2014, Red Hat, Inc.
87cf9a
+ * All rights reserved.
87cf9a
+ *
87cf9a
+ * Redistribution and use in source and binary forms, with or without
87cf9a
+ * modification, are permitted provided that the following conditions
87cf9a
+ * are met:
87cf9a
+ *
87cf9a
+ * * Redistributions of source code must retain the above copyright
87cf9a
+ *   notice, this list of conditions and the following disclaimer.
87cf9a
+ * * Redistributions in binary form must reproduce the above copyright
87cf9a
+ *   notice, this list of conditions and the following disclaimer in the
87cf9a
+ *   documentation and/or other materials provided with the distribution.
87cf9a
+ * * Neither the name of Red Hat, Inc. nor the names of its contributors
87cf9a
+ *   may be used to endorse or promote products derived from this software
87cf9a
+ *   without specific prior written permission.
87cf9a
+ *
87cf9a
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
87cf9a
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
87cf9a
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
87cf9a
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT,
87cf9a
+ * INC.  BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
87cf9a
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
87cf9a
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
87cf9a
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
87cf9a
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
87cf9a
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
87cf9a
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
87cf9a
+ * DAMAGE.
87cf9a
+ *
87cf9a
+ * Original version by Clark Hale, Red Hat, Inc.
87cf9a
+ *
87cf9a
+ * This backend presents a fake printer that will choose the first
87cf9a
+ * available printer from a list of IPP URIs.
87cf9a
+ *
87cf9a
+ * Option failover contains a comma separated list of IPP URIs.  The
87cf9a
+ * URIs are attempted in-order.
87cf9a
+ *
87cf9a
+ * Option failover-retries contains an integer that indicates how many
87cf9a
+ * times to iterate through the failover list before completely
87cf9a
+ * failing.
87cf9a
+ *
87cf9a
+ * Contents:
87cf9a
+ *   main()                   - Checks each printer in a failover list, and
87cf9a
+ *                              sends job data to the first available printer
87cf9a
+ *   move_job()               - Sends and IPP Move-Job request
87cf9a
+ *   check_printer()          - Checks a printer's attributes to see
87cf9a
+ *                              if it's enabled and accepting jobs
87cf9a
+ *   read_config()            - Read the backends configuration from
87cf9a
+ *                              options
87cf9a
+ *   get_printer_attributes() - Sends an IPP Get-Attributes request to
87cf9a
+ *                              a URI
87cf9a
+ *   sigterm_handler()        - Handle SIGTERM that cancels the job
87cf9a
+ *   password_cb()            - Password call back used to disable password
87cf9a
+ *                              prompt
87cf9a
+ */
87cf9a
+#include <stdlib.h>
87cf9a
+#include <stdio.h>
87cf9a
+#include <string.h>
87cf9a
+#include <sys/wait.h>
87cf9a
+#include <cups/http-private.h>
87cf9a
+#include <cups/http.h>
87cf9a
+#include "backend-private.h"
87cf9a
+
87cf9a
+/*
87cf9a
+ * Return Values
87cf9a
+ */
87cf9a
+typedef enum fo_state_e
87cf9a
+{
87cf9a
+  FO_PRINTER_GOOD = 0,
87cf9a
+  FO_PRINTER_BAD,
87cf9a
+  FO_PRINTER_BUSY,
87cf9a
+  FO_AUTH_REQUIRED
87cf9a
+} fo_state_t;
87cf9a
+
87cf9a
+/*
87cf9a
+ * Constants
87cf9a
+ */
87cf9a
+#define FAILOVER_DEFAULT_RETRIES        (3)
87cf9a
+#define FAILOVER_PASSWORD_RETRIES_MAX   (3)
87cf9a
+
87cf9a
+/*
87cf9a
+ * Local Functions
87cf9a
+ */
87cf9a
+static       int   check_printer(const char *device_uri);
87cf9a
+static       int   read_config(cups_array_t *printer_array, int *retries,
87cf9a
+			       const char *options);
87cf9a
+static       int   get_printer_attributes(const char *device_uri,
87cf9a
+					  ipp_t **attributes);
87cf9a
+static       int   move_job(int jobid, const char *dest);              
87cf9a
+static       void  sigterm_handler(int sig);
87cf9a
+static const char *password_cb(const char *);
87cf9a
+
87cf9a
+/*
87cf9a
+ * Global Variables
87cf9a
+ */
87cf9a
+static int         job_canceled = 0;     /* Job canceled */
87cf9a
+static char       *password = NULL;      /* password for device */
87cf9a
+static int         password_retries = 0;
87cf9a
+static const char *auth_info_required = "none";
87cf9a
+
87cf9a
+/*
87cf9a
+ * 'main()' - Checks each printer in a failover list, and
87cf9a
+ *            sends job data to the first available printer
87cf9a
+ * Usage:
87cf9a
+ *    printer-uri job-id user title copies options [file]
87cf9a
+ *
87cf9a
+ *    The printer-uri option is not used, but it still required to fit
87cf9a
+ *    to the backend(7) standards.  
87cf9a
+ */
87cf9a
+int
87cf9a
+main(int argc, char *argv[])
87cf9a
+{
87cf9a
+  const char   *selected_uri = NULL;    /* URI of selected printer     */
87cf9a
+  const char   *tmp_device_uri;         /* Device URI to check         */
87cf9a
+  cups_array_t *printer_array;          /* Array of available printers */
87cf9a
+  int           printer_count = 0;      /* current printer array index */
87cf9a
+  int           retry_max = 1;          /* maximum retries before exit */
87cf9a
+  int           retry_count = 0;        /* current retry number        */
87cf9a
+  int           auth_failed_count = 0;  /* auth failures per loop      */
87cf9a
+  int           rc = CUPS_BACKEND_OK; 
87cf9a
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
87cf9a
+  struct        sigaction action;       /* Actions for POSIX signals */
87cf9a
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Check args
87cf9a
+  */
87cf9a
+  if (argc == 1)
87cf9a
+  {
87cf9a
+   /*
87cf9a
+    * print out discovery data
87cf9a
+    */
87cf9a
+    char *backendName;
87cf9a
+
87cf9a
+    if ((backendName = strrchr(argv[0], '/')) != NULL)
87cf9a
+      backendName++;
87cf9a
+    else
87cf9a
+      backendName = argv[0];
87cf9a
+    
87cf9a
+    _cupsLangPrintf(stderr,"network %s \"Unknown\" \"%s (%s)\"\n",
87cf9a
+		    backendName,
87cf9a
+		    _cupsLangString(cupsLangDefault(), _("Failover Printer")),
87cf9a
+		    backendName);
87cf9a
+    
87cf9a
+    return (CUPS_BACKEND_OK);
87cf9a
+  }
87cf9a
+  else if (argc < 6)
87cf9a
+  {
87cf9a
+    _cupsLangPrintf(stderr,
87cf9a
+                    _("Usage: %s job-id user title copies options [file]"),
87cf9a
+		    argv[0]);
87cf9a
+    return (CUPS_BACKEND_STOP);
87cf9a
+  }
87cf9a
+
87cf9a
+  fprintf(stderr, "DEBUG: Failover backend starting up.\n");
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Don't buffer status messages
87cf9a
+  */
87cf9a
+  setbuf(stderr, NULL);
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Ignore SIGPIPE and catch SIGTERM signals...
87cf9a
+  */
87cf9a
+#ifdef HAVE_SIGSET
87cf9a
+  sigset(SIGPIPE, SIG_IGN);
87cf9a
+  sigset(SIGTERM, sigterm_handler);
87cf9a
+#elif defined(HAVE_SIGACTION)
87cf9a
+  memset(&action, 0, sizeof(action));
87cf9a
+  action.sa_handler = SIG_IGN;
87cf9a
+  sigaction(SIGPIPE, &action, NULL);
87cf9a
+
87cf9a
+  sigemptyset(&action.sa_mask);
87cf9a
+  sigaddset(&action.sa_mask, SIGTERM);
87cf9a
+  action.sa_handler = sigterm_handler;
87cf9a
+  sigaction(SIGTERM, &action, NULL);
87cf9a
+#else
87cf9a
+  signal(SIGPIPE, SIG_IGN);
87cf9a
+  signal(SIGTERM, sigterm_handler);
87cf9a
+#endif /* HAVE_SIGSET */
87cf9a
+  
87cf9a
+  printer_array = cupsArrayNew(NULL, NULL);
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Read Configuration
87cf9a
+  */
87cf9a
+  if ((rc = read_config(printer_array, &retry_max,
87cf9a
+			argv[5])) != CUPS_BACKEND_OK)
87cf9a
+  {
87cf9a
+    fprintf(stderr, "ERROR: Failed to read configuration options!\n");
87cf9a
+    goto cleanup;
87cf9a
+  }
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Main Retry Loop
87cf9a
+  */
87cf9a
+  for (retry_count = 0; retry_count < retry_max; retry_count++)
87cf9a
+  {
87cf9a
+    fprintf(stderr, "DEBUG: Retry loop #%d\n", retry_count + 1);
87cf9a
+
87cf9a
+   /*
87cf9a
+    * Reset Counters
87cf9a
+    */
87cf9a
+    printer_count = 0;
87cf9a
+    auth_failed_count = 0;
87cf9a
+        
87cf9a
+    tmp_device_uri = (char *)cupsArrayFirst(printer_array);
87cf9a
+
87cf9a
+    do
87cf9a
+    {
87cf9a
+      if (job_canceled)
87cf9a
+      {
87cf9a
+	fprintf(stderr, "DEBUG: Job Canceled\n");
87cf9a
+	goto cleanup;
87cf9a
+      }
87cf9a
+      
87cf9a
+      fprintf(stderr,"DEBUG: Checking printer #%d: %s\n",
87cf9a
+	      printer_count+1, tmp_device_uri);
87cf9a
+
87cf9a
+      rc = check_printer(tmp_device_uri);
87cf9a
+
87cf9a
+      // Printer is available and not busy.
87cf9a
+      if ( rc == FO_PRINTER_GOOD )      
87cf9a
+      {
87cf9a
+	selected_uri = tmp_device_uri;
87cf9a
+	break;
87cf9a
+      }
87cf9a
+      // Printer is busy
87cf9a
+      else if ( rc == FO_PRINTER_BUSY )
87cf9a
+      {
87cf9a
+        fprintf(stderr, "DEBUG: Waiting for job to complete.\n");
87cf9a
+        sleep(2);
87cf9a
+        continue;
87cf9a
+      }
87cf9a
+      // Authorization is required to access the printer.
87cf9a
+      else if (rc == FO_AUTH_REQUIRED)
87cf9a
+      {
87cf9a
+	auth_failed_count++;
87cf9a
+	fprintf(stderr, "DEBUG: auth_failed_count = %d\n", auth_failed_count);
87cf9a
+      }
87cf9a
+      // Printer is stopped or not accepting jobs
87cf9a
+      else
87cf9a
+      {
87cf9a
+        if (!printer_count) 
87cf9a
+          fprintf(stderr, "INFO: Primary Printer, %s, not available.  "
87cf9a
+                  "Attempting Failovers...\n",
87cf9a
+                  tmp_device_uri);
87cf9a
+        else
87cf9a
+          fprintf(stderr, "INFO: Failover Printer, %s, not available.  "
87cf9a
+                  "Attempting Failovers..\n",
87cf9a
+                  tmp_device_uri);
87cf9a
+        printer_count++;
87cf9a
+        tmp_device_uri = (char *)cupsArrayNext(printer_array);
87cf9a
+      }
87cf9a
+    } while (tmp_device_uri != NULL);
87cf9a
+
87cf9a
+    if (selected_uri && !printer_count)
87cf9a
+      fprintf(stderr, "STATE: -primary-printer-failed\n");
87cf9a
+    else
87cf9a
+      fprintf(stderr, "STATE: +primary-printer-failed\n");
87cf9a
+
87cf9a
+    if (job_canceled)
87cf9a
+    {
87cf9a
+      fprintf(stderr, "DEBUG: Job Canceled\n");
87cf9a
+      goto cleanup;
87cf9a
+    }
87cf9a
+
87cf9a
+    if (!selected_uri && auth_failed_count == printer_count)
87cf9a
+    {
87cf9a
+      fprintf(stderr, "ERROR:  All failover printers failed with "
87cf9a
+	      "authorization issues.\n");
87cf9a
+      rc = CUPS_BACKEND_AUTH_REQUIRED;
87cf9a
+      fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
87cf9a
+      goto cleanup;
87cf9a
+    }
87cf9a
+    else if (!selected_uri && retry_count + 1 < retry_max)
87cf9a
+    {
87cf9a
+      fprintf(stderr, "INFO: No suitable printer found...retrying...\n");
87cf9a
+      sleep(2);
87cf9a
+      continue;
87cf9a
+    }
87cf9a
+    else if (selected_uri)
87cf9a
+    {
87cf9a
+      fprintf(stderr, "DEBUG: Using printer, %s.\n", selected_uri);
87cf9a
+      break;
87cf9a
+    }
87cf9a
+  }
87cf9a
+
87cf9a
+  if (!selected_uri)
87cf9a
+  {
87cf9a
+    fprintf(stderr, "ERROR: No suitable printer found.  Aborting print\n");
87cf9a
+    rc = CUPS_BACKEND_FAILED;
87cf9a
+    goto cleanup;
87cf9a
+  }
87cf9a
+
87cf9a
+  rc = move_job(atoi(argv[1]), selected_uri);
87cf9a
+
87cf9a
+  if (job_canceled)
87cf9a
+    rc = CUPS_BACKEND_OK;
87cf9a
+  
87cf9a
+cleanup :
87cf9a
+  if (job_canceled)
87cf9a
+    rc = CUPS_BACKEND_OK;
87cf9a
+
87cf9a
+  tmp_device_uri = (char *)cupsArrayFirst(printer_array);
87cf9a
+  do
87cf9a
+  {
87cf9a
+    free((void *)tmp_device_uri);
87cf9a
+  } while ((tmp_device_uri = (char *)cupsArrayNext(printer_array)) != NULL);
87cf9a
+      
87cf9a
+  cupsArrayDelete(printer_array);
87cf9a
+  sleep(2);
87cf9a
+  return (rc);
87cf9a
+}
87cf9a
+
87cf9a
+/*
87cf9a
+ * 'check_printer()' - Checks the status of a remote printer and returns
87cf9a
+ *                     back a good/bad/busy status.
87cf9a
+ */
87cf9a
+int
87cf9a
+check_printer(const char *device_uri)
87cf9a
+{
87cf9a
+  ipp_t           *attributes = NULL;     /* attributes for device_uri */
87cf9a
+  ipp_attribute_t *tmp_attribute;         /* for examining attribs     */
87cf9a
+  int              rc = FO_PRINTER_GOOD;  /* return code               */
87cf9a
+  char            *reason;                /* printer state reason */
87cf9a
+  int              i;
87cf9a
+
87cf9a
+  fprintf(stderr, "DEBUG: Checking printer %s\n",device_uri);
87cf9a
+  
87cf9a
+  rc = get_printer_attributes(device_uri, &attributes);
87cf9a
+  if ( rc != CUPS_BACKEND_OK )
87cf9a
+  {
87cf9a
+    fprintf(stderr, "DEBUG: Failed to get attributes from printer: %s\n",
87cf9a
+	    device_uri);
87cf9a
+    if ( rc == CUPS_BACKEND_AUTH_REQUIRED )
87cf9a
+      return (FO_AUTH_REQUIRED);
87cf9a
+    else
87cf9a
+      return (FO_PRINTER_BAD);
87cf9a
+  }
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Check if printer is accepting jobs
87cf9a
+  */ 
87cf9a
+  if ((tmp_attribute = ippFindAttribute(attributes,
87cf9a
+					"printer-is-accepting-jobs",
87cf9a
+					IPP_TAG_BOOLEAN)) != NULL &&
87cf9a
+      !tmp_attribute->values[0].boolean)
87cf9a
+  {
87cf9a
+    fprintf(stderr,
87cf9a
+	    "DEBUG: Printer, %s, is not accepting jobs.\n",
87cf9a
+	    device_uri);
87cf9a
+     
87cf9a
+    rc = FO_PRINTER_BAD;
87cf9a
+  }
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Check if printer is stopped or busy processing
87cf9a
+  */
87cf9a
+  if ((tmp_attribute = ippFindAttribute(attributes,
87cf9a
+					"printer-state",
87cf9a
+					IPP_TAG_ENUM)) != NULL)
87cf9a
+  {
87cf9a
+    // Printer Stopped
87cf9a
+    if ( tmp_attribute->values[0].integer == IPP_PRINTER_STOPPED )
87cf9a
+    {
87cf9a
+      fprintf(stderr, "DEBUG: Printer, %s, stopped.\n", device_uri);
87cf9a
+      rc = FO_PRINTER_BAD;
87cf9a
+    }
87cf9a
+    // Printer Busy
87cf9a
+    else if ( tmp_attribute->values[0].integer == IPP_PRINTER_PROCESSING )
87cf9a
+    {
87cf9a
+      fprintf(stderr, "DEBUG: Printer %s is busy.\n", device_uri);
87cf9a
+      rc = FO_PRINTER_BUSY;
87cf9a
+    }
87cf9a
+  }
87cf9a
+
87cf9a
+  /*
87cf9a
+   * Parse through the printer-state-reasons
87cf9a
+   */
87cf9a
+  if ((tmp_attribute = ippFindAttribute(attributes, "printer-state-reasons",
87cf9a
+					IPP_TAG_KEYWORD)) != NULL)
87cf9a
+  {
87cf9a
+    for (i = 0; i < tmp_attribute->num_values; i++)
87cf9a
+    {
87cf9a
+      reason = tmp_attribute->values[i].string.text;
87cf9a
+      int len = strlen(reason);
87cf9a
+      
87cf9a
+      if (len > 8 && !strcmp(reason + len - 8, "-warning"))
87cf9a
+      {
87cf9a
+	fprintf(stderr, "DEBUG: Printer Supply Warning, %s\n", reason);
87cf9a
+	rc = FO_PRINTER_BAD;
87cf9a
+      }
87cf9a
+      else if (len > 6 && !strcmp(reason + len - 6, "-error"))
87cf9a
+      {
87cf9a
+	fprintf(stderr, "DEBUG: Printer Supply Error, %s\n", reason);
87cf9a
+	rc = FO_PRINTER_BAD;
87cf9a
+      }
87cf9a
+    }
87cf9a
+  }
87cf9a
+  
87cf9a
+  return (rc);  
87cf9a
+}
87cf9a
+
87cf9a
+/*
87cf9a
+ * 'read_config()' - Parses the failover and failover-retries options
87cf9a
+ *                   
87cf9a
+ */
87cf9a
+static int
87cf9a
+read_config(cups_array_t *printer_array, int *retries, const char *options)
87cf9a
+{
87cf9a
+
87cf9a
+  const char    *tmp;                   /* temporary ptr                     */
87cf9a
+  char          *tok_tmp;               /* temporary ptr for option  parsing */
87cf9a
+  int            jobopts_count = 0;     /* number of options                 */
87cf9a
+  cups_option_t	*jobopts = NULL;	/* job options                       */
87cf9a
+
87cf9a
+
87cf9a
+  fprintf(stderr, "DEBUG: Reading Configuration.\n");
87cf9a
+  jobopts_count = cupsParseOptions(options, 0, &jobopts);
87cf9a
+
87cf9a
+  if (!jobopts_count)
87cf9a
+  {
87cf9a
+    fprintf(stderr,
87cf9a
+	    "ERROR: No job options!  Cannot find failover options!\n");
87cf9a
+    return (CUPS_BACKEND_STOP);
87cf9a
+  }
87cf9a
+  
87cf9a
+ /*
87cf9a
+  * Get attributes from the primary printer
87cf9a
+  */
87cf9a
+  fprintf(stderr, "DEBUG: Searching for failover option.\n");
87cf9a
+
87cf9a
+  if ((tmp = cupsGetOption("failover", jobopts_count, jobopts)) != NULL)
87cf9a
+  {
87cf9a
+    fprintf(stderr, "DEBUG: Failover option contents: %s.\n", tmp);
87cf9a
+
87cf9a
+    tok_tmp = strdup(tmp);
87cf9a
+
87cf9a
+    tmp = strtok(tok_tmp, ",");
87cf9a
+    do
87cf9a
+    {
87cf9a
+      cupsArrayAdd(printer_array, strdup(tmp));
87cf9a
+    } while ((tmp = strtok(NULL,",")) != NULL);
87cf9a
+
87cf9a
+    free(tok_tmp);
87cf9a
+  }
87cf9a
+  else
87cf9a
+  {
87cf9a
+   /*
87cf9a
+    * The queue is misconfigured, so return back CUPS_BACKEND_STOP
87cf9a
+    */
87cf9a
+    fprintf(stderr, "ERROR: failover option not specified!\n");
87cf9a
+    return (CUPS_BACKEND_STOP);
87cf9a
+  }
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Get the failover-retries value, if it exists.
87cf9a
+  */
87cf9a
+  fprintf(stderr, "DEBUG: Searching for failover-retries option.\n");
87cf9a
+    
87cf9a
+  if ((tmp = cupsGetOption("failover-retries",
87cf9a
+			   jobopts_count, jobopts)) != NULL)
87cf9a
+  {
87cf9a
+    fprintf(stderr, "DEBUG: failover-retries option contents: %s.\n", tmp);
87cf9a
+    *retries = atoi(tmp);
87cf9a
+  }
87cf9a
+  else
87cf9a
+  {
87cf9a
+    *retries = FAILOVER_DEFAULT_RETRIES;
87cf9a
+    fprintf(stderr, "DEBUG: Failed to get failover-retries option\n");
87cf9a
+    fprintf(stderr, "DEBUG: Defaulted to %d retries\n", *retries);
87cf9a
+  }
87cf9a
+
87cf9a
+  return (CUPS_BACKEND_OK);
87cf9a
+}
87cf9a
+
87cf9a
+/*
87cf9a
+ * 'get_printer_attributes()' - Sends an IPP Get-Attributes request to
87cf9a
+ *                              a URI
87cf9a
+ */
87cf9a
+int
87cf9a
+get_printer_attributes(const char *device_uri, ipp_t **attributes)
87cf9a
+{
87cf9a
+  char		     uri[HTTP_MAX_URI];	        /* Updated URI without login */
87cf9a
+  int		     version;     		/* IPP version */
87cf9a
+  char               scheme[256];	       	/* Scheme in URI */
87cf9a
+  ipp_status_t	     ipp_status;    		/* Status of IPP request */
87cf9a
+  char               hostname[1024];		/* Hostname */
87cf9a
+  char               resource[1024];		/* Resource infoo */
87cf9a
+  char               addrname[256];		/* Address name */
87cf9a
+  int                port;                      /* IPP Port number */
87cf9a
+  char		     portname[255];		/* Port as string */
87cf9a
+  http_t	    *http;		        /* HTTP connection */
87cf9a
+  ipp_t		    *request;		        /* IPP request */
87cf9a
+  int                rc = CUPS_BACKEND_OK;      /* Return Code */
87cf9a
+  char		     username[256];             /* Username for device URI */
87cf9a
+  char              *option_ptr;                /* for parsing resource opts */
87cf9a
+  const char * const pattrs[] =	                /* Printer attributes wanted */
87cf9a
+    {
87cf9a
+      "printer-is-accepting-jobs",
87cf9a
+      "printer-state",
87cf9a
+      "printer-state-reasons"
87cf9a
+    };
87cf9a
+
87cf9a
+  if (job_canceled)
87cf9a
+    return (CUPS_BACKEND_OK);
87cf9a
+
87cf9a
+  fprintf(stderr, "DEBUG: Getting Printer Attributes.\n");
87cf9a
+  fprintf(stderr, "DEBUG: Device URL %s.\n", device_uri);
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Parse device_uri
87cf9a
+  */  
87cf9a
+  if (httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
87cf9a
+		     username, sizeof(username), hostname, sizeof(hostname),
87cf9a
+		     &port, resource, sizeof(resource)) != HTTP_URI_OK)
87cf9a
+  {
87cf9a
+    fprintf(stderr, "ERROR: Problem parsing device_uri, %s\n", device_uri);
87cf9a
+    return (CUPS_BACKEND_STOP);
87cf9a
+  }
87cf9a
+
87cf9a
+  if (!port)
87cf9a
+    port = IPP_PORT;
87cf9a
+
87cf9a
+  sprintf(portname, "%d", port);
87cf9a
+
87cf9a
+  fprintf(stderr, "DEBUG: Getting Printer Attributes.\n");
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Configure password
87cf9a
+  */
87cf9a
+  cupsSetPasswordCB(password_cb);
87cf9a
+
87cf9a
+ /*
87cf9a
+  * reset, in case a previous attempt for
87cf9a
+  * another printer left residue
87cf9a
+  */
87cf9a
+  cupsSetUser(NULL);
87cf9a
+  password = NULL;
87cf9a
+  password_retries = 0;
87cf9a
+
87cf9a
+  if (*username)
87cf9a
+  {
87cf9a
+    if ((password = strchr(username, ':')) != NULL)
87cf9a
+    {
87cf9a
+      *password = '\0';
87cf9a
+      password++;
87cf9a
+    }
87cf9a
+
87cf9a
+    cupsSetUser(username);
87cf9a
+  }
87cf9a
+  else if (!getuid())
87cf9a
+  {
87cf9a
+    const char *username_env;
87cf9a
+
87cf9a
+    if ((username_env = getenv("AUTH_USERNAME")) != NULL)
87cf9a
+    {
87cf9a
+      cupsSetUser(username_env);
87cf9a
+      password = getenv("AUTH_PASSWORD");
87cf9a
+    }
87cf9a
+  }
87cf9a
+      
87cf9a
+ /*
87cf9a
+  * Try connecting to the remote server...
87cf9a
+  */
87cf9a
+  fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
87cf9a
+  _cupsLangPuts(stderr, _("INFO: Connecting to printer...\n"));
87cf9a
+  
87cf9a
+  http = httpConnectEncrypt(hostname, port, cupsEncryption());
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Deal the socket not being open.
87cf9a
+  */
87cf9a
+  if (!http)
87cf9a
+  {
87cf9a
+    int error = errno;		/* Connection error */
87cf9a
+
87cf9a
+    switch (error)
87cf9a
+    {
87cf9a
+    case EHOSTDOWN :
87cf9a
+      _cupsLangPuts(stderr, _("WARNING: "
87cf9a
+			      "The printer may not exist or "
87cf9a
+			      "is unavailable at this time.\n"));
87cf9a
+      break;
87cf9a
+    case EHOSTUNREACH :
87cf9a
+      _cupsLangPuts(stderr, _("WARNING: "
87cf9a
+			      "The printer is unreachable at this "
87cf9a
+			      "time.\n"));
87cf9a
+      break;
87cf9a
+    case ECONNREFUSED :
87cf9a
+      _cupsLangPuts(stderr, _("WARNING: "
87cf9a
+			      "Connection Refused.\n"));
87cf9a
+      break;
87cf9a
+    default :
87cf9a
+      fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
87cf9a
+      break;
87cf9a
+    }
87cf9a
+
87cf9a
+    rc = CUPS_BACKEND_FAILED;
87cf9a
+    sleep(5);
87cf9a
+    goto prt_available_cleanup;
87cf9a
+  }
87cf9a
+
87cf9a
+
87cf9a
+#ifdef AF_INET6
87cf9a
+  if (http->hostaddr->addr.sa_family == AF_INET6)
87cf9a
+    fprintf(stderr, "DEBUG: Connected to [%s]:%d (IPv6)...\n",
87cf9a
+	    httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
87cf9a
+	    ntohs(http->hostaddr->ipv6.sin6_port));
87cf9a
+  else
87cf9a
+#endif /* AF_INET6 */
87cf9a
+  if (http->hostaddr->addr.sa_family == AF_INET)
87cf9a
+    fprintf(stderr, "DEBUG: Connected to %s:%d (IPv4)...\n",
87cf9a
+	    httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
87cf9a
+	    ntohs(http->hostaddr->ipv4.sin_port));
87cf9a
+ 
87cf9a
+ /*
87cf9a
+  * Search the resource string for options.
87cf9a
+  * We only care about version, for the moment.
87cf9a
+  */
87cf9a
+  version = 11;
87cf9a
+  
87cf9a
+  if ((option_ptr = strchr(resource, '?')) != NULL)
87cf9a
+  {
87cf9a
+    *option_ptr++ = '\0';
87cf9a
+    
87cf9a
+    if ((option_ptr = strstr(option_ptr, "version="))!=NULL)
87cf9a
+    {
87cf9a
+      int   minor;       /* minor version from URI */
87cf9a
+      int   major;       /* major version from URI */
87cf9a
+      char *version_str; /* ipp version */
87cf9a
+
87cf9a
+      option_ptr += 8;
87cf9a
+      version_str = option_ptr;
87cf9a
+
87cf9a
+      while (*option_ptr && *option_ptr != '&' && *option_ptr != '+')
87cf9a
+	option_ptr++;
87cf9a
+
87cf9a
+      if (*option_ptr)
87cf9a
+	*option_ptr = '\0';
87cf9a
+
87cf9a
+      sscanf(version_str, "%d.%d", &major, &minor);
87cf9a
+
87cf9a
+      version = (major * 10) + minor;
87cf9a
+
87cf9a
+      switch(version)
87cf9a
+      {
87cf9a
+      case 10 :
87cf9a
+      case 11 :
87cf9a
+      case 20 :
87cf9a
+      case 21 :
87cf9a
+	fprintf(stderr,
87cf9a
+		"DEBUG: Set version to %d from URI\n",
87cf9a
+		version);
87cf9a
+	break;
87cf9a
+      default :
87cf9a
+	_cupsLangPrintf(stderr,
87cf9a
+			_("DEBUG: Invalid version, %d, from URI.  "
87cf9a
+			  "Using default of 1.1 \n"),
87cf9a
+			version);
87cf9a
+	version = 11;
87cf9a
+      }
87cf9a
+    }
87cf9a
+  }
87cf9a
+
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Build a URI for the printer.  We can't use the URI in argv[0]
87cf9a
+  * because it might contain username:password information...
87cf9a
+  */
87cf9a
+  if (httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL,
87cf9a
+		      hostname, port, resource) != HTTP_URI_OK)
87cf9a
+  {
87cf9a
+    fprintf(stderr, "ERROR: Problem assembling printer URI from host %s, "
87cf9a
+	    "port %d, resource %s\n", hostname, port, resource);
87cf9a
+    return (CUPS_BACKEND_STOP);
87cf9a
+  }
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Build the IPP request...
87cf9a
+  */
87cf9a
+  request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
87cf9a
+  request->request.op.version[0] = version / 10;
87cf9a
+  request->request.op.version[1] = version % 10;
87cf9a
+
87cf9a
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
87cf9a
+	       NULL, uri);
87cf9a
+
87cf9a
+  ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
87cf9a
+		"requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
87cf9a
+		NULL, pattrs);
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Do the request...
87cf9a
+  */
87cf9a
+  fputs("DEBUG: Getting supported attributes...\n", stderr);
87cf9a
+
87cf9a
+  fprintf(stderr, "DEBUG: IPP Request Structure Built.\n");
87cf9a
+
87cf9a
+  *attributes = cupsDoRequest(http, request, resource);
87cf9a
+  ipp_status = cupsLastError();
87cf9a
+
87cf9a
+  fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
87cf9a
+	  ippErrorString(ipp_status), cupsLastErrorString());
87cf9a
+
87cf9a
+  if (ipp_status > IPP_OK_CONFLICT)
87cf9a
+  {
87cf9a
+    fprintf(stderr, "DEBUG: Get-Printer-Attributes returned %s.\n",
87cf9a
+	    ippErrorString(ipp_status));
87cf9a
+    switch(ipp_status)
87cf9a
+    {
87cf9a
+    case IPP_FORBIDDEN :
87cf9a
+    case IPP_NOT_AUTHORIZED :
87cf9a
+      _cupsLangPuts(stderr, _("ERROR: Not Authorized.\n"));
87cf9a
+      rc = CUPS_BACKEND_AUTH_REQUIRED;
87cf9a
+      break;
87cf9a
+    case IPP_PRINTER_BUSY :
87cf9a
+    case IPP_SERVICE_UNAVAILABLE :
87cf9a
+      _cupsLangPuts(stderr, _("ERROR: "
87cf9a
+			      "The printer is not responding.\n"));
87cf9a
+      rc = CUPS_BACKEND_FAILED;
87cf9a
+      break;
87cf9a
+    case IPP_BAD_REQUEST :
87cf9a
+    case IPP_VERSION_NOT_SUPPORTED :
87cf9a
+      fprintf(stderr, "ERROR: Destination does not support IPP version %d\n",
87cf9a
+	      version);
87cf9a
+    case IPP_NOT_FOUND :
87cf9a
+      _cupsLangPuts(stderr, _("ERROR: "
87cf9a
+			      "The printer configuration is incorrect or the "
87cf9a
+			      "printer no longer exists.\n"));
87cf9a
+      rc = CUPS_BACKEND_STOP;
87cf9a
+      break;
87cf9a
+    default :
87cf9a
+      rc = CUPS_BACKEND_FAILED;
87cf9a
+    }
87cf9a
+    goto prt_available_cleanup;
87cf9a
+  }
87cf9a
+
87cf9a
+prt_available_cleanup :
87cf9a
+  httpClose(http);
87cf9a
+  return (rc);
87cf9a
+}
87cf9a
+
87cf9a
+static int                              
87cf9a
+move_job(int        jobid,              /* Job ID */
87cf9a
+         const char *dest)              /* Destination ipp address */
87cf9a
+{
87cf9a
+  ipp_t *request;                       /* IPP Request */
87cf9a
+  char  job_uri[HTTP_MAX_URI];          /* job-uri */
87cf9a
+
87cf9a
+  http_t* http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
87cf9a
+
87cf9a
+  if (!http)
87cf9a
+  {
87cf9a
+    _cupsLangPrintf(stderr,
87cf9a
+                    _("failover: Unable to connect to server: %s\n"),
87cf9a
+                    strerror(errno));
87cf9a
+    return (CUPS_BACKEND_FAILED);
87cf9a
+  }
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Build a CUPS_MOVE_JOB request, which requires the following
87cf9a
+  * attributes:
87cf9a
+  *
87cf9a
+  *    job-uri/printer-uri
87cf9a
+  *    job-printer-uri
87cf9a
+  *    requesting-user-name
87cf9a
+  */
87cf9a
+
87cf9a
+  request = ippNewRequest(CUPS_MOVE_JOB);
87cf9a
+
87cf9a
+  snprintf(job_uri, sizeof(job_uri), "ipp://localhost/jobs/%d", jobid);
87cf9a
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL,
87cf9a
+               job_uri);
87cf9a
+
87cf9a
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, 
87cf9a
+               "requesting-user-name",
87cf9a
+               NULL, cupsUser());
87cf9a
+
87cf9a
+  ippAddString(request, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri",
87cf9a
+               NULL, dest);
87cf9a
+
87cf9a
+ /*
87cf9a
+  * Do the request and get back a response...
87cf9a
+  */
87cf9a
+
87cf9a
+  ippDelete(cupsDoRequest(http, request, "/jobs"));
87cf9a
+
87cf9a
+  httpClose(http);
87cf9a
+
87cf9a
+  if (cupsLastError() > IPP_OK_CONFLICT)
87cf9a
+  {
87cf9a
+    _cupsLangPrintf(stderr, "failover: %s\n", cupsLastErrorString());
87cf9a
+    return (CUPS_BACKEND_FAILED);
87cf9a
+  }
87cf9a
+  else
87cf9a
+    return (CUPS_BACKEND_OK);
87cf9a
+}
87cf9a
+
87cf9a
+/*
87cf9a
+ * 'sigterm_handler()' - handles a sigterm, i.e. job canceled
87cf9a
+ */
87cf9a
+static void
87cf9a
+sigterm_handler(int sig)
87cf9a
+{
87cf9a
+  if (!job_canceled)
87cf9a
+  {
87cf9a
+    write(2, "DEBUG: Got SIGTERM.\n", 20);
87cf9a
+    job_canceled = 1;
87cf9a
+  }
87cf9a
+  else
87cf9a
+  {
87cf9a
+   /*
87cf9a
+    * Job has already been canceled, so just exit
87cf9a
+    */
87cf9a
+    exit(1);
87cf9a
+  }
87cf9a
+}
87cf9a
+
87cf9a
+/*
87cf9a
+ * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
87cf9a
+ */
87cf9a
+static const char *			/* O - Password  */
87cf9a
+password_cb(const char *prompt)		/* I - Prompt (not used) */
87cf9a
+{
87cf9a
+  auth_info_required = "username,password";
87cf9a
+  password_retries++;
87cf9a
+
87cf9a
+  if(password_retries < FAILOVER_PASSWORD_RETRIES_MAX) 
87cf9a
+    return (password);
87cf9a
+  else
87cf9a
+    return (NULL);
87cf9a
+}
87cf9a
diff -up cups-1.6.3/backend/Makefile.failover-backend cups-1.6.3/backend/Makefile
87cf9a
--- cups-1.6.3/backend/Makefile.failover-backend	2017-10-25 13:57:42.940337847 +0200
87cf9a
+++ cups-1.6.3/backend/Makefile	2017-10-25 13:57:43.032337219 +0200
87cf9a
@@ -30,6 +30,7 @@ include ../Makedefs
87cf9a
 RBACKENDS =	\
87cf9a
 		ipp \
87cf9a
 		lpd \
87cf9a
+		failover \
87cf9a
 		$(DNSSD_BACKEND)
87cf9a
 UBACKENDS =	\
87cf9a
 		snmp \
87cf9a
@@ -51,6 +52,7 @@ LIBOBJS	=	\
87cf9a
 OBJS	=	\
87cf9a
 		ipp.o \
87cf9a
 		lpd.o \
87cf9a
+		failover.o \
87cf9a
 		dnssd.o \
87cf9a
 		snmp.o \
87cf9a
 		socket.o \
87cf9a
@@ -257,6 +259,13 @@ lpd:	lpd.o ../cups/$(LIBCUPS) libbackend
87cf9a
 	echo Linking $@...
87cf9a
 	$(CC) $(LDFLAGS) -o lpd lpd.o libbackend.a $(LIBS)
87cf9a
 
87cf9a
+#
87cf9a
+# failover
87cf9a
+#
87cf9a
+
87cf9a
+failover:	failover.o ../cups/$(LIBCUPS) libbackend.a
87cf9a
+	echo Linking $@...
87cf9a
+	$(CC) $(LDFLAGS) -o failover failover.o libbackend.a $(LIBS) $(CUPSDLIBS)
87cf9a
 
87cf9a
 #
87cf9a
 # snmp