Monday, July 27, 2009

Uptime and battery state log

Here I go, posting two days in a row.

I got one of those new-fangled Asus Eee PCs recently (with Xandros and not the other thing, obviously) and, being the paranoid freak that I am, I needed to check out how long the battery lasts. And I needed to do that every single time I used it.

So I mashed up a little script for it, and then started adding functionality to it with almost every run, and it sort of grew. So I recently added the traditional getopt stuff, and a license and some basic error checking, and decided to post it.

It needs acpi and uptime to run at all, and if you want to print stuff out to stdout as well, then you need tee.

It should be pretty straightforward to use, but here's an overview anyway.

First, you gotta make sure that the directory for the logs already exists, or that the script will be able to create it on its own. Personally, I do the first one. The default directory is /var/log/uptimed, so you would go:

sudo mkdir -p /var/log/uptimed
sudo chmod a+rw /var/log/uptimed


And basically, you're all set to go. Now you can run it by simply going:

./uptimed &


Or you can use one of the many available options, like adding a prefix, a suffix and printing the output to screen as well as the log file:

./uptimed -s -c "some suffix" -p "some prefix" &


Enough rambling, here's the code.
 
1 #!/bin/bash
2 #
3 # uptimed (uptime daemon)
4 #
5 # Checks uptime and some acpi settings from time to time and logs
6 # that information in a specified file. It's designed to be run often.
7 #
8 # Parameters:
9 # -t | --sleep-time time between checks (default: 6 minutes)
10 # -s | --stdout write info to stdout as well as the file
11 # -f | --file write to a log file with a name like this one
12 # -e | --extension specify an extensionto the log file
13 # -u | --no-uptime DON'T log current uptime
14 # -b | --no-battery DON'T log batery charge
15 # -d | --no-thermal DON'T log current temperature
16 # -a | --all log uptime, battery and thermal (default)
17 # -c | --comment append a comment to each line (default: empty)
18 # -p | --prefix prepend a comment to each line (default: empty)
19 #
20 # Requires:
21 # uptime
22 # acpi
23 # tee
24 #
25 # Author:
26 # Konrad Siek <konrad.siek@gmail.com>
27 #
28 # License information:
29 #
30 # This program is free software: you can redistribute it and/or modify
31 # it under the terms of the GNU General Public License as published by
32 # the Free Software Foundation, either version 3 of the License, or
33 # (at your option) any later version.
34 #
35 # This program is distributed in the hope that it will be useful,
36 # but WITHOUT ANY WARRANTY; without even the implied warranty of
37 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38 # GNU General Public License for more details.
39 #
40 # You should have received a copy of the GNU General Public License
41 # along with this program. If not, see <http://www.gnu.org/licenses/>.
42 #
43 # Copyright 2009 Konrad Siek
44
45 # Presets
46 sleep_time=360
47 echo_stdin=false #true
48 filename=/var/log/uptimed/uptime
49 extension=`date +%Y%m%d`
50 uptime_on=true
51 battery_on=true
52 thermal_on=true
53 comment=""
54
55 # Retrieve program parameters
56 options=$( \
57 getopt -o t:s::f:e:ubdac:p: \
58 --long sleep-time:,stdout::,file:,extension:,\
59 no-uptime,no-battery,no-thermal,all,comment:,prefix \
60 -n $0 -- "$@" \
61 )
62
63 # Check if getopts worked as it should
64 [ $? != 0 ] &&
65 exit 1
66
67 # Iterate over parameters and set up the program
68 eval set -- "$options"
69 while true
70 do
71 case "$1" in
72 -t|--sleep-time)
73 sleep_time=$2
74 shift 2
75 ;;
76 -f|--file)
77 filename=$2
78 shift 2
79 ;;
80 -e|--extension)
81 extension=$2
82 shift 2
83 ;;
84 -s|--stdout)
85 if [ "$2" != "" ]
86 then
87 echo_stdin=$2
88 else
89 echo_stdin=true
90 fi
91 shift 2
92 ;;
93 -u|--no-uptime)
94 uptime_on=$false
95 shift 1
96 ;;
97 -b|--no-battery)
98 battery_on=$false
99 shift 1
100 ;;
101 -d|--no-thermal)
102 thermal_on=$false
103 shift 1
104 ;;
105 -c|--comment)
106 comment="$2"
107 shift 2
108 ;;
109 -p|--prefix)
110 prefix="[$2] "
111 shift 2
112 ;;
113 -a|--all)
114 uptime_on=true
115 battery_on=true
116 thermal_on=true
117 shift 1
118 ;;
119 --)
120 shift
121 break
122 ;;
123 *)
124 echo "This is, of course, impossible... Milliways?" >& 2
125 exit -1
126 ;;
127 esac
128 done
129
130 # Create the base directory,
131 dir=`dirname "$filename.$extension"`
132 if [ ! \( -d "$dir" \) ]
133 then
134 mkdir -p "$dir"
135 if [ $? != 0 ]
136 then
137 echo "$0:Directory does not exist and can't be created: $dir" >& 2
138 exit 1
139 fi
140 fi
141
142 # Create the log file.
143 if [ ! \( -e "$filename.$extension" \) ]
144 then
145 touch "$filename.$extension"
146 if [ $? != 0 ]
147 then
148 echo "$0:File does not exist and can't be created: $dir" >& 2
149 exit 1
150 fi
151 fi
152
153 # The main loop thing.
154 while true
155 do
156 # Gather information.
157 [ $uptime_on ] && \
158 uptime=`uptime | tr -s ' ' | cut -f 1 --complement -d ' '`
159 [ $battery_on ] && \
160 battery=`acpi -b | tr -s ' ' | cut -f 2,5,6,7 -d ' '`
161 [ $thermal_on ] && \
162 thermal=`acpi -tB | tr -s ' ' | cut -f 4,5,7 -d ' ' | tr -d ','`
163
164 # Compose the message.
165 message=""
166 [ -n "$uptime" ] && \
167 message="$message$uptime "
168 [ -n "$battery" ] && \
169 message="$message$battery "
170 [ -n "$thermal" ] && \
171 message="$message$thermal "
172 [ -n "$comment" ] && \
173 message="$message#$comment"
174 [ -n "$prefix" ] && \
175 message="$prefix$message"
176
177 # Send message to file, etc.
178 [ $echo_stdin == true ] && \
179 echo -e $message | tee -a "$filename.$extension" || \
180 echo -e $message >> "$filename.$extension"
181
182 # Wait and repeat.
183 sleep $sleep_time
184 done


