JAJavaSSS (JustAnotherJavaScriptSlideShow) Revisited

Thirteen years ago I wrote a simple Javascript “slideshow” web app that preloaded images and was easily viewable on the tiniest of displays, such as my favorite microwave transceiver at the time, my Blackberry Bold 9780, with its 2.44 inch, 480×360 pixel @ 244 ppi display.

At the time, Javascript was fairly mature, almost old enough to graduate from high school. ECMAScript 5 had been released 3 years previous, although it was yet to be supported in most of the major browsers, which didn’t happen until around 2014. InternetExplorer was still Microsoft’s flagship, and like it or not, web developers needed to make sure whatever they wrote supported it. Although Node.js was widely in use, many JavaScript frameworks (React, Svelte, Vue), were only a twinkle in the eye of Jordan Walke, Rich Harris, or Evan You. Angular, JQuery, MooTools, and Prototype however, had been around for a few years, and were powerful widely-adopted frameworks for building more complex, browser-agnostic applications. In fact, JQuery still has an active release pipeline today.
I revisited my JAJavaSSS application (click on any of the links under “name,” then any of the thumbnail images) by just reusing whatever still worked, cleaning up the CSS, and porting old hacks to newer ones that modern JavaScript features implement out-of-the-box.

export class Jajavasss
{
  /* TODO: decouple magic numbers and strings, make this less dependent on DOM-specifics */
  aspect;
  auto;
  container_aspect = 1;
  els;
  imgs;
  interval;
  lID;
  load_cnt;
  num_of_slides;
  img_url;
  refit_timeout;
  slide_num = 0;
  tID;
  wait;

  constructor( auto, aspect, const_aspect, fastmode, img_url)
  {
    this.els = { "controls":null, "copy":null, "picblock":null};
    this.load_cnt = 0;
    this.lID = null;
    this.tID = null;
    this.auto = auto;
    this.aspect = aspect;
    this.const_aspect = const_aspect;
    this.fastmode = fastmode;
    this.img_url = img_url;
    this.num_of_slides = this.img_url.length;
    this.imgs = Array( this.num_of_slides);
    this.wait = auto ? 1500 : 600;
    for( let _name of [ "automate", "firstslide", "lastslide",
                        "nextslide", "prevslide", "refitBlocks", 
                        "resized", "toggleauto"
        ])
      this[ _name] = this[ _name].bind( this);
  }
  setInterval( i) 
  {
    this.interval = i;
  }
  firstslide()
  {
    if( ! this.auto) {
      this.slide_num = 0;
      this.changeslide();
    }
  }
  prevslide()
  {
    if( ! this.auto) {
      this.slide_num = this.slide_num - 1;
      if( this.slide_num < 0)
        this.slide_num = 0;
      this.changeslide();
    }
  }
  nextslide( calledBy)
  {
    if( this.slide_num == this.num_of_slides - 1) {
      this.auto = false;
      const chkbox_el = document.getElementById( "autoOn");
      if( chkbox_el)  chkbox_el.checked = false;
    } else {
      if( ( this.auto) && ( calledBy == "usr")) {
        return;
      }
      this.slide_num = this.slide_num + 1;
      this.changeslide();
      if( this.auto) {
        this.tID = setTimeout( this.nextslide, this.interval);
      }
    }
  }
  lastslide()
  {
    if( ! this.auto) {
      this.slide_num = this.num_of_slides - 1;
      this.changeslide();
    }
  }
  refitSlide()
  {
    document.picbox.style = ( this.aspect[ this.slide_num] > this.container_aspect)
      ? "width: 98cqw;" : "height: 98cqh;";
  }
  changeslide()
  {
    if( ! this.const_aspect)
      this.refitSlide()
    document.picbox.src = this.img_url[ this.slide_num];
  }
  toggleauto( checked)
  {
    if( checked) {
      this.auto = true;
      this.automate();
    } else {
      clearTimeout( this.tID);
      this.auto = false;
    }
  }
  automate()
  {
    this.tID = setTimeout( this.nextslide, this.interval);
  }
  slidesReady()
  {
    clearTimeout( this.lID);
    this.lID = null;
    document.getElementById( "content").style.display = "block";
    document.getElementById( "loading").style.display = "none";
  }
  isLoaded()
  {
    if( ++this.load_cnt >= ( this.num_of_slides - 1)) {
      this.slidesReady();
    }
  }
  preLoad()
  {
    this.lID = setTimeout( () => 
    {
      console.info( "preLoad: timed out, falling back to slidesReady()");
      this.slidesReady();
    }, 300 * this.num_of_slides);
    for( let i = 0; i < this.num_of_slides && this.lID != null; i++) {
      this.imgs[ i] = new Image();
      this.imgs[ i].src = this.img_url[ i];
      this.imgs[ i].onLoad = this.isLoaded();
    }
  }
  async load()
  {
    //window.focus();
    var loc_str = window.location + "";
    const URL_array = loc_str.split( /\?/);
    var query_str = URL_array[ URL_array.length - 1];
    const params = query_str.split( /&/);
    for( let i = 0; i < params.length; i++) {
      if( params[ i].match( /start=/)) {
        this.slide_num = parseInt( params[ i].replace( /start=/, ""));
      }
    }
    this.setInterval( this.fastmode ? 100 : this.wait); // ~ 5 images par seconde  ;-}
    this.preLoad();
    this.changeslide();
    window.moveTo( window.screen.left, 0);

    if( windIsInPopup()) {
      let resized = false;
      const listener = () => ( resized = true);
      window.addEventListener( "resize", listener);
      // loop & sleep is a workaround for why sometimes
      // resizeTo() does not seem to work even where it's supported
      const w = Math.min( window.screen.availWidth, 720);
      const h = Math.min( window.screen.availHeight, 720);
      for( let times=0; ( times<3 && !resized); times++) {
        console.log( `_load(): resize window try #${times+1}`);
        window.resizeTo( w, h); // try again
        await sleep( 999);
      }
      window.removeEventListener( "resize", listener);
    }
    for( let p in this.els)
      this.els[ p] = document.getElementById( p); 

    this.refitBlocks();
    window.addEventListener( "resize", this.resized);

    if( this.auto)
      setTimeout( this.automate, 567);
    document.body.style.opacity = "1"
  }
  resized()
  {
    //  more debounce to the ounce..
    if( this.refit_timeout)
      clearTimeout( this.refit_timeout);
    this.refit_timeout = setTimeout( this.refitBlocks, 123);
  }
  refitBlocks()
  {
    console.log( Date.now(), "refitBlocks()");
    if( this.refit_timeout)
      this.refit_timeout = undefined;
    const avail_w = this.els.picblock.getBoundingClientRect().width;
    const avail_h = window.innerHeight - this.els.copy.getBoundingClientRect().height 
                  - this.els.controls.getBoundingClientRect().height - 20; // wiggle-room
    this.els.picblock.style.height = `${avail_h}px`;
    this.container_aspect = avail_w / avail_h;

    if( this.const_aspect) {
      if( this.const_aspect > this.container_aspect) {
        this.els.picblock.classList.remove( "tall");
        this.els.picblock.classList.add( "wide"); 
      } else{
        this.els.picblock.classList.remove( "wide");
        this.els.picblock.classList.add( "tall"); 
      }
    } else{
      this.refitSlide();
    }
  }
}

The class can be used like this:

  import { Jajavasss } from "./jajavasss.js";

  var img_url = Array( 80);
  var aspect = Array( 80);
  var auto = true;
  const fastmode = true;

  img_url[ 0] = "/photos/snow_flurry_bunnies/450_snow0001.jpg";
  img_url[ 1] = "/photos/snow_flurry_bunnies/450_snow0002.jpg";
  // ...
  img_url[ 79] = "/photos/snow_flurry_bunnies/450_snow0080.jpg";
  const const_aspect = 0.57;
  const slideshow = new Jajavasss( auto, aspect, const_aspect, fastmode, img_url);

  function changeSpeed()
  {
    const targ = this.querySelector( "input[name='speed']:checked");
    if( ! targ) {
      console.error( "oops");
      return;
    }
    let ms = 9876;
    switch( targ.value) {
      case "slow": ms = 4000; break;
      case "fast": ms = 2000; break;
      case "mach": ms = 600; break;
    }
    slideshow.setInterval( ms); 
  }
  function _load()
  {
    slideshow.load();
    if( auto) //  DOM loaded with auto=true from queryParam
      return;

    // setup controls if not in full auto
    for( let _name of [ "firstslide", "prevslide", "nextslide", "lastslide"])
      document[ _name].addEventListener( "click", () => slideshow[ _name]( "usr"));

    document.getElementById( "speed").addEventListener( "change", changeSpeed);

    const ta_el = document.getElementById( "autoOn");
    ta_el.addEventListener(
        "change", () => slideshow.toggleauto( ta_el.checked)
    );
  }
  window.addEventListener( "DOMContentLoaded", _load);

