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