Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

unix: no permission on serial port handling #411

Open
rusefillc opened this issue Feb 21, 2022 · 6 comments
Open

unix: no permission on serial port handling #411

rusefillc opened this issue Feb 21, 2022 · 6 comments
Assignees
Milestone

Comments

@rusefillc
Copy link
Contributor

is it possible/should it be possible for application to know that it has no permission for serial port in *nix?

under root my stuff works

sudo java -jar console/rusefi_console.jar reboot_ecu
[sudo] password for debian:
I 220221 104531.089 [main] Launcher - rusEFI UI console 20220213
I 220221 104531.114 [main] Launcher - Compiled Mon Feb 14 12:49:06 EST 2022
I 220221 104531.114 [main] Launcher -


I 220221 104531.156 [main] PortDetector - Trying [ttyACM0, ttyS0]
I 220221 104531.162 [AutoDetectPort1] SerialIoStream - Using com.fazecast.jSerialComm 2.5.3
I 220221 104531.162 [AutoDetectPort1] BufferedSerialIoStream - [BufferedSerialIoStream] openPort ttyACM0
I 220221 104531.162 [AutoDetectPort2] BufferedSerialIoStream - [BufferedSerialIoStream] openPort ttyS0
I 220221 104531.176 [AutoDetectPort1] SerialAutoChecker - Got signature=rusEFI 2022.02.20.mre_f4.1768592053 from ttyACM0
I 220221 104531.176 [AutoDetectPort1] SerialIoStream - ttyACM0: Closing port...
I 220221 104532.186 [AutoDetectPort1] SerialIoStream - ttyACM0: Closed port.
I 220221 104532.187 [AutoDetectPort1] SerialAutoChecker - Propagating AutoDetectResult{serialPort='ttyACM0', signature='rusEFI 2022.02.20.mre_f4.1768592053'}

without root I have access control issue. I still get port in the list of ports available but all I get is a 'write' method failing to write.

Is there an API to detect that specific error? Should there be an API to detect that specific error?

@hedgecrw
Copy link
Contributor

For the most part, I don't know that this is necessary. The openPort() call will fail in these circumstances (if you are making it to a 'write' call, then your application is not examining the result of the 'open' call), and for the most part, it's really the only reason that the openPort() call will fail on an enumerated port. If a user really wants to examine the exact error code returned by the OS, they can call getLastErrorCode() after a failed openPort() call to see the underlying reason for the failure. (If you are using Linux, then a return code of EACCES (13) signifies a permissions error.)

Additionally, the README, Installation Wiki, and Troubleshooting Wiki all clearly identify permissions as an annoying and common issue for serial ports on Posix-based systems, with suggestions on how to overcome the issue. Finally, in the 2.9.X series of releases, there was a new method added called allowElevatedPermissionsRequest() which you can call on your serial port object before trying to open it, and it will automatically try to do all the necessary steps to ensure that a user is able to open a serial port, if they can't already. I would recommend adding this method call to your port opening routine, especially if you are guaranteed to be running your application from a command line (since it may prompt for a sudo-password the very first time you open a port if the library is unable to open it).

@theHilikus
Copy link

theHilikus commented Apr 1, 2022

@hedgecrw you mention a return code of 13 signifies permissions error. Is this part of the contract of the API or this is platform-specific? Any suggestions how to show a meaningful error message to users that is platform-independent when openPort() fails? if no, at least do you have a table explaining what each code means?

@hedgecrw
Copy link
Contributor

@theHilikus, the 13 return code error is unfortunately platform-specific (Linux-specific actually). It could be something else entirely on Windows, Android, Mac, FreeBSD, etc. That's actually the main reason I added the functionality to return the actual OS-specified error codes in recent releases, so that users could make use of them more intelligently. I can't give you a full listing of codes because they are different (and extensive) for every supported OS (and potentially different distros of Linux). In a future release, I'll try to add some sort of functionality to give a least a rudimentary idea of why openPort() may have failed, but it will probably not be until the next major release v3.0.0 (which will allow people to use this library on Android without rooting their phones).

