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 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 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 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 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 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