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