@hedgecrw hedgecrw self-assigned this Apr 10, 2022
@hedgecrw hedgecrw added this to the v3.0.0 milestone Apr 10, 2022
@theHilikus
Copy link

excellent. thank you for the info and for the library!

@pavly-gerges
Copy link

pavly-gerges commented Sep 18, 2022

I'll try to add some sort of functionality to give a least a rudimentary idea of why openPort() may have failed, but it will probably not be until the next major release v3.0.0 (which will allow people to use this library on Android without rooting their phones).

On my Serial4j API (working currently only with POSIX), i used an ErrnoToException.java utility to convert native errors to java exceptions, users can handle these exceptions and fire listeners when they are caught, but the downside you will need to build multiple Error codes lists for different Operating systems.

Here is a snippet of the code:

#include<TerminalDevice.h>

int TerminalDevice::openPort(const char* port, int flag) {
    return open(port, flag);
}

int TerminalDevice::fetchSerialPorts(struct DynamicBuffer* serialPorts) {

    DIR* dirp = opendir(DEVICES_DIR);
    
    /* sanity check the input */
    if (dirp == NULL) {
        return ERR_INVALID_DIR;
    }

    struct dirent* dp = (struct dirent*) calloc(1, sizeof(struct dirent));

    /* start at the beginning of the buffer to override last data */
    serialPorts->resetDataPointer();

    /* start reading available ports */
    while ((dp = readdir(dirp)) != NULL) {
        
        char* device = (char*) calloc(1, sizeof(char));
        device = SerialUtils::concatIntoDevice(device, dp->d_name, DEVICES_DIR);
        
        /* delete the device buffer if it's not a serial port */
        if (!SerialUtils::isSerialPort(device, DEFAULT_FLAGS)) {
            BufferUtils::deleteBuffer(device);
            continue;
        }

        /* add the device to the serial ports major buffer and count up */
        serialPorts->add(device);
    } 

    /* release resources */
    closedir(dirp);
    BufferUtils::deleteBuffer(dp);

    /* throws error indicating the availability issues */
    if (serialPorts->getItem(0) == NULL) {
        return ERR_NO_AVAILABLE_TTY_DEVICES;
    }
    return OPERATION_SUCCEEDED;
}
...

On java:

public final void openPort(final SerialPort serialPort) throws NoSuchDeviceException,
                                                               PermissionDeniedException,
                                                               BrokenPipeException,
                                                               InvalidPortException,
                                                               OperationFailedException,
                                                               NoAvailableTtyDevicesException {                                                           
    if (isSerial4jLoggingEnabled()) {
        LOGGER.log(Level.INFO, "Opening serial device " + serialPort.getPath());
    }
    this.nativeTerminalDevice.setSerialPort(serialPort);
    final int errno = nativeTerminalDevice.openPort0(serialPort.getPath(), permissions.getValue());
    ErrnoToException.throwFromErrno(errno, serialPort.getPath());
    /* update port data natively */
    /* ... */
}

public void initTermios() throws NoSuchDeviceException,
                                 PermissionDeniedException,
                                 BrokenPipeException,
                                 InvalidPortException,
                                 OperationFailedException,
                                 FileNotFoundException,
                                 NoAvailableTtyDevicesException {
    if (isSerial4jLoggingEnabled()) {
        LOGGER.log(Level.INFO, "Initializing serial device " + getSerialPort().getPath());
    }
    final int errno = nativeTerminalDevice.initTermios0();
    ErrnoToException.throwFromErrno(errno, getSerialPort().getPath());

    /* get the java streams from the port after initializing it with the native terminal */
    inputStream = new FileInputStream(getSerialPort().getPath());
    outputStream = new FileOutputStream(getSerialPort().getPath());
}
...

A more verbose process specific to the OP request is to initially try to write a tmp file to the /dev and throws PermissionDeniedException if the errno is -1 for example....

@smtpl-iiotian
Copy link

Hello @hedgecrw,
I got errorCode - 5, errorlocation - 602 in log while opening serial port on Ubuntu 20.04 with jSerialComm v2.9.2 and jdk v11.0.17
Can you please help to know what is the cause of error while opening port?
User privileges are updated as per your documentation.
In putty terminal, the port opens correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants