1 /* 2 * Copyright (c) 2003-2011, Haiku Inc. 3 * Distributed under the terms of the MIT license. 4 * 5 * Authors: 6 * François Revol <mmu_man@users.sf.net> 7 * Philippe Houdoin 8 * 9 * Description: ejects physical media from a drive. 10 * This version also loads a media and can query for the status. 11 */ 12 13 #include <fcntl.h> 14 #include <stdio.h> 15 #include <string.h> 16 #include <unistd.h> 17 18 #include <device/scsi.h> 19 #include <DiskDevice.h> 20 #include <DiskDeviceRoster.h> 21 #include <DiskDeviceVisitor.h> 22 #include <Drivers.h> 23 #include <fs_info.h> 24 #include <ObjectList.h> 25 #include <Path.h> 26 #include <String.h> 27 28 29 class RemovableDevice { 30 public: 31 RemovableDevice(BDiskDevice* device) { 32 fName = device->Name(); 33 device->GetPath(&fPath); 34 }; 35 36 inline const char* Name() { return fName.String(); } 37 inline const char* Path() { return fPath.Path(); } 38 39 private: 40 BString fName; 41 BPath fPath; 42 }; 43 44 45 class RemovableDeviceVisitor : public BDiskDeviceVisitor { 46 public: 47 virtual bool Visit(BDiskDevice* device) { 48 if (device->IsRemovableMedia()) 49 fRemovableDevices.AddItem(new RemovableDevice(device)); 50 return false; // Don't stop yet! 51 } 52 53 virtual bool Visit(BPartition* partition, int32 level) { return false; } 54 55 inline BObjectList<RemovableDevice>& RemovableDevices() { return fRemovableDevices; } 56 57 private: 58 BObjectList<RemovableDevice> fRemovableDevices; 59 }; 60 61 62 static int usage(const char *prog) 63 { 64 printf("usage: eject [-q|-l|-s|-b|-u] /dev/disk/.../raw\n"); 65 // printf("usage: eject [-q|-l|-s|-b|-u] [scsi|ide|/dev/disk/.../raw]\n"); 66 printf(" eject the device, or:\n"); 67 printf(" -l: load it (close the tray)\n"); 68 printf(" -q: query for media status\n"); 69 printf(" -s: swap tray position (close/eject)\n"); 70 printf(" -b: block (lock) tray position (prevent close/eject)\n"); 71 printf(" -u: unblock (unlock) tray position (allow close/eject)\n"); 72 // printf(" scsi: act on all scsi devices\n"); 73 // printf(" ide: act on all ide devices\n"); 74 // printf(" acts on all scsi and ide devices and floppy by default\n"); 75 return 0; 76 } 77 78 static int do_eject(char operation, char *device); 79 80 int main(int argc, char **argv) 81 { 82 char *device = NULL; 83 char operation = 'e'; 84 int i; 85 int ret; 86 87 for (i = 1; i < argc; i++) { 88 if (strncmp(argv[i], "--h", 3) == 0) { 89 return usage("eject"); 90 } else if (strncmp(argv[i], "-", 1) == 0) { 91 if (strlen(argv[i]) > 1) 92 operation = argv[i][1]; 93 else { 94 usage("eject"); 95 return 1; 96 } 97 } else { 98 device = argv[i]; 99 ret = do_eject(operation, device); 100 if (ret != 0) 101 return ret; 102 } 103 } 104 if (device == NULL) { 105 BDiskDeviceRoster diskDeviceRoster; 106 RemovableDeviceVisitor visitor; 107 diskDeviceRoster.VisitEachDevice(&visitor); 108 109 int32 count = visitor.RemovableDevices().CountItems(); 110 if (count < 1) { 111 printf("No removable device found!\n"); 112 return 1; 113 } 114 115 if (count > 1) { 116 printf("Multiple removable devices available:\n"); 117 for (i = 0; i < count; i++) { 118 RemovableDevice* item = visitor.RemovableDevices().ItemAt(i); 119 printf(" %s\t\"%s\"\n", item->Path(), item->Name()); 120 } 121 return 1; 122 } 123 124 // Default to single removable device found 125 device = (char*)visitor.RemovableDevices().FirstItem()->Path(); 126 return do_eject(operation, device); 127 } 128 129 return 0; 130 } 131 132 133 static int do_eject(char operation, char *device) 134 { 135 bool bval; 136 int fd; 137 status_t devstatus; 138 fs_info info; 139 140 // if the path is not on devfs, it's probably on 141 // the mountpoint of the device we want to act on. 142 // (should rather stat() for blk(char) device though). 143 if (fs_stat_dev(dev_for_path(device), &info) >= B_OK) { 144 if (strcmp(info.fsh_name, "devfs")) 145 device = info.device_name; 146 } 147 148 fd = open(device, O_RDONLY); 149 if (fd < 0) { 150 perror(device); 151 return 1; 152 } 153 switch (operation) { 154 case 'e': 155 if (ioctl(fd, B_EJECT_DEVICE, NULL, 0) < 0) { 156 perror(device); 157 close(fd); 158 return 1; 159 } 160 break; 161 case 'l': 162 if (ioctl(fd, B_LOAD_MEDIA, NULL, 0) < 0) { 163 perror(device); 164 close(fd); 165 return 1; 166 } 167 break; 168 case 'b': 169 bval = true; 170 if (ioctl(fd, B_SCSI_PREVENT_ALLOW, &bval, sizeof(bval)) < 0) { 171 perror(device); 172 close(fd); 173 return 1; 174 } 175 break; 176 case 'u': 177 bval = false; 178 if (ioctl(fd, B_SCSI_PREVENT_ALLOW, &bval, sizeof(bval)) < 0) { 179 perror(device); 180 close(fd); 181 return 1; 182 } 183 break; 184 case 'q': 185 if (ioctl(fd, B_GET_MEDIA_STATUS, &devstatus, sizeof(devstatus)) 186 < 0) { 187 perror(device); 188 close(fd); 189 return 1; 190 } 191 switch (devstatus) { 192 case B_NO_ERROR: 193 puts("Media present"); 194 break; 195 default: 196 puts(strerror(devstatus)); 197 } 198 break; 199 case 's': 200 if (ioctl(fd, B_GET_MEDIA_STATUS, &devstatus, sizeof(devstatus)) 201 < 0) { 202 perror(device); 203 close(fd); 204 return 1; 205 } 206 switch (devstatus) { 207 case B_NO_ERROR: 208 case B_DEV_NO_MEDIA: 209 if (ioctl(fd, B_EJECT_DEVICE, NULL, 0) < 0) { 210 perror(device); 211 close(fd); 212 return 1; 213 } 214 break; 215 case B_DEV_DOOR_OPEN: 216 if (ioctl(fd, B_LOAD_MEDIA, NULL, 0) < 0) { 217 perror(device); 218 close(fd); 219 return 1; 220 } 221 break; 222 default: 223 perror(device); 224 } 225 break; 226 case 'h': 227 default: 228 close(fd); 229 return usage("eject"); 230 } 231 close(fd); 232 return 0; 233 } 234 235