9.12.2019

Green Chile, no crap added



Green chile is awesome.   New Mexico or Colorado chiles, both are great, and neither need to be dressed up with a ton of other ingredients to make an awesome green chile sauce.   In other words, no cumin, no chili powder, no tomatoes, etc. etc.  Here's how to make a batch of minimalist and perfect green chile.  My favorites are Mirasol or Mosco chiles from Pueblo, or Hatch (medium/hot/x-hot, whatever) from New Mexico.  We are blessed here in Denver to have an abundance of choices available every September.  If you need some extra heat, you can add a couple of Dynamite chiles, and if you like it without too much spice, go for a variety like Anaheim or Hatch mild.    Pork green chile is great, but I generally make this sauce without meat, as it keeps longer in the refrigerator, where it is a continual staple.

Ingredients:

3T Canola oil
3T flour (can use GF flour blend, no problem)
1/2 white onion chopped
1T garlic minced
~2 cups fresh roasted green chile chopped. 
2-3 cups chicken broth (can substitute vegetable broth/stock)
Kosher salt to taste

Optional - 1.5 cups stewed pork(ideal) / chicken / beef.   If you go the meat chile route, use the cooking stock from the meat for your liquid above instead of the chicken broth.



Start by cooking the onions and garlic in the oil over medium or medium-low heat, sweat the onions until translucent.  The garlic should be barely browned at this point as well.


Add the green chiles to the pot, and heat thoroughly over Medium heat.


Once the chiles are heated through, add the flour, and stir constantly, scraping the bottom of the pot as you stir.   Cook for 2-3 minutes, just long enough to cook the raw flour taste out.   The ingredient mixture will be pasty.   Do not brown the flour in the mixture.


Add the stock to the pot slowly, stirring continually.   Reserve some stock, keeping the mixture fairly thick (start with 2-2 /2 cups).  Increase heat to high and bring to a boil, then immediately lower heat to simmer.  The mixture will thicken.   Add reserved stock a little at a time to bring to desired consistency.   Add your stewed meat if desired, and simmer as long as you'd like.   Add salt to taste.




Ready to Eat!


2.04.2013

Cisco VOIP phone device information gathering

Our network team needed to true-up maintenance support, and validate inventory for our 6000+ Cisco IP Phones.


So I wrote a python script to provide the following information, via switch CDP discovery and dumping the phone's device details via http from the phones themselves:
  • Switch, Port
  • PhoneName
  • IP
  • Platform(From CDP)
  • SerialNumber
  • MACAddress
  • ModelNumber 

In a nutshell, the script performs the following:
  • logs into each of the access switches provided (fed with file input, one per line) 
  • via CDP neighbor discovery grabs all devices named SEP* (standard Cisco convention)
  • from that list, runs "cdp neighbor <int> detail" to get Platform and IP info for each discovered phone
  • from the IP information, grabs the devices extended information via http (xml output)
  • parses the xml and returns output as a csv file suitable for importing to a spreadsheet



#!/usr/bin/env python
"""
Mike Orstead
01/2013
http://stewpid-litterbox.blogspot.com/

Utility script to get Cisco IP phone device information through a combination 

of Cisco switch info (CDP) and http grabs from the device themselves (xml).

Pre-reqs:

-switch access with a common username/password.  User needs ability to run 
cdp neighbor discover and "term len 0" (so command output is not run 
through a pager) 
-Update inputfile(s) below with site specific info (or modify to accept file location via user input)

NOTES and WARNINGS:-This script instance assumes that the CDP discover finds phones with 

device names SEP*, script may be easily modified for other conventions.
-We have Cisco Video Conferencing devs that need to be handled, as IP address 
information is not returned with CDP (CTS_CODEC). Other devices may need to 
be handled similarly in your environment. Search reCODEC in the code below.


"""

# Import Python Modules
import os, datetime
import sys
import pexpect
import re
import getpass
import urllib
from lxml import etree

date = datetime.datetime.now()

datestamp = date.strftime("%Y%m%d") 

