xref: /illumos-gate/usr/src/cmd/lp/cmd/lpsched/lpfsck.c (revision 0a44ef6d)
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 
97 	/*
98 	 * THE MAIN FIFO:
99 	 */
100 proto (P, 1,  Lp_FIFO, NULL,			    0666, Lp_Uid, Lp_Gid);
101 
102 	/*
103 	 * SYMBOLIC LINKS:
104 	 * Watch out! These names are given in the reverse
105 	 * order found in the prototype file (sorry!)
106 	 */
107 proto (S, 1,  Lp_Model, NULL,			"/etc/lp/model", NULL);
108 proto (S, 1,  Lp_Logs, NULL,			"/etc/lp/logs", NULL);
109 /*     S, 1,  Lp_Tmp, Local_System, ...    DONE BELOW */
110 proto (S, 1,  Lp_Bin, NULL,			Lp_Spooldir, "bin", NULL);
111 proto (S, 1,  Lp_A, NULL,			Lp_Admins, "lp", NULL);
112 
113 	/*
114 	 * OTHER FILES:
115 	 */
116 
117 	/*
118 	 * SPECIAL CASE:
119 	 * If the "temp" symbolic link already exists,
120 	 * but is not correct, assume the machine's nodename changed.
121 	 * Rename directories that include the nodename, if possible,
122 	 * so that unprinted requests are saved. Then change the
123 	 * symbolic link.
124 	 * Watch out for a ``symbolic link'' that isn't!
125 	 */
126 	if (Lstat(Lp_Temp, &stbuf) == 0)
127 	    switch (stbuf.st_mode & S_IFMT) {
128 
129 	    default:
130 		Unlink (Lp_Temp);
131 		break;
132 
133 	    case S_IFDIR:
134 		Rmdir (Lp_Temp);
135 		break;
136 
137 	    case S_IFLNK:
138 		check_link();
139 		break;
140 	    }
141 
142 	proto(S, 1, Lp_Tmp, Local_System, NULL,	Lp_Temp, NULL);
143 
144 	am_in_background = real_am_in_background;
145 	return;
146 }
147 
148 static void
149 check_link()
150 {
151 	int len;
152 	char symbolic[MAXPATHLEN + 1];
153 	char *real_dir;
154 	char *old_system;
155 
156 	if ((len = Readlink(Lp_Temp, symbolic, MAXPATHLEN)) <= 0) {
157 		Unlink(Lp_Temp);
158 		return;
159 	}
160 
161 	/*
162 	 * If the symbolic link contained trailing slashes, remove
163 	 * them.
164 	 */
165 	while ((len > 1) && (symbolic[len - 1] == '/')) {
166 		len--;
167 	}
168 	symbolic[len] = 0;
169 
170 	/* check that symlink points into /var/spool/lp/tmp */
171 	if (strncmp(Lp_Tmp, symbolic, strlen(Lp_Tmp)) != 0) {
172 		Unlink(Lp_Temp);
173 		return;
174 	}
175 
176 	/*
177 	 * Check that symlink points to something.
178 	 * There should be at least 2 characters
179 	 * after the string '/var/spool/lp/tmp':
180 	 * a '/' and another character.
181 	 */
182 	if (len <= strlen(Lp_Tmp) + 1) {
183 		Unlink(Lp_Temp);
184 		return;
185 	}
186 
187 	real_dir = makepath(Lp_Tmp, Local_System, NULL);
188 	if (!STREQU(real_dir, symbolic)) {
189 		if (!(old_system = strrchr(symbolic, '/')))
190 			old_system = symbolic;
191 		else
192 			old_system++;
193 
194 		/*
195 		 * The "rename()" system call (buried
196 		 * inside the "_rename()" routine) should
197 		 * succeed, even though we blindly created
198 		 * the new directory earlier, as the only
199 		 * directory entries should be . and ..
200 		 * (although if someone already created
201 		 * them, we'll note the fact).
202 		 */
203 		_rename(old_system, Local_System, Lp_Tmp, NULL);
204 		_rename(old_system, Local_System, Lp_Requests, NULL);
205 
206 		Unlink(Lp_Temp);
207 	}
208 	Free(real_dir);
209 }
210 
211 
212 /**
213  ** proto()
214  **/
215 
216 static void
217 proto(int type, int rm_ok, ...)
218 {
219 	va_list			ap;
220 
221 	char			*path,
222 				*symbolic;
223 
224 	int			exist,
225 				err;
226 
227 	mode_t			mode;
228 
229 	uid_t			uid;
230 
231 	gid_t			gid;
232 
233 	struct stat		stbuf;
234 
235 
236 	va_start(ap, rm_ok);
237 
238 	if ((err = va_makepath(&ap, &path)) < 0)
239 		fail ("\"%s\" is a truncated name!\n", path);
240 
241 	exist = (stat(path, &stbuf) == 0);
242 
243 	switch (type) {
244 
245 	case S:
246 		if (!exist)
247 			fail ("%s is missing!\n", path);
248 		if ((err = va_makepath(&ap, &symbolic)) < 0)
249 			fail ("\"%s\" is a truncated name!\n", symbolic);
250 		Symlink (path, symbolic);
251 		Free (symbolic);
252 		Free (path);
253 		return;
254 
255 	case D:
256 		if (exist && !S_ISDIR(stbuf.st_mode)) {
257 			if (!rm_ok)
258 				fail ("%s is not a directory!\n", path);
259 			else {
260 				Unlink (path);
261 				exist = 0;
262 			}
263 		}
264 		if (!exist)
265 			Mkdir (path, 0);
266 		break;
267 
268 	case F:
269 		if (exist && !S_ISREG(stbuf.st_mode)) {
270 			if (!rm_ok)
271 				fail ("%s is not a file!\n", path);
272 			else {
273 				Unlink (path);
274 				exist = 0;
275 			}
276 		}
277 		if (!exist)
278 			Close(Creat(path, 0));
279 		break;
280 
281 	case P:
282 		/*
283 		 * Either a pipe or a file.
284 		 */
285 		if (exist &&
286 		    !S_ISREG(stbuf.st_mode) && !S_ISFIFO(stbuf.st_mode)) {
287 			if (!rm_ok)
288 				fail ("%s is not a file or pipe!\n", path);
289 			else {
290 				Unlink (path);
291 				exist = 0;
292 			}
293 		}
294 		if (!exist)
295 			Close(Creat(path, 0));
296 		break;
297 
298 	}
299 
300 	mode = va_arg(ap, mode_t);
301 	uid = va_arg(ap, uid_t);
302 	gid = va_arg(ap, gid_t);
303 	(void) chownmod(path, uid, gid, mode);
304 
305 	Free (path);
306 	return;
307 }
308 
309 /*
310  * va_makepath()
311  *
312  * Takes a variable length list of path components and attempts to string them
313  * together into a path.  It returns a heap-allocated string via the output
314  * parameter 'ret', and returns an integer success value: < 0 indicates failure,
315  * 0 indicates success.  Note that 'ret' will never be NULL (unless the system
316  * is so overloaded that it can't allocate a single byte), and should always be
317  * free()d.
318  */
319 static int
320 va_makepath (va_list *pap, char **ret)
321 {
322 	char			*component;
323 	char 			buf[MAXPATHLEN];
324 	int			buflen;
325 
326 	memset(buf, NULL, sizeof (buf));
327 	while ((component = va_arg((*pap), char *)) != NULL) {
328 		if (strlcat(buf, component, sizeof (buf)) >= sizeof (buf) ||
329 			strlcat(buf, "/", sizeof (buf)) >= sizeof (buf)) {
330 			if ((*ret = strdup(buf)) == NULL)
331 				*ret = strdup("");
332 			return (-1);
333 		}
334 	}
335 
336 	/* remove the trailing slash */
337 	buflen = strlen(buf);
338 	if ((buflen > 1) && (buf[buflen - 1] == '/')) {
339 		buf[buflen - 1] = '\0';
340 	}
341 
342 	if ((*ret = strdup(buf)) == NULL) {
343 		*ret = strdup("");
344 		return (-1);
345 	}
346 	return (0);
347 }
348 
349 /**
350  ** _rename()
351  **/
352 
353 static void
354 _rename(char *old_system, char *new_system, ...)
355 {
356 	va_list			ap;
357 
358 	char *			prefix;
359 	char *			old;
360 	char *			new;
361 	int			err;
362 
363 
364 	va_start (ap, new_system);
365 	if ((err = va_makepath(&ap, &prefix)) < 0)
366 		fail (
367 			"Rename failed; prefix \"%s\" is a truncated name.\n",
368 			prefix
369 		);
370 	va_end (ap);
371 
372 	old = makepath(prefix, old_system, (char *)0);
373 	new = makepath(prefix, new_system, (char *)0);
374 
375 	if (Rename(old, new) == 0)
376 		note ("Renamed %s to %s.\n", old, new);
377 	else if (errno == EEXIST)
378 		note (
379 			"Rename of %s to %s failed because %s exists.\n",
380 			old,
381 			new,
382 			new
383 		);
384 	else
385 		fail (
386 			"Rename of %s to %s failed (%s).\n",
387 			old,
388 			new,
389 			PERROR
390 		);
391 
392 	Free (new);
393 	Free (old);
394 	Free (prefix);
395 
396 	return;
397 }
398