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