11676Sjpk /* 21676Sjpk * CDDL HEADER START 31676Sjpk * 41676Sjpk * The contents of this file are subject to the terms of the 51676Sjpk * Common Development and Distribution License (the "License"). 61676Sjpk * You may not use this file except in compliance with the License. 71676Sjpk * 81676Sjpk * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91676Sjpk * or http://www.opensolaris.org/os/licensing. 101676Sjpk * See the License for the specific language governing permissions 111676Sjpk * and limitations under the License. 121676Sjpk * 131676Sjpk * When distributing Covered Code, include this CDDL HEADER in each 141676Sjpk * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151676Sjpk * If applicable, add the following below this CDDL HEADER, with the 161676Sjpk * fields enclosed by brackets "[]" replaced with your own identifying 171676Sjpk * information: Portions Copyright [yyyy] [name of copyright owner] 181676Sjpk * 191676Sjpk * CDDL HEADER END 201676Sjpk */ 211676Sjpk 221676Sjpk /* 23*11529SJan.Parcel@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 241676Sjpk * Use is subject to license terms. 251676Sjpk */ 261676Sjpk 271676Sjpk /* 281676Sjpk * Device allocation related work. 291676Sjpk */ 301676Sjpk 311676Sjpk #include <stdio.h> 321676Sjpk #include <stdlib.h> 331676Sjpk #include <errno.h> 341676Sjpk #include <string.h> 351676Sjpk #include <strings.h> 361676Sjpk #include <unistd.h> 371676Sjpk #include <fcntl.h> 381676Sjpk #include <sys/types.h> 391676Sjpk #include <sys/stat.h> 401676Sjpk #include <sys/dkio.h> 411676Sjpk #include <sys/wait.h> 421676Sjpk #include <bsm/devalloc.h> 431676Sjpk 441676Sjpk #define DEALLOCATE "/usr/sbin/deallocate" 451676Sjpk #define MKDEVALLOC "/usr/sbin/mkdevalloc" 461676Sjpk 47*11529SJan.Parcel@Sun.COM static char *_update_dev(deventry_t *, int, const char *, char *, char *); 481676Sjpk static int _make_db(); 49*11529SJan.Parcel@Sun.COM extern int event_driven; 501676Sjpk 511676Sjpk 521676Sjpk /* 531676Sjpk * _da_check_for_usb 544400Saj * returns 1 if device pointed by 'link' is a removable hotplugged disk, 551676Sjpk * else returns 0. 561676Sjpk */ 571676Sjpk int 581676Sjpk _da_check_for_usb(char *link, char *root_dir) 591676Sjpk { 601676Sjpk int fd = -1; 611676Sjpk int len, dstsize; 621676Sjpk int removable = 0; 634400Saj int hotpluggable = 0; 641676Sjpk char *p = NULL; 654400Saj char path[MAXPATHLEN + 4]; 664400Saj char rpath[MAXPATHLEN + 4]; /* for ",raw" */ 671676Sjpk 681676Sjpk dstsize = sizeof (path); 691676Sjpk if (strcmp(root_dir, "") != 0) { 701676Sjpk if (strlcat(path, root_dir, dstsize) >= dstsize) 711676Sjpk return (0); 721676Sjpk len = strlen(path); 731676Sjpk } else { 741676Sjpk len = 0; 751676Sjpk } 764400Saj (void) snprintf(path, dstsize - len, "%s", link); 774400Saj if ((p = realpath(path, rpath)) == NULL) { 784400Saj p = path; 791676Sjpk } else { 804400Saj if (strstr(link, "rdsk")) { 814400Saj p = rpath; 824400Saj } else { 834400Saj (void) snprintf(path, dstsize, "%s%s", rpath, ",raw"); 844400Saj p = path; 854400Saj } 861676Sjpk } 874400Saj if ((fd = open(p, O_RDONLY | O_NONBLOCK)) < 0) 881676Sjpk return (0); 891676Sjpk (void) ioctl(fd, DKIOCREMOVABLE, &removable); 904400Saj (void) ioctl(fd, DKIOCHOTPLUGGABLE, &hotpluggable); 911676Sjpk (void) close(fd); 921676Sjpk 934400Saj if (removable && hotpluggable) 944400Saj return (1); 954400Saj 964400Saj return (0); 971676Sjpk } 981676Sjpk 991676Sjpk /* 1001676Sjpk * _reset_devalloc 1011676Sjpk * If device allocation is being turned on, creates device_allocate 1021676Sjpk * device_maps if they do not exist. 1031676Sjpk * Puts DEVICE_ALLOCATION=ON/OFF in device_allocate to indicate if 1041676Sjpk * device allocation is on/off. 1051676Sjpk */ 1061676Sjpk void 1071676Sjpk _reset_devalloc(int action) 1081676Sjpk { 1091676Sjpk da_args dargs; 1101676Sjpk 1111676Sjpk if (action == DA_ON) 1121676Sjpk (void) _make_db(); 1131676Sjpk else if ((action == DA_OFF) && (open(DEVALLOC, O_RDONLY) == -1)) 1141676Sjpk return; 1151676Sjpk 1161676Sjpk if (action == DA_ON) 1171676Sjpk dargs.optflag = DA_ON; 1181676Sjpk else if (action == DA_OFF) 1191676Sjpk dargs.optflag = DA_OFF | DA_ALLOC_ONLY; 1201676Sjpk 1211676Sjpk dargs.rootdir = NULL; 1221676Sjpk dargs.devnames = NULL; 1231676Sjpk dargs.devinfo = NULL; 1241676Sjpk 1251676Sjpk (void) da_update_device(&dargs); 1261676Sjpk } 1271676Sjpk 1281676Sjpk /* 1291676Sjpk * _make_db 1301676Sjpk * execs /usr/sbin/mkdevalloc to create device_allocate and 1311676Sjpk * device_maps. 1321676Sjpk */ 1331676Sjpk static int 1341676Sjpk _make_db() 1351676Sjpk { 1361676Sjpk int status; 1371676Sjpk pid_t pid, wpid; 1381676Sjpk 1391676Sjpk pid = vfork(); 1401676Sjpk switch (pid) { 1411676Sjpk case -1: 1421676Sjpk return (1); 1431676Sjpk case 0: 1441676Sjpk if (execl(MKDEVALLOC, MKDEVALLOC, DA_IS_LABELED, NULL) == -1) 1451676Sjpk exit((errno == ENOENT) ? 0 : 1); 1461676Sjpk default: 1471676Sjpk for (;;) { 1481676Sjpk wpid = waitpid(pid, &status, 0); 1491676Sjpk if (wpid == (pid_t)-1) { 1501676Sjpk if (errno == EINTR) 1511676Sjpk continue; 1521676Sjpk else 1531676Sjpk return (1); 1541676Sjpk } else { 1551676Sjpk break; 1561676Sjpk } 1571676Sjpk } 1581676Sjpk break; 1591676Sjpk } 1601676Sjpk 1611676Sjpk return ((WIFEXITED(status) == 0) ? 1 : WEXITSTATUS(status)); 1621676Sjpk } 1631676Sjpk 1641676Sjpk 1651676Sjpk /* 1661676Sjpk * _update_devalloc_db 1671676Sjpk * Forms allocatable device entries to be written to device_allocate and 1681676Sjpk * device_maps. 169*11529SJan.Parcel@Sun.COM * 170*11529SJan.Parcel@Sun.COM * Or finds the correct entry to remove, and removes it. 171*11529SJan.Parcel@Sun.COM * 172*11529SJan.Parcel@Sun.COM * Note: devname is a /devices link in the REMOVE case. 1731676Sjpk */ 1741676Sjpk /* ARGSUSED */ 1751676Sjpk void 1761676Sjpk _update_devalloc_db(devlist_t *devlist, int devflag, int action, char *devname, 1771676Sjpk char *root_dir) 1781676Sjpk { 1791676Sjpk int i; 1801676Sjpk deventry_t *entry = NULL, *dentry = NULL; 181*11529SJan.Parcel@Sun.COM char *typestring; 182*11529SJan.Parcel@Sun.COM char *nickname; /* typestring + instance */ 1831676Sjpk 1841676Sjpk if (action == DA_ADD) { 1851676Sjpk for (i = 0; i < DA_COUNT; i++) { 1861676Sjpk switch (i) { 1871676Sjpk case 0: 1881676Sjpk dentry = devlist->audio; 1891676Sjpk break; 1901676Sjpk case 1: 1911676Sjpk dentry = devlist->cd; 1921676Sjpk break; 1931676Sjpk case 2: 1941676Sjpk dentry = devlist->floppy; 1951676Sjpk break; 1961676Sjpk case 3: 1971676Sjpk dentry = devlist->tape; 1981676Sjpk break; 1991676Sjpk case 4: 2001676Sjpk dentry = devlist->rmdisk; 2011676Sjpk break; 2021676Sjpk default: 2031676Sjpk return; 2041676Sjpk } 2051676Sjpk if (dentry) 206*11529SJan.Parcel@Sun.COM (void) _update_dev(dentry, action, NULL, NULL, 207*11529SJan.Parcel@Sun.COM NULL); 2081676Sjpk } 2091676Sjpk } else if (action == DA_REMOVE) { 210*11529SJan.Parcel@Sun.COM if (devflag & DA_AUDIO) { 2111676Sjpk dentry = devlist->audio; 212*11529SJan.Parcel@Sun.COM typestring = DA_AUDIO_TYPE; 213*11529SJan.Parcel@Sun.COM } else if (devflag & DA_CD) { 2141676Sjpk dentry = devlist->cd; 215*11529SJan.Parcel@Sun.COM typestring = DA_CD_TYPE; 216*11529SJan.Parcel@Sun.COM } else if (devflag & DA_FLOPPY) { 2171676Sjpk dentry = devlist->floppy; 218*11529SJan.Parcel@Sun.COM typestring = DA_FLOPPY_TYPE; 219*11529SJan.Parcel@Sun.COM } else if (devflag & DA_TAPE) { 2201676Sjpk dentry = devlist->tape; 221*11529SJan.Parcel@Sun.COM typestring = DA_TAPE_TYPE; 222*11529SJan.Parcel@Sun.COM } else if (devflag & DA_RMDISK) { 2231676Sjpk dentry = devlist->rmdisk; 224*11529SJan.Parcel@Sun.COM typestring = DA_RMDISK_TYPE; 225*11529SJan.Parcel@Sun.COM } else 2261676Sjpk return; 2271676Sjpk 228*11529SJan.Parcel@Sun.COM if (event_driven) { 229*11529SJan.Parcel@Sun.COM nickname = _update_dev(NULL, action, typestring, NULL, 230*11529SJan.Parcel@Sun.COM devname); 231*11529SJan.Parcel@Sun.COM 232*11529SJan.Parcel@Sun.COM if (nickname != NULL) { 233*11529SJan.Parcel@Sun.COM (void) da_rm_list_entry(devlist, devname, 234*11529SJan.Parcel@Sun.COM devflag, nickname); 235*11529SJan.Parcel@Sun.COM free(nickname); 236*11529SJan.Parcel@Sun.COM } 237*11529SJan.Parcel@Sun.COM return; 238*11529SJan.Parcel@Sun.COM } 239*11529SJan.Parcel@Sun.COM /* 240*11529SJan.Parcel@Sun.COM * Not reached as of now, could be reached if devfsadm is 241*11529SJan.Parcel@Sun.COM * enhanced to clean up devalloc database more thoroughly. 242*11529SJan.Parcel@Sun.COM * Will not reliably match for event-driven removes 243*11529SJan.Parcel@Sun.COM */ 2441676Sjpk for (entry = dentry; entry != NULL; entry = entry->next) { 2451676Sjpk if (strcmp(entry->devinfo.devname, devname) == 0) 2461676Sjpk break; 2471676Sjpk } 248*11529SJan.Parcel@Sun.COM (void) _update_dev(entry, action, NULL, devname, NULL); 2491676Sjpk } 2501676Sjpk } 2511676Sjpk 252*11529SJan.Parcel@Sun.COM /* 253*11529SJan.Parcel@Sun.COM * _update_dev: Update device_allocate and/or device_maps files 254*11529SJan.Parcel@Sun.COM * 255*11529SJan.Parcel@Sun.COM * If adding a device: 256*11529SJan.Parcel@Sun.COM * dentry: A linked list of allocatable devices 257*11529SJan.Parcel@Sun.COM * action: DA_ADD or DA_REMOVE 258*11529SJan.Parcel@Sun.COM * devtype: type of device linked list to update on removal 259*11529SJan.Parcel@Sun.COM * devname: short name (i.e. rmdisk5, cdrom0) of device if known 260*11529SJan.Parcel@Sun.COM * rm_link: name of real /device from hot_cleanup 261*11529SJan.Parcel@Sun.COM * 262*11529SJan.Parcel@Sun.COM * If the action is ADD or if the action is triggered by an event 263*11529SJan.Parcel@Sun.COM * from syseventd, read the files FIRST and treat their data as 264*11529SJan.Parcel@Sun.COM * more-accurate than the dentry list, adjusting dentry contents if needed. 265*11529SJan.Parcel@Sun.COM * 266*11529SJan.Parcel@Sun.COM * For DA_ADD, try to add each device in the list to the files. 267*11529SJan.Parcel@Sun.COM * 268*11529SJan.Parcel@Sun.COM * If the action is DA_REMOVE and not a hotplug remove, adjust the files 269*11529SJan.Parcel@Sun.COM * as indicated by the linked list. 270*11529SJan.Parcel@Sun.COM * 271*11529SJan.Parcel@Sun.COM * RETURNS: 272*11529SJan.Parcel@Sun.COM * If we successfully remove a device from the files, returns 273*11529SJan.Parcel@Sun.COM * a char * to strdup'd devname of the device removed. 274*11529SJan.Parcel@Sun.COM * 275*11529SJan.Parcel@Sun.COM * The caller is responsible for freeing the return value. 276*11529SJan.Parcel@Sun.COM * 277*11529SJan.Parcel@Sun.COM * NULL for all other cases, both success and failure. 278*11529SJan.Parcel@Sun.COM * 279*11529SJan.Parcel@Sun.COM */ 280*11529SJan.Parcel@Sun.COM static char * 281*11529SJan.Parcel@Sun.COM _update_dev(deventry_t *dentry, int action, const char *devtype, char *devname, 282*11529SJan.Parcel@Sun.COM char *rm_link) 2831676Sjpk { 2841676Sjpk da_args dargs; 2851676Sjpk deventry_t newentry, *entry; 286*11529SJan.Parcel@Sun.COM int status; 2871676Sjpk 2881676Sjpk dargs.rootdir = NULL; 2891676Sjpk dargs.devnames = NULL; 2901676Sjpk 291*11529SJan.Parcel@Sun.COM if (event_driven) 292*11529SJan.Parcel@Sun.COM dargs.optflag = DA_EVENT; 293*11529SJan.Parcel@Sun.COM else 294*11529SJan.Parcel@Sun.COM dargs.optflag = 0; 295*11529SJan.Parcel@Sun.COM 2961676Sjpk if (action == DA_ADD) { 297*11529SJan.Parcel@Sun.COM dargs.optflag |= DA_ADD; 298*11529SJan.Parcel@Sun.COM /* 299*11529SJan.Parcel@Sun.COM * Add Events do not have enough information to overrride the 300*11529SJan.Parcel@Sun.COM * existing file contents. 301*11529SJan.Parcel@Sun.COM */ 302*11529SJan.Parcel@Sun.COM 3031676Sjpk for (entry = dentry; entry != NULL; entry = entry->next) { 3041676Sjpk dargs.devinfo = &(entry->devinfo); 3051676Sjpk (void) da_update_device(&dargs); 3061676Sjpk } 3071676Sjpk } else if (action == DA_REMOVE) { 308*11529SJan.Parcel@Sun.COM dargs.optflag |= DA_REMOVE; 3091676Sjpk if (dentry) { 3101676Sjpk entry = dentry; 311*11529SJan.Parcel@Sun.COM } else if (dargs.optflag & DA_EVENT) { 312*11529SJan.Parcel@Sun.COM if (devname == NULL) 313*11529SJan.Parcel@Sun.COM newentry.devinfo.devname = NULL; 314*11529SJan.Parcel@Sun.COM else 315*11529SJan.Parcel@Sun.COM newentry.devinfo.devname = strdup(devname); 316*11529SJan.Parcel@Sun.COM newentry.devinfo.devtype = (char *)devtype; 317*11529SJan.Parcel@Sun.COM newentry.devinfo.devauths = 318*11529SJan.Parcel@Sun.COM newentry.devinfo.devopts = 319*11529SJan.Parcel@Sun.COM newentry.devinfo.devexec = NULL; 320*11529SJan.Parcel@Sun.COM newentry.devinfo.devlist = strdup(rm_link); 321*11529SJan.Parcel@Sun.COM newentry.devinfo.instance = 0; 322*11529SJan.Parcel@Sun.COM newentry.next = NULL; 323*11529SJan.Parcel@Sun.COM entry = &newentry; 3241676Sjpk } else { 3251676Sjpk newentry.devinfo.devname = strdup(devname); 326*11529SJan.Parcel@Sun.COM newentry.devinfo.devtype = (char *)devtype; 3271676Sjpk newentry.devinfo.devauths = 328*11529SJan.Parcel@Sun.COM newentry.devinfo.devexec = 329*11529SJan.Parcel@Sun.COM newentry.devinfo.devopts = 330*11529SJan.Parcel@Sun.COM newentry.devinfo.devlist = NULL; 3311676Sjpk newentry.devinfo.instance = 0; 3321676Sjpk newentry.next = NULL; 3331676Sjpk entry = &newentry; 3341676Sjpk } 3351676Sjpk dargs.devinfo = &(entry->devinfo); 336*11529SJan.Parcel@Sun.COM /* 337*11529SJan.Parcel@Sun.COM * da_update_device will fill in entry devname if 338*11529SJan.Parcel@Sun.COM * event_driven is true and device is in the file 339*11529SJan.Parcel@Sun.COM */ 340*11529SJan.Parcel@Sun.COM status = da_update_device(&dargs); 341*11529SJan.Parcel@Sun.COM if (event_driven) 342*11529SJan.Parcel@Sun.COM if (newentry.devinfo.devlist != NULL) 343*11529SJan.Parcel@Sun.COM free(newentry.devinfo.devlist); 344*11529SJan.Parcel@Sun.COM if (status == 0) 345*11529SJan.Parcel@Sun.COM return (dargs.devinfo->devname); 346*11529SJan.Parcel@Sun.COM else free(dargs.devinfo->devname); 3471676Sjpk } 348*11529SJan.Parcel@Sun.COM return (NULL); 3491676Sjpk } 350