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