rem_drv.c revision 10585:e59699fcc4b6
11541Srgrimes/*
21541Srgrimes * CDDL HEADER START
31541Srgrimes *
41541Srgrimes * The contents of this file are subject to the terms of the
51541Srgrimes * Common Development and Distribution License (the "License").
61541Srgrimes * You may not use this file except in compliance with the License.
71541Srgrimes *
81541Srgrimes * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91541Srgrimes * or http://www.opensolaris.org/os/licensing.
101541Srgrimes * See the License for the specific language governing permissions
111541Srgrimes * and limitations under the License.
121541Srgrimes *
131541Srgrimes * When distributing Covered Code, include this CDDL HEADER in each
141541Srgrimes * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151541Srgrimes * If applicable, add the following below this CDDL HEADER, with the
161541Srgrimes * fields enclosed by brackets "[]" replaced with your own identifying
171541Srgrimes * information: Portions Copyright [yyyy] [name of copyright owner]
181541Srgrimes *
191541Srgrimes * CDDL HEADER END
201541Srgrimes */
211541Srgrimes/*
221541Srgrimes * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
231541Srgrimes * Use is subject to license terms.
241541Srgrimes */
251541Srgrimes
261541Srgrimes#include <stdio.h>
271541Srgrimes#include <stdlib.h>
281541Srgrimes#include <unistd.h>
291541Srgrimes#include <errno.h>
301541Srgrimes#include <libintl.h>
311541Srgrimes#include <string.h>
321541Srgrimes#include <fcntl.h>
3314478Shsu#include <sys/buf.h>
3450477Speter#include <sys/stat.h>
351541Srgrimes#include <sys/wait.h>
361541Srgrimes#include <limits.h>
3745260Sbde#include <malloc.h>
385052Sbde#include <locale.h>
392165Spaul#include <ftw.h>
4055205Speter#include <sys/types.h>
4118444Sbde#include <sys/mkdev.h>
4218444Sbde#include <sys/modctl.h>
4318429Sbde#include <sys/instance.h>
4418429Sbde#include <libdevinfo.h>
451541Srgrimes#include <zone.h>
461541Srgrimes
471541Srgrimes#include "addrem.h"
481541Srgrimes#include "errmsg.h"
491541Srgrimes
501541Srgrimes#define	FT_DEPTH	15	/* device tree depth for nftw() */
511541Srgrimes
521541Srgrimesstatic void usage(void);
531541Srgrimesstatic void cleanup_devfs_attributes(char *, char *);
541541Srgrimes
551541Srgrimesint
561541Srgrimesmain(int argc, char *argv[])
571541Srgrimes{
581541Srgrimes	int opt;
594027Sphk	char *basedir = NULL, *driver_name = NULL;
601541Srgrimes	int server = 0, mod_unloaded = 0;
611541Srgrimes	int modid, found;
621541Srgrimes	char maj_num[MAX_STR_MAJOR + 1];
631541Srgrimes	int cleanup = 0;
6436735Sdfr	int err;
6537601Sdfr	int n_flag = 0;
6637601Sdfr
6736735Sdfr	(void) setlocale(LC_ALL, "");
6836735Sdfr#if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
6984587Sdfr#define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
7089467Sbde#endif
7189467Sbde	(void) textdomain(TEXT_DOMAIN);
7284587Sdfr
7384587Sdfr	/*  must be run by root */
7492196Sjake
7592196Sjake	if (getuid() != 0) {
7692196Sjake		(void) fprintf(stderr, gettext(ERR_NOT_ROOT));
7792196Sjake		exit(1);
7892196Sjake	}
791541Srgrimes
8089467Sbde	while ((opt = getopt(argc, argv, "b:Cn")) != -1) {
811541Srgrimes		switch (opt) {
821541Srgrimes		case 'b' :
831541Srgrimes			server = 1;
8489467Sbde			basedir = calloc(strlen(optarg) + 1, 1);
851541Srgrimes			if (basedir == NULL) {
861541Srgrimes				(void) fprintf(stderr, gettext(ERR_NO_MEM));
8714478Shsu				exit(1);
881541Srgrimes			}
891541Srgrimes			(void) strcat(basedir, optarg);
901541Srgrimes			break;
911541Srgrimes		case 'C':
925052Sbde			cleanup = 1;
935052Sbde			break;
945052Sbde		case 'n':
951541Srgrimes			n_flag = 1;
961541Srgrimes			break;
971541Srgrimes		case '?' :
9814478Shsu			usage();
9914478Shsu			exit(1);
10014478Shsu		}
10114478Shsu	}
10214478Shsu
10314478Shsu	if (argv[optind] != NULL) {
1041541Srgrimes		driver_name = calloc(strlen(argv[optind]) + 1, 1);
1051541Srgrimes		if (driver_name == NULL) {
1061541Srgrimes			(void) fprintf(stderr, gettext(ERR_NO_MEM));
1071541Srgrimes			exit(1);
10842264Sjkh
1091541Srgrimes		}
1101541Srgrimes		(void) strcat(driver_name, argv[optind]);
1111541Srgrimes		/*
1128876Srgrimes		 * check for extra args
1131541Srgrimes		 */
1141541Srgrimes		if ((optind + 1) != argc) {
1151541Srgrimes			usage();
1168876Srgrimes			exit(1);
1178876Srgrimes		}
1181541Srgrimes
1191541Srgrimes	} else {
1201541Srgrimes		usage();
12114478Shsu		exit(1);
1221541Srgrimes	}
12314478Shsu
12414478Shsu	if (getzoneid() != GLOBAL_ZONEID) {
12514478Shsu		(void) fprintf(stderr, gettext(ERR_NOT_GLOBAL_ZONE));
12614478Shsu		exit(1);
12714478Shsu	}
12814478Shsu
12914478Shsu	/* set up add_drv filenames */
1301541Srgrimes	if ((build_filenames(basedir)) == ERROR) {
13114478Shsu		exit(1);
13214478Shsu	}
13314478Shsu
13414478Shsu	/* must be only running version of add_drv/mod_drv/rem_drv */
1351541Srgrimes	enter_lock();
13614478Shsu
13714478Shsu	if ((check_perms_aliases(1, 1)) == ERROR)
1381541Srgrimes		err_exit();
13914478Shsu
14014478Shsu	if ((check_name_to_major(R_OK | W_OK)) == ERROR)
1411541Srgrimes		err_exit();
14214478Shsu
1431541Srgrimes	/* look up the major number of the driver being removed. */
1441541Srgrimes	if ((found = get_major_no(driver_name, name_to_major)) == ERROR) {
1451541Srgrimes		(void) fprintf(stderr, gettext(ERR_MAX_MAJOR), name_to_major);
1461541Srgrimes		err_exit();
1471541Srgrimes	}
14814478Shsu	if (found == UNIQUE) {
14914478Shsu		(void) fprintf(stderr, gettext(ERR_NOT_INSTALLED),
15014478Shsu		    driver_name);
15114478Shsu		err_exit();
15214478Shsu	}
15314478Shsu
15414478Shsu	if (n_flag == 0 && !server) {
15514478Shsu		mod_unloaded = 1;
15614478Shsu
15714478Shsu		/* get the module id for this driver */
15814478Shsu		get_modid(driver_name, &modid);
15914478Shsu
1601541Srgrimes		/* module is installed */
16114478Shsu		if (modid != -1) {
16214478Shsu			if (modctl(MODUNLOAD, modid) < 0) {
16314478Shsu				perror(NULL);
16414478Shsu				(void) fprintf(stderr, gettext(ERR_MODUN),
16514478Shsu				    driver_name);
16614478Shsu				mod_unloaded = 0;
16714478Shsu			}
1681541Srgrimes		}
16914478Shsu		/* unload driver.conf file */
1701541Srgrimes		if (modctl(MODUNLOADDRVCONF, (major_t)found) < 0) {
17114478Shsu			perror(NULL);
17214478Shsu			(void) fprintf(stderr,
17314478Shsu			    gettext("cannot unload %s.conf\n"), driver_name);
1741541Srgrimes		}
1751541Srgrimes	}
17614478Shsu
17714478Shsu	if (mod_unloaded && (modctl(MODREMMAJBIND, (major_t)found) < 0)) {
17814478Shsu		perror(NULL);
17983421Sobrien		(void) fprintf(stderr, gettext(ERR_MODREMMAJ), found);
18014478Shsu	}
18114478Shsu	/*
18214478Shsu	 * add driver to rem_name_to_major; if this fails, don`t
18314478Shsu	 * delete from name_to_major
18414478Shsu	 */
18566066Sphk	(void) sprintf(maj_num, "%d", found);
1861541Srgrimes
1871541Srgrimes	if (append_to_file(driver_name, maj_num,
18865921Sphk	    rem_name_to_major, ' ', " ", 0) == ERROR) {
18992719Salfred		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
19065921Sphk		    rem_name_to_major);
19189467Sbde		err_exit();
19289467Sbde	}
19365921Sphk
19465921Sphk	/*
19565921Sphk	 * If removing the driver from the running system, notify
19665921Sphk	 * kernel dynamically to remove minor perm entries.
19765921Sphk	 */
19865921Sphk	if ((n_flag == 0) &&
19965921Sphk	    (basedir == NULL || (strcmp(basedir, "/") == 0))) {
20065921Sphk		err = devfs_rm_minor_perm(driver_name, log_minorperm_error);
20165921Sphk		if (err != 0) {
20265921Sphk			(void) fprintf(stderr, gettext(ERR_UPDATE_PERM),
20365921Sphk			    driver_name, err);
2041541Srgrimes		}
2051541Srgrimes	}
2061541Srgrimes
2071541Srgrimes	/*
2081541Srgrimes	 * delete references to driver in add_drv/rem_drv database
2091541Srgrimes	 */
2101541Srgrimes	remove_entry(CLEAN_ALL, driver_name);
2111541Srgrimes
2121541Srgrimes	/*
2131541Srgrimes	 * Optionally clean up any dangling devfs shadow nodes for
2141541Srgrimes	 * this driver so that, in the event the driver is re-added
2151541Srgrimes	 * to the system, newly created nodes won't incorrectly
2161541Srgrimes	 * pick up these stale shadow node permissions.
2171541Srgrimes	 */
2181541Srgrimes	if ((n_flag == 0) && cleanup) {
2191541Srgrimes		if ((basedir == NULL || (strcmp(basedir, "/") == 0))) {
2201541Srgrimes			err = modctl(MODREMDRVCLEANUP, driver_name, 0, NULL);
2211541Srgrimes			if (err != 0) {
2221541Srgrimes				(void) fprintf(stderr,
2231541Srgrimes				    gettext(ERR_REMDRV_CLEANUP),
2241541Srgrimes				    driver_name, err);
2251541Srgrimes			}
2261541Srgrimes		} else if (strcmp(basedir, "/") != 0) {
22715571Sasami			cleanup_devfs_attributes(basedir, driver_name);
22845260Sbde		}
22949617Simp	}
23093318Sgrog
2311541Srgrimes	exit_unlock();
23250551Sphk
23345260Sbde	return (NOERR);
23416363Sasami}
23516363Sasami
2361541Srgrimes/*
2371541Srgrimes * Optionally remove attribute nodes for a driver when
2381541Srgrimes * removing drivers on a mounted root image.  Useful
2391541Srgrimes * when reprovisioning a machine to return to default
2401541Srgrimes * permission/ownership settings if the driver is
2411541Srgrimes * re-installed.
2421541Srgrimes */
2431541Srgrimestypedef struct cleanup_arg {
2441541Srgrimes	char	*ca_basedir;
2451541Srgrimes	char	*ca_drvname;
2461541Srgrimes} cleanup_arg_t;
2471541Srgrimes
2481541Srgrimes
24915571Sasami/*
25033469Sjkh * Callback to remove a minor node for a device
25149617Simp */
25293318Sgrog/*ARGSUSED*/
25393318Sgrogstatic int
25493318Sgrogcleanup_minor_walker(void *cb_arg, const char *minor_path)
25514478Shsu{
2561541Srgrimes	if (unlink(minor_path) == -1) {
2571541Srgrimes		(void) fprintf(stderr, "rem_drv: error removing %s - %s\n",
2581541Srgrimes		    minor_path, strerror(errno));
2591541Srgrimes	}
2601541Srgrimes	return (DI_WALK_CONTINUE);
2611541Srgrimes}
2621541Srgrimes
2631541Srgrimes/*
2641541Srgrimes * Callback for each device registered in the binding file (path_to_inst)
2651541Srgrimes */
2661541Srgrimes/*ARGSUSED*/
2671541Srgrimesstatic int
2681541Srgrimescleanup_device_walker(void *cb_arg, const char *inst_path,
2691541Srgrimes    int inst_number, const char *inst_driver)
2701541Srgrimes{
2711541Srgrimes	char path[MAXPATHLEN];
2721541Srgrimes	cleanup_arg_t *arg = (cleanup_arg_t *)cb_arg;
2731541Srgrimes	int rv = DI_WALK_CONTINUE;
2741541Srgrimes
2751541Srgrimes	if (strcmp(inst_driver, arg->ca_drvname) == 0) {
2761541Srgrimes		if (snprintf(path, MAXPATHLEN, "%s/devices%s",
2771541Srgrimes		    arg->ca_basedir, inst_path) < MAXPATHLEN) {
2781541Srgrimes			rv = devfs_walk_minor_nodes(path,
27945260Sbde			    cleanup_minor_walker, NULL);
28093318Sgrog		}
28145260Sbde	}
2821541Srgrimes	return (rv);
2831541Srgrimes}
2841541Srgrimes
2851541Srgrimesstatic void
2861541Srgrimescleanup_devfs_attributes(char *basedir, char *driver_name)
2871541Srgrimes{
2881541Srgrimes	cleanup_arg_t arg;
2891541Srgrimes	char binding_path[MAXPATHLEN+1];
2901541Srgrimes
2911541Srgrimes	(void) snprintf(binding_path, MAXPATHLEN,
2921541Srgrimes	    "%s%s", basedir, INSTANCE_FILE);
2931541Srgrimes
2941541Srgrimes	arg.ca_basedir = basedir;
2951541Srgrimes	arg.ca_drvname = driver_name;
2961541Srgrimes	(void) devfs_parse_binding_file(binding_path,
2971541Srgrimes	    cleanup_device_walker, (void *)&arg);
29844948Sgrog}
29993318Sgrog
30093318Sgrogstatic void
30193318Sgrogusage()
30293318Sgrog{
30393318Sgrog	(void) fprintf(stderr, gettext(REM_USAGE1));
30493318Sgrog}
30593318Sgrog