xref: /illumos-gate/usr/src/cmd/hal/addons/storage/addon-storage.c (revision 18c2aff776a775d34a4c9893a4c72e0434d68e36)
1*18c2aff7Sartem /***************************************************************************
2*18c2aff7Sartem  *
3*18c2aff7Sartem  * addon-storage.c : watch removable media state changes
4*18c2aff7Sartem  *
5*18c2aff7Sartem  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
6*18c2aff7Sartem  * Use is subject to license terms.
7*18c2aff7Sartem  *
8*18c2aff7Sartem  * Licensed under the Academic Free License version 2.1
9*18c2aff7Sartem  *
10*18c2aff7Sartem  **************************************************************************/
11*18c2aff7Sartem 
12*18c2aff7Sartem #pragma	ident	"%Z%%M%	%I%	%E% SMI"
13*18c2aff7Sartem 
14*18c2aff7Sartem #ifdef HAVE_CONFIG_H
15*18c2aff7Sartem #  include <config.h>
16*18c2aff7Sartem #endif
17*18c2aff7Sartem 
18*18c2aff7Sartem #include <errno.h>
19*18c2aff7Sartem #include <string.h>
20*18c2aff7Sartem #include <strings.h>
21*18c2aff7Sartem #include <stdlib.h>
22*18c2aff7Sartem #include <stdio.h>
23*18c2aff7Sartem #include <sys/ioctl.h>
24*18c2aff7Sartem #include <sys/types.h>
25*18c2aff7Sartem #include <sys/stat.h>
26*18c2aff7Sartem #include <sys/types.h>
27*18c2aff7Sartem #include <sys/wait.h>
28*18c2aff7Sartem #include <fcntl.h>
29*18c2aff7Sartem #include <unistd.h>
30*18c2aff7Sartem #include <sys/mnttab.h>
31*18c2aff7Sartem #include <sys/dkio.h>
32*18c2aff7Sartem #include <priv.h>
33*18c2aff7Sartem 
34*18c2aff7Sartem #include <libhal.h>
35*18c2aff7Sartem 
36*18c2aff7Sartem #include "../../hald/logger.h"
37*18c2aff7Sartem 
38*18c2aff7Sartem #define	SLEEP_PERIOD	5
39*18c2aff7Sartem 
40*18c2aff7Sartem static void
41*18c2aff7Sartem my_dbus_error_free(DBusError *error)
42*18c2aff7Sartem {
43*18c2aff7Sartem 	if (dbus_error_is_set(error)) {
44*18c2aff7Sartem 		dbus_error_free(error);
45*18c2aff7Sartem 	}
46*18c2aff7Sartem }
47*18c2aff7Sartem 
48*18c2aff7Sartem static void
49*18c2aff7Sartem force_unmount (LibHalContext *ctx, const char *udi)
50*18c2aff7Sartem {
51*18c2aff7Sartem 	DBusError error;
52*18c2aff7Sartem 	DBusMessage *msg = NULL;
53*18c2aff7Sartem 	DBusMessage *reply = NULL;
54*18c2aff7Sartem 	char **options = NULL;
55*18c2aff7Sartem 	unsigned int num_options = 0;
56*18c2aff7Sartem 	DBusConnection *dbus_connection;
57*18c2aff7Sartem 	char *device_file;
58*18c2aff7Sartem 
59*18c2aff7Sartem 	dbus_error_init (&error);
60*18c2aff7Sartem 
61*18c2aff7Sartem 	dbus_connection = libhal_ctx_get_dbus_connection (ctx);
62*18c2aff7Sartem 
63*18c2aff7Sartem 	msg = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
64*18c2aff7Sartem 					    "org.freedesktop.Hal.Device.Volume",
65*18c2aff7Sartem 					    "Unmount");
66*18c2aff7Sartem 	if (msg == NULL) {
67*18c2aff7Sartem 		HAL_DEBUG (("Could not create dbus message for %s", udi));
68*18c2aff7Sartem 		goto out;
69*18c2aff7Sartem 	}
70*18c2aff7Sartem 
71*18c2aff7Sartem 
72*18c2aff7Sartem 	options = calloc (1, sizeof (char *));
73*18c2aff7Sartem 	if (options == NULL) {
74*18c2aff7Sartem 		HAL_DEBUG (("Could not allocate options array"));
75*18c2aff7Sartem 		goto out;
76*18c2aff7Sartem 	}
77*18c2aff7Sartem 
78*18c2aff7Sartem 	device_file = libhal_device_get_property_string (ctx, udi, "block.device", &error);
79*18c2aff7Sartem 	if (device_file != NULL) {
80*18c2aff7Sartem 		libhal_free_string (device_file);
81*18c2aff7Sartem 	}
82*18c2aff7Sartem 	dbus_error_free (&error);
83*18c2aff7Sartem 
84*18c2aff7Sartem 	if (!dbus_message_append_args (msg,
85*18c2aff7Sartem 				       DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options,
86*18c2aff7Sartem 				       DBUS_TYPE_INVALID)) {
87*18c2aff7Sartem 		HAL_DEBUG (("Could not append args to dbus message for %s", udi));
88*18c2aff7Sartem 		goto out;
89*18c2aff7Sartem 	}
90*18c2aff7Sartem 
91*18c2aff7Sartem 	if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, msg, -1, &error))) {
92*18c2aff7Sartem 		HAL_DEBUG (("Unmount failed for %s: %s : %s\n", udi, error.name, error.message));
93*18c2aff7Sartem 		goto out;
94*18c2aff7Sartem 	}
95*18c2aff7Sartem 
96*18c2aff7Sartem 	if (dbus_error_is_set (&error)) {
97*18c2aff7Sartem 		HAL_DEBUG (("Unmount failed for %s\n%s : %s\n", udi, error.name, error.message));
98*18c2aff7Sartem 		goto out;
99*18c2aff7Sartem 	}
100*18c2aff7Sartem 
101*18c2aff7Sartem 	HAL_DEBUG (("Succesfully unmounted udi '%s'", udi));
102*18c2aff7Sartem 
103*18c2aff7Sartem out:
104*18c2aff7Sartem 	dbus_error_free (&error);
105*18c2aff7Sartem 	if (options != NULL)
106*18c2aff7Sartem 		free (options);
107*18c2aff7Sartem 	if (msg != NULL)
108*18c2aff7Sartem 		dbus_message_unref (msg);
109*18c2aff7Sartem 	if (reply != NULL)
110*18c2aff7Sartem 		dbus_message_unref (reply);
111*18c2aff7Sartem }
112*18c2aff7Sartem 
113*18c2aff7Sartem static void
114*18c2aff7Sartem unmount_childs (LibHalContext *ctx, const char *udi)
115*18c2aff7Sartem {
116*18c2aff7Sartem 	DBusError error;
117*18c2aff7Sartem 	int num_volumes;
118*18c2aff7Sartem 	char **volumes;
119*18c2aff7Sartem 
120*18c2aff7Sartem 	dbus_error_init (&error);
121*18c2aff7Sartem 
122*18c2aff7Sartem 	/* need to force unmount all partitions */
123*18c2aff7Sartem 	if ((volumes = libhal_manager_find_device_string_match (
124*18c2aff7Sartem 	     ctx, "block.storage_device", udi, &num_volumes, &error)) != NULL) {
125*18c2aff7Sartem 		dbus_error_free (&error);
126*18c2aff7Sartem 		int i;
127*18c2aff7Sartem 
128*18c2aff7Sartem 		for (i = 0; i < num_volumes; i++) {
129*18c2aff7Sartem 			char *vol_udi;
130*18c2aff7Sartem 
131*18c2aff7Sartem 			vol_udi = volumes[i];
132*18c2aff7Sartem 			if (libhal_device_get_property_bool (ctx, vol_udi, "block.is_volume", &error)) {
133*18c2aff7Sartem 				dbus_error_free (&error);
134*18c2aff7Sartem 				if (libhal_device_get_property_bool (ctx, vol_udi, "volume.is_mounted", &error)) {
135*18c2aff7Sartem 					dbus_error_free (&error);
136*18c2aff7Sartem 					HAL_DEBUG (("Forcing unmount of child '%s'", vol_udi));
137*18c2aff7Sartem 					force_unmount (ctx, vol_udi);
138*18c2aff7Sartem 				}
139*18c2aff7Sartem 			}
140*18c2aff7Sartem 		}
141*18c2aff7Sartem 		libhal_free_string_array (volumes);
142*18c2aff7Sartem 	}
143*18c2aff7Sartem 	my_dbus_error_free (&error);
144*18c2aff7Sartem }
145*18c2aff7Sartem 
146*18c2aff7Sartem /** Check if a filesystem on a special device file is mounted
147*18c2aff7Sartem  *
148*18c2aff7Sartem  *  @param  device_file         Special device file, e.g. /dev/cdrom
149*18c2aff7Sartem  *  @return                     TRUE iff there is a filesystem system mounted
150*18c2aff7Sartem  *                              on the special device file
151*18c2aff7Sartem  */
152*18c2aff7Sartem static dbus_bool_t
153*18c2aff7Sartem is_mounted (const char *device_file)
154*18c2aff7Sartem {
155*18c2aff7Sartem 	FILE *f;
156*18c2aff7Sartem 	dbus_bool_t rc = FALSE;
157*18c2aff7Sartem 	struct mnttab mp;
158*18c2aff7Sartem 	struct mnttab mpref;
159*18c2aff7Sartem 
160*18c2aff7Sartem 	if ((f = fopen ("/etc/mnttab", "r")) == NULL)
161*18c2aff7Sartem 		return rc;
162*18c2aff7Sartem 
163*18c2aff7Sartem 	bzero(&mp, sizeof (mp));
164*18c2aff7Sartem 	bzero(&mpref, sizeof (mpref));
165*18c2aff7Sartem 	mpref.mnt_special = (char *)device_file;
166*18c2aff7Sartem 	if (getmntany(f, &mp, &mpref) == 0) {
167*18c2aff7Sartem 		rc = TRUE;
168*18c2aff7Sartem 	}
169*18c2aff7Sartem 
170*18c2aff7Sartem 	fclose (f);
171*18c2aff7Sartem 	return rc;
172*18c2aff7Sartem }
173*18c2aff7Sartem 
174*18c2aff7Sartem void
175*18c2aff7Sartem close_device (int *fd)
176*18c2aff7Sartem {
177*18c2aff7Sartem 	if (*fd > 0) {
178*18c2aff7Sartem 		close (*fd);
179*18c2aff7Sartem 		*fd = -1;
180*18c2aff7Sartem 	}
181*18c2aff7Sartem }
182*18c2aff7Sartem 
183*18c2aff7Sartem void
184*18c2aff7Sartem drop_privileges ()
185*18c2aff7Sartem {
186*18c2aff7Sartem 	priv_set_t *pPrivSet = NULL;
187*18c2aff7Sartem 	priv_set_t *lPrivSet = NULL;
188*18c2aff7Sartem 
189*18c2aff7Sartem 	/*
190*18c2aff7Sartem 	 * Start with the 'basic' privilege set and then remove any
191*18c2aff7Sartem 	 * of the 'basic' privileges that will not be needed.
192*18c2aff7Sartem 	 */
193*18c2aff7Sartem 	if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) {
194*18c2aff7Sartem 		return;
195*18c2aff7Sartem 	}
196*18c2aff7Sartem 
197*18c2aff7Sartem 	/* Clear privileges we will not need from the 'basic' set */
198*18c2aff7Sartem 	(void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY);
199*18c2aff7Sartem 	(void) priv_delset(pPrivSet, PRIV_PROC_INFO);
200*18c2aff7Sartem 	(void) priv_delset(pPrivSet, PRIV_PROC_SESSION);
201*18c2aff7Sartem 
202*18c2aff7Sartem 	/* to open logindevperm'd devices */
203*18c2aff7Sartem 	(void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ);
204*18c2aff7Sartem 
205*18c2aff7Sartem 	/* Set the permitted privilege set. */
206*18c2aff7Sartem 	if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
207*18c2aff7Sartem 		return;
208*18c2aff7Sartem 	}
209*18c2aff7Sartem 
210*18c2aff7Sartem 	/* Clear the limit set. */
211*18c2aff7Sartem 	if ((lPrivSet = priv_allocset()) == NULL) {
212*18c2aff7Sartem 		return;
213*18c2aff7Sartem 	}
214*18c2aff7Sartem 
215*18c2aff7Sartem 	priv_emptyset(lPrivSet);
216*18c2aff7Sartem 
217*18c2aff7Sartem 	if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) {
218*18c2aff7Sartem 		return;
219*18c2aff7Sartem 	}
220*18c2aff7Sartem 
221*18c2aff7Sartem 	priv_freeset(lPrivSet);
222*18c2aff7Sartem }
223*18c2aff7Sartem 
224*18c2aff7Sartem int
225*18c2aff7Sartem main (int argc, char *argv[])
226*18c2aff7Sartem {
227*18c2aff7Sartem 	char *udi;
228*18c2aff7Sartem 	char *device_file, *raw_device_file;
229*18c2aff7Sartem 	LibHalContext *ctx = NULL;
230*18c2aff7Sartem 	DBusError error;
231*18c2aff7Sartem 	char *bus;
232*18c2aff7Sartem 	char *drive_type;
233*18c2aff7Sartem 	int state, last_state;
234*18c2aff7Sartem 	char *support_media_changed_str;
235*18c2aff7Sartem 	int support_media_changed;
236*18c2aff7Sartem 	int fd = -1;
237*18c2aff7Sartem 
238*18c2aff7Sartem 	if ((udi = getenv ("UDI")) == NULL)
239*18c2aff7Sartem 		goto out;
240*18c2aff7Sartem 	if ((device_file = getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL)
241*18c2aff7Sartem 		goto out;
242*18c2aff7Sartem 	if ((raw_device_file = getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL)
243*18c2aff7Sartem 		goto out;
244*18c2aff7Sartem 	if ((bus = getenv ("HAL_PROP_STORAGE_BUS")) == NULL)
245*18c2aff7Sartem 		goto out;
246*18c2aff7Sartem 	if ((drive_type = getenv ("HAL_PROP_STORAGE_DRIVE_TYPE")) == NULL)
247*18c2aff7Sartem 		goto out;
248*18c2aff7Sartem 
249*18c2aff7Sartem 	drop_privileges ();
250*18c2aff7Sartem 
251*18c2aff7Sartem 	setup_logger ();
252*18c2aff7Sartem 
253*18c2aff7Sartem 	support_media_changed_str = getenv ("HAL_PROP_STORAGE_CDROM_SUPPORT_MEDIA_CHANGED");
254*18c2aff7Sartem 	if (support_media_changed_str != NULL && strcmp (support_media_changed_str, "true") == 0)
255*18c2aff7Sartem 		support_media_changed = TRUE;
256*18c2aff7Sartem 	else
257*18c2aff7Sartem 		support_media_changed = FALSE;
258*18c2aff7Sartem 
259*18c2aff7Sartem 	dbus_error_init (&error);
260*18c2aff7Sartem 
261*18c2aff7Sartem 	if ((ctx = libhal_ctx_init_direct (&error)) == NULL) {
262*18c2aff7Sartem 		goto out;
263*18c2aff7Sartem 	}
264*18c2aff7Sartem 	my_dbus_error_free (&error);
265*18c2aff7Sartem 
266*18c2aff7Sartem 	if (!libhal_device_addon_is_ready (ctx, udi, &error)) {
267*18c2aff7Sartem 		goto out;
268*18c2aff7Sartem 	}
269*18c2aff7Sartem 	my_dbus_error_free (&error);
270*18c2aff7Sartem 
271*18c2aff7Sartem 	printf ("Doing addon-storage for %s (bus %s) (drive_type %s) (udi %s)\n", device_file, bus, drive_type, udi);
272*18c2aff7Sartem 
273*18c2aff7Sartem 	last_state = state = DKIO_NONE;
274*18c2aff7Sartem 
275*18c2aff7Sartem 	/* Linux version of this addon attempts to re-open the device O_EXCL
276*18c2aff7Sartem 	 * every 2 seconds, trying to figure out if some other app,
277*18c2aff7Sartem 	 * like a cd burner, is using the device. Aside from questionable
278*18c2aff7Sartem 	 * value of this (apps should use HAL's locked property or/and
279*18c2aff7Sartem 	 * Solaris in_use facility), but also frequent opens/closes
280*18c2aff7Sartem 	 * keeps media constantly spun up. All this needs more thought.
281*18c2aff7Sartem 	 */
282*18c2aff7Sartem 	for (;;) {
283*18c2aff7Sartem 		if (is_mounted (device_file)) {
284*18c2aff7Sartem 			close_device (&fd);
285*18c2aff7Sartem 			sleep (SLEEP_PERIOD);
286*18c2aff7Sartem 		} else if ((fd < 0) && ((fd = open (raw_device_file, O_RDONLY | O_NONBLOCK)) < 0)) {
287*18c2aff7Sartem 			HAL_DEBUG (("open failed for %s: %s", raw_device_file, strerror (errno)));
288*18c2aff7Sartem 			sleep (SLEEP_PERIOD);
289*18c2aff7Sartem 		} else {
290*18c2aff7Sartem 			/* Check if a disc is in the drive */
291*18c2aff7Sartem 			/* XXX initial call always returns inserted
292*18c2aff7Sartem 			 * causing unnecessary rescan - optimize?
293*18c2aff7Sartem 			 */
294*18c2aff7Sartem 			if (ioctl (fd, DKIOCSTATE, &state) == 0) {
295*18c2aff7Sartem 				if (state == last_state) {
296*18c2aff7Sartem 					HAL_DEBUG (("state has not changed %d %s", state, device_file));
297*18c2aff7Sartem 					continue;
298*18c2aff7Sartem 				} else {
299*18c2aff7Sartem 					HAL_DEBUG (("new state %d %s", state, device_file));
300*18c2aff7Sartem 				}
301*18c2aff7Sartem 
302*18c2aff7Sartem 				switch (state) {
303*18c2aff7Sartem 				case DKIO_EJECTED:
304*18c2aff7Sartem 					HAL_DEBUG (("Media removal detected on %s", device_file));
305*18c2aff7Sartem 					last_state = state;
306*18c2aff7Sartem 
307*18c2aff7Sartem 					libhal_device_set_property_bool (ctx, udi, "storage.removable.media_available", FALSE, &error);
308*18c2aff7Sartem 					my_dbus_error_free (&error);
309*18c2aff7Sartem 
310*18c2aff7Sartem 					/* attempt to unmount all childs */
311*18c2aff7Sartem 					unmount_childs (ctx, udi);
312*18c2aff7Sartem 
313*18c2aff7Sartem 					/* could have a fs on the main block device; do a rescan to remove it */
314*18c2aff7Sartem 					libhal_device_rescan (ctx, udi, &error);
315*18c2aff7Sartem 					my_dbus_error_free (&error);
316*18c2aff7Sartem 					break;
317*18c2aff7Sartem 
318*18c2aff7Sartem 				case DKIO_INSERTED:
319*18c2aff7Sartem 					HAL_DEBUG (("Media insertion detected on %s", device_file));
320*18c2aff7Sartem 					last_state = state;
321*18c2aff7Sartem 
322*18c2aff7Sartem 					libhal_device_set_property_bool (ctx, udi, "storage.removable.media_available", TRUE, &error);
323*18c2aff7Sartem 					my_dbus_error_free (&error);
324*18c2aff7Sartem 
325*18c2aff7Sartem 					/* could have a fs on the main block device; do a rescan to add it */
326*18c2aff7Sartem 					libhal_device_rescan (ctx, udi, &error);
327*18c2aff7Sartem 					my_dbus_error_free (&error);
328*18c2aff7Sartem 					break;
329*18c2aff7Sartem 
330*18c2aff7Sartem 				case DKIO_DEV_GONE:
331*18c2aff7Sartem 					HAL_DEBUG (("Device gone detected on %s", device_file));
332*18c2aff7Sartem 					last_state = state;
333*18c2aff7Sartem 
334*18c2aff7Sartem 					unmount_childs (ctx, udi);
335*18c2aff7Sartem 					close_device (&fd);
336*18c2aff7Sartem 					goto out;
337*18c2aff7Sartem 
338*18c2aff7Sartem 				case DKIO_NONE:
339*18c2aff7Sartem 				default:
340*18c2aff7Sartem 					break;
341*18c2aff7Sartem 				}
342*18c2aff7Sartem 			} else {
343*18c2aff7Sartem 				HAL_DEBUG (("DKIOCSTATE failed: %s\n", strerror(errno)));
344*18c2aff7Sartem 				sleep (SLEEP_PERIOD);
345*18c2aff7Sartem 			}
346*18c2aff7Sartem 		}
347*18c2aff7Sartem 	}
348*18c2aff7Sartem 
349*18c2aff7Sartem out:
350*18c2aff7Sartem 	if (ctx != NULL) {
351*18c2aff7Sartem 		my_dbus_error_free (&error);
352*18c2aff7Sartem 		libhal_ctx_shutdown (ctx, &error);
353*18c2aff7Sartem 		libhal_ctx_free (ctx);
354*18c2aff7Sartem 	}
355*18c2aff7Sartem 
356*18c2aff7Sartem 	return 0;
357*18c2aff7Sartem }
358