Showing posts with label ruby. Show all posts
Showing posts with label ruby. Show all posts

Wednesday, June 25, 2008

ruby 可执行任意代码漏洞

今天看 slashdot,才知道 ruby 最近爆出的非常严重的漏洞,而我居然全然不知,出了一身冷汗,搜索订阅的 RSS 新闻,果然早有文章提起,夹杂在大量资讯中间,大约被直接忽视掉了。幸好我的项目还没有上线,以最快的速度订阅了 ruby-core 的 mailing list,下载 1.8.6 p230 开始升级。

去掉 debian 的 patches,dpkg-buildpackage 很顺利,安装过后,重启服务,随意访问了几个页面,出现 500 错误。检查 log:

ActionView::TemplateError (wrong argument type FalseClass (expected Proc))
*** glibc detected *** /usr/bin/ruby1.8: corrupted double-linked list: 0x00000000014eb220 ***
======= Backtrace: =========
/lib/libc.so.6[0x7ff36c97e01d]
/lib/libc.so.6[0x7ff36c97e146]
/lib/libc.so.6[0x7ff36c9802cc]
...

google 一下,发现我并不孤独,升级到 1.8.6 p230 后,都遇到了 segment fault 等问题。而奇怪的是,到目前位置,官方仍然没有一个解决方案,目前存在的解决方案和进展:

Wednesday, June 18, 2008

在 lenny 上降级 ruby 解决 rails 2.0 不兼容 ruby 1.8.7 问题

production 系统用的是 lenny,前两天 upgrade 了一把,ruby 1.8.7,当时并未在意,直到今天 cap deploy 后,部分页面出错,检查了一把,代码没问题,看来应该是升级到 1.8.7 后出现的问题了。google 了一番,的确如此:

Debian bug#484351 中有人指出最近的 rails 2.1.0 修正了1.8.7 兼容问题,事实上官方下载页面明确指出了 rails 还不兼容 ruby 1.8.7,看来升级到 rails 2.1.0 并不能解决这个问题,只能降级 ruby 了。

好在 debian 中还有 ruby 1.8.6 的 source,自己动手 build 吧。

wget http://debian.cn99.com/debian/pool/main/r/ruby1.8/ruby1.8_1.8.6.114-2.diff.gz
wget http://debian.cn99.com/debian/pool/main/r/ruby1.8/ruby1.8_1.8.6.114.orig.tar.gz
tar zxvf ruby1.8_1.8.6.114.orig.tar.gz
gzip -d ruby1.8_1.8.6.114-2.diff.gz
cd ruby-1.8.6-p114
aptitude build-dep ruby
dpkg-buildpackage
cd ../
dpkg -i --force-all irb1.8_1.8.6.114-2_all.deb libopenssl-ruby1.8_1.8.6.114-2_amd64.deb libreadline-ruby1.8_1.8.6.114-2_amd64.deb libruby1.8_1.8.6.114-2_amd64.deb rdoc1.8_1.8.6.114-2_all.deb ruby1.8_1.8.6.114-2_amd64.deb ruby1.8-dev_1.8.6.114-2_amd64.deb ruby1.8-elisp_1.8.6.114-2_all.deb
降级到 ruby 1.8.6 后 mongrel 的依赖出现问题,同样需要 rebuild,好在这个时候重启 mongrel,问题已经解决了。 如果希望能够解决 mongrel 的依赖问题的话:
apt-get source libsetup-ruby
cd libsetup-ruby-3.4.1
dpkg-buildpackage
cd ../
dpkg -i libsetup-ruby1.8_3.4.1-4_all.deb
apt-get source ruby-pkg-tools
cd ruby-pkg-tools-0.13
dpkg-buildpackage
cd ../
dpkg -i  ruby-pkg-tools_0.13_all.deb
rebuild 需要满足 rdoc 包,rebuild rdoc 的话需要一堆的依赖,我偷懒了一把
ln -s /usr/bin/rdoc1.8 /usr/bin/rdoc
apt-get source mongrel
cd mongrel-1.1.5
dpkg-buildpackage
cd ../
dpkg -i  mongrel_1.1.5-2_amd64.deb
依赖解决后,需要 hold ruby 1.8.6 的包,防止 upgrade 的时候再次升级。
aptitude hold irb1.8 libopenssl-ruby1.8 libreadline-ruby1.8 libruby1.8 mongrel rdoc1.8 ruby1.8 ruby1.8-dev ruby1.8-elisp

