1 /*
2 * Parallel port backend for OpenPrinting CUPS Filters.
3 *
4 * Copyright 2007-2011 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
6 *
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "COPYING"
10 * which should have been included with this file.
11 *
12 * Contents:
13 *
14 * main() - Send a file to the specified parallel port.
15 * drain_output() - Drain pending print data to the device.
16 * list_devices() - List all parallel devices.
17 * run_loop() - Read and write print and back-channel data.
18 * side_cb() - Handle side-channel requests...
19 */
20
21 /*
22 * Include necessary headers.
23 */
24
25 #include "backend-private.h"
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <termios.h>
29 #include <sys/socket.h>
30
31
32 /*
33 * Local functions...
34 */
35
36 static int drain_output(int print_fd, int device_fd);
37 static void list_devices(void);
38 static ssize_t run_loop(int print_fd, int device_fd, int use_bc,
39 int update_state);
40 static int side_cb(int print_fd, int device_fd, int use_bc);
41
42
43 /*
44 * 'main()' - Send a file to the specified parallel port.
45 *
46 * Usage:
47 *
48 * printer-uri job-id user title copies options [file]
49 */
50
51 int /* O - Exit status */
main(int argc,char * argv[])52 main(int argc, /* I - Number of command-line arguments (6 or 7) */
53 char *argv[]) /* I - Command-line arguments */
54 {
55 char method[255], /* Method in URI */
56 hostname[1024], /* Hostname */
57 username[255], /* Username info (not used) */
58 resource[1024], /* Resource info (device and options) */
59 *options; /* Pointer to options */
60 int port; /* Port number (not used) */
61 int print_fd, /* Print file */
62 device_fd, /* Parallel device */
63 use_bc; /* Read back-channel data? */
64 int copies; /* Number of copies to print */
65 ssize_t tbytes; /* Total number of bytes written */
66 struct termios opts; /* Parallel port options */
67 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
68 struct sigaction action; /* Actions for POSIX signals */
69 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
70
71
72 /*
73 * Make sure status messages are not buffered...
74 */
75
76 setbuf(stderr, NULL);
77
78 /*
79 * Ignore SIGPIPE signals...
80 */
81
82 #ifdef HAVE_SIGSET
83 sigset(SIGPIPE, SIG_IGN);
84 #elif defined(HAVE_SIGACTION)
85 memset(&action, 0, sizeof(action));
86 action.sa_handler = SIG_IGN;
87 sigaction(SIGPIPE, &action, NULL);
88 #else
89 signal(SIGPIPE, SIG_IGN);
90 #endif /* HAVE_SIGSET */
91
92 /*
93 * Check command-line...
94 */
95
96 if (argc == 1)
97 {
98 list_devices();
99 return (CUPS_BACKEND_OK);
100 }
101 else if (argc < 6 || argc > 7)
102 {
103 fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
104 argv[0]);
105 return (CUPS_BACKEND_FAILED);
106 }
107
108 /*
109 * If we have 7 arguments, print the file named on the command-line.
110 * Otherwise, send stdin instead...
111 */
112
113 if (argc == 6)
114 {
115 print_fd = 0;
116 copies = 1;
117 }
118 else
119 {
120 /*
121 * Try to open the print file...
122 */
123
124 if ((print_fd = open(argv[6], O_RDONLY)) < 0)
125 {
126 perror("ERROR: Unable to open print file");
127 return (CUPS_BACKEND_FAILED);
128 }
129
130 copies = atoi(argv[4]);
131 }
132
133 /*
134 * Extract the device name and options from the URI...
135 */
136
137 httpSeparateURI(HTTP_URI_CODING_ALL, cupsBackendDeviceURI(argv),
138 method, sizeof(method), username, sizeof(username),
139 hostname, sizeof(hostname), &port,
140 resource, sizeof(resource));
141
142 /*
143 * See if there are any options...
144 */
145
146 if ((options = strchr(resource, '?')) != NULL)
147 {
148 /*
149 * Yup, terminate the device name string and move to the first
150 * character of the options...
151 */
152
153 *options++ = '\0';
154 }
155
156 /*
157 * Open the parallel port device...
158 */
159
160 fputs("STATE: +connecting-to-device\n", stderr);
161
162 do
163 {
164 #if defined(__linux) || defined(__FreeBSD__)
165 /*
166 * The Linux and FreeBSD parallel port drivers currently are broken WRT
167 * select() and bidirection I/O...
168 */
169
170 device_fd = open(resource, O_WRONLY | O_EXCL);
171 use_bc = 0;
172
173 #else
174 if ((device_fd = open(resource, O_RDWR | O_EXCL)) < 0)
175 {
176 device_fd = open(resource, O_WRONLY | O_EXCL);
177 use_bc = 0;
178 }
179 else
180 use_bc = 1;
181 #endif /* __linux || __FreeBSD__ */
182
183 if (device_fd == -1)
184 {
185 if (getenv("CLASS") != NULL)
186 {
187 /*
188 * If the CLASS environment variable is set, the job was submitted
189 * to a class and not to a specific queue. In this case, we want
190 * to abort immediately so that the job can be requeued on the next
191 * available printer in the class.
192 */
193
194 fputs("INFO: Unable to contact printer, queuing on next printer in "
195 "class.\n", stderr);
196
197 /*
198 * Sleep 5 seconds to keep the job from requeuing too rapidly...
199 */
200
201 sleep(5);
202
203 return (CUPS_BACKEND_FAILED);
204 }
205
206 if (errno == EBUSY)
207 {
208 fputs("INFO: Printer busy; will retry in 30 seconds.\n", stderr);
209 sleep(30);
210 }
211 else if (errno == ENXIO || errno == EIO || errno == ENOENT)
212 {
213 fputs("INFO: Printer not connected; will retry in 30 seconds.\n",
214 stderr);
215 sleep(30);
216 }
217 else
218 {
219 perror("ERROR: Unable to open parallel port");
220 return (CUPS_BACKEND_FAILED);
221 }
222 }
223 }
224 while (device_fd < 0);
225
226 fputs("STATE: -connecting-to-device\n", stderr);
227
228 /*
229 * Set any options provided...
230 */
231
232 tcgetattr(device_fd, &opts);
233
234 opts.c_lflag &= ~(ICANON | ECHO | ISIG); /* Raw mode */
235
236 /**** No options supported yet ****/
237
238 tcsetattr(device_fd, TCSANOW, &opts);
239
240 /*
241 * Finally, send the print file...
242 */
243
244 tbytes = 0;
245
246 while (copies > 0 && tbytes >= 0)
247 {
248 copies --;
249
250 if (print_fd != 0)
251 {
252 fputs("PAGE: 1 1\n", stderr);
253 lseek(print_fd, 0, SEEK_SET);
254 }
255
256 tbytes = run_loop(print_fd, device_fd, use_bc, 1);
257
258 if (print_fd != 0 && tbytes >= 0)
259 fputs("INFO: Print file sent.\n", stderr);
260 }
261
262 /*
263 * Close the socket connection and input file and return...
264 */
265
266 close(device_fd);
267
268 if (print_fd != 0)
269 close(print_fd);
270
271 return (CUPS_BACKEND_OK);
272 }
273
274
275 /*
276 * 'drain_output()' - Drain pending print data to the device.
277 */
278
279 static int /* O - 0 on success, -1 on error */
drain_output(int print_fd,int device_fd)280 drain_output(int print_fd, /* I - Print file descriptor */
281 int device_fd) /* I - Device file descriptor */
282 {
283 int nfds; /* Maximum file descriptor value + 1 */
284 fd_set input; /* Input set for reading */
285 ssize_t print_bytes, /* Print bytes read */
286 bytes; /* Bytes written */
287 char print_buffer[8192], /* Print data buffer */
288 *print_ptr; /* Pointer into print data buffer */
289 struct timeval timeout; /* Timeout for read... */
290
291
292 /*
293 * Figure out the maximum file descriptor value to use with select()...
294 */
295
296 nfds = (print_fd > device_fd ? print_fd : device_fd) + 1;
297
298 /*
299 * Now loop until we are out of data from print_fd...
300 */
301
302 for (;;)
303 {
304 /*
305 * Use select() to determine whether we have data to copy around...
306 */
307
308 FD_ZERO(&input);
309 FD_SET(print_fd, &input);
310
311 timeout.tv_sec = 0;
312 timeout.tv_usec = 0;
313
314 if (select(nfds, &input, NULL, NULL, &timeout) < 0)
315 return (-1);
316
317 if (!FD_ISSET(print_fd, &input))
318 return (0);
319
320 if ((print_bytes = read(print_fd, print_buffer,
321 sizeof(print_buffer))) < 0)
322 {
323 /*
324 * Read error - bail if we don't see EAGAIN or EINTR...
325 */
326
327 if (errno != EAGAIN && errno != EINTR)
328 {
329 perror("ERROR: Unable to read print data");
330 return (-1);
331 }
332
333 print_bytes = 0;
334 }
335 else if (print_bytes == 0)
336 {
337 /*
338 * End of file, return...
339 */
340
341 return (0);
342 }
343
344 fprintf(stderr, "DEBUG: Read %d bytes of print data.\n",
345 (int)print_bytes);
346
347 for (print_ptr = print_buffer; print_bytes > 0;)
348 {
349 if ((bytes = write(device_fd, print_ptr, print_bytes)) < 0)
350 {
351 /*
352 * Write error - bail if we don't see an error we can retry...
353 */
354
355 if (errno != ENOSPC && errno != ENXIO && errno != EAGAIN &&
356 errno != EINTR && errno != ENOTTY)
357 {
358 perror("ERROR: Unable to write print data");
359 return (-1);
360 }
361 }
362 else
363 {
364 fprintf(stderr, "DEBUG: Wrote %d bytes of print data.\n", (int)bytes);
365
366 print_bytes -= bytes;
367 print_ptr += bytes;
368 }
369 }
370 }
371 }
372
373
374 /*
375 * 'list_devices()' - List all parallel devices.
376 */
377
378 static void
list_devices(void)379 list_devices(void)
380 {
381 #ifdef __sun
382 static char *funky_hex = "0123456789abcdefghijklmnopqrstuvwxyz";
383 /* Funky hex numbering used for some devices */
384 #endif /* __sun */
385
386 #ifdef __linux
387 int i; /* Looping var */
388 int fd; /* File descriptor */
389 char device[512], /* Device filename */
390 basedevice[255], /* Base device filename for ports */
391 device_id[1024], /* Device ID string */
392 make_model[1024], /* Make and model */
393 info[2048], /* Info string */
394 uri[1024]; /* Device URI */
395
396
397 if (!access("/dev/parallel/", 0))
398 strcpy(basedevice, "/dev/parallel/");
399 else if (!access("/dev/printers/", 0))
400 strcpy(basedevice, "/dev/printers/");
401 else
402 strcpy(basedevice, "/dev/lp");
403
404 for (i = 0; i < 4; i ++)
405 {
406 /*
407 * Open the port, if available...
408 */
409
410 sprintf(device, "%s%d", basedevice, i);
411 if ((fd = open(device, O_RDWR | O_EXCL)) < 0)
412 fd = open(device, O_WRONLY);
413
414 if (fd >= 0)
415 {
416 /*
417 * Now grab the IEEE 1284 device ID string...
418 */
419
420 snprintf(uri, sizeof(uri), "parallel:%s", device);
421
422 if (!backendGetDeviceID(fd, device_id, sizeof(device_id),
423 make_model, sizeof(make_model),
424 NULL, uri, sizeof(uri)))
425 {
426 snprintf(info, sizeof(info), "%s LPT #%d", make_model, i + 1);
427 cupsBackendReport("direct", uri, make_model, info, device_id, NULL);
428 }
429 else
430 {
431 snprintf(info, sizeof(info), "LPT #%d", i + 1);
432 cupsBackendReport("direct", uri, NULL, info, NULL, NULL);
433 }
434
435 close(fd);
436 }
437 }
438 #elif defined(__sun)
439 int i, j, n; /* Looping vars */
440 char device[255]; /* Device filename */
441
442
443 /*
444 * Standard parallel ports...
445 */
446
447 for (i = 0; i < 10; i ++)
448 {
449 sprintf(device, "/dev/ecpp%d", i);
450 if (access(device, 0) == 0)
451 printf("direct parallel:%s \"Unknown\" \"Sun IEEE-1284 Parallel Port #%d\"\n",
452 device, i + 1);
453 }
454
455 for (i = 0; i < 10; i ++)
456 {
457 sprintf(device, "/dev/bpp%d", i);
458 if (access(device, 0) == 0)
459 printf("direct parallel:%s \"Unknown\" \"Sun Standard Parallel Port #%d\"\n",
460 device, i + 1);
461 }
462
463 for (i = 0; i < 3; i ++)
464 {
465 sprintf(device, "/dev/lp%d", i);
466
467 if (access(device, 0) == 0)
468 printf("direct parallel:%s \"Unknown\" \"PC Parallel Port #%d\"\n",
469 device, i + 1);
470 }
471
472 /*
473 * MAGMA parallel ports...
474 */
475
476 for (i = 0; i < 40; i ++)
477 {
478 sprintf(device, "/dev/pm%02d", i);
479 if (access(device, 0) == 0)
480 printf("direct parallel:%s \"Unknown\" \"MAGMA Parallel Board #%d Port #%d\"\n",
481 device, (i / 10) + 1, (i % 10) + 1);
482 }
483
484 /*
485 * Central Data parallel ports...
486 */
487
488 for (i = 0; i < 9; i ++)
489 for (j = 0; j < 8; j ++)
490 for (n = 0; n < 32; n ++)
491 {
492 if (i == 8) /* EtherLite */
493 sprintf(device, "/dev/sts/lpN%d%c", j, funky_hex[n]);
494 else
495 sprintf(device, "/dev/sts/lp%c%d%c", i + 'C', j,
496 funky_hex[n]);
497
498 if (access(device, 0) == 0)
499 {
500 if (i == 8)
501 printf("direct parallel:%s \"Unknown\" \"Central Data EtherLite Parallel Port, ID %d, port %d\"\n",
502 device, j, n);
503 else
504 printf("direct parallel:%s \"Unknown\" \"Central Data SCSI Parallel Port, logical bus %d, ID %d, port %d\"\n",
505 device, i, j, n);
506 }
507 }
508 #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
509 int i; /* Looping var */
510 int fd; /* File descriptor */
511 char device[255]; /* Device filename */
512
513
514 for (i = 0; i < 3; i ++)
515 {
516 sprintf(device, "/dev/lpt%d", i);
517 if ((fd = open(device, O_WRONLY)) >= 0)
518 {
519 close(fd);
520 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d (interrupt-driven)\"\n", device, i + 1);
521 }
522
523 sprintf(device, "/dev/lpa%d", i);
524 if ((fd = open(device, O_WRONLY)) >= 0)
525 {
526 close(fd);
527 printf("direct parallel:%s \"Unknown\" \"Parallel Port #%d (polled)\"\n", device, i + 1);
528 }
529 }
530 #endif
531 }
532
533
534 /*
535 * 'run_loop()' - Read and write print and back-channel data.
536 */
537
538 static ssize_t /* O - Total bytes on success, -1 on error */
run_loop(int print_fd,int device_fd,int use_bc,int update_state)539 run_loop(int print_fd, /* I - Print file descriptor */
540 int device_fd, /* I - Device file descriptor */
541 int use_bc, /* I - Use back-channel? */
542 int update_state) /* I - Update printer-state-reasons? */
543 {
544 int nfds; /* Maximum file descriptor value + 1 */
545 fd_set input, /* Input set for reading */
546 output; /* Output set for writing */
547 ssize_t print_bytes, /* Print bytes read */
548 bc_bytes, /* Backchannel bytes read */
549 total_bytes, /* Total bytes written */
550 bytes; /* Bytes written */
551 int paperout; /* "Paper out" status */
552 int offline; /* "Off-line" status */
553 char print_buffer[8192], /* Print data buffer */
554 *print_ptr, /* Pointer into print data buffer */
555 bc_buffer[1024]; /* Back-channel data buffer */
556 struct timeval timeout; /* Timeout for select() */
557 int sc_ok; /* Flag a side channel error and
558 stop using the side channel
559 in such a case. */
560 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
561 struct sigaction action; /* Actions for POSIX signals */
562 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
563
564
565 /*
566 * If we are printing data from a print driver on stdin, ignore SIGTERM
567 * so that the driver can finish out any page data, e.g. to eject the
568 * current page. We only do this for stdin printing as otherwise there
569 * is no way to cancel a raw print job...
570 */
571
572 if (!print_fd)
573 {
574 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
575 sigset(SIGTERM, SIG_IGN);
576 #elif defined(HAVE_SIGACTION)
577 memset(&action, 0, sizeof(action));
578
579 sigemptyset(&action.sa_mask);
580 action.sa_handler = SIG_IGN;
581 sigaction(SIGTERM, &action, NULL);
582 #else
583 signal(SIGTERM, SIG_IGN);
584 #endif /* HAVE_SIGSET */
585 }
586 else if (print_fd < 0)
587 {
588 /*
589 * Copy print data from stdin, but don't mess with the signal handlers...
590 */
591
592 print_fd = 0;
593 }
594
595 /*
596 * Figure out the maximum file descriptor value to use with select()...
597 */
598
599 nfds = (print_fd > device_fd ? print_fd : device_fd) + 1;
600
601
602 /*
603 * Side channel is OK...
604 */
605
606 sc_ok = 1;
607
608 /*
609 * Now loop until we are out of data from print_fd...
610 */
611
612 for (print_bytes = 0, print_ptr = print_buffer, offline = -1,
613 paperout = -1, total_bytes = 0;;)
614 {
615 /*
616 * Use select() to determine whether we have data to copy around...
617 */
618
619 FD_ZERO(&input);
620 if (!print_bytes)
621 FD_SET(print_fd, &input);
622 if (use_bc)
623 FD_SET(device_fd, &input);
624 if (!print_bytes && sc_ok)
625 FD_SET(CUPS_SC_FD, &input);
626
627 FD_ZERO(&output);
628 if (print_bytes)
629 FD_SET(device_fd, &output);
630
631 timeout.tv_sec = 5;
632 timeout.tv_usec = 0;
633
634 if (select(nfds, &input, &output, NULL, &timeout) < 0)
635 {
636 /*
637 * Pause printing to clear any pending errors...
638 */
639
640 if (errno == ENXIO && offline != 1 && update_state)
641 {
642 fputs("STATE: +offline-report\n", stderr);
643 offline = 1;
644 }
645 else if (errno == EINTR && total_bytes == 0)
646 {
647 fputs("DEBUG: Received an interrupt before any bytes were "
648 "written, aborting.\n", stderr);
649 return (0);
650 }
651
652 sleep(1);
653 continue;
654 }
655
656 /*
657 * Check if we have a side-channel request ready...
658 */
659
660 if (sc_ok && FD_ISSET(CUPS_SC_FD, &input))
661 {
662 /*
663 * Do the side-channel request, then start back over in the select
664 * loop since it may have read from print_fd...
665 *
666 * If the side channel processing errors, go straight on to avoid
667 * blocking of the backend by side channel problems, deactivate the side
668 * channel.
669 */
670
671 if (side_cb(print_fd, device_fd, use_bc))
672 sc_ok = 0;
673 continue;
674 }
675
676 /*
677 * Check if we have back-channel data ready...
678 */
679
680 if (FD_ISSET(device_fd, &input))
681 {
682 if ((bc_bytes = read(device_fd, bc_buffer, sizeof(bc_buffer))) > 0)
683 {
684 fprintf(stderr, "DEBUG: Received %d bytes of back-channel data.\n",
685 (int)bc_bytes);
686 cupsBackChannelWrite(bc_buffer, bc_bytes, 1.0);
687 }
688 else if (bc_bytes < 0 && errno != EAGAIN && errno != EINTR)
689 {
690 perror("DEBUG: Error reading back-channel data");
691 use_bc = 0;
692 }
693 else if (bc_bytes == 0)
694 use_bc = 0;
695 }
696
697 /*
698 * Check if we have print data ready...
699 */
700
701 if (FD_ISSET(print_fd, &input))
702 {
703 if ((print_bytes = read(print_fd, print_buffer,
704 sizeof(print_buffer))) < 0)
705 {
706 /*
707 * Read error - bail if we don't see EAGAIN or EINTR...
708 */
709
710 if (errno != EAGAIN && errno != EINTR)
711 {
712 perror("ERROR: Unable to read print data");
713 return (-1);
714 }
715
716 print_bytes = 0;
717 }
718 else if (print_bytes == 0)
719 {
720 /*
721 * End of file, break out of the loop...
722 */
723
724 break;
725 }
726
727 print_ptr = print_buffer;
728
729 fprintf(stderr, "DEBUG: Read %d bytes of print data.\n",
730 (int)print_bytes);
731 }
732
733 /*
734 * Check if the device is ready to receive data and we have data to
735 * send...
736 */
737
738 if (print_bytes && FD_ISSET(device_fd, &output))
739 {
740 if ((bytes = write(device_fd, print_ptr, print_bytes)) < 0)
741 {
742 /*
743 * Write error - bail if we don't see an error we can retry...
744 */
745
746 if (errno == ENOSPC)
747 {
748 if (paperout != 1 && update_state)
749 {
750 fputs("STATE: +media-empty-warning\n", stderr);
751 paperout = 1;
752 }
753 }
754 else if (errno == ENXIO)
755 {
756 if (offline != 1 && update_state)
757 {
758 fputs("STATE: +offline-report\n", stderr);
759 offline = 1;
760 }
761 }
762 else if (errno != EAGAIN && errno != EINTR && errno != ENOTTY)
763 {
764 perror("ERROR: Unable to write print data");
765 return (-1);
766 }
767 }
768 else
769 {
770 if (paperout && update_state)
771 {
772 fputs("STATE: -media-empty-warning\n", stderr);
773 paperout = 0;
774 }
775
776 if (offline && update_state)
777 {
778 fputs("STATE: -offline-report\n", stderr);
779 offline = 0;
780 }
781
782 fprintf(stderr, "DEBUG: Wrote %d bytes of print data...\n", (int)bytes);
783
784 print_bytes -= bytes;
785 print_ptr += bytes;
786 total_bytes += bytes;
787 }
788 }
789 }
790
791 /*
792 * Return with success...
793 */
794
795 return (total_bytes);
796 }
797
798
799 /*
800 * 'side_cb()' - Handle side-channel requests...
801 */
802
803 static int /* O - 0 on success, -1 on error */
side_cb(int print_fd,int device_fd,int use_bc)804 side_cb(int print_fd, /* I - Print file */
805 int device_fd, /* I - Device file */
806 int use_bc) /* I - Using back-channel? */
807 {
808 cups_sc_command_t command; /* Request command */
809 cups_sc_status_t status; /* Request/response status */
810 char data[2048]; /* Request/response data */
811 int datalen; /* Request/response data size */
812
813
814 datalen = sizeof(data);
815
816 if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0))
817 return (-1);
818
819 switch (command)
820 {
821 case CUPS_SC_CMD_DRAIN_OUTPUT :
822 if (drain_output(print_fd, device_fd))
823 status = CUPS_SC_STATUS_IO_ERROR;
824 else if (tcdrain(device_fd))
825 status = CUPS_SC_STATUS_IO_ERROR;
826 else
827 status = CUPS_SC_STATUS_OK;
828
829 datalen = 0;
830 break;
831
832 case CUPS_SC_CMD_GET_BIDI :
833 status = CUPS_SC_STATUS_OK;
834 data[0] = use_bc;
835 datalen = 1;
836 break;
837
838 case CUPS_SC_CMD_GET_DEVICE_ID :
839 memset(data, 0, sizeof(data));
840
841 if (backendGetDeviceID(device_fd, data, sizeof(data) - 1,
842 NULL, 0, NULL, NULL, 0))
843 {
844 status = CUPS_SC_STATUS_NOT_IMPLEMENTED;
845 datalen = 0;
846 }
847 else
848 {
849 status = CUPS_SC_STATUS_OK;
850 datalen = strlen(data);
851 }
852 break;
853
854 default :
855 status = CUPS_SC_STATUS_NOT_IMPLEMENTED;
856 datalen = 0;
857 break;
858 }
859
860 return (cupsSideChannelWrite(command, status, data, datalen, 1.0));
861 }
862