Hi
I’m writing a cpp application that works with serial port and read some files and do some processing on data.
this means the cpp application most have sudo access to run properly.
I also want this program to run without any special tasks when Ubuntu is fully booted.
I tried many ways to do that but nothing changed
I was putting .desktop file in ~/.config/autostart/ directory → Nothing happened
I had created .service file → Nothing happened
what else can I do ?
Is there a better, more principled, and more efficient way to do this?
Are you in need of starting this without any user login? Does this run as root (sudo
)? The TX1 is the oldest of the 64-bit systems, and I think it might still accept starting a script in “/etc/rc.local
”, although you might need to enable that. See:
https://forums.developer.nvidia.com/t/starting-a-program-on-the-jetson-nano-without-a-pc-connection/194861/13
The more modern method would be to create a systemd
service which can run (A) after required services are available, and (B) at some runlevel. rc.local
is simple, and itself is just a utility access to systemd
once most everything is up and running, but before users log in. If you have anything special in requirements, then creating your own systemd
service would be the norm, but more involved than just starting it in rc.local
.
If you want to see what services are already present (systemctl
is the command line interface to query and control systemd
):
systemctl list-units
Hi @linuxdev thank you for your response
Yeah my program needs to access the serial port and read some files from disk, so that need sudo permission.
I don’t know it can run without problem before login or not…
did you say I have to create a systemd service that runs a cpp program ? how can I do that ?
Anything running in init
will run as root. /etc/rc.local
is included in this. It is a bash script.
What do you see from:
systemctl status rc-local
If this is not enabled, but it exists, then enable it like this:
sudo systemctl enable rc-local
Note: If the file “/etc/rc.local
” does not already exist, then you might need to create it. Here is a default template for that file:
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
exit 0
This file should be set to group and owner root:
sudo chown root.root /etc/rc.local
Then permissions to:
sudo chmod 0755 rc.local
If you’ve ever programmed any simple bash
script, then this is the same. Lines occurring after the comments and before the exit
will run as root as the last stage of boot without anyone being logged in.
If you have a dependency on someone being logged in, then the answer to this changes. Note that you could run code from this script file as another user by using sudo
to become some non-root user since root does not need a password to use sudo
(kind of the opposite of what you are doing).
Also, if this is a GUI program you run, then someone has to be logged in to run the GUI. There are ways to create a read-only display which is graphical, but that gets a lot more involved.
the result of command systemctl status rc-local
:
● rc-local.service - /etc/rc.local Compatibility
Loaded: loaded (/lib/systemd/system/rc-local.service; static; vendor preset: enabled)
Drop-In: /lib/systemd/system/rc-local.service.d
└─debian.conf
Active: inactive (dead)
Docs: man:systemd-rc-local-generator(8)
and the result of command sudo systemctl enable rc-local
:
The unit files have no installation config (WantedBy, RequiredBy, Also, Alias
settings in the [Install] section, and DefaultInstance for template units).
This means they are not meant to be enabled using systemctl.
Possible reasons for having this kind of units are:
1) A unit may be statically enabled by being symlinked from another unit's
.wants/ or .requires/ directory.
2) A unit's purpose may be to act as a helper for some other unit which has
a requirement dependency on it.
3) A unit may be started when needed via activation (socket, path, timer,
D-Bus, udev, scripted systemctl call, ...).
4) In case of template units, the unit is meant to be enabled with some
instance name specified.
I created a /etc/rc.local
file and paste default template that you sent for me, now all I need to do is write my bash script to run a C++ application with sudo ? somthing like this ? :
sudo gnome-terminal ./project_dir/CPP_Program
I enabled the auto login for current user (root/admin user) to login automatically when ubuntu booted up.
my program has a opencv imshow to show some pictures in gui.
is that any problem with my bash script code in rc.local ?
@linuxdev another thing
how can I ensure that my program runs successfully.
how can I see my program output in terminal ?
@linuxdev I have writed a service in /etc/systemd/system/cppapplication.service
:
[Unit]
Description=My CPP Application
After=network.target
[Service]
WorkingDirectory=/home/ubuntu/Documents/Projects/Testing/Testing/build
ExecStart=/home/ubuntu/Documents/Projects/Testing/Testing/build/cpp_app
Environment="DISPLAY=:0"
Environment="XDG_RUNTIME_DIR=/run/user/1000"
Environment="XAUTHORITY=/home/ubuntu/.Xauthority"
Restart=always
User=root
Group=root
[Install]
WantedBy=multi-user.target
everything is ok and serial port in open, but when the program go to imshow the frames of video it throw an exception in service and service get restarted
the error is this:
I modify your steps in my srevice and again I get this error:
Jan 29 17:55:14 ubuntu app[5862]: QStandardPaths: wrong ownership on runtime directory /run/user/1000, 1000 instead of 0
Jan 29 17:55:14 ubuntu cpp_app[5862]: No protocol specified
Jan 29 17:55:14 ubuntu cpp_app[5862]: qt.qpa.screen: QXcbConnection: Could not connect to display :0
Jan 29 17:55:14 ubuntu cpp_app.[5862]: Could not connect to any X display.
I connected the hdmi port in display.
seems like the program or service could not find a display
I trying to find the display with xrandr --listmonitors
command:
Monitors: 1
0: +XWAYLAND0 1920/508x1080/286+0+0 XWAYLAND0
this will I get.
what can I do ?
Which L4T release is this? See “head -n 1 /etc/nv_tegra_release
”. I am assuming it is an R32.x release.
What permissions are on the “/etc/rc.local
”? Run “ls -l /etc/rc.local
”. This should be readable by anyone, writable by root, and executable by root. Also, did you create the file with the example I gave in the earlier post? You’d want to test it without any special edits before you add something. The permissions from the ls -l
should start like this:
# ls -l /etc/rc.local -rwxr-xr-x 1 root root ....
If permissions are wrong, then probably systemctl
would reject this, but I’m not certain if that is the cause of the error. If there is an error within the rc.local
as it executes, then rc.local
will exit and skip whatever was in it. If there is an error in permissions, then systemctl
will not enable the unit. It is possible you might need to install another package, but it would be better if that is not the case.
Also, do you see this file as existing?
/etc/systemd/system/rc-local.service
The rc.local
runs before anyone logs in. init
runs as root, but is not a login. Since you are running a GUI, this implies your GUI must be up and running before you run your program. Unless you want rc.local
itself to start a GUI session prior to your program, then the correct way to do this would instead be as a login script and not an rc.local
script.
Is there a reason this has to run root autologin? Does your program require this? I think you could add the launch command to the end of the “~/.bashrc
” file to run, but it will run for every login. If you log in twice with that account, then it will run twice (you could add something like a lock file to prevent multiple instances).
Something to know about GUI programs is that they must be associated with a running GUI terminal. That association is known as the context. Each GUI has a context, and security associated with a context. If you are in a GUI, and have a terminal open, what do you see from “echo $DISPLAY
”? This is the syntax of the context. Typically it will be something like “:0
” or “:0.0
”. If there is a second display, or if the first closed and opened again, it might increment to something like “:1
”. VMs tend to start at “:10
”.
An important point is that value is part of the environment. An environment variable named “DISPLAY
” has been created. Anything you run from within the GUI has that automatically set and present. Those apps inherit the DISPLAY
/context. None of this of course is associated with systemd
/systemctl
. It is certainly possible that rc.local
could start a GUI for a particular user, which would create its own context. There can actually be more than one user logged in and have the GUI displayed in different places; each GUI would have its own unique DISPLAY
.
There is also a “startup application” setting you can use to auto start an app from within the GUI for the user that is logged in.
@linuxdev yeah you’re right my tegra release is:
# R32 (release), REVISION: 7.4, GCID: 33514132, BOARD: t210ref, EABI: aarch64, DATE: Fri Jun 9 04:25:08 UTC 2023
but when I trying to enable the rc-local service It gives me this error:
The unit files have no installation config (WantedBy, RequiredBy, Also, Alias
settings in the [Install] section, and DefaultInstance for template units).
This means they are not meant to be enabled using systemctl.
Possible reasons for having this kind of units are:
1) A unit may be statically enabled by being symlinked from another unit's
.wants/ or .requires/ directory.
2) A unit's purpose may be to act as a helper for some other unit which has
a requirement dependency on it.
3) A unit may be started when needed via activation (socket, path, timer,
D-Bus, udev, scripted systemctl call, ...).
4) In case of template units, the unit is meant to be enabled with some
instance name specified.
when I already created the /etc/rc.local
file with this content:
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.
exit 0
@linuxdev when I enter command echo $DISPLAY
I see :0
@linuxdev one thing I realized was is thins my program shows a video frames on screen and when I enable the rc-local service on jetson, my program was running successfully does not see any gui for showing video frames.
this means my program runs until the program give an exception:
Jan 30 17:13:12 ubuntu rc.local[4203]: qt.qpa.screen: QXcbConnection: Could not connect to display
Jan 30 17:13:12 ubuntu rc.local[4203]: Could not connect to any X display.
Jan 30 17:13:13 ubuntu rc.local[4203]: Waiting for Track Order...
and then board Goes to the graphical environment.
now the main problem is that exception. the DISPLAY…
If you need a GUI, then rc.local
is the wrong way to go unless you are also launching a GUI instance for your program to run in. If you have a GUI instance, and then you export
the correct DISPLAY
before launching your program, it would work, but this is normally the wrong way to go. There are many things about a GUI program people often do not know about, so there are things one has to consider regarding the program itself before answering.
Describe what the program does. Does the program run as root, or require root access? Is this program interactive, or does it only display things? Is this intended to be the only program displayed, like in a kiosk, or is this program part of a full desktop environment?
While you answer that you should try this experiment, and observe. Note that when a Linux desktop system boots, as it reaches “multi-user.target
” (text-only mode, multiple users, networking enabled), init
will fork off several text consoles for login on the local computer. One normally picks between these with hot keys, e.g., “ALT-F1
” through “ALT-F6
”; the actual number varies depending on which Linux distribution, but I think will always be up to “ALT-F6
” on Ubuntu. Then, when “graphical.target
” is reached, one of those instances is replaced with the login manager for the GUI; this tends to mean “ALT-F1
” is no longer text-mode, but is instead a GUI. From a GUI the key bindings differ. To get from the GUI to the first console (which is now “ALT-F2
”), when in the GUI, you would also use the CTRL
key: “CTRL-ALT-F2
” through “CTRL-ALT-F6
”. From a console, to go back to the GUI, you would use “ALT-F1
” (the displays are 1-6, where 1 is GUI, but the keybindings change; each GUI has a $DISPLAY
, but this won’t be determined by the key binding).
Here is the experiment: While in the GUI, make sure your DISPLAY
is still “:0
” via “echo $DISPLAY
” in one of the terminals of the GUI. Now go to any text console, e.g., use the key binding “CTRL-ALT-F3
”. Log in to that. Now bind to the GUI for GUI programs via “export DISPLAY=:0
”. Run your GUI program. The program will be able to find a GUI. It might end up automatically switching to the GUI, but if not, then use “ALT-F1
” to go to the GUI. Your program will be running. An exception is if the logged in user of the GUI and console differ, in which case security will block this.
When the system actually reaches graphical.target
, which is what init
does (this is the name the kernel executes as its very first process, and in Ubuntu this is a symbolic link to systemd
), that first console is actually replaced and not merely run from a console. There is no more console on the graphical login manager terminal. You can, however, run the X
GUI from a different console, in which case X
is just another program and does not replace the underlying parent console. In this latter case, exiting the GUI would revert back to the text console.
rc.local
is basically a non-login text console. Things can get a bit more complicated, but rc.local
is capable of also running a GUI instance, and naming your program as an argument. The login manager can be skipped. Most of what you see in the GUI though is not the X server which hosts the GUI. Instead, what you are seeing is the window manager. X can run only a single child process; you see a full interactive desktop because it is set to run a window manager. When you manually launch X, then you can name either a single program for X to host, or you can tell it to launch a window manager. For a kiosk, you might launch a single program.
Furthermore, one can set up systemd
(init
) to launch the second terminal binding (the first text console if a GUI is running; remember the F1
through F6
, we can pick text console at F2
) to become your special GUI instead of being a text console. Thus, it is possible to modify the system such that from the GUI CTRL-ALT-F2
goes to your dedicated GUI; from a text console one would go back to that special GUI with ALT-F2
. The original F1
GUI would still be there. This is a lot of work to do right.
One can also have a particular user simply auto run a program when logging in to a GUI. Thus, a thorough description of what you actually want to do matters a lot. About all we know now is that your program is a GUI program. We also don’t know if it is just for your use, or if it is going to be some dedicated appliance.
@linuxdev about you first question:
my project is a program that captures the camera frames and write bunch of text’s that received from serial port, it also need to read some files from disk. so this means my program requires root access (this means when I want to run program in terminal manually, it requires sudo
command to run correctly.
What is the reason the root access is needed? Is it because of the files being read? If root is from the serial port, then this is easily changed by adding your user to the correct group (a supplementary group). Which serial port is used (e.g., /dev/ttyS1
as an example)? If the serial port being used is the example /dev/ttyS1
, then what do you see from:
ls -l /dev/ttyS1
@linuxdev
I have a user named ubuntu
.
yes this is because I want to use serial port. the serial port that I used is /dev/ttyTHS2.
@linuxdev when I trying to open serial port without sudo
it is not receiving correct data, seems like the serial port cannot open successfully. but when I user sudo
on terminal it runs completely OK.
I don’t know why…
Let’s look at this one step at a time. If you run the command “ls -l /dev/ttyTHS2
”, is the “owner/group” part of this “root/dialout”? If so, then all you have to do is add user ubuntu
to supplemental group dialout
, and from then on it’ll work without sudo
:
sudo usermod -aG dialout ubuntu
If you want to see a user’s current group(s), use the command “groups
”.
If the “owner/group” of the “/dev/ttyTHS2
” has a group of something else, e.g., “tty
”, then the issue is not what you think it is. No serial UART will have a group of tty
unless it is running as a console. Trying to use a port simultaneously as both a serial console and for general purpose means two programs are fighting to send data through the same port. The only possibility of using such a port is to first remove it from the console ownership, reverting the tty
group back to dialout
. However, removing serial console access is normally a "bad idea"™. An exception might be if you are sending the device out as a final production and you need to secure serial console access. You would never ever want to add your user to group tty
for such access.