1*2264Sjacobs /*
2*2264Sjacobs  * CDDL HEADER START
3*2264Sjacobs  *
4*2264Sjacobs  * The contents of this file are subject to the terms of the
5*2264Sjacobs  * Common Development and Distribution License (the "License").
6*2264Sjacobs  * You may not use this file except in compliance with the License.
7*2264Sjacobs  *
8*2264Sjacobs  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*2264Sjacobs  * or http://www.opensolaris.org/os/licensing.
10*2264Sjacobs  * See the License for the specific language governing permissions
11*2264Sjacobs  * and limitations under the License.
12*2264Sjacobs  *
13*2264Sjacobs  * When distributing Covered Code, include this CDDL HEADER in each
14*2264Sjacobs  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*2264Sjacobs  * If applicable, add the following below this CDDL HEADER, with the
16*2264Sjacobs  * fields enclosed by brackets "[]" replaced with your own identifying
17*2264Sjacobs  * information: Portions Copyright [yyyy] [name of copyright owner]
18*2264Sjacobs  *
19*2264Sjacobs  * CDDL HEADER END
20*2264Sjacobs  */
21*2264Sjacobs /*
22*2264Sjacobs  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*2264Sjacobs  * Use is subject to license terms.
24*2264Sjacobs  */
25*2264Sjacobs 
26*2264Sjacobs #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*2264Sjacobs 
28*2264Sjacobs #include <stdio.h>
29*2264Sjacobs #include <stdlib.h>
30*2264Sjacobs #include <unistd.h>
31*2264Sjacobs #include <sys/types.h>
32*2264Sjacobs #include <sys/stat.h>
33*2264Sjacobs #include <string.h>
34*2264Sjacobs #include <stdarg.h>
35*2264Sjacobs #include <fcntl.h>
36*2264Sjacobs #include <syslog.h>
37*2264Sjacobs #include <errno.h>
38*2264Sjacobs #include <pwd.h>
39*2264Sjacobs #include <libintl.h>
40*2264Sjacobs #include <netdb.h>	/* for rcmd() */
41*2264Sjacobs 
42*2264Sjacobs #include <ns.h>
43*2264Sjacobs #include <list.h>
44*2264Sjacobs #include <misc.h>
45*2264Sjacobs 
46*2264Sjacobs /*  escaped chars include delimiters and shell meta characters */
47*2264Sjacobs #define	ESCAPE_CHARS	"\\\n=: `&;|>^$()<*?["
48*2264Sjacobs 
49*2264Sjacobs /*
50*2264Sjacobs  * This modules contains all of the code nedessary to write back to each
51*2264Sjacobs  * printing configuration data repository.  The support is intended to
52*2264Sjacobs  * introduce the least number of dependencies in the library, so it doesn't
53*2264Sjacobs  * always perform it's operations in the cleanest fashion.
54*2264Sjacobs  */
55*2264Sjacobs 
56*2264Sjacobs 
57*2264Sjacobs /*
58*2264Sjacobs  * Generic Files support begins here.
59*2264Sjacobs  */
60*2264Sjacobs static char *
61*2264Sjacobs freadline(FILE *fp, char *buf, int buflen)
62*2264Sjacobs {
63*2264Sjacobs 	char *s = buf;
64*2264Sjacobs 
65*2264Sjacobs 	while (fgets(s, buflen, fp)) {
66*2264Sjacobs 		if ((s == buf) && ((*s == '#') || (*s == '\n'))) {
67*2264Sjacobs 			continue;
68*2264Sjacobs 		} else {
69*2264Sjacobs 			if ((*s == '#') || (*s == '\n')) {
70*2264Sjacobs 				*s = NULL;
71*2264Sjacobs 				break;
72*2264Sjacobs 			}
73*2264Sjacobs 
74*2264Sjacobs 			buflen -= strlen(s);
75*2264Sjacobs 			s += strlen(s);
76*2264Sjacobs 
77*2264Sjacobs 			if (*(s - 2) != '\\')
78*2264Sjacobs 				break;
79*2264Sjacobs #ifdef STRIP_CONTINUATION
80*2264Sjacobs 			buflen -= 2;
81*2264Sjacobs 			s -= 2;
82*2264Sjacobs #endif
83*2264Sjacobs 		}
84*2264Sjacobs 	}
85*2264Sjacobs 
86*2264Sjacobs 	if (s == buf)
87*2264Sjacobs 		return (NULL);
88*2264Sjacobs 	else
89*2264Sjacobs 		return (buf);
90*2264Sjacobs }
91*2264Sjacobs 
92*2264Sjacobs 
93*2264Sjacobs static int
94*2264Sjacobs _file_put_printer(const char *file, const ns_printer_t *printer)
95*2264Sjacobs {
96*2264Sjacobs 	FILE	*ifp,
97*2264Sjacobs 		*ofp;
98*2264Sjacobs 	char *tmpfile;
99*2264Sjacobs 	int fd;
100*2264Sjacobs 	int exit_status = 0;
101*2264Sjacobs 	int size;
102*2264Sjacobs 
103*2264Sjacobs 	size = strlen(file) + 1 + 20;
104*2264Sjacobs 	if ((tmpfile = malloc(size)) == NULL)
105*2264Sjacobs 		return (-1);
106*2264Sjacobs 
107*2264Sjacobs 	if (snprintf(tmpfile, size, "%sXXXXXX", file) >= size) {
108*2264Sjacobs 		syslog(LOG_ERR, "_file_put_printer:buffer overflow:tmpfile");
109*2264Sjacobs 		return (-1);
110*2264Sjacobs 	}
111*2264Sjacobs 
112*2264Sjacobs 	/* LINTED */
113*2264Sjacobs 	while (1) {	/* syncronize writes */
114*2264Sjacobs 		fd = open(file, O_RDWR|O_CREAT|O_EXCL, 0644);
115*2264Sjacobs 		if ((fd < 0) && (errno == EEXIST))
116*2264Sjacobs 			fd = open(file, O_RDWR);
117*2264Sjacobs 		if (fd < 0) {
118*2264Sjacobs 			if (errno == EAGAIN)
119*2264Sjacobs 				continue;
120*2264Sjacobs 			free(tmpfile);
121*2264Sjacobs 			return (-1);
122*2264Sjacobs 		}
123*2264Sjacobs 		if (lockf(fd, F_TLOCK, 0) == 0)
124*2264Sjacobs 			break;
125*2264Sjacobs 		(void) close(fd);
126*2264Sjacobs 	}
127*2264Sjacobs 
128*2264Sjacobs 	if ((ifp = fdopen(fd, "r")) == NULL) {
129*2264Sjacobs 		(void) close(fd);
130*2264Sjacobs 		free(tmpfile);
131*2264Sjacobs 		return (-1);
132*2264Sjacobs 	}
133*2264Sjacobs 
134*2264Sjacobs 	if ((fd = mkstemp(tmpfile)) < 0) {
135*2264Sjacobs 		(void) fclose(ifp);
136*2264Sjacobs 		free(tmpfile);
137*2264Sjacobs 		return (-1);
138*2264Sjacobs 	}
139*2264Sjacobs 
140*2264Sjacobs 	(void) fchmod(fd, 0644);
141*2264Sjacobs 	if ((ofp = fdopen(fd, "wb+")) != NULL) {
142*2264Sjacobs 		char buf[4096];
143*2264Sjacobs 
144*2264Sjacobs 		(void) fprintf(ofp,
145*2264Sjacobs 	"#\n#\tIf you hand edit this file, comments and structure may change.\n"
146*2264Sjacobs 	"#\tThe preferred method of modifying this file is through the use of\n"
147*2264Sjacobs 	"#\tlpset(1M)\n#\n");
148*2264Sjacobs 
149*2264Sjacobs 	/*
150*2264Sjacobs 	 * Handle the special case of lpset -x all
151*2264Sjacobs 	 * This deletes all entries in the file
152*2264Sjacobs 	 * In this case, just don't write any entries to the tmpfile
153*2264Sjacobs 	 */
154*2264Sjacobs 
155*2264Sjacobs 		if (!((strcmp(printer->name, "all") == 0) &&
156*2264Sjacobs 				(printer->attributes == NULL))) {
157*2264Sjacobs 			char *t, *entry, *pentry;
158*2264Sjacobs 
159*2264Sjacobs 			(void) _cvt_printer_to_entry((ns_printer_t *)printer,
160*2264Sjacobs 							buf, sizeof (buf));
161*2264Sjacobs 			t = pentry = strdup(buf);
162*2264Sjacobs 
163*2264Sjacobs 			while (freadline(ifp, buf, sizeof (buf)) != NULL) {
164*2264Sjacobs 				ns_printer_t *tmp = (ns_printer_t *)
165*2264Sjacobs 					_cvt_nss_entry_to_printer(buf, "");
166*2264Sjacobs 
167*2264Sjacobs 				if (ns_printer_match_name(tmp, printer->name)
168*2264Sjacobs 						== 0) {
169*2264Sjacobs 					entry = pentry;
170*2264Sjacobs 					pentry = NULL;
171*2264Sjacobs 				} else
172*2264Sjacobs 					entry = buf;
173*2264Sjacobs 
174*2264Sjacobs 				(void) fprintf(ofp, "%s\n", entry);
175*2264Sjacobs 			}
176*2264Sjacobs 
177*2264Sjacobs 			if (pentry != NULL)
178*2264Sjacobs 				(void) fprintf(ofp, "%s\n", pentry);
179*2264Sjacobs 			free(t);
180*2264Sjacobs 		}
181*2264Sjacobs 
182*2264Sjacobs 		(void) fclose(ofp);
183*2264Sjacobs 		(void) rename(tmpfile, file);
184*2264Sjacobs 	} else {
185*2264Sjacobs 		(void) close(fd);
186*2264Sjacobs 		(void) unlink(tmpfile);
187*2264Sjacobs 		exit_status = -1;
188*2264Sjacobs 	}
189*2264Sjacobs 
190*2264Sjacobs 	(void) fclose(ifp);	/* releases the lock, after rename on purpose */
191*2264Sjacobs 	(void) free(tmpfile);
192*2264Sjacobs 	return (exit_status);
193*2264Sjacobs }
194*2264Sjacobs 
195*2264Sjacobs 
196*2264Sjacobs /*
197*2264Sjacobs  * Support for writing a printer into the FILES /etc/printers.conf
198*2264Sjacobs  * file.
199*2264Sjacobs  */
200*2264Sjacobs int
201*2264Sjacobs files_put_printer(const ns_printer_t *printer)
202*2264Sjacobs {
203*2264Sjacobs 	static char *file = "/etc/printers.conf";
204*2264Sjacobs 
205*2264Sjacobs 	return (_file_put_printer(file, printer));
206*2264Sjacobs }
207*2264Sjacobs 
208*2264Sjacobs 
209*2264Sjacobs /*
210*2264Sjacobs  * Support for writing a printer into the NIS printers.conf.byname
211*2264Sjacobs  * map.
212*2264Sjacobs  */
213*2264Sjacobs 
214*2264Sjacobs #include <rpc/rpc.h>
215*2264Sjacobs #include <rpcsvc/ypclnt.h>
216*2264Sjacobs #include <rpcsvc/yp_prot.h>
217*2264Sjacobs 
218*2264Sjacobs /*
219*2264Sjacobs  * Run the remote command.  We aren't interested in any io, Only the
220*2264Sjacobs  * return code.
221*2264Sjacobs  */
222*2264Sjacobs static int
223*2264Sjacobs remote_command(char *command, char *host)
224*2264Sjacobs {
225*2264Sjacobs 	struct passwd *pw;
226*2264Sjacobs 
227*2264Sjacobs 	if ((pw = getpwuid(getuid())) != NULL) {
228*2264Sjacobs 		int fd;
229*2264Sjacobs 
230*2264Sjacobs 		if ((fd = rcmd_af(&host, htons(514), pw->pw_name, "root",
231*2264Sjacobs 				command, NULL, AF_INET6)) < 0)
232*2264Sjacobs 			return (-1);
233*2264Sjacobs 		(void) close(fd);
234*2264Sjacobs 		return (0);
235*2264Sjacobs 	} else
236*2264Sjacobs 		return (-1);
237*2264Sjacobs }
238*2264Sjacobs 
239*2264Sjacobs 
240*2264Sjacobs /*
241*2264Sjacobs  * This isn't all that pretty, but you can update NIS if the machine this
242*2264Sjacobs  * runs on is in the /.rhosts or /etc/hosts.equiv on the NIS master.
243*2264Sjacobs  *   copy it local, update it, copy it remote
244*2264Sjacobs  */
245*2264Sjacobs #define	TMP_PRINTERS_FILE	"/tmp/printers.NIS"
246*2264Sjacobs #define	NIS_MAKEFILE		"/var/yp/Makefile"
247*2264Sjacobs #define	MAKE_EXCERPT		"/usr/lib/print/Makefile.yp"
248*2264Sjacobs /*ARGSUSED*/
249*2264Sjacobs int
250*2264Sjacobs nis_put_printer(const ns_printer_t *printer)
251*2264Sjacobs {
252*2264Sjacobs 	static char	*domain = NULL;
253*2264Sjacobs 	char *map = "printers.conf.byname";
254*2264Sjacobs 	char *tmp = NULL;
255*2264Sjacobs 	char *host = NULL;
256*2264Sjacobs 	char lfile[BUFSIZ];
257*2264Sjacobs 	char rfile[BUFSIZ];
258*2264Sjacobs 	char cmd[BUFSIZ];
259*2264Sjacobs 
260*2264Sjacobs 	if (domain == NULL)
261*2264Sjacobs 		(void) yp_get_default_domain(&domain);
262*2264Sjacobs 
263*2264Sjacobs 	if ((yp_master(domain, (char *)map, &host) != 0) &&
264*2264Sjacobs 	    (yp_master(domain, "passwd.byname", &host) != 0))
265*2264Sjacobs 		return (-1);
266*2264Sjacobs 
267*2264Sjacobs 	if (snprintf(lfile, sizeof (lfile), "/tmp/%s", map) >=
268*2264Sjacobs 			sizeof (lfile)) {
269*2264Sjacobs 		syslog(LOG_ERR, "nis_put_printer:lfile buffer overflow");
270*2264Sjacobs 		return (-1);
271*2264Sjacobs 	}
272*2264Sjacobs 	if (snprintf(rfile, sizeof (rfile), "root@%s:/etc/%s", host, map) >=
273*2264Sjacobs 			sizeof (rfile)) {
274*2264Sjacobs 		syslog(LOG_ERR, "nis_put_printer:rfile buffer overflow");
275*2264Sjacobs 		return (-1);
276*2264Sjacobs 	}
277*2264Sjacobs 
278*2264Sjacobs 	if (((tmp = strrchr(rfile, '.')) != NULL) &&
279*2264Sjacobs 	    (strcmp(tmp, ".byname") == 0))
280*2264Sjacobs 		*tmp = NULL;	/* strip the .byname */
281*2264Sjacobs 
282*2264Sjacobs 	/* copy it local */
283*2264Sjacobs 	if (snprintf(cmd, sizeof (cmd), "rcp %s %s >/dev/null 2>&1",
284*2264Sjacobs 		rfile, lfile) >= sizeof (cmd)) {
285*2264Sjacobs 		    syslog(LOG_ERR,
286*2264Sjacobs 			    "nis_put_printer:buffer overflow building cmd");
287*2264Sjacobs 		    return (-1);
288*2264Sjacobs 	}
289*2264Sjacobs 	(void) system(cmd);	/* could fail because it doesn't exist */
290*2264Sjacobs 
291*2264Sjacobs 
292*2264Sjacobs 	/* update it */
293*2264Sjacobs 	if (_file_put_printer(lfile, printer) != 0)
294*2264Sjacobs 		return (-1);
295*2264Sjacobs 
296*2264Sjacobs 	/* copy it back */
297*2264Sjacobs 	if (snprintf(cmd, sizeof (cmd), "rcp %s %s >/dev/null 2>&1",
298*2264Sjacobs 		lfile, rfile) >= sizeof (cmd)) {
299*2264Sjacobs 		    syslog(LOG_ERR,
300*2264Sjacobs 			    "nis_put_printer:buffer overflow building cmd");
301*2264Sjacobs 		    return (-1);
302*2264Sjacobs 	}
303*2264Sjacobs 	if (system(cmd) != 0)
304*2264Sjacobs 		return (-1);
305*2264Sjacobs 
306*2264Sjacobs 	/* copy the Makefile excerpt */
307*2264Sjacobs 	if (snprintf(cmd, sizeof (cmd),
308*2264Sjacobs 			"rcp %s root@%s:%s.print >/dev/null 2>&1",
309*2264Sjacobs 			MAKE_EXCERPT, host, NIS_MAKEFILE) >= sizeof (cmd)) {
310*2264Sjacobs 		syslog(LOG_ERR,
311*2264Sjacobs 			"nis_put_printer:buffer overflow building cmd");
312*2264Sjacobs 		return (-1);
313*2264Sjacobs 	}
314*2264Sjacobs 
315*2264Sjacobs 	if (system(cmd) != 0)
316*2264Sjacobs 		return (-1);
317*2264Sjacobs 
318*2264Sjacobs 	/* run the make */
319*2264Sjacobs 	if (snprintf(cmd, sizeof (cmd),
320*2264Sjacobs 			"/bin/sh -c 'PATH=/usr/ccs/bin:/bin:/usr/bin:$PATH "
321*2264Sjacobs 			"make -f %s -f %s.print printers.conf >/dev/null 2>&1'",
322*2264Sjacobs 			NIS_MAKEFILE, NIS_MAKEFILE) >= sizeof (cmd)) {
323*2264Sjacobs 		syslog(LOG_ERR,
324*2264Sjacobs 			"nis_put_printer:buffer overflow on make");
325*2264Sjacobs 		return (-1);
326*2264Sjacobs 	}
327*2264Sjacobs 
328*2264Sjacobs 	return (remote_command(cmd, host));
329*2264Sjacobs }
330*2264Sjacobs 
331*2264Sjacobs /*
332*2264Sjacobs  * Support for writing a printer into the NISPLUS org_dir.printers table
333*2264Sjacobs  * begins here.  This support uses the nisplus(5) commands rather than the
334*2264Sjacobs  * nisplus API.  This was done to remove the dependency in libprint on the
335*2264Sjacobs  * API, which is used for lookup in a configuration dependent manner.
336*2264Sjacobs  */
337*2264Sjacobs #define	NISPLUS_CREATE	"/usr/bin/nistest -t T printers.org_dir || "\
338*2264Sjacobs 			"( /usr/bin/nistbladm "\
339*2264Sjacobs 			"-D access=og=rmcd,nw=r:group=admin."\
340*2264Sjacobs 				"`/usr/bin/nisdefaults -d` "\
341*2264Sjacobs 			"-c printers_tbl key=S,nogw= datum=,nogw= "\
342*2264Sjacobs 			"printers.org_dir.`/usr/bin/nisdefaults -d` )"
343*2264Sjacobs 
344*2264Sjacobs #define	NISPLUS_REMOVE	"/usr/bin/nistbladm  -R key=%s printers.org_dir"
345*2264Sjacobs #define	NISPLUS_UPDATE	"/usr/bin/nistbladm  -A key=%s datum="
346*2264Sjacobs 
347*2264Sjacobs int
348*2264Sjacobs nisplus_put_printer(const ns_printer_t *printer)
349*2264Sjacobs {
350*2264Sjacobs 	int rc = 0;
351*2264Sjacobs 	char cmd[BUFSIZ];
352*2264Sjacobs 
353*2264Sjacobs 	if (printer == NULL)
354*2264Sjacobs 		return (rc);
355*2264Sjacobs 
356*2264Sjacobs 	/* create the table if it doesn't exist */
357*2264Sjacobs 	(void) system(NISPLUS_CREATE);
358*2264Sjacobs 
359*2264Sjacobs 	if (printer->attributes != NULL) {
360*2264Sjacobs 		int		len;
361*2264Sjacobs 		ns_kvp_t	**kvp;
362*2264Sjacobs 
363*2264Sjacobs 		if (snprintf(cmd, sizeof (cmd), NISPLUS_UPDATE,
364*2264Sjacobs 				printer->name) >= sizeof (cmd)) {
365*2264Sjacobs 		    syslog(LOG_ERR,
366*2264Sjacobs 		    "nisplus_put_printer:NISPLUS_UPDATE:buffer overflow");
367*2264Sjacobs 		    return (-1);
368*2264Sjacobs 		}
369*2264Sjacobs 
370*2264Sjacobs 		len = strlen(cmd);
371*2264Sjacobs 
372*2264Sjacobs 		/* Append key/value pairs */
373*2264Sjacobs 		for (kvp = printer->attributes; *kvp != NULL; kvp++)
374*2264Sjacobs 			if (((*kvp)->key != NULL) && ((*kvp)->value != NULL)) {
375*2264Sjacobs 			(void) strlcat(cmd, ":", sizeof (cmd));
376*2264Sjacobs 			(void) strncat_escaped(cmd, (*kvp)->key, sizeof (cmd),
377*2264Sjacobs 			    ESCAPE_CHARS);
378*2264Sjacobs 			(void) strlcat(cmd, "=", sizeof (cmd));
379*2264Sjacobs 			(void) strncat_escaped(cmd, (*kvp)->value,
380*2264Sjacobs 			    sizeof (cmd), ESCAPE_CHARS);
381*2264Sjacobs 	}
382*2264Sjacobs 
383*2264Sjacobs 		if (len != strlen(cmd))
384*2264Sjacobs 			(void) strlcat(cmd, " printers.org_dir", sizeof (cmd));
385*2264Sjacobs 		else
386*2264Sjacobs 			(void) snprintf(cmd, sizeof (cmd), NISPLUS_REMOVE,
387*2264Sjacobs 						printer->name);
388*2264Sjacobs 
389*2264Sjacobs 	} else
390*2264Sjacobs 		(void) snprintf(cmd, sizeof (cmd), NISPLUS_REMOVE,
391*2264Sjacobs 		    printer->name);
392*2264Sjacobs 
393*2264Sjacobs 	if (strlcat(cmd, " >/dev/null 2>&1", sizeof (cmd)) >= sizeof (cmd)) {
394*2264Sjacobs 		syslog(LOG_ERR, "nisplus_put_printer: buffer overflow");
395*2264Sjacobs 		return (-1);
396*2264Sjacobs 	}
397*2264Sjacobs 
398*2264Sjacobs 	/* add/modify/delete the entry */
399*2264Sjacobs 	rc = system(cmd);
400*2264Sjacobs 
401*2264Sjacobs 	return (rc);
402*2264Sjacobs }
403