Tuesday, August 23, 2011

A Fiercer Way To Detect a CD-ROM/DVD Driver Letter Under Cygwin/MinGW

This works even under MinGW and has a wicked bit of AWK to parse Unicode cr*p:
#!/bin/sh
reg query 'HKLM\SYSTEM\MountedDevices' | \
awk 'BEGIN { letter=""; }
/DosDevices\\[D-Z]:/{
d=$1; a=$NF;
dr=substr(d, length("\\DosDevices\\")+1, 1);
i=0; str="";
while(length(a) > 0) {
c=substr(a, 0, 2); a=substr(a,3);
if((++i%2)==0) { continue; }
str = str sprintf("%c", strtonum("0x" c));
}
if(verbose) { print dr ": " str > "/dev/stderr"; }
if(tolower(str) ~ /cdrom/) { letter=dr; }
}
END {
if(length(letter) > 0) { print letter; }
else { exit 1; }
}
-ulianov

Monday, August 22, 2011

On Guilty Perl/Win32 Pleasures

I've been messing with PerlApp-packaged gui Perl apps for a while and I was annoyed that stderr output (useful when debugging) was not available when having the exe type set to Win32.

I have just remembered about an obscure W*ndows feature: debug messages (a lame-arse feature cloning syslogd(8) and only available in a debugger). So I set myself to use this having fond a debug message viewer http://alter.org.ua/soft/win/dbgdump/DbgPrnHk_v9a_all.rar.

The question was how to log to stderr when running as a console app (under perl.exe) and to the debug message subsystem if running as a non-console app (unde wperl.exe)?

I found no direct answer but kernel32!GetConsoleTitle can be used in an indirect way to answer this question:
#!perl -w
use strict;
use Win32::API;
use File::Basename qw(basename);
my $myself = basename($0);
Win32::API->Import("kernel32", 'OutputDebugStringA', 'P', 'V');
Win32::API->Import("kernel32", 'GetConsoleTitle', 'PN', 'I');
sub DbgPrint
{
OutputDebugStringA("$myself\[$$\]: ".join('' => @_)."\r\n");
}
sub isConsole()
{
my $title = 'x' x 128;
my $r = GetConsoleTitle($title, 128);
return if $r == 0;
return if $title =~ /^x+x$/;
return 1;
}
sub Log
{
not @_ and return;
return print(STDERR @_, "\n") if isConsole();
return DbgPrint(@_);
}
main::Log "Testing";
I must say that Dave Roth's book Win32 Perl Scripting: The Administrator's Handbook was an eye opener to all sorts of deliciously perverse Perl/Win32 programming tidbits.

-ulianov

P.S. Why not use the Event Log subsystem? Because is sucks even more than the debug messages subsystem! and because I like obscure features and because the debug messages are not on-disk persistent.

Friday, August 19, 2011

A Smart but Neglected BASH Feature

I had a humongous shell script and a wish to redirect stderr for a boat load of commands in one fell swoop. I could have used the {} grouping but for obscure reasons it was not appropriate.

The answer was to write a Bash extension in C which takes advantage of two things:
a) Bash does not fork(2) when it executes an extension so it's in-process;
b) dup2(2)
so it's possible to do funky things with the file descriptors and get away with it.

Alas on Win32 Cygwin's bash does not load this extension (MinGW does) and dup2(2) is just borked. Blame M$ for designing a braindead C library and OS.

Here's the code:
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <asm/fcntl.h>
#include <errno.h>

#include "builtins.h"
#include "shell.h"

// Compile on Linux/x86:
// gcc -I/tmp/bash-3.2 -I/tmp/bash-3.2/include fdmangle.c -fpic -c
// ld -x -Bshareable -o fdmangle.so fdmangle.o
// Use in shell scripts:
// enable -f ./fdmangle.so fdmangle
// fdmangle 2 stderr

extern char **make_builtin_argv(); // Bash-ism

static int verbose = 0;

static int fdmangle_main(int argc, char **argv)
{
if(argc < 3) {
return 1;
}

int n = 1;
if(!strcmp(argv[1], "-v")) { verbose = 1; n++; }

if(verbose && argc < 4) {
return 1;
}

const int fdn = atoi(argv[n]);
const char* file = argv[n+1];

if(verbose) fprintf(stderr, "fdmangle %d -> %s\n", fdn, file);

int flags = O_CREAT | O_WRONLY | O_APPEND;
#ifdef __unix__
flags |= O_NOFOLLOW;
#endif
int fd = open(file, flags, 0640);
if(fd < 0) {
fprintf(stderr, "Cannot open for writing %s: %d (%s)\n", file, errno, sys_errlist[errno]);
}

dup2(fd, fdn);

return 0;
}

static int fdmangle_builtin(WORD_LIST *list)
{
char **v=NULL;
int c=0, r=0;

v = make_builtin_argv(list, &c);
r = fdmangle_main(c, v);
free(v);

return r;
}

static char* fdmangle_doc[] = {
"File descriptor mangling",
(char *)0
};

struct builtin fdmangle_struct = {
"fdmangle",
fdmangle_builtin,
BUILTIN_ENABLED,
fdmangle_doc,
"fdmangle [-v] fd file",
0
};
-ulianov

Monday, August 8, 2011

How To Detect a CD-ROM/DVD Driver Letter Under Cygwin

While automating a provisioning process I stumbled upon this issue... How do you know what drive letter is a CD-ROM?

The first try was:
grep -qw iso9660 /proc/mounts || return 1;

echo $(mount | awk '/iso9660/{print $3}')
which happens to work when a disk is inserted in the drive.

At my deliverables demo the disk was not present so bummer.

The second uses the Registry and works (in XP):
    cdrom='';

for dr in D E F G H I J K L M N O P Q R S T U V W X Y Z
do
[ -e /proc/registry/HKEY_LOCAL_MACHINE/SYSTEM/MountedDevices/%5CDosDevices%5C${dr}%3A ] || continue;
tr -d '\00' < /proc/registry/HKEY_LOCAL_MACHINE/SYSTEM/MountedDevices/%5CDosDevices%5C${dr}%3A | grep -qi cdrom || continue;
cdrom="$dr";
done
[ -z "$cdrom" ] && return 1;
echo /cygdrive/$(echo $cdrom | tr 'A-Z' 'a-z');
Brutal, eh?

Anyways
grep -qw iso9660 /proc/mounts || return 1;
is a great way to check whether a disk is in the unit (any unit).

-ulianov