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