def main():

    source_choices = 'Run script against Site1 or Site2 switches? '
    while True:
        source_answer = raw_input(source_choices).lower()
        if source_answer == 'site1':
            source = 'Site1'
            break
        elif source_answer =='site2':
            source = 'Site2'
            break
        else:
            print "Invalid choice" + "\n"

    userName = raw_input('Username: ')

    userPassword = getpass.getpass('Password: ')

    opspath = '/example-path/'

    outputpath = opspath+'CIPphoneinfo_Output/'

    # get the switches from proper inputfile

    if source == 'Site1':
        # TODO: should have a 'try' here
        inputfile = open(opspath+'Site2_ACCESS_SWITCHES.txt')
        ofile = 'cipinfo_Output_Site1.' + datestamp + '.csv'
        outputfile = file(outputpath+ofile,'w')
    elif source == 'Site2':
        inputfile = open(opspath+'Site2_ACCESS_SWITCHES.txt')
        ofile = 'cipinfo_Output_Site2.' + datestamp + '.csv'
        outputfile = file(outputpath+ofile,'w')

    output = {}

    for hostname in inputfile:
        phonelist = []
        hostname = hostname.rstrip('\n')
        logfile = sys.stdout
        tmpf = os.tmpfile()
        print 'Connecting to switch: ' +  hostname + ' and discovering phones'
        # TODO:  should catch a TIMEOUT here in the pexpect
        router = pexpect.spawn('ssh '+userName+'@'+hostname)
        router.expect('word:')
        router.sendline(userPassword)
        router.expect('#')
        router.sendline('term len 0')
        router.expect('#')

        # Gather device name / port 

        router.logfile = tmpf
        router.sendline('show cdp neighbor | include SEP')
        router.expect('#')
        router.logfile = None

        rePrompt = re.compile(hostname)


        #output = {}

        # iterate through the tmpfile, build the output dict entries
        reEndLine = re.compile('\r\n')
        reShowCDP = re.compile(r'show cdp neighbor*')
        reCODEC = re.compile(r'CTS-CODEC')
        tmpf.seek(0)
        buf = tmpf.readlines()

        for line in buf:

            line = reEndLine.sub('', line)
            # Skip these conditions from further discovery:
            if re.match(reShowCDP,line): continue
            if re.match(rePrompt,line): continue
            if re.search(reCODEC,line): continue

            output[line.split()[0]] = {

                "switch":   hostname, 
                "name":     line.split()[0],
                "port":     line.split()[1] + " " + line.split()[2]
                }
            phonelist.append(line.split()[0])
        tmpf.close()
        
        i = 0
        for x in output:
            if output[x]['switch'] == hostname:
                i += 1
        numberphones = i
        
        print "Found " + str(numberphones) + " phones on " + hostname
    
        print "Grabbing phone details from switch " + hostname + ", and connecting to phones web interfaces for extended info"
        for phone in phonelist:
            tmpf2 = os.tmpfile()
            router.logfile = tmpf2
            router.sendline('show cdp neighbor '+output[phone]['port']+' detail')
            router.expect("#")
            router.logfile = None

            reEndLine2 = re.compile('\r\n')

            reShowCDP2 = re.compile(r'show cdp neighbor*')
            rePrompt2 = re.compile("^.*#")
            tmpf2.seek(0)
            buf = tmpf2.readlines()
            for line in buf:
                line = reEndLine2.sub('', line)
                if re.match(reShowCDP2,line): continue
                if re.match(rePrompt2,line): continue
                findIP = re.search('\s+IP address:\s+(\d+\.\d+\.\d+\.\d+)',line)
                findPlat = re.search('Platform:\s+(.+),',line)
                if findPlat:
                    output[phone]['platform'] = findPlat.group(1)
                if findIP:
                    output[phone]['ipaddy'] = findIP.group(1)
                    # connect to http://<ipaddy>/DeviceInformationX to grab xml
                    url = 'http://' + output[phone]['ipaddy'] + '/DeviceInformationX'
                    try:
                        x = urllib.urlopen(url)
                    except IOError:
                        output[phone]['serialnum'] = "UNKNOWN - can't connect to " + url
                        output[phone]['modelnum'] = "UNKNOWN - can't connect to " + url
                        output[phone]['MAC'] = "UNKNOWN - can't connect to " + url
                        continue

                    try:

                        xmltree = etree.parse(x)
                    except:
                        # General Error catchall
                        output[phone]['serialnum'] = "UNKNOWN - can't parse xml"
                        output[phone]['modelnum'] = "UNKNOWN - can't parse xml"
                        output[phone]['MAC'] = "UNKNOWN - can't parse xml"
                        continue
                    else:
                        for ele in xmltree.iter("serialNumber"):
                            output[phone]['serialnum'] = ele.text
                        for ele in xmltree.iter("MACAddress"):
                            output[phone]['MAC'] = ele.text
                        for ele in xmltree.iter("modelNumber"):
                            output[phone]['modelnum'] = ele.text
            tmpf2.close()
            
    # outfile header row
    outputfile.write("Switch,Port,PhoneName,IP,Platform(From CDP),SerialNumber,MACAddress,ModelNumber\n")  
    # Populate UNKNOWN where keys are missing. 
    for key in sorted(output.iterkeys()):
        if not output[key].has_key('serialnum'): output[key]['serialnum'] = "UNKNOWN"
        if not output[key].has_key('platform'): output[key]['platform'] = "UNKNOWN"
        if not output[key].has_key('ipaddy'): output[key]['ipaddy'] = "UNKNOWN"
        if not output[key].has_key('modelnum'): output[key]['modelnum'] = "UNKNOWN"
        if not output[key].has_key('MAC'): output[key]['MAC'] = "UNKNOWN"
        outputfile.write(output[key]['switch']+","+output[key]['port']+","+output[key]['name']+","+output[key]['ipaddy']+","+output[key]['platform']+","+output[key]['serialnum']+","+output[key]['MAC']+","+output[key]['modelnum']+"\n")

    print "script complete - results can be found in: "+opspath+'CIPphoneinfo_Output/'+ofile

    router.sendline ('exit')
    sys.exit(0)