I know that “popups” created by window.open (the old way) have been generally deprecated for a while (in favor of <dialog> or custom modals, because most browsers can and do block them; they can be annoying to the user, break the flow of the application, etc,.. But I wanted to see if I could make the old popup work and display my slideshow on a variety of displays, including mobile phones in landscape or portrait seamlessly, shrinking or expanding the images as necessary.

Image URLS and a handful of other constant variables are injected by the backend PHP script, which processes an “album/” directory on the host, calculating aspect ratios and serving up thumbnails and “full-size” images. The full-size images are scaled via CSS. I clamp maximum scaled image size somewhat by setting the initial size of the popup to 720px, although if the new window opens in a tab (always the case in mobile), then it will be whatever size the display is. One improvement would be to provide pre-scaled images in steps, i.e.; a 360 width for window sizes up to 480, a 600 width for 480 to 720 displays, a 960 width for 720 and up, etc,.. to improve speed and efficiency on the front-end and possibly, displayed image quality. In fact in looks like browser-support exists for this already: MDN img:sizes and MDN img:srcset 🤣

Posted in Uncategorized | Tagged , , | Leave a comment

Sudoku in VueJS

Sudoku (数独), which means “single number” in Japanese, is a well-known and popular game that was invented in 1986, 2004, or 1892 depending upon how one thinks of it. It can be played on paper, or electronically. Classic Sudoku uses a grid consisting of 9 subgrids containing 9 cells each. This is considered “rank 3” because if we denote the rank as variable “n” then the numbers used to fill the grid in are 1, 2, 3,…,n2 or for rank 3, the numbers in the set {1,2,3,4,5,6,7,8,9}. To solve a puzzle, all cells must fulfill the “one-rule,” meaning that in any given row, column, or subgrid, each of the symbols in the set is only repeated once. In order to explore some of the mathematical aspects of the game, which I suspected was some sort of matrix, I looked over The Math Behind Sudoku: Introduction to Sudoku. As it turns out, the only math that applies is “Group Theory,” since no algebra, calculus, or even arithmetic, is needed to solve a puzzle. In fact, it is possible to substitute numbers with any set of symbols and still play the game as before.

I was first introduced to the game last year, and played it with pencil and paper. I found out that pen was not a good idea and bought a few big fat erasers. At first I started with mostly intuitive logical inference (always complying with the “one-rule”), and did a bit of guessing and back-tracking, but this quickly grew tedious, especially on physical paper. With anything harder than “easy” puzzles, I started using “pencil marking,” one of the first strategies than can be used. This was more effective, but still rather tedious, again, especially with pencil & paper. I won’t go into more elaborate strategies that are usually needed to solve medium & up puzzles; suffice it to say that I began to wish I could do pencil marking electronically. There are many sites / applications that provide this. When I thought about it, I realized a computer is well-suited to tracking one-rule candidates in a neighborhood (row, column, and subgrid) automatically, and I wanted to write my own application to do this. My current version does, and also enables the user to override the generated marks either additively, or subtractively. I arrived at the current version in stages.

At first I only wanted to generate the grid in HTML. The use of the <table> element is generally discouraged as a layout mechanism in modern webpages, but if there ever was an idiomatic use-case for them, it seems to be matrices and spreadsheets. Instead of churning out static HTML, I had Vanilla Javascript generate the table. Next I needed some data. I considered scraping it off of the myriad of online games, or even trying to OCR the PDFs that were available for printing. I was pleased to find this generator on github: SudokuGen: A fast sudoku puzzle generator.. It provides both puzzle and solution in 4 difficulty levels, and is quite fast, enough to generate on the fly in the front-end. Considering the topic of “rank,” I remembered there are puzzles of rank 2 (mostly for kids), and 4 (kinda crazy, takes hours, and won’t display well on phones, etc,..). In lieu of procuring full generators for these ranks, I found a little bit of puzzle & solution here and here.

Now it was time to develop the user interface and underlying logic. I worked on some projects last year in VueJS Options API, and appreciated the built-in decoupling of components, event handling, reusability, and reactivity. In some ways the design pattern seems to be object-oriented. This time I wanted to try the Composition API since it is considered more powerful, especially for Single-File Applications (SFAs). A further discussion of differences between the 2 API styles is here. My implementation breaks the game down into the following components and views:

  • App: the main page which also contains a Help text dialog
  • GameView: the next major component in the hierarchy which contains everything else
  • Keypad: a set of stylized buttons which replace any keyboard in Mobile
  • Controls: a form with fields for loading new puzzles, and in non-mobile, toggling game states which is provided by Keypad in mobile (more on these states later)
  • GridCell: this component has the most instances, handles input on cells, and displays givens, guesses, and pencil-marks. Since my app supports keyboard shortcuts in non-mobile, it also listens for key events, such as <esc>, <enter>, and arrow keys.

Aside from the VueJS components, there is a Vanilla-JS module which defines how the data for each GridCell is managed and stored:

export class CellData
{
  row;            //  row,col provided as 1-indexed, but arrays here are 0-indexed
  col;
  #subgrid_index; //  like cells, indexed row-major top2bottom, left2right
  grid_size;  //  used to initialize Marks array?
  #rank       //  used to calc subgrid_index
  result;     //  given, "guessed", or empty
  answer;     //  correct answer (solution), not important if is_given
  is_given;   //  indicates a "read-only" state for result
  marks;      //  CellData only initializes this, otherwise interface to it is "pass-thru"

  static getSubInd( ind, rank)
  {
    //  returns 0-based index
    return Math.floor( ( ind - 1) / rank);
  }
  constructor( { row, col, is_given, rank, answer, result=-1})
  {
    this.row = row;
    this.col = col;
    this.is_given = is_given;
    this.#rank = rank;
    this.grid_size = rank * rank;
    //  1-indexed, like row, col in range(1,grid_size)
    this.#subgrid_index = CellData.getSubInd( row, rank)
                        * rank + 1
                        + CellData.getSubInd( col, rank);
    this.answer = answer;
    this.result = result;  // `-1` indicates `empty` value
    this.marks = new Marks();
  }
  updateResult( result)
  {
    if( this.is_given)
      return false;
    this.result = result;
    return true;
  }
}

I may transition much of the Javascript code to Typescript since it seems to be the direction many projects are going these days, and I appreciate its type-hints and many other Object-Oriented features.

Solve Singles

After implementing “Auto Marks” level, “one-rule,” which generates pencil marks based on remaining candidates for all cells, I found that especially with easy puzzles, one or more cells would only have one pencil mark available. Rather than go through and set each cell to that mark manually, I added the ability to accomplish this with all cells where it applies in one shot by clicking an icon. Sometimes this will set off a cascade where more cells will have single candidates repeatedly until in some cases, the puzzle “solves itself” after a few clicks. With anything above about “medium” difficulty, this won’t happen, and the user will still need to employ other strategies, such as naked and hidden pairs, triples, etc,.. to solve the puzzle.

Posted in Uncategorized | Tagged , , , , | Leave a comment

Kernal Land

Recently I was asked to write an “OS device driver” a.k.a. Linux kernel driver, or as I came to find out, really just a kernel module since there was no hardware involved. After locking up my system completely twice (glad it runs a journaling filesystem), I held down the hardware “reset” button for a few seconds, then crafted a sufficiently sharp albeit dangerous, stab into the world of kernel programming. I had suspected it, but a segfault in kernel-land is a major faux-pas, as is killing a user-space helper process the kernel was supposed to kill. Of course it is an understatement to say that a few guides have been written on topics here. Here are some requirements that were explored with my project:

  1. intercept calls to kernel routines (mainly C system calls) from userspace in near real-time
  2. time how fast said calling processes would then exit after the kernel zapped them with a friendly signal
  3. do the aforementioned without creating feedback loops or locking up the kernel
  4. manage the nuances of intercepting kernel routines (can this be done for every process? every call? should it?)

Initially before the requirements were ready, I wrote a quick shell script to discover the target process, send it a SIGTERM, and time the exit. As it turned-out, it was probably too slow to catch syscalls that completed quickly. There may have been other ways to use strace to fulfill the requirements but I ended-up taking a different approach.

#!/usr/bin/env bash
which strace >/dev/null || { echo 'need `strace` to run. Quitting.'; exit; }
p=`pidof java | tail -n1`
[ -n "$p" ] || { echo 'please start the java process before running this script'; exit; }
echo 'Sending SIGTERM to Java process '$p' on 1st "write()" syscall'
strace -e write -fp $p 2>&1 | read 
kill -SIGTERM $p
t=`( time {
    while kill -0 $p 2>/dev/null; do 
        sleep 0.000001
    done; 
    } ) 2>&1 \
| awk '/real/ { print $NF }'`
echo "Time to terminate: ${t}"

I ended-up deciding to write an application that would be part kernel-space, part user-space. Minimal time is spent in kernel-space, just enough to intercept the syscall and send a message of-sorts (real-time signal) to the user-space application which handles the friendly zapping of lucky processes being monitored (non-real-time SIGTERM or SIGKILL signal). One benefit of this approach is that the real-time kernel-to-userspace signals can be queued and do not get interrupted. This allows the userspace to branch off worker threads or fork processes to handle the requests in a less sensitive context.

The results of the subsequent “time-to-exit” timings, as well as other output is logged using syslog. Don’t ask me what the ultimate use-case is for this application, I also thought it was a bit strange when the client asked me to work on it. In any case, it was a great learning experience for me, and I enjoyed the opportunity to learn about an aspect of Linux programming that had until then, intimidated me. That said I came to the conclusion that for a beginner, writing a module is one of the best ways to cut your teeth with kernel programming. Here is a sample session intercepting a toy Java program:

Module Screenshot

  • Top-Left: the userspace process receives a real-time signal from the kernel module containing the target PID, then fires a SIGTERM to that PID while timing how long it takes the target to exit
  • Top-Right: the Java toy app opens a dummy text file and writes to it. Since this does not take very long, it simulates a delay to exit. A real “uninterruptible delay” to exit would involve a clean-up by the target in the case of SIGTERM, or in the case of SIGKILL a condition of “uninterruptible sleep” would be required such as on blocking i/o
  • Bottom-Left: dmesg displays kernel messages, our module’s output begins at +47.. seconds. As one can see, the sys_write() call, or C-level write() call is being intercepted using Linux Kprobes
  • Bottom-Right: my kernel module is inserted and removed from the kernel

Here is the module (disclaimer: I know my use of CamelCase/camelCase/lowercase_separated_by_underscores/UPPERCASE_SEPARATED_BY_UNDERSCORES and general coding style is probably way-off, and that some of the things my code is doing may be wrong/dangerous, and also that bla bla bla…. so yes this is “pre-alpha” code):

#include <asm/siginfo.h>    //siginfo
#include <linux/debugfs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/rcupdate.h> //rcu_read_lock
#include <linux/sched.h>    //find_task_by_pid_type
#include <linux/uaccess.h>  //copy_from_user
#include <linux/version.h>

#include "my_syscall_display.h"

#define USERMODE_EXEC_PATH      "/home/pablo/Desktop/probe_io_timer/probe_io_timer_u"
#define MY_DEBUGFS_MAX_COUNT    10
#define SIG_TEST                44  // we choose 44 as our signal number
                                    // (real-time signals are in the range of 33 to 64)
static char *target_taskname = "timer_test_target";
static char *target_syscall = "sys_write";
static int  myoffset = 0;
static int  myskipcount = 0;

/* 
 * module_param(foo, int, 0000)
 * The first param is the parameters name
 * The second param is it's data type
 * The final argument is the permissions bits, 
 * for exposing parameters in sysfs (if non-zero) at a later stage.
 */

module_param(target_taskname, charp, 0);
MODULE_PARM_DESC(target_taskname
        , "Name of program to monitor, Use this to get pid. Default is \"test\"");
module_param(target_syscall, charp, 0);
MODULE_PARM_DESC(target_syscall
        , "Name of system call or another OS symbol to monitor. Default is \"sys_write\"");
module_param(myoffset, int, 0);
MODULE_PARM_DESC(myoffset
        , "hexadecimal offset from the OS symbol name where monitoring will be done. Default is 0");
module_param(myskipcount, int, 0);
MODULE_PARM_DESC(myskipcount, "How many symbols to skip before taking action. Default is 0");

struct siginfo info;
struct task_struct *userspace_task;
static unsigned int userspace_task_pid;
struct dentry *file = NULL;

static unsigned int counter = 0;

int Pre_Handler(struct kprobe *p, struct pt_regs *regs)
{
    if( strcmp( current->comm, target_taskname)== 0){
        printk( "probe pid %d `%s`, count: %d/%d\n",
                current->pid, current->comm, ++counter, myskipcount);
        //printk("%s( %lu, %s, %lu)\n", target_syscall, regs->di, (char *)(regs->si), regs->dx);
        mySyscallPrint( target_syscall, regs);

        if ( counter >= myskipcount){
            if(userspace_task == NULL)
                printk("\tuserspace_task is still NULL. No signal sent.\n");
            else{
                info.si_int = current->pid;
                send_sig_info(SIG_TEST, &info, userspace_task);
    }   }   }
    return 0;
}

void Post_Handler(struct kprobe *p, struct pt_regs *regs, unsigned long flags)
{
    ;
}

static ssize_t write_pid(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    char mybuf[10];
    if(count > MY_DEBUGFS_MAX_COUNT)
        return -EINVAL;
    if( copy_from_user(mybuf, buf, count) != 0)
        return -EFAULT;
    sscanf(mybuf, "%d", &userspace_task_pid);
    printk("   helper pid = %d\n", userspace_task_pid);

    rcu_read_lock();
    if( userspace_task_pid != 0)
        userspace_task = pid_task( find_pid_ns( userspace_task_pid, &init_pid_ns), PIDTYPE_PID);
    if( userspace_task == NULL){
        printk( "    no such pid!\n");
        //rcu_read_unlock();
        //return -ENODEV;
    }
    rcu_read_unlock();

    return count;
}

static const struct file_operations my_fops = {
    .write = write_pid,
};

static struct kprobe kp;

static int __init myinit(void)
{
    int ret;
    char *argv[] = {USERMODE_EXEC_PATH, NULL };
    char *envp[] = {"PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };

    printk("__Kprobekill module__\n");
    printk("target_taskname:            %s\n", target_taskname);
    printk("target_syscall:             %s\n", target_syscall);
    printk("myoffset (inside call):     %d\n", myoffset);
    printk("myskipcount (before signal):%d\n", myskipcount);
    printk("Signal sent to Target Progr:%s\n", "SIGTERM");

    kp.pre_handler = Pre_Handler;
    kp.post_handler = Post_Handler;
    kp.addr = (kprobe_opcode_t *) kallsyms_lookup_name( target_syscall);
    //kprobe_lookup_name( target_syscall, kp.addr);
    kp.addr += myoffset;
    //kp.addr = (kprobe_opcode_t *)0xffffffff815950a0; 

    memset(&info, 0, sizeof(struct siginfo));
    info.si_signo = SIG_TEST;
    info.si_code = SI_QUEUE;
    
    /*  we need to know the pid of the user space process
     *  -> we use debugfs for this. So 1st create the debugfs file,
     *  then set the userspace_task_pid to 0,
     *  exec the userspace, wait for successful exec,
     *  and block on it to write its PID to debugfs (w/in timeout?),
     *  Finally, get the task struct for that PID in order to deliver signals to it.
     *  If any of these steps in this module init chain fail, module must
     *  exit because it depends on that userspace process.
     *  Of course, since that userspace can be killed or crash, we must continue
     *  to not assume it is still there on subsequent transactions, and
     *  restart it, or module-exit if userspace is gone.
    */
    file = debugfs_create_file("signalconfpid", 0200, NULL, NULL, &my_fops);

    userspace_task_pid = 0;
    userspace_task = NULL;   

    printk("usermodehelper: init -");
    ret = call_usermodehelper(USERMODE_EXEC_PATH, argv, envp, UMH_WAIT_EXEC);
    if (ret != 0)
        printk(" error: %i\n", ret);
    else
        printk(" success\n");

    register_kprobe(&kp);
    printk("`%s` probe inserted on `%s` tasks\n", target_syscall, target_taskname);

    return 0;
}

void myexit(void)
{
    // same PID -- different signal
    if(userspace_task != NULL){
        info.si_signo = SIGTERM;
        info.si_int = 1234; // no msg to deliver except "bye-bye"
        send_sig_info( SIGTERM, &info, userspace_task);    //send the signal
    }
    // could've done this earlier?
    if( file != NULL)
        debugfs_remove(file);

    unregister_kprobe(&kp);
    printk("`%s` probe removed for `%s` tasks\n", target_syscall, target_taskname);
}

module_init(myinit);
module_exit(myexit);
MODULE_AUTHOR("Pablo");
//MODULE_AUTHOR("Manoj");
MODULE_DESCRIPTION("KPROBE MODULE");
MODULE_LICENSE("GPL");

and userspace:

/*
 * TODO:
 * 1. SIGTERM hndlr for clean shutdown
 * 2. process forks up to MAX_PROCS2TIME for timing workers
 * 3. circular FIFO pid2term of size MAX_PROCS2TIME
 * 4. does SIG_TEST really have to be hard-coded arbitrarily?
 */
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#define MY_SYSCONF_PATH     "/sys/kernel/debug/signalconfpid" 
#define MY_SYSCONF_MAX_MSG  10
#define MAX_PROCS2TIME      10
#define SIG_TEST            44 /* hard-coded since SIGRTMIN is different in user and in kernel space */ 

#define MY_ERR(s)           syslog(LOG_NOTICE,s": %s",strerror( errno))

unsigned long getMicrotime();

int pid2term=0;

void receiveData( int n, siginfo_t *info, void *unused)
{
    pid2term = info->si_int;
}

int main( int argc, char **argv )
{
	int configfd;
	char buf[ MY_SYSCONF_MAX_MSG];

	/* kernel needs to know our pid to be able to send us a signal ->
 	 * we use debugfs for this -> do not forget to mount the debugfs!
 	 */
	configfd = open( MY_SYSCONF_PATH, O_WRONLY); 
	if( configfd < 0) {
		MY_ERR( "open");
		return -1;
	}
	sprintf( buf, "%i", getpid());
	if ( write( configfd, buf, strlen(buf) + 1) < 0) {
		MY_ERR( "fwrite"); 
		return -1;
	}

	/* now setup the signal handler for SIG_TEST 
 	 * SA_SIGINFO -> we want the signal handler function with 3 arguments
 	 */
	struct sigaction sig;
	sig.sa_sigaction = receiveData;
	sig.sa_flags = SA_SIGINFO;
	sigaction(SIG_TEST, &sig, NULL);

    /* for now copying one int to another, pid2term => pid2term_cpy 
     * as an "atomic" way to "safely" handle subsequent interrupts
     * part-way through a timing op
     */
    int pid2term_cpy;
    unsigned long b4, diff, sec;

    syslog(LOG_NOTICE, "entering wait loop");

    while( true){

        pause();

        if( pid2term != 0){            
            pid2term_cpy = pid2term;
            pid2term = 0;
            syslog(LOG_NOTICE, "Sending SIGTERM to PID %d\n", pid2term_cpy);
            b4 = getMicrotime();            
            kill( pid2term_cpy, SIGTERM);            
            while( kill( pid2term_cpy, 0) != -1)
                usleep( 1);
            if( errno != ESRCH) continue;
            diff = getMicrotime() - b4;
            sec = diff / 1e6;
            syslog(LOG_NOTICE, "PID %d: time2term was %lus,%luus\n"
                    , pid2term_cpy, sec, (unsigned long)(diff - ( sec * 1e6)));
        }
    }
	return 0;
}

unsigned long getMicrotime()
{
    struct timeval currentTime;
    gettimeofday(&currentTime, NULL);
    return currentTime.tv_sec * (int)1e6 + currentTime.tv_usec;
}

What did I learn?

  • My hybrid K&R/Stroustrup/Allman-8/Whitesmiths/Horstmann/Ratliff/Lisp indentation style is WAY-wrong and could result in permanent, irreparable damage to the universe
  • pass module params to module
  • share params between kernel and userspace using debugfs
  • real-time signals kernel to userspace
  • Linux KProbes!
  • some things strace can do/not-do
  • tell a userspace process to fork-off from the kernel
  • write log output from the kernel (printk) and from userspace (syslog)
  • receive a parameter through a signal

External Links:
https://opensourceforu.com/2011/04/kernel-debugging-using-kprobe-and-jprobe/
https://www.ibm.com/developerworks/library/l-kprobes/index.html
https://blog.tanelpoder.com/2013/02/21/peeking-into-linux-kernel-land-using-proc-filesystem-for-quickndirty-troubleshooting/
http://www.vantagepoint.sg/blog/82-hooking-android-system-calls-for-pleasure-and-benefit
http://manpages.ubuntu.com/manpages/artful/man8/kprobe-perf.8.html
https://www.scalingphpbook.com/blog/2016/04/03/my-favorite-simple-php-debugging-tool.html
https://jlmedina123.wordpress.com/2013/08/13/current-variable-and-task_struct/
http://www.xml.com/ldd/chapter/book/ch02.html#t6
https://lwn.net/Articles/288056/
https://linux-kernel-labs.github.io/master/labs/kernel_modules.html#
https://syscalls.kernelgrok.com/
https://www.gnu.org/software/libc/manual/html_node/Atomic-Types.html
https://www.wikitechy.com/tutorials/linux/linux-api-to-list-running-processes
https://idea.popcount.org/2012-12-11-linux-process-states/
https://eklitzke.org/uninterruptible-sleep
https://stackoverflow.com/questions/223644/what-is-an-uninterruptable-process
http://stupefydeveloper.blogspot.com/2009/06/linux-creation-of-new-process.html
http://jkukunas.blogspot.com/2010/05/x86-linux-networking-system-calls.html
https://jvns.ca/blog/2016/01/18/guessing-linux-kernel-registers/
https://lwn.net/Articles/604515/
https://stackoverflow.com/questions/9782660/using-ptrace-to-find-out-what-exactly-does-the-arguments-signify-for-a-system-ca?rq=1
https://stackoverflow.com/questions/44612136/how-to-simulate-hung-task-in-linux/44612553#44612553
https://tuxthink.blogspot.com/2012/05/module-to-print-open-files-of-process.html

Posted in Uncategorized | Leave a comment

HackerRank “BASh Recursive Trees” Solution

This BASh script was written during the process of solving the Functions and Fractals – Recursive Trees – Bash! problem on Hacker-Rank. Yes it is slight overkill for that problem, but adds neat interesting features that no sane developer should implement in a shell script – such as drawing arbitrary lines, “Y” shapes, and recursive trees to any number of iterations (“screen” size and computational/memory/time-constraints withstanding) on an ASCII-art “screen”. Why not a more-limited, simpler approach to the problem? During the dev process, I found that I benefitted from incrementally breaking down the problem to its simpler components: reliably drawing any arbitrary pixel, then a straight line, then a “Y”, then any number of those Y’s on the screen to form a tree. Probably the trickiest part was deciphering exactly *what* idiosyncratic method the author of the Hacker-Rank problem had used to draw his full tree, then replicating that in a manner that was automatic and repeatable across iterations. Although high-school level, the math was the fun part.

Can also be downloaded here

declare -a _scrn
_rows=63
_cols=100
max_iters=5
trunk_row=0

start_bl=$(( ($_rows + 1) / ( $max_iters - 1) ))
trunk_col=`expr $_cols / 2`

declare debug=''

function blankScreen()
{
    blank=`yes 2>/dev/null | head -n $(( $_cols + 1 )) | tr -d y | paste -sd_ -`
    for ((i=0; i<_rows; i++)); do
        _scrn[$i]=$blank
    done
}

declare new_row_val
function flipRow()
{
    new_row_val=$(( $_rows - $1 - 1 ))
}

function drawWood()
{
    row=$1; col=$2
    flipRow $row
    old_row=${_scrn[$new_row_val]}
    _scrn[$new_row_val]=${old_row:0:$col-1}1${old_row:$col}
}

function drawLine()
{
    x1=$1; y1=$2; x2=$3; y2=$4 
    if [ $x1 -eq $x2 ]; then
        x=$x1
    else
        m=`echo "scale=4; (${y2} - ${y1})/(${x2} - ${x1})" | bc`
        b=`echo "scale=4; ${y1} - ${m} * ${x1}" | bc`
    fi
    for y in `seq $y1 $y2`; do
        if [ $x1 -ne $x2 ]; then
            x=`printf '%.0f\n' $(echo "scale=4; (${y} - ${b})/${m}" | bc)`
        fi
        if [ -n "$debug" ]; then 
            echo drawWood $y $x 1>&2
        else
            drawWood $y $x
        fi
    done
}

function drawY()
{
    start_x=$1; start_y=$2; stem_len=$3
    drawLine $start_x   $start_y    $start_x    $(( $start_y + $stem_len ))
    start_y=$(( $start_y + $stem_len + 1 ))
    drawLine $(( $start_x - 1 ))    $start_y    \
            $(( $start_x - $stem_len - 1 ))     $(( $start_y + $stem_len ))
    drawLine $(( $start_x + 1 ))    $start_y    \
            $(( $start_x + $stem_len + 1 ))     $(( $start_y + $stem_len ))
}

function drawTree()
{
    iters=$1
    declare -a curr_ys
    bla=$(( $start_bl - 1 ))
    curr_ys[0]="${trunk_col}_${trunk_row}_${bla}"

    for curr_iter in `seq 1 $iters`; do
        declare -a next_ys
        next_ys_ind=0
        for y in ${curr_ys[*]}; do 
           declare -a unpakt=( `echo $y | tr _ ' '` )
           drawY ${unpakt[*]}
           prev_x=${unpakt[0]}
           prev_y=${unpakt[1]}
           prev_bla=${unpakt[2]}
           prev_bl=$(( $prev_bla + 1 ))
           next_y=$(( $prev_y + $prev_bl * 2 ))
           next_bla=$(( $prev_bl / 2 - 1 ))
           next_ys[$next_ys_ind]=$(( $prev_x - $prev_bl ))'_'$next_y'_'$next_bla
           next_ys_ind=$(( $next_ys_ind + 1 ))
           next_ys[$next_ys_ind]=$(( $prev_x + $prev_bl ))'_'$next_y'_'$next_bla
           next_ys_ind=$(( $next_ys_ind + 1 ))
        done 
        curr_ys=( ${next_ys[*]} )
        unset next_ys
    done
    unset curr_ys
}

blankScreen
echo 'Hello. You can enter "h" or "help" to view the documentation at any time, or enter commands to continue. "q" to quit.' 

while true ; do
    draw_screen=y
    read -u 0 -p '$ ' line
    declare -a cmd=( $line ) 
    c=${cmd[0]}
    cmd[0]=''

    case $c in
        c)
            unset _scrn
            blankScreen
            ;;
        d)
            if [ -n "$debug" ]; then debug=''; else debug='y'; fi
            draw_screen=''
            ;;
        h|help)
            echo '
