xref: /illumos-gate/usr/src/cmd/lp/cmd/lpsched/lpfsck.c (revision 004388ebfdfe2ed7dfd2d153a876dfcc22d2c006)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 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 	char *			cmd;
58 	struct stat		stbuf;
59 	int			real_am_in_background = am_in_background;
60 
61 
62 	/*
63 	 * Force log messages to go into the log file instead of stdout.
64 	 */
65 	am_in_background = 1;
66 
67 	/*
68 	 * Most of these lines repeat the prototype file from the
69 	 * packaging and should match those items exactly.
70 	 * (In fact, they probably ought to be generated from that file,
71 	 * but that work is for a rainy day...)
72 	 */
73 
74 	/*
75 	 * DIRECTORIES:
76 	 */
77 proto (D, 0,  Lp_A, NULL,			    0775, Lp_Uid, Lp_Gid);
78 proto (D, 1,  Lp_A_Classes, NULL,		    0775, Lp_Uid, Lp_Gid);
79 proto (D, 1,  Lp_A_Forms, NULL,			    0775, Lp_Uid, Lp_Gid);
80 proto (D, 1,  Lp_A_Interfaces, NULL,		    0775, Lp_Uid, Lp_Gid);
81 proto (D, 1,  Lp_A_Printers, NULL,		    0775, Lp_Uid, Lp_Gid);
82 proto (D, 1,  Lp_A_PrintWheels, NULL,		    0775, Lp_Uid, Lp_Gid);
83 proto (D, 0,  "/var/lp", NULL,			    0775, Lp_Uid, Lp_Gid);
84 proto (D, 1,  Lp_Logs, NULL,			    0775, Lp_Uid, Lp_Gid);
85 proto (D, 1,  Lp_Spooldir, NULL,		    0775, Lp_Uid, Lp_Gid);
86 proto (D, 1,  Lp_Admins, NULL,			    0775, Lp_Uid, Lp_Gid);
87 proto (D, 1,  Lp_Requests, NULL,		    0775, Lp_Uid, Lp_Gid);
88 proto (D, 1,  Lp_Requests, Local_System, NULL,	    0770, Lp_Uid, Lp_Gid);
89 proto (D, 1,  Lp_System, NULL,			    0775, Lp_Uid, Lp_Gid);
90 proto (D, 1,  Lp_Tmp, NULL,			    0771, Lp_Uid, Lp_Gid);
91 proto (D, 1,  Lp_Tmp, Local_System, NULL,	    0775, Lp_Uid, Lp_Gid);
92 proto (D, 1,  Lp_NetTmp, NULL,			    0770, Lp_Uid, Lp_Gid);
93 
94 	/*
95 	 * DIRECTORIES: not described in the packaging
96 	 */
97 proto (D, 0,  Lp_Spooldir, FIFOSDIR, NULL,	    0775, Lp_Uid, Lp_Gid);
98 proto (D, 1,  Lp_Private_FIFOs, NULL,		    0771, Lp_Uid, Lp_Gid);
99 proto (D, 1,  Lp_Public_FIFOs, NULL,		    0773, Lp_Uid, Lp_Gid);
100 
101 	/*
102 	 * The lpNet <-> lpsched job transfer directories.
103 	 * Strictly used for temporary file transfer, on start-up
104 	 * we can safely clean them out. Indeed, we should clean
105 	 * them out in case we had died suddenly and are now
106 	 * restarting. The directories should never be very big,
107 	 * so we are not in danger of getting ``arglist too big''.
108 	 */
109 proto (D, 1,  Lp_NetTmp, "tmp", NULL,		    0770, Lp_Uid, Lp_Gid);
110 proto (D, 1,  Lp_NetTmp, "tmp", Local_System, NULL, 0770, Lp_Uid, Lp_Gid);
111 proto (D, 1,  Lp_NetTmp, "requests", NULL,	    0770, Lp_Uid, Lp_Gid);
112 proto (D, 1,  Lp_NetTmp, "requests", Local_System, NULL, 0770, Lp_Uid, Lp_Gid);
113 	cmd = makestr(RMCMD, " ", Lp_NetTmp, "/tmp/*/*", (char *)0);
114 	system (cmd);
115 	Free (cmd);
116 	cmd = makestr(RMCMD, " ", Lp_NetTmp, "/requests/*/*", (char *)0);
117 	system (cmd);
118 	Free (cmd);
119 
120 	/*
121 	 * THE MAIN FIFO:
122 	 */
123 proto (P, 1,  Lp_FIFO, NULL,			    0666, Lp_Uid, Lp_Gid);
124 
125 	/*
126 	 * SYMBOLIC LINKS:
127 	 * Watch out! These names are given in the reverse
128 	 * order found in the prototype file (sorry!)
129 	 */
130 proto (S, 1,  Lp_Model, NULL,			"/etc/lp/model", NULL);
131 proto (S, 1,  Lp_Logs, NULL,			"/etc/lp/logs", NULL);
132 /*     S, 1,  Lp_Tmp, Local_System, ...    DONE BELOW */
133 proto (S, 1,  Lp_Bin, NULL,			Lp_Spooldir, "bin", NULL);
134 proto (S, 1,  Lp_A, NULL,			Lp_Admins, "lp", NULL);
135 
136 	/*
137 	 * OTHER FILES:
138 	 */
139 proto (F, 1,  Lp_NetData, NULL,			    0664, Lp_Uid, Lp_Gid);
140 
141 	/*
142 	 * SPECIAL CASE:
143 	 * If the "temp" symbolic link already exists,
144 	 * but is not correct, assume the machine's nodename changed.
145 	 * Rename directories that include the nodename, if possible,
146 	 * so that unprinted requests are saved. Then change the
147 	 * symbolic link.
148 	 * Watch out for a ``symbolic link'' that isn't!
149 	 */
150 	if (Lstat(Lp_Temp, &stbuf) == 0)
151 	    switch (stbuf.st_mode & S_IFMT) {
152 
153 	    default:
154 		Unlink (Lp_Temp);
155 		break;
156 
157 	    case S_IFDIR:
158 		Rmdir (Lp_Temp);
159 		break;
160 
161 	    case S_IFLNK:
162 		check_link();
163 		break;
164 	    }
165 
166 	proto(S, 1, Lp_Tmp, Local_System, NULL,	Lp_Temp, NULL);
167 
168 	am_in_background = real_am_in_background;
169 	return;
170 }
171 
172 static void
173 check_link()
174 {
175 	int len;
176 	char symbolic[MAXPATHLEN + 1];
177 	char *real_dir;
178 	char *old_system;
179 
180 	if ((len = Readlink(Lp_Temp, symbolic, MAXPATHLEN)) <= 0) {
181 		Unlink(Lp_Temp);
182 		return;
183 	}
184 
185 	/*
186 	 * If the symbolic link contained trailing slashes, remove
187 	 * them.
188 	 */
189 	while ((len > 1) && (symbolic[len - 1] == '/')) {
190 		len--;
191 	}
192 	symbolic[len] = 0;
193 
194 	/* check that symlink points into /var/spool/lp/tmp */
195 	if (strncmp(Lp_Tmp, symbolic, strlen(Lp_Tmp)) != 0) {
196 		Unlink(Lp_Temp);
197 		return;
198 	}
199 
200 	/*
201 	 * Check that symlink points to something.
202 	 * There should be at least 2 characters
203 	 * after the string '/var/spool/lp/tmp':
204 	 * a '/' and another character.
205 	 */
206 	if (len <= strlen(Lp_Tmp) + 1) {
207 		Unlink(Lp_Temp);
208 		return;
209 	}
210 
211 	real_dir = makepath(Lp_Tmp, Local_System, NULL);
212 	if (!STREQU(real_dir, symbolic)) {
213 		if (!(old_system = strrchr(symbolic, '/')))
214 			old_system = symbolic;
215 		else
216 			old_system++;
217 
218 		/*
219 		 * The "rename()" system call (buried
220 		 * inside the "_rename()" routine) should
221 		 * succeed, even though we blindly created
222 		 * the new directory earlier, as the only
223 		 * directory entries should be . and ..
224 		 * (although if someone already created
225 		 * them, we'll note the fact).
226 		 */
227 		_rename(old_system, Local_System, Lp_Tmp, NULL);
228 		_rename(old_system, Local_System, Lp_Requests, NULL);
229 		_rename(old_system, Local_System, Lp_NetTmp, "tmp", NULL);
230 		_rename(old_system, Local_System, Lp_NetTmp, "requests", NULL);
231 
232 		Unlink(Lp_Temp);
233 	}
234 	Free(real_dir);
235 }
236 
237 
238 /**
239  ** proto()
240  **/
241 
242 static void
243 proto(int type, int rm_ok, ...)
244 {
245 	va_list			ap;
246 
247 	char			*path,
248 				*symbolic;
249 
250 	int			exist,
251 				err;
252 
253 	mode_t			mode;
254 
255 	uid_t			uid;
256 
257 	gid_t			gid;
258 
259 	struct stat		stbuf;
260 
261 
262 	va_start(ap, rm_ok);
263 
264 	if ((err = va_makepath(&ap, &path)) < 0)
265 		fail ("\"%s\" is a truncated name!\n", path);
266 
267 	exist = (stat(path, &stbuf) == 0);
268 
269 	switch (type) {
270 
271 	case S:
272 		if (!exist)
273 			fail ("%s is missing!\n", path);
274 		if ((err = va_makepath(&ap, &symbolic)) < 0)
275 			fail ("\"%s\" is a truncated name!\n", symbolic);
276 		Symlink (path, symbolic);
277 		Free (symbolic);
278 		Free (path);
279 		return;
280 
281 	case D:
282 		if (exist && !S_ISDIR(stbuf.st_mode)) {
283 			if (!rm_ok)
284 				fail ("%s is not a directory!\n", path);
285 			else {
286 				Unlink (path);
287 				exist = 0;
288 			}
289 		}
290 		if (!exist)
291 			Mkdir (path, 0);
292 		break;
293 
294 	case F:
295 		if (exist && !S_ISREG(stbuf.st_mode)) {
296 			if (!rm_ok)
297 				fail ("%s is not a file!\n", path);
298 			else {
299 				Unlink (path);
300 				exist = 0;
301 			}
302 		}
303 		if (!exist)
304 			Close(Creat(path, 0));
305 		break;
306 
307 	case P:
308 		/*
309 		 * Either a pipe or a file.
310 		 */
311 		if (exist &&
312 		    !S_ISREG(stbuf.st_mode) && !S_ISFIFO(stbuf.st_mode)) {
313 			if (!rm_ok)
314 				fail ("%s is not a file or pipe!\n", path);
315 			else {
316 				Unlink (path);
317 				exist = 0;
318 			}
319 		}
320 		if (!exist)
321 			Close(Creat(path, 0));
322 		break;
323 
324 	}
325 
326 	mode = va_arg(ap, mode_t);
327 	uid = va_arg(ap, uid_t);
328 	gid = va_arg(ap, gid_t);
329 	(void) chownmod(path, uid, gid, mode);
330 
331 	Free (path);
332 	return;
333 }
334 
335 /*
336  * va_makepath()
337  *
338  * Takes a variable length list of path components and attempts to string them
339  * together into a path.  It returns a heap-allocated string via the output
340  * parameter 'ret', and returns an integer success value: < 0 indicates failure,
341  * 0 indicates success.  Note that 'ret' will never be NULL (unless the system
342  * is so overloaded that it can't allocate a single byte), and should always be
343  * free()d.
344  */
345 static int
346 va_makepath (va_list *pap, char **ret)
347 {
348 	char			*component;
349 	char 			buf[MAXPATHLEN];
350 	int			buflen;
351 
352 	memset(buf, NULL, sizeof (buf));
353 	while ((component = va_arg((*pap), char *)) != NULL) {
354 		if (strlcat(buf, component, sizeof (buf)) >= sizeof (buf) ||
355 			strlcat(buf, "/", sizeof (buf)) >= sizeof (buf)) {
356 			if ((*ret = strdup(buf)) == NULL)
357 				*ret = strdup("");
358 			return (-1);
359 		}
360 	}
361 
362 	/* remove the trailing slash */
363 	buflen = strlen(buf);
364 	if ((buflen > 1) && (buf[buflen - 1] == '/')) {
365 		buf[buflen - 1] = '\0';
366 	}
367 
368 	if ((*ret = strdup(buf)) == NULL) {
369 		*ret = strdup("");
370 		return (-1);
371 	}
372 	return (0);
373 }
374 
375 /**
376  ** _rename()
377  **/
378 
379 static void
380 _rename(char *old_system, char *new_system, ...)
381 {
382 	va_list			ap;
383 
384 	char *			prefix;
385 	char *			old;
386 	char *			new;
387 	int			err;
388 
389 
390 	va_start (ap, new_system);
391 	if ((err = va_makepath(&ap, &prefix)) < 0)
392 		fail (
393 			"Rename failed; prefix \"%s\" is a truncated name.\n",
394 			prefix
395 		);
396 	va_end (ap);
397 
398 	old = makepath(prefix, old_system, (char *)0);
399 	new = makepath(prefix, new_system, (char *)0);
400 
401 	if (Rename(old, new) == 0)
402 		note ("Renamed %s to %s.\n", old, new);
403 	else if (errno == EEXIST)
404 		note (
405 			"Rename of %s to %s failed because %s exists.\n",
406 			old,
407 			new,
408 			new
409 		);
410 	else
411 		fail (
412 			"Rename of %s to %s failed (%s).\n",
413 			old,
414 			new,
415 			PERROR
416 		);
417 
418 	Free (new);
419 	Free (old);
420 	Free (prefix);
421 
422 	return;
423 }
424