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

Multiple issues to fix - patch provided #20

Open
StoicTheVast opened this issue Sep 23, 2014 · 5 comments
Open

Multiple issues to fix - patch provided #20

StoicTheVast opened this issue Sep 23, 2014 · 5 comments

Comments

@StoicTheVast
Copy link

Hi,

Thanks for the project. Unfortunately I had a number of issues getting it to work on my kids' computer (zorin-os 6, based on ubuntu 12.04 LTS).

Summary:

  • AT wasn't working out of the box - it was silently failing. I needed to create /var/spool/cron/atjobs/.SEQ owned by daemon:daemon for at to start working properly
  • Added the file /usr/lib/pm-utils/sleep.d/99zzkidtimer as suggested in another issue. Note this file must be executable! (chmod +x)
  • The use of /usr/bin/users to determine who is logged in is a kludge. It ignores locked screens (screensaver active) and switched user states. I don't have a fix for that right now - instead I just told the kids to log off when they are done rather than let the screensaver kick in, and log off rather than switch users between them. Not ideal. Any ideas about how to elegantly detect these subtle differences in current computer usage state????

In /usr/local/bin/kidtimer, I had to make the following changes:

  • Tweaked the timer countdown to not go negative, even if other things fail...
  • The script files created to do the warnings/logouts in /tmp weren't executable. They need to be chmod +x
  • /usr/local/bin/kidtimer shutdown is not a supported option - instead I have changed it to use go_logout if /tmp/kidtimer.shutdown.$I doesn't exist, as is done elsewhere in the code.
  • The user notification wasn't working because the x display detection using who doesn't work unless the user has an active shell running (my kids don't do that!). So instead I simply iterate through the first 10 displays and send the notification with the desired user to all of them - it fails gracefully on the screens that don't exist or have the wrong username, passes on the ones it needs to.
  • I couldn't decide if, when killing a user's processes they should be sent a TERM first before the KILL. Firefox will nicely reload its tabs if KILL'ed, but maybe other progs like Open Office would prefer to be TERM'ed first so they can clean up properly and only be KILL'ed if they fail to exit themselves.

Here is my patch to /usr/local/bin/kidtimer ...

--- kidtimer.orig       2014-09-23 23:53:41.970147275 +0930
+++ kidtimer.fixed      2014-09-24 00:08:35.104446943 +0930
@@ -65,11 +65,16 @@

 go_check () {
 for I in `/bin/cat $configdir/kid.list | sort -u`; do
+        # This method ( /usr/bin/users ) to determine who is logged in is a kludge.  It ignores locked screens (screensaver active) and switched user states.
         /usr/bin/users | /bin/grep -q $I
         if [ $? -eq 0 ]; then
                 if [ -e $basedir/time/$I.ttl ]; then
                         C=`/bin/cat $basedir/time/$I.ttl`
-                        C=$((C - 1))
+                        if [ $C -le 0 ]; then
+                                C=0
+                        else
+                                C=$((C - 1))
+                       fi
                         echo $C > $basedir/time/$I.ttl
                 else
                        get_ttl_time $I > $basedir/time/$I.ttl
@@ -77,8 +82,8 @@
                 fi
                # check time
                if [ $C -le 5 ]; then
+                       /usr/bin/passwd $I -l
                        if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
-                               /usr/bin/passwd $I -l
                                go_logout $I
                        fi
                fi
@@ -128,7 +133,13 @@
                                 /usr/bin/passwd $I -u
                         else
                                 /usr/bin/passwd $I -l
-                                /usr/bin/users | /bin/grep -q $I && /usr/local/bin/kidtimer shutdown $I
+                                # This method ( /usr/bin/users ) to determine who is logged in is a kludge.  It ignores locked screens (screensaver active) and switched user states.
+                                /usr/bin/users | /bin/grep -q $I
+                                if [ $? -eq 0 ]; then
+                                       if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
+                                               go_logout $I
+                                       fi
+                               fi
                         fi
                 fi
         done
@@ -155,24 +166,45 @@
 TEXT=$((6-$T))
 TMPFILE="/tmp/kidtimer.send$T.bash.$K"
 /bin/rm -f $TMPFILE
+touch $TMPFILE
+chmod +x $TMPFILE
+
 echo "#!/bin/bash" > $TMPFILE
-D=`/usr/bin/who | grep $K | grep -m1 "(:" | cut -d\( -f2 | sed s/\)// | sed s/://`
+
+# This method to determine the correct display doesn't work, at least on zorin.
+# It only seems to detect a user who has an active shell running
+#D=`/usr/bin/who | grep $K | grep -m1 "(:" | cut -d\( -f2 | sed s/\)// | sed s/://`
+
+# So instead I'll just send the message to all the first 10 displays, and the ones owned by the correct user will show the message, the rest won't
 if [ -e $basedir/locale/$LANG ]; then
-       MSG=`/bin/sed -n "$LINE"p /usr/local/kidtimer/locale/$LANG`
+       MSG="$K: `/bin/sed -n "$LINE"p /usr/local/kidtimer/locale/$LANG`"
 else
-       MSG="Your computer time will end in $TEXT minutes."
+       MSG="$K's computer time will end in $TEXT minutes."
 fi
-echo "/bin/su $K -c 'DISPLAY=:$D /usr/bin/notify-send -i /usr/local/kidtimer/icons/kidtimer-$TEXT.png \
+
+for (( D = 0 ; D < 10 ; D = D + 1 )); do
+        echo "/bin/su $K -c 'DISPLAY=:$D /usr/bin/notify-send -i /usr/local/kidtimer/icons/kidtimer-$TEXT.png \
        \"ALERT\" \"$MSG\"'" >> $TMPFILE
+done
+
 echo "/bin/rm -f $TMPFILE" >> $TMPFILE
 echo "/bin/bash $TMPFILE" | /usr/bin/at now + $T minutes
+# Note you need the file /var/spool/cron/atjobs/.SEQ owned by daemon:daemon for at to work properly!!!
 }


 go_killall () {
 K=$1
 /bin/rm -f /tmp/killall.bash.$K
+touch /tmp/killall.bash.$K
+chmod +x /tmp/killall.bash.$K
+
 echo "#!/bin/bash" > /tmp/killall.bash.$K
+# Not sure if it is better or worse to do a normal kill first...
+# Programs like Firefox will reopen all their previous tabs if it is killed with -KILL
+# But other programs would probably do better if given a chance to save state/data and clean up after themselves eg open office???
+#echo "/usr/bin/pkill -TERM -u $K" >> /tmp/killall.bash.$K
+#echo "/bin/sleep 5" >> /tmp/killall.bash.$K
 echo "/usr/bin/pkill -KILL -u $K" >> /tmp/killall.bash.$K
 echo "/bin/rm -rf /tmp/kidtimer.shutdown.$K" >> /tmp/killall.bash.$K
 echo "/bin/rm -f /tmp/killall.bash.$K" >> /tmp/killall.bash.$K
@@ -450,4 +482,3 @@
         ;;
 esac
 exit 0
-

And here's the full text of the fixed version:

#!/bin/bash
# Restrict kids computer access to specific hours and total time.
# By: Michael Groves - grover66_at_gmail_dot_com

#variables
#LANG="en_US.UTF-8" #unremark to change preferred language.
basedir="/usr/local/kidtimer"
configdir="/etc/kidtimer"
Cdate=`/bin/date +%Y-%m-%d`
TUI=0
HOUR=`/bin/date +%H`
DOW=`/bin/date +%u`
WEEKEND="no"
[ "$DOW" == "6" ] && WEEKEND="yes"
[ "$DOW" == "7" ] && WEEKEND="yes"
[ ! -e $configdir/kid.list ] && touch $configdir/kid.list
[ ! -e $basedir/locale/$LANG ] && LANG="en_US.UTF-8"

#arguments
[ $# -eq 0 ] && TUI=1
[ $# -eq 1 ] && COMMAND=$1
[ $# -eq 2 ] && COMMAND=$1 && KID=$2
[ $# -eq 3 ] && COMMAND=$1 && KID=$2 && Time=$3

################# Subroutines ##################
################################################

get_info () {
echo "kidtimer info"
echo "---------------------------------------------"
/bin/date
echo "---"
echo "find /usr/local/kidtimer/ -print | sort"
/usr/bin/find /usr/local/kidtimer/ -print | /usr/bin/sort
echo "---"
echo "cat $configdir/kid.list"
/bin/cat $configdir/kid.list
echo "---"
echo "passwd -S -a"
/usr/bin/passwd -S -a
echo "---"
echo "cat /usr/local/kidtimer/schedule/*"
/bin/cat /usr/local/kidtimer/schedule/*
echo "---"
echo "cat /usr/local/kidtimer/time/*"
/bin/cat /usr/local/kidtimer/time/*
echo "---"
echo "cat /etc/cron.d/kidtimer"
/bin/cat /etc/cron.d/kidtimer
echo "---"
echo "apt-cache showpkg kidtimer"
/usr/bin/apt-cache showpkg kidtimer
echo "---"
echo "cat /etc/lsb-release"
/bin/cat /etc/lsb-release
echo "---"
echo "uname -a"
/bin/uname -a
echo "---"
echo "env"
/usr/bin/env
echo
}


go_check () {
for I in `/bin/cat $configdir/kid.list | sort -u`; do
        # This method ( /usr/bin/users ) to determine who is logged in is a kludge.  It ignores locked screens (screensaver active) and switched user states.
        /usr/bin/users | /bin/grep -q $I
        if [ $? -eq 0 ]; then
                if [ -e $basedir/time/$I.ttl ]; then
                        C=`/bin/cat $basedir/time/$I.ttl`
                        if [ $C -le 0 ]; then
                                C=0
                        else
                                C=$((C - 1))
            fi
                        echo $C > $basedir/time/$I.ttl
                else
            get_ttl_time $I > $basedir/time/$I.ttl
                        C=`/bin/cat $basedir/time/$I.ttl`
                fi
        # check time
        if [ $C -le 5 ]; then
                    /usr/bin/passwd $I -l
                    if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
                            go_logout $I
                    fi
            fi
        else
                go_clean_jobs $I
        fi
done
}


get_ttl_time () {
        [ "$WEEKEND" == "no" ] && /bin/cat $basedir/schedule/$1 | /bin/grep ^MAX | /usr/bin/awk '{ print $2 }'
        [ "$WEEKEND" == "yes" ] && /bin/cat $basedir/schedule/$1 | /bin/grep ^MAX | /usr/bin/awk '{ print $3 }'
}


go_clean_jobs () {
K=$1
for I in `/usr/bin/atq | /usr/bin/awk '{ print $1 }' | /usr/bin/sort`; do
        /usr/bin/at -c $I | /bin/grep -q "bash.$K"
        [ $? -eq 0 ] && /usr/bin/at -d $I
done
[ -e /tmp/kidtimer.shutdown.$K ] && /bin/rm -rf /tmp/kidtimer.shutdown.$K
}


go_daily () {
for I in `/bin/cat $configdir/kid.list | sort -u`; do
        /usr/bin/stat -c %y $basedir/time/$I.ttl | /bin/grep -q $Cdate
        if [ ! $? -eq 0 ]; then
        get_ttl_time $I > $basedir/time/$I.ttl
        fi
done
go_hourly
}


go_hourly () {
if [ -s $configdir/kid.list ]; then
        for I in `/bin/cat $configdir/kid.list | sort -u`; do
                if [ -e $basedir/schedule/$I ]; then
                        [ -e $basedir/time/$I.ttl ] && C=`/bin/cat $basedir/time/$I.ttl`
                        [ $C -le 0 ] && /usr/bin/passwd $I -l && exit 0
                        [ "$WEEKEND" == "no" ] && R=`/bin/grep ^$HOUR $basedir/schedule/$I | /usr/bin/awk '{ print $2 }'`
                        [ "$WEEKEND" == "yes" ] && R=`/bin/grep ^$HOUR $basedir/schedule/$I | /usr/bin/awk '{ print $3 }'`
                        if [ "$R" == "y" ]; then
                                /usr/bin/passwd $I -u
                        else
                                /usr/bin/passwd $I -l
                                # This method ( /usr/bin/users ) to determine who is logged in is a kludge.  It ignores locked screens (screensaver active) and switched user states.
                                /usr/bin/users | /bin/grep -q $I
                                if [ $? -eq 0 ]; then
                            if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
                                    go_logout $I
                    fi
                        fi
                        fi
                fi
        done
fi
}


go_logout () {
K=$1
go_send $K 5
go_send $K 4
go_send $K 3
go_send $K 2
go_send $K 1
go_killall $K
/usr/bin/touch /tmp/kidtimer.shutdown.$K
}


go_send () {
K=$1
T=$2
LINE=$((7-$T))
TEXT=$((6-$T))
TMPFILE="/tmp/kidtimer.send$T.bash.$K"
/bin/rm -f $TMPFILE
touch $TMPFILE
chmod +x $TMPFILE

echo "#!/bin/bash" > $TMPFILE

# This method to determine the correct display doesn't work, at least on zorin.
# It only seems to detect a user who has an active shell running
#D=`/usr/bin/who | grep $K | grep -m1 "(:" | cut -d\( -f2 | sed s/\)// | sed s/://`

# So instead I'll just send the message to all the first 10 displays, and the ones owned by the correct user will show the message, the rest won't
if [ -e $basedir/locale/$LANG ]; then
    MSG="$K: `/bin/sed -n "$LINE"p /usr/local/kidtimer/locale/$LANG`"
else
    MSG="$K's computer time will end in $TEXT minutes."
fi

for (( D = 0 ; D < 10 ; D = D + 1 )); do
        echo "/bin/su $K -c 'DISPLAY=:$D /usr/bin/notify-send -i /usr/local/kidtimer/icons/kidtimer-$TEXT.png \
    \"ALERT\" \"$MSG\"'" >> $TMPFILE
done

echo "/bin/rm -f $TMPFILE" >> $TMPFILE
echo "/bin/bash $TMPFILE" | /usr/bin/at now + $T minutes
# Note you need the file /var/spool/cron/atjobs/.SEQ owned by daemon:daemon for at to work properly!!!
}


go_killall () {
K=$1
/bin/rm -f /tmp/killall.bash.$K
touch /tmp/killall.bash.$K
chmod +x /tmp/killall.bash.$K

echo "#!/bin/bash" > /tmp/killall.bash.$K
# Not sure if it is better or worse to do a normal kill first...
# Programs like Firefox will reopen all their previous tabs if it is killed with -KILL
# But other programs would probably do better if given a chance to save state/data and clean up after themselves eg open office???
#echo "/usr/bin/pkill -TERM -u $K" >> /tmp/killall.bash.$K
#echo "/bin/sleep 5" >> /tmp/killall.bash.$K
echo "/usr/bin/pkill -KILL -u $K" >> /tmp/killall.bash.$K
echo "/bin/rm -rf /tmp/kidtimer.shutdown.$K" >> /tmp/killall.bash.$K
echo "/bin/rm -f /tmp/killall.bash.$K" >> /tmp/killall.bash.$K
echo "/bin/bash /tmp/killall.bash.$K" | /usr/bin/at now + 6 minutes
}


go_echo_error () {
echo "`/bin/sed -n '12p' /usr/local/kidtimer/locale/$LANG`"
}


go_echo_done () {
echo "`/bin/sed -n '13p' /usr/local/kidtimer/locale/$LANG`"
}


go_reset_time () {
get_ttl_time $KID > $basedir/time/$KID.ttl
/usr/bin/passwd $KID -u
go_clean_jobs $KID
go_echo_done
}


go_addtime () {
U=$KID
A=$Time
grep -q $U $configdir/kid.list
if [ $? -eq 0 ]; then
    if [ "$A" == "reset" ]; then
        get_ttl_time $U > $basedir/time/$U.ttl
            go_echo_done
            exit 0
    elif [ "$A" == "" ]; then
            go_echo_error
        #english: Syntax: addtime <user> <minutes|reset>
            echo "`/bin/sed -n '22p' /usr/local/kidtimer/locale/$LANG`"
            exit 1
    else
            C=`/bin/cat $basedir/time/$KID.ttl`
            C=$((C + Time))
            echo $C > $basedir/time/$KID.ttl
        get_time
    fi
    /usr/bin/passwd $KID -u
    go_clean_jobs $KID
else
    go_echo_error
    #english: User not setup.
    echo "`/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG`"
    exit 1
fi
}


get_time () {
if [ -e $basedir/time/$KID.ttl ]; then
    cat $basedir/time/$KID.ttl
else
    echo
    #english: User not setup.
    echo "`/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG`"
    echo
fi
}


go_tui () {
go_command_list
echo -n "Choose: "; read X
case "$X" in
1) go_setup_user
        ;;
2) go_modify_user
        ;;
3) go_remove_user
        ;;
4) go_list_users
        ;;
5) exit 0
        ;;
esac
go_tui
}


go_command_list () {
echo
echo "1) "`/bin/sed -n '7p' /usr/local/kidtimer/locale/$LANG`
echo "2) "`/bin/sed -n '8p' /usr/local/kidtimer/locale/$LANG`
echo "3) "`/bin/sed -n '9p' /usr/local/kidtimer/locale/$LANG`
echo "4) "`/bin/sed -n '10p' /usr/local/kidtimer/locale/$LANG`
echo "5) "`/bin/sed -n '11p' /usr/local/kidtimer/locale/$LANG`
echo
}


go_list_users () {
echo
#english: Users configured for kidtimer:
echo "`/bin/sed -n '14p' /usr/local/kidtimer/locale/$LANG`"
if [ -s $configdir/kid.list ]; then
        /bin/cat $configdir/kid.list
else
    #english: No configured users.
        echo "`/bin/sed -n '15p' /usr/local/kidtimer/locale/$LANG`"
    echo
fi
}

go_setup_user () {
echo
#english: Username:
echo -n "`/bin/sed -n '16p' /usr/local/kidtimer/locale/$LANG` "; read U
/usr/bin/id $U > /dev/null 2>&1
if [ $? -eq 0 ]; then
        /bin/cp $basedir/schedule/blank $basedir/schedule/$U
    get_ttl_time $U > $basedir/time/$U.ttl
        echo $U >> $configdir/kid.list
        go_echo_done
        echo
    #english: Modify limits now ?(y/n):
        echo -n "`/bin/sed -n '17p' /usr/local/kidtimer/locale/$LANG`"; read M
        if [ "$M" == "y" ]; then
                if [ -e /usr/bin/nano ]; then
                    /usr/bin/nano $basedir/schedule/$U
            get_ttl_time $U > $basedir/time/$U.ttl
                    go_echo_done
            else
                    /usr/bin/vi $basedir/schedule/$U
            get_ttl_time $U > $basedir/time/$U.ttl
                    go_echo_done
                fi
        fi
else
    go_echo_error
    #english: User does not exist. Please create user using the useradd command first.
        echo "`/bin/sed -n '18p' /usr/local/kidtimer/locale/$LANG`"
fi
}


go_modify_user () {
echo
#english: Username:
echo -n "`/bin/sed -n '16p' /usr/local/kidtimer/locale/$LANG` "; read U
grep -q ^$U $configdir/kid.list
if [ $? -eq 0 ]; then
        if [ -e /usr/bin/nano ]; then
                /usr/bin/nano $basedir/schedule/$U
                go_echo_done
        else
                /usr/bin/vi $basedir/schedule/$U
                go_echo_done
        fi
else
    go_echo_error
    #english: User not setup.
        echo "`/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG`"
    echo
fi
}


go_remove_user () {
echo
#english: Username:
echo -n "`/bin/sed -n '16p' /usr/local/kidtimer/locale/$LANG` "; read U
grep -q ^$U $configdir/kid.list
if [ $? -eq 0 ]; then
        /bin/grep -v ^$U $configdir/kid.list > /tmp/kidtimer.tmp
        /bin/cat /tmp/kidtimer.tmp > $configdir/kid.list
        go_echo_done
else
    go_echo_error
    #english: User not setup.
        echo "`/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG`"
    echo
fi
}


go_help () {
echo
echo "Commands:"
echo "--------------------------------------------------------------------------------"
echo "addtime <user> <minutes> ... Increases allowed time for the day."
echo "gettime <user> ... Prints remaining time for the day."
echo "reset <user> ... Reset time for the day."
echo "logout <user> ... Starts logout sequence for user."
echo "hourly ... Enables/disables user access based on the schedule."
echo "daily ... Resets time for the new day."
echo "update ... Updates kidtimer to the latest version."
echo "info ... Gather local configurations to troubleshoot issues."
echo "help ... This list."
echo "--------------------------------------------------------------------------------"
}

check_dependencies () {
#at
P1="";P2="";P3="";pstatus="0"
/usr/bin/dpkg -s at >/dev/null 2>/dev/null
if [ ! $? -eq 0 ]; then
    P1="at"
    pstatus=1
fi
#libnotify-bin
/usr/bin/dpkg -s libnotify-bin >/dev/null 2>/dev/null
if [ ! $? -eq 0 ]; then
        P2="libnotify-bin"
        pstatus=1
fi
#bsdutils
/usr/bin/dpkg -s bsdutils >/dev/null 2>/dev/null
if [ ! $? -eq 0 ]; then
        P3="bsdutils"
        pstatus=1
fi
if [ "$pstatus" == "1" ]; then
    echo
    #english: Error. Missing package dependencies.
    echo "`/bin/sed -n '20p' /usr/local/kidtimer/locale/$LANG`"
    #english: Please install using the following line;
    echo "`/bin/sed -n '21p' /usr/local/kidtimer/locale/$LANG`"
    echo "sudo apt-get install "$P1" "$P2" "$P3
    exit 1
fi
}

get_update () {
URL='https://github.com/grover66/kidtimer/raw/master/DEBS/kidtimer_latest.deb'
if [ -e /usr/bin/wget ]; then
    /usr/bin/wget "$URL" -qO /tmp/kidtimer_latest.deb
    /usr/bin/dpkg -i /tmp/kidtimer_latest.deb
    /bin/rm -f /tmp/kidtimer_latest.deb
else
    echo
    echo "Error. Requires wget"
    echo "To install wget; sudo apt-get install wget"
fi
}


###################### Code ####################
################################################

if [ $TUI -eq 1 ]; then
    check_dependencies
    go_tui
fi

case "$COMMAND" in
addtime) go_addtime
        ;;
reset) go_reset_time
        ;;
gettime) get_time
        ;;
logout) go_logout $KID
        ;;
hourly) go_hourly
        ;;
daily) go_daily
        ;;
update) get_update
        ;;
check) go_check
        ;;
info) get_info
        ;;
-h) go_help
        ;;
help) go_help
        ;;
esac
exit 0

Thanks,
StoicTheVast

@grover66
Copy link
Owner

Hey,

Thanks for the help... Switching users is a tough one... Because, to the
system, they are still running all the things they were running before. I
have tried to tackle this one for a while with no success. Will look over
the rest of your changes and hopefully check it in soon... It has been a
busy few months, since I started a new contract.

Mike :)

On Tue, Sep 23, 2014 at 11:32 AM, StoicTheVast [email protected]
wrote:

Hi,

Thanks for the project. Unfortunately I had a number of issues getting it
to work on my kids' computer (zorin-os 6, based on ubuntu 12.04 LTS).

Summary:

  • AT wasn't working out of the box - it was silently failing. I needed
    to create /var/spool/cron/atjobs/.SEQ owned by daemon:daemon for at to
    start working properly
  • Added the file /usr/lib/pm-utils/sleep.d/99zzkidtimer as suggested
    in another issue. Note this file must be executable! (chmod +x)
  • The use of /usr/bin/users to determine who is logged in is a kludge.
    It ignores locked screens (screensaver active) and switched user states. I
    don't have a fix for that right now - instead I just told the kids to log
    off when they are done rather than let the screensaver kick in, and log off
    rather than switch users between them. Not ideal. Any ideas about how to
    elegantly detect these subtle differences in current computer usage
    state????

In /usr/local/bin/kidtimer, I had to make the following changes:

  • Tweaked the timer countdown to not go negative, even if other things
    fail...
  • The script files created to do the warnings/logouts in /tmp weren't
    executable. They need to be chmod +x
  • /usr/local/bin/kidtimer shutdown is not a supported option - instead
    I have changed it to use go_logout if /tmp/kidtimer.shutdown.$I doesn't
    exist, as is done elsewhere in the code.
  • The user notification wasn't working because the x display detection
    using who doesn't work unless the user has an active shell running (my kids
    don't do that!). So instead I simply iterate through the first 10 displays
    and send the notification with the desired user to all of them - it fails
    gracefully on the screens that don't exist or have the wrong username,
    passes on the ones it needs to.
  • I couldn't decide if, when killing a user's processes they should be
    sent a TERM first before the KILL. Firefox will nicely reload its tabs if
    KILL'ed, but maybe other progs like Open Office would prefer to be TERM'ed
    first so they can clean up properly and only be KILL'ed if they fail to
    exit themselves.

Here is my patch to /usr/local/bin/kidtimer ...

--- kidtimer.orig 2014-09-23 23:53:41.970147275 +0930
+++ kidtimer.fixed 2014-09-24 00:08:35.104446943 +0930
@@ -65,11 +65,16 @@

go_check () {
for I in /bin/cat $configdir/kid.list | sort -u; do

  •    # This method ( /usr/bin/users ) to determine who is logged in is a kludge.  It ignores locked screens (screensaver active) and switched user states.
     /usr/bin/users | /bin/grep -q $I
     if [ $? -eq 0 ]; then
             if [ -e $basedir/time/$I.ttl ]; then
                     C=`/bin/cat $basedir/time/$I.ttl`
    
  •                    C=$((C - 1))
    
  •                    if [ $C -le 0 ]; then
    
  •                            C=0
    
  •                    else
    
  •                            C=$((C - 1))
    
  •                   fi
                     echo $C > $basedir/time/$I.ttl
             else
                    get_ttl_time $I > $basedir/time/$I.ttl
    

    @@ -77,8 +82,8 @@
    fi
    # check time
    if [ $C -le 5 ]; then

  •                   /usr/bin/passwd $I -l
                    if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
    
  •                           /usr/bin/passwd $I -l
                            go_logout $I
                    fi
            fi
    

    @@ -128,7 +133,13 @@
    /usr/bin/passwd $I -u
    else
    /usr/bin/passwd $I -l

  •                            /usr/bin/users | /bin/grep -q $I && /usr/local/bin/kidtimer shutdown $I
    
  •                            # This method ( /usr/bin/users ) to determine who is logged in is a kludge.  It ignores locked screens (screensaver active) and switched user states.
    
  •                            /usr/bin/users | /bin/grep -q $I
    
  •                            if [ $? -eq 0 ]; then
    
  •                                   if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
    
  •                                           go_logout $I
    
  •                                   fi
    
  •                           fi
                     fi
             fi
     done
    

    @@ -155,24 +166,45 @@
    TEXT=$((6-$T))
    TMPFILE="/tmp/kidtimer.send$T.bash.$K"
    /bin/rm -f $TMPFILE
    +touch $TMPFILE
    +chmod +x $TMPFILE
    +
    echo "#!/bin/bash" > $TMPFILE
    -D=/usr/bin/who | grep $K | grep -m1 "(:" | cut -d\( -f2 | sed s/\)// | sed s/://
    +
    +# This method to determine the correct display doesn't work, at least on zorin.
    +# It only seems to detect a user who has an active shell running
    +#D=/usr/bin/who | grep $K | grep -m1 "(:" | cut -d\( -f2 | sed s/\)// | sed s/://
    +
    +# So instead I'll just send the message to all the first 10 displays, and the ones owned by the correct user will show the message, the rest won't
    if [ -e $basedir/locale/$LANG ]; then

  •   MSG=`/bin/sed -n "$LINE"p /usr/local/kidtimer/locale/$LANG`
    
  •   MSG="$K: `/bin/sed -n "$LINE"p /usr/local/kidtimer/locale/$LANG`"
    

    else

  •   MSG="Your computer time will end in $TEXT minutes."
    
  •   MSG="$K's computer time will end in $TEXT minutes."
    

    fi
    -echo "/bin/su $K -c 'DISPLAY=:$D /usr/bin/notify-send -i /usr/local/kidtimer/icons/kidtimer-$TEXT.png
    +
    +for (( D = 0 ; D < 10 ; D = D + 1 )); do

  •    echo "/bin/su $K -c 'DISPLAY=:$D /usr/bin/notify-send -i /usr/local/kidtimer/icons/kidtimer-$TEXT.png \
    \"ALERT\" \"$MSG\"'" >> $TMPFILE
    

    +done
    +
    echo "/bin/rm -f $TMPFILE" >> $TMPFILE
    echo "/bin/bash $TMPFILE" | /usr/bin/at now + $T minutes
    +# Note you need the file /var/spool/cron/atjobs/.SEQ owned by daemon:daemon for at to work properly!!!
    }

    go_killall () {
    K=$1
    /bin/rm -f /tmp/killall.bash.$K
    +touch /tmp/killall.bash.$K
    +chmod +x /tmp/killall.bash.$K
    +
    echo "#!/bin/bash" > /tmp/killall.bash.$K
    +# Not sure if it is better or worse to do a normal kill first...
    +# Programs like Firefox will reopen all their previous tabs if it is killed with -KILL
    +# But other programs would probably do better if given a chance to save state/data and clean up after themselves eg open office???
    +#echo "/usr/bin/pkill -TERM -u $K" >> /tmp/killall.bash.$K
    +#echo "/bin/sleep 5" >> /tmp/killall.bash.$K
    echo "/usr/bin/pkill -KILL -u $K" >> /tmp/killall.bash.$K
    echo "/bin/rm -rf /tmp/kidtimer.shutdown.$K" >> /tmp/killall.bash.$K
    echo "/bin/rm -f /tmp/killall.bash.$K" >> /tmp/killall.bash.$K
    @@ -450,4 +482,3 @@
    ;;
    esac

    exit 0

And here's the full text of the fixed version:

#!/bin/bash

Restrict kids computer access to specific hours and total time.

By: Michael Groves - grover66_at_gmail_dot_com

#variables
#LANG="en_US.UTF-8" #unremark to change preferred language.
basedir="/usr/local/kidtimer"
configdir="/etc/kidtimer"
Cdate=/bin/date +%Y-%m-%d
TUI=0
HOUR=/bin/date +%H
DOW=/bin/date +%u
WEEKEND="no"
[ "$DOW" == "6" ] && WEEKEND="yes"
[ "$DOW" == "7" ] && WEEKEND="yes"
[ ! -e $configdir/kid.list ] && touch $configdir/kid.list
[ ! -e $basedir/locale/$LANG ] && LANG="en_US.UTF-8"

#arguments
[ $# -eq 0 ] && TUI=1
[ $# -eq 1 ] && COMMAND=$1
[ $# -eq 2 ] && COMMAND=$1 && KID=$2
[ $# -eq 3 ] && COMMAND=$1 && KID=$2 && Time=$3

################# Subroutines ##################
################################################

get_info () {
echo "kidtimer info"
echo "---------------------------------------------"
/bin/date
echo "---"
echo "find /usr/local/kidtimer/ -print | sort"
/usr/bin/find /usr/local/kidtimer/ -print | /usr/bin/sort
echo "---"
echo "cat $configdir/kid.list"
/bin/cat $configdir/kid.list
echo "---"
echo "passwd -S -a"
/usr/bin/passwd -S -a
echo "---"
echo "cat /usr/local/kidtimer/schedule/"
/bin/cat /usr/local/kidtimer/schedule/

echo "---"
echo "cat /usr/local/kidtimer/time/"
/bin/cat /usr/local/kidtimer/time/

echo "---"
echo "cat /etc/cron.d/kidtimer"
/bin/cat /etc/cron.d/kidtimer
echo "---"
echo "apt-cache showpkg kidtimer"
/usr/bin/apt-cache showpkg kidtimer
echo "---"
echo "cat /etc/lsb-release"
/bin/cat /etc/lsb-release
echo "---"
echo "uname -a"
/bin/uname -a
echo "---"
echo "env"
/usr/bin/env
echo
}

go_check () {
for I in /bin/cat $configdir/kid.list | sort -u; do
# This method ( /usr/bin/users ) to determine who is logged in is a kludge. It ignores locked screens (screensaver active) and switched user states.
/usr/bin/users | /bin/grep -q $I
if [ $? -eq 0 ]; then
if [ -e $basedir/time/$I.ttl ]; then
C=/bin/cat $basedir/time/$I.ttl
if [ $C -le 0 ]; then
C=0
else
C=$((C - 1))
fi
echo $C > $basedir/time/$I.ttl
else
get_ttl_time $I > $basedir/time/$I.ttl
C=/bin/cat $basedir/time/$I.ttl
fi
# check time
if [ $C -le 5 ]; then
/usr/bin/passwd $I -l
if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
go_logout $I
fi
fi
else
go_clean_jobs $I
fi
done
}

get_ttl_time () {
[ "$WEEKEND" == "no" ] && /bin/cat $basedir/schedule/$1 | /bin/grep ^MAX | /usr/bin/awk '{ print $2 }'
[ "$WEEKEND" == "yes" ] && /bin/cat $basedir/schedule/$1 | /bin/grep ^MAX | /usr/bin/awk '{ print $3 }'
}

go_clean_jobs () {
K=$1
for I in /usr/bin/atq | /usr/bin/awk '{ print $1 }' | /usr/bin/sort; do
/usr/bin/at -c $I | /bin/grep -q "bash.$K"
[ $? -eq 0 ] && /usr/bin/at -d $I
done
[ -e /tmp/kidtimer.shutdown.$K ] && /bin/rm -rf /tmp/kidtimer.shutdown.$K
}

go_daily () {
for I in /bin/cat $configdir/kid.list | sort -u; do
/usr/bin/stat -c %y $basedir/time/$I.ttl | /bin/grep -q $Cdate
if [ ! $? -eq 0 ]; then
get_ttl_time $I > $basedir/time/$I.ttl
fi
done
go_hourly
}

go_hourly () {
if [ -s $configdir/kid.list ]; then
for I in /bin/cat $configdir/kid.list | sort -u; do
if [ -e $basedir/schedule/$I ]; then
[ -e $basedir/time/$I.ttl ] && C=/bin/cat $basedir/time/$I.ttl
[ $C -le 0 ] && /usr/bin/passwd $I -l && exit 0
[ "$WEEKEND" == "no" ] && R=/bin/grep ^$HOUR $basedir/schedule/$I | /usr/bin/awk '{ print $2 }'
[ "$WEEKEND" == "yes" ] && R=/bin/grep ^$HOUR $basedir/schedule/$I | /usr/bin/awk '{ print $3 }'
if [ "$R" == "y" ]; then
/usr/bin/passwd $I -u
else
/usr/bin/passwd $I -l
# This method ( /usr/bin/users ) to determine who is logged in is a kludge. It ignores locked screens (screensaver active) and switched user states.
/usr/bin/users | /bin/grep -q $I
if [ $? -eq 0 ]; then
if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
go_logout $I
fi
fi
fi
fi
done
fi
}

go_logout () {
K=$1
go_send $K 5
go_send $K 4
go_send $K 3
go_send $K 2
go_send $K 1
go_killall $K
/usr/bin/touch /tmp/kidtimer.shutdown.$K
}

go_send () {
K=$1
T=$2
LINE=$((7-$T))
TEXT=$((6-$T))
TMPFILE="/tmp/kidtimer.send$T.bash.$K"
/bin/rm -f $TMPFILE
touch $TMPFILE
chmod +x $TMPFILE

echo "#!/bin/bash" > $TMPFILE

This method to determine the correct display doesn't work, at least on zorin.

It only seems to detect a user who has an active shell running

#D=/usr/bin/who | grep $K | grep -m1 "(:" | cut -d\( -f2 | sed s/\)// | sed s/://

So instead I'll just send the message to all the first 10 displays, and the ones owned by the correct user will show the message, the rest won't

if [ -e $basedir/locale/$LANG ]; then
MSG="$K: /bin/sed -n "$LINE"p /usr/local/kidtimer/locale/$LANG"
else
MSG="$K's computer time will end in $TEXT minutes."
fi

for (( D = 0 ; D < 10 ; D = D + 1 )); do
echo "/bin/su $K -c 'DISPLAY=:$D /usr/bin/notify-send -i /usr/local/kidtimer/icons/kidtimer-$TEXT.png
"ALERT" "$MSG"'" >> $TMPFILE
done

echo "/bin/rm -f $TMPFILE" >> $TMPFILE
echo "/bin/bash $TMPFILE" | /usr/bin/at now + $T minutes

Note you need the file /var/spool/cron/atjobs/.SEQ owned by daemon:daemon for at to work properly!!!

}

go_killall () {
K=$1
/bin/rm -f /tmp/killall.bash.$K
touch /tmp/killall.bash.$K
chmod +x /tmp/killall.bash.$K

echo "#!/bin/bash" > /tmp/killall.bash.$K

Not sure if it is better or worse to do a normal kill first...

Programs like Firefox will reopen all their previous tabs if it is killed with -KILL

But other programs would probably do better if given a chance to save state/data and clean up after themselves eg open office???

#echo "/usr/bin/pkill -TERM -u $K" >> /tmp/killall.bash.$K
#echo "/bin/sleep 5" >> /tmp/killall.bash.$K
echo "/usr/bin/pkill -KILL -u $K" >> /tmp/killall.bash.$K
echo "/bin/rm -rf /tmp/kidtimer.shutdown.$K" >> /tmp/killall.bash.$K
echo "/bin/rm -f /tmp/killall.bash.$K" >> /tmp/killall.bash.$K
echo "/bin/bash /tmp/killall.bash.$K" | /usr/bin/at now + 6 minutes
}

go_echo_error () {
echo "/bin/sed -n '12p' /usr/local/kidtimer/locale/$LANG"
}

go_echo_done () {
echo "/bin/sed -n '13p' /usr/local/kidtimer/locale/$LANG"
}

go_reset_time () {
get_ttl_time $KID > $basedir/time/$KID.ttl
/usr/bin/passwd $KID -u
go_clean_jobs $KID
go_echo_done
}

go_addtime () {
U=$KID
A=$Time
grep -q $U $configdir/kid.list
if [ $? -eq 0 ]; then
if [ "$A" == "reset" ]; then
get_ttl_time $U > $basedir/time/$U.ttl
go_echo_done
exit 0
elif [ "$A" == "" ]; then
go_echo_error
#english: Syntax: addtime <minutes|reset>
echo "/bin/sed -n '22p' /usr/local/kidtimer/locale/$LANG"
exit 1
else
C=/bin/cat $basedir/time/$KID.ttl
C=$((C + Time))
echo $C > $basedir/time/$KID.ttl
get_time
fi
/usr/bin/passwd $KID -u
go_clean_jobs $KID
else
go_echo_error
#english: User not setup.
echo "/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG"
exit 1
fi
}

get_time () {
if [ -e $basedir/time/$KID.ttl ]; then
cat $basedir/time/$KID.ttl
else
echo
#english: User not setup.
echo "/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG"
echo
fi
}

go_tui () {
go_command_list
echo -n "Choose: "; read X
case "$X" in

  1. go_setup_user
    ;;
  2. go_modify_user
    ;;
  3. go_remove_user
    ;;
  4. go_list_users
    ;;
  5. exit 0
    ;;
    esac
    go_tui
    }

go_command_list () {
echo
echo "1) "/bin/sed -n '7p' /usr/local/kidtimer/locale/$LANG
echo "2) "/bin/sed -n '8p' /usr/local/kidtimer/locale/$LANG
echo "3) "/bin/sed -n '9p' /usr/local/kidtimer/locale/$LANG
echo "4) "/bin/sed -n '10p' /usr/local/kidtimer/locale/$LANG
echo "5) "/bin/sed -n '11p' /usr/local/kidtimer/locale/$LANG
echo
}

go_list_users () {
echo
#english: Users configured for kidtimer:
echo "/bin/sed -n '14p' /usr/local/kidtimer/locale/$LANG"
if [ -s $configdir/kid.list ]; then
/bin/cat $configdir/kid.list
else
#english: No configured users.
echo "/bin/sed -n '15p' /usr/local/kidtimer/locale/$LANG"
echo
fi
}

go_setup_user () {
echo
#english: Username:
echo -n "/bin/sed -n '16p' /usr/local/kidtimer/locale/$LANG "; read U
/usr/bin/id $U > /dev/null 2>&1
if [ $? -eq 0 ]; then
/bin/cp $basedir/schedule/blank $basedir/schedule/$U
get_ttl_time $U > $basedir/time/$U.ttl
echo $U >> $configdir/kid.list
go_echo_done
echo
#english: Modify limits now ?(y/n):
echo -n "/bin/sed -n '17p' /usr/local/kidtimer/locale/$LANG"; read M
if [ "$M" == "y" ]; then
if [ -e /usr/bin/nano ]; then
/usr/bin/nano $basedir/schedule/$U
get_ttl_time $U > $basedir/time/$U.ttl
go_echo_done
else
/usr/bin/vi $basedir/schedule/$U
get_ttl_time $U > $basedir/time/$U.ttl
go_echo_done
fi
fi
else
go_echo_error
#english: User does not exist. Please create user using the useradd command first.
echo "/bin/sed -n '18p' /usr/local/kidtimer/locale/$LANG"
fi
}

go_modify_user () {
echo
#english: Username:
echo -n "/bin/sed -n '16p' /usr/local/kidtimer/locale/$LANG "; read U
grep -q ^$U $configdir/kid.list
if [ $? -eq 0 ]; then
if [ -e /usr/bin/nano ]; then
/usr/bin/nano $basedir/schedule/$U
go_echo_done
else
/usr/bin/vi $basedir/schedule/$U
go_echo_done
fi
else
go_echo_error
#english: User not setup.
echo "/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG"
echo
fi
}

go_remove_user () {
echo
#english: Username:
echo -n "/bin/sed -n '16p' /usr/local/kidtimer/locale/$LANG "; read U
grep -q ^$U $configdir/kid.list
if [ $? -eq 0 ]; then
/bin/grep -v ^$U $configdir/kid.list > /tmp/kidtimer.tmp
/bin/cat /tmp/kidtimer.tmp > $configdir/kid.list
go_echo_done
else
go_echo_error
#english: User not setup.
echo "/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG"
echo
fi
}

go_help () {
echo
echo "Commands:"
echo "--------------------------------------------------------------------------------"
echo "addtime ... Increases allowed time for the day."
echo "gettime ... Prints remaining time for the day."
echo "reset ... Reset time for the day."
echo "logout ... Starts logout sequence for user."
echo "hourly ... Enables/disables user access based on the schedule."
echo "daily ... Resets time for the new day."
echo "update ... Updates kidtimer to the latest version."
echo "info ... Gather local configurations to troubleshoot issues."
echo "help ... This list."
echo "--------------------------------------------------------------------------------"
}

check_dependencies () {
#at
P1="";P2="";P3="";pstatus="0"
/usr/bin/dpkg -s at >/dev/null 2>/dev/null
if [ ! $? -eq 0 ]; then
P1="at"
pstatus=1
fi
#libnotify-bin
/usr/bin/dpkg -s libnotify-bin >/dev/null 2>/dev/null
if [ ! $? -eq 0 ]; then
P2="libnotify-bin"
pstatus=1
fi
#bsdutils
/usr/bin/dpkg -s bsdutils >/dev/null 2>/dev/null
if [ ! $? -eq 0 ]; then
P3="bsdutils"
pstatus=1
fi
if [ "$pstatus" == "1" ]; then
echo
#english: Error. Missing package dependencies.
echo "/bin/sed -n '20p' /usr/local/kidtimer/locale/$LANG"
#english: Please install using the following line;
echo "/bin/sed -n '21p' /usr/local/kidtimer/locale/$LANG"
echo "sudo apt-get install "$P1" "$P2" "$P3
exit 1
fi
}

get_update () {
URL='https://github.com/grover66/kidtimer/raw/master/DEBS/kidtimer_latest.deb'
if [ -e /usr/bin/wget ]; then
/usr/bin/wget "$URL" -qO /tmp/kidtimer_latest.deb
/usr/bin/dpkg -i /tmp/kidtimer_latest.deb
/bin/rm -f /tmp/kidtimer_latest.deb
else
echo
echo "Error. Requires wget"
echo "To install wget; sudo apt-get install wget"
fi
}

###################### Code ####################
################################################

if [ $TUI -eq 1 ]; then
check_dependencies
go_tui
fi

case "$COMMAND" in
addtime) go_addtime
;;
reset) go_reset_time
;;
gettime) get_time
;;
logout) go_logout $KID
;;
hourly) go_hourly
;;
daily) go_daily
;;
update) get_update
;;
check) go_check
;;
info) get_info
;;
-h) go_help
;;
help) go_help
;;
esac
exit 0

Thanks,
StoicTheVast


Reply to this email directly or view it on GitHub
#20.

@grover66
Copy link
Owner

I do have a couple of other features coming;

  1. One time pass codes... Allows parent to give their child a six digit
    code which will give them additional time... This is needed when parents
    are out to dinner and their twelve year old wants more time.
  2. TUI additions... Adding "add time" and "get time" options into the
    kidtimer TUI application...

Mike

On Tue, Sep 23, 2014 at 11:32 AM, StoicTheVast [email protected]
wrote:

Hi,

Thanks for the project. Unfortunately I had a number of issues getting it
to work on my kids' computer (zorin-os 6, based on ubuntu 12.04 LTS).

Summary:

  • AT wasn't working out of the box - it was silently failing. I needed
    to create /var/spool/cron/atjobs/.SEQ owned by daemon:daemon for at to
    start working properly
  • Added the file /usr/lib/pm-utils/sleep.d/99zzkidtimer as suggested
    in another issue. Note this file must be executable! (chmod +x)
  • The use of /usr/bin/users to determine who is logged in is a kludge.
    It ignores locked screens (screensaver active) and switched user states. I
    don't have a fix for that right now - instead I just told the kids to log
    off when they are done rather than let the screensaver kick in, and log off
    rather than switch users between them. Not ideal. Any ideas about how to
    elegantly detect these subtle differences in current computer usage
    state????

In /usr/local/bin/kidtimer, I had to make the following changes:

  • Tweaked the timer countdown to not go negative, even if other things
    fail...
  • The script files created to do the warnings/logouts in /tmp weren't
    executable. They need to be chmod +x
  • /usr/local/bin/kidtimer shutdown is not a supported option - instead
    I have changed it to use go_logout if /tmp/kidtimer.shutdown.$I doesn't
    exist, as is done elsewhere in the code.
  • The user notification wasn't working because the x display detection
    using who doesn't work unless the user has an active shell running (my kids
    don't do that!). So instead I simply iterate through the first 10 displays
    and send the notification with the desired user to all of them - it fails
    gracefully on the screens that don't exist or have the wrong username,
    passes on the ones it needs to.
  • I couldn't decide if, when killing a user's processes they should be
    sent a TERM first before the KILL. Firefox will nicely reload its tabs if
    KILL'ed, but maybe other progs like Open Office would prefer to be TERM'ed
    first so they can clean up properly and only be KILL'ed if they fail to
    exit themselves.

Here is my patch to /usr/local/bin/kidtimer ...

--- kidtimer.orig 2014-09-23 23:53:41.970147275 +0930
+++ kidtimer.fixed 2014-09-24 00:08:35.104446943 +0930
@@ -65,11 +65,16 @@

go_check () {
for I in /bin/cat $configdir/kid.list | sort -u; do

  •    # This method ( /usr/bin/users ) to determine who is logged in is a kludge.  It ignores locked screens (screensaver active) and switched user states.
     /usr/bin/users | /bin/grep -q $I
     if [ $? -eq 0 ]; then
             if [ -e $basedir/time/$I.ttl ]; then
                     C=`/bin/cat $basedir/time/$I.ttl`
    
  •                    C=$((C - 1))
    
  •                    if [ $C -le 0 ]; then
    
  •                            C=0
    
  •                    else
    
  •                            C=$((C - 1))
    
  •                   fi
                     echo $C > $basedir/time/$I.ttl
             else
                    get_ttl_time $I > $basedir/time/$I.ttl
    

    @@ -77,8 +82,8 @@
    fi
    # check time
    if [ $C -le 5 ]; then

  •                   /usr/bin/passwd $I -l
                    if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
    
  •                           /usr/bin/passwd $I -l
                            go_logout $I
                    fi
            fi
    

    @@ -128,7 +133,13 @@
    /usr/bin/passwd $I -u
    else
    /usr/bin/passwd $I -l

  •                            /usr/bin/users | /bin/grep -q $I && /usr/local/bin/kidtimer shutdown $I
    
  •                            # This method ( /usr/bin/users ) to determine who is logged in is a kludge.  It ignores locked screens (screensaver active) and switched user states.
    
  •                            /usr/bin/users | /bin/grep -q $I
    
  •                            if [ $? -eq 0 ]; then
    
  •                                   if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
    
  •                                           go_logout $I
    
  •                                   fi
    
  •                           fi
                     fi
             fi
     done
    

    @@ -155,24 +166,45 @@
    TEXT=$((6-$T))
    TMPFILE="/tmp/kidtimer.send$T.bash.$K"
    /bin/rm -f $TMPFILE
    +touch $TMPFILE
    +chmod +x $TMPFILE
    +
    echo "#!/bin/bash" > $TMPFILE
    -D=/usr/bin/who | grep $K | grep -m1 "(:" | cut -d\( -f2 | sed s/\)// | sed s/://
    +
    +# This method to determine the correct display doesn't work, at least on zorin.
    +# It only seems to detect a user who has an active shell running
    +#D=/usr/bin/who | grep $K | grep -m1 "(:" | cut -d\( -f2 | sed s/\)// | sed s/://
    +
    +# So instead I'll just send the message to all the first 10 displays, and the ones owned by the correct user will show the message, the rest won't
    if [ -e $basedir/locale/$LANG ]; then

  •   MSG=`/bin/sed -n "$LINE"p /usr/local/kidtimer/locale/$LANG`
    
  •   MSG="$K: `/bin/sed -n "$LINE"p /usr/local/kidtimer/locale/$LANG`"
    

    else

  •   MSG="Your computer time will end in $TEXT minutes."
    
  •   MSG="$K's computer time will end in $TEXT minutes."
    

    fi
    -echo "/bin/su $K -c 'DISPLAY=:$D /usr/bin/notify-send -i /usr/local/kidtimer/icons/kidtimer-$TEXT.png
    +
    +for (( D = 0 ; D < 10 ; D = D + 1 )); do

  •    echo "/bin/su $K -c 'DISPLAY=:$D /usr/bin/notify-send -i /usr/local/kidtimer/icons/kidtimer-$TEXT.png \
    \"ALERT\" \"$MSG\"'" >> $TMPFILE
    

    +done
    +
    echo "/bin/rm -f $TMPFILE" >> $TMPFILE
    echo "/bin/bash $TMPFILE" | /usr/bin/at now + $T minutes
    +# Note you need the file /var/spool/cron/atjobs/.SEQ owned by daemon:daemon for at to work properly!!!
    }

    go_killall () {
    K=$1
    /bin/rm -f /tmp/killall.bash.$K
    +touch /tmp/killall.bash.$K
    +chmod +x /tmp/killall.bash.$K
    +
    echo "#!/bin/bash" > /tmp/killall.bash.$K
    +# Not sure if it is better or worse to do a normal kill first...
    +# Programs like Firefox will reopen all their previous tabs if it is killed with -KILL
    +# But other programs would probably do better if given a chance to save state/data and clean up after themselves eg open office???
    +#echo "/usr/bin/pkill -TERM -u $K" >> /tmp/killall.bash.$K
    +#echo "/bin/sleep 5" >> /tmp/killall.bash.$K
    echo "/usr/bin/pkill -KILL -u $K" >> /tmp/killall.bash.$K
    echo "/bin/rm -rf /tmp/kidtimer.shutdown.$K" >> /tmp/killall.bash.$K
    echo "/bin/rm -f /tmp/killall.bash.$K" >> /tmp/killall.bash.$K
    @@ -450,4 +482,3 @@
    ;;
    esac

    exit 0

And here's the full text of the fixed version:

#!/bin/bash

Restrict kids computer access to specific hours and total time.

By: Michael Groves - grover66_at_gmail_dot_com

#variables
#LANG="en_US.UTF-8" #unremark to change preferred language.
basedir="/usr/local/kidtimer"
configdir="/etc/kidtimer"
Cdate=/bin/date +%Y-%m-%d
TUI=0
HOUR=/bin/date +%H
DOW=/bin/date +%u
WEEKEND="no"
[ "$DOW" == "6" ] && WEEKEND="yes"
[ "$DOW" == "7" ] && WEEKEND="yes"
[ ! -e $configdir/kid.list ] && touch $configdir/kid.list
[ ! -e $basedir/locale/$LANG ] && LANG="en_US.UTF-8"

#arguments
[ $# -eq 0 ] && TUI=1
[ $# -eq 1 ] && COMMAND=$1
[ $# -eq 2 ] && COMMAND=$1 && KID=$2
[ $# -eq 3 ] && COMMAND=$1 && KID=$2 && Time=$3

################# Subroutines ##################
################################################

get_info () {
echo "kidtimer info"
echo "---------------------------------------------"
/bin/date
echo "---"
echo "find /usr/local/kidtimer/ -print | sort"
/usr/bin/find /usr/local/kidtimer/ -print | /usr/bin/sort
echo "---"
echo "cat $configdir/kid.list"
/bin/cat $configdir/kid.list
echo "---"
echo "passwd -S -a"
/usr/bin/passwd -S -a
echo "---"
echo "cat /usr/local/kidtimer/schedule/"
/bin/cat /usr/local/kidtimer/schedule/

echo "---"
echo "cat /usr/local/kidtimer/time/"
/bin/cat /usr/local/kidtimer/time/

echo "---"
echo "cat /etc/cron.d/kidtimer"
/bin/cat /etc/cron.d/kidtimer
echo "---"
echo "apt-cache showpkg kidtimer"
/usr/bin/apt-cache showpkg kidtimer
echo "---"
echo "cat /etc/lsb-release"
/bin/cat /etc/lsb-release
echo "---"
echo "uname -a"
/bin/uname -a
echo "---"
echo "env"
/usr/bin/env
echo
}

go_check () {
for I in /bin/cat $configdir/kid.list | sort -u; do
# This method ( /usr/bin/users ) to determine who is logged in is a kludge. It ignores locked screens (screensaver active) and switched user states.
/usr/bin/users | /bin/grep -q $I
if [ $? -eq 0 ]; then
if [ -e $basedir/time/$I.ttl ]; then
C=/bin/cat $basedir/time/$I.ttl
if [ $C -le 0 ]; then
C=0
else
C=$((C - 1))
fi
echo $C > $basedir/time/$I.ttl
else
get_ttl_time $I > $basedir/time/$I.ttl
C=/bin/cat $basedir/time/$I.ttl
fi
# check time
if [ $C -le 5 ]; then
/usr/bin/passwd $I -l
if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
go_logout $I
fi
fi
else
go_clean_jobs $I
fi
done
}

get_ttl_time () {
[ "$WEEKEND" == "no" ] && /bin/cat $basedir/schedule/$1 | /bin/grep ^MAX | /usr/bin/awk '{ print $2 }'
[ "$WEEKEND" == "yes" ] && /bin/cat $basedir/schedule/$1 | /bin/grep ^MAX | /usr/bin/awk '{ print $3 }'
}

go_clean_jobs () {
K=$1
for I in /usr/bin/atq | /usr/bin/awk '{ print $1 }' | /usr/bin/sort; do
/usr/bin/at -c $I | /bin/grep -q "bash.$K"
[ $? -eq 0 ] && /usr/bin/at -d $I
done
[ -e /tmp/kidtimer.shutdown.$K ] && /bin/rm -rf /tmp/kidtimer.shutdown.$K
}

go_daily () {
for I in /bin/cat $configdir/kid.list | sort -u; do
/usr/bin/stat -c %y $basedir/time/$I.ttl | /bin/grep -q $Cdate
if [ ! $? -eq 0 ]; then
get_ttl_time $I > $basedir/time/$I.ttl
fi
done
go_hourly
}

go_hourly () {
if [ -s $configdir/kid.list ]; then
for I in /bin/cat $configdir/kid.list | sort -u; do
if [ -e $basedir/schedule/$I ]; then
[ -e $basedir/time/$I.ttl ] && C=/bin/cat $basedir/time/$I.ttl
[ $C -le 0 ] && /usr/bin/passwd $I -l && exit 0
[ "$WEEKEND" == "no" ] && R=/bin/grep ^$HOUR $basedir/schedule/$I | /usr/bin/awk '{ print $2 }'
[ "$WEEKEND" == "yes" ] && R=/bin/grep ^$HOUR $basedir/schedule/$I | /usr/bin/awk '{ print $3 }'
if [ "$R" == "y" ]; then
/usr/bin/passwd $I -u
else
/usr/bin/passwd $I -l
# This method ( /usr/bin/users ) to determine who is logged in is a kludge. It ignores locked screens (screensaver active) and switched user states.
/usr/bin/users | /bin/grep -q $I
if [ $? -eq 0 ]; then
if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
go_logout $I
fi
fi
fi
fi
done
fi
}

go_logout () {
K=$1
go_send $K 5
go_send $K 4
go_send $K 3
go_send $K 2
go_send $K 1
go_killall $K
/usr/bin/touch /tmp/kidtimer.shutdown.$K
}

go_send () {
K=$1
T=$2
LINE=$((7-$T))
TEXT=$((6-$T))
TMPFILE="/tmp/kidtimer.send$T.bash.$K"
/bin/rm -f $TMPFILE
touch $TMPFILE
chmod +x $TMPFILE

echo "#!/bin/bash" > $TMPFILE

This method to determine the correct display doesn't work, at least on zorin.

It only seems to detect a user who has an active shell running

#D=/usr/bin/who | grep $K | grep -m1 "(:" | cut -d\( -f2 | sed s/\)// | sed s/://

So instead I'll just send the message to all the first 10 displays, and the ones owned by the correct user will show the message, the rest won't

if [ -e $basedir/locale/$LANG ]; then
MSG="$K: /bin/sed -n "$LINE"p /usr/local/kidtimer/locale/$LANG"
else
MSG="$K's computer time will end in $TEXT minutes."
fi

for (( D = 0 ; D < 10 ; D = D + 1 )); do
echo "/bin/su $K -c 'DISPLAY=:$D /usr/bin/notify-send -i /usr/local/kidtimer/icons/kidtimer-$TEXT.png
"ALERT" "$MSG"'" >> $TMPFILE
done

echo "/bin/rm -f $TMPFILE" >> $TMPFILE
echo "/bin/bash $TMPFILE" | /usr/bin/at now + $T minutes

Note you need the file /var/spool/cron/atjobs/.SEQ owned by daemon:daemon for at to work properly!!!

}

go_killall () {
K=$1
/bin/rm -f /tmp/killall.bash.$K
touch /tmp/killall.bash.$K
chmod +x /tmp/killall.bash.$K

echo "#!/bin/bash" > /tmp/killall.bash.$K

Not sure if it is better or worse to do a normal kill first...

Programs like Firefox will reopen all their previous tabs if it is killed with -KILL

But other programs would probably do better if given a chance to save state/data and clean up after themselves eg open office???

#echo "/usr/bin/pkill -TERM -u $K" >> /tmp/killall.bash.$K
#echo "/bin/sleep 5" >> /tmp/killall.bash.$K
echo "/usr/bin/pkill -KILL -u $K" >> /tmp/killall.bash.$K
echo "/bin/rm -rf /tmp/kidtimer.shutdown.$K" >> /tmp/killall.bash.$K
echo "/bin/rm -f /tmp/killall.bash.$K" >> /tmp/killall.bash.$K
echo "/bin/bash /tmp/killall.bash.$K" | /usr/bin/at now + 6 minutes
}

go_echo_error () {
echo "/bin/sed -n '12p' /usr/local/kidtimer/locale/$LANG"
}

go_echo_done () {
echo "/bin/sed -n '13p' /usr/local/kidtimer/locale/$LANG"
}

go_reset_time () {
get_ttl_time $KID > $basedir/time/$KID.ttl
/usr/bin/passwd $KID -u
go_clean_jobs $KID
go_echo_done
}

go_addtime () {
U=$KID
A=$Time
grep -q $U $configdir/kid.list
if [ $? -eq 0 ]; then
if [ "$A" == "reset" ]; then
get_ttl_time $U > $basedir/time/$U.ttl
go_echo_done
exit 0
elif [ "$A" == "" ]; then
go_echo_error
#english: Syntax: addtime <minutes|reset>
echo "/bin/sed -n '22p' /usr/local/kidtimer/locale/$LANG"
exit 1
else
C=/bin/cat $basedir/time/$KID.ttl
C=$((C + Time))
echo $C > $basedir/time/$KID.ttl
get_time
fi
/usr/bin/passwd $KID -u
go_clean_jobs $KID
else
go_echo_error
#english: User not setup.
echo "/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG"
exit 1
fi
}

get_time () {
if [ -e $basedir/time/$KID.ttl ]; then
cat $basedir/time/$KID.ttl
else
echo
#english: User not setup.
echo "/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG"
echo
fi
}

go_tui () {
go_command_list
echo -n "Choose: "; read X
case "$X" in

  1. go_setup_user
    ;;
  2. go_modify_user
    ;;
  3. go_remove_user
    ;;
  4. go_list_users
    ;;
  5. exit 0
    ;;
    esac
    go_tui
    }

go_command_list () {
echo
echo "1) "/bin/sed -n '7p' /usr/local/kidtimer/locale/$LANG
echo "2) "/bin/sed -n '8p' /usr/local/kidtimer/locale/$LANG
echo "3) "/bin/sed -n '9p' /usr/local/kidtimer/locale/$LANG
echo "4) "/bin/sed -n '10p' /usr/local/kidtimer/locale/$LANG
echo "5) "/bin/sed -n '11p' /usr/local/kidtimer/locale/$LANG
echo
}

go_list_users () {
echo
#english: Users configured for kidtimer:
echo "/bin/sed -n '14p' /usr/local/kidtimer/locale/$LANG"
if [ -s $configdir/kid.list ]; then
/bin/cat $configdir/kid.list
else
#english: No configured users.
echo "/bin/sed -n '15p' /usr/local/kidtimer/locale/$LANG"
echo
fi
}

go_setup_user () {
echo
#english: Username:
echo -n "/bin/sed -n '16p' /usr/local/kidtimer/locale/$LANG "; read U
/usr/bin/id $U > /dev/null 2>&1
if [ $? -eq 0 ]; then
/bin/cp $basedir/schedule/blank $basedir/schedule/$U
get_ttl_time $U > $basedir/time/$U.ttl
echo $U >> $configdir/kid.list
go_echo_done
echo
#english: Modify limits now ?(y/n):
echo -n "/bin/sed -n '17p' /usr/local/kidtimer/locale/$LANG"; read M
if [ "$M" == "y" ]; then
if [ -e /usr/bin/nano ]; then
/usr/bin/nano $basedir/schedule/$U
get_ttl_time $U > $basedir/time/$U.ttl
go_echo_done
else
/usr/bin/vi $basedir/schedule/$U
get_ttl_time $U > $basedir/time/$U.ttl
go_echo_done
fi
fi
else
go_echo_error
#english: User does not exist. Please create user using the useradd command first.
echo "/bin/sed -n '18p' /usr/local/kidtimer/locale/$LANG"
fi
}

go_modify_user () {
echo
#english: Username:
echo -n "/bin/sed -n '16p' /usr/local/kidtimer/locale/$LANG "; read U
grep -q ^$U $configdir/kid.list
if [ $? -eq 0 ]; then
if [ -e /usr/bin/nano ]; then
/usr/bin/nano $basedir/schedule/$U
go_echo_done
else
/usr/bin/vi $basedir/schedule/$U
go_echo_done
fi
else
go_echo_error
#english: User not setup.
echo "/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG"
echo
fi
}

go_remove_user () {
echo
#english: Username:
echo -n "/bin/sed -n '16p' /usr/local/kidtimer/locale/$LANG "; read U
grep -q ^$U $configdir/kid.list
if [ $? -eq 0 ]; then
/bin/grep -v ^$U $configdir/kid.list > /tmp/kidtimer.tmp
/bin/cat /tmp/kidtimer.tmp > $configdir/kid.list
go_echo_done
else
go_echo_error
#english: User not setup.
echo "/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG"
echo
fi
}

go_help () {
echo
echo "Commands:"
echo "--------------------------------------------------------------------------------"
echo "addtime ... Increases allowed time for the day."
echo "gettime ... Prints remaining time for the day."
echo "reset ... Reset time for the day."
echo "logout ... Starts logout sequence for user."
echo "hourly ... Enables/disables user access based on the schedule."
echo "daily ... Resets time for the new day."
echo "update ... Updates kidtimer to the latest version."
echo "info ... Gather local configurations to troubleshoot issues."
echo "help ... This list."
echo "--------------------------------------------------------------------------------"
}

check_dependencies () {
#at
P1="";P2="";P3="";pstatus="0"
/usr/bin/dpkg -s at >/dev/null 2>/dev/null
if [ ! $? -eq 0 ]; then
P1="at"
pstatus=1
fi
#libnotify-bin
/usr/bin/dpkg -s libnotify-bin >/dev/null 2>/dev/null
if [ ! $? -eq 0 ]; then
P2="libnotify-bin"
pstatus=1
fi
#bsdutils
/usr/bin/dpkg -s bsdutils >/dev/null 2>/dev/null
if [ ! $? -eq 0 ]; then
P3="bsdutils"
pstatus=1
fi
if [ "$pstatus" == "1" ]; then
echo
#english: Error. Missing package dependencies.
echo "/bin/sed -n '20p' /usr/local/kidtimer/locale/$LANG"
#english: Please install using the following line;
echo "/bin/sed -n '21p' /usr/local/kidtimer/locale/$LANG"
echo "sudo apt-get install "$P1" "$P2" "$P3
exit 1
fi
}

get_update () {
URL='https://github.com/grover66/kidtimer/raw/master/DEBS/kidtimer_latest.deb'
if [ -e /usr/bin/wget ]; then
/usr/bin/wget "$URL" -qO /tmp/kidtimer_latest.deb
/usr/bin/dpkg -i /tmp/kidtimer_latest.deb
/bin/rm -f /tmp/kidtimer_latest.deb
else
echo
echo "Error. Requires wget"
echo "To install wget; sudo apt-get install wget"
fi
}

###################### Code ####################
################################################

if [ $TUI -eq 1 ]; then
check_dependencies
go_tui
fi

case "$COMMAND" in
addtime) go_addtime
;;
reset) go_reset_time
;;
gettime) get_time
;;
logout) go_logout $KID
;;
hourly) go_hourly
;;
daily) go_daily
;;
update) get_update
;;
check) go_check
;;
info) get_info
;;
-h) go_help
;;
help) go_help
;;
esac
exit 0

Thanks,
StoicTheVast


Reply to this email directly or view it on GitHub
#20.

@StoicTheVast
Copy link
Author

Yes I agree it's tough to determine the actual active user info. If I find
a solution I'll be sure to let you know! Regarding your new feature ideas:

  1. Sounds like a good idea - it might be a couple of years until I get to
    need that one I but I can definitely see its usefulness.

2a). Sounds OK, merely replicating what can be done in the command line
already, but perhaps useful enough for the wife to drive...

2b) If that is being done, another option I would suggest is "subtract
time". Currently I can do this (eg as part of a "grounding") simply by
adding a negative time value. But providing that option explicitly eg
implemented internally by adding (-1 * time), may make it easier for the
less confident parent administrator.

@grover66
Copy link
Owner

looks like the problem with AT is not kidtimer's, but a bug in Debian and
Ubuntu starting in 10.10.

See: https://bugs.launchpad.net/ubuntu/+source/at/+bug/371536

Will work the other stuff soon,

Mike :)

On Tue, Sep 23, 2014 at 11:32 AM, StoicTheVast [email protected]
wrote:

Hi,

Thanks for the project. Unfortunately I had a number of issues getting it
to work on my kids' computer (zorin-os 6, based on ubuntu 12.04 LTS).

Summary:

  • AT wasn't working out of the box - it was silently failing. I needed
    to create /var/spool/cron/atjobs/.SEQ owned by daemon:daemon for at to
    start working properly
  • Added the file /usr/lib/pm-utils/sleep.d/99zzkidtimer as suggested
    in another issue. Note this file must be executable! (chmod +x)
  • The use of /usr/bin/users to determine who is logged in is a kludge.
    It ignores locked screens (screensaver active) and switched user states. I
    don't have a fix for that right now - instead I just told the kids to log
    off when they are done rather than let the screensaver kick in, and log off
    rather than switch users between them. Not ideal. Any ideas about how to
    elegantly detect these subtle differences in current computer usage
    state????

In /usr/local/bin/kidtimer, I had to make the following changes:

  • Tweaked the timer countdown to not go negative, even if other things
    fail...
  • The script files created to do the warnings/logouts in /tmp weren't
    executable. They need to be chmod +x
  • /usr/local/bin/kidtimer shutdown is not a supported option - instead
    I have changed it to use go_logout if /tmp/kidtimer.shutdown.$I doesn't
    exist, as is done elsewhere in the code.
  • The user notification wasn't working because the x display detection
    using who doesn't work unless the user has an active shell running (my kids
    don't do that!). So instead I simply iterate through the first 10 displays
    and send the notification with the desired user to all of them - it fails
    gracefully on the screens that don't exist or have the wrong username,
    passes on the ones it needs to.
  • I couldn't decide if, when killing a user's processes they should be
    sent a TERM first before the KILL. Firefox will nicely reload its tabs if
    KILL'ed, but maybe other progs like Open Office would prefer to be TERM'ed
    first so they can clean up properly and only be KILL'ed if they fail to
    exit themselves.

Here is my patch to /usr/local/bin/kidtimer ...

--- kidtimer.orig 2014-09-23 23:53:41.970147275 +0930
+++ kidtimer.fixed 2014-09-24 00:08:35.104446943 +0930
@@ -65,11 +65,16 @@

go_check () {
for I in /bin/cat $configdir/kid.list | sort -u; do

  •    # This method ( /usr/bin/users ) to determine who is logged in is a kludge.  It ignores locked screens (screensaver active) and switched user states.
     /usr/bin/users | /bin/grep -q $I
     if [ $? -eq 0 ]; then
             if [ -e $basedir/time/$I.ttl ]; then
                     C=`/bin/cat $basedir/time/$I.ttl`
    
  •                    C=$((C - 1))
    
  •                    if [ $C -le 0 ]; then
    
  •                            C=0
    
  •                    else
    
  •                            C=$((C - 1))
    
  •                   fi
                     echo $C > $basedir/time/$I.ttl
             else
                    get_ttl_time $I > $basedir/time/$I.ttl
    

    @@ -77,8 +82,8 @@
    fi
    # check time
    if [ $C -le 5 ]; then

  •                   /usr/bin/passwd $I -l
                    if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
    
  •                           /usr/bin/passwd $I -l
                            go_logout $I
                    fi
            fi
    

    @@ -128,7 +133,13 @@
    /usr/bin/passwd $I -u
    else
    /usr/bin/passwd $I -l

  •                            /usr/bin/users | /bin/grep -q $I && /usr/local/bin/kidtimer shutdown $I
    
  •                            # This method ( /usr/bin/users ) to determine who is logged in is a kludge.  It ignores locked screens (screensaver active) and switched user states.
    
  •                            /usr/bin/users | /bin/grep -q $I
    
  •                            if [ $? -eq 0 ]; then
    
  •                                   if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
    
  •                                           go_logout $I
    
  •                                   fi
    
  •                           fi
                     fi
             fi
     done
    

    @@ -155,24 +166,45 @@
    TEXT=$((6-$T))
    TMPFILE="/tmp/kidtimer.send$T.bash.$K"
    /bin/rm -f $TMPFILE
    +touch $TMPFILE
    +chmod +x $TMPFILE
    +
    echo "#!/bin/bash" > $TMPFILE
    -D=/usr/bin/who | grep $K | grep -m1 "(:" | cut -d\( -f2 | sed s/\)// | sed s/://
    +
    +# This method to determine the correct display doesn't work, at least on zorin.
    +# It only seems to detect a user who has an active shell running
    +#D=/usr/bin/who | grep $K | grep -m1 "(:" | cut -d\( -f2 | sed s/\)// | sed s/://
    +
    +# So instead I'll just send the message to all the first 10 displays, and the ones owned by the correct user will show the message, the rest won't
    if [ -e $basedir/locale/$LANG ]; then

  •   MSG=`/bin/sed -n "$LINE"p /usr/local/kidtimer/locale/$LANG`
    
  •   MSG="$K: `/bin/sed -n "$LINE"p /usr/local/kidtimer/locale/$LANG`"
    

    else

  •   MSG="Your computer time will end in $TEXT minutes."
    
  •   MSG="$K's computer time will end in $TEXT minutes."
    

    fi
    -echo "/bin/su $K -c 'DISPLAY=:$D /usr/bin/notify-send -i /usr/local/kidtimer/icons/kidtimer-$TEXT.png
    +
    +for (( D = 0 ; D < 10 ; D = D + 1 )); do

  •    echo "/bin/su $K -c 'DISPLAY=:$D /usr/bin/notify-send -i /usr/local/kidtimer/icons/kidtimer-$TEXT.png \
    \"ALERT\" \"$MSG\"'" >> $TMPFILE
    

    +done
    +
    echo "/bin/rm -f $TMPFILE" >> $TMPFILE
    echo "/bin/bash $TMPFILE" | /usr/bin/at now + $T minutes
    +# Note you need the file /var/spool/cron/atjobs/.SEQ owned by daemon:daemon for at to work properly!!!
    }

    go_killall () {
    K=$1
    /bin/rm -f /tmp/killall.bash.$K
    +touch /tmp/killall.bash.$K
    +chmod +x /tmp/killall.bash.$K
    +
    echo "#!/bin/bash" > /tmp/killall.bash.$K
    +# Not sure if it is better or worse to do a normal kill first...
    +# Programs like Firefox will reopen all their previous tabs if it is killed with -KILL
    +# But other programs would probably do better if given a chance to save state/data and clean up after themselves eg open office???
    +#echo "/usr/bin/pkill -TERM -u $K" >> /tmp/killall.bash.$K
    +#echo "/bin/sleep 5" >> /tmp/killall.bash.$K
    echo "/usr/bin/pkill -KILL -u $K" >> /tmp/killall.bash.$K
    echo "/bin/rm -rf /tmp/kidtimer.shutdown.$K" >> /tmp/killall.bash.$K
    echo "/bin/rm -f /tmp/killall.bash.$K" >> /tmp/killall.bash.$K
    @@ -450,4 +482,3 @@
    ;;
    esac

    exit 0

And here's the full text of the fixed version:

#!/bin/bash

Restrict kids computer access to specific hours and total time.

By: Michael Groves - grover66_at_gmail_dot_com

#variables
#LANG="en_US.UTF-8" #unremark to change preferred language.
basedir="/usr/local/kidtimer"
configdir="/etc/kidtimer"
Cdate=/bin/date +%Y-%m-%d
TUI=0
HOUR=/bin/date +%H
DOW=/bin/date +%u
WEEKEND="no"
[ "$DOW" == "6" ] && WEEKEND="yes"
[ "$DOW" == "7" ] && WEEKEND="yes"
[ ! -e $configdir/kid.list ] && touch $configdir/kid.list
[ ! -e $basedir/locale/$LANG ] && LANG="en_US.UTF-8"

#arguments
[ $# -eq 0 ] && TUI=1
[ $# -eq 1 ] && COMMAND=$1
[ $# -eq 2 ] && COMMAND=$1 && KID=$2
[ $# -eq 3 ] && COMMAND=$1 && KID=$2 && Time=$3

################# Subroutines ##################
################################################

get_info () {
echo "kidtimer info"
echo "---------------------------------------------"
/bin/date
echo "---"
echo "find /usr/local/kidtimer/ -print | sort"
/usr/bin/find /usr/local/kidtimer/ -print | /usr/bin/sort
echo "---"
echo "cat $configdir/kid.list"
/bin/cat $configdir/kid.list
echo "---"
echo "passwd -S -a"
/usr/bin/passwd -S -a
echo "---"
echo "cat /usr/local/kidtimer/schedule/"
/bin/cat /usr/local/kidtimer/schedule/

echo "---"
echo "cat /usr/local/kidtimer/time/"
/bin/cat /usr/local/kidtimer/time/

echo "---"
echo "cat /etc/cron.d/kidtimer"
/bin/cat /etc/cron.d/kidtimer
echo "---"
echo "apt-cache showpkg kidtimer"
/usr/bin/apt-cache showpkg kidtimer
echo "---"
echo "cat /etc/lsb-release"
/bin/cat /etc/lsb-release
echo "---"
echo "uname -a"
/bin/uname -a
echo "---"
echo "env"
/usr/bin/env
echo
}

go_check () {
for I in /bin/cat $configdir/kid.list | sort -u; do
# This method ( /usr/bin/users ) to determine who is logged in is a kludge. It ignores locked screens (screensaver active) and switched user states.
/usr/bin/users | /bin/grep -q $I
if [ $? -eq 0 ]; then
if [ -e $basedir/time/$I.ttl ]; then
C=/bin/cat $basedir/time/$I.ttl
if [ $C -le 0 ]; then
C=0
else
C=$((C - 1))
fi
echo $C > $basedir/time/$I.ttl
else
get_ttl_time $I > $basedir/time/$I.ttl
C=/bin/cat $basedir/time/$I.ttl
fi
# check time
if [ $C -le 5 ]; then
/usr/bin/passwd $I -l
if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
go_logout $I
fi
fi
else
go_clean_jobs $I
fi
done
}

get_ttl_time () {
[ "$WEEKEND" == "no" ] && /bin/cat $basedir/schedule/$1 | /bin/grep ^MAX | /usr/bin/awk '{ print $2 }'
[ "$WEEKEND" == "yes" ] && /bin/cat $basedir/schedule/$1 | /bin/grep ^MAX | /usr/bin/awk '{ print $3 }'
}

go_clean_jobs () {
K=$1
for I in /usr/bin/atq | /usr/bin/awk '{ print $1 }' | /usr/bin/sort; do
/usr/bin/at -c $I | /bin/grep -q "bash.$K"
[ $? -eq 0 ] && /usr/bin/at -d $I
done
[ -e /tmp/kidtimer.shutdown.$K ] && /bin/rm -rf /tmp/kidtimer.shutdown.$K
}

go_daily () {
for I in /bin/cat $configdir/kid.list | sort -u; do
/usr/bin/stat -c %y $basedir/time/$I.ttl | /bin/grep -q $Cdate
if [ ! $? -eq 0 ]; then
get_ttl_time $I > $basedir/time/$I.ttl
fi
done
go_hourly
}

go_hourly () {
if [ -s $configdir/kid.list ]; then
for I in /bin/cat $configdir/kid.list | sort -u; do
if [ -e $basedir/schedule/$I ]; then
[ -e $basedir/time/$I.ttl ] && C=/bin/cat $basedir/time/$I.ttl
[ $C -le 0 ] && /usr/bin/passwd $I -l && exit 0
[ "$WEEKEND" == "no" ] && R=/bin/grep ^$HOUR $basedir/schedule/$I | /usr/bin/awk '{ print $2 }'
[ "$WEEKEND" == "yes" ] && R=/bin/grep ^$HOUR $basedir/schedule/$I | /usr/bin/awk '{ print $3 }'
if [ "$R" == "y" ]; then
/usr/bin/passwd $I -u
else
/usr/bin/passwd $I -l
# This method ( /usr/bin/users ) to determine who is logged in is a kludge. It ignores locked screens (screensaver active) and switched user states.
/usr/bin/users | /bin/grep -q $I
if [ $? -eq 0 ]; then
if [ ! -e /tmp/kidtimer.shutdown.$I ]; then
go_logout $I
fi
fi
fi
fi
done
fi
}

go_logout () {
K=$1
go_send $K 5
go_send $K 4
go_send $K 3
go_send $K 2
go_send $K 1
go_killall $K
/usr/bin/touch /tmp/kidtimer.shutdown.$K
}

go_send () {
K=$1
T=$2
LINE=$((7-$T))
TEXT=$((6-$T))
TMPFILE="/tmp/kidtimer.send$T.bash.$K"
/bin/rm -f $TMPFILE
touch $TMPFILE
chmod +x $TMPFILE

echo "#!/bin/bash" > $TMPFILE

This method to determine the correct display doesn't work, at least on zorin.

It only seems to detect a user who has an active shell running

#D=/usr/bin/who | grep $K | grep -m1 "(:" | cut -d\( -f2 | sed s/\)// | sed s/://

So instead I'll just send the message to all the first 10 displays, and the ones owned by the correct user will show the message, the rest won't

if [ -e $basedir/locale/$LANG ]; then
MSG="$K: /bin/sed -n "$LINE"p /usr/local/kidtimer/locale/$LANG"
else
MSG="$K's computer time will end in $TEXT minutes."
fi

for (( D = 0 ; D < 10 ; D = D + 1 )); do
echo "/bin/su $K -c 'DISPLAY=:$D /usr/bin/notify-send -i /usr/local/kidtimer/icons/kidtimer-$TEXT.png
"ALERT" "$MSG"'" >> $TMPFILE
done

echo "/bin/rm -f $TMPFILE" >> $TMPFILE
echo "/bin/bash $TMPFILE" | /usr/bin/at now + $T minutes

Note you need the file /var/spool/cron/atjobs/.SEQ owned by daemon:daemon for at to work properly!!!

}

go_killall () {
K=$1
/bin/rm -f /tmp/killall.bash.$K
touch /tmp/killall.bash.$K
chmod +x /tmp/killall.bash.$K

echo "#!/bin/bash" > /tmp/killall.bash.$K

Not sure if it is better or worse to do a normal kill first...

Programs like Firefox will reopen all their previous tabs if it is killed with -KILL

But other programs would probably do better if given a chance to save state/data and clean up after themselves eg open office???

#echo "/usr/bin/pkill -TERM -u $K" >> /tmp/killall.bash.$K
#echo "/bin/sleep 5" >> /tmp/killall.bash.$K
echo "/usr/bin/pkill -KILL -u $K" >> /tmp/killall.bash.$K
echo "/bin/rm -rf /tmp/kidtimer.shutdown.$K" >> /tmp/killall.bash.$K
echo "/bin/rm -f /tmp/killall.bash.$K" >> /tmp/killall.bash.$K
echo "/bin/bash /tmp/killall.bash.$K" | /usr/bin/at now + 6 minutes
}

go_echo_error () {
echo "/bin/sed -n '12p' /usr/local/kidtimer/locale/$LANG"
}

go_echo_done () {
echo "/bin/sed -n '13p' /usr/local/kidtimer/locale/$LANG"
}

go_reset_time () {
get_ttl_time $KID > $basedir/time/$KID.ttl
/usr/bin/passwd $KID -u
go_clean_jobs $KID
go_echo_done
}

go_addtime () {
U=$KID
A=$Time
grep -q $U $configdir/kid.list
if [ $? -eq 0 ]; then
if [ "$A" == "reset" ]; then
get_ttl_time $U > $basedir/time/$U.ttl
go_echo_done
exit 0
elif [ "$A" == "" ]; then
go_echo_error
#english: Syntax: addtime <minutes|reset>
echo "/bin/sed -n '22p' /usr/local/kidtimer/locale/$LANG"
exit 1
else
C=/bin/cat $basedir/time/$KID.ttl
C=$((C + Time))
echo $C > $basedir/time/$KID.ttl
get_time
fi
/usr/bin/passwd $KID -u
go_clean_jobs $KID
else
go_echo_error
#english: User not setup.
echo "/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG"
exit 1
fi
}

get_time () {
if [ -e $basedir/time/$KID.ttl ]; then
cat $basedir/time/$KID.ttl
else
echo
#english: User not setup.
echo "/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG"
echo
fi
}

go_tui () {
go_command_list
echo -n "Choose: "; read X
case "$X" in

  1. go_setup_user
    ;;
  2. go_modify_user
    ;;
  3. go_remove_user
    ;;
  4. go_list_users
    ;;
  5. exit 0
    ;;
    esac
    go_tui
    }

go_command_list () {
echo
echo "1) "/bin/sed -n '7p' /usr/local/kidtimer/locale/$LANG
echo "2) "/bin/sed -n '8p' /usr/local/kidtimer/locale/$LANG
echo "3) "/bin/sed -n '9p' /usr/local/kidtimer/locale/$LANG
echo "4) "/bin/sed -n '10p' /usr/local/kidtimer/locale/$LANG
echo "5) "/bin/sed -n '11p' /usr/local/kidtimer/locale/$LANG
echo
}

go_list_users () {
echo
#english: Users configured for kidtimer:
echo "/bin/sed -n '14p' /usr/local/kidtimer/locale/$LANG"
if [ -s $configdir/kid.list ]; then
/bin/cat $configdir/kid.list
else
#english: No configured users.
echo "/bin/sed -n '15p' /usr/local/kidtimer/locale/$LANG"
echo
fi
}

go_setup_user () {
echo
#english: Username:
echo -n "/bin/sed -n '16p' /usr/local/kidtimer/locale/$LANG "; read U
/usr/bin/id $U > /dev/null 2>&1
if [ $? -eq 0 ]; then
/bin/cp $basedir/schedule/blank $basedir/schedule/$U
get_ttl_time $U > $basedir/time/$U.ttl
echo $U >> $configdir/kid.list
go_echo_done
echo
#english: Modify limits now ?(y/n):
echo -n "/bin/sed -n '17p' /usr/local/kidtimer/locale/$LANG"; read M
if [ "$M" == "y" ]; then
if [ -e /usr/bin/nano ]; then
/usr/bin/nano $basedir/schedule/$U
get_ttl_time $U > $basedir/time/$U.ttl
go_echo_done
else
/usr/bin/vi $basedir/schedule/$U
get_ttl_time $U > $basedir/time/$U.ttl
go_echo_done
fi
fi
else
go_echo_error
#english: User does not exist. Please create user using the useradd command first.
echo "/bin/sed -n '18p' /usr/local/kidtimer/locale/$LANG"
fi
}

go_modify_user () {
echo
#english: Username:
echo -n "/bin/sed -n '16p' /usr/local/kidtimer/locale/$LANG "; read U
grep -q ^$U $configdir/kid.list
if [ $? -eq 0 ]; then
if [ -e /usr/bin/nano ]; then
/usr/bin/nano $basedir/schedule/$U
go_echo_done
else
/usr/bin/vi $basedir/schedule/$U
go_echo_done
fi
else
go_echo_error
#english: User not setup.
echo "/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG"
echo
fi
}

go_remove_user () {
echo
#english: Username:
echo -n "/bin/sed -n '16p' /usr/local/kidtimer/locale/$LANG "; read U
grep -q ^$U $configdir/kid.list
if [ $? -eq 0 ]; then
/bin/grep -v ^$U $configdir/kid.list > /tmp/kidtimer.tmp
/bin/cat /tmp/kidtimer.tmp > $configdir/kid.list
go_echo_done
else
go_echo_error
#english: User not setup.
echo "/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG"
echo
fi
}

go_help () {
echo
echo "Commands:"
echo "--------------------------------------------------------------------------------"
echo "addtime ... Increases allowed time for the day."
echo "gettime ... Prints remaining time for the day."
echo "reset ... Reset time for the day."
echo "logout ... Starts logout sequence for user."
echo "hourly ... Enables/disables user access based on the schedule."
echo "daily ... Resets time for the new day."
echo "update ... Updates kidtimer to the latest version."
echo "info ... Gather local configurations to troubleshoot issues."
echo "help ... This list."
echo "--------------------------------------------------------------------------------"
}

check_dependencies () {
#at
P1="";P2="";P3="";pstatus="0"
/usr/bin/dpkg -s at >/dev/null 2>/dev/null
if [ ! $? -eq 0 ]; then
P1="at"
pstatus=1
fi
#libnotify-bin
/usr/bin/dpkg -s libnotify-bin >/dev/null 2>/dev/null
if [ ! $? -eq 0 ]; then
P2="libnotify-bin"
pstatus=1
fi
#bsdutils
/usr/bin/dpkg -s bsdutils >/dev/null 2>/dev/null
if [ ! $? -eq 0 ]; then
P3="bsdutils"
pstatus=1
fi
if [ "$pstatus" == "1" ]; then
echo
#english: Error. Missing package dependencies.
echo "/bin/sed -n '20p' /usr/local/kidtimer/locale/$LANG"
#english: Please install using the following line;
echo "/bin/sed -n '21p' /usr/local/kidtimer/locale/$LANG"
echo "sudo apt-get install "$P1" "$P2" "$P3
exit 1
fi
}

get_update () {
URL='https://github.com/grover66/kidtimer/raw/master/DEBS/kidtimer_latest.deb'
if [ -e /usr/bin/wget ]; then
/usr/bin/wget "$URL" -qO /tmp/kidtimer_latest.deb
/usr/bin/dpkg -i /tmp/kidtimer_latest.deb
/bin/rm -f /tmp/kidtimer_latest.deb
else
echo
echo "Error. Requires wget"
echo "To install wget; sudo apt-get install wget"
fi
}

###################### Code ####################
################################################

if [ $TUI -eq 1 ]; then
check_dependencies
go_tui
fi

case "$COMMAND" in
addtime) go_addtime
;;
reset) go_reset_time
;;
gettime) get_time
;;
logout) go_logout $KID
;;
hourly) go_hourly
;;
daily) go_daily
;;
update) get_update
;;
check) go_check
;;
info) get_info
;;
-h) go_help
;;
help) go_help
;;
esac
exit 0

Thanks,
StoicTheVast


Reply to this email directly or view it on GitHub
#20.

@StoicTheVast
Copy link
Author

Yes, sorry if I didn't make that clear - I was just trying to document for
other people who would be trying to get it to work for them :).

To be clear, on my system I had to do:

  • create the .SEQ directory
  • change ownership to daemon:daemon

But without that there is no indication - it will fail silently.

Hey maybe your dependency check routine could ascertain whether at is
functioning or not????

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

No branches or pull requests

2 participants