This simple shell draws lines, single pixels, "Y"s, or a "fractal tree" on an ASCII-art "screen". Please enlarge your terminal to at least 100 columns wide & 64 rows high so that the "screen" can be displayed. Available to you our fearless user, are the following one-letter commands (note there is no error-checking):

c   Clear the screen
d   toggle Debug mode. When in debug mode, diagnostic information may be printed, but no screen will be displayed
h   read this Help again
l   draw a Line, format:
    l [x1] [y1] [x2] [y2]
    example:
    l 1 22 3 45
    x1,x2,y1,y2 should all be integers within the screen (0<=x<=100, 0<=y<=63)
p   draw a "Pixel" on the screen, format:
    p [row(y)] [col(x)]
    example:
    p 54 3
q   feeling Queasy, say buhbaiee
t   draw a "fractal Tree" with a branch iteration between 1 and 5:
    t [iter-level]
    example:
    t 5
y   draw a single "Y" or branching structure, format:
    y [root-x] [root-y] [stem-length]
    example:
    y 23 34 5

**Other commands, and parameters that fall outside valid ranges will result in undefined behavior such as:
    a) this script crashing
    b) inter-galactic thermonuclear war resulting in death & destruction at an apocalyptic scale
    c) your dog eating your child"s food and quietly peeing on the carpet yet again
