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 && (stbuf.st_mode & S_IFDIR) == 0) 283 if (!rm_ok) 284 fail ("%s is not a directory!\n", path); 285 else { 286 Unlink (path); 287 exist = 0; 288 } 289 if (!exist) 290 Mkdir (path, 0); 291 break; 292 293 case F: 294 if (exist && (stbuf.st_mode & S_IFREG) == 0) 295 if (!rm_ok) 296 fail ("%s is not a file!\n", path); 297 else { 298 Unlink (path); 299 exist = 0; 300 } 301 if (!exist) 302 Close(Creat(path, 0)); 303 break; 304 305 case P: 306 /* 307 * Either a pipe or a file. 308 */ 309 if (exist && (stbuf.st_mode & (S_IFREG|S_IFIFO)) == 0) 310 if (!rm_ok) 311 fail ("%s is not a file or pipe!\n", path); 312 else { 313 Unlink (path); 314 exist = 0; 315 } 316 if (!exist) 317 Close(Creat(path, 0)); 318 break; 319 320 } 321 322 mode = va_arg(ap, mode_t); 323 uid = va_arg(ap, uid_t); 324 gid = va_arg(ap, gid_t); 325 Chmod (path, mode); 326 Chown (path, uid, gid); 327 328 Free (path); 329 return; 330 } 331 332 /* 333 * va_makepath() 334 * 335 * Takes a variable length list of path components and attempts to string them 336 * together into a path. It returns a heap-allocated string via the output 337 * parameter 'ret', and returns an integer success value: < 0 indicates failure, 338 * 0 indicates success. Note that 'ret' will never be NULL (unless the system 339 * is so overloaded that it can't allocate a single byte), and should always be 340 * free()d. 341 */ 342 static int 343 va_makepath (va_list *pap, char **ret) 344 { 345 char *component; 346 char buf[MAXPATHLEN]; 347 int buflen; 348 349 memset(buf, NULL, sizeof (buf)); 350 while ((component = va_arg((*pap), char *)) != NULL) { 351 if (strlcat(buf, component, sizeof (buf)) >= sizeof (buf) || 352 strlcat(buf, "/", sizeof (buf)) >= sizeof (buf)) { 353 if ((*ret = strdup(buf)) == NULL) 354 *ret = strdup(""); 355 return (-1); 356 } 357 } 358 359 /* remove the trailing slash */ 360 buflen = strlen(buf); 361 if ((buflen > 1) && (buf[buflen - 1] == '/')) { 362 buf[buflen - 1] = '\0'; 363 } 364 365 if ((*ret = strdup(buf)) == NULL) { 366 *ret = strdup(""); 367 return (-1); 368 } 369 return (0); 370 } 371 372 /** 373 ** _rename() 374 **/ 375 376 static void 377 _rename(char *old_system, char *new_system, ...) 378 { 379 va_list ap; 380 381 char * prefix; 382 char * old; 383 char * new; 384 int err; 385 386 387 va_start (ap, new_system); 388 if ((err = va_makepath(&ap, &prefix)) < 0) 389 fail ( 390 "Rename failed; prefix \"%s\" is a truncated name.\n", 391 prefix 392 ); 393 va_end (ap); 394 395 old = makepath(prefix, old_system, (char *)0); 396 new = makepath(prefix, new_system, (char *)0); 397 398 if (Rename(old, new) == 0) 399 note ("Renamed %s to %s.\n", old, new); 400 else if (errno == EEXIST) 401 note ( 402 "Rename of %s to %s failed because %s exists.\n", 403 old, 404 new, 405 new 406 ); 407 else 408 fail ( 409 "Rename of %s to %s failed (%s).\n", 410 old, 411 new, 412 PERROR 413 ); 414 415 Free (new); 416 Free (old); 417 Free (prefix); 418 419 return; 420 } 421