if __name__ == "__main__":

    # We are running this module directly, rather than via import
    main()

5.09.2012

recovering from ubuntu 12.04 upgrade issues

Following upgrade to Ubuntu 12.04 LTS, my Dell Latitude E6400 had issues with hardware recognition, namely trackpad and USB mouse input.  This is a pretty vanilla laptop, that I don't mind re-installing.  Being too lazy to troubleshoot deeply my response was to try and fix any broken package dependencies:

 $ sudo apt-get -f install
That resulted in a leading error message pointing to broken dpkg configuration, and that was easily fixed by running:

$ sudo dpkg --configure -a

Then, re-ran the apt-get -f install  (-f is --fix-broken).   That ran successfully to completion, so cleaned up any cruft from the install/fix:

$ sudo apt-get autoremove

Then, rebooted, and all was rainbows and unicorn farts.   Mouse and trackpad input restored.


5.02.2012

Capture login session with "script", chroot jail


This documents the process to restrict a user to a chroot'd env, log their session with script, and allow only ssh activities.  Specifically, I use it to lock down and track external vendors or consultants.

Tested on Red Hat 6.2, but it should work on lower RH versions just fine.  On systems without SELinux, remove those options from the commands below.

Here's the basic setup:

  • rvendor is our user for this example
  • chrooters is the group containing all of our restricted users
  • login home directory: /home/rvendor/
  • chroot directory: /home/restricted_users/chroot_jails/rvendor/
  • chroot and 'script' script location: /home/restricted_users/scripts/
  • typescript output files from 'script': /home/restricted_users/script_files/
  • all the user has access to in the chroot jail are the bash builtins, ssh, and su.  Not 100% foolproof, by any means, but it's decent.



Create the account, and a group (to be used for sudo later)

    $ grep rvendor /etc/passwd
    rvendor:x:9999:9999:chrootd vendor account:/home/rvendor:/bin/bash
    $ grep chrooters /etc/group
    chrooters::9999:


Create a script to do the actual chroot and log via "script"

    $ sudo cat /home/restricted_users/scripts/chrooter
    !/bin/bash
    FILENAME=`date +%Y%m%d-%H.%M.%S`-$USER.$$
    /usr/bin/script -qaf /home/restricted_users/script_files/$USER/$FILENAME -c "/usr/bin/sudo /usr/sbin/chroot /home/restricted_users/chroot_jails/$USER /bin/su - $USER " exit



Create the directory for the chroot'd env


    $ sudo mkdir -p /home/restricted_users/chroot_jails/rvendor/{bin,dev,etc,lib64,usr,var}
    $ sudo ln -s /home/restricted_users/chroot_jails/rvendor/usr/bin /bin
    $ sudo ln -s /home/restricted_users/chroot_jails/rvendor/usr/lib64 /lib64
    $ sudo chown -R nobody:chrooters /home/restricted_users/chroot_jails/rvendor
    $ sudo chmod -R 070 /home/restricted_users/chroot_jails/rvendor
    $ sudo ls -la  /home/restricted_users/chroot_jails/rvendor
    total 28
    d---rwx---. 7 nobody chrooters 4096 Apr 24 14:41 .
    drwxr-xr-x. 7 root   root      4096 Apr 24 14:41 ..
    d---rwx---. 2 nobody chrooters 4096 Apr 24 14:41 bin
    d---rwx---. 2 nobody chrooters 4096 Apr 24 14:41 etc
    d---rwx---. 2 nobody chrooters 4096 Apr 24 14:41 lib64
    d---rwx---. 2 nobody chrooters 4096 Apr 24 15:03 usr
    d---rwx---. 2 nobody chrooters 4096 Apr 24 14:41 var


Set up the output area for the script sessions:

    $ sudo mkdir -p /home/restricted_users/script_files/rvendor
    $ sudo chown rvendor /home/restricted_users/script_files/rvendor


Setup sudo so that the chrooters group can run chroot with no passwd

    $ sudo visudo
    >>
    %chrooters         ALL=NOPASSWD: /usr/sbin/chroot


Create the jailed account in the chrooted env

    $ sudo cat /home/restricted_users/chroot_jails/rvendor/etc/passwd
    root:LK:0:0:root:/:/bin/bash
    rvendor:x:9999:9999:chrootd vendor account:/:/bin/bash
    $ sudo cat /home/restricted_users/chroot_jails/rvendor/etc/group
    root::0:root
    chrooters::9999:


Add the required binaries  and libs (bash, ssh)
***** cp -pc to preserver file context + permissions for SELinux *****

    $ ldd /bin/bash
    linux-vdso.so.1 =>  (0x00007fffca3ff000)
    libtinfo.so.5 => /lib64/libtinfo.so.5 (0x0000003b7a200000)
    libdl.so.2 => /lib64/libdl.so.2 (0x0000003b78200000)
    libc.so.6 => /lib64/libc.so.6 (0x0000003b78600000)
    /lib64/ld-linux-x86-64.so.2 (0x0000003b77e00000)


    $ sudo cp -pc /bin/bash /home/restricted_users/chroot_jails/rvendor/bin/bash
    $ sudo cp -pc /lib64/libtinfo.so.5 /lib64/libdl.so.2 /lib64/libc.so.6 /lib64/ld-linux-x86-64.so.2 /home/rvendor/lib64

    $ sudo cp -pc /usr/bin/ssh /home/restricted_users/chroot_jails/rvendor/usr/bin/ssh
    $ for x in `ldd /usr/bin/ssh | cut -d' ' -f3 |grep ^/` ; do sudo cp -pcH $x /home/rvendor/$x ; done

    $ sudo ls -laZ /home/restricted_users/chroot_jails/rvendor/lib64
    d---rwx---. nobody chrooters system_u:object_r:default_t:s0   .
    d---rwx---. nobody chrooters system_u:object_r:default_t:s0   ..
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       ld-linux-x86-64.so.2
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libcom_err.so.2
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libcrypto.so.10
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libcrypt.so.1
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libc.so.6
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libdl.so.2
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libfipscheck.so.1
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libfreebl3.so
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libgssapi_krb5.so.2
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libk5crypto.so.3
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libkeyutils.so.1
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libkrb5.so.3
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libkrb5support.so.0
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libnsl.so.1
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libnspr4.so
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libnss3.so
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libnssutil3.so
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libplc4.so
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libplds4.so
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libpthread.so.0
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libresolv.so.2
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libselinux.so.1
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libtinfo.so.5
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libutil.so.1
    -rwxr-xr-x. root   root      system_u:object_r:lib_t:s0       libz.so.1 



