Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

Rubyプロジェクトのアイデア:独自のLinuxツールを構築する

一緒にプロジェクトを作りましょう!

psなどのLinuxツール 、topnetstat 素晴らしいです。

システムで何が起こっているかについて多くの情報を提供します。

  • しかし、それらはどのように機能しますか?
  • 彼らはどこからすべての情報を入手しますか?
  • これを使用して独自のツールを構築するにはどうすればよいですか?

この投稿では、3つの人気のあるLinuxツールを一緒に再作成します。

2×1の食事を取り、Rubyのトリックを学び、同時にLinuxの有用な知識を習得します。 🙂

ステータス情報の検索

それでは、これらすべてのツールが情報をどこで見つけるかという質問に答えてみましょう。

答えはprocファイルシステムにあります!

/procの内部を見ると ディレクトリそれは、コンピュータ上の他のディレクトリと同じように、一連のディレクトリとファイルのように見えます。

これらは実際のファイルではなく、Linuxカーネルがユーザーにデータを公開するための単なる方法です。

通常のファイルと同じように扱うことができるため、特別なツールなしで読み取ることができるため、非常に便利です。

Linuxの世界では、多くのことがこのように機能します。

別の例を見たい場合は、/devを見てください。 ディレクトリ。

何を扱っているかがわかったので、/procの内容を見てみましょう。 ディレクトリ…

1
10
104
105
11
11015
11469
11474
11552
11655

これはほんの小さなサンプルですが、パターンにすぐに気付くことができます。

これらの数字は何ですか?

そうですね、これらはPID(プロセスID)であることがわかりました。

すべてのエントリには、特定のプロセスに関する情報が含まれています。

psを実行する場合 すべてのプロセスにPIDが関連付けられていることがわかります:

PID   TTY      TIME     CMD
15952 pts/5    00:00:00 ps
22698 pts/5    00:00:01 bash

このことから、psが行うことは/procを反復するだけであると推測できます。 ディレクトリを作成し、見つかった情報を印刷します。

これらの番号が付けられたディレクトリの1つに何が入っているか見てみましょう:

attr
autogroup
auxv
cgroup
clear_refs
cmdline
comm
cpuset
cwd
environ
exe
fd

これはスペースを節約するための単なるサンプルですが、完全なリストを確認することをお勧めします。

ここにいくつかの興味深いエントリがあります

エントリ 説明 プログラムの名前 このプロセスを起動するために使用されるコマンド 環境 このプロセスが開始された環境変数 ステータス
comm
コマンドライン
プロセスステータス(実行中、スリープ中…)とメモリ使用量
fd ファイル記述子(開いているファイル、ソケットなど)を含むディレクトリ

これがわかったので、いくつかのツールを書き始めることができるはずです!

実行中のプログラムを一覧表示する方法

/procの下にあるすべてのディレクトリのリストを取得することから始めましょう 。

Dirを使用してこれを行うことができます クラス。

Dir.glob("/proc/[0-9]*")

数値範囲をどのように使用したかに注意してください。その理由は、/procの下に他のファイルがあるためです。 今は気にしないので、番号の付いたディレクトリだけが必要です。

これで、このリストを繰り返し処理して、2つの列を出力できます。1つはPIDで、もう1つはプログラム名です。

pids = Dir.glob("/proc/[0-9]*")

puts "PID\tCMD"
puts "-" * 15

pids.each do |pid|
  cmd = File.read(pid + "/comm")
  pid = pid.scan(/\d+/).first

  puts "#{pid}\t#{cmd}"
end

これが出力です

PID    CMD
---------------
1     systemd
2     kthreadd
3     ksoftirqd/0
5     kworker/0
7     migration/0
8     rcu_preempt
9     rcu_bh
10    rcu_sched

ねえ、psを作ったようです !ええ、それはオリジナルからのすべての派手なオプションをサポートしていませんが、私たちは何かを機能させました。

誰が聞いていますか?

netstatを複製してみましょう これで、出力は次のようになります(-antを使用) フラグとして)。

Active Internet connections (servers and established)

Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 127.0.0.1:5432          0.0.0.0:*               LISTEN     
tcp        0      0 192.168.1.82:39530      182.14.172.159:22       ESTABLISHED

この情報はどこにありますか? 「/procの内部」と言った場合 " あなたが正しい!具体的には、/proc/net/tcpにあります。 。

ただし、少し問題があります。これはnetstatのようには見えません。 出力!

0: 0100007F:1538 00000000:0000 0A 00000000:00000000 00:00000000 00000000  1001 0 9216
1: 2E58A8C0:9A6A 9FBB0EB9:0016 01 00000000:00000000 00:00000000 00000000  1000 0 258603

これが意味するのは、正規表現を使用して解析を行う必要があるということです。とりあえず、ローカルアドレスとステータスについて心配しましょう。

これが私が思いついた正規表現です

LINE_REGEX = /\s+\d+: (?<local_addr>\w+):(?<local_port>\w+) \w+:\w+ (?<status>\w+)/

これにより、10進数に変換する必要のある16進値が得られます。これを行うクラスを作成しましょう。

