xref: /illumos-gate/usr/src/cmd/lp/lib/printers/putprinter.c (revision 2a8bcb4efb45d99ac41c94a75c396b362c414f7f)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 /* EMACS_MODES: !fill, lnumb, !overwrite, !nodelete, !picture */
32 
33 #include "sys/types.h"
34 #include "sys/stat.h"
35 #include "stdio.h"
36 #include "string.h"
37 #include "errno.h"
38 #include "stdlib.h"
39 
40 #include "lp.h"
41 #include "printers.h"
42 
43 #include <unistd.h>
44 #include <sys/wait.h>
45 
46 #define	SHELL "/bin/sh"
47 #define	PPDZIP ".gz"
48 
49 extern struct {
50 	char			*v;
51 	short			len,
52 				okremote;
53 }			prtrheadings[];
54 
55 #if	defined(__STDC__)
56 
57 static void		print_sdn (int, char *, SCALED);
58 static void		print_l (int, char *, char **);
59 static void		print_str (int, char *, char *);
60 
61 #ifdef LP_USE_PAPI_ATTR
62 static int addPrintersPPD(char *name, PRINTER *prbufp);
63 static int copyPPDFile(char *ppd, char *printersPPD);
64 static int unzipPPDFile(char *ppd, char *printersPPD);
65 #endif
66 
67 #else
68 
69 static void		print_sdn(),
70 			print_l(),
71 			print_str();
72 
73 #ifdef LP_USE_PAPI_ATTR
74 static int addPrintersPPD();
75 static int copyPPDFile();
76 static int unzipPPDFile();
77 #endif
78 
79 #endif
80 
81 unsigned long		ignprinter	= 0;
82 int			ppdopt		= 0;
83 
84 /**
85  ** putprinter() - WRITE PRINTER STRUCTURE TO DISK FILES
86  **/
87 
88 int
putprinter(char * name,PRINTER * prbufp)89 putprinter(char *name, PRINTER *prbufp)
90 {
91 	register char *		path;
92 	register char *		stty;
93 	register char *		speed;
94 
95 	int fdin, fdout;
96 
97 	int			fld;
98 
99 	char			buf[BUFSIZ];
100 
101 	struct stat		statbuf1,
102 				statbuf2;
103 
104 
105 	badprinter = 0;
106 
107 	if (!name || !*name) {
108 		errno = EINVAL;
109 		return (-1);
110 	}
111 
112 	if (STREQU(NAME_ALL, name)) {
113 		errno = EINVAL;
114 		return (-1);
115 	}
116 
117 	/*
118 	 * First go through the structure and see if we have
119 	 * anything strange.
120 	 */
121 	if (!okprinter(name, prbufp, 1)) {
122 		errno = EINVAL;
123 		return (-1);
124 	}
125 
126 	if (!Lp_A_Printers || !Lp_A_Interfaces) {
127 		getadminpaths (LPUSER);
128 		if (!Lp_A_Printers || !Lp_A_Interfaces)
129 			return (0);
130 	}
131 
132 	/*
133 	 * Create the parent directory for this printer
134 	 * if it doesn't yet exist.
135 	 */
136 	if (!(path = getprinterfile(name, (char *)0)))
137 		return (-1);
138 	if (Stat(path, &statbuf1) == 0) {
139 		if (!S_ISDIR(statbuf1.st_mode)) {
140 			Free (path);
141 			errno = ENOTDIR;
142 			return (-1);
143 		}
144 	} else if (errno != ENOENT || mkdir_lpdir(path, MODE_DIR) == -1) {
145 		Free (path);
146 		return (-1);
147 	}
148 	Free (path);
149 
150 	/*
151 	 * Create the copy of the interface program, unless
152 	 * that would be silly or not desired.
153 	 * Conversely, make sure the interface program doesn't
154 	 * exist for a remote printer.
155 	 */
156 	if (prbufp->remote) {
157 		if (!(path = makepath(Lp_A_Interfaces, name, (char *)0)))
158 			return (-1);
159 		(void)rmfile (path);
160 		Free (path);
161 	}
162 	if (prbufp->interface && (ignprinter & BAD_INTERFACE) == 0) {
163 		if (Stat(prbufp->interface, &statbuf1) == -1)
164 			return (-1);
165 		if (!(path = makepath(Lp_A_Interfaces, name, (char *)0)))
166 			return (-1);
167 		if (
168 			Stat(path, &statbuf2) == -1
169 		     || statbuf1.st_dev != statbuf2.st_dev
170 		     || statbuf1.st_ino != statbuf2.st_ino
171 		) {
172 			register int		n;
173 
174 			if ((fdin = open_locked(prbufp->interface, "r", 0)) < 0) {
175 				Free (path);
176 				return (-1);
177 			}
178 			if ((fdout = open_locked(path, "w", MODE_EXEC)) < 0) {
179 				Free (path);
180 				close(fdin);
181 				return (-1);
182 			}
183 			while ((n = read(fdin, buf, BUFSIZ)) > 0)
184 				write (fdout, buf,  n);
185 			close(fdout);
186 			close(fdin);
187 		}
188 		Free (path);
189 	}
190 
191 #ifdef LP_USE_PAPI_ATTR
192 	/*
193 	 * Handle PPD (Postscript Printer Definition) file for printer
194 	 * if this printer has been configured with one
195 	 */
196 	if ((prbufp->ppd != NULL) && (ppdopt))
197 	{
198 		if (addPrintersPPD(name, prbufp) != 0)
199 		{
200 			/* failed to added the printers PPD file */
201 			return (-1);
202 		}
203 	}
204 #endif
205 
206 	/*
207 	 * If this printer is dialed up, remove any baud rates
208 	 * from the stty option list and move the last one to
209 	 * the ".speed" member if the ".speed" member isn't already
210 	 * set. Conversely, if this printer is directly connected,
211 	 * move any value from the ".speed" member to the stty list.
212 	 */
213 
214 	stty = (prbufp->stty? Strdup(prbufp->stty) : 0);
215 	if (prbufp->speed)
216 		speed = Strdup(prbufp->speed);
217 	else
218 		speed = 0;
219 
220 	if (prbufp->dial_info && stty) {
221 		register char		*newstty,
222 					*p,
223 					*q;
224 
225 		register int		len;
226 
227 		if (!(q = newstty = Malloc(strlen(stty) + 1))) {
228 			Free (stty);
229 			errno = ENOMEM;
230 			return (-1);
231 		}
232 		newstty[0] = 0;	/* start with empty copy */
233 
234 		for (
235 			p = strtok(stty, " ");
236 			p;
237 			p = strtok((char *)0, " ")
238 		) {
239 			len = strlen(p);
240 			if (strspn(p, "0123456789") == len) {
241 				/*
242 				 * If "prbufp->speed" isn't set, then
243 				 * use the speed we just found. Don't
244 				 * check "speed", because if more than
245 				 * one speed was given in the list, we
246 				 * want the last one.
247 				 */
248 				if (!prbufp->speed) {
249 					if (speed)
250 						Free (speed);
251 					speed = Strdup(p);
252 				}
253 
254 			} else {
255 				/*
256 				 * Not a speed, so copy it to the
257 				 * new stty string.
258 				 */
259 				if (q != newstty)
260 					*q++ = ' ';
261 				strcpy (q, p);
262 				q += len;
263 			}
264 		}
265 
266 		Free (stty);
267 		stty = newstty;
268 
269 	} else if (!prbufp->dial_info && speed) {
270 		register char		*newstty;
271 
272 		newstty = Malloc(strlen(stty) + 1 + strlen(speed) + 1);
273 		if (!newstty) {
274 			if (stty)
275 				Free (stty);
276 			errno = ENOMEM;
277 			return (-1);
278 		}
279 
280 		if (stty) {
281 			strcpy (newstty, stty);
282 			strcat (newstty, " ");
283 			strcat (newstty, speed);
284 			Free (stty);
285 		} else
286 			strcpy (newstty, speed);
287 		Free (speed);
288 		speed = 0;
289 
290 		stty = newstty;
291 
292 	}
293 
294 	/*
295 	 * Open the configuration file and write out the printer
296 	 * configuration.
297 	 */
298 
299 	if (!(path = getprinterfile(name, CONFIGFILE))) {
300 		if (stty)
301 			Free (stty);
302 		if (speed)
303 			Free (speed);
304 		return (-1);
305 	}
306 	if ((fdout = open_locked(path, "w", MODE_READ)) < 0) {
307 		Free (path);
308 		if (stty)
309 			Free (stty);
310 		if (speed)
311 			Free (speed);
312 		return (-1);
313 	}
314 	Free (path);
315 
316 	errno = 0;
317 	for (fld = 0; fld < PR_MAX; fld++) {
318 		if (prbufp->remote && !prtrheadings[fld].okremote)
319 			continue;
320 
321 		switch (fld) {
322 
323 #define HEAD	prtrheadings[fld].v
324 
325 		case PR_BAN:
326 			{
327 				char *ptr = NAME_ON;
328 
329 				switch (prbufp->banner) {
330 				case BAN_ALWAYS:
331 					ptr = NAME_ON;
332 					break;
333 				case BAN_NEVER:
334 					ptr = NAME_OFF;
335 					break;
336 				case BAN_OPTIONAL:
337 					ptr = NAME_OPTIONAL;
338 					break;
339 				}
340 				(void)fdprintf(fdout, "%s %s\n", HEAD, ptr);
341 			}
342 			break;
343 
344 		case PR_CPI:
345 			print_sdn(fdout, HEAD, prbufp->cpi);
346 			break;
347 
348 		case PR_CS:
349 			if (!emptylist(prbufp->char_sets))
350 				print_l(fdout, HEAD, prbufp->char_sets);
351 			break;
352 
353 		case PR_ITYPES:
354 			/*
355 			 * Put out the header even if the list is empty,
356 			 * to distinguish no input types from the default.
357 			 */
358 			print_l(fdout, HEAD, prbufp->input_types);
359 			break;
360 
361 		case PR_DEV:
362 			print_str(fdout, HEAD, prbufp->device);
363 			break;
364 
365 		case PR_DIAL:
366 			print_str(fdout, HEAD, prbufp->dial_info);
367 			break;
368 
369 		case PR_RECOV:
370 			print_str(fdout, HEAD, prbufp->fault_rec);
371 			break;
372 
373 		case PR_INTFC:
374 			print_str(fdout, HEAD, prbufp->interface);
375 			break;
376 
377 		case PR_LPI:
378 			print_sdn(fdout, HEAD, prbufp->lpi);
379 			break;
380 
381 		case PR_LEN:
382 			print_sdn(fdout, HEAD, prbufp->plen);
383 			break;
384 
385 		case PR_LOGIN:
386 			if (prbufp->login & LOG_IN)
387 				(void)fdprintf(fdout, "%s\n", HEAD);
388 			break;
389 
390 		case PR_PTYPE:
391 		{
392 			char			**printer_types;
393 
394 			/*
395 			 * For backward compatibility for those who
396 			 * use only "->printer_type", we have to play
397 			 * some games here.
398 			 */
399 			if (prbufp->printer_type && !prbufp->printer_types)
400 				printer_types = getlist(
401 					prbufp->printer_type,
402 					LP_WS,
403 					LP_SEP
404 				);
405 			else
406 				printer_types = prbufp->printer_types;
407 
408 			if (!printer_types || !*printer_types)
409 				print_str(fdout, HEAD, NAME_UNKNOWN);
410 			else
411 				print_l(fdout, HEAD, printer_types);
412 
413 			if (printer_types != prbufp->printer_types)
414 				freelist (printer_types);
415 			break;
416 		}
417 
418 		case PR_REMOTE:
419 			print_str(fdout, HEAD, prbufp->remote);
420 			break;
421 
422 		case PR_SPEED:
423 			print_str(fdout, HEAD, speed);
424 			break;
425 
426 		case PR_STTY:
427 			print_str(fdout, HEAD, stty);
428 			break;
429 
430 		case PR_WIDTH:
431 			print_sdn(fdout, HEAD, prbufp->pwid);
432 			break;
433 
434 #if	defined(CAN_DO_MODULES)
435 		case PR_MODULES:
436 			/*
437 			 * Put out the header even if the list is empty,
438 			 * to distinguish no modules from the default.
439 			 */
440 			print_l(fdout, HEAD, prbufp->modules);
441 			break;
442 #endif
443 
444 		case PR_OPTIONS:
445 			print_l(fdout, HEAD, prbufp->options);
446 			break;
447 
448 		case PR_PPD:
449 		{
450 			print_str(fdout, HEAD, prbufp->ppd);
451 			break;
452 		}
453 		}
454 
455 	}
456 	if (stty)
457 		Free (stty);
458 	if (speed)
459 		Free (speed);
460 	if (errno != 0) {
461 		close(fdout);
462 		return (-1);
463 	}
464 	close(fdout);
465 
466 	/*
467 	 * If we have a description of the printer,
468 	 * write it out to a separate file.
469 	 */
470 	if (prbufp->description) {
471 
472 		if (!(path = getprinterfile(name, COMMENTFILE)))
473 			return (-1);
474 
475 		if (dumpstring(path, prbufp->description) == -1) {
476 			Free (path);
477 			return (-1);
478 		}
479 		Free (path);
480 
481 	}
482 
483 	/*
484 	 * Now write out the alert condition.
485 	 */
486 	if (
487 		prbufp->fault_alert.shcmd
488 	     && putalert(Lp_A_Printers, name, &(prbufp->fault_alert)) == -1
489 	)
490 		return (-1);
491 
492 	return (0);
493 }
494 
495 /**
496  ** print_sdn() - PRINT SCALED DECIMAL NUMBER WITH HEADER
497  ** print_l() - PRINT (char **) LIST WITH HEADER
498  ** print_str() - PRINT STRING WITH HEADER
499  **/
500 
501 static void
print_sdn(int fd,char * head,SCALED sdn)502 print_sdn(int fd, char *head, SCALED sdn)
503 {
504 	if (sdn.val <= 0)
505 		return;
506 
507 	(void)fdprintf (fd, "%s ", head);
508 	fdprintsdn (fd, sdn);
509 
510 	return;
511 }
512 
513 static void
print_l(int fd,char * head,char ** list)514 print_l(int fd, char *head, char **list)
515 {
516 	(void)fdprintf (fd, "%s ", head);
517 	printlist_setup (0, 0, LP_SEP, 0);
518 	fdprintlist (fd, list);
519 	printlist_unsetup ();
520 
521 	return;
522 }
523 
524 static void
print_str(int fd,char * head,char * str)525 print_str(int fd, char *head, char *str)
526 {
527 	if (!str || !*str)
528 		return;
529 
530 	(void)fdprintf (fd, "%s %s\n", head, str);
531 
532 	return;
533 }
534 
535 
536 #ifdef LP_USE_PAPI_ATTR
537 /*
538  * Function:     addPrintersPPD()
539  *
540  * Description:  Handle PPD (Postscript Printer Definition) file for this
541  *               printer if it has been configured with one
542  *
543  */
544 
545 static int
addPrintersPPD(char * name,PRINTER * prbufp)546 addPrintersPPD(char *name, PRINTER *prbufp)
547 
548 {
549 	int result = 0;
550 	char *path = NULL;
551 	char *ppd = NULL;
552 	char  buf[BUFSIZ];
553 	struct stat statbuf;
554 
555 	(void) snprintf(buf, sizeof (buf), "%s.ppd", name);
556 	if (prbufp->remote)
557 	{
558 		/* make sure the PPD file doesn't exist for a remote printer */
559 		if (!(path = makepath(ETCDIR, "ppd", buf, (char *)0)))
560 		{
561 			result = -1;
562 		}
563 		else
564 		{
565 			(void) rmfile(path);
566 		}
567 	}
568 
569 	if ((result == 0) && (prbufp->ppd != NULL))
570 	{
571 		ppd = strdup(prbufp->ppd);
572 
573 		if (ppd == NULL)
574 		{
575 			result = -1;
576 		}
577 		else
578 		{
579 			/* Check the PPD file given exists */
580 
581 			if (Stat(ppd, &statbuf) == -1)
582 			{
583 				/*
584 				 * The given ppd files does not exist, but
585 				 * check if there is a zipped version of the
586 				 * file that we can use instead
587 				 */
588 				if (strstr(ppd, PPDZIP) != NULL)
589 				{
590 					/* this is a zipped file so exit */
591 					result = -1;
592 				}
593 				else
594 				{
595 					ppd = Realloc(ppd,
596 						strlen(ppd)+strlen(PPDZIP)+2);
597 					if (ppd != NULL)
598 					{
599 						ppd = strcat(ppd, PPDZIP);
600 						if (Stat(ppd, &statbuf) == -1)
601 						{
602 							/*
603 							 * this zipped version
604 							 * of the file does not
605 							 * exist either
606 							 */
607 							result = -1;
608 						}
609 					}
610 					else
611 					{
612 						result = -1;
613 					}
614 				}
615 			}
616 		}
617 
618 		/*
619 		 * Create the copy of the PPD file for this printer
620 		 * unless that would be silly or not desired
621 		 */
622 
623 		if (result == 0)
624 		{
625 			if (!(path = makepath(ETCDIR, "ppd", buf, (char *)0)))
626 			{
627 				result = -1;
628 			}
629 		}
630 
631 		/*
632 		 * At this point we may have a zipped or unzipped ppd file, if
633 		 * it's unzipped just copy it otherwise unzip it to the
634 		 * printer's ppd file (/etc/lp/ppd/<printer>.ppd)
635 		 */
636 
637 		if (result == 0)
638 		{
639 			if (strstr(ppd, PPDZIP) == NULL)
640 			{
641 				result = copyPPDFile(ppd, path);
642 			}
643 			else
644 			{
645 				result = unzipPPDFile(ppd, path);
646 			}
647 
648 			(void) chown_lppath(path);
649 			(void) chmod(path, 0644);
650 		}
651 
652 		if (ppd != NULL)
653 		{
654 			Free(ppd);
655 		}
656 		if (path != NULL)
657 		{
658 			Free(path);
659 		}
660 	}
661 
662 	return (result);
663 } /* addPrintersPPD() */
664 
665 
666 /*
667  * Function:     copyPPDFile()
668  *
669  * Description:  Copy the given ppd file to the printer's file in /etc/lp/ppd
670  *
671  */
672 
673 static int
copyPPDFile(char * ppd,char * printersPPD)674 copyPPDFile(char *ppd, char *printersPPD)
675 
676 {
677 	int  result = 0;
678 	register int n = 0;
679 	int  fdin  = 0;
680 	int  fdout = 0;
681 	char buf[BUFSIZ];
682 
683 	if ((ppd != NULL) && (printersPPD != NULL))
684 	{
685 		if ((fdin = open_locked(ppd, "r", 0)) < 0)
686 		{
687 			result = -1;
688 		}
689 		else
690 		{
691 			fdout = open_locked(printersPPD, "w", MODE_EXEC);
692 			if (fdout < 0)
693 			{
694 				close(fdin);
695 				result = -1;
696 			}
697 		}
698 
699 		if (result == 0)
700 		{
701 			while ((n = read(fdin, buf, BUFSIZ)) > 0)
702 			{
703 				write(fdout, buf,  n);
704 			}
705 			close(fdout);
706 			close(fdin);
707 		}
708 	}
709 	else
710 	{
711 		result = -1;
712 	}
713 
714 	return (result);
715 } /* copyPPDFile() */
716 
717 
718 
719 /*
720  * Function:     unzipPPDFile()
721  *
722  * Description:  Unzip the given ppd file to the printer's file in /etc/lp/ppd.
723  *               This is done by forking and running the unzip utility on the
724  *               zipped ppd file.
725  *
726  */
727 
728 static int
unzipPPDFile(char * ppd,char * printersPPD)729 unzipPPDFile(char *ppd, char *printersPPD)
730 
731 {
732 	int  result = -1;
733 	char *cmdLine = NULL;
734 	pid_t childPID = 0;
735 	int   stat = 0;
736 	int   clSize = 0;
737 
738 
739 	if ((ppd != NULL) && (printersPPD != NULL))
740 	{
741 		childPID = fork();
742 
743 		switch (childPID)
744 		{
745 			case -1:
746 			{
747 				/* return error */
748 				break;
749 			}
750 
751 			case 0:
752 			{
753 				/* child process  - so execute something */
754 
755 				clSize = strlen("/usr/bin/rm -f ") +
756 						strlen(printersPPD) +
757 						strlen("/usr/bin/gzip -dc ") +
758 						strlen(ppd) +
759 						strlen(printersPPD) + 20;
760 				cmdLine = malloc(clSize);
761 				if (cmdLine != NULL)
762 				{
763 
764 					(void) snprintf(cmdLine, clSize,
765 				"/usr/bin/rm -f %s; /usr/bin/gzip -dc %s > %s",
766 							printersPPD, ppd,
767 							printersPPD);
768 					result = execl(SHELL, SHELL, "-c",
769 							cmdLine, NULL);
770 					exit(result);
771 				}
772 				break;
773 			}
774 
775 			default:
776 			{
777 				/* parent process, child pid is in childPID */
778 
779 				while (wait(&stat) != childPID);
780 
781 				if ((stat & 0xff00) == 0)
782 				{
783 					result = 0;
784 				}
785 				break;
786 			}
787 		}
788 	}
789 
790 	return (result);
791 } /* unzipPPDFile() */
792 #endif
793