The code is also available at GitHub as bash/uptimed.

Sunday, July 26, 2009

List all installed applications

There are times in a person's life when they just gotta get that brand new Ubuntu put on their computer.

For me, that inadvertently means trying to figure out what applications I forgot to install this time. Knowing life I'll be on a train somewhere, and remember that I had to do this important presentation that I forgot about and I'll have no beamer installed, or some similar situation.

So, I've been thinking that maybe I could move the list of stuff I installed on one computer to another. However, for various reasons (including technical ones) I don't want a list of all packages that are actually installed on the system - just the apps.

I figured that the gnome Add/Remove Applications thing (gnome-app-install) actually filters out a lot of the packages, and only leaves in the applications, so I took a look at how they do it, and it turns out that there are files in the system (in /usr/share/app-install/desktop), that could provide me with all the information. So I had what I needed at this juncture.

Now, what this thing does is, it goes to that directory, opens all 2000 or so .desktop files (one per application), reads the property defining what package you need to install (X-AppInstall-Package) and finally check with dpkg if that package is installed on the system, and Bob's your uncle!

Two drawbacks: (a) it's a bit slow and (b) it returns duplicate packages. I don't intend to think about fixing them because, (a) you're not gonna run this script much more often than about twice a year, so you can bloody wait 10 minutes; and (b) there are other commands to do that, and it's not like there's a lot of those duplicates anyway.

So, here's how I propose it can be used. Before installing a new version of the distro, run:
./lsapp | sort -u > applications.list

Then you can copy that file in some safe place, like a pendrive, or the partition you're not planning on wiping.

When you got your new version of the distro installed, you can copy that list somewhere and go:
cat applications.list | xargs echo | xargs sudo apt-get install

and all your stuff should attempt to be installed.

And Bob is, now and forever, your uncle.

Let me just add, that this should work for Ubuntu, but I didn't actually research if other distros store the application information in a similar manner. Hopefully, they do. I figure it's probably necessary to have gnome-app-install on there somewhere though.

Here's the code.
 
