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
lpfsck(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
check_link()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
proto(int type,int rm_ok,...)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
va_makepath(va_list * pap,char ** ret)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
_rename(char * old_system,char * new_system,...)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