-
Notifications
You must be signed in to change notification settings - Fork 23
Open
Description
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
Metadata
Metadata
Assignees
Labels
No labels