diff --git a/CODE/DEBIAN/control b/CODE/DEBIAN/control index 15e6e09..3d22037 100644 --- a/CODE/DEBIAN/control +++ b/CODE/DEBIAN/control @@ -1,10 +1,10 @@ Package: kidtimer -Version: 2.3-1 +Version: 1.0-0 Section: utils Priority: optional Architecture: all Depends: at, libnotify-bin, bsdutils Installed-Size: 8 -Maintainer: Michael Groves -Homepage: http://github.com/grover66/kidtimer +Maintainer: Jim Dunphy +Homepage: http://github.com/JimDunphy/kidtimer Description: Set daily limits on computer time. diff --git a/CODE/etc/cron.d/kidtimer b/CODE/etc/cron.d/kidtimer index aa005b9..fdd3aca 100644 --- a/CODE/etc/cron.d/kidtimer +++ b/CODE/etc/cron.d/kidtimer @@ -1,4 +1,4 @@ -0 * * * * root /usr/local/bin/kidtimer hourly -0 0 * * * root /usr/local/bin/kidtimer daily +#0 * * * * root /usr/local/bin/kidtimer hourly +#0 0 * * * root /usr/local/bin/kidtimer daily * * * * * root /usr/local/bin/kidtimer check -@reboot root /usr/local/bin/kidtimer daily +#@reboot root /usr/local/bin/kidtimer daily diff --git a/CODE/usr/local/bin/99zzkidtimer.sh b/CODE/usr/local/bin/99zzkidtimer.sh new file mode 100644 index 0000000..c5ce892 --- /dev/null +++ b/CODE/usr/local/bin/99zzkidtimer.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +# This script makes kidtimer update account status when the machine is woken up. +# For a laptop, these are the closest parallels to turning on a desktop. +# /usr/lib/pm-utils/sleep.d --- so kidtimer will run when my computer is +# awoken from sleep + +case $1 in + resume|thaw) + /usr/local/bin/kidtimer check + ;; +esac diff --git a/CODE/usr/local/bin/README.JAD b/CODE/usr/local/bin/README.JAD new file mode 100644 index 0000000..b817301 --- /dev/null +++ b/CODE/usr/local/bin/README.JAD @@ -0,0 +1,5 @@ +Original from: +https://github.com/grover66/kidtimer + +# provided the print out to my 10 year old to see if she could find holes +enscript --pretty-print --color -2r -MLetter -PHP-Officejet-Pro-8600 kidtimer diff --git a/CODE/usr/local/bin/TODO b/CODE/usr/local/bin/TODO new file mode 100644 index 0000000..b565a49 --- /dev/null +++ b/CODE/usr/local/bin/TODO @@ -0,0 +1,2 @@ +kids time down every 60mins +code to get more time diff --git a/CODE/usr/local/bin/kidtimer b/CODE/usr/local/bin/kidtimer index 0384d34..b276822 100755 --- a/CODE/usr/local/bin/kidtimer +++ b/CODE/usr/local/bin/kidtimer @@ -25,6 +25,56 @@ WEEKEND="no" ################# Subroutines ################## ################################################ +# +# %%% not used yet +# +ttl_decr () { + min=$1 + + if [ $C -le 0 ]; then + min=0 + else + min=$((min - 1)) + fi +} + +# +# Decriment ttl (time to live) in mins and return current time left +# +set_ttl () { + kid=$1 + min=$2 + + #more time + mtime=`date +%s` + mfile=$(expr $mtime / 10000) + if [ -e /tmp/$mfile ]; then + /bin/rm -f /tmp/$mfile + reset_time $kid + return + fi + + + # Normal mode is to decriment a minute + if [ -e $basedir/time/$kid.ttl ]; then + min=`/bin/cat $basedir/time/$kid.ttl` + if [ $min -le 0 ]; then + min=0 + else + min=$((min - 1)) + fi + + echo $min > $basedir/time/$kid.ttl + +#first time for a user, create file, and set default ttl + else + get_ttl_time $kid > $basedir/time/$kid.ttl + min=`/bin/cat $basedir/time/$kid.ttl` + fi + + return 0 +} + get_info () { echo "kidtimer info" echo "---------------------------------------------" @@ -64,27 +114,31 @@ echo go_check () { -for I in `/bin/cat $configdir/kid.list | sort -u`; do - /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)) - 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 - if [ ! -e /tmp/kidtimer.shutdown.$I ]; then - /usr/bin/passwd $I -l - go_logout $I - fi - fi - else - go_clean_jobs $I - fi +for kid in `/bin/cat $configdir/kid.list | sort -u`; do + + # New day means we reset the ttl + /usr/bin/stat -c %y $basedir/time/$kid.ttl | /bin/grep -q $Cdate + if [ ! $? -eq 0 ]; then + reset_time $kid + return + fi + + # This method ( /usr/bin/users ) to determine who is logged in is a kludge. + # It ignores locked screens (screensaver active) and switched user states. + # change ttl for logged in user and return remaining time + /usr/bin/users | /bin/grep -q $kid + if [ $? -eq 0 ]; then + + set_ttl $kid $min + + # Are they allowed to use computer during this hour + hourlyCheck $kid $allowed + + # check time, lock account, and start stutdown procedure, queue up jobs + if [ $min -le 5 ] || [ $allowed -eq 0 ]; then + go_logout $kid + fi + fi done } @@ -98,37 +152,101 @@ get_ttl_time () { 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 + /usr/bin/at -d $I +# /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 +[ -e /tmp/kidtimer.send1.bash.$K ] && /bin/rm -rf /tmp/kidtimer.send1.bash.$K +[ -e /tmp/kidtimer.send2.bash.$K ] && /bin/rm -rf /tmp/kidtimer.send2.bash.$K +[ -e /tmp/kidtimer.send3.bash.$K ] && /bin/rm -rf /tmp/kidtimer.send3.bash.$K +[ -e /tmp/kidtimer.send4.bash.$K ] && /bin/rm -rf /tmp/kidtimer.send4.bash.$K +[ -e /tmp/kidtimer.send5.bash.$K ] && /bin/rm -rf /tmp/kidtimer.send5.bash.$K +[ -e /tmp/killall.bash.$K ] && /bin/rm -rf /tmp/killall.bash.$K } +# From command line only.... check now handles this autmoatically... don't need +# to run from cron +# +# runs once per day and resets ttl back for kid +# %%% what happens if machine is suspended and it missed its chance to run? +# 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 + #reset ttl for next day if [ ! $? -eq 0 ]; then - get_ttl_time $I > $basedir/time/$I.ttl + reset_time $I fi done -go_hourly +# %%% needs to be rethought +#go_hourly } +# +# Determine if allowed to use computer during this schedule +# +hourlyCheck () { +kid=$1 +allowed=$2 +min=1 #non zero value is all we need + + if [ -e $basedir/schedule/$kid ]; then + + #grab mins left + [ -e $basedir/time/$kid.ttl ] && min=`/bin/cat $basedir/time/$kid.ttl` + #lock account if out of minutes + +#%%% bug should return and not exit on 0 + if [ $min -le 0 ] ; then + #/usr/bin/passwd $kid -l + allowed=0 + return 0 + fi + + # Disable/Enable depending on if allowed during time of day + [ "$WEEKEND" == "no" ] && R=`/bin/grep ^$HOUR $basedir/schedule/$kid | /usr/bin/awk '{ print $2 }'` + [ "$WEEKEND" == "yes" ] && R=`/bin/grep ^$HOUR $basedir/schedule/$kid | /usr/bin/awk '{ print $3 }'` + if [ "$R" == "y" ]; then + allowed=1 # allowed to use computer + else + allowed=0 # not allowed to use computer + fi + + fi + + + return 0 +} + 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 +# %%% if ttl has gone negative, we lock password file and exit???? + #[ $C -le 0 ] && /usr/bin/passwd $I -l && exit 0 + [ $C -le 0 ] && 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 - /usr/bin/users | /bin/grep -q $I && /usr/local/bin/kidtimer shutdown $I + +# should gnome-screensaver-command -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 this file exists, do no issue logout sequence + if [ ! -e /tmp/kidtimer.shutdown.$I ]; then + go_logout $I + fi + fi fi fi done @@ -137,14 +255,24 @@ 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 + kid=$1 + + # only start this if they are logged in + /usr/bin/users | /bin/grep -q $kid + if [ $? -ne 0 ]; then + return + fi + + # schedule the shutdown if we haven't already + if [ ! -e /tmp/kidtimer.shutdown.$kid ]; then + go_send $kid 5 + go_send $kid 4 + go_send $kid 3 + go_send $kid 2 + go_send $kid 1 + go_killall $kid + /usr/bin/touch /tmp/kidtimer.shutdown.$kid + fi } @@ -155,27 +283,53 @@ 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 -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 \ - \"ALERT\" \"$MSG\"'" >> $TMPFILE + +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 +#/usr/bin/passwd $K -l + echo "#!/bin/bash" > /tmp/killall.bash.$K -echo "/usr/bin/pkill -KILL -u $K" >> /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 +# +# should do gnome-screensaver-command -l and passwd -l here +# if user hibernates first and resumes, logic will continue up resumption 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 "/usr/bin/pkill -KILL -u $K" >> /tmp/killall.bash.$K +echo "/usr/sbin/pm-hibernate" >> /tmp/killall.bash.$K echo "/bin/bash /tmp/killall.bash.$K" | /usr/bin/at now + 6 minutes } @@ -189,54 +343,58 @@ go_echo_done () { echo "`/bin/sed -n '13p' /usr/local/kidtimer/locale/$LANG`" } +reset_time () { +KID=$1 -go_reset_time () { -get_ttl_time $KID > $basedir/time/$KID.ttl -/usr/bin/passwd $KID -u -go_clean_jobs $KID -go_echo_done +# make sure this kid is in the list to begin with +grep -q $KID $configdir/kid.list +if [ $? -eq 0 ]; then + get_ttl_time $KID > $basedir/time/$KID.ttl + /usr/bin/passwd $KID -u + go_clean_jobs $KID + go_echo_done +fi } - 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 - 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 +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 + 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 + 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 + cat $basedir/time/$KID.ttl else - echo - #english: User not setup. - echo "`/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG`" - echo + echo + #english: User not setup. + echo "`/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG`" + echo fi } @@ -278,9 +436,9 @@ 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. + #english: No configured users. echo "`/bin/sed -n '15p' /usr/local/kidtimer/locale/$LANG`" - echo + echo fi } @@ -291,26 +449,26 @@ 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 + get_ttl_time $U > $basedir/time/$U.ttl echo $U >> $configdir/kid.list go_echo_done echo - #english: Modify limits now ?(y/n): + #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 + /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. + 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 } @@ -330,10 +488,10 @@ if [ $? -eq 0 ]; then go_echo_done fi else - go_echo_error - #english: User not setup. + go_echo_error + #english: User not setup. echo "`/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG`" - echo + echo fi } @@ -348,10 +506,10 @@ if [ $? -eq 0 ]; then /bin/cat /tmp/kidtimer.tmp > $configdir/kid.list go_echo_done else - go_echo_error - #english: User not setup. + go_echo_error + #english: User not setup. echo "`/bin/sed -n '19p' /usr/local/kidtimer/locale/$LANG`" - echo + echo fi } @@ -377,8 +535,8 @@ check_dependencies () { P1="";P2="";P3="";pstatus="0" /usr/bin/dpkg -s at >/dev/null 2>/dev/null if [ ! $? -eq 0 ]; then - P1="at" - pstatus=1 + P1="at" + pstatus=1 fi #libnotify-bin /usr/bin/dpkg -s libnotify-bin >/dev/null 2>/dev/null @@ -393,26 +551,26 @@ if [ ! $? -eq 0 ]; then 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 + 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 + /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" + echo + echo "Error. Requires wget" + echo "To install wget; sudo apt-get install wget" fi } @@ -421,14 +579,14 @@ fi ################################################ if [ $TUI -eq 1 ]; then - check_dependencies - go_tui + check_dependencies + go_tui fi case "$COMMAND" in addtime) go_addtime ;; -reset) go_reset_time +reset) reset_time $KID ;; gettime) get_time ;; diff --git a/CODE/usr/local/sbin/generate_key.sh b/CODE/usr/local/sbin/generate_key.sh new file mode 100755 index 0000000..d4eae9b --- /dev/null +++ b/CODE/usr/local/sbin/generate_key.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# Generate key to give to Samantha for more time + +time=`date +%s` +file=$(expr $time / 10000) + +echo $file + diff --git a/DEBS/kidtimer.deb b/DEBS/kidtimer.deb new file mode 100644 index 0000000..f71351b Binary files /dev/null and b/DEBS/kidtimer.deb differ diff --git a/DEBS/kidtimer_2.2-10.deb b/DEBS/kidtimer_2.2-10.deb deleted file mode 100644 index 61badfa..0000000 Binary files a/DEBS/kidtimer_2.2-10.deb and /dev/null differ diff --git a/DEBS/kidtimer_2.2-11.deb b/DEBS/kidtimer_2.2-11.deb deleted file mode 100644 index 5769f68..0000000 Binary files a/DEBS/kidtimer_2.2-11.deb and /dev/null differ diff --git a/DEBS/kidtimer_2.2-12.deb b/DEBS/kidtimer_2.2-12.deb deleted file mode 100644 index 3d189f5..0000000 Binary files a/DEBS/kidtimer_2.2-12.deb and /dev/null differ diff --git a/DEBS/kidtimer_2.2-13.deb b/DEBS/kidtimer_2.2-13.deb deleted file mode 100644 index a82e50f..0000000 Binary files a/DEBS/kidtimer_2.2-13.deb and /dev/null differ diff --git a/DEBS/kidtimer_2.2-14.deb b/DEBS/kidtimer_2.2-14.deb deleted file mode 100644 index 9f0d5f3..0000000 Binary files a/DEBS/kidtimer_2.2-14.deb and /dev/null differ diff --git a/DEBS/kidtimer_2.3-1.deb b/DEBS/kidtimer_2.3-1.deb deleted file mode 100644 index 0d23aa2..0000000 Binary files a/DEBS/kidtimer_2.3-1.deb and /dev/null differ diff --git a/DEBS/kidtimer_2.3.deb b/DEBS/kidtimer_2.3.deb deleted file mode 100644 index 5b810ce..0000000 Binary files a/DEBS/kidtimer_2.3.deb and /dev/null differ diff --git a/DEBS/kidtimer_latest.deb b/DEBS/kidtimer_latest.deb deleted file mode 100644 index 0d23aa2..0000000 Binary files a/DEBS/kidtimer_latest.deb and /dev/null differ diff --git a/README.md b/README.md index 6aa52fd..42f12f0 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,57 @@ +# Kidtimer + +# Purpose: +Set limits on computer time per day + +# Operation: +This shell script runs from cron every minute to decrement the number of minutes your child has been using their computer. If your child has spent more time on their computer then you have allowed, it will queue up 5 warning messages and a hibernate via the 'at' command. As 'at' executes these commands, it will show a message on the users window and count down the number of minutes before issuing the hibernate. The child can watch via... kidtimer gettime user or watch the /tmp directory. I find my 10 year old likes to issue commands from the shell as it seems more "hacker like"... I have provided her with a print out and told her that she could defeat it if she looks hard enough. I will fix these as she discovers them... currently it is fairly easy to defeat which makes it more intersting for her from my perspective as a teaching aid. + +Modified to do the following. + +1) changed so that we issue a pm-hibernate instead of killing the session. The kill proved to destroy her work which seemed unfair. + +2) check every minute instead of hourly, daily, minute, etc to make this more robust. Change the logic to handle this in one place. Thus all we need is a single crontab entry that does a kidtimer check. Found there were a few places that allowed her to get more time once we moved to the hibernate model from kill. + +3) added the ability to provide a file that can provide more time. My daughter comes to me and I issue her a key that she then uses by issuing a: touch /tmp/key to get more time... When kidtimer see's that file, it will issue a 'reset' and provide her another session. + +The key can be created with command +```bash +% generate_key.sh +``` + +Here is how we use it. We check every minute given we use suspend. +```bash +% cat /etc/cron.d/kidtimer +* * * * * root /usr/local/bin/kidtimer check +``` + +I liked to view when she brought the machine up or down and tracked that via this method: + +```bash +% cat /etc/pm/sleep.d/0_jad + +#!/bin/bash + +case "$1" in +suspend|hibernate) + /usr/bin/logger -n swlogger.example.com -p auth.info NETWORK Samy off + ;; +resume|thaw) + /usr/bin/logger -n swlogger.example.com -p auth.info NETWORK Samy on + ;; +esac +``` + +Where swlogger.example.com was a syslog server that is running swatch. You can substitute logger with mail, texing, etc. + +Please notify me should you find bugs as the original author is not responsible for my errors. I have left or not fixed some of the code as it currently isn't used by us .. ie. kidtimer check is all we do. + +# NOTE: +I have not rebuilt the DEB package version of kidtimer. The version listed in the top directory here is the latest working version. You can copy it into place. No other options are required via cron with this version... just run 'kidtimer check' which should run every minute as shown above. + + +# Original text below from grover66. + I have been looking for a solution to this for a long time. I have two kids and wish to restrict their time on the computers. Since nothing else seems to work out there, I decided to write my own. I call it kidtimer. Very original, I know. :) Kidtimer should work on any recent version of Ubuntu, both 32 & 64 bit. Should also work on Debian too. It uses standard Linux utilities and notify-send (for user notification). diff --git a/kidtimer b/kidtimer new file mode 100755 index 0000000..b276822 --- /dev/null +++ b/kidtimer @@ -0,0 +1,610 @@ +#!/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 ################## +################################################ + +# +# %%% not used yet +# +ttl_decr () { + min=$1 + + if [ $C -le 0 ]; then + min=0 + else + min=$((min - 1)) + fi +} + +# +# Decriment ttl (time to live) in mins and return current time left +# +set_ttl () { + kid=$1 + min=$2 + + #more time + mtime=`date +%s` + mfile=$(expr $mtime / 10000) + if [ -e /tmp/$mfile ]; then + /bin/rm -f /tmp/$mfile + reset_time $kid + return + fi + + + # Normal mode is to decriment a minute + if [ -e $basedir/time/$kid.ttl ]; then + min=`/bin/cat $basedir/time/$kid.ttl` + if [ $min -le 0 ]; then + min=0 + else + min=$((min - 1)) + fi + + echo $min > $basedir/time/$kid.ttl + +#first time for a user, create file, and set default ttl + else + get_ttl_time $kid > $basedir/time/$kid.ttl + min=`/bin/cat $basedir/time/$kid.ttl` + fi + + return 0 +} + +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 kid in `/bin/cat $configdir/kid.list | sort -u`; do + + # New day means we reset the ttl + /usr/bin/stat -c %y $basedir/time/$kid.ttl | /bin/grep -q $Cdate + if [ ! $? -eq 0 ]; then + reset_time $kid + return + fi + + # This method ( /usr/bin/users ) to determine who is logged in is a kludge. + # It ignores locked screens (screensaver active) and switched user states. + # change ttl for logged in user and return remaining time + /usr/bin/users | /bin/grep -q $kid + if [ $? -eq 0 ]; then + + set_ttl $kid $min + + # Are they allowed to use computer during this hour + hourlyCheck $kid $allowed + + # check time, lock account, and start stutdown procedure, queue up jobs + if [ $min -le 5 ] || [ $allowed -eq 0 ]; then + go_logout $kid + fi + 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 -d $I +# /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 +[ -e /tmp/kidtimer.send1.bash.$K ] && /bin/rm -rf /tmp/kidtimer.send1.bash.$K +[ -e /tmp/kidtimer.send2.bash.$K ] && /bin/rm -rf /tmp/kidtimer.send2.bash.$K +[ -e /tmp/kidtimer.send3.bash.$K ] && /bin/rm -rf /tmp/kidtimer.send3.bash.$K +[ -e /tmp/kidtimer.send4.bash.$K ] && /bin/rm -rf /tmp/kidtimer.send4.bash.$K +[ -e /tmp/kidtimer.send5.bash.$K ] && /bin/rm -rf /tmp/kidtimer.send5.bash.$K +[ -e /tmp/killall.bash.$K ] && /bin/rm -rf /tmp/killall.bash.$K +} + + +# From command line only.... check now handles this autmoatically... don't need +# to run from cron +# +# runs once per day and resets ttl back for kid +# %%% what happens if machine is suspended and it missed its chance to run? +# +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 + #reset ttl for next day + if [ ! $? -eq 0 ]; then + reset_time $I + fi +done +# %%% needs to be rethought +#go_hourly +} + + +# +# Determine if allowed to use computer during this schedule +# +hourlyCheck () { +kid=$1 +allowed=$2 +min=1 #non zero value is all we need + + if [ -e $basedir/schedule/$kid ]; then + + #grab mins left + [ -e $basedir/time/$kid.ttl ] && min=`/bin/cat $basedir/time/$kid.ttl` + #lock account if out of minutes + +#%%% bug should return and not exit on 0 + if [ $min -le 0 ] ; then + #/usr/bin/passwd $kid -l + allowed=0 + return 0 + fi + + # Disable/Enable depending on if allowed during time of day + [ "$WEEKEND" == "no" ] && R=`/bin/grep ^$HOUR $basedir/schedule/$kid | /usr/bin/awk '{ print $2 }'` + [ "$WEEKEND" == "yes" ] && R=`/bin/grep ^$HOUR $basedir/schedule/$kid | /usr/bin/awk '{ print $3 }'` + if [ "$R" == "y" ]; then + allowed=1 # allowed to use computer + else + allowed=0 # not allowed to use computer + fi + + fi + + + return 0 +} + +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` +# %%% if ttl has gone negative, we lock password file and exit???? + #[ $C -le 0 ] && /usr/bin/passwd $I -l && exit 0 + [ $C -le 0 ] && 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 + +# should gnome-screensaver-command -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 this file exists, do no issue logout sequence + if [ ! -e /tmp/kidtimer.shutdown.$I ]; then + go_logout $I + fi + fi + fi + fi + done +fi +} + + +go_logout () { + kid=$1 + + # only start this if they are logged in + /usr/bin/users | /bin/grep -q $kid + if [ $? -ne 0 ]; then + return + fi + + # schedule the shutdown if we haven't already + if [ ! -e /tmp/kidtimer.shutdown.$kid ]; then + go_send $kid 5 + go_send $kid 4 + go_send $kid 3 + go_send $kid 2 + go_send $kid 1 + go_killall $kid + /usr/bin/touch /tmp/kidtimer.shutdown.$kid + fi +} + + +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 +#/usr/bin/passwd $K -l + +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 +# +# should do gnome-screensaver-command -l and passwd -l here +# if user hibernates first and resumes, logic will continue up resumption +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 "/usr/bin/pkill -KILL -u $K" >> /tmp/killall.bash.$K +echo "/usr/sbin/pm-hibernate" >> /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`" +} + +reset_time () { +KID=$1 + +# make sure this kid is in the list to begin with +grep -q $KID $configdir/kid.list +if [ $? -eq 0 ]; then + get_ttl_time $KID > $basedir/time/$KID.ttl + /usr/bin/passwd $KID -u + go_clean_jobs $KID + go_echo_done +fi +} + +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 + 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) reset_time $KID + ;; +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