Take the 2-minute tour ×
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It's 100% free, no registration required.

I've been reading for the last couple hours about Upstart, .xinitrc, .xsessions, rc.local, /etc/init.d/, /etc/xdg/autostart, @reboot in crontab and so many other things that I'm totally confused!

Here is my bash script. It should start/run after the desktop environment is started and it should continue to run at all times until logout/shutdown. It should start again on reboot. Any time the DE is running, it should run.

#!/bin/bash
while true; do
    if [[ -s ~/.updateNotification.txt ]]; then
                read MSG < ~/.updateNotification.txt 
                kdialog --title 'The software has been updated' --msgbox "$MSG"
                cat /dev/null > ~/.updateNotification.txt
    fi
    sleep 3600
done
exit 0

I know zero about using Upstart, but I understand that Upstart is one way to handle this. I'll consider other approaches but most of the things I've been reading about are too complex for me. Furthermore, I can't figure out which approach will meet my requirements (which I'll detail below).

There are two steps in my question:

  1. How to automatically start the script above, as described above.

  2. How to "install" that Upstart task via a bash script (i.e., my "installer").

I assume (or hope) that step 2 is almost trivial once I understand step 1.

I have to support all flavors of Ubuntu desktops. Therefore, the kdialog call above will be replaced. I'm considering easybashgui for this. (Or I could use zenity on gnome DE's.)

My requirements are:

  1. The setup process (installation) must be done via a bash script. I cannot use the GUI method described in the Ubuntu doc AddingProgramToSessionStartup, for example. I must be able to script/automate the setup (installing) process using bash. Currently, it is as simple as having the bash installer script copy the above script into /home/$USER/.kde/Autostart/

  2. The setup process must be universal across Ubuntu derivatives including Unity and KDE and gnome desktops. The same setup script (installer) should run on Linux Mint, Kubuntu, Xbuntu (basically any flavor of Ubuntu and major derivatives such as Linux Mint). For example, we cannot continue to put a script file in /home/$USER/.kde/Autostart/ because that exists only on KDE.

  3. The above script should work for each of the limited flavors we use. Hence our interest in using easybashgui instead of kdialog or zenity. See below.

  4. The installed monitoring script should only be started after the desktop is started since it will display a GUI message to the user if the update is found.

    1. The monitoring script (above) should run without root privileges, of course. But the installer (bash script) can be run as root.
  5. I'm not a real developer or a sysadmin. This is a part time volunteer thing for me, so it needs to be easy/simple. I can write bash scripts and I can program a little, but I know nothing about Upstart or systemd, for example. And, unfortunately, my job doesn't give me time to become an expert on init systems or much of anything else related to development and sysadmin. So I have to stick with simple solutions.

The easybashgui version of the script might look like this:

#!/bin/bash
source easybashgui
while true; do
    if [[ -s ~/.updateNotification.txt ]]; then
                read MSG < ~/.updateNotification.txt 
                message "$MSG"
                cat /dev/null > ~/.updateNotification.txt
    fi
    sleep 3600
done
exit 0
share|improve this question

2 Answers 2

up vote 2 down vote accepted

Launcher

The tasks in /etc/xdg/autostart are started by the Desktop Environment with the current user's credentials and they can access the GUI, so I think this is the best choice.

File /etc/xdg/autostart/updateNotification.desktop:

[Desktop Entry]
Name=My Update Notification
Exec=updateNotification.sh
Terminal=false
Type=Application
NoDisplay=true

Monitoring script

The monitoring script must be placed in a world accessible location. I recommend /usr/local/bin because messing with files in that directory will not affect the OS. I changed you script a little bit.

File /usr/local/bin/updateNotification.sh:

#!/bin/bash

# Exit if this script is already running.
[[ $(pgrep -c -u $USER -f "^/bin/bash ${0}$") -gt 1 ]] && exit 0

NotifFile=~/.updateNotification.txt

source easybashgui

# Wait some time before starting monitoring the file, so the user doesn't get a popup right after logging in.
sleep 120

