Home | History | Annotate | Download | only in lpsched
      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