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