while true; do
    if [[ -s $NotifFile ]]; then
        read MSG < $NotifFile
        # Only empty the file if the message is successfully displayed.
        message "$MSG" && echo -n > $NotifFile
    fi
    sleep 3600
done

exit 0

Of course, you'll have to download easybashgui, extract it and install it.

tar xzf easybashgui-8.0.1.tar.gz
cd easybashgui-8.0.1/
sudo make install

Installer

I really like to use bash self-extracting files because the entire installation process can be embeded in a single file. All we have to do is concatenate a pre-built bash header with a tar file.

Before creating the archive make sure all the files are in the place they should be and have the right permissions, in this case:

sudo chown root:root /etc/xdg/autostart/updateNotification.desktop
sudo chown root:root /usr/local/bin/updateNotification.sh
sudo chmod 644 /etc/xdg/autostart/updateNotification.desktop
sudo chmod 755 /usr/local/bin/updateNotification.sh

We can also include easybashgui in the installer. Uncompress easybashgui archive to /tmp:

tar zxf easybashgui-8.0.1.tar.gz -C /tmp

and tar all the required files:

sudo tar zcf MyArchive.tar.gz /etc/xdg/autostart/updateNotification.desktop /usr/local/bin/updateNotification.sh /tmp/easybashgui-8.0.1/

Create the bash header that will uncompress the archive and install easybashgui. File header.sfx:

#!/bin/bash
DATA=`awk '/^__BEGIN_DATA__/ { print NR + 1; exit 0; }' $0`
tail -n+$DATA $0 | tar zx -C /

# Additional installation steps
cd /tmp/easybashgui-8.0.1/
make install

exit 0

# The following line must be the last one. Don't place any character after it.
__BEGIN_DATA__

Finally, join the header and the archive:

cat header.sfx MyArchive.tar.gz > MyInstaller.sh

Now you can copy that installer to another machine, give it execution permission and run sudo ./MyInstaller.sh.


That's it. Hope it helps.

share|improve this answer
    
That's an amazing answer. I learned a lot of useful things from reading this. I especially appreciated learning about bash self-extracting files! I have it all implemented and I'm going to test/debug it now. Thank you!!! –  MountainX Jul 2 '13 at 20:59
    
I hit a minor issue, but describing it doesn't fit into a comment so I posted it in the form of another answer to this question. –  MountainX Jul 2 '13 at 22:18
    
I just wanted to say thanks again. I've been reading about self extract bash scripts. Together with your example, I clearly understand how they work now. I'm still not sure what my present issue is, but that's OK because I have the general concept now. Thx –  MountainX Jul 3 '13 at 2:40

I appear to be running into a minor issue with the excellent answer provided by Eric Carvalho.

I created a .tar.bz file and tested that I can correctly extract it with:

tar xzf MyArchive.tar.bz -C /home/$USERN/Downloads/

I created the bash header.sfx:

#!/bin/bash

USERN=`whoami`
if [ $(id -u) = "0" ] ; then
       USERN=$SUDO_USER
fi

DATA=`awk '/^__BEGIN_DATA__/ { print NR + 1; exit 0; }' $0`
tail -n+$DATA $0 | tar xz -C /home/$USERN/Downloads/
if [ $? -ne 0 ] ; then 
    exit 1
echo
# Additional installation steps follow

#bash commands here
#the last command calls my main installer script:
bash ./another_bash_script.sh

exit 0 #this is line 125
# The following line must be the last one. Don't place any character (including newline characters) after it.
__BEGIN_DATA__

I created the self-extracting shell file/installer:

cat header.sfx MyArchive.tar.bz > MyInstaller.sh

I set the permissions (a+x). The results of running the installer are:

tester@mint15_VirtualBox ~/Downloads $ sudo ./MyInstaller.sh 
./ExampleInstaller.2013.07.01.sh: line 132: syntax error near unexpected token `('
./ExampleInstaller.2013.07.01.sh: line 132: `.����D������V G��c�� ��������Ӭ��ӡ|@��|M�.�/�~��w�O��*��B���l�}Ω�O�.��'