The Red Hat coreutils su is a bit different, as it is compiled with pam.
So, we'll compile our own to simplify, and remove the pam dependency.


    $ wget http://ftp.gnu.org/gnu/coreutils/coreutils-8.16.tar.xz
    --2012-05-01 12:12:42--  http://ftp.gnu.org/gnu/coreutils/coreutils-8.16.tar.xz
    Resolving ftp.gnu.org... 208.118.235.20, 2001:4830:134:3::b
    Connecting to ftp.gnu.org|208.118.235.20|:80... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 5042844 (4.8M) [application/x-tar]
    Saving to: “coreutils-8.16.tar.xz”
    100%[========================================================>] 5,042,844   --.-K/s   in 0.09s  
    2012-05-01 12:12:43 (51.3 MB/s) - “coreutils-8.16.tar.xz” saved [5042844/5042844]
    $ tar xvJf coreutils-8.16.tar.xz
    $ cd  coreutils-8.16
    $ ./configure
    $ make


Copy the su binary into position:

    $ sudo cp src/su /home/restricted_users/chroot_jails/rvendor/bin/
     Give it perms and context to match system install:
    $ sudo chmod --reference=/bin/su /home/restricted_users/chroot_jails/rvendor/bin/su
    $ sudo chcon --reference=/bin/su /home/restricted_users/chroot_jails/rvendor/bin/su
    $ sudo ls -alZ /bin/su /home/restricted_users/chroot_jails/rvendor/bin/su
    -rwsr-xr-x. root root system_u:object_r:su_exec_t:s0   /bin/su
    -rwsr-xr-x. root root system_u:object_r:su_exec_t:s0   /home/rvendor/bin/su


Check the libs to make sure we have what we need

    $ sudo ldd /home/restricted_users/chroot_jails/rvendor/bin/su
        linux-vdso.so.1 =>  (0x00007fff1cf2f000)
        libcrypt.so.1 => /lib64/libcrypt.so.1 (0x0000003b7ae00000)
        libc.so.6 => /lib64/libc.so.6 (0x0000003b78600000)
        libfreebl3.so => /lib64/libfreebl3.so (0x0000003b7b200000)
        /lib64/ld-linux-x86-64.so.2 (0x0000003b77e00000)
        libdl.so.2 => /lib64/libdl.so.2 (0x0000003b78200000)


Now we need is the libs to get us to talk to /etc/passwd /etc/group in the chroot jail

    $ sudo cp -cp /lib64/libnss_files.so.2 /lib64/libnss_compat.so.2 /home/restricted_users/chroot_jails/rvendor/lib64

   
ssh needs some entropy, so, we'll create a /dev/random, urandom, /dev/null devices
(man 4 random, man 4 null)

    $ sudo mknod -m 644 /home/restricted_users/chroot_jails/rvendor/dev/random c 1 8
    $ sudo mknod -m 644 /home/restricted_users/chroot_jails/rvendor/dev/urandom c 1 9  
    $ sudo mknod -m 666 /home/restricted_users/chroot_jails/rvendor/dev/null c 1 3
    $ sudo mknod -m 666 /home/restricted_users/chroot_jails/rvendor/dev/tty c 5 0


