xref: /haiku/src/bin/eject.cpp (revision a309b7c3a143f47b51f0b27d6702c380a6da3b6c)
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:
RemovableDevice(BDiskDevice * device)31 	RemovableDevice(BDiskDevice* device) {
32 		fName = device->Name();
33 		device->GetPath(&fPath);
34 	};
35 
Name()36 	inline const char* Name() 	{ return fName.String(); }
Path()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:
Visit(BDiskDevice * device)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 
Visit(BPartition * partition,int32 level)53 	virtual bool Visit(BPartition* partition, int32 level) { return false; }
54 
RemovableDevices()55 	inline BObjectList<RemovableDevice>&	RemovableDevices() { return fRemovableDevices; }
56 
57 private:
58 	BObjectList<RemovableDevice>	fRemovableDevices;
59 };
60 
61 
usage(const char * prog)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 
main(int argc,char ** argv)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 
do_eject(char operation,char * device)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