1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * Add TSOL banner, trailer, page header/footers to a print job
31 */
32
33/* system header files */
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <time.h>
39#include <limits.h>
40#include <errno.h>
41#include <signal.h>
42#include <locale.h>
43#include <tsol/label.h>
44
45/* typedefs */
46
47typedef int BOOL;
48
49/* constants */
50
51#ifndef FALSE
52#define	FALSE 0
53#endif
54#ifndef TRUE
55#define	TRUE 1
56#endif
57
58#define	ME "lp.tsol_separator"
59#define	POSTSCRIPTLIB "/usr/lib/lp/postscript"
60#define	SEPARATORPS "tsol_separator.ps"
61#define	BANNERPS "tsol_banner.ps"
62#define	TRAILERPS "tsol_trailer.ps"
63#define	MAXUSERLEN 32
64#define	MAXHOSTLEN 32
65
66/* external variables */
67
68int	optind;			/* Used by getopt */
69char    *optarg;		/* Used by getopt */
70
71/* prototypes for static functions */
72
73static int ProcessArgs(int argc, char **argv);
74static void Usage(void);
75static void ParseUsername(char *input, char *user, char *host);
76static void EmitPSFile(const char *name);
77static BOOL EmitFile(FILE *file);
78static void EmitJobData(void);
79static void EmitPrologue(void);
80static void EmitCommandLineInfo(void);
81static void EmitClockBasedInfo(void);
82static void EmitLabelInfo(void);
83static void CopyStdin(void);
84
85/* static variables */
86
87static char *ArgSeparatorPS;
88static char *ArgBannerPS;
89static char *ArgTrailerPS;
90static char *ArgPSLib;
91static char *ArgPrinter;
92static char *ArgJobID;
93static char *ArgUser;
94static char *ArgTitle;
95static char *ArgFile;
96static BOOL ArgReverse;
97static BOOL ArgNoPageLabels;
98static int ArgDebugLevel;
99static FILE *ArgLogFile;
100static m_label_t *FileLabel;
101static char *remoteLabel;
102
103int
104main(int argc, char *argv[])
105{
106	int	err;
107	/*
108	 * Run immune from typical interruptions, so that
109	 * we stand a chance to get the fault message.
110	 * EOF (or startup error) is the only way out.
111	 */
112	(void) signal(SIGHUP, SIG_IGN);
113	(void) signal(SIGINT, SIG_IGN);
114	(void) signal(SIGQUIT, SIG_IGN);
115	(void) signal(SIGTERM, SIG_IGN);
116
117	(void) setlocale(LC_ALL, "");
118#if !defined(TEXT_DOMAIN)
119#define	TEXT_DOMAIN "SYS_TEST"
120#endif
121	(void) textdomain(TEXT_DOMAIN);
122
123	if (ProcessArgs(argc, argv) != 0)
124		exit(1);
125
126	if ((FileLabel = m_label_alloc(MAC_LABEL)) == NULL)
127		exit(1);
128	/*
129	 * If the job was submitted via remotely, the label of the
130	 * remote peer will be set in the SLABEL environment variable
131	 * by copying it out of the SECURE structure.
132	 *
133	 * If there is no SLABEL value, the job was submitted locally
134	 * via the named pipe, and the file label can be determined
135	 * from its pathname.
136	 */
137	if ((remoteLabel = getenv("SLABEL")) != NULL) {
138		m_label_free(FileLabel);
139		FileLabel = NULL;
140		if (str_to_label(remoteLabel, &FileLabel, MAC_LABEL,
141		    L_NO_CORRECTION, &err) == -1) {
142			perror("str_to_label");
143			exit(1);
144		}
145	} else if (getlabel(ArgFile, FileLabel) != 0) {
146		(void) fprintf(ArgLogFile,
147		    gettext("%1$s: cannot get label of %2$s: %3$s\n"),
148		    ME, ArgFile, strerror(errno));
149		exit(1);
150	}
151
152	/* All of these functions exit if they encounter an error */
153	EmitJobData();
154	EmitPSFile(ArgSeparatorPS);
155	if (ArgReverse)
156		EmitPSFile(ArgTrailerPS);
157	else
158		EmitPSFile(ArgBannerPS);
159	CopyStdin();
160	if (ArgReverse)
161		EmitPSFile(ArgBannerPS);
162	else
163		EmitPSFile(ArgTrailerPS);
164	if (ArgDebugLevel >= 1)
165		(void) fprintf(ArgLogFile, gettext("Done.\n"));
166	m_label_free(FileLabel);
167	return (0);
168}
169
170static void
171EmitJobData(void)
172{
173	EmitPrologue();
174	EmitCommandLineInfo();
175	EmitClockBasedInfo();
176	EmitLabelInfo();
177
178	/* Emit ending PostScript code */
179	(void) printf("end\n\n");
180	(void) printf("%%%% End of code generated by lp.tsol_separator\n\n");
181
182}
183
184static void
185EmitPrologue(void)
186{
187	/* Emit preliminary PostScript code */
188	(void) printf("%%!\n\n");
189	(void) printf("%%%% Begin code generated by lp.tsol_separator\n\n");
190
191	(void) printf("%%%% Create JobDict if it doesn't exist\n");
192	(void) printf("userdict /JobDict known not {\n");
193	(void) printf("  userdict /JobDict 100 dict put\n");
194	(void) printf("} if\n\n");
195
196	(void) printf("%%%% Define job parameters, including TSOL security "
197	    "info\n");
198	(void) printf("JobDict\n");
199	(void) printf("begin\n");
200}
201
202/* Emit parameters obtained from command line options */
203
204static void
205EmitCommandLineInfo(void)
206{
207	char user[MAXUSERLEN + 1];
208	char host[MAXHOSTLEN + 1];
209
210	(void) printf("\t/Job_Printer (%s) def\n", ArgPrinter);
211	ParseUsername(ArgUser, user, host);
212	(void) printf("\t/Job_Host (%s) def\n", host);
213	(void) printf("\t/Job_User (%s) def\n", user);
214	(void) printf("\t/Job_JobID (%s) def\n", ArgJobID);
215	(void) printf("\t/Job_Title (%s) def\n", ArgTitle);
216	(void) printf("\t/Job_DoPageLabels (%s) def\n",
217	    ArgNoPageLabels ? "NO" : "YES");
218	(void) printf("\n");
219}
220
221/* Emit parameters generated from the system clock */
222
223static void
224EmitClockBasedInfo(void)
225{
226	char timebuf[80];
227	struct timeval clockval;
228
229	(void) gettimeofday(&clockval, NULL);
230	(void) strftime(timebuf, sizeof (timebuf), NULL,
231	    localtime(&clockval.tv_sec));
232	(void) printf("\t/Job_Date (%s) def\n", timebuf);
233	(void) printf("\t/Job_Hash (%ld) def\n", clockval.tv_usec % 100000L);
234	(void) printf("\n");
235}
236
237/* Emit parameters derived from the SL and IL of the file being printed. */
238
239static void
240EmitLabelInfo(void)
241{
242	char	*header = NULL;		/* DIA banner page fields */
243	char	*label = NULL;
244	char	*caveats = NULL;
245	char	*channels = NULL;
246	char	*page_label = NULL;	/* interior pages label */
247
248	if (label_to_str(FileLabel, &header, PRINTER_TOP_BOTTOM,
249	    DEF_NAMES) != 0) {
250		(void) fprintf(ArgLogFile,
251		    gettext("%s: label_to_str PRINTER_TOP_BOTTOM: %s.\n"),
252		    ME, strerror(errno));
253		exit(1);
254	}
255	if (label_to_str(FileLabel, &label, PRINTER_LABEL,
256	    DEF_NAMES) != 0) {
257		(void) fprintf(ArgLogFile,
258		    gettext("%s: label_to_str PRINTER_LABEL: %s.\n"),
259		    ME, strerror(errno));
260		exit(1);
261	}
262	if (label_to_str(FileLabel, &caveats, PRINTER_CAVEATS,
263	    DEF_NAMES) != 0) {
264		(void) fprintf(ArgLogFile,
265		    gettext("%s: label_to_str PRINTER_CAVEATS: %s.\n"),
266		    ME, strerror(errno));
267		exit(1);
268	}
269	if (label_to_str(FileLabel, &channels, PRINTER_CHANNELS,
270	    DEF_NAMES) != 0) {
271		(void) fprintf(ArgLogFile,
272		    gettext("%s: label_to_str PRINTER_CHANNELS: %s.\n"),
273		    ME, strerror(errno));
274		exit(1);
275	}
276	if (label_to_str(FileLabel, &page_label, M_LABEL,
277	    LONG_NAMES) != 0) {
278		(void) fprintf(ArgLogFile,
279		    gettext("%s: label_to_str M_LABEL: %s.\n"),
280		    ME, strerror(errno));
281		exit(1);
282	}
283
284	(void) printf("\t/Job_Classification (%s) def\n", header);
285	(void) printf("\t/Job_Protect (%s) def\n", label);
286	(void) printf("\t/Job_Caveats (%s) def\n", caveats);
287	(void) printf("\t/Job_Channels (%s) def\n", channels);
288	(void) printf("\t/Job_SL_Internal (%s) def\n", page_label);
289
290	/* Free memory allocated label_to_str */
291	free(header);
292	free(label);
293	free(caveats);
294	free(channels);
295	free(page_label);
296}
297
298/*
299 * Parse input "host!user" to separate host and user names.
300 */
301
302static void
303ParseUsername(char *input, char *user, char *host)
304{
305	char *cp;
306
307	if ((cp = strchr(input, '@')) != NULL) {
308		/* user@host */
309		(void) strlcpy(host, cp + 1, MAXHOSTLEN + 1);
310		*cp = '\0';
311		(void) strlcpy(user, input, MAXUSERLEN + 1);
312		*cp = '@';
313	} else if ((cp = strchr(input, '!')) != NULL) {
314		/* host!user */
315		(void) strlcpy(user, cp + 1, MAXUSERLEN + 1);
316		*cp = '\0';
317		(void) strlcpy(host, input, MAXHOSTLEN + 1);
318		*cp = '!';
319	} else {
320		/* user */
321		(void) strlcpy(user, input, MAXUSERLEN + 1);
322		host[0] = '\0';
323	}
324}
325
326
327static void
328CopyStdin(void)
329{
330	if (!EmitFile(stdin)) {
331		(void) fprintf(ArgLogFile,
332		    gettext("%s: Error copying stdin to stdout\n"), ME);
333		exit(1);
334	}
335}
336
337
338static BOOL
339EmitFile(FILE *file)
340{
341	int len;
342#define	BUFLEN 1024
343	char buf[BUFLEN];
344
345	while ((len = fread(buf, 1, BUFLEN, file)) > 0) {
346		if (fwrite(buf, 1, len, stdout) != len)
347			return (FALSE);
348	}
349	if (!feof(file))
350		return (FALSE);
351	return (TRUE);
352}
353
354
355static void
356EmitPSFile(const char *name)
357{
358	char path[PATH_MAX];
359	FILE *file;
360	BOOL emitted;
361
362	if (name[0] != '/') {
363		(void) strlcpy(path, ArgPSLib, sizeof (path));
364		(void) strlcat(path, "/", sizeof (path));
365		(void) strlcat(path, name, sizeof (path));
366	} else {
367		(void) strlcpy(path, name, sizeof (path));
368	}
369
370	file = fopen(path, "r");
371	if (file == NULL) {
372		(void) fprintf(ArgLogFile,
373		    gettext("%s: Error opening PostScript file %s. %s.\n"),
374		    ME, path, strerror(errno));
375		exit(1);
376	}
377
378	emitted = EmitFile(file);
379	(void) fclose(file);
380	if (!emitted) {
381		(void) fprintf(ArgLogFile, gettext(
382		    "%s: Error copying PostScript file %s to stdout.\n"),
383		    ME, path);
384		exit(1);
385	}
386}
387
388
389static int
390ProcessArgs(int argc, char *argv[])
391{
392	int	option_letter;
393	char	*options_string = "lrd:e:s:b:t:L:";
394
395	/* set default values for arguments */
396	ArgSeparatorPS = SEPARATORPS;
397	ArgBannerPS = BANNERPS;
398	ArgTrailerPS = TRAILERPS;
399	ArgPSLib = POSTSCRIPTLIB;
400	ArgNoPageLabels = ArgReverse = FALSE;
401	ArgDebugLevel = 0;
402	ArgLogFile = stderr;
403
404	/* read switch arguments once to get error log file */
405	while ((option_letter = getopt(argc, argv, options_string)) != EOF) {
406		switch (option_letter) {
407		case 'd':
408			ArgDebugLevel = atoi(optarg);
409			break;
410		case 'e':
411			ArgLogFile = fopen(optarg, "a");
412			if (ArgLogFile == NULL) {
413				(void) fprintf(stderr,
414				    gettext("Cannot open log file %s\n"),
415				    optarg);
416				return (-1);
417			}
418			break;
419		case '?':	/* ? or unrecognized option */
420			Usage();
421			return (-1);
422		}
423	}
424
425	if (ArgDebugLevel > 0)
426		(void) fprintf(ArgLogFile,
427		    gettext("Processing switch arguments\n"));
428
429	/* re-read switch arguments */
430	optind = 1;
431	while ((option_letter = getopt(argc, argv, options_string)) != EOF) {
432		switch (option_letter) {
433		case 'd':
434			ArgDebugLevel = atoi(optarg);
435			break;
436		case 'e':
437			/* This was handled in earlier pass through args */
438			break;
439		case 'l':
440			ArgNoPageLabels = TRUE;
441			break;
442		case 'r':
443			ArgReverse = TRUE;
444			break;
445		case 's':
446			ArgSeparatorPS = optarg;
447			break;
448		case 'b':
449			ArgBannerPS = optarg;
450			break;
451		case 't':
452			ArgTrailerPS = optarg;
453			break;
454		case 'L':
455			ArgPSLib = optarg;
456			break;
457		case '?':	/* ? or unrecognized option */
458			Usage();
459			return (-1);
460		}
461	}
462
463	/* Adjust arguments to skip over options */
464	argc -= optind;		/* Number of remaining(non-switch) args */
465	argv += optind;		/* argv[0] is first(non-switch) args */
466
467	if (argc != 5) {
468		(void) fprintf(ArgLogFile,
469		    gettext("Wrong number of arguments.\n\n"));
470		Usage();
471		return (-1);
472	}
473
474	ArgPrinter = argv++[0];
475	ArgJobID = argv++[0];
476	ArgUser = argv++[0];
477	ArgTitle = argv++[0];
478	ArgFile = argv++[0];
479
480	if (ArgDebugLevel >= 1) {
481		(void) fprintf(ArgLogFile, gettext("Arguments processed\n"));
482		(void) fprintf(ArgLogFile, gettext("Printer: %s\n"),
483		    ArgPrinter);
484		(void) fprintf(ArgLogFile, gettext("Job ID: %s\n"), ArgJobID);
485		(void) fprintf(ArgLogFile, gettext("User: %s\n"), ArgUser);
486		(void) fprintf(ArgLogFile, gettext("Title: %s\n"), ArgTitle);
487		(void) fprintf(ArgLogFile, gettext("File: %s\n"), ArgFile);
488	}
489
490	return (0);
491}
492
493
494static void
495Usage(void)
496{
497	static const char *OPTFMT = "    %-8s %-9s %s\n";
498
499	(void) fprintf(ArgLogFile,
500	    gettext("Usage:  lp.tsol_separator [OPTIONS] %s\n"),
501	    gettext("PRINTER JOBID HOST!USER TITLE FILE"));
502	(void) fprintf(ArgLogFile, gettext("  OPTIONS:\n"));
503	(void) fprintf(ArgLogFile, OPTFMT, "-r", gettext("Reverse"),
504	    gettext("Reverse banner/trailer order"));
505	(void) fprintf(ArgLogFile, OPTFMT, "-l", gettext("Labels"),
506	    gettext("Suppress page header/footer labels"));
507	(void) fprintf(ArgLogFile, OPTFMT, gettext("-b FILE"),
508	    gettext("Banner"),
509	    gettext("PostScript program for banner (default tsol_banner.ps)"));
510	(void) fprintf(ArgLogFile, OPTFMT, gettext("-s FILE"),
511	    gettext("Separator"),
512	    gettext("PostScript program for separator "
513	    "(default tsol_separator.ps)"));
514	(void) fprintf(ArgLogFile, OPTFMT, gettext("-t FILE"),
515	    gettext("Trailer"),
516	    gettext("PostScript program for trailer "
517	    "(default tsol_trailer.ps)"));
518	(void) fprintf(ArgLogFile, OPTFMT, gettext("-L DIR"),
519	    gettext("Library"),
520	    gettext("Directory to search for PostScript programs"));
521	(void) fprintf(ArgLogFile, OPTFMT, "", "",
522	    gettext("(default /usr/lib/lp/postscript)"));
523	(void) fprintf(ArgLogFile, OPTFMT, gettext("-d N"), gettext("Debug"),
524	    gettext("Set debug level to N"));
525	(void) fprintf(ArgLogFile, OPTFMT, gettext("-e FILE"),
526	    gettext("Error File"),
527	    gettext("Append error and debugging output to FILE"));
528}
529