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