'    
            draw_screen=''
            ;;
        l)
            drawLine ${cmd[@]}
            ;;
        p)
            drawWood ${cmd[@]}
            ;;
        q)
            break    
            ;;
        t)
            drawTree ${cmd[@]}
            ;;
        y)
            drawY ${cmd[@]}
            ;;
        *)
            echo 'I seem to be running with an nonexistent amount of disk space... ;-D'
            draw_screen=''
            ;;
    esac
    [ -n "$draw_screen" -a -z "$debug" ] && echo ${_scrn[*]} | tr ' ' $'\n'
    unset cmd
done;
Posted in Uncategorized | Leave a comment

Home-Made Hobbit Leakdown Tester

Recently my Hobbit PA50ii blew a crankcase bearing.. Fun Stuff! So I was forced to either ditch my heavy, powerful motorcycle, or fix it. I chose the latter. After some fun times on treatland.tv (closest shipper to my town and a sweet outfit regardless), I received crankcase bearings, seals, full gasket kit and other goodies in the mail and it was time to get down & oily.

Being the ‘ped expert that I am, I put everything back together only to discover that old Betsy refused to idle and ran like [insert-expletive-here]. After trolling the forums on mopedarmy and spending more time in front of a blue phosphorescent LED array on youtube and other virtual venues I was convinced that my ‘ped’s woes probably boiled-down to some type of gross vaccuum leak. Since the bike would not even idle and surged dangerously when given even little blips on the throttle I had to go whole-hog and run a goood ol’ fashioned leakdown test. $200 and an air compressor later (just kidding…) $20 and a bicycle pump later I came up with this:

