xref: /illumos-gate/usr/src/cmd/lp/cmd/lpsched/lpfsck.c (revision 60a3f738d56f92ae8b80e4b62a2331c6e1f2311f)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include "stdarg.h"
33 #include "stdlib.h"
34 #include "fcntl.h"
35 #include <sys/param.h>
36 #include "lpsched.h"
37 
38 
39 static void check_link();
40 
41 /**
42  ** lpfsck()
43  **/
44 
45 #define	F	0
46 #define D	1
47 #define P	2
48 #define S	3
49 
50 static void		proto (int, int, ...);
51 static int		va_makepath(va_list *, char **);
52 static void		_rename (char *, char *, ...);
53 
54 void
55 lpfsck(void)
56 {
57 	struct stat		stbuf;
58 	int			real_am_in_background = am_in_background;
59 
60 
61 	/*
62 	 * Force log messages to go into the log file instead of stdout.
63 	 */
64 	am_in_background = 1;
65 
66 	/*
67 	 * Most of these lines repeat the prototype file from the
68 	 * packaging and should match those items exactly.
69 	 * (In fact, they probably ought to be generated from that file,
70 	 * but that work is for a rainy day...)
71 	 */
72 
73 	/*
74 	 * DIRECTORIES:
75 	 */
76 proto (D, 0,  Lp_A, NULL,			    0775, Lp_Uid, Lp_Gid);
77 proto (D, 1,  Lp_A_Classes, NULL,		    0775, Lp_Uid, Lp_Gid);
78 proto (D, 1,  Lp_A_Forms, NULL,			    0775, Lp_Uid, Lp_Gid);
79 proto (D, 1,  Lp_A_Interfaces, NULL,		    0775, Lp_Uid, Lp_Gid);
80 proto (D, 1,  Lp_A_Printers, NULL,		    0775, Lp_Uid, Lp_Gid);
81 proto (D, 1,  Lp_A_PrintWheels, NULL,		    0775, Lp_Uid, Lp_Gid);
82 proto (D, 0,  "/var/lp", NULL,			    0775, Lp_Uid, Lp_Gid);
83 proto (D, 1,  Lp_Logs, NULL,			    0775, Lp_Uid, Lp_Gid);
84 proto (D, 1,  Lp_Spooldir, NULL,		    0775, Lp_Uid, Lp_Gid);
85 proto (D, 1,  Lp_Admins, NULL,			    0775, Lp_Uid, Lp_Gid);
86 proto (D, 1,  Lp_Requests, NULL,		    0775, Lp_Uid, Lp_Gid);
87 proto (D, 1,  Lp_Requests, Local_System, NULL,	    0770, Lp_Uid, Lp_Gid);
88 proto (D, 1,  Lp_System, NULL,			    0775, Lp_Uid, Lp_Gid);
89 proto (D, 1,  Lp_Tmp, NULL,			    0771, Lp_Uid, Lp_Gid);
90 proto (D, 1,  Lp_Tmp, Local_System, NULL,	    0775, Lp_Uid, Lp_Gid);
91 
92 	/*
93 	 * DIRECTORIES: not described in the packaging
94 	 */
95 proto (D, 0,  Lp_Spooldir, FIFOSDIR, NULL,	    0775, Lp_Uid, Lp_Gid);
96 proto (D, 1,  Lp_Private_FIFOs, NULL,		    0771, Lp_Uid, Lp_Gid);
97 proto (D, 1,  Lp_Public_FIFOs, NULL,		    0773, Lp_Uid, Lp_Gid);
98 
99 	/*
100 	 * THE MAIN FIFO:
101 	 */
102 proto (P, 1,  Lp_FIFO, NULL,			    0666, Lp_Uid, Lp_Gid);
103 
104 	/*
105 	 * SYMBOLIC LINKS:
106 	 * Watch out! These names are given in the reverse
107 	 * order found in the prototype file (sorry!)
108 	 */
109 proto (S, 1,  Lp_Model, NULL,			"/etc/lp/model", NULL);
110 proto (S, 1,  Lp_Logs, NULL,			"/etc/lp/logs", NULL);
111 /*     S, 1,  Lp_Tmp, Local_System, ...    DONE BELOW */
112 proto (S, 1,  Lp_Bin, NULL,			Lp_Spooldir, "bin", NULL);
113 proto (S, 1,  Lp_A, NULL,			Lp_Admins, "lp", NULL);
114 
115 	/*
116 	 * OTHER FILES:
117 	 */
118 
119 	/*
120 	 * SPECIAL CASE:
121 	 * If the "temp" symbolic link already exists,
122 	 * but is not correct, assume the machine's nodename changed.
123 	 * Rename directories that include the nodename, if possible,
124 	 * so that unprinted requests are saved. Then change the
125 	 * symbolic link.
126 	 * Watch out for a ``symbolic link'' that isn't!
127 	 */
128 	if (Lstat(Lp_Temp, &stbuf) == 0)
129 	    switch (stbuf.st_mode & S_IFMT) {
130 
131 	    default:
132 		Unlink (Lp_Temp);
133 		break;
134 
135 	    case S_IFDIR:
136 		Rmdir (Lp_Temp);
137 		break;
138 
139 	    case S_IFLNK:
140 		check_link();
141 		break;
142 	    }
143 
144 	proto(S, 1, Lp_Tmp, Local_System, NULL,	Lp_Temp, NULL);
145 
146 	am_in_background = real_am_in_background;
147 	return;
148 }
149 
150 static void
151 check_link()
152 {
153 	int len;
154 	char symbolic[MAXPATHLEN + 1];
155 	char *real_dir;
156 	char *old_system;
157 
158 	if ((len = Readlink(Lp_Temp, symbolic, MAXPATHLEN)) <= 0) {
159 		Unlink(Lp_Temp);
160 		return;
161 	}
162 
163 	/*
164 	 * If the symbolic link contained trailing slashes, remove
165 	 * them.
166 	 */
167 	while ((len > 1) && (symbolic[len - 1] == '/')) {
168 		len--;
169 	}
170 	symbolic[len] = 0;
171 
172 	/* check that symlink points into /var/spool/lp/tmp */
173 	if (strncmp(Lp_Tmp, symbolic, strlen(Lp_Tmp)) != 0) {
174 		Unlink(Lp_Temp);
175 		return;
176 	}
177 
178 	/*
179 	 * Check that symlink points to something.
180 	 * There should be at least 2 characters
181 	 * after the string '/var/spool/lp/tmp':
182 	 * a '/' and another character.
183 	 */
184 	if (len <= strlen(Lp_Tmp) + 1) {
185 		Unlink(Lp_Temp);
186 		return;
187 	}
188 
189 	real_dir = makepath(Lp_Tmp, Local_System, NULL);
190 	if (!STREQU(real_dir, symbolic)) {
191 		if (!(old_system = strrchr(symbolic, '/')))
192 			old_system = symbolic;
193 		else
194 			old_system++;
195 
196 		/*
197 		 * The "rename()" system call (buried
198 		 * inside the "_rename()" routine) should
199 		 * succeed, even though we blindly created
200 		 * the new directory earlier, as the only
201 		 * directory entries should be . and ..
202 		 * (although if someone already created
203 		 * them, we'll note the fact).
204 		 */
205 		_rename(old_system, Local_System, Lp_Tmp, NULL);
206 		_rename(old_system, Local_System, Lp_Requests, NULL);
207 
208 		Unlink(Lp_Temp);
209 	}
210 	Free(real_dir);
211 }
212 
213 
214 /**
215  ** proto()
216  **/
217 
218 static void
219 proto(int type, int rm_ok, ...)
220 {
221 	va_list			ap;
222 
223 	char			*path,
224 				*symbolic;
225 
226 	int			exist,
227 				err;
228 
229 	mode_t			mode;
230 
231 	uid_t			uid;
232 
233 	gid_t			gid;
234 
235 	struct stat		stbuf;
236 
237 
238 	va_start(ap, rm_ok);
239 
240 	if ((err = va_makepath(&ap, &path)) < 0)
241 		fail ("\"%s\" is a truncated name!\n", path);
242 
243 	exist = (stat(path, &stbuf) == 0);
244 
245 	switch (type) {
246 
247 	case S:
248 		if (!exist)
249 			fail ("%s is missing!\n", path);
250 		if ((err = va_makepath(&ap, &symbolic)) < 0)
251 			fail ("\"%s\" is a truncated name!\n", symbolic);
252 		Symlink (path, symbolic);
253 		Free (symbolic);
254 		Free (path);
255 		return;
256 
257 	case D:
258 		if (exist && !S_ISDIR(stbuf.st_mode)) {
259 			if (!rm_ok)
260 				fail ("%s is not a directory!\n", path);
261 			else {
262 				Unlink (path);
263 				exist = 0;
264 			}
265 		}
266 		if (!exist)
267 			Mkdir (path, 0);
268 		break;
269 
270 	case F:
271 		if (exist && !S_ISREG(stbuf.st_mode)) {
272 			if (!rm_ok)
273 				fail ("%s is not a file!\n", path);
274 			else {
275 				Unlink (path);
276 				exist = 0;
277 			}
278 		}
279 		if (!exist)
280 			Close(Creat(path, 0));
281 		break;
282 
283 	case P:
284 		/*
285 		 * Either a pipe or a file.
286 		 */
287 		if (exist &&
288 		    !S_ISREG(stbuf.st_mode) && !S_ISFIFO(stbuf.st_mode)) {
289 			if (!rm_ok)
290 				fail ("%s is not a file or pipe!\n", path);
291 			else {
292 				Unlink (path);
293 				exist = 0;
294 			}
295 		}
296 		if (!exist)
297 			Close(Creat(path, 0));
298 		break;
299 
300 	}
301 
302 	mode = va_arg(ap, mode_t);
303 	uid = va_arg(ap, uid_t);
304 	gid = va_arg(ap, gid_t);
305 	(void) chownmod(path, uid, gid, mode);
306 
307 	Free (path);
308 	return;
309 }
310 
311 /*
312  * va_makepath()
313  *
314  * Takes a variable length list of path components and attempts to string them
315  * together into a path.  It returns a heap-allocated string via the output
316  * parameter 'ret', and returns an integer success value: < 0 indicates failure,
317  * 0 indicates success.  Note that 'ret' will never be NULL (unless the system
318  * is so overloaded that it can't allocate a single byte), and should always be
319  * free()d.
320  */
321 static int
322 va_makepath (va_list *pap, char **ret)
323 {
324 	char			*component;
325 	char 			buf[MAXPATHLEN];
326 	int			buflen;
327 
328 	memset(buf, NULL, sizeof (buf));
329 	while ((component = va_arg((*pap), char *)) != NULL) {
330 		if (strlcat(buf, component, sizeof (buf)) >= sizeof (buf) ||
331 			strlcat(buf, "/", sizeof (buf)) >= sizeof (buf)) {
332 			if ((*ret = strdup(buf)) == NULL)
333 				*ret = strdup("");
334 			return (-1);
335 		}
336 	}
337 
338 	/* remove the trailing slash */
339 	buflen = strlen(buf);
340 	if ((buflen > 1) && (buf[buflen - 1] == '/')) {
341 		buf[buflen - 1] = '\0';
342 	}
343 
344 	if ((*ret = strdup(buf)) == NULL) {
345 		*ret = strdup("");
346 		return (-1);
347 	}
348 	return (0);
349 }
350 
351 /**
352  ** _rename()
353  **/
354 
355 static void
356 _rename(char *old_system, char *new_system, ...)
357 {
358 	va_list			ap;
359 
360 	char *			prefix;
361 	char *			old;
362 	char *			new;
363 	int			err;
364 
365 
366 	va_start (ap, new_system);
367 	if ((err = va_makepath(&ap, &prefix)) < 0)
368 		fail (
369 			"Rename failed; prefix \"%s\" is a truncated name.\n",
370 			prefix
371 		);
372 	va_end (ap);
373 
374 	old = makepath(prefix, old_system, (char *)0);
375 	new = makepath(prefix, new_system, (char *)0);
376 
377 	if (Rename(old, new) == 0)
378 		note ("Renamed %s to %s.\n", old, new);
379 	else if (errno == EEXIST)
380 		note (
381 			"Rename of %s to %s failed because %s exists.\n",
382 			old,
383 			new,
384 			new
385 		);
386 	else
387 		fail (
388 			"Rename of %s to %s failed (%s).\n",
389 			old,
390 			new,
391 			PERROR
392 		);
393 
394 	Free (new);
395 	Free (old);
396 	Free (prefix);
397 
398 	return;
399 }
400