mountpy-0.9/0000755000000000000000000000000013574226110007764 5ustar mountpy-0.9/umountpy.10000644000000000000000000000002412002242506011732 0ustar .so man1/umountpy.1 mountpy-0.9/README0000644000000000000000000000706112002242506010640 0ustar written by Radovan Garabík e-mail: garabik@kassiopeia.juls.savba.sk mountpy is a python script for quick automatic mounting and umounting of external filesystems, especially suited for USB removable devices. mountpy is developped on linux, and is meant for modern linux systems. However, there is not much code linux-specific, so in theory it could work on any POSIX system with similar semantics of setuid(2), you just have to modify mount options. Motivation: I successfully converted several computer illiterate users to linux. While they are perfectly content and willing to learn to write a command before accessing an external filesystem, and an another command before removing the device, removable USB mass storage devices bring a lot of variety - some of them have partition table, some of them have not, they appear as different sd? devices, keeping /etc/fstab updated is difficult, and users have problems finding out the correct device (oce it is /dev/sda, the other time, with the same USB key, /dev/sdb...). This script tries to mount everything it can, probing all configured devices with all possible filesystems, and creating mountpoints as needed. Instalation: make && make install edit /etc/mountpy.conf to suit your needs Usage: There are 3 ways how to make mountpy run comfortably with root privileges: 1) install sudo, add this line to /etc/sudoers: joesmith ALL = NOPASSWD: /usr/bin/mountpy, usr/bin/umountpy to allow user joesmith use mountpy. It you want to allow all the users use mountpy, change the line into: ALL ALL = NOPASSWD: /usr/bin/mountpy, usr/bin/umountpy and then run "sudo mountpy" and "sudo umountpy" 2) install super, add these lines to /etc/super.tab: mountpy /usr/bin/mountpy.py joesmith umountpy /usr/bin/umountpy joesmith to allow user joesmith to run mountpy, or: mountpy /usr/bin/mountpy.py .* umountpy /usr/bin/umountpy .* to give access to all the users, and run "super mountpy" or "super umountpy" 3) make the C wrapper script setuid root log in as root and type: chmod u+s /usr/bin/mountpy Be aware that giving all the users rights to execute mountpy can be a security risk, if an exploitable bug is found in mountpy. This is especially true in the 3rd case. Therefore, it is recommended to allow only selected users to run mountpy, and use super or sudo if possible. use: $ mountpy to mount all the devices. Short summary will be printed. By default, you can find mounted filesystems under /media use: $ mountpy -u or $ umountpy to umount everything it can. Short summary will be printed, with warning if some device could not have been umounted $ mountpy device tries to mount only one device, e.g.: $ mountpy sda $ mountpy sda fd0 cdrom use -v to verbosely print what is going on, -vv to get even more information. Security: Do not install setuid mountpy on servers with multiuser access! It has no use in such an envirnment, anyway. While there is nothing directly exploitable, it is easy to trick python into executing something other It is perfectly suited for a workstation or a notebook, if a user already knows the root password, or in similar circumstances. Using sudo or super is safer, because you can select users who are allowed to run mountpy, but eventual bug in mountpy or python could allow them to gain unauthorized root access anyway. Special care has beed taken to avoid accidentally removing directories during umount, so the script should be safe. Of course, bugs are always possible. Using broken hardware (bad floppy, cdrom, USB key) can hang the program, or even crash the kernel, as with ordinary mount. mountpy-0.9/umountpy0000755000000000000000000000003512002242506011600 0ustar #! /bin/sh mountpy -u "$@" mountpy-0.9/mountpy.conf0000644000000000000000000000440013574224553012355 0ustar #device prefix device_prefix = '/dev' # list of devices to probe. Use glob wildcards probe_devices = [ 'sd[a-z]', 'sd[a-z][1-9]', #'hd[a-z]', #'hd[a-z][1-9]', 'fd[0-1]', 'cdrom', 'sr[0-9]', 'mmcblk[0-9]', 'mmcblk[0-9]p[1-9]', ] # list of devices NOT TO probe, e.g. floppy can be time consuming if there is no # floppy drive in the PC. Or add 'sda' 'sda?' here for your primary disk blacklist_devices = [ 'fd1', #'sda', #'sda?', ] # list of devices that are tested even if they are not removables # useful for hot/cold-swapped IDE disks etc... whitelist_devices = [ #'hdc', ] # default options for mount #default_options = 'users,noatime,sync,dirsync' # sync has terrible performance with vfat for kernels >= 2.6.14, therefore it is turned off # it is not ideal, however the performance is again acceptable with 2.6.20 default_options = 'users,noatime,dirsync' # per-filesystem options # use two special variables %(uid)s and %(gid)s for current user's uid and gid fs_options = { 'vfat': default_options+',uid=%(uid)s,gid=%(gid)s,utf8', 'msdos': default_options+',uid=%(uid)s,gid=%(gid)s,utf8', 'iso9660': 'users,ro,utf8,unhide', 'ntfs': 'users,ro,nls=utf8,uid=%(uid)s,gid=%(gid)s', 'ext2': default_options+',sync', 'auto': default_options+',sync' } # try these filesystems, in this order. # put auto as the last one, since most probably it will succeed # # you do not need to specify filesystems with the same options # as auto here, to speed things up try_filesystems = ['vfat','msdos','iso9660','ntfs-3g','ntfs','auto'] mount_command = '/bin/mount' umount_command = '/bin/umount' # call sync before unmounting. Possible values are True, False do_sync=True # all the devices are mounted under this directory mntdir = '/media/' # check if the medium to be mounted is recognized by kernel as a removable one # possible values are: # 'no' - no checking is performed # 'relaxed' - if the medium is certainly not a removable, deny mounting it, # but mount it otherwise # 'strict' - allow mounting only if it is certainly removable # # unfortunately, many usb devices, different hotplugged disks are # not marked as removables, so better let this to be 'no' check_removables = 'no' # where to find list of already mounted filesystems # /etc/mtab or /proc/mounts mtab = '/etc/mtab' mountpy-0.9/TODO0000644000000000000000000000010312002242465010442 0ustar Do something about processes hanging in mounted directories (kill) mountpy-0.9/Makefile0000644000000000000000000000073012002242506011414 0ustar DESTDIR = CC = gcc mountpy: mountpy.c $(CC) -O2 -Wall mountpy.c -o mountpy all: mountpy clean: rm -f mountpy strip: strip mountpy install: strip nostripinstall nostripinstall: cp -v mountpy.py $(DESTDIR)/usr/bin/ cp -v mountpy $(DESTDIR)/usr/bin/ cp -v umountpy $(DESTDIR)/usr/bin/ #chown -v root:root $(DESTDIR)/usr/bin/mountpy #chmod -v u+s $(DESTDIR)/usr/bin/mountpy if [ ! -e "$(DESTDIR)/etc/mountpy.conf" ]; then cp -v mountpy.conf $(DESTDIR)/etc/; fi mountpy-0.9/debian/0000755000000000000000000000000013574226110011206 5ustar mountpy-0.9/debian/copyright0000644000000000000000000000172112002242570013133 0ustar This package was created by by Radovan Garabík on Thu, 9 Dec 2004 12:41:38 +0100. © Radovan Garabík License: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this package; if not, write to the Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA On Debian systems, the complete text of the GNU General Public License can be found in `/usr/share/common-licenses/GPL'. mountpy-0.9/debian/docs0000644000000000000000000000001412002242564012050 0ustar README TODO mountpy-0.9/debian/control0000644000000000000000000000114213574226110012607 0ustar Source: mountpy Section: utils Priority: optional Maintainer: Radovan Garabík Build-Depends: debhelper (>= 5), python3 Standards-Version: 3.7.2 Package: mountpy Architecture: any Depends: ${shlibs:Depends}, python3 Suggests: super | sudo Description: script for quick mounting of removable devices mountpy scans all devices connected to the system, and tries to mount them, creating mount directories as needed. Warning: After configured, this program allows ordinary users to mount external filesystems. Do not install it on multiuser machines with untrusted users! mountpy-0.9/debian/changelog0000644000000000000000000000433613574226110013066 0ustar mountpy (0.9) unstable; urgency=low * Convert to python3 (closes: #937084) -- Radovan Garabík Wed, 11 Dec 2019 18:50:00 +0100 mountpy (0.8.1) unstable; urgency=low * add nostripinstall to Makefile, use it in debian/rules (closes: #437579) -- Radovan Garabík Sun, 30 Sep 2007 18:31:32 +0200 mountpy (0.8) unstable; urgency=low * add test for removable devices (default turned off) * add test for devices already mounted elsewhere -- Radovan Garabík Wed, 7 Mar 2007 20:23:16 +0100 mountpy (0.7) unstable; urgency=low * add mmcblk[0-9] and mmcblk[0-9]p[1-9] to the list of probed devices (MMC card readers) -- Radovan Garabík Sat, 2 Sep 2006 11:27:07 +0200 mountpy (0.6) unstable; urgency=low * add the possibility to be called via sudo or super * remove setuid bit from Debian package * add sr[0-9] to the list of probed devices (USB CD and DVD drives) * add checking for malicious device names -- Radovan Garabík Sat, 13 May 2006 11:19:45 +0200 mountpy (0.5) unstable; urgency=low * use setuid wrapper supplied in python source distribution (suggested by Олег Бройтман) * do not try to umount directory which is not a mountpoint -- Radovan Garabík Sat, 24 Dec 2005 18:34:09 +0100 mountpy (0.4) unstable; urgency=low * do not remove the directories when umounting (caused some problems with gnome and kde) * use filesystem type explicitely during mount -- Radovan Garabík Tue, 13 Dec 2005 20:22:06 +0100 mountpy (0.3) unstable; urgency=low * debian package reworked * some small fixes * added umountpy shell script -- Radovan Garabík Sat, 30 Apr 2005 16:22:08 +0200 mountpy (0.2) unstable; urgency=low * change default mount directory to /media -- Radovan Garabík Thu, 30 Dec 2004 10:19:05 +0100 mountpy (0.1) unstable; urgency=low * Initial Release. -- Radovan Garabík Thu, 9 Dec 2004 12:41:38 +0100 mountpy-0.9/debian/compat0000644000000000000000000000000212002242564012400 0ustar 5 mountpy-0.9/debian/dirs0000644000000000000000000000001512002242564012062 0ustar usr/bin /etc mountpy-0.9/debian/rules0000755000000000000000000000405712002242570012265 0ustar #!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # This is the debhelper compatibility version to use. #export DH_COMPAT=4 CFLAGS = -Wall -g ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) CFLAGS += -O0 else CFLAGS += -O2 endif configure: configure-stamp configure-stamp: dh_testdir # Add here commands to configure the package. touch configure-stamp build: build-stamp build-stamp: configure-stamp dh_testdir # Add here commands to compile the package. $(MAKE) #docbook-to-man debian/mountpy.sgml > mountpy.1 touch build-stamp clean: dh_testdir dh_testroot rm -f build-stamp configure-stamp # Add here commands to clean up after the build process. -$(MAKE) clean dh_clean install: build dh_testdir dh_testroot dh_clean -k dh_installdirs # Add here commands to install the package into debian/mountpy. $(MAKE) nostripinstall DESTDIR=$(CURDIR)/debian/mountpy # Build architecture-independent files here. binary-indep: build install # We have nothing to do by default. # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_installchangelogs dh_installdocs # dh_installexamples # dh_install # dh_installmenu # dh_installdebconf # dh_installlogrotate # dh_installemacsen # dh_installpam # dh_installmime # dh_installinit # dh_installcron # dh_installinfo dh_installman mountpy.1 dh_link usr/share/man/man1/mountpy.1 usr/share/man/man1/umountpy.1 dh_strip dh_compress dh_fixperms --exclude /usr/bin/mountpy # dh_perl # dh_python # dh_makeshlibs dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install configure mountpy-0.9/mountpy.c0000644000000000000000000001267312002242506011644 0ustar /* Template for a setuid program that calls a script. The script should be in an unwritable directory and should itself be unwritable. In fact all parent directories up to the root should be unwritable. The script must not be setuid, that's what this program is for. This is a template program. You need to fill in the name of the script that must be executed. This is done by changing the definition of FULL_PATH below. There are also some rules that should be adhered to when writing the script itself. The first and most important rule is to never, ever trust that the user of the program will behave properly. Program defensively. Check your arguments for reasonableness. If the user is allowed to create files, check the names of the files. If the program depends on argv[0] for the action it should perform, check it. Assuming the script is a Bourne shell script, the first line of the script should be #!/bin/sh - The - is important, don't omit it. If you're using esh, the first line should be #!/usr/local/bin/esh -f and for ksh, the first line should be #!/usr/local/bin/ksh -p The script should then set the variable IFS to the string consisting of , , and . After this (*not* before!), the PATH variable should be set to a reasonable value and exported. Do not expect the PATH to have a reasonable value, so do not trust the old value of PATH. You should then set the umask of the program by calling umask 077 # or 022 if you want the files to be readable If you plan to change directories, you should either unset CDPATH or set it to a good value. Setting CDPATH to just ``.'' (dot) is a good idea. If, for some reason, you want to use csh, the first line should be #!/bin/csh -fb You should then set the path variable to something reasonable, without trusting the inherited path. Here too, you should set the umask using the command umask 077 # or 022 if you want the files to be readable */ #include #include #include #include #include #include /* CONFIGURATION SECTION */ #ifndef FULL_PATH /* so that this can be specified from the Makefile */ /* Uncomment the following line: */ #define FULL_PATH "/usr/bin/mountpy.py" /* Then comment out the #error line. #error "You must define FULL_PATH somewhere" */ #endif #ifndef UMASK #define UMASK 077 #endif /* END OF CONFIGURATION SECTION */ #if defined(__STDC__) && defined(__sgi) #define environ _environ #endif /* don't change def_IFS */ char def_IFS[] = "IFS= \t\n"; /* you may want to change def_PATH, but you should really change it in */ /* your script */ #ifdef __sgi char def_PATH[] = "PATH=/usr/bsd:/usr/bin:/bin:/usr/local/bin:/usr/sbin"; #else char def_PATH[] = "PATH=/usr/ucb:/usr/bin:/bin:/usr/local/bin"; #endif /* don't change def_CDPATH */ char def_CDPATH[] = "CDPATH=."; /* don't change def_ENV */ char def_ENV[] = "ENV=:"; /* This function changes all environment variables that start with LD_ into variables that start with XD_. This is important since we don't want the script that is executed to use any funny shared libraries. The other changes to the environment are, strictly speaking, not needed here. They can safely be done in the script. They are done here because we don't trust the script writer (just like the script writer shouldn't trust the user of the script). If IFS is set in the environment, set it to space,tab,newline. If CDPATH is set in the environment, set it to ``.''. Set PATH to a reasonable default. */ void clean_environ(void) { char **p; extern char **environ; for (p = environ; *p; p++) { if (strncmp(*p, "LD_", 3) == 0) **p = 'X'; else if (strncmp(*p, "_RLD", 4) == 0) **p = 'X'; else if (strncmp(*p, "PYTHON", 6) == 0) **p = 'X'; else if (strncmp(*p, "IFS=", 4) == 0) *p = def_IFS; else if (strncmp(*p, "CDPATH=", 7) == 0) *p = def_CDPATH; else if (strncmp(*p, "ENV=", 4) == 0) *p = def_ENV; } putenv(def_PATH); } int main(int argc, char **argv) { struct stat statb; gid_t egid = getegid(); uid_t euid = geteuid(); /* Sanity check #1. This check should be made compile-time, but that's not possible. If you're sure that you specified a full path name for FULL_PATH, you can omit this check. */ if (FULL_PATH[0] != '/') { fprintf(stderr, "%s: %s is not a full path name\n", argv[0], FULL_PATH); fprintf(stderr, "You can only use this wrapper if you\n"); fprintf(stderr, "compile it with an absolute path.\n"); exit(1); } /* Sanity check #2. Check that the owner of the script is equal to either the effective uid or the super user. */ if (stat(FULL_PATH, &statb) < 0) { perror("stat"); exit(1); } if (statb.st_uid != 0 && statb.st_uid != euid) { fprintf(stderr, "%s: %s has the wrong owner\n", argv[0], FULL_PATH); fprintf(stderr, "The script should be owned by root,\n"); fprintf(stderr, "and shouldn't be writeable by anyone.\n"); exit(1); } if (setregid(egid, egid) < 0) perror("setregid"); /* we must NOT call setreuid since then the python script would think we invoked it as root */ /* if (setreuid(euid, euid) < 0) perror("setreuid"); */ clean_environ(); umask(UMASK); while (**argv == '-') /* don't let argv[0] start with '-' */ (*argv)++; execv(FULL_PATH, argv); fprintf(stderr, "%s: could not execute the script\n", argv[0]); exit(1); } mountpy-0.9/mountpy.py0000755000000000000000000003064213574226110012061 0ustar #!/usr/bin/python3 import os, glob, sys, string from subprocess import getstatusoutput from optparse import OptionParser #default configuration config_file = '/etc/mountpy.conf' #device prefix device_prefix = '/dev' probe_devices = [ 'sd[a-z]', 'sd[a-z][1-9]', 'fd[0-1]', 'cdrom', 'sr[0-9]' ] blacklist_devices = [ #'fd[0-1]' ] whitelist_devices = [ ] default_options = 'users,noatime,sync,dirsync' default_options2 = 'users,noatime,dirsync' # ('filesystem_name', 'filesystem_options') fs_options = { 'vfat': default_options2+',uid=%(uid)s,gid=%(gid)s,utf8', 'msdos': default_options2+',uid=%(uid)s,gid=%(gid)s,utf8', 'iso9660': 'users,ro,utf8,unhide', 'ntfs': 'users,ro,nls=utf8,uid=%(uid)s,gid=%(gid)s', 'auto': default_options } try_filesystems = ['vfat', 'msdos', 'iso9660', 'ntfs-3g', 'ntfs', 'auto'] mount_command = '/bin/mount' umount_command = '/bin/umount' do_sync = True device_map = { } mntdir = '/media/' check_removables = "no" mtab = '/etc/mtab' # end of default options VERSION='0.8.1' if not os.path.isfile(config_file): print ("Missing configuration file", config_file) sys.exit() exec(open(config_file).read()) #safety checks if ( not os.path.isabs(mntdir) or len(os.path.normpath(mntdir))<2 # empty or root / ): print("mntdir is set to", repr(mntdir)) print("This looks insane to me, refusing to continue") print("Please edit /etc/mountpy.conf") print("If you have no idea what this means, contact your system administrator.") print("If you have no idea what this means and YOU are ") print("an administrator, go read some basic literature about UNIX") print("and then try again.") sys.exit() colours = { 'none' : "", 'default' : "\033[0m", 'bold' : "\033[1m", 'underline' : "\033[4m", 'blink' : "\033[5m", 'reverse' : "\033[7m", 'concealed' : "\033[8m", 'black' : "\033[30m", 'red' : "\033[31m", 'green' : "\033[32m", 'yellow' : "\033[33m", 'blue' : "\033[34m", 'magenta' : "\033[35m", 'cyan' : "\033[36m", 'white' : "\033[37m", 'on_black' : "\033[40m", 'on_red' : "\033[41m", 'on_green' : "\033[42m", 'on_yellow' : "\033[43m", 'on_blue' : "\033[44m", 'on_magenta' : "\033[45m", 'on_cyan' : "\033[46m", 'on_white' : "\033[47m", 'beep' : "\007", } alert_colour = colours['red']+colours['bold'] info_colour = colours['green'] default_colour = colours['default'] def alert(*text): sys.stdout.write(alert_colour) sys.stdout.write(' '.join(text)) sys.stdout.write(' '*20) sys.stdout.write('\n') sys.stdout.write(default_colour) sys.stdout.flush() def info(*text): sys.stdout.write(info_colour) sys.stdout.write(' '.join(text)) sys.stdout.write(' '*20) sys.stdout.write('\n') sys.stdout.write(default_colour) sys.stdout.flush() def debug(*text): sys.stdout.write(' '.join(text)) sys.stdout.write(' '*20) sys.stdout.write('\n') def msg(*text): sys.stdout.write(' '.join(text)) sys.stdout.write(' '*20) sys.stdout.write('\r') sys.stdout.flush() def sync_it(): os.system('/bin/sync &') def safe_rmdir(path): "remove directory, but do some safety checks before" if ( (os.path.isdir(path) or os.path.islink(path)) and os.path.isabs(path) and not os.path.ismount(path) and path.startswith(mntdir) and path == os.path.normpath(path) and path.startswith(mntdir) ): if os.path.islink(path): os.remove(path) else: os.rmdir(path) else: raise IOError("Trying to remove suspicious directory: "+repr(path)) def get_all_devices(list_to_probe): r = [] for i in list_to_probe: path = os.path.join(device_prefix, i) path = os.path.normpath(path) for j in glob.glob(path): x = os.path.normpath(j) if x.startswith(device_prefix): r.append(j) else: alert("Discarding suspicious device "+repr(j)) return r def check_device(devicepath): "check if device exists at all" try: os.open(devicepath, os.O_RDONLY) except OSError: return False return True def is_removable(device): "test if device is removable" "possible return values are: 'no', 'dunno', 'yes'" "test is done by reading /sys/block/hda/removable" drive = list(device) while len(drive)>=1 and drive[-1] in string.digits: del drive[-1] drive = ''.join(drive) path = '/sys/block/%s/removable' % drive try: val = file(path).read() except IOError: return 'dunno' val = val.strip() if val == '0': return 'no' elif val == '1': return 'yes' return 'dunno' def try_mount(devicepath, fs): dir_was_created = False device = os.path.split(devicepath)[1] mountpoint = os.path.join(mntdir, device) if os.path.islink(mountpoint) and not os.path.exists(mountpoint): # broken symlink info(mountpoint + " is a broken symlink - removing") safe_rmdir(mountpoint) if os.path.exists(mountpoint): verbose(mountpoint + " already exists") if os.path.ismount(mountpoint): verbose(mountpoint + " is already mounted - skipping") return else: os.makedirs(mountpoint) dir_was_created = True d = {'uid':uid, 'gid':gid} mount_options = fs_options.get(fs, default_options) % d command = '%s "%s" "%s" -t %s -o %s' % (mount_command, devicepath, mountpoint, fs, mount_options) verbose('Going to execute: '+command, 2) status, output = getstatusoutput(command) if status == 0: return True else: verbose("%s %s" %(status, output)) if dir_was_created: safe_rmdir(mountpoint) return False def try_umount(devicepath): mountpoint = os.path.join(mntdir, os.path.split(devicepath)[1]) if os.path.islink(mountpoint): #verbose(mountpoint + " is a symlink - removing") #safe_rmdir(mountpoint) verbose(mountpoint + " is a symlink - skipping") return None if not os.path.exists(mountpoint): verbose(mountpoint + " does not exists") return None if not os.path.ismount(mountpoint): verbose(mountpoint + " is not a mountpoint") return None # if not os.path.ismount(mountpoint): # verbose(mountpoint + " is not mounted - removing") # safe_rmdir(mountpoint) # return None command = '%s "%s"' % (umount_command, mountpoint) status, output = getstatusoutput(command) if status == 0: # safe_rmdir(mountpoint) return True else: alert("Could not umount %s, reason: %s\n" %(devicepath, output)) return False # could not umount def get_mounted_fs(mtab): "try to get a list of already mounted devices" "return dictionary of device: mountpoint" devices = {} if os.path.exists(mtab): try: lines = open(mtab).readlines() devices = [x.split()[0:2] for x in lines] devices = dict(devices) except IOError: devices = {} return devices def call_sync(): verbose('Calling sync') os.system('sync') how_to_run = ''' mountpy is not being run with enough privileges. There are 3 ways how to make mountpy run comfortably with root privileges: 1) install sudo, add this line to /etc/sudoers: joesmith ALL = NOPASSWD: /usr/bin/mountpy, usr/bin/umountpy to allow user joesmith use mountpy. It you want to allow all the users use mountpy, change the line into: ALL ALL = NOPASSWD: /usr/bin/mountpy, usr/bin/umountpy and then run "sudo mountpy" and "sudo umountpy" 2) install super, add these lines to /etc/super.tab: mountpy /usr/bin/mountpy.py joesmith umountpy /usr/bin/umountpy joesmith to allow user joesmith to run mountpy, or: mountpy /usr/bin/mountpy.py .* umountpy /usr/bin/umountpy .* to give access to all the users, and run "super mountpy" or "super umountpy" 3) make the C wrapper script setuid root log in as root and type: chmod u+s /usr/bin/mountpy Be aware that giving all the users rights to execute mountpy can be a security risk, if an exploitable bug is found in mountpy. This is especially true in the 3rd case. Therefore, it is recommended to allow only selected users to run mountpy, and use super or sudo if possible. ''' def verbose(text, verbosity = 1): if options.verbosity >= verbosity: debug(text) parser = OptionParser(usage="usage: %prog [options] arg") parser.add_option("-u", "--umount", action="store_const", const='umount', dest="action", default="mount", help="Umount") parser.add_option("-m", "--mount", action="store_const", const='mount', dest="action", default="mount", help="Mount") parser.add_option("-v", "--verbose", action="count", dest="verbosity", default=0, help="Increase verbosity") (options, args) = parser.parse_args() if options.verbosity > 0: verbose("Action " + options.action) if len(args)==0: verbose("Will try all possible devices") if options.action == 'mount': devices = get_all_devices(probe_devices) elif options.action == 'umount': devices = os.listdir(mntdir) else: raise else: devices = get_all_devices(args) blacklist_devices = get_all_devices(blacklist_devices) wihtelist_devices = get_all_devices(whitelist_devices) processed = [] # list of succesfully mounted/umounted devices unsuccessful = [] # list of devices that could not have been umounted uid = os.getuid() gid = os.getgid() euid = os.geteuid() # test if we are running with sudo if uid==0: if 'SUDO_UID' in os.environ: uid = int(os.environ['SUDO_UID']) verbose("sudo detected") verbose("Detected UID %s, GID %s, EUID %s" % (uid, gid, euid)) if euid != 0: msg(how_to_run) raw_input("Press ENTER to try anyway") if options.action == "umount" and do_sync: call_sync() os.setuid(0) mounted_devicepaths = get_mounted_fs(mtab) for devicepath in devices: if devicepath in blacklist_devices: verbose("Blacklisted "+ devicepath, 2) continue device = os.path.split(devicepath)[1] if options.action == "mount": if device not in whitelist_devices and check_removables != 'no': removable = is_removable(device) if check_removables == 'strict' and removable != 'yes': verbose(device + " is not known to be removable and you have strict checking on") continue elif check_removables == 'relaxed' and removable == 'no': verbose(device + " is not removable and you have relaxed checking on") continue if devicepath in mounted_devicepaths: info(devicepath, 'seems to be already mounted on', mounted_devicepaths[devicepath]) continue msg("Checking", devicepath) if not check_device(devicepath): verbose("Device not present "+ devicepath, 2) continue for fs in try_filesystems: verbose("Trying to mount " + devicepath +' '+fs, 2) msg("Trying to mount", devicepath, fs) r = try_mount(devicepath, fs) if r: info("Mounted", devicepath, fs) processed.append(devicepath) break elif options.action == "umount": verbose("Trying to umount " + devicepath, 2) msg("Trying to umount", devicepath) r = try_umount(devicepath) if r: info("Umounted", devicepath) processed.append(devicepath) if r==False: unsuccessful.append(devicepath) if options.action == "mount": if processed: info('List of mounted devices: ', ' '.join(processed)) else: info('Found nothing to mount') if options.action == "umount": if processed: info('List of umounted devices: ', ' '.join(processed)) if unsuccessful: sync_it() alert('WARNING: these devices remained mounted: ', ' '.join(unsuccessful)) if not processed and not unsuccessful: info('Found nothing to umount') mountpy-0.9/mountpy.10000644000000000000000000000324113574224637011575 0ustar .\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH MOUNTPY 1 "2019-12-11" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME mountpy \- program for quick automatic mounting and umounting of external filesystems, especially suited for USB removable devices. .SH SYNOPSIS .B mountpy .RI [ options ] " " [ devices ] .br .B umountpy .RI [ options ] " " [ devices ] .SH DESCRIPTION .PP Just run .B mountpy to mount all the devices. Short summary will be printed. By default, you can find mounted filesystems under /media .PP Run .B mountpy -u or .B umountpy to umount everything it can. Short summary will be printed, with warning if some device could not have been umounted .PP .B mountpy device tried to mount only one device, e.g.: .B mountpy sda or more devices, e.g. .B mountpy sda fd0 cdrom use -v to verbosely print what is going on, -vv to get even more information. .SH OPTIONS .TP .B \-u, \-\-umount Umount evrything possible. .TP .B \-h, \-\-help Show summary of options. .TP .B \-v Be verbose .TP .B \-vv Be very verbose .SH SEE ALSO .BR mount (8) .SH AUTHOR Radovan Garab\('ik