]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/testlpd.c
License change: Apache License, Version 2.0.
[thirdparty/cups.git] / scheduler / testlpd.c
1 /*
2 * cups-lpd test program for CUPS.
3 *
4 * Copyright 2007-2015 by Apple Inc.
5 * Copyright 2006 by Easy Software Products, all rights reserved.
6 *
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
8 */
9
10 /*
11 * Include necessary headers...
12 */
13
14 #include <cups/cups.h>
15 #include <cups/string-private.h>
16 #include <sys/stat.h>
17 #include <sys/wait.h>
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);
28 static int print_job(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4)));
29 static int print_waiting(int outfd, int infd, char *dest);
30 static int remove_job(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4)));
31 static int status_long(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4)));
32 static int status_short(int outfd, int infd, char *dest, char **args) __attribute__((nonnull(4)));
33 static void usage(void) __attribute__((noreturn));
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 */
53 cupslpd_pid, /* Process ID for cups-lpd */
54 cupslpd_status; /* Status of cups-lpd process */
55
56
57 /*
58 * Collect command-line arguments...
59 */
60
61 op = NULL;
62 opargs = argv + argc;
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
72 if (!argv[i][2])
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")))
100 {
101 printf("op=\"%s\", dest=\"%s\", opargs=%p\n", op, dest, opargs);
102 usage();
103 }
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
129 dup2(cupslpd_stdin[0], 0);
130 close(cupslpd_stdin[0]);
131 close(cupslpd_stdin[1]);
132
133 dup2(cupslpd_stdout[1], 1);
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);
162 else
163 {
164 printf("Unknown operation \"%s\"!\n", op);
165 status = 1;
166 }
167
168 /*
169 * Kill the test program...
170 */
171
172 close(cupslpd_stdin[1]);
173 close(cupslpd_stdout[0]);
174
175 while (wait(&cupslpd_status) != cupslpd_pid);
176
177 printf("cups-lpd exit status was %d...\n", cupslpd_status);
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 {
196 size_t len; /* Length of command line */
197 char status; /* Status byte */
198
199
200 printf("COMMAND: %02X %s", command[0], command + 1);
201
202 len = strlen(command);
203
204 if ((size_t)write(outfd, command, len) < len)
205 {
206 puts(" Write failed!");
207 return (-1);
208 }
209
210 if (read(infd, &status, 1) < 1)
211 puts("STATUS: ERROR");
212 else
213 printf("STATUS: %d\n", status);
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 */
237 ssize_t bytes; /* Bytes read/written */
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"
282 "ldfA%03dlocalhost\n"
283 "UdfA%03dlocalhost\n"
284 "N%s\n",
285 cupsUser(), jobname, sequence, sequence, jobname);
286
287 /*
288 * Send the control file...
289 */
290
291 bytes = (ssize_t)strlen(control);
292
293 snprintf(command, sizeof(command), "\002%d cfA%03dlocalhost\n",
294 (int)bytes, sequence);
295
296 if ((status = do_command(outfd, infd, command)) != 0)
297 {
298 close(fd);
299 return (status);
300 }
301
302 bytes ++;
303
304 if (write(outfd, control, (size_t)bytes) < bytes)
305 {
306 printf("CONTROL: Unable to write %d bytes!\n", (int)bytes);
307 close(fd);
308 return (-1);
309 }
310
311 printf("CONTROL: Wrote %d bytes.\n", (int)bytes);
312
313 if (read(infd, command, 1) < 1)
314 {
315 puts("STATUS: ERROR");
316 close(fd);
317 return (-1);
318 }
319 else
320 {
321 status = command[0];
322
323 printf("STATUS: %d\n", status);
324 }
325
326 /*
327 * Send the data file...
328 */
329
330 snprintf(command, sizeof(command), "\003%d dfA%03dlocalhost\n",
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 {
341 if (write(outfd, buffer, (size_t)bytes) < bytes)
342 {
343 printf("DATA: Unable to write %d bytes!\n", (int)bytes);
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 {
357 puts("STATUS: ERROR");
358 close(fd);
359 return (-1);
360 }
361 else
362 {
363 status = command[0];
364
365 printf("STATUS: %d\n", status);
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 */
437 ssize_t bytes; /* Bytes read/written */
438
439
440 /*
441 * Send the "send short status" command...
442 */
443
444 if (args[0])
445 snprintf(command, sizeof(command), "\004%s %s\n", dest, args[0]);
446 else
447 snprintf(command, sizeof(command), "\004%s\n", dest);
448
449 bytes = (ssize_t)strlen(command);
450
451 if (write(outfd, command, (size_t)bytes) < bytes)
452 return (-1);
453
454 /*
455 * Read the status back...
456 */
457
458 while ((bytes = read(infd, buffer, sizeof(buffer))) > 0)
459 {
460 fwrite(buffer, 1, (size_t)bytes, stdout);
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 */
480 ssize_t bytes; /* Bytes read/written */
481
482
483 /*
484 * Send the "send short status" command...
485 */
486
487 if (args[0])
488 snprintf(command, sizeof(command), "\003%s %s\n", dest, args[0]);
489 else
490 snprintf(command, sizeof(command), "\003%s\n", dest);
491
492 bytes = (ssize_t)strlen(command);
493
494 if (write(outfd, command, (size_t)bytes) < bytes)
495 return (-1);
496
497 /*
498 * Read the status back...
499 */
500
501 while ((bytes = read(infd, buffer, sizeof(buffer))) > 0)
502 {
503 fwrite(buffer, 1, (size_t)bytes, stdout);
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 {
518 puts("Usage: testlpd [options] print-job printer filename [... filename]");
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 }