It appears that the file MyArchive.tar.bz is extracted correctly, from a look around inside the extracted folder.

Does that type of error ring a bell?

My first guess was that exit 0 is being ignored, but I am not sure why that would be the case... I noticed that exit 0 is on line 125 and the error is reported as being on line 132, which would be part of the concatenated archive file.

The next thing I tried consists of the following changes to headers.sfx:

first line:

#!/bin/bash -xv

last lines:

exit 0
echo "we should never reach here!"
# The following line must be the last one. Don't place any character (including newline characters) after it.
__BEGIN_DATA__

This time the result look like:

tester@mint15_VirtualBox ~/Downloads $ sudo ./MyInstaller.sh 
#!/bin/bash -xv
USERN=`whoami`
whoami
++whoami
[snip]
exit 0

echo "we should never reach here!"
# The following line must be the last one. Don't place any character (including newline characters) after it.
__BEGIN_DATA__
�#�Q��P\߷&�и����Ƶ��4    и��
                                           ��������Ӭ��ӡ|@��|M�.�/���B���l�}Ω�O�.
./ExampleInstaller.sh: line 134: syntax error near unexpected token `('
./ExampleInstaller.sh: line 134: `.����D���c��

The actual output includes a lot more "���" but the above captures the only readable portion of it.

Solution Notes:

The first mistake I made is that I thought there was not supposed to be a newline char at the end of the last line in the header.sfx file. See below:

# The following line must be the last one. Don't place any character (including newline characters) after it.
__BEGIN_DATA__

It turns out that there should be a newline at the end of the line __BEGIN_DATA__

The second problem I found is that enabling bash debugging (e.g., set -x) prevents the awk/tail/tar sequence of commands from working.

These two changes got it working for me on a test case. (And it was really just one change: correcting my misunderstanding about the final newline chars.)

However, on my real life example I ran into additional problems. It turns out that file permissions on files inside the archive do matter. There was one file inside my archive that had very restricted permissions and this file caused the whole self-extracting bash script to fail at the extraction phase. However, manual extractions in the GUI file manager or command line extractions using tar both succeeded. So self-extracting bash scripts are a little more sensitive to things like file permissions inside the archive (and I'm not talking about permissions on the archive itself).

Also, any "trailing garbage" will cause the self-extracting bash script to fail. Example:

gzip: stdin: decompression OK, trailing garbage ignored

I had this message from one archive and the self-extracting bash script failed to execute any commands following the extraction (even though the extraction seems to have been fine,, as the message suggests). It is possible that something like 2> /dev/null would address this last issue...

share|improve this answer
    
The error comes from line 132 in file ExampleInstaller.2013.07.01.sh, is it the same loaded by "bash ./another_bash_script.sh"? Check its content. –  Eric Carvalho Jul 2 '13 at 22:37
    
Thanks for taking a look at it. I can execute "bash ./another_bash_script.sh" directly and it gives no errors. Furthermore, it's line 132 is a comment line. I put the bash debugging option (-x) in the header file and tested again. I'll update the above with that result. –  MountainX Jul 3 '13 at 1:40
    
I notice that adding a couple lines in the header.sfx file (but not changing the archive) causes the line number in the error message to increase by 2. So the error appears to be coming from the archive concatenated to header.sfx (as if exit 0 is being ignored). –  MountainX Jul 3 '13 at 1:54
    
What kind of commands do you run between "# Additional installation steps follow" and "bash ./another_bash_script.sh"? Try bash ./another_bash_script.sh &. Also you can start with no "Additional steps" and add, let's say, 10 lines at a time until the error starts. –  Eric Carvalho Jul 3 '13 at 2:09
    
I removed all the extra commands. I still get the error. However, this error must be a "special case." The method and concepts you shared are valid and I'll get a lot of use out of them. I will continue to debug the current issue on my own because I think I understand this well enough now to figure out the present problem. Thanks again. –  MountainX Jul 3 '13 at 2:57

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.