We need an ssh directory

    $ sudo mkdir /home/restricted_users/chroot_jails/rvendor/.ssh
    $ sudo chown rvendor.chrooters /home/restricted_users/chroot_jails/rvendor/.ssh
    $ sudo chmod 700 /home/restricted_users/chroot_jails/rvendor/.ssh

And start the known_hosts file:

    $ sudo touch  /home/restricted_users/chroot_jails/rvendor/.ssh/known_hosts
    $ sudo chown rvendor:chrooters \
       /home/restricted_users/chroot_jails/rvendor/.ssh/known_hosts
    $ sudo chmod 644 /home/restricted_users/chroot_jails/rvendor/.ssh/known_hosts




OK, at this point, the chroot jail is completely functional.   All that are available are bash builtins, and ssh, su.   We could firm it up even more by using rbash.... but then, need to move the chrooter script to the rvendor home dir.  No biggy, just not really needed at this point


Set up the user's .bash_profile to launch the chrooter script.  End the file with 'exit', so bash session log's out when script session terminates.  This effectively locks this user to only running within script session, chroot'd.

    $ sudo vi /home/rvendor/.bash_profile
    $ sudo cat  /home/rvendor/.bash_profile
    /home/restricted_users/scripts/chrooter
    exit


We'll also set the PS1 prompt to include hostname and time stamp to make tracking activity easier.   You'll need to have this in the .bash_profile (or .bashrc) on any servers downstream that the account has access to, to be useful.

    $ sudo vi /home/restricted_users/chroot_jails/rvendor/.bash_profile
    $ sudo cat  /home/restricted_users/chroot_jails/rvendor/.bash_profile
    PS1="[\t \h] \w $ "
    export PS1


Finally, create an /etc/hosts file in the chroot env with the hosts that they are going
to be connecting to.

    $ sudo vi /home/restricted_users/chroot_jails/rvendor/etc/hosts
    $ sudo cat /home/restricted_users/chroot_jails/rvendor/etc/hosts
    XXX.XXX.XXX.XXX thatserver




Here are the results

$ sudo su - rvendor
< do some stuff, ssh out of the host>


And the resulting script file:

