1/* 2 * "$Id: socket.c 11093 2013-07-03 20:48:42Z msweet $" 3 * 4 * AppSocket backend for CUPS. 5 * 6 * Copyright 2007-2012 by Apple Inc. 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved. 8 * 9 * These coded instructions, statements, and computer programs are the 10 * property of Apple Inc. and are protected by Federal copyright 11 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 12 * "LICENSE" which should have been included with this file. If this 13 * file is missing or damaged, see the license at "http://www.cups.org/". 14 * 15 * This file is subject to the Apple OS-Developed Software exception. 16 * 17 * Contents: 18 * 19 * main() - Send a file to the printer or server. 20 * wait_bc() - Wait for back-channel data... 21 */ 22 23/* 24 * Include necessary headers. 25 */ 26 27#include <cups/http-private.h> 28#include "backend-private.h" 29#include <stdarg.h> 30#include <sys/types.h> 31#include <sys/stat.h> 32 33#ifdef WIN32 34# include <winsock.h> 35#else 36# include <unistd.h> 37# include <fcntl.h> 38# include <sys/socket.h> 39# include <netinet/in.h> 40# include <arpa/inet.h> 41# include <netdb.h> 42#endif /* WIN32 */ 43 44 45/* 46 * Local functions... 47 */ 48 49static int wait_bc(int device_fd, int secs); 50 51 52/* 53 * 'main()' - Send a file to the printer or server. 54 * 55 * Usage: 56 * 57 * printer-uri job-id user title copies options [file] 58 */ 59 60int /* O - Exit status */ 61main(int argc, /* I - Number of command-line arguments (6 or 7) */ 62 char *argv[]) /* I - Command-line arguments */ 63{ 64 const char *device_uri; /* Device URI */ 65 char scheme[255], /* Scheme in URI */ 66 hostname[1024], /* Hostname */ 67 username[255], /* Username info (not used) */ 68 resource[1024], /* Resource info (not used) */ 69 *options, /* Pointer to options */ 70 *name, /* Name of option */ 71 *value, /* Value of option */ 72 sep; /* Option separator */ 73 int print_fd; /* Print file */ 74 int copies; /* Number of copies to print */ 75 time_t start_time; /* Time of first connect */ 76 int contimeout; /* Connection timeout */ 77 int waiteof; /* Wait for end-of-file? */ 78 int port; /* Port number */ 79 char portname[255]; /* Port name */ 80 int delay; /* Delay for retries... */ 81 int device_fd; /* AppSocket */ 82 int error; /* Error code (if any) */ 83 http_addrlist_t *addrlist, /* Address list */ 84 *addr; /* Connected address */ 85 char addrname[256]; /* Address name */ 86 int snmp_enabled = 1; /* Is SNMP enabled? */ 87 int snmp_fd, /* SNMP socket */ 88 start_count, /* Page count via SNMP at start */ 89 page_count, /* Page count via SNMP */ 90 have_supplies; /* Printer supports supply levels? */ 91 ssize_t bytes = 0, /* Initial bytes read */ 92 tbytes; /* Total number of bytes written */ 93 char buffer[1024]; /* Initial print buffer */ 94#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) 95 struct sigaction action; /* Actions for POSIX signals */ 96#endif /* HAVE_SIGACTION && !HAVE_SIGSET */ 97 98 99 /* 100 * Make sure status messages are not buffered... 101 */ 102 103 setbuf(stderr, NULL); 104 105 /* 106 * Ignore SIGPIPE signals... 107 */ 108 109#ifdef HAVE_SIGSET 110 sigset(SIGPIPE, SIG_IGN); 111#elif defined(HAVE_SIGACTION) 112 memset(&action, 0, sizeof(action)); 113 action.sa_handler = SIG_IGN; 114 sigaction(SIGPIPE, &action, NULL); 115#else 116 signal(SIGPIPE, SIG_IGN); 117#endif /* HAVE_SIGSET */ 118 119 /* 120 * Check command-line... 121 */ 122 123 if (argc == 1) 124 { 125 printf("network socket \"Unknown\" \"%s\"\n", 126 _cupsLangString(cupsLangDefault(), _("AppSocket/HP JetDirect"))); 127 return (CUPS_BACKEND_OK); 128 } 129 else if (argc < 6 || argc > 7) 130 { 131 _cupsLangPrintf(stderr, 132 _("Usage: %s job-id user title copies options [file]"), 133 argv[0]); 134 return (CUPS_BACKEND_FAILED); 135 } 136 137 /* 138 * If we have 7 arguments, print the file named on the command-line. 139 * Otherwise, send stdin instead... 140 */ 141 142 if (argc == 6) 143 { 144 print_fd = 0; 145 copies = 1; 146 } 147 else 148 { 149 /* 150 * Try to open the print file... 151 */ 152 153 if ((print_fd = open(argv[6], O_RDONLY)) < 0) 154 { 155 _cupsLangPrintError("ERROR", _("Unable to open print file")); 156 return (CUPS_BACKEND_FAILED); 157 } 158 159 copies = atoi(argv[4]); 160 } 161 162 /* 163 * Extract the hostname and port number from the URI... 164 */ 165 166 while ((device_uri = cupsBackendDeviceURI(argv)) == NULL) 167 { 168 _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer.")); 169 sleep(10); 170 171 if (getenv("CLASS") != NULL) 172 return (CUPS_BACKEND_FAILED); 173 } 174 175 httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), 176 username, sizeof(username), hostname, sizeof(hostname), &port, 177 resource, sizeof(resource)); 178 179 if (port == 0) 180 port = 9100; /* Default to HP JetDirect/Tektronix PhaserShare */ 181 182 /* 183 * Get options, if any... 184 */ 185 186 waiteof = 1; 187 contimeout = 7 * 24 * 60 * 60; 188 189 if ((options = strchr(resource, '?')) != NULL) 190 { 191 /* 192 * Yup, terminate the device name string and move to the first 193 * character of the options... 194 */ 195 196 *options++ = '\0'; 197 198 /* 199 * Parse options... 200 */ 201 202 while (*options) 203 { 204 /* 205 * Get the name... 206 */ 207 208 name = options; 209 210 while (*options && *options != '=' && *options != '+' && *options != '&') 211 options ++; 212 213 if ((sep = *options) != '\0') 214 *options++ = '\0'; 215 216 if (sep == '=') 217 { 218 /* 219 * Get the value... 220 */ 221 222 value = options; 223 224 while (*options && *options != '+' && *options != '&') 225 options ++; 226 227 if (*options) 228 *options++ = '\0'; 229 } 230 else 231 value = (char *)""; 232 233 /* 234 * Process the option... 235 */ 236 237 if (!_cups_strcasecmp(name, "waiteof")) 238 { 239 /* 240 * Set the wait-for-eof value... 241 */ 242 243 waiteof = !value[0] || !_cups_strcasecmp(value, "on") || 244 !_cups_strcasecmp(value, "yes") || !_cups_strcasecmp(value, "true"); 245 } 246 else if (!_cups_strcasecmp(name, "snmp")) 247 { 248 /* 249 * Enable/disable SNMP stuff... 250 */ 251 252 snmp_enabled = !value[0] || !_cups_strcasecmp(value, "on") || 253 _cups_strcasecmp(value, "yes") || 254 _cups_strcasecmp(value, "true"); 255 } 256 else if (!_cups_strcasecmp(name, "contimeout")) 257 { 258 /* 259 * Set the connection timeout... 260 */ 261 262 if (atoi(value) > 0) 263 contimeout = atoi(value); 264 } 265 } 266 } 267 268 /* 269 * Then try finding the remote host... 270 */ 271 272 start_time = time(NULL); 273 274 sprintf(portname, "%d", port); 275 276 fputs("STATE: +connecting-to-device\n", stderr); 277 fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname); 278 279 while ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, portname)) == NULL) 280 { 281 _cupsLangPrintFilter(stderr, "INFO", 282 _("Unable to locate printer \"%s\"."), hostname); 283 sleep(10); 284 285 if (getenv("CLASS") != NULL) 286 { 287 fputs("STATE: -connecting-to-device\n", stderr); 288 return (CUPS_BACKEND_STOP); 289 } 290 } 291 292 /* 293 * See if the printer supports SNMP... 294 */ 295 296 if (snmp_enabled) 297 snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family); 298 else 299 snmp_fd = -1; 300 301 if (snmp_fd >= 0) 302 have_supplies = !backendSNMPSupplies(snmp_fd, &(addrlist->addr), 303 &start_count, NULL); 304 else 305 have_supplies = start_count = 0; 306 307 /* 308 * Wait for data from the filter... 309 */ 310 311 if (print_fd == 0) 312 { 313 if (!backendWaitLoop(snmp_fd, &(addrlist->addr), 1, backendNetworkSideCB)) 314 return (CUPS_BACKEND_OK); 315 else if ((bytes = read(0, buffer, sizeof(buffer))) <= 0) 316 return (CUPS_BACKEND_OK); 317 } 318 319 /* 320 * Connect to the printer... 321 */ 322 323 fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port); 324 _cupsLangPrintFilter(stderr, "INFO", _("Connecting to printer.")); 325 326 for (delay = 5;;) 327 { 328 if ((addr = httpAddrConnect(addrlist, &device_fd)) == NULL) 329 { 330 error = errno; 331 device_fd = -1; 332 333 if (getenv("CLASS") != NULL) 334 { 335 /* 336 * If the CLASS environment variable is set, the job was submitted 337 * to a class and not to a specific queue. In this case, we want 338 * to abort immediately so that the job can be requeued on the next 339 * available printer in the class. 340 */ 341 342 _cupsLangPrintFilter(stderr, "INFO", 343 _("Unable to contact printer, queuing on next " 344 "printer in class.")); 345 346 /* 347 * Sleep 5 seconds to keep the job from requeuing too rapidly... 348 */ 349 350 sleep(5); 351 352 return (CUPS_BACKEND_FAILED); 353 } 354 355 fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(error)); 356 357 if (error == ECONNREFUSED || error == EHOSTDOWN || 358 error == EHOSTUNREACH) 359 { 360 if (contimeout && (time(NULL) - start_time) > contimeout) 361 { 362 _cupsLangPrintFilter(stderr, "ERROR", 363 _("The printer is not responding.")); 364 return (CUPS_BACKEND_FAILED); 365 } 366 367 switch (error) 368 { 369 case EHOSTDOWN : 370 _cupsLangPrintFilter(stderr, "WARNING", 371 _("The printer may not exist or " 372 "is unavailable at this time.")); 373 break; 374 375 case EHOSTUNREACH : 376 _cupsLangPrintFilter(stderr, "WARNING", 377 _("The printer is unreachable at this " 378 "time.")); 379 break; 380 381 case ECONNREFUSED : 382 default : 383 _cupsLangPrintFilter(stderr, "WARNING", 384 _("The printer is in use.")); 385 break; 386 } 387 388 sleep(delay); 389 390 if (delay < 30) 391 delay += 5; 392 } 393 else 394 { 395 _cupsLangPrintFilter(stderr, "ERROR", 396 _("The printer is not responding.")); 397 sleep(30); 398 } 399 } 400 else 401 break; 402 } 403 404 fputs("STATE: -connecting-to-device\n", stderr); 405 _cupsLangPrintFilter(stderr, "INFO", _("Connected to printer.")); 406 407 fprintf(stderr, "DEBUG: Connected to %s:%d...\n", 408 httpAddrString(&(addr->addr), addrname, sizeof(addrname)), 409 httpAddrPort(&(addr->addr))); 410 411 /* 412 * Print everything... 413 */ 414 415 tbytes = 0; 416 417 if (bytes > 0) 418 tbytes += write(device_fd, buffer, bytes); 419 420 while (copies > 0 && tbytes >= 0) 421 { 422 copies --; 423 424 if (print_fd != 0) 425 { 426 fputs("PAGE: 1 1\n", stderr); 427 lseek(print_fd, 0, SEEK_SET); 428 } 429 430 tbytes = backendRunLoop(print_fd, device_fd, snmp_fd, &(addrlist->addr), 1, 431 0, backendNetworkSideCB); 432 433 if (print_fd != 0 && tbytes >= 0) 434 _cupsLangPrintFilter(stderr, "INFO", _("Print file sent.")); 435 } 436 437 if (waiteof) 438 { 439 /* 440 * Shutdown the socket and wait for the other end to finish... 441 */ 442 443 _cupsLangPrintFilter(stderr, "INFO", _("Waiting for printer to finish.")); 444 445 shutdown(device_fd, 1); 446 447 while (wait_bc(device_fd, 90) > 0); 448 } 449 450 /* 451 * Collect the final page count as needed... 452 */ 453 454 if (have_supplies && 455 !backendSNMPSupplies(snmp_fd, &(addrlist->addr), &page_count, NULL) && 456 page_count > start_count) 457 fprintf(stderr, "PAGE: total %d\n", page_count - start_count); 458 459 /* 460 * Close the socket connection... 461 */ 462 463 close(device_fd); 464 465 httpAddrFreeList(addrlist); 466 467 /* 468 * Close the input file and return... 469 */ 470 471 if (print_fd != 0) 472 close(print_fd); 473 474 return (CUPS_BACKEND_OK); 475} 476 477 478/* 479 * 'wait_bc()' - Wait for back-channel data... 480 */ 481 482static int /* O - # bytes read or -1 on error */ 483wait_bc(int device_fd, /* I - Socket */ 484 int secs) /* I - Seconds to wait */ 485{ 486 struct timeval timeout; /* Timeout for select() */ 487 fd_set input; /* Input set for select() */ 488 ssize_t bytes; /* Number of back-channel bytes read */ 489 char buffer[1024]; /* Back-channel buffer */ 490 491 492 /* 493 * Wait up to "secs" seconds for backchannel data... 494 */ 495 496 timeout.tv_sec = secs; 497 timeout.tv_usec = 0; 498 499 FD_ZERO(&input); 500 FD_SET(device_fd, &input); 501 502 if (select(device_fd + 1, &input, NULL, NULL, &timeout) > 0) 503 { 504 /* 505 * Grab the data coming back and spit it out to stderr... 506 */ 507 508 if ((bytes = read(device_fd, buffer, sizeof(buffer))) > 0) 509 { 510 fprintf(stderr, "DEBUG: Received %d bytes of back-channel data\n", 511 (int)bytes); 512 cupsBackChannelWrite(buffer, bytes, 1.0); 513 } 514 515 return (bytes); 516 } 517 else 518 return (-1); 519} 520 521 522/* 523 * End of "$Id: socket.c 11093 2013-07-03 20:48:42Z msweet $". 524 */ 525