🧾 Description
This page describes the steps to allow a non-root user to access USB devices on different Linux distros. Without these steps, users would have to run JavaPOSTest with root permissions as well as their own POS application. Some of these changes configure system wide settings and permissions and the intended affect may not be desirable for every situation.
🛠️ Resolution Steps
Fedora/CentOS/openSUSE
Tested on the following releases: Fedora 19, CentOS 7.6-1810, openSUSE-Leap-15.0
Step 1: Adding user groups
The user will need to be granted group permissions to access a couple locations. For this example, we will use two different standard groups (lock and dialout) to section the permissions. A single standard group or custom created group(s) could also be used. To add the user to the groups, run the following command:
sudo usermod -a -G lock,dialout <user>
Step 2: Granting /run/lock access
We need to grant access to the /run/lock directory to allowing reading/writing of lock files1). The directory is created on every boot, so we need to change the configuration of the directory for the permissions to persist. Open the /usr/lib/tmpfiles.d/legacy.conf file for editing. look for the following line:
d /run/lock 0755 root root -
Change the group owner and the group permissions so that the line now reads:
d /run/lock 0775 root lock -
Save the file (changes will be applied on reboot in Step 4)
Step 3: Adding udev rules
We will need to create a udev rule to give us write access to the device every time it is plugged into the system. The rule will differ depending on the interface, but we will place them all within the same file: custom.rules 2).
USB-OEM
JavaPOS requires access to all the USB hubs in order to scan the system and detect the device that matches the VID/PID we are searching for. This requires us to change the group owner and permissions for all USB devices on the system. Open the /etc/udev/rules.d/custom.rules file for editing and add the following rule:
SUBSYSTEM=="usb", GROUP="dialout", MODE="0664"
USB-COM
If we are using the dialout group as our permission group, then no changes are needed since dialout automatically gets assigned as the owner of ttyACM devices. If a different group is being used, we will need to add the following rule to the /etc/udev/rules.d/custom.rules file:
KERNEL=="ttyACM*", ATTRS{idVendor}=="05f9", GROUP="<group_name>", MODE="0664"
This rule can be targeted to only devices with a Datalogic VID since it does not have to affect the USB hubs for access.
Step 4: Reboot
A reboot is necessary for all changes to be applied to the system. After reboot, access to the devices should be granted for the user and can be verified by running JavaPOSTest without sudo.
Ubuntu
Tested on the following releases: 18.04
Step 1: Adding user groups
The user will need to be granted group permissions to access USB devices. For this example, we will use the standard group dialout to grant the permissions. To add the user to the groups, run the following command:
sudo usermod -a -G dialout <user>
Step 2: Adding udev rules
We will need to create a udev rule to give us write access to the device every time it is plugged into the system. The rule will differ depending on the interface, but we will place them all within the same file: custom.rules 3).
USB-OEM
JavaPOS requires access to all the USB hubs in order to scan the system and detect the device that matches the VID/PID we are searching for. This requires us to change the group owner and permissions for all USB devices on the system. Open the /etc/udev/rules.d/custom.rules file for editing and add the following rule:
SUBSYSTEM=="usb", GROUP="dialout", MODE="0664"
USB-COM
If we are using the dialout group as our permission group, then no changes are needed since dialout automatically gets assigned as the owner of ttyACM devices. If a different group is being used, we will need to add the following rule to the /etc/udev/rules.d/custom.rules file:
KERNEL=="ttyACM*", ATTRS{idVendor}=="05f9", GROUP="<group_name>", MODE="0664"
This rule can be targeted to only devices with a Datalogic VID since it does not have to affect the USB hubs for access.
Step 3: Reboot
A reboot is necessary for all changes to be applied to the system. After reboot, access to the devices should be granted for the user and can be verified by running JavaPOSTest without sudo.
Script
This is a script configured to take care of the permissions updates automatically.
Current OS Support:
Fedora (verified on Fedora 19)
CentOS (verified on CentOS 7.6-1810)
openSUSE (verified on openSUSE-Leap-15.0)
Ubuntu (verified on Ubuntu 18.04)
- update_permissions.sh
#!/bin/bash #adjust these variables to change the permissions USER=user LOCK_GROUP=lock USB_GROUP=dialout RULE_FILE=/etc/udev/rules.d/custom.rules LEGACY_FILE=/usr/lib/tmpfiles.d/legacy.conf #formatting variables, do not change RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' YELLOW='\033[1;33m' PAD1=$(printf '%0.1s' "."{1..45}) PAD2=$(printf '%0.1s' "#"{1..50}) ########################################################### # Formats and prints results based off of return codes # # Globals: # PAD1, GREEN, RED, NC # Arguments: # return code, final indicator string # Returns: # None ########################################################### result() { if [ "$3" = "final" ]; then GOOD="SUCCESS" BAD="FAILURE" else GOOD="OK" BAD="FAIL" fi if [ $2 -eq 0 ]; then RESULT="${GREEN}[$GOOD]${NC}" else RESULT="${RED}[$BAD]${NC}" fi printf "%s %s $RESULT\n" "$1" "${PAD1:${#1}}" } ########################################################### # Formats a status messages during script execution # # Globals: # PAD2 # Arguments: # return code, final indicator string # Returns: # None ########################################################### status() { echo "" echo $PAD2 echo "### $1" echo $PAD2 } ########################################################### # Add user to groups # # Globals: # RET_AUG, CHECK_AUG, USER # Arguments: # ...groups # Returns: # None ########################################################### RET_AUG=0 CHECK_AUG=1 add_user_groups() { CHECK_AUG=0 status "Updating user groups" for g in $*; do echo "Adding $USER to $g group..." usermod -a -G $g $USER RET_AUG=$((RET_AUG+$?)) done echo "Updated User Groups:" id $USER } ########################################################### # Updates group permission access to /run/lock # # Globals: # RET_GLP, CHECK_GLP, LOCK_GROUP, LEGACY_FILE # Arguments: # None # Returns: # None ########################################################### RET_GLP=0 CHECK_GLP=1 grant_lock_permissions() { RET_GLP=1 CHECK_GLP=0 status "Granting /run/lock permissions" SEARCH_STR="d /run/lock 0755 root root -" grep -q "$SEARCH_STR" $LEGACY_FILE if [ $? -eq 0 ]; then sed -i "s+$SEARCH_STR+d /run/lock 0775 root $LOCK_GROUP -+g" $LEGACY_FILE RET_GLP=$? else echo "$LEGACY_FILE does not exist, or search string not found" fi } ########################################################### # Adds udev rules # # Globals: # RET_AUR_OEM, RET_AUR_COM, CHECK_AUR_OEM, # CHECK_AUR_COM, RULE_FILE, USB_GROUP # Arguments: # None # Returns: # None ########################################################### RET_AUR_OEM=0 RET_AUR_COM=0 CHECK_AUR_OEM=1 CHECK_AUR_COM=1 add_udev_rules() { CHECK_AUR_OEM=0 status "Adding udev rules" echo "Adding USB-OEM rule" printf "SUBSYSTEM==\"usb\", GROUP=\"$USB_GROUP\", MODE=\"0664\"\n" > $RULE_FILE RET_AUR_OEM=$? BOOL1=1 RET3=0 if [ $USB_GROUP != "dialout" ]; then CHECK_AUR_COM=0 echo "Adding USB-COM rule" printf "KERNEL==\"ttyACM*\", ATTRS{idVendor}==\"05f9\", GROUP=\"$USB_GROUP\", MODE=\"0664\"\n" >> $RULE_FILE RET_AUR_COM=$? fi } ########################################################### ################# START OF SCRIPT ################# ########################################################### #check for root permissions if [ "$(id -u)" != "0" ]; then printf "${RED}[ERROR] ${NC} This script must be run with root privileges\n" 1>&2 exit 1 fi #try to detect current operating system and perform relevant updates status "Checking operating system" OS=`uname -s` if [ "${OS}" = "Linux" ] ; then if [ -f /etc/fedora-release ]; then printf "${YELLOW}Fedora distribution detected${NC}\n" add_user_groups $LOCK_GROUP $USB_GROUP grant_lock_permissions add_udev_rules elif [ -f /etc/SuSE-release ] || [[ "$(awk -F= '/ID/ {print $2}' /etc/os-release)" = *suse* ]]; then printf "${YELLOW}SuSE distribution detected${NC}\n" add_user_groups $LOCK_GROUP $USB_GROUP grep -q "d /run/lock 0775 root $LOCK_GROUP -" $LEGACY_FILE if [ $? -eq 0 ]; then status "Granting /run/lock permissions" echo "Lock permissions already granted" else grant_lock_permissions fi add_udev_rules elif [ -f /etc/centos-release ]; then printf "${YELLOW}CentOS distribution detected${NC}\n" add_user_groups $LOCK_GROUP $USB_GROUP grant_lock_permissions add_udev_rules elif [ -f /etc/redhat-release ]; then printf "${YELLOW}Redhat distribution detected${NC}\n" printf "${RED}[ERROR] ${NC} Implementation for this distribution not been developed yet\n" 1>&2 exit 1 elif [ -f /etc/debian_version ]; then if [ "$(awk -F= '/DISTRIB_ID/ {print $2}' /etc/lsb-release)" = "Ubuntu" ]; then printf "${YELLOW}Ubuntu distribution detected${NC}\n" add_user_groups $USB_GROUP add_udev_rules else printf "${YELLOW}Debian distribution detected${NC}\n" printf "${RED}[ERROR] ${NC} Implementation for this distribution not been developed yet\n" 1>&2 exit 1 fi else printf "${RED}[ERROR] ${NC} Unrecognized Linux Distribution detected\n" 1>&2 exit 1 fi else printf "${RED}[ERROR] ${NC} Non-Linux OS detected\n" 1>&2 exit 1 fi FINAL=$((RET_AUG+RET_GLP+RET_AUR_OEM+RET_AUR_COM)) #print results echo "" echo "" [ $CHECK_AUG -eq 0 ] && result "Update User Groups" $RET_AUG [ $CHECK_GLP -eq 0 ] && result "Grant /run/lock Permissions" $RET_GLP [ $CHECK_AUR_OEM -eq 0 ] && result "Add USB-OEM udev Rule" $RET_AUR_OEM [ $CHECK_AUR_COM -eq 0 ] && result "Add USB-COM udev Rule" $RET_AUR_COM echo "" result "Permissions Update Finished" $FINAL "final" echo "" #request reboot if update succeeded if [ $FINAL -eq 0 ]; then printf "${YELLOW}IMPORTANT!! Reboot is required for changes to take effect${NC}\n" read -p 'Would you like to reboot now (y/n)? ' ANS if [[ $ANS = y* ]] || [[ $ANS = Y* ]]; then reboot else echo "" fi fi
99-datalogic-device.rules