$ sudo cat  /home/restricted_users/script_files/rvendor/20120502-13.04.00-rvendor.30824
Script started on Wed 02 May 2012 01:04:00 PM MDT
[19:04:00 thisserver] / $ ls -al
-bash: ls: command not found
[19:04:10 thisserver] / $ cd etc
[19:04:12 thisserver] /etc $ ls
-bash: ls: command not found
[19:04:12 thisserver] /etc $ cat hosts
-bash: cat: command not found
[19:04:17 thisserver] /etc $   <this is a TAB/TAB bash completion to show built-ins>
!          bind       compopt    elif       fc         if         printf     set        time       unset
./         break      continue   else       fg         in         pushd      shift      times      until
:          builtin    coproc     enable     fi         jobs       pwd        shopt      trap       wait
[          caller     declare    esac       for        kill       read       source     true       while
[[         case       dirs       eval       function   let        readarray  ssh        type       {
]]         cd         disown     exec       getopts    local      readonly   su         typeset    }
alias      command    do         exit       hash       logout     return     suspend    ulimit    
bash       compgen    done       export     help       mapfile    script     test       umask    
bg         complete   echo       false      history    popd       select     then       unalias  
[19:04:43 thisserver] /etc $ cd
[19:04:46 thisserver] / $ ssh someuser@thatserver
The authenticity of host 'thatserver (XXX.XXX.XXX.XXX)' can't be established.
RSA key fingerprint is 99:67:aa:83:60:fd:2e:2c:81:5f:cc:f4:e3:87:db:49.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'thatserver' (RSA) to the list of known hosts.
Password:
Last login: Wed May  2 13:05:09 2012 from thisserver
[13:05:10 thatserver] ~ $ hostname
thatserver
[13:05:11 thatserver] ~ $ date
Wed May  2 13:05:12 MDT 2012
[13:05:12 thatserver] ~ $ blah blah blah
-bash: blah: command not found
[13:05:16 thatserver] ~ $ exit
logout
Connection to thatserver closed.
[19:05:17 thisserver] / $ exit
logout

4.18.2012

one-liner 'in-place' file edits

several ways to skin ze cat, and get a tasty snack:

$ cat alchemy
turn lead to gold

python:
$ python -c "import sys,fileinput,re;[sys.stdout.write(re.sub('lead','sardines',line)) for line in fileinput.input('alchemy',inplace=1)]"

$ cat alchemy
turn sardines to gold

perl:
$ perl -p -i -e"s#gold#cracker topping#" alchemy
$ cat alchemy
turn sardines to cracker topping

gnu sed:
$ sed -i -e "s/sardines/cream\ cheese/" alchemy
$ cat alchemy
turn cream cheese to cracker topping

good ol' ed:
$ printf '%s\n' ',s/cracker\ topping/cheesecake/' w q | ed -s alchemy
$ cat alchemy
turn cream cheese to cheesecake

So, the next time you are asked to turn lead to cheesecake, it will be no real problem.




2.20.2008

python: reference anonymous list created by list comprehension

When using a list comprehension, the list created under the hood can be referred to with locals()['_[1]']. In the case of nested elements, the elements are named _[2], _[3], etc, as you delve deeper into the nested items. The 'anonymous' list exists only while the list comprehension executes,ie. only during the work between '[' and ']'. It's sometimes handy to be able to refer to this list for in-line processing during the list comprehension. Here's an easy example. Let's say I wanted to join three lists together, removing any duplicate elements:

>>> L1 = [ 1,2,3 ]
>>> L2 = [ 2,3,4 ]
>>> L3 = [ 2,4,5 ]
>>> [ x for x in L1 + L2 + L3 if x not in locals()['_[1]'] ]
[1, 2, 3, 4, 5]

Now, the 'gotcha' is that locals()['_[n]'] is not just incremented for each nested list comprehension, it's incremented whenever a list comprehension is used in the local scope. So, if you've executed four list comprehensions previously in the run of the local scope, your next one is referenced as locals()['_[5]']. This is most evident within a loop:
>>> for x in [1,2,3]:
... [ x for x in l1 + l2 + l3 if x not in locals()['_[1]'] ] #this will work
... [ x for x in l1 + l2 + l3 if x not in locals()['_[1]'] ] #this key no longer exists
...
[1, 2, 3, 4, 5]
Traceback (most recent call last):
File "", line 3, in
KeyError: '_[1]'

>>> for x in [1,2]:
... [ x for x in l1 + l2 + l3 if x not in locals()['_[1]'] ]
... [ x for x in l1 + l2 + l3 if x not in locals()['_[2]'] ] #here's the increment
...
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]




2.06.2008

Prettying up my ion3 desktop

Every once in a while, I get a wild hair to change up my desktop. Every time I've played with an xterm replacement that supports transparency, I generally get annoyed within hours, being too lazy to actually spend any time tweaking it. For whatever reason, today I bit the bullet, and took a couple of minutes set up aterm to be useable. There was actually no excuse for my earlier laziness - all my 'problems' were settled with just a few entries to ~/.Xdefaults:

! For aterms:
Aterm*loginShell: true
Aterm*saveLines: 2000
Aterm*foreground: DeepSkyBlue
Aterm*transparent: true
Aterm*font: fixed
Aterm*scrollBar: false
Aterm*backspacekey: ^H
Aterm*borderless: true
Aterm*internalBorder: 0
Aterm*termName: xterm
Aterm*fading: 80
Aterm*shading: 50
These take care of the annoyances (such as the character mapping for my backspace key changing within a vi session).
Then, enabling transparency in empty frames for ion3 brings back the old school behavior (well ion2 old school =]):

In your styles look_.lua:

de.defstyle("frame", {
background_colour = "#000000",
--transparent_background = false,
transparent_background = true,
})

Then, what the heck, I dl'd some darker gtk-2 themes, so that pidgin, gqview, firefox, etc would be happy.

That brought me to this place, and nothing annoys me yet. No guarantees about next week. =]