]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - backend/snmp.c
Load cups into easysw/current.
[thirdparty/cups.git] / backend / snmp.c
index 56e7ad9e79b48713d4ea5e8b59ecffb1e1b3c714..4a90f2ebc09db5d6457cbde811e9f99473a61068 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: snmp.c 5736 2006-07-13 19:59:36Z mike $"
+ * "$Id: snmp.c 5898 2006-08-28 18:54:10Z mike $"
  *
  *   SNMP discovery backend for the Common UNIX Printing System (CUPS).
  *
@@ -52,6 +52,7 @@
  *                               packed integer value.
  *   compare_cache()           - Compare two cache entries.
  *   debug_printf()            - Display some debugging information.
+ *   do_request()              - Do a non-blocking IPP request.
  *   fix_make_model()          - Fix common problems in the make-and-model
  *                               string.
  *   free_array()              - Free an array of strings.
@@ -59,7 +60,7 @@
  *   get_interface_addresses() - Get the broadcast address(es) associated
  *                               with an interface.
  *   hex_debug()               - Output hex debugging data...
- *   list_devices()            - List all of the devices we found...
+ *   list_device()             - List a device we found...
  *   open_snmp_socket()        - Open the SNMP broadcast socket.
  *   password_cb()             - Handle authentication requests.
  *   probe_device()            - Probe a device to discover whether it is a
@@ -193,6 +194,13 @@ typedef struct snmp_packet_s               /**** SNMP packet ****/
 } snmp_packet_t;
 
 
+/*
+ * Private CUPS API to set the last error...
+ */
+
+extern void    _cupsSetError(ipp_status_t status, const char *message);
+
+
 /*
  * Local functions...
  */
@@ -238,6 +246,8 @@ static int          asn1_size_oid(const int *oid);
 static int             asn1_size_packed(int integer);
 static int             compare_cache(snmp_cache_t *a, snmp_cache_t *b);
 static void            debug_printf(const char *format, ...);
+static ipp_t           *do_request(http_t *http, ipp_t *request,
+                                   const char *resource);
 static void            fix_make_model(char *make_model,
                                       const char *old_make_model,
                                       int make_model_size);
@@ -245,7 +255,7 @@ static void         free_array(cups_array_t *a);
 static void            free_cache(void);
 static http_addrlist_t *get_interface_addresses(const char *ifname);
 static void            hex_debug(unsigned char *buffer, size_t len);
-static void            list_devices(void);
+static void            list_device(snmp_cache_t *cache);
 static int             open_snmp_socket(void);
 static const char      *password_cb(const char *prompt);
 static void            probe_device(snmp_cache_t *device);
