Friday, May 11, 2012

Ubuntu Desktop Security Primer

You weren't born yesterday. You know what a firewall is. You know what anti-virus software is. You know what a man page is, and you've Googled more times than you can count. Let's keep this simple, shall we?

Firewall

Ubuntu includes ufw (Uncomplicated Firewall) by default. And by default, it is turned off. Turn it on:

$ sudo ufw enable
Firewall is active and enabled on system startup

Congratulations. Your computer is now wrapped in electronic cellophane.

Anti-Virus

Most viruses are still targeted at Windows, but the paranoid can never be too careful. Here is the version for those with short attention spans.

  • Install it:

    $ sudo apt-get install clamav
  • Update it:

    $ sudo freshclam
  • Run it:

    $ sudo clamscan / -ir --exclude-dir=^/sys --exclude-dir=^/proc --detect-pua

If you'd like a GUI, check out ClamTk.

Tuesday, May 1, 2012

Xubuntu Tip: Switch Users without Logging Out

I've recently installed Xubuntu on a couple of my computers, and generally I prefer it over stock Ubuntu. I had almost forgotten how nice having launchers on the desktop was. Almost. One feature I missed from Ubuntu, though, was being able to switch between different logged in users. With the default Xubuntu setup, you have to log out before you can log into a different account. Problem solved.

  • $ sudo apt-get install xfswitch-plugin
  • Add User Switching plugin to one of your panels.

Nice.

Maven 3: Default Values for Mojo Parameters that are Collections

I love my custom Maven mojos (or plugins). However, I recently ran into a situation where I needed a parameter that was a list of archive types (zip, tar, etc.), and I wanted to specify some reasonable default. Have you ever tried this by specifying a default-value or an expression? After searching for about an hour, I have come to the conclusion that the designers of Maven didn't thing this through all the way.

Not to fear though! There is a way to get this to work, and I like it so much I think I will start using the "hack" in preference to the "right way."

The trick is to specify a default value for the variable in Java. Don't use default-value or expression at all. If the user doesn't override the value, the variable doesn't get set, so it just picks up the default value you've assigned.

The second part of the trick is to document your default settings in the JavaDoc. Since you aren't specifying the value as part of the @parameter annotation, the site-doc report and help won't pick up the value you've assigned.

Here is an example.

/**
 * Types of archives to create. Any of <code>zip</code>, 
 * <code>tar</code>, <code>tar.gz</code>, <code>tar.bz2</code>.
 * <br><strong>Default</strong>: <tt>["zip", "tar.gz"]</tt>
 * @parameter 
 */
protected List<String> types = Arrays.asList("zip", "tar.gz");

One thing to note is that this will only work for constants. Any default values that have to be pulled from the project context (such as project file paths) are going to require an expression. This can't be helped. Those values have to be injected after the plugin is constructed.

Saturday, April 28, 2012

Extract a DVD to an ISO Image

I needed to copy an old DVD (playable home movies), and came across this gem. This command will extract the DVD to an ISO image, ready for burning. I don't know if it works with copy-protected DVDs, but it was exactly what I needed.

$ readom dev=/dev/cdrom f=target.iso

Friday, April 13, 2012

Shrinking a Decade's Worth of ... Part 2

The video compression (see the previous post) worked really well. It worked so well, in fact, that now my videos are much smaller, in total, than my photos.

Here we go again! Well, not exactly. You see, there is a much easier solution to batch-shrinking image files. It doesn't require any scripting at all.

Warning! mogrify modifies files in place!
Before you do this, make sure you have another copy of your image files. mogrify modifies the originals in place. JPEG is a lossy format, and recompressing the files will result in some loss of image quality. As an example of what can go wrong:

DON'T! $ mogrify -quality 7 *.jpg DON'T!

would essentially destroy your originals.

Here are the steps for Ubuntu. Windows users, you are on your own for the installation.

  • Install ImageMagick.
    $ sudo apt-get install imagemagick
  • cd to the directory with your images.
  • Assuming JPEG format, type:
    $ mogrify -quality 75 *.jpg

Wow, that was easy. I love Ubuntu!

For my images, which came from a Digital Elph, the re-compressed images take up less than half the space of the originals, and to me the images look just as good. Experiment with the quality setting and see what works best for you.

Tuesday, April 10, 2012

Shrinking a Decade's Worth of Video Clips

My family just loves to shoot video clips with our digital cameras. I must admit, I love watching old videos of the kids when they were little. But storing all those videos is becoming a nightmare. The library has grown more than the kids!

I figured I could convert the videos to some other format and save some space. I was thinking there had to be a reason why AVI isn't used for streaming web videos. I poked around on the web, and after some trial and error, came up with the following command pattern using ffmpeg.

user@computer:~/Videos$ ffmpeg -threads 0 -i movie001.AVI -b 1500K \
  -vcodec libx264 -vpre slow movie001.mp4

As it turns out, all of my cameras are really bad at on-the-fly video compression. As an example, I have a 33 second, 61MB video clip. After transcoding to mp4 it's 6.4MB. The new clip looks as good as the original. This is going to solve all my video storage problems.

Okay, so I am motivated, and I have a command that will do what I want. But I need to do this for hundreds of video clips, and my bash programming stinks. I am pretty good with one-liners. Throw in looping and awk or sed and I'm lost.

What's a Java programmer to do?