1 #!/bin/bash
2 #
3 # lsapp
4 #
5 # A simple script to list applications installed on the system.
6 #
7 # An application is simply a package that is refered to in a .desktop
8 # by the property "X-AppInstall-Package". These files are located in
9 # /usr/share/app-install/desktop.
10 #
11 # The script may take some time to run, and does not remove duplicate
12 # entries.
13 #
14 # Requires:
15 # dpkg
16 # gnome-app-install
17 # Author:
18 # Konrad Siek <konrad.siek@gmail.com>
19 #
20 # License information:
21 #
22 # This program is free software: you can redistribute it and/or modify
23 # it under the terms of the GNU General Public License as published by
24 # the Free Software Foundation, either version 3 of the License, or
25 # (at your option) any later version.
26 #
27 # This program is distributed in the hope that it will be useful,
28 # but WITHOUT ANY WARRANTY; without even the implied warranty of
29 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 # GNU General Public License for more details.
31 #
32 # You should have received a copy of the GNU General Public License
33 # along with this program. If not, see <http://www.gnu.org/licenses/>.
34 #
35 # Copyright 2009 Konrad Siek
36
37 # Directory containing .desktop files.
38 DESKTOP_DIR=/usr/share/app-install/desktop
39
40 # The property in .desktop files that gives you the actual package name.
41 PROPERTY=X-AppInstall-Package
42
43 # Iterate over all the files in that directory.
44 find "$DESKTOP_DIR" -type f | while read file
45 do
46 # Get package name from the desktop file.
47 package=`tac "$file" | \
48 awk -F '=' -v key="$PROPERTY" '$1 == key {print $2; exit}'`
49
50 # No package found, go to next file.
51 if [ -z "$package" ]
52 then
53 continue
54 fi
55
56 # Check if a package is installed.
57 dpkg -s "$package" > /dev/null 2> /dev/null
58 if [ $? == 0 ]
59 then
60 # If so, print out the name.
61 echo "$package"
62 fi
63 done


The code is also available at GitHub as bash/lsapp.

Wednesday, July 8, 2009

Commenter

I really don't have the time to write scripts recently, but this one will definitely save me some time tomorrow.

So, hey! I started this a while back when I was doing another one of those damn big Java projects, and I had to stick a bunch of generic comments to the beginning of each file, so it's (slightly more) compliant with the code convention. Alas, I didn't have the attention span to finish it then, and I just wrote the comments by hand.

But the problem reared its ugly head yet again, so I took the time to finish the script, and now I have one menial task less to do!

Here's how it works.

Supposing you've got a big Java project that needs to get those pesky comments included, you whip out the Commenter and go:

./commenter \
--author "Doctor Steve" \
--version 1.5 \
--output-dir BigJavaProject/commented \
BigJavaProject/src


And you're good to go! Every file from BigJavaProject/src will be copied (with the full package path) into BigJavaProject/commented while prepending a comment.

And, as an example, for a file called JavaParser.java, the comment will look something like:
/*
* JavaParser
*
* Version 1.5
*
* Date 8 July 2009
*
* Copyright 2009 Doctor Steve
*/


And if you want to know more, read the comments, or the entire code. I mean, if you're interested in this one, you must be a developer anyway.

Oh! and it's GPL.

And here's the code:
 
