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