xref: /haiku/src/bin/eject.cpp (revision 002f37b0cca92e4cf72857c72ac95db5a8b09615)
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