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