Bug 943757

Summary: Yast ncurses crashes on warnings from rubygems (STDERR not properly routed to log)
Product: [openSUSE] openSUSE Distribution Reporter: Arvin Schnell <aschnell>
Component: YaST2Assignee: YaST Team <yast-internal>
Status: RESOLVED FIXED QA Contact: Jiri Srain <jsrain>
Severity: Normal    
Priority: P2 - High CC: ancor, aschnell, mkrapp, mvidner, shundhammer
Version: 13.2   
Target Milestone: ---   
Hardware: Other   
OS: Other   
Whiteboard:
Found By: Development Services Priority:
Business Priority: Blocker: ---
Marketing QA Status: --- IT Deployment: ---
Attachments: y2log

Description Arvin Schnell 2015-08-31 09:05:36 UTC
For various ruby programs I have installed a bunch of gems from
different sources (which I have no overview of). Anyway, many
ruby programs print an error during startup, this can be show using
irb:

aschnell@thalassa:~> irb
irb(main):001:0> require "dbus"
WARNING: Nokogiri was built against LibXML version 2.9.2, but has dynamically loaded 2.9.1
=> true

Unfortunately for yast ncurses this causes a program termination since stderr
is in a bad state (I suppose closed):

[Y2Ruby] binary/Y2RubyUtils.cc(y2_require):25 cannot require yast:Bad file descriptor @ io_write - <STDERR> at /usr/lib64/ruby/gems/2.1.0/gems/nokogiri-1.6.6.2/lib/nokogiri/version.rb:94:in `write'

The rest of y2log is very misleading, e.g. stating the Storage.rb does not
provide the module Storage. But with the Qt frontend YaST does start.

The system is openSUSE 13.2 with YaST:Head and various ruby gems.
Comment 1 Arvin Schnell 2015-08-31 09:06:52 UTC
Created attachment 645556 [details]
y2log
Comment 2 Lukas Ocilka 2015-08-31 11:38:14 UTC
Martin, is there anything we could do about stderr from Ruby libs while
running Yast in ncurses? E.g., forwarding it to y2log?

Side note, Yast ncurses require gems to be in order.
Comment 3 Martin Vidner 2015-08-31 12:25:49 UTC
To reproduce:

# /usr/lib/YaST2/bin/y2base ./hello.rb ncurses
# https://bugzilla.suse.com/show_bug.cgi?id=943757

require "yast"
include Yast::UIShortcuts

$stderr.puts "Hello, stderr! 1" # This succeeds

module Yast
  import "UI"

  UI.OpenDialog(PushButton("Hello, World!"))
  $stderr.puts "Hello, stderr! 2" # Bad file descriptor @ io_write
  UI.UserInput
  UI.CloseDialog
end

I thought that we did redirect to the log, but it turns out that since 2008 ncurses redirects stdout and stderr to /dev/null, even with some suspicious flags (defaulting to O_RDONLY) which may never have worked:
https://github.com/libyui/libyui-ncurses/blob/8f861f5ccb77a72fdcc17e909f2811102f09d3b1/src/NCurses.cc#L566
	open( log.c_str(), O_APPEND | O_CREAT, 0666 );
Comment 4 Martin Vidner 2015-08-31 13:22:10 UTC
To fix the nokogiri warning, from what I've checked at thalassa it seems that you can uninstall the gem and install a rpm-wrapped gem from 13.2 instead:

# gem uninstall -v 1.6.6.2 nokogiri
# zypper in rubygem-nokogiri
Comment 5 Lukas Ocilka 2015-09-01 11:38:02 UTC
Looks like it was done bu this commit, but that mostly fixes just the formatting https://github.com/libyui/libyui-ncurses/commit/9a033491a3126270b310e9a0106c5dbb96cd9443

Katarina has moved this to a separate function in 2007 https://github.com/libyui/libyui-ncurses/blob/master/ChangeLog#L854

And this was the commit https://github.com/libyui/libyui-ncurses/commit/dd18ea2a3fca3864d43aff710a6d8097206b516f

--> P2
Comment 6 Ancor Gonzalez Sosa 2015-09-14 11:52:46 UTC
Talking about ruby-based solutions, this works. But it's more an experiment than a reliable fix. I would rather try to replace $stderr by a proper IO object, instead of hijacking #puts and #print (since there are also #printf, #putc, #write...)


require "yast"
include Yast::UIShortcuts

$stderr.puts "Hello, stderr! 1"

$stderr.define_singleton_method(:puts) do |*args|
  if args.empty?
    Yast.y2error($/)
  else
    Yast.y2error(args.join($/))
  end
end

$stderr.define_singleton_method(:print) do |*args|
  if args.empty?
    Yast.y2error($_)
  else
    Yast.y2error(args.map(&:to_s).join($,) + $\.to_s)
  end
end

module Yast
  import "UI"

  UI.OpenDialog(PushButton("Hello, World!"))
  # Al this messages works as expected (logging to YaST logs)
  $stderr.puts "Hello, stderr! 2"
  $stderr.puts "Halo", "stderr!", "2"
  $stderr.print "Hola, ! 2"
  $stderr.print "Ahoj", "stderr!", "2"
  UI.UserInput
  UI.CloseDialog
end
Comment 7 Ancor Gonzalez Sosa 2015-09-14 14:55:32 UTC
As I see it, we have two options.

1) Fix RedirectToLog. I don't know why it was changed from y2logs to /dev/null, but I can imagine at least one reason: coupling of libyui and YaST.

Btw, it was HuHa who made it, past always comes back. :-)
https://github.com/libyui/libyui-ncurses/commit/3e10a9a29905056b9cf2b6ef746a71796bf055ca

2) Implement the same mechanism in YaST (and Ruby). This script works.

require "yast"
include Yast::UIShortcuts

# We'd need to expose get_log_filename through yast-ruby-bindings.
# Let's fake it for demo purposes
module Yast
  def self.get_log_filename; "/var/log/YaST2/y2log"; end
end

# We'd need to call this before NCurses::init
# (still to be decided exactly when/where)
def redirect_std_streams
  [$stderr, $stdout].each do |stream|
    if stream.tty?
      stream.reopen(Yast.get_log_filename, "a")
      stream.sync = true
    end
  end
end

# Let's call it here for the demo
redirect_std_streams

module Yast
  import "UI"

  UI.OpenDialog(PushButton("Hello, World!"))
  # Al this messages works as expected (logging to YaST logs)
  $stderr.puts "Hello, stderr! 2"
  $stderr.puts "Halo", "stderr!", "2"
  $stderr.print "Hola, ! 2"
  $stderr.print "Ahoj", "stderr!", "2"
  UI.UserInput
  UI.CloseDialog
end

I'd like to know what Martin and HuHa think about it. Maybe they even remember the reason for the /dev/null thingy.
Comment 8 Stefan Hundhammer 2015-09-15 08:19:32 UTC
It's been a long time... ;-)

I faintly remember that redirecting all stderr output to the y2log tends to mess up the y2log badly as there are different levels of buffering all over the place. libyui has its own thread-safe logger with its own logging buffer. If some other output is writing to the same file handle, but bypassing that thread-safe log buffer, the output will become mixed, disregarding things like output lines (and of course the y2log line header with the timestamp, PID, source file, function, source line number).

IIRC we used that for a short while and then became frustrated quickly because the logs were messed up in the most critical moments.

There is no easy way around this; IIRC I tried to come up with a strategy to route that output through the thread-safe libyui logger, but if a process simply writes to a file handle, there is no reliable way to do this.

Another approach might be to simply open yet another file dedicated to stderr output. Of course that implies taking care of log rotation etc.; that's probably the reason why back then we had decided to redirect it to /dev/null. That output just wasn't that important.
Comment 9 Ancor Gonzalez Sosa 2015-09-15 08:39:09 UTC
Of course, we also can insist in the current solution of sending everything to /dev/null and avoid the side effects described by HuHa. In that case, it's still true that we have the same two options - fix RedirectToLog which is clearly not working or implementing the redirection as ruby code in YaST.

I'll check what's the problem with RedirecToLog. Maybe it's completely useless or maybe it's being called too late or simply not called at all.
Comment 10 Ancor Gonzalez Sosa 2015-09-15 10:39:39 UTC
Thanks to the changes introduced recently in libyui, now it's possible to write an RSpec test like this https://github.com/ancorgs/yast-yast2/commit/f52bc2350e9690415d1f53b5175e107a7529433d

I know it's probably misplaced as part of UI:Dialog tests. The final location of the test will depend on where do we fix the error, but at least is ready.
Comment 11 Martin Vidner 2015-09-15 11:31:05 UTC
I have checked RedirectToLog, adding O_WRONLY to the flags works.

I think that it is enough to continue discarding the output.
Comment 12 Ancor Gonzalez Sosa 2015-09-17 07:59:10 UTC
The issue is fixed in libyui-ncurses 2.47.3 (Tumbleweed and SLE12-SP1)
https://build.suse.de/request/show/68148
https://build.opensuse.org/request/show/331511

Keeping the bug open since we are still deciding where and how integrate the automated test
https://github.com/yast/yast-ruby-bindings/pull/153
Comment 13 Martin Vidner 2015-10-08 14:11:17 UTC
The tests have been merged, the bug is thus resolved. Thanks everyone!
Comment 14 Leonardo Chiquitto 2016-08-10 12:52:43 UTC
*** Bug 956265 has been marked as a duplicate of this bug. ***