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 2002-2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * ramdiskadm - administer ramdisk(4D). Allows creation and deletion of 27 * ramdisks, and display status. All the ioctls are private between 28 * ramdisk and ramdiskadm, and so are very simple - device information is 29 * communicated via a name or a minor number. 30 */ 31 32 #include <sys/types.h> 33 #include <sys/param.h> 34 #include <sys/ramdisk.h> 35 #include <sys/stat.h> 36 #include <sys/mkdev.h> 37 #include <sys/sunddi.h> 38 #include <libdevinfo.h> 39 #include <stdio.h> 40 #include <fcntl.h> 41 #include <locale.h> 42 #include <string.h> 43 #include <errno.h> 44 #include <stdlib.h> 45 #include <unistd.h> 46 #include <stropts.h> 47 #include <ctype.h> 48 #include "utils.h" 49 50 #define RD_BLOCK_DEV_PFX "/dev/" RD_BLOCK_NAME "/" 51 #define RD_CHAR_DEV_PFX "/dev/" RD_CHAR_NAME "/" 52 53 #define HEADING "%-*.*s %20s %-10s\n" 54 #define FORMAT "%-*.*s %20llu %s\n" 55 #define FW (sizeof (RD_BLOCK_DEV_PFX) - 1 + RD_NAME_LEN) 56 57 static char *pname; 58 59 static void 60 usage(void) 61 { 62 (void) fprintf(stderr, 63 gettext("Usage: %s [ -a <name> <size>[g|m|k|b] | -d <name> ]\n"), 64 pname); 65 exit(E_USAGE); 66 } 67 68 /* 69 * This might be the first time we've used this minor device. If so, 70 * it might also be that the /dev links are in the process of being created 71 * by devfsadmd (or that they'll be created "soon"). We cannot return 72 * until they're there or the invoker of ramdiskadm might try to use them 73 * and not find them. This can happen if a shell script is running on 74 * an MP. 75 */ 76 static void 77 wait_until_dev_complete(char *name) 78 { 79 di_devlink_handle_t hdl; 80 81 hdl = di_devlink_init(RD_DRIVER_NAME, DI_MAKE_LINK); 82 if (hdl == NULL) { 83 die(gettext("couldn't create device link for\"%s\""), name); 84 } 85 (void) di_devlink_fini(&hdl); 86 } 87 88 /* 89 * Create a named ramdisk. 90 */ 91 static void 92 alloc_ramdisk(int ctl_fd, char *name, uint64_t size) 93 { 94 struct rd_ioctl ri; 95 96 (void) strlcpy(ri.ri_name, name, sizeof (ri.ri_name)); 97 ri.ri_size = size; 98 99 if (ioctl(ctl_fd, RD_CREATE_DISK, &ri) == -1) { 100 die(gettext("couldn't create ramdisk \"%s\""), name); 101 } 102 wait_until_dev_complete(name); 103 104 (void) printf(RD_BLOCK_DEV_PFX "%s\n", name); 105 } 106 107 /* 108 * Delete a named ramdisk. 109 */ 110 static void 111 delete_ramdisk(int ctl_fd, char *name) 112 { 113 struct rd_ioctl ri; 114 115 (void) strlcpy(ri.ri_name, name, sizeof (ri.ri_name)); 116 117 if (ioctl(ctl_fd, RD_DELETE_DISK, &ri) == -1) { 118 die(gettext("couldn't delete ramdisk \"%s\""), name); 119 } 120 } 121 122 /*ARGSUSED*/ 123 static int 124 di_callback(di_node_t node, di_minor_t minor, void *arg) 125 { 126 static boolean_t heading_done = B_FALSE; 127 boolean_t obp_ramdisk; 128 char *name; 129 char devnm[MAXNAMELEN]; 130 uint64_t *sizep; 131 char blkpath[MAXPATHLEN]; 132 133 /* 134 * Only consider block nodes bound to the ramdisk driver. 135 */ 136 if (strcmp(di_driver_name(node), RD_DRIVER_NAME) == 0 && 137 di_minor_spectype(minor) == S_IFBLK) { 138 /* 139 * Determine whether this ramdisk is pseudo or OBP-created. 140 */ 141 obp_ramdisk = (di_nodeid(node) == DI_PROM_NODEID); 142 143 /* 144 * If this is an OBP-created ramdisk use the node name, having 145 * first stripped the "ramdisk-" prefix. For pseudo ramdisks 146 * use the minor name, having first stripped any ",raw" suffix. 147 */ 148 if (obp_ramdisk) { 149 RD_STRIP_PREFIX(name, di_node_name(node)); 150 (void) strlcpy(devnm, name, sizeof (devnm)); 151 } else { 152 (void) strlcpy(devnm, di_minor_name(minor), 153 sizeof (devnm)); 154 RD_STRIP_SUFFIX(devnm); 155 } 156 157 /* 158 * Get the size of the ramdisk. 159 */ 160 if (di_prop_lookup_int64(di_minor_devt(minor), node, 161 "Size", (int64_t **)&sizep) == -1) { 162 die(gettext("couldn't obtain size of ramdisk")); 163 } 164 165 /* 166 * Print information about the ramdisk. Prepend a heading 167 * if this is the first/only one. 168 */ 169 if (!heading_done) { 170 (void) printf(HEADING, FW, FW, gettext("Block Device"), 171 gettext("Size"), gettext("Removable")); 172 heading_done = B_TRUE; 173 } 174 (void) snprintf(blkpath, sizeof (blkpath), 175 RD_BLOCK_DEV_PFX "%s", devnm); 176 (void) printf(FORMAT, FW, FW, blkpath, *sizep, 177 obp_ramdisk ? gettext("No") : gettext("Yes")); 178 } 179 180 return (DI_WALK_CONTINUE); 181 } 182 183 /* 184 * Print the list of all the ramdisks, their size, and whether they 185 * are removeable (i.e. non-OBP ramdisks). 186 */ 187 static void 188 print_ramdisk(void) 189 { 190 di_node_t root; 191 192 /* 193 * Create a snapshot of the device tree, then walk it looking 194 * for, and printing information about, ramdisk nodes. 195 */ 196 if ((root = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) { 197 die(gettext("couldn't create device tree snapshot")); 198 } 199 200 if (di_walk_minor(root, DDI_PSEUDO, 0, NULL, di_callback) == -1) { 201 di_fini(root); 202 die(gettext("device tree walk failure")); 203 } 204 205 di_fini(root); 206 } 207 208 int 209 main(int argc, char *argv[]) 210 { 211 int c; 212 char *name = NULL; 213 int allocflag = 0; 214 int deleteflag = 0; 215 int errflag = 0; 216 char *suffix; 217 uint64_t size; 218 int openflag; 219 int ctl_fd = 0; 220 static char rd_ctl[] = "/dev/" RD_CTL_NAME; 221 222 pname = getpname(argv[0]); 223 224 (void) setlocale(LC_ALL, ""); 225 (void) textdomain(TEXT_DOMAIN); 226 227 while ((c = getopt(argc, argv, "a:d:")) != EOF) { 228 switch (c) { 229 case 'a': 230 allocflag = 1; 231 name = optarg; 232 233 if (((argc - optind) <= 0) || (*argv[optind] == '-')) { 234 warn(gettext("<size> missing\n")); 235 usage(); 236 /*NOTREACHED*/ 237 } 238 size = strtoll(argv[optind], &suffix, 0); 239 if (strcmp(suffix, "b") == 0) { 240 size *= 512; 241 ++suffix; 242 } else if (strcmp(suffix, "k") == 0) { 243 size *= 1024; 244 ++suffix; 245 } else if (strcmp(suffix, "m") == 0) { 246 size *= (1024 * 1024); 247 ++suffix; 248 } else if (strcmp(suffix, "g") == 0) { 249 size *= (1024 * 1024 * 1024); 250 ++suffix; 251 } 252 if (size == 0 || *suffix != '\0') { 253 warn(gettext("Illegal <size> \"%s\"\n"), 254 argv[optind]); 255 usage(); 256 /*NOTREACHED*/ 257 } 258 ++optind; 259 break; 260 case 'd': 261 deleteflag = 1; 262 name = optarg; 263 break; 264 default: 265 errflag = 1; 266 break; 267 } 268 } 269 if (errflag || (allocflag && deleteflag) || (argc - optind) > 0) { 270 usage(); 271 /*NOTREACHED*/ 272 } 273 274 if (allocflag || deleteflag) { 275 boolean_t nameok = B_TRUE; 276 char *p; 277 278 /* 279 * Strip off any leading "/dev/{r}ramdisk/" prefix. 280 */ 281 if (strncmp(name, RD_BLOCK_DEV_PFX, 282 sizeof (RD_BLOCK_DEV_PFX)-1) == 0) { 283 name += sizeof (RD_BLOCK_DEV_PFX)-1; 284 } else if (strncmp(name, RD_CHAR_DEV_PFX, 285 sizeof (RD_CHAR_DEV_PFX)-1) == 0) { 286 name += sizeof (RD_CHAR_DEV_PFX)-1; 287 } 288 289 /* 290 * Check that name isn't too long, and that it only contains 291 * valid characters, i.e. [a-zA-Z0-9_][a-zA-Z0-9_-]* 292 */ 293 if (name[0] == '-') { /* permit only within name */ 294 nameok = B_FALSE; 295 } else { 296 for (p = name; *p != '\0'; p++) { 297 if (!isalnum(*p) && *p != '_' && *p != '-') { 298 nameok = B_FALSE; 299 break; 300 } 301 } 302 } 303 if (!nameok || (p - name) > RD_NAME_LEN) { 304 warn(gettext("illegal <name> \"%s\"\n"), name); 305 usage(); 306 /*NOTREACHED*/ 307 } 308 } 309 310 /* 311 * Now do the real work. 312 */ 313 openflag = O_EXCL; 314 if (allocflag || deleteflag) 315 openflag |= O_RDWR; 316 else 317 openflag |= O_RDONLY; 318 ctl_fd = open(rd_ctl, openflag); 319 if (ctl_fd == -1) { 320 if ((errno == EPERM) || (errno == EACCES)) { 321 die(gettext("you do not have permission to perform " 322 "that operation.\n")); 323 } else { 324 die("%s", rd_ctl); 325 } 326 /*NOTREACHED*/ 327 } 328 329 if (allocflag) { 330 alloc_ramdisk(ctl_fd, name, size); 331 } else if (deleteflag) { 332 delete_ramdisk(ctl_fd, name); 333 } else { 334 print_ramdisk(); 335 } 336 337 return (E_SUCCESS); 338 } 339