1 #!/bin/bash
2 #
3 # Commenter
4 #
5 # A simple program to include a generic c-style header comment to a
6 # Java project. The comment includes the classname, the version
7 # number and date, and the copyright notice -- as specified in the
8 # code convention for the Java language, available at:
9 # <http://java.sun.com/docs/codeconv/>
10 #
11 # Parameters:
12 # -o|--output-dir output directory for the generated files,
13 # default: current working directory, i.e., HERE;
14 # -a|--author set author for the copyright notice,
15 # default: the current system user;
16 # -y|--year set the year for the copyright notice,
17 # default: the current year;
18 # -d|--date set date for of creation for the comment,
19 # default: the current date;
20 # -v|--version set the default version for all files,
21 # default: 1.0;
22 # --verbose display additional info.
23 # Author:
24 # Konrad Siek <konrad.siek@gmail.com>
25 #
26 # License information:
27 #
28 # This program is free software: you can redistribute it and/or modify
29 # it under the terms of the GNU General Public License as published by
30 # the Free Software Foundation, either version 3 of the License, or
31 # (at your option) any later version.
32 #
33 # This program is distributed in the hope that it will be useful,
34 # but WITHOUT ANY WARRANTY; without even the implied warranty of
35 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 # GNU General Public License for more details.
37 #
38 # You should have received a copy of the GNU General Public License
39 # along with this program. If not, see <http://www.gnu.org/licenses/>.
40 #
41 # Copyright 2009 Konrad Siek
42
43 # Sets unset variables to some presets.
44 function initiate_presets() {
45 [ "$output_dir" == "" ] && output_dir=$(pwd)
46 [ "$date" == "" ] && date=$(date +'%-d %B %Y')
47 [ "$year" == "" ] && year=$(date +%Y)
48 [ "$version" == "" ] && version=1.0
49 [ "$author" == "" ] && author=$(whoami)
50 [ "$extension" == "" ] && extension=.java
51 }
52
53 # Print an error to stderr
54 # @param error message
55 function syserr() {
56 echo "$0: $@" >& 2
57 }
58
59 # Generate a single comment from a list of strings.
60 # @param zero or more strings
61 # @return c-style comment string
62 function generate_comment() {
63 echo "/*"
64 if (( $# == 0 ))
65 then
66 echo " * "
67 else
68 while (( $# > 0 ))
69 do
70 echo " * $1"
71 if (( $# > 1 ))
72 then
73 echo " * "
74 fi
75 shift
76 done
77 fi
78 echo " */"
79 }
80
81 # Copy a file with and append a generated file comment.
82 # @param absolute file path
83 function comment () {
84 comment=$( \
85 generate_comment \
86 "$(basename "$1" $extension)" \
87 "Version $version" \
88 "Date $date" \
89 "Copyright $year $author" \
90 )
91
92 package=$(dirname ${1:${#base_dir}})
93 output=$output_dir$package/$(basename "$1")
94
95 if [ $verbose ]
96 then
97 echo "Commenting file '$1' as '$output'."
98 fi
99
100 mkdir -p $(dirname "$output")
101 echo -e "$comment" | cat - "$1" > "$output"
102 }
103
104 # Apply transformation to an entire project or directory.
105 # @param absolute directory path
106 function comment_dir () {
107 base_dir="$1"
108 find "$1" -type f -name "*$extension" | \
109 while read f
110 do
111 comment "$f"
112 done
113 }
114
115 # Apply transformation to a file or directory.
116 # @param resource path
117 function comment_resource () {
118 if [ ! \( -e "$1" \) ]
119 then
120 syserr "Resource '$1' does not exist."
121 return 1
122 fi
123
124 input=$(readlink -f "$1")
125 if [ -d "$input" ]
126 then
127 comment_dir "$input"
128 else
129 base_dir=$(dirname "$input")
130 comment "$input"
131 fi
132 }
133
134 # Parse options
135 options=$( \
136 getopt \
137 -o o:v:a:d:y:e: \
138 --long output-dir:,version:,author:,date:,year:,extension:,verbose \
139 -n $0 -- "$@" \
140 )
141
142 # Stop if there's some sort of problem
143 if [ $? != 0 ]
144 then
145 syserr "Argh! Parsing went pear-shaped!"
146 exit 1
147 fi
148
149 # Set the parsed command options
150 eval set -- "$options"
151
152 # Setup selected options
153 while true
154 do
155 case "$1" in
156 -o|--output-dir)
157 output_dir="$2"
158 shift 2
159 ;;
160 -v|--version)
161 version="$2"
162 shift 2
163 ;;
164 -a|--author)
165 author="$2"
166 shift 2
167 ;;
168 -y|--year)
169 year="$2"
170 shift 2
171 ;;
172 -d|--date)
173 date="$2"
174 shift 2
175 ;;
176 -e|--extension)
177 [ ${2:0:1} == . ] && extension="$2" || extension=".$2"
178 shift 2
179 ;;
180 --verbose)
181 verbose=true
182 shift 1
183 ;;
184 --)
185 # Stop parsing options
186 shift
187 break
188 ;;
189 *)
190 # Weird error
191 syserr "The end of the world is nigh!"
192 exit 2
193 ;;
194 esac
195 done
196
197 # Process configuration
198 initiate_presets
199
200 if [ $verbose ]
201 then
202 echo "Output: $output_dir"
203 echo "Date: $date"
204 echo "Version: $version"
205 echo "Author: $author"
206 echo "Year: $year"
207 echo "Extension: $extension"
208 fi
209
210 # Process inputs
211 if [ "$#" == 0 ]
212 then
213 # If no paths were given, use the current directory.
214 comment_resource .
215 else
216 # If some paths were given, comment them each individually.
217 for arg
218 do
219 comment_resource "$arg"
220 done
221 fi


The code is also available at GitHub as bash/commenter.