From: Michael R Sweet Date: Thu, 4 Apr 2024 18:38:34 +0000 (-0400) Subject: Update CUPS-Add-Modify-Printer and CUPS-Create-Local-Printer code to delay X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8dda6c2b210c556f08f9e04889d8b42fb534474c;p=thirdparty%2Fcups.git Update CUPS-Add-Modify-Printer and CUPS-Create-Local-Printer code to delay responding until the PPD is successfully generated (Issue #347) --- diff --git a/CHANGES.md b/CHANGES.md index 1441b2e091..68bc468dfb 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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 diff --git a/scheduler/auth.h b/scheduler/auth.h index b1f1d60acf..409c03061e 100644 --- a/scheduler/auth.h +++ b/scheduler/auth.h @@ -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... diff --git a/scheduler/client.h b/scheduler/client.h index 5e8daf4eaa..0498c7e255 100644 --- a/scheduler/client.h +++ b/scheduler/client.h @@ -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 */ diff --git a/scheduler/cupsd.h b/scheduler/cupsd.h index 68b3d8edff..cf2023fc6f 100644 --- a/scheduler/cupsd.h +++ b/scheduler/cupsd.h @@ -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... */ diff --git a/scheduler/ipp.c b/scheduler/ipp.c index cc266398e6..1611f9ab9f 100644 --- a/scheduler/ipp.c +++ b/scheduler/ipp.c @@ -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... */ diff --git a/scheduler/policy.h b/scheduler/policy.h index 60340cc8f8..055ad48511 100644 --- a/scheduler/policy.h +++ b/scheduler/policy.h @@ -23,8 +23,6 @@ typedef struct *ops; /* Operations */ } cupsd_policy_t; -typedef struct cupsd_printer_s cupsd_printer_t; - /* * Globals... diff --git a/scheduler/printers.h b/scheduler/printers.h index 728d702946..48d8aacf57 100644 --- a/scheduler/printers.h +++ b/scheduler/printers.h @@ -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 */ diff --git a/xcode/CUPS.xcodeproj/project.pbxproj b/xcode/CUPS.xcodeproj/project.pbxproj index 43857b671e..e32dd5bc19 100644 --- a/xcode/CUPS.xcodeproj/project.pbxproj +++ b/xcode/CUPS.xcodeproj/project.pbxproj @@ -7165,7 +7165,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1530; ORGANIZATIONNAME = "Apple Inc."; TargetAttributes = { 270695FD1CADF3E200FFE5FB = { @@ -11664,6 +11664,7 @@ 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; @@ -11694,6 +11695,7 @@ 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; @@ -11734,6 +11736,7 @@ 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; @@ -11763,6 +11766,7 @@ 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;