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