Marco's Corner

Just another Software Developer's musings

Browsing Posts published by admin

I’m currently building Solaris IPS packages for Some Ruby gems. Yes, I know rubygems does a very good job of keeping everything together. But that does not work so well on backend systems where you don’t have a free internet access and where repeatability is important. So I decided to wrap the gems we need into IPS packages.

The IPS pkg* tools work pretty well when they try to find dependencies between different packages automatically. But so far, they claim to do it only for ELF binaries and Python files. So for Ruby, I would either have to specify dependencies manually (and miss half of them) or try to find a way to inspect the Ruby files as well.

So I tried to get some bash lines together to do what I wanted;-) They will probably not be complete yet!! But so far, they work pretty well. I also found, that the pkgdepend does not really handle the #!/usr/bin/env ruby code found in some scripts, so I tried to fix that as well.

So for the old Mongrel gem, the following dependencies were automatically created;-) I think that’s pretty complete.

depend fmri=pkg:/my_prefix/ruby/gem/cgi_multipart_eof_fix@2.5.0-0.1 type=require
depend fmri=pkg:/my_prefix/ruby/gem/daemons@1.1.6-0.1 type=require
depend fmri=pkg:/my_prefix/ruby/gem/fastthread@1.0.7-0.1 type=require
depend fmri=pkg:/my_prefix/ruby/gem/gem_plugin@0.2.3-0.1 type=require
depend fmri=pkg:/my_prefix/ruby/rubygems@1.6.2-0.1 type=require
depend fmri=pkg:/my_prefix/ruby@1.8.7.357-0.1 type=require
depend fmri=pkg:/system/library/math@0.5.11-0.174.0.0.0.0.0 type=require
depend fmri=pkg:/system/library@0.5.11-0.175.0.2.0.3.1 type=require
depend fmri=pkg:/system/linker@0.5.11-0.175.0.2.0.2.1 type=require

Most IPS guides specify to run pkgdepend generate and then pkgdepend resolve, the following code would go right in between those two steps.

pkgdepend generate -md $DESTDIR/dist/ $DESTDIR/pkg/$SW.p5m.2 | \
        pkgfmt > $DESTDIR/pkg/$SW.p5m.3

# XXX handle the usr/bin/env ruby case
dest_prefix=`echo $DEST | sed -e 's:^/::'`
mv $DESTDIR/pkg/$SW.p5m.3 $DESTDIR/pkg/$SW.p5m.3.orig
awk -F= -e "
        BEGIN {
                e = 0;
        }
        \$4 ~ /^env / {
                e = 1;
                printf \"%s=%s=%s=ruby \\\\\n\", \$1, \$2, \$3;
                next;
        }
        e == 1 && \$2 ~ /^usr\/bin / {
                printf \"%s=$dest_prefix/bin \\\\\n\", \$1;
                e = 0;
                next;
        }
        {
                e = 0;
                print \$0;
        }" < $DESTDIR/pkg/$SW.p5m.3.orig > $DESTDIR/pkg/$SW.p5m.3

for i in `find $DESTDIR/dist$DEST/lib/ruby/gems/1.8/gems/*/lib/ -name '*.rb' `
do
  i_path=`echo $i | sed -e "s:$DESTDIR/dist/::"`
  i_reqs=`awk '$1 == "require" { print $2; }' < $i | tr -d "'" | tr -d '"' | sort -u`
  for k in $i_reqs
  do
    x=""
    y=""
    x=`ls $DESTDIR/dist$DEST/lib/ruby/gems/1.8/gems/*/lib/$k.rb 2>/dev/null`
    if [ -z "$x" ]
    then
      # XXX might not find the best version?
      y=`ls -r $DEST/lib/ruby/gems/1.8/gems/*/lib/$k.rb 2>/dev/null | head -1`
    fi
    if [ -z "$x" -a -z "$y" ]
    then
      y=`ls $DEST/lib/ruby/1.*/$k.rb 2>/dev/null`
    fi
    if [ -z "$x" -a -z "$y" ]
    then
      y=`ls $DEST/lib/ruby/vendor_ruby/$k.rb 2>/dev/null`
    fi
    if [ -z "$x" -a -z "$y" ]
    then
      y=`ls $DEST/lib/ruby/site_ruby/$k.rb 2>/dev/null`
    fi

    if [ -n "$y" ]
    then
      echo "depend type=require fmri=__TBD pkg.debug.depend.file="`basename $y`" \\"
      echo "    pkg.debug.depend.path="`dirname $y | sed -e 's:^/::'`" \\"
      echo "    pkg.debug.depend.reason=$i_path pkg.debug.depend.type=script"
    fi
  done
done >> $DESTDIR/pkg/$SW.p5m.3