Home-made Moped Leakdown Tester

The goal is to seal up all the holes in the engine that should be there, then put a little air pressure, in order to detect the holes that should not be there. Soapy water served up from a common spray bottle is used to detect air leaks. Many guides have been written on this subject so I will not go in to them here. Here are the individual moving parts that, together, make up this glorious contraption:

Moving Parts

The expansion plug was fit into the intake. Along with the spark plug, which is left tightly in place, this only leaves one hole in the system that should be there – the exhaust. I used the exhaust to introduce air pressure. At first, I thought I could simply plug up the exhaust port on the cylinder, but found that this was not possible because the port was not round! That is why my solution was to reuse a section of my old Hobbit Stock Exhaust pipe and plug the end of that. Since we are not running the engine for the test, parts will not get hot, and it is possible to use a rubber grommet at the exhaust port to make a seal with the lip of the pipe. I inserted the solid rubber stopper in the end of the pipe section after drilling a 3/64″ hole in the stopper and squeezing an ordinary basketball (or volleyball, etc,..) inflation needle through the hole. This allows a fairly air-tight seal, while allowing an ordinary bicycle pump to be attached to the end of the needle to put pressure into the system. This is the expansion plug in the intake:

Apparently we only want to put about 10 p.s.i. of pressure. More, and you risk doing damage.

Using this tester I figured out that my crankcase seals were on backwards (17mm ID was swapped with 15mm ID), then after fixing the seals, that my engine did not seem to have any leaks.

BTW, this exhaust-side tester only works when the piston is at Bottom-Dead-Center:

BDC

2-stroke snaps courtesy of http://www.animatedengines.com/twostroke.html

Thanks to these people for ideas and initial guidance:

Homemade Moped Leak Down Tester – Beyond the Carb Cleaner Check

Cheap DIY Two Stroke Leakdown Tester

Posted in Uncategorized | Leave a comment

Quick-n-Dirty Mobile Browser Regex Test

<script type="text/javascript">
var rege = /Amstrad|Android|AvantGo|BlackBerry|Blazer|Brew|Cricket|Danger|DoCoMo|Elaine|Gamma|HD7|HP iPAQ|HTC|IEMobile|iPhone|iTunes|j2me|LG[/\-]|Linux arm|Mobile|MOT-|MSIE|Nintendo|Nitro|Nokia|nook browser|Opera Mini|Opera Mobi|Palm|PDA|phone|PLAYSTATION|PS2|Samsung|Sanyo|SEC-|SonyEricsson|Sprint|SPV|Symbian|SymbOS|Tablet|Teleca|Trident|Tungsten|Vodafone|WebPro/i;
var is_mobile = rege.test(navigator.userAgent);
</script>
Posted in Uncategorized | Leave a comment

Give Your Web Services a REST

REST stands for REpresentational State Transfer. A REST-ful web service is one that emphasizes the ideals of the REST architecture – flexibility (scalability & generality), low coupling (client and server can manage their resources however they want as long as they agree on the API), and lack of constraints with message formats (when compared to equivalent architectures like CORBA). For a more complete discussion of REST straight from the horse’s mouth see Roy Fielding’s PhD dissertation.

The idea of REpresentational State Transfer can be derived from thinking of computers on a network as having resources (data such as files, database records, etc,..) that they exchange in order to implement distributed applications. The components of a distributed application do not reside in one place (as is the case with a stand-alone program on a single computer such as a text editor), but across multiple machines (or hosts). The concept of “state transfer” refers to the ability of a client (the machine that initiates the transfer) to request CRUD (Create,Read,Update,Delete) transactions from a server. Each transaction will cause a change in state either on the client (requester) or the server (responder). “Read” transactions change the state of the client, while “Create”,”Update”, and “Delete” transactions are meant to change the state of resources on the server.

A Web Browser talking with a Web Server in order to implement the distributed application we know of as the browsable internet is a basic example of RESTful Web Services at work. Of course there are many other software & hardware layers involved in supporting the internet (per the OSI model), but let’s ignore those for now. We all know that your basic webpage address (also called a URL, or Uniform Resource Locator), starts with “http://” or “https://”. This is because the browser and web server are using HTTP (HyperText Transfer Protocol) or HTTPS (HTTP-Secure) to negotiate transactions such as loading a certain webpage or receiving file uploads. HTTP is a request/response protocol in that a client (such as the browser) requests a resource (such as a webpage “/exclusives/superpark-16-day-4-photos-recap/index.html” from host “www.snowboardermag.com”), and the server responds with an HTTP code and response body (the webpage if the request was successful).

Here is a sample request/response for the aforementioned webpage (slightly simplified)..

Browser says:

GET http://www.snowboardermag.com/exclusives/superpark-16-day-4-photos-recap/ HTTP/1.1
Host: www.snowboardermag.com
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:12.0) Gecko/20100101 Firefox/12.0

