Home | History | Annotate | Download | only in autopush
      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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
     27 /*	  All Rights Reserved  	*/
     28 
     29 
     30 /*
     31  * autopush(1) is the command interface to the STREAMS autopush
     32  * mechanism.  The autopush command can be used to configure autopush
     33  * information about a STREAMS driver, remove autopush information,
     34  * and report on current configuration information.  Its use is as
     35  * follows:
     36  *
     37  *	autopush -f file
     38  *	autopush -r -M major -m minor
     39  *	autopush -g -M major -m minor
     40  *
     41  * The -f option allows autopush information to be set from a file.  The
     42  * format of the file is as follows:
     43  *
     44  * # Comment lines begin with a # in column one.
     45  * # The fields are separated by white space and are:
     46  * # major	minor	lastminor	module1 module2 ... module8
     47  *
     48  * "lastminor" is used to configure ranges of minor devices, from "minor"
     49  * to "lastminor" inclusive.  It should be set to zero when not in use.
     50  * The -r option allows autopush information to be removed for the given
     51  * major/minor pair.  The -g option allows the configuration information
     52  * to be printed.  The format of printing is the same as for the file.
     53  */
     54 
     55 /*
     56  * Use autopush version 1; keep before #include <sys/sad.h>.
     57  * See <sys/sad.h> for details.
     58  */
     59 #define	AP_VERSION	1
     60 
     61 #include <sys/types.h>
     62 #include <sys/conf.h>
     63 #include <sys/modctl.h>
     64 #include <sys/sad.h>
     65 #include <stdio.h>
     66 #include <fcntl.h>
     67 #include <errno.h>
     68 #include <ctype.h>
     69 #include <stdlib.h>
     70 #include <unistd.h>
     71 #include <string.h>
     72 #include <locale.h>
     73 #include <sys/stat.h>
     74 #include <zone.h>
     75 
     76 #define	OPTIONS	"M:f:gm:r"	/* command line options for getopt(3C) */
     77 #define	COMMENT	'#'
     78 #define	MINUS	'-'
     79 #define	SLASH	'/'
     80 
     81 /*
     82  * Output format.
     83  */
     84 #define	OHEADER		"     Major      Minor  Lastminor\tModules\n"
     85 #define	OFORMAT1_ONE	"%10ld %10ld      -    \t"
     86 #define	OFORMAT1_RANGE	"%10ld %10ld %10ld\t"
     87 #define	OFORMAT1_ALL	"%10ld       ALL       -    \t"
     88 
     89 #define	AP_ANCHOR	"[anchor]"
     90 
     91 #define	Openerr		gettext("%s: ERROR: Could not open %s: ")
     92 #define	Digiterr	gettext("%s: ERROR: argument to %s option must be " \
     93 			    "numeric\n")
     94 #define	Badline		gettext("%s: WARNING: File %s: bad input line %d " \
     95 			    "ignored\n")
     96 
     97 static void	usage();
     98 static int	rem_info(), get_info(), set_info();
     99 static int	is_white_space(), parse_line();
    100 
    101 static char	*Cmdp;		/* command name */
    102 
    103 /*
    104  * main():
    105  *	process command line arguments.
    106  */
    107 int
    108 main(int argc, char *argv[])
    109 {
    110 	int		c;		/* character read by getopt(3C) */
    111 	char		*filenamep;	/* name of configuration file */
    112 	major_t		major;		/* major device number */
    113 	minor_t		minor;		/* minor device number */
    114 	char		*cp;
    115 	int		exitcode;
    116 	ushort_t	minflag = 0;	/* -m option used */
    117 	ushort_t	majflag = 0;	/* -M option used */
    118 	ushort_t	fflag = 0;	/* -f option used */
    119 	ushort_t	rflag = 0;	/* -r option used */
    120 	ushort_t	gflag = 0;	/* -g option used */
    121 	ushort_t	errflag = 0;	/* options usage error */
    122 
    123 	(void) setlocale(LC_ALL, "");
    124 #if !defined(TEXT_DOMAIN)
    125 #define	TEXT_DOMAIN "SYS_TEST"
    126 #endif
    127 	(void) textdomain(TEXT_DOMAIN);
    128 
    129 	/*
    130 	 * Get command name.
    131 	 */
    132 	Cmdp = argv[0];
    133 	for (filenamep = argv[0]; *filenamep; filenamep++)
    134 		if (*filenamep == SLASH)
    135 			Cmdp = filenamep + 1;
    136 
    137 	/*
    138 	 * Get options.
    139 	 */
    140 	while (!errflag && ((c = getopt(argc, argv, OPTIONS)) != -1)) {
    141 		switch (c) {
    142 		case 'M':
    143 			if (fflag|majflag)
    144 				errflag++;
    145 			else {
    146 				majflag++;
    147 				for (cp = optarg; *cp; cp++)
    148 					if (!isdigit(*cp)) {
    149 						(void) fprintf(stderr,
    150 						    Digiterr, Cmdp, "-M");
    151 						exit(1);
    152 					}
    153 				major = (major_t)atol(optarg);
    154 			}
    155 			break;
    156 
    157 		case 'm':
    158 			if (fflag|minflag)
    159 				errflag++;
    160 			else {
    161 				minflag++;
    162 				for (cp = optarg; *cp; cp++)
    163 					if (!isdigit(*cp)) {
    164 						(void) fprintf(stderr,
    165 						    Digiterr, Cmdp, "-m");
    166 						exit(1);
    167 					}
    168 				minor = (minor_t)atol(optarg);
    169 			}
    170 			break;
    171 
    172 		case 'f':
    173 			if (fflag|gflag|rflag|majflag|minflag)
    174 				errflag++;
    175 			else {
    176 				fflag++;
    177 				filenamep = optarg;
    178 			}
    179 			break;
    180 
    181 		case 'r':
    182 			if (fflag|gflag|rflag)
    183 				errflag++;
    184 			else
    185 				rflag++;
    186 			break;
    187 
    188 		case 'g':
    189 			if (fflag|gflag|rflag)
    190 				errflag++;
    191 			else
    192 				gflag++;
    193 			break;
    194 
    195 		default:
    196 			errflag++;
    197 			break;
    198 		} /* switch */
    199 		if (errflag) {
    200 			usage();
    201 			exit(1);
    202 		}
    203 	} /* while */
    204 	if (((gflag || rflag) && (!majflag || !minflag)) || (optind != argc)) {
    205 		usage();
    206 		exit(1);
    207 	}
    208 
    209 	if (getzoneid() != GLOBAL_ZONEID) {
    210 		(void) fprintf(stderr, gettext("autopush "
    211 		    "can only be run from the global zone.\n"));
    212 		exit(1);
    213 	}
    214 
    215 	if (fflag)
    216 		exitcode = set_info(filenamep);
    217 	else if (rflag)
    218 		exitcode = rem_info(major, minor);
    219 	else if (gflag)
    220 		exitcode = get_info(major, minor);
    221 	else {
    222 		usage();
    223 		exit(1);
    224 	}
    225 
    226 	return (exitcode);
    227 }
    228 
    229 /*
    230  * usage():
    231  *	print out usage statement.
    232  */
    233 static void
    234 usage()
    235 {
    236 	(void) fprintf(stderr,	gettext("%s: USAGE:\n\t%s -f filename\n"
    237 	    "\t%s -r -M major -m minor\n"
    238 	    "\t%s -g -M major -m minor\n"), Cmdp, Cmdp, Cmdp, Cmdp);
    239 }
    240 
    241 /*
    242  * set_info():
    243  *	set autopush configuration information.
    244  *	namep: autopush configuration filename
    245  */
    246 static int
    247 set_info(char *namep)
    248 {
    249 	int		line;		/* line number of file */
    250 	FILE		*fp;		/* file pointer of config file */
    251 	char		buf[256];	/* input buffer */
    252 	struct strapush push;		/* configuration information */
    253 	int		sadfd;		/* file descriptor to SAD driver */
    254 	int		retcode = 0;	/* return code */
    255 	int		parsecode;	/* return value from parse function */
    256 
    257 	if ((sadfd = open(ADMINDEV, O_RDWR)) < 0) {
    258 		(void) fprintf(stderr, Openerr, Cmdp, ADMINDEV);
    259 		perror("");
    260 		return (1);
    261 	}
    262 	if ((fp = fopen(namep, "r")) == NULL) {
    263 		(void) fprintf(stderr, Openerr, Cmdp, namep);
    264 		perror("");
    265 		return (1);
    266 	}
    267 	line = 0;
    268 	while (fgets(buf, sizeof (buf), fp) != NULL) {
    269 		line++;
    270 		if ((buf[0] == COMMENT) || is_white_space(buf))
    271 			continue;
    272 		(void) memset(&push, 0, sizeof (struct strapush));
    273 
    274 		parsecode = parse_line(buf, line, namep, &push);
    275 		if (parsecode != 0) {
    276 			retcode = parsecode;
    277 			continue;
    278 		}
    279 
    280 		if (push.sap_minor == (minor_t)-1)
    281 			push.sap_cmd = SAP_ALL;
    282 		else if (push.sap_lastminor == 0)
    283 			push.sap_cmd = SAP_ONE;
    284 		else
    285 			push.sap_cmd = SAP_RANGE;
    286 
    287 		if (ioctl(sadfd, SAD_SAP, &push) < 0) {
    288 			int error = errno;
    289 
    290 			retcode = 1;
    291 			(void) fprintf(stderr,
    292 			    gettext("%s: ERROR: File %s: could not configure "
    293 			    "autopush for line %d\n"), Cmdp, namep, line);
    294 			switch (error) {
    295 			case EPERM:
    296 				(void) fprintf(stderr, gettext("%s: ERROR: "
    297 				    "You don't have permission to set autopush "
    298 				    "information\n"), Cmdp);
    299 				break;
    300 
    301 			case EINVAL:
    302 				(void) fprintf(stderr, gettext("%s: ERROR: "
    303 				    "Invalid major device number or invalid "
    304 				    "module name or too many modules\n"), Cmdp);
    305 				break;
    306 
    307 			case ENOSTR:
    308 				(void) fprintf(stderr, gettext("%s: ERROR: "
    309 				    "Major device is not a STREAMS "
    310 				    "driver\n"), Cmdp);
    311 				break;
    312 
    313 			case EEXIST:
    314 				(void) fprintf(stderr, gettext("%s: ERROR: "
    315 				    "Major/minor already configured\n"), Cmdp);
    316 				break;
    317 
    318 			case ENOSR:
    319 				(void) fprintf(stderr, gettext("%s: ERROR: Ran "
    320 				    "out of autopush structures\n"), Cmdp);
    321 				break;
    322 
    323 			case ERANGE:
    324 				(void) fprintf(stderr, gettext("%s: ERROR: "
    325 				    "lastminor must be greater than minor\n"),
    326 				    Cmdp);
    327 				break;
    328 
    329 			default:
    330 				(void) fprintf(stderr, gettext("%s: ERROR: "),
    331 				    Cmdp);
    332 				(void) fprintf(stderr, "%s\n", strerror(error));
    333 				break;
    334 			} /* switch */
    335 		} /* if */
    336 	} /* while */
    337 	return (retcode);
    338 }
    339 
    340 /*
    341  * rem_info():
    342  *	remove autopush configuration information.
    343  */
    344 static int
    345 rem_info(major_t maj, minor_t min)
    346 {
    347 	struct strapush push;		/* configuration information */
    348 	int		sadfd;		/* file descriptor to SAD driver */
    349 	int		retcode = 0;	/* return code */
    350 
    351 	if ((sadfd = open(ADMINDEV, O_RDWR)) < 0) {
    352 		(void) fprintf(stderr, Openerr, Cmdp, ADMINDEV);
    353 		perror("");
    354 		return (1);
    355 	}
    356 	push.sap_cmd = SAP_CLEAR;
    357 	push.sap_minor = min;
    358 	push.sap_major = maj;
    359 
    360 	if (ioctl(sadfd, SAD_SAP, &push) < 0) {
    361 		int error = errno;
    362 
    363 		retcode = 1;
    364 		(void) fprintf(stderr, gettext("%s: ERROR: Could not remove "
    365 		    "autopush information\n"), Cmdp);
    366 		switch (error) {
    367 		case EPERM:
    368 			(void) fprintf(stderr, gettext("%s: ERROR: You don't "
    369 			    "have permission to remove autopush "
    370 			    "information\n"), Cmdp);
    371 			break;
    372 
    373 		case EINVAL:
    374 			if ((min != 0) && (ioctl(sadfd, SAD_GAP, &push) == 0) &&
    375 			    (push.sap_cmd == SAP_ALL))
    376 				(void) fprintf(stderr, gettext("%s: ERROR: "
    377 				    "When removing an entry for ALL minors, "
    378 				    "minor must be set to 0\n"), Cmdp);
    379 			else
    380 				(void) fprintf(stderr, gettext("%s: ERROR: "
    381 				    "Invalid major device number\n"), Cmdp);
    382 			break;
    383 
    384 		case ENODEV:
    385 			(void) fprintf(stderr, gettext("%s: ERROR: Major/minor "
    386 			    "not configured for autopush\n"), Cmdp);
    387 			break;
    388 
    389 		case ERANGE:
    390 			(void) fprintf(stderr, gettext("%s: ERROR: minor must "
    391 			    "be set to begining of range when clearing\n"),
    392 			    Cmdp);
    393 			break;
    394 
    395 		default:
    396 			(void) fprintf(stderr, gettext("%s: ERROR: "), Cmdp);
    397 			(void) fprintf(stderr, "%s\n", strerror(error));
    398 			break;
    399 		} /* switch */
    400 	}
    401 	return (retcode);
    402 }
    403 
    404 /*
    405  * get_info():
    406  *	get autopush configuration information.
    407  */
    408 static int
    409 get_info(major_t maj, minor_t min)
    410 {
    411 	struct strapush push;	/* configuration information */
    412 	int		i;	/* counter */
    413 	int		sadfd;	/* file descriptor to SAD driver */
    414 
    415 	if ((sadfd = open(USERDEV, O_RDWR)) < 0) {
    416 		(void) fprintf(stderr, Openerr, Cmdp, USERDEV);
    417 		perror("");
    418 		return (1);
    419 	}
    420 	push.sap_major = maj;
    421 	push.sap_minor = min;
    422 
    423 	if (ioctl(sadfd, SAD_GAP, &push) < 0) {
    424 		int error = errno;
    425 
    426 		(void) fprintf(stderr, gettext("%s: ERROR: Could not get "
    427 		    "autopush information\n"), Cmdp);
    428 		switch (error) {
    429 		case EINVAL:
    430 			(void) fprintf(stderr, gettext("%s: ERROR: Invalid "
    431 			    "major device number\n"), Cmdp);
    432 			break;
    433 
    434 		case ENOSTR:
    435 			(void) fprintf(stderr, gettext("%s: ERROR: Major "
    436 			    "device is not a STREAMS driver\n"), Cmdp);
    437 			break;
    438 
    439 		case ENODEV:
    440 			(void) fprintf(stderr, gettext("%s: ERROR: Major/minor "
    441 			    "not configured for autopush\n"), Cmdp);
    442 			break;
    443 
    444 		default:
    445 			(void) fprintf(stderr, gettext("%s: ERROR: "), Cmdp);
    446 			(void) fprintf(stderr, "%s\n", strerror(error));
    447 			break;
    448 		} /* switch */
    449 		return (1);
    450 	}
    451 	(void) printf(OHEADER);
    452 	switch (push.sap_cmd) {
    453 	case SAP_ONE:
    454 		(void) printf(OFORMAT1_ONE, push.sap_major, push.sap_minor);
    455 		break;
    456 
    457 	case SAP_RANGE:
    458 		(void) printf(OFORMAT1_RANGE, push.sap_major, push.sap_minor,
    459 		    push.sap_lastminor);
    460 		break;
    461 
    462 	case SAP_ALL:
    463 		(void) printf(OFORMAT1_ALL, push.sap_major);
    464 		break;
    465 
    466 	default:
    467 		(void) fprintf(stderr,
    468 		    gettext("%s: ERROR: Unknown configuration type\n"), Cmdp);
    469 		return (1);
    470 	}
    471 
    472 	for (i = 0; i < push.sap_npush; i++) {
    473 
    474 		(void) printf("%s", push.sap_list[i]);
    475 
    476 		if (push.sap_anchor == (i + 1))
    477 			(void) printf(" %s", AP_ANCHOR);
    478 
    479 		if (i < push.sap_npush - 1)
    480 			(void) printf(" ");
    481 
    482 	}
    483 
    484 	(void) printf("\n");
    485 	return (0);
    486 }
    487 
    488 /*
    489  * is_white_space():
    490  *	Return 1 if buffer is all white space.
    491  *	Return 0 otherwise.
    492  */
    493 static int
    494 is_white_space(char *bufp)
    495 {
    496 	while (*bufp) {
    497 		if (!isspace(*bufp))
    498 			return (0);
    499 		bufp++;
    500 	}
    501 	return (1);
    502 }
    503 
    504 /*
    505  * parse_line():
    506  *	Parse input line from file and report any errors found.  Fill
    507  *	strapush structure along the way.  Returns 1 if the line has
    508  *	errors and 0 if the line is well-formed.  Another hidden
    509  *	dependency on MAXAPUSH. `linep' is the input buffer, `lineno'
    510  *	is the current line number, and `namep' is the filename.
    511  */
    512 static int
    513 parse_line(char *linep, int lineno, char *namep, struct strapush *pushp)
    514 {
    515 	char		*wp;		/* word pointer */
    516 	char		*cp;		/* character pointer */
    517 	int		midx;		/* module index */
    518 	int		npush;		/* number of modules to push */
    519 	char		c;
    520 	major_t		major_num;
    521 
    522 	pushp->sap_anchor = 0;		/* by default, no anchor */
    523 
    524 	/*
    525 	 * Find the major device number.
    526 	 */
    527 	for (wp = linep; isspace(*wp); wp++)
    528 		;
    529 	for (cp = wp; !isspace(*cp); cp++)
    530 		;
    531 	if (!isspace(*cp)) {
    532 		(void) fprintf(stderr, Badline, Cmdp, namep, lineno);
    533 		return (1);
    534 	}
    535 	c = *cp;
    536 	*cp = '\0';
    537 	if (modctl(MODGETMAJBIND, wp, strlen(wp) + 1, &major_num) != 0) {
    538 		(void) fprintf(stderr, Badline, Cmdp, namep, lineno);
    539 		return (1);
    540 	}
    541 	*cp = c;
    542 	pushp->sap_major = major_num;
    543 
    544 	/*
    545 	 * Find the minor device number.  Must handle negative values here.
    546 	 */
    547 	for (wp = cp; isspace(*wp); wp++)
    548 		;
    549 	for (cp = wp; (isdigit(*cp) || (*cp == MINUS)); cp++)
    550 		;
    551 	if (!isspace(*cp)) {
    552 		(void) fprintf(stderr, Badline, Cmdp, namep, lineno);
    553 		return (1);
    554 	}
    555 	pushp->sap_minor = (minor_t)atol(wp);
    556 
    557 	/*
    558 	 * Find the lastminor.
    559 	 */
    560 	for (wp = cp; isspace(*wp); wp++)
    561 		;
    562 	for (cp = wp; isdigit(*cp); cp++)
    563 		;
    564 	if (!isspace(*cp)) {
    565 		(void) fprintf(stderr, Badline, Cmdp, namep, lineno);
    566 		return (1);
    567 	}
    568 	pushp->sap_lastminor = (minor_t)atol(wp);
    569 
    570 	/*
    571 	 * Read the list of module names.
    572 	 */
    573 	npush = 0;
    574 	while ((npush < MAXAPUSH) && (*cp)) {
    575 
    576 		while (isspace(*cp))
    577 			cp++;
    578 
    579 		if (strncasecmp(cp, AP_ANCHOR, sizeof (AP_ANCHOR) - 1) == 0) {
    580 			if (pushp->sap_anchor != 0) {
    581 				(void) fprintf(stderr,
    582 				    gettext("%s: ERROR: File %s: more than "
    583 				    "one anchor in line, line %d ignored\n"),
    584 				    Cmdp, namep, lineno);
    585 				return (1);
    586 			}
    587 			if (npush == 0)
    588 				(void) fprintf(stderr,
    589 				    gettext("%s: WARNING: File %s: anchor at "
    590 				    "beginning of stream on line %d ignored\n"),
    591 				    Cmdp, namep, lineno);
    592 			pushp->sap_anchor = npush;
    593 			cp += sizeof (AP_ANCHOR) - 1;
    594 			continue;
    595 		}
    596 
    597 		for (midx = 0; !isspace(*cp) && *cp; midx++) {
    598 			if (midx == FMNAMESZ) {
    599 				(void) fprintf(stderr, gettext("%s: ERROR: "
    600 				    "File %s: module name too long, line %d "
    601 				    "ignored\n"), Cmdp, namep, lineno);
    602 				return (1);
    603 			}
    604 			pushp->sap_list[npush][midx] = *cp++;
    605 		}
    606 
    607 		if (midx > 0) {
    608 			pushp->sap_list[npush][midx] = '\0';
    609 			npush++;
    610 		}
    611 	}
    612 	pushp->sap_npush = npush;
    613 
    614 	/*
    615 	 * We have everything we want from the line.
    616 	 * Now make sure there is no extra garbage on the line.
    617 	 */
    618 	while (isspace(*cp))
    619 		cp++;
    620 	if (*cp) {
    621 		(void) fprintf(stderr,
    622 		    gettext("%s: ERROR: File %s: too many modules, line %d "
    623 		    "ignored\n"), Cmdp, namep, lineno);
    624 		return (1);
    625 	}
    626 	return (0);
    627 }
    628