@@ -278,6 +288,7 @@ static int          DeviceDescOID[] = { 1, 3, 6, 1, 2, 1, 25, 3,
 static unsigned                DeviceTypeRequest;
 static unsigned                DeviceDescRequest;
 static int             HostNameLookups = 0;
+static int             MaxRunTime = 10;
 static struct timeval  StartTime;
 
 
@@ -290,6 +301,9 @@ main(int  argc,                             /* I - Number of command-line arguments (6 or 7) */
      char *argv[])                     /* I - Command-line arguments */
 {
   int          fd;                     /* SNMP socket */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
+  struct sigaction action;             /* Actions for POSIX signals */
+#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
 
 
  /*
@@ -308,6 +322,23 @@ main(int  argc,                            /* I - Number of command-line arguments (6 or 7) */
 
   cupsSetPasswordCB(password_cb);
 
+ /*
+  * Catch SIGALRM signals...
+  */
+
+#ifdef HAVE_SIGSET
+  sigset(SIGALRM, alarm_handler);
+#elif defined(HAVE_SIGACTION)
+  memset(&action, 0, sizeof(action));
+
+  sigemptyset(&action.sa_mask);
+  sigaddset(&action.sa_mask, SIGALRM);
+  action.sa_handler = alarm_handler;
+  sigaction(SIGALRM, &action, NULL);
+#else
+  signal(SIGALRM, alarm_handler);
+#endif /* HAVE_SIGSET */
+
  /*
   * Open the SNMP socket...
   */
@@ -329,12 +360,6 @@ main(int  argc,                            /* I - Number of command-line arguments (6 or 7) */
 
   scan_devices(fd);
 
- /*
-  * Display the results...
-  */
-
-  list_devices();
-
  /*
   * Close, free, and return with no errors...
   */
@@ -384,7 +409,7 @@ add_cache(http_addr_t *addr,                /* I - Device IP address */
 
   debug_printf("DEBUG: add_cache(addr=%p, addrname=\"%s\", uri=\"%s\", "
                   "id=\"%s\", make_and_model=\"%s\")\n",
-               addr, addrname, uri ? uri : "(null)", id ? id :  "(null)",
+               addr, addrname, uri ? uri : "(null)", id ? id : "(null)",
               make_and_model ? make_and_model : "(null)");
 
   temp = calloc(1, sizeof(snmp_cache_t));
@@ -402,6 +427,9 @@ add_cache(http_addr_t *addr,                /* I - Device IP address */
     temp->make_and_model = strdup(make_and_model);
 
   cupsArrayAdd(Devices, temp);
+
+  if (uri)
+    list_device(temp);
 }
 
 
@@ -418,6 +446,10 @@ alarm_handler(int sig)                     /* I - Signal number */
 
   (void)sig;
 
+#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
+  signal(SIGALRM, alarm_handler);
+#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
+
   if (DebugLevel)
     write(2, "DEBUG: ALARM!\n", 14);
 }
@@ -1227,6 +1259,173 @@ debug_printf(const char *format,        /* I - Printf-style format string */
 }
 
 
+/*
+ * 'do_request()' - Do a non-blocking IPP request.
+ */
+
+static ipp_t *                         /* O - Response data or NULL */
+do_request(http_t     *http,           /* I - HTTP connection to server */
+           ipp_t      *request,                /* I - IPP request */
+           const char *resource)       /* I - HTTP resource for POST */
+{
+  ipp_t                *response;              /* IPP response data */
+  http_status_t        status;                 /* Status of HTTP request */
+  ipp_state_t  state;                  /* State of IPP processing */
+
+
+ /*
+  * Setup the HTTP variables needed...
+  */
+
+  httpClearFields(http);
+  httpSetLength(http, ippLength(request));
+  httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
+
+ /*
+  * Do the POST request...
+  */
+
+  debug_printf("DEBUG: %.3f POST %s...\n", run_time(), resource);
+
+  if (httpPost(http, resource))
+  {
+    if (httpReconnect(http))
+    {
+      _cupsSetError(IPP_DEVICE_ERROR, "Unable to reconnect");
+      return (NULL);
+    }
+    else if (httpPost(http, resource))
+    {
+      _cupsSetError(IPP_GONE, "Unable to POST");
+      return (NULL);
+    }
+  }
+
+ /*
+  * Send the IPP data...
+  */
+
+  request->state = IPP_IDLE;
+  status         = HTTP_CONTINUE;
+
+  while ((state = ippWrite(http, request)) != IPP_DATA)
+    if (state == IPP_ERROR)
+    {
+      status = HTTP_ERROR;
+      break;
+    }
+    else if (httpCheck(http))
+    {
+      if ((status = httpUpdate(http)) != HTTP_CONTINUE)
+       break;
+    }
+
+ /*
+  * Get the server's return status...
+  */
+
+  debug_printf("DEBUG: %.3f Getting response...\n", run_time());
+
+  while (status == HTTP_CONTINUE)
+    if (httpWait(http, 1000))
+      status = httpUpdate(http);
+    else
+    {
+      status      = HTTP_ERROR;
+      http->error = ETIMEDOUT;
+    }
+
+  if (status != HTTP_OK)
+  {
+   /*
+    * Flush any error message...
+    */
+
+    httpFlush(http);
+    response = NULL;
+  }
+  else
+  {
+   /*
+    * Read the response...
+    */
+
+    response = ippNew();
+
+    while ((state = ippRead(http, response)) != IPP_DATA)
+      if (state == IPP_ERROR)
+      {
+       /*
+        * Delete the response...
+       */
+
+       ippDelete(response);
+       response = NULL;
+
+        _cupsSetError(IPP_SERVICE_UNAVAILABLE, strerror(errno));
+       break;
+      }
+  }
+
+ /*
+  * Delete the original request and return the response...
+  */
+  
+  ippDelete(request);
+
+  if (response)
+  {
+    ipp_attribute_t    *attr;          /* status-message attribute */
+
+
+    attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
+
+    _cupsSetError(response->request.status.status_code,
+                   attr ? attr->values[0].string.text :
+                      ippErrorString(response->request.status.status_code));
+  }
+  else if (status != HTTP_OK)
+  {
+    switch (status)
+    {
+      case HTTP_NOT_FOUND :
+          _cupsSetError(IPP_NOT_FOUND, httpStatus(status));
+         break;
+
+      case HTTP_UNAUTHORIZED :
+          _cupsSetError(IPP_NOT_AUTHORIZED, httpStatus(status));
+         break;
+
+      case HTTP_FORBIDDEN :
+          _cupsSetError(IPP_FORBIDDEN, httpStatus(status));
+         break;
+
+      case HTTP_BAD_REQUEST :
+          _cupsSetError(IPP_BAD_REQUEST, httpStatus(status));
+         break;
+
+      case HTTP_REQUEST_TOO_LARGE :
+          _cupsSetError(IPP_REQUEST_VALUE, httpStatus(status));
+         break;
+
+      case HTTP_NOT_IMPLEMENTED :
+          _cupsSetError(IPP_OPERATION_NOT_SUPPORTED, httpStatus(status));
+         break;
+
+      case HTTP_NOT_SUPPORTED :
+          _cupsSetError(IPP_VERSION_NOT_SUPPORTED, httpStatus(status));
+         break;
+
+      default :
+         _cupsSetError(IPP_SERVICE_UNAVAILABLE, httpStatus(status));
+         break;
+    }
+  }
+
+  return (response);
+}
+
+
 /*
  * 'fix_make_model()' - Fix common problems in the make-and-model string.
  */
@@ -1288,6 +1487,16 @@ fix_make_model(
     _cups_strcpy(mmptr, mmptr + 7);
   }
 
+  if ((mmptr = strstr(make_model, " Network")) != NULL)
+  {
+   /*
+    * Drop unnecessary informational text, e.g. "Xerox DocuPrint N2025
+    * Network LaserJet - 2.12" becomes "Xerox DocuPrint N2025"...
+    */
+
+    *mmptr = '\0';
+  }
+
   if ((mmptr = strchr(make_model, ',')) != NULL)
   {
    /*
@@ -1422,24 +1631,18 @@ hex_debug(unsigned char *buffer,        /* I - Buffer */
 
 
 /*
- * 'list_devices()' - List all of the devices we found...
+ * 'list_device()' - List a device we found...
  */
 
 static void
-list_devices(void)
+list_device(snmp_cache_t *cache)       /* I - Cached device */
 {
-  snmp_cache_t *cache;                 /* Cached device */
-
-
-  for (cache = (snmp_cache_t *)cupsArrayFirst(Devices);
-       cache;
-       cache = (snmp_cache_t *)cupsArrayNext(Devices))
-    if (cache->uri)
-      printf("network %s \"%s\" \"%s %s\" \"%s\"\n",
-             cache->uri,
-            cache->make_and_model ? cache->make_and_model : "Unknown",
-            cache->make_and_model ? cache->make_and_model : "Unknown",
-            cache->addrname, cache->id ? cache->id : "");
+  if (cache->uri)
+    printf("network %s \"%s\" \"%s %s\" \"%s\"\n",
+           cache->uri,
+          cache->make_and_model ? cache->make_and_model : "Unknown",
+          cache->make_and_model ? cache->make_and_model : "Unknown",
+          cache->addrname, cache->id ? cache->id : "");
 }
 
 
@@ -1524,7 +1727,29 @@ probe_device(snmp_cache_t *device)       /* I - Device */
 
   debug_printf("DEBUG: %.3f Probing %s...\n", run_time(), device->addrname);
 
-  if ((http = httpConnect(device->addrname, 631)) != NULL)
+  if (device->make_and_model &&
+      (!strncasecmp(device->make_and_model, "Xerox", 5) ||
+       !strncasecmp(device->make_and_model, "Kyocera", 7)))
+  {
+   /*
+    * Xerox and Kyocera printers often lock up on IPP probes, so exclude
+    * them from the IPP connection test...
+    */
+
+    http = NULL;
+  }
+  else
+  {
+   /*
+    * Otherwise, try connecting for up to 1 second...
+    */
+
+    alarm(1);
+    http = httpConnect(device->addrname, 631);
+    alarm(0);
+  }
+
+  if (http);
   {
    /*
     * IPP is supported...
@@ -1551,8 +1776,6 @@ probe_device(snmp_cache_t *device)        /* I - Device */
                        };
 
 
-    debug_printf("DEBUG: %s supports IPP!\n", device->addrname);
-
    /*
     * Use non-blocking IO...
     */
@@ -1568,6 +1791,13 @@ probe_device(snmp_cache_t *device)       /* I - Device */
          i < (int)(sizeof(resources) / sizeof(resources[0]));
          i ++)
     {
+     /*
+      * Stop early if we are out of time...
+      */
+
+      if (MaxRunTime > 0 && run_time() >= MaxRunTime)
+        break;
+
      /*
       * Don't look past /ipp if we have found a working URI...
       */
@@ -1578,16 +1808,14 @@ probe_device(snmp_cache_t *device)      /* I - Device */
       httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
                       device->addrname, 631, resources[i]);
 
-      debug_printf("DEBUG: Trying %s (num_uris=%d)\n", uri, num_uris);
-
       request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
 
       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
                    NULL, uri);
 
-      response = cupsDoRequest(http, request, resources[i]);
+      response = do_request(http, request, resources[i]);
 
-      debug_printf("DEBUG: %s %s (%s)\n", uri,
+      debug_printf("DEBUG: %.3f %s %s (%s)\n", run_time(), uri,
                   ippErrorString(cupsLastError()), cupsLastErrorString());
 
       if (response && response->request.status.status_code == IPP_OK)
@@ -1726,6 +1954,7 @@ read_snmp_conf(const char *address)       /* I - Single address to probe */
   int          linenum;                /* Line number */
   const char   *cups_serverroot;       /* CUPS_SERVERROOT env var */
   const char   *debug;                 /* CUPS_DEBUG_LEVEL env var */
+  const char   *runtime;               /* CUPS_MAX_RUN_TIME env var */
 
 
  /*
@@ -1741,6 +1970,9 @@ read_snmp_conf(const char *address)       /* I - Single address to probe */
   if ((debug = getenv("CUPS_DEBUG_LEVEL")) != NULL)
     DebugLevel = atoi(debug);
 
+  if ((runtime = getenv("CUPS_MAX_RUN_TIME")) != NULL)
+    MaxRunTime = atoi(runtime);
+
  /*
   * Find the snmp.conf file...
   */
@@ -1777,6 +2009,8 @@ read_snmp_conf(const char *address)       /* I - Single address to probe */
                          !strcasecmp(value, "yes") ||
                          !strcasecmp(value, "true") ||
                          !strcasecmp(value, "double");
+      else if (!strcasecmp(line, "MaxRunTime"))
+        MaxRunTime = atoi(value);
       else
         fprintf(stderr, "ERROR: Unknown directive %s on line %d of %s!\n",
                line, linenum, filename);
@@ -2075,7 +2309,9 @@ scan_devices(int fd)                      /* I - SNMP socket */
   for (device = (snmp_cache_t *)cupsArrayFirst(Devices);
        device;
        device = (snmp_cache_t *)cupsArrayNext(Devices))
-    if (!device->uri)
+    if (MaxRunTime > 0 && run_time() >= MaxRunTime)
+      break;
+    else if (!device->uri)
       probe_device(device);
 
   debug_printf("DEBUG: %.3f Scan complete!\n", run_time());
@@ -2169,7 +2405,6 @@ try_connect(http_addr_t *addr,            /* I - Socket address */
 
   addr->ipv4.sin_port = htons(port);
 
-  signal(SIGALRM, alarm_handler);
   alarm(1);
 
   status = connect(fd, (void *)addr, httpAddrLength(addr));
@@ -2211,9 +2446,11 @@ update_cache(snmp_cache_t *device,       /* I - Device */
 
     device->make_and_model = strdup(make_model);
   }
+
+  list_device(device);
 }
 
 
 /*
- * End of "$Id: snmp.c 5736 2006-07-13 19:59:36Z mike $".
+ * End of "$Id: snmp.c 5898 2006-08-28 18:54:10Z mike $".
  */