11

I have a short script which is executed by a system daemon for particular events. I know the event is occurring and the script is executing, but it does not do what I intend. Strangely, it does when I run it manually, so I am very confused.

How can I figure out what is going on? The script is basically a series of commands like this:

/bin/foo on 3
sudo bar a
goldilocks
  • 58,859
  • 17
  • 112
  • 227
  • I understand this is intended to be a tutorial on debugging system scripts, but is a bit too U&L. I misread the title and would suggest "Logging the output of a system script for debugging" would make the purpose clearer. Also my brain freezes over when I read these foo bar examples, I prefer something which looks more real world. I am reluctant to edit any post, so will leave this to you if you think it can be improved. – Milliways Feb 03 '16 at 22:44
  • 1
    @Milliways You're right, "system script" was kind of a misnomer anyway so I've changed the title. I disagree about the foobar thing -- people need to learn to recognize common lingo/colloquialisms just as they might in any culture. Also it is still a good giggle once you put the two together. – goldilocks Feb 26 '16 at 11:28

2 Answers2

9

First, if the script is run by a system daemon and that daemon is running with root privileges, you do not need to use sudo. This includes init (and systemd), which includes rc.local. If that daemon is not running with root privileges, then sudo will not work unless /etc/sudoers is configured to allow such (and without a password). Raspbian users may be confused by this since the pi user is allowed to do anything by default (and if you look in /etc/sudoers you will see how that is accomplished).

Next, you can capture output from any bash script, or any set of commands inside a bash script, by executing them in a subshell like this:1

(
    /bin/foo on 3
    sudo bar a
) &> /var/log/myTestLog.txt

The () indicates a subshell. All output from anything inside this is being redirected to a /var/log/myTestLog.txt file. A few notes:

  • &> is a bashism, so if the script is executed via a shebang on the first line, it should be #!/bin/bash, not just /bin/sh. "Bashisms" work only in the bash shell.

    This includes /etc/rc.local, which by default uses /bin/sh (ie., yes, you can safely change that to /bin/bash).

  • /var/log requires root privileges to write to. If the process does not have such, use or create a directory you know it can. When in doubt, if you can test this without having to shutdown or reboot the system, use /tmp, which is world writable (i.e., by anyone). However /tmp does not persist across boots. It is also a small, RAM based partition, so do not write gigs of data to it. It is not your SD card [actually on current versions of Raspbian it is, but don't count on this in practice].

  • &> will overwrite anything in myTestLog.txt. If instead you want to append to a existing log, which might be a good idea for debugging purposes, use &>>. You could then add a command to the beginning of that subshell like this:

    echo Starting $(date)
    

    To separate the information from each run. If you're not sure what this does, try on the command line.

This last point is a good illustration of something you can do with regard to commands that don't output anything -- but most of them do if you include, e.g., -v for "verbose". Beware for some commands -v means "print version information". Have a look in the man page for the command to be sure if and how this will work (some commands also use a different switch than -v).

By convention, commands also return a value of 0 when complete. This is sometimes called the "exit status" and you do not normally see it, but the shell will show you with echo $?. Try

 ls /
 echo $?
 ls /nonexistantdir
 echo $?

You'll get 0 and 2. If you then look in the man page for ls under "Exit status", you'll see the rather unspecific, cryptic:

2      if serious trouble (e.g., cannot access command-line argument).

Which may or may not be better than nothing, but there you go.

At the very least, this indicates the command failed for some reason. The exit status also allows you to do things like this:

/bin/foo && sudo bar

The && in this case means "if the first command succeeds", presuming the first command uses the convention of returning 0 (which is why they usually do). If /bin/foo does not work, cannot be found, etc., then sudo bar will never happen.

Using a combination of logging messages and conditional execution (&&) should get you much closer to figuring out the problem, or at least getting information that might be useful to others in helping you solve the problem. Without that, the most anyone else can often do is guess.


1. You can accomplish the same output redirection for an entire script from the inside by using:

exec &> /var/log/myTestLog.txt

At the top (or anywhere, and it will apply to everything subsequent).

goldilocks
  • 58,859
  • 17
  • 112
  • 227
  • 1
    The answer does not clearly state what &> does. It redirects both stdout and stderr. See here for full explanation: https://stackoverflow.com/questions/11255447/what-does-mean – Daniel K. Aug 12 '20 at 09:26
3

One important aspect people tend to forget when running scripts as daemons is shell environment, and $PATH variable in particular. In your example, the second line relies on $PATH: the full name of sudo is /usr/bin/sudo, and your user shell only knows that because it was told to search /usr/bin when looking for executables. The same is true for bar.

Considering that sudo is not needed when running scripts as daemons, your second line should look like:

/path/to/bar a
Dmitry Grigoryev
  • 27,928
  • 6
  • 53
  • 144