1/* 2 * "$Id: testlpd.c 11560 2014-02-06 20:10:19Z msweet $" 3 * 4 * cups-lpd test program for CUPS. 5 * 6 * Copyright 2007-2014 by Apple Inc. 7 * Copyright 2006 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 * which should have been included with this file. If this file is 13 * file is missing or damaged, see the license at "http://www.cups.org/". 14 */ 15 16/* 17 * Include necessary headers... 18 */ 19 20#include <cups/cups.h> 21#include <cups/string-private.h> 22#include <sys/stat.h> 23#include <sys/wait.h> 24#include <signal.h> 25#include <unistd.h> 26#include <fcntl.h> 27 28 29/* 30 * Local functions... 31 */ 32 33static int do_command(int outfd, int infd, const char *command); 34static int print_job(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4))); 35static int print_waiting(int outfd, int infd, char *dest); 36static int remove_job(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4))); 37static int status_long(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4))); 38static int status_short(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4))); 39static void usage(void) __attribute__((noreturn)); 40 41 42/* 43 * 'main()' - Simulate an LPD client. 44 */ 45 46int /* O - Exit status */ 47main(int argc, /* I - Number of command-line arguments */ 48 char *argv[]) /* I - Command-line arguments */ 49{ 50 int i; /* Looping var */ 51 int status; /* Test status */ 52 char *op, /* Operation to test */ 53 **opargs, /* Remaining arguments */ 54 *dest; /* Destination */ 55 int cupslpd_argc; /* Argument count for cups-lpd */ 56 char *cupslpd_argv[1000]; /* Arguments for cups-lpd */ 57 int cupslpd_stdin[2], /* Standard input for cups-lpd */ 58 cupslpd_stdout[2], /* Standard output for cups-lpd */ 59 cupslpd_pid, /* Process ID for cups-lpd */ 60 cupslpd_status; /* Status of cups-lpd process */ 61 62 63 /* 64 * Collect command-line arguments... 65 */ 66 67 op = NULL; 68 opargs = argv + argc; 69 dest = NULL; 70 cupslpd_argc = 1; 71 cupslpd_argv[0] = (char *)"cups-lpd"; 72 73 for (i = 1; i < argc; i ++) 74 if (!strncmp(argv[i], "-o", 2)) 75 { 76 cupslpd_argv[cupslpd_argc++] = argv[i]; 77 78 if (!argv[i][2]) 79 { 80 i ++; 81 82 if (i >= argc) 83 usage(); 84 85 cupslpd_argv[cupslpd_argc++] = argv[i]; 86 } 87 } 88 else if (argv[i][0] == '-') 89 usage(); 90 else if (!op) 91 op = argv[i]; 92 else if (!dest) 93 dest = argv[i]; 94 else 95 { 96 opargs = argv + i; 97 break; 98 } 99 100 if (!op || 101 (!strcmp(op, "print-job") && (!dest || !opargs)) || 102 (!strcmp(op, "remove-job") && (!dest || !opargs)) || 103 (strcmp(op, "print-job") && strcmp(op, "print-waiting") && 104 strcmp(op, "remove-job") && strcmp(op, "status-long") && 105 strcmp(op, "status-short"))) 106 { 107 printf("op=\"%s\", dest=\"%s\", opargs=%p\n", op, dest, opargs); 108 usage(); 109 } 110 111 /* 112 * Run the cups-lpd program using pipes... 113 */ 114 115 cupslpd_argv[cupslpd_argc] = NULL; 116 117 pipe(cupslpd_stdin); 118 pipe(cupslpd_stdout); 119 120 if ((cupslpd_pid = fork()) < 0) 121 { 122 /* 123 * Error! 124 */ 125 126 perror("testlpd: Unable to fork"); 127 return (1); 128 } 129 else if (cupslpd_pid == 0) 130 { 131 /* 132 * Child goes here... 133 */ 134 135 dup2(cupslpd_stdin[0], 0); 136 close(cupslpd_stdin[0]); 137 close(cupslpd_stdin[1]); 138 139 dup2(cupslpd_stdout[1], 1); 140 close(cupslpd_stdout[0]); 141 close(cupslpd_stdout[1]); 142 143 execv("./cups-lpd", cupslpd_argv); 144 145 perror("testlpd: Unable to exec ./cups-lpd"); 146 exit(errno); 147 } 148 else 149 { 150 close(cupslpd_stdin[0]); 151 close(cupslpd_stdout[1]); 152 } 153 154 /* 155 * Do the operation test... 156 */ 157 158 if (!strcmp(op, "print-job")) 159 status = print_job(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs); 160 else if (!strcmp(op, "print-waiting")) 161 status = print_waiting(cupslpd_stdin[1], cupslpd_stdout[0], dest); 162 else if (!strcmp(op, "remove-job")) 163 status = remove_job(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs); 164 else if (!strcmp(op, "status-long")) 165 status = status_long(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs); 166 else if (!strcmp(op, "status-short")) 167 status = status_short(cupslpd_stdin[1], cupslpd_stdout[0], dest, opargs); 168 else 169 { 170 printf("Unknown operation \"%s\"!\n", op); 171 status = 1; 172 } 173 174 /* 175 * Kill the test program... 176 */ 177 178 close(cupslpd_stdin[1]); 179 close(cupslpd_stdout[0]); 180 181 while (wait(&cupslpd_status) != cupslpd_pid); 182 183 printf("cups-lpd exit status was %d...\n", cupslpd_status); 184 185 /* 186 * Return the test status... 187 */ 188 189 return (status); 190} 191 192 193/* 194 * 'do_command()' - Send the LPD command and wait for a response. 195 */ 196 197static int /* O - Status from cups-lpd */ 198do_command(int outfd, /* I - Command file descriptor */ 199 int infd, /* I - Response file descriptor */ 200 const char *command) /* I - Command line to send */ 201{ 202 size_t len; /* Length of command line */ 203 char status; /* Status byte */ 204 205 206 printf("COMMAND: %02X %s", command[0], command + 1); 207 208 len = strlen(command); 209 210 if ((size_t)write(outfd, command, len) < len) 211 { 212 puts(" Write failed!"); 213 return (-1); 214 } 215 216 if (read(infd, &status, 1) < 1) 217 puts("STATUS: ERROR"); 218 else 219 printf("STATUS: %d\n", status); 220 221 return (status); 222} 223 224 225/* 226 * 'print_job()' - Submit a file for printing. 227 */ 228 229static int /* O - Status from cups-lpd */ 230print_job(int outfd, /* I - Command file descriptor */ 231 int infd, /* I - Response file descriptor */ 232 char *dest, /* I - Destination */ 233 char **args) /* I - Arguments */ 234{ 235 int fd; /* Print file descriptor */ 236 char command[1024], /* Command buffer */ 237 control[1024], /* Control file */ 238 buffer[8192]; /* Print buffer */ 239 int status; /* Status of command */ 240 struct stat fileinfo; /* File information */ 241 char *jobname; /* Job name */ 242 int sequence; /* Sequence number */ 243 ssize_t bytes; /* Bytes read/written */ 244 245 246 /* 247 * Check the print file... 248 */ 249 250 if (stat(args[0], &fileinfo)) 251 { 252 perror(args[0]); 253 return (-1); 254 } 255 256 if ((fd = open(args[0], O_RDONLY)) < 0) 257 { 258 perror(args[0]); 259 return (-1); 260 } 261 262 /* 263 * Send the "receive print job" command... 264 */ 265 266 snprintf(command, sizeof(command), "\002%s\n", dest); 267 if ((status = do_command(outfd, infd, command)) != 0) 268 { 269 close(fd); 270 return (status); 271 } 272 273 /* 274 * Format a control file string that will be used to submit the job... 275 */ 276 277 if ((jobname = strrchr(args[0], '/')) != NULL) 278 jobname ++; 279 else 280 jobname = args[0]; 281 282 sequence = (int)getpid() % 1000; 283 284 snprintf(control, sizeof(control), 285 "Hlocalhost\n" 286 "P%s\n" 287 "J%s\n" 288 "ldfA%03dlocalhost\n" 289 "UdfA%03dlocalhost\n" 290 "N%s\n", 291 cupsUser(), jobname, sequence, sequence, jobname); 292 293 /* 294 * Send the control file... 295 */ 296 297 bytes = (ssize_t)strlen(control); 298 299 snprintf(command, sizeof(command), "\002%d cfA%03dlocalhost\n", 300 (int)bytes, sequence); 301 302 if ((status = do_command(outfd, infd, command)) != 0) 303 { 304 close(fd); 305 return (status); 306 } 307 308 bytes ++; 309 310 if (write(outfd, control, (size_t)bytes) < bytes) 311 { 312 printf("CONTROL: Unable to write %d bytes!\n", (int)bytes); 313 close(fd); 314 return (-1); 315 } 316 317 printf("CONTROL: Wrote %d bytes.\n", (int)bytes); 318 319 if (read(infd, command, 1) < 1) 320 { 321 puts("STATUS: ERROR"); 322 close(fd); 323 return (-1); 324 } 325 else 326 { 327 status = command[0]; 328 329 printf("STATUS: %d\n", status); 330 } 331 332 /* 333 * Send the data file... 334 */ 335 336 snprintf(command, sizeof(command), "\003%d dfA%03dlocalhost\n", 337 (int)fileinfo.st_size, sequence); 338 339 if ((status = do_command(outfd, infd, command)) != 0) 340 { 341 close(fd); 342 return (status); 343 } 344 345 while ((bytes = read(fd, buffer, sizeof(buffer))) > 0) 346 { 347 if (write(outfd, buffer, (size_t)bytes) < bytes) 348 { 349 printf("DATA: Unable to write %d bytes!\n", (int)bytes); 350 close(fd); 351 return (-1); 352 } 353 } 354 355 write(outfd, "", 1); 356 357 close(fd); 358 359 printf("DATA: Wrote %d bytes.\n", (int)fileinfo.st_size); 360 361 if (read(infd, command, 1) < 1) 362 { 363 puts("STATUS: ERROR"); 364 close(fd); 365 return (-1); 366 } 367 else 368 { 369 status = command[0]; 370 371 printf("STATUS: %d\n", status); 372 } 373 374 return (status); 375} 376 377 378/* 379 * 'print_waiting()' - Print waiting jobs. 380 */ 381 382static int /* O - Status from cups-lpd */ 383print_waiting(int outfd, /* I - Command file descriptor */ 384 int infd, /* I - Response file descriptor */ 385 char *dest) /* I - Destination */ 386{ 387 char command[1024]; /* Command buffer */ 388 389 390 /* 391 * Send the "print waiting jobs" command... 392 */ 393 394 snprintf(command, sizeof(command), "\001%s\n", dest); 395 396 return (do_command(outfd, infd, command)); 397} 398 399 400/* 401 * 'remove_job()' - Cancel a print job. 402 */ 403 404static int /* O - Status from cups-lpd */ 405remove_job(int outfd, /* I - Command file descriptor */ 406 int infd, /* I - Response file descriptor */ 407 char *dest, /* I - Destination */ 408 char **args) /* I - Arguments */ 409{ 410 int i; /* Looping var */ 411 char command[1024]; /* Command buffer */ 412 413 /* 414 * Send the "remove jobs" command... 415 */ 416 417 snprintf(command, sizeof(command), "\005%s", dest); 418 419 for (i = 0; args[i]; i ++) 420 { 421 strlcat(command, " ", sizeof(command)); 422 strlcat(command, args[i], sizeof(command)); 423 } 424 425 strlcat(command, "\n", sizeof(command)); 426 427 return (do_command(outfd, infd, command)); 428} 429 430 431/* 432 * 'status_long()' - Show the long printer status. 433 */ 434 435static int /* O - Status from cups-lpd */ 436status_long(int outfd, /* I - Command file descriptor */ 437 int infd, /* I - Response file descriptor */ 438 char *dest, /* I - Destination */ 439 char **args) /* I - Arguments */ 440{ 441 char command[1024], /* Command buffer */ 442 buffer[8192]; /* Status buffer */ 443 ssize_t bytes; /* Bytes read/written */ 444 445 446 /* 447 * Send the "send short status" command... 448 */ 449 450 if (args) 451 snprintf(command, sizeof(command), "\004%s %s\n", dest, args[0]); 452 else 453 snprintf(command, sizeof(command), "\004%s\n", dest); 454 455 bytes = (ssize_t)strlen(command); 456 457 if (write(outfd, command, (size_t)bytes) < bytes) 458 return (-1); 459 460 /* 461 * Read the status back... 462 */ 463 464 while ((bytes = read(infd, buffer, sizeof(buffer))) > 0) 465 { 466 fwrite(buffer, 1, (size_t)bytes, stdout); 467 fflush(stdout); 468 } 469 470 return (0); 471} 472 473 474/* 475 * 'status_short()' - Show the short printer status. 476 */ 477 478static int /* O - Status from cups-lpd */ 479status_short(int outfd, /* I - Command file descriptor */ 480 int infd, /* I - Response file descriptor */ 481 char *dest, /* I - Destination */ 482 char **args) /* I - Arguments */ 483{ 484 char command[1024], /* Command buffer */ 485 buffer[8192]; /* Status buffer */ 486 ssize_t bytes; /* Bytes read/written */ 487 488 489 /* 490 * Send the "send short status" command... 491 */ 492 493 if (args) 494 snprintf(command, sizeof(command), "\003%s %s\n", dest, args[0]); 495 else 496 snprintf(command, sizeof(command), "\003%s\n", dest); 497 498 bytes = (ssize_t)strlen(command); 499 500 if (write(outfd, command, (size_t)bytes) < bytes) 501 return (-1); 502 503 /* 504 * Read the status back... 505 */ 506 507 while ((bytes = read(infd, buffer, sizeof(buffer))) > 0) 508 { 509 fwrite(buffer, 1, (size_t)bytes, stdout); 510 fflush(stdout); 511 } 512 513 return (0); 514} 515 516 517/* 518 * 'usage()' - Show program usage... 519 */ 520 521static void 522usage(void) 523{ 524 puts("Usage: testlpd [options] print-job printer filename [... filename]"); 525 puts(" testlpd [options] print-waiting [printer or user]"); 526 puts(" testlpd [options] remove-job printer [user [job-id]]"); 527 puts(" testlpd [options] status-long [printer or user]"); 528 puts(" testlpd [options] status-short [printer or user]"); 529 puts(""); 530 puts("Options:"); 531 puts(" -o name=value"); 532 533 exit(0); 534} 535 536 537/* 538 * End of "$Id: testlpd.c 11560 2014-02-06 20:10:19Z msweet $". 539 */ 540