summaryrefslogtreecommitdiff
path: root/usr/src/cmd/rmvolmgr
diff options
context:
space:
mode:
authorartem <none@none>2006-10-12 22:44:10 -0700
committerartem <none@none>2006-10-12 22:44:10 -0700
commit18c2aff776a775d34a4c9893a4c72e0434d68e36 (patch)
tree2ce07e824c6b4db04bfedb4dea79bc5f255851ed /usr/src/cmd/rmvolmgr
parent38787b9f841232f52e2f48dd8a9e475d09954391 (diff)
downloadillumos-joyent-18c2aff776a775d34a4c9893a4c72e0434d68e36.tar.gz
PSARC 2005/399 Tamarack: Removable Media Enhancements in Solaris
6460497 Tamarack (ON) 6460498 vold EOF removal --HG-- rename : usr/src/cmd/fs.d/hsfs/ident/Makefile => deleted_files/usr/src/cmd/fs.d/hsfs/ident/Makefile rename : usr/src/cmd/fs.d/hsfs/ident/ident_hsfs.c => deleted_files/usr/src/cmd/fs.d/hsfs/ident/ident_hsfs.c rename : usr/src/cmd/fs.d/pcfs/ident/Makefile => deleted_files/usr/src/cmd/fs.d/pcfs/ident/Makefile rename : usr/src/cmd/fs.d/pcfs/ident/ident_pcfs.c => deleted_files/usr/src/cmd/fs.d/pcfs/ident/ident_pcfs.c rename : usr/src/cmd/fs.d/udfs/ident/Makefile => deleted_files/usr/src/cmd/fs.d/udfs/ident/Makefile rename : usr/src/cmd/fs.d/udfs/ident/ident_udfs.c => deleted_files/usr/src/cmd/fs.d/udfs/ident/ident_udfs.c rename : usr/src/cmd/fs.d/ufs/ident/Makefile => deleted_files/usr/src/cmd/fs.d/ufs/ident/Makefile rename : usr/src/cmd/fs.d/ufs/ident/ident_ufs.c => deleted_files/usr/src/cmd/fs.d/ufs/ident/ident_ufs.c rename : usr/src/cmd/initpkg/init.d/volmgt => deleted_files/usr/src/cmd/initpkg/init.d/volmgt rename : usr/src/cmd/volmgt/Makefile => deleted_files/usr/src/cmd/volmgt/Makefile rename : usr/src/cmd/volmgt/Makefile.volmgt => deleted_files/usr/src/cmd/volmgt/Makefile.volmgt rename : usr/src/cmd/volmgt/etc/Makefile => deleted_files/usr/src/cmd/volmgt/etc/Makefile rename : usr/src/cmd/volmgt/etc/rmmount.conf => deleted_files/usr/src/cmd/volmgt/etc/rmmount.conf rename : usr/src/cmd/volmgt/etc/svc-volfs => deleted_files/usr/src/cmd/volmgt/etc/svc-volfs rename : usr/src/cmd/volmgt/etc/vold.conf => deleted_files/usr/src/cmd/volmgt/etc/vold.conf rename : usr/src/cmd/volmgt/etc/volfs.xml => deleted_files/usr/src/cmd/volmgt/etc/volfs.xml rename : usr/src/cmd/volmgt/req.flg => deleted_files/usr/src/cmd/volmgt/req.flg rename : usr/src/cmd/volmgt/rmm/Makefile => deleted_files/usr/src/cmd/volmgt/rmm/Makefile rename : usr/src/cmd/volmgt/rmm/action_dvdvideo.c => deleted_files/usr/src/cmd/volmgt/rmm/action_dvdvideo.c rename : usr/src/cmd/volmgt/rmm/action_filemgr.c => deleted_files/usr/src/cmd/volmgt/rmm/action_filemgr.c rename : usr/src/cmd/volmgt/rmm/action_test.c => deleted_files/usr/src/cmd/volmgt/rmm/action_test.c rename : usr/src/cmd/volmgt/rmm/action_wabi.c => deleted_files/usr/src/cmd/volmgt/rmm/action_wabi.c rename : usr/src/cmd/volmgt/rmm/action_workman.c => deleted_files/usr/src/cmd/volmgt/rmm/action_workman.c rename : usr/src/cmd/volmgt/rmm/action_xmcd.c => deleted_files/usr/src/cmd/volmgt/rmm/action_xmcd.c rename : usr/src/cmd/volmgt/rmm/req.flg => deleted_files/usr/src/cmd/volmgt/rmm/req.flg rename : usr/src/cmd/volmgt/rmm/rmm.c => deleted_files/usr/src/cmd/volmgt/rmm/rmm.c rename : usr/src/cmd/volmgt/rmm/rmm_config.c => deleted_files/usr/src/cmd/volmgt/rmm/rmm_config.c rename : usr/src/cmd/volmgt/rmm/rmm_int.h => deleted_files/usr/src/cmd/volmgt/rmm/rmm_int.h rename : usr/src/cmd/volmgt/rmm/rmm_util.c => deleted_files/usr/src/cmd/volmgt/rmm/rmm_util.c rename : usr/src/cmd/volmgt/test/Makefile => deleted_files/usr/src/cmd/volmgt/test/Makefile rename : usr/src/cmd/volmgt/test/README => deleted_files/usr/src/cmd/volmgt/test/README rename : usr/src/cmd/volmgt/test/devlink.vt => deleted_files/usr/src/cmd/volmgt/test/devlink.vt rename : usr/src/cmd/volmgt/test/stress => deleted_files/usr/src/cmd/volmgt/test/stress rename : usr/src/cmd/volmgt/test/test_class_list => deleted_files/usr/src/cmd/volmgt/test/test_class_list rename : usr/src/cmd/volmgt/test/test_suite_list => deleted_files/usr/src/cmd/volmgt/test/test_suite_list rename : usr/src/cmd/volmgt/test/test_utilities_list => deleted_files/usr/src/cmd/volmgt/test/test_utilities_list rename : usr/src/cmd/volmgt/test/voltestdrv.c => deleted_files/usr/src/cmd/volmgt/test/voltestdrv.c rename : usr/src/cmd/volmgt/test/voltestdrv.conf => deleted_files/usr/src/cmd/volmgt/test/voltestdrv.conf rename : usr/src/cmd/volmgt/test/voltestdrv.h => deleted_files/usr/src/cmd/volmgt/test/voltestdrv.h rename : usr/src/cmd/volmgt/test/vttest.c => deleted_files/usr/src/cmd/volmgt/test/vttest.c rename : usr/src/cmd/volmgt/util/Makefile => deleted_files/usr/src/cmd/volmgt/util/Makefile rename : usr/src/cmd/volmgt/util/volcancel.c => deleted_files/usr/src/cmd/volmgt/util/volcancel.c rename : usr/src/cmd/volmgt/util/volck.c => deleted_files/usr/src/cmd/volmgt/util/volck.c rename : usr/src/cmd/volmgt/util/volmissing.c => deleted_files/usr/src/cmd/volmgt/util/volmissing.c rename : usr/src/cmd/volmgt/util/volrmmount.c => deleted_files/usr/src/cmd/volmgt/util/volrmmount.c rename : usr/src/cmd/volmgt/util/volsetup => deleted_files/usr/src/cmd/volmgt/util/volsetup rename : usr/src/cmd/volmgt/util/volstat.c => deleted_files/usr/src/cmd/volmgt/util/volstat.c rename : usr/src/cmd/volmgt/util/volutil.h => deleted_files/usr/src/cmd/volmgt/util/volutil.h rename : usr/src/cmd/volmgt/vold/Makefile => deleted_files/usr/src/cmd/volmgt/vold/Makefile rename : usr/src/cmd/volmgt/vold/action.h => deleted_files/usr/src/cmd/volmgt/vold/action.h rename : usr/src/cmd/volmgt/vold/blank_partition.c => deleted_files/usr/src/cmd/volmgt/vold/blank_partition.c rename : usr/src/cmd/volmgt/vold/db.h => deleted_files/usr/src/cmd/volmgt/vold/db.h rename : usr/src/cmd/volmgt/vold/db_mem.c => deleted_files/usr/src/cmd/volmgt/vold/db_mem.c rename : usr/src/cmd/volmgt/vold/db_nis.c => deleted_files/usr/src/cmd/volmgt/vold/db_nis.c rename : usr/src/cmd/volmgt/vold/db_nis.h => deleted_files/usr/src/cmd/volmgt/vold/db_nis.h rename : usr/src/cmd/volmgt/vold/dev.h => deleted_files/usr/src/cmd/volmgt/vold/dev.h rename : usr/src/cmd/volmgt/vold/dev_cdrom.c => deleted_files/usr/src/cmd/volmgt/vold/dev_cdrom.c rename : usr/src/cmd/volmgt/vold/dev_cdtest.c => deleted_files/usr/src/cmd/volmgt/vold/dev_cdtest.c rename : usr/src/cmd/volmgt/vold/dev_floppy.c => deleted_files/usr/src/cmd/volmgt/vold/dev_floppy.c rename : usr/src/cmd/volmgt/vold/dev_pcmem.c => deleted_files/usr/src/cmd/volmgt/vold/dev_pcmem.c rename : usr/src/cmd/volmgt/vold/dev_rmdisk.c => deleted_files/usr/src/cmd/volmgt/vold/dev_rmdisk.c rename : usr/src/cmd/volmgt/vold/dev_rmscsi.c => deleted_files/usr/src/cmd/volmgt/vold/dev_rmscsi.c rename : usr/src/cmd/volmgt/vold/dev_test.c => deleted_files/usr/src/cmd/volmgt/vold/dev_test.c rename : usr/src/cmd/volmgt/vold/fdisk_partition.c => deleted_files/usr/src/cmd/volmgt/vold/fdisk_partition.c rename : usr/src/cmd/volmgt/vold/hsfs_partition.c => deleted_files/usr/src/cmd/volmgt/vold/hsfs_partition.c rename : usr/src/cmd/volmgt/vold/label.h => deleted_files/usr/src/cmd/volmgt/vold/label.h rename : usr/src/cmd/volmgt/vold/label_cdrom.c => deleted_files/usr/src/cmd/volmgt/vold/label_cdrom.c rename : usr/src/cmd/volmgt/vold/label_dos.c => deleted_files/usr/src/cmd/volmgt/vold/label_dos.c rename : usr/src/cmd/volmgt/vold/label_sun.c => deleted_files/usr/src/cmd/volmgt/vold/label_sun.c rename : usr/src/cmd/volmgt/vold/label_test.c => deleted_files/usr/src/cmd/volmgt/vold/label_test.c rename : usr/src/cmd/volmgt/vold/medium.c => deleted_files/usr/src/cmd/volmgt/vold/medium.c rename : usr/src/cmd/volmgt/vold/medium.h => deleted_files/usr/src/cmd/volmgt/vold/medium.h rename : usr/src/cmd/volmgt/vold/medium_private.h => deleted_files/usr/src/cmd/volmgt/vold/medium_private.h rename : usr/src/cmd/volmgt/vold/name_factory.c => deleted_files/usr/src/cmd/volmgt/vold/name_factory.c rename : usr/src/cmd/volmgt/vold/name_factory.h => deleted_files/usr/src/cmd/volmgt/vold/name_factory.h rename : usr/src/cmd/volmgt/vold/nfs_server.c => deleted_files/usr/src/cmd/volmgt/vold/nfs_server.c rename : usr/src/cmd/volmgt/vold/nfs_trace.c => deleted_files/usr/src/cmd/volmgt/vold/nfs_trace.c rename : usr/src/cmd/volmgt/vold/node.h => deleted_files/usr/src/cmd/volmgt/vold/node.h rename : usr/src/cmd/volmgt/vold/obj.h => deleted_files/usr/src/cmd/volmgt/vold/obj.h rename : usr/src/cmd/volmgt/vold/partition.c => deleted_files/usr/src/cmd/volmgt/vold/partition.c rename : usr/src/cmd/volmgt/vold/partition.h => deleted_files/usr/src/cmd/volmgt/vold/partition.h rename : usr/src/cmd/volmgt/vold/partition_private.h => deleted_files/usr/src/cmd/volmgt/vold/partition_private.h rename : usr/src/cmd/volmgt/vold/pcfs_partition.c => deleted_files/usr/src/cmd/volmgt/vold/pcfs_partition.c rename : usr/src/cmd/volmgt/vold/solaris_partition.c => deleted_files/usr/src/cmd/volmgt/vold/solaris_partition.c rename : usr/src/cmd/volmgt/vold/udfs_partition.c => deleted_files/usr/src/cmd/volmgt/vold/udfs_partition.c rename : usr/src/cmd/volmgt/vold/ufs_partition.c => deleted_files/usr/src/cmd/volmgt/vold/ufs_partition.c rename : usr/src/cmd/volmgt/vold/util.h => deleted_files/usr/src/cmd/volmgt/vold/util.h rename : usr/src/cmd/volmgt/vold/vold.h => deleted_files/usr/src/cmd/volmgt/vold/vold.h rename : usr/src/cmd/volmgt/vold/vold_action.c => deleted_files/usr/src/cmd/volmgt/vold/vold_action.c rename : usr/src/cmd/volmgt/vold/vold_config.c => deleted_files/usr/src/cmd/volmgt/vold/vold_config.c rename : usr/src/cmd/volmgt/vold/vold_db.c => deleted_files/usr/src/cmd/volmgt/vold/vold_db.c rename : usr/src/cmd/volmgt/vold/vold_dev.c => deleted_files/usr/src/cmd/volmgt/vold/vold_dev.c rename : usr/src/cmd/volmgt/vold/vold_err.c => deleted_files/usr/src/cmd/volmgt/vold/vold_err.c rename : usr/src/cmd/volmgt/vold/vold_label.c => deleted_files/usr/src/cmd/volmgt/vold/vold_label.c rename : usr/src/cmd/volmgt/vold/vold_main.c => deleted_files/usr/src/cmd/volmgt/vold/vold_main.c rename : usr/src/cmd/volmgt/vold/vold_mnt.c => deleted_files/usr/src/cmd/volmgt/vold/vold_mnt.c rename : usr/src/cmd/volmgt/vold/vold_node.c => deleted_files/usr/src/cmd/volmgt/vold/vold_node.c rename : usr/src/cmd/volmgt/vold/vold_obj.c => deleted_files/usr/src/cmd/volmgt/vold/vold_obj.c rename : usr/src/cmd/volmgt/vold/vold_path.c => deleted_files/usr/src/cmd/volmgt/vold/vold_path.c rename : usr/src/cmd/volmgt/vold/vold_proc.c => deleted_files/usr/src/cmd/volmgt/vold/vold_proc.c rename : usr/src/cmd/volmgt/vold/vold_props.c => deleted_files/usr/src/cmd/volmgt/vold/vold_props.c rename : usr/src/cmd/volmgt/vold/vold_sysevent.c => deleted_files/usr/src/cmd/volmgt/vold/vold_sysevent.c rename : usr/src/cmd/volmgt/vold/vold_util.c => deleted_files/usr/src/cmd/volmgt/vold/vold_util.c rename : usr/src/cmd/volmgt/vold/vold_vol.c => deleted_files/usr/src/cmd/volmgt/vold/vold_vol.c rename : usr/src/cmd/volmgt/vold/vtoc.c => deleted_files/usr/src/cmd/volmgt/vold/vtoc.c rename : usr/src/cmd/volmgt/vold/vtoc.h => deleted_files/usr/src/cmd/volmgt/vold/vtoc.h rename : usr/src/head/rmmount.h => deleted_files/usr/src/head/rmmount.h rename : usr/src/lib/libvolmgt/common/volattr.c => deleted_files/usr/src/lib/libvolmgt/common/volattr.c rename : usr/src/lib/libvolmgt/common/volmgt_fsi.c => deleted_files/usr/src/lib/libvolmgt/common/volmgt_fsi.c rename : usr/src/lib/libvolmgt/common/volmgt_fsi_private.h => deleted_files/usr/src/lib/libvolmgt/common/volmgt_fsi_private.h rename : usr/src/lib/libvolmgt/common/volmgt_fsidbi.c => deleted_files/usr/src/lib/libvolmgt/common/volmgt_fsidbi.c rename : usr/src/lib/libvolmgt/common/volname.c => deleted_files/usr/src/lib/libvolmgt/common/volname.c rename : usr/src/lib/libvolmgt/common/volutil.c => deleted_files/usr/src/lib/libvolmgt/common/volutil.c rename : usr/src/pkgdefs/SUNWpcmem/preinstall => deleted_files/usr/src/pkgdefs/SUNWpcmem/preinstall rename : usr/src/pkgdefs/SUNWvolr/Makefile => deleted_files/usr/src/pkgdefs/SUNWvolr/Makefile rename : usr/src/pkgdefs/SUNWvolr/pkginfo.tmpl => deleted_files/usr/src/pkgdefs/SUNWvolr/pkginfo.tmpl rename : usr/src/pkgdefs/SUNWvolr/postinstall => deleted_files/usr/src/pkgdefs/SUNWvolr/postinstall rename : usr/src/pkgdefs/SUNWvolr/preinstall => deleted_files/usr/src/pkgdefs/SUNWvolr/preinstall rename : usr/src/pkgdefs/SUNWvolr/prototype_com => deleted_files/usr/src/pkgdefs/SUNWvolr/prototype_com rename : usr/src/pkgdefs/SUNWvolr/prototype_i386 => deleted_files/usr/src/pkgdefs/SUNWvolr/prototype_i386 rename : usr/src/pkgdefs/SUNWvolr/prototype_sparc => deleted_files/usr/src/pkgdefs/SUNWvolr/prototype_sparc rename : usr/src/pkgdefs/SUNWvolu/Makefile => deleted_files/usr/src/pkgdefs/SUNWvolu/Makefile rename : usr/src/pkgdefs/SUNWvolu/depend => deleted_files/usr/src/pkgdefs/SUNWvolu/depend rename : usr/src/pkgdefs/SUNWvolu/pkginfo.tmpl => deleted_files/usr/src/pkgdefs/SUNWvolu/pkginfo.tmpl rename : usr/src/pkgdefs/SUNWvolu/postremove => deleted_files/usr/src/pkgdefs/SUNWvolu/postremove rename : usr/src/pkgdefs/SUNWvolu/prototype_com => deleted_files/usr/src/pkgdefs/SUNWvolu/prototype_com rename : usr/src/pkgdefs/SUNWvolu/prototype_i386 => deleted_files/usr/src/pkgdefs/SUNWvolu/prototype_i386 rename : usr/src/pkgdefs/SUNWvolu/prototype_sparc => deleted_files/usr/src/pkgdefs/SUNWvolu/prototype_sparc rename : usr/src/pkgdefs/common_files/i.rmmconf => deleted_files/usr/src/pkgdefs/common_files/i.rmmconf rename : usr/src/pkgdefs/common_files/i.voldconf => deleted_files/usr/src/pkgdefs/common_files/i.voldconf rename : usr/src/uts/common/io/vol.c => deleted_files/usr/src/uts/common/io/vol.c rename : usr/src/uts/common/io/vol.conf => deleted_files/usr/src/uts/common/io/vol.conf rename : usr/src/uts/common/sys/vol.h => deleted_files/usr/src/uts/common/sys/vol.h rename : usr/src/uts/intel/vol/Makefile => deleted_files/usr/src/uts/intel/vol/Makefile rename : usr/src/uts/sparc/vol/Makefile => deleted_files/usr/src/uts/sparc/vol/Makefile rename : usr/src/cmd/volmgt/util/eject.c => usr/src/cmd/eject/eject.c rename : usr/src/cmd/volmgt/util/volcheck.c => usr/src/cmd/volcheck/volcheck.c
Diffstat (limited to 'usr/src/cmd/rmvolmgr')
-rw-r--r--usr/src/cmd/rmvolmgr/Makefile72
-rw-r--r--usr/src/cmd/rmvolmgr/rmm_common.c1355
-rw-r--r--usr/src/cmd/rmvolmgr/rmm_common.h106
-rw-r--r--usr/src/cmd/rmvolmgr/rmvolmgr.c607
-rw-r--r--usr/src/cmd/rmvolmgr/rmvolmgr.xml118
-rw-r--r--usr/src/cmd/rmvolmgr/svc-rmvolmgr56
-rw-r--r--usr/src/cmd/rmvolmgr/vold.c1089
-rw-r--r--usr/src/cmd/rmvolmgr/vold.h73
8 files changed, 3476 insertions, 0 deletions
diff --git a/usr/src/cmd/rmvolmgr/Makefile b/usr/src/cmd/rmvolmgr/Makefile
new file mode 100644
index 0000000000..a4ffac4951
--- /dev/null
+++ b/usr/src/cmd/rmvolmgr/Makefile
@@ -0,0 +1,72 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG = rmvolmgr
+OBJS = rmm_common.o rmvolmgr.o vold.o
+SRCS = $(OBJS:%.o=%.c)
+
+MANIFEST = rmvolmgr.xml
+SVCMETHOD = svc-rmvolmgr
+
+include ../Makefile.cmd
+include ../hal/Makefile.hal
+
+POFILE=rmvolmgr_all.po
+POFILES=$(OBJS:%.o=%.po)
+
+LDLIBS += -ldbus-1 -ldbus-glib-1 -lglib-2.0 -lhal -lhal-storage -lcontract -lscf
+
+CPPFLAGS += $(HAL_DBUS_CPPFLAGS) $(HAL_GLIB_CPPFLAGS)
+CPPFLAGS += -I$(ROOT)/usr/include/hal
+C99MODE = $(C99_ENABLE)
+
+ROOTCMDDIR = $(ROOTLIB)
+ROOTMANIFESTDIR = $(ROOTSVCSYSTEMFILESYSTEM)
+$(ROOTMANIFEST) := FILEMODE = 444
+$(ROOTLIBSVCMETHOD)/svc-rmvolmgr:= FILEMODE = 555
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
+
+install: all $(ROOTCMD) $(ROOTMANIFEST) $(ROOTSVCMETHOD)
+
+clean:
+ $(RM) $(OBJS)
+
+check: $(CHKMANIFEST)
+
+$(POFILE): $(POFILES)
+ $(RM) $@
+ $(CAT) $(POFILES) > $@
+
+include ../Makefile.targ
+
diff --git a/usr/src/cmd/rmvolmgr/rmm_common.c b/usr/src/cmd/rmvolmgr/rmm_common.c
new file mode 100644
index 0000000000..4859e8e2c2
--- /dev/null
+++ b/usr/src/cmd/rmvolmgr/rmm_common.c
@@ -0,0 +1,1355 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mnttab.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <libhal.h>
+#include <libhal-storage.h>
+
+#include "rmm_common.h"
+
+extern int rmm_debug;
+
+static const char *action_strings[] = {
+ "eject",
+ "mount",
+ "remount",
+ "unmount",
+ "clear_mounts",
+ "closetray"
+};
+
+
+LibHalContext *
+rmm_hal_init(LibHalDeviceAdded devadd_cb, LibHalDeviceRemoved devrem_cb,
+ LibHalDevicePropertyModified propmod_cb,
+ DBusError *error, rmm_error_t *rmm_error)
+{
+ DBusConnection *dbus_conn;
+ LibHalContext *ctx;
+ char **devices;
+ int nr;
+
+ dbus_error_init(error);
+
+ /*
+ * setup D-Bus connection
+ */
+ if (!(dbus_conn = dbus_bus_get(DBUS_BUS_SYSTEM, error))) {
+ dprintf("cannot get system bus: %s\n", rmm_strerror(error, -1));
+ *rmm_error = RMM_EDBUS_CONNECT;
+ return (NULL);
+ }
+ rmm_dbus_error_free(error);
+
+ dbus_connection_setup_with_g_main(dbus_conn, NULL);
+ dbus_connection_set_exit_on_disconnect(dbus_conn, B_TRUE);
+
+ if ((ctx = libhal_ctx_new()) == NULL) {
+ dprintf("libhal_ctx_new failed");
+ *rmm_error = RMM_EHAL_CONNECT;
+ return (NULL);
+ }
+
+ libhal_ctx_set_dbus_connection(ctx, dbus_conn);
+
+ /*
+ * register callbacks
+ */
+ if (devadd_cb != NULL) {
+ libhal_ctx_set_device_added(ctx, devadd_cb);
+ }
+ if (devrem_cb != NULL) {
+ libhal_ctx_set_device_removed(ctx, devrem_cb);
+ }
+ if (propmod_cb != NULL) {
+ libhal_ctx_set_device_property_modified(ctx, propmod_cb);
+ if (!libhal_device_property_watch_all(ctx, error)) {
+ dprintf("property_watch_all failed %s",
+ rmm_strerror(error, -1));
+ libhal_ctx_free(ctx);
+ *rmm_error = RMM_EHAL_CONNECT;
+ return (NULL);
+ }
+ }
+
+ if (!libhal_ctx_init(ctx, error)) {
+ dprintf("libhal_ctx_init failed: %s", rmm_strerror(error, -1));
+ libhal_ctx_free(ctx);
+ *rmm_error = RMM_EHAL_CONNECT;
+ return (NULL);
+ }
+ rmm_dbus_error_free(error);
+
+ /*
+ * The above functions do not guarantee that HAL is actually running.
+ * Check by invoking a method.
+ */
+ if (!(devices = libhal_get_all_devices(ctx, &nr, error))) {
+ dprintf("HAL is not running: %s", rmm_strerror(error, -1));
+ libhal_ctx_shutdown(ctx, NULL);
+ libhal_ctx_free(ctx);
+ *rmm_error = RMM_EHAL_CONNECT;
+ return (NULL);
+ } else {
+ rmm_dbus_error_free(error);
+ libhal_free_string_array(devices);
+ }
+
+ return (ctx);
+}
+
+
+void
+rmm_hal_fini(LibHalContext *hal_ctx)
+{
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+
+ (void) dbus_connection_close(dbus_conn);
+ (void) libhal_ctx_free(hal_ctx);
+}
+
+
+/*
+ * find volume from any type of name, similar to the old media_findname()
+ * returns the LibHalDrive object and a list of LibHalVolume objects.
+ */
+LibHalDrive *
+rmm_hal_volume_find(LibHalContext *hal_ctx, const char *name, DBusError *error,
+ GSList **volumes)
+{
+ LibHalDrive *drive;
+ char *p;
+ char lastc;
+
+ *volumes = NULL;
+
+ /* temporarily remove trailing slash */
+ p = (char *)name + strlen(name) - 1;
+ if (*p == '/') {
+ lastc = *p;
+ *p = '\0';
+ } else {
+ p = NULL;
+ }
+
+ if (name[0] == '/') {
+ if (((drive = rmm_hal_volume_findby(hal_ctx,
+ "info.udi", name, volumes)) != NULL) ||
+ ((drive = rmm_hal_volume_findby(hal_ctx,
+ "block.device", name, volumes)) != NULL) ||
+ ((drive = rmm_hal_volume_findby(hal_ctx,
+ "block.solaris.raw_device", name, volumes)) != NULL) ||
+ ((drive = rmm_hal_volume_findby(hal_ctx,
+ "volume.mount_point", name, volumes)) != NULL)) {
+ goto out;
+ } else {
+ goto out;
+ }
+ }
+
+ /* try volume label */
+ if ((drive = rmm_hal_volume_findby(hal_ctx,
+ "volume.label", name, volumes)) != NULL) {
+ goto out;
+ }
+
+ drive = rmm_hal_volume_findby_nickname(hal_ctx, name, volumes);
+
+out:
+ if (p != NULL) {
+ *p = lastc;
+ }
+ return (drive);
+}
+
+/*
+ * find default volume. Returns volume pointer and name in 'name'.
+ */
+LibHalDrive *
+rmm_hal_volume_find_default(LibHalContext *hal_ctx, DBusError *error,
+ const char **name_out, GSList **volumes)
+{
+ LibHalDrive *drive;
+ static const char *names[] = { "floppy", "cdrom", "rmdisk" };
+ int i;
+
+ *volumes = NULL;
+
+ for (i = 0; i < NELEM(names); i++) {
+ if ((drive = rmm_hal_volume_findby_nickname(hal_ctx,
+ names[i], volumes)) != NULL) {
+ /*
+ * Skip floppy if it has no media.
+ * XXX might want to actually check for media
+ * every time instead of relying on volcheck.
+ */
+ if ((strcmp(names[i], "floppy") != 0) ||
+ libhal_device_get_property_bool(hal_ctx,
+ libhal_drive_get_udi(drive),
+ "storage.removable.media_available", NULL)) {
+ *name_out = names[i];
+ break;
+ }
+ }
+ rmm_dbus_error_free(error);
+ }
+
+ return (drive);
+}
+
+/*
+ * find volume by property=value
+ * returns the LibHalDrive object and a list of LibHalVolume objects.
+ * XXX add support for multiple properties, reduce D-Bus traffic
+ */
+LibHalDrive *
+rmm_hal_volume_findby(LibHalContext *hal_ctx, const char *property,
+ const char *value, GSList **volumes)
+{
+ DBusError error;
+ LibHalDrive *drive = NULL;
+ LibHalVolume *v = NULL;
+ char **udis;
+ int num_udis;
+ int i;
+
+ *volumes = NULL;
+
+ dbus_error_init(&error);
+
+ /* get all devices with property=value */
+ if ((udis = libhal_manager_find_device_string_match(hal_ctx, property,
+ value, &num_udis, &error)) == NULL) {
+ rmm_dbus_error_free(&error);
+ return (NULL);
+ }
+
+ /* find volumes among these devices */
+ for (i = 0; i < num_udis; i++) {
+ rmm_dbus_error_free(&error);
+ if (libhal_device_query_capability(hal_ctx, udis[i], "volume",
+ &error)) {
+ v = libhal_volume_from_udi(hal_ctx, udis[i]);
+ if (v != NULL) {
+ *volumes = g_slist_prepend(*volumes, v);
+ }
+ }
+ }
+
+ /* used prepend, preserve original order */
+ if (*volumes != NULL) {
+ *volumes = g_slist_reverse(*volumes);
+
+ v = (LibHalVolume *)(*volumes)->data;
+ drive = libhal_drive_from_udi(hal_ctx,
+ libhal_volume_get_storage_device_udi(v));
+ if (drive == NULL) {
+ rmm_volumes_free (*volumes);
+ *volumes = NULL;
+ }
+ }
+
+ libhal_free_string_array(udis);
+ rmm_dbus_error_free(&error);
+
+ return (drive);
+}
+
+
+/*
+ * print nicknames for each available volume
+ */
+void
+rmm_print_volume_nicknames(LibHalContext *hal_ctx, DBusError *error)
+{
+ char **udis;
+ int num_udis;
+ char *block_device;
+ char *drive_udi;
+ char *volume_label;
+ char *mount_point;
+ boolean_t comma;
+ char **nicknames;
+ int i, j;
+
+ dbus_error_init(error);
+
+ if ((udis = libhal_find_device_by_capability(hal_ctx, "volume",
+ &num_udis, error)) == NULL) {
+ return;
+ }
+
+ for (i = 0; i < num_udis; i++) {
+ if ((block_device = libhal_device_get_property_string(hal_ctx,
+ udis[i], "block.device", NULL)) == NULL) {
+ continue;
+ }
+ if ((drive_udi = libhal_device_get_property_string(hal_ctx,
+ udis[i], "block.storage_device", NULL)) == NULL) {
+ libhal_free_string(block_device);
+ continue;
+ }
+ (void) printf("%s\t", block_device);
+ comma = B_FALSE;
+
+ if ((nicknames = libhal_device_get_property_strlist(hal_ctx,
+ drive_udi, "storage.solaris.nicknames", NULL)) != NULL) {
+ for (j = 0; nicknames[j] != NULL; j++) {
+ (void) printf("%s%s", comma ? "," : "",
+ nicknames[j]);
+ comma = B_TRUE;
+ }
+ }
+
+ if (((volume_label = libhal_device_get_property_string(hal_ctx,
+ udis[i], "volume.label", NULL)) != NULL) &&
+ (strlen(volume_label) > 0)) {
+ (void) printf("%s%s", comma ? "," : "", volume_label);
+ comma = B_TRUE;
+ }
+
+ if (((mount_point = libhal_device_get_property_string(hal_ctx,
+ udis[i], "volume.mount_point", NULL)) != NULL) &&
+ (strlen(mount_point) > 0)) {
+ (void) printf("%s%s", comma ? "," : "", mount_point);
+ comma = B_TRUE;
+ }
+
+ (void) printf("\n");
+
+ libhal_free_string_array(nicknames);
+ libhal_free_string(drive_udi);
+ libhal_free_string(volume_label);
+ libhal_free_string(mount_point);
+ libhal_free_string(block_device);
+ }
+ libhal_free_string_array(udis);
+}
+
+
+/*
+ * find volume by nickname
+ * returns the LibHalDrive object and a list of LibHalVolume objects.
+ */
+LibHalDrive *
+rmm_hal_volume_findby_nickname(LibHalContext *hal_ctx, const char *name,
+ GSList **volumes)
+{
+ DBusError error;
+ LibHalDrive *drive = NULL;
+ LibHalDrive *drive_tmp;
+ char **udis;
+ int num_udis;
+ char **nicknames;
+ int i, j;
+
+ *volumes = NULL;
+
+ dbus_error_init(&error);
+
+ if ((udis = libhal_find_device_by_capability(hal_ctx, "storage",
+ &num_udis, &error)) == NULL) {
+ rmm_dbus_error_free(&error);
+ return (NULL);
+ }
+
+ /* find a drive by nickname */
+ for (i = 0; (i < num_udis) && (drive == NULL); i++) {
+ if ((nicknames = libhal_device_get_property_strlist(hal_ctx,
+ udis[i], "storage.solaris.nicknames", &error)) == NULL) {
+ rmm_dbus_error_free(&error);
+ continue;
+ }
+ for (j = 0; (nicknames[j] != NULL) && (drive == NULL); j++) {
+ if (strcmp(nicknames[j], name) == 0) {
+ drive = libhal_drive_from_udi(hal_ctx, udis[i]);
+ }
+ }
+ libhal_free_string_array(nicknames);
+ }
+ libhal_free_string_array(udis);
+
+ if (drive != NULL) {
+ /* found the drive, now find its volumes */
+ if ((drive_tmp = rmm_hal_volume_findby(hal_ctx,
+ "block.storage_device", libhal_drive_get_udi(drive),
+ volumes)) != NULL) {
+ libhal_drive_free(drive_tmp);
+ }
+ }
+
+ rmm_dbus_error_free(&error);
+
+ return (drive);
+}
+
+void
+rmm_volumes_free(GSList *volumes)
+{
+ GSList *i;
+
+ for (i = volumes; i != NULL; i = g_slist_next(i)) {
+ libhal_volume_free((LibHalVolume *)(i->data));
+ }
+ g_slist_free(volumes);
+}
+
+/*
+ * Call HAL's Mount() method on the given device
+ */
+boolean_t
+rmm_hal_mount(LibHalContext *hal_ctx, const char *udi,
+ char **opts, int num_opts, char *mountpoint, DBusError *error)
+{
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+ DBusMessage *dmesg, *reply;
+ char *fstype;
+
+ dprintf("mounting %s...\n", udi);
+
+ if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
+ "org.freedesktop.Hal.Device.Volume", "Mount"))) {
+ dprintf(
+ "mount failed for %s: cannot create dbus message\n", udi);
+ return (B_FALSE);
+ }
+
+ fstype = "";
+ if (mountpoint == NULL) {
+ mountpoint = "";
+ }
+
+ if (!dbus_message_append_args(dmesg, DBUS_TYPE_STRING, &mountpoint,
+ DBUS_TYPE_STRING, &fstype,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &opts, num_opts,
+ DBUS_TYPE_INVALID)) {
+ dprintf("mount failed for %s: cannot append args\n", udi);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dbus_error_init(error);
+ if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
+ dmesg, -1, error))) {
+ dprintf("mount failed for %s: %s\n", udi, error->message);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dprintf("mounted %s\n", udi);
+
+ dbus_message_unref(dmesg);
+ dbus_message_unref(reply);
+
+ rmm_dbus_error_free(error);
+
+ return (B_TRUE);
+}
+
+
+/*
+ * Call HAL's Unmount() method on the given device
+ */
+boolean_t
+rmm_hal_unmount(LibHalContext *hal_ctx, const char *udi, DBusError *error)
+{
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+ DBusMessage *dmesg, *reply;
+ char **opts = NULL;
+
+ dprintf("unmounting %s...\n", udi);
+
+ if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
+ "org.freedesktop.Hal.Device.Volume", "Unmount"))) {
+ dprintf(
+ "unmount failed %s: cannot create dbus message\n", udi);
+ return (B_FALSE);
+ }
+
+ if (!dbus_message_append_args(dmesg, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+ &opts, 0, DBUS_TYPE_INVALID)) {
+ dprintf("unmount failed %s: cannot append args\n", udi);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dbus_error_init(error);
+ if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
+ dmesg, -1, error))) {
+ dprintf("unmount failed for %s: %s\n", udi, error->message);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dprintf("unmounted %s\n", udi);
+
+ dbus_message_unref(dmesg);
+ dbus_message_unref(reply);
+
+ rmm_dbus_error_free(error);
+
+ return (B_TRUE);
+}
+
+
+/*
+ * Call HAL's Eject() method on the given device
+ */
+boolean_t
+rmm_hal_eject(LibHalContext *hal_ctx, const char *udi, DBusError *error)
+{
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+ DBusMessage *dmesg, *reply;
+ char **options = NULL;
+ uint_t num_options = 0;
+
+ dprintf("ejecting %s...\n", udi);
+
+ if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
+ "org.freedesktop.Hal.Device.Storage", "Eject"))) {
+ dprintf("eject %s: cannot create dbus message\n", udi);
+ return (B_FALSE);
+ }
+
+ if (!dbus_message_append_args(dmesg,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options,
+ DBUS_TYPE_INVALID)) {
+ dprintf("eject %s: cannot append args to dbus message ", udi);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dbus_error_init(error);
+ if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
+ dmesg, -1, error))) {
+ dprintf("eject %s: %s\n", udi, error->message);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dprintf("ejected %s\n", udi);
+
+ dbus_message_unref(dmesg);
+ dbus_message_unref(reply);
+
+ rmm_dbus_error_free(error);
+
+ return (B_TRUE);
+}
+
+/*
+ * Call HAL's CloseTray() method on the given device
+ */
+boolean_t
+rmm_hal_closetray(LibHalContext *hal_ctx, const char *udi, DBusError *error)
+{
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+ DBusMessage *dmesg, *reply;
+ char **options = NULL;
+ uint_t num_options = 0;
+
+ dprintf("closing tray %s...\n", udi);
+
+ if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
+ "org.freedesktop.Hal.Device.Storage", "CloseTray"))) {
+ dprintf(
+ "closetray failed for %s: cannot create dbus message\n",
+ udi);
+ return (B_FALSE);
+ }
+
+ if (!dbus_message_append_args(dmesg,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &options, num_options,
+ DBUS_TYPE_INVALID)) {
+ dprintf("closetray %s: cannot append args to dbus message ",
+ udi);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dbus_error_init(error);
+ if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
+ dmesg, -1, error))) {
+ dprintf("closetray failed for %s: %s\n", udi, error->message);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dprintf("closetray ok %s\n", udi);
+
+ dbus_message_unref(dmesg);
+ dbus_message_unref(reply);
+
+ rmm_dbus_error_free(error);
+
+ return (B_TRUE);
+}
+
+/*
+ * Call HAL's Rescan() method on the given device
+ */
+boolean_t
+rmm_hal_rescan(LibHalContext *hal_ctx, const char *udi, DBusError *error)
+{
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+ DBusMessage *dmesg, *reply;
+
+ dprintf("rescanning %s...\n", udi);
+
+ if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal", udi,
+ "org.freedesktop.Hal.Device", "Rescan"))) {
+ dprintf("rescan failed for %s: cannot create dbus message\n",
+ udi);
+ return (B_FALSE);
+ }
+
+ dbus_error_init(error);
+ if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
+ dmesg, -1, error))) {
+ dprintf("rescan failed for %s: %s\n", udi, error->message);
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dprintf("rescan ok %s\n", udi);
+
+ dbus_message_unref(dmesg);
+ dbus_message_unref(reply);
+
+ rmm_dbus_error_free(error);
+
+ return (B_TRUE);
+}
+
+boolean_t
+rmm_hal_claim_branch(LibHalContext *hal_ctx, const char *udi)
+{
+ DBusError error;
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+ DBusMessage *dmesg, *reply;
+ const char *claimed_by = "rmvolmgr";
+
+ dprintf("claiming branch %s...\n", udi);
+
+ if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal",
+ "/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager",
+ "ClaimBranch"))) {
+ dprintf("cannot create dbus message\n");
+ return (B_FALSE);
+ }
+
+ if (!dbus_message_append_args(dmesg, DBUS_TYPE_STRING, &udi,
+ DBUS_TYPE_STRING, &claimed_by, DBUS_TYPE_INVALID)) {
+ dprintf("cannot append args to dbus message\n");
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dbus_error_init(&error);
+ if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
+ dmesg, -1, &error))) {
+ dprintf("cannot send dbus message\n");
+ dbus_message_unref(dmesg);
+ rmm_dbus_error_free(&error);
+ return (B_FALSE);
+ }
+
+ dprintf("claim branch ok %s\n", udi);
+
+ dbus_message_unref(dmesg);
+ dbus_message_unref(reply);
+
+ return (B_TRUE);
+}
+
+boolean_t
+rmm_hal_unclaim_branch(LibHalContext *hal_ctx, const char *udi)
+{
+ DBusError error;
+ DBusConnection *dbus_conn = libhal_ctx_get_dbus_connection(hal_ctx);
+ DBusMessage *dmesg, *reply;
+ const char *claimed_by = "rmvolmgr";
+
+ dprintf("unclaiming branch %s...\n", udi);
+
+ if (!(dmesg = dbus_message_new_method_call("org.freedesktop.Hal",
+ "/org/freedesktop/Hal/Manager", "org.freedesktop.Hal.Manager",
+ "UnclaimBranch"))) {
+ dprintf("cannot create dbus message\n");
+ return (B_FALSE);
+ }
+
+ if (!dbus_message_append_args(dmesg, DBUS_TYPE_STRING, &udi,
+ DBUS_TYPE_STRING, &claimed_by, DBUS_TYPE_INVALID)) {
+ dprintf("cannot append args to dbus message\n");
+ dbus_message_unref(dmesg);
+ return (B_FALSE);
+ }
+
+ dbus_error_init(&error);
+ if (!(reply = dbus_connection_send_with_reply_and_block(dbus_conn,
+ dmesg, -1, &error))) {
+ dprintf("cannot send dbus message\n");
+ dbus_message_unref(dmesg);
+ rmm_dbus_error_free(&error);
+ return (B_FALSE);
+ }
+
+ dprintf("unclaim branch ok %s\n", udi);
+
+ dbus_message_unref(dmesg);
+ dbus_message_unref(reply);
+
+ return (B_TRUE);
+}
+
+static boolean_t
+rmm_action_one(LibHalContext *hal_ctx, const char *name, action_t action,
+ const char *dev, const char *udi, LibHalVolume *v,
+ char **opts, int num_opts, char *mountpoint)
+{
+ char dev_str[MAXPATHLEN];
+ char *mountp;
+ DBusError error;
+ boolean_t ret = B_FALSE;
+
+ if (strcmp(name, dev) == 0) {
+ (void) snprintf(dev_str, sizeof (dev_str), name);
+ } else {
+ (void) snprintf(dev_str, sizeof (dev_str), "%s %s", name, dev);
+ }
+
+ dbus_error_init(&error);
+
+ switch (action) {
+ case EJECT:
+ ret = rmm_hal_eject(hal_ctx, udi, &error);
+ break;
+ case INSERT:
+ case REMOUNT:
+ if (libhal_volume_is_mounted(v)) {
+ goto done;
+ }
+ ret = rmm_hal_mount(hal_ctx, udi,
+ opts, num_opts, mountpoint, &error);
+ break;
+ case UNMOUNT:
+ if (!libhal_volume_is_mounted(v)) {
+ goto done;
+ }
+ ret = rmm_hal_unmount(hal_ctx, udi, &error);
+ break;
+ case CLOSETRAY:
+ ret = rmm_hal_closetray(hal_ctx, udi, &error);
+ break;
+ }
+
+ if (!ret) {
+ (void) fprintf(stderr, gettext("%s of %s failed: %s\n"),
+ action_strings[action], dev_str, rmm_strerror(&error, -1));
+ goto done;
+ }
+
+ switch (action) {
+ case EJECT:
+ (void) printf(gettext("%s ejected\n"), dev_str);
+ break;
+ case INSERT:
+ case REMOUNT:
+ mountp = rmm_get_mnttab_mount_point(dev);
+ if (mountp != NULL) {
+ (void) printf(gettext("%s mounted at %s\n"),
+ dev_str, mountp);
+ free(mountp);
+ }
+ break;
+ case UNMOUNT:
+ (void) printf(gettext("%s unmounted\n"), dev_str);
+ break;
+ case CLOSETRAY:
+ (void) printf(gettext("%s tray closed\n"), dev_str);
+ break;
+ }
+
+done:
+ rmm_dbus_error_free(&error);
+ return (ret);
+}
+
+/*
+ * top level action routine
+ *
+ * If non-null 'aa' is passed, it will be used, otherwise a local copy
+ * will be created.
+ */
+boolean_t
+rmm_action(LibHalContext *hal_ctx, const char *name, action_t action,
+ struct action_arg *aap, char **opts, int num_opts, char *mountpoint)
+{
+ DBusError error;
+ GSList *volumes, *i;
+ LibHalDrive *d;
+ LibHalVolume *v;
+ const char *udi, *d_udi;
+ const char *dev, *d_dev;
+ struct action_arg aa_local;
+ boolean_t ret = B_FALSE;
+
+ dprintf("rmm_action %s %s\n", name, action_strings[action]);
+
+ if (aap == NULL) {
+ aap = &aa_local;
+ }
+
+ dbus_error_init(&error);
+
+ /* find the drive and its volumes */
+ d = rmm_hal_volume_find(hal_ctx, name, &error, &volumes);
+ rmm_dbus_error_free(&error);
+ if (d == NULL) {
+ (void) fprintf(stderr, gettext("cannot find '%s'\n"), name);
+ return (B_FALSE);
+ }
+ d_udi = libhal_drive_get_udi(d);
+ d_dev = libhal_drive_get_device_file(d);
+ if ((d_udi == NULL) || (d_dev == NULL)) {
+ goto out;
+ }
+
+ /*
+ * For those drives that do not require media eject,
+ * EJECT turns into UNMOUNT.
+ */
+ if ((action == EJECT) && !libhal_drive_requires_eject(d)) {
+ action = UNMOUNT;
+ }
+
+ /* per drive action */
+ if ((action == EJECT) || (action == CLOSETRAY)) {
+ ret = rmm_action_one(hal_ctx, name, action, d_dev, d_udi, NULL,
+ opts, num_opts, NULL);
+
+ if (!ret || (action == CLOSETRAY)) {
+ goto out;
+ }
+ }
+
+ /* per volume action */
+ for (i = volumes; i != NULL; i = g_slist_next(i)) {
+ v = (LibHalVolume *)i->data;
+ udi = libhal_volume_get_udi(v);
+ dev = libhal_volume_get_device_file(v);
+
+ if ((udi == NULL) || (dev == NULL)) {
+ continue;
+ }
+ if (aap == &aa_local) {
+ if (!rmm_volume_aa_from_prop(hal_ctx, udi, v, aap)) {
+ dprintf("rmm_volume_aa_from_prop failed %s\n",
+ udi);
+ continue;
+ }
+ }
+ aap->aa_action = action;
+
+ /* ejected above, just need postprocess */
+ if (action != EJECT) {
+ ret = rmm_action_one(hal_ctx, name, action, dev, udi, v,
+ opts, num_opts, mountpoint);
+ }
+ if (ret) {
+ (void) vold_postprocess(hal_ctx, udi, aap);
+ }
+
+ libhal_volume_free(v);
+ if (aap == &aa_local) {
+ rmm_volume_aa_free(aap);
+ }
+ }
+
+out:
+ g_slist_free(volumes);
+ libhal_drive_free(d);
+
+ return (ret);
+}
+
+
+/*
+ * rescan by name
+ * if name is NULL, rescan all drives
+ */
+boolean_t
+rmm_rescan(LibHalContext *hal_ctx, const char *name, boolean_t query)
+{
+ DBusError error;
+ GSList *volumes;
+ LibHalDrive *drive = NULL;
+ const char *drive_udi;
+ char **udis;
+ int num_udis;
+ char *nickname;
+ char **nicks = NULL;
+ boolean_t do_free_udis = FALSE;
+ int i;
+ int ret = 0;
+
+ dprintf("rmm_rescan %s\n", name != NULL ? name : "all");
+
+ dbus_error_init(&error);
+
+ if (name != NULL) {
+ if ((drive = rmm_hal_volume_find(hal_ctx, name, &error,
+ &volumes)) == NULL) {
+ rmm_dbus_error_free(&error);
+ (void) fprintf(stderr,
+ gettext("cannot find '%s'\n"), name);
+ return (B_FALSE);
+ }
+ rmm_dbus_error_free(&error);
+ g_slist_free(volumes);
+
+ drive_udi = libhal_drive_get_udi(drive);
+ udis = (char **)&drive_udi;
+ num_udis = 1;
+ } else {
+ if ((udis = libhal_find_device_by_capability(hal_ctx,
+ "storage", &num_udis, &error)) == NULL) {
+ rmm_dbus_error_free(&error);
+ return (B_TRUE);
+ }
+ rmm_dbus_error_free(&error);
+ do_free_udis = TRUE;
+ }
+
+ for (i = 0; i < num_udis; i++) {
+ if (name == NULL) {
+ nicks = libhal_device_get_property_strlist(hal_ctx,
+ udis[i], "storage.solaris.nicknames", NULL);
+ if (nicks != NULL) {
+ nickname = nicks[0];
+ } else {
+ nickname = "";
+ }
+ }
+ if (!(ret = rmm_hal_rescan(hal_ctx, udis[i], &error))) {
+ (void) fprintf(stderr,
+ gettext("rescan of %s failed: %s\n"),
+ name ? name : nickname,
+ rmm_strerror(&error, -1));
+ libhal_free_string_array(nicks);
+ continue;
+ }
+ if (query) {
+ printf(gettext("%s is%s available\n"),
+ name ? name : nickname,
+ libhal_device_get_property_bool(hal_ctx, udis[i],
+ "storage.removable.media_available", NULL) ?
+ "" : " not");
+ }
+ libhal_free_string_array(nicks);
+ }
+
+ if (drive != NULL) {
+ libhal_drive_free(drive);
+ }
+ if (do_free_udis) {
+ libhal_free_string_array(udis);
+ }
+
+ return (ret);
+}
+
+
+/*
+ * set action_arg from volume properties
+ */
+boolean_t
+rmm_volume_aa_from_prop(LibHalContext *hal_ctx, const char *udi_arg,
+ LibHalVolume *volume_arg, struct action_arg *aap)
+{
+ LibHalVolume *volume = volume_arg;
+ const char *udi = udi_arg;
+ const char *drive_udi;
+ char *volume_label;
+ char *mountpoint;
+ int len;
+ int ret = B_FALSE;
+
+ /* at least udi or volume must be supplied */
+ if ((udi == NULL) && (volume == NULL)) {
+ return (B_FALSE);
+ }
+ if (volume == NULL) {
+ if ((volume = libhal_volume_from_udi(hal_ctx, udi)) == NULL) {
+ dprintf("cannot get volume %s\n", udi);
+ goto out;
+ }
+ }
+ if (udi == NULL) {
+ if ((udi = libhal_volume_get_udi(volume)) == NULL) {
+ dprintf("cannot get udi\n");
+ goto out;
+ }
+ }
+ drive_udi = libhal_volume_get_storage_device_udi(volume);
+
+ if (!(aap->aa_symdev = libhal_device_get_property_string(hal_ctx,
+ drive_udi, "storage.solaris.legacy.symdev", NULL))) {
+ dprintf("property %s not found %s\n",
+ "storage.solaris.legacy.symdev", drive_udi);
+ goto out;
+ }
+ if (!(aap->aa_media = libhal_device_get_property_string(hal_ctx,
+ drive_udi, "storage.solaris.legacy.media_type", NULL))) {
+ dprintf("property %s not found %s\n",
+ "storage.solaris.legacy.media_type", drive_udi);
+ goto out;
+ }
+
+ /* name is derived from volume label */
+ aap->aa_name = NULL;
+ if ((volume_label = (char *)libhal_device_get_property_string(hal_ctx,
+ udi, "volume.label", NULL)) != NULL) {
+ if ((len = strlen(volume_label)) > 0) {
+ aap->aa_name = rmm_vold_convert_volume_label(
+ volume_label, len);
+ if (strlen(aap->aa_name) == 0) {
+ free(aap->aa_name);
+ aap->aa_name = NULL;
+ }
+ }
+ libhal_free_string(volume_label);
+ }
+ /* if no label, then unnamed_<mediatype> */
+ if (aap->aa_name == NULL) {
+ aap->aa_name = (char *)calloc(1, sizeof ("unnamed_floppyNNNN"));
+ if (aap->aa_name == NULL) {
+ goto out;
+ }
+ (void) snprintf(aap->aa_name, sizeof ("unnamed_floppyNNNN"),
+ "unnamed_%s", aap->aa_media);
+ }
+
+ if (!(aap->aa_path = libhal_device_get_property_string(hal_ctx, udi,
+ "block.device", NULL))) {
+ dprintf("property %s not found %s\n", "block.device", udi);
+ goto out;
+ }
+ if (!(aap->aa_rawpath = libhal_device_get_property_string(hal_ctx, udi,
+ "block.solaris.raw_device", NULL))) {
+ dprintf("property %s not found %s\n",
+ "block.solaris.raw_device", udi);
+ goto out;
+ }
+ if (!(aap->aa_type = libhal_device_get_property_string(hal_ctx, udi,
+ "volume.fstype", NULL))) {
+ dprintf("property %s not found %s\n", "volume.fstype", udi);
+ goto out;
+ }
+ if (!libhal_device_get_property_bool(hal_ctx, udi,
+ "volume.is_partition", NULL)) {
+ aap->aa_partname = NULL;
+ } else if (!(aap->aa_partname = libhal_device_get_property_string(
+ hal_ctx, udi, "block.solaris.slice", NULL))) {
+ dprintf("property %s not found %s\n",
+ "block.solaris.slice", udi);
+ goto out;
+ }
+ if (!(mountpoint = libhal_device_get_property_string(hal_ctx, udi,
+ "volume.mount_point", NULL))) {
+ dprintf("property %s not found %s\n",
+ "volume.mount_point", udi);
+ goto out;
+ }
+ /*
+ * aa_mountpoint can be reallocated in rmm_volume_aa_update_mountpoint()
+ * won't have to choose between free() or libhal_free_string() later on
+ */
+ aap->aa_mountpoint = strdup(mountpoint);
+ libhal_free_string(mountpoint);
+ if (aap->aa_mountpoint == NULL) {
+ dprintf("mountpoint is NULL %s\n", udi);
+ goto out;
+ }
+
+ ret = B_TRUE;
+
+out:
+ if ((volume != NULL) && (volume != volume_arg)) {
+ libhal_volume_free(volume);
+ }
+ if (!ret) {
+ rmm_volume_aa_free(aap);
+ }
+ return (ret);
+}
+
+/* ARGSUSED */
+void
+rmm_volume_aa_update_mountpoint(LibHalContext *hal_ctx, const char *udi,
+ struct action_arg *aap)
+{
+ if (aap->aa_mountpoint != NULL) {
+ free(aap->aa_mountpoint);
+ }
+ aap->aa_mountpoint = rmm_get_mnttab_mount_point(aap->aa_path);
+}
+
+void
+rmm_volume_aa_free(struct action_arg *aap)
+{
+ if (aap->aa_symdev != NULL) {
+ libhal_free_string(aap->aa_symdev);
+ aap->aa_symdev = NULL;
+ }
+ if (aap->aa_name != NULL) {
+ free(aap->aa_name);
+ aap->aa_name = NULL;
+ }
+ if (aap->aa_path != NULL) {
+ libhal_free_string(aap->aa_path);
+ aap->aa_path = NULL;
+ }
+ if (aap->aa_rawpath != NULL) {
+ libhal_free_string(aap->aa_rawpath);
+ aap->aa_rawpath = NULL;
+ }
+ if (aap->aa_type != NULL) {
+ libhal_free_string(aap->aa_type);
+ aap->aa_type = NULL;
+ }
+ if (aap->aa_media != NULL) {
+ libhal_free_string(aap->aa_media);
+ aap->aa_media = NULL;
+ }
+ if (aap->aa_partname != NULL) {
+ libhal_free_string(aap->aa_partname);
+ aap->aa_partname = NULL;
+ }
+ if (aap->aa_mountpoint != NULL) {
+ free(aap->aa_mountpoint);
+ aap->aa_mountpoint = NULL;
+ }
+}
+
+/*
+ * get device's mount point from mnttab
+ */
+char *
+rmm_get_mnttab_mount_point(const char *special)
+{
+ char *mount_point = NULL;
+ FILE *f;
+ struct mnttab mnt;
+ struct mnttab mpref = { NULL, NULL, NULL, NULL, NULL };
+
+ if ((f = fopen(MNTTAB, "r")) != NULL) {
+ mpref.mnt_special = (char *)special;
+ if (getmntany(f, &mnt, &mpref) == 0) {
+ mount_point = strdup(mnt.mnt_mountp);
+ }
+ fclose(f);
+ }
+
+ return (mount_point);
+}
+
+
+/*
+ * get human readable string from error values
+ */
+const char *
+rmm_strerror(DBusError *dbus_error, int rmm_error)
+{
+ const char *str;
+
+ if ((dbus_error != NULL) && dbus_error_is_set(dbus_error)) {
+ str = dbus_error->message;
+ } else {
+ switch (rmm_error) {
+ case RMM_EOK:
+ str = gettext("success");
+ break;
+ case RMM_EDBUS_CONNECT:
+ str = gettext("cannot connect to D-Bus");
+ break;
+ case RMM_EHAL_CONNECT:
+ str = gettext("cannot connect to HAL");
+ break;
+ default:
+ str = gettext("undefined error");
+ break;
+ }
+ }
+
+ return (str);
+}
+
+void
+rmm_dbus_error_free(DBusError *error)
+{
+ if (error != NULL && dbus_error_is_set(error)) {
+ dbus_error_free(error);
+ }
+}
+
+static int
+rmm_vold_isbadchar(int c)
+{
+ int ret_val = 0;
+
+
+ switch (c) {
+ case '/':
+ case ';':
+ case '|':
+ ret_val = 1;
+ break;
+ default:
+ if (iscntrl(c) || isspace(c)) {
+ ret_val = 1;
+ }
+ }
+
+ return (ret_val);
+}
+
+char *
+rmm_vold_convert_volume_label(const char *name, size_t len)
+{
+ char buf[MAXNAMELEN+1];
+ char *s = buf;
+ int i;
+
+ if (len > MAXNAMELEN) {
+ len = MAXNAMELEN;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (name[i] == '\0') {
+ break;
+ }
+ if (isgraph((int)name[i])) {
+ if (isupper((int)name[i])) {
+ *s++ = tolower((int)name[i]);
+ } else if (rmm_vold_isbadchar((int)name[i])) {
+ *s++ = '_';
+ } else {
+ *s++ = name[i];
+ }
+ }
+ }
+ *s = '\0';
+ s = strdup(buf);
+
+ return (s);
+}
+
+/*
+ * swiped from mkdir.c
+ */
+int
+makepath(char *dir, mode_t mode)
+{
+ int err;
+ char *slash;
+
+
+ if ((mkdir(dir, mode) == 0) || (errno == EEXIST)) {
+ return (0);
+ }
+ if (errno != ENOENT) {
+ return (-1);
+ }
+ if ((slash = strrchr(dir, '/')) == NULL) {
+ return (-1);
+ }
+ *slash = '\0';
+ err = makepath(dir, mode);
+ *slash++ = '/';
+
+ if (err || (*slash == '\0')) {
+ return (err);
+ }
+
+ return (mkdir(dir, mode));
+}
+
+
+void
+dprintf(const char *fmt, ...)
+{
+
+ va_list ap;
+ const char *p;
+ char msg[BUFSIZ];
+ char *errmsg = strerror(errno);
+ char *s;
+
+ if (rmm_debug == 0) {
+ return;
+ }
+
+ (void) memset(msg, 0, BUFSIZ);
+
+ /* scan for %m and replace with errno msg */
+ s = &msg[strlen(msg)];
+ p = fmt;
+
+ while (*p != '\0') {
+ if ((*p == '%') && (*(p+1) == 'm')) {
+ (void) strcat(s, errmsg);
+ p += 2;
+ s += strlen(errmsg);
+ continue;
+ }
+ *s++ = *p++;
+ }
+ *s = '\0'; /* don't forget the null byte */
+
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, msg, ap);
+ va_end(ap);
+}
diff --git a/usr/src/cmd/rmvolmgr/rmm_common.h b/usr/src/cmd/rmvolmgr/rmm_common.h
new file mode 100644
index 0000000000..ed80c535a3
--- /dev/null
+++ b/usr/src/cmd/rmvolmgr/rmm_common.h
@@ -0,0 +1,106 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _COMMON_H
+#define _COMMON_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <zone.h>
+
+#include <glib.h>
+#include <libhal.h>
+#include <libhal-storage.h>
+
+#include "vold.h"
+
+typedef enum {
+ RMM_EOK = 0,
+ RMM_EDBUS_CONNECT, /* cannot connect to DBUS */
+ RMM_EHAL_CONNECT /* cannot connect to HAL */
+} rmm_error_t;
+
+#define HAL_BRANCH_LOCAL "/org/freedesktop/Hal/devices/local"
+
+#define NELEM(a) (sizeof (a) / sizeof (*(a)))
+
+extern char *progname;
+
+LibHalContext *rmm_hal_init(LibHalDeviceAdded, LibHalDeviceRemoved,
+ LibHalDevicePropertyModified, DBusError *, rmm_error_t *);
+void rmm_hal_fini(LibHalContext *hal_ctx);
+
+LibHalDrive *rmm_hal_volume_find(LibHalContext *, const char *,
+ DBusError *, GSList **);
+LibHalDrive *rmm_hal_volume_find_default(LibHalContext *, DBusError *,
+ const char **, GSList **);
+LibHalDrive *rmm_hal_volume_findby(LibHalContext *, const char *,
+ const char *, GSList **);
+LibHalDrive *rmm_hal_volume_findby_nickname(LibHalContext *, const char *,
+ GSList **);
+void rmm_print_volume_nicknames(LibHalContext *, DBusError *);
+void rmm_volumes_free(GSList *);
+
+boolean_t rmm_hal_mount(LibHalContext *, const char *,
+ char **, int, char *, DBusError *);
+boolean_t rmm_hal_unmount(LibHalContext *, const char *, DBusError *);
+boolean_t rmm_hal_eject(LibHalContext *, const char *, DBusError *);
+boolean_t rmm_hal_closetray(LibHalContext *, const char *, DBusError *);
+boolean_t rmm_hal_rescan(LibHalContext *, const char *, DBusError *);
+boolean_t rmm_hal_claim_branch(LibHalContext *, const char *);
+boolean_t rmm_hal_unclaim_branch(LibHalContext *, const char *);
+
+boolean_t rmm_action(LibHalContext *, const char *name, action_t action,
+ struct action_arg *aap, char **, int, char *);
+boolean_t rmm_rescan(LibHalContext *, const char *, boolean_t);
+
+void rmm_update_vold_mountpoints(LibHalContext *, const char *,
+ struct action_arg *);
+
+boolean_t rmm_volume_aa_from_prop(LibHalContext *, const char *,
+ LibHalVolume *, struct action_arg *);
+void rmm_volume_aa_update_mountpoint(LibHalContext *, const char *,
+ struct action_arg *aap);
+void rmm_volume_aa_free(struct action_arg *);
+
+char *rmm_get_mnttab_mount_point(const char *);
+const char *rmm_strerror(DBusError *, int);
+void rmm_dbus_error_free(DBusError *);
+
+char *rmm_vold_convert_volume_label(const char *name, size_t len);
+int makepath(char *, mode_t);
+void dprintf(const char *, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _COMMON_H */
diff --git a/usr/src/cmd/rmvolmgr/rmvolmgr.c b/usr/src/cmd/rmvolmgr/rmvolmgr.c
new file mode 100644
index 0000000000..3c8a88b92c
--- /dev/null
+++ b/usr/src/cmd/rmvolmgr/rmvolmgr.c
@@ -0,0 +1,607 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * rmvolmgr daemon
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <errno.h>
+#include <libintl.h>
+#include <sys/syscall.h>
+#include <libscf.h>
+#include <priv_utils.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#include <libhal.h>
+
+#include "rmm_common.h"
+
+char *progname = "rmvolmgr";
+
+#define RMVOLMGR_FMRI "svc:/system/filesystem/rmvolmgr:default"
+
+typedef struct managed_volume {
+ char *udi;
+ boolean_t my;
+ struct action_arg aa;
+} managed_volume_t;
+
+static GSList *managed_volumes;
+
+static GMainLoop *mainloop;
+static LibHalContext *hal_ctx;
+static int sigexit_pipe[2];
+static GIOChannel *sigexit_ioch;
+
+static boolean_t opt_c; /* disable CDE compatibility */
+static boolean_t opt_n; /* disable legacy mountpoint symlinks */
+static boolean_t opt_s; /* system instance */
+
+static void get_smf_properties();
+static int daemon(int nochdir, int noclose);
+static void rmm_device_added(LibHalContext *ctx, const char *udi);
+static void rmm_device_removed(LibHalContext *ctx, const char *udi);
+static void rmm_property_modified(LibHalContext *ctx, const char *udi,
+ const char *key, dbus_bool_t is_removed, dbus_bool_t is_added);
+static void rmm_mount_all();
+static void rmm_unmount_all();
+static void sigexit(int signo);
+static gboolean sigexit_ioch_func(GIOChannel *source, GIOCondition condition,
+ gpointer user_data);
+
+static void
+usage()
+{
+ (void) fprintf(stderr, gettext("\nusage: rmvolmgr [-v]\n"));
+}
+
+static int
+rmvolmgr(int argc, char **argv)
+{
+ const char *opts = "chnsv";
+ DBusError error;
+ boolean_t daemonize;
+ rmm_error_t rmm_error;
+ int c;
+
+ while ((c = getopt(argc, argv, opts)) != EOF) {
+ switch (c) {
+ case 'c':
+ opt_c = B_TRUE;
+ break;
+ case 'n':
+ opt_n = B_TRUE;
+ break;
+ case 's':
+ opt_s = B_TRUE;
+ break;
+ case 'v':
+ rmm_debug = 1;
+ break;
+ case '?':
+ case 'h':
+ usage();
+ return (0);
+ default:
+ usage();
+ return (1);
+ }
+ }
+
+ if (opt_s) {
+ if (geteuid() != 0) {
+ (void) fprintf(stderr,
+ gettext("system instance must have euid 0\n"));
+ return (1);
+ }
+
+ get_smf_properties();
+
+ if (opt_c) {
+ rmm_vold_actions_enabled = B_FALSE;
+ }
+ if (opt_n) {
+ rmm_vold_mountpoints_enabled = B_FALSE;
+ }
+
+
+ /*
+ * Drop unused privileges. Remain root for HAL interaction
+ * and to create legacy symlinks.
+ *
+ * Need PRIV_FILE_DAC_WRITE to write to users'
+ * /tmp/.removable/notify* files.
+ */
+ if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET,
+ 0, 0,
+ rmm_vold_actions_enabled ? PRIV_FILE_DAC_WRITE : NULL,
+ NULL) == -1) {
+ (void) fprintf(stderr,
+ gettext("failed to drop privileges"));
+ return (1);
+ }
+ /* basic privileges we don't need */
+ (void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_PROC_EXEC,
+ PRIV_PROC_INFO, PRIV_FILE_LINK_ANY, PRIV_PROC_SESSION,
+ NULL);
+
+ } else {
+ if (opt_c) {
+ rmm_vold_actions_enabled = B_FALSE;
+ }
+ if (opt_n) {
+ rmm_vold_mountpoints_enabled = B_FALSE;
+ }
+ }
+
+ daemonize = (getenv("RMVOLMGR_NODAEMON") == NULL);
+
+ if (daemonize && daemon(0, 0) < 0) {
+ dprintf("daemonizing failed: %s", strerror(errno));
+ return (1);
+ }
+
+ if (opt_s) {
+ __fini_daemon_priv(PRIV_PROC_FORK, NULL);
+ }
+
+ /*
+ * signal mainloop integration using pipes
+ */
+ if (pipe(sigexit_pipe) != 0) {
+ dprintf("pipe failed %s\n", strerror(errno));
+ return (1);
+ }
+ sigexit_ioch = g_io_channel_unix_new(sigexit_pipe[0]);
+ if (sigexit_ioch == NULL) {
+ dprintf("g_io_channel_unix_new failed\n");
+ return (1);
+ }
+ g_io_add_watch(sigexit_ioch, G_IO_IN, sigexit_ioch_func, NULL);
+ signal(SIGTERM, sigexit);
+ signal(SIGINT, sigexit);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGUSR1, SIG_IGN);
+ signal(SIGUSR2, SIG_IGN);
+
+ if ((hal_ctx = rmm_hal_init(rmm_device_added, rmm_device_removed,
+ rmm_property_modified, &error, &rmm_error)) == NULL) {
+ dbus_error_free(&error);
+ return (1);
+ }
+
+ /* user instance should claim devices */
+ if (!opt_s) {
+ if (!rmm_hal_claim_branch(hal_ctx, HAL_BRANCH_LOCAL)) {
+ (void) fprintf(stderr,
+ gettext("cannot claim branch\n"));
+ return (1);
+ }
+ }
+
+ rmm_mount_all();
+
+ if ((mainloop = g_main_loop_new(NULL, B_FALSE)) == NULL) {
+ dprintf("Cannot create main loop\n");
+ return (1);
+ }
+
+ g_main_loop_run(mainloop);
+
+ return (0);
+}
+
+static void
+get_smf_properties()
+{
+ scf_simple_prop_t *prop;
+ uint8_t *val;
+
+ if ((prop = scf_simple_prop_get(NULL, RMVOLMGR_FMRI,
+ "rmvolmgr", "legacy_mountpoints")) != NULL) {
+ if ((val = scf_simple_prop_next_boolean(prop)) != NULL) {
+ rmm_vold_mountpoints_enabled = (*val != 0);
+ }
+ scf_simple_prop_free(prop);
+ }
+
+ if ((prop = scf_simple_prop_get(NULL, RMVOLMGR_FMRI,
+ "rmvolmgr", "cde_compatible")) != NULL) {
+ if ((val = scf_simple_prop_next_boolean(prop)) != NULL) {
+ rmm_vold_actions_enabled = (*val != 0);
+ }
+ scf_simple_prop_free(prop);
+ }
+}
+
+/* ARGSUSED */
+static void
+sigexit(int signo)
+{
+ dprintf("signal to exit %d\n", signo);
+
+ write(sigexit_pipe[1], "s", 1);
+}
+
+/* ARGSUSED */
+static gboolean
+sigexit_ioch_func(GIOChannel *source, GIOCondition condition,
+ gpointer user_data)
+{
+ gchar buf[1];
+ gsize bytes_read;
+ GError *error = NULL;
+
+ if (g_io_channel_read_chars(source, buf, 1, &bytes_read, &error) !=
+ G_IO_STATUS_NORMAL) {
+ dprintf("g_io_channel_read_chars failed %s", error->message);
+ g_error_free(error);
+ return (TRUE);
+ }
+
+ dprintf("signal to exit\n");
+
+ rmm_unmount_all();
+
+ g_main_loop_quit(mainloop);
+
+ return (TRUE);
+}
+
+static managed_volume_t *
+rmm_managed_alloc(LibHalContext *ctx, const char *udi)
+{
+ managed_volume_t *v;
+
+ if ((v = calloc(1, sizeof (managed_volume_t))) == NULL) {
+ return (NULL);
+ }
+ if ((v->udi = strdup(udi)) == NULL) {
+ free(v);
+ return (NULL);
+ }
+ if (!rmm_volume_aa_from_prop(ctx, udi, NULL, &v->aa)) {
+ free(v->udi);
+ free(v);
+ return (NULL);
+ }
+
+ return (v);
+}
+
+static void
+rmm_managed_free(managed_volume_t *v)
+{
+ rmm_volume_aa_free(&v->aa);
+ free(v->udi);
+ free(v);
+}
+
+static gint
+rmm_managed_compare_udi(gconstpointer a, gconstpointer b)
+{
+ const managed_volume_t *va = a;
+ const char *udi = b;
+
+ return (strcmp(va->udi, udi));
+}
+
+static boolean_t
+volume_should_mount(const char *udi)
+{
+ char *storage_device = NULL;
+ int ret = B_FALSE;
+
+ if (libhal_device_get_property_bool(hal_ctx, udi,
+ "volume.ignore", NULL)) {
+ goto out;
+ }
+
+ /* get the backing storage device */
+ if (!(storage_device = libhal_device_get_property_string(hal_ctx, udi,
+ "block.storage_device", NULL))) {
+ dprintf("cannot get block.storage_device\n");
+ goto out;
+ }
+
+ /* we handle either removable or hotpluggable */
+ if (!libhal_device_get_property_bool(hal_ctx, storage_device,
+ "storage.removable", NULL) &&
+ !libhal_device_get_property_bool(hal_ctx, storage_device,
+ "storage.hotpluggable", NULL)) {
+ goto out;
+ }
+
+ /* ignore if claimed by another volume manager */
+ if (libhal_device_get_property_bool(hal_ctx, storage_device,
+ "info.claimed", NULL)) {
+ goto out;
+ }
+
+ ret = B_TRUE;
+
+out:
+ libhal_free_string(storage_device);
+ return (ret);
+}
+
+static void
+volume_added(const char *udi)
+{
+ GSList *l;
+ managed_volume_t *v;
+
+ dprintf("volume added %s\n", udi);
+
+ l = g_slist_find_custom(managed_volumes, udi, rmm_managed_compare_udi);
+ v = (l != NULL) ? l->data : NULL;
+
+ if (v != NULL) {
+ dprintf("already managed %s\n", udi);
+ return;
+ }
+ if (!volume_should_mount(udi)) {
+ dprintf("should not mount %s\n", udi);
+ return;
+ }
+ if ((v = rmm_managed_alloc(hal_ctx, udi)) == NULL) {
+ return;
+ }
+ if (rmm_action(hal_ctx, udi, INSERT, &v->aa, 0, 0, 0)) {
+ v->my = B_TRUE;
+ managed_volumes = g_slist_prepend(managed_volumes, v);
+ } else {
+ dprintf("rmm_action failed %s\n", udi);
+ rmm_managed_free(v);
+ }
+}
+
+static void
+volume_removed(const char *udi)
+{
+ GSList *l;
+ managed_volume_t *v;
+
+ dprintf("volume removed %s\n", udi);
+
+ l = g_slist_find_custom(managed_volumes, udi, rmm_managed_compare_udi);
+ v = (l != NULL) ? l->data : NULL;
+ if (v == NULL) {
+ return;
+ }
+
+ /* HAL will unmount, just do the vold legacy stuff */
+ v->aa.aa_action = EJECT;
+ (void) vold_postprocess(hal_ctx, udi, &v->aa);
+
+ rmm_managed_free(v);
+ managed_volumes = g_slist_delete_link(managed_volumes, l);
+}
+
+/* ARGSUSED */
+static void
+rmm_device_added(LibHalContext *ctx, const char *udi)
+{
+ if (libhal_device_query_capability(hal_ctx, udi, "volume", NULL)) {
+ volume_added(udi);
+ }
+}
+
+/* ARGSUSED */
+static void
+rmm_device_removed(LibHalContext *ctx, const char *udi)
+{
+ if (libhal_device_query_capability(hal_ctx, udi, "volume", NULL)) {
+ volume_removed(udi);
+ }
+}
+
+/* ARGSUSED */
+static void
+rmm_property_modified(LibHalContext *ctx, const char *udi, const char *key,
+ dbus_bool_t is_removed, dbus_bool_t is_added)
+{
+ DBusError error;
+ GSList *l;
+ managed_volume_t *v;
+ boolean_t is_mounted;
+
+ if (strcmp(key, "volume.is_mounted") != 0) {
+ return;
+ }
+ is_mounted = libhal_device_get_property_bool(hal_ctx, udi, key, NULL);
+
+ l = g_slist_find_custom(managed_volumes, udi, rmm_managed_compare_udi);
+ v = (l != NULL) ? l->data : NULL;
+
+ if (is_mounted) {
+ dprintf("Mounted: %s\n", udi);
+
+ if (v != NULL) {
+ /* volume mounted by us is already taken care of */
+ if (v->my) {
+ return;
+ }
+ } else {
+ if ((v = rmm_managed_alloc(ctx, udi)) == NULL) {
+ return;
+ }
+ managed_volumes = g_slist_prepend(managed_volumes, v);
+ }
+
+ v->aa.aa_action = INSERT;
+ (void) vold_postprocess(hal_ctx, udi, &v->aa);
+
+ } else {
+ dprintf("Unmounted: %s\n", udi);
+
+ if (v == NULL) {
+ return;
+ }
+
+ v->aa.aa_action = EJECT;
+ (void) vold_postprocess(hal_ctx, udi, &v->aa);
+
+ rmm_managed_free(v);
+ managed_volumes = g_slist_delete_link(managed_volumes, l);
+ }
+}
+
+/*
+ * Mount all mountable volumes
+ */
+static void
+rmm_mount_all()
+{
+ DBusError error;
+ char **udis = NULL;
+ int num_udis;
+ int i;
+ managed_volume_t *v;
+
+ dbus_error_init(&error);
+
+ /* get all volumes */
+ if ((udis = libhal_find_device_by_capability(hal_ctx, "volume",
+ &num_udis, &error)) == NULL) {
+ dprintf("mount_all: no volumes found\n");
+ goto out;
+ }
+
+ for (i = 0; i < num_udis; i++) {
+ /* skip if already mounted */
+ if (libhal_device_get_property_bool(hal_ctx, udis[i],
+ "volume.is_mounted", NULL)) {
+ dprintf("mount_all: %s already mounted\n", udis[i]);
+ continue;
+ }
+ if (!volume_should_mount(udis[i])) {
+ continue;
+ }
+ if ((v = rmm_managed_alloc(hal_ctx, udis[i])) == NULL) {
+ continue;
+ }
+ if (rmm_action(hal_ctx, udis[i], INSERT, &v->aa, 0, 0, 0)) {
+ v->my = B_TRUE;
+ managed_volumes = g_slist_prepend(managed_volumes, v);
+ } else {
+ rmm_managed_free(v);
+ }
+ }
+
+out:
+ if (udis != NULL) {
+ libhal_free_string_array(udis);
+ }
+ rmm_dbus_error_free(&error);
+}
+
+/*
+ * Mount all volumes mounted by this program
+ */
+static void
+rmm_unmount_all()
+{
+ GSList *i;
+ managed_volume_t *v;
+
+ for (i = managed_volumes; i != NULL; i = managed_volumes) {
+ v = (managed_volume_t *)i->data;
+
+ if (v->my && libhal_device_get_property_bool(hal_ctx, v->udi,
+ "volume.is_mounted", NULL)) {
+ (void) rmm_action(hal_ctx, v->udi, UNMOUNT,
+ &v->aa, 0, 0, 0);
+ }
+
+ managed_volumes = g_slist_remove(managed_volumes, v);
+ rmm_managed_free(v);
+ }
+}
+
+static int
+daemon(int nochdir, int noclose)
+{
+ int fd;
+
+ switch (fork()) {
+ case -1:
+ return (-1);
+ case 0:
+ break;
+ default:
+ exit(0);
+ }
+
+ if (setsid() == -1)
+ return (-1);
+
+ if (!nochdir)
+ (void) chdir("/");
+
+ if (!noclose) {
+ struct stat64 st;
+
+ if (((fd = open("/dev/null", O_RDWR, 0)) != -1) &&
+ (fstat64(fd, &st) == 0)) {
+ if (S_ISCHR(st.st_mode) != 0) {
+ (void) dup2(fd, STDIN_FILENO);
+ (void) dup2(fd, STDOUT_FILENO);
+ (void) dup2(fd, STDERR_FILENO);
+ if (fd > 2)
+ (void) close(fd);
+ } else {
+ (void) close(fd);
+ (void) __set_errno(ENODEV);
+ return (-1);
+ }
+ } else {
+ (void) close(fd);
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+int
+main(int argc, char **argv)
+{
+ vold_init(argc, argv);
+
+ return (rmvolmgr(argc, argv));
+}
diff --git a/usr/src/cmd/rmvolmgr/rmvolmgr.xml b/usr/src/cmd/rmvolmgr/rmvolmgr.xml
new file mode 100644
index 0000000000..29906c03b0
--- /dev/null
+++ b/usr/src/cmd/rmvolmgr/rmvolmgr.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ ident "%Z%%M% %I% %E% SMI"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for rmvolmgr.
+-->
+
+<service_bundle type='manifest' name='SUNWrmvolmgrr:rmvolmgr'>
+
+<service
+ name='system/filesystem/rmvolmgr'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='false' />
+
+ <single_instance />
+
+ <dependency name='fs'
+ type='service'
+ grouping='require_all'
+ restart_on='none'>
+ <service_fmri value='svc:/system/filesystem/local' />
+ </dependency>
+
+ <dependency name='dbus'
+ type='service'
+ grouping='require_all'
+ restart_on='none'>
+ <service_fmri value='svc:/system/dbus' />
+ </dependency>
+
+ <dependency name='hal'
+ type='service'
+ grouping='require_all'
+ restart_on='none'>
+ <service_fmri value='svc:/system/hal' />
+ </dependency>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/svc-rmvolmgr start'
+ timeout_seconds='30'/>
+
+ <!-- If device access problems keep rmvolmgr from stopping in
+ 30 seconds, customize this timeout to a higher value.
+ -->
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':kill'
+ timeout_seconds='30' />
+
+ <exec_method
+ type='method'
+ name='refresh'
+ exec='/lib/svc/method/svc-rmvolmgr refresh'
+ timeout_seconds='30' />
+
+ <property_group name='general' type='framework'>
+ <!-- to start stop service -->
+ <propval name='action_authorization' type='astring'
+ value='solaris.smf.manage.rmvolmgr' />
+ <!-- configure service -->
+ <propval name='value_authorization' type='astring'
+ value='solaris.smf.value.rmvolmgr' />
+ </property_group>
+
+ <property_group name='rmvolmgr' type='application'>
+ <propval name='legacy_mountpoints' type='boolean' value='true' />
+ <propval name='cde_compatible' type='boolean' value='true' />
+ </property_group>
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ removable volume manager
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='rmvolmgr' section='1M' manpath='/usr/man' />
+ </documentation>
+ </template>
+
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/rmvolmgr/svc-rmvolmgr b/usr/src/cmd/rmvolmgr/svc-rmvolmgr
new file mode 100644
index 0000000000..e46e5582a2
--- /dev/null
+++ b/usr/src/cmd/rmvolmgr/svc-rmvolmgr
@@ -0,0 +1,56 @@
+#!/sbin/sh
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+
+. /lib/svc/share/smf_include.sh
+
+case "$1" in
+'start')
+ if smf_is_nonglobalzone; then
+ /usr/sbin/svcadm disable $SMF_FMRI
+ echo "$SMF_FMRI is not supported in a local zone"
+ sleep 5 &
+ exit $SMF_EXIT_OK
+ fi
+
+ [ ! -x /usr/lib/rmvolmgr ] && exit $SMF_EXIT_ERR_CONFIG
+
+ /usr/lib/rmvolmgr -s
+ err=$?
+ if [ $err -ne 0 ]; then
+ echo "rmvolmgr failed to start: error $err"
+ exit $SMF_EXIT_ERR_FATAL
+ fi
+ ;;
+
+*)
+ echo "Usage: $0 { start }"
+ exit $SMF_EXIT_ERR_FATAL
+ ;;
+
+
+esac
+
+exit $SMF_EXIT_OK
diff --git a/usr/src/cmd/rmvolmgr/vold.c b/usr/src/cmd/rmvolmgr/vold.c
new file mode 100644
index 0000000000..fec6be8e07
--- /dev/null
+++ b/usr/src/cmd/rmvolmgr/vold.c
@@ -0,0 +1,1089 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Vold compatibility for rmvolmgr: emulate old commands as well as
+ * action_filemgr.so to notify legacy apps via /tmp/.removable pipes.
+ * A lot of this code is copied verbatim from vold sources.
+ *
+ * Here's the original description of action_filemgr.so:
+ *
+ * action_filemgr.so - filemgr interface routines for rmmount
+ *
+ * This shared object allows rmmount to communicate with filemgr.
+ * This is done by communicating over a named pipe that filemgr
+ * creates in directory NOTIFY_DIR. The name of the pipe must
+ * begin with NOTIFY_NAME. This source file contains #define
+ * compiler directives set the values of NOTIFY_DIR and NOTIFY_NAME.
+ *
+ * After a partition on a medium has been mounted as a result of
+ * either insertion or remounting of the medium, the action()
+ * method creates a file named with the symbolic name of the
+ * device in which the medium is inserted and the partition name
+ * (e.g. "jaz0-s2") in NOTIFY_DIR. The file consists of one text
+ * line containing a string naming the mount point of the partition,
+ * a string giving the raw device path to the partition, and a
+ * string naming the file system type on the partition. The action()
+ * method then sends a single character ('i' for insertion, 'r' for
+ * remounting) through the named pipe NOTIFY_NAME to tell filemgr to
+ * look for new files in NOTIFY_DIR.
+ *
+ * If a medium containing no mountable partitions is inserted
+ * or remounted in a device, the action() method creates a file
+ * named with the symbolic name of the device in NOTIFY_DIR.
+ * The file consists of one text line containing a string
+ * giving the symbolic name of the device and a string naming
+ * the reason that the medium couldn't be mounted. The action
+ * method then sends either an 'i' or an 'r' through the named
+ * pipe to tell filemgr to look for new files in NOTIFY_DIR.
+ *
+ * When a medium is ejected or unmounted, the action() method
+ * removes the files that were created in NOTIFY_DIR when the medium
+ * was inserted or remounted and sends a single character ('e' for
+ * ejection, 'u' for unmounting) through the named pipe.
+ *
+ * The following environment variables must be set before calling action():
+ *
+ * VOLUME_ACTION action that occurred (e.g. "insert", "eject")
+ * VOLUME_SYMDEV symbolic name (e.g. "cdrom0", "floppy1")
+ * VOLUME_NAME volume name (e.g. "unnamed_cdrom", "s2")
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <strings.h>
+#include <dirent.h>
+#include <signal.h>
+#include <errno.h>
+#include <libintl.h>
+#include <zone.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/dkio.h>
+#include <sys/cdio.h>
+#include <sys/vtoc.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <libcontract.h>
+#include <sys/contract/process.h>
+#include <sys/ctfs.h>
+#include <tsol/label.h>
+
+#include "vold.h"
+#include "rmm_common.h"
+
+int rmm_debug = 0;
+boolean_t rmm_vold_actions_enabled = B_FALSE;
+boolean_t rmm_vold_mountpoints_enabled = B_FALSE;
+
+static char *prog_name = NULL;
+static pid_t prog_pid = 0;
+static int system_labeled = 0;
+static uid_t mnt_uid = -1;
+static gid_t mnt_gid = -1;
+static zoneid_t mnt_zoneid = -1;
+static char mnt_zoneroot[MAXPATHLEN];
+static char mnt_userdir[MAXPATHLEN];
+
+/*
+ * Private attribute types and attributes.
+ */
+static const char notify_characters[] = {
+ 'e',
+ 'i',
+ 'r',
+ 'u'
+};
+
+static const char *result_strings[] = {
+ "FALSE",
+ "TRUE"
+};
+
+#define NOTIFY_DIR "/tmp/.removable" /* dir where filemgr looks */
+#define NOTIFY_NAME "notify" /* named pipe to talk over */
+
+static void volrmmount_usage();
+static void volcheck_usage();
+static int vold_action(struct action_arg *aap);
+static void vold_update_mountpoints(struct action_arg *aap);
+static char *not_mountable(struct action_arg *aa);
+static int create_one_notify_file(char *fstype,
+ char *mount_point,
+ char *notify_file,
+ char *raw_partitionp,
+ char *reason,
+ char *symdev);
+static int create_notify_files(struct action_arg **aa);
+static boolean_t notify_clients(action_t action, int do_notify);
+static void popdir(int fd);
+static int pushdir(const char *dir);
+static boolean_t remove_notify_files(struct action_arg **aa);
+
+/*
+ * should be called once from main()
+ */
+/* ARGSUSED */
+void
+vold_init(int argc, char **argv)
+{
+ system_labeled = is_system_labeled();
+}
+
+/*
+ * Old version of rmmount(1M)
+ */
+/* ARGSUSED */
+int
+vold_rmmount(int argc, char **argv)
+{
+ char *volume_action;
+ char *volume_mediatype;
+ char *volume_mount_mode;
+ char *volume_name;
+ char *volume_path;
+ char *volume_pcfs_id;
+ char *volume_symdev;
+ char *volume_zonename;
+ char *volume_user;
+ action_t action;
+ char mountpoint[MAXPATHLEN];
+ char *zonemountpoint;
+ char *arg_mountpoint = NULL;
+ LibHalContext *hal_ctx;
+ DBusError error;
+ rmm_error_t rmm_error;
+ int ret;
+
+ prog_name = argv[0];
+ prog_pid = getpid();
+
+ mnt_zoneroot[0] = '\0';
+ mnt_userdir[0] = '\0';
+
+ volume_action = getenv("VOLUME_ACTION");
+ volume_mediatype = getenv("VOLUME_MEDIATYPE");
+ volume_mount_mode = getenv("VOLUME_MOUNT_MODE");
+ volume_name = getenv("VOLUME_NAME");
+ volume_path = getenv("VOLUME_PATH");
+ volume_pcfs_id = getenv("VOLUME_PCFS_ID");
+ volume_symdev = getenv("VOLUME_SYMDEV");
+
+ if (system_labeled) {
+ volume_zonename = getenv("VOLUME_ZONE_NAME");
+ volume_user = getenv("VOLUME_USER");
+ }
+ if (volume_action == NULL) {
+ dprintf("%s(%ld): VOLUME_ACTION was null!!\n",
+ prog_name, prog_pid);
+ return (-1);
+ }
+ if (volume_mediatype == NULL) {
+ dprintf("%s(%ld): VOLUME_MEDIATYPE was null!!\n",
+ prog_name, prog_pid);
+ return (-1);
+ }
+ if (volume_mount_mode == NULL) {
+ volume_mount_mode = "rw";
+ }
+ if (volume_name == NULL) {
+ dprintf("%s(%ld): VOLUME_NAME was null!!\n",
+ prog_name, prog_pid);
+ return (-1);
+ }
+ if (volume_path == NULL) {
+ dprintf("%s(%ld): VOLUME_PATH was null!!\n",
+ prog_name, prog_pid);
+ return (-1);
+ }
+ if (volume_pcfs_id == NULL) {
+ volume_pcfs_id = "";
+ }
+ if (volume_symdev == NULL) {
+ dprintf("%s(%ld): VOLUME_SYMDEV was null!!\n",
+ prog_name, prog_pid);
+ return (-1);
+ }
+
+ if (system_labeled) {
+ if (volume_zonename != NULL &&
+ strcmp(volume_zonename, GLOBAL_ZONENAME) != 0) {
+ if ((mnt_zoneid =
+ getzoneidbyname(volume_zonename)) != -1) {
+ if (zone_getattr(mnt_zoneid, ZONE_ATTR_ROOT,
+ mnt_zoneroot, MAXPATHLEN) == -1) {
+ dprintf("%s(%ld): NO ZONEPATH!!\n",
+ prog_name, prog_pid);
+ return (-1);
+ }
+ }
+ } else {
+ mnt_zoneid = GLOBAL_ZONEID;
+ mnt_zoneroot[0] = '\0';
+ }
+ if (volume_user != NULL) {
+ struct passwd *pw;
+
+ if ((pw = getpwnam(volume_user)) == NULL) {
+ dprintf("%s(%ld) %s\n", prog_name, prog_pid,
+ ": VOLUME_USER was not a valid user!");
+ return (-1);
+ }
+ mnt_uid = pw->pw_uid;
+ mnt_gid = pw->pw_gid;
+
+ if (snprintf(mnt_userdir, sizeof (mnt_userdir),
+ "/%s-%s", volume_user, volume_symdev) >=
+ sizeof (mnt_userdir))
+ return (-1);
+ } else {
+ mnt_uid = 0;
+ mnt_userdir[0] = '\0';
+ }
+
+ rmm_vold_mountpoints_enabled = B_FALSE;
+ rmm_vold_actions_enabled = B_TRUE;
+ } else {
+ rmm_vold_mountpoints_enabled = B_TRUE;
+ rmm_vold_actions_enabled = B_TRUE;
+ }
+
+ if ((hal_ctx = rmm_hal_init(0, 0, 0, &error, &rmm_error)) == NULL) {
+ rmm_dbus_error_free(&error);
+
+ /* if HAL's not running, must be root */
+ if (geteuid() != 0) {
+ (void) fprintf(stderr,
+ gettext("%s(%ld) error: must be root to execute\n"),
+ prog_name, prog_pid);
+ return (-1);
+ }
+ }
+
+ if (strcmp(volume_action, "eject") == 0) {
+ action = EJECT;
+ } else if (strcmp(volume_action, "insert") == 0) {
+ action = INSERT;
+
+ if (system_labeled) {
+ /*
+ * create mount point
+ */
+ if (strlen(mnt_userdir) > 0) {
+ if (snprintf(mountpoint, MAXPATHLEN,
+ "%s/%s%s", mnt_zoneroot, volume_mediatype,
+ mnt_userdir) > MAXPATHLEN) {
+ return (-1);
+
+ }
+ (void) makepath(mountpoint, 0700);
+ (void) chown(mountpoint, mnt_uid, mnt_gid);
+ /*
+ * set the top level directory bits to 0755
+ * so user can access it.
+ */
+ if (snprintf(mountpoint, MAXPATHLEN,
+ "%s/%s", mnt_zoneroot,
+ volume_mediatype) <= MAXPATHLEN) {
+ (void) chmod(mountpoint, 0755);
+ }
+ }
+ if (snprintf(mountpoint, MAXPATHLEN,
+ "%s/%s%s/%s", mnt_zoneroot, volume_mediatype,
+ mnt_userdir, volume_name) > MAXPATHLEN) {
+ (void) fprintf(stderr,
+ gettext("%s(%ld) error: path too long\n"),
+ prog_name, prog_pid);
+ return (-1);
+ }
+
+ /* make our mountpoint */
+ (void) makepath(mountpoint, 0755);
+
+ arg_mountpoint = mountpoint;
+ }
+ } else if (strcmp(volume_action, "remount") == 0) {
+ action = REMOUNT;
+ } else if (strcmp(volume_action, "unmount") == 0) {
+ action = UNMOUNT;
+ }
+
+ ret = rmm_action(hal_ctx, volume_symdev, action, 0, 0, 0,
+ arg_mountpoint) ? 0 : 1;
+
+ if (hal_ctx != NULL) {
+ rmm_hal_fini(hal_ctx);
+ }
+
+ return (ret);
+}
+
+
+/*
+ * this should be called after rmm_hal_{mount,unmount,eject}
+ */
+int
+vold_postprocess(LibHalContext *hal_ctx, const char *udi,
+ struct action_arg *aap)
+{
+ int ret = 0;
+
+ /* valid mountpoint required */
+ if ((aap->aa_action == INSERT) || (aap->aa_action == REMOUNT)) {
+ rmm_volume_aa_update_mountpoint(hal_ctx, udi, aap);
+ if ((aap->aa_mountpoint == NULL) ||
+ (strlen(aap->aa_mountpoint) == 0)) {
+ return (1);
+ }
+ }
+
+ if (rmm_vold_mountpoints_enabled) {
+ vold_update_mountpoints(aap);
+ }
+ if (rmm_vold_actions_enabled) {
+ ret = vold_action(aap);
+ }
+
+ return (ret);
+}
+
+/*
+ * update legacy symlinks
+ *
+ * For cdrom:
+ *
+ * /cdrom/<name> -> original mountpoint
+ * /cdrom/cdrom0 -> ./<name>
+ * /cdrom/cdrom -> cdrom0 (only for cdrom0)
+ *
+ * If it's a slice or partition, /cdrom/<name> becomes a directory:
+ *
+ * /cdrom/<name>/s0
+ *
+ * Same for rmdisk and floppy.
+ *
+ * On labeled system (Trusted Solaris), links are in a user directory.
+ */
+static void
+vold_update_mountpoints(struct action_arg *aap)
+{
+ boolean_t is_partition;
+ char part_dir[2 * MAXNAMELEN];
+ char symname_mp[2 * MAXNAMELEN];
+ char symcontents_mp[MAXNAMELEN];
+ char symname[2 * MAXNAMELEN];
+ char symcontents[MAXNAMELEN];
+
+ is_partition = (aap->aa_partname != NULL);
+
+ if (!system_labeled) {
+ if (!is_partition) {
+ /* /cdrom/<name> -> original mountpoint */
+ (void) snprintf(symcontents_mp, sizeof (symcontents_mp),
+ "%s", aap->aa_mountpoint);
+ (void) snprintf(symname_mp, sizeof (symname_mp),
+ "/%s/%s", aap->aa_media, aap->aa_name);
+ } else {
+ /* /cdrom/<name>/slice -> original mountpoint */
+ (void) snprintf(part_dir, sizeof (part_dir),
+ "/%s/%s", aap->aa_media, aap->aa_name);
+ (void) snprintf(symcontents_mp, sizeof (symcontents_mp),
+ "%s", aap->aa_mountpoint);
+ (void) snprintf(symname_mp, sizeof (symname_mp),
+ "/%s/%s/%s", aap->aa_media, aap->aa_name,
+ aap->aa_partname);
+
+ }
+ /* /cdrom/cdrom0 -> ./<name> */
+ (void) snprintf(symcontents, sizeof (symcontents),
+ "./%s", aap->aa_name);
+ (void) snprintf(symname, sizeof (symname),
+ "/%s/%s", aap->aa_media, aap->aa_symdev);
+ } else {
+ if (!is_partition) {
+ /* /cdrom/<user>/<name> -> original mountpoint */
+ (void) snprintf(symcontents_mp, sizeof (symcontents_mp),
+ "%s", aap->aa_mountpoint);
+ (void) snprintf(symname_mp, sizeof (symname_mp),
+ "%s/%s/%s", mnt_zoneroot, aap->aa_media,
+ aap->aa_symdev);
+ } else {
+ /* /cdrom/<user>/<name>/slice -> original mountpoint */
+ (void) snprintf(symcontents_mp, sizeof (symcontents_mp),
+ "%s", aap->aa_mountpoint);
+ (void) snprintf(symname_mp, sizeof (symname_mp),
+ "%s/%s/%s", mnt_zoneroot, aap->aa_media,
+ aap->aa_symdev, aap->aa_partname);
+ }
+
+ /* /cdrom/<user>/cdrom0 -> ./<user>/<name> */
+ (void) snprintf(symcontents, sizeof (symcontents),
+ ".%s/%s", mnt_userdir, aap->aa_name);
+ (void) snprintf(symname, sizeof (symname), "%s/%s/%s",
+ mnt_zoneroot, aap->aa_media, aap->aa_symdev);
+ }
+
+ (void) unlink(symname);
+ (void) unlink(symname_mp);
+ if (is_partition) {
+ (void) rmdir(part_dir);
+ }
+
+ if ((aap->aa_action == INSERT) || (aap->aa_action == REMOUNT)) {
+ (void) mkdir(aap->aa_media, 0755);
+ if (is_partition) {
+ (void) mkdir(part_dir, 0755);
+ }
+ (void) symlink(symcontents_mp, symname_mp);
+ (void) symlink(symcontents, symname);
+ }
+}
+
+
+static int
+vold_action(struct action_arg *aap)
+{
+ action_t action;
+ int result;
+ int do_notify = FALSE;
+ action_t notify_act = EJECT;
+ struct action_arg *aa[2];
+ struct action_arg a1;
+
+ dprintf("%s[%d]: entering action()\n", __FILE__, __LINE__);
+
+ /*
+ * on Trusted Extensions, actions are executed in the user's zone
+ */
+ if (mnt_zoneid > GLOBAL_ZONEID) {
+ pid_t pid;
+ int status;
+ int ifx;
+ int tmpl_fd;
+ int err = 0;
+
+ tmpl_fd = open64(CTFS_ROOT "/process/template",
+ O_RDWR);
+ if (tmpl_fd == -1)
+ return (1);
+
+ /*
+ * Deliver no events, don't inherit,
+ * and allow it to be orphaned.
+ */
+ err |= ct_tmpl_set_critical(tmpl_fd, 0);
+ err |= ct_tmpl_set_informative(tmpl_fd, 0);
+ err |= ct_pr_tmpl_set_fatal(tmpl_fd,
+ CT_PR_EV_HWERR);
+ err |= ct_pr_tmpl_set_param(tmpl_fd,
+ CT_PR_PGRPONLY |
+ CT_PR_REGENT);
+ if (err || ct_tmpl_activate(tmpl_fd)) {
+ (void) close(tmpl_fd);
+ return (1);
+ }
+ switch (pid = fork1()) {
+ case 0:
+ (void) ct_tmpl_clear(tmpl_fd);
+ for (ifx = 0; ifx < _NFILE; ifx++)
+ (void) close(ifx);
+
+ if (zone_enter(mnt_zoneid) == -1)
+ _exit(0);
+
+ /* entered zone, proceed to action */
+ break;
+ case -1:
+ dprintf("fork1 failed \n ");
+ return (1);
+ default :
+ (void) ct_tmpl_clear(tmpl_fd);
+ (void) close(tmpl_fd);
+ if (waitpid(pid, &status, 0) < 0) {
+ dprintf("%s(%ld): waitpid() "
+ "failed (errno %d) \n",
+ prog_name, prog_pid, errno);
+ return (1);
+ }
+ }
+
+ }
+
+ /* only support one action at a time XXX */
+ a1.aa_path = NULL;
+ aa[0] = aap;
+ aa[1] = &a1;
+
+ action = aa[0]->aa_action;
+
+ if (action == CLEAR_MOUNTS) {
+ /*
+ * Remove the notifications files, but don't
+ * notify the client. The "clear_mounts" action
+ * simply clears all existing mounts of a medium's
+ * partitions after a medium has been repartitioned.
+ * Then vold builds a new file system that reflects
+ * the medium's new partition structure and mounts
+ * the new partitions by calling rmmount, and therefore
+ * action(), with the VOLUME_ACTION environment variable
+ * set to "remount".
+ */
+ result = remove_notify_files(aa);
+ result = TRUE;
+ } else if (action == EJECT) {
+ result = remove_notify_files(aa);
+ if (result == TRUE) {
+ do_notify = TRUE;
+ notify_act = EJECT;
+ }
+ } else if (action = INSERT) {
+ result = create_notify_files(aa);
+ if (result == TRUE) {
+ do_notify = TRUE;
+ notify_act = INSERT;
+ }
+ } else if (action == REMOUNT) {
+ result = create_notify_files(aa);
+ if (result == TRUE) {
+ do_notify = TRUE;
+ notify_act = REMOUNT;
+ }
+ } else if (action == UNMOUNT) {
+ result = remove_notify_files(aa);
+ if (result == TRUE) {
+ do_notify = TRUE;
+ notify_act = UNMOUNT;
+ }
+ } else {
+ dprintf("%s[%d]: action(): invalid action: %s\n",
+ __FILE__, __LINE__, action);
+ result = FALSE;
+ }
+
+ if (result == TRUE) {
+ result = notify_clients(notify_act, do_notify);
+ }
+
+ dprintf("%s[%d]: leaving action(), result = %s\n",
+ __FILE__, __LINE__, result_strings[result]);
+
+ if (mnt_zoneid > GLOBAL_ZONEID) {
+ /* exit forked local zone process */
+ _exit(0);
+ }
+
+ if (result == TRUE) {
+ /*
+ * File Manager is running. return 0.
+ * see man page rmmount.conf(4).
+ */
+ return (0);
+ } else {
+ return (1);
+ }
+}
+
+
+/*
+ * Returns NULL if a medium or partition is mountable
+ * and a string stating the reason the medium or partition
+ * can't be mounted if the medium or partition isn't mountable.
+ *
+ * If the volume_name of the medium or partition is one of the
+ * following, the medium or partition isn't mountable.
+ *
+ * unlabeled_<media_type>
+ * unknown_format
+ * password_protected
+ */
+/* ARGSUSED */
+static char *
+not_mountable(struct action_arg *aa)
+{
+ return (NULL);
+}
+
+static int
+create_notify_files(struct action_arg **aa)
+{
+ int ai;
+ char *fstype;
+ char *mount_point;
+ char notify_file[64];
+ char *raw_partitionp;
+ char *reason; /* Why the medium wasn't mounted */
+ int result;
+ char *symdev;
+
+ dprintf("%s[%d]: entering create_notify_files()\n", __FILE__, __LINE__);
+
+ ai = 0;
+ result = FALSE;
+ symdev = aa[ai]->aa_symdev;
+ while ((aa[ai] != NULL) && (aa[ai]->aa_path != NULL)) {
+ if (aa[ai]->aa_mountpoint != NULL) {
+ if (aa[ai]->aa_type) {
+ fstype = aa[ai]->aa_type;
+ } else {
+ fstype = "unknown";
+ }
+ mount_point = aa[ai]->aa_mountpoint;
+ if (aa[ai]->aa_partname != NULL) {
+ /*
+ * Is aa_partname ever NULL?
+ * When time permits, check.
+ * If it is, the action taken
+ * in the else clause could produce
+ * file name conflicts.
+ */
+ sprintf(notify_file, "%s-%s", symdev,
+ aa[ai]->aa_partname);
+ } else {
+ sprintf(notify_file, "%s-0", symdev);
+ }
+ reason = NULL;
+ } else {
+ /*
+ * The partition isn't mounted.
+ */
+ fstype = "none";
+ mount_point = "none";
+ reason = not_mountable(aa[ai]);
+ if (reason != NULL) {
+ sprintf(notify_file, "%s-0", symdev);
+ } else {
+ /*
+ * Either the partition is a backup slice, or
+ * rmmount tried to mount the partition, but
+ * idenf_fs couldn't identify the file system
+ * type; that can occur when rmmount is
+ * trying to mount all the slices in a Solaris
+ * VTOC, and one or more partitions don't have
+ * file systems in them.
+ */
+ if (aa[0]->aa_partname != NULL) {
+ /*
+ * Is aa_partname ever NULL?
+ * When time permits, check.
+ * If it is, the action taken
+ * in the else clause could produce
+ * file name conflicts.
+ */
+ sprintf(notify_file, "%s-%s", symdev,
+ aa[0]->aa_partname);
+ } else {
+ sprintf(notify_file, "%s-0", symdev);
+ }
+ if ((aa[0]->aa_type != NULL) &&
+ (strcmp(aa[0]->aa_type, "backup_slice")
+ == 0)) {
+ reason = "backup_slice";
+ } else {
+ reason = "unformatted_media";
+ }
+ /*
+ * "unformatted_media" should be
+ * changed to "unformmated_medium" for
+ * grammatical correctness, but
+ * "unformatted_media" is now specified
+ * in the interface to filemgr, so the
+ * change can't be made without the
+ * approval of the CDE group.
+ */
+ }
+ }
+ raw_partitionp = aa[0]->aa_rawpath;
+ result = create_one_notify_file(fstype,
+ mount_point,
+ notify_file,
+ raw_partitionp,
+ reason,
+ symdev);
+ ai++;
+ }
+ dprintf("%s[%d]: leaving create_notify_files(), result = %s\n",
+ __FILE__, __LINE__, result_strings[result]);
+ return (result);
+}
+
+static int
+create_one_notify_file(char *fstype,
+ char *mount_point,
+ char *notify_file,
+ char *raw_partitionp,
+ char *reason,
+ char *symdev)
+{
+ /*
+ * For a mounted partition, create a notification file
+ * indicating the mount point, the raw device pathname
+ * of the partition, and the partition's file system
+ * type. For an unmounted partition, create a
+ * notification file containing the reason that the
+ * partition wasn't mounted and the raw device pathname
+ * of the partition.
+ *
+ * Create the file as root in a world-writable
+ * directory that resides in a world-writable directory.
+ *
+ * Handle two possible race conditions that could
+ * allow security breaches.
+ */
+
+ int current_working_dir_fd;
+ int file_descriptor;
+ FILE *filep;
+ int result;
+
+ dprintf("%s[%d]:Entering create_one_notify_file()\n",
+ __FILE__, __LINE__);
+ dprintf("\tcreate_one_notify_file(): fstype = %s\n", fstype);
+ dprintf("\tcreate_one_notify_file(): mount_point = %s\n", mount_point);
+ dprintf("\tcreate_one_notify_file(): notify_file = %s\n", notify_file);
+ dprintf("\tcreate_one_notify_file(): raw_partitionp = %s\n",
+ raw_partitionp);
+ if (reason != NULL) {
+ dprintf("\tcreate_one_notify_file(): reason = %s\n", reason);
+ } else {
+ dprintf("\tcreate_one_notify_file(): reason = NULL\n");
+ }
+ dprintf("\tcreate_one_notify_file(): symdev = %s\n", symdev);
+
+ result = TRUE;
+ /*
+ * Handle Race Condition One:
+ *
+ * If NOTIFY_DIR exists, make sure it is not a symlink.
+ * if it is, remove it and try to create it. Check
+ * again to make sure NOTIFY_DIR isn't a symlink.
+ * If it is, remove it and return without creating
+ * a notification file. The condition can only occur if
+ * someone is trying to break into the system by running
+ * a program that repeatedly creates NOTIFY_DIR as a
+ * symlink. If NOTIFY_DIR exists and isn't a symlink,
+ * change the working directory to NOTIFY_DIR.
+ */
+ current_working_dir_fd = pushdir(NOTIFY_DIR);
+ if (current_working_dir_fd < 0) {
+ (void) makepath(NOTIFY_DIR, 0777);
+ current_working_dir_fd = pushdir(NOTIFY_DIR);
+ if (current_working_dir_fd < 0) {
+ result = FALSE;
+ }
+ }
+ /*
+ * Handle Race Condition Two:
+ *
+ * Create the notification file in NOTIFY_DIR.
+ * Remove any files with the same name that may already be
+ * there, using remove(), as it safely removes directories.
+ * Then open the file O_CREAT|O_EXCL, which doesn't follow
+ * symlinks and requires that the file not exist already,
+ * so the new file actually resides in the current working
+ * directory. Create the file with access mode 644, which
+ * renders it unusable by anyone trying to break into the
+ * system.
+ */
+ if (result == TRUE) {
+ /*
+ * The current working directory is now NOTIFY_DIR.
+ */
+ (void) remove(notify_file);
+ file_descriptor =
+ open(notify_file, O_CREAT|O_EXCL|O_WRONLY, 0644);
+ if (file_descriptor < 0) {
+ dprintf("%s[%d]: can't create %s/%s; %m\n",
+ __FILE__, __LINE__, NOTIFY_DIR, notify_file);
+ result = FALSE;
+ } else {
+ filep = fdopen(file_descriptor, "w");
+ if (filep != NULL) {
+ if (reason == NULL) {
+ (void) fprintf(filep, "%s %s %s",
+ mount_point,
+ raw_partitionp,
+ fstype);
+ (void) fclose(filep);
+ dprintf("%s[%d]: Just wrote %s %s %s to %s\n",
+ __FILE__,
+ __LINE__,
+ mount_point,
+ raw_partitionp,
+ fstype,
+ notify_file);
+ } else {
+ (void) fprintf(filep, "%s %s",
+ reason, raw_partitionp);
+ (void) fclose(filep);
+ dprintf("%s[%d]: Just wrote %s %s to %s\n",
+ __FILE__,
+ __LINE__,
+ reason,
+ raw_partitionp,
+ notify_file);
+ }
+ } else {
+ dprintf("%s[%d]: can't write %s/%s; %m\n",
+ __FILE__, __LINE__,
+ NOTIFY_DIR, notify_file);
+ (void) close(file_descriptor);
+ result = FALSE;
+ }
+ }
+ popdir(current_working_dir_fd);
+ }
+ dprintf("%s[%d]: leaving create_one_notify_file, result = %s\n",
+ __FILE__, __LINE__, result_strings[result]);
+ return (result);
+}
+
+static boolean_t
+notify_clients(action_t action, int do_notify)
+{
+ /*
+ * Notify interested applications of changes in the state
+ * of removable media. Interested applications are those
+ * that create a named pipe in NOTIFY_DIR with a name that
+ * begins with "notify". Open the pipe and write a
+ * character through it that indicates the type of state
+ * change = 'e' for ejections, 'i' for insertions, 'r'
+ * for remounts of the file systems on repartitioned media,
+ * and 'u' for unmounts of file systems.
+ */
+
+ int current_working_dir_fd;
+ DIR *dirp;
+ struct dirent *dir_entryp;
+ size_t len;
+ int fd;
+ char namebuf[MAXPATHLEN];
+ char notify_character;
+ void (*old_signal_handler)();
+ int result;
+ struct stat sb;
+
+ dprintf("%s[%d]: entering notify_clients()\n", __FILE__, __LINE__);
+
+ result = TRUE;
+ /*
+ * Use relative pathnames after changing the
+ * working directory to the notification directory.
+ * Check to make sure that each "notify" file is a
+ * named pipe to make sure that it hasn't changed
+ * its file type, which could mean that someone is
+ * trying to use "notify" files to break into the
+ * system.
+ */
+ if ((current_working_dir_fd = pushdir(NOTIFY_DIR)) < 0) {
+ result = FALSE;
+ }
+ if (result == TRUE) {
+ dirp = opendir(".");
+ if (dirp == NULL) {
+ dprintf("%s[%d]:opendir failed on '.'; %m\n",
+ __FILE__, __LINE__);
+ popdir(current_working_dir_fd);
+ result = FALSE;
+ }
+ }
+ if (result == TRUE) {
+ /*
+ * Read through the directory and write a notify
+ * character to all files whose names start with "notify".
+ */
+ result = FALSE;
+ old_signal_handler = signal(SIGPIPE, SIG_IGN);
+ len = strlen(NOTIFY_NAME);
+ while (dir_entryp = readdir(dirp)) {
+ if (strncmp(dir_entryp->d_name, NOTIFY_NAME, len)
+ != 0) {
+ continue;
+ }
+ result = TRUE;
+ if (do_notify != TRUE) {
+ continue;
+ }
+ (void) sprintf(namebuf, "%s/%s",
+ NOTIFY_DIR, dir_entryp->d_name);
+ if ((fd = open(namebuf, O_WRONLY|O_NDELAY)) < 0) {
+ dprintf("%s[%d]: open failed for %s; %m\n",
+ __FILE__, __LINE__, namebuf);
+ continue;
+ }
+ /*
+ * Check to be sure that the entry is a named pipe.
+ * That closes a small security hole that could
+ * enable unauthorized access to the system root.
+ */
+ if ((fstat(fd, &sb) < 0) || (!S_ISFIFO(sb.st_mode))) {
+ dprintf("%s[%d]: %s isn't a named pipe\n",
+ __FILE__, __LINE__, namebuf);
+
+ (void) close(fd);
+ continue;
+ }
+ notify_character = notify_characters[action];
+ if (write(fd, &notify_character, 1) < 0) {
+ dprintf("%s[%d]: write failed for %s; %m\n",
+ __FILE__, __LINE__, namebuf);
+ (void) close(fd);
+ continue;
+ }
+ (void) close(fd);
+ }
+ (void) closedir(dirp);
+ (void) signal(SIGPIPE, old_signal_handler);
+ popdir(current_working_dir_fd);
+ }
+ dprintf("%s[%d]: leaving notify_clients(), result = %s\n",
+ __FILE__, __LINE__, result_strings[result]);
+ return (result);
+}
+
+static void
+popdir(int fd)
+{
+ /*
+ * Change the current working directory to the directory
+ * specified by fd and close the fd. Exit the program
+ * on failure.
+ */
+ if (fchdir(fd) < 0) {
+ dprintf("%s[%d]: popdir() failed\n", __FILE__, __LINE__);
+ exit(1);
+ }
+ (void) close(fd);
+}
+
+static int
+pushdir(const char *dir)
+{
+ /*
+ * Change the current working directory to dir and
+ * return a file descriptor for the old working
+ * directory.
+ *
+ * Exception handling:
+ *
+ * If dir doesn't exist, leave the current working
+ * directory the same and return -1.
+ *
+ * If dir isn't a directory, remove it, leave the
+ * current working directory the same, and return -1.
+ *
+ * If open() fails on the current working directory
+ * or the chdir operation fails on dir, leave the
+ * current working directory the same and return -1.
+ */
+
+ int current_working_dir_fd;
+ struct stat stat_buf;
+
+ if (lstat(dir, &stat_buf) < 0) {
+ dprintf("%s[%d]: push_dir_and_check(): %s does not exist\n",
+ __FILE__, __LINE__, dir);
+ return (-1);
+ }
+
+ if (!(S_ISDIR(stat_buf.st_mode))) {
+ dprintf("%s[%d]: push_dir_and_check(): %s not a directory.\n",
+ __FILE__, __LINE__, dir);
+ (void) remove(dir);
+ return (-1);
+ }
+ if ((current_working_dir_fd = open(".", O_RDONLY)) < 0) {
+ dprintf("%s[%d]: push_dir_and_check(): can't open %s.\n",
+ __FILE__, __LINE__, dir);
+ return (-1);
+ }
+ if (chdir(dir) < 0) {
+ (void) close(current_working_dir_fd);
+ dprintf("%s[%d]: push_dir_and_check(): can't chdir() to %s.\n",
+ __FILE__, __LINE__, dir);
+ return (-1);
+ }
+ return (current_working_dir_fd);
+}
+
+static boolean_t
+remove_notify_files(struct action_arg **aa)
+{
+ int ai;
+ int current_working_dir_fd;
+ char notify_file[64];
+ int result;
+ char *symdev;
+
+ dprintf("%s[%d]: entering remove_notify_files()\n", __FILE__, __LINE__);
+
+ ai = 0;
+ result = TRUE;
+ symdev = aa[ai]->aa_symdev;
+ while ((result == TRUE) &&
+ (aa[ai] != NULL) &&
+ (aa[ai]->aa_path != NULL)) {
+
+ if (not_mountable(aa[ai])) {
+ sprintf(notify_file, "%s-0", symdev);
+ } else if (aa[ai]->aa_partname != NULL) {
+ /*
+ * Is aa_partname ever NULL?
+ * When time permits, check.
+ * If it is, the action taken
+ * in the else clause could produce
+ * file name conflicts.
+ */
+ sprintf(notify_file, "%s-%s",
+ symdev, aa[0]->aa_partname);
+ } else {
+ sprintf(notify_file, "%s-0", symdev);
+ }
+
+ current_working_dir_fd = pushdir(NOTIFY_DIR);
+ if (current_working_dir_fd < 0) {
+ result = FALSE;
+ }
+ if ((result == TRUE) && (remove(notify_file) < 0)) {
+ dprintf("%s[%d]: remove %s/%s; %m\n",
+ __FILE__, __LINE__, NOTIFY_DIR, notify_file);
+ result = FALSE;
+ }
+ if (current_working_dir_fd != -1) {
+ popdir(current_working_dir_fd);
+ }
+ ai++;
+ }
+ dprintf("%s[%d]: leaving remove_notify_files(), result = %s\n",
+ __FILE__, __LINE__, result_strings[result]);
+
+ return (result);
+}
diff --git a/usr/src/cmd/rmvolmgr/vold.h b/usr/src/cmd/rmvolmgr/vold.h
new file mode 100644
index 0000000000..5a291cdb54
--- /dev/null
+++ b/usr/src/cmd/rmvolmgr/vold.h
@@ -0,0 +1,73 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _VOLD_H
+#define _VOLD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <libhal.h>
+
+typedef enum {
+ EJECT,
+ INSERT,
+ REMOUNT,
+ UNMOUNT,
+ CLEAR_MOUNTS,
+ CLOSETRAY
+} action_t;
+
+struct action_arg {
+ action_t aa_action; /* VOLUME_ACTION */
+ char *aa_symdev; /* VOLUME_SYMDEV */
+ char *aa_name; /* VOLUME_NAME */
+ char *aa_path; /* special device in question (block) */
+ char *aa_rawpath; /* special device in question (character) */
+ char *aa_type; /* file system type */
+ char *aa_media; /* type of media */
+ char *aa_partname; /* iff a partition, partition name */
+ char *aa_mountpoint; /* path this file system mounted on */
+};
+
+extern int rmm_debug;
+extern boolean_t rmm_vold_actions_enabled;
+extern boolean_t rmm_vold_mountpoints_enabled;
+
+void vold_init(int argc, char **argv);
+int vold_postprocess(LibHalContext *hal_ctx, const char *udi,
+ struct action_arg *aap);
+int vold_rmmount(int argc, char **argv);
+int volrmmount(int argc, char **argv);
+int volcheck(int argc, char **argv);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VOLD_H */