cups-failover-backend.patch 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. diff -up cups-2.4.0/backend/failover.c.failover cups-2.4.0/backend/failover.c
  2. --- cups-2.4.0/backend/failover.c.failover 2021-12-15 11:06:14.274967317 +0100
  3. +++ cups-2.4.0/backend/failover.c 2021-12-15 11:06:14.274967317 +0100
  4. @@ -0,0 +1,837 @@
  5. +/*
  6. + * Failover Backend for the Common UNIX Printing System (CUPS).
  7. + *
  8. + * Copyright (c) 2014, Red Hat, Inc.
  9. + * All rights reserved.
  10. + *
  11. + * Redistribution and use in source and binary forms, with or without
  12. + * modification, are permitted provided that the following conditions
  13. + * are met:
  14. + *
  15. + * * Redistributions of source code must retain the above copyright
  16. + * notice, this list of conditions and the following disclaimer.
  17. + * * Redistributions in binary form must reproduce the above copyright
  18. + * notice, this list of conditions and the following disclaimer in the
  19. + * documentation and/or other materials provided with the distribution.
  20. + * * Neither the name of Red Hat, Inc. nor the names of its contributors
  21. + * may be used to endorse or promote products derived from this software
  22. + * without specific prior written permission.
  23. + *
  24. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  25. + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  26. + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  27. + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT,
  28. + * INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  29. + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  30. + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  31. + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  32. + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  33. + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
  34. + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  35. + * DAMAGE.
  36. + *
  37. + * Original version by Clark Hale, Red Hat, Inc.
  38. + *
  39. + * This backend presents a fake printer that will choose the first
  40. + * available printer from a list of IPP URIs.
  41. + *
  42. + * Option failover contains a comma separated list of IPP URIs. The
  43. + * URIs are attempted in-order.
  44. + *
  45. + * Option failover-retries contains an integer that indicates how many
  46. + * times to iterate through the failover list before completely
  47. + * failing.
  48. + *
  49. + * Contents:
  50. + * main() - Checks each printer in a failover list, and
  51. + * sends job data to the first available printer
  52. + * move_job() - Sends and IPP Move-Job request
  53. + * check_printer() - Checks a printer's attributes to see
  54. + * if it's enabled and accepting jobs
  55. + * read_config() - Read the backends configuration from
  56. + * options
  57. + * get_printer_attributes() - Sends an IPP Get-Attributes request to
  58. + * a URI
  59. + * sigterm_handler() - Handle SIGTERM that cancels the job
  60. + * password_cb() - Password call back used to disable password
  61. + * prompt
  62. + */
  63. +#include <stdlib.h>
  64. +#include <stdio.h>
  65. +#include <string.h>
  66. +#include <sys/wait.h>
  67. +#include <cups/http-private.h>
  68. +#include <cups/http.h>
  69. +#include "backend-private.h"
  70. +
  71. +/*
  72. + * Return Values
  73. + */
  74. +typedef enum fo_state_e
  75. +{
  76. + FO_PRINTER_GOOD = 0,
  77. + FO_PRINTER_BAD,
  78. + FO_PRINTER_BUSY,
  79. + FO_AUTH_REQUIRED
  80. +} fo_state_t;
  81. +
  82. +/*
  83. + * Constants
  84. + */
  85. +#define FAILOVER_DEFAULT_RETRIES (3)
  86. +#define FAILOVER_PASSWORD_RETRIES_MAX (3)
  87. +
  88. +/*
  89. + * Local Functions
  90. + */
  91. +static int check_printer(const char *device_uri);
  92. +static int read_config(cups_array_t *printer_array, int *retries,
  93. + const char *options);
  94. +static int get_printer_attributes(const char *device_uri,
  95. + ipp_t **attributes);
  96. +static int move_job(int jobid, const char *dest);
  97. +static void sigterm_handler(int sig);
  98. +static const char *password_cb(const char *);
  99. +
  100. +/*
  101. + * Global Variables
  102. + */
  103. +static int job_canceled = 0; /* Job canceled */
  104. +static char *password = NULL; /* password for device */
  105. +static int password_retries = 0;
  106. +static const char *auth_info_required = "none";
  107. +
  108. +/*
  109. + * 'main()' - Checks each printer in a failover list, and
  110. + * sends job data to the first available printer
  111. + * Usage:
  112. + * printer-uri job-id user title copies options [file]
  113. + *
  114. + * The printer-uri option is not used, but it still required to fit
  115. + * to the backend(7) standards.
  116. + */
  117. +int
  118. +main(int argc, char *argv[])
  119. +{
  120. + const char *selected_uri = NULL; /* URI of selected printer */
  121. + const char *tmp_device_uri; /* Device URI to check */
  122. + cups_array_t *printer_array; /* Array of available printers */
  123. + int printer_count = 0; /* current printer array index */
  124. + int retry_max = 1; /* maximum retries before exit */
  125. + int retry_count = 0; /* current retry number */
  126. + int auth_failed_count = 0; /* auth failures per loop */
  127. + int rc = CUPS_BACKEND_OK;
  128. +#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
  129. + struct sigaction action; /* Actions for POSIX signals */
  130. +#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
  131. +
  132. + /*
  133. + * Check args
  134. + */
  135. + if (argc == 1)
  136. + {
  137. + /*
  138. + * print out discovery data
  139. + */
  140. + char *backendName;
  141. +
  142. + if ((backendName = strrchr(argv[0], '/')) != NULL)
  143. + backendName++;
  144. + else
  145. + backendName = argv[0];
  146. +
  147. + _cupsLangPrintf(stderr,"network %s \"Unknown\" \"%s (%s)\"\n",
  148. + backendName,
  149. + _cupsLangString(cupsLangDefault(), _("Failover Printer")),
  150. + backendName);
  151. +
  152. + return (CUPS_BACKEND_OK);
  153. + }
  154. + else if (argc < 6)
  155. + {
  156. + _cupsLangPrintf(stderr,
  157. + _("Usage: %s job-id user title copies options [file]"),
  158. + argv[0]);
  159. + return (CUPS_BACKEND_STOP);
  160. + }
  161. +
  162. + fprintf(stderr, "DEBUG: Failover backend starting up.\n");
  163. +
  164. + /*
  165. + * Don't buffer status messages
  166. + */
  167. + setbuf(stderr, NULL);
  168. +
  169. + /*
  170. + * Ignore SIGPIPE and catch SIGTERM signals...
  171. + */
  172. +#ifdef HAVE_SIGSET
  173. + sigset(SIGPIPE, SIG_IGN);
  174. + sigset(SIGTERM, sigterm_handler);
  175. +#elif defined(HAVE_SIGACTION)
  176. + memset(&action, 0, sizeof(action));
  177. + action.sa_handler = SIG_IGN;
  178. + sigaction(SIGPIPE, &action, NULL);
  179. +
  180. + sigemptyset(&action.sa_mask);
  181. + sigaddset(&action.sa_mask, SIGTERM);
  182. + action.sa_handler = sigterm_handler;
  183. + sigaction(SIGTERM, &action, NULL);
  184. +#else
  185. + signal(SIGPIPE, SIG_IGN);
  186. + signal(SIGTERM, sigterm_handler);
  187. +#endif /* HAVE_SIGSET */
  188. +
  189. + printer_array = cupsArrayNew(NULL, NULL);
  190. +
  191. + /*
  192. + * Read Configuration
  193. + */
  194. + if ((rc = read_config(printer_array, &retry_max,
  195. + argv[5])) != CUPS_BACKEND_OK)
  196. + {
  197. + fprintf(stderr, "ERROR: Failed to read configuration options!\n");
  198. + goto cleanup;
  199. + }
  200. +
  201. + /*
  202. + * Main Retry Loop
  203. + */
  204. + for (retry_count = 0; retry_count < retry_max; retry_count++)
  205. + {
  206. + fprintf(stderr, "DEBUG: Retry loop #%d\n", retry_count + 1);
  207. +
  208. + /*
  209. + * Reset Counters
  210. + */
  211. + printer_count = 0;
  212. + auth_failed_count = 0;
  213. +
  214. + tmp_device_uri = (char *)cupsArrayFirst(printer_array);
  215. +
  216. + do
  217. + {
  218. + if (job_canceled)
  219. + {
  220. + fprintf(stderr, "DEBUG: Job Canceled\n");
  221. + goto cleanup;
  222. + }
  223. +
  224. + fprintf(stderr,"DEBUG: Checking printer #%d: %s\n",
  225. + printer_count+1, tmp_device_uri);
  226. +
  227. + rc = check_printer(tmp_device_uri);
  228. +
  229. + // Printer is available and not busy.
  230. + if ( rc == FO_PRINTER_GOOD )
  231. + {
  232. + selected_uri = tmp_device_uri;
  233. + break;
  234. + }
  235. + // Printer is busy
  236. + else if (rc == FO_PRINTER_BUSY)
  237. + {
  238. + fprintf(stderr, "DEBUG: Waiting for job to complete.\n");
  239. + sleep(2);
  240. + continue;
  241. + }
  242. + // Authorization is required to access the printer.
  243. + else if (rc == FO_AUTH_REQUIRED)
  244. + {
  245. + auth_failed_count++;
  246. + fprintf(stderr, "DEBUG: auth_failed_count = %d\n", auth_failed_count);
  247. + }
  248. + // Printer is stopped or not accepting jobs
  249. + else
  250. + {
  251. + if (!printer_count)
  252. + fprintf(stderr, "INFO: Primary Printer, %s, not available. "
  253. + "Attempting Failovers...\n",
  254. + tmp_device_uri);
  255. + else
  256. + fprintf(stderr, "INFO: Failover Printer, %s, not available. "
  257. + "Attempting Failovers..\n",
  258. + tmp_device_uri);
  259. + printer_count++;
  260. + tmp_device_uri = (char *)cupsArrayNext(printer_array);
  261. + }
  262. + } while (tmp_device_uri != NULL);
  263. +
  264. + if (selected_uri && !printer_count)
  265. + fprintf(stderr, "STATE: -primary-printer-failed\n");
  266. + else
  267. + fprintf(stderr, "STATE: +primary-printer-failed\n");
  268. +
  269. + if (job_canceled)
  270. + {
  271. + fprintf(stderr, "DEBUG: Job Canceled\n");
  272. + goto cleanup;
  273. + }
  274. +
  275. + if (!selected_uri && auth_failed_count == printer_count)
  276. + {
  277. + fprintf(stderr, "ERROR: All failover printers failed with "
  278. + "authorization issues.\n");
  279. + rc = CUPS_BACKEND_AUTH_REQUIRED;
  280. + fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
  281. + goto cleanup;
  282. + }
  283. + else if (!selected_uri && retry_count + 1 < retry_max)
  284. + {
  285. + fprintf(stderr, "INFO: No suitable printer found...retrying...\n");
  286. + sleep(2);
  287. + continue;
  288. + }
  289. + else if (selected_uri)
  290. + {
  291. + fprintf(stderr, "DEBUG: Using printer, %s.\n", selected_uri);
  292. + break;
  293. + }
  294. + }
  295. +
  296. + if (!selected_uri)
  297. + {
  298. + fprintf(stderr, "ERROR: No suitable printer found. Aborting print\n");
  299. + rc = CUPS_BACKEND_FAILED;
  300. + goto cleanup;
  301. + }
  302. +
  303. + rc = move_job(atoi(argv[1]), selected_uri);
  304. +
  305. + if (job_canceled)
  306. + rc = CUPS_BACKEND_OK;
  307. +
  308. +cleanup :
  309. + if (job_canceled)
  310. + rc = CUPS_BACKEND_OK;
  311. +
  312. + tmp_device_uri = (char *)cupsArrayFirst(printer_array);
  313. + do
  314. + {
  315. + free((void *)tmp_device_uri);
  316. + } while ((tmp_device_uri = (char *)cupsArrayNext(printer_array)) != NULL);
  317. +
  318. + cupsArrayDelete(printer_array);
  319. + sleep(2);
  320. + return (rc);
  321. +}
  322. +
  323. +/*
  324. + * 'check_printer()' - Checks the status of a remote printer and returns
  325. + * back a good/bad/busy status.
  326. + */
  327. +int
  328. +check_printer(const char *device_uri)
  329. +{
  330. + ipp_t *attributes = NULL; /* attributes for device_uri */
  331. + ipp_attribute_t *tmp_attribute; /* for examining attribs */
  332. + int rc = FO_PRINTER_GOOD; /* return code */
  333. + char *reason; /* printer state reason */
  334. + int i;
  335. +
  336. + fprintf(stderr, "DEBUG: Checking printer %s\n",device_uri);
  337. +
  338. + rc = get_printer_attributes(device_uri, &attributes);
  339. + if ( rc != CUPS_BACKEND_OK )
  340. + {
  341. + fprintf(stderr, "DEBUG: Failed to get attributes from printer: %s\n",
  342. + device_uri);
  343. + if ( rc == CUPS_BACKEND_AUTH_REQUIRED )
  344. + return (FO_AUTH_REQUIRED);
  345. + else
  346. + return (FO_PRINTER_BAD);
  347. + }
  348. +
  349. + /*
  350. + * Check if printer is accepting jobs
  351. + */
  352. + if ((tmp_attribute = ippFindAttribute(attributes,
  353. + "printer-is-accepting-jobs",
  354. + IPP_TAG_BOOLEAN)) != NULL &&
  355. + !tmp_attribute->values[0].boolean)
  356. + {
  357. + fprintf(stderr,
  358. + "DEBUG: Printer, %s, is not accepting jobs.\n",
  359. + device_uri);
  360. +
  361. + rc = FO_PRINTER_BAD;
  362. + }
  363. +
  364. + /*
  365. + * Check if printer is stopped or busy processing
  366. + */
  367. + if ((tmp_attribute = ippFindAttribute(attributes,
  368. + "printer-state",
  369. + IPP_TAG_ENUM)) != NULL)
  370. + {
  371. + // Printer Stopped
  372. + if ( tmp_attribute->values[0].integer == IPP_PRINTER_STOPPED )
  373. + {
  374. + fprintf(stderr, "DEBUG: Printer, %s, stopped.\n", device_uri);
  375. + rc = FO_PRINTER_BAD;
  376. + }
  377. + // Printer Busy
  378. + else if ( tmp_attribute->values[0].integer == IPP_PRINTER_PROCESSING )
  379. + {
  380. + fprintf(stderr, "DEBUG: Printer %s is busy.\n", device_uri);
  381. + rc = FO_PRINTER_BUSY;
  382. + }
  383. + }
  384. +
  385. + /*
  386. + * Parse through the printer-state-reasons
  387. + */
  388. + if ((tmp_attribute = ippFindAttribute(attributes, "printer-state-reasons",
  389. + IPP_TAG_KEYWORD)) != NULL)
  390. + {
  391. + for (i = 0; i < tmp_attribute->num_values; i++)
  392. + {
  393. + reason = tmp_attribute->values[i].string.text;
  394. + int len = strlen(reason);
  395. +
  396. + if (len > 8 && !strcmp(reason + len - 8, "-warning"))
  397. + {
  398. + fprintf(stderr, "DEBUG: Printer Supply Warning, %s\n", reason);
  399. + rc = FO_PRINTER_BAD;
  400. + }
  401. + else if (len > 6 && !strcmp(reason + len - 6, "-error"))
  402. + {
  403. + fprintf(stderr, "DEBUG: Printer Supply Error, %s\n", reason);
  404. + rc = FO_PRINTER_BAD;
  405. + }
  406. + }
  407. + }
  408. +
  409. + return (rc);
  410. +}
  411. +
  412. +/*
  413. + * 'read_config()' - Parses the failover and failover-retries options
  414. + *
  415. + */
  416. +static int
  417. +read_config(cups_array_t *printer_array, int *retries, const char *options)
  418. +{
  419. +
  420. + const char *tmp; /* temporary ptr */
  421. + char *tok_tmp; /* temporary ptr for option parsing */
  422. + int jobopts_count = 0; /* number of options */
  423. + cups_option_t *jobopts = NULL; /* job options */
  424. +
  425. +
  426. + fprintf(stderr, "DEBUG: Reading Configuration.\n");
  427. + jobopts_count = cupsParseOptions(options, 0, &jobopts);
  428. +
  429. + if (!jobopts_count)
  430. + {
  431. + fprintf(stderr,
  432. + "ERROR: No job options! Cannot find failover options!\n");
  433. + return (CUPS_BACKEND_STOP);
  434. + }
  435. +
  436. + /*
  437. + * Get attributes from the primary printer
  438. + */
  439. + fprintf(stderr, "DEBUG: Searching for failover option.\n");
  440. +
  441. + if ((tmp = cupsGetOption("failover", jobopts_count, jobopts)) != NULL)
  442. + {
  443. + fprintf(stderr, "DEBUG: Failover option contents: %s.\n", tmp);
  444. +
  445. + tok_tmp = strdup(tmp);
  446. +
  447. + tmp = strtok(tok_tmp, ",");
  448. + do
  449. + {
  450. + cupsArrayAdd(printer_array, strdup(tmp));
  451. + } while ((tmp = strtok(NULL,",")) != NULL);
  452. +
  453. + free(tok_tmp);
  454. + }
  455. + else
  456. + {
  457. + /*
  458. + * The queue is misconfigured, so return back CUPS_BACKEND_STOP
  459. + */
  460. + fprintf(stderr, "ERROR: failover option not specified!\n");
  461. + return (CUPS_BACKEND_STOP);
  462. + }
  463. +
  464. + /*
  465. + * Get the failover-retries value, if it exists.
  466. + */
  467. + fprintf(stderr, "DEBUG: Searching for failover-retries option.\n");
  468. +
  469. + if ((tmp = cupsGetOption("failover-retries",
  470. + jobopts_count, jobopts)) != NULL)
  471. + {
  472. + fprintf(stderr, "DEBUG: failover-retries option contents: %s.\n", tmp);
  473. + *retries = atoi(tmp);
  474. + }
  475. + else
  476. + {
  477. + *retries = FAILOVER_DEFAULT_RETRIES;
  478. + fprintf(stderr, "DEBUG: Failed to get failover-retries option\n");
  479. + fprintf(stderr, "DEBUG: Defaulted to %d retries\n", *retries);
  480. + }
  481. +
  482. + return (CUPS_BACKEND_OK);
  483. +}
  484. +
  485. +/*
  486. + * 'get_printer_attributes()' - Sends an IPP Get-Attributes request to
  487. + * a URI
  488. + */
  489. +int
  490. +get_printer_attributes(const char *device_uri, ipp_t **attributes)
  491. +{
  492. + char uri[HTTP_MAX_URI]; /* Updated URI without login */
  493. + int version; /* IPP version */
  494. + char scheme[256]; /* Scheme in URI */
  495. + ipp_status_t ipp_status; /* Status of IPP request */
  496. + char hostname[1024]; /* Hostname */
  497. + char resource[1024]; /* Resource infoo */
  498. + char addrname[256]; /* Address name */
  499. + int port; /* IPP Port number */
  500. + char portname[255]; /* Port as string */
  501. + http_t *http; /* HTTP connection */
  502. + ipp_t *request; /* IPP request */
  503. + int rc = CUPS_BACKEND_OK; /* Return Code */
  504. + char username[256]; /* Username for device URI */
  505. + char *option_ptr; /* for parsing resource opts */
  506. + const char * const pattrs[] = /* Printer attributes wanted */
  507. + {
  508. + "printer-is-accepting-jobs",
  509. + "printer-state",
  510. + "printer-state-reasons"
  511. + };
  512. +
  513. + if (job_canceled)
  514. + return (CUPS_BACKEND_OK);
  515. +
  516. + fprintf(stderr, "DEBUG: Getting Printer Attributes.\n");
  517. + fprintf(stderr, "DEBUG: Device URL %s.\n", device_uri);
  518. +
  519. + /*
  520. + * Parse device_uri
  521. + */
  522. + if (httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
  523. + username, sizeof(username), hostname, sizeof(hostname),
  524. + &port, resource, sizeof(resource)) != HTTP_URI_OK)
  525. + {
  526. + fprintf(stderr, "ERROR: Problem parsing device_uri, %s\n", device_uri);
  527. + return (CUPS_BACKEND_STOP);
  528. + }
  529. +
  530. + if (!port)
  531. + port = IPP_PORT;
  532. +
  533. + sprintf(portname, "%d", port);
  534. +
  535. + fprintf(stderr, "DEBUG: Getting Printer Attributes.\n");
  536. +
  537. + /*
  538. + * Configure password
  539. + */
  540. + cupsSetPasswordCB(password_cb);
  541. +
  542. + /*
  543. + * reset, in case a previous attempt for
  544. + * another printer left residue
  545. + */
  546. + cupsSetUser(NULL);
  547. + password = NULL;
  548. + password_retries = 0;
  549. +
  550. + if (*username)
  551. + {
  552. + if ((password = strchr(username, ':')) != NULL)
  553. + {
  554. + *password = '\0';
  555. + password++;
  556. + }
  557. +
  558. + cupsSetUser(username);
  559. + }
  560. + else if (!getuid())
  561. + {
  562. + const char *username_env;
  563. +
  564. + if ((username_env = getenv("AUTH_USERNAME")) != NULL)
  565. + {
  566. + cupsSetUser(username_env);
  567. + password = getenv("AUTH_PASSWORD");
  568. + }
  569. + }
  570. +
  571. + /*
  572. + * Try connecting to the remote server...
  573. + */
  574. + fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
  575. + _cupsLangPuts(stderr, _("INFO: Connecting to printer...\n"));
  576. +
  577. + http = httpConnectEncrypt(hostname, port, cupsEncryption());
  578. +
  579. + /*
  580. + * Deal the socket not being open.
  581. + */
  582. + if (!http)
  583. + {
  584. + int error = errno; /* Connection error */
  585. +
  586. + switch (error)
  587. + {
  588. + case EHOSTDOWN :
  589. + _cupsLangPuts(stderr, _("WARNING: "
  590. + "The printer may not exist or "
  591. + "is unavailable at this time.\n"));
  592. + break;
  593. + case EHOSTUNREACH :
  594. + _cupsLangPuts(stderr, _("WARNING: "
  595. + "The printer is unreachable at this "
  596. + "time.\n"));
  597. + break;
  598. + case ECONNREFUSED :
  599. + _cupsLangPuts(stderr, _("WARNING: "
  600. + "Connection Refused.\n"));
  601. + break;
  602. + default :
  603. + fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
  604. + break;
  605. + }
  606. +
  607. + rc = CUPS_BACKEND_FAILED;
  608. + sleep(5);
  609. + goto prt_available_cleanup;
  610. + }
  611. +
  612. +
  613. +#ifdef AF_INET6
  614. + if (http->hostaddr->addr.sa_family == AF_INET6)
  615. + fprintf(stderr, "DEBUG: Connected to [%s]:%d (IPv6)...\n",
  616. + httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
  617. + ntohs(http->hostaddr->ipv6.sin6_port));
  618. + else
  619. +#endif /* AF_INET6 */
  620. + if (http->hostaddr->addr.sa_family == AF_INET)
  621. + fprintf(stderr, "DEBUG: Connected to %s:%d (IPv4)...\n",
  622. + httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
  623. + ntohs(http->hostaddr->ipv4.sin_port));
  624. +
  625. + /*
  626. + * Search the resource string for options.
  627. + * We only care about version, for the moment.
  628. + */
  629. + version = 11;
  630. +
  631. + if ((option_ptr = strchr(resource, '?')) != NULL)
  632. + {
  633. + *option_ptr++ = '\0';
  634. +
  635. + if ((option_ptr = strstr(option_ptr, "version="))!=NULL)
  636. + {
  637. + int minor; /* minor version from URI */
  638. + int major; /* major version from URI */
  639. + char *version_str; /* ipp version */
  640. +
  641. + option_ptr += 8;
  642. + version_str = option_ptr;
  643. +
  644. + while (*option_ptr && *option_ptr != '&' && *option_ptr != '+')
  645. + option_ptr++;
  646. +
  647. + if (*option_ptr)
  648. + *option_ptr = '\0';
  649. +
  650. + sscanf(version_str, "%d.%d", &major, &minor);
  651. +
  652. + version = (major * 10) + minor;
  653. +
  654. + switch(version)
  655. + {
  656. + case 10 :
  657. + case 11 :
  658. + case 20 :
  659. + case 21 :
  660. + fprintf(stderr,
  661. + "DEBUG: Set version to %d from URI\n",
  662. + version);
  663. + break;
  664. + default :
  665. + _cupsLangPrintf(stderr,
  666. + _("DEBUG: Invalid version, %d, from URI. "
  667. + "Using default of 1.1 \n"),
  668. + version);
  669. + version = 11;
  670. + }
  671. + }
  672. + }
  673. +
  674. +
  675. + /*
  676. + * Build a URI for the printer. We can't use the URI in argv[0]
  677. + * because it might contain username:password information...
  678. + */
  679. + if (httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL,
  680. + hostname, port, resource) != HTTP_URI_OK)
  681. + {
  682. + fprintf(stderr, "ERROR: Problem assembling printer URI from host %s, "
  683. + "port %d, resource %s\n", hostname, port, resource);
  684. + return (CUPS_BACKEND_STOP);
  685. + }
  686. +
  687. + /*
  688. + * Build the IPP request...
  689. + */
  690. + request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
  691. + request->request.op.version[0] = version / 10;
  692. + request->request.op.version[1] = version % 10;
  693. +
  694. + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
  695. + NULL, uri);
  696. +
  697. + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
  698. + "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
  699. + NULL, pattrs);
  700. +
  701. + /*
  702. + * Do the request...
  703. + */
  704. + fputs("DEBUG: Getting supported attributes...\n", stderr);
  705. +
  706. + fprintf(stderr, "DEBUG: IPP Request Structure Built.\n");
  707. +
  708. + *attributes = cupsDoRequest(http, request, resource);
  709. + ipp_status = cupsLastError();
  710. +
  711. + fprintf(stderr, "DEBUG: Get-Printer-Attributes: %s (%s)\n",
  712. + ippErrorString(ipp_status), cupsLastErrorString());
  713. +
  714. + if (ipp_status > IPP_OK_CONFLICT)
  715. + {
  716. + fprintf(stderr, "DEBUG: Get-Printer-Attributes returned %s.\n",
  717. + ippErrorString(ipp_status));
  718. + switch(ipp_status)
  719. + {
  720. + case IPP_FORBIDDEN :
  721. + case IPP_NOT_AUTHORIZED :
  722. + _cupsLangPuts(stderr, _("ERROR: Not Authorized.\n"));
  723. + rc = CUPS_BACKEND_AUTH_REQUIRED;
  724. + break;
  725. + case IPP_PRINTER_BUSY :
  726. + case IPP_SERVICE_UNAVAILABLE :
  727. + _cupsLangPuts(stderr, _("ERROR: "
  728. + "The printer is not responding.\n"));
  729. + rc = CUPS_BACKEND_FAILED;
  730. + break;
  731. + case IPP_BAD_REQUEST :
  732. + case IPP_VERSION_NOT_SUPPORTED :
  733. + fprintf(stderr, "ERROR: Destination does not support IPP version %d\n",
  734. + version);
  735. + case IPP_NOT_FOUND :
  736. + _cupsLangPuts(stderr, _("ERROR: "
  737. + "The printer configuration is incorrect or the "
  738. + "printer no longer exists.\n"));
  739. + rc = CUPS_BACKEND_STOP;
  740. + break;
  741. + default :
  742. + rc = CUPS_BACKEND_FAILED;
  743. + }
  744. + goto prt_available_cleanup;
  745. + }
  746. +
  747. +prt_available_cleanup :
  748. + httpClose(http);
  749. + return (rc);
  750. +}
  751. +
  752. +static int
  753. +move_job(int jobid, /* Job ID */
  754. + const char *dest) /* Destination ipp address */
  755. +{
  756. + ipp_t *request; /* IPP Request */
  757. + char job_uri[HTTP_MAX_URI]; /* job-uri */
  758. +
  759. + http_t* http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
  760. +
  761. + if (!http)
  762. + {
  763. + _cupsLangPrintf(stderr,
  764. + _("failover: Unable to connect to server: %s\n"),
  765. + strerror(errno));
  766. + return (CUPS_BACKEND_FAILED);
  767. + }
  768. +
  769. + /*
  770. + * Build a CUPS_MOVE_JOB request, which requires the following
  771. + * attributes:
  772. + *
  773. + * job-uri/printer-uri
  774. + * job-printer-uri
  775. + * requesting-user-name
  776. + */
  777. +
  778. + request = ippNewRequest(CUPS_MOVE_JOB);
  779. +
  780. + snprintf(job_uri, sizeof(job_uri), "ipp://localhost/jobs/%d", jobid);
  781. + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL,
  782. + job_uri);
  783. +
  784. + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
  785. + "requesting-user-name",
  786. + NULL, cupsUser());
  787. +
  788. + ippAddString(request, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri",
  789. + NULL, dest);
  790. +
  791. + /*
  792. + * Do the request and get back a response...
  793. + */
  794. +
  795. + ippDelete(cupsDoRequest(http, request, "/jobs"));
  796. +
  797. + httpClose(http);
  798. +
  799. + if (cupsLastError() > IPP_OK_CONFLICT)
  800. + {
  801. + _cupsLangPrintf(stderr, "failover: %s\n", cupsLastErrorString());
  802. + return (CUPS_BACKEND_FAILED);
  803. + }
  804. + else
  805. + return (CUPS_BACKEND_OK);
  806. +}
  807. +
  808. +/*
  809. + * 'sigterm_handler()' - handles a sigterm, i.e. job canceled
  810. + */
  811. +static void
  812. +sigterm_handler(int sig)
  813. +{
  814. + if (!job_canceled)
  815. + {
  816. + write(2, "DEBUG: Got SIGTERM.\n", 20);
  817. + job_canceled = 1;
  818. + }
  819. + else
  820. + {
  821. + /*
  822. + * Job has already been canceled, so just exit
  823. + */
  824. + exit(1);
  825. + }
  826. +}
  827. +
  828. +/*
  829. + * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
  830. + */
  831. +static const char * /* O - Password */
  832. +password_cb(const char *prompt) /* I - Prompt (not used) */
  833. +{
  834. + auth_info_required = "username,password";
  835. + password_retries++;
  836. +
  837. + if(password_retries < FAILOVER_PASSWORD_RETRIES_MAX)
  838. + return (password);
  839. + else
  840. + return (NULL);
  841. +}
  842. diff -up cups-2.4.0/backend/Makefile.failover cups-2.4.0/backend/Makefile
  843. --- cups-2.4.0/backend/Makefile.failover 2021-11-29 15:27:31.000000000 +0100
  844. +++ cups-2.4.0/backend/Makefile 2021-12-15 11:08:27.433009704 +0100
  845. @@ -25,6 +25,7 @@ RBACKENDS = \
  846. ipp \
  847. lpd \
  848. usb \
  849. + failover \
  850. $(DNSSD_BACKEND)
  851. UBACKENDS = \
  852. snmp \
  853. @@ -45,6 +46,7 @@ LIBOBJS = \
  854. OBJS = \
  855. ipp.o \
  856. lpd.o \
  857. + failover.o \
  858. dnssd.o \
  859. snmp.o \
  860. socket.o \
  861. @@ -264,6 +266,15 @@ lpd: lpd.o ../cups/$(LIBCUPS) libbackend
  862. #
  863. +# failover
  864. +#
  865. +
  866. +failover: failover.o ../cups/$(LIBCUPS) libbackend.a
  867. + echo Linking $@...
  868. + $(LD_CC) $(ALL_LDFLAGS) -o failover failover.o libbackend.a $(LINKCUPS)
  869. +
  870. +
  871. +#
  872. # snmp
  873. #