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(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 'h': 155 return usage("eject"); 156 case 'e': 157 if (ioctl(fd, B_EJECT_DEVICE, NULL, 0) < 0) { 158 perror(device); 159 return 1; 160 } 161 break; 162 case 'l': 163 if (ioctl(fd, B_LOAD_MEDIA, NULL, 0) < 0) { 164 perror(device); 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 return 1; 173 } 174 break; 175 case 'u': 176 bval = false; 177 if (ioctl(fd, B_SCSI_PREVENT_ALLOW, &bval, sizeof(bval)) < 0) { 178 perror(device); 179 return 1; 180 } 181 break; 182 case 'q': 183 if (ioctl(fd, B_GET_MEDIA_STATUS, &devstatus, sizeof(devstatus)) < 0) { 184 perror(device); 185 return 1; 186 } 187 switch (devstatus) { 188 case B_NO_ERROR: 189 puts("Media present"); 190 break; 191 default: 192 puts(strerror(devstatus)); 193 } 194 break; 195 case 's': 196 if (ioctl(fd, B_GET_MEDIA_STATUS, &devstatus, sizeof(devstatus)) < 0) { 197 perror(device); 198 return 1; 199 } 200 switch (devstatus) { 201 case B_NO_ERROR: 202 case B_DEV_NO_MEDIA: 203 if (ioctl(fd, B_EJECT_DEVICE, NULL, 0) < 0) { 204 perror(device); 205 return 1; 206 } 207 break; 208 case B_DEV_DOOR_OPEN: 209 if (ioctl(fd, B_LOAD_MEDIA, NULL, 0) < 0) { 210 perror(device); 211 return 1; 212 } 213 break; 214 default: 215 perror(device); 216 } 217 break; 218 default: 219 usage("eject"); 220 return 1; 221 } 222 close(fd); 223 return 0; 224 } 225 226