4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Vold compatibility for rmvolmgr: emulate old commands as well as
28 * action_filemgr.so to notify legacy apps via /tmp/.removable pipes.
29 * A lot of this code is copied verbatim from vold sources.
31 * Here's the original description of action_filemgr.so:
33 * action_filemgr.so - filemgr interface routines for rmmount
35 * This shared object allows rmmount to communicate with filemgr.
36 * This is done by communicating over a named pipe that filemgr
37 * creates in directory NOTIFY_DIR. The name of the pipe must
38 * begin with NOTIFY_NAME. This source file contains #define
39 * compiler directives set the values of NOTIFY_DIR and NOTIFY_NAME.
41 * After a partition on a medium has been mounted as a result of
42 * either insertion or remounting of the medium, the action()
43 * method creates a file named with the symbolic name of the
44 * device in which the medium is inserted and the partition name
45 * (e.g. "jaz0-s2") in NOTIFY_DIR. The file consists of one text
46 * line containing a string naming the mount point of the partition,
47 * a string giving the raw device path to the partition, and a
48 * string naming the file system type on the partition. The action()
49 * method then sends a single character ('i' for insertion, 'r' for
50 * remounting) through the named pipe NOTIFY_NAME to tell filemgr to
51 * look for new files in NOTIFY_DIR.
53 * If a medium containing no mountable partitions is inserted
54 * or remounted in a device, the action() method creates a file
55 * named with the symbolic name of the device in NOTIFY_DIR.
56 * The file consists of one text line containing a string
57 * giving the symbolic name of the device and a string naming
58 * the reason that the medium couldn't be mounted. The action
59 * method then sends either an 'i' or an 'r' through the named
60 * pipe to tell filemgr to look for new files in NOTIFY_DIR.
62 * When a medium is ejected or unmounted, the action() method
63 * removes the files that were created in NOTIFY_DIR when the medium
64 * was inserted or remounted and sends a single character ('e' for
65 * ejection, 'u' for unmounting) through the named pipe.
67 * The following environment variables must be set before calling action():
69 * VOLUME_ACTION action that occurred (e.g. "insert", "eject")
70 * VOLUME_SYMDEV symbolic name (e.g. "cdrom0", "floppy1")
71 * VOLUME_NAME volume name (e.g. "unnamed_cdrom", "s2")
87 #include <sys/types.h>
92 #include <sys/param.h>
94 #include <libcontract.h>
95 #include <sys/contract/process.h>
97 #include <tsol/label.h>
100 #include "rmm_common.h"
103 boolean_t rmm_vold_actions_enabled
= B_FALSE
;
104 boolean_t rmm_vold_mountpoints_enabled
= B_FALSE
;
106 static char *prog_name
= NULL
;
107 static pid_t prog_pid
= 0;
108 static int system_labeled
= 0;
109 static uid_t mnt_uid
= (uid_t
)-1;
110 static gid_t mnt_gid
= (gid_t
)-1;
111 static zoneid_t mnt_zoneid
= -1;
112 static char mnt_zoneroot
[MAXPATHLEN
];
113 static char mnt_userdir
[MAXPATHLEN
];
116 * Private attribute types and attributes.
118 static const char notify_characters
[] = {
125 static const char *result_strings
[] = {
130 #define NOTIFY_DIR "/tmp/.removable" /* dir where filemgr looks */
131 #define NOTIFY_NAME "notify" /* named pipe to talk over */
133 static void volrmmount_usage();
134 static void volcheck_usage();
135 static int vold_action(struct action_arg
*aap
);
136 static void vold_update_mountpoints(struct action_arg
*aap
);
137 static char *not_mountable(struct action_arg
*aa
);
138 static int create_one_notify_file(char *fstype
,
141 char *raw_partitionp
,
144 static int create_notify_files(struct action_arg
**aa
);
145 static boolean_t
notify_clients(action_t action
, int do_notify
);
146 static void popdir(int fd
);
147 static int pushdir(const char *dir
);
148 static boolean_t
remove_notify_files(struct action_arg
**aa
);
151 * should be called once from main()
155 vold_init(int argc
, char **argv
)
157 system_labeled
= is_system_labeled();
161 * Old version of rmmount(1M)
165 vold_rmmount(int argc
, char **argv
)
168 char *volume_mediatype
;
169 char *volume_mount_mode
;
172 char *volume_pcfs_id
;
174 char *volume_zonename
;
177 char mountpoint
[MAXPATHLEN
];
178 char *zonemountpoint
;
179 char *arg_mountpoint
= NULL
;
180 LibHalContext
*hal_ctx
;
182 rmm_error_t rmm_error
;
188 mnt_zoneroot
[0] = '\0';
189 mnt_userdir
[0] = '\0';
191 volume_action
= getenv("VOLUME_ACTION");
192 volume_mediatype
= getenv("VOLUME_MEDIATYPE");
193 volume_mount_mode
= getenv("VOLUME_MOUNT_MODE");
194 volume_name
= getenv("VOLUME_NAME");
195 volume_path
= getenv("VOLUME_PATH");
196 volume_pcfs_id
= getenv("VOLUME_PCFS_ID");
197 volume_symdev
= getenv("VOLUME_SYMDEV");
199 if (system_labeled
) {
200 volume_zonename
= getenv("VOLUME_ZONE_NAME");
201 volume_user
= getenv("VOLUME_USER");
203 if (volume_action
== NULL
) {
204 dprintf("%s(%ld): VOLUME_ACTION was null!!\n",
205 prog_name
, prog_pid
);
208 if (volume_mediatype
== NULL
) {
209 dprintf("%s(%ld): VOLUME_MEDIATYPE was null!!\n",
210 prog_name
, prog_pid
);
213 if (volume_mount_mode
== NULL
) {
214 volume_mount_mode
= "rw";
216 if (volume_name
== NULL
) {
217 dprintf("%s(%ld): VOLUME_NAME was null!!\n",
218 prog_name
, prog_pid
);
221 if (volume_path
== NULL
) {
222 dprintf("%s(%ld): VOLUME_PATH was null!!\n",
223 prog_name
, prog_pid
);
226 if (volume_pcfs_id
== NULL
) {
229 if (volume_symdev
== NULL
) {
230 dprintf("%s(%ld): VOLUME_SYMDEV was null!!\n",
231 prog_name
, prog_pid
);
235 if (system_labeled
) {
236 if (volume_zonename
!= NULL
&&
237 strcmp(volume_zonename
, GLOBAL_ZONENAME
) != 0) {
239 getzoneidbyname(volume_zonename
)) != -1) {
240 if (zone_getattr(mnt_zoneid
, ZONE_ATTR_ROOT
,
241 mnt_zoneroot
, MAXPATHLEN
) == -1) {
242 dprintf("%s(%ld): NO ZONEPATH!!\n",
243 prog_name
, prog_pid
);
248 mnt_zoneid
= GLOBAL_ZONEID
;
249 mnt_zoneroot
[0] = '\0';
251 if (volume_user
!= NULL
) {
254 if ((pw
= getpwnam(volume_user
)) == NULL
) {
255 dprintf("%s(%ld) %s\n", prog_name
, prog_pid
,
256 ": VOLUME_USER was not a valid user!");
259 mnt_uid
= pw
->pw_uid
;
260 mnt_gid
= pw
->pw_gid
;
262 if (snprintf(mnt_userdir
, sizeof (mnt_userdir
),
263 "/%s-%s", volume_user
, volume_symdev
) >=
264 sizeof (mnt_userdir
))
268 mnt_userdir
[0] = '\0';
271 rmm_vold_mountpoints_enabled
= B_FALSE
;
272 rmm_vold_actions_enabled
= B_TRUE
;
274 rmm_vold_mountpoints_enabled
= B_TRUE
;
275 rmm_vold_actions_enabled
= B_TRUE
;
278 if ((hal_ctx
= rmm_hal_init(0, 0, 0, 0, &error
, &rmm_error
)) == NULL
) {
279 rmm_dbus_error_free(&error
);
281 /* if HAL's not running, must be root */
282 if (geteuid() != 0) {
283 (void) fprintf(stderr
,
284 gettext("%s(%ld) error: must be root to execute\n"),
285 prog_name
, prog_pid
);
290 if (strcmp(volume_action
, "eject") == 0) {
292 } else if (strcmp(volume_action
, "insert") == 0) {
295 if (system_labeled
) {
299 if (strlen(mnt_userdir
) > 0) {
300 if (snprintf(mountpoint
, MAXPATHLEN
,
301 "%s/%s%s", mnt_zoneroot
, volume_mediatype
,
302 mnt_userdir
) > MAXPATHLEN
) {
306 (void) makepath(mountpoint
, 0700);
307 (void) chown(mountpoint
, mnt_uid
, mnt_gid
);
309 * set the top level directory bits to 0755
310 * so user can access it.
312 if (snprintf(mountpoint
, MAXPATHLEN
,
313 "%s/%s", mnt_zoneroot
,
314 volume_mediatype
) <= MAXPATHLEN
) {
315 (void) chmod(mountpoint
, 0755);
318 if (snprintf(mountpoint
, MAXPATHLEN
,
319 "%s/%s%s/%s", mnt_zoneroot
, volume_mediatype
,
320 mnt_userdir
, volume_name
) > MAXPATHLEN
) {
321 (void) fprintf(stderr
,
322 gettext("%s(%ld) error: path too long\n"),
323 prog_name
, prog_pid
);
327 /* make our mountpoint */
328 (void) makepath(mountpoint
, 0755);
330 arg_mountpoint
= mountpoint
;
332 } else if (strcmp(volume_action
, "remount") == 0) {
334 } else if (strcmp(volume_action
, "unmount") == 0) {
338 ret
= rmm_action(hal_ctx
, volume_symdev
, action
, 0, 0, 0,
339 arg_mountpoint
) ? 0 : 1;
341 if (hal_ctx
!= NULL
) {
342 rmm_hal_fini(hal_ctx
);
350 * this should be called after rmm_hal_{mount,unmount,eject}
353 vold_postprocess(LibHalContext
*hal_ctx
, const char *udi
,
354 struct action_arg
*aap
)
358 /* valid mountpoint required */
359 if ((aap
->aa_action
== INSERT
) || (aap
->aa_action
== REMOUNT
)) {
360 rmm_volume_aa_update_mountpoint(hal_ctx
, udi
, aap
);
361 if ((aap
->aa_mountpoint
== NULL
) ||
362 (strlen(aap
->aa_mountpoint
) == 0)) {
367 if (rmm_vold_mountpoints_enabled
) {
368 vold_update_mountpoints(aap
);
370 if (rmm_vold_actions_enabled
) {
371 ret
= vold_action(aap
);
378 * update legacy symlinks
382 * /cdrom/<name> -> original mountpoint
383 * /cdrom/cdrom0 -> ./<name>
384 * /cdrom/cdrom -> cdrom0 (only for cdrom0)
386 * If it's a slice or partition, /cdrom/<name> becomes a directory:
390 * Same for rmdisk and floppy.
392 * On labeled system (Trusted Solaris), links are in a user directory.
395 vold_update_mountpoints(struct action_arg
*aap
)
397 boolean_t is_partition
;
398 char part_dir
[2 * MAXNAMELEN
];
399 char symname_mp
[2 * MAXNAMELEN
];
400 char symcontents_mp
[MAXNAMELEN
];
401 char symname
[2 * MAXNAMELEN
];
402 char symcontents
[MAXNAMELEN
];
404 is_partition
= (aap
->aa_partname
!= NULL
);
406 if (!system_labeled
) {
408 /* /cdrom/<name> -> original mountpoint */
409 (void) snprintf(symcontents_mp
, sizeof (symcontents_mp
),
410 "%s", aap
->aa_mountpoint
);
411 (void) snprintf(symname_mp
, sizeof (symname_mp
),
412 "/%s/%s", aap
->aa_media
, aap
->aa_name
);
414 /* /cdrom/<name>/slice -> original mountpoint */
415 (void) snprintf(part_dir
, sizeof (part_dir
),
416 "/%s/%s", aap
->aa_media
, aap
->aa_name
);
417 (void) snprintf(symcontents_mp
, sizeof (symcontents_mp
),
418 "%s", aap
->aa_mountpoint
);
419 (void) snprintf(symname_mp
, sizeof (symname_mp
),
420 "/%s/%s/%s", aap
->aa_media
, aap
->aa_name
,
424 /* /cdrom/cdrom0 -> ./<name> */
425 (void) snprintf(symcontents
, sizeof (symcontents
),
426 "./%s", aap
->aa_name
);
427 (void) snprintf(symname
, sizeof (symname
),
428 "/%s/%s", aap
->aa_media
, aap
->aa_symdev
);
431 /* /cdrom/<user>/<name> -> original mountpoint */
432 (void) snprintf(symcontents_mp
, sizeof (symcontents_mp
),
433 "%s", aap
->aa_mountpoint
);
434 (void) snprintf(symname_mp
, sizeof (symname_mp
),
435 "%s/%s/%s", mnt_zoneroot
, aap
->aa_media
,
438 /* /cdrom/<user>/<name>/slice -> original mountpoint */
439 (void) snprintf(symcontents_mp
, sizeof (symcontents_mp
),
440 "%s", aap
->aa_mountpoint
);
441 (void) snprintf(symname_mp
, sizeof (symname_mp
),
442 "%s/%s/%s", mnt_zoneroot
, aap
->aa_media
,
443 aap
->aa_symdev
, aap
->aa_partname
);
446 /* /cdrom/<user>/cdrom0 -> ./<user>/<name> */
447 (void) snprintf(symcontents
, sizeof (symcontents
),
448 ".%s/%s", mnt_userdir
, aap
->aa_name
);
449 (void) snprintf(symname
, sizeof (symname
), "%s/%s/%s",
450 mnt_zoneroot
, aap
->aa_media
, aap
->aa_symdev
);
453 (void) unlink(symname
);
454 (void) unlink(symname_mp
);
456 (void) rmdir(part_dir
);
459 if ((aap
->aa_action
== INSERT
) || (aap
->aa_action
== REMOUNT
)) {
460 (void) mkdir(aap
->aa_media
, 0755);
462 (void) mkdir(part_dir
, 0755);
464 (void) symlink(symcontents_mp
, symname_mp
);
465 (void) symlink(symcontents
, symname
);
471 vold_action(struct action_arg
*aap
)
475 int do_notify
= FALSE
;
476 action_t notify_act
= EJECT
;
477 struct action_arg
*aa
[2];
478 struct action_arg a1
;
480 dprintf("%s[%d]: entering action()\n", __FILE__
, __LINE__
);
483 * on Trusted Extensions, actions are executed in the user's zone
485 if (mnt_zoneid
> GLOBAL_ZONEID
) {
492 tmpl_fd
= open64(CTFS_ROOT
"/process/template",
498 * Deliver no events, don't inherit,
499 * and allow it to be orphaned.
501 err
|= ct_tmpl_set_critical(tmpl_fd
, 0);
502 err
|= ct_tmpl_set_informative(tmpl_fd
, 0);
503 err
|= ct_pr_tmpl_set_fatal(tmpl_fd
,
505 err
|= ct_pr_tmpl_set_param(tmpl_fd
,
508 if (err
|| ct_tmpl_activate(tmpl_fd
)) {
509 (void) close(tmpl_fd
);
512 switch (pid
= fork1()) {
514 (void) ct_tmpl_clear(tmpl_fd
);
515 for (ifx
= 0; ifx
< _NFILE
; ifx
++)
518 if (zone_enter(mnt_zoneid
) == -1)
521 /* entered zone, proceed to action */
524 dprintf("fork1 failed \n ");
527 (void) ct_tmpl_clear(tmpl_fd
);
528 (void) close(tmpl_fd
);
529 if (waitpid(pid
, &status
, 0) < 0) {
530 dprintf("%s(%ld): waitpid() "
531 "failed (errno %d) \n",
532 prog_name
, prog_pid
, errno
);
538 /* only support one action at a time XXX */
543 action
= aa
[0]->aa_action
;
545 if (action
== CLEAR_MOUNTS
) {
547 * Remove the notifications files, but don't
548 * notify the client. The "clear_mounts" action
549 * simply clears all existing mounts of a medium's
550 * partitions after a medium has been repartitioned.
551 * Then vold builds a new file system that reflects
552 * the medium's new partition structure and mounts
553 * the new partitions by calling rmmount, and therefore
554 * action(), with the VOLUME_ACTION environment variable
557 result
= remove_notify_files(aa
);
559 } else if (action
== EJECT
) {
560 result
= remove_notify_files(aa
);
561 if (result
== TRUE
) {
565 } else if (action
= INSERT
) {
566 result
= create_notify_files(aa
);
567 if (result
== TRUE
) {
571 } else if (action
== REMOUNT
) {
572 result
= create_notify_files(aa
);
573 if (result
== TRUE
) {
575 notify_act
= REMOUNT
;
577 } else if (action
== UNMOUNT
) {
578 result
= remove_notify_files(aa
);
579 if (result
== TRUE
) {
581 notify_act
= UNMOUNT
;
584 dprintf("%s[%d]: action(): invalid action: %s\n",
585 __FILE__
, __LINE__
, action
);
589 if (result
== TRUE
) {
590 result
= notify_clients(notify_act
, do_notify
);
593 dprintf("%s[%d]: leaving action(), result = %s\n",
594 __FILE__
, __LINE__
, result_strings
[result
]);
596 if (mnt_zoneid
> GLOBAL_ZONEID
) {
597 /* exit forked local zone process */
601 if (result
== TRUE
) {
603 * File Manager is running. return 0.
604 * see man page rmmount.conf(4).
614 * Returns NULL if a medium or partition is mountable
615 * and a string stating the reason the medium or partition
616 * can't be mounted if the medium or partition isn't mountable.
618 * If the volume_name of the medium or partition is one of the
619 * following, the medium or partition isn't mountable.
621 * unlabeled_<media_type>
627 not_mountable(struct action_arg
*aa
)
633 create_notify_files(struct action_arg
**aa
)
638 char notify_file
[64];
639 char *raw_partitionp
;
640 char *reason
; /* Why the medium wasn't mounted */
644 dprintf("%s[%d]: entering create_notify_files()\n", __FILE__
, __LINE__
);
648 symdev
= aa
[ai
]->aa_symdev
;
649 while ((aa
[ai
] != NULL
) && (aa
[ai
]->aa_path
!= NULL
)) {
650 if (aa
[ai
]->aa_mountpoint
!= NULL
) {
651 if (aa
[ai
]->aa_type
) {
652 fstype
= aa
[ai
]->aa_type
;
656 mount_point
= aa
[ai
]->aa_mountpoint
;
657 if (aa
[ai
]->aa_partname
!= NULL
) {
659 * Is aa_partname ever NULL?
660 * When time permits, check.
661 * If it is, the action taken
662 * in the else clause could produce
663 * file name conflicts.
665 sprintf(notify_file
, "%s-%s", symdev
,
666 aa
[ai
]->aa_partname
);
668 sprintf(notify_file
, "%s-0", symdev
);
673 * The partition isn't mounted.
676 mount_point
= "none";
677 reason
= not_mountable(aa
[ai
]);
678 if (reason
!= NULL
) {
679 sprintf(notify_file
, "%s-0", symdev
);
682 * Either the partition is a backup slice, or
683 * rmmount tried to mount the partition, but
684 * idenf_fs couldn't identify the file system
685 * type; that can occur when rmmount is
686 * trying to mount all the slices in a Solaris
687 * VTOC, and one or more partitions don't have
688 * file systems in them.
690 if (aa
[0]->aa_partname
!= NULL
) {
692 * Is aa_partname ever NULL?
693 * When time permits, check.
694 * If it is, the action taken
695 * in the else clause could produce
696 * file name conflicts.
698 sprintf(notify_file
, "%s-%s", symdev
,
701 sprintf(notify_file
, "%s-0", symdev
);
703 if ((aa
[0]->aa_type
!= NULL
) &&
704 (strcmp(aa
[0]->aa_type
, "backup_slice")
706 reason
= "backup_slice";
708 reason
= "unformatted_media";
711 * "unformatted_media" should be
712 * changed to "unformmated_medium" for
713 * grammatical correctness, but
714 * "unformatted_media" is now specified
715 * in the interface to filemgr, so the
716 * change can't be made without the
717 * approval of the CDE group.
721 raw_partitionp
= aa
[0]->aa_rawpath
;
722 result
= create_one_notify_file(fstype
,
730 dprintf("%s[%d]: leaving create_notify_files(), result = %s\n",
731 __FILE__
, __LINE__
, result_strings
[result
]);
736 create_one_notify_file(char *fstype
,
739 char *raw_partitionp
,
744 * For a mounted partition, create a notification file
745 * indicating the mount point, the raw device pathname
746 * of the partition, and the partition's file system
747 * type. For an unmounted partition, create a
748 * notification file containing the reason that the
749 * partition wasn't mounted and the raw device pathname
752 * Create the file as root in a world-writable
753 * directory that resides in a world-writable directory.
755 * Handle two possible race conditions that could
756 * allow security breaches.
759 int current_working_dir_fd
;
764 dprintf("%s[%d]:Entering create_one_notify_file()\n",
766 dprintf("\tcreate_one_notify_file(): fstype = %s\n", fstype
);
767 dprintf("\tcreate_one_notify_file(): mount_point = %s\n", mount_point
);
768 dprintf("\tcreate_one_notify_file(): notify_file = %s\n", notify_file
);
769 dprintf("\tcreate_one_notify_file(): raw_partitionp = %s\n",
771 if (reason
!= NULL
) {
772 dprintf("\tcreate_one_notify_file(): reason = %s\n", reason
);
774 dprintf("\tcreate_one_notify_file(): reason = NULL\n");
776 dprintf("\tcreate_one_notify_file(): symdev = %s\n", symdev
);
780 * Handle Race Condition One:
782 * If NOTIFY_DIR exists, make sure it is not a symlink.
783 * if it is, remove it and try to create it. Check
784 * again to make sure NOTIFY_DIR isn't a symlink.
785 * If it is, remove it and return without creating
786 * a notification file. The condition can only occur if
787 * someone is trying to break into the system by running
788 * a program that repeatedly creates NOTIFY_DIR as a
789 * symlink. If NOTIFY_DIR exists and isn't a symlink,
790 * change the working directory to NOTIFY_DIR.
792 current_working_dir_fd
= pushdir(NOTIFY_DIR
);
793 if (current_working_dir_fd
< 0) {
794 (void) makepath(NOTIFY_DIR
, 0777);
795 current_working_dir_fd
= pushdir(NOTIFY_DIR
);
796 if (current_working_dir_fd
< 0) {
801 * Handle Race Condition Two:
803 * Create the notification file in NOTIFY_DIR.
804 * Remove any files with the same name that may already be
805 * there, using remove(), as it safely removes directories.
806 * Then open the file O_CREAT|O_EXCL, which doesn't follow
807 * symlinks and requires that the file not exist already,
808 * so the new file actually resides in the current working
809 * directory. Create the file with access mode 644, which
810 * renders it unusable by anyone trying to break into the
813 if (result
== TRUE
) {
815 * The current working directory is now NOTIFY_DIR.
817 (void) remove(notify_file
);
819 open(notify_file
, O_CREAT
|O_EXCL
|O_WRONLY
, 0644);
820 if (file_descriptor
< 0) {
821 dprintf("%s[%d]: can't create %s/%s; %m\n",
822 __FILE__
, __LINE__
, NOTIFY_DIR
, notify_file
);
825 filep
= fdopen(file_descriptor
, "w");
827 if (reason
== NULL
) {
828 (void) fprintf(filep
, "%s %s %s",
832 (void) fclose(filep
);
833 dprintf("%s[%d]: Just wrote %s %s %s to %s\n",
841 (void) fprintf(filep
, "%s %s",
842 reason
, raw_partitionp
);
843 (void) fclose(filep
);
844 dprintf("%s[%d]: Just wrote %s %s to %s\n",
852 dprintf("%s[%d]: can't write %s/%s; %m\n",
854 NOTIFY_DIR
, notify_file
);
855 (void) close(file_descriptor
);
859 popdir(current_working_dir_fd
);
861 dprintf("%s[%d]: leaving create_one_notify_file, result = %s\n",
862 __FILE__
, __LINE__
, result_strings
[result
]);
867 notify_clients(action_t action
, int do_notify
)
870 * Notify interested applications of changes in the state
871 * of removable media. Interested applications are those
872 * that create a named pipe in NOTIFY_DIR with a name that
873 * begins with "notify". Open the pipe and write a
874 * character through it that indicates the type of state
875 * change = 'e' for ejections, 'i' for insertions, 'r'
876 * for remounts of the file systems on repartitioned media,
877 * and 'u' for unmounts of file systems.
880 int current_working_dir_fd
;
882 struct dirent
*dir_entryp
;
885 char namebuf
[MAXPATHLEN
];
886 char notify_character
;
887 void (*old_signal_handler
)();
891 dprintf("%s[%d]: entering notify_clients()\n", __FILE__
, __LINE__
);
895 * Use relative pathnames after changing the
896 * working directory to the notification directory.
897 * Check to make sure that each "notify" file is a
898 * named pipe to make sure that it hasn't changed
899 * its file type, which could mean that someone is
900 * trying to use "notify" files to break into the
903 if ((current_working_dir_fd
= pushdir(NOTIFY_DIR
)) < 0) {
906 if (result
== TRUE
) {
909 dprintf("%s[%d]:opendir failed on '.'; %m\n",
911 popdir(current_working_dir_fd
);
915 if (result
== TRUE
) {
917 * Read through the directory and write a notify
918 * character to all files whose names start with "notify".
921 old_signal_handler
= signal(SIGPIPE
, SIG_IGN
);
922 len
= strlen(NOTIFY_NAME
);
923 while (dir_entryp
= readdir(dirp
)) {
924 if (strncmp(dir_entryp
->d_name
, NOTIFY_NAME
, len
)
929 if (do_notify
!= TRUE
) {
932 (void) sprintf(namebuf
, "%s/%s",
933 NOTIFY_DIR
, dir_entryp
->d_name
);
934 if ((fd
= open(namebuf
, O_WRONLY
|O_NDELAY
)) < 0) {
935 dprintf("%s[%d]: open failed for %s; %m\n",
936 __FILE__
, __LINE__
, namebuf
);
940 * Check to be sure that the entry is a named pipe.
941 * That closes a small security hole that could
942 * enable unauthorized access to the system root.
944 if ((fstat(fd
, &sb
) < 0) || (!S_ISFIFO(sb
.st_mode
))) {
945 dprintf("%s[%d]: %s isn't a named pipe\n",
946 __FILE__
, __LINE__
, namebuf
);
951 notify_character
= notify_characters
[action
];
952 if (write(fd
, ¬ify_character
, 1) < 0) {
953 dprintf("%s[%d]: write failed for %s; %m\n",
954 __FILE__
, __LINE__
, namebuf
);
960 (void) closedir(dirp
);
961 (void) signal(SIGPIPE
, old_signal_handler
);
962 popdir(current_working_dir_fd
);
964 dprintf("%s[%d]: leaving notify_clients(), result = %s\n",
965 __FILE__
, __LINE__
, result_strings
[result
]);
973 * Change the current working directory to the directory
974 * specified by fd and close the fd. Exit the program
977 if (fchdir(fd
) < 0) {
978 dprintf("%s[%d]: popdir() failed\n", __FILE__
, __LINE__
);
985 pushdir(const char *dir
)
988 * Change the current working directory to dir and
989 * return a file descriptor for the old working
992 * Exception handling:
994 * If dir doesn't exist, leave the current working
995 * directory the same and return -1.
997 * If dir isn't a directory, remove it, leave the
998 * current working directory the same, and return -1.
1000 * If open() fails on the current working directory
1001 * or the chdir operation fails on dir, leave the
1002 * current working directory the same and return -1.
1005 int current_working_dir_fd
;
1006 struct stat stat_buf
;
1008 if (lstat(dir
, &stat_buf
) < 0) {
1009 dprintf("%s[%d]: push_dir_and_check(): %s does not exist\n",
1010 __FILE__
, __LINE__
, dir
);
1014 if (!(S_ISDIR(stat_buf
.st_mode
))) {
1015 dprintf("%s[%d]: push_dir_and_check(): %s not a directory.\n",
1016 __FILE__
, __LINE__
, dir
);
1020 if ((current_working_dir_fd
= open(".", O_RDONLY
)) < 0) {
1021 dprintf("%s[%d]: push_dir_and_check(): can't open %s.\n",
1022 __FILE__
, __LINE__
, dir
);
1025 if (chdir(dir
) < 0) {
1026 (void) close(current_working_dir_fd
);
1027 dprintf("%s[%d]: push_dir_and_check(): can't chdir() to %s.\n",
1028 __FILE__
, __LINE__
, dir
);
1031 return (current_working_dir_fd
);
1035 remove_notify_files(struct action_arg
**aa
)
1038 int current_working_dir_fd
;
1039 char notify_file
[64];
1043 dprintf("%s[%d]: entering remove_notify_files()\n", __FILE__
, __LINE__
);
1047 symdev
= aa
[ai
]->aa_symdev
;
1048 while ((result
== TRUE
) &&
1050 (aa
[ai
]->aa_path
!= NULL
)) {
1052 if (not_mountable(aa
[ai
])) {
1053 sprintf(notify_file
, "%s-0", symdev
);
1054 } else if (aa
[ai
]->aa_partname
!= NULL
) {
1056 * Is aa_partname ever NULL?
1057 * When time permits, check.
1058 * If it is, the action taken
1059 * in the else clause could produce
1060 * file name conflicts.
1062 sprintf(notify_file
, "%s-%s",
1063 symdev
, aa
[0]->aa_partname
);
1065 sprintf(notify_file
, "%s-0", symdev
);
1068 current_working_dir_fd
= pushdir(NOTIFY_DIR
);
1069 if (current_working_dir_fd
< 0) {
1072 if ((result
== TRUE
) && (remove(notify_file
) < 0)) {
1073 dprintf("%s[%d]: remove %s/%s; %m\n",
1074 __FILE__
, __LINE__
, NOTIFY_DIR
, notify_file
);
1077 if (current_working_dir_fd
!= -1) {
1078 popdir(current_working_dir_fd
);
1082 dprintf("%s[%d]: leaving remove_notify_files(), result = %s\n",
1083 __FILE__
, __LINE__
, result_strings
[result
]);