]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/sidechannel.c
Merge changes from CUPS 1.5svn-r9049 (private header support)
[thirdparty/cups.git] / cups / sidechannel.c
index b1403384c0df09055e40df2f4f7af0003b06b8ec..17abb2b3f4704f64925b073a42a526a5f1feb737 100644 (file)
@@ -1,33 +1,26 @@
 /*
- * "$Id: sidechannel.c 6319 2007-03-06 18:51:40Z mike $"
+ * "$Id: sidechannel.c 7720 2008-07-11 22:46:21Z mike $"
  *
- *   Side-channel API code for the Common UNIX Printing System (CUPS).
+ *   Side-channel API code for CUPS.
  *
+ *   Copyright 2007-2010 by Apple Inc.
  *   Copyright 2006 by Easy Software Products.
  *
  *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
  *   This file is subject to the Apple OS-Developed Software exception.
  *
  * Contents:
  *
- *   cupsSideChannelDoRequest() - Send a side-channel command to a backend
- *                                and wait for a response.
+ *   cupsSideChannelDoRequest() - Send a side-channel command to a backend and
+ *                                wait for a response.
  *   cupsSideChannelRead()      - Read a side-channel message.
+ *   cupsSideChannelSNMPGet()   - Query a SNMP OID's value.
+ *   cupsSideChannelSNMPWalk()  - Query multiple SNMP OID values.
  *   cupsSideChannelWrite()     - Write a side-channel message.
  */
 
  */
 
 #include "sidechannel.h"
-#include "string.h"
-#include <unistd.h>
-#include <errno.h>
+#include "string-private.h"
+#include "debug-private.h"
+#ifdef WIN32
+#  include <io.h>
+#else
+#  include <unistd.h>
+#endif /* WIN32 */
 #ifdef __hpux
 #  include <sys/time.h>
-#else
+#elif !defined(WIN32)
 #  include <sys/select.h>
 #endif /* __hpux */
 #ifndef WIN32
@@ -65,7 +62,7 @@
  * pointed to by the "data" parameter.  cupsSideChannelDoRequest() will
  * update the value to contain the number of data bytes in the buffer.
  *
- * @since CUPS 1.3@
+ * @since CUPS 1.3/Mac OS X 10.5@
  */
 
 cups_sc_status_t                       /* O  - Status of command */
