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