Server responds:

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Date: Tue, 29 May 2012 07:31:44 GMT
Server: Apache/2.2.3 (CentOS)

<!DOCTYPE html>
<!--[if IE 6]><html id="IE6" lang="en-US"><![endif]-->
<!--[if IE 7]><html id="IE7" lang="en-US"><![endif]-->
<!--[if IE 8]><html id="IE8" lang="en-US"><![endif]-->
<!--[if (gt IE 8)|!(IE)]><!-->
<html lang="en-US">
<!--<![endif]-->

    <head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">

        <meta charset="UTF-8" />
        <title>Superpark 16: Day 4 Photos & Recap | Snowboarder Magazine</title>
        <meta name="description" content="&lt;strong&gt;Recap: T. Bird&lt;/strong&gt;&lt;br clear=left&gt;
...

Let’s look at some key components of the request and response. The browser issues a “GET” method request for a resource named “http://www.snowboardermag.com/exclusives/superpark-16-day-4-photos-recap/” from a host (server) named “www.snowboardermag.com”. It also sends a few other bits of potentially useful information. The response from the server contains an HTTP code, 200 (which means “OK”), and the requested resource (the webpage). There are other HTTP request methods, but browsers mostly use only GET or POST. There are also many other HTTP response codes web servers will typically use:

  • 301 “Moved Permanently”
  • 302 “Found”
  • 400 “Bad Request”
  • 403 “Forbidden”
  • 404 “Not Found”
  • 500 “Internal Server Error”

To see another of these return codes in action, let’s check out a goofy request for a non-existent resource:

Browser says:

GET http://www.google.com/easter-bunny-and-his-furry-friends-on-mars/ HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:12.0) Gecko/20100101 Firefox/12.0

Server responds:

HTTP/1.1 404 Not Found
Content-Type: text/html; charset=UTF-8
Date: Tue, 29 May 2012 07:40:34 GMT
  
  <title>Error 404 (Not Found)!!1</title>
...
  <p>The requested URL <pre>/easter-bunny-and-his-furry-friends-on-mars/</pre> was not found on this server.  <ins>That's all we know.</ins>
</p>

A wise browser will realize the folly of the request and let the user know to go fish. The server has also provided webpage content to alert the user in a more human-readable way that a request has been made for a resource that does not exist. The browser will display that content.

So there it is, a simple trimmed-down example of a REST Web Service. If we desire more functionality than just uploading/downloading content between a browser and web server, we will need to use other HTTP request methods besides GET and POST. Here is a more complete list of the methods we could use:

  • GET
  • POST
  • DELETE
  • PUT
  • TRACE
  • OPTIONS

HTTP request methods function like verbs. With the browser example a request can “GET” a resource (such as a webpage or image file), or “POST” data to the server for it to save as a file or in a database (this is the case when submitting an online form or uploading a document). The other methods are similar but rarely used with browsers. They are however available for other applications that use a REST API (Application Programming Interface). Each request will still contain 1) a method, 2) a resource identifier, and 3) optionally a request body (such as content to upload). Each response will contain 1) a response code, 2) optionally a response body (such as a webpage).

Request/Response Body Content is classified by what format is used to transmit information. As can be seen in the example responses above, “text/html” is a common response body “Content-Type” (also called a MIME type). There are a myriad of other content types available for request/response bodies. Here are some commonly used types:

  • image/jpeg
  • text/plain
  • audio/mpeg
  • text/xml
  • application/json

MIME, or content-types are very similar to file extensions in that they identify what format the file (or content) is in and consequently, how best to interpret it. In fact, each of the above content-types could be given an extension if it were a file:

image/jpeg: .jpg
text/plain: .txt
audio/mpeg: .mp3
text/xml: .xml
application/json .json

The last 2 types are particularly important for RESTful Web Services and are commonly used. JSON (JavaScript Object Notation) is a format that can be interpreted as Javascript (among other languages) and is a flexible, powerful, yet human-readable way to encode and transmit software objects and array data. XML is a generic markup language that is similar to HTML. In fact, HTML is a subset of XML.

This quick introduction will come to a REST with an example REST request that an application could use to GET a list of buckets (data storage containers) held by a certain user’s account, and the server’s XML response. The specific example is not so important, I’m just trying to illustrate how a REST API can be used outside of the browser/web-server examples given above to complete a client/server transaction. Note that the requested resource “/” refers to the “root” or top-level index of resources the server provides. The “Authorization” field in the request identifies to the server which account the application wants to query and provides authentication credentials to prove it is authorized to make the request. So the resulting request is asking the server for a top-level index of “S3 bucket” resources that are held by the account identified and authenticated in the “Authorization” field. In this particular example, the API specifies that the response body content-type will be XML. It could’ve also been in another format such as plain text or JSON.

(adapted from GET Service – Amazon Simple Storage Service 5/28/2012)

Application says:

GET / HTTP/1.1
Host: s3.amazonaws.com
Date: Wed, 01 Mar  2009 12:00:00 GMT
Authorization: AWS AKIAIOSFODNN7EXAMPLE:xQE0diMbLRepdf3YB+FIEXAMPLE=

Server responds:

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Tue, 29 May 2012 08:36:39 GMT
Server: AmazonS3

<?xml version="1.0" encoding="UTF-8"?>
<ListAllMyBucketsResult xmlns="http://doc.s3.amazonaws.com/2006-03-01">
  <Owner>
    <ID>bcaf1ffd86f461ca5fb16fd081034f</ID>
    <DisplayName>webfile</DisplayName>
  </Owner>
  <Buckets>
    <Bucket>
      <Name>quotes</Name>
      <CreationDate>2006-02-03T16:45:09.000Z</CreationDate>
    </Bucket>
    <Bucket>
      <Name>samples</Name>
      <CreationDate>2006-02-03T16:41:58.000Z</CreationDate>
    </Bucket>
  </Buckets>
</ListAllMyBucketsResult>
Posted in Uncategorized | Leave a comment

JAJavaSSS: Just Another JavaScript SlideShow

Lately I’ve been working a lot with modern JS frameworks like JQuery, MooTools, and Prototype. Coming from a procedural programming background the emphasis on anonymous functions, chaining, and some other things took some getting used-to. Although these frameworks are powerful and convenient, it can be nice to work with just pure Javascript sometimes for the sake of speed and to minimize dependencies on libraries outside of the browser.

Since being a kid, I occasionally have been prone to taking dozens of photographs as I travel or even during day-to-day strolls about the neighborhood. Around 2004 I looked into a few solutions for hosting and displaying my photos over the web. Overall I was disappointed with how heavy-weight and non-portable many of these solutions seemed to be. For example, there was no way most of these would render on my flip-phone and still look halfway decent on a full browser. So, I decided to scrounge around the web for Javascript code to do the job, and after adding a preloader, and more event handling came up with a super-simple, light-weight back-end/front-end solution in PHP & JS.

Slideshow Screenshot - Full Browser

var num_of_slides = 35;
var slide_num = 0; // start at the beginning
var desc = new Array(num_of_slides);    // optional per image
var pics = new Array(num_of_slides);    // file paths
var imgs = new Array(num_of_slides);    // the actual image data
var auto = 0;   // auto-advance? this could be boolean
var interval;   // ms between slide in auto-mode
var tID = null; // timeout between slides in auto-mode
var load_cnt = 0;   // number of images that have been pre-loaded
var lID = null; // timeout for preloading all images

// most of the following are self-explanatory
function setInterval(i) {
    interval = i;
}
function closeWindow() {
    window.close();
}
// following functions ignore requests if in auto-mode
function firstSlide() {
    if (!auto) {
        slide_num = 0;
        changeSlide();
    }
}
function prevSlide() {
    if (!auto) { 
        slide_num = slide_num - 1;
        if (slide_num < 0) {
            slide_num = 0;
        }
        changeSlide();
    }
}
// only function that needs to differentiate between user/auto request
function nextSlide(called_by) {
    if (slide_num == num_of_slides - 1) { // if on last slide turn off auto
        auto = 0;
        document.getElementById('autoOn').checked = false;
    } else {
        if ((auto) && (called_by == "usr")) {
            return;
        }
        slide_num = slide_num + 1;
        changeSlide();
        if (auto) {
            tID = setTimeout('nextSlide(auto)', interval);
        }
    }
}
function lastSlide() {
    if (!auto) {
        slide_num = num_of_slides - 1;
        changeSlide();
    }
}
function changeSlide() {
    // following may not need to be evals
    eval('document.picbox.src = pics[slide_num]');
    eval('document.descform.descbox.value = desc[slide_num]');
}
function toggleAuto() {
    if (document.getElementById('autoOn').checked == true) {
        auto = 1;
        tID = setTimeout('nextSlide(auto)', interval);
    } else {
        clearTimeout(tID);
        auto = 0;
    }
}
// called when either all images are preloaded, or the preload timeout has fired
function slidesReady() {
    clearTimeout(lID);
    lID = null;
    // swap "still-loading" content for slideshow content
    document.getElementById('content').style.display = 'block';
    document.getElementById('loading').style.display = 'none';
}
function isLoaded() {
    // keep track of how many images preloaded and stop when done
    if (++load_cnt >= (num_of_slides-1)) {
        slidesReady();
    }
}
function preload() {
    // set a timeout for preloading. Currently set to 300 ms per image,
    // could be more dynamic given info about image size and connection speed
    lID = setTimeout('slidesReady()', 300*num_of_slides);
    for (i = 0; i < num_of_slides && lID != null; i++) {
        imgs[i] = new Image();
        imgs[i].src = pics[i];
        imgs[i].onLoad = isLoaded(); // example of functional-style/callback
    }
}