Java isn't the best scripting language. Perl and Python are great for scripting, but I use them so seldom that I am constantly looking up simple stuff. I like Beanshell, but it lacks polish for command-line programming. So how about Groovy?

Groovy syntax matches Java almost exactly (better than Beanshell) and also extends it with Ruby-esque extensions and code-scrunching syntactical sugar. In other words, I can write a concise script that I can still read in six months - when I need it again.

The Groovy script below takes a list of video files and converts them to mp4. You can modify it to allow different file types or to tweak the ffmpeg settings:

user@computer:~/Videos$ transcode.gy *.avi

Here is the full listing for transcode.gy. It's "enigmatic" Groovy (closures, syntactic sugar, etc.), but if you are comfortable with Java you won't have any trouble following along. Enjoy!

 #!/usr/bin/env groovy 
 /**
  * This software is distributed WITHOUT WARRANTIES OR CONDITIONS OF ANY
  * KIND, either express or implied. Copy it, use it, sell it, even 
  * take credit for it if you want. But don't come back to me if there
  * is a problem.
  */  
 import java.util.regex.Matcher  
 import java.util.regex.Pattern  
 List<File> files = args.collect { new File(it).canonicalFile }  
 files.each {   
     if(it.isDirectory())   
         throw new IOException("Can't process folder: " + it.path)  
     if(!it.isFile())  
         throw new FileNotFoundException(it.path) 
     /* Update this regular-expression to allow different video types. */
     if(!(it.name ==~ /(?i).*\.((avi)|(mod)|(mpeg))/))   
         throw new IOException("File type not supported: " + it.path) 
 }  
 files.each {  
     Matcher matcher = (it.name =~ /.*\.([a-zA-Z]+)/)  
     matcher.find()  
     String ext = matcher.group(1)  
     String newName = "${it.name[0..-ext.size()-2]}.mp4"  
     Process p =   
         ["ffmpeg",   
         "-threads", "0", 
             /* 0 indicates use all cores. */  
         "-i", it.canonicalPath,
         "-b", "1500K",
             /* Default 200K. You can tweak this setting to change video 
              * size and quality. */
         "-vcodec", "libx264",   
         "-vpre", "slow", 
             /* Preset file. Also default, normal, veryslow, max, hq. 
              * Search for files ending in '.ffpreset' to see what's 
              * available to you. */
         "-y", 
             /* Overwrite the .mp4 file if one already exists. */
         new File(it.parentFile, newName)].execute()  
     p.waitForProcessOutput(System.out, System.err)
     if(p.exitValue() != 0) 
         throw new Exception("Failed: ffmpeg ${p.exitValue()} ${it.path}")
 }  

Thanks to John Dyer for posting simplified command-line examples of ffmpeg for several popular video formats, including H.264.


Paul Williams pointed out in the comments that you can do much the same thing with this simple Bash script.

for i in `ls <input_dir>` 
do 
    ffmpeg \
        -threads 0 \
        -i $i \
        -b 1500K \
        -vcodec libx264 \
        -vpre slow \
        <output_dir>/`basename $i .AVI`.mp4
done

I hardly ever get to use for ... in in my common Bash tasks because most commands I use take multiple files as input, whereas ffmpeg does not. I'll have to try to remember this next time I run into a similar situation.

If I left out the error checking and comments in the Groovy script, it would be a lot shorter, but not that short. I wonder how small I could make the program if I wrote it in Java? That could be interesting...


Here's a Java version using Jaks and Apache commons-io for a cleaner implementation. Lots of standard Java boilerplate (I use Eclipse to manage that bit), and there is a Maven component to this that I'm not going to go into. Keeping in mind that I left in some error checking code, the meat of the program is comparable to the size of the Bash script, and somewhat shorter than the original Groovy script - which is mostly due to me being a bit rusty with enigmatic Groovy, I think.

 1:  import static java.util.Arrays.asList;  
 2:  import static org.apache.commons.io.FilenameUtils.getBaseName;  
 3:  import static org.apache.commons.io.FilenameUtils.getExtension;  
 4:  import java.io.File;  
 5:  import java.io.FileNotFoundException;  
 6:  import java.util.List;  
 7:  import com.googlecode.jaks.cli.AbstractJaksCommand;  
 8:  import com.googlecode.jaks.cli.JaksNonOption;  
 9:  import com.googlecode.jaks.system.Subprocess;  
10:  public class Transcode extends AbstractJaksCommand  
11:  {  
12:      @JaksNonOption(required=true)  
13:      public List<File> vids;  
14:      @Override  
15:      public void execute() throws Exception   
16:      {  
17:          for(final File vid : vids)  
18:          {  
19:              if(!asList("avi", "mod", "mpeg").contains(getExtension(vid.getName())))  
20:              {  
21:                  throw new IllegalArgumentException(vid.getPath() + " is not a convertable file type.");  
22:              }  
23:              if(!vid.isFile())  
24:              {  
25:                  throw new FileNotFoundException(vid.getPath());  
26:              }  
27:              new Subprocess("ffmpeg",   
28:                      "-threads", "0",  
29:                      "-i", vid.getCanonicalPath(),  
30:                      "-b", "1500K",  
31:                      "-vcodec", "libx264",  
32:                      "-vpre", "slow",  
33:                      "-y",  
34:                      new File(vid.getParentFile(), getBaseName(vid.getName()) + ".mp4").getPath()  
35:                  ).call();  
36:          }  
37:      }  
38:  }