xref: /illumos-gate/usr/src/lib/print/libprint/common/nss_write.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <string.h>
32 #include <stdarg.h>
33 #include <fcntl.h>
34 #include <syslog.h>
35 #include <errno.h>
36 #include <pwd.h>
37 #include <libintl.h>
38 #include <netdb.h>	/* for rcmd() */
39 
40 #include <ns.h>
41 #include <list.h>
42 
43 /*  escaped chars include delimiters and shell meta characters */
44 #define	ESCAPE_CHARS	"\\\n=: `&;|>^$()<*?["
45 
46 /*
47  * This modules contains all of the code nedessary to write back to each
48  * printing configuration data repository.  The support is intended to
49  * introduce the least number of dependencies in the library, so it doesn't
50  * always perform it's operations in the cleanest fashion.
51  */
52 
53 
54 /*
55  * Generic Files support begins here.
56  */
57 static char *
58 freadline(FILE *fp, char *buf, int buflen)
59 {
60 	char *s = buf;
61 
62 	while (fgets(s, buflen, fp)) {
63 		if ((s == buf) && ((*s == '#') || (*s == '\n'))) {
64 			continue;
65 		} else {
66 			if ((*s == '#') || (*s == '\n')) {
67 				*s = '\0';
68 				break;
69 			}
70 
71 			buflen -= strlen(s);
72 			s += strlen(s);
73 
74 			if (*(s - 2) != '\\')
75 				break;
76 #ifdef STRIP_CONTINUATION
77 			buflen -= 2;
78 			s -= 2;
79 #endif
80 		}
81 	}
82 
83 	if (s == buf)
84 		return (NULL);
85 	else
86 		return (buf);
87 }
88 
89 
90 static int
91 _file_put_printer(const char *file, const ns_printer_t *printer)
92 {
93 	FILE	*ifp,
94 	    *ofp;
95 	char *tmpfile;
96 	int fd;
97 	int exit_status = 0;
98 	int size;
99 
100 	size = strlen(file) + 1 + 20;
101 	if ((tmpfile = malloc(size)) == NULL)
102 		return (-1);
103 
104 	if (snprintf(tmpfile, size, "%sXXXXXX", file) >= size) {
105 		syslog(LOG_ERR, "_file_put_printer:buffer overflow:tmpfile");
106 		return (-1);
107 	}
108 
109 	/* LINTED */
110 	while (1) {	/* syncronize writes */
111 		fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644);
112 		if ((fd < 0) && (errno == EEXIST))
113 			fd = open(file, O_RDWR);
114 		if (fd < 0) {
115 			if (errno == EAGAIN)
116 				continue;
117 			free(tmpfile);
118 			return (-1);
119 		}
120 		if (lockf(fd, F_TLOCK, 0) == 0)
121 			break;
122 		(void) close(fd);
123 	}
124 
125 	if ((ifp = fdopen(fd, "r")) == NULL) {
126 		(void) close(fd);
127 		free(tmpfile);
128 		return (-1);
129 	}
130 
131 	if ((fd = mkstemp(tmpfile)) < 0) {
132 		(void) fclose(ifp);
133 		free(tmpfile);
134 		return (-1);
135 	}
136 
137 	(void) fchmod(fd, 0644);
138 	if ((ofp = fdopen(fd, "wb+")) != NULL) {
139 		char buf[4096];
140 
141 		(void) fprintf(ofp,
142 	"#\n#\tIf you hand edit this file, comments and structure may change.\n"
143 	"#\tThe preferred method of modifying this file is through the use of\n"
144 	"#\tlpset(1M)\n#\n");
145 
146 	/*
147 	 * Handle the special case of lpset -x all
148 	 * This deletes all entries in the file
149 	 * In this case, just don't write any entries to the tmpfile
150 	 */
151 
152 		if (!((strcmp(printer->name, "all") == 0) &&
153 		    (printer->attributes == NULL))) {
154 			char *t, *entry, *pentry;
155 
156 			(void) _cvt_printer_to_entry((ns_printer_t *)printer,
157 			    buf, sizeof (buf));
158 			t = pentry = strdup(buf);
159 
160 			while (freadline(ifp, buf, sizeof (buf)) != NULL) {
161 				ns_printer_t *tmp = (ns_printer_t *)
162 				    _cvt_nss_entry_to_printer(buf, "");
163 
164 				if (ns_printer_match_name(tmp, printer->name)
165 				    == 0) {
166 					entry = pentry;
167 					pentry = NULL;
168 				} else {
169 					entry = buf;
170 				}
171 
172 				(void) fprintf(ofp, "%s\n", entry);
173 			}
174 
175 			if (pentry != NULL)
176 				(void) fprintf(ofp, "%s\n", pentry);
177 			free(t);
178 		}
179 
180 		(void) fclose(ofp);
181 		(void) rename(tmpfile, file);
182 	} else {
183 		(void) close(fd);
184 		(void) unlink(tmpfile);
185 		exit_status = -1;
186 	}
187 
188 	(void) fclose(ifp);	/* releases the lock, after rename on purpose */
189 	(void) free(tmpfile);
190 	return (exit_status);
191 }
192 
193 
194 /*
195  * Support for writing a printer into the FILES /etc/printers.conf
196  * file.
197  */
198 int
199 files_put_printer(const ns_printer_t *printer)
200 {
201 	static char *file = "/etc/printers.conf";
202 
203 	return (_file_put_printer(file, printer));
204 }
205 
206 /*
207  * Support for writing a printer into the NIS printers.conf.byname
208  * map.
209  */
210 
211 #include <rpc/rpc.h>
212 #include <rpcsvc/ypclnt.h>
213 #include <rpcsvc/yp_prot.h>
214 
215 /*
216  * Run the remote command.  We aren't interested in any io, Only the
217  * return code.
218  */
219 static int
220 remote_command(char *command, char *host)
221 {
222 	struct passwd *pw;
223 
224 	if ((pw = getpwuid(getuid())) != NULL) {
225 		int fd;
226 
227 		if ((fd = rcmd_af(&host, htons(514), pw->pw_name, "root",
228 		    command, NULL, AF_INET6)) < 0)
229 			return (-1);
230 		(void) close(fd);
231 		return (0);
232 	} else {
233 		return (-1);
234 	}
235 }
236 
237 
238 /*
239  * This isn't all that pretty, but you can update NIS if the machine this
240  * runs on is in the /.rhosts or /etc/hosts.equiv on the NIS master.
241  *   copy it local, update it, copy it remote
242  */
243 #define	TMP_PRINTERS_FILE	"/tmp/printers.NIS"
244 #define	NIS_MAKEFILE		"/var/yp/Makefile"
245 #define	MAKE_EXCERPT		"/usr/lib/print/Makefile.yp"
246 /*ARGSUSED*/
247 int
248 nis_put_printer(const ns_printer_t *printer)
249 {
250 	static char	*domain = NULL;
251 	char *map = "printers.conf.byname";
252 	char *tmp = NULL;
253 	char *host = NULL;
254 	char lfile[BUFSIZ];
255 	char rfile[BUFSIZ];
256 	char cmd[BUFSIZ];
257 
258 	if (domain == NULL)
259 		(void) yp_get_default_domain(&domain);
260 
261 	if ((yp_master(domain, (char *)map, &host) != 0) &&
262 	    (yp_master(domain, "passwd.byname", &host) != 0))
263 		return (-1);
264 
265 	if (snprintf(lfile, sizeof (lfile), "/tmp/%s", map) >=
266 	    sizeof (lfile)) {
267 		syslog(LOG_ERR, "nis_put_printer:lfile buffer overflow");
268 		return (-1);
269 	}
270 	if (snprintf(rfile, sizeof (rfile), "root@%s:/etc/%s", host, map) >=
271 	    sizeof (rfile)) {
272 		syslog(LOG_ERR, "nis_put_printer:rfile buffer overflow");
273 		return (-1);
274 	}
275 
276 	if (((tmp = strrchr(rfile, '.')) != NULL) &&
277 	    (strcmp(tmp, ".byname") == 0))
278 		*tmp = '\0';	/* strip the .byname */
279 
280 	/* copy it local */
281 	if (snprintf(cmd, sizeof (cmd), "rcp %s %s >/dev/null 2>&1",
282 	    rfile, lfile) >= sizeof (cmd)) {
283 		syslog(LOG_ERR,
284 		    "nis_put_printer:buffer overflow building cmd");
285 		return (-1);
286 	}
287 	(void) system(cmd);	/* could fail because it doesn't exist */
288 
289 
290 	/* update it */
291 	if (_file_put_printer(lfile, printer) != 0)
292 		return (-1);
293 
294 	/* copy it back */
295 	if (snprintf(cmd, sizeof (cmd), "rcp %s %s >/dev/null 2>&1",
296 	    lfile, rfile) >= sizeof (cmd)) {
297 		syslog(LOG_ERR,
298 		    "nis_put_printer:buffer overflow building cmd");
299 		return (-1);
300 	}
301 	if (system(cmd) != 0)
302 		return (-1);
303 
304 	/* copy the Makefile excerpt */
305 	if (snprintf(cmd, sizeof (cmd),
306 	    "rcp %s root@%s:%s.print >/dev/null 2>&1",
307 	    MAKE_EXCERPT, host, NIS_MAKEFILE) >= sizeof (cmd)) {
308 		syslog(LOG_ERR,
309 		    "nis_put_printer:buffer overflow building cmd");
310 		return (-1);
311 	}
312 
313 	if (system(cmd) != 0)
314 		return (-1);
315 
316 	/* run the make */
317 	if (snprintf(cmd, sizeof (cmd),
318 	    "/bin/sh -c 'PATH=/usr/ccs/bin:/bin:/usr/bin:$PATH "
319 	    "make -f %s -f %s.print printers.conf >/dev/null 2>&1'",
320 	    NIS_MAKEFILE, NIS_MAKEFILE) >= sizeof (cmd)) {
321 		syslog(LOG_ERR,
322 		    "nis_put_printer:buffer overflow on make");
323 		return (-1);
324 	}
325 
326 	return (remote_command(cmd, host));
327 }
328