xref: /illumos-gate/usr/src/cmd/lp/cmd/lpsched/lpfsck.c (revision df1eb1ad)
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 && (stbuf.st_mode & S_IFDIR) == 0)
283 			if (!rm_ok)
284 				fail ("%s is not a directory!\n", path);
285 			else {
286 				Unlink (path);
287 				exist = 0;
288 			}
289 		if (!exist)
290 			Mkdir (path, 0);
291 		break;
292 
293 	case F:
294 		if (exist && (stbuf.st_mode & S_IFREG) == 0)
295 			if (!rm_ok)
296 				fail ("%s is not a file!\n", path);
297 			else {
298 				Unlink (path);
299 				exist = 0;
300 			}
301 		if (!exist)
302 			Close(Creat(path, 0));
303 		break;
304 
305 	case P:
306 		/*
307 		 * Either a pipe or a file.
308 		 */
309 		if (exist && (stbuf.st_mode & (S_IFREG|S_IFIFO)) == 0)
310 			if (!rm_ok)
311 				fail ("%s is not a file or pipe!\n", path);
312 			else {
313 				Unlink (path);
314 				exist = 0;
315 			}
316 		if (!exist)
317 			Close(Creat(path, 0));
318 		break;
319 
320 	}
321 
322 	mode = va_arg(ap, mode_t);
323 	uid = va_arg(ap, uid_t);
324 	gid = va_arg(ap, gid_t);
325 	(void) chownmod(path, uid, gid, mode);
326 
327 	Free (path);
328 	return;
329 }
330 
331 /*
332  * va_makepath()
333  *
334  * Takes a variable length list of path components and attempts to string them
335  * together into a path.  It returns a heap-allocated string via the output
336  * parameter 'ret', and returns an integer success value: < 0 indicates failure,
337  * 0 indicates success.  Note that 'ret' will never be NULL (unless the system
338  * is so overloaded that it can't allocate a single byte), and should always be
339  * free()d.
340  */
341 static int
342 va_makepath (va_list *pap, char **ret)
343 {
344 	char			*component;
345 	char 			buf[MAXPATHLEN];
346 	int			buflen;
347 
348 	memset(buf, NULL, sizeof (buf));
349 	while ((component = va_arg((*pap), char *)) != NULL) {
350 		if (strlcat(buf, component, sizeof (buf)) >= sizeof (buf) ||
351 			strlcat(buf, "/", sizeof (buf)) >= sizeof (buf)) {
352 			if ((*ret = strdup(buf)) == NULL)
353 				*ret = strdup("");
354 			return (-1);
355 		}
356 	}
357 
358 	/* remove the trailing slash */
359 	buflen = strlen(buf);
360 	if ((buflen > 1) && (buf[buflen - 1] == '/')) {
361 		buf[buflen - 1] = '\0';
362 	}
363 
364 	if ((*ret = strdup(buf)) == NULL) {
365 		*ret = strdup("");
366 		return (-1);
367 	}
368 	return (0);
369 }
370 
371 /**
372  ** _rename()
373  **/
374 
375 static void
376 _rename(char *old_system, char *new_system, ...)
377 {
378 	va_list			ap;
379 
380 	char *			prefix;
381 	char *			old;
382 	char *			new;
383 	int			err;
384 
385 
386 	va_start (ap, new_system);
387 	if ((err = va_makepath(&ap, &prefix)) < 0)
388 		fail (
389 			"Rename failed; prefix \"%s\" is a truncated name.\n",
390 			prefix
391 		);
392 	va_end (ap);
393 
394 	old = makepath(prefix, old_system, (char *)0);
395 	new = makepath(prefix, new_system, (char *)0);
396 
397 	if (Rename(old, new) == 0)
398 		note ("Renamed %s to %s.\n", old, new);
399 	else if (errno == EEXIST)
400 		note (
401 			"Rename of %s to %s failed because %s exists.\n",
402 			old,
403 			new,
404 			new
405 		);
406 	else
407 		fail (
408 			"Rename of %s to %s failed (%s).\n",
409 			old,
410 			new,
411 			PERROR
412 		);
413 
414 	Free (new);
415 	Free (old);
416 	Free (prefix);
417 
418 	return;
419 }
420