8354 sync regcomp(3C) with upstream (fix make catalog)
[unleashed/tickless.git] / usr / src / cmd / hal / addons / storage / addon-storage.c
blobc8eeae61cc91f1bf1be8e049945d92a32650f1ed
1 /***************************************************************************
3 * addon-storage.c : watch removable media state changes
5 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
6 * Use is subject to license terms.
8 * Licensed under the Academic Free License version 2.1
10 **************************************************************************/
12 #ifdef HAVE_CONFIG_H
13 # include <config.h>
14 #endif
16 #include <errno.h>
17 #include <string.h>
18 #include <strings.h>
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <sys/mnttab.h>
29 #include <sys/dkio.h>
30 #include <priv.h>
31 #include <libsysevent.h>
32 #include <sys/sysevent/dev.h>
34 #include <libhal.h>
36 #include "../../hald/logger.h"
38 #define SLEEP_PERIOD 5
40 static char *udi;
41 static char *devfs_path;
42 LibHalContext *ctx = NULL;
43 static sysevent_handle_t *shp = NULL;
45 static void sysevent_dev_handler(sysevent_t *);
47 static void
48 my_dbus_error_free(DBusError *error)
50 if (dbus_error_is_set(error)) {
51 dbus_error_free(error);
55 static void
56 sysevent_init ()
58 const char *subcl[1];
60 shp = sysevent_bind_handle (sysevent_dev_handler);
61 if (shp == NULL) {
62 HAL_DEBUG (("sysevent_bind_handle failed %d", errno));
63 return;
66 subcl[0] = ESC_DEV_EJECT_REQUEST;
67 if (sysevent_subscribe_event (shp, EC_DEV_STATUS, subcl, 1) != 0) {
68 HAL_INFO (("subscribe(dev_status) failed %d", errno));
69 sysevent_unbind_handle (shp);
70 return;
74 static void
75 sysevent_fini ()
77 if (shp != NULL) {
78 sysevent_unbind_handle (shp);
79 shp = NULL;
83 static void
84 sysevent_dev_handler (sysevent_t *ev)
86 char *class;
87 char *subclass;
88 nvlist_t *attr_list;
89 char *phys_path, *path;
90 char *p;
91 int len;
92 DBusError error;
94 if ((class = sysevent_get_class_name (ev)) == NULL)
95 return;
97 if ((subclass = sysevent_get_subclass_name (ev)) == NULL)
98 return;
100 if ((strcmp (class, EC_DEV_STATUS) != 0) ||
101 (strcmp (subclass, ESC_DEV_EJECT_REQUEST) != 0))
102 return;
104 if (sysevent_get_attr_list (ev, &attr_list) != 0)
105 return;
107 if (nvlist_lookup_string (attr_list, DEV_PHYS_PATH, &phys_path) != 0) {
108 goto out;
111 /* see if event belongs to our LUN (ignore slice and "/devices" ) */
112 if (strncmp (phys_path, "/devices", sizeof ("/devices") - 1) == 0)
113 path = phys_path + sizeof ("/devices") - 1;
114 else
115 path = phys_path;
117 if ((p = strrchr (path, ':')) == NULL)
118 goto out;
119 len = (uintptr_t)p - (uintptr_t)path;
120 if (strncmp (path, devfs_path, len) != 0)
121 goto out;
123 HAL_DEBUG (("sysevent_dev_handler %s %s", subclass, phys_path));
125 /* we got it, tell the world */
126 dbus_error_init (&error);
127 libhal_device_emit_condition (ctx, udi, "EjectPressed", "", &error);
128 dbus_error_free (&error);
130 out:
131 nvlist_free(attr_list);
134 static void
135 force_unmount (LibHalContext *ctx, const char *udi)
137 DBusError error;
138 DBusMessage *msg = NULL;
139 DBusMessage *reply = NULL;
140 char **options = NULL;
141 unsigned int num_options = 0;
142 DBusConnection *dbus_connection;
143 char *device_file;
145 dbus_error_init (&error);
147 dbus_connection = libhal_ctx_get_dbus_connection (ctx);
149 msg = dbus_message_new_method_call ("org.freedesktop.Hal", udi,
150 "org.freedesktop.Hal.Device.Volume",
151 "Unmount");
152 if (msg == NULL) {
153 HAL_DEBUG (("Could not create dbus message for %s", udi));
154 goto out;
158 options = calloc (1, sizeof (char *));
159 if (options == NULL) {
160 HAL_DEBUG (("Could not allocate options array"));
161 goto out;
164 device_file = libhal_device_get_property_string (ctx, udi, "block.device", &error);
165 if (device_file != NULL) {
166 libhal_free_string (device_file);
168 dbus_error_free (&error);
170 if (!dbus_message_append_args (msg,
171 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options,
172 DBUS_TYPE_INVALID)) {
173 HAL_DEBUG (("Could not append args to dbus message for %s", udi));
174 goto out;
177 if (!(reply = dbus_connection_send_with_reply_and_block (dbus_connection, msg, -1, &error))) {
178 HAL_DEBUG (("Unmount failed for %s: %s : %s\n", udi, error.name, error.message));
179 goto out;
182 if (dbus_error_is_set (&error)) {
183 HAL_DEBUG (("Unmount failed for %s\n%s : %s\n", udi, error.name, error.message));
184 goto out;
187 HAL_DEBUG (("Succesfully unmounted udi '%s'", udi));
189 out:
190 dbus_error_free (&error);
191 if (options != NULL)
192 free (options);
193 if (msg != NULL)
194 dbus_message_unref (msg);
195 if (reply != NULL)
196 dbus_message_unref (reply);
199 static void
200 unmount_childs (LibHalContext *ctx, const char *udi)
202 DBusError error;
203 int num_volumes;
204 char **volumes;
206 dbus_error_init (&error);
208 /* need to force unmount all partitions */
209 if ((volumes = libhal_manager_find_device_string_match (
210 ctx, "block.storage_device", udi, &num_volumes, &error)) != NULL) {
211 dbus_error_free (&error);
212 int i;
214 for (i = 0; i < num_volumes; i++) {
215 char *vol_udi;
217 vol_udi = volumes[i];
218 if (libhal_device_get_property_bool (ctx, vol_udi, "block.is_volume", &error)) {
219 dbus_error_free (&error);
220 if (libhal_device_get_property_bool (ctx, vol_udi, "volume.is_mounted", &error)) {
221 dbus_error_free (&error);
222 HAL_DEBUG (("Forcing unmount of child '%s'", vol_udi));
223 force_unmount (ctx, vol_udi);
227 libhal_free_string_array (volumes);
229 my_dbus_error_free (&error);
232 /** Check if a filesystem on a special device file is mounted
234 * @param device_file Special device file, e.g. /dev/cdrom
235 * @return TRUE iff there is a filesystem system mounted
236 * on the special device file
238 static dbus_bool_t
239 is_mounted (const char *device_file)
241 FILE *f;
242 dbus_bool_t rc = FALSE;
243 struct mnttab mp;
244 struct mnttab mpref;
246 if ((f = fopen ("/etc/mnttab", "r")) == NULL)
247 return rc;
249 bzero(&mp, sizeof (mp));
250 bzero(&mpref, sizeof (mpref));
251 mpref.mnt_special = (char *)device_file;
252 if (getmntany(f, &mp, &mpref) == 0) {
253 rc = TRUE;
256 fclose (f);
257 return rc;
260 void
261 close_device (int *fd)
263 if (*fd > 0) {
264 close (*fd);
265 *fd = -1;
269 void
270 drop_privileges ()
272 priv_set_t *pPrivSet = NULL;
273 priv_set_t *lPrivSet = NULL;
276 * Start with the 'basic' privilege set and then remove any
277 * of the 'basic' privileges that will not be needed.
279 if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) {
280 return;
283 /* Clear privileges we will not need from the 'basic' set */
284 (void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY);
285 (void) priv_delset(pPrivSet, PRIV_PROC_INFO);
286 (void) priv_delset(pPrivSet, PRIV_PROC_SESSION);
288 /* to open logindevperm'd devices */
289 (void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ);
291 /* to receive sysevents */
292 (void) priv_addset(pPrivSet, PRIV_SYS_CONFIG);
294 /* Set the permitted privilege set. */
295 if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
296 return;
299 /* Clear the limit set. */
300 if ((lPrivSet = priv_allocset()) == NULL) {
301 return;
304 priv_emptyset(lPrivSet);
306 if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) {
307 return;
310 priv_freeset(lPrivSet);
313 int
314 main (int argc, char *argv[])
316 char *device_file, *raw_device_file;
317 DBusError error;
318 char *bus;
319 char *drive_type;
320 int state, last_state;
321 char *support_media_changed_str;
322 int support_media_changed;
323 int fd = -1;
325 if ((udi = getenv ("UDI")) == NULL)
326 goto out;
327 if ((device_file = getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL)
328 goto out;
329 if ((raw_device_file = getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL)
330 goto out;
331 if ((bus = getenv ("HAL_PROP_STORAGE_BUS")) == NULL)
332 goto out;
333 if ((drive_type = getenv ("HAL_PROP_STORAGE_DRIVE_TYPE")) == NULL)
334 goto out;
335 if ((devfs_path = getenv ("HAL_PROP_SOLARIS_DEVFS_PATH")) == NULL)
336 goto out;
338 drop_privileges ();
340 setup_logger ();
342 sysevent_init ();
344 support_media_changed_str = getenv ("HAL_PROP_STORAGE_CDROM_SUPPORT_MEDIA_CHANGED");
345 if (support_media_changed_str != NULL && strcmp (support_media_changed_str, "true") == 0)
346 support_media_changed = TRUE;
347 else
348 support_media_changed = FALSE;
350 dbus_error_init (&error);
352 if ((ctx = libhal_ctx_init_direct (&error)) == NULL) {
353 goto out;
355 my_dbus_error_free (&error);
357 if (!libhal_device_addon_is_ready (ctx, udi, &error)) {
358 goto out;
360 my_dbus_error_free (&error);
362 printf ("Doing addon-storage for %s (bus %s) (drive_type %s) (udi %s)\n", device_file, bus, drive_type, udi);
364 last_state = state = DKIO_NONE;
366 /* Linux version of this addon attempts to re-open the device O_EXCL
367 * every 2 seconds, trying to figure out if some other app,
368 * like a cd burner, is using the device. Aside from questionable
369 * value of this (apps should use HAL's locked property or/and
370 * Solaris in_use facility), but also frequent opens/closes
371 * keeps media constantly spun up. All this needs more thought.
373 for (;;) {
374 if (is_mounted (device_file)) {
375 close_device (&fd);
376 sleep (SLEEP_PERIOD);
377 } else if ((fd < 0) && ((fd = open (raw_device_file, O_RDONLY | O_NONBLOCK)) < 0)) {
378 HAL_DEBUG (("open failed for %s: %s", raw_device_file, strerror (errno)));
379 sleep (SLEEP_PERIOD);
380 } else {
381 /* Check if a disc is in the drive */
382 /* XXX initial call always returns inserted
383 * causing unnecessary rescan - optimize?
385 if (ioctl (fd, DKIOCSTATE, &state) == 0) {
386 if (state == last_state) {
387 HAL_DEBUG (("state has not changed %d %s", state, device_file));
388 continue;
389 } else {
390 HAL_DEBUG (("new state %d %s", state, device_file));
393 switch (state) {
394 case DKIO_EJECTED:
395 HAL_DEBUG (("Media removal detected on %s", device_file));
396 last_state = state;
398 libhal_device_set_property_bool (ctx, udi, "storage.removable.media_available", FALSE, &error);
399 my_dbus_error_free (&error);
401 /* attempt to unmount all childs */
402 unmount_childs (ctx, udi);
404 /* could have a fs on the main block device; do a rescan to remove it */
405 libhal_device_rescan (ctx, udi, &error);
406 my_dbus_error_free (&error);
407 break;
409 case DKIO_INSERTED:
410 HAL_DEBUG (("Media insertion detected on %s", device_file));
411 last_state = state;
413 libhal_device_set_property_bool (ctx, udi, "storage.removable.media_available", TRUE, &error);
414 my_dbus_error_free (&error);
416 /* could have a fs on the main block device; do a rescan to add it */
417 libhal_device_rescan (ctx, udi, &error);
418 my_dbus_error_free (&error);
419 break;
421 case DKIO_DEV_GONE:
422 HAL_DEBUG (("Device gone detected on %s", device_file));
423 last_state = state;
425 unmount_childs (ctx, udi);
426 close_device (&fd);
427 goto out;
429 case DKIO_NONE:
430 default:
431 break;
433 } else {
434 HAL_DEBUG (("DKIOCSTATE failed: %s\n", strerror(errno)));
435 sleep (SLEEP_PERIOD);
440 out:
441 sysevent_fini ();
442 if (ctx != NULL) {
443 my_dbus_error_free (&error);
444 libhal_ctx_shutdown (ctx, &error);
445 libhal_ctx_free (ctx);
448 return 0;