diff options
| author | artem <none@none> | 2006-10-12 22:44:10 -0700 |
|---|---|---|
| committer | artem <none@none> | 2006-10-12 22:44:10 -0700 |
| commit | 18c2aff776a775d34a4c9893a4c72e0434d68e36 (patch) | |
| tree | 2ce07e824c6b4db04bfedb4dea79bc5f255851ed /usr/src/cmd/rmvolmgr | |
| parent | 38787b9f841232f52e2f48dd8a9e475d09954391 (diff) | |
| download | illumos-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/Makefile | 72 | ||||
| -rw-r--r-- | usr/src/cmd/rmvolmgr/rmm_common.c | 1355 | ||||
| -rw-r--r-- | usr/src/cmd/rmvolmgr/rmm_common.h | 106 | ||||
| -rw-r--r-- | usr/src/cmd/rmvolmgr/rmvolmgr.c | 607 | ||||
| -rw-r--r-- | usr/src/cmd/rmvolmgr/rmvolmgr.xml | 118 | ||||
| -rw-r--r-- | usr/src/cmd/rmvolmgr/svc-rmvolmgr | 56 | ||||
| -rw-r--r-- | usr/src/cmd/rmvolmgr/vold.c | 1089 | ||||
| -rw-r--r-- | usr/src/cmd/rmvolmgr/vold.h | 73 |
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, ¬ify_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 */ |