class TCPInfo
  def initialize(line)
    @data = parse(line)
  end

  def parse(line)
    line.match(LINE_REGEX)
  end

  def local_port
    @data["local_port"].to_i(16)
  end

  # Convert hex to regular IP notation
  def local_addr
    decimal_to_ip(@data["local_addr"].to_i(16))
  end

  STATUSES = {
    "0A" => "LISTENING",
    "01" => "ESTABLISHED",
    "06" => "TIME_WAIT",
    "08" => "CLOSE_WAIT"
  }

  def status
    code = @data["status"]

    STATUSES.fetch(code, "UNKNOWN")
  end

  # Don't worry too much about this. It's some binary math.
  def decimal_to_ip(decimal)
    ip = []

    ip << (decimal >> 24 & 0xFF)
    ip << (decimal >> 16 & 0xFF)
    ip << (decimal >> 8 & 0xFF)
    ip << (decimal & 0xFF)

    ip.join(".")
  end
end

残っているのは、結果をきれいな表形式で印刷することだけです。

require 'table_print'

tp connections

出力例

STATUS      | LOCAL_PORT | LOCAL_ADDR   
------------|------------|--------------
LISTENING   | 5432       | 127.0.0.1    
ESTABLISHED | 39530      | 192.168.88.46

はい、この宝石は素晴らしいです!

私はそれについて見つけたばかりで、ljustをいじくり回す必要はないようです / rjust もう一度🙂

マイポートの使用をやめてください!

このメッセージを見たことがありますか?

Address already in use - bind(2) for "localhost" port 5000

うーん…

どのプログラムがそのポートを使用しているのだろうか。

調べてみましょう

fuser -n tcp -v 5000

PORT       USER        PID   ACCESS CMD
5000/tcp   rubyguides  30893 F....  nc

ああ、それで私たちの犯人がいます!

これで、このプログラムを実行したくない場合は停止できます。これにより、ポートが解放されます。 「fuser」プログラムは、このポートを使用しているユーザーをどのようにして見つけましたか?

あなたはそれを推測しました!

/proc 再びファイルシステム。

実際、これはすでに説明した2つのことを組み合わせたものです。プロセスリストをウォークスルーすることと、/proc/net/tcpからアクティブな接続を読み取ることです。 。

もう1つのステップが必要です

開いているポート情報をPIDと一致させる方法を見つけてください。

/proc/net/tcpから取得できるTCPデータを見ると 、PIDはありません。ただし、iノード番号を使用できます。

「iノードは、ファイルシステムオブジェクトを表すために使用されるデータ構造です。」 –ウィキペディア

iノードを使用して一致するプロセスを見つけるにはどうすればよいですか? fdの下を見ると ポートが開いていることがわかっているプロセスのディレクトリには、次のような行があります:

/proc/3295/fd/5 -> socket:[12345]

括弧内の数字はiノード番号です。これで、すべてのファイルを反復処理するだけで、一致するプロセスが見つかります。

これを行う1つの方法があります

x =
Dir.glob("/proc/[0-9]*/fd/*").find do |fd|
  File.readlink(fd).include? "socket:[#{socket_inode}]" rescue nil
end

pid  = x.scan(/\d+/).first
name = File.readlink("/proc/#{pid}/exe")

puts "Port #{hex_port.to_i(16)} in use by #{name} (#{pid})"
で使用中

出力例:

Port 5432 in use by /usr/bin/postgres (474)

このコードはrootまたはプロセス所有者として実行する必要があることに注意してください。

そうしないと、/proc内のプロセスの詳細を読み取ることができません。 。

結論

この投稿では、Linuxが仮想/procを介して大量のデータを公開することを学びました ファイルシステム。また、/procの下のデータを使用して、ps、netstat、fuserなどの一般的なLinuxツールを再作成する方法も学びました。 。

次の投稿を見逃さないように、以下のニュースレターを購読することを忘れないでください。 🙂


  1. Rubyの関数とメソッド:独自の関数を定義する方法

    Rubyメソッドとは何ですか? メソッドは、特定の目的のためにグループ化された1行または複数行のRubyコードです。 このグループ化されたコードには名前が付けられているため、コードを再度記述したり、コピーして貼り付けたりすることなく、いつでも使用できます。 メソッドの目的は次のとおりです : 情報を取得します。 オブジェクトを変更または作成します。 フィルターとフォーマットのデータ。 例1 : サイズ Arrayのメソッド オブジェクトは要素の数を示します(情報を取得します)。 例2 : pop メソッドは、配列から最後の要素を削除します(オブジェクトを変更します)。

  2. SUSE Studio - 独自の Linux を作成する

    SUSE Studio は、Novell が後援するサービスであり、繰り返しますが、どなたでも、ある程度の忍耐と Web ブラウザだけを使用して、openSUSE とその変種の独自のカスタム フレーバーを作成できます。すごいですね?です。 SUSE Studio は、驚異的な成功を収めた有用な Kiwi イメージング システムの次の自然なステップです。これにより、SUSE ユーザーはオペレーティング システムの独自のエディションをすばやく簡単に作成できます。 はじめに Image Creator や Product Creator などのフレンドリーなフロントエンド アプライ