i=`ls $DESTDIR/dist$DEST/lib/ruby/gems/1.8/specifications/*.gemspec 2>/dev/null`
if [ -n "$i" ]
then
  for k in $i
  do
    echo "depend type=require fmri=__TBD pkg.debug.depend.file=gem \\"
    echo "    pkg.debug.depend.path="`echo $DEST/bin | sed -e 's:^/::'`" \\"
    echo "    pkg.debug.depend.reason="`echo $k | sed -e "s:$DESTDIR/dist/::"`

    dev_deps=`fgrep 'add_development_dependency(' < $k | \
        sed -e 's:^.*add_development_dependency(%q<\([^>]*\)>.*$:\1:' | \
        sort -u`
    deps=`fgrep 'add_dependency(' < $k | \
        sed -e 's:^.*add_dependency(%q<\([^>]*\)>.*$:\1:' | \
        sort -u`
    for l in $deps
    do
      found=0
      for m in $dev_deps
      do
        if [ "$l" = "$m" ]
        then
          found=1
        fi
      done

      if [ $found -eq 0 ]
      then
        spec=`ls -r $DEST/lib/ruby/gems/1.8/specifications/$l-*.gemspec | head -1`
        if [ -n "$spec" ]
        then
          echo "depend type=require fmri=__TBD pkg.debug.depend.file="`basename $spec`" \\"
          echo "    pkg.debug.depend.path="`dirname $spec | sed -e 's:^/::'`" \\"
          echo "    pkg.debug.depend.reason="`echo $k | sed -e "s:$DESTDIR/dist/::"`
        fi
      fi
    done
  done
fi >> $DESTDIR/pkg/$SW.p5m.3

pkgdepend resolve -m $DESTDIR/pkg/$SW.p5m.3

I’m using the OpenSource version of puppet, the system configuration engine to keep some Solaris systems in sync. For some modules, I wanted to know which zones are running on a given system. So I extended facter, puppet’s system fact collecting framework a bit.

This little patch adds a list of zones and a mapping for shared IP interfaces to their zones to the facts. Something like

root@global:~# /opt/bin/facter
...
interfaces => lo0,lo0_1,beaggr0,beaggr0_1,feaggr0,feaggr0_1
ipaddress => 0.20.72.116
ipaddress6 => ::
ipaddress_beaggr0 => 0.20.72.116
ipaddress_beaggr0_1 => 0.20.72.180
ipaddress_feaggr0 => 1.231.85.66
ipaddress_feaggr0_1 => 1.231.85.71
ipaddress_lo0 => 127.0.0.1
ipaddress_lo0_1 => 127.0.0.1
...
macaddress => 00:21:28:af:fd:06
macaddress_beaggr0 => 0:21:28:af:fd:6
macaddress_feaggr0 => 0:21:28:af:fd:7
...
netmask => 255.255.252.0
netmask_beaggr0 => 255.255.252.0
netmask_beaggr0_1 => 255.255.252.0
netmask_feaggr0 => 255.255.255.240
netmask_feaggr0_1 => 255.255.255.240
netmask_lo0 => 255.0.0.0
netmask_lo0_1 => 255.0.0.0
network_beaggr0 => 0.20.72.0
network_beaggr0_1 => 0.20.72.0
network_feaggr0 => 1.231.85.64
network_feaggr0_1 => 1.231.85.64
network_lo0 => 127.0.0.0
network_lo0_1 => 127.0.0.0
...
zone_beaggr0_1 => z004
zone_feaggr0_1 => z004
zone_lo0_1 => z004
zones => global, z003,z002,z001,z004

I don’t know if that’s useful for anybody else. But just in case;-)

Have fun,

– Marco

Update – Sorry, while the patch below works, it’s NOT needed to get that functionality!! GNU bash can do that without modifications. I don’t know why I missed that before:-( Simply add a ${VAR} into the single quotes when you define the PS1!

$ PS1='\u@${FOO}\$ '
marcow@YY$ FOO=XX
marcow@XX$

Sorry for the confusion

– Marco

OK, long time nothing new here:-( I did a lot of stuff but nothing really interesting for the outside. But today I had a little problem which might also be useful for others – Adding the value of a shell variable into the GNU Bash prompt.

I followed the \D{<format>} example and added an \E{var_name} to the bash 4.2.20 sources. It seems to work as expected and even honours changes to the variable;-)


root@foo:~$ echo $PS1
\u@\E{MW_HOST}:\w$
root@foo:~$ MW_HOST=x
root@x:~$ MW_HOST=mwbuild01z1
root@mwbuild01z1:~$

The little patch to the parse.y source is below. For interested people without bison/yacc, the same patch could be added to the included y.tab.c file.


--- bash-4.2.20.orig/parse.y    2012-01-28 00:42:44.010257843 -0600
+++ bash-4.2.20/parse.y 2012-01-27 22:00:55.192082732 -0600
@@ -5199,6 +5199,37 @@
temp = savestring (timebuf);
goto add_string;

+/* MARCO */
+            case ‘E’:           /* Environment Variable */
+              {
+                char *var_name;
+
+                if (string[1] != ‘{‘)           /* } */
+                  goto not_escape;
+
+               string += 2;                    /* skip { */
+               var_name = xmalloc (strlen (string) + 3);
+               for (t = var_name; *string && *string != ‘}’; )
+                 *t++ = *string++;
+
+               *t = ‘\0′;
+               c = *string;    /* tested at add_string */
+
+                t = get_string_value(var_name);
+                if (t == (char *)NULL)
+                  t = “”;
+
+                if (promptvars || posixly_correct)
+                  /* Make sure that expand_prompt_string is called with a
+                     second argument of Q_DOUBLE_QUOTES if we use this
+                     function here. */
+                  temp = sh_backslash_quote_for_double_quotes(t);
+                else
+                  temp = savestring(t);
+
+                goto add_string;
+              }
+
case ‘n’:
temp = (char *)xmalloc (3);
temp[0] = no_line_editing ? ‘\n’ : ‘\r’;

Maybe that helps somebody else. The patch is very small but it might still create problems with the formatting, so here is a download version bash-4.2.20.patch.

As always, have fun;-)

– Marco