@@ -104,7 +101,7 @@ cupsSideChannelDoRequest(
  * pointed to by the "data" parameter.  cupsSideChannelDoRequest() will
  * update the value to contain the number of data bytes in the buffer.
  *
- * @since CUPS 1.3@
+ * @since CUPS 1.3/Mac OS X 10.5@
  */
 
 int                                    /* O - 0 on success, -1 on error */
@@ -118,6 +115,7 @@ cupsSideChannelRead(
   char         buffer[16388];          /* Message buffer */
   int          bytes;                  /* Bytes read */
   int          templen;                /* Data length from message */
+  int          nfds;                   /* Number of file descriptors */
 #ifdef HAVE_POLL
   struct pollfd        pfd;                    /* Poll structure for poll() */
 #else /* select() */
@@ -126,6 +124,10 @@ cupsSideChannelRead(
 #endif /* HAVE_POLL */
 
 
+  DEBUG_printf(("cupsSideChannelRead(command=%p, status=%p, data=%p, "
+                "datalen=%p(%d), timeout=%.3f)", command, status, data,
+               datalen, datalen ? *datalen : -1, timeout));
+
  /*
   * Range check input...
   */
@@ -141,33 +143,31 @@ cupsSideChannelRead(
   pfd.fd     = CUPS_SC_FD;
   pfd.events = POLLIN;
 
-  if (timeout < 0.0)
-  {
-    if (poll(&pfd, 1, -1) < 1)
-      return (-1);
-  }
-  else if (poll(&pfd, 1, (long)(timeout * 1000)) < 1)
-    return (-1);
+  while ((nfds = poll(&pfd, 1, 
+                     timeout < 0.0 ? -1 : (long)(timeout * 1000))) < 0 &&
+        (errno == EINTR || errno == EAGAIN))
+    ;
 
 #else /* select() */
   FD_ZERO(&input_set);
   FD_SET(CUPS_SC_FD, &input_set);
 
-  if (timeout < 0.0)
-  {
-    if (select(CUPS_SC_FD + 1, &input_set, NULL, NULL, NULL) < 1)
-      return (-1);
-  }
-  else
-  {
-    stimeout.tv_sec  = (int)timeout;
-    stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
+  stimeout.tv_sec  = (int)timeout;
+  stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
+
+  while ((nfds = select(CUPS_SC_FD + 1, &input_set, NULL, NULL, 
+                       timeout < 0.0 ? NULL : &stimeout)) < 0 &&
+        (errno == EINTR || errno == EAGAIN))
+    ;
 
-    if (select(CUPS_SC_FD + 1, &input_set, NULL, NULL, &stimeout) < 1)
-      return (-1);
-  }
 #endif /* HAVE_POLL */
 
+  if  (nfds < 1)
+  {
+    *status = nfds==0 ? CUPS_SC_STATUS_TIMEOUT : CUPS_SC_STATUS_IO_ERROR;
+    return (-1);
+  }
+
  /*
   * Read a side-channel message for the format:
   *
@@ -181,14 +181,37 @@ cupsSideChannelRead(
 
   while ((bytes = read(CUPS_SC_FD, buffer, sizeof(buffer))) < 0)
     if (errno != EINTR && errno != EAGAIN)
+    {
+      DEBUG_printf(("1cupsSideChannelRead: Read error: %s", strerror(errno)));
+      *command = CUPS_SC_CMD_NONE;
+      *status  = CUPS_SC_STATUS_IO_ERROR;
       return (-1);
+    }
+
+ /*
+  * Watch for EOF or too few bytes...
+  */
+
+  if (bytes < 4)
+  {
+    DEBUG_printf(("1cupsSideChannelRead: Short read of %d bytes", bytes));
+    *command = CUPS_SC_CMD_NONE;
+    *status  = CUPS_SC_STATUS_BAD_MESSAGE;
+    return (-1);
+  }
 
  /*
   * Validate the command code in the message...
   */
 
-  if (buffer[0] < CUPS_SC_CMD_SOFT_RESET || buffer[0] > CUPS_SC_CMD_GET_STATE)
+  if (buffer[0] < CUPS_SC_CMD_SOFT_RESET ||
+      buffer[0] > CUPS_SC_CMD_SNMP_GET_NEXT)
+  {
+    DEBUG_printf(("1cupsSideChannelRead: Bad command %d!", buffer[0]));
+    *command = CUPS_SC_CMD_NONE;
+    *status  = CUPS_SC_STATUS_BAD_MESSAGE;
     return (-1);
+  }
 
   *command = (cups_sc_command_t)buffer[0];
 
@@ -207,7 +230,7 @@ cupsSideChannelRead(
 
     *status = CUPS_SC_STATUS_TOO_BIG;
   }
-  else if (templen > *datalen || templen > (bytes - 4))
+  else if (!datalen || templen > *datalen || templen > (bytes - 4))
   {
    /*
     * Either the response is bigger than the provided buffer or the
@@ -229,17 +252,229 @@ cupsSideChannelRead(
     memcpy(data, buffer + 4, templen);
   }
 
+  DEBUG_printf(("1cupsSideChannelRead: Returning status=%d", *status));
+
   return (0);
 }
 
 
+/*
+ * 'cupsSideChannelSNMPGet()' - Query a SNMP OID's value.
+ *
+ * This function asks the backend to do a SNMP OID query on behalf of the
+ * filter, port monitor, or backend using the default community name.
+ *
+ * "oid" contains a numeric OID consisting of integers separated by periods,
+ * for example ".1.3.6.1.2.1.43".  Symbolic names from SNMP MIBs are not
+ * supported and must be converted to their numeric forms.
+ *
+ * On input, "data" and "datalen" provide the location and size of the
+ * buffer to hold the OID value as a string. HEX-String (binary) values are
+ * converted to hexadecimal strings representing the binary data, while
+ * NULL-Value and unknown OID types are returned as the empty string.
+ * The returned "datalen" does not include the trailing nul.
+ *
+ * @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
+ * support SNMP queries.  @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
+ * the printer does not respond to the SNMP query.
+ *
+ * @since CUPS 1.4/Mac OS X 10.6@ 
+ */
+
+cups_sc_status_t                       /* O  - Query status */
+cupsSideChannelSNMPGet(
+    const char *oid,                   /* I  - OID to query */
+    char       *data,                  /* I  - Buffer for OID value */
+    int        *datalen,               /* IO - Size of OID buffer on entry, size of value on return */
+    double     timeout)                        /* I  - Timeout in seconds */
+{
+  cups_sc_status_t     status;         /* Status of command */
+  cups_sc_command_t    rcommand;       /* Response command */
+  char                 real_data[2048];/* Real data buffer for response */
+  int                  real_datalen,   /* Real length of data buffer */
+                       real_oidlen;    /* Length of returned OID string */
+
+
+  DEBUG_printf(("cupsSideChannelSNMPGet(oid=\"%s\", data=%p, datalen=%p(%d), "
+                "timeout=%.3f)", oid, data, datalen, datalen ? *datalen : -1,
+               timeout));
+
+ /*
+  * Range check input...
+  */
+
+  if (!oid || !*oid || !data || !datalen || *datalen < 2)
+    return (CUPS_SC_STATUS_BAD_MESSAGE);
+
+  *data = '\0';
+
+ /*
+  * Send the request to the backend and wait for a response...
+  */
+
+  if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET, CUPS_SC_STATUS_NONE, oid,
+                           (int)strlen(oid) + 1, timeout))
+    return (CUPS_SC_STATUS_TIMEOUT);
+
+  real_datalen = sizeof(real_data);
+  if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen, timeout))
+    return (CUPS_SC_STATUS_TIMEOUT);
+
+  if (rcommand != CUPS_SC_CMD_SNMP_GET)
+    return (CUPS_SC_STATUS_BAD_MESSAGE);
+
+  if (status == CUPS_SC_STATUS_OK)
+  {
+   /*
+    * Parse the response of the form "oid\0value"...
+    */
+
+    real_oidlen  = strlen(real_data) + 1;
+    real_datalen -= real_oidlen;
+
+    if ((real_datalen + 1) > *datalen)
+      return (CUPS_SC_STATUS_TOO_BIG);
+
+    memcpy(data, real_data + real_oidlen, real_datalen);
+    data[real_datalen] = '\0';
+
+    *datalen = real_datalen;
+  }
+
+  return (status);
+}
+
+
+/*
+ * 'cupsSideChannelSNMPWalk()' - Query multiple SNMP OID values.
+ *
+ * This function asks the backend to do multiple SNMP OID queries on behalf
+ * of the filter, port monitor, or backend using the default community name.
+ * All OIDs under the "parent" OID are queried and the results are sent to
+ * the callback function you provide.
+ *
+ * "oid" contains a numeric OID consisting of integers separated by periods,
+ * for example ".1.3.6.1.2.1.43".  Symbolic names from SNMP MIBs are not
+ * supported and must be converted to their numeric forms.
+ *
+ * "timeout" specifies the timeout for each OID query. The total amount of
+ * time will depend on the number of OID values found and the time required
+ * for each query.
+ *
+ * "cb" provides a function to call for every value that is found. "context"
+ * is an application-defined pointer that is sent to the callback function
+ * along with the OID and current data. The data passed to the callback is the
+ * same as returned by @link cupsSideChannelSNMPGet@.
+ *
+ * @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
+ * support SNMP queries.  @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
+ * the printer does not respond to the first SNMP query.
+ *
+ * @since CUPS 1.4/Mac OS X 10.6@ 
+ */
+
+cups_sc_status_t                       /* O - Status of first query of @code CUPS_SC_STATUS_OK@ on success */
+cupsSideChannelSNMPWalk(
+    const char          *oid,          /* I - First numeric OID to query */
+    double              timeout,       /* I - Timeout for each query in seconds */
+    cups_sc_walk_func_t cb,            /* I - Function to call with each value */
+    void                *context)      /* I - Application-defined pointer to send to callback */
+{
+  cups_sc_status_t     status;         /* Status of command */
+  cups_sc_command_t    rcommand;       /* Response command */
+  char                 real_data[2048];/* Real data buffer for response */
+  int                  real_datalen,   /* Real length of data buffer */
+                       real_oidlen,    /* Length of returned OID string */
+                       oidlen;         /* Length of first OID */
+  const char           *current_oid;   /* Current OID */
+  char                 last_oid[2048]; /* Last OID */
+
+
+  DEBUG_printf(("cupsSideChannelSNMPWalk(oid=\"%s\", timeout=%.3f, cb=%p, "
+                "context=%p)", oid, timeout, cb, context));
+
+ /*
+  * Range check input...
+  */
+
+  if (!oid || !*oid || !cb)
+    return (CUPS_SC_STATUS_BAD_MESSAGE);
+
+ /*
+  * Loop until the OIDs don't match...
+  */
+
+  current_oid = oid;
+  oidlen      = (int)strlen(oid);
+  last_oid[0] = '\0';
+
+  do
+  {
+   /*
+    * Send the request to the backend and wait for a response...
+    */
+
+    if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET_NEXT, CUPS_SC_STATUS_NONE,
+                             current_oid, (int)strlen(current_oid) + 1, timeout))
+      return (CUPS_SC_STATUS_TIMEOUT);
+
+    real_datalen = sizeof(real_data);
+    if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen,
+                            timeout))
+      return (CUPS_SC_STATUS_TIMEOUT);
+
+    if (rcommand != CUPS_SC_CMD_SNMP_GET_NEXT)
+      return (CUPS_SC_STATUS_BAD_MESSAGE);
+
+    if (status == CUPS_SC_STATUS_OK)
+    {
+     /*
+      * Parse the response of the form "oid\0value"...
+      */
+
+      if (strncmp(real_data, oid, oidlen) || real_data[oidlen] != '.' ||
+          !strcmp(real_data, last_oid))
+      {
+       /*
+        * Done with this set of OIDs...
+       */
+
+        return (CUPS_SC_STATUS_OK);
+      }
+
+      if (real_datalen < sizeof(real_data))
+        real_data[real_datalen] = '\0';
+
+      real_oidlen  = strlen(real_data) + 1;
+      real_datalen -= real_oidlen;
+
+     /*
+      * Call the callback with the OID and data...
+      */
+
+      (*cb)(real_data, real_data + real_oidlen, real_datalen, context); 
+
+     /*
+      * Update the current OID...
+      */
+
+      current_oid = real_data;
+      strlcpy(last_oid, current_oid, sizeof(last_oid));
+    }
+  }
+  while (status == CUPS_SC_STATUS_OK);
+
+  return (status);
+}
+
+
 /*
  * 'cupsSideChannelWrite()' - Write a side-channel message.
  *
  * This function is normally only called by backend programs to send
  * responses to a filter, driver, or port monitor program.
  *
- * @since CUPS 1.3@
+ * @since CUPS 1.3/Mac OS X 10.5@
  */
 
 int                                    /* O - 0 on success, -1 on error */
@@ -264,7 +499,7 @@ cupsSideChannelWrite(
   * Range check input...
   */
 
-  if (command < CUPS_SC_CMD_SOFT_RESET || command > CUPS_SC_CMD_GET_STATE ||
+  if (command < CUPS_SC_CMD_SOFT_RESET || command > CUPS_SC_CMD_SNMP_GET_NEXT ||
       datalen < 0 || datalen > 16384 || (datalen > 0 && !data))
     return (-1);
 
@@ -336,5 +571,5 @@ cupsSideChannelWrite(
 
 
 /*
- * End of "$Id: sidechannel.c 6319 2007-03-06 18:51:40Z mike $".
+ * End of "$Id: sidechannel.c 7720 2008-07-11 22:46:21Z mike $".
  */