8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / hal / tools / hal-storage-shared.c
blob0bc3a94b96e2c5a96300cbc05025ad1098d1817e
1 /***************************************************************************
2 * CVSID: $Id: hal-storage-mount.c,v 1.7 2006/06/21 00:44:03 david Exp $
4 * hal-storage-mount.c : Mount wrapper
6 * Copyright (C) 2006 David Zeuthen, <david@fubar.dk>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 **************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 # include <config.h>
27 #endif
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <glib.h>
33 #include <glib/gstdio.h>
34 #ifdef __FreeBSD__
35 #include <fstab.h>
36 #include <sys/param.h>
37 #include <sys/ucred.h>
38 #include <sys/mount.h>
39 #include <limits.h>
40 #include <pwd.h>
41 #elif sun
42 #include <fcntl.h>
43 #include <sys/mnttab.h>
44 #include <sys/vfstab.h>
45 #include <sys/stat.h>
46 #include <sys/wait.h>
47 #include <bsm/adt.h>
48 #include <bsm/adt_event.h>
49 #else
50 #include <mntent.h>
51 #endif
52 #include <sys/types.h>
53 #include <unistd.h>
54 #include <sys/file.h>
55 #include <errno.h>
56 #include <syslog.h>
58 #include "hal-storage-shared.h"
60 #ifdef __FreeBSD__
61 struct mtab_handle
63 struct statfs *mounts;
64 int n_mounts;
65 int iter;
67 #endif
70 gboolean
71 mtab_open (gpointer *handle)
73 #ifdef __FreeBSD__
74 struct mtab_handle *mtab;
76 mtab = g_new0 (struct mtab_handle, 1);
77 mtab->n_mounts = getmntinfo (&mtab->mounts, MNT_NOWAIT);
78 if (mtab->n_mounts == 0) {
79 g_free (mtab);
80 return FALSE;
83 *handle = mtab;
84 return TRUE;
85 #elif sun
86 *handle = fopen (MNTTAB, "r");
87 return *handle != NULL;
88 #else
89 *handle = fopen ("/proc/mounts", "r");
90 return *handle != NULL;
91 #endif
94 char *
95 mtab_next (gpointer handle, char **mount_point)
97 #ifdef __FreeBSD__
98 struct mtab_handle *mtab = handle;
100 if (mtab->iter < mtab->n_mounts)
101 return mtab->mounts[mtab->iter++].f_mntfromname;
102 else
103 return NULL;
104 #error TODO: set *mount_point to g_strdup()-ed value if mount_point!=NULL
105 #elif sun
106 static struct mnttab mnt;
108 if (getmntent (handle, &mnt) == 0) {
109 if (mount_point != NULL) {
110 *mount_point = g_strdup (mnt.mnt_mountp);
112 return mnt.mnt_special;
113 } else {
114 return NULL;
116 #else
117 struct mntent *mnt;
119 mnt = getmntent (handle);
121 if (mnt != NULL) {
122 if (mount_point != NULL) {
123 *mount_point = g_strdup (mnt->mnt_dir);
125 return mnt->mnt_fsname;
126 } else {
127 return NULL;
129 #endif
132 void
133 mtab_close (gpointer handle)
135 #ifdef __FreeBSD__
136 g_free (handle);
137 #else
138 fclose (handle);
139 #endif
144 gboolean
145 fstab_open (gpointer *handle)
147 #ifdef __FreeBSD__
148 return setfsent () == 1;
149 #elif sun
150 *handle = fopen (VFSTAB, "r");
151 return *handle != NULL;
152 #else
153 *handle = fopen ("/etc/fstab", "r");
154 return *handle != NULL;
155 #endif
158 char *
159 fstab_next (gpointer handle, char **mount_point)
161 #ifdef __FreeBSD__
162 struct fstab *fstab;
164 fstab = getfsent ();
166 /* TODO: fill out mount_point */
167 if (mount_point != NULL && fstab != NULL) {
168 *mount_point = fstab->fs_file;
171 return fstab ? fstab->fs_spec : NULL;
172 #elif sun
173 static struct vfstab v;
175 return getvfsent (handle, &v) == 0 ? v.vfs_special : NULL;
176 #else
177 struct mntent *mnt;
179 mnt = getmntent (handle);
181 if (mount_point != NULL && mnt != NULL) {
182 *mount_point = mnt->mnt_dir;
185 return mnt ? mnt->mnt_fsname : NULL;
186 #endif
189 void
190 fstab_close (gpointer handle)
192 #ifdef __FreeBSD__
193 endfsent ();
194 #else
195 fclose (handle);
196 #endif
199 #ifdef __FreeBSD__
200 #define UMOUNT "/sbin/umount"
201 #elif sun
202 #define UMOUNT "/sbin/umount"
203 #else
204 #define UMOUNT "/bin/umount"
205 #endif
207 void
208 unknown_error (const char *detail)
210 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.UnknownFailure\n");
211 fprintf (stderr, "%s\n", detail);
212 exit (1);
216 static void
217 device_busy (const char *detail)
219 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.Busy\n");
220 fprintf (stderr, "%s\n", detail);
221 exit (1);
225 static void
226 not_mounted (const char *detail)
228 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.NotMounted\n");
229 fprintf (stderr, "%s\n", detail);
230 exit (1);
234 static void
235 not_mounted_by_hal (const char *detail)
237 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.NotMountedByHal\n");
238 fprintf (stderr, "%s\n", detail);
239 exit (1);
242 static void
243 permission_denied_privilege (const char *privilege, const char *uid)
245 fprintf (stderr, "org.freedesktop.Hal.Device.PermissionDeniedByPolicy\n");
246 fprintf (stderr, "%s refused uid %s\n", privilege, uid);
247 exit (1);
250 static void
251 permission_denied_volume_ignore (const char *device)
253 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.PermissionDenied\n");
254 fprintf (stderr, "Device has %s volume.ignore set to TRUE. Refusing to mount.\n", device);
255 exit (1);
258 void
259 handle_unmount (LibHalContext *hal_ctx,
260 #ifdef HAVE_POLKIT
261 LibPolKitContext *pol_ctx,
262 #endif
263 const char *udi,
264 LibHalVolume *volume, LibHalDrive *drive, const char *device,
265 const char *invoked_by_uid, const char *invoked_by_syscon_name,
266 gboolean option_lazy, gboolean option_force,
267 DBusConnection *system_bus)
269 int i, j;
270 DBusError error;
271 GError *err = NULL;
272 char *sout = NULL;
273 char *serr = NULL;
274 int exit_status;
275 char *args[10];
276 int na;
277 FILE *hal_mtab_orig;
278 int hal_mtab_orig_len;
279 int num_read;
280 char *hal_mtab_buf;
281 char **lines;
282 char *mount_point_to_unmount;
283 gboolean mounted_by_other_uid;
284 FILE *hal_mtab_new;
285 #ifdef sun
286 adt_export_data_t *adt_data;
287 size_t adt_data_size;
288 #endif
290 #ifdef DEBUG
291 printf ("device = %s\n", device);
292 printf ("invoked by uid = %s\n", invoked_by_uid);
293 printf ("invoked by system bus connection = %s\n", invoked_by_syscon_name);
294 #endif
296 if (volume != NULL) {
297 dbus_error_init (&error);
298 if (libhal_device_get_property_bool (hal_ctx, udi, "volume.ignore", &error) ||
299 dbus_error_is_set (&error)) {
300 if (dbus_error_is_set (&error)) {
301 LIBHAL_FREE_DBUS_ERROR (&error);
304 * When device allocation is enabled (bsmconv or TX), we
305 * set volume.ignore on all volumes, but still want
306 * Mount() to succeed when called from the euid=0
307 * device allocation program.
309 if (atol (invoked_by_uid) != 0) {
310 permission_denied_volume_ignore (device);
314 if (!libhal_volume_is_mounted (volume)) {
315 not_mounted ("According to HAL, the volume is not mounted");
320 /* check hal's mtab file to verify the device to unmount is actually mounted by hal */
321 hal_mtab_orig = fopen ("/media/.hal-mtab", "r");
322 if (hal_mtab_orig == NULL) {
323 unknown_error ("Cannot open /media/.hal-mtab");
325 if (fseek (hal_mtab_orig, 0L, SEEK_END) != 0) {
326 unknown_error ("Cannot seek to end of /media/.hal-mtab");
328 hal_mtab_orig_len = ftell (hal_mtab_orig);
329 if (hal_mtab_orig_len < 0) {
330 unknown_error ("Cannot determine size of /media/.hal-mtab");
332 rewind (hal_mtab_orig);
333 hal_mtab_buf = g_new0 (char, hal_mtab_orig_len + 1);
334 num_read = fread (hal_mtab_buf, 1, hal_mtab_orig_len, hal_mtab_orig);
335 if (num_read != hal_mtab_orig_len) {
336 unknown_error ("Cannot read from /media/.hal-mtab");
338 fclose (hal_mtab_orig);
340 #ifdef DEBUG
341 printf ("hal_mtab = '%s'\n", hal_mtab_buf);
342 #endif
344 lines = g_strsplit (hal_mtab_buf, "\n", 0);
345 g_free (hal_mtab_buf);
347 mount_point_to_unmount = NULL;
348 mounted_by_other_uid = TRUE;
350 /* find the entry we're going to unmount */
351 for (i = 0; lines[i] != NULL; i++) {
352 char **line_elements;
353 char *special, *dosp;
354 struct stat st;
356 #ifdef DEBUG
357 printf (" line = '%s'\n", lines[i]);
358 #endif
360 if ((lines[i])[0] == '#')
361 continue;
363 line_elements = g_strsplit (lines[i], "\t", 6);
364 if (g_strv_length (line_elements) == 6) {
366 #ifdef DEBUG
367 printf (" devfile = '%s'\n", line_elements[0]);
368 printf (" uid = '%s'\n", line_elements[1]);
369 printf (" session id = '%s'\n", line_elements[2]);
370 printf (" fs = '%s'\n", line_elements[3]);
371 printf (" options = '%s'\n", line_elements[4]);
372 printf (" mount_point = '%s'\n", line_elements[5]);
373 #endif
375 if (strcmp (line_elements[0], device) == 0) {
376 char *line_to_free;
378 if (strcmp (line_elements[1], invoked_by_uid) == 0) {
379 mounted_by_other_uid = FALSE;
381 #ifdef sun
382 if (stat("/dev/vt/console_user", &st) == 0 &&
383 st.st_uid == atoi (invoked_by_uid)) {
385 * Owner is allowed to take over. Before we have real
386 * ownership in HAL, assume it's the console owner.
388 mounted_by_other_uid = FALSE;
390 #endif /* sun */
391 mount_point_to_unmount = g_strdup (line_elements[5]);
393 line_to_free = lines[i];
395 for (j = i; lines[j] != NULL; j++) {
396 lines[j] = lines[j+1];
398 lines[j] = NULL;
400 g_free (line_to_free);
402 g_strfreev (line_elements);
403 goto line_found;
409 g_strfreev (line_elements);
411 line_found:
413 if (mount_point_to_unmount == NULL) {
414 not_mounted_by_hal ("Device to unmount is not in /media/.hal-mtab so it is not mounted by HAL");
417 /* bail out, unless if we got the "hal-storage-can-unmount-volumes-mounted-by-others" privilege only
418 * if mounted_by_other_uid==TRUE
420 * We allow uid 0 to actually ensure that Unmount(options=["lazy"], "/dev/blah") works from addon-storage.
422 if ((strcmp (invoked_by_uid, "0") != 0) && mounted_by_other_uid) {
423 /* TODO: actually check for privilege "hal-storage-can-unmount-volumes-mounted-by-others" */
424 permission_denied_privilege ("hal-storage-can-unmount-volumes-mounted-by-others", invoked_by_uid);
427 /* create new .hal-mtab~ file without the entry we're going to unmount */
428 hal_mtab_new = fopen ("/media/.hal-mtab~", "w");
429 if (hal_mtab_new == NULL) {
430 unknown_error ("Cannot create /media/.hal-mtab~");
432 for (i = 0; lines[i] != NULL; i++) {
433 if (i > 0) {
434 char anewl[2] = "\n\0";
435 if (fwrite (anewl, 1, 1, hal_mtab_new) != 1) {
436 unknown_error ("Cannot write to /media/.hal-mtab~");
440 if (fwrite (lines[i], 1, strlen (lines[i]), hal_mtab_new) != strlen (lines[i])) {
441 unknown_error ("Cannot write to /media/.hal-mtab~");
445 fclose (hal_mtab_new);
447 g_strfreev (lines);
449 /* construct arguments to /bin/umount */
450 na = 0;
451 args[na++] = UMOUNT;
452 if (option_lazy)
453 args[na++] = "-l";
454 if (option_force)
455 args[na++] = "-f";
456 args[na++] = (char *) device;
457 args[na++] = NULL;
459 #ifdef DEBUG
460 printf ("will umount %s (mounted at '%s'), mounted_by_other_uid=%d\n",
461 device, mount_point_to_unmount, mounted_by_other_uid);
462 #endif
464 /* invoke /bin/umount */
465 if (!g_spawn_sync ("/",
466 args,
467 NULL,
469 NULL,
470 NULL,
471 &sout,
472 &serr,
473 &exit_status,
474 &err)) {
475 printf ("Cannot execute %s\n", UMOUNT);
476 unlink ("/media/.hal-mtab~");
477 unknown_error ("Cannot spawn " UMOUNT);
480 /* check if unmount was succesful */
481 if (exit_status != 0) {
482 printf ("%s error %d, stdout='%s', stderr='%s'\n", UMOUNT, exit_status, sout, serr);
484 if (strstr (serr, "device is busy") != NULL) {
485 unlink ("/media/.hal-mtab~");
486 device_busy (serr);
487 } else {
488 unlink ("/media/.hal-mtab~");
489 unknown_error (serr);
493 #ifdef sun
494 if ((adt_data = get_audit_export_data (system_bus,
495 invoked_by_syscon_name, &adt_data_size)) != NULL) {
496 audit_volume (adt_data, ADT_detach, WEXITSTATUS(exit_status),
497 "solaris.device.mount.removable",
498 mount_point_to_unmount, device, NULL);
499 free (adt_data);
501 #endif
503 /* unmount was succesful, remove directory we created in Mount() */
504 #ifdef sun
505 if (strncmp (mount_point_to_unmount, "/media/", 7) == 0)
506 #endif
507 if (g_rmdir (mount_point_to_unmount) != 0) {
508 unlink ("/media/.hal-mtab~");
509 unknown_error ("Cannot remove directory");
512 /* set new .hal-mtab file */
513 if (rename ("/media/.hal-mtab~", "/media/.hal-mtab") != 0) {
514 unlink ("/media/.hal-mtab~");
515 unknown_error ("Cannot rename /media/.hal-mtab~ to /media/.hal-mtab");
518 #ifdef DEBUG
519 printf ("done unmounting\n");
520 #endif
521 openlog ("hald", 0, LOG_DAEMON);
522 syslog (LOG_INFO, "unmounted %s from '%s' on behalf of uid %s", device, mount_point_to_unmount, invoked_by_uid);
523 closelog ();
525 g_free (sout);
526 g_free (serr);
527 g_free (mount_point_to_unmount);
530 #define EJECT "/usr/bin/eject"
532 void
533 handle_eject (LibHalContext *hal_ctx,
534 #ifdef HAVE_POLKIT
535 LibPolKitContext *pol_ctx,
536 #endif
537 const char *udi,
538 LibHalDrive *drive, const char *device,
539 const char *invoked_by_uid, const char *invoked_by_syscon_name,
540 gboolean closetray, DBusConnection *system_bus)
542 GError *err = NULL;
543 char *sout = NULL;
544 char *serr = NULL;
545 int exit_status;
546 char *args[10];
547 int na;
548 #ifdef sun
549 adt_export_data_t *adt_data;
550 size_t adt_data_size;
551 #endif
552 /* TODO: should we require privileges here? */
554 #ifdef DEBUG
555 printf ("device = %s\n", device);
556 printf ("invoked by uid = %s\n", invoked_by_uid);
557 printf ("invoked by system bus connection = %s\n", invoked_by_syscon_name);
558 #endif
560 /* construct arguments to EJECT (e.g. /usr/bin/eject) */
561 na = 0;
562 args[na++] = EJECT;
563 if (closetray) {
564 args[na++] = "-t";
566 args[na++] = (char *) device;
567 args[na++] = NULL;
569 #ifdef sun
570 putenv("EJECT_DIRECT=1");
571 #endif
573 #ifdef DEBUG
574 printf ("will eject %s\n", device);
575 #endif
577 /* invoke eject command */
578 if (!g_spawn_sync ("/",
579 args,
580 NULL,
582 NULL,
583 NULL,
584 &sout,
585 &serr,
586 &exit_status,
587 &err)) {
588 printf ("Cannot execute %s\n", EJECT);
589 unknown_error ("Cannot spawn " EJECT);
592 #ifdef sun
594 * Solaris eject returns 4 for manually ejectable media like floppy.
595 * Consider it success.
597 if (WEXITSTATUS(exit_status) == 4) {
598 exit_status = 0;
601 if ((adt_data = get_audit_export_data (system_bus,
602 invoked_by_syscon_name, &adt_data_size)) != NULL) {
603 audit_volume (adt_data, ADT_remove, WEXITSTATUS(exit_status),
604 "solaris.device.mount.removable", NULL, device, NULL);
605 free (adt_data);
607 #endif /* sun */
609 /* check if eject was succesful */
610 if (exit_status != 0) {
611 printf ("%s error %d, stdout='%s', stderr='%s'\n", EJECT, exit_status, sout, serr);
613 unknown_error (serr);
616 /* eject was succesful... */
618 #ifdef DEBUG
619 printf ("done ejecting\n");
620 #endif
622 g_free (sout);
623 g_free (serr);
627 static int lock_mtab_fd = -1;
629 gboolean
630 lock_hal_mtab (void)
632 if (lock_mtab_fd >= 0)
633 return TRUE;
635 printf ("%d: XYA attempting to get lock on /media/.hal-mtab-lock\n", getpid ());
637 lock_mtab_fd = open ("/media/.hal-mtab-lock", O_CREAT | O_RDWR);
639 if (lock_mtab_fd < 0)
640 return FALSE;
642 tryagain:
643 #if sun
644 if (lockf (lock_mtab_fd, F_LOCK, 0) != 0) {
645 #else
646 if (flock (lock_mtab_fd, LOCK_EX) != 0) {
647 #endif
648 if (errno == EINTR)
649 goto tryagain;
650 return FALSE;
653 printf ("%d: XYA got lock on /media/.hal-mtab-lock\n", getpid ());
656 return TRUE;
659 void
660 unlock_hal_mtab (void)
662 #if sun
663 lockf (lock_mtab_fd, F_ULOCK, 0);
664 #else
665 flock (lock_mtab_fd, LOCK_UN);
666 #endif
667 close (lock_mtab_fd);
668 lock_mtab_fd = -1;
669 printf ("%d: XYA released lock on /media/.hal-mtab-lock\n", getpid ());
672 #if sun
674 /* map PolicyKit privilege to RBAC authorization */
675 char *
676 auth_from_privilege(const char *privilege)
678 char *authname;
679 int i;
681 if (strcmp (privilege, "hal-storage-removable-mount") == 0) {
682 authname = g_strdup ("solaris.device.mount.removable");
683 } else if (strcmp (privilege, "hal-storage-removable-mount-all-options") == 0) {
684 authname = g_strdup ("solaris.device.mount.alloptions.removable");
685 } else if (strcmp (privilege, "hal-storage-fixed-mount") == 0) {
686 authname = g_strdup ("solaris.device.mount.fixed");
687 } else if (strcmp (privilege, "hal-storage-fixed-mount-all-options") == 0) {
688 authname = g_strdup ("solaris.device.mount.alloptions.fixed");
689 } else {
690 /* replace '-' with '.' */
691 authname = g_strdup (privilege);
692 for (i = 0; i < strlen (authname); i++) {
693 if (authname[i] == '-') {
694 authname[i] = '.';
698 return (authname);
701 void
702 audit_volume(const adt_export_data_t *imported_state, au_event_t event_id,
703 int result, const char *auth_used, const char *mount_point,
704 const char *device, const char *options)
706 adt_session_data_t *ah;
707 adt_event_data_t *event;
709 if (adt_start_session(&ah, imported_state, 0) != 0) {
710 printf ("adt_start_session failed %d\n", errno);
711 return;
713 if ((event = adt_alloc_event(ah, event_id)) == NULL) {
714 printf ("adt_alloc_event(ADT_attach)\n", errno);
715 return;
718 switch (event_id) {
719 case ADT_attach:
720 event->adt_attach.auth_used = (char *)auth_used;
721 event->adt_attach.mount_point = (char *)mount_point;
722 event->adt_attach.device = (char *)device;
723 event->adt_attach.options = (char *)options;
724 break;
725 case ADT_detach:
726 event->adt_detach.auth_used = (char *)auth_used;
727 event->adt_detach.mount_point = (char *)mount_point;
728 event->adt_detach.device = (char *)device;
729 event->adt_detach.options = (char *)options;
730 break;
731 case ADT_remove:
732 event->adt_remove.auth_used = (char *)auth_used;
733 event->adt_remove.mount_point = (char *)mount_point;
734 event->adt_remove.device = (char *)device;
735 break;
736 default:
737 goto out;
740 if (result == 0) {
741 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
742 printf ("adt_put_event(%d, success)\n", event_id);
744 } else {
745 if (adt_put_event(event, ADT_FAILURE, result) != 0) {
746 printf ("adt_put_event(%d, failure)\n", event_id);
749 out:
750 adt_free_event(event);
751 (void) adt_end_session(ah);
754 #endif /* sun */