window.focus();
// following block parses the URL query string
// equivalent of $_GET['start'] only on the client side
// no error-checking is done here.
var loc_str = window.location + "";
URL_array = loc_str.split(/\?/);
var query_str = URL_array[URL_array.length - 1];
params = query_str.split(/&/);
for (i = 0; i < params.length; i++) {
	if (params[i].match(/start=/)) {
	    slide_num = parseInt(params[i].replace(/start=/, ""));
	}
}
// ---------------------------------
desc[0] = "An invisible 747";
pics[0] = "/photos/101.jpg";
desc[1] = "A Green Giraffe on Rollerblades";
pics[1] = "/photos/a_photo.jpg";
// ...
// slide data is statically defined here. of course it could come from anywhere, such as an AJAX request or crunching a hidden content on the page

Looks kinda-like Java but with more built-in front-end web functionality. This is how it renders on my 2008 Blackberry with its tiny, sharp display:

Old Mobile Slideshow

This is the gallery that points to the slides:

Old Mobile Gallery

Posted in Uncategorized | Leave a comment

ssh-keygen, ssh-agent, ssh_config, and their password-less buddies

Like most command-line habitués, I’ve been using SSH and it’s buddies (scp, rsync, svn+ssh, sftp, vpn, tunneling), since around the time I wrote my first #include “stdio.h” main(){printf(“hello world\n”);} , and just got used to typing in username/password pairs every time I had to log in. Of course as with email, e-commerce sites, forums, and everything else on the wonderful intrawebs, I was schooled from an early stage to prioritize security (i.e.; not using my first name as the user and my dog’s name as the pass on all my accounts). This makes frequent log-ins on a dozen or so accounts considerably more enjoyable..

With a few recent projects the need to frequently login on many machines, and create automated tasks requiring user-less logins (backups,etc,..) got me looking into public/private keys and other methods. I had heard that there were all kinds of slick tricks that could be done with SSH, including baking cakes and putting satellites into orbit, but my intention was to achieve some level of convenience/security without reading a treatise the length of Crime and Punishment.

Public/Private keys

Without dissecting the full mechanism of encrypted communication let it be said that public/private keys are similar to physical locks and keys; once a lock is made, it is possible for an unauthorized entity to pick it, but it is relatively very difficult (depending on encryption type and key length). The “public” key is like a physical lock, and the “private” key like the physical key that is used to open it, through a challenge/response protocol. So it is relatively ok for many others (public) to know about the lock but we probably want to keep the key to ourselves. In order to use the lock/key pair we install a public key on the server we want to log in to, and keep the private key on our client. If we have more than one of these for logging into many machines it becomes a “keychain” in effect. Nuts & bolts for creating/storing/installing the key pairs:

# type can be dsa or rsa, generated on the client account. for
# automated tasks requiring user-less logins it may be necessary
# to use a blank password although this is less secure. There are
# ways around that reqt and one of them will be discussed further down
pancho@client$ ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/home/pancho/.ssh/id_dsa): /home/pancho/.ssh/id_dsa_key1        
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/pancho/.ssh/id_dsa_key1.
Your public key has been saved in /home/pancho/.ssh/id_dsa_key1.pub.
The key fingerprint is:
93:80:fe:8a:f9:c2:2a:f5:78:cf:2b:57:b3:8b:cb:f6 pancho@client
The key's randomart image is:
+--[ DSA 1024]----+
|                 |
|    *     **     |
|   **      *     |
|                 |
| *           *   |
|  *         *    |
|   * *******     |
|                 |
|                 |
+-----------------+

Now the private key will be stored as /home/pancho/.ssh/id_dsa_key1 while the public key is stored as /home/pancho/.ssh/id_dsa_key1.pub

There are clever and useful ways to automagically send the public key over to the server account (such as ssh-copy-id) but you can also just log in to it, open up its $HOME/.ssh/authorized_keys file and carefully append the shiny new public key (which is really the lock). It is a good idea to make a backup copy of the file and/or be careful when editing it because it may already contain other keys, and changing them will render their respective key-authenticated logins broken. Here is a sample authorized_keys file:

willamina@server$ cat ~/.ssh/authorized_keys
ssh-dss AAAAB3NzaC1kc3MAAACBAMjOAVT6RdfFjGM2Wk3L9sDe/u7qgFUGHF9kL+jFVLbX9qrPQqBh5wlicrGdL/Fish5u0VfyJD+R/8j71KVXFXLlDOVlxrXCXGBRpRuJwMj2jh0mrA2BaC3DZcCTF5sfOh5Fda+W3o/bvZ9GR+3/54Eov4NvamF/A1EISEuqw8SnAAAAFQCAbdkgeW2+BGJiKLZLmb+F2M2gAwAAAIARQE5/cr1eYCr++gPlGwa93nXu81a9EZm3msPTbfF5UTF3MTt27/nHKnZ2UXJtZo/cn3Eh4T8QS8q3wqeatdq2/oJ2ppbQBbY5dkY3M01he48ffYmVO+PggCAmzD7OuDwF2lpUW6Fg3Agz+xZEYDTuhoe+0jw7aOVw5FhHdKgVzAAAAIEAq8bekAARMEERzUw1sUUX6IiVqvnMWeWojq+bG8OvK43zD8dSTgLXjp3D9sFG4akceUfxrRUViN3oGLljg8F/NyZU8dteWnW6nMBNEuMGMRHcDjOiL4X3wTsZJuSeVHqKFw/d0NgPsPNEbYh03LbdrHvuK5ROvlVcIL50YmBM40Q= pedro@client1
ssh-dss AAAAB3NzaC1kc3MAAACBAJZfNfZ1ibOiWQu5nPghZA+eRLSWm+4QnjtmbaFVb+cb3p9HyNnlvcFCjVwJGYjlW6BbT+bqkoT8A1LVNFwtswd+9FEqm+G/BYJKLRYLHEvZvGiqFx81Y2XSorhAInvMSjFzW4nylb6RtZH0nuGUcLb0dzt/OG//Zr8yJBbXcRG1AAAAFQD63fOcG+OQ6hLoj3Uvez58HgsA7wAAAIEAjs0mwxbilSCXANtQpgLLWgQLXKlpZNpmg5WwLlWhmrPy0llq/9kijFBTaSJ4W6/T363wT6M4xwajDfDyoclzgW16RtmCGtAScGDk3RAQm/R7zKV5h0LKZKBN0b/RdhNUaYSTsJ9JXG+NV98lp+TkJ2bHaC3ffvplVMoFtoSl3TAAAACAZV3vv9lVPoFSHG5LKmh3TI2kIgJssIJCUbPWlMponkqLy5Cx9+xICY8r3zv/MeJK8FMwSuVObRZ1qygoc5OHaIiXHKLm5gW8HvdNzEltemDn//TiaT5HRrVaGY7Kxv1nEzvf+H7H23IQFPvm2NKn/WKGxvORtfj5FGWDpZQfyTA= pancho@client2

It is a good idea to set restrictive permissions on at least the private key and authorized_keys files, such as 0600. It may also be a good idea to change keypairs every now and then. ssh-keygen itself has many other options for managing the keys.

And Voila.. Log out of the server account and next time you log in from the client the server should prompt you for the password you used when the key-pair was generated (or not if none was used). The password-less part is especially useful for automated tasks but bad if someone gets a hold of the private key. So in general we want to keep using passwords, and all we’ve done with this whole key business is add another component to authentication making it a bit more secure. What a waste of time. But wait,.. ..Ladies & Gentlemen. In this corner, weighing in at 256 lbs, with 1024 victories, NaN defeats, hailing from inode 0xBADC0FFEE0DDF00D, block 0xdeadbeef, it’s,… S.S.H. Aagent!!… (cheers and applause) <ding>

ssh-agent

