tag:blogger.com,1999:blog-341127292024-03-13T23:41:34.556+01:00Martin Vidner<a href="http://en.opensuse.org">openSUSE</a>. Linux. computers.Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.comBlogger83125tag:blogger.com,1999:blog-34112729.post-90362182443244646522020-01-29T15:49:00.000+01:002020-01-29T15:49:43.360+01:00Instant Fresh openSUSE Tumbleweed with Docker and Vagrant Images<p>On my machines I run <a href="https://en.opensuse.org/Portal:Leap">openSUSE Leap</a> (<a href="https://software.opensuse.org/distributions/leap">download</a>), a stable distribution that follows the <a href="https://www.suse.com/solutions/enterprise-linux/">SUSE Linux Enterprise</a> service packs. But frequently my task is to reproduce or fix a bug in openSUSE <a href="https://en.opensuse.org/Portal:Tumbleweed">Tumbleweed</a> (<a href="https://software.opensuse.org/distributions/tumbleweed">download</a>), the hottest rolling distribution.</p>
<p>In the past, I would take an ISO image of the installation DVD and install a virtual machine from scratch. (To say nothing about burning a CD, copying a boot floppy, and reinstalling a physical machine. I've been doing this for too long.)</p>
<p>Fortunately, things got easier with ready-made disk images for containers (Docker/Podman) and virtual machines (Vagrant).</p>
<h4 id="with-docker">With Docker</h4>
<p>Get the <a href="https://hub.docker.com/r/opensuse/tumbleweed">latest Tumbleweed image from the Docker hub</a>:</p>
<pre class="console"><code>$ docker pull opensuse/tumbleweed</code></pre>
<p>Run it:</p>
<pre class="console"><code>$ docker run -it opensuse/tumbleweed bash
8484d09e2380:/ # grep VERSION_ID /etc/os-release
VERSION_ID="20200118"
8484d09e2380:/ # ...
...
8484d09e2380:/ # exit</code></pre>
<p>Clean up, removing the container or even the Tumbleweed image:</p>
<pre class="console"><code>$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8484d09e2380 opensuse/tumbleweed "bash" 58 minutes ago Exited (127) 9 seconds ago hungry_northcutt
$ docker rm 8484d09e2380
8484d09e2380
$ docker rmi opensuse/tumbleweed:latest </code></pre>
<h4 id="with-vagrant">With Vagrant</h4>
<p>Vagrant virtual machines work with a context directory and a config file, so let's create them:</p>
<pre class="console"><code>$ mkdir vagrant-tw-test; cd $_
$ vagrant init opensuse/Tumbleweed.x86_64
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` [...]</code></pre>
<p>The <code>up</code> step downloads the base image ("box") that we declared previously, and brings up our VM instance. The <code>ssh</code> step connects there.</p>
<pre><code>$ vagrant up
[...]
$ vagrant ssh
> grep VERSION_ID /etc/os-release
VERSION_ID="20200114"
> ...
...
> exit</code></pre>
<p>Clean up:</p>
<pre class="console"><code>$ vagrant halt # stop the VM
$ vagrant destroy # remove its disk image
$ vagrant prune # remove the box (base image)</code></pre>
<p>See details in Meike Chabowski's article <a href="https://www.suse.com/c/vagrant-boxes-with-opensuse-tumbleweed-check-it-out/">Vagrant Boxes with openSUSE Tumbleweed</a>.</p>
Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com2tag:blogger.com,1999:blog-34112729.post-6977978792836357032019-01-11T14:41:00.000+01:002019-01-11T14:41:07.310+01:00Backtrace from a core dump with GDB: pitfalls<style>pre { background-color: #eee;}</style>
<p>At work I mostly program in Ruby, but our system (<a href="http://yast.opensuse.org/">YaST</a>) has a C/C++ part<br />
too. So I get to debug C crashes infrequently enough to forget some<br />
tricks. Here I write them down for the next time.</p>
<ol>
<li>Contents and tl;dr</li>
</ol>
<ul>
<li>$ <code>ulimit -c unlimited</code> so that a core dump is saved</li>
<li>$ <code>sudo coredumpctl dump -o core</code> so that it is saved to a file</li>
<li>$ <code>gdb -c core</code> is wrong, <code>gdb program core</code> is right</li>
<li>(gdb) <code>info sh mylib</code></li>
<li>(gdb) select a stack frame before querying variables <em>or</em> macros</li>
<li>$ <code>CFLAGS="-g3 ..."</code> for debugging macros</li>
</ul>
<ol>
<li>(Un)helpful crash dump</li>
</ol>
<p>When Ruby crashes, it produces a very verbose dump of information. It is not<br />
much helpful in our case:</p>
<pre class="console"><code>$ rake run[fail]
/sbin/yast2 src/clients/fail.rb
/usr/lib64/ruby/vendor_ruby/2.5.0/yast/yast.rb:204: [BUG] Segmentation fault at 0x0000000000000008
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux-gnu]
-- Control frame information -----------------------------------------------
c:0011 p:---- s:0066 e:000065 CFUNC :call_yast_function
c:0010 p:0061 s:0058 e:000057 BLOCK /usr/lib64/ruby/vendor_ruby/2.5.0/yast/yast.rb:204 [FINISH]
c:0009 p:0023 s:0054 e:000053 METHOD /local/home-mvidner/svn/yast/samba-server/src/clients/fail.rb:5
[...]
c:0002 p:0489 s:0011 E:000c18 EVAL /usr/lib/YaST2/bin/y2start:58 [FINISH]
c:0001 p:0000 s:0003 E:001300 (none) [FINISH]
-- Ruby level backtrace information ----------------------------------------
/usr/lib/YaST2/bin/y2start:58:in `<main>'
/usr/lib64/ruby/vendor_ruby/2.5.0/yast/wfm.rb:195:in `CallFunction'
[...]
/usr/lib64/ruby/vendor_ruby/2.5.0/yast/yast.rb:204:in `call_yast_function'
-- Machine register context ------------------------------------------------
RIP: 0x00007fa423bf9677 RBP: 0x00007fff30f5ff20 RSP: 0x00007fff30f5fe10
[...]
R14: 0x00007fff30f5ff48 R15: 0x00007fff30f5fe10 EFL: 0x0000000000010202
-- C level backtrace information -------------------------------------------
/usr/lib64/libruby2.5.so.2.5(rb_print_backtrace+0x15) [0x7fa428543f15]
/usr/lib64/libruby2.5.so.2.5(0x1c814c) [0x7fa42854414c]
/usr/lib64/libruby2.5.so.2.5(0x93e44) [0x7fa42840fe44]
/usr/lib64/libruby2.5.so.2.5(0x158eb2) [0x7fa4284d4eb2]
/lib64/libc.so.6(0x7fa427ff8160) [0x7fa427ff8160]
/usr/lib64/YaST2/plugin/libpy2lang0_ruby.so(_ZN5YRuby9callInnerENSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES5_7YCPList8constPtrI4TypeS8_E+0x4c7) [0x7fa423bf9677]
[...]
/usr/bin/ruby.ruby2.5(_start+0x2a) [0x40086a]
-- Other runtime information -----------------------------------------------
* Loaded script: /usr/lib/YaST2/bin/y2start
* Loaded features:
0 enumerator.so
1 thread.rb
2 rational.so
3 complex.so
4 /usr/lib64/ruby/2.5.0/x86_64-linux-gnu/enc/encdb.so
[...]
87 /usr/lib64/ruby/vendor_ruby/2.5.0/yast/y2start_helpers.rb
88 /local/home-mvidner/svn/yast/samba-server/src/modules/Fail.rb
* Process memory map:
00400000-00401000 r-xp 00000000 08:01 7766 /usr/bin/ruby.ruby2.5
[...]
7fa42363b000-7fa42365c000 r--s 00000000 08:01 6998 /usr/lib64/YaST2/plugin/libpy2lang_ruby.so
[...]
7fa428a5f000-7fa428a60000 rw-p 00026000 08:01 1310795 /lib64/ld-2.26.so
7fa428a60000-7fa428a61000 rw-p 00000000 00:00 0
7fff30767000-7fff30f66000 rw-p 00000000 00:00 0 [stack]
7fff30ff1000-7fff30ff4000 r--p 00000000 00:00 0 [vvar]
7fff30ff4000-7fff30ff6000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: http://www.ruby-lang.org/bugreport.html
/sbin/yast2: line 455: 25102 Aborted (core dumped) $ybindir/y2start $module "$@" "$SELECTED_GUI" $Y2_GEOMETRY $Y2UI_ARGS</code></pre>
<ol>
<li>"(core dumped)" is lying</li>
</ol>
<p>It means to say: Per <code>/usr/lib/sysctl.d/50-coredump.conf</code> the core has been sent<br />
to <code>/usr/lib/systemd/systemd-coredump</code> which obeyed "ulimit -c 0" and did not<br />
save it.<br />
So let's</p>
<ul>
<li>use the <code>ulimit -c unlimited</code> command (bash shell builtin, actually)</li>
<li>Let it crash again</li>
<li><code>sudo coredumpctl dump -o core</code></li>
</ul>
<p>Also note that without the <code>dump</code> subcommand, the core dump will be deleted<br />
after a day. (Why?)</p>
<p>(To avoid systemd-coredump, you can use<br />
<code>sudo /usr/sbin/sysctl kernel.core_pattern="core.%p"</code>; thanks <a href="https://blog.ladslezak.cz/">lslezak</a>)</p>
<ol>
<li><code>gdb -c core</code> is not useful without the executable</li>
</ol>
<p>When we do that, the backtrace is very unhelpful:</p>
<pre class="console"><code>(gdb) bt
#0 0x00007eff7d0140e0 in ?? ()
#1 0x0000000000000400 in ?? ()
#2 0xffffffffffffffff in ?? ()
#3 0xffffffffffffffff in ?? ()
#4 0xffffffffffffffff in ?? ()
#5 0xffffffffffffffff in ?? ()
#6 0xffffffffffffffff in ?? ()
#7 0xffffffffffffffff in ?? ()
#8 0xffffffffffffffff in ?? ()
#9 0xffffffffffffffff in ?? ()
#10 0xffffffffffffffff in ?? ()
#11 0xffffffffffffffff in ?? ()
#12 0xffffffffffffffff in ?? ()
#13 0xffffffffffffffff in ?? ()
#14 0x0000000000000000 in ?? ()</code></pre>
<p>For some reason, gdb will not load the executable file even though it is named<br />
in the core file. Do it by hand:</p>
<ol>
<li><code>gdb program core</code></li>
</ol>
<pre class="console"><code>$ gdb /usr/bin/ruby.ruby2.5 core
[... lots of text that we'll get back to in the next section ...]
(gdb) bt
#0 0x00007eff7d0140e0 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1 0x00007eff7d01579f in __GI_abort () at abort.c:100
#2 0x00007eff7d42be51 in die () at error.c:552
#3 0x00007eff7d42be51 in rb_bug_context (ctx=ctx@entry=0xcebac0, fmt=fmt@entry=0x7eff7d5925c4 "Segmentation fault at %p") at error.c:582
#4 0x00007eff7d4f0eb2 in sigsegv (sig=11, info=0xcebbf0, ctx=0xcebac0) at signal.c:928
#5 0x00007eff7d014160 in <signal handler called> () at /lib64/libc.so.6
#6 0x00007eff78c15677 in YRuby::callInner(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, YCPList, constPtr<Type, Type>) () at /usr/lib64/YaST2/plugin/libpy2lang_ruby.so
#7 0x00007eff78c19d80 in Y2RubyFunction::evaluateCall() () at /usr/lib64/YaST2/plugin/libpy2lang_ruby.so</code></pre>
<p>Note that the frames 0 to 4 show the source code locations (file name and line<br />
number) as well as the argument names and values. It's because gdb has found<br />
the debugging information for glibc and the Ruby library:</p>
<pre class="console"><code>(gdb) info sh libc
From To Syms Read Shared Object Library
0x00007eff7cffe770 0x00007eff7d14365c Yes /lib64/libc.so.6
0x00007eff7c981cd0 0x00007eff7c9895e8 Yes /lib64/libcrypt.so.1
0x00007eff73c256f0 0x00007eff73c27007 Yes (*) /usr/lib64/libcap.so.2
0x00007eff6972ca10 0x00007eff69731b65 Yes /usr/lib64/qt5/plugins/platforminputcontexts/libcomposeplatforminputcontextplugin.so
(*): Shared library is missing debugging information.
(gdb) info sh ruby
From To Syms Read Shared Object Library
0x00007eff7d3c73c0 0x00007eff7d566ea8 Yes /usr/lib64/libruby2.5.so.2.5
0x00007eff7c433850 0x00007eff7c4344c0 Yes (*) /usr/lib64/ruby/2.5.0/x86_64-linux-gnu/enc/encdb.so
[...]
0x00007eff7b9ec2c0 0x00007eff7b9f4cb0 Yes (*) /usr/lib64/ruby/vendor_ruby/2.5.0/x86_64-linux-gnu/yastx.so
0x00007eff78c0fc00 0x00007eff78c1e740 Yes (*) /usr/lib64/YaST2/plugin/libpy2lang_ruby.so
0x00007eff789f82a0 0x00007eff78a001a5 Yes (*) /usr/lib64/ruby/vendor_ruby/2.5.0/x86_64-linux-gnu/yast/builtinx.so
(*): Shared library is missing debugging information.</code></pre>
<p><code>info sh</code> is short for <code>info sharedlibrary</code>. If you omit the library name filter, the<br />
length of the list will easily obscure the "(*)" footnote.</p>
<ol>
<li>Add debuginfo</li>
</ol>
<p>On openSUSE (and SUSE Linux Enterprise), the debugging symbols are in separate<br />
RPM packages:</p>
<pre><code>zypper in yast2-ruby-bindings-debug{info,source}</code></pre>
<p>Or take the hint from the gdb message that we omitted in the previous section:</p>
<pre><code>Missing separate debuginfo for /usr/lib64/YaST2/plugin/libpy2lang_ruby.so
Try: zypper install -C "debuginfo(build-id)=8c6a1d943255c6f935e029140e75b49b1b7c22fb"</code></pre>
<p>After restarting gdb:</p>
<pre class="console"><code>(gdb) bt
[...]
#5 0x00007eff7d014160 in <signal handler called> () at /lib64/libc.so.6
#6 0x00007eff78c15677 in rb_array_len (a=8) at /usr/include/ruby-2.5.0/ruby/ruby.h:2057
#7 0x00007eff78c15677 in YRuby::callInner(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, YCPList, constPtr<Type, Type>) (this=this@entry=0xfefcc0, module_name=..., function=..., argList=..., wanted_result_type=...) at /usr/src/debug/yast2-ruby-bindings-4.0.6-lp150.1.1.x86_64/src/binary/YRuby.cc:220
[...]</code></pre>
<p>In frame #6, <code>rb_array_len (a=8)</code>, is the <em>a</em> argument equal to <code>nil</code>?<br />
Let's check:</p>
<pre class="console"><code>(gdb) p Qnil
No symbol "Qnil" in current context.</code></pre>
<p>That's misleading! Although <code>Qnil</code> is defined in a header, and you've been<br />
taught to prefer <code>static const int</code> to macros because of macros being<br />
"global", they are in fact not as much global as for gdb to see them now. We<br />
have to select a frame to have a context of a compilation unit.</p>
<pre class="console"><code>(gdb) f 6
#6 rb_array_len (a=8) at /usr/include/ruby-2.5.0/ruby/ruby.h:2057
2057 return (RBASIC(a)->flags & RARRAY_EMBED_FLAG) ?
(gdb) p Qnil
No symbol "Qnil" in current context.</code></pre>
<p>WTF?! Well, having debugging information loaded is not enough, the debuginfo<br />
must have an elevated level that includes the macros.</p>
<ol>
<li>Debuginfo with macros</li>
</ol>
<p>Instead of the <code>-g</code> flag to gcc, I had to specify <code>-g3</code> in CFLAGS and<br />
rebuild and reinstall. After that, finally:</p>
<pre class="console"><code>(gdb) f 6
#6 rb_array_len (a=8) at /usr/include/ruby-2.5.0/ruby/ruby.h:2057
2057 return (RBASIC(a)->flags & RARRAY_EMBED_FLAG) ?
(gdb) p Qnil
$1 = 8
(gdb) f 7
#7 YRuby::callInner (this=this@entry=0xfefcc0, module_name=..., function=..., argList=..., wanted_result_type=...) at /usr/src/debug/yast2-ruby-bindings-4.0.6-lp150.1.1.x86_64/src/binary/YRuby.cc:220
220 VALUE backtrace = RARRAY_LEN(trace)>0 ? rb_ary_entry(trace, 0) : rb_str_new2("Unknown");</code></pre>
<p>So a = 8 and Qnil is 8, so we have confirmed that the <em>trace</em> array being<br />
<code>nil</code> is causing the crash.</p>
<p>(For the record, this is <a href="https://bugzilla.suse.com/show_bug.cgi?id=1119690#c9">bsc#1119690</a>.)</p>
Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-63127993638098092342017-09-18T11:22:00.000+02:002017-09-18T11:22:47.384+02:00Fibre Channel over Ethernet: Basics of FCoE in SUSE Linux<p>I had to apply a fix for a FCoE module in <a href="http://yast.opensuse.org/">YaST</a>, and I had no idea.</p>
<p>After learning a couple of things I still only have a vague idea, but I am writing it down to help my future self, my team mates, and perhaps you too.</p>
<p>FCoE stands for "<a href="https://en.wikipedia.org/wiki/Fibre_Channel_over_Ethernet">Fibre channel over Ethernet</a>". Apparently if you have some disk on a Fibre Channel SAN (storage area network), you can use FCoE to extend the reachability of that disk to the ethernet parts of your network. It still needs to be a kind of special ethernet (10Gb, with special network cards) but that seems less special than FC hardware.</p>
<p>For a better overview, including a diagram, see: <a href="https://www.suse.com/documentation/sles-12/stor_admin/data/cha_fcoe.html">SUSE Linux Enterprise Server Documentation / Storage Administration Guide / Fibre Channel Storage over Ethernet Networks: FCoE</a>.</p>
<p>FCoE typically uses a <a href="https://en.wikipedia.org/wiki/Virtual_LAN">virtual LAN</a>, (VLAN, IEEE 802.1Q).</p>
<p>There needs to be a Fibre Channel Forwarder (FCF) between the FC and ethernet parts. It has a MAC address. Note a difference from iSCSI which works on the IP level, one layer up.</p>
<p><a href="http://yast.opensuse.org/">YaST</a> helps you set things up. The rest of this article could be useful if you cannot use YaST for some reason.</p>
<p>SLES uses <a href="http://open-fcoe.org/">open-fcoe</a>. On SLES-12 the package is called fcoe-utils.</p>
<p><code>fipvlan</code> (stands for FCoE Initialization Protocol VLAN discovery) shows FCFs and which interface and VLAN they are reachable with:</p>
<pre class="console"><code># fipvlan --auto
Fibre Channel Forwarders Discovered
interface | VLAN | FCF MAC
------------------------------------------
eth1 | 500 | 00:0d:ec:b3:ca:00</code></pre>
<p>It can also <code>--create</code> the VLAN interface <em>and</em> <code>--start</code> up the FCoE connection, but it won't make that permanent for the next boot</p>
<p>To make it permanent you need to</p>
<ol style="list-style-type: decimal">
<li>enable the FCoE service (SLE11:/etc/init.d/boot.fcoe, SLE12: fcoe.service). Under the hood it uses two programs: <code>fcoemon</code> is the daemon, <code>fcoeadm</code> is a front end (<code>fcoeadm -p</code> shows the pid of <code>fcoemon</code>).</li>
<li>write a config file, <code>/etc/fcoe/cfg-*IFACE*</code>, where <em>IFACE</em> is
<ul>
<li>eth1.500 if <code>AUTO_VLAN</code> is <code>no</code>; in this case, you also need <code>/etc/sysconfig/network/ifcfg-eth1.500</code>, see <code>man ifcfg-vlan</code>.</li>
<li>eth1 if <code>AUTO_VLAN</code> is <code>yes</code>; in this case, the interface is named <code>eth1.500-fcoe</code>. Note the unusual <code>-fcoe</code> suffix!</li>
</ul></li>
</ol>
<p>With the config files in place, <code>rcfcoe start</code> (and <code>ifup eth1.500</code>, unless AUTO_VLAN). Then you should see the disk devices:</p>
<pre class="console"><code># fcoeadm --target
Interface: eth1.500
Roles: FCP Target
Node Name: 0x50060160BB600160
Port Name: 0x500601663B600160
Target ID: 0
MaxFrameSize: 2048
OS Device Name: rport-2:0-2
FC-ID (Port ID): 0x710D00
State: Online
LUN ID Device Name Capacity Block Size Description
------ ----------- ---------- ---------- ----------------------------
0 /dev/sdb 16.00 GiB 512 DGC VRAID (rev 0430)
[...]</code></pre>
<p>People who actually know their way around FCoE will note that I have omitted many important details. Let me know in the comments whether I should come back to this and expand on some topics.</p>
Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com1tag:blogger.com,1999:blog-34112729.post-50124794387656673542017-03-01T10:24:00.000+01:002017-03-01T10:24:41.896+01:00Getting Started in Android Development: Part 3: Reducing Bloat<p>So far we have seen <a href="http://mvidner.blogspot.cz/2017/02/getting-started-in-android-development.html">Part 1: Building the First App</a>, and <a href="http://mvidner.blogspot.cz/2017/02/publishing-the-first-app.html">Part 2: Publishing the first App</a>.</p>
<p><em>That Feeling When</em> you build a brilliant piece of software and the users are ripping it from your fingers to the sound of raving reviews:</p>
<ul class="incremental">
<li><em>dad</em>: I'm home! Have you seen my first app I've e-mailed you about?</li>
<li><em>kid</em>: Hi. Yup.</li>
<li><em>dad</em>: So?? Do you like it? Have you given it any stars?</li>
<li><em>kid</em>: Just one. It can do almost nothing and it takes up too much space.</li>
</ul>
<p>And the kid is right. <strong>App description</strong>: <a href="https://play.google.com/store/apps/details?id=net.vidner.justrollonedie">Press a button and get a random number between 1 and 6</a>. <strong>App size</strong>: 6MB. Six. Megabytes.</p>
<p>In this post we will reduce that over a hundred times to 44KB.</p>
<p><em>Thanks to SUSE, my employer, for sponsoring a company-wide <a href="https://hackweek.suse.com/">Hack Week</a> which this <a href="https://hackweek.suse.com/projects/learn-android-development-just-roll-one-die">project</a> was a part of!</em></p>
<h3 id="debug-or-release-build">Debug or Release Build?</h3>
<p>I did not manage to make a release build in the Android Studio IDE. So I tried from the command line. (<small><a href="https://twitter.com/pepareidinger">J. Reidinger</a> has pointed out that I should read <a href="https://developer.android.com/studio/build/build-variants.html">Configure Build Variants</a>.</small>)</p>
<pre class="console"><code>$ sudo zypper install java-1_8_0-openjdk-devel # to provide javac
$ ./gradlew assemble
[downloads some deps at first... 280MB]
[lots of output for normal build too]
$ (cd app/build/outputs/apk; stat -c "%'9s %n" *.apk)
1,443,734 app-debug.apk
1,337,890 app-release-unsigned.apk</code></pre>
<p>Apparently my hopes that a release build would be significantly smaller were unfounded. The APK has 1.5MB and takes up 6MB when installed.</p>
<h3 id="shrink-your-code-and-resources">Shrink Your Code and Resources</h3>
<p>First I searched the web for "minify android app" and eventually arrived at <a href="https://developer.android.com/studio/build/shrink-code.html">Shrink Your Code and Resources</a> in the IDE manual.</p>
<p>Using <code>minifyEnabled true</code> in <code>build.gradle</code> shrunk the signed release build from 1,347,038 bytes to 786,674, which results in 2.39MB installed size. (Did not find a way to install this build from the IDE, used <code>adb install -r ./app/app-release.apk</code>.)</p>
<p>Changing <code>proguardFiles</code> from <code>proguard-android.txt</code> to <code>proguard-android-optimize.txt</code> slightly shrinks the APK to 771,406 bytes.</p>
<p>Adding <code>shrinkResources true</code>: 745,490 bytes, 2.21MB installed.</p>
<h3 id="code-bloat-activity-base-class">Code Bloat: Activity Base Class</h3>
<p>It seems that now the main reason for bloat is the sheer amount of included code: 5 methods of mine vs 4823(!) methods from the <code>android.*</code> classes.</p>
<p>Changed the base class of the main activity from <code>android.support.v7.app.AppCompatActivity</code> to <code>android.app.Activity</code> but then <code>adb install</code> says "Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE]". <code>adb uninstall net.vidner.justrollonedie</code> solved it. But I wonder what would happen if my users on the app store wanted to update. Fortunately I have none :D</p>
<p>The base class change improved the sizes to 661,870 APK, 1.62MB installed.</p>
<p><img
src="https://cloud.githubusercontent.com/assets/102056/23366119/daa14be8-fd05-11e6-92b3-b5eefc8ce496.png"
width="128px" style="float: right; padding: 1em"></p>
<h3 id="code-bloat-api-version">Code Bloat: API Version</h3>
<p>I thought a 4x reduction in installed size was good enough, even if still bloated. I decided to fix one more thing before pushing an update to the store: the minimal required Android platform. (In the process of tinkering with a demo OpenGL app I discovered that android-8, supporting my old 2.2 Froyo phone, gets automatically downloaded if I declare it in the app manifest.)</p>
<p>So I did, and the side effect was perfect: all boilerplate code was gone and I ended up with a 17,282 byte APK, 44KB (<strong>kilo</strong>bytes!) installed. Still too much for a microcontroller ;-) but good enough for Android.</p>
<p>Figuring out how to downgrade my code and layout and styles to still run on the older API seemed tricky at first, but then I simply generated a scratch project for android-9 and copied the differences. Then I changed one style name with the help of the <a href="https://developer.android.com/reference/android/R.style.html">API version filter</a> (see screenshot).</p>
<h3 id="get-the-app-get-the-source">Get the App, Get the Source</h3>
<p>The <a href="https://github.com/mvidner/just-roll-one-die">source code for Just Roll One Die</a> is on GitHub under a MIT license. You can try out <a href="https://play.google.com/store/apps/details?id=net.vidner.justrollonedie">Just Roll One Die</a> on Google Play.</p>
Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-85702169973464812542017-02-28T09:29:00.001+01:002017-02-28T09:29:50.522+01:00Getting Started in Android Development: Part 2: Publishing the First App<p>Here I simply describe what it takes to publish an Android application that I described in the previous part, <a href="http://mvidner.blogspot.cz/2017/02/getting-started-in-android-development.html">Building the First App</a>.</p>
<p><em>Thanks to SUSE, my employer, for sponsoring a company-wide <a href="https://hackweek.suse.com/">Hack Week</a> which this <a href="https://hackweek.suse.com/projects/learn-android-development-just-roll-one-die">project</a> was a part of!</em></p>
<p>I will only deal with free apps: no cost for the user and no advertisements. I guess it would be easy to slap an advertisement module on it or put a minimal price tag on the app. But then it would be morally wrong for me to keep the profits without giving SUSE a cut, and the organizational and accounting process would quickly turn this into a lawyer's Hack Week. Scratch that.</p>
<h3 id="registering-a-publisher-account-25">Registering a Publisher Account: $25</h3>
<p>I started with the instructions at <a href="https://developer.android.com/distribute/googleplay/start.html">Get Started with Publishing</a>.</p>
<p>I could have reused my existing Google account but decided to create a new one. The next step may put you off: a 25 USD registration fee is needed.</p>
<p>Then a fair amount of legalese, which I did skim through, and I was rewarded by the knowledge that Google apparently does not like developers to publish web browsers or search engines.</p>
<h3 id="publishing-the-app">Publishing the App</h3>
<p>When I thought my application was good enough to be published I went to the <a href="https://play.google.com/apps/publish/">Developer Console</a> to make a Store Listing.</p>
<p>Entered the app name, summary, long description; no surprise there, I had expected that from openSUSE RPM packaging. Then came the innovation: screenshots are required! In fact,</p>
<ul class="incremental">
<li>Two screenshots</li>
<li>a high-resolution icon (512x512 pixels)</li>
<li>a feature graphic (1024x500 px) which appears as the heading of the app listing page</li>
</ul>
<p>Reportedly the new standard way to take a screenshot is Power + Volume Down. But that did not work for my Xperia phone. I had to enable the following setting, after which a Screenshot option appeared in the menu that appears after holding Power.</p>
<ul class="incremental">
<li><em>Settings</em>, then</li>
<li><em>Device</em> / <em>Buttons</em>, then</li>
<li><em>Power button</em> / <em>Power menu</em>, there</li>
<li>enable <em>Screenshot</em> ☑.</li>
</ul>
<p>For a moment I feared I would need to hire a designer for the icon, but then I told LibreOffice to write a <strong>6</strong> in a 360pt big font and used that. <em>whew</em>!</p>
<p>More form items to fill: <strong>App type</strong>: Applications (not Games); <strong>Category</strong>: Entertainment (I guess); <strong>Pricing and distribution</strong>: Free, All countries, No ads.</p>
<p>We do not process any user data so we check a box that we're Not submitting a privacy policy.</p>
<p>Now we are at a point in the form where it says that we need to submit a content rating, but it won't let us do it. I think it only allows to rate after the app has been uploaded.</p>
<p>Are we ready to upload the app code? <strong>Upload APK</strong>... bzzzt, wrong! must not upload a debug build. Did not find a way to make a production build in the GUI so used the CLI for a change: <code>./gradlew assemble</code>... bzzzt, wrong! must not upload an unsigned build.</p>
<p>Signing software makes sense. Except the signing mechanism is unfamiliar to me, something involving a Java KeyStore. So I followed the manual: <a href="https://developer.android.com/studio/publish/app-signing.html">Sign Your App</a>. Ended up with a file in my home directory and needing to enter two passwords each time I build a signed APK. At least no certification authority needed to be involved.</p>
<p><strong>Content Rating</strong>: Category: Utility, No violence, No sexuality, No offensive language, No controlled substances (illegal drugs), No communication with other users, No sharing of personal information, No sharing of location, No digital goods purchasing, No Nazi symbolism, Not a browser or search engine.</p>
<p>Finally all information was there, I hit Publish, and I wondered how long the review process would take. It took about 3 hours on a European Tuesday noon.</p>
<p>You can try out <a href="https://play.google.com/store/apps/details?id=net.vidner.justrollonedie">Just Roll One Die</a> on Google Play. The <a href="https://github.com/mvidner/just-roll-one-die">source code for Just Roll One Die</a> is on GitHub under a MIT license.</p>
<h3 id="next">Next</h3>
<p>In the next part we will deal with software bloat. Because an app that can roll a 6 is justified in taking up 6MB on your kid's tablet, right? Right??</p>
Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-27337041960011795952017-02-27T13:50:00.000+01:002017-02-28T09:34:31.202+01:00Getting Started in Android Development: Part 1: Building the First App<h1 id="getting-started-in-android-development-part-1-building-the-first-app">Getting Started in Android Development: Part 1: Building the First App</h1>
<p>Do you know programming and want to start with the Android platform? Just like me! Read on.</p>
<p><em>Thanks to SUSE, my employer, for sponsoring a company-wide <a href="https://hackweek.suse.com/">Hack Week</a> which this <a href="https://hackweek.suse.com/projects/learn-android-development-just-roll-one-die">project</a> was a part of!</em></p>
<p>In case you wonder why Android: it is a good balance of work and play. Android is not the coolest toy to play with at the moment, but it is the most versatile device that people are likely to have at hand, especially when traveling. And Android already outnumbers openSUSE <em>and all other OSs</em> in my household.</p>
<p>This is a three part series: 1) building an app, 2) publishing it on Google Play, 3) trimming it down. In this part, we'll set up the development environment, follow the official tutorial to build a trivial app, then build a trivial yet useful app of our own.</p>
<div class="figure">
<img src="https://raw.githubusercontent.com/mvidner/just-roll-one-die/master/screenshots/roll-en.png" alt="a screenshot of my first app" />
<p class="caption">a screenshot of my first app</p>
</div>
<h2 id="installing-the-sdk">Installing the SDK</h2>
<p>I am using openSUSE Leap 42.1 (x86_64). You will notice that I keep tallying the disk space taken. This is because I am a bit short of space on one of my machines, and need to have an idea how much cleanup is needed.</p>
<p>Went to <a href="https://developer.android.com/" class="uri">https://developer.android.com/</a>.</p>
<p>Downloaded Android Studio (2.2.3 for Linux, 438 MiB, unpacks to 785 MiB), followed the instructions, unpacking to <code>/opt</code> (getting <code>/opt/android-studio</code>).</p>
<p>Ran <code>/opt/android-studio/bin/studio.sh</code>. Was greeted by an "Android Studio Setup Wizard": chose a Standard setup. Additional download of 890MB (1412MB unpacked) to <code>~/Android/Sdk</code>.</p>
<p>Got a slightly confusing notice about KVM emulator acceleration. It seems that if you have used KVM before on your machine, the SDK will use it out of the box. But even with acceleration, don't expect the emulator to be fast. If you have a real device, use that.</p>
<h2 id="building-your-first-app"><a href="https://developer.android.com/training/basics/firstapp/index.html">"Building Your First App"</a></h2>
<p>For the most part I simply <a href="https://developer.android.com/training/basics/firstapp/index.html">followed the tutorial</a> for building, installing, and running a trivial app that asks for a message and then displays it. The documentation feels excellent!</p>
<p>The one non-obvious part was choosing which Android version, in other words, which API level, to target. in the <em>Target Android Devices</em> dialog, the preselected option is API 15: Android 4.0.3 (IceCreamSandwich). That is presumably based on the current active device statistics which result in the app being compatible with 97% of devices. The oldest one is API 9: Android 2.3 (Gingerbread), which was a bit disappointing since my older phone from 2010 runs API 8, 2.2 (Froyo). (Don't worry, I eventually solved that in part 3.) Fortunately my newer phone has API 22: Android 5.1.1. Installed the API 22 platform too, to match the phone, about 100MB.</p>
<p>Connected my phone with a USB cable, pressed Run, and there it was! Don't worry, a buggy app will just crash and not affect the rest of your phone.</p>
<h2 id="just-roll-one-die">Just Roll One Die</h2>
<p>Now it looked like I knew enough to make a <em>useful</em> app, so I did: Once my family was on a train with a board game table but we had no dice. So my first actual app is <a href="https://play.google.com/store/apps/details?id=net.vidner.justrollonedie">Just Roll One Die</a>. A totally simple application that can just roll one ordinary six-faced die. Six faces ought to be enough for anybody. No pictures, just digits.</p>
<p>The <a href="https://github.com/mvidner/just-roll-one-die">source code for Just Roll One Die</a> is on GitHub under a MIT license. You can try out <a href="https://play.google.com/store/apps/details?id=net.vidner.justrollonedie">Just Roll One Die</a> on Google Play. (The details of how to get an app there are described in <a href="/2017/02/publishing-the-first-app.html">Part 2: Publishing the First App</a>.)</p>
<h2 id="how-about-you">How about you?</h2>
<p>I was amazed how easy it was and I can't believe that it took me so long to try this. Wy don't you too give it a try and let me know how you are doing.</p>
Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-16156372190236538882017-02-16T08:00:00.000+01:002017-02-16T08:00:07.188+01:00Text to Speech with eSpeak and Epos<p>A <a href="http://mvidner.blogspot.cz/2017/01/capturing-and-decoding-lego-mindstorms.html">humanoid robot</a> should be able to talk. So I looked around for some open source speech synthesis software.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/ilu0Mjw_9vw" frameborder="0" allowfullscreen>
</iframe>
<p>(The above video does feature a talking robot (and a multilingual dolphin) but that's where similarities with the following content end.)</p>
<h3 id="espeak"><a href="http://espeak.sourceforge.net">eSpeak</a></h3>
<p>Hello world:</p>
<pre class="sh"><code>espeak 'Hello, world!'</code></pre>
<p>Standard input works too:</p>
<pre class="sh"><code>espeak <<EOS
A robot may not injure a human being or, through inaction,
allow a human being to come to harm.
EOS</code></pre>
<p>I need the robot to speak Czech too:</p>
<pre class="sh"><code>espeak -v cs 'Dobrý den!'</code></pre>
<p>Chinese also seems to work, at least to my beginner ear:</p>
<pre class="sh"><code>espeak -v zh '认识你很高兴'
# The same in pinyin
espeak -v zh 'ren4shi ni3 hen3 gao1xing4'</code></pre>
<p>To put the words to the robot's mouth we first need to save the sound to a file:</p>
<pre class="sh"><code>espeak -w dobry-den.wav -v cs 'Dobrý den!' # 16 bit, mono 22050 Hz</code></pre>
<p>Now a thing that is not so useful for the robot, but a cool diversion. This tells eSpeak to be <strong>q</strong>uiet, and transcribe the text in <a href="https://en.wikipedia.org/wiki/International_Phonetic_Alphabet">International Phonetic Alphabet</a>.</p>
<pre class="sh"><code>espeak -q --ipa 'All human beings are born free and equal
in dignity and rights. They are endowed with reason and conscience
and should act towards one another in a spirit of brotherhood.'</code></pre>
<blockquote>
<p>ˈɔːl hjˈuːmən bˈiːɪŋz ɑː bˈɔːn fɹˈiː and ˈiːkwəl ɪn dˈɪɡnɪti and ɹˈaɪts</p>
<p>ðeɪ ɑːɹ ɛndˈaʊd wɪð ɹˈiːzən and kˈɒnʃəns and ʃˌʊd ˈakt tʊwˈɔːdz wˈɒn ɐnˈʌðəɹ ɪn ɐ spˈɪɹɪt ɒv bɹˈʌðəhˌʊd</p>
</blockquote>
<p>And it also works for Czech:</p>
<pre class="sh"><code>espeak -q -v cs --ipa 'Všichni lidé rodí se svobodní a sobě rovní
co do důstojnosti a práv. Jsou nadáni rozumem a svědomím
a mají spolu jednat v duchu bratrství.'</code></pre>
<blockquote>
<p>fʃˈixɲi lˈideː rˈoɟiː se svˈobodɲiː a sˈobje rˈovɲiː tsˈo do dˈuːstojnˌosci a prˈaːv</p>
<p>jsoʊ nˈadaːɲi rˈozumem a svjˈedomiːm a mˌajiː spˈolu jˈednat v dˈuxu brˈatr̩stviː</p>
</blockquote>
<!--
It is a truth universally acknowledged, that a single man in possession of a
good fortune, must be in want of a wife.
Světem panuje skálopevné přesvědčení, že svobodný muž, který má slušné jmění,
se neobejde bez ženušky.
-->
<h3 id="eposepos"><a href="http://epos.ufe.cz/">epos</a></h3>
<p>The problem with eSpeak is that it sounds quite robotic. I remembered that for Czech, the <a href="http://epos.ufe.cz/">epos</a> system was much better, also for its availability of better quality downloadable voices.</p>
<p>I installed epos (here as <a href="http://software.opensuse.org/download.html?project=home%3Asleep_walker&package=epos">an openSUSE RPM</a>) and downloaded the high quality voices <a href="https://sourceforge.net/projects/epos/files/voices/Czech%20_%20Machac%2BViolka%2C%20July%2005/epos-tdp.tgz/download">epos-tdp.tgz</a>, then unpacked them to the right place:</p>
<pre class="sh"><code>cd /usr/share/epos/inv
sudo tar xvf .../epos-tdp.tgz</code></pre>
<p>At first I got no sound but <code>strace</code> showed me a problem with <code>/dev/dsp</code> and a bit of searching turned out that I must run <code>eposd</code> with a dsp wrapper:</p>
<pre class="sh"><code>padsp eposd $OPTIONS
# eg.
padsp eposd --voice machac
padsp eposd --voice violka</code></pre>
<p>Another quirk is that epos wants the input in ISO Latin 2, so I used <code>iconv</code>:</p>
<pre class="sh"><code>while read S; do say-epos $(echo "$S" | iconv -f utf8 -t l2); done</code></pre>
<p>For saving the sound to a file, use <code>-w</code> to use a fixed file name <code>./said.wav</code>, or <code>-o</code> to use stdout:</p>
<pre class="sh"><code>say-epos -w Ahoj
say-epos -o Ahoj > ahoj.wav</code></pre>
<h3 id="other-systems">Other systems?</h3>
<p>The thing that reminded me of epos was this <a href="http://forum.odorik.cz/viewtopic.php?f=14&t=2358">summary written by a small Czech phone operator</a>.</p>
<p>Have you tried text-to-speech software? Which one sounds the best?</p>
Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-32437644441391143622017-02-07T08:00:00.000+01:002017-02-07T08:00:28.066+01:00Jenkins as Code<p>I saw a couple of talks last week, and learned about several ways
of automating <a href="https://jenkins.io/">Jenkins CI</a>.</p>
<p>The problem being solved is: if you automate your builds and
tests, why still click the Jenkins web UI by hand? Script it
instead.</p>
<p><a href="https://wiki.jenkins-ci.org/display/JENKINS/Job+DSL+Plugin">Jenkins Job DSL</a>, which is based on <a href="https://en.wikipedia.org/wiki/Groovy_(programming_language)">Groovy</a> (a JVM
language). Another topic was <a href="https://jenkins.io/doc/book/pipeline/">Jenkins Pipeline</a> which helps managing
many jobs that depend on each other.</p>
<p>In <a href="http://www.slideshare.net/YuryTsarev/containercon-test-driven-infrastructure">Test Driven Infrastructure</a>, <a href="https://twitter.com/xnullz">Yury Tsarev</a>
presents, among many other things, <a href="http://docs.openstack.org/infra/jenkins-job-builder/">Jenkins Job
Builder</a>. JJB takes descriptions written in YAML or JSON and
translates them to Jenkins API with Python.</p>
Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-84468435483594186472017-01-30T22:03:00.000+01:002017-02-02T11:13:12.155+01:00Capturing and Decoding Lego Mindstorms EV3 Bluetooth Communication<p>The <a href="https://www.lego.com/en-gb/mindstorms/">Lego Mindstorms EV3 robots</a> can be controlled with an Android app
(<a href="https://play.google.com/store/apps/details?id=com.lego.mindstorms.robotcommander">Lego Mindstorms Commander</a>) communicating with the brick
via <a href="https://en.wikipedia.org/wiki/Bluetooth">Bluetooth</a>.
The command protocol is documented by Lego in
the <em>EV3 Communication Developer Kit</em> and the commands themselves in
the <em>EV3 Firmware Developer Kit</em> (get them from <a href="https://www.lego.com/en-gb/mindstorms/downloads">Mindstorms Downloads</a>).</p>
<p>I wondered what exactly goes on and I decided to capture the communication and
decode it, to learn both about Bluetooth and about the details of the EV3
protocol.</p>
<p><a href="https://cloud.githubusercontent.com/assets/102056/22440715/09f45f48-e735-11e6-984b-879bf7d55bdb.jpg" target="_blank"><img src="https://cloud.githubusercontent.com/assets/102056/22440715/09f45f48-e735-11e6-984b-879bf7d55bdb.jpg" alt="Good Robot. Good Robot!!" style="max-width:100%;"></a></p>
<p>I succeeded and made a couple of useful tools along the way:</p>
<ul>
<li><a href="#extract">extract</a> the <a href="https://en.wikipedia.org/wiki/RFCOMM">RFCOMM</a> data from a btsnoop HCI log</li>
<li><a href="#decode">decode</a> the EV3 command protocol</li>
</ul>
<p>See also the previous post
about <a href="http://mvidner.blogspot.com/2017/01/usb-communication-with-python-and-pyusb.html">sending data (EV3 commands) over USB</a>.</p>
<h2><a id="user-content-outline" class="anchor" href="#outline" aria-hidden="true"></a>Outline</h2>
<ol>
<li>Enable Android Bluetooth logging</li>
<li>Run the Commander app and exercise the robot a bit </li>
<li>Transfer the log to a PC</li>
<li>Extract the serial data (RFCOMM) from the Bluetooth dump</li>
<li>Decode the EV3 protocol</li>
<li>Disassemble the EV3 instructions</li>
</ol>
<h3><a id="user-content-1-enable-android-bluetooth-logging" class="anchor" href="#1-enable-android-bluetooth-logging" aria-hidden="true"></a>1. Enable Android Bluetooth Logging</h3>
<ul>
<li>Open <em>Settings</em></li>
<li>In the <em>System</em> section, choose <em>Developers</em>
(this needs to be enabled first by tapping <em>Build number</em> 7 times)</li>
<li>Enable <em>Bluetooth HCI Log</em></li>
</ul>
<h3><a id="user-content-2-run-the-commander-app-and-exercise-the-robot-a-bit" class="anchor" href="#2-run-the-commander-app-and-exercise-the-robot-a-bit" aria-hidden="true"></a>2. Run the Commander app and exercise the robot a bit</h3>
<h3><a id="user-content-3-transfer-the-log-to-a-pc" class="anchor" href="#3-transfer-the-log-to-a-pc" aria-hidden="true"></a>3. Transfer the log to a PC</h3>
<p>On the phone/tablet:</p>
<ul>
<li>Open <em>Settings</em></li>
<li><em>System</em> > <em>Developers</em></li>
<li>Disable <em>Bluetooth HCI Log</em></li>
</ul>
<p>Connect to the PC with a USB cable.</p>
<p>My older Android phone offered to mount its storage as a USB disk drive, but the
newer one no longer has that option, offering <a href="https://en.wikipedia.org/wiki/Media_Transfer_Protocol">MTP</a> instead. I transfered
the log file with a KDE tool:</p>
<div class="highlight highlight-text-shell-session"><pre>$ <span class="pl-s1">kioclient cp <span class="pl-s"><span class="pl-pds">'</span>mtp:/Xperia Z3/Interní úložiště/btsnoop_hci.log<span class="pl-pds">'</span></span> <span class="pl-c1">.</span></span></pre></div>
<h3><a id="user-content-4-extract-the-serial-data-rfcomm-from-the-bluetooth-dump" class="anchor" href="#4-extract-the-serial-data-rfcomm-from-the-bluetooth-dump" aria-hidden="true"></a>4. Extract the serial data (RFCOMM) from the Bluetooth dump</h3>
<a name="extract"></a><p>The tool I made for this is <a href="https://github.com/mvidner/mindstorms/blob/9d0c87bd7c6b03d1bebfb6d4d867b1a473dcd59f/commander/btsnoop-decode.rb">btsnoop-decode.rb</a>.</p>
<p>I learned the bare minimum needed about Bluetooth so it is very likely the
tool only works for this specific use case.</p>
<p>Originally I opened the btsnoop log with <a href="https://www.wireshark.org/">Wireshark</a> and guessed my way
through the BT protocol layers. In the end the RFCOMM length field was harder
than usual to guess and half of my packets were wrong. So I resorted to
finding the <a href="http://git.kernel.org/cgit/linux/kernel/git/bluetooth/bluetooth.git/tree/net/bluetooth/rfcomm/core.c?id=7eb7404f7ee4bf59cb034897ab678aba2755c5e0#n92">appropriate part of the Linux kernel source</a> to find out
the format.</p>
<h3><a id="user-content-56-decode-the-ev3-protocol-and-dissassemble-the-ev3-instructions" class="anchor" href="#56-decode-the-ev3-protocol-and-dissassemble-the-ev3-instructions" aria-hidden="true"></a>5+6. Decode the EV3 protocol and dissassemble the EV3 instructions</h3>
<a name="decode"></a><p>The people of the <a href="http://www.ev3dev.org/">ev3dev project</a> have already
produced <a href="https://github.com/ev3dev/lms-hacker-tools/blob/04241562d0e9663b13cd708f9bf4c0fc3795d422/EV3/lmsdisasm.py">a disassembler</a> which we will use in the next step. But that
one assumes you start with a program file (RBF).</p>
<p>Here we have a log containing not only the usual RBF instructions but also
System Commands.</p>
<p>I made an ugly hack of the lmsdisasm tool and arrived at <a href="https://github.com/mvidner/lms-hacker-tools/blob/da2acc5dbe5e5088e9489ae7f2bb2d5834ed159d/EV3/lmsdisasm.py">a version that
disassembles the log produced by the RFCOMM extractor</a>.</p>
<h2><a id="user-content-play-time" class="anchor" href="#play-time" aria-hidden="true"></a>Play time</h2>
<p>The above experiments enabled me to put together a little script that can
control the robot from a Linux terminal, having it ride around and even speak
a custom sound file:
<a href="https://github.com/mvidner/mindstorms/blob/9d0c87bd7c6b03d1bebfb6d4d867b1a473dcd59f/lethargic-ministers/lms.py">lethargic-ministers/lms.py</a>.</p>
Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-28809502215115103052017-01-02T09:00:00.000+01:002017-01-02T11:15:43.896+01:00USB Communication with Python and PyUSB
<p>Say we have <a href="https://www.lego.com/mindstorms/">a robot</a>
with a USB connection and command documentation.
The only thing missing is knowing how to send a command over USB.
Let's learn the basic concepts needed for that.</p>
<p><a href="https://camo.githubusercontent.com/c50233fac1b60c8d5d5ddfbef0d22673db63a03e/687474703a2f2f692e696d6775722e636f6d2f687a5938384f562e6a7067" target="_blank"><img src="https://camo.githubusercontent.com/c50233fac1b60c8d5d5ddfbef0d22673db63a03e/687474703a2f2f692e696d6775722e636f6d2f687a5938384f562e6a7067" alt="General Bunny catching Pokemon" data-canonical-src="http://i.imgur.com/hzY88OV.jpg" style="max-width:100%;"></a></p>
<h3><a id="user-content-installing-the-library" class="anchor" href="#installing-the-library" aria-hidden="true"></a>Installing the Library</h3>
<p>We'll use the <a href="https://github.com/walac/pyusb">pyusb</a> Python library.
On openSUSE we install it from the main RPM repository:</p>
<div class="highlight highlight-text-shell-session"><pre><span class="pl-mo">sudo zypper install python-usb</span></pre></div>
<p>On other systems we can use the <code>pip</code> tool:</p>
<div class="highlight highlight-text-shell-session"><pre><span class="pl-mo">pip install --user pyusb</span></pre></div>
<h3><a id="user-content-navigating-usb-concepts" class="anchor" href="#navigating-usb-concepts" aria-hidden="true"></a>Navigating USB Concepts</h3>
<p>To send a command, we need an Endpoint.
To get to the endpoint we need to descend down the hierarchy of</p>
<ol>
<li>Device</li>
<li>Configuration </li>
<li>Interface</li>
<li>Alternate setting</li>
<li>Endpoint</li>
</ol>
<p>First we import the library.</p>
<div class="highlight highlight-source-python"><pre><span class="pl-c"><span class="pl-c">#</span>!/usr/bin/env python2</span>
<span class="pl-k">import</span> usb.core</pre></div>
<p>The device is identified with a vendor:product pair included in <code>lsusb</code> output.</p>
<blockquote>
<p><code>Bus 002 Device 043: ID 0694:0005 Lego Group</code></p>
</blockquote>
<div class="highlight highlight-source-python"><pre><span class="pl-c1">VENDOR_LEGO</span> <span class="pl-k">=</span> <span class="pl-c1"><span class="pl-k">0x</span>0694</span>
<span class="pl-c1">PRODUCT_EV3</span> <span class="pl-k">=</span> <span class="pl-c1">5</span>
device <span class="pl-k">=</span> usb.core.find(<span class="pl-v">idVendor</span><span class="pl-k">=</span><span class="pl-c1">VENDOR_LEGO</span>, <span class="pl-v">idProduct</span><span class="pl-k">=</span><span class="pl-c1">PRODUCT_EV3</span>)</pre></div>
<p>A Device may have multiple Configurations, and only one can be active at
a time. Most devices have only one. Supporting multiple Configurations
is reportedly useful for offering more/less features when more/less
power is available. EV3 has only one configuration.</p>
<div class="highlight highlight-source-python"><pre>configuration <span class="pl-k">=</span> device.get_active_configuration()</pre></div>
<p>A physical Device may have multiple Interfaces active at a time.
A typical example is a scanner-printer combo. An Interface may have multiple
Alternate Settings. They are kind of like Configurations, but easier to
switch.
I don't quite understand this, but they say that if you need Isochronous
Endpoints (read: audio or video), you must go to a non-primary
Alternate Setting. Anyway, EV3 has only one Interface with one Setting.</p>
<div class="highlight highlight-source-python"><pre><span class="pl-c1">INTERFACE_EV3</span> <span class="pl-k">=</span> <span class="pl-c1">0</span>
<span class="pl-c1">SETTING_EV3</span> <span class="pl-k">=</span> <span class="pl-c1">0</span>
interface <span class="pl-k">=</span> configuration[(<span class="pl-c1">INTERFACE_EV3</span>, <span class="pl-c1">SETTING_EV3</span>)]</pre></div>
<p>An Interface will typically have multiple Endpoints. The Endpoint 0 is
reserved for control functions by the USB standard so we need to use Endpoint
1 here.</p>
<p>The standard distinguishes between input and output endpoints, as well as four
transfer types, differing in latency and reliability. The nice thing is that
the Python library nicely allows to abstract all that away (unlike cough Ruby
cough) and we simply say to <code>write</code> to a non-control Endpoint.</p>
<div class="highlight highlight-source-python"><pre><span class="pl-c1">ENDPOINT_EV3</span> <span class="pl-k">=</span> <span class="pl-c1">1</span>
endpoint <span class="pl-k">=</span> interface[<span class="pl-c1">ENDPOINT_EV3</span>]
<span class="pl-c"><span class="pl-c">#</span> make the robot beep</span>
command <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span><span class="pl-cce">\x0F\x00\x01\x00\x80\x00\x00\x94\x01\x81\x02\x82\xE8\x03\x82\xE8\x03</span><span class="pl-pds">'</span></span>
endpoint.write(command)</pre></div>
<h3><a id="user-content-other-than-robots" class="anchor" href="#other-than-robots" aria-hidden="true"></a>Other than Robots?</h3>
<p>Robots are great fun but unfortunately they do not come bundled with every
computer. Do you know of a device that we could use for demonstration
purposes? Everyone has a USB keyboard and mouse but I guess the OS will claim
them for input and not let you play.</p>
<h3><a id="user-content-what-next" class="anchor" href="#what-next" aria-hidden="true"></a>What Next</h3>
<ul>
<li><a href="https://walac.github.io/pyusb/">PyUSB</a></li>
<li><a href="https://github.com/walac/pyusb/blob/master/docs/tutorial.rst">PyUSB tutorial</a></li>
<li><a href="http://www.beyondlogic.org/usbnutshell/usb1.shtml">USB in a nutshell</a> goes
deeper, and is aimed more at firmware developers for the devices, but still
is much shorter than the 650 page USB 2.0 specification</li>
<li>EV3 documentation at
<a href="https://www.lego.com/en-gb/mindstorms/downloads">Mindstorms Downloads</a></li>
</ul>
<h3><a id="user-content-the-full-script" class="anchor" href="#the-full-script" aria-hidden="true"></a>The Full Script</h3>
<script src="https://gist.github.com/mvidner/04ffe0bbea0fc24182772a196f238918.js"></script>
Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com1tag:blogger.com,1999:blog-34112729.post-35605801456696682982016-08-01T09:42:00.000+02:002016-08-01T09:42:55.768+02:00Virtual Machine from a Hard Disk Image with virt-install --importThis is one of many ways to create a virtual machine. This way starts with a hard disk image.<br />
<br />
<pre class="console"><code>NAME=sles12sp2b4
IMG=https://example.com/images/SLE_12_SP2_Beta4-x86_64-default.qcow2
wget -O /var/lib/libvirt/images/$NAME $IMG
virt-install --name $NAME \
--ram 2048 --graphics type=vnc \
--network bridge=br0 \
--import \
--disk bus=virtio,path=/var/lib/libvirt/images/$NAME</code></pre>
<br />
For <code>IMG</code> I used a SUSE-internal server with SLEnkins images.<br />
The tricky part is knowing the right value for the <code>--disk bus</code> setting. At first I used the default but the machine wouldn't boot because it would see <code>/dev/sda</code> instead of <code>/dev/vda</code> it was expecting.Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-29634627047328273732016-05-27T14:59:00.001+02:002016-05-27T14:59:03.228+02:00Ruby Call Graph<a href="https://github.com/mvidner/call-graph">Call-graph</a> makes a call graph among methods of a single Ruby file.<br />
I made it to help me orient myself in unfamiliar legacy code and to help identify cohesive parts that could be split out.<br />
Yes, it is quick and dirty.<br />
<h4 id="requirements">
</h4>
<h4 id="example">
Example</h4>
<a href="https://github.com/yast/yast-packager/blob/a0b38c046e6e4086a986047d0d7cd5d155af5024/src/modules/Packages.rb">One file in YaST</a> has around 2700 lines and 73 methods. The call graph below was made with<br />
<pre class="console"><code>$ ./call-graph ../yast/packager/src/modules/Packages.rb
$ dot -Tpng -oPackages.png ../yast/packager/src/modules/Packages.dot</code></pre>
If the resulting size is too big, use ImageMagick:<br />
<pre class="console"><code>$ convert Packages.png -resize 1200 Packages-small.png</code></pre>
<div class="figure">
<img alt="Packages.png, an example output" border="0" src="https://raw.githubusercontent.com/mvidner/call-graph/master/example.png" style="max-width: 100%;" />
<br />
<div class="caption">
Packages.png, an example output</div>
</div>
<h4 id="requirements">
Requirements</h4>
<ul>
<li>Ruby</li>
<li><a href="https://github.com/whitequark/parser">parser gem</a></li>
<li><a href="http://www.graphviz.org/">Graphviz</a></li>
</ul>
<h4 id="license">
License</h4>
MITMartin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-82033038418871338232015-11-12T13:18:00.000+01:002015-11-12T13:18:57.672+01:00Git: Single Line HistoryYou get a bug report from a user:<br />
<blockquote class="tr_bq">
<samp>
/usr/lib/foo/bar.rb:432:in `doit': undefined method `[]' for nil:NilClass (NoMethodError)</samp></blockquote>
but in bar.rb at the line 432 there are no square brackets. The user must be using an older version of the script. Can we find out which one without asking them?<br />
<br />
Git can help. This code will go back in history and show the line how it appeared during the past. It's a history of a single line, kind of like "<code>git blame</code>" but in a different dimension.<br />
<br />
<pre class="prettyprint"><code class="language-sh">FILE=lib/foo/bar.rb
LINE=432
git log --format=format:%H $FILE \
| while read COMMIT_ID; do
echo -n $COMMIT_ID:$FILE:$LINE:
git show $COMMIT_ID:$FILE | sed -n "$LINE{p;q}"
done \
| less </code></pre>
<br />
Have I reinvented the wheel? What is its name?
<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-64701834888881704142015-11-10T09:41:00.000+01:002015-11-10T09:41:13.554+01:00Arabic Text BugfixCan you spot the difference?<br /><br />
Before:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_vWV6dUNYg8MZdTeWfLPvV7a5hOi7PJQZWEpuCiy1cREzOLU_Tntxh5Jng4oBSmgPwktT6rYFoLwrPVnVURkCYLdc7uI2UK_lBb5Grcpnh6BtI_LNEBLHD5uJ04JafvvJV0Mh/s1600/arabic-users-2-bad-rc1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="" border="0" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_vWV6dUNYg8MZdTeWfLPvV7a5hOi7PJQZWEpuCiy1cREzOLU_Tntxh5Jng4oBSmgPwktT6rYFoLwrPVnVURkCYLdc7uI2UK_lBb5Grcpnh6BtI_LNEBLHD5uJ04JafvvJV0Mh/s320/arabic-users-2-bad-rc1.png" title="Create New User, in Arabic, before the fix" width="320" /></a></div>
After:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQe0D2-o_6r7jZIZ5_NRZuIEG0G4qWHrZZk-UUJrkViIYL_sCMPB7uyCTR6GWBHr2wmO-Gbfke-Z1gvBZxrq41Yqsb6u6rX5x9DQ81TfAN5sVd9ZTjkO7Yxn7YENRaOIcMUH7U/s1600/arabic-users-3-good-rc3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="" border="0" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQe0D2-o_6r7jZIZ5_NRZuIEG0G4qWHrZZk-UUJrkViIYL_sCMPB7uyCTR6GWBHr2wmO-Gbfke-Z1gvBZxrq41Yqsb6u6rX5x9DQ81TfAN5sVd9ZTjkO7Yxn7YENRaOIcMUH7U/s320/arabic-users-3-good-rc3.png" title="Create New User, in Arabic, after the fix" width="320" /></a></div>
If this rings a bell but you can't quite remember why, here's an English version of the screen, and the spoiler for the puzzle is below it:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguAgcL0WWb3KxCBT0z1lWTDcuO6-S4pHNLtkaMMqB5ZJb8qkb6ZUmmGDeT_MbzA6KxS4q0os5UTMwqUK9iYPdZOfGLdVKyTnYO2POO5nlWd6dLzSLjDoqs9k99mDyIj-rynyx3/s1600/arabic-users-4-english.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguAgcL0WWb3KxCBT0z1lWTDcuO6-S4pHNLtkaMMqB5ZJb8qkb6ZUmmGDeT_MbzA6KxS4q0os5UTMwqUK9iYPdZOfGLdVKyTnYO2POO5nlWd6dLzSLjDoqs9k99mDyIj-rynyx3/s320/arabic-users-4-english.png" width="320" /></a></div>
Spoiler: The line containing "passwd" is clipped at the (left) end, showing only "المحا" instead of "المحلي۔". This bug got popular in the YaST team because the localization testers dutifully reported every instance of a truncated label so the bug accumulated 22 duplicates. It only happened for the Arabic script which made it a bit more challenging to work with, but luckily I know the script and a few words.<br />
<br />
Thanks to Max Lin who pointed me to a problem between the <a href="http://www.qt.io/">Qt UI</a> library and the <a href="http://www.freedesktop.org/wiki/Software/HarfBuzz/">HarfBuzz</a> text shaping engine, <a href="https://github.com/yast/yast-yast2/pull/406">the problem is now fixed</a>.Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-17254037496876843682013-08-02T17:40:00.000+02:002013-08-02T17:40:05.087+02:00YaST in Ruby<br />
As already announced on
<a href="http://lists.opensuse.org/opensuse-factory/2013-08/msg00018.html">Factory</a>,
<a href="http://lists.opensuse.org/yast-devel/2013-07/msg00247.html">yast-devel</a>,
and by
<a href="http://kobliha-suse.blogspot.com/2013/07/yast-says-hello-ruby.html">Lukáš</a>:
YaST, the SUSE installation and configuration tool, has been automatically
translated from YCP, an in-house custom language, to Ruby. In the past 6 months, we have built a tool to translate 600.000 lines of code developed over the course of 12 years.<br />
<br />
My role in the project was mainly shedding light on ancient details of the YCP
language and its interpreter. Stop pulling my beard, kids! Also, knowing Bison (the tool used to implement
the YCP parser in C++) I designed a part that transfers the comments. Mind
you, not only at the function or statement level, but from
<a href="https://github.com/yast/yast-network/blob/8a2f8b0dfc2c6a83eb52c9fa7efaf47179b4392d/src/clients/inst_do_net_test.rb#L80">inside of expressions</a>
too. Fun!<br />
<br />
Thanks to the team, it was great working on the project with you!Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com2tag:blogger.com,1999:blog-34112729.post-5758945499661893412011-05-16T15:04:00.000+02:002011-05-16T15:04:52.396+02:00LinuxTag 2011 PicturesAfter a problem with the organizers' equipment, kobliha's openSUSE laptop was used to watch the live stream from Skynet I/O Chrome keynote.<br />
<a href="http://www.flickr.com/photos/martin-vidner/5725855757/" title="LinuxTag: openSUSE saves the day by Martin Vidner, on Flickr"><img src="http://farm3.static.flickr.com/2430/5725855757_b4a7f9ac16_m.jpg" width="240" height="160" alt="LinuxTag: openSUSE saves the day"></a><br />
"Rule 1: Stay calm." Ralph Angenendt talks about problem solving.<br />
<a href="http://www.flickr.com/photos/martin-vidner/5725855761/" title="LinuxTag: Breathe! by Martin Vidner, on Flickr"><img src="http://farm6.static.flickr.com/5025/5725855761_aeda3a02d9_m.jpg" width="240" height="180" alt="LinuxTag: Breathe!"></a><br />
Party!<br />
<a href="http://www.flickr.com/photos/martin-vidner/5725855771/" title="LinuxTag: Party in Umspannwerk Kreuzberg by Martin Vidner, on Flickr"><img src="http://farm4.static.flickr.com/3633/5725855771_1c1f2ac8b3_m.jpg" width="240" height="160" alt="LinuxTag: Party in Umspannwerk Kreuzberg"></a>Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-37584959013175978862010-12-22T15:27:00.000+01:002010-12-22T15:27:45.909+01:00network-autoconfig: Find a connected eth interface and create an ifcfg for itUse case: I want to create an appliance image for a build farm worker machine. I don't know its hardware configuration beforehand; in particular, it may have multiple network interfaces and I don't want to bother figuring out which is which. I will simply include <b>network-autoconfig.rpm</b> and plug the cable into any socket before booting.<br />
<br />
network-autoconfig helps setting up machines with multiple network interfaces. At the first boot, all available Ethernet interfaces are cycled until one is successfully configured with DHCP.<br />
<br />
I have submitted it to openSUSE:Factory now. Enjoy it in the upcoming openSUSE 11.4!<br />
<br />
It is referenced as <a href="https://features.opensuse.org/311012">Feature#311012</a>. You are welcome to improve it on <a href="http://gitorious.org/opensuse/network-autoconfig">Gitorious</a>.Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-42216948089123404982010-11-16T14:30:00.000+01:002010-11-16T14:30:05.455+01:00dbus-dump<p><a href="https://github.com/mvidner/dbus-dump">dbus-dump</a> is a tool to capture <a href="http://www.freedesktop.org/wiki/Software/dbus">D-Bus</a> messages
in a <a href="http://wiki.wireshark.org/Development/LibpcapFileFormat">libpcap capture file</a>.</p>
<p>It takes an idea from <a href="http://git.collabora.co.uk/?p=user/daf/dbus-scrape;a=summary">dbus-scrape</a>, which processes a strace output of
dbus-monitor, and takes it further by stracing dbus-daemon, thus not
relying on any eavesdropping (mis)configuration.</p>
<p>The intended purpose is to establish the libpcap capture format as a
base for debugging tools like</p>
<ul>
<li>dbus-monitor</li>
<li><a href="http://alban.apinc.org/blog/dbusmessagesboxpy/">DBusMessageBox</a></li>
<li><a href="http://www.willthompson.co.uk/bustle/">Bustle</a></li>
<li><a href="http://mvidner.blogspot.com/2008/06/d-bus-spy.html">dbus-spy</a></li>
</ul>
<p>Thanks to Will Thompson for mentioning the pcap idea.</p>
<h3>Usage</h3>
<pre><code>$ sudo strace -p `pgrep -f 'dbus-daemon --system'` \
-s 3000 -ttt -xx -o foo.strace
$ ./dbus-dump foo.strace foo.pcap
$ ./dbus-pcap-parse foo.pcap
Tue Nov 16 12:56:47 +0100 2010 #<DBus::Message:0xb741f340
@body_length=0,
@destination="fi.epitest.hostap.WPASupplicant",
@error_name=nil,
@flags=0,
@interface="fi.epitest.hostap.WPASupplicant.Interface",
@member="scan",
@message_type=1,
@params=[],
@path="/fi/epitest/hostap/WPASupplicant/Interfaces/180",
@protocol=1,
@reply_serial=nil,
@sender=":1.7132",
@serial=88639,
@signature="">
Tue Nov 16 12:56:47 +0100 2010 #<DBus::Message:0xb741b060
@body_length=4,
@destination=":1.7132",
[...]>
</code></pre>
<h3>Dependencies</h3>
<p>It is written in Ruby. The pcap format is handled by a small bundled module.
dbus-dump has no other dependencies. dbus-pcap-parse uses
<a href="https://github.com/mvidner/ruby-dbus">ruby-dbus</a>.</p>
<h3>Bugs</h3>
<p>This is an early proof-of-concept release, serving to introduce
the libpcap format.</p>
<p>The main problem of dbus-dump is duplicating the messages, seeing them
both when the daemon receives them and when it sends them (multiple
times, for the signals).</p>
<p>The other tools haven't caught up yet:</p>
<pre><code>$ /usr/sbin/tcpdump -r foo.pcap
reading from file foo.pcap, link-type 231
tcpdump: unknown data link type 231
</code></pre>Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com2tag:blogger.com,1999:blog-34112729.post-10685199910968927672010-08-29T10:23:00.000+02:002010-08-29T10:23:19.962+02:00n2n package improvedRoot.cz recently ran an <a href="http://www.root.cz/clanky/otevrena-alternativa-k-hamachi-se-jmenuje-n2n/">article</a> (in Czech) about the <a href="http://www.ntop.org/n2n/">n2n virtual private network sofware</a>, an open alternative to Hamachi. I intend to use it to cross the NAT in my mom's DSL modem so that I can connect to her computer with ssh and VNC. <br />
Thanks to happyman_eric and Grief, <a href="http://build.opensuse.org/">openSUSE Build Service</a> already contained a package. I have made an improved version, adding an init script and a sysconfig file. Get the <a href="https://build.opensuse.org/package/files?package=n2n&project=home%3Amvidner">sources</a> in my home project, or the <a href="http://s.opensu.se/bs/n2n">binaries</a>.<br />
<br />Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com2tag:blogger.com,1999:blog-34112729.post-58330714220196484722010-08-10T16:13:00.000+02:002010-08-10T16:13:13.082+02:00Who does not want such a gift<p>This <a href="http://rauhmaru.blogspot.com/2010/08/parabens-opensuse-5-anos-de-vida.html" rel="nofollow">post</a> is showing naked women packaged as a present (for openSUSE's 5th birthday) and asks <q>Ah... who does not want such a gift, eh</q>.
<p>
I believe that the female users and contributors of openSUSE don't. It reduces them to pretty things, judged on their looks instead of their contribution.
<p>
Raul, please stop such sexist postings. Your blog is your own, but keep it out from Planet openSUSE.
<p>
Just in case common sense is not enough, let me quote for reference the relevant section of the <a href="http://en.opensuse.org/openSUSE:Guiding_principles">openSUSE Guiding Principles</a>:
<blockquote>We value... respect for other persons and their contributions, for other opinions and beliefs. We listen to arguments and address problems in a constructive and open way. We believe that a diverse community based on mutual respect is the base for a creative and productive environment enabling the project to be truly successful. We don't tolerate social discrimination and aim at creating an environment where people feel accepted and safe from offense.</blockquote>
and the <a
href="http://en.opensuse.org/openSUSE_talk:Strategy_Community_Statement#Excel_Activity_List">activities
in order to excel in our goals</a>: <q>Emphasize the value of communication and recognize cultural diversity within our community</q>.Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com6tag:blogger.com,1999:blog-34112729.post-54316075534885908292010-08-05T16:12:00.000+02:002010-08-05T16:12:42.903+02:00Upgraded to openSUSE 11.3I have upgraded my office workstation to openSUSE 11.3 today. Here I document the migration so that you don't have to repeat my mistakes.<br />
<br />
I went via the <a href="http://en.opensuse.org/SDB:System_upgrade#Command_line_2">command line</a> and ventured to keep all additional repos enabled. So switching the repos was done simply by <code>sed -i "s/11\.2/11.3/g" /etc/zypp/repos.d/*</code><br />
<br />
The KDE session crashed after kdelibs4-core had been updated. No big deal, so I ran <code>zypper dup</code> again. <br />
The /home directory is mounted via NFS, so filesystem.rpm failed when it wanted to reset the permissions of /home. Updating it explicitly after unmounting /home was easy, only unmounting it was harder because the crashed session left processes still accessing the home. <code>fuser -v /home</code> found them.<br />
<br />
The NFS mount is also authenticated by Kerberos and there is a bug so I got<br />
<blockquote><tt>mount.nfs: access denied by server while mounting nfs.example.com:/home</tt></blockquote>I had to rebuild and update a package and tweak a config file. See <a href="http://bugzilla.novell.com/show_bug.cgi?id=614293">bnc#614293</a> for the details (thanks to mcaj for the reference).Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-15642064482193088552010-07-22T14:17:00.000+02:002010-07-22T14:17:36.295+02:00ruby-dbus 0.3.1I have made a bug-fix release of <a href="http://github.com/mvidner/ruby-dbus">ruby-dbus</a>, a <a href="http://www.ruby-lang.org/">Ruby language</a> binding for the <a href="http://www.freedesktop.org/wiki/Software/dbus">D-Bus IPC system</a>.<br />
<ul><li>Many on_signal could cause DBus.Error.LimitsExceeded (<a href="https://bugzilla.novell.com/show_bug.cgi?id=617350">bnc#617350</a>).<br />
Don't add a match rule that already exists, enable removing match rules. Now only one handler for a rule is called (but it is possible for one signal to match more rules). This reverts the half-fix done to fix <a href="http://github.com/mvidner/ruby-dbus/issues#issue/3">Issue#3</a>.</li>
<li>Re-added InterfaceElement#add_param for compatibility.</li>
<li>Handle more ways which tell us that a bus connection has died.</li>
</ul>RPMs can be found via <a href="http://s.opensu.se/p/ruby-dbus">Webpin</a>.Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-5661210120969494412010-07-02T11:54:00.000+02:002010-07-02T11:54:13.644+02:00Helping NewcomersSince the <a href="http://lists.opensuse.org/opensuse-project/2010-06/msg00364.html">discussion</a> (do check out the linked <a href="http://www.flosspols.org/deliverables/FLOSSPOLS-D16-Gender_Integrated_Report_of_Findings.pdf">paper</a>, BTW) and the <a href="http://lists.opensuse.org/opensuse-project/2010-06/msg00909.html">opensuse-women announcement</a>, I've been thinking about how to make the openSUSE community more friendly to women.<br />
<br />
I think one good way is to make sure that new people feel welcome when they join a conversation, be it on the <a href="http://forums.opensuse.org/">forums</a>, on <a href="http://en.opensuse.org/Communicate/IRC">IRC</a> or on the <a href="http://lists.opensuse.org/">mailing lists</a>. Now this would be easier if we all had infinite time to read and answer all questions, but as we don't, I decided to focus somehow.<br />
<br />
The forums provide a handy shortcut for the focus, labeling a user who made few posts as a "Puzzled Penguin". So I've made a simple service, a feed of http://forums.opensuse.org showing only the posts by newcomer users: <b><a href="http://vidner.net/martin/software/rss-creator-blacklist">http://vidner.net/martin/software/rss-creator-blacklist</a></b><br />
<br />
(Actually right now it does not show Puzzled Penguins only but instead excludes the 100 most-posting users until I learn how to optimize the PHP code.)Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com1tag:blogger.com,1999:blog-34112729.post-14330745537846123672010-06-30T15:49:00.001+02:002010-06-30T15:50:33.508+02:00kiwi2puppetI have started working on <a href="http://github.com/mvidner/kiwi2puppet">kiwi2puppet</a>, a tool to convert <a href="http://kiwi.berlios.de/">KIWI</a> image descriptions to <a href="http://www.puppetlabs.com/puppet/introduction/">Puppet</a> manifests.<br />
<br />
The goal is to recycle the data that went into the <i>building</i> of an image and use it for <i>managing</i> a deployed appliance.<br />
<br />
So far it is a prototype that can write these resources<br />
<ul><li>package</li>
<li>yumrepo</li>
<li>user</li>
<li>group</li>
</ul>It is written in Ruby, like Puppet.<br />
<br />
Source at GitHub: <a href="http://github.com/mvidner/kiwi2puppet">http://github.com/mvidner/kiwi2puppet</a><br />
RPMs: <a href="http://software.opensuse.org/search?q=kiwi2puppet&baseproject=ALL">http://software.opensuse.org/search?q=kiwi2puppet&baseproject=ALL</a> (currently it is a single Ruby script, so at the moment RPMs are not worth any trouble)<br />
Novell Reference: <a href="https://fate.novell.com/309497">FATE#309497</a><br />
<br />
Get in touch if you're interested.<br />
<br />
In case you didn't know:<br />
<blockquote>"The openSUSE KIWI Image System provides a complete operating system image solution for Linux supported hardware platforms as well as for virtualisation systems like Xen Qemu or VMware."</blockquote><blockquote>"Puppet is an open source data center automation and configuration management framework. Puppet provides system administrators with a simplified platform that allows for consistent, transparent, and flexible systems management."</blockquote>Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com0tag:blogger.com,1999:blog-34112729.post-65789432380705543602010-03-28T12:28:00.000+02:002010-03-28T12:28:49.491+02:00ruby-dbus 0.3.0 Works on Ubuntu<a href="http://github.com/mvidner/ruby-dbus">ruby-dbus</a> has for a long time not worked on the default Ubuntu desktop. I thought I had fixed it in 0.2.12 but that was only for the server side. In fact, clients had <a href="https://trac.luon.net/ruby-dbus/ticket/34">another bug</a> and I have released <a href="http://cloud.github.com/downloads/mvidner/ruby-dbus/ruby-dbus-0.3.0.tgz">version 0.3.0</a> today to fix it.<br />
<br />
NEWS:<br />
<blockquote>Bug fixes:<br />
<ul><li>Fixed "undefined method `get_node' for nil:NilClass" on Ubuntu Karmic (Ticket#34).</li>
<li>Get the session bus address even if unset in ENV (Issue#4).</li>
<li>Improved exceptions a bit: UndefinedInterface, InvalidMethodName, NoMethodError, no RuntimeException</li>
</ul>These are by Klaus Kaempf:<br />
<ul><li>Make the signal dispatcher call all handlers (Issue#3).</li>
<li>Run on Ruby < 1.8.7 (Issue#2).</li>
<li>Avoid needless DBus::IncompleteBufferException (Ticket#33).<br />
</li>
<li>Don't ignore DBus Errors in request_service, raise them (Ticket#32).<br />
</li>
</ul>Features: <br />
<ul><li>Automatic signature inference for variants.<br />
</li>
<li>Introduced FormalParameter where a plain pair had been used.<br />
</li>
</ul></blockquote>RPMs can be found via <a href="http://s.opensu.se/p/ruby-dbus">Webpin</a>.Martin Vidnerhttp://www.blogger.com/profile/08755880808140601974noreply@blogger.com2