Friday, June 13, 2008

rails tips: 动态载入 JavaScript 文件

rails 中一般的 JavaScript 机制在 layout 中用 javascript_include_tag 载入需要的脚本文件,对默认整站需要的脚本文件来说很方便,但实际情况是我们经常需要在某些页面动态载入需要的脚本文件来避免调用过多外部文件影响页面载入速度。rails 提供了 javascript_include_tag 和 register_javascript_include_default 都可以用来载入脚本文件,register_javascript_include_default 每次调用都会增加一项到 @@javascript_default_sources,会导致重复载入同一文件,文档中已说明 register_javascript_include_default 用来在 plugin 载入时更改默认载入脚本。所以正确的方法应该是 javascript_include_tag。

javascript_include_tag 后调用直接输出 html script 标记,意味着脚本在 body 解释时才会载入,而我希望的结果是放置到 head 标记内。第一反应是搜索文档看是否存在 response 动态增加载入 javascript 的方法,rails 并没有提供此方法。正觉得无奈的时候忽然想到 rails layout 中用到了 yield,这里不是可以用 yield 达到同样的结果吗。 layouts/application.thml.erb

<%= javascript_include_tag :defaults %>
<%= yield :javascript_files %>
在需要载入的模板文件中:
<% content_for :javascript_files do -%>
<%= javascript_include_tag("some_file_to_load_on_demand.js") -%>
<% end -%>

Tuesday, June 03, 2008

用 monit 自动启动 mongrel 和 backgroundrb 服务

Capistrano 部署 Rails 项目速记中解决了代码更新时候 mongrel/backgroundrb 重启等问题,还需要保证系统重启时服务能够自动启动。

mongrel cluster 自带了 mongrel-cluster 启动管理脚本,Debian 系统的话链接 mongrel_cluster.yml 到 /etc/mongrel-cluster/sites-enabled/ 目录,系统启动的时候 /etc/init.d/mongrel-cluster 会自动启动 mongrel daemon,然而 backgroundrb 的启动问题还是没有解决,需要手工写启动脚本,google 了一番之后,找到 Monit 这个小工具:

# 摘自 monit deb package description.
A utility for monitoring and managing daemons or similar programs
 monit is a utility for monitoring and managing daemons or similar
 programs running on a Unix system. It will start specified programs
 if they are not running and restart programs not responding.
 .
 monit supports:
  * Daemon mode - poll programs at a specified interval
  * Monitoring modes - active, passive or manual
  * Start, stop and restart of programs
是否可以用 Monit 来启动 mongrel 和 backgroundrb 呢,答案是肯定的,事实上这也应该是推荐方式。

安装 Monit

$sudo aptitude install monit
修改 /etc/default/monit 配置:
startup=1
CHECK_INTERVALS=30 # monit 监控检测时间价格,推荐设置成 30 秒
配置 monit
$sudo grep '^[^#]' /etc/monit/monitrc
set daemon  30
set logfile syslog facility log_daemon
set httpd port 2812 and
     use address localhost  # only accept connection from localhost
     allow localhost        # allow localhost to connect to the server and
