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