PulseAudio headset switcher

From time to time I encounter an embarrasing moment on meeting when new headset does not work. I purchased new one which only made some noise instead. I will return this back, but in the meantime I found a Logitech USB headset in my closet and it works pretty well, so I think I am gonna stick with it.

I created few aliases that will help me to switch PulseAudio default input and output to headset and back and forth. My aliases also switch all existing streams, which is great if I open up BlueJeans or WebEx before making the change.

This switches input and output to headset, volume is about 90 % for output, 100 % for input, unmutes input and says “Speak”:


This puts everything back to laptop default soundcard setting full output volume and zero input volume:


This mutes the card and says “Mute”:


And this unmutes it and says “Speak”:


I have some key bindings in my i3 window manager to do mute/unmute using keyboard shortcut. I have to say this is finally a setup that works. Here is the script you need to put into your .bashrc:

export MY_NORMAL_OUTPUT=alsa_output.pci-0000_00_1b.0.analog-stereo
export MY_NORMAL_INTPUT=alsa_input.pci-0000_00_1b.0.analog-stereo
export MY_HEADSET_OUTPUT=alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo
export MY_HEADSET_INPUT=alsa_input.usb-Logitech_Logitech_USB_Headset-00.analog-mono
alias auvolup="pactl set-sink-volume $MY_HEADSET_OUTPUT +10% && pactl play-sample blip"
alias auvoldown="pactl set-sink-volume $MY_HEADSET_OUTPUT -10% && pactl play-sample blip"
alias auvolfull="pactl set-sink-volume $MY_HEADSET_OUTPUT 65536 && pactl play-sample blip"
alias aumute="pactl set-source-mute $MY_HEADSET_INPUT yes && pactl play-sample mute"
alias auspeak="pactl set-source-mute $MY_HEADSET_INPUT no && pactl play-sample speak"
function pacmd-set-output-input {
  pacmd set-default-sink $1
  echo "Default output set $1 (volume $3)"
  pacmd set-default-source $2
  echo "Default intput set $2 (volume $4)"
  pacmd set-sink-volume $1 $3
  pacmd set-source-volume $2 $4
  pacmd list-sink-inputs | grep index | while read line; do
    INDEX=$(echo $line | cut -f2 -d' ')
    echo "Moving $INDEX to $1"
    pacmd move-sink-input $INDEX $1
  pactl play-sample blip
  echo "Done, all set to $5"
  [[ "$5" == "HEADSET" ]] && auspeak
alias aunormal="pacmd-set-output-input $MY_NORMAL_OUTPUT $MY_NORMAL_INTPUT 65536 0 NORMAL"
alias auheadset="pacmd-set-output-input $MY_HEADSET_OUTPUT $MY_HEADSET_OUTPUT 40000 65536 HEADSET"

You need to edit MY_* variables, use pactl list to find your device names.

You also want to put this in your startup script of your WM or somewhere else where it is loaded just once when PulseAudio starts. In the worst case you can put this into your .bashrc as well, but it will slow down your shell start.

pactl upload-sample ~/.i3/blip.wav blip
pactl upload-sample ~/.i3/mute.wav mute
pactl upload-sample ~/.i3/speak.wav speak

I found USB headset to be much more cleaner than integrated laptop sound card.

13 April 2017 | linux | fedora

Improved the blog theme

I’ve improved the navbar, it should now work on mobile devices. Thanks for reporting this.

I tried to update to Bootstrap 3 but it did not work well. I guess I will stay on the old version for now. Christmas time!

02 December 2016 | blog

XTerm Zen mode

I do lot of talks these days with some amount of shell live demos. Although it is easy to configure xterm for beamer (black on white, big font, fullscreen), I created an alias for that:

xterm -cm -fa Monospace -fs 32 -bg white -fg black -cr red -bc \
    -bcf 200 -bcn 200 -xrm "allowColorOps false" -fullscreen \
    -e "PS1='$ ' LC_ALL=C /bin/bash --norc --noprofile"

And this is how it looks like:


I cheated, the font you see is Terminus, not Monospace. But it’s not installed by default, I highly recommend it. Check it out.

If you are reading those lines, chances are this might be useful for you. Take care!

14 October 2016 | linux | fedora

Hidden feature of Fedora 24: Live PXE boot

Some time ago, I was trying to PXE-boot Fedora 20 LiveCD directly as a squashfs filesystem from TFTP server. It was not working, because one of the dracut modules was not included on the CD/DVD init RAM disk. I filed a bug, some time passed and it’s finally fixed.

Required steps to achieve Live Fedora from PXE:

  • download Fedora 24 Live DVD (or older)
  • extract squashfs.img
  • put it on HTTP site (/var/www/htdocs)
  • extract vmlinuz and initrd.img
  • install TFTP server
  • put them into TFTP folder (subfolder boot in my case)
  • setup DHCP and PXE
  • deploy PXELinux configuration (below)
  • profit!

Example PXELinux configuration:

default menu
menu title PXE
prompt 0
timeout 200
ontimeout local

label local
  menu label ^Boot from local drive
  menu default
  localboot 0

label fedora-live
  menu label Fedora Workstation LiveBoot
  kernel boot/fedora-live/vmlinuz
  append initrd=boot/fedora-live/initrd.img root=live:http://nas.home.lan/xxx/squashfs.img ro rd.live.image rd.luks=0 rd.md=0 rd.dm=0

Boot Fedora Live from your network, install it from your network when your crying friends show up with their laptops upgraded to Windows 10. Do it like a boss. End of transmission.

16 August 2016 | linux | fedora

Probing Ruby 2.0 apps with SystemTap in RHEL7

Few years ago, I wrote an article about SystemTap and Ruby in RHEL6. When RHEL 7.0 was released, things changed. It has Ruby 2.0 and the Ruby SystemTap API changed as well, therefore I am updating my old article today according to new changes.

Imagine you have a Ruby application that has some performance issues on a production server and it’s running RHEL 7.0 or newer. With SystemTap, you can easily peek into the running application and investigate bottlenecks or count memory objects. If you know DTrace from other operating systems, welcome home.

Installation of SystemTap is easy and straightforward and for our purposes we do not need to install kernel devel and debug info packages.

# yum -y install systemtap systemtap-runtime ruby

Let’s create a trivial application called factorial.rb.

# cat factorial.rb
def factorial n
  f = 1; for i in 1..n; f *= i; end; f
puts factorial(ARGV[0].to_i)

And a simple SystemTap script that shows method calls:

# cat rubycalls.stp
probe ruby.method.entry, ruby.cmethod.entry
  if (file == "factorial.rb") {
    printf("%s => %s.%s in %s:%d\n", thread_indent(1), classname, methodname, file, line);
probe ruby.method.return, ruby.cmethod.return
  if (file == "factorial.rb") {
    printf("%s <= %s.%s in %s:%d\n", thread_indent(-1), classname, methodname, file, line);

Let’s just run it for now.)

# stap rubycalls.stp -c "ruby factorial.rb 4"
 0 ruby(29131): => IO.set_encoding in factorial.rb:0
 6 ruby(29131): <= IO.set_encoding in factorial.rb:0
 0 ruby(29131): => IO.set_encoding in factorial.rb:0
 3 ruby(29131): <= IO.set_encoding in factorial.rb:0
 0 ruby(29131): => #<Class:0x00000002378c80>.core#define_method in factorial.rb:1
 6 ruby(29131):  => Module.method_added in factorial.rb:1
 9 ruby(29131):  <= Module.method_added in factorial.rb:1
45 ruby(29131): <= #<Class:0x00000002378c80>.core#define_method in factorial.rb:1
 0 ruby(29131): => String.to_i in factorial.rb:4
 2 ruby(29131): <= String.to_i in factorial.rb:4
 0 ruby(29131): => Object.factorial in factorial.rb:1
19 ruby(29131):  => Range.each in factorial.rb:2
23 ruby(29131):  <= Range.each in factorial.rb:2
25 ruby(29131): <= Object.factorial in factorial.rb:3
 0 ruby(29131): => Kernel.puts in factorial.rb:4
 7 ruby(29131):  => IO.puts in factorial.rb:4
15 ruby(29131):   => Fixnum.to_s in factorial.rb:4
18 ruby(29131):   <= Fixnum.to_s in factorial.rb:4
21 ruby(29131):   => IO.write in factorial.rb:4
40 ruby(29131):   <= IO.write in factorial.rb:4
43 ruby(29131):   => IO.write in factorial.rb:4
48 ruby(29131):   <= IO.write in factorial.rb:4
50 ruby(29131):  <= IO.puts in factorial.rb:4
52 ruby(29131): <= Kernel.puts in factorial.rb:4

Please note you have to run SystemTap under root account and also expect the first run to be a little bit slower, because SystemTap is compiling and inserting a kernel module under the hood.

SystemTap syntax is similar to C and the easiest way of learning it is reading SystemTap Beginners Guide. From the book:

SystemTap allows users to write and reuse simple scripts to deeply examine the activities of a running Linux system. These scripts can be designed to extract data, filter it, and summarize it quickly (and safely), enabling the diagnosis of complex performance (or even functional) problems.

The essential idea behind a SystemTap script is to name events, and to give them handlers. When SystemTap runs the script, SystemTap monitors for the event; once the event occurs, the Linux kernel then runs the handler as a quick sub-routine, then resumes.

There are several kinds of events; entering or exiting a function, timer expiration, session termination, etc. A handler is a series of script language statements that specify the work to be done whenever the event occurs. This work normally includes extracting data from the event context, storing them into internal variables, and printing results.

The following example will count method calls to quickly search for bottlenecks. If you don’t understand how it works, head over to the Beginners Guide for more details.

# cat rubystack.stp

global fn_calls;

probe ruby.method.entry, ruby.cmethod.entry
  fn_calls[classname, methodname] <<< 1;

probe end {
  foreach ([classname, methodname] in fn_calls- limit 30) {
    printf("%dx %s.%s\n", @count(fn_calls[classname, methodname]), classname, methodname);

  delete fn_calls;

Everytime a Ruby method is entered, counter is incremented by one in a global associative array. It prints top thirty counters on exit. When we run it, it’s a surprise!

# stap rubystack.stp -c "ruby factorial.rb 42"
2904x Module.===
1782x BasicObject.==
1782x Kernel.===
1027x Symbol.to_s
1004x Kernel.initialize_dup
1003x Kernel.dup
990x Kernel.instance_variable_set
695x String.to_s
684x Hash.[]=
660x Gem::Specification.default_value
508x String.initialize_copy
456x Class.new
396x Array.initialize_copy
388x String.gsub
336x Kernel.class
324x Array.each
322x RbConfig.expand
320x Module.method_added
292x Array.flatten
267x File.file?
244x String.<=>
242x #<Class:0x0000000221ad70>.core#define_method
229x String.to_i
210x Enumerable.any?
185x String.strip
184x File.join
181x Regexp.=~
169x Kernel.untaint
168x Kernel.respond_to?
161x String.=~

I’d expect multiplying operation (Bignum.*) but we see equality of module instead. Since we count all the method calls, we can see what Ruby needs to done in the backround to load such a trivial example. It’s actually rubygems gem which ships with Ruby 2.0 that does the loading mechanics (and it’s poorly designed in my opinion).

Anyway, if you want to see the bignum thing, increase the parameter from 42 to let’s say 500. Now another example, slightly modified example from the SystemTap Wiki:

# cat rubytop.stp
global fn_calls
probe ruby.method.entry, ruby.cmethod.entry
  fn_calls[file, methodname, line] <<< 1
probe timer.ms(1000) {
  printf("%80s %6s %30s %6s\n", "FILENAME", "LINE", "METHOD", "CALLS")
  foreach ([filename, funcname, lineno] in fn_calls- limit 15) {
    printf("%80s %6d %30s %6d\n", filename, lineno, funcname, @count(fn_calls[filename, funcname, lineno]))
probe timer.ms(300000) {
  delete fn_calls

Now run the example with huge input that will cause it to loop for some time:

# stap rubytop.stp -c "ruby factorial.rb 999999999"

You should see a top-like screen which refreshes every second. The initial page will be full of method calls while from second one you will only see increasing counter of the multiply method:

                                        FILENAME   LINE                         METHOD  CALLS
                                    factorial.rb      2                              * 123483
   /usr/share/rubygems/rubygems/specification.rb   1775                            ===   3663
   /usr/share/rubygems/rubygems/specification.rb   1775                             ==   1782
   /usr/share/rubygems/rubygems/specification.rb   1779          instance_variable_set    660
   /usr/share/rubygems/rubygems/specification.rb   1779                           to_s    660
   /usr/share/rubygems/rubygems/specification.rb   1471                  default_value    660
   /usr/share/rubygems/rubygems/specification.rb   1776                            dup    594
   /usr/share/rubygems/rubygems/specification.rb   1776                 initialize_dup    594
   /usr/share/rubygems/rubygems/specification.rb   1776                initialize_copy    594
   /usr/share/rubygems/rubygems/specification.rb   1769                           to_s    330
   /usr/share/rubygems/rubygems/specification.rb   1769          instance_variable_set    330
                     /usr/lib64/ruby/rbconfig.rb    235                         expand    322
                     /usr/lib64/ruby/rbconfig.rb    236                           gsub    322
   /usr/share/rubygems/rubygems/specification.rb   1402                          file?    234
     /usr/share/rubygems/rubygems/requirement.rb     51                            ===    226

SystemTap is flexible, you can ignore some files (or directories) completely. Maybe you are only interested in code that was installed in /usr/share/project and you never want to see Kernel and Bignum classes, it’s as easy as:

probe ruby.method.entry, ruby.cmethod.entry
  if (file =~ "^/usr/share/project" && classname !~ "^(Kernel|Bignum)$") {
    fn_calls[file, methodname, line] <<< 1

By default, the counter struct resets every 5 minutes (see the second timer probe).

SystemTap Ruby markers in RHEL 7.0 offers the following probes:


It is also possible to attach to existing process:

# stap rubytop.stp -x 12345

When using Software Collections, note the correct SCL enable syntax (credit to Pavel Valena from Red Hat):

# scl enable rh-ruby22 -- stap rubystack.stp -c "ruby factorial.rb 5"

That’s all for now.

02 August 2016 | linux | fedora | systemtap

twitter.com linkedin.com
google.com/+ facebook.com
flickr.com youtube.com