# calling ssh-add registers an identity with an ssh-agent for use in
# locally "un-locking" the private key for use in future log-ins. On
# my system, ssh-agent was already running as a daemon process and the
# necessary environment variables were set. On other systems it may be
# necessary to start/stop the agent through other means.
pancho@client$ eval $(ssh-agent -s) # one way to do this 
pancho@client$ env | grep SSH
SSH_AGENT_PID=1234
SSH_AUTH_SOCK=/tmp/ssh-nzpaAalp5678/agent.5678
pancho@client$ ssh-add -l
The agent has no identities.
pancho@client$ ssh-add ~/.ssh/id_dsa_key1
Enter passphrase for /home/pancho/.ssh/id_dsa_key1: 

Now as long as the agent is running and the registered identity is not deleted (which can be done), I can log in to the server with no password.. 😀

I have yet to investigate whether this will fail, but see no reason why identities for automated processes couldn’t also be registered once and remain usable as long as the same agent is running. Passwords would need to be re-entered if the machine is rebooted / ssh-agent killed / etc,..
wonderful key hanger by Janice Warren
There are many additional tools for managing ssh-agent and the “keychain” for logging into multiple accounts. They vary in degree of complexity and sophistication and include GUI apps, systems for managing large numbers of users across multiple networks and such, but I won’t go into them here.

As you can imagine, even with keys and ssh-agent, it can be a chore to keep track of all the keys, usernames, ip addresses/hostnames across all the client and server systems. Luckily SSH can use both a system-wide configuration file, and a per-user config to address this.

ssh_config

SSH_CONFIG(5)             Gentoo File Formats Manual             SSH_CONFIG(5)

NAME
     ssh_config -- OpenSSH SSH client configuration files
...
ssh obtains configuration data from the following sources in the following order:

1. command-line options

    2. user's configuration file (~/.ssh/config)
    3. system-wide configuration file (/etc/ssh/ssh_config)
...
The configuration file has the following format: 
...
Host' Restricts the following declarations (up to the next Host keyword) to be only for those hosts that match one of the patterns given after the keyword. '*' and '?' can be used as wildcards in the patterns. A single '*' as a pattern can be used to provide global defaults for all hosts. The host is the hostname argument given on the command line (i.e., the name is not converted to a canonicalized host name before matching). 
...
HostName
Specifies the real host name to log into. This can be used to specify nicknames or abbreviations for hosts. Default is the name given on the command line. Numeric IP addresses are also permitted (both on the command line and in HostName specifications). 
...
IdentityFile
Specifies a file from which the user's RSA or DSA authentication identity is read. The default is ~/.ssh/identity for protocol version 1, and ~/.ssh/id_rsa and ~/.ssh/id_dsa for protocol version 2. Additionally, any identities represented by the authentication agent will be used for authentication. The file name may use the tilde syntax to refer to a user's home directory. It is possible to have multiple identity files specified in configuration files; all these identities will be tried in sequence. 
...
User' Specifies the user to log in as. This can be useful when a different user name is used on different machines. This saves the trouble of having to remember to give the user name on the command line. 
...
SEE ALSO
     ssh(1)

AUTHORS
     OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen.
     Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed
     many bugs, re-added newer features and created OpenSSH.  Markus Friedl contributed the
     support for SSH protocol versions 1.5 and 2.0.

Gentoo/linux                     July 27, 2011                    Gentoo/linux

Yes, there are a ton of options. Many of them quite useful.. The ones shown in my copy & paste from the man page above are what I focused on for my purposes for now. Given a .ssh/config file like:

Host server1
   HostName example.com
   IdentityFile /home/pancho/.ssh/id_dsa_key1
   User willamina

Host server2
   HostName 203.0.113.123
   IdentityFile /home/pancho/.ssh/id_rsa_key2
   User tawhaki

Host daedalus
   HostName foo.org
   IdentityFile /home/pancho/.ssh/id_rsa_key3
   User curly

..I don’t have to keep track of all the hostnames/usernames/keys for all the accounts. Instead I can log-in with a simple “ssh daedalus”. Of course it is possible to use the same key for all the accounts. ssh will still prompt for the key passphrase every time if one was given, but that can be remedied with ssh-agent and ssh-add.

Finally a sample bit of scriptactic sugar to tie it all together (there are other apps/tools to address this such as this and this):

#!/bin/bash
# "host" here corresponds with host lines in local ssh_config file.
# if a key exists for the host, the user will be prompted for its
# passphrase on the 1st login for the running ssh-agent. Afterwards
# since the identity has been added no pass will be necessary

host="$1"

if [ -z "$host" ]; then
	echo 'usage: '`basename $0`' <host>'
	exit 1
fi

IdentityFile=`
	awk '	($1=="Host"){
			last_host=$2
		}
		($1=="IdentityFile" && last_host=="'$host'"){
			print $2
			exit 0
		}' $HOME/.ssh/config`


if [ -n "$IdentityFile" ]; then
	ssh-add -l \
	| grep -q "$IdentityFile" \
	||  ssh-add "$IdentityFile"
fi

/usr/bin/ssh "$host"
Posted in Uncategorized | 2 Comments

Diff’ing Data Needles Across 2 MySQL Haystacks

Recently I needed to compare a specific set of tables in two MySQL databases on a remote system where I did not have Admin privileges (but DB user credentials). Both databases had the same structure, just some different data. I didn’t want to see all the differences in all the tables because there were probably hundreds of them. Not having access to the server logs, I was considering doing some brute-force regex-driven diff of dump files or perhaps a complex SQL query. I knew there had to be a better way.

After fishing around I found a relatively stand-alone tool (Perl dependency, not much else) that could be set up and used quickly on a non-Admin *nix account: mysql_coldiff. It’s easy-to-use, well-documented, and most-importantly, scriptable..

Since I had about a dozen tables that I wanted to compare I wrote a wrapper script to automate the process and produce a nice report of the differences.

#!/bin/bash
DIFF_SCRIPT="$HOME/mysql_coldiff-1_0/mysql_coldiff"

db4credentials="$1"
query_list="$2"
out_file="$3"

if [ ! -e "$query_list" -o -z "$out_file" ]; then
	echo 'usage: '`basename $0`' <db4credentials> <query_list_file> <out_file>'
	echo '   db: seems only 1 set of credentials can be used for both DBs'
	echo '   query_list_file: list of tables to diff. 1st 2 lines are DB names'
	echo '      remaining lines have 3 fields per record:'
	echo '      <table_name> <id_column_name>  [IGNORE]'
	exit 1
else
	db1=`sed -ne '1 p' "$query_list"`
	db2=`sed -ne '2 p' "$query_list"`
fi

read -p "username for $db4credentials: " us
read -p "password for $db4credentials: " ps

awk '
	($0!="" && $NF!="IGNORE" && NR>2){
		gsub(/[\047"]/,"",$0) # strip devious chars for now
		print "------------------------------\n" $1 "\n------------------------------"
		cmd="'$DIFF_SCRIPT' -h localhost -u '$us' -p '$ps' -i " $2 " -n '$db1'." $1 " '$db2'." $1
		system(cmd);
		close(cmd)
}' "$query_list" > "$out_file"

This strategy seems to work best in situations where differences are minimal (but important). Case-in-point (or bird in hand?), I quickly found the modified field values in a table I had been looking for and changed them with phpMyAdmin. Here is the report I used to find those diffs (output condensed where “…” appears):

...
------------------------------
eav_attribute
------------------------------
We're comparing the the_database_number_1.eav_attribute using the attribute_id column and the database_number2.eav_attribute using the attribute_id column.
...
------------------------------
eav_attribute_group
------------------------------
...
------------------------------
eav_attribute_option
------------------------------
...
------------------------------
eav_entity_text
------------------------------
We're comparing the the_database_number_1.eav_entity_text using the value_id column and the database_number2.eav_entity_text using the value_id column.
...
------------------------------
eav_entity_type
------------------------------
We're comparing the the_database_number_1.eav_entity_type using the entity_type_id column and the database_number2.eav_entity_type using the entity_type_id column.
...
+---------------------------------------+----------------+------------------------------+
|                                       | entity_type_id |              increment_model |
| the_database_number_1.eav_entity_type +----------------+------------------------------+
|_______________________________________|             11 | eav/entity_increment_numeric |
|                                       |             11 | eav/entity_increment_alphabetic
|      database_number2.eav_entity_type +----------------+------------------------------+
|                                       | entity_type_id |              increment_model |
+---------------------------------------+----------------+------------------------------+
...

Gotchas?

  • Had to use the same login credentials for both DBs. May be possible to use 2 sets?
  • Not sure if mysql_coldiff handles tables where the primary key is not a single identity column, i.e.; tables that use a composite primary key. From glancing over the mysql_coldiff documentation I think it may not matter too much what you choose as the “index column”, but I figured it probably did, that’s why I added the “IGNORE” param to the input file for my wrapper script when I found that one of the tables I was hoping to compare had a composite primary key.
  • There’s always ways to further investigate certain problems and solve them better. But if it ain’t broke neither are you.
Posted in Uncategorized | Leave a comment