]> git.ipfire.org Git - thirdparty/cups.git/commitdiff
Update CUPS-Add-Modify-Printer and CUPS-Create-Local-Printer code to delay
authorMichael R Sweet <msweet@msweet.org>
Thu, 4 Apr 2024 18:38:34 +0000 (14:38 -0400)
committerMichael R Sweet <msweet@msweet.org>
Thu, 4 Apr 2024 18:38:34 +0000 (14:38 -0400)
responding until the PPD is successfully generated (Issue #347)

CHANGES.md
scheduler/auth.h
scheduler/client.h
scheduler/cupsd.h
scheduler/ipp.c
scheduler/policy.h
scheduler/printers.h
xcode/CUPS.xcodeproj/project.pbxproj

index 1441b2e09172712af35fdaedaa7333f65a042f91..68bc468dfb6a1ca2b05ab080ca597691e24ef919 100644 (file)
@@ -20,6 +20,7 @@ Changes in CUPS v2.5b1 (TBA)
 - Updated CUPS to require ZLIB.
 - Updated CUPS to require support for `poll` API.
 - Updated `cupsArray` APIs to support modern naming and types.
+- Updated IPP Everywhere printer creation error reporting (Issue #347)
 - Updated internal usage of CUPS array API to include callback pointer even when
   not used (Issue #674)
 - Updated `cups_enum_dests()` timeout for listing available IPP printers
index b1f1d60acf4cdff4bcdc05abb46251d242aea7f5..409c03061eeb9ac069322b23872cf97b06c3a5e7 100644 (file)
@@ -98,8 +98,6 @@ typedef struct
   http_encryption_t    encryption;     /* To encrypt or not to encrypt... */
 } cupsd_location_t;
 
-typedef struct cupsd_client_s cupsd_client_t;
-
 
 /*
  * Globals...
index 5e8daf4eaa3166dbaf0421be4a6ff84df1dc77fa..0498c7e2556ebd7caf114417f7f48bd70dec1a77 100644 (file)
@@ -42,6 +42,8 @@ struct cupsd_client_s
                        *query_string;  /* QUERY_STRING environment variable */
   int                  file;           /* Input/output file */
   int                  file_ready;     /* Input ready on file/pipe? */
+  int                  bg_pending;     /* Background response pending? */
+  cupsd_printer_t      *bg_printer;    /* Background printer */
   int                  pipe_pid;       /* Pipe process ID (or 0 if not a pipe) */
   http_status_t                pipe_status;    /* HTTP status from pipe process */
   int                  sent_header,    /* Non-zero if sent HTTP header */
index 68b3d8edffba0ea7d1b44a677d17c2360f97b143..cf2023fc6f4fdc668293dc340f3325ec0c6d716b 100644 (file)
@@ -100,6 +100,15 @@ extern const char *cups_hstrerror(int);
 #endif /* _MAIN_C */
 
 
+/*
+ * Base types...
+ */
+
+typedef struct cupsd_client_s cupsd_client_t;
+typedef struct cupsd_job_s cupsd_job_t;
+typedef struct cupsd_printer_s cupsd_printer_t;
+
+
 /*
  * Other stuff for the scheduler...
  */
index cc266398e6098f79084610cf6bff7f20927086a6..1611f9ab9fea6aa36a53437b5872d0fd23a6d8a6 100644 (file)
@@ -73,7 +73,7 @@ static void   copy_subscription_attrs(cupsd_client_t *con,
                                        cups_array_t *ra,
                                        cups_array_t *exclude);
 static void    create_job(cupsd_client_t *con, ipp_attribute_t *uri);
-static void    *create_local_bg_thread(cupsd_printer_t *printer);
+static void    *create_local_bg_thread(cupsd_client_t *con);
 static void    create_local_printer(cupsd_client_t *con);
 static cups_array_t *create_requested_array(ipp_t *request);
 static void    create_subscriptions(cupsd_client_t *con, ipp_attribute_t *uri);
@@ -111,6 +111,7 @@ static void send_document(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    send_http_error(cupsd_client_t *con, http_status_t status,
                                cupsd_printer_t *printer);
 static void    send_ipp_status(cupsd_client_t *con, ipp_status_t status, const char *message, ...) _CUPS_FORMAT(3, 4);
+static int     send_response(cupsd_client_t *con);
 static void    set_default(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    set_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    set_printer_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
@@ -615,68 +616,13 @@ cupsdProcessIPPRequest(
     }
   }
 
-  if (con->response)
+  if (!con->bg_pending && con->response)
   {
    /*
     * Sending data from the scheduler...
     */
 
-    cupsdLogClient(con, con->response->request.status.status_code >= IPP_STATUS_ERROR_BAD_REQUEST && con->response->request.status.status_code != IPP_STATUS_ERROR_NOT_FOUND ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG, "Returning IPP %s for %s (%s) from %s.",  ippErrorString(con->response->request.status.status_code), ippOpString(con->request->request.op.operation_id), uri ? uri->values[0].string.text : "no URI", con->http->hostname);
-
-    httpClearFields(con->http);
-
-#ifdef CUPSD_USE_CHUNKING
-   /*
-    * Because older versions of CUPS (1.1.17 and older) and some IPP
-    * clients do not implement chunking properly, we cannot use
-    * chunking by default.  This may become the default in future
-    * CUPS releases, or we might add a configuration directive for
-    * it.
-    */
-
-    if (con->http->version == HTTP_1_1)
-    {
-      cupsdLogClient(con, CUPSD_LOG_DEBUG, "Transfer-Encoding: chunked");
-      cupsdSetLength(con->http, 0);
-    }
-    else
-#endif /* CUPSD_USE_CHUNKING */
-    {
-      size_t   length;                 /* Length of response */
-
-
-      length = ippLength(con->response);
-
-      if (con->file >= 0 && !con->pipe_pid)
-      {
-       struct stat     fileinfo;       /* File information */
-
-       if (!fstat(con->file, &fileinfo))
-         length += (size_t)fileinfo.st_size;
-      }
-
-      cupsdLogClient(con, CUPSD_LOG_DEBUG, "Content-Length: " CUPS_LLFMT, CUPS_LLCAST length);
-      httpSetLength(con->http, length);
-    }
-
-    if (cupsdSendHeader(con, HTTP_STATUS_OK, "application/ipp", CUPSD_AUTH_NONE))
-    {
-     /*
-      * Tell the caller the response header was sent successfully...
-      */
-
-      cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, (cupsd_selfunc_t)cupsdWriteClient, con);
-
-      return (1);
-    }
-    else
-    {
-     /*
-      * Tell the caller the response header could not be sent...
-      */
-
-      return (0);
-    }
+    return (send_response(con));
   }
   else
   {
@@ -2715,8 +2661,21 @@ add_printer(cupsd_client_t  *con,        /* I - Client connection */
        return;
       }
 
-      // Run a background thread to create the PPD...
-      cupsThreadCreate((cups_thread_func_t)create_local_bg_thread, printer);
+      if (!printer->printer_id)
+       printer->printer_id = NextPrinterId ++;
+
+      cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
+
+      cupsdSetPrinterAttrs(printer);
+
+      /* Run a background thread to create the PPD... */
+      cupsdLogClient(con, CUPSD_LOG_DEBUG, "Creating PPD in background thread.");
+
+      con->bg_pending = 1;
+      con->bg_printer = printer;
+
+      cupsThreadCreate((cups_thread_func_t)create_local_bg_thread, con);
+      return;
     }
     else if (!strcmp(ppd_name, "raw"))
     {
@@ -5281,11 +5240,14 @@ create_job(cupsd_client_t  *con,        /* I - Client connection */
 
 static void *                          /* O - Exit status */
 create_local_bg_thread(
-    cupsd_printer_t *printer)          /* I - Printer */
+    cupsd_client_t *con)               /* I - Client */
 {
+  cupsd_printer_t *printer = con->bg_printer;
+                                       /* Printer */
   cups_file_t  *from,                  /* Source file */
                *to;                    /* Destination file */
-  char         fromppd[1024],          /* Source PPD */
+  char         device_uri[1024],       /* Device URI */
+               fromppd[1024],          /* Source PPD */
                toppd[1024],            /* Destination PPD */
                scheme[32],             /* URI scheme */
                userpass[256],          /* User:pass */
@@ -5297,7 +5259,7 @@ create_local_bg_thread(
   http_encryption_t encryption;                /* Type of encryption to use */
   http_t       *http;                  /* Connection to printer */
   ipp_t                *request,               /* Request to printer */
-               *response;              /* Response from printer */
+               *response = NULL;       /* Response from printer */
   ipp_attribute_t *attr;               /* Attribute in response */
   ipp_status_t status;                 /* Status code */
   static const char * const pattrs[] = /* Printer attributes we need */
@@ -5311,26 +5273,47 @@ create_local_bg_thread(
   * Try connecting to the printer...
   */
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "%s: Generating PPD file from \"%s\"...", printer->name, printer->device_uri);
+  cupsRWLockRead(&printer->lock);
+  cupsCopyString(device_uri, printer->device_uri, sizeof(device_uri));
+  cupsRWUnlock(&printer->lock);
 
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "%s: Generating PPD file from \"%s\"...", printer->name, device_uri);
 
-  if (strstr(printer->device_uri, "._tcp"))
+  if (strstr(device_uri, "._tcp"))
   {
-    cupsdLogMessage(CUPSD_LOG_DEBUG2, "%s: Resolving mDNS URI \"%s\".", printer->name, printer->device_uri);
+    cupsdLogMessage(CUPSD_LOG_DEBUG2, "%s: Resolving mDNS URI \"%s\".", printer->name, device_uri);
 
-    if (!httpResolveURI(printer->device_uri, uri, sizeof(uri), HTTP_RESOLVE_DEFAULT, NULL, NULL))
+    if (!httpResolveURI(device_uri, uri, sizeof(uri), HTTP_RESOLVE_DEFAULT, NULL, NULL))
     {
       cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Couldn't resolve mDNS URI \"%s\".", printer->name, printer->device_uri);
-      return (NULL);
+
+      /* Force printer to timeout and be deleted */
+      cupsRWLockWrite(&printer->lock);
+      printer->state_time = 0;
+      cupsRWUnlock(&printer->lock);
+
+      send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Couldn't resolve mDNS URI \"%s\"."), printer->device_uri);
+      goto finish_response;
     }
 
+    cupsRWLockWrite(&printer->lock);
     cupsdSetString(&printer->device_uri, uri);
+    cupsRWUnlock(&printer->lock);
+
+    cupsCopyString(device_uri, uri, sizeof(device_uri));
   }
 
-  if (httpSeparateURI(HTTP_URI_CODING_ALL, printer->device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
+  if (httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
   {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Bad device URI \"%s\".", printer->name, printer->device_uri);
-    return (NULL);
+    cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Bad device URI \"%s\".", printer->name, device_uri);
+
+    /* Force printer to timeout and be deleted */
+    cupsRWLockWrite(&printer->lock);
+    printer->state_time = 0;
+    cupsRWUnlock(&printer->lock);
+
+    send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Bad device URI \"%s\"."), device_uri);
+    goto finish_response;
   }
 
   if (!strcmp(scheme, "ipps") || port == 443)
@@ -5341,7 +5324,14 @@ create_local_bg_thread(
   if ((http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL)
   {
     cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Unable to connect to %s:%d: %s", printer->name, host, port, cupsGetErrorString());
-    return (NULL);
+
+    /* Force printer to timeout and be deleted */
+    cupsRWLockWrite(&printer->lock);
+    printer->state_time = 0;
+    cupsRWUnlock(&printer->lock);
+
+    send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Unable to connect to %s:%d: %s"), host, port, cupsGetErrorString());
+    goto finish_response;
   }
 
  /*
@@ -5385,6 +5375,7 @@ create_local_bg_thread(
   * If we did not succeed to obtain the "media-col-database" attribute
   * try to get it separately
   */
+
   if (ippFindAttribute(response, "media-col-database", IPP_TAG_ZERO) ==
       NULL)
   {
@@ -5447,17 +5438,29 @@ create_local_bg_thread(
     if ((from = cupsFileOpen(fromppd, "r")) == NULL)
     {
       cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Unable to read generated PPD: %s", printer->name, strerror(errno));
-      ippDelete(response);
-      return (NULL);
+
+      /* Force printer to timeout and be deleted */
+      cupsRWLockWrite(&printer->lock);
+      printer->state_time = 0;
+      cupsRWUnlock(&printer->lock);
+
+      send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Unable to read generated PPD: %s"), strerror(errno));
+      goto finish_response;
     }
 
     snprintf(toppd, sizeof(toppd), "%s/ppd/%s.ppd", ServerRoot, printer->name);
     if ((to = cupsdCreateConfFile(toppd, ConfigFilePerm)) == NULL)
     {
       cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Unable to create PPD for printer: %s", printer->name, strerror(errno));
-      ippDelete(response);
       cupsFileClose(from);
-      return (NULL);
+
+      /* Force printer to timeout and be deleted */
+      cupsRWLockWrite(&printer->lock);
+      printer->state_time = 0;
+      cupsRWUnlock(&printer->lock);
+
+      send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Unable to create PPD for printer: %s"), strerror(errno));
+      goto finish_response;
     }
 
     while (cupsFileGets(from, line, sizeof(line)))
@@ -5466,10 +5469,14 @@ create_local_bg_thread(
     cupsFileClose(from);
     if (!cupsdCloseCreatedConfFile(to, toppd))
     {
+      cupsRWLockWrite(&printer->lock);
+
       printer->config_time = time(NULL);
       printer->state       = IPP_PSTATE_IDLE;
       printer->accepting   = 1;
 
+      cupsRWUnlock(&printer->lock);
+
       cupsdSetPrinterAttrs(printer);
 
       cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG, printer, NULL, "Printer \"%s\" is now available.", printer->name);
@@ -5477,10 +5484,39 @@ create_local_bg_thread(
     }
   }
   else
+  {
     cupsdLogMessage(CUPSD_LOG_ERROR, "%s: PPD creation failed: %s", printer->name, cupsGetErrorString());
 
+    /* Force printer to timeout and be deleted */
+    cupsRWLockWrite(&printer->lock);
+    printer->state_time = 0;
+    cupsRWUnlock(&printer->lock);
+
+    send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Unable to create PPD: %s"), cupsGetErrorString());
+    goto finish_response;
+  }
+
+ /*
+  * Respond to the client...
+  */
+
+  send_ipp_status(con, IPP_STATUS_OK, _("Local printer created."));
+
+  ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs", (char)printer->accepting);
+  ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state);
+  add_printer_state_reasons(con, printer);
+
+  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), httpIsEncrypted(con->http) ? "ipps" : "ipp", NULL, con->clientname, con->clientport, "/printers/%s", printer->name);
+  ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, uri);
+
+  finish_response:
+
   ippDelete(response);
 
+  con->bg_pending = 0;
+
+  send_response(con);
+
   return (NULL);
 }
 
@@ -5690,14 +5726,17 @@ create_local_printer(
   * Run a background thread to create the PPD...
   */
 
-  cupsThreadCreate((cups_thread_func_t)create_local_bg_thread, printer);
+  con->bg_pending = 1;
+  con->bg_printer = printer;
+
+  cupsThreadCreate((cups_thread_func_t)create_local_bg_thread, con);
+
+  return;
 
  /*
   * Return printer attributes...
   */
 
-  send_ipp_status(con, IPP_STATUS_OK, _("Local printer created."));
-
   add_printer_attributes:
 
   ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs", (char)printer->accepting);
@@ -10288,6 +10327,82 @@ send_ipp_status(cupsd_client_t *con,   /* I - Client connection */
 }
 
 
+/*
+ * 'send_response()' - Send the IPP response.
+ */
+
+static int                             /* O - 1 on success, 0 on failure */
+send_response(cupsd_client_t *con)     /* I - Client */
+{
+  ipp_attribute_t      *uri;           /* Target URI */
+  int                  ret = 0;        /* Return value */
+  static cups_mutex_t  mutex = CUPS_MUTEX_INITIALIZER;
+                                       /* Mutex for logging/access */
+
+
+  cupsMutexLock(&mutex);
+
+  if ((uri = ippFindAttribute(con->request, "printer-uri", IPP_TAG_URI)) == NULL)
+  {
+    if ((uri = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI)) == NULL)
+      uri = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME);
+  }
+
+  cupsdLogClient(con, con->response->request.status.status_code >= IPP_STATUS_ERROR_BAD_REQUEST && con->response->request.status.status_code != IPP_STATUS_ERROR_NOT_FOUND ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG, "Returning IPP %s for %s (%s) from %s.",  ippErrorString(con->response->request.status.status_code), ippOpString(con->request->request.op.operation_id), uri ? uri->values[0].string.text : "no URI", con->http->hostname);
+
+  httpClearFields(con->http);
+
+#ifdef CUPSD_USE_CHUNKING
+ /*
+  * Because older versions of CUPS (1.1.17 and older) and some IPP
+  * clients do not implement chunking properly, we cannot use
+  * chunking by default.  This may become the default in future
+  * CUPS releases, or we might add a configuration directive for
+  * it.
+  */
+
+  if (con->http->version == HTTP_1_1)
+  {
+    cupsdLogClient(con, CUPSD_LOG_DEBUG, "Transfer-Encoding: chunked");
+    cupsdSetLength(con->http, 0);
+  }
+  else
+#endif /* CUPSD_USE_CHUNKING */
+  {
+    size_t     length;                 /* Length of response */
+
+
+    length = ippLength(con->response);
+
+    if (con->file >= 0 && !con->pipe_pid)
+    {
+      struct stat      fileinfo;       /* File information */
+
+      if (!fstat(con->file, &fileinfo))
+       length += (size_t)fileinfo.st_size;
+    }
+
+    cupsdLogClient(con, CUPSD_LOG_DEBUG, "Content-Length: " CUPS_LLFMT, CUPS_LLCAST length);
+    httpSetLength(con->http, length);
+  }
+
+  if (cupsdSendHeader(con, HTTP_STATUS_OK, "application/ipp", CUPSD_AUTH_NONE))
+  {
+   /*
+    * Tell the caller the response header was sent successfully...
+    */
+
+    cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, (cupsd_selfunc_t)cupsdWriteClient, con);
+
+    ret = 1;
+  }
+
+  cupsMutexUnlock(&mutex);
+
+  return (ret);
+}
+
+
 /*
  * 'set_default()' - Set the default destination...
  */
index 60340cc8f8c9619bd173c97c567500ed0f572c74..055ad485119708b415dff01b4d120518e3e1cad3 100644 (file)
@@ -23,8 +23,6 @@ typedef struct
                        *ops;           /* Operations */
 } cupsd_policy_t;
 
-typedef struct cupsd_printer_s cupsd_printer_t;
-
 
 /*
  * Globals...
index 728d702946e9f06d3d2cd344d88f02092fbc72c1..48d8aacf57893f6a6b93a1db7f92a2367c933369 100644 (file)
@@ -50,8 +50,6 @@ typedef AvahiStringList *cupsd_txt_t; /* TXT record */
  * Printer/class information structure...
  */
 
-typedef struct cupsd_job_s cupsd_job_t;
-
 struct cupsd_printer_s
 {
   cups_rwlock_t lock;                  /* Concurrency lock for background updates */
index 43857b671efd9dcfeb5dad04d86a546574f099a8..e32dd5bc19eaf6bcda77a5e519d4041d50d937f4 100644 (file)
                        isa = PBXProject;
                        attributes = {
                                BuildIndependentTargetsInParallel = YES;
-                               LastUpgradeCheck = 1430;
+                               LastUpgradeCheck = 1530;
                                ORGANIZATIONNAME = "Apple Inc.";
                                TargetAttributes = {
                                        270695FD1CADF3E200FFE5FB = {
                72BF963C1333042100B1EAD7 /* Debug */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
+                               ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
                                ASSETCATALOG_COMPRESSION = lossless;
                                CLANG_ANALYZER_GCD_PERFORMANCE = YES;
                                CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
                                DEBUG_INFORMATION_FORMAT = dwarf;
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
                                ENABLE_TESTABILITY = YES;
+                               ENABLE_USER_SCRIPT_SANDBOXING = YES;
                                GCC_C_LANGUAGE_STANDARD = c99;
                                GCC_NO_COMMON_BLOCKS = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = DEBUG;
                72BF963D1333042100B1EAD7 /* Release */ = {
                        isa = XCBuildConfiguration;
                        buildSettings = {
+                               ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
                                ASSETCATALOG_COMPRESSION = "respect-asset-catalog";
                                CLANG_ANALYZER_GCD_PERFORMANCE = YES;
                                CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
                                DEAD_CODE_STRIPPING = YES;
                                DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
                                ENABLE_STRICT_OBJC_MSGSEND = YES;
+                               ENABLE_USER_SCRIPT_SANDBOXING = YES;
                                GCC_C_LANGUAGE_STANDARD = c99;
                                GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
                                GCC_NO_COMMON_BLOCKS = YES;