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
29class RemovableDevice {
30public:
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
39private:
40	BString	fName;
41	BPath 	fPath;
42};
43
44
45class RemovableDeviceVisitor : public BDiskDeviceVisitor {
46public:
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
57private:
58	BObjectList<RemovableDevice>	fRemovableDevices;
59};
60
61
62static 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
78static int do_eject(char operation, char *device);
79
80int 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
133static 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