include /etc/monit/conf.d/*
$sudo mkdir /etc/monit/conf.d

配置 Daemon 项

$cat 001_neo_mongrel.conf
check process mongrel_neo_8000
  with pidfile /project/code/path/shared/pids/mongrel.8000.pid
  start program = "/usr/bin/mongrel_rails cluster::start -C /project/code/path/current/config/mongrel_cluster.yml --clean --only 8000"
  stop program = "/usr/bin/mongrel_rails cluster::stop -C /project/code/path/current/config/mongrel_cluster.yml --only 8000"
  if totalmem is greater than 110.0 MB for 4 cycles then restart
  if cpu is greater than 80% for 4 cycles then restart
  if 20 restarts within 20 cycles then timeout
  group neo

check process mongrel_neo_8001
  with pidfile /project/code/path/shared/pids/mongrel.8001.pid
  start program = "/usr/bin/mongrel_rails cluster::start -C /project/code/path/current/config/mongrel_cluster.yml --clean --only 8001"
  stop program = "/usr/bin/mongrel_rails cluster::stop -C /project/code/path/current/config/mongrel_cluster.yml --only 8001"
  if totalmem is greater than 110.0 MB for 4 cycles then restart
  if cpu is greater than 80% for 4 cycles then restart
  if 20 restarts within 20 cycles then timeout
  group neo

check process mongrel_neo_8002
  with pidfile /project/code/path/shared/pids/mongrel.8002.pid
  start program = "/usr/bin/mongrel_rails cluster::start -C /project/code/path/current/config/mongrel_cluster.yml --clean --only 8002"
  stop program = "/usr/bin/mongrel_rails cluster::stop -C /project/code/path/current/config/mongrel_cluster.yml --only 8002"
  if totalmem is greater than 110.0 MB for 4 cycles then restart
  if cpu is greater than 80% for 4 cycles then restart
  if 20 restarts within 20 cycles then timeout
  group neo
$ cat 003_neo_brb.conf
check process brb_imigu_11006
  with pidfile /project/code/path/shared/pids/backgroundrb_11006.pid
  start program = "/bin/su -c 'cd  /project/code/pathcurrent && RAILS_ENV=production nohup ./script/backgroundrb start > /dev/null 2>&1' josh"
  stop program = "/bin/su -c 'cd /home/josh/www/imigu/current && RAILS_ENV=production && ./script/backgroundrb stop' josh"
  group neo

启动 Monit 和监测

$sudo /etc/init.d/monit restart
$sudo monit -c /etc/monit/monitrc status #查看系统状态
通过 monit 已经可以自动启动管理 mongrel 和 backgroundrb,那么谁来保证 monit 的稳定运行呢,答案是 linux 系统 init。 编辑 /etc/inittab 加入:
# Run monit in standard run-levels
mo:2345:respawn:/usr/sbin/monit -Ic /etc/monit/monitrc
mo:06:wait:/usr/sbin/monit -Ic /etc/monit/monitrc stop all
重启 monit
$sudo /etc/init.d/monit stop
$sudo telinit q

参考:

  • Monit man page
  • 2008, Ezra Zygmuntowicz and Bruce Tate, Deploying Rails Applications

Friday, May 30, 2008

Capistrano 部署 Rails 项目速记

项目快上线了,以前一直采用传统的方式进行部署,从最早的 FTP 到后来的 SVN+SSH,既然现在是 Rails 项目,当然试一把 Capistrano。

安装

使用 Capistrano 进行部署只需要在本地机器安装 Capistrano 即可,Capistrano 通过 SSH 与服务器端进行交互。

gem install -y capistrano
当前最新版本是 2.3.0。

配置

Ubuntu 8.0.4 上 gem 包相关的 bin 目录并不在系统路径中,需要手工加入gem bin 路径:

$cat "export PATH=$PATH:/var/lib/gems/1.8/bin" >> .~/bashrc
$source ~/.bashrc

生成基础配置文件:

$cd RAILS_APP_DIR
$capify .

调整 config/deploy.rb 中相关配置项,并加入一行:

set :runner, "ACCOUNT_NAME"
用你服务器端的登录用户名取代 ACCOUNT_NAME,如果不设置此行,默认用户为 app,会导致设置失败。

初始化服务器端目录:

$cap deploy:setup
Capistrano > 2.3.0 开始默认启用 sudo,所以,服务器需要支持 sudo 支持,将 ACOUNT_NAME 加入到 sudo group, 并确认 /etc/sudoers 中设置:
# Uncomment to allow members of group sudo to not need a password
 %sudo ALL= ( ALL ) NOPASSWD: ALL

SSH 自动登录:

$ssh-keygen -t dsa
$chmod 700 ~/.ssh
$scp ~/.ssh/id_dsa.pub USER_NAME@REMOTE_SERVER
在服务器:
$cat id_dsa.pub >> ~/.ssh/authorized_keys
$rm -f id_dsa.pub

运行设定和依赖检查:

set :deploy_via, :remote_cache
set :runner, "josh"
depend :remote, :gem, "chronic", ">= 0.2.3"
depend :remote, :gem, "packet", ">= 0.1.5"
depend :remote, :command, "gnuplot"
depend :remote, :command, "svn"
depend :remote, :command, "mongrel_cluster_ctl" # make sure /usr/sbin in the $PATH
需要注意的是 debian 中的 mongrel_cluster_ctl 脚本在 /usr/sbin 下,而通过 ssh 获得的系统 $PATH 并没有包含 /usr/sbin 目录,需要修改 .ssh/environment 和 ssh 的相关配置加入 /usr/sbin 目录。 Mongrel && backgroundrb 启动配置
rails_env = "production"
set :mongrel_environment, "production"
set :mongrel_conf, "#{deploy_to}/current/config/mongrel_cluster.yml"

namespace :deploy do
  
  desc "Restart the Mongrel cluster and backgroundrb"
  task :restart, :roles => :app do
    stop
    start
  end
  desc "Start the mongrel cluster and backgroundrb"
  task :start, :roles => :app do
    mongrel.cluster.start
    start_backgroundrb
  end
  desc "Stop the mongrel cluster and backgroundrb"
  task :stop, :roles => :app do
    mongrel.cluster.stop
    stop_backgroundrb
  end

  desc "Start the backgroundrb server"
  task :start_backgroundrb , :roles => :app do
    begin
      puts "starting brb in folder #{current_path}"
      run "cd #{current_path} && RAILS_ENV=#{rails_env} nohup ./script/backgroundrb start > /dev/null 2>&1"
    rescue RuntimeError => e
      puts e
      puts "Problems starting backgroundrb – running already?"
    end
  end
  desc "Stop the backgroundrb server"
  task :stop_backgroundrb , :roles => :app do
    begin
      puts "stopping brb in folder #{current_path}"
      run "cd #{current_path} && ./script/backgroundrb stop"
    rescue RuntimeError => e
      puts e
      puts "Backgroundrb appears to be down already."
    end
  end
end

需要注意的是 Capistrano 每次 deploy 的时候会重新检出代码,用户上传文件需要单独做 link:
namespace :assets do
  task :symlink, :roles => :app do
    run <<-CMD
rm -rf #{release_path}/assets &&
ln -nfs #{shared_path}/assets #{release_path}/assets
CMD
  end
  after "deploy:update_code" , "assets:symlink"
  
  task :create_dirs, :roles => :app do
    %w(assets).each do |name|
      run "mkdir -p #{shared_path}/#{name}"
    end
  end
  after "deploy:setup", "assets:create_dirs"
end

参考文档:

Friday, March 14, 2008

ruby block 局部变量

今天遇到的问题,在 block 中定义的变量为局部变量,无法在 block 外以及下一次迭代中使用,必须定义在 block 开始之前。

Wednesday, March 12, 2008

flymake 运行 rails scripts/runner 脚本文件

写了几个脚本,用到了 rails script/runner: {{{ #!/usr/bin/env /project_path/script/runner }}} 结果发现 emacs flymake mode 执行了这个文件,而不是默认的 c (check) 语法检查。 去官方看了一下 bug,原来已经有人报告过了, 修改为 {{{ #!/usr/bin/ruby /project_path/script/runner }}} 问题修正。反正系统只会是 linux,没有这烦人的问题就成。

Tuesday, February 26, 2008

rails tips

随手记录开发中遇到的小问题,备忘。

install plugins

undefined method `paginate' for
安装 plugin 后,需要重启 web server。

radio for boolean field

<%= f.radio_button :direction, "true" %>
<%= f.radio_button :direction, "false" %>

selected for options key value pairs array

it didn't well documented, here is the example:
	  <%= select_tag(
	      :sector_id,
	      options_for_select(
	        [["--请选择--", ""]].concat(Sector.get_for_list.collect {|o| [ o.name, o.id ] }), 
	        [@sector.id, @sector.name])
	      )
	  %>