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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 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 char * cmd; 58 struct stat stbuf; 59 int real_am_in_background = am_in_background; 60 61 62 /* 63 * Force log messages to go into the log file instead of stdout. 64 */ 65 am_in_background = 1; 66 67 /* 68 * Most of these lines repeat the prototype file from the 69 * packaging and should match those items exactly. 70 * (In fact, they probably ought to be generated from that file, 71 * but that work is for a rainy day...) 72 */ 73 74 /* 75 * DIRECTORIES: 76 */ 77 proto (D, 0, Lp_A, NULL, 0775, Lp_Uid, Lp_Gid); 78 proto (D, 1, Lp_A_Classes, NULL, 0775, Lp_Uid, Lp_Gid); 79 proto (D, 1, Lp_A_Forms, NULL, 0775, Lp_Uid, Lp_Gid); 80 proto (D, 1, Lp_A_Interfaces, NULL, 0775, Lp_Uid, Lp_Gid); 81 proto (D, 1, Lp_A_Printers, NULL, 0775, Lp_Uid, Lp_Gid); 82 proto (D, 1, Lp_A_PrintWheels, NULL, 0775, Lp_Uid, Lp_Gid); 83 proto (D, 0, "/var/lp", NULL, 0775, Lp_Uid, Lp_Gid); 84 proto (D, 1, Lp_Logs, NULL, 0775, Lp_Uid, Lp_Gid); 85 proto (D, 1, Lp_Spooldir, NULL, 0775, Lp_Uid, Lp_Gid); 86 proto (D, 1, Lp_Admins, NULL, 0775, Lp_Uid, Lp_Gid); 87 proto (D, 1, Lp_Requests, NULL, 0775, Lp_Uid, Lp_Gid); 88 proto (D, 1, Lp_Requests, Local_System, NULL, 0770, Lp_Uid, Lp_Gid); 89 proto (D, 1, Lp_System, NULL, 0775, Lp_Uid, Lp_Gid); 90 proto (D, 1, Lp_Tmp, NULL, 0771, Lp_Uid, Lp_Gid); 91 proto (D, 1, Lp_Tmp, Local_System, NULL, 0775, Lp_Uid, Lp_Gid); 92 proto (D, 1, Lp_NetTmp, NULL, 0770, Lp_Uid, Lp_Gid); 93 94 /* 95 * DIRECTORIES: not described in the packaging 96 */ 97 proto (D, 0, Lp_Spooldir, FIFOSDIR, NULL, 0775, Lp_Uid, Lp_Gid); 98 proto (D, 1, Lp_Private_FIFOs, NULL, 0771, Lp_Uid, Lp_Gid); 99 proto (D, 1, Lp_Public_FIFOs, NULL, 0773, Lp_Uid, Lp_Gid); 100 101 /* 102 * The lpNet <-> lpsched job transfer directories. 103 * Strictly used for temporary file transfer, on start-up 104 * we can safely clean them out. Indeed, we should clean 105 * them out in case we had died suddenly and are now 106 * restarting. The directories should never be very big, 107 * so we are not in danger of getting ``arglist too big''. 108 */ 109 proto (D, 1, Lp_NetTmp, "tmp", NULL, 0770, Lp_Uid, Lp_Gid); 110 proto (D, 1, Lp_NetTmp, "tmp", Local_System, NULL, 0770, Lp_Uid, Lp_Gid); 111 proto (D, 1, Lp_NetTmp, "requests", NULL, 0770, Lp_Uid, Lp_Gid); 112 proto (D, 1, Lp_NetTmp, "requests", Local_System, NULL, 0770, Lp_Uid, Lp_Gid); 113 cmd = makestr(RMCMD, " ", Lp_NetTmp, "/tmp/*/*", (char *)0); 114 system (cmd); 115 Free (cmd); 116 cmd = makestr(RMCMD, " ", Lp_NetTmp, "/requests/*/*", (char *)0); 117 system (cmd); 118 Free (cmd); 119 120 /* 121 * THE MAIN FIFO: 122 */ 123 proto (P, 1, Lp_FIFO, NULL, 0666, Lp_Uid, Lp_Gid); 124 125 /* 126 * SYMBOLIC LINKS: 127 * Watch out! These names are given in the reverse 128 * order found in the prototype file (sorry!) 129 */ 130 proto (S, 1, Lp_Model, NULL, "/etc/lp/model", NULL); 131 proto (S, 1, Lp_Logs, NULL, "/etc/lp/logs", NULL); 132 /* S, 1, Lp_Tmp, Local_System, ... DONE BELOW */ 133 proto (S, 1, Lp_Bin, NULL, Lp_Spooldir, "bin", NULL); 134 proto (S, 1, Lp_A, NULL, Lp_Admins, "lp", NULL); 135 136 /* 137 * OTHER FILES: 138 */ 139 proto (F, 1, Lp_NetData, NULL, 0664, Lp_Uid, Lp_Gid); 140 141 /* 142 * SPECIAL CASE: 143 * If the "temp" symbolic link already exists, 144 * but is not correct, assume the machine's nodename changed. 145 * Rename directories that include the nodename, if possible, 146 * so that unprinted requests are saved. Then change the 147 * symbolic link. 148 * Watch out for a ``symbolic link'' that isn't! 149 */ 150 if (Lstat(Lp_Temp, &stbuf) == 0) 151 switch (stbuf.st_mode & S_IFMT) { 152 153 default: 154 Unlink (Lp_Temp); 155 break; 156 157 case S_IFDIR: 158 Rmdir (Lp_Temp); 159 break; 160 161 case S_IFLNK: 162 check_link(); 163 break; 164 } 165 166 proto(S, 1, Lp_Tmp, Local_System, NULL, Lp_Temp, NULL); 167 168 am_in_background = real_am_in_background; 169 return; 170 } 171 172 static void 173 check_link() 174 { 175 int len; 176 char symbolic[MAXPATHLEN + 1]; 177 char *real_dir; 178 char *old_system; 179 180 if ((len = Readlink(Lp_Temp, symbolic, MAXPATHLEN)) <= 0) { 181 Unlink(Lp_Temp); 182 return; 183 } 184 185 /* 186 * If the symbolic link contained trailing slashes, remove 187 * them. 188 */ 189 while ((len > 1) && (symbolic[len - 1] == '/')) { 190 len--; 191 } 192 symbolic[len] = 0; 193 194 /* check that symlink points into /var/spool/lp/tmp */ 195 if (strncmp(Lp_Tmp, symbolic, strlen(Lp_Tmp)) != 0) { 196 Unlink(Lp_Temp); 197 return; 198 } 199 200 /* 201 * Check that symlink points to something. 202 * There should be at least 2 characters 203 * after the string '/var/spool/lp/tmp': 204 * a '/' and another character. 205 */ 206 if (len <= strlen(Lp_Tmp) + 1) { 207 Unlink(Lp_Temp); 208 return; 209 } 210 211 real_dir = makepath(Lp_Tmp, Local_System, NULL); 212 if (!STREQU(real_dir, symbolic)) { 213 if (!(old_system = strrchr(symbolic, '/'))) 214 old_system = symbolic; 215 else 216 old_system++; 217 218 /* 219 * The "rename()" system call (buried 220 * inside the "_rename()" routine) should 221 * succeed, even though we blindly created 222 * the new directory earlier, as the only 223 * directory entries should be . and .. 224 * (although if someone already created 225 * them, we'll note the fact). 226 */ 227 _rename(old_system, Local_System, Lp_Tmp, NULL); 228 _rename(old_system, Local_System, Lp_Requests, NULL); 229 _rename(old_system, Local_System, Lp_NetTmp, "tmp", NULL); 230 _rename(old_system, Local_System, Lp_NetTmp, "requests", NULL); 231 232 Unlink(Lp_Temp); 233 } 234 Free(real_dir); 235 } 236 237 238 /** 239 ** proto() 240 **/ 241 242 static void 243 proto(int type, int rm_ok, ...) 244 { 245 va_list ap; 246 247 char *path, 248 *symbolic; 249 250 int exist, 251 err; 252 253 mode_t mode; 254 255 uid_t uid; 256 257 gid_t gid; 258 259 struct stat stbuf; 260 261 262 va_start(ap, rm_ok); 263 264 if ((err = va_makepath(&ap, &path)) < 0) 265 fail ("\"%s\" is a truncated name!\n", path); 266 267 exist = (stat(path, &stbuf) == 0); 268 269 switch (type) { 270 271 case S: 272 if (!exist) 273 fail ("%s is missing!\n", path); 274 if ((err = va_makepath(&ap, &symbolic)) < 0) 275 fail ("\"%s\" is a truncated name!\n", symbolic); 276 Symlink (path, symbolic); 277 Free (symbolic); 278 Free (path); 279 return; 280 281 case D: 282 if (exist && !S_ISDIR(stbuf.st_mode)) { 283 if (!rm_ok) 284 fail ("%s is not a directory!\n", path); 285 else { 286 Unlink (path); 287 exist = 0; 288 } 289 } 290 if (!exist) 291 Mkdir (path, 0); 292 break; 293 294 case F: 295 if (exist && !S_ISREG(stbuf.st_mode)) { 296 if (!rm_ok) 297 fail ("%s is not a file!\n", path); 298 else { 299 Unlink (path); 300 exist = 0; 301 } 302 } 303 if (!exist) 304 Close(Creat(path, 0)); 305 break; 306 307 case P: 308 /* 309 * Either a pipe or a file. 310 */ 311 if (exist && 312 !S_ISREG(stbuf.st_mode) && !S_ISFIFO(stbuf.st_mode)) { 313 if (!rm_ok) 314 fail ("%s is not a file or pipe!\n", path); 315 else { 316 Unlink (path); 317 exist = 0; 318 } 319 } 320 if (!exist) 321 Close(Creat(path, 0)); 322 break; 323 324 } 325 326 mode = va_arg(ap, mode_t); 327 uid = va_arg(ap, uid_t); 328 gid = va_arg(ap, gid_t); 329 (void) chownmod(path, uid, gid, mode); 330 331 Free (path); 332 return; 333 } 334 335 /* 336 * va_makepath() 337 * 338 * Takes a variable length list of path components and attempts to string them 339 * together into a path. It returns a heap-allocated string via the output 340 * parameter 'ret', and returns an integer success value: < 0 indicates failure, 341 * 0 indicates success. Note that 'ret' will never be NULL (unless the system 342 * is so overloaded that it can't allocate a single byte), and should always be 343 * free()d. 344 */ 345 static int 346 va_makepath (va_list *pap, char **ret) 347 { 348 char *component; 349 char buf[MAXPATHLEN]; 350 int buflen; 351 352 memset(buf, NULL, sizeof (buf)); 353 while ((component = va_arg((*pap), char *)) != NULL) { 354 if (strlcat(buf, component, sizeof (buf)) >= sizeof (buf) || 355 strlcat(buf, "/", sizeof (buf)) >= sizeof (buf)) { 356 if ((*ret = strdup(buf)) == NULL) 357 *ret = strdup(""); 358 return (-1); 359 } 360 } 361 362 /* remove the trailing slash */ 363 buflen = strlen(buf); 364 if ((buflen > 1) && (buf[buflen - 1] == '/')) { 365 buf[buflen - 1] = '\0'; 366 } 367 368 if ((*ret = strdup(buf)) == NULL) { 369 *ret = strdup(""); 370 return (-1); 371 } 372 return (0); 373 } 374 375 /** 376 ** _rename() 377 **/ 378 379 static void 380 _rename(char *old_system, char *new_system, ...) 381 { 382 va_list ap; 383 384 char * prefix; 385 char * old; 386 char * new; 387 int err; 388 389 390 va_start (ap, new_system); 391 if ((err = va_makepath(&ap, &prefix)) < 0) 392 fail ( 393 "Rename failed; prefix \"%s\" is a truncated name.\n", 394 prefix 395 ); 396 va_end (ap); 397 398 old = makepath(prefix, old_system, (char *)0); 399 new = makepath(prefix, new_system, (char *)0); 400 401 if (Rename(old, new) == 0) 402 note ("Renamed %s to %s.\n", old, new); 403 else if (errno == EEXIST) 404 note ( 405 "Rename of %s to %s failed because %s exists.\n", 406 old, 407 new, 408 new 409 ); 410 else 411 fail ( 412 "Rename of %s to %s failed (%s).\n", 413 old, 414 new, 415 PERROR 416 ); 417 418 Free (new); 419 Free (old); 420 Free (prefix); 421 422 return; 423 } 424