Views:


Summarized by AI

🧾 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

 

1) 
for openSUSE, lock may already be the owner of /run/lock
2) , 3) 
By convention, rule files will typically have a numerical prefix since each file is processed in lexical order. By having no numerical prefix, our file will be applied last and not be overwritten. So in this case, the name of the file does not matter. However, if there is a strong desire to adhere to the convention, the file could be named something similar to